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:
Natasha Lee 2019-07-02 17:38:09 +00:00 committed by Commit Bot service account
parent ce32a94b37
commit 4886403b61
6 changed files with 131 additions and 19 deletions

View File

@ -26,6 +26,7 @@
#include "dawn_native/opengl/RenderPipelineGL.h" #include "dawn_native/opengl/RenderPipelineGL.h"
#include "dawn_native/opengl/SamplerGL.h" #include "dawn_native/opengl/SamplerGL.h"
#include "dawn_native/opengl/TextureGL.h" #include "dawn_native/opengl/TextureGL.h"
#include "dawn_native/opengl/UtilsGL.h"
#include <cstring> #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 // 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. // 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. // 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() { void CommandBuffer::Execute() {
const OpenGLFunctions& gl = ToBackend(GetDevice())->gl; 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; Command type;
while (mCommands.NextCommandId(&type)) { while (mCommands.NextCommandId(&type)) {
switch (type) { switch (type) {
case Command::BeginComputePass: { case Command::BeginComputePass: {
mCommands.NextCommand<BeginComputePassCmd>(); mCommands.NextCommand<BeginComputePassCmd>();
TransitionForPass(passResourceUsages[nextPassNumber]);
ExecuteComputePass(); ExecuteComputePass();
nextPassNumber++;
} break; } break;
case Command::BeginRenderPass: { case Command::BeginRenderPass: {
auto* cmd = mCommands.NextCommand<BeginRenderPassCmd>(); auto* cmd = mCommands.NextCommand<BeginRenderPassCmd>();
TransitionForPass(passResourceUsages[nextPassNumber]);
ExecuteRenderPass(cmd); ExecuteRenderPass(cmd);
nextPassNumber++;
} break; } break;
case Command::CopyBufferToBuffer: { case Command::CopyBufferToBuffer: {
@ -384,6 +393,11 @@ namespace dawn_native { namespace opengl {
Texture* texture = ToBackend(dst.texture.Get()); Texture* texture = ToBackend(dst.texture.Get());
GLenum target = texture->GetGLTarget(); GLenum target = texture->GetGLTarget();
auto format = texture->GetGLFormat(); 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.BindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer->GetHandle());
gl.ActiveTexture(GL_TEXTURE0); gl.ActiveTexture(GL_TEXTURE0);
@ -426,6 +440,7 @@ namespace dawn_native { namespace opengl {
auto format = texture->GetGLFormat(); auto format = texture->GetGLFormat();
GLenum target = texture->GetGLTarget(); 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 // 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. // glReadPixels with a pack buffer. Create a temporary FBO for the copy.
gl.BindTexture(target, texture->GetHandle()); gl.BindTexture(target, texture->GetHandle());
@ -473,7 +488,12 @@ namespace dawn_native { namespace opengl {
auto& copySize = copy->copySize; auto& copySize = copy->copySize;
Texture* srcTexture = ToBackend(src.texture.Get()); Texture* srcTexture = ToBackend(src.texture.Get());
Texture* dstTexture = ToBackend(dst.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(), gl.CopyImageSubData(srcTexture->GetHandle(), srcTexture->GetGLTarget(),
src.level, src.origin.x, src.origin.y, src.slice, src.level, src.origin.x, src.origin.y, src.slice,
dstTexture->GetHandle(), dstTexture->GetGLTarget(), dstTexture->GetHandle(), dstTexture->GetGLTarget(),
@ -683,7 +703,6 @@ namespace dawn_native { namespace opengl {
if (renderPass->sampleCount > 1) { if (renderPass->sampleCount > 1) {
ResolveMultisampledRenderTargets(gl, renderPass); ResolveMultisampledRenderTargets(gl, renderPass);
} }
gl.DeleteFramebuffers(1, &fbo); gl.DeleteFramebuffers(1, &fbo);
return; return;
} break; } break;

View File

@ -14,8 +14,10 @@
#include "dawn_native/opengl/TextureGL.h" #include "dawn_native/opengl/TextureGL.h"
#include "dawn_native/opengl/DeviceGL.h" #include "dawn_native/opengl/DeviceGL.h"
#include "dawn_native/opengl/UtilsGL.h"
#include "common/Assert.h" #include "common/Assert.h"
#include "dawn_native/Commands.h"
#include <algorithm> #include <algorithm>
#include <vector> #include <vector>
@ -174,9 +176,8 @@ namespace dawn_native { namespace opengl {
ASSERT(GetFormat().blockByteSize <= MAX_TEXEL_SIZE); ASSERT(GetFormat().blockByteSize <= MAX_TEXEL_SIZE);
GLubyte clearColor[MAX_TEXEL_SIZE]; GLubyte clearColor[MAX_TEXEL_SIZE];
std::fill(clearColor, clearColor + MAX_TEXEL_SIZE, 255); std::fill(clearColor, clearColor + MAX_TEXEL_SIZE, 255);
// TODO(natlee@microsoft.com): clear all subresources // 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); gl.ClearTexImage(mHandle, i, formatInfo.format, formatInfo.type, clearColor);
} }
} }
@ -211,6 +212,60 @@ namespace dawn_native { namespace opengl {
return GetGLFormatInfo(GetFormat().format); 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::TextureView(TextureBase* texture, const TextureViewDescriptor* descriptor) TextureView::TextureView(TextureBase* texture, const TextureViewDescriptor* descriptor)

View File

@ -42,8 +42,17 @@ namespace dawn_native { namespace opengl {
GLenum GetGLTarget() const; GLenum GetGLTarget() const;
TextureFormatInfo GetGLFormat() const; TextureFormatInfo GetGLFormat() const;
void EnsureSubresourceContentInitialized(uint32_t baseMipLevel,
uint32_t levelCount,
uint32_t baseArrayLayer,
uint32_t layerCount);
private: private:
void DestroyImpl() override; void DestroyImpl() override;
void ClearTexture(GLint baseMipLevel,
GLint levelCount,
GLint baseArrayLayer,
uint32_t layerCount);
GLuint mHandle; GLuint mHandle;
GLenum mTarget; GLenum mTarget;

View File

@ -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 }} // namespace dawn_native::opengl

View File

@ -21,7 +21,7 @@
namespace dawn_native { namespace opengl { namespace dawn_native { namespace opengl {
GLuint ToOpenGLCompareFunction(dawn::CompareFunction compareFunction); GLuint ToOpenGLCompareFunction(dawn::CompareFunction compareFunction);
GLint GetStencilMaskFromStencilFormat(dawn::TextureFormat depthStencilFormat);
}} // namespace dawn_native::opengl }} // namespace dawn_native::opengl
#endif // DAWNNATIVE_OPENGL_UTILSGL_H_ #endif // DAWNNATIVE_OPENGL_UTILSGL_H_

View File

@ -403,8 +403,29 @@ TEST_P(TextureZeroInitTest, RenderingLoadingDepthStencil) {
EXPECT_TEXTURE_RGBA8_EQ(expected.data(), srcTexture, 0, 0, kSize, kSize, 0, 0); EXPECT_TEXTURE_RGBA8_EQ(expected.data(), srcTexture, 0, 0, kSize, kSize, 0, 0);
} }
DAWN_INSTANTIATE_TEST(TextureZeroInitTest, // This tests the color attachments clear to 0s
ForceWorkarounds(D3D12Backend, TEST_P(TextureZeroInitTest, ColorAttachmentsClear) {
{"nonzero_clear_resources_on_creation_for_testing"}), dawn::TextureDescriptor descriptor = CreateTextureDescriptor(
ForceWorkarounds(VulkanBackend, 1, 1, dawn::TextureUsageBit::OutputAttachment | dawn::TextureUsageBit::TransferSrc,
{"nonzero_clear_resources_on_creation_for_testing"})); 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"}));