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/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;

View File

@ -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)

View File

@ -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;

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

View File

@ -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_

View File

@ -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"}));