Implement 3D texture copy on D3D12: T2B and T2T

This change implements 3D texture copy on D3D12 for texture to
buffer and texture to texture copy for full copy upon the entire
3D texture.

It also uses a function named CopyBufferToTexture to wrap
CopyBufferTo3DTexture and CopyBufferTo2DTextureWithCopySplits.
Likewise, it uses another function named CopyTextureToBuffer to
wrap Copy3DTextureToBuffer and Copy2DTextureToBufferWithCopySplits.

BUG: dawn:547

Change-Id: I4293f6ca4d37f604e6f1c10827686d17469a07ca
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/46820
Commit-Queue: Yunchao He <yunchao.he@intel.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
Yunchao He 2021-04-07 03:08:00 +00:00 committed by Commit Bot service account
parent ee1a3b32ce
commit 1fb3f1dafc
5 changed files with 137 additions and 87 deletions

View File

@ -178,15 +178,15 @@ namespace dawn_native { namespace d3d12 {
bufferCopy.offset = 0;
bufferCopy.bytesPerRow = bytesPerRow;
bufferCopy.rowsPerImage = rowsPerImage;
Copy2DTextureToBufferWithCopySplit(recordingContext->GetCommandList(), srcCopy,
bufferCopy, srcTexture, tempBuffer.Get(), copySize);
RecordCopyTextureToBuffer(recordingContext->GetCommandList(), srcCopy, bufferCopy,
srcTexture, tempBuffer.Get(), copySize);
// Copy from tempBuffer into destination texture
tempBuffer->TrackUsageAndTransitionNow(recordingContext, wgpu::BufferUsage::CopySrc);
Texture* dstTexture = ToBackend(dstCopy.texture).Get();
CopyBufferTo2DTextureWithCopySplit(recordingContext, dstCopy,
tempBuffer->GetD3D12Resource(), 0, bytesPerRow,
rowsPerImage, copySize, dstTexture, dstCopy.aspect);
RecordCopyBufferToTexture(recordingContext, dstCopy, tempBuffer->GetD3D12Resource(), 0,
bytesPerRow, rowsPerImage, copySize, dstTexture,
dstCopy.aspect);
// Save tempBuffer into recordingContext
recordingContext->AddToTempBuffers(std::move(tempBuffer));
@ -756,22 +756,10 @@ namespace dawn_native { namespace d3d12 {
texture->TrackUsageAndTransitionNow(commandContext, wgpu::TextureUsage::CopyDst,
subresources);
// Record the CopyTextureRegion commands for 3D textures. Multiple depths of 3D
// textures can be copied in one shot and copySplits are not needed.
if (texture->GetDimension() == wgpu::TextureDimension::e3D) {
CopyBufferTo3DTexture(commandContext, copy->destination,
RecordCopyBufferToTexture(commandContext, copy->destination,
buffer->GetD3D12Resource(), copy->source.offset,
copy->source.bytesPerRow, copy->source.rowsPerImage,
copy->copySize, texture, subresources.aspects);
} else {
// Compute the copySplits and record the CopyTextureRegion commands for 2D
// textures.
CopyBufferTo2DTextureWithCopySplit(
commandContext, copy->destination, buffer->GetD3D12Resource(),
copy->source.offset, copy->source.bytesPerRow,
copy->source.rowsPerImage, copy->copySize, texture,
subresources.aspects);
}
break;
}
@ -793,14 +781,8 @@ namespace dawn_native { namespace d3d12 {
subresources);
buffer->TrackUsageAndTransitionNow(commandContext, wgpu::BufferUsage::CopyDst);
if (texture->GetDimension() == wgpu::TextureDimension::e3D) {
Copy3DTextureToBuffer(commandList, copy->source, copy->destination, texture,
RecordCopyTextureToBuffer(commandList, copy->source, copy->destination, texture,
buffer, copy->copySize);
} else {
Copy2DTextureToBufferWithCopySplit(commandList, copy->source,
copy->destination, texture, buffer,
copy->copySize);
}
break;
}

View File

@ -405,7 +405,7 @@ namespace dawn_native { namespace d3d12 {
CommandRecordingContext* commandContext;
DAWN_TRY_ASSIGN(commandContext, GetPendingCommandContext());
Texture* texture = ToBackend(dst->texture.Get());
ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D);
ASSERT(texture->GetDimension() != wgpu::TextureDimension::e1D);
SubresourceRange range = GetSubresourcesAffectedByCopy(*dst, copySizePixels);
@ -417,10 +417,9 @@ namespace dawn_native { namespace d3d12 {
texture->TrackUsageAndTransitionNow(commandContext, wgpu::TextureUsage::CopyDst, range);
// compute the copySplits and record the CopyTextureRegion commands
CopyBufferTo2DTextureWithCopySplit(commandContext, *dst, ToBackend(source)->GetResource(),
src.offset, src.bytesPerRow, src.rowsPerImage,
copySizePixels, texture, range.aspects);
RecordCopyBufferToTexture(commandContext, *dst, ToBackend(source)->GetResource(),
src.offset, src.bytesPerRow, src.rowsPerImage, copySizePixels,
texture, range.aspects);
return {};
}

View File

@ -237,6 +237,29 @@ namespace dawn_native { namespace d3d12 {
bytesPerRow, texture, textureCopy.mipLevel, textureCopy.origin.z, aspect);
}
void RecordCopyBufferToTexture(CommandRecordingContext* commandContext,
const TextureCopy& textureCopy,
ID3D12Resource* bufferResource,
const uint64_t offset,
const uint32_t bytesPerRow,
const uint32_t rowsPerImage,
const Extent3D& copySize,
Texture* texture,
Aspect aspect) {
// Record the CopyTextureRegion commands for 3D textures. Multiple depths of 3D
// textures can be copied in one shot and copySplits are not needed.
if (texture->GetDimension() == wgpu::TextureDimension::e3D) {
CopyBufferTo3DTexture(commandContext, textureCopy, bufferResource, offset, bytesPerRow,
rowsPerImage, copySize, texture, aspect);
} else {
// Compute the copySplits and record the CopyTextureRegion commands for 2D
// textures.
CopyBufferTo2DTextureWithCopySplit(commandContext, textureCopy, bufferResource, offset,
bytesPerRow, rowsPerImage, copySize, texture,
aspect);
}
}
void RecordCopyTextureToBufferFromTextureCopySplit(ID3D12GraphicsCommandList* commandList,
const Texture2DCopySplit& baseCopySplit,
Buffer* buffer,
@ -331,4 +354,18 @@ namespace dawn_native { namespace d3d12 {
textureCopy.mipLevel, textureCopy.origin.z, textureCopy.aspect);
}
void RecordCopyTextureToBuffer(ID3D12GraphicsCommandList* commandList,
const TextureCopy& textureCopy,
const BufferCopy& bufferCopy,
Texture* texture,
Buffer* buffer,
const Extent3D& copySize) {
if (texture->GetDimension() == wgpu::TextureDimension::e3D) {
Copy3DTextureToBuffer(commandList, textureCopy, bufferCopy, texture, buffer, copySize);
} else {
Copy2DTextureToBufferWithCopySplit(commandList, textureCopy, bufferCopy, texture,
buffer, copySize);
}
}
}} // namespace dawn_native::d3d12

View File

@ -54,25 +54,15 @@ namespace dawn_native { namespace d3d12 {
uint32_t textureSlice,
Aspect aspect);
void CopyBufferTo2DTextureWithCopySplit(CommandRecordingContext* commandContext,
const TextureCopy& textureCopy,
ID3D12Resource* bufferResource,
const uint64_t offset,
const uint32_t bytesPerRow,
const uint32_t rowsPerImage,
const Extent3D& copySize,
Texture* texture,
Aspect aspect);
void CopyBufferTo3DTexture(CommandRecordingContext* commandContext,
const TextureCopy& textureCopy,
ID3D12Resource* bufferResource,
const uint64_t offset,
const uint32_t bytesPerRow,
const uint32_t rowsPerImage,
const Extent3D& copySize,
Texture* texture,
Aspect aspect);
void RecordCopyBufferToTexture(CommandRecordingContext* commandContext,
const TextureCopy& textureCopy,
ID3D12Resource* bufferResource,
const uint64_t offset,
const uint32_t bytesPerRow,
const uint32_t rowsPerImage,
const Extent3D& copySize,
Texture* texture,
Aspect aspect);
void RecordCopyTextureToBufferFromTextureCopySplit(ID3D12GraphicsCommandList* commandList,
const Texture2DCopySplit& baseCopySplit,
@ -84,19 +74,12 @@ namespace dawn_native { namespace d3d12 {
uint32_t textureSlice,
Aspect aspect);
void Copy2DTextureToBufferWithCopySplit(ID3D12GraphicsCommandList* commandList,
const TextureCopy& textureCopy,
const BufferCopy& bufferCopy,
Texture* texture,
Buffer* buffer,
const Extent3D& copySize);
void Copy3DTextureToBuffer(ID3D12GraphicsCommandList* commandList,
const TextureCopy& textureCopy,
const BufferCopy& bufferCopy,
Texture* texture,
Buffer* buffer,
const Extent3D& copySize);
void RecordCopyTextureToBuffer(ID3D12GraphicsCommandList* commandList,
const TextureCopy& textureCopy,
const BufferCopy& bufferCopy,
Texture* texture,
Buffer* buffer,
const Extent3D& copySize);
}} // namespace dawn_native::d3d12

View File

@ -130,14 +130,15 @@ class CopyTests_T2B : public CopyTests {
protected:
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);
const uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(textureSpec.format);
// 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;
@ -186,11 +187,18 @@ class CopyTests_T2B : public CopyTests {
uint64_t bufferOffset = bufferSpec.offset;
const wgpu::Extent3D copySizePerSlice = {copySize.width, copySize.height, 1};
uint32_t copyLayer = copySize.depthOrArrayLayers;
uint32_t copyDepth = 1;
if (dimension == wgpu::TextureDimension::e3D) {
copyLayer = 1;
copyDepth = copySize.depthOrArrayLayers;
}
const wgpu::Extent3D copySizePerLayer = {copySize.width, copySize.height, copyDepth};
// Texels in single slice.
const uint32_t texelCountInCopyRegion = utils::GetTexelCountInCopyRegion(
bufferSpec.bytesPerRow, bufferSpec.rowsPerImage, copySizePerSlice, textureSpec.format);
const uint32_t maxArrayLayer = textureSpec.copyOrigin.z + copySize.depthOrArrayLayers;
bufferSpec.bytesPerRow, bufferSpec.rowsPerImage, copySizePerLayer, textureSpec.format);
const uint32_t maxArrayLayer = textureSpec.copyOrigin.z + copyLayer;
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
@ -203,7 +211,7 @@ class CopyTests_T2B : public CopyTests {
PackTextureData(bytesPerTexel,
textureArrayData.data() + expectedTexelArrayDataStartIndex,
copySize.width, copySize.height, 1, copyLayout.bytesPerRow,
copySize.width, copySize.height, copyDepth, copyLayout.bytesPerRow,
expected.data(), bufferSpec.bytesPerRow);
EXPECT_BUFFER_U32_RANGE_EQ(reinterpret_cast<const uint32_t*>(expected.data()), buffer,
@ -314,12 +322,13 @@ class CopyTests_T2T : public CopyTests {
void DoTest(const TextureSpec& srcSpec,
const TextureSpec& dstSpec,
const wgpu::Extent3D& copySize,
bool copyWithinSameTexture = false) {
bool copyWithinSameTexture = false,
wgpu::TextureDimension dimension = wgpu::TextureDimension::e2D) {
ASSERT_EQ(srcSpec.format, dstSpec.format);
const wgpu::TextureFormat format = srcSpec.format;
wgpu::TextureDescriptor srcDescriptor;
srcDescriptor.dimension = wgpu::TextureDimension::e2D;
srcDescriptor.dimension = dimension;
srcDescriptor.size = srcSpec.textureSize;
srcDescriptor.sampleCount = 1;
srcDescriptor.format = format;
@ -332,7 +341,7 @@ class CopyTests_T2T : public CopyTests {
dstTexture = srcTexture;
} else {
wgpu::TextureDescriptor dstDescriptor;
dstDescriptor.dimension = wgpu::TextureDimension::e2D;
dstDescriptor.dimension = dimension;
dstDescriptor.size = dstSpec.textureSize;
dstDescriptor.sampleCount = 1;
dstDescriptor.format = format;
@ -393,14 +402,21 @@ class CopyTests_T2T : public CopyTests {
// Validate if the data in outputBuffer is what we expected, including the untouched data
// outside of the copy.
{
uint32_t copyLayer = copySize.depthOrArrayLayers;
uint32_t copyDepth = 1;
if (dimension == wgpu::TextureDimension::e3D) {
copyLayer = 1;
copyDepth = copySize.depthOrArrayLayers;
}
const uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(format);
const uint64_t validDataSizePerDstTextureLayer = utils::RequiredBytesInCopy(
dstDataCopyLayout.bytesPerRow, dstDataCopyLayout.mipSize.height,
dstDataCopyLayout.mipSize.width, dstDataCopyLayout.mipSize.height, 1,
dstDataCopyLayout.mipSize.width, dstDataCopyLayout.mipSize.height, copyDepth,
bytesPerTexel);
std::vector<uint8_t> expectedDstDataPerSlice(validDataSizePerDstTextureLayer);
for (uint32_t slice = 0; slice < copySize.depthOrArrayLayers; ++slice) {
for (uint32_t slice = 0; slice < copyLayer; ++slice) {
// For each source texture array slice involved in the copy, emulate the T2T copy
// on the CPU side by "copying" the copy data from the "source texture"
// (srcTextureCopyData) to the "destination texture" (expectedDstDataPerSlice).
@ -419,10 +435,10 @@ class CopyTests_T2T : public CopyTests {
dstSpec.copyOrigin.y * dstDataCopyLayout.mipSize.width) *
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, 1, srcDataCopyLayout.bytesPerRow,
&expectedDstDataPerSlice[expectedDstDataOffset],
dstDataCopyLayout.bytesPerRow);
PackTextureData(
bytesPerTexel, &srcTextureCopyData[srcTexelDataOffset], copySize.width,
copySize.height, copyDepth, srcDataCopyLayout.bytesPerRow,
&expectedDstDataPerSlice[expectedDstDataOffset], dstDataCopyLayout.bytesPerRow);
// Compare the content of the destination texture at the (dstSpec.copyOrigin.z +
// slice)-th layer to its expected data after the copy (the outputBuffer contains
@ -820,7 +836,7 @@ TEST_P(CopyTests_T2B, StrideSpecialCases) {
}
// Test that copying whole texture 2D array layers in one texture-to-buffer-copy works.
TEST_P(CopyTests_T2B, Texture2DArrayRegion) {
TEST_P(CopyTests_T2B, Texture2DArrayFull) {
constexpr uint32_t kWidth = 256;
constexpr uint32_t kHeight = 128;
constexpr uint32_t kLayers = 6u;
@ -930,7 +946,24 @@ 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,
// Test that copying whole 3D texture in one texture-to-buffer-copy works.
TEST_P(CopyTests_T2B, Texture3DFull) {
// 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 kDepth = 6u;
TextureSpec textureSpec;
textureSpec.textureSize = {kWidth, kHeight, kDepth};
DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight, kDepth), {kWidth, kHeight, kDepth},
wgpu::TextureDimension::e3D);
}
// TODO(yunchao.he@intel.com): add T2B tests for 3D textures, like RowPitch,
// RowsPerImage, buffer offset, partial depth range, non-zero level, etc.
DAWN_INSTANTIATE_TEST(CopyTests_T2B,
@ -1279,7 +1312,7 @@ TEST_P(CopyTests_B2T, StrideSpecialCases) {
}
// Test that copying whole texture 2D array layers in one texture-to-buffer-copy works.
TEST_P(CopyTests_B2T, Texture2DArrayRegion) {
TEST_P(CopyTests_B2T, Texture2DArrayFull) {
constexpr uint32_t kWidth = 256;
constexpr uint32_t kHeight = 128;
constexpr uint32_t kLayers = 6u;
@ -1370,20 +1403,20 @@ 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) {
// Test that copying whole texture 3D in one buffer-to-texture-copy works.
TEST_P(CopyTests_B2T, Texture3DFull) {
// 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;
constexpr uint32_t kDepth = 6u;
TextureSpec textureSpec;
textureSpec.textureSize = {kWidth, kHeight, kLayers};
textureSpec.textureSize = {kWidth, kHeight, kDepth};
DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight, kLayers), {kWidth, kHeight, kLayers},
DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight, kDepth), {kWidth, kHeight, kDepth},
wgpu::TextureDimension::e3D);
}
@ -1434,7 +1467,7 @@ TEST_P(CopyTests_T2T, TextureRegion) {
}
// Test copying the whole 2D array texture.
TEST_P(CopyTests_T2T, Texture2DArray) {
TEST_P(CopyTests_T2T, Texture2DArrayFull) {
constexpr uint32_t kWidth = 256;
constexpr uint32_t kHeight = 128;
constexpr uint32_t kLayers = 6u;
@ -1700,7 +1733,23 @@ TEST_P(CopyTests_T2T, CopyFromNonZeroMipLevelWithTexelBlockSizeLessThan4Bytes) {
}
}
// TODO(yunchao.he@intel.com): add T2T tests for 3D textures, like entire texture copy, RowPitch,
// Test that copying whole 3D texture in one texture-to-texture-copy works.
TEST_P(CopyTests_T2T, Texture3DFull) {
// 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 kDepth = 6u;
TextureSpec textureSpec;
textureSpec.textureSize = {kWidth, kHeight, kDepth};
DoTest(textureSpec, textureSpec, {kWidth, kHeight, kDepth}, false, wgpu::TextureDimension::e3D);
}
// TODO(yunchao.he@intel.com): add T2T tests for 3D textures, like RowPitch,
// RowsPerImage, buffer offset, partial depth range, non-zero level, etc.
DAWN_INSTANTIATE_TEST(