Implement 3D texture copy on D3D12 backend: copy to entire 3DTexture

This is the first patch to implement 3D texture copy. It starts with
implementation for 3D texture copy on D3D12 backend with the simplest
case: copy to the entire 3D texture. And texture's width is aligned
with 256 bytes.

The implementation for 3d texture copy might be inaccurate/incorrect
in some functions for complicated cases. But don't panic. The previous
implementation is also incorrect because many functions assumes that
we are copying to/from 2D textures only. And I will incrementally fix
the incorrect functions via upcoming tests for 3d texture copy.

BUG: dawn:547

Change-Id: I588b09fc8d0f0398e0798573415ba3a6a3f576fc
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/45980
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Yunchao He <yunchao.he@intel.com>
This commit is contained in:
Yunchao He
2021-04-01 22:40:43 +00:00
committed by Commit Bot service account
parent 61fbb28547
commit 3e8f3f9609
16 changed files with 237 additions and 102 deletions

View File

@@ -1018,7 +1018,7 @@ std::ostringstream& DawnTestBase::AddTextureExpectationImpl(const char* file,
uint32_t rowsPerImage = extent.height;
uint32_t size = utils::RequiredBytesInCopy(bytesPerRow, rowsPerImage, extent.width,
extent.height, extent.depth, dataSize);
extent.height, extent.depthOrArrayLayers, dataSize);
// TODO(enga): We should have the map async alignment in Contants.h. Also, it should change to 8
// for Float64Array.
@@ -1026,8 +1026,8 @@ std::ostringstream& DawnTestBase::AddTextureExpectationImpl(const char* file,
// We need to enqueue the copy immediately because by the time we resolve the expectation,
// the texture might have been modified.
wgpu::ImageCopyTexture imageCopyTexture =
utils::CreateImageCopyTexture(texture, level, {origin.x, origin.y, layer}, aspect);
wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture(
texture, level, {origin.x, origin.y, origin.z + layer}, aspect);
wgpu::ImageCopyBuffer imageCopyBuffer =
utils::CreateImageCopyBuffer(readback.buffer, readback.offset, bytesPerRow, rowsPerImage);

View File

@@ -110,13 +110,18 @@ class CopyTests : public DawnTest {
const void* srcData,
uint32_t widthInBlocks,
uint32_t heightInBlocks,
uint32_t depthInBlocks,
uint32_t srcBytesPerRow,
void* dstData,
uint32_t dstBytesPerRow) {
for (unsigned int y = 0; y < heightInBlocks; ++y) {
memcpy(static_cast<uint8_t*>(dstData) + y * dstBytesPerRow,
static_cast<const uint8_t*>(srcData) + y * srcBytesPerRow,
widthInBlocks * bytesPerTexelBlock);
for (unsigned int z = 0; z < depthInBlocks; ++z) {
uint32_t srcDepthOffset = z * srcBytesPerRow * heightInBlocks;
uint32_t dstDepthOffset = z * dstBytesPerRow * heightInBlocks;
for (unsigned int y = 0; y < heightInBlocks; ++y) {
memcpy(static_cast<uint8_t*>(dstData) + srcDepthOffset + y * dstBytesPerRow,
static_cast<const uint8_t*>(srcData) + dstDepthOffset + y * srcBytesPerRow,
widthInBlocks * bytesPerTexelBlock);
}
}
}
};
@@ -198,7 +203,7 @@ class CopyTests_T2B : public CopyTests {
PackTextureData(bytesPerTexel,
textureArrayData.data() + expectedTexelArrayDataStartIndex,
copySize.width, copySize.height, copyLayout.bytesPerRow,
copySize.width, copySize.height, 1, copyLayout.bytesPerRow,
expected.data(), bufferSpec.bytesPerRow);
EXPECT_BUFFER_U32_RANGE_EQ(reinterpret_cast<const uint32_t*>(expected.data()), buffer,
@@ -229,10 +234,10 @@ class CopyTests_B2T : public CopyTests {
void DoTest(const TextureSpec& textureSpec,
const BufferSpec& bufferSpec,
const wgpu::Extent3D& copySize) {
const wgpu::Extent3D& copySize,
wgpu::TextureDimension dimension = wgpu::TextureDimension::e2D) {
// TODO(jiawei.shao@intel.com): support testing arbitrary formats
ASSERT_EQ(kDefaultFormat, textureSpec.format);
// Create a buffer of size `size` and populate it with data
const uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(textureSpec.format);
std::vector<RGBA8> bufferData(bufferSpec.size / bytesPerTexel);
@@ -243,7 +248,7 @@ class CopyTests_B2T : public CopyTests {
// Create a texture that is `width` x `height` with (`level` + 1) mip levels.
wgpu::TextureDescriptor descriptor;
descriptor.dimension = wgpu::TextureDimension::e2D;
descriptor.dimension = dimension;
descriptor.size = textureSpec.textureSize;
descriptor.sampleCount = 1;
descriptor.format = textureSpec.format;
@@ -253,13 +258,6 @@ class CopyTests_B2T : public CopyTests {
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
const utils::TextureDataCopyLayout copyLayout =
utils::GetTextureDataCopyLayoutForTexture2DAtLevel(
textureSpec.format, textureSpec.textureSize, textureSpec.copyLevel,
bufferSpec.rowsPerImage);
const uint32_t maxArrayLayer = textureSpec.copyOrigin.z + copySize.depthOrArrayLayers;
wgpu::ImageCopyBuffer imageCopyBuffer = utils::CreateImageCopyBuffer(
buffer, bufferSpec.offset, bufferSpec.bytesPerRow, bufferSpec.rowsPerImage);
wgpu::ImageCopyTexture imageCopyTexture =
@@ -269,28 +267,41 @@ class CopyTests_B2T : public CopyTests {
wgpu::CommandBuffer commands = encoder.Finish();
queue.Submit(1, &commands);
const utils::TextureDataCopyLayout copyLayout =
utils::GetTextureDataCopyLayoutForTexture2DAtLevel(
textureSpec.format, textureSpec.textureSize, textureSpec.copyLevel,
bufferSpec.rowsPerImage);
uint32_t copyLayer = copySize.depthOrArrayLayers;
uint32_t copyDepth = 1;
if (dimension == wgpu::TextureDimension::e3D) {
copyLayer = 1;
copyDepth = copySize.depthOrArrayLayers;
}
uint64_t bufferOffset = bufferSpec.offset;
const uint32_t texelCountLastLayer =
copyLayout.texelBlocksPerRow * (copyLayout.mipSize.height - 1) +
copyLayout.mipSize.width;
for (uint32_t slice = textureSpec.copyOrigin.z; slice < maxArrayLayer; ++slice) {
copyDepth * (copyLayout.texelBlocksPerRow * (copyLayout.mipSize.height - 1) +
copyLayout.mipSize.width);
for (uint32_t layer = 0; layer < copyLayer; ++layer) {
// Pack the data used to create the buffer in the specified copy region to have the same
// format as the expected texture data.
std::vector<RGBA8> expected(texelCountLastLayer);
PackTextureData(bytesPerTexel, bufferData.data() + bufferOffset / bytesPerTexel,
copySize.width, copySize.height, bufferSpec.bytesPerRow,
copySize.width, copySize.height, copyDepth, bufferSpec.bytesPerRow,
expected.data(), copySize.width * bytesPerTexel);
EXPECT_TEXTURE_RGBA8_EQ(expected.data(), texture,
(textureSpec.copyOrigin.x, textureSpec.copyOrigin.y),
(copySize.width, copySize.height), textureSpec.copyLevel, slice)
EXPECT_TEXTURE_RGBA8_EQ(
expected.data(), texture,
(textureSpec.copyOrigin.x, textureSpec.copyOrigin.y, textureSpec.copyOrigin.z),
(copySize.width, copySize.height, copyDepth), textureSpec.copyLevel, layer)
<< "Buffer to Texture copy failed copying " << bufferSpec.size
<< "-byte buffer with offset " << bufferSpec.offset << " and bytes per row "
<< bufferSpec.bytesPerRow << " to [(" << textureSpec.copyOrigin.x << ", "
<< textureSpec.copyOrigin.y << "), (" << textureSpec.copyOrigin.x + copySize.width
<< ", " << textureSpec.copyOrigin.y + copySize.height << ")) region of "
<< textureSpec.textureSize.width << " x " << textureSpec.textureSize.height
<< " texture at mip level " << textureSpec.copyLevel << " layer " << slice
<< " texture at mip level " << textureSpec.copyLevel << " layer " << layer
<< std::endl;
bufferOffset += copyLayout.bytesPerImage;
}
@@ -408,7 +419,7 @@ class CopyTests_T2T : public CopyTests {
bytesPerTexel;
// Do the T2T "copy" on the CPU side to get the expected texel value at the
PackTextureData(bytesPerTexel, &srcTextureCopyData[srcTexelDataOffset],
copySize.width, copySize.height, srcDataCopyLayout.bytesPerRow,
copySize.width, copySize.height, 1, srcDataCopyLayout.bytesPerRow,
&expectedDstDataPerSlice[expectedDstDataOffset],
dstDataCopyLayout.bytesPerRow);
@@ -918,6 +929,9 @@ TEST_P(CopyTests_T2B, Texture2DArrayRegionWithOffsetEvenRowsPerImage) {
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kCopyLayers});
}
// TODO(yunchao.he@intel.com): add T2B tests for 3D textures, like entire texture copy, RowPitch,
// RowsPerImage, buffer offset, partial depth range, non-zero level, etc.
DAWN_INSTANTIATE_TEST(CopyTests_T2B,
D3D12Backend(),
MetalBackend(),
@@ -1355,6 +1369,26 @@ TEST_P(CopyTests_B2T, Texture2DArrayRegionWithOffsetEvenRowsPerImage) {
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kCopyLayers});
}
// Test that copying whole texture 3D in one texture-to-buffer-copy works.
TEST_P(CopyTests_B2T, Texture3DRegion) {
// TODO(yunchao.he@intel.com): implement 3D texture copy on Vulkan, Metal, OpenGL and OpenGLES
// backend.
DAWN_SKIP_TEST_IF(IsVulkan() || IsMetal() || IsOpenGL() || IsOpenGLES());
constexpr uint32_t kWidth = 256;
constexpr uint32_t kHeight = 128;
constexpr uint32_t kLayers = 6u;
TextureSpec textureSpec;
textureSpec.textureSize = {kWidth, kHeight, kLayers};
DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight, kLayers), {kWidth, kHeight, kLayers},
wgpu::TextureDimension::e3D);
}
// TODO(yunchao.he@intel.com): add more tests like RowPitch, RowsPerImage, buffer offset, partial
// depth range, non-zero level, etc.
DAWN_INSTANTIATE_TEST(CopyTests_B2T,
D3D12Backend(),
MetalBackend(),
@@ -1665,6 +1699,9 @@ TEST_P(CopyTests_T2T, CopyFromNonZeroMipLevelWithTexelBlockSizeLessThan4Bytes) {
}
}
// TODO(yunchao.he@intel.com): add T2T tests for 3D textures, like entire texture copy, RowPitch,
// RowsPerImage, buffer offset, partial depth range, non-zero level, etc.
DAWN_INSTANTIATE_TEST(
CopyTests_T2T,
D3D12Backend(),