Support multiple array layers in one texture-to-texture copy command
This patch adds the supports of copying multiple array layers of a 2D array texture in one texture-to-texture call. Note that in D3D12 and Metal it is implemented by copying each array layer in a for-loop. Note that we need extra validations when the source and destination texture are the same one in a texture-to-texture copy. This CL does not include these validations and we will add them in another one. BUG=dawn:18 TEST=dawn_unittests, dawn_end2end_tests Change-Id: I1239543e5692e140474b3c1de0b3579be449e283 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/22140 Commit-Queue: Jiawei Shao <jiawei.shao@intel.com> Reviewed-by: Austin Eng <enga@chromium.org>
This commit is contained in:
parent
f62ab75a5c
commit
a3636ed888
|
@ -37,6 +37,8 @@ namespace dawn_native {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
// TODO(jiawei.shao@intel.com): add validations on the texture-to-texture copies within the
|
||||||
|
// same texture.
|
||||||
MaybeError ValidateCopySizeFitsInTexture(const TextureCopyView& textureCopy,
|
MaybeError ValidateCopySizeFitsInTexture(const TextureCopyView& textureCopy,
|
||||||
const Extent3D& copySize) {
|
const Extent3D& copySize) {
|
||||||
const TextureBase* texture = textureCopy.texture;
|
const TextureBase* texture = textureCopy.texture;
|
||||||
|
@ -44,7 +46,9 @@ namespace dawn_native {
|
||||||
return DAWN_VALIDATION_ERROR("Copy mipLevel out of range");
|
return DAWN_VALIDATION_ERROR("Copy mipLevel out of range");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (textureCopy.arrayLayer >= texture->GetArrayLayers()) {
|
if (static_cast<uint64_t>(textureCopy.arrayLayer) +
|
||||||
|
static_cast<uint64_t>(copySize.depth) >
|
||||||
|
static_cast<uint64_t>(texture->GetArrayLayers())) {
|
||||||
return DAWN_VALIDATION_ERROR("Copy arrayLayer out of range");
|
return DAWN_VALIDATION_ERROR("Copy arrayLayer out of range");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,17 +56,18 @@ namespace dawn_native {
|
||||||
|
|
||||||
// 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 (uint64_t(textureCopy.origin.x) + uint64_t(copySize.width) >
|
if (static_cast<uint64_t>(textureCopy.origin.x) +
|
||||||
|
static_cast<uint64_t>(copySize.width) >
|
||||||
static_cast<uint64_t>(extent.width) ||
|
static_cast<uint64_t>(extent.width) ||
|
||||||
uint64_t(textureCopy.origin.y) + uint64_t(copySize.height) >
|
static_cast<uint64_t>(textureCopy.origin.y) +
|
||||||
|
static_cast<uint64_t>(copySize.height) >
|
||||||
static_cast<uint64_t>(extent.height)) {
|
static_cast<uint64_t>(extent.height)) {
|
||||||
return DAWN_VALIDATION_ERROR("Copy would touch outside of the texture");
|
return DAWN_VALIDATION_ERROR("Copy would touch outside of the texture");
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(cwallez@chromium.org): Check the depth bound differently for 2D arrays and 3D
|
// TODO(cwallez@chromium.org): Check the depth bound differently for 3D textures.
|
||||||
// textures
|
if (textureCopy.origin.z != 0) {
|
||||||
if (textureCopy.origin.z != 0 || copySize.depth > 1) {
|
return DAWN_VALIDATION_ERROR("No support for z != 0 for now");
|
||||||
return DAWN_VALIDATION_ERROR("No support for z != 0 and depth > 1 for now");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
|
|
@ -660,15 +660,17 @@ namespace dawn_native { namespace d3d12 {
|
||||||
Texture* destination = ToBackend(copy->destination.texture.Get());
|
Texture* destination = ToBackend(copy->destination.texture.Get());
|
||||||
|
|
||||||
source->EnsureSubresourceContentInitialized(
|
source->EnsureSubresourceContentInitialized(
|
||||||
commandContext, copy->source.mipLevel, 1, copy->source.arrayLayer, 1);
|
commandContext, copy->source.mipLevel, 1, copy->source.arrayLayer,
|
||||||
|
copy->copySize.depth);
|
||||||
if (IsCompleteSubresourceCopiedTo(destination, copy->copySize,
|
if (IsCompleteSubresourceCopiedTo(destination, copy->copySize,
|
||||||
copy->destination.mipLevel)) {
|
copy->destination.mipLevel)) {
|
||||||
destination->SetIsSubresourceContentInitialized(
|
destination->SetIsSubresourceContentInitialized(
|
||||||
true, copy->destination.mipLevel, 1, copy->destination.arrayLayer, 1);
|
true, copy->destination.mipLevel, 1, copy->destination.arrayLayer,
|
||||||
|
copy->copySize.depth);
|
||||||
} else {
|
} else {
|
||||||
destination->EnsureSubresourceContentInitialized(
|
destination->EnsureSubresourceContentInitialized(
|
||||||
commandContext, copy->destination.mipLevel, 1,
|
commandContext, copy->destination.mipLevel, 1,
|
||||||
copy->destination.arrayLayer, 1);
|
copy->destination.arrayLayer, copy->copySize.depth);
|
||||||
}
|
}
|
||||||
source->TrackUsageAndTransitionNow(commandContext, wgpu::TextureUsage::CopySrc);
|
source->TrackUsageAndTransitionNow(commandContext, wgpu::TextureUsage::CopySrc);
|
||||||
destination->TrackUsageAndTransitionNow(commandContext,
|
destination->TrackUsageAndTransitionNow(commandContext,
|
||||||
|
@ -678,21 +680,29 @@ namespace dawn_native { namespace d3d12 {
|
||||||
commandList->CopyResource(destination->GetD3D12Resource(),
|
commandList->CopyResource(destination->GetD3D12Resource(),
|
||||||
source->GetD3D12Resource());
|
source->GetD3D12Resource());
|
||||||
} else {
|
} else {
|
||||||
|
// TODO(jiawei.shao@intel.com): support copying with 1D and 3D textures.
|
||||||
|
ASSERT(source->GetDimension() == wgpu::TextureDimension::e2D &&
|
||||||
|
destination->GetDimension() == wgpu::TextureDimension::e2D);
|
||||||
|
const dawn_native::Extent3D copyExtentOneSlice = {
|
||||||
|
copy->copySize.width, copy->copySize.height, 1u};
|
||||||
|
for (uint32_t slice = 0; slice < copy->copySize.depth; ++slice) {
|
||||||
D3D12_TEXTURE_COPY_LOCATION srcLocation =
|
D3D12_TEXTURE_COPY_LOCATION srcLocation =
|
||||||
ComputeTextureCopyLocationForTexture(source, copy->source.mipLevel,
|
ComputeTextureCopyLocationForTexture(
|
||||||
copy->source.arrayLayer);
|
source, copy->source.mipLevel, copy->source.arrayLayer + slice);
|
||||||
|
|
||||||
D3D12_TEXTURE_COPY_LOCATION dstLocation =
|
D3D12_TEXTURE_COPY_LOCATION dstLocation =
|
||||||
ComputeTextureCopyLocationForTexture(destination,
|
ComputeTextureCopyLocationForTexture(
|
||||||
copy->destination.mipLevel,
|
destination, copy->destination.mipLevel,
|
||||||
copy->destination.arrayLayer);
|
copy->destination.arrayLayer + slice);
|
||||||
|
|
||||||
D3D12_BOX sourceRegion =
|
D3D12_BOX sourceRegion = ComputeD3D12BoxFromOffsetAndSize(
|
||||||
ComputeD3D12BoxFromOffsetAndSize(copy->source.origin, copy->copySize);
|
copy->source.origin, copyExtentOneSlice);
|
||||||
|
|
||||||
commandList->CopyTextureRegion(
|
commandList->CopyTextureRegion(&dstLocation, copy->destination.origin.x,
|
||||||
&dstLocation, copy->destination.origin.x, copy->destination.origin.y,
|
copy->destination.origin.y,
|
||||||
copy->destination.origin.z, &srcLocation, &sourceRegion);
|
copy->destination.origin.z, &srcLocation,
|
||||||
|
&sourceRegion);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -325,10 +325,6 @@ namespace dawn_native { namespace metal {
|
||||||
return MTLOriginMake(origin.x, origin.y, origin.z);
|
return MTLOriginMake(origin.x, origin.y, origin.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
MTLSize MakeMTLSize(Extent3D extent) {
|
|
||||||
return MTLSizeMake(extent.width, extent.height, extent.depth);
|
|
||||||
}
|
|
||||||
|
|
||||||
TextureBufferCopySplit ComputeTextureBufferCopySplit(Origin3D origin,
|
TextureBufferCopySplit ComputeTextureBufferCopySplit(Origin3D origin,
|
||||||
Extent3D copyExtent,
|
Extent3D copyExtent,
|
||||||
Format textureFormat,
|
Format textureFormat,
|
||||||
|
@ -445,19 +441,19 @@ namespace dawn_native { namespace metal {
|
||||||
void EnsureSourceTextureInitialized(Texture* texture,
|
void EnsureSourceTextureInitialized(Texture* texture,
|
||||||
const Extent3D& size,
|
const Extent3D& size,
|
||||||
const TextureCopy& src) {
|
const TextureCopy& src) {
|
||||||
// TODO(crbug.com/dawn/145): Specify multiple layers based on |size|
|
texture->EnsureSubresourceContentInitialized(src.mipLevel, 1, src.arrayLayer,
|
||||||
texture->EnsureSubresourceContentInitialized(src.mipLevel, 1, src.arrayLayer, 1);
|
size.depth);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EnsureDestinationTextureInitialized(Texture* texture,
|
void EnsureDestinationTextureInitialized(Texture* texture,
|
||||||
const Extent3D& size,
|
const Extent3D& size,
|
||||||
const TextureCopy& dst) {
|
const TextureCopy& dst) {
|
||||||
// TODO(crbug.com/dawn/145): Specify multiple layers based on |size|
|
|
||||||
if (IsCompleteSubresourceCopiedTo(texture, size, dst.mipLevel)) {
|
if (IsCompleteSubresourceCopiedTo(texture, size, dst.mipLevel)) {
|
||||||
texture->SetIsSubresourceContentInitialized(true, dst.mipLevel, 1, dst.arrayLayer,
|
texture->SetIsSubresourceContentInitialized(true, dst.mipLevel, 1, dst.arrayLayer,
|
||||||
1);
|
size.depth);
|
||||||
} else {
|
} else {
|
||||||
texture->EnsureSubresourceContentInitialized(dst.mipLevel, 1, dst.arrayLayer, 1);
|
texture->EnsureSubresourceContentInitialized(dst.mipLevel, 1, dst.arrayLayer,
|
||||||
|
size.depth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -809,16 +805,24 @@ namespace dawn_native { namespace metal {
|
||||||
EnsureDestinationTextureInitialized(dstTexture, copy->copySize,
|
EnsureDestinationTextureInitialized(dstTexture, copy->copySize,
|
||||||
copy->destination);
|
copy->destination);
|
||||||
|
|
||||||
|
// TODO(jiawei.shao@intel.com): support copies with 1D and 3D textures.
|
||||||
|
ASSERT(srcTexture->GetDimension() == wgpu::TextureDimension::e2D &&
|
||||||
|
dstTexture->GetDimension() == wgpu::TextureDimension::e2D);
|
||||||
|
const MTLSize mtlSizeOneLayer =
|
||||||
|
MTLSizeMake(copy->copySize.width, copy->copySize.height, 1);
|
||||||
|
for (uint32_t slice = 0; slice < copy->copySize.depth; ++slice) {
|
||||||
[commandContext->EnsureBlit()
|
[commandContext->EnsureBlit()
|
||||||
copyFromTexture:srcTexture->GetMTLTexture()
|
copyFromTexture:srcTexture->GetMTLTexture()
|
||||||
sourceSlice:copy->source.arrayLayer
|
sourceSlice:copy->source.arrayLayer + slice
|
||||||
sourceLevel:copy->source.mipLevel
|
sourceLevel:copy->source.mipLevel
|
||||||
sourceOrigin:MakeMTLOrigin(copy->source.origin)
|
sourceOrigin:MakeMTLOrigin(copy->source.origin)
|
||||||
sourceSize:MakeMTLSize(copy->copySize)
|
sourceSize:mtlSizeOneLayer
|
||||||
toTexture:dstTexture->GetMTLTexture()
|
toTexture:dstTexture->GetMTLTexture()
|
||||||
destinationSlice:copy->destination.arrayLayer
|
destinationSlice:copy->destination.arrayLayer + slice
|
||||||
destinationLevel:copy->destination.mipLevel
|
destinationLevel:copy->destination.mipLevel
|
||||||
destinationOrigin:MakeMTLOrigin(copy->destination.origin)];
|
destinationOrigin:MakeMTLOrigin(copy->destination.origin)];
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -623,17 +623,17 @@ namespace dawn_native { namespace opengl {
|
||||||
srcTexture->EnsureSubresourceContentInitialized(src.mipLevel, 1, src.arrayLayer,
|
srcTexture->EnsureSubresourceContentInitialized(src.mipLevel, 1, src.arrayLayer,
|
||||||
1);
|
1);
|
||||||
if (IsCompleteSubresourceCopiedTo(dstTexture, copySize, dst.mipLevel)) {
|
if (IsCompleteSubresourceCopiedTo(dstTexture, copySize, dst.mipLevel)) {
|
||||||
dstTexture->SetIsSubresourceContentInitialized(true, dst.mipLevel, 1,
|
dstTexture->SetIsSubresourceContentInitialized(
|
||||||
dst.arrayLayer, 1);
|
true, dst.mipLevel, 1, dst.arrayLayer, copy->copySize.depth);
|
||||||
} else {
|
} else {
|
||||||
dstTexture->EnsureSubresourceContentInitialized(dst.mipLevel, 1,
|
dstTexture->EnsureSubresourceContentInitialized(
|
||||||
dst.arrayLayer, 1);
|
dst.mipLevel, 1, dst.arrayLayer, copy->copySize.depth);
|
||||||
}
|
}
|
||||||
gl.CopyImageSubData(srcTexture->GetHandle(), srcTexture->GetGLTarget(),
|
gl.CopyImageSubData(srcTexture->GetHandle(), srcTexture->GetGLTarget(),
|
||||||
src.mipLevel, src.origin.x, src.origin.y, src.arrayLayer,
|
src.mipLevel, src.origin.x, src.origin.y, src.arrayLayer,
|
||||||
dstTexture->GetHandle(), dstTexture->GetGLTarget(),
|
dstTexture->GetHandle(), dstTexture->GetGLTarget(),
|
||||||
dst.mipLevel, dst.origin.x, dst.origin.y, dst.arrayLayer,
|
dst.mipLevel, dst.origin.x, dst.origin.y, dst.arrayLayer,
|
||||||
copySize.width, copySize.height, 1);
|
copySize.width, copySize.height, copy->copySize.depth);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,10 +64,13 @@ namespace dawn_native { namespace vulkan {
|
||||||
|
|
||||||
VkImageCopy region;
|
VkImageCopy region;
|
||||||
|
|
||||||
|
// TODO(jiawei.shao@intel.com): support 1D and 3D textures
|
||||||
|
ASSERT(srcTexture->GetDimension() == wgpu::TextureDimension::e2D &&
|
||||||
|
dstTexture->GetDimension() == wgpu::TextureDimension::e2D);
|
||||||
region.srcSubresource.aspectMask = srcTexture->GetVkAspectMask();
|
region.srcSubresource.aspectMask = srcTexture->GetVkAspectMask();
|
||||||
region.srcSubresource.mipLevel = srcCopy.mipLevel;
|
region.srcSubresource.mipLevel = srcCopy.mipLevel;
|
||||||
region.srcSubresource.baseArrayLayer = srcCopy.arrayLayer;
|
region.srcSubresource.baseArrayLayer = srcCopy.arrayLayer;
|
||||||
region.srcSubresource.layerCount = 1;
|
region.srcSubresource.layerCount = copySize.depth;
|
||||||
|
|
||||||
region.srcOffset.x = srcCopy.origin.x;
|
region.srcOffset.x = srcCopy.origin.x;
|
||||||
region.srcOffset.y = srcCopy.origin.y;
|
region.srcOffset.y = srcCopy.origin.y;
|
||||||
|
@ -76,7 +79,7 @@ namespace dawn_native { namespace vulkan {
|
||||||
region.dstSubresource.aspectMask = dstTexture->GetVkAspectMask();
|
region.dstSubresource.aspectMask = dstTexture->GetVkAspectMask();
|
||||||
region.dstSubresource.mipLevel = dstCopy.mipLevel;
|
region.dstSubresource.mipLevel = dstCopy.mipLevel;
|
||||||
region.dstSubresource.baseArrayLayer = dstCopy.arrayLayer;
|
region.dstSubresource.baseArrayLayer = dstCopy.arrayLayer;
|
||||||
region.dstSubresource.layerCount = 1;
|
region.dstSubresource.layerCount = copySize.depth;
|
||||||
|
|
||||||
region.dstOffset.x = dstCopy.origin.x;
|
region.dstOffset.x = dstCopy.origin.x;
|
||||||
region.dstOffset.y = dstCopy.origin.y;
|
region.dstOffset.y = dstCopy.origin.y;
|
||||||
|
@ -86,7 +89,7 @@ namespace dawn_native { namespace vulkan {
|
||||||
Extent3D imageExtent = ComputeTextureCopyExtent(dstCopy, copySize);
|
Extent3D imageExtent = ComputeTextureCopyExtent(dstCopy, copySize);
|
||||||
region.extent.width = imageExtent.width;
|
region.extent.width = imageExtent.width;
|
||||||
region.extent.height = imageExtent.height;
|
region.extent.height = imageExtent.height;
|
||||||
region.extent.depth = imageExtent.depth;
|
region.extent.depth = 1;
|
||||||
|
|
||||||
return region;
|
return region;
|
||||||
}
|
}
|
||||||
|
@ -495,20 +498,21 @@ namespace dawn_native { namespace vulkan {
|
||||||
if (IsCompleteSubresourceCopiedTo(dst.texture.Get(), copy->copySize,
|
if (IsCompleteSubresourceCopiedTo(dst.texture.Get(), copy->copySize,
|
||||||
dst.mipLevel)) {
|
dst.mipLevel)) {
|
||||||
// Since destination texture has been overwritten, it has been "initialized"
|
// Since destination texture has been overwritten, it has been "initialized"
|
||||||
dst.texture->SetIsSubresourceContentInitialized(true, dst.mipLevel, 1,
|
dst.texture->SetIsSubresourceContentInitialized(
|
||||||
dst.arrayLayer, 1);
|
true, dst.mipLevel, 1, dst.arrayLayer, copy->copySize.depth);
|
||||||
} else {
|
} else {
|
||||||
ToBackend(dst.texture)
|
ToBackend(dst.texture)
|
||||||
->EnsureSubresourceContentInitialized(recordingContext, dst.mipLevel, 1,
|
->EnsureSubresourceContentInitialized(recordingContext, dst.mipLevel, 1,
|
||||||
dst.arrayLayer, 1);
|
dst.arrayLayer,
|
||||||
|
copy->copySize.depth);
|
||||||
}
|
}
|
||||||
|
|
||||||
ToBackend(src.texture)
|
ToBackend(src.texture)
|
||||||
->TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopySrc,
|
->TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopySrc,
|
||||||
src.mipLevel, 1, src.arrayLayer, 1);
|
src.mipLevel, 1, src.arrayLayer, copy->copySize.depth);
|
||||||
ToBackend(dst.texture)
|
ToBackend(dst.texture)
|
||||||
->TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopyDst,
|
->TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopyDst,
|
||||||
dst.mipLevel, 1, dst.arrayLayer, 1);
|
dst.mipLevel, 1, dst.arrayLayer, copy->copySize.depth);
|
||||||
|
|
||||||
// In some situations we cannot do texture-to-texture copies with vkCmdCopyImage
|
// In some situations we cannot do texture-to-texture copies with vkCmdCopyImage
|
||||||
// because as Vulkan SPEC always validates image copies with the virtual size of
|
// because as Vulkan SPEC always validates image copies with the virtual size of
|
||||||
|
|
|
@ -281,11 +281,13 @@ class CopyTests_T2T : public CopyTests {
|
||||||
uint32_t y;
|
uint32_t y;
|
||||||
uint32_t level;
|
uint32_t level;
|
||||||
uint32_t arraySize = 1u;
|
uint32_t arraySize = 1u;
|
||||||
|
uint32_t baseArrayLayer = 0u;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CopySize {
|
struct CopySize {
|
||||||
uint32_t width;
|
uint32_t width;
|
||||||
uint32_t height;
|
uint32_t height;
|
||||||
|
uint32_t arrayLayerCount = 1u;
|
||||||
};
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -324,77 +326,56 @@ class CopyTests_T2T : public CopyTests {
|
||||||
uint32_t texelsPerRow = bytesPerRow / kBytesPerTexel;
|
uint32_t texelsPerRow = bytesPerRow / kBytesPerTexel;
|
||||||
uint32_t texelCountPerLayer = texelsPerRow * (height - 1) + width;
|
uint32_t texelCountPerLayer = texelsPerRow * (height - 1) + width;
|
||||||
|
|
||||||
std::vector<std::vector<RGBA8>> textureArrayData(srcSpec.arraySize);
|
// TODO(jiawei.shao@intel.com): support copying into multiple contiguous array layers in one
|
||||||
for (uint32_t slice = 0; slice < srcSpec.arraySize; ++slice) {
|
// copyBufferToTexture() call.
|
||||||
textureArrayData[slice].resize(texelCountPerLayer);
|
std::vector<std::vector<RGBA8>> textureArrayCopyData(copy.arrayLayerCount);
|
||||||
|
for (uint32_t slice = 0; slice < copy.arrayLayerCount; ++slice) {
|
||||||
|
textureArrayCopyData[slice].resize(texelCountPerLayer);
|
||||||
FillTextureData(width, height, bytesPerRow / kBytesPerTexel, slice,
|
FillTextureData(width, height, bytesPerRow / kBytesPerTexel, slice,
|
||||||
textureArrayData[slice].data());
|
textureArrayCopyData[slice].data());
|
||||||
|
|
||||||
wgpu::Buffer uploadBuffer = utils::CreateBufferFromData(
|
wgpu::Buffer uploadBuffer = utils::CreateBufferFromData(
|
||||||
device, textureArrayData[slice].data(),
|
device, textureArrayCopyData[slice].data(),
|
||||||
static_cast<uint32_t>(sizeof(RGBA8) * textureArrayData[slice].size()),
|
static_cast<uint32_t>(sizeof(RGBA8) * textureArrayCopyData[slice].size()),
|
||||||
wgpu::BufferUsage::CopySrc);
|
wgpu::BufferUsage::CopySrc);
|
||||||
wgpu::BufferCopyView bufferCopyView =
|
wgpu::BufferCopyView bufferCopyView =
|
||||||
utils::CreateBufferCopyView(uploadBuffer, 0, bytesPerRow, 0);
|
utils::CreateBufferCopyView(uploadBuffer, 0, bytesPerRow, 0);
|
||||||
wgpu::TextureCopyView textureCopyView =
|
wgpu::TextureCopyView textureCopyView = utils::CreateTextureCopyView(
|
||||||
utils::CreateTextureCopyView(srcTexture, srcSpec.level, slice, {0, 0, 0});
|
srcTexture, srcSpec.level, srcSpec.baseArrayLayer + slice, {0, 0, 0});
|
||||||
wgpu::Extent3D bufferCopySize = {width, height, 1};
|
wgpu::Extent3D bufferCopySize = {width, height, 1};
|
||||||
|
|
||||||
encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, &bufferCopySize);
|
encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, &bufferCopySize);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create an upload buffer filled with empty data and use it to populate the `level` mip of
|
|
||||||
// the texture. Note: Prepopulating the texture with empty data ensures that there is not
|
|
||||||
// random data in the expectation and helps ensure that the padding due to the bytes per row
|
|
||||||
// is not modified by the copy
|
|
||||||
{
|
|
||||||
uint32_t dstWidth = dstSpec.width >> dstSpec.level;
|
|
||||||
uint32_t dstHeight = dstSpec.height >> dstSpec.level;
|
|
||||||
uint32_t dstRowPitch = Align(kBytesPerTexel * dstWidth, kTextureBytesPerRowAlignment);
|
|
||||||
uint32_t dstTexelsPerRow = dstRowPitch / kBytesPerTexel;
|
|
||||||
uint32_t dstTexelCount = dstTexelsPerRow * (dstHeight - 1) + dstWidth;
|
|
||||||
|
|
||||||
std::vector<RGBA8> emptyData(dstTexelCount);
|
|
||||||
wgpu::Buffer uploadBuffer = utils::CreateBufferFromData(
|
|
||||||
device, emptyData.data(), static_cast<uint32_t>(sizeof(RGBA8) * emptyData.size()),
|
|
||||||
wgpu::BufferUsage::CopySrc);
|
|
||||||
wgpu::BufferCopyView bufferCopyView =
|
|
||||||
utils::CreateBufferCopyView(uploadBuffer, 0, dstRowPitch, 0);
|
|
||||||
wgpu::TextureCopyView textureCopyView =
|
|
||||||
utils::CreateTextureCopyView(dstTexture, dstSpec.level, 0, {0, 0, 0});
|
|
||||||
wgpu::Extent3D dstCopySize = {dstWidth, dstHeight, 1};
|
|
||||||
encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, &dstCopySize);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Perform the texture to texture copy
|
// Perform the texture to texture copy
|
||||||
for (uint32_t slice = 0; slice < srcSpec.arraySize; ++slice) {
|
|
||||||
wgpu::TextureCopyView srcTextureCopyView = utils::CreateTextureCopyView(
|
wgpu::TextureCopyView srcTextureCopyView = utils::CreateTextureCopyView(
|
||||||
srcTexture, srcSpec.level, slice, {srcSpec.x, srcSpec.y, 0});
|
srcTexture, srcSpec.level, srcSpec.baseArrayLayer, {srcSpec.x, srcSpec.y, 0});
|
||||||
wgpu::TextureCopyView dstTextureCopyView = utils::CreateTextureCopyView(
|
wgpu::TextureCopyView dstTextureCopyView = utils::CreateTextureCopyView(
|
||||||
dstTexture, dstSpec.level, slice, {dstSpec.x, dstSpec.y, 0});
|
dstTexture, dstSpec.level, dstSpec.baseArrayLayer, {dstSpec.x, dstSpec.y, 0});
|
||||||
wgpu::Extent3D copySize = {copy.width, copy.height, 1};
|
wgpu::Extent3D copySize = {copy.width, copy.height, copy.arrayLayerCount};
|
||||||
encoder.CopyTextureToTexture(&srcTextureCopyView, &dstTextureCopyView, ©Size);
|
encoder.CopyTextureToTexture(&srcTextureCopyView, &dstTextureCopyView, ©Size);
|
||||||
}
|
|
||||||
|
|
||||||
wgpu::CommandBuffer commands = encoder.Finish();
|
wgpu::CommandBuffer commands = encoder.Finish();
|
||||||
queue.Submit(1, &commands);
|
queue.Submit(1, &commands);
|
||||||
|
|
||||||
std::vector<RGBA8> expected(bytesPerRow / kBytesPerTexel * (copy.height - 1) + copy.width);
|
std::vector<RGBA8> expected(bytesPerRow / kBytesPerTexel * (copy.height - 1) + copy.width);
|
||||||
for (uint32_t slice = 0; slice < srcSpec.arraySize; ++slice) {
|
for (uint32_t slice = 0; slice < copy.arrayLayerCount; ++slice) {
|
||||||
std::fill(expected.begin(), expected.end(), RGBA8());
|
std::fill(expected.begin(), expected.end(), RGBA8());
|
||||||
PackTextureData(
|
PackTextureData(&textureArrayCopyData[slice][srcSpec.x + srcSpec.y * (bytesPerRow /
|
||||||
&textureArrayData[slice][srcSpec.x + srcSpec.y * (bytesPerRow / kBytesPerTexel)],
|
kBytesPerTexel)],
|
||||||
copy.width, copy.height, texelsPerRow, expected.data(), copy.width);
|
copy.width, copy.height, texelsPerRow, expected.data(), copy.width);
|
||||||
|
|
||||||
EXPECT_TEXTURE_RGBA8_EQ(expected.data(), dstTexture, dstSpec.x, dstSpec.y, copy.width,
|
EXPECT_TEXTURE_RGBA8_EQ(expected.data(), dstTexture, dstSpec.x, dstSpec.y, copy.width,
|
||||||
copy.height, dstSpec.level, slice)
|
copy.height, dstSpec.level, dstSpec.baseArrayLayer + slice)
|
||||||
<< "Texture to Texture copy failed copying region [(" << srcSpec.x << ", "
|
<< "Texture to Texture copy failed copying region [(" << srcSpec.x << ", "
|
||||||
<< srcSpec.y << "), (" << srcSpec.x + copy.width << ", " << srcSpec.y + copy.height
|
<< srcSpec.y << "), (" << srcSpec.x + copy.width << ", " << srcSpec.y + copy.height
|
||||||
<< ")) from " << srcSpec.width << " x " << srcSpec.height
|
<< ")) from " << srcSpec.width << " x " << srcSpec.height
|
||||||
<< " texture at mip level " << srcSpec.level << " layer " << slice << " to [("
|
<< " texture at mip level " << srcSpec.level << " layer "
|
||||||
<< dstSpec.x << ", " << dstSpec.y << "), (" << dstSpec.x + copy.width << ", "
|
<< srcSpec.baseArrayLayer + slice << " to [(" << dstSpec.x << ", " << dstSpec.y
|
||||||
<< dstSpec.y + copy.height << ")) region of " << dstSpec.width << " x "
|
<< "), (" << dstSpec.x + copy.width << ", " << dstSpec.y + copy.height
|
||||||
<< dstSpec.height << " texture at mip level " << dstSpec.level << std::endl;
|
<< ")) region of " << dstSpec.width << " x " << dstSpec.height
|
||||||
|
<< " texture at mip level " << dstSpec.level << " layer "
|
||||||
|
<< dstSpec.baseArrayLayer + slice << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -723,26 +704,61 @@ TEST_P(CopyTests_T2T, TextureRegion) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test copying the whole 2D array texture.
|
||||||
TEST_P(CopyTests_T2T, Texture2DArray) {
|
TEST_P(CopyTests_T2T, Texture2DArray) {
|
||||||
|
// TODO(jiawei.shao@intel.com): investigate why this test fails with swiftshader.
|
||||||
|
DAWN_SKIP_TEST_IF(IsSwiftshader());
|
||||||
|
|
||||||
constexpr uint32_t kWidth = 256;
|
constexpr uint32_t kWidth = 256;
|
||||||
constexpr uint32_t kHeight = 128;
|
constexpr uint32_t kHeight = 128;
|
||||||
constexpr uint32_t kLayers = 6u;
|
constexpr uint32_t kLayers = 6u;
|
||||||
DoTest({kWidth, kHeight, 0, 0, 0, kLayers}, {kWidth, kHeight, 0, 0, 0, kLayers},
|
DoTest({kWidth, kHeight, 0, 0, 0, kLayers}, {kWidth, kHeight, 0, 0, 0, kLayers},
|
||||||
{kWidth, kHeight});
|
{kWidth, kHeight, kLayers});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test copying a subresource region of the 2D array texture.
|
||||||
TEST_P(CopyTests_T2T, Texture2DArrayRegion) {
|
TEST_P(CopyTests_T2T, Texture2DArrayRegion) {
|
||||||
|
// TODO(jiawei.shao@intel.com): investigate why this test fails with swiftshader.
|
||||||
|
DAWN_SKIP_TEST_IF(IsSwiftshader());
|
||||||
|
|
||||||
constexpr uint32_t kWidth = 256;
|
constexpr uint32_t kWidth = 256;
|
||||||
constexpr uint32_t kHeight = 128;
|
constexpr uint32_t kHeight = 128;
|
||||||
constexpr uint32_t kLayers = 6u;
|
constexpr uint32_t kLayers = 6u;
|
||||||
for (unsigned int w : {64, 128, 256}) {
|
for (unsigned int w : {64, 128, 256}) {
|
||||||
for (unsigned int h : {16, 32, 48}) {
|
for (unsigned int h : {16, 32, 48}) {
|
||||||
DoTest({kWidth, kHeight, 0, 0, 0, kLayers}, {kWidth, kHeight, 0, 0, 0, kLayers},
|
DoTest({kWidth, kHeight, 0, 0, 0, kLayers}, {kWidth, kHeight, 0, 0, 0, kLayers},
|
||||||
{w, h});
|
{w, h, kLayers});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test copying one slice of a 2D array texture.
|
||||||
|
TEST_P(CopyTests_T2T, Texture2DArrayCopyOneSlice) {
|
||||||
|
constexpr uint32_t kWidth = 256;
|
||||||
|
constexpr uint32_t kHeight = 128;
|
||||||
|
constexpr uint32_t kLayers = 6u;
|
||||||
|
constexpr uint32_t kSrcBaseLayer = 1u;
|
||||||
|
constexpr uint32_t kDstBaseLayer = 3u;
|
||||||
|
DoTest({kWidth, kHeight, 0, 0, 0, kLayers, kSrcBaseLayer},
|
||||||
|
{kWidth, kHeight, 0, 0, 0, kLayers, kDstBaseLayer}, {kWidth, kHeight, 1u});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test copying multiple contiguous slices of a 2D array texture.
|
||||||
|
TEST_P(CopyTests_T2T, Texture2DArrayCopyMultipleSlices) {
|
||||||
|
// TODO(jiawei.shao@intel.com): investigate why this test fails with swiftshader.
|
||||||
|
DAWN_SKIP_TEST_IF(IsSwiftshader());
|
||||||
|
|
||||||
|
constexpr uint32_t kWidth = 256;
|
||||||
|
constexpr uint32_t kHeight = 128;
|
||||||
|
constexpr uint32_t kLayers = 6u;
|
||||||
|
constexpr uint32_t kSrcBaseLayer = 0u;
|
||||||
|
constexpr uint32_t kDstBaseLayer = 3u;
|
||||||
|
constexpr uint32_t kCopyArrayLayerCount = 3u;
|
||||||
|
DoTest({kWidth, kHeight, 0, 0, 0, kLayers, kSrcBaseLayer},
|
||||||
|
{kWidth, kHeight, 0, 0, 0, kLayers, kDstBaseLayer},
|
||||||
|
{kWidth, kHeight, kCopyArrayLayerCount});
|
||||||
|
}
|
||||||
|
|
||||||
TEST_P(CopyTests_T2T, TextureMip) {
|
TEST_P(CopyTests_T2T, TextureMip) {
|
||||||
constexpr uint32_t kWidth = 256;
|
constexpr uint32_t kWidth = 256;
|
||||||
constexpr uint32_t kHeight = 128;
|
constexpr uint32_t kHeight = 128;
|
||||||
|
|
|
@ -990,9 +990,9 @@ class CopyCommandTest_T2T : public CopyCommandTest {};
|
||||||
|
|
||||||
TEST_F(CopyCommandTest_T2T, Success) {
|
TEST_F(CopyCommandTest_T2T, Success) {
|
||||||
wgpu::Texture source =
|
wgpu::Texture source =
|
||||||
Create2DTexture(16, 16, 5, 2, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc);
|
Create2DTexture(16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc);
|
||||||
wgpu::Texture destination =
|
wgpu::Texture destination =
|
||||||
Create2DTexture(16, 16, 5, 2, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst);
|
Create2DTexture(16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst);
|
||||||
|
|
||||||
// Different copies, including some that touch the OOB condition
|
// Different copies, including some that touch the OOB condition
|
||||||
{
|
{
|
||||||
|
@ -1019,6 +1019,16 @@ TEST_F(CopyCommandTest_T2T, Success) {
|
||||||
// Copy between slices
|
// Copy between slices
|
||||||
TestT2TCopy(utils::Expectation::Success, source, 0, 1, {0, 0, 0}, destination, 0, 1,
|
TestT2TCopy(utils::Expectation::Success, source, 0, 1, {0, 0, 0}, destination, 0, 1,
|
||||||
{0, 0, 0}, {16, 16, 1});
|
{0, 0, 0}, {16, 16, 1});
|
||||||
|
|
||||||
|
// Copy multiple slices (srcTextureCopyView.arrayLayer + copySize.depth ==
|
||||||
|
// srcTextureCopyView.texture.arrayLayerCount)
|
||||||
|
TestT2TCopy(utils::Expectation::Success, source, 0, 2, {0, 0, 0}, destination, 0, 0,
|
||||||
|
{0, 0, 0}, {16, 16, 2});
|
||||||
|
|
||||||
|
// Copy multiple slices (dstTextureCopyView.arrayLayer + copySize.depth ==
|
||||||
|
// dstTextureCopyView.texture.arrayLayerCount)
|
||||||
|
TestT2TCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, destination, 0, 2,
|
||||||
|
{0, 0, 0}, {16, 16, 2});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Empty copies are valid
|
// Empty copies are valid
|
||||||
|
@ -1058,9 +1068,9 @@ TEST_F(CopyCommandTest_T2T, IncorrectUsage) {
|
||||||
|
|
||||||
TEST_F(CopyCommandTest_T2T, OutOfBounds) {
|
TEST_F(CopyCommandTest_T2T, OutOfBounds) {
|
||||||
wgpu::Texture source =
|
wgpu::Texture source =
|
||||||
Create2DTexture(16, 16, 5, 2, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc);
|
Create2DTexture(16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc);
|
||||||
wgpu::Texture destination =
|
wgpu::Texture destination =
|
||||||
Create2DTexture(16, 16, 5, 2, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst);
|
Create2DTexture(16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst);
|
||||||
|
|
||||||
// OOB on source
|
// OOB on source
|
||||||
{
|
{
|
||||||
|
@ -1076,12 +1086,16 @@ TEST_F(CopyCommandTest_T2T, OutOfBounds) {
|
||||||
TestT2TCopy(utils::Expectation::Failure, source, 1, 0, {0, 0, 0}, destination, 0, 0,
|
TestT2TCopy(utils::Expectation::Failure, source, 1, 0, {0, 0, 0}, destination, 0, 0,
|
||||||
{0, 0, 0}, {9, 9, 1});
|
{0, 0, 0}, {9, 9, 1});
|
||||||
|
|
||||||
|
// arrayLayer + depth OOB
|
||||||
|
TestT2TCopy(utils::Expectation::Failure, source, 0, 3, {0, 0, 0}, destination, 0, 0,
|
||||||
|
{0, 0, 0}, {16, 16, 2});
|
||||||
|
|
||||||
// empty copy on non-existent mip fails
|
// empty copy on non-existent mip fails
|
||||||
TestT2TCopy(utils::Expectation::Failure, source, 6, 0, {0, 0, 0}, destination, 0, 0,
|
TestT2TCopy(utils::Expectation::Failure, source, 6, 0, {0, 0, 0}, destination, 0, 0,
|
||||||
{0, 0, 0}, {0, 0, 1});
|
{0, 0, 0}, {0, 0, 1});
|
||||||
|
|
||||||
// empty copy from non-existent slice fails
|
// empty copy from non-existent slice fails
|
||||||
TestT2TCopy(utils::Expectation::Failure, source, 0, 2, {0, 0, 0}, destination, 0, 0,
|
TestT2TCopy(utils::Expectation::Failure, source, 0, 4, {0, 0, 0}, destination, 0, 0,
|
||||||
{0, 0, 0}, {0, 0, 1});
|
{0, 0, 0}, {0, 0, 1});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1099,12 +1113,16 @@ TEST_F(CopyCommandTest_T2T, OutOfBounds) {
|
||||||
TestT2TCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 1, 0,
|
TestT2TCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 1, 0,
|
||||||
{0, 0, 0}, {9, 9, 1});
|
{0, 0, 0}, {9, 9, 1});
|
||||||
|
|
||||||
|
// arrayLayer + depth OOB
|
||||||
|
TestT2TCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 0, 3,
|
||||||
|
{0, 0, 0}, {16, 16, 2});
|
||||||
|
|
||||||
// empty copy on non-existent mip fails
|
// empty copy on non-existent mip fails
|
||||||
TestT2TCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 6, 0,
|
TestT2TCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 6, 0,
|
||||||
{0, 0, 0}, {0, 0, 1});
|
{0, 0, 0}, {0, 0, 1});
|
||||||
|
|
||||||
// empty copy on non-existent slice fails
|
// empty copy on non-existent slice fails
|
||||||
TestT2TCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 0, 2,
|
TestT2TCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 0, 4,
|
||||||
{0, 0, 0}, {0, 0, 1});
|
{0, 0, 0}, {0, 0, 1});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1122,10 +1140,6 @@ TEST_F(CopyCommandTest_T2T, 2DTextureDepthConstraints) {
|
||||||
// Empty copy on destination with z > 0 fails
|
// Empty copy on destination with z > 0 fails
|
||||||
TestT2TCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 0, 0, {0, 0, 1},
|
TestT2TCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 0, 0, {0, 0, 1},
|
||||||
{0, 0, 1});
|
{0, 0, 1});
|
||||||
|
|
||||||
// Empty copy with depth > 1 fails
|
|
||||||
TestT2TCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 0, 0, {0, 0, 0},
|
|
||||||
{0, 0, 2});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CopyCommandTest_T2T, 2DTextureDepthStencil) {
|
TEST_F(CopyCommandTest_T2T, 2DTextureDepthStencil) {
|
||||||
|
|
Loading…
Reference in New Issue