diff --git a/src/dawn_native/CommandEncoder.cpp b/src/dawn_native/CommandEncoder.cpp index bc5b5333ac..09eb91f12a 100644 --- a/src/dawn_native/CommandEncoder.cpp +++ b/src/dawn_native/CommandEncoder.cpp @@ -654,19 +654,22 @@ namespace dawn_native { Align(copySize->width * blockInfo.blockByteSize, kTextureBytesPerRowAlignment); } - // Record the copy command. - CopyBufferToTextureCmd* copy = - allocator->Allocate(Command::CopyBufferToTexture); - copy->source.buffer = source->buffer; - copy->source.offset = source->layout.offset; - copy->source.bytesPerRow = bytesPerRow; - copy->source.rowsPerImage = defaultedRowsPerImage; - copy->destination.texture = destination->texture; - copy->destination.origin = destination->origin; - copy->destination.mipLevel = destination->mipLevel; - copy->destination.aspect = - ConvertAspect(destination->texture->GetFormat(), destination->aspect); - copy->copySize = *copySize; + // Skip noop copies. + if (copySize->width != 0 && copySize->height != 0 && copySize->depth != 0) { + // Record the copy command. + CopyBufferToTextureCmd* copy = + allocator->Allocate(Command::CopyBufferToTexture); + copy->source.buffer = source->buffer; + copy->source.offset = source->layout.offset; + copy->source.bytesPerRow = bytesPerRow; + copy->source.rowsPerImage = defaultedRowsPerImage; + copy->destination.texture = destination->texture; + copy->destination.origin = destination->origin; + copy->destination.mipLevel = destination->mipLevel; + copy->destination.aspect = + ConvertAspect(destination->texture->GetFormat(), destination->aspect); + copy->copySize = *copySize; + } return {}; }); @@ -713,18 +716,21 @@ namespace dawn_native { Align(copySize->width * blockInfo.blockByteSize, kTextureBytesPerRowAlignment); } - // Record the copy command. - CopyTextureToBufferCmd* copy = - allocator->Allocate(Command::CopyTextureToBuffer); - copy->source.texture = source->texture; - copy->source.origin = source->origin; - copy->source.mipLevel = source->mipLevel; - copy->source.aspect = ConvertAspect(source->texture->GetFormat(), source->aspect); - copy->destination.buffer = destination->buffer; - copy->destination.offset = destination->layout.offset; - copy->destination.bytesPerRow = bytesPerRow; - copy->destination.rowsPerImage = defaultedRowsPerImage; - copy->copySize = *copySize; + // Skip noop copies. + if (copySize->width != 0 && copySize->height != 0 && copySize->depth != 0) { + // Record the copy command. + CopyTextureToBufferCmd* copy = + allocator->Allocate(Command::CopyTextureToBuffer); + copy->source.texture = source->texture; + copy->source.origin = source->origin; + copy->source.mipLevel = source->mipLevel; + copy->source.aspect = ConvertAspect(source->texture->GetFormat(), source->aspect); + copy->destination.buffer = destination->buffer; + copy->destination.offset = destination->layout.offset; + copy->destination.bytesPerRow = bytesPerRow; + copy->destination.rowsPerImage = defaultedRowsPerImage; + copy->copySize = *copySize; + } return {}; }); @@ -754,18 +760,21 @@ namespace dawn_native { mTopLevelTextures.insert(destination->texture); } - CopyTextureToTextureCmd* copy = - allocator->Allocate(Command::CopyTextureToTexture); - copy->source.texture = source->texture; - copy->source.origin = source->origin; - copy->source.mipLevel = source->mipLevel; - copy->source.aspect = ConvertAspect(source->texture->GetFormat(), source->aspect); - copy->destination.texture = destination->texture; - copy->destination.origin = destination->origin; - copy->destination.mipLevel = destination->mipLevel; - copy->destination.aspect = - ConvertAspect(destination->texture->GetFormat(), destination->aspect); - copy->copySize = *copySize; + // Skip noop copies. + if (copySize->width != 0 && copySize->height != 0 && copySize->depth != 0) { + CopyTextureToTextureCmd* copy = + allocator->Allocate(Command::CopyTextureToTexture); + copy->source.texture = source->texture; + copy->source.origin = source->origin; + copy->source.mipLevel = source->mipLevel; + copy->source.aspect = ConvertAspect(source->texture->GetFormat(), source->aspect); + copy->destination.texture = destination->texture; + copy->destination.origin = destination->origin; + copy->destination.mipLevel = destination->mipLevel; + copy->destination.aspect = + ConvertAspect(destination->texture->GetFormat(), destination->aspect); + copy->copySize = *copySize; + } return {}; }); diff --git a/src/tests/end2end/CopyTests.cpp b/src/tests/end2end/CopyTests.cpp index 311d98a37b..9dbbbbd3e0 100644 --- a/src/tests/end2end/CopyTests.cpp +++ b/src/tests/end2end/CopyTests.cpp @@ -88,6 +88,7 @@ class CopyTests_T2B : public CopyTests { void DoTest(const TextureSpec& textureSpec, const BufferSpec& bufferSpec, const wgpu::Extent3D& copySize) { + const uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(kTextureFormat); // Create a texture that is `width` x `height` with (`level` + 1) mip levels. wgpu::TextureDescriptor descriptor; descriptor.dimension = wgpu::TextureDimension::e2D; @@ -121,13 +122,10 @@ class CopyTests_T2B : public CopyTests { // Prepopulating the buffer with empty data ensures that there is not random data in the // expectation and helps ensure that the padding due to the bytes per row is not modified // by the copy. - // TODO(jiawei.shao@intel.com): remove the initialization of the buffer after we support - // buffer lazy-initialization. - const uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(kTextureFormat); - const std::vector emptyData(bufferSpec.size / bytesPerTexel, RGBA8()); - wgpu::Buffer buffer = - utils::CreateBufferFromData(device, emptyData.data(), bufferSpec.size, - wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst); + wgpu::BufferDescriptor bufferDesc; + bufferDesc.size = bufferSpec.size; + bufferDesc.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; + wgpu::Buffer buffer = device.CreateBuffer(&bufferDesc); { wgpu::TextureCopyView textureCopyView = @@ -141,8 +139,11 @@ class CopyTests_T2B : public CopyTests { queue.Submit(1, &commands); uint64_t bufferOffset = bufferSpec.offset; - const uint32_t texelCountInCopyRegion = - bufferSpec.bytesPerRow / bytesPerTexel * (copySize.height - 1) + copySize.width; + + const wgpu::Extent3D copySizePerSlice = {copySize.width, copySize.height, 1}; + // Texels in single slice. + const uint32_t texelCountInCopyRegion = utils::GetTexelCountInCopyRegion( + bufferSpec.bytesPerRow, bufferSpec.rowsPerImage, copySizePerSlice, kTextureFormat); const uint32_t maxArrayLayer = textureSpec.copyOrigin.z + copySize.depth; std::vector expected(texelCountInCopyRegion); for (uint32_t slice = textureSpec.copyOrigin.z; slice < maxArrayLayer; ++slice) { @@ -298,6 +299,7 @@ class CopyTests_T2T : public CopyTests { utils::CreateTextureCopyView(srcTexture, srcSpec.level, {0, 0, srcSpec.copyOrigin.z}); encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©Layout.mipSize); + const wgpu::Extent3D copySizePerSlice = {copySize.width, copySize.height, 1}; // Perform the texture to texture copy wgpu::TextureCopyView srcTextureCopyView = utils::CreateTextureCopyView(srcTexture, srcSpec.level, srcSpec.copyOrigin); @@ -308,8 +310,10 @@ class CopyTests_T2T : public CopyTests { wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); - const uint32_t texelCountInCopyRegion = - copyLayout.texelBlocksPerRow * (copySize.height - 1) + copySize.width; + // Texels in single slice. + const uint32_t texelCountInCopyRegion = utils::GetTexelCountInCopyRegion( + copyLayout.bytesPerRow, copyLayout.bytesPerImage / copyLayout.bytesPerRow, + copySizePerSlice, kTextureFormat); std::vector expected(texelCountInCopyRegion); for (uint32_t slice = 0; slice < copySize.depth; ++slice) { std::fill(expected.begin(), expected.end(), RGBA8()); @@ -395,6 +399,21 @@ TEST_P(CopyTests_T2B, FullTextureAligned) { DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight), {kWidth, kHeight, 1}); } +// Test noop copies +TEST_P(CopyTests_T2B, ZeroSizedCopy) { + constexpr uint32_t kWidth = 256; + constexpr uint32_t kHeight = 128; + + TextureSpec textureSpec; + textureSpec.textureSize = {kWidth, kHeight, 1}; + textureSpec.copyOrigin = {0, 0, 0}; + textureSpec.level = 0; + + DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight), {0, kHeight, 1}); + DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight), {kWidth, 0, 1}); + DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight), {kWidth, kHeight, 0}); +} + // Test that copying an entire texture without 256-byte aligned dimensions works TEST_P(CopyTests_T2B, FullTextureUnaligned) { constexpr uint32_t kWidth = 259; @@ -830,6 +849,21 @@ TEST_P(CopyTests_B2T, FullTextureAligned) { DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight), {kWidth, kHeight, 1}); } +// Test noop copies. +TEST_P(CopyTests_B2T, ZeroSizedCopy) { + constexpr uint32_t kWidth = 256; + constexpr uint32_t kHeight = 128; + + TextureSpec textureSpec; + textureSpec.textureSize = {kWidth, kHeight, 1}; + textureSpec.copyOrigin = {0, 0, 0}; + textureSpec.level = 0; + + DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight), {0, kHeight, 1}); + DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight), {kWidth, 0, 1}); + DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight), {kWidth, kHeight, 0}); +} + // Test that copying an entire texture without 256-byte aligned dimensions works TEST_P(CopyTests_B2T, FullTextureUnaligned) { constexpr uint32_t kWidth = 259; @@ -1244,6 +1278,20 @@ TEST_P(CopyTests_T2T, Texture) { DoTest(textureSpec, textureSpec, {kWidth, kHeight, 1}); } +// Test noop copies. +TEST_P(CopyTests_T2T, ZeroSizedCopy) { + constexpr uint32_t kWidth = 256; + constexpr uint32_t kHeight = 128; + + TextureSpec textureSpec; + textureSpec.copyOrigin = {0, 0, 0}; + textureSpec.level = 0; + textureSpec.textureSize = {kWidth, kHeight, 1}; + DoTest(textureSpec, textureSpec, {0, kHeight, 1}); + DoTest(textureSpec, textureSpec, {kWidth, 0, 1}); + DoTest(textureSpec, textureSpec, {kWidth, kHeight, 0}); +} + TEST_P(CopyTests_T2T, TextureRegion) { constexpr uint32_t kWidth = 256; constexpr uint32_t kHeight = 128; diff --git a/src/utils/TestUtils.cpp b/src/utils/TestUtils.cpp index cdbe3b3fc8..c56cb8df92 100644 --- a/src/utils/TestUtils.cpp +++ b/src/utils/TestUtils.cpp @@ -87,6 +87,14 @@ namespace utils { } } + uint64_t GetTexelCountInCopyRegion(uint64_t bytesPerRow, + uint64_t rowsPerImage, + wgpu::Extent3D copyExtent, + wgpu::TextureFormat textureFormat) { + return RequiredBytesInCopy(bytesPerRow, rowsPerImage, copyExtent, textureFormat) / + utils::GetTexelBlockSizeInBytes(textureFormat); + } + void UnalignDynamicUploader(wgpu::Device device) { std::vector data = {1}; diff --git a/src/utils/TestUtils.h b/src/utils/TestUtils.h index d1ba25fa41..c75ca643b5 100644 --- a/src/utils/TestUtils.h +++ b/src/utils/TestUtils.h @@ -46,6 +46,11 @@ namespace utils { wgpu::Extent3D copyExtent, wgpu::TextureFormat textureFormat); + uint64_t GetTexelCountInCopyRegion(uint64_t bytesPerRow, + uint64_t rowsPerImage, + wgpu::Extent3D copyExtent, + wgpu::TextureFormat textureFormat); + // A helper function used for testing DynamicUploader offset alignment. // A call of this function will do a Queue::WriteTexture with 1 byte of data, // so that assuming that WriteTexture uses DynamicUploader, the first RingBuffer