Support copying multiple array layers in one B2T and T2B copy command
This patch adds the support of copying with multiple texture array
layers in one buffer-to-texture and texture-to-buffer copy command.
BUG=dawn:453
TEST=dawn_end2end_tests
Change-Id: If009dbb29f2b0ef0667715eed0d66053b1491fd4
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/23248
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
diff --git a/src/tests/end2end/CopyTests.cpp b/src/tests/end2end/CopyTests.cpp
index 4b98414..d447f3e 100644
--- a/src/tests/end2end/CopyTests.cpp
+++ b/src/tests/end2end/CopyTests.cpp
@@ -34,6 +34,7 @@
uint64_t size;
uint64_t offset;
uint32_t bytesPerRow;
+ uint32_t rowsPerImage;
};
static std::vector<RGBA8> GetExpectedTextureData(
@@ -56,12 +57,14 @@
}
static BufferSpec MinimumBufferSpec(uint32_t width,
- uint32_t height,
- uint32_t arrayLayer = 1) {
+ uint32_t rowsPerImage,
+ uint32_t arrayLayer = 1,
+ bool testZeroRowsPerImage = true) {
const uint32_t bytesPerRow = utils::GetMinimumBytesPerRow(kTextureFormat, width);
const uint32_t totalBufferSize = utils::GetBytesInBufferTextureCopy(
- kTextureFormat, width, bytesPerRow, height, arrayLayer);
- return {totalBufferSize, 0, bytesPerRow};
+ kTextureFormat, width, bytesPerRow, rowsPerImage, arrayLayer);
+ uint32_t appliedRowsPerImage = testZeroRowsPerImage ? 0 : rowsPerImage;
+ return {totalBufferSize, 0, bytesPerRow, appliedRowsPerImage};
}
static void PackTextureData(const RGBA8* srcData, uint32_t width, uint32_t height, uint32_t srcTexelsPerRow, RGBA8* dstData, uint32_t dstTexelsPerRow) {
@@ -90,30 +93,24 @@
descriptor.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc;
wgpu::Texture texture = device.CreateTexture(&descriptor);
- const uint32_t rowsPerImage = textureSpec.textureSize.height >> textureSpec.level;
const utils::BufferTextureCopyLayout copyLayout =
utils::GetBufferTextureCopyLayoutForTexture2DAtLevel(
- kTextureFormat, textureSpec.textureSize, textureSpec.level, rowsPerImage);
+ kTextureFormat, textureSpec.textureSize, textureSpec.level,
+ bufferSpec.rowsPerImage);
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
// Initialize the source texture
std::vector<RGBA8> textureArrayData = GetExpectedTextureData(copyLayout);
-
- // TODO(jiawei.shao@intel.com): copy into multiple texture array layers in one
- // buffer-to-texture copy command.
- wgpu::Buffer uploadBuffer = utils::CreateBufferFromData(
- device, textureArrayData.data(), copyLayout.byteLength, wgpu::BufferUsage::CopySrc);
- uint64_t uploadBufferOffset = 0;
- for (uint32_t slice = 0; slice < textureSpec.textureSize.depth; ++slice) {
+ {
+ wgpu::Buffer uploadBuffer =
+ utils::CreateBufferFromData(device, textureArrayData.data(),
+ copyLayout.byteLength, wgpu::BufferUsage::CopySrc);
wgpu::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(
- uploadBuffer, uploadBufferOffset, copyLayout.bytesPerRow, 0);
+ uploadBuffer, 0, copyLayout.bytesPerRow, bufferSpec.rowsPerImage);
wgpu::TextureCopyView textureCopyView =
- utils::CreateTextureCopyView(texture, textureSpec.level, {0, 0, slice});
- wgpu::Extent3D copyOneLayerSize = {copyLayout.mipSize.width,
- copyLayout.mipSize.height, 1};
- encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©OneLayerSize);
- uploadBufferOffset += copyLayout.bytesPerImage;
+ utils::CreateTextureCopyView(texture, textureSpec.level, {0, 0, 0});
+ encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©Layout.mipSize);
}
// Create a buffer of `size` and populate it with empty data (0,0,0,0) Note:
@@ -128,31 +125,21 @@
utils::CreateBufferFromData(device, emptyData.data(), bufferSpec.size,
wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst);
- const uint32_t maxArrayLayer = textureSpec.copyOrigin.z + copySize.depth;
-
- // TODO(jiawei.shao@intel.com): copy from multiple texture array layers in one
- // texture-to-buffer copy command.
- uint64_t bufferOffset = bufferSpec.offset;
- for (uint32_t slice = textureSpec.copyOrigin.z; slice < maxArrayLayer; ++slice) {
- // Copy the region [(`x`, `y`, slice), (`x + testCopySize.width, `y +
- // testCopySize.height`, 1)] from the `level` mip into the buffer at `offset +
- // bufferSpec.size * slice` and `bytesPerRow`
- wgpu::TextureCopyView textureCopyView = utils::CreateTextureCopyView(
- texture, textureSpec.level,
- {textureSpec.copyOrigin.x, textureSpec.copyOrigin.y, slice});
- wgpu::BufferCopyView bufferCopyView =
- utils::CreateBufferCopyView(buffer, bufferOffset, bufferSpec.bytesPerRow, 0);
- wgpu::Extent3D copyOneLayerSize = {copySize.width, copySize.height, 1};
- encoder.CopyTextureToBuffer(&textureCopyView, &bufferCopyView, ©OneLayerSize);
- bufferOffset += copyLayout.bytesPerImage;
+ {
+ wgpu::TextureCopyView textureCopyView =
+ utils::CreateTextureCopyView(texture, textureSpec.level, textureSpec.copyOrigin);
+ wgpu::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(
+ buffer, bufferSpec.offset, bufferSpec.bytesPerRow, bufferSpec.rowsPerImage);
+ encoder.CopyTextureToBuffer(&textureCopyView, &bufferCopyView, ©Size);
}
wgpu::CommandBuffer commands = encoder.Finish();
queue.Submit(1, &commands);
- bufferOffset = bufferSpec.offset;
+ uint64_t bufferOffset = bufferSpec.offset;
const uint32_t texelCountInCopyRegion =
bufferSpec.bytesPerRow / bytesPerTexel * (copySize.height - 1) + copySize.width;
+ const uint32_t maxArrayLayer = textureSpec.copyOrigin.z + copySize.depth;
std::vector<RGBA8> expected(texelCountInCopyRegion);
for (uint32_t slice = textureSpec.copyOrigin.z; slice < maxArrayLayer; ++slice) {
// Pack the data used to create the upload buffer in the specified copy region to have
@@ -218,34 +205,23 @@
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
- const uint32_t rowsPerImage = textureSpec.textureSize.height >> textureSpec.level;
const utils::BufferTextureCopyLayout copyLayout =
utils::GetBufferTextureCopyLayoutForTexture2DAtLevel(
- kTextureFormat, textureSpec.textureSize, textureSpec.level, rowsPerImage);
+ kTextureFormat, textureSpec.textureSize, textureSpec.level,
+ bufferSpec.rowsPerImage);
const uint32_t maxArrayLayer = textureSpec.copyOrigin.z + copySize.depth;
- // TODO(jiawei.shao@intel.com): support copying into multiple texture array layers in one
- // copy command.
- uint64_t bufferOffset = bufferSpec.offset;
- for (uint32_t slice = textureSpec.copyOrigin.z; slice < maxArrayLayer; ++slice) {
- // Copy to the region [(`x`, `y`, `slice`), (`x + testCopySize.width, `y +
- // testCopySize.height`, 1] at the `level` mip
- // from the buffer at the specified `offset` and `bytesPerRow`
- wgpu::BufferCopyView bufferCopyView =
- utils::CreateBufferCopyView(buffer, bufferOffset, bufferSpec.bytesPerRow, 0);
- wgpu::TextureCopyView textureCopyView = utils::CreateTextureCopyView(
- texture, textureSpec.level,
- {textureSpec.copyOrigin.x, textureSpec.copyOrigin.y, slice});
- wgpu::Extent3D copyOneLayerSize = {copySize.width, copySize.height, 1};
- encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©OneLayerSize);
- bufferOffset += copyLayout.bytesPerImage;
- }
+ wgpu::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(
+ buffer, bufferSpec.offset, bufferSpec.bytesPerRow, bufferSpec.rowsPerImage);
+ wgpu::TextureCopyView textureCopyView =
+ utils::CreateTextureCopyView(texture, textureSpec.level, textureSpec.copyOrigin);
+ encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©Size);
wgpu::CommandBuffer commands = encoder.Finish();
queue.Submit(1, &commands);
- bufferOffset = bufferSpec.offset;
+ uint64_t bufferOffset = bufferSpec.offset;
const uint32_t texelCountLastLayer =
copyLayout.texelBlocksPerRow * (copyLayout.mipSize.height - 1) +
copyLayout.mipSize.width;
@@ -305,30 +281,21 @@
// Create an upload buffer and use it to populate the current slice of the texture in
// `level` mip level
- const uint32_t rowsPerImage = srcSpec.textureSize.height >> srcSpec.level;
const utils::BufferTextureCopyLayout copyLayout =
utils::GetBufferTextureCopyLayoutForTexture2DAtLevel(
kTextureFormat,
{srcSpec.textureSize.width, srcSpec.textureSize.height, copySize.depth},
- srcSpec.level, rowsPerImage);
+ srcSpec.level, 0);
const std::vector<RGBA8> textureArrayCopyData = GetExpectedTextureData(copyLayout);
- // TODO(jiawei.shao@intel.com): support copying into multiple contiguous array layers in one
- // copyBufferToTexture() call.
wgpu::Buffer uploadBuffer = utils::CreateBufferFromData(
device, textureArrayCopyData.data(), copyLayout.byteLength, wgpu::BufferUsage::CopySrc);
- uint64_t uploadBufferOffset = 0;
- for (uint32_t slice = 0; slice < copySize.depth; ++slice) {
- wgpu::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(
- uploadBuffer, uploadBufferOffset, copyLayout.bytesPerRow, 0);
- wgpu::TextureCopyView textureCopyView = utils::CreateTextureCopyView(
- srcTexture, srcSpec.level, {0, 0, srcSpec.copyOrigin.z + slice});
- wgpu::Extent3D copyOneLayerSize = {copyLayout.mipSize.width, copyLayout.mipSize.height,
- 1};
- encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©OneLayerSize);
- uploadBufferOffset += copyLayout.bytesPerImage;
- }
+ wgpu::BufferCopyView bufferCopyView =
+ utils::CreateBufferCopyView(uploadBuffer, 0, copyLayout.bytesPerRow, 0);
+ wgpu::TextureCopyView textureCopyView =
+ utils::CreateTextureCopyView(srcTexture, srcSpec.level, {0, 0, srcSpec.copyOrigin.z});
+ encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©Layout.mipSize);
// Perform the texture to texture copy
wgpu::TextureCopyView srcTextureCopyView =
@@ -700,8 +667,12 @@
}
}
-// Test that copying regions of each texture 2D array layer works
+// Test that copying whole texture 2D array layers in one texture-to-buffer-copy works.
TEST_P(CopyTests_T2B, Texture2DArrayRegion) {
+ // TODO(jiawei.shao@intel.com): investigate why copies with multiple texture array layer fail
+ // with swiftshader.
+ DAWN_SKIP_TEST_IF(IsSwiftshader());
+
constexpr uint32_t kWidth = 256;
constexpr uint32_t kHeight = 128;
constexpr uint32_t kLayers = 6u;
@@ -714,8 +685,12 @@
DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight, kLayers), {kWidth, kHeight, kLayers});
}
-// Test that copying a sub-region of each texture 2D array layer works
+// Test that copying a range of texture 2D array layers in one texture-to-buffer-copy works.
TEST_P(CopyTests_T2B, Texture2DArraySubRegion) {
+ // TODO(jiawei.shao@intel.com): investigate why copies with multiple texture array layer fail
+ // with swiftshader.
+ DAWN_SKIP_TEST_IF(IsSwiftshader());
+
constexpr uint32_t kWidth = 256;
constexpr uint32_t kHeight = 128;
constexpr uint32_t kLayers = 6u;
@@ -733,6 +708,10 @@
// Test that copying texture 2D array mips with 256-byte aligned sizes works
TEST_P(CopyTests_T2B, Texture2DArrayMip) {
+ // TODO(jiawei.shao@intel.com): investigate why copies with multiple texture array layers fail
+ // with swiftshader.
+ DAWN_SKIP_TEST_IF(IsSwiftshader());
+
constexpr uint32_t kWidth = 256;
constexpr uint32_t kHeight = 128;
constexpr uint32_t kLayers = 6u;
@@ -750,6 +729,31 @@
}
}
+// Test that copying from a range of texture 2D array layers in one texture-to-buffer-copy when
+// RowsPerImage is not equal to the height of the texture works.
+TEST_P(CopyTests_T2B, Texture2DArrayRegionNonzeroRowsPerImage) {
+ // TODO(jiawei.shao@intel.com): investigate why copies with multiple texture array layers fail
+ // with swiftshader.
+ DAWN_SKIP_TEST_IF(IsSwiftshader());
+
+ constexpr uint32_t kWidth = 256;
+ constexpr uint32_t kHeight = 128;
+ constexpr uint32_t kLayers = 6u;
+ constexpr uint32_t kBaseLayer = 2u;
+ constexpr uint32_t kCopyLayers = 3u;
+
+ constexpr uint32_t kRowsPerImage = kHeight * 2;
+
+ TextureSpec textureSpec;
+ textureSpec.copyOrigin = {0, 0, kBaseLayer};
+ textureSpec.textureSize = {kWidth, kHeight, kLayers};
+ textureSpec.level = 0;
+
+ BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kRowsPerImage, kCopyLayers, false);
+ bufferSpec.rowsPerImage = kRowsPerImage;
+ DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kCopyLayers});
+}
+
DAWN_INSTANTIATE_TEST(CopyTests_T2B, D3D12Backend(), MetalBackend(), OpenGLBackend(), VulkanBackend());
// Test that copying an entire texture with 256-byte aligned dimensions works
@@ -1038,8 +1042,12 @@
}
}
-// Test that copying into regions of each texture 2D array layer works
+// Test that copying whole texture 2D array layers in one texture-to-buffer-copy works.
TEST_P(CopyTests_B2T, Texture2DArrayRegion) {
+ // TODO(jiawei.shao@intel.com): investigate why copies with multiple texture array layers fail
+ // with swiftshader.
+ DAWN_SKIP_TEST_IF(IsSwiftshader());
+
constexpr uint32_t kWidth = 256;
constexpr uint32_t kHeight = 128;
constexpr uint32_t kLayers = 6u;
@@ -1052,8 +1060,12 @@
DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight, kLayers), {kWidth, kHeight, kLayers});
}
-// Test that copying into a sub-region of each texture 2D array layer works
+// Test that copying a range of texture 2D array layers in one texture-to-buffer-copy works.
TEST_P(CopyTests_B2T, Texture2DArraySubRegion) {
+ // TODO(jiawei.shao@intel.com): investigate why copies with multiple texture array layers fail
+ // with swiftshader.
+ DAWN_SKIP_TEST_IF(IsSwiftshader());
+
constexpr uint32_t kWidth = 256;
constexpr uint32_t kHeight = 128;
constexpr uint32_t kLayers = 6u;
@@ -1069,6 +1081,31 @@
{kWidth, kHeight, kCopyLayers});
}
+// Test that copying into a range of texture 2D array layers in one texture-to-buffer-copy when
+// RowsPerImage is not equal to the height of the texture works.
+TEST_P(CopyTests_B2T, Texture2DArrayRegionNonzeroRowsPerImage) {
+ // TODO(jiawei.shao@intel.com): investigate why copies with multiple texture array layers fail
+ // with swiftshader.
+ DAWN_SKIP_TEST_IF(IsSwiftshader());
+
+ constexpr uint32_t kWidth = 256;
+ constexpr uint32_t kHeight = 128;
+ constexpr uint32_t kLayers = 6u;
+ constexpr uint32_t kBaseLayer = 2u;
+ constexpr uint32_t kCopyLayers = 3u;
+
+ constexpr uint32_t kRowsPerImage = kHeight * 2;
+
+ TextureSpec textureSpec;
+ textureSpec.copyOrigin = {0, 0, kBaseLayer};
+ textureSpec.textureSize = {kWidth, kHeight, kLayers};
+ textureSpec.level = 0;
+
+ BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kRowsPerImage, kCopyLayers, false);
+ bufferSpec.rowsPerImage = kRowsPerImage;
+ DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kCopyLayers});
+}
+
DAWN_INSTANTIATE_TEST(CopyTests_B2T, D3D12Backend(), MetalBackend(), OpenGLBackend(), VulkanBackend());
TEST_P(CopyTests_T2T, Texture) {