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>
diff --git a/src/dawn_native/CommandBuffer.cpp b/src/dawn_native/CommandBuffer.cpp
index 9fd1a99..f02a20f 100644
--- a/src/dawn_native/CommandBuffer.cpp
+++ b/src/dawn_native/CommandBuffer.cpp
@@ -66,11 +66,16 @@
const uint32_t mipLevel) {
Extent3D extent = texture->GetMipLevelPhysicalSize(mipLevel);
- ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D);
- if (extent.width == copySize.width && extent.height == copySize.height) {
- return true;
+ ASSERT(texture->GetDimension() != wgpu::TextureDimension::e1D);
+ switch (texture->GetDimension()) {
+ case wgpu::TextureDimension::e2D:
+ return extent.width == copySize.width && extent.height == copySize.height;
+ case wgpu::TextureDimension::e3D:
+ return extent.width == copySize.width && extent.height == copySize.height &&
+ extent.depthOrArrayLayers == copySize.depthOrArrayLayers;
+ default:
+ UNREACHABLE();
}
- return false;
}
SubresourceRange GetSubresourcesAffectedByCopy(const TextureCopy& copy,
@@ -79,6 +84,8 @@
case wgpu::TextureDimension::e2D:
return {
copy.aspect, {copy.origin.z, copySize.depthOrArrayLayers}, {copy.mipLevel, 1}};
+ case wgpu::TextureDimension::e3D:
+ return {copy.aspect, {0, 1}, {copy.mipLevel, 1}};
default:
UNREACHABLE();
}
diff --git a/src/dawn_native/CommandEncoder.cpp b/src/dawn_native/CommandEncoder.cpp
index 5fb9a4d..8d06963 100644
--- a/src/dawn_native/CommandEncoder.cpp
+++ b/src/dawn_native/CommandEncoder.cpp
@@ -648,7 +648,7 @@
// because in the latter we divide copyExtent.width by blockWidth and
// copyExtent.height by blockHeight while the divisibility conditions are
// checked in validating texture copy range.
- DAWN_TRY(ValidateTextureCopyRange(*destination, fixedCopySize));
+ DAWN_TRY(ValidateTextureCopyRange(GetDevice(), *destination, fixedCopySize));
}
const TexelBlockInfo& blockInfo =
destination->texture->GetFormat().GetAspectInfo(destination->aspect).block;
@@ -707,7 +707,7 @@
// because in the latter we divide copyExtent.width by blockWidth and
// copyExtent.height by blockHeight while the divisibility conditions are
// checked in validating texture copy range.
- DAWN_TRY(ValidateTextureCopyRange(*source, fixedCopySize));
+ DAWN_TRY(ValidateTextureCopyRange(GetDevice(), *source, fixedCopySize));
}
const TexelBlockInfo& blockInfo =
source->texture->GetFormat().GetAspectInfo(source->aspect).block;
@@ -761,8 +761,8 @@
DAWN_TRY(
ValidateTextureToTextureCopyRestrictions(*source, *destination, fixedCopySize));
- DAWN_TRY(ValidateTextureCopyRange(*source, fixedCopySize));
- DAWN_TRY(ValidateTextureCopyRange(*destination, fixedCopySize));
+ DAWN_TRY(ValidateTextureCopyRange(GetDevice(), *source, fixedCopySize));
+ DAWN_TRY(ValidateTextureCopyRange(GetDevice(), *destination, fixedCopySize));
DAWN_TRY(ValidateCanUseAs(source->texture, wgpu::TextureUsage::CopySrc));
DAWN_TRY(ValidateCanUseAs(destination->texture, wgpu::TextureUsage::CopyDst));
diff --git a/src/dawn_native/CommandValidation.cpp b/src/dawn_native/CommandValidation.cpp
index 7ddd044..1e2fc38 100644
--- a/src/dawn_native/CommandValidation.cpp
+++ b/src/dawn_native/CommandValidation.cpp
@@ -308,19 +308,29 @@
return {};
}
- MaybeError ValidateTextureCopyRange(const ImageCopyTexture& textureCopy,
+ MaybeError ValidateTextureCopyRange(DeviceBase const* device,
+ const ImageCopyTexture& textureCopy,
const Extent3D& copySize) {
// TODO(jiawei.shao@intel.com): add validations on the texture-to-texture copies within the
// same texture.
const TextureBase* texture = textureCopy.texture;
+ ASSERT(texture->GetDimension() != wgpu::TextureDimension::e1D);
+
+ // Disallow copy to/from a 3D texture as unsafe until it is fully implemented.
+ if (texture->GetDimension() == wgpu::TextureDimension::e3D &&
+ device->IsToggleEnabled(Toggle::DisallowUnsafeAPIs)) {
+ return DAWN_VALIDATION_ERROR(
+ "Copy to/from a 3D texture is disallowed because it is not fully implemented");
+ }
+
// Validation for the copy being in-bounds:
Extent3D mipSize = texture->GetMipLevelPhysicalSize(textureCopy.mipLevel);
- // For 2D textures, include the array layer as depth so it can be checked with other
+ // For 1D/2D textures, include the array layer as depth so it can be checked with other
// dimensions.
- ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D);
- mipSize.depthOrArrayLayers = texture->GetArrayLayers();
-
+ if (texture->GetDimension() != wgpu::TextureDimension::e3D) {
+ mipSize.depthOrArrayLayers = texture->GetArrayLayers();
+ }
// All texture dimensions are in uint32_t so by doing checks in uint64_t we avoid
// overflows.
if (static_cast<uint64_t>(textureCopy.origin.x) + static_cast<uint64_t>(copySize.width) >
diff --git a/src/dawn_native/CommandValidation.h b/src/dawn_native/CommandValidation.h
index 5b6290c..9c8a42f 100644
--- a/src/dawn_native/CommandValidation.h
+++ b/src/dawn_native/CommandValidation.h
@@ -48,7 +48,8 @@
uint64_t byteSize,
const TexelBlockInfo& blockInfo,
const Extent3D& copyExtent);
- MaybeError ValidateTextureCopyRange(const ImageCopyTexture& imageCopyTexture,
+ MaybeError ValidateTextureCopyRange(DeviceBase const* device,
+ const ImageCopyTexture& imageCopyTexture,
const Extent3D& copySize);
ResultOrError<Aspect> SingleAspectUsedByImageCopyTexture(const ImageCopyTexture& view);
MaybeError ValidateLinearToDepthStencilCopyRestrictions(const ImageCopyTexture& dst);
diff --git a/src/dawn_native/CopyTextureForBrowserHelper.cpp b/src/dawn_native/CopyTextureForBrowserHelper.cpp
index 5d20bd5..b6374be 100644
--- a/src/dawn_native/CopyTextureForBrowserHelper.cpp
+++ b/src/dawn_native/CopyTextureForBrowserHelper.cpp
@@ -208,8 +208,8 @@
DAWN_TRY(ValidateCopyTextureForBrowserRestrictions(*source, *destination, *copySize));
- DAWN_TRY(ValidateTextureCopyRange(*source, *copySize));
- DAWN_TRY(ValidateTextureCopyRange(*destination, *copySize));
+ DAWN_TRY(ValidateTextureCopyRange(device, *source, *copySize));
+ DAWN_TRY(ValidateTextureCopyRange(device, *destination, *copySize));
DAWN_TRY(ValidateCanUseAs(source->texture, wgpu::TextureUsage::CopySrc));
DAWN_TRY(ValidateCanUseAs(destination->texture, wgpu::TextureUsage::CopyDst));
diff --git a/src/dawn_native/Queue.cpp b/src/dawn_native/Queue.cpp
index 124832c..c113c1e 100644
--- a/src/dawn_native/Queue.cpp
+++ b/src/dawn_native/Queue.cpp
@@ -528,7 +528,7 @@
// because in the latter we divide copyExtent.width by blockWidth and
// copyExtent.height by blockHeight while the divisibility conditions are
// checked in validating texture copy range.
- DAWN_TRY(ValidateTextureCopyRange(*destination, *writeSize));
+ DAWN_TRY(ValidateTextureCopyRange(GetDevice(), *destination, *writeSize));
const TexelBlockInfo& blockInfo =
destination->texture->GetFormat().GetAspectInfo(destination->aspect).block;
diff --git a/src/dawn_native/Texture.cpp b/src/dawn_native/Texture.cpp
index a3764c2..a8d153d 100644
--- a/src/dawn_native/Texture.cpp
+++ b/src/dawn_native/Texture.cpp
@@ -293,7 +293,7 @@
if (descriptor->dimension != wgpu::TextureDimension::e2D &&
device->IsToggleEnabled(Toggle::DisallowUnsafeAPIs)) {
return DAWN_VALIDATION_ERROR(
- "1D and 3D textures are disallowed because they are not fully implemented ");
+ "1D and 3D textures are disallowed because they are not fully implemented");
}
if (descriptor->dimension != wgpu::TextureDimension::e2D && format->isCompressed) {
@@ -421,7 +421,7 @@
mUsage(descriptor->usage),
mState(state) {
uint32_t subresourceCount =
- mMipLevelCount * mSize.depthOrArrayLayers * GetAspectCount(mFormat.aspects);
+ mMipLevelCount * GetArrayLayers() * GetAspectCount(mFormat.aspects);
mIsSubresourceContentInitializedAtIndex = std::vector<bool>(subresourceCount, false);
// Add readonly storage usage if the texture has a storage usage. The validation rules in
diff --git a/src/dawn_native/d3d12/CommandBufferD3D12.cpp b/src/dawn_native/d3d12/CommandBufferD3D12.cpp
index 33811d8..a483b42 100644
--- a/src/dawn_native/d3d12/CommandBufferD3D12.cpp
+++ b/src/dawn_native/d3d12/CommandBufferD3D12.cpp
@@ -178,15 +178,15 @@
bufferCopy.offset = 0;
bufferCopy.bytesPerRow = bytesPerRow;
bufferCopy.rowsPerImage = rowsPerImage;
- CopyTextureToBufferWithCopySplit(recordingContext->GetCommandList(), srcCopy,
- bufferCopy, srcTexture, tempBuffer.Get(), copySize);
+ Copy2DTextureToBufferWithCopySplit(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();
- CopyBufferToTextureWithCopySplit(recordingContext, dstCopy,
- tempBuffer->GetD3D12Resource(), 0, bytesPerRow,
- rowsPerImage, copySize, dstTexture, dstCopy.aspect);
+ CopyBufferTo2DTextureWithCopySplit(recordingContext, dstCopy,
+ tempBuffer->GetD3D12Resource(), 0, bytesPerRow,
+ rowsPerImage, copySize, dstTexture, dstCopy.aspect);
// Save tempBuffer into recordingContext
recordingContext->AddToTempBuffers(std::move(tempBuffer));
@@ -741,7 +741,7 @@
DAWN_TRY(buffer->EnsureDataInitialized(commandContext));
- ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D);
+ ASSERT(texture->GetDimension() != wgpu::TextureDimension::e1D);
SubresourceRange subresources =
GetSubresourcesAffectedByCopy(copy->destination, copy->copySize);
@@ -756,11 +756,22 @@
texture->TrackUsageAndTransitionNow(commandContext, wgpu::TextureUsage::CopyDst,
subresources);
- // compute the copySplits and record the CopyTextureRegion commands
- CopyBufferToTextureWithCopySplit(
- commandContext, copy->destination, buffer->GetD3D12Resource(),
- copy->source.offset, copy->source.bytesPerRow, copy->source.rowsPerImage,
- copy->copySize, texture, subresources.aspects);
+ // 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,
+ 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;
}
@@ -772,7 +783,7 @@
DAWN_TRY(buffer->EnsureDataInitializedAsDestination(commandContext, copy));
- ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D);
+ ASSERT(texture->GetDimension() != wgpu::TextureDimension::e1D);
SubresourceRange subresources =
GetSubresourcesAffectedByCopy(copy->source, copy->copySize);
@@ -782,8 +793,14 @@
subresources);
buffer->TrackUsageAndTransitionNow(commandContext, wgpu::BufferUsage::CopyDst);
- CopyTextureToBufferWithCopySplit(commandList, copy->source, copy->destination,
- texture, buffer, copy->copySize);
+ if (texture->GetDimension() == wgpu::TextureDimension::e3D) {
+ Copy3DTextureToBuffer(commandList, copy->source, copy->destination, texture,
+ buffer, copy->copySize);
+ } else {
+ Copy2DTextureToBufferWithCopySplit(commandList, copy->source,
+ copy->destination, texture, buffer,
+ copy->copySize);
+ }
break;
}
diff --git a/src/dawn_native/d3d12/DeviceD3D12.cpp b/src/dawn_native/d3d12/DeviceD3D12.cpp
index ef0b2d7..e03c034 100644
--- a/src/dawn_native/d3d12/DeviceD3D12.cpp
+++ b/src/dawn_native/d3d12/DeviceD3D12.cpp
@@ -418,9 +418,9 @@
texture->TrackUsageAndTransitionNow(commandContext, wgpu::TextureUsage::CopyDst, range);
// compute the copySplits and record the CopyTextureRegion commands
- CopyBufferToTextureWithCopySplit(commandContext, *dst, ToBackend(source)->GetResource(),
- src.offset, src.bytesPerRow, src.rowsPerImage,
- copySizePixels, texture, range.aspects);
+ CopyBufferTo2DTextureWithCopySplit(commandContext, *dst, ToBackend(source)->GetResource(),
+ src.offset, src.bytesPerRow, src.rowsPerImage,
+ copySizePixels, texture, range.aspects);
return {};
}
diff --git a/src/dawn_native/d3d12/TextureCopySplitter.cpp b/src/dawn_native/d3d12/TextureCopySplitter.cpp
index d75257e..e5307cd 100644
--- a/src/dawn_native/d3d12/TextureCopySplitter.cpp
+++ b/src/dawn_native/d3d12/TextureCopySplitter.cpp
@@ -209,7 +209,8 @@
const TexelBlockInfo& blockInfo,
uint64_t offset,
uint32_t bytesPerRow,
- uint32_t rowsPerImage) {
+ uint32_t rowsPerImage,
+ bool is3DTexture) {
TextureCopySplits copies;
const uint64_t bytesPerSlice = bytesPerRow * rowsPerImage;
@@ -225,15 +226,19 @@
// slice. Moreover, if "rowsPerImage" is even, both the first and second copy layers can
// share the same copy split, so in this situation we just need to compute copy split once
// and reuse it for all the slices.
- const dawn_native::Extent3D copyOneLayerSize = {copySize.width, copySize.height, 1};
- const dawn_native::Origin3D copyFirstLayerOrigin = {origin.x, origin.y, 0};
+ Extent3D copyOneLayerSize = copySize;
+ Origin3D copyFirstLayerOrigin = origin;
+ if (!is3DTexture) {
+ copyOneLayerSize.depthOrArrayLayers = 1;
+ copyFirstLayerOrigin.z = 0;
+ }
copies.copies2D[0] = ComputeTextureCopySplit(copyFirstLayerOrigin, copyOneLayerSize,
blockInfo, offset, bytesPerRow, rowsPerImage);
- // When the copy only refers one texture 2D array layer copies.copies2D[1] will never be
- // used so we can safely early return here.
- if (copySize.depthOrArrayLayers == 1) {
+ // When the copy only refers one texture 2D array layer or a 3D texture, copies.copies2D[1]
+ // will never be used so we can safely early return here.
+ if (copySize.depthOrArrayLayers == 1 || is3DTexture) {
return copies;
}
diff --git a/src/dawn_native/d3d12/TextureCopySplitter.h b/src/dawn_native/d3d12/TextureCopySplitter.h
index 962c332..f4bdb7b 100644
--- a/src/dawn_native/d3d12/TextureCopySplitter.h
+++ b/src/dawn_native/d3d12/TextureCopySplitter.h
@@ -61,7 +61,8 @@
const TexelBlockInfo& blockInfo,
uint64_t offset,
uint32_t bytesPerRow,
- uint32_t rowsPerImage);
+ uint32_t rowsPerImage,
+ bool is3DTexture = false);
}} // namespace dawn_native::d3d12
#endif // DAWNNATIVE_D3D12_TEXTURECOPYSPLITTER_H_
diff --git a/src/dawn_native/d3d12/TextureD3D12.cpp b/src/dawn_native/d3d12/TextureD3D12.cpp
index 145fff6..fdcd43e 100644
--- a/src/dawn_native/d3d12/TextureD3D12.cpp
+++ b/src/dawn_native/d3d12/TextureD3D12.cpp
@@ -767,8 +767,7 @@
const ExecutionSerial pendingCommandSerial =
ToBackend(GetDevice())->GetPendingCommandSerial();
- // This transitions assume it is a 2D texture
- ASSERT(GetDimension() == wgpu::TextureDimension::e2D);
+ ASSERT(GetDimension() != wgpu::TextureDimension::e1D);
mSubresourceStateAndDecay.Update(
range, [&](const SubresourceRange& updateRange, StateAndDecay* state) {
diff --git a/src/dawn_native/d3d12/UtilsD3D12.cpp b/src/dawn_native/d3d12/UtilsD3D12.cpp
index a2cc893..4aea0ff 100644
--- a/src/dawn_native/d3d12/UtilsD3D12.cpp
+++ b/src/dawn_native/d3d12/UtilsD3D12.cpp
@@ -174,15 +174,15 @@
}
}
- void CopyBufferToTextureWithCopySplit(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 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) {
ASSERT(HasOneBit(aspect));
// See comments in ComputeTextureCopySplits() for more details.
const TexelBlockInfo& blockInfo = texture->GetFormat().GetAspectInfo(aspect).block;
@@ -217,6 +217,26 @@
}
}
+ 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) {
+ ASSERT(HasOneBit(aspect));
+ // See comments in ComputeTextureCopySplits() for more details.
+ const TexelBlockInfo& blockInfo = texture->GetFormat().GetAspectInfo(aspect).block;
+ const TextureCopySplits copySplits = ComputeTextureCopySplits(
+ textureCopy.origin, copySize, blockInfo, offset, bytesPerRow, rowsPerImage, true);
+
+ RecordCopyBufferToTextureFromTextureCopySplit(
+ commandContext->GetCommandList(), copySplits.copies2D[0], bufferResource, 0,
+ bytesPerRow, texture, textureCopy.mipLevel, textureCopy.origin.z, aspect);
+ }
+
void RecordCopyTextureToBufferFromTextureCopySplit(ID3D12GraphicsCommandList* commandList,
const Texture2DCopySplit& baseCopySplit,
Buffer* buffer,
@@ -249,12 +269,13 @@
}
}
- void CopyTextureToBufferWithCopySplit(ID3D12GraphicsCommandList* commandList,
- const TextureCopy& textureCopy,
- const BufferCopy& bufferCopy,
- Texture* texture,
- Buffer* buffer,
- const Extent3D& copySize) {
+ void Copy2DTextureToBufferWithCopySplit(ID3D12GraphicsCommandList* commandList,
+ const TextureCopy& textureCopy,
+ const BufferCopy& bufferCopy,
+ Texture* texture,
+ Buffer* buffer,
+ const Extent3D& copySize) {
+ ASSERT(HasOneBit(textureCopy.aspect));
const TexelBlockInfo& blockInfo =
texture->GetFormat().GetAspectInfo(textureCopy.aspect).block;
@@ -290,4 +311,24 @@
}
}
+ void Copy3DTextureToBuffer(ID3D12GraphicsCommandList* commandList,
+ const TextureCopy& textureCopy,
+ const BufferCopy& bufferCopy,
+ Texture* texture,
+ Buffer* buffer,
+ const Extent3D& copySize) {
+ ASSERT(HasOneBit(textureCopy.aspect));
+ const TexelBlockInfo& blockInfo =
+ texture->GetFormat().GetAspectInfo(textureCopy.aspect).block;
+
+ // See comments around ComputeTextureCopySplits() for more details.
+ const TextureCopySplits copySplits =
+ ComputeTextureCopySplits(textureCopy.origin, copySize, blockInfo, bufferCopy.offset,
+ bufferCopy.bytesPerRow, bufferCopy.rowsPerImage, true);
+
+ RecordCopyTextureToBufferFromTextureCopySplit(
+ commandList, copySplits.copies2D[0], buffer, 0, bufferCopy.bytesPerRow, texture,
+ textureCopy.mipLevel, textureCopy.origin.z, textureCopy.aspect);
+ }
+
}} // namespace dawn_native::d3d12
diff --git a/src/dawn_native/d3d12/UtilsD3D12.h b/src/dawn_native/d3d12/UtilsD3D12.h
index 5733543..fd9be66 100644
--- a/src/dawn_native/d3d12/UtilsD3D12.h
+++ b/src/dawn_native/d3d12/UtilsD3D12.h
@@ -54,15 +54,25 @@
uint32_t textureSlice,
Aspect aspect);
- void CopyBufferToTextureWithCopySplit(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 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 RecordCopyTextureToBufferFromTextureCopySplit(ID3D12GraphicsCommandList* commandList,
const Texture2DCopySplit& baseCopySplit,
@@ -74,12 +84,19 @@
uint32_t textureSlice,
Aspect aspect);
- void CopyTextureToBufferWithCopySplit(ID3D12GraphicsCommandList* commandList,
- const TextureCopy& textureCopy,
- const BufferCopy& bufferCopy,
- Texture* texture,
- Buffer* buffer,
- const Extent3D& copySize);
+ 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);
}} // namespace dawn_native::d3d12
diff --git a/src/tests/DawnTest.cpp b/src/tests/DawnTest.cpp
index 46f1924..940aa1b 100644
--- a/src/tests/DawnTest.cpp
+++ b/src/tests/DawnTest.cpp
@@ -1018,7 +1018,7 @@
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 @@
// 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);
diff --git a/src/tests/end2end/CopyTests.cpp b/src/tests/end2end/CopyTests.cpp
index 845fb2f..631dfd9 100644
--- a/src/tests/end2end/CopyTests.cpp
+++ b/src/tests/end2end/CopyTests.cpp
@@ -110,13 +110,18 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
}
+// 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(),