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) {
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();
}

View File

@ -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));

View File

@ -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) >

View File

@ -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);

View File

@ -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));

View File

@ -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;

View File

@ -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

View File

@ -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;
}

View File

@ -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 {};
}

View File

@ -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;
}

View File

@ -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_

View File

@ -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) {

View File

@ -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

View File

@ -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

View File

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

View File

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