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

@ -66,11 +66,16 @@ namespace dawn_native {
const uint32_t mipLevel) { const uint32_t mipLevel) {
Extent3D extent = texture->GetMipLevelPhysicalSize(mipLevel); Extent3D extent = texture->GetMipLevelPhysicalSize(mipLevel);
ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D); ASSERT(texture->GetDimension() != wgpu::TextureDimension::e1D);
if (extent.width == copySize.width && extent.height == copySize.height) { switch (texture->GetDimension()) {
return true; 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, SubresourceRange GetSubresourcesAffectedByCopy(const TextureCopy& copy,
@ -79,6 +84,8 @@ namespace dawn_native {
case wgpu::TextureDimension::e2D: case wgpu::TextureDimension::e2D:
return { return {
copy.aspect, {copy.origin.z, copySize.depthOrArrayLayers}, {copy.mipLevel, 1}}; copy.aspect, {copy.origin.z, copySize.depthOrArrayLayers}, {copy.mipLevel, 1}};
case wgpu::TextureDimension::e3D:
return {copy.aspect, {0, 1}, {copy.mipLevel, 1}};
default: default:
UNREACHABLE(); UNREACHABLE();
} }

View File

@ -648,7 +648,7 @@ namespace dawn_native {
// because in the latter we divide copyExtent.width by blockWidth and // because in the latter we divide copyExtent.width by blockWidth and
// copyExtent.height by blockHeight while the divisibility conditions are // copyExtent.height by blockHeight while the divisibility conditions are
// checked in validating texture copy range. // checked in validating texture copy range.
DAWN_TRY(ValidateTextureCopyRange(*destination, fixedCopySize)); DAWN_TRY(ValidateTextureCopyRange(GetDevice(), *destination, fixedCopySize));
} }
const TexelBlockInfo& blockInfo = const TexelBlockInfo& blockInfo =
destination->texture->GetFormat().GetAspectInfo(destination->aspect).block; destination->texture->GetFormat().GetAspectInfo(destination->aspect).block;
@ -707,7 +707,7 @@ namespace dawn_native {
// because in the latter we divide copyExtent.width by blockWidth and // because in the latter we divide copyExtent.width by blockWidth and
// copyExtent.height by blockHeight while the divisibility conditions are // copyExtent.height by blockHeight while the divisibility conditions are
// checked in validating texture copy range. // checked in validating texture copy range.
DAWN_TRY(ValidateTextureCopyRange(*source, fixedCopySize)); DAWN_TRY(ValidateTextureCopyRange(GetDevice(), *source, fixedCopySize));
} }
const TexelBlockInfo& blockInfo = const TexelBlockInfo& blockInfo =
source->texture->GetFormat().GetAspectInfo(source->aspect).block; source->texture->GetFormat().GetAspectInfo(source->aspect).block;
@ -761,8 +761,8 @@ namespace dawn_native {
DAWN_TRY( DAWN_TRY(
ValidateTextureToTextureCopyRestrictions(*source, *destination, fixedCopySize)); ValidateTextureToTextureCopyRestrictions(*source, *destination, fixedCopySize));
DAWN_TRY(ValidateTextureCopyRange(*source, fixedCopySize)); DAWN_TRY(ValidateTextureCopyRange(GetDevice(), *source, fixedCopySize));
DAWN_TRY(ValidateTextureCopyRange(*destination, fixedCopySize)); DAWN_TRY(ValidateTextureCopyRange(GetDevice(), *destination, fixedCopySize));
DAWN_TRY(ValidateCanUseAs(source->texture, wgpu::TextureUsage::CopySrc)); DAWN_TRY(ValidateCanUseAs(source->texture, wgpu::TextureUsage::CopySrc));
DAWN_TRY(ValidateCanUseAs(destination->texture, wgpu::TextureUsage::CopyDst)); DAWN_TRY(ValidateCanUseAs(destination->texture, wgpu::TextureUsage::CopyDst));

View File

@ -308,19 +308,29 @@ namespace dawn_native {
return {}; return {};
} }
MaybeError ValidateTextureCopyRange(const ImageCopyTexture& textureCopy, MaybeError ValidateTextureCopyRange(DeviceBase const* device,
const ImageCopyTexture& textureCopy,
const Extent3D& copySize) { const Extent3D& copySize) {
// TODO(jiawei.shao@intel.com): add validations on the texture-to-texture copies within the // TODO(jiawei.shao@intel.com): add validations on the texture-to-texture copies within the
// same texture. // same texture.
const TextureBase* texture = textureCopy.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: // Validation for the copy being in-bounds:
Extent3D mipSize = texture->GetMipLevelPhysicalSize(textureCopy.mipLevel); 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. // dimensions.
ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D); if (texture->GetDimension() != wgpu::TextureDimension::e3D) {
mipSize.depthOrArrayLayers = texture->GetArrayLayers(); mipSize.depthOrArrayLayers = texture->GetArrayLayers();
}
// All texture dimensions are in uint32_t so by doing checks in uint64_t we avoid // All texture dimensions are in uint32_t so by doing checks in uint64_t we avoid
// overflows. // overflows.
if (static_cast<uint64_t>(textureCopy.origin.x) + static_cast<uint64_t>(copySize.width) > if (static_cast<uint64_t>(textureCopy.origin.x) + static_cast<uint64_t>(copySize.width) >

View File

@ -48,7 +48,8 @@ namespace dawn_native {
uint64_t byteSize, uint64_t byteSize,
const TexelBlockInfo& blockInfo, const TexelBlockInfo& blockInfo,
const Extent3D& copyExtent); const Extent3D& copyExtent);
MaybeError ValidateTextureCopyRange(const ImageCopyTexture& imageCopyTexture, MaybeError ValidateTextureCopyRange(DeviceBase const* device,
const ImageCopyTexture& imageCopyTexture,
const Extent3D& copySize); const Extent3D& copySize);
ResultOrError<Aspect> SingleAspectUsedByImageCopyTexture(const ImageCopyTexture& view); ResultOrError<Aspect> SingleAspectUsedByImageCopyTexture(const ImageCopyTexture& view);
MaybeError ValidateLinearToDepthStencilCopyRestrictions(const ImageCopyTexture& dst); MaybeError ValidateLinearToDepthStencilCopyRestrictions(const ImageCopyTexture& dst);

View File

@ -208,8 +208,8 @@ namespace dawn_native {
DAWN_TRY(ValidateCopyTextureForBrowserRestrictions(*source, *destination, *copySize)); DAWN_TRY(ValidateCopyTextureForBrowserRestrictions(*source, *destination, *copySize));
DAWN_TRY(ValidateTextureCopyRange(*source, *copySize)); DAWN_TRY(ValidateTextureCopyRange(device, *source, *copySize));
DAWN_TRY(ValidateTextureCopyRange(*destination, *copySize)); DAWN_TRY(ValidateTextureCopyRange(device, *destination, *copySize));
DAWN_TRY(ValidateCanUseAs(source->texture, wgpu::TextureUsage::CopySrc)); DAWN_TRY(ValidateCanUseAs(source->texture, wgpu::TextureUsage::CopySrc));
DAWN_TRY(ValidateCanUseAs(destination->texture, wgpu::TextureUsage::CopyDst)); DAWN_TRY(ValidateCanUseAs(destination->texture, wgpu::TextureUsage::CopyDst));

View File

@ -528,7 +528,7 @@ namespace dawn_native {
// because in the latter we divide copyExtent.width by blockWidth and // because in the latter we divide copyExtent.width by blockWidth and
// copyExtent.height by blockHeight while the divisibility conditions are // copyExtent.height by blockHeight while the divisibility conditions are
// checked in validating texture copy range. // checked in validating texture copy range.
DAWN_TRY(ValidateTextureCopyRange(*destination, *writeSize)); DAWN_TRY(ValidateTextureCopyRange(GetDevice(), *destination, *writeSize));
const TexelBlockInfo& blockInfo = const TexelBlockInfo& blockInfo =
destination->texture->GetFormat().GetAspectInfo(destination->aspect).block; destination->texture->GetFormat().GetAspectInfo(destination->aspect).block;

View File

@ -421,7 +421,7 @@ namespace dawn_native {
mUsage(descriptor->usage), mUsage(descriptor->usage),
mState(state) { mState(state) {
uint32_t subresourceCount = uint32_t subresourceCount =
mMipLevelCount * mSize.depthOrArrayLayers * GetAspectCount(mFormat.aspects); mMipLevelCount * GetArrayLayers() * GetAspectCount(mFormat.aspects);
mIsSubresourceContentInitializedAtIndex = std::vector<bool>(subresourceCount, false); mIsSubresourceContentInitializedAtIndex = std::vector<bool>(subresourceCount, false);
// Add readonly storage usage if the texture has a storage usage. The validation rules in // Add readonly storage usage if the texture has a storage usage. The validation rules in

View File

@ -178,13 +178,13 @@ namespace dawn_native { namespace d3d12 {
bufferCopy.offset = 0; bufferCopy.offset = 0;
bufferCopy.bytesPerRow = bytesPerRow; bufferCopy.bytesPerRow = bytesPerRow;
bufferCopy.rowsPerImage = rowsPerImage; bufferCopy.rowsPerImage = rowsPerImage;
CopyTextureToBufferWithCopySplit(recordingContext->GetCommandList(), srcCopy, Copy2DTextureToBufferWithCopySplit(recordingContext->GetCommandList(), srcCopy,
bufferCopy, srcTexture, tempBuffer.Get(), copySize); bufferCopy, srcTexture, tempBuffer.Get(), copySize);
// Copy from tempBuffer into destination texture // Copy from tempBuffer into destination texture
tempBuffer->TrackUsageAndTransitionNow(recordingContext, wgpu::BufferUsage::CopySrc); tempBuffer->TrackUsageAndTransitionNow(recordingContext, wgpu::BufferUsage::CopySrc);
Texture* dstTexture = ToBackend(dstCopy.texture).Get(); Texture* dstTexture = ToBackend(dstCopy.texture).Get();
CopyBufferToTextureWithCopySplit(recordingContext, dstCopy, CopyBufferTo2DTextureWithCopySplit(recordingContext, dstCopy,
tempBuffer->GetD3D12Resource(), 0, bytesPerRow, tempBuffer->GetD3D12Resource(), 0, bytesPerRow,
rowsPerImage, copySize, dstTexture, dstCopy.aspect); rowsPerImage, copySize, dstTexture, dstCopy.aspect);
@ -741,7 +741,7 @@ namespace dawn_native { namespace d3d12 {
DAWN_TRY(buffer->EnsureDataInitialized(commandContext)); DAWN_TRY(buffer->EnsureDataInitialized(commandContext));
ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D); ASSERT(texture->GetDimension() != wgpu::TextureDimension::e1D);
SubresourceRange subresources = SubresourceRange subresources =
GetSubresourcesAffectedByCopy(copy->destination, copy->copySize); GetSubresourcesAffectedByCopy(copy->destination, copy->copySize);
@ -756,11 +756,22 @@ namespace dawn_native { namespace d3d12 {
texture->TrackUsageAndTransitionNow(commandContext, wgpu::TextureUsage::CopyDst, texture->TrackUsageAndTransitionNow(commandContext, wgpu::TextureUsage::CopyDst,
subresources); subresources);
// compute the copySplits and record the CopyTextureRegion commands // Record the CopyTextureRegion commands for 3D textures. Multiple depths of 3D
CopyBufferToTextureWithCopySplit( // textures can be copied in one shot and copySplits are not needed.
commandContext, copy->destination, buffer->GetD3D12Resource(), if (texture->GetDimension() == wgpu::TextureDimension::e3D) {
copy->source.offset, copy->source.bytesPerRow, copy->source.rowsPerImage, CopyBufferTo3DTexture(commandContext, copy->destination,
buffer->GetD3D12Resource(), copy->source.offset,
copy->source.bytesPerRow, copy->source.rowsPerImage,
copy->copySize, texture, subresources.aspects); 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; break;
} }
@ -772,7 +783,7 @@ namespace dawn_native { namespace d3d12 {
DAWN_TRY(buffer->EnsureDataInitializedAsDestination(commandContext, copy)); DAWN_TRY(buffer->EnsureDataInitializedAsDestination(commandContext, copy));
ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D); ASSERT(texture->GetDimension() != wgpu::TextureDimension::e1D);
SubresourceRange subresources = SubresourceRange subresources =
GetSubresourcesAffectedByCopy(copy->source, copy->copySize); GetSubresourcesAffectedByCopy(copy->source, copy->copySize);
@ -782,8 +793,14 @@ namespace dawn_native { namespace d3d12 {
subresources); subresources);
buffer->TrackUsageAndTransitionNow(commandContext, wgpu::BufferUsage::CopyDst); buffer->TrackUsageAndTransitionNow(commandContext, wgpu::BufferUsage::CopyDst);
CopyTextureToBufferWithCopySplit(commandList, copy->source, copy->destination, if (texture->GetDimension() == wgpu::TextureDimension::e3D) {
texture, buffer, copy->copySize); Copy3DTextureToBuffer(commandList, copy->source, copy->destination, texture,
buffer, copy->copySize);
} else {
Copy2DTextureToBufferWithCopySplit(commandList, copy->source,
copy->destination, texture, buffer,
copy->copySize);
}
break; break;
} }

View File

@ -418,7 +418,7 @@ namespace dawn_native { namespace d3d12 {
texture->TrackUsageAndTransitionNow(commandContext, wgpu::TextureUsage::CopyDst, range); texture->TrackUsageAndTransitionNow(commandContext, wgpu::TextureUsage::CopyDst, range);
// compute the copySplits and record the CopyTextureRegion commands // compute the copySplits and record the CopyTextureRegion commands
CopyBufferToTextureWithCopySplit(commandContext, *dst, ToBackend(source)->GetResource(), CopyBufferTo2DTextureWithCopySplit(commandContext, *dst, ToBackend(source)->GetResource(),
src.offset, src.bytesPerRow, src.rowsPerImage, src.offset, src.bytesPerRow, src.rowsPerImage,
copySizePixels, texture, range.aspects); copySizePixels, texture, range.aspects);

View File

@ -209,7 +209,8 @@ namespace dawn_native { namespace d3d12 {
const TexelBlockInfo& blockInfo, const TexelBlockInfo& blockInfo,
uint64_t offset, uint64_t offset,
uint32_t bytesPerRow, uint32_t bytesPerRow,
uint32_t rowsPerImage) { uint32_t rowsPerImage,
bool is3DTexture) {
TextureCopySplits copies; TextureCopySplits copies;
const uint64_t bytesPerSlice = bytesPerRow * rowsPerImage; const uint64_t bytesPerSlice = bytesPerRow * rowsPerImage;
@ -225,15 +226,19 @@ namespace dawn_native { namespace d3d12 {
// slice. Moreover, if "rowsPerImage" is even, both the first and second copy layers can // 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 // share the same copy split, so in this situation we just need to compute copy split once
// and reuse it for all the slices. // and reuse it for all the slices.
const dawn_native::Extent3D copyOneLayerSize = {copySize.width, copySize.height, 1}; Extent3D copyOneLayerSize = copySize;
const dawn_native::Origin3D copyFirstLayerOrigin = {origin.x, origin.y, 0}; Origin3D copyFirstLayerOrigin = origin;
if (!is3DTexture) {
copyOneLayerSize.depthOrArrayLayers = 1;
copyFirstLayerOrigin.z = 0;
}
copies.copies2D[0] = ComputeTextureCopySplit(copyFirstLayerOrigin, copyOneLayerSize, copies.copies2D[0] = ComputeTextureCopySplit(copyFirstLayerOrigin, copyOneLayerSize,
blockInfo, offset, bytesPerRow, rowsPerImage); blockInfo, offset, bytesPerRow, rowsPerImage);
// When the copy only refers one texture 2D array layer copies.copies2D[1] will never be // When the copy only refers one texture 2D array layer or a 3D texture, copies.copies2D[1]
// used so we can safely early return here. // will never be used so we can safely early return here.
if (copySize.depthOrArrayLayers == 1) { if (copySize.depthOrArrayLayers == 1 || is3DTexture) {
return copies; return copies;
} }

View File

@ -61,7 +61,8 @@ namespace dawn_native { namespace d3d12 {
const TexelBlockInfo& blockInfo, const TexelBlockInfo& blockInfo,
uint64_t offset, uint64_t offset,
uint32_t bytesPerRow, uint32_t bytesPerRow,
uint32_t rowsPerImage); uint32_t rowsPerImage,
bool is3DTexture = false);
}} // namespace dawn_native::d3d12 }} // namespace dawn_native::d3d12
#endif // DAWNNATIVE_D3D12_TEXTURECOPYSPLITTER_H_ #endif // DAWNNATIVE_D3D12_TEXTURECOPYSPLITTER_H_

View File

@ -767,8 +767,7 @@ namespace dawn_native { namespace d3d12 {
const ExecutionSerial pendingCommandSerial = const ExecutionSerial pendingCommandSerial =
ToBackend(GetDevice())->GetPendingCommandSerial(); ToBackend(GetDevice())->GetPendingCommandSerial();
// This transitions assume it is a 2D texture ASSERT(GetDimension() != wgpu::TextureDimension::e1D);
ASSERT(GetDimension() == wgpu::TextureDimension::e2D);
mSubresourceStateAndDecay.Update( mSubresourceStateAndDecay.Update(
range, [&](const SubresourceRange& updateRange, StateAndDecay* state) { range, [&](const SubresourceRange& updateRange, StateAndDecay* state) {

View File

@ -174,7 +174,7 @@ namespace dawn_native { namespace d3d12 {
} }
} }
void CopyBufferToTextureWithCopySplit(CommandRecordingContext* commandContext, void CopyBufferTo2DTextureWithCopySplit(CommandRecordingContext* commandContext,
const TextureCopy& textureCopy, const TextureCopy& textureCopy,
ID3D12Resource* bufferResource, ID3D12Resource* bufferResource,
const uint64_t offset, const uint64_t offset,
@ -217,6 +217,26 @@ namespace dawn_native { namespace d3d12 {
} }
} }
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, void RecordCopyTextureToBufferFromTextureCopySplit(ID3D12GraphicsCommandList* commandList,
const Texture2DCopySplit& baseCopySplit, const Texture2DCopySplit& baseCopySplit,
Buffer* buffer, Buffer* buffer,
@ -249,12 +269,13 @@ namespace dawn_native { namespace d3d12 {
} }
} }
void CopyTextureToBufferWithCopySplit(ID3D12GraphicsCommandList* commandList, void Copy2DTextureToBufferWithCopySplit(ID3D12GraphicsCommandList* commandList,
const TextureCopy& textureCopy, const TextureCopy& textureCopy,
const BufferCopy& bufferCopy, const BufferCopy& bufferCopy,
Texture* texture, Texture* texture,
Buffer* buffer, Buffer* buffer,
const Extent3D& copySize) { const Extent3D& copySize) {
ASSERT(HasOneBit(textureCopy.aspect));
const TexelBlockInfo& blockInfo = const TexelBlockInfo& blockInfo =
texture->GetFormat().GetAspectInfo(textureCopy.aspect).block; texture->GetFormat().GetAspectInfo(textureCopy.aspect).block;
@ -290,4 +311,24 @@ namespace dawn_native { namespace d3d12 {
} }
} }
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 }} // namespace dawn_native::d3d12

View File

@ -54,7 +54,17 @@ namespace dawn_native { namespace d3d12 {
uint32_t textureSlice, uint32_t textureSlice,
Aspect aspect); Aspect aspect);
void CopyBufferToTextureWithCopySplit(CommandRecordingContext* commandContext, 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, const TextureCopy& textureCopy,
ID3D12Resource* bufferResource, ID3D12Resource* bufferResource,
const uint64_t offset, const uint64_t offset,
@ -74,7 +84,14 @@ namespace dawn_native { namespace d3d12 {
uint32_t textureSlice, uint32_t textureSlice,
Aspect aspect); Aspect aspect);
void CopyTextureToBufferWithCopySplit(ID3D12GraphicsCommandList* commandList, 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 TextureCopy& textureCopy,
const BufferCopy& bufferCopy, const BufferCopy& bufferCopy,
Texture* texture, Texture* texture,

View File

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

View File

@ -110,15 +110,20 @@ class CopyTests : public DawnTest {
const void* srcData, const void* srcData,
uint32_t widthInBlocks, uint32_t widthInBlocks,
uint32_t heightInBlocks, uint32_t heightInBlocks,
uint32_t depthInBlocks,
uint32_t srcBytesPerRow, uint32_t srcBytesPerRow,
void* dstData, void* dstData,
uint32_t dstBytesPerRow) { uint32_t dstBytesPerRow) {
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) { for (unsigned int y = 0; y < heightInBlocks; ++y) {
memcpy(static_cast<uint8_t*>(dstData) + y * dstBytesPerRow, memcpy(static_cast<uint8_t*>(dstData) + srcDepthOffset + y * dstBytesPerRow,
static_cast<const uint8_t*>(srcData) + y * srcBytesPerRow, static_cast<const uint8_t*>(srcData) + dstDepthOffset + y * srcBytesPerRow,
widthInBlocks * bytesPerTexelBlock); widthInBlocks * bytesPerTexelBlock);
} }
} }
}
}; };
class CopyTests_T2B : public CopyTests { class CopyTests_T2B : public CopyTests {
@ -198,7 +203,7 @@ class CopyTests_T2B : public CopyTests {
PackTextureData(bytesPerTexel, PackTextureData(bytesPerTexel,
textureArrayData.data() + expectedTexelArrayDataStartIndex, textureArrayData.data() + expectedTexelArrayDataStartIndex,
copySize.width, copySize.height, copyLayout.bytesPerRow, copySize.width, copySize.height, 1, copyLayout.bytesPerRow,
expected.data(), bufferSpec.bytesPerRow); expected.data(), bufferSpec.bytesPerRow);
EXPECT_BUFFER_U32_RANGE_EQ(reinterpret_cast<const uint32_t*>(expected.data()), buffer, 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, void DoTest(const TextureSpec& textureSpec,
const BufferSpec& bufferSpec, 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 // TODO(jiawei.shao@intel.com): support testing arbitrary formats
ASSERT_EQ(kDefaultFormat, textureSpec.format); ASSERT_EQ(kDefaultFormat, textureSpec.format);
// Create a buffer of size `size` and populate it with data // Create a buffer of size `size` and populate it with data
const uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(textureSpec.format); const uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(textureSpec.format);
std::vector<RGBA8> bufferData(bufferSpec.size / bytesPerTexel); 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. // Create a texture that is `width` x `height` with (`level` + 1) mip levels.
wgpu::TextureDescriptor descriptor; wgpu::TextureDescriptor descriptor;
descriptor.dimension = wgpu::TextureDimension::e2D; descriptor.dimension = dimension;
descriptor.size = textureSpec.textureSize; descriptor.size = textureSpec.textureSize;
descriptor.sampleCount = 1; descriptor.sampleCount = 1;
descriptor.format = textureSpec.format; descriptor.format = textureSpec.format;
@ -253,13 +258,6 @@ class CopyTests_B2T : public CopyTests {
wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); 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( wgpu::ImageCopyBuffer imageCopyBuffer = utils::CreateImageCopyBuffer(
buffer, bufferSpec.offset, bufferSpec.bytesPerRow, bufferSpec.rowsPerImage); buffer, bufferSpec.offset, bufferSpec.bytesPerRow, bufferSpec.rowsPerImage);
wgpu::ImageCopyTexture imageCopyTexture = wgpu::ImageCopyTexture imageCopyTexture =
@ -269,28 +267,41 @@ class CopyTests_B2T : public CopyTests {
wgpu::CommandBuffer commands = encoder.Finish(); wgpu::CommandBuffer commands = encoder.Finish();
queue.Submit(1, &commands); 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; uint64_t bufferOffset = bufferSpec.offset;
const uint32_t texelCountLastLayer = const uint32_t texelCountLastLayer =
copyLayout.texelBlocksPerRow * (copyLayout.mipSize.height - 1) + copyDepth * (copyLayout.texelBlocksPerRow * (copyLayout.mipSize.height - 1) +
copyLayout.mipSize.width; copyLayout.mipSize.width);
for (uint32_t slice = textureSpec.copyOrigin.z; slice < maxArrayLayer; ++slice) { 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 // Pack the data used to create the buffer in the specified copy region to have the same
// format as the expected texture data. // format as the expected texture data.
std::vector<RGBA8> expected(texelCountLastLayer); std::vector<RGBA8> expected(texelCountLastLayer);
PackTextureData(bytesPerTexel, bufferData.data() + bufferOffset / bytesPerTexel, PackTextureData(bytesPerTexel, bufferData.data() + bufferOffset / bytesPerTexel,
copySize.width, copySize.height, bufferSpec.bytesPerRow, copySize.width, copySize.height, copyDepth, bufferSpec.bytesPerRow,
expected.data(), copySize.width * bytesPerTexel); expected.data(), copySize.width * bytesPerTexel);
EXPECT_TEXTURE_RGBA8_EQ(expected.data(), texture, EXPECT_TEXTURE_RGBA8_EQ(
(textureSpec.copyOrigin.x, textureSpec.copyOrigin.y), expected.data(), texture,
(copySize.width, copySize.height), textureSpec.copyLevel, slice) (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 << "Buffer to Texture copy failed copying " << bufferSpec.size
<< "-byte buffer with offset " << bufferSpec.offset << " and bytes per row " << "-byte buffer with offset " << bufferSpec.offset << " and bytes per row "
<< bufferSpec.bytesPerRow << " to [(" << textureSpec.copyOrigin.x << ", " << bufferSpec.bytesPerRow << " to [(" << textureSpec.copyOrigin.x << ", "
<< textureSpec.copyOrigin.y << "), (" << textureSpec.copyOrigin.x + copySize.width << textureSpec.copyOrigin.y << "), (" << textureSpec.copyOrigin.x + copySize.width
<< ", " << textureSpec.copyOrigin.y + copySize.height << ")) region of " << ", " << textureSpec.copyOrigin.y + copySize.height << ")) region of "
<< textureSpec.textureSize.width << " x " << textureSpec.textureSize.height << 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; << std::endl;
bufferOffset += copyLayout.bytesPerImage; bufferOffset += copyLayout.bytesPerImage;
} }
@ -408,7 +419,7 @@ class CopyTests_T2T : public CopyTests {
bytesPerTexel; bytesPerTexel;
// Do the T2T "copy" on the CPU side to get the expected texel value at the // Do the T2T "copy" on the CPU side to get the expected texel value at the
PackTextureData(bytesPerTexel, &srcTextureCopyData[srcTexelDataOffset], PackTextureData(bytesPerTexel, &srcTextureCopyData[srcTexelDataOffset],
copySize.width, copySize.height, srcDataCopyLayout.bytesPerRow, copySize.width, copySize.height, 1, srcDataCopyLayout.bytesPerRow,
&expectedDstDataPerSlice[expectedDstDataOffset], &expectedDstDataPerSlice[expectedDstDataOffset],
dstDataCopyLayout.bytesPerRow); dstDataCopyLayout.bytesPerRow);
@ -918,6 +929,9 @@ TEST_P(CopyTests_T2B, Texture2DArrayRegionWithOffsetEvenRowsPerImage) {
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kCopyLayers}); 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, DAWN_INSTANTIATE_TEST(CopyTests_T2B,
D3D12Backend(), D3D12Backend(),
MetalBackend(), MetalBackend(),
@ -1355,6 +1369,26 @@ TEST_P(CopyTests_B2T, Texture2DArrayRegionWithOffsetEvenRowsPerImage) {
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kCopyLayers}); 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, DAWN_INSTANTIATE_TEST(CopyTests_B2T,
D3D12Backend(), D3D12Backend(),
MetalBackend(), 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( DAWN_INSTANTIATE_TEST(
CopyTests_T2T, CopyTests_T2T,
D3D12Backend(), D3D12Backend(),