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:
parent
61fbb28547
commit
3e8f3f9609
|
@ -66,11 +66,16 @@ namespace dawn_native {
|
|||
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 @@ namespace dawn_native {
|
|||
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();
|
||||
}
|
||||
|
|
|
@ -648,7 +648,7 @@ namespace dawn_native {
|
|||
// 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 @@ namespace dawn_native {
|
|||
// 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 @@ namespace dawn_native {
|
|||
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));
|
||||
|
|
|
@ -308,19 +308,29 @@ namespace dawn_native {
|
|||
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) >
|
||||
|
|
|
@ -48,7 +48,8 @@ namespace dawn_native {
|
|||
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);
|
||||
|
|
|
@ -208,8 +208,8 @@ namespace dawn_native {
|
|||
|
||||
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));
|
||||
|
|
|
@ -528,7 +528,7 @@ namespace dawn_native {
|
|||
// 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;
|
||||
|
|
|
@ -293,7 +293,7 @@ namespace dawn_native {
|
|||
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 @@ namespace dawn_native {
|
|||
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
|
||||
|
|
|
@ -178,15 +178,15 @@ namespace dawn_native { namespace d3d12 {
|
|||
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 @@ namespace dawn_native { namespace d3d12 {
|
|||
|
||||
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 @@ namespace dawn_native { namespace d3d12 {
|
|||
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 @@ namespace dawn_native { namespace d3d12 {
|
|||
|
||||
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 @@ namespace dawn_native { namespace d3d12 {
|
|||
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;
|
||||
}
|
||||
|
|
|
@ -418,9 +418,9 @@ namespace dawn_native { namespace d3d12 {
|
|||
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 {};
|
||||
}
|
||||
|
|
|
@ -209,7 +209,8 @@ namespace dawn_native { namespace d3d12 {
|
|||
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 @@ namespace dawn_native { namespace d3d12 {
|
|||
// 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -61,7 +61,8 @@ namespace dawn_native { namespace d3d12 {
|
|||
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_
|
||||
|
|
|
@ -767,8 +767,7 @@ namespace dawn_native { namespace d3d12 {
|
|||
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) {
|
||||
|
|
|
@ -174,15 +174,15 @@ namespace dawn_native { namespace d3d12 {
|
|||
}
|
||||
}
|
||||
|
||||
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 @@ 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,
|
||||
const Texture2DCopySplit& baseCopySplit,
|
||||
Buffer* buffer,
|
||||
|
@ -249,12 +269,13 @@ namespace dawn_native { namespace d3d12 {
|
|||
}
|
||||
}
|
||||
|
||||
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 @@ 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
|
||||
|
|
|
@ -54,15 +54,25 @@ namespace dawn_native { namespace d3d12 {
|
|||
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 @@ namespace dawn_native { namespace d3d12 {
|
|||
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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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(),
|
||||
|
|
Loading…
Reference in New Issue