From d0dd661f180503e1da910a6256b42ff8d72ab936 Mon Sep 17 00:00:00 2001 From: Jiawei Shao Date: Mon, 20 Jul 2020 02:08:59 +0000 Subject: [PATCH] Implement buffer lazy initialization before CopyBufferToTexture() This patch adds the check and implementations of buffer lazy initialization before CopyBufferToTexture(). The support of buffer lazy initialization before CopyTextureToBuffer() is much more complicated than what we do for CopyBufferToTexture(), so we decide to put it in another CL instead of writing them together with CopyBufferToTexture(). BUG=dawn:414 TEST=dawn_end2end_tests Change-Id: I45fdcdde2c9a0dafff23623815fc35c877990ef1 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/25140 Commit-Queue: Jiawei Shao Reviewed-by: Austin Eng --- src/dawn_native/d3d12/CommandBufferD3D12.cpp | 2 + src/dawn_native/metal/CommandBufferMTL.mm | 1 + src/dawn_native/opengl/CommandBufferGL.cpp | 2 + src/dawn_native/vulkan/CommandBufferVk.cpp | 2 + src/tests/end2end/BufferZeroInitTests.cpp | 82 ++++++++++++++++++++ 5 files changed, 89 insertions(+) diff --git a/src/dawn_native/d3d12/CommandBufferD3D12.cpp b/src/dawn_native/d3d12/CommandBufferD3D12.cpp index 1c301d21ed..5fbe2c62c2 100644 --- a/src/dawn_native/d3d12/CommandBufferD3D12.cpp +++ b/src/dawn_native/d3d12/CommandBufferD3D12.cpp @@ -663,6 +663,8 @@ namespace dawn_native { namespace d3d12 { Buffer* buffer = ToBackend(copy->source.buffer.Get()); Texture* texture = ToBackend(copy->destination.texture.Get()); + DAWN_TRY(buffer->EnsureDataInitialized(commandContext)); + ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D); SubresourceRange subresources = {copy->destination.mipLevel, 1, copy->destination.origin.z, diff --git a/src/dawn_native/metal/CommandBufferMTL.mm b/src/dawn_native/metal/CommandBufferMTL.mm index 0e4d439b31..517caf9688 100644 --- a/src/dawn_native/metal/CommandBufferMTL.mm +++ b/src/dawn_native/metal/CommandBufferMTL.mm @@ -601,6 +601,7 @@ namespace dawn_native { namespace metal { Buffer* buffer = ToBackend(src.buffer.Get()); Texture* texture = ToBackend(dst.texture.Get()); + buffer->EnsureDataInitialized(commandContext); EnsureDestinationTextureInitialized(texture, copy->destination, copy->copySize); TextureBufferCopySplit splitCopies = ComputeTextureBufferCopySplit( diff --git a/src/dawn_native/opengl/CommandBufferGL.cpp b/src/dawn_native/opengl/CommandBufferGL.cpp index b06dac47a7..252fbe621d 100644 --- a/src/dawn_native/opengl/CommandBufferGL.cpp +++ b/src/dawn_native/opengl/CommandBufferGL.cpp @@ -518,6 +518,8 @@ namespace dawn_native { namespace opengl { GLenum target = texture->GetGLTarget(); const GLFormat& format = texture->GetGLFormat(); + buffer->EnsureDataInitialized(); + ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D); SubresourceRange subresources = {dst.mipLevel, 1, dst.origin.z, copy->copySize.depth}; diff --git a/src/dawn_native/vulkan/CommandBufferVk.cpp b/src/dawn_native/vulkan/CommandBufferVk.cpp index f7fb0a3cd8..1379221b50 100644 --- a/src/dawn_native/vulkan/CommandBufferVk.cpp +++ b/src/dawn_native/vulkan/CommandBufferVk.cpp @@ -447,6 +447,8 @@ namespace dawn_native { namespace vulkan { auto& src = copy->source; auto& dst = copy->destination; + ToBackend(src.buffer)->EnsureDataInitialized(recordingContext); + VkBufferImageCopy region = ComputeBufferImageCopyRegion(src, dst, copy->copySize); VkImageSubresourceLayers subresource = region.imageSubresource; diff --git a/src/tests/end2end/BufferZeroInitTests.cpp b/src/tests/end2end/BufferZeroInitTests.cpp index fa09f2102c..c4a3809ac1 100644 --- a/src/tests/end2end/BufferZeroInitTests.cpp +++ b/src/tests/end2end/BufferZeroInitTests.cpp @@ -59,6 +59,25 @@ class BufferZeroInitTest : public DawnTest { WaitABit(); } } + + wgpu::Texture CreateAndInitialize2DTexture(const wgpu::Extent3D& size, + wgpu::TextureFormat format) { + wgpu::TextureDescriptor descriptor; + descriptor.size = size; + descriptor.format = format; + descriptor.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc | + wgpu::TextureUsage::OutputAttachment; + wgpu::Texture texture = device.CreateTexture(&descriptor); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + utils::ComboRenderPassDescriptor renderPassDescriptor({texture.CreateView()}); + wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDescriptor); + renderPass.EndPass(); + wgpu::CommandBuffer commandBuffer = encoder.Finish(); + queue.Submit(1, &commandBuffer); + + return texture; + } }; // Test that calling writeBuffer to overwrite the entire buffer doesn't need to lazily initialize @@ -379,6 +398,69 @@ TEST_P(BufferZeroInitTest, MapAtCreation) { kExpectedData.size()); } +// Test that the code path of CopyBufferToTexture clears the source buffer correctly when it is the +// first use of the buffer. +TEST_P(BufferZeroInitTest, CopyBufferToTexture) { + constexpr wgpu::Extent3D kTextureSize = {16u, 16u, 1u}; + + constexpr wgpu::TextureFormat kTextureFormat = wgpu::TextureFormat::R32Uint; + + wgpu::Texture texture = CreateAndInitialize2DTexture(kTextureSize, kTextureFormat); + const wgpu::TextureCopyView textureCopyView = + utils::CreateTextureCopyView(texture, 0, {0, 0, 0}); + + const uint32_t requiredBufferSizeForCopy = utils::GetBytesInBufferTextureCopy( + kTextureFormat, kTextureSize.width, kTextureBytesPerRowAlignment, kTextureSize.width, + kTextureSize.depth); + + constexpr wgpu::BufferUsage kBufferUsage = + wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; + + // bufferOffset == 0 + { + constexpr uint64_t kOffset = 0; + const uint32_t totalBufferSize = requiredBufferSizeForCopy + kOffset; + wgpu::BufferDescriptor bufferDescriptor; + bufferDescriptor.size = totalBufferSize; + bufferDescriptor.usage = kBufferUsage; + + wgpu::Buffer buffer = device.CreateBuffer(&bufferDescriptor); + const wgpu::BufferCopyView bufferCopyView = utils::CreateBufferCopyView( + buffer, kOffset, kTextureBytesPerRowAlignment, kTextureSize.height); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, &kTextureSize); + wgpu::CommandBuffer commandBuffer = encoder.Finish(); + EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commandBuffer)); + + std::vector expectedValues(totalBufferSize / sizeof(uint32_t), 0); + EXPECT_BUFFER_U32_RANGE_EQ(expectedValues.data(), buffer, 0, + totalBufferSize / sizeof(uint32_t)); + } + + // bufferOffset > 0 + { + constexpr uint64_t kOffset = 8u; + const uint32_t totalBufferSize = requiredBufferSizeForCopy + kOffset; + wgpu::BufferDescriptor bufferDescriptor; + bufferDescriptor.size = totalBufferSize; + bufferDescriptor.usage = kBufferUsage; + + wgpu::Buffer buffer = device.CreateBuffer(&bufferDescriptor); + const wgpu::BufferCopyView bufferCopyView = utils::CreateBufferCopyView( + buffer, kOffset, kTextureBytesPerRowAlignment, kTextureSize.height); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, &kTextureSize); + wgpu::CommandBuffer commandBuffer = encoder.Finish(); + EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commandBuffer)); + + std::vector expectedValues(totalBufferSize / sizeof(uint32_t), 0); + EXPECT_BUFFER_U32_RANGE_EQ(expectedValues.data(), buffer, 0, + totalBufferSize / sizeof(uint32_t)); + } +} + DAWN_INSTANTIATE_TEST(BufferZeroInitTest, D3D12Backend({"nonzero_clear_resources_on_creation_for_testing", "lazy_clear_buffer_on_first_use"}),