Clear OpenGL textures on first use if not initialized already
This prevents dirty textures to be used when memory is recycled while destroying/creating new textures. If a texture is being used for the first time and has not yet been initialized, it will be cleared to zeros. Bug: dawn:145 Change-Id: I1140ff0535241b247b855df2afefc01fbc003470 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/8380 Reviewed-by: Corentin Wallez <cwallez@chromium.org> Reviewed-by: Austin Eng <enga@chromium.org> Commit-Queue: Natasha Lee <natlee@microsoft.com>
This commit is contained in:
parent
ce32a94b37
commit
4886403b61
|
@ -26,6 +26,7 @@
|
|||
#include "dawn_native/opengl/RenderPipelineGL.h"
|
||||
#include "dawn_native/opengl/SamplerGL.h"
|
||||
#include "dawn_native/opengl/TextureGL.h"
|
||||
#include "dawn_native/opengl/UtilsGL.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
|
@ -129,15 +130,6 @@ namespace dawn_native { namespace opengl {
|
|||
}
|
||||
}
|
||||
|
||||
GLint GetStencilMaskFromStencilFormat(dawn::TextureFormat depthStencilFormat) {
|
||||
switch (depthStencilFormat) {
|
||||
case dawn::TextureFormat::Depth24PlusStencil8:
|
||||
return 0xFF;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
// Vertex buffers and index buffers are implemented as part of an OpenGL VAO that
|
||||
// corresponds to an VertexInput. On the contrary in Dawn they are part of the global state.
|
||||
// This means that we have to re-apply these buffers on an VertexInput change.
|
||||
|
@ -349,17 +341,34 @@ namespace dawn_native { namespace opengl {
|
|||
void CommandBuffer::Execute() {
|
||||
const OpenGLFunctions& gl = ToBackend(GetDevice())->gl;
|
||||
|
||||
auto TransitionForPass = [](const PassResourceUsage& usages) {
|
||||
for (size_t i = 0; i < usages.textures.size(); i++) {
|
||||
Texture* texture = ToBackend(usages.textures[i]);
|
||||
texture->EnsureSubresourceContentInitialized(0, texture->GetNumMipLevels(), 0,
|
||||
texture->GetArrayLayers());
|
||||
}
|
||||
};
|
||||
|
||||
const std::vector<PassResourceUsage>& passResourceUsages = GetResourceUsages().perPass;
|
||||
uint32_t nextPassNumber = 0;
|
||||
|
||||
Command type;
|
||||
while (mCommands.NextCommandId(&type)) {
|
||||
switch (type) {
|
||||
case Command::BeginComputePass: {
|
||||
mCommands.NextCommand<BeginComputePassCmd>();
|
||||
TransitionForPass(passResourceUsages[nextPassNumber]);
|
||||
ExecuteComputePass();
|
||||
|
||||
nextPassNumber++;
|
||||
} break;
|
||||
|
||||
case Command::BeginRenderPass: {
|
||||
auto* cmd = mCommands.NextCommand<BeginRenderPassCmd>();
|
||||
TransitionForPass(passResourceUsages[nextPassNumber]);
|
||||
ExecuteRenderPass(cmd);
|
||||
|
||||
nextPassNumber++;
|
||||
} break;
|
||||
|
||||
case Command::CopyBufferToBuffer: {
|
||||
|
@ -384,6 +393,11 @@ namespace dawn_native { namespace opengl {
|
|||
Texture* texture = ToBackend(dst.texture.Get());
|
||||
GLenum target = texture->GetGLTarget();
|
||||
auto format = texture->GetGLFormat();
|
||||
if (IsCompleteSubresourceCopiedTo(texture, copySize, dst.level)) {
|
||||
texture->SetIsSubresourceContentInitialized(dst.level, 1, dst.slice, 1);
|
||||
} else {
|
||||
texture->EnsureSubresourceContentInitialized(dst.level, 1, dst.slice, 1);
|
||||
}
|
||||
|
||||
gl.BindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer->GetHandle());
|
||||
gl.ActiveTexture(GL_TEXTURE0);
|
||||
|
@ -426,6 +440,7 @@ namespace dawn_native { namespace opengl {
|
|||
auto format = texture->GetGLFormat();
|
||||
GLenum target = texture->GetGLTarget();
|
||||
|
||||
texture->EnsureSubresourceContentInitialized(src.level, 1, src.slice, 1);
|
||||
// The only way to move data from a texture to a buffer in GL is via
|
||||
// glReadPixels with a pack buffer. Create a temporary FBO for the copy.
|
||||
gl.BindTexture(target, texture->GetHandle());
|
||||
|
@ -473,7 +488,12 @@ namespace dawn_native { namespace opengl {
|
|||
auto& copySize = copy->copySize;
|
||||
Texture* srcTexture = ToBackend(src.texture.Get());
|
||||
Texture* dstTexture = ToBackend(dst.texture.Get());
|
||||
|
||||
srcTexture->EnsureSubresourceContentInitialized(src.level, 1, src.slice, 1);
|
||||
if (IsCompleteSubresourceCopiedTo(dstTexture, copySize, dst.level)) {
|
||||
dstTexture->SetIsSubresourceContentInitialized(dst.level, 1, dst.slice, 1);
|
||||
} else {
|
||||
dstTexture->EnsureSubresourceContentInitialized(dst.level, 1, dst.slice, 1);
|
||||
}
|
||||
gl.CopyImageSubData(srcTexture->GetHandle(), srcTexture->GetGLTarget(),
|
||||
src.level, src.origin.x, src.origin.y, src.slice,
|
||||
dstTexture->GetHandle(), dstTexture->GetGLTarget(),
|
||||
|
@ -683,7 +703,6 @@ namespace dawn_native { namespace opengl {
|
|||
if (renderPass->sampleCount > 1) {
|
||||
ResolveMultisampledRenderTargets(gl, renderPass);
|
||||
}
|
||||
|
||||
gl.DeleteFramebuffers(1, &fbo);
|
||||
return;
|
||||
} break;
|
||||
|
|
|
@ -14,8 +14,10 @@
|
|||
|
||||
#include "dawn_native/opengl/TextureGL.h"
|
||||
#include "dawn_native/opengl/DeviceGL.h"
|
||||
#include "dawn_native/opengl/UtilsGL.h"
|
||||
|
||||
#include "common/Assert.h"
|
||||
#include "dawn_native/Commands.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
@ -174,9 +176,8 @@ namespace dawn_native { namespace opengl {
|
|||
ASSERT(GetFormat().blockByteSize <= MAX_TEXEL_SIZE);
|
||||
GLubyte clearColor[MAX_TEXEL_SIZE];
|
||||
std::fill(clearColor, clearColor + MAX_TEXEL_SIZE, 255);
|
||||
|
||||
// TODO(natlee@microsoft.com): clear all subresources
|
||||
for (uint32_t i = 0; i < GL_TEXTURE_MAX_LEVEL; i++) {
|
||||
for (uint32_t i = 0; i < GetNumMipLevels(); i++) {
|
||||
gl.ClearTexImage(mHandle, i, formatInfo.format, formatInfo.type, clearColor);
|
||||
}
|
||||
}
|
||||
|
@ -211,6 +212,60 @@ namespace dawn_native { namespace opengl {
|
|||
return GetGLFormatInfo(GetFormat().format);
|
||||
}
|
||||
|
||||
void Texture::ClearTexture(GLint baseMipLevel,
|
||||
GLint levelCount,
|
||||
GLint baseArrayLayer,
|
||||
uint32_t layerCount) {
|
||||
const OpenGLFunctions& gl = ToBackend(GetDevice())->gl;
|
||||
if (GetFormat().HasDepthOrStencil()) {
|
||||
bool doDepthClear = GetFormat().HasDepth();
|
||||
bool doStencilClear = GetFormat().HasStencil();
|
||||
GLfloat depth = 0.0f;
|
||||
GLint stencil = 0u;
|
||||
if (doDepthClear) {
|
||||
gl.DepthMask(GL_TRUE);
|
||||
}
|
||||
if (doStencilClear) {
|
||||
gl.StencilMask(GetStencilMaskFromStencilFormat(GetFormat().format));
|
||||
}
|
||||
|
||||
GLuint framebuffer = 0;
|
||||
gl.GenFramebuffers(1, &framebuffer);
|
||||
gl.BindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer);
|
||||
gl.FramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GetGLTarget(),
|
||||
GetHandle(), 0);
|
||||
if (doDepthClear && doStencilClear) {
|
||||
gl.ClearBufferfi(GL_DEPTH_STENCIL, 0, depth, stencil);
|
||||
} else if (doDepthClear) {
|
||||
gl.ClearBufferfv(GL_DEPTH, 0, &depth);
|
||||
} else if (doStencilClear) {
|
||||
gl.ClearBufferiv(GL_STENCIL, 0, &stencil);
|
||||
}
|
||||
gl.DeleteFramebuffers(1, &framebuffer);
|
||||
} else {
|
||||
auto formatInfo = GetGLFormatInfo(GetFormat().format);
|
||||
for (GLint level = baseMipLevel; level < baseMipLevel + levelCount; ++level) {
|
||||
gl.ClearTexSubImage(mHandle, level, 0, 0, baseArrayLayer, GetSize().width,
|
||||
GetSize().height, layerCount, formatInfo.format,
|
||||
formatInfo.type, nullptr);
|
||||
}
|
||||
}
|
||||
SetIsSubresourceContentInitialized(baseMipLevel, levelCount, baseArrayLayer, layerCount);
|
||||
}
|
||||
|
||||
void Texture::EnsureSubresourceContentInitialized(uint32_t baseMipLevel,
|
||||
uint32_t levelCount,
|
||||
uint32_t baseArrayLayer,
|
||||
uint32_t layerCount) {
|
||||
if (!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
|
||||
return;
|
||||
}
|
||||
if (!IsSubresourceContentInitialized(baseMipLevel, levelCount, baseArrayLayer,
|
||||
layerCount)) {
|
||||
ClearTexture(baseMipLevel, levelCount, baseArrayLayer, layerCount);
|
||||
}
|
||||
}
|
||||
|
||||
// TextureView
|
||||
|
||||
TextureView::TextureView(TextureBase* texture, const TextureViewDescriptor* descriptor)
|
||||
|
|
|
@ -42,8 +42,17 @@ namespace dawn_native { namespace opengl {
|
|||
GLenum GetGLTarget() const;
|
||||
TextureFormatInfo GetGLFormat() const;
|
||||
|
||||
void EnsureSubresourceContentInitialized(uint32_t baseMipLevel,
|
||||
uint32_t levelCount,
|
||||
uint32_t baseArrayLayer,
|
||||
uint32_t layerCount);
|
||||
|
||||
private:
|
||||
void DestroyImpl() override;
|
||||
void ClearTexture(GLint baseMipLevel,
|
||||
GLint levelCount,
|
||||
GLint baseArrayLayer,
|
||||
uint32_t layerCount);
|
||||
|
||||
GLuint mHandle;
|
||||
GLenum mTarget;
|
||||
|
|
|
@ -41,4 +41,12 @@ namespace dawn_native { namespace opengl {
|
|||
}
|
||||
}
|
||||
|
||||
GLint GetStencilMaskFromStencilFormat(dawn::TextureFormat depthStencilFormat) {
|
||||
switch (depthStencilFormat) {
|
||||
case dawn::TextureFormat::Depth24PlusStencil8:
|
||||
return 0xFF;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
}} // namespace dawn_native::opengl
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
namespace dawn_native { namespace opengl {
|
||||
|
||||
GLuint ToOpenGLCompareFunction(dawn::CompareFunction compareFunction);
|
||||
|
||||
GLint GetStencilMaskFromStencilFormat(dawn::TextureFormat depthStencilFormat);
|
||||
}} // namespace dawn_native::opengl
|
||||
|
||||
#endif // DAWNNATIVE_OPENGL_UTILSGL_H_
|
||||
|
|
|
@ -403,8 +403,29 @@ TEST_P(TextureZeroInitTest, RenderingLoadingDepthStencil) {
|
|||
EXPECT_TEXTURE_RGBA8_EQ(expected.data(), srcTexture, 0, 0, kSize, kSize, 0, 0);
|
||||
}
|
||||
|
||||
DAWN_INSTANTIATE_TEST(TextureZeroInitTest,
|
||||
ForceWorkarounds(D3D12Backend,
|
||||
{"nonzero_clear_resources_on_creation_for_testing"}),
|
||||
ForceWorkarounds(VulkanBackend,
|
||||
{"nonzero_clear_resources_on_creation_for_testing"}));
|
||||
// This tests the color attachments clear to 0s
|
||||
TEST_P(TextureZeroInitTest, ColorAttachmentsClear) {
|
||||
dawn::TextureDescriptor descriptor = CreateTextureDescriptor(
|
||||
1, 1, dawn::TextureUsageBit::OutputAttachment | dawn::TextureUsageBit::TransferSrc,
|
||||
kColorFormat);
|
||||
dawn::Texture texture = device.CreateTexture(&descriptor);
|
||||
utils::BasicRenderPass renderPass = utils::BasicRenderPass(kSize, kSize, texture, kColorFormat);
|
||||
renderPass.renderPassInfo.cColorAttachmentsInfoPtr[0]->loadOp = dawn::LoadOp::Load;
|
||||
|
||||
dawn::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||
{
|
||||
dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
|
||||
pass.EndPass();
|
||||
}
|
||||
dawn::CommandBuffer commands = encoder.Finish();
|
||||
queue.Submit(1, &commands);
|
||||
|
||||
std::vector<RGBA8> expected(kSize * kSize, {0, 0, 0, 0});
|
||||
EXPECT_TEXTURE_RGBA8_EQ(expected.data(), renderPass.color, 0, 0, kSize, kSize, 0, 0);
|
||||
}
|
||||
|
||||
DAWN_INSTANTIATE_TEST(
|
||||
TextureZeroInitTest,
|
||||
ForceWorkarounds(D3D12Backend, {"nonzero_clear_resources_on_creation_for_testing"}),
|
||||
ForceWorkarounds(OpenGLBackend, {"nonzero_clear_resources_on_creation_for_testing"}),
|
||||
ForceWorkarounds(VulkanBackend, {"nonzero_clear_resources_on_creation_for_testing"}));
|
||||
|
|
Loading…
Reference in New Issue