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"}),