mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-05-15 03:41:34 +00:00
Implement Texture-to-Texture Copies
Implement texture-to-texture copies for D3D12, Vulkan, and Metal. Includes end2end and unit tests. Bug: dawn:18 Change-Id: Ib48453704599bee43a76af21e6164aa9b8db7075 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/5620 Reviewed-by: Corentin Wallez <cwallez@chromium.org> Commit-Queue: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
parent
041aca1620
commit
d3d3aa03e1
@ -319,6 +319,14 @@
|
|||||||
{"name": "destination", "type": "buffer copy view", "annotation": "const*"},
|
{"name": "destination", "type": "buffer copy view", "annotation": "const*"},
|
||||||
{"name": "copy size", "type": "extent 3D", "annotation": "const*"}
|
{"name": "copy size", "type": "extent 3D", "annotation": "const*"}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "copy texture to texture",
|
||||||
|
"args": [
|
||||||
|
{"name": "source", "type": "texture copy view", "annotation": "const*"},
|
||||||
|
{"name": "destination", "type": "texture copy view", "annotation": "const*"},
|
||||||
|
{"name": "copy size", "type": "extent 3D", "annotation": "const*"}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -141,6 +141,49 @@ namespace dawn_native {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MaybeError ValidateEntireSubresourceCopied(const TextureCopy& src,
|
||||||
|
const TextureCopy& dst,
|
||||||
|
const Extent3D& copySize) {
|
||||||
|
Extent3D srcSize = src.texture.Get()->GetSize();
|
||||||
|
|
||||||
|
if (dst.origin.x != 0 || dst.origin.y != 0 || dst.origin.z != 0 ||
|
||||||
|
srcSize.width != copySize.width || srcSize.height != copySize.height ||
|
||||||
|
srcSize.depth != copySize.depth) {
|
||||||
|
return DAWN_VALIDATION_ERROR(
|
||||||
|
"The entire subresource must be copied when using a depth/stencil texture or "
|
||||||
|
"when samples are greater than 1.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
MaybeError ValidateTextureToTextureCopyRestrictions(const TextureCopy& src,
|
||||||
|
const TextureCopy& dst,
|
||||||
|
const Extent3D& copySize) {
|
||||||
|
const uint32_t srcSamples = src.texture.Get()->GetSampleCount();
|
||||||
|
const uint32_t dstSamples = dst.texture.Get()->GetSampleCount();
|
||||||
|
|
||||||
|
if (srcSamples != dstSamples) {
|
||||||
|
return DAWN_VALIDATION_ERROR(
|
||||||
|
"Source and destination textures must have matching sample counts.");
|
||||||
|
} else if (srcSamples > 1) {
|
||||||
|
// D3D12 requires entire subresource to be copied when using CopyTextureRegion when
|
||||||
|
// samples > 1.
|
||||||
|
DAWN_TRY(ValidateEntireSubresourceCopied(src, dst, copySize));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (src.texture.Get()->GetFormat() != dst.texture.Get()->GetFormat()) {
|
||||||
|
// Metal requires texture-to-texture copies be the same format
|
||||||
|
return DAWN_VALIDATION_ERROR("Source and destination texture formats must match.");
|
||||||
|
} else if (TextureFormatHasDepthOrStencil(src.texture.Get()->GetFormat())) {
|
||||||
|
// D3D12 requires entire subresource to be copied when using CopyTextureRegion is
|
||||||
|
// used with depth/stencil.
|
||||||
|
DAWN_TRY(ValidateEntireSubresourceCopied(src, dst, copySize));
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
MaybeError ComputeTextureCopyBufferSize(const Extent3D& copySize,
|
MaybeError ComputeTextureCopyBufferSize(const Extent3D& copySize,
|
||||||
uint32_t rowPitch,
|
uint32_t rowPitch,
|
||||||
uint32_t imageHeight,
|
uint32_t imageHeight,
|
||||||
@ -734,6 +777,35 @@ namespace dawn_native {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CommandEncoderBase::CopyTextureToTexture(const TextureCopyView* source,
|
||||||
|
const TextureCopyView* destination,
|
||||||
|
const Extent3D* copySize) {
|
||||||
|
if (ConsumedError(ValidateCanRecordTopLevelCommands())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ConsumedError(GetDevice()->ValidateObject(source->texture))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ConsumedError(GetDevice()->ValidateObject(destination->texture))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CopyTextureToTextureCmd* copy =
|
||||||
|
mAllocator.Allocate<CopyTextureToTextureCmd>(Command::CopyTextureToTexture);
|
||||||
|
new (copy) CopyTextureToTextureCmd;
|
||||||
|
copy->source.texture = source->texture;
|
||||||
|
copy->source.origin = source->origin;
|
||||||
|
copy->source.level = source->level;
|
||||||
|
copy->source.slice = source->slice;
|
||||||
|
copy->destination.texture = destination->texture;
|
||||||
|
copy->destination.origin = destination->origin;
|
||||||
|
copy->destination.level = destination->level;
|
||||||
|
copy->destination.slice = destination->slice;
|
||||||
|
copy->copySize = *copySize;
|
||||||
|
}
|
||||||
|
|
||||||
CommandBufferBase* CommandEncoderBase::Finish() {
|
CommandBufferBase* CommandEncoderBase::Finish() {
|
||||||
if (GetDevice()->ConsumedError(ValidateFinish())) {
|
if (GetDevice()->ConsumedError(ValidateFinish())) {
|
||||||
return CommandBufferBase::MakeError(GetDevice());
|
return CommandBufferBase::MakeError(GetDevice());
|
||||||
@ -870,6 +942,25 @@ namespace dawn_native {
|
|||||||
mResourceUsages.topLevelBuffers.insert(copy->destination.buffer.Get());
|
mResourceUsages.topLevelBuffers.insert(copy->destination.buffer.Get());
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case Command::CopyTextureToTexture: {
|
||||||
|
CopyTextureToTextureCmd* copy =
|
||||||
|
mIterator.NextCommand<CopyTextureToTextureCmd>();
|
||||||
|
|
||||||
|
DAWN_TRY(ValidateTextureToTextureCopyRestrictions(
|
||||||
|
copy->source, copy->destination, copy->copySize));
|
||||||
|
|
||||||
|
DAWN_TRY(ValidateCopySizeFitsInTexture(copy->source, copy->copySize));
|
||||||
|
DAWN_TRY(ValidateCopySizeFitsInTexture(copy->destination, copy->copySize));
|
||||||
|
|
||||||
|
DAWN_TRY(ValidateCanUseAs(copy->source.texture.Get(),
|
||||||
|
dawn::TextureUsageBit::TransferSrc));
|
||||||
|
DAWN_TRY(ValidateCanUseAs(copy->destination.texture.Get(),
|
||||||
|
dawn::TextureUsageBit::TransferDst));
|
||||||
|
|
||||||
|
mResourceUsages.topLevelTextures.insert(copy->source.texture.Get());
|
||||||
|
mResourceUsages.topLevelTextures.insert(copy->destination.texture.Get());
|
||||||
|
} break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return DAWN_VALIDATION_ERROR("Command disallowed outside of a pass");
|
return DAWN_VALIDATION_ERROR("Command disallowed outside of a pass");
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,9 @@ namespace dawn_native {
|
|||||||
void CopyTextureToBuffer(const TextureCopyView* source,
|
void CopyTextureToBuffer(const TextureCopyView* source,
|
||||||
const BufferCopyView* destination,
|
const BufferCopyView* destination,
|
||||||
const Extent3D* copySize);
|
const Extent3D* copySize);
|
||||||
|
void CopyTextureToTexture(const TextureCopyView* source,
|
||||||
|
const TextureCopyView* destination,
|
||||||
|
const Extent3D* copySize);
|
||||||
CommandBufferBase* Finish();
|
CommandBufferBase* Finish();
|
||||||
|
|
||||||
// Functions to interact with the encoders
|
// Functions to interact with the encoders
|
||||||
|
@ -49,6 +49,11 @@ namespace dawn_native {
|
|||||||
CopyTextureToBufferCmd* copy = commands->NextCommand<CopyTextureToBufferCmd>();
|
CopyTextureToBufferCmd* copy = commands->NextCommand<CopyTextureToBufferCmd>();
|
||||||
copy->~CopyTextureToBufferCmd();
|
copy->~CopyTextureToBufferCmd();
|
||||||
} break;
|
} break;
|
||||||
|
case Command::CopyTextureToTexture: {
|
||||||
|
CopyTextureToTextureCmd* copy =
|
||||||
|
commands->NextCommand<CopyTextureToTextureCmd>();
|
||||||
|
copy->~CopyTextureToTextureCmd();
|
||||||
|
} break;
|
||||||
case Command::Dispatch: {
|
case Command::Dispatch: {
|
||||||
DispatchCmd* dispatch = commands->NextCommand<DispatchCmd>();
|
DispatchCmd* dispatch = commands->NextCommand<DispatchCmd>();
|
||||||
dispatch->~DispatchCmd();
|
dispatch->~DispatchCmd();
|
||||||
@ -152,6 +157,10 @@ namespace dawn_native {
|
|||||||
commands->NextCommand<CopyTextureToBufferCmd>();
|
commands->NextCommand<CopyTextureToBufferCmd>();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Command::CopyTextureToTexture:
|
||||||
|
commands->NextCommand<CopyTextureToTextureCmd>();
|
||||||
|
break;
|
||||||
|
|
||||||
case Command::Dispatch:
|
case Command::Dispatch:
|
||||||
commands->NextCommand<DispatchCmd>();
|
commands->NextCommand<DispatchCmd>();
|
||||||
break;
|
break;
|
||||||
|
@ -36,6 +36,7 @@ namespace dawn_native {
|
|||||||
CopyBufferToBuffer,
|
CopyBufferToBuffer,
|
||||||
CopyBufferToTexture,
|
CopyBufferToTexture,
|
||||||
CopyTextureToBuffer,
|
CopyTextureToBuffer,
|
||||||
|
CopyTextureToTexture,
|
||||||
Dispatch,
|
Dispatch,
|
||||||
Draw,
|
Draw,
|
||||||
DrawIndexed,
|
DrawIndexed,
|
||||||
@ -119,6 +120,12 @@ namespace dawn_native {
|
|||||||
Extent3D copySize; // Texels
|
Extent3D copySize; // Texels
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct CopyTextureToTextureCmd {
|
||||||
|
TextureCopy source;
|
||||||
|
TextureCopy destination;
|
||||||
|
Extent3D copySize; // Texels
|
||||||
|
};
|
||||||
|
|
||||||
struct DispatchCmd {
|
struct DispatchCmd {
|
||||||
uint32_t x;
|
uint32_t x;
|
||||||
uint32_t y;
|
uint32_t y;
|
||||||
|
@ -44,6 +44,32 @@ namespace dawn_native { namespace d3d12 {
|
|||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
D3D12_TEXTURE_COPY_LOCATION CreateTextureCopyLocationForTexture(const Texture& texture,
|
||||||
|
uint32_t level,
|
||||||
|
uint32_t slice) {
|
||||||
|
D3D12_TEXTURE_COPY_LOCATION copyLocation;
|
||||||
|
copyLocation.pResource = texture.GetD3D12Resource();
|
||||||
|
copyLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
|
||||||
|
copyLocation.SubresourceIndex = texture.GetNumMipLevels() * slice + level;
|
||||||
|
|
||||||
|
return copyLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CanUseCopyResource(const uint32_t sourceNumMipLevels,
|
||||||
|
const Extent3D& srcSize,
|
||||||
|
const Extent3D& dstSize,
|
||||||
|
const Extent3D& copySize) {
|
||||||
|
if (sourceNumMipLevels == 1 && srcSize.width == dstSize.width &&
|
||||||
|
srcSize.height == dstSize.height && srcSize.depth == dstSize.depth &&
|
||||||
|
srcSize.width == copySize.width && srcSize.height == copySize.height &&
|
||||||
|
srcSize.depth == copySize.depth) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
struct BindGroupStateTracker {
|
struct BindGroupStateTracker {
|
||||||
@ -425,12 +451,9 @@ namespace dawn_native { namespace d3d12 {
|
|||||||
static_cast<uint32_t>(TextureFormatPixelSize(texture->GetFormat())),
|
static_cast<uint32_t>(TextureFormatPixelSize(texture->GetFormat())),
|
||||||
copy->source.offset, copy->source.rowPitch, copy->source.imageHeight);
|
copy->source.offset, copy->source.rowPitch, copy->source.imageHeight);
|
||||||
|
|
||||||
D3D12_TEXTURE_COPY_LOCATION textureLocation;
|
D3D12_TEXTURE_COPY_LOCATION textureLocation =
|
||||||
textureLocation.pResource = texture->GetD3D12Resource();
|
CreateTextureCopyLocationForTexture(*texture, copy->destination.level,
|
||||||
textureLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
|
copy->destination.slice);
|
||||||
textureLocation.SubresourceIndex =
|
|
||||||
texture->GetNumMipLevels() * copy->destination.slice +
|
|
||||||
copy->destination.level;
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < copySplit.count; ++i) {
|
for (uint32_t i = 0; i < copySplit.count; ++i) {
|
||||||
auto& info = copySplit.copies[i];
|
auto& info = copySplit.copies[i];
|
||||||
@ -473,11 +496,9 @@ namespace dawn_native { namespace d3d12 {
|
|||||||
copy->destination.offset, copy->destination.rowPitch,
|
copy->destination.offset, copy->destination.rowPitch,
|
||||||
copy->destination.imageHeight);
|
copy->destination.imageHeight);
|
||||||
|
|
||||||
D3D12_TEXTURE_COPY_LOCATION textureLocation;
|
D3D12_TEXTURE_COPY_LOCATION textureLocation =
|
||||||
textureLocation.pResource = texture->GetD3D12Resource();
|
CreateTextureCopyLocationForTexture(*texture, copy->source.level,
|
||||||
textureLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
|
copy->source.slice);
|
||||||
textureLocation.SubresourceIndex =
|
|
||||||
texture->GetNumMipLevels() * copy->source.slice + copy->source.level;
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < copySplit.count; ++i) {
|
for (uint32_t i = 0; i < copySplit.count; ++i) {
|
||||||
auto& info = copySplit.copies[i];
|
auto& info = copySplit.copies[i];
|
||||||
@ -507,6 +528,45 @@ namespace dawn_native { namespace d3d12 {
|
|||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case Command::CopyTextureToTexture: {
|
||||||
|
CopyTextureToTextureCmd* copy =
|
||||||
|
mCommands.NextCommand<CopyTextureToTextureCmd>();
|
||||||
|
|
||||||
|
Texture* source = ToBackend(copy->source.texture.Get());
|
||||||
|
Texture* destination = ToBackend(copy->destination.texture.Get());
|
||||||
|
|
||||||
|
source->TransitionUsageNow(commandList, dawn::TextureUsageBit::TransferSrc);
|
||||||
|
destination->TransitionUsageNow(commandList,
|
||||||
|
dawn::TextureUsageBit::TransferDst);
|
||||||
|
|
||||||
|
if (CanUseCopyResource(source->GetNumMipLevels(), source->GetSize(),
|
||||||
|
destination->GetSize(), copy->copySize)) {
|
||||||
|
commandList->CopyResource(destination->GetD3D12Resource(),
|
||||||
|
source->GetD3D12Resource());
|
||||||
|
|
||||||
|
} else {
|
||||||
|
D3D12_TEXTURE_COPY_LOCATION srcLocation =
|
||||||
|
CreateTextureCopyLocationForTexture(*source, copy->source.level,
|
||||||
|
copy->source.slice);
|
||||||
|
|
||||||
|
D3D12_TEXTURE_COPY_LOCATION dstLocation =
|
||||||
|
CreateTextureCopyLocationForTexture(
|
||||||
|
*destination, copy->destination.level, copy->destination.slice);
|
||||||
|
|
||||||
|
D3D12_BOX sourceRegion;
|
||||||
|
sourceRegion.left = copy->source.origin.x;
|
||||||
|
sourceRegion.top = copy->source.origin.y;
|
||||||
|
sourceRegion.front = copy->source.origin.z;
|
||||||
|
sourceRegion.right = copy->source.origin.x + copy->copySize.width;
|
||||||
|
sourceRegion.bottom = copy->source.origin.y + copy->copySize.height;
|
||||||
|
sourceRegion.back = copy->source.origin.z + copy->copySize.depth;
|
||||||
|
|
||||||
|
commandList->CopyTextureRegion(
|
||||||
|
&dstLocation, copy->destination.origin.x, copy->destination.origin.y,
|
||||||
|
copy->destination.origin.z, &srcLocation, &sourceRegion);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
default: { UNREACHABLE(); } break;
|
default: { UNREACHABLE(); } break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -150,7 +150,7 @@ namespace dawn_native { namespace d3d12 {
|
|||||||
return D3D12TextureFormat(GetFormat());
|
return D3D12TextureFormat(GetFormat());
|
||||||
}
|
}
|
||||||
|
|
||||||
ID3D12Resource* Texture::GetD3D12Resource() {
|
ID3D12Resource* Texture::GetD3D12Resource() const {
|
||||||
return mResourcePtr;
|
return mResourcePtr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ namespace dawn_native { namespace d3d12 {
|
|||||||
~Texture();
|
~Texture();
|
||||||
|
|
||||||
DXGI_FORMAT GetD3D12Format() const;
|
DXGI_FORMAT GetD3D12Format() const;
|
||||||
ID3D12Resource* GetD3D12Resource();
|
ID3D12Resource* GetD3D12Resource() const;
|
||||||
|
|
||||||
void TransitionUsageNow(ComPtr<ID3D12GraphicsCommandList> commandList,
|
void TransitionUsageNow(ComPtr<ID3D12GraphicsCommandList> commandList,
|
||||||
dawn::TextureUsageBit usage);
|
dawn::TextureUsageBit usage);
|
||||||
|
@ -476,6 +476,40 @@ namespace dawn_native { namespace metal {
|
|||||||
destinationBytesPerImage:lastRowDataSize];
|
destinationBytesPerImage:lastRowDataSize];
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case Command::CopyTextureToTexture: {
|
||||||
|
CopyTextureToTextureCmd* copy =
|
||||||
|
mCommands.NextCommand<CopyTextureToTextureCmd>();
|
||||||
|
Texture* srcTexture = ToBackend(copy->source.texture.Get());
|
||||||
|
Texture* dstTexture = ToBackend(copy->destination.texture.Get());
|
||||||
|
|
||||||
|
MTLOrigin srcOrigin;
|
||||||
|
srcOrigin.x = copy->source.origin.x;
|
||||||
|
srcOrigin.y = copy->source.origin.y;
|
||||||
|
srcOrigin.z = copy->source.origin.z;
|
||||||
|
|
||||||
|
MTLOrigin dstOrigin;
|
||||||
|
dstOrigin.x = copy->destination.origin.x;
|
||||||
|
dstOrigin.y = copy->destination.origin.y;
|
||||||
|
dstOrigin.z = copy->destination.origin.z;
|
||||||
|
|
||||||
|
MTLSize size;
|
||||||
|
size.width = copy->copySize.width;
|
||||||
|
size.height = copy->copySize.height;
|
||||||
|
size.depth = copy->copySize.depth;
|
||||||
|
|
||||||
|
encoders.EnsureBlit(commandBuffer);
|
||||||
|
|
||||||
|
[encoders.blit copyFromTexture:srcTexture->GetMTLTexture()
|
||||||
|
sourceSlice:copy->source.slice
|
||||||
|
sourceLevel:copy->source.level
|
||||||
|
sourceOrigin:srcOrigin
|
||||||
|
sourceSize:size
|
||||||
|
toTexture:dstTexture->GetMTLTexture()
|
||||||
|
destinationSlice:copy->destination.slice
|
||||||
|
destinationLevel:copy->destination.level
|
||||||
|
destinationOrigin:dstOrigin];
|
||||||
|
} break;
|
||||||
|
|
||||||
default: { UNREACHABLE(); } break;
|
default: { UNREACHABLE(); } break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -443,6 +443,22 @@ namespace dawn_native { namespace opengl {
|
|||||||
glDeleteFramebuffers(1, &readFBO);
|
glDeleteFramebuffers(1, &readFBO);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case Command::CopyTextureToTexture: {
|
||||||
|
CopyTextureToTextureCmd* copy =
|
||||||
|
mCommands.NextCommand<CopyTextureToTextureCmd>();
|
||||||
|
auto& src = copy->source;
|
||||||
|
auto& dst = copy->destination;
|
||||||
|
auto& copySize = copy->copySize;
|
||||||
|
Texture* srcTexture = ToBackend(src.texture.Get());
|
||||||
|
Texture* dstTexture = ToBackend(dst.texture.Get());
|
||||||
|
|
||||||
|
glCopyImageSubData(srcTexture->GetHandle(), srcTexture->GetGLTarget(),
|
||||||
|
src.level, src.origin.x, src.origin.y, src.slice,
|
||||||
|
dstTexture->GetHandle(), dstTexture->GetGLTarget(),
|
||||||
|
dst.level, dst.origin.x, dst.origin.y, dst.slice,
|
||||||
|
copySize.width, copySize.height, 1);
|
||||||
|
} break;
|
||||||
|
|
||||||
default: { UNREACHABLE(); } break;
|
default: { UNREACHABLE(); } break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,6 +70,39 @@ namespace dawn_native { namespace vulkan {
|
|||||||
return region;
|
return region;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VkImageCopy ComputeImageCopyRegion(const TextureCopy& srcCopy,
|
||||||
|
const TextureCopy& dstCopy,
|
||||||
|
const Extent3D& copySize) {
|
||||||
|
const Texture* srcTexture = ToBackend(srcCopy.texture.Get());
|
||||||
|
const Texture* dstTexture = ToBackend(dstCopy.texture.Get());
|
||||||
|
|
||||||
|
VkImageCopy region;
|
||||||
|
|
||||||
|
region.srcSubresource.aspectMask = srcTexture->GetVkAspectMask();
|
||||||
|
region.srcSubresource.mipLevel = srcCopy.level;
|
||||||
|
region.srcSubresource.baseArrayLayer = srcCopy.slice;
|
||||||
|
region.srcSubresource.layerCount = 1;
|
||||||
|
|
||||||
|
region.srcOffset.x = srcCopy.origin.x;
|
||||||
|
region.srcOffset.y = srcCopy.origin.y;
|
||||||
|
region.srcOffset.z = srcCopy.origin.z;
|
||||||
|
|
||||||
|
region.dstSubresource.aspectMask = dstTexture->GetVkAspectMask();
|
||||||
|
region.dstSubresource.mipLevel = dstCopy.level;
|
||||||
|
region.dstSubresource.baseArrayLayer = dstCopy.slice;
|
||||||
|
region.dstSubresource.layerCount = 1;
|
||||||
|
|
||||||
|
region.dstOffset.x = dstCopy.origin.x;
|
||||||
|
region.dstOffset.y = dstCopy.origin.y;
|
||||||
|
region.dstOffset.z = dstCopy.origin.z;
|
||||||
|
|
||||||
|
region.extent.width = copySize.width;
|
||||||
|
region.extent.height = copySize.height;
|
||||||
|
region.extent.depth = copySize.depth;
|
||||||
|
|
||||||
|
return region;
|
||||||
|
}
|
||||||
|
|
||||||
class DescriptorSetTracker {
|
class DescriptorSetTracker {
|
||||||
public:
|
public:
|
||||||
void OnSetBindGroup(uint32_t index, VkDescriptorSet set) {
|
void OnSetBindGroup(uint32_t index, VkDescriptorSet set) {
|
||||||
@ -299,6 +332,26 @@ namespace dawn_native { namespace vulkan {
|
|||||||
dstBuffer, 1, ®ion);
|
dstBuffer, 1, ®ion);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case Command::CopyTextureToTexture: {
|
||||||
|
CopyTextureToTextureCmd* copy =
|
||||||
|
mCommands.NextCommand<CopyTextureToTextureCmd>();
|
||||||
|
TextureCopy& src = copy->source;
|
||||||
|
TextureCopy& dst = copy->destination;
|
||||||
|
|
||||||
|
ToBackend(src.texture)
|
||||||
|
->TransitionUsageNow(commands, dawn::TextureUsageBit::TransferSrc);
|
||||||
|
ToBackend(dst.texture)
|
||||||
|
->TransitionUsageNow(commands, dawn::TextureUsageBit::TransferDst);
|
||||||
|
|
||||||
|
VkImage srcImage = ToBackend(src.texture)->GetHandle();
|
||||||
|
VkImage dstImage = ToBackend(dst.texture)->GetHandle();
|
||||||
|
|
||||||
|
VkImageCopy region = ComputeImageCopyRegion(src, dst, copy->copySize);
|
||||||
|
|
||||||
|
device->fn.CmdCopyImage(commands, srcImage, VK_IMAGE_LAYOUT_GENERAL, dstImage,
|
||||||
|
VK_IMAGE_LAYOUT_GENERAL, 1, ®ion);
|
||||||
|
} break;
|
||||||
|
|
||||||
case Command::BeginRenderPass: {
|
case Command::BeginRenderPass: {
|
||||||
BeginRenderPassCmd* cmd = mCommands.NextCommand<BeginRenderPassCmd>();
|
BeginRenderPassCmd* cmd = mCommands.NextCommand<BeginRenderPassCmd>();
|
||||||
|
|
||||||
|
@ -40,6 +40,21 @@ class CopyTests : public DawnTest {
|
|||||||
uint32_t rowPitch;
|
uint32_t rowPitch;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void FillTextureData(uint32_t width,
|
||||||
|
uint32_t height,
|
||||||
|
uint32_t texelsPerRow,
|
||||||
|
uint32_t layer,
|
||||||
|
RGBA8* data) {
|
||||||
|
for (uint32_t y = 0; y < height; ++y) {
|
||||||
|
for (uint32_t x = 0; x < width; ++x) {
|
||||||
|
uint32_t i = x + y * texelsPerRow;
|
||||||
|
data[i] = RGBA8(static_cast<uint8_t>((x + layer * x) % 256),
|
||||||
|
static_cast<uint8_t>((y + layer * y) % 256),
|
||||||
|
static_cast<uint8_t>(x / 256), static_cast<uint8_t>(y / 256));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
BufferSpec MinimumBufferSpec(uint32_t width, uint32_t height) {
|
BufferSpec MinimumBufferSpec(uint32_t width, uint32_t height) {
|
||||||
uint32_t rowPitch = Align(width * kBytesPerTexel, kTextureRowPitchAlignment);
|
uint32_t rowPitch = Align(width * kBytesPerTexel, kTextureRowPitchAlignment);
|
||||||
return { rowPitch * (height - 1) + width * kBytesPerTexel, 0, rowPitch };
|
return { rowPitch * (height - 1) + width * kBytesPerTexel, 0, rowPitch };
|
||||||
@ -58,18 +73,6 @@ class CopyTests : public DawnTest {
|
|||||||
|
|
||||||
class CopyTests_T2B : public CopyTests {
|
class CopyTests_T2B : public CopyTests {
|
||||||
protected:
|
protected:
|
||||||
static void FillTextureData(uint32_t width, uint32_t height, uint32_t texelsPerRow, RGBA8* data, uint32_t layer) {
|
|
||||||
for (unsigned int y = 0; y < height; ++y) {
|
|
||||||
for (unsigned int x = 0; x < width; ++x) {
|
|
||||||
unsigned int i = x + y * texelsPerRow;
|
|
||||||
data[i] = RGBA8(
|
|
||||||
static_cast<uint8_t>((x + layer * x)% 256),
|
|
||||||
static_cast<uint8_t>((y + layer * y)% 256),
|
|
||||||
static_cast<uint8_t>(x / 256),
|
|
||||||
static_cast<uint8_t>(y / 256));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DoTest(const TextureSpec& textureSpec, const BufferSpec& bufferSpec) {
|
void DoTest(const TextureSpec& textureSpec, const BufferSpec& bufferSpec) {
|
||||||
// Create a texture that is `width` x `height` with (`level` + 1) mip levels.
|
// Create a texture that is `width` x `height` with (`level` + 1) mip levels.
|
||||||
@ -96,7 +99,8 @@ class CopyTests_T2B : public CopyTests {
|
|||||||
std::vector<std::vector<RGBA8>> textureArrayData(textureSpec.arraySize);
|
std::vector<std::vector<RGBA8>> textureArrayData(textureSpec.arraySize);
|
||||||
for (uint32_t slice = 0; slice < textureSpec.arraySize; ++slice) {
|
for (uint32_t slice = 0; slice < textureSpec.arraySize; ++slice) {
|
||||||
textureArrayData[slice].resize(texelCountPerLayer);
|
textureArrayData[slice].resize(texelCountPerLayer);
|
||||||
FillTextureData(width, height, rowPitch / kBytesPerTexel, textureArrayData[slice].data(), slice);
|
FillTextureData(width, height, rowPitch / kBytesPerTexel, slice,
|
||||||
|
textureArrayData[slice].data());
|
||||||
|
|
||||||
// Create an upload buffer and use it to populate the current slice of the texture in `level` mip level
|
// Create an upload buffer and use it to populate the current slice of the texture in `level` mip level
|
||||||
dawn::Buffer uploadBuffer = utils::CreateBufferFromData(device, textureArrayData[slice].data(),
|
dawn::Buffer uploadBuffer = utils::CreateBufferFromData(device, textureArrayData[slice].data(),
|
||||||
@ -245,6 +249,134 @@ protected:
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class CopyTests_T2T : public CopyTests {
|
||||||
|
struct TextureSpec {
|
||||||
|
uint32_t width;
|
||||||
|
uint32_t height;
|
||||||
|
uint32_t x;
|
||||||
|
uint32_t y;
|
||||||
|
uint32_t level;
|
||||||
|
uint32_t arraySize = 1u;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CopySize {
|
||||||
|
uint32_t width;
|
||||||
|
uint32_t height;
|
||||||
|
};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void DoTest(const TextureSpec& srcSpec, const TextureSpec& dstSpec, const CopySize& copy) {
|
||||||
|
dawn::TextureDescriptor srcDescriptor;
|
||||||
|
srcDescriptor.dimension = dawn::TextureDimension::e2D;
|
||||||
|
srcDescriptor.size.width = srcSpec.width;
|
||||||
|
srcDescriptor.size.height = srcSpec.height;
|
||||||
|
srcDescriptor.size.depth = 1;
|
||||||
|
srcDescriptor.arrayLayerCount = srcSpec.arraySize;
|
||||||
|
srcDescriptor.sampleCount = 1;
|
||||||
|
srcDescriptor.format = dawn::TextureFormat::R8G8B8A8Unorm;
|
||||||
|
srcDescriptor.mipLevelCount = srcSpec.level + 1;
|
||||||
|
srcDescriptor.usage =
|
||||||
|
dawn::TextureUsageBit::TransferSrc | dawn::TextureUsageBit::TransferDst;
|
||||||
|
dawn::Texture srcTexture = device.CreateTexture(&srcDescriptor);
|
||||||
|
|
||||||
|
dawn::TextureDescriptor dstDescriptor;
|
||||||
|
dstDescriptor.dimension = dawn::TextureDimension::e2D;
|
||||||
|
dstDescriptor.size.width = dstSpec.width;
|
||||||
|
dstDescriptor.size.height = dstSpec.height;
|
||||||
|
dstDescriptor.size.depth = 1;
|
||||||
|
dstDescriptor.arrayLayerCount = dstSpec.arraySize;
|
||||||
|
dstDescriptor.sampleCount = 1;
|
||||||
|
dstDescriptor.format = dawn::TextureFormat::R8G8B8A8Unorm;
|
||||||
|
dstDescriptor.mipLevelCount = dstSpec.level + 1;
|
||||||
|
dstDescriptor.usage =
|
||||||
|
dawn::TextureUsageBit::TransferSrc | dawn::TextureUsageBit::TransferDst;
|
||||||
|
dawn::Texture dstTexture = device.CreateTexture(&dstDescriptor);
|
||||||
|
|
||||||
|
dawn::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
|
||||||
|
// Create an upload buffer and use it to populate the current slice of the texture in
|
||||||
|
// `level` mip level
|
||||||
|
uint32_t width = srcSpec.width >> srcSpec.level;
|
||||||
|
uint32_t height = srcSpec.height >> srcSpec.level;
|
||||||
|
uint32_t rowPitch = Align(kBytesPerTexel * width, kTextureRowPitchAlignment);
|
||||||
|
uint32_t texelsPerRow = rowPitch / kBytesPerTexel;
|
||||||
|
uint32_t texelCountPerLayer = texelsPerRow * (height - 1) + width;
|
||||||
|
|
||||||
|
std::vector<std::vector<RGBA8>> textureArrayData(srcSpec.arraySize);
|
||||||
|
for (uint32_t slice = 0; slice < srcSpec.arraySize; ++slice) {
|
||||||
|
textureArrayData[slice].resize(texelCountPerLayer);
|
||||||
|
FillTextureData(width, height, rowPitch / kBytesPerTexel, slice,
|
||||||
|
textureArrayData[slice].data());
|
||||||
|
|
||||||
|
dawn::Buffer uploadBuffer = utils::CreateBufferFromData(
|
||||||
|
device, textureArrayData[slice].data(),
|
||||||
|
static_cast<uint32_t>(sizeof(RGBA8) * textureArrayData[slice].size()),
|
||||||
|
dawn::BufferUsageBit::TransferSrc);
|
||||||
|
dawn::BufferCopyView bufferCopyView =
|
||||||
|
utils::CreateBufferCopyView(uploadBuffer, 0, rowPitch, 0);
|
||||||
|
dawn::TextureCopyView textureCopyView =
|
||||||
|
utils::CreateTextureCopyView(srcTexture, srcSpec.level, slice, {0, 0, 0});
|
||||||
|
dawn::Extent3D bufferCopySize = {width, height, 1};
|
||||||
|
|
||||||
|
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 row pitch 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, kTextureRowPitchAlignment);
|
||||||
|
uint32_t dstTexelsPerRow = dstRowPitch / kBytesPerTexel;
|
||||||
|
uint32_t dstTexelCount = dstTexelsPerRow * (dstHeight - 1) + dstWidth;
|
||||||
|
|
||||||
|
std::vector<RGBA8> emptyData(dstTexelCount);
|
||||||
|
dawn::Buffer uploadBuffer = utils::CreateBufferFromData(
|
||||||
|
device, emptyData.data(), static_cast<uint32_t>(sizeof(RGBA8) * emptyData.size()),
|
||||||
|
dawn::BufferUsageBit::TransferSrc);
|
||||||
|
dawn::BufferCopyView bufferCopyView =
|
||||||
|
utils::CreateBufferCopyView(uploadBuffer, 0, dstRowPitch, 0);
|
||||||
|
dawn::TextureCopyView textureCopyView =
|
||||||
|
utils::CreateTextureCopyView(dstTexture, dstSpec.level, 0, {0, 0, 0});
|
||||||
|
dawn::Extent3D dstCopySize = {dstWidth, dstHeight, 1};
|
||||||
|
encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, &dstCopySize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform the texture to texture copy
|
||||||
|
for (uint32_t slice = 0; slice < srcSpec.arraySize; ++slice) {
|
||||||
|
dawn::TextureCopyView srcTextureCopyView = utils::CreateTextureCopyView(
|
||||||
|
srcTexture, srcSpec.level, slice, {srcSpec.x, srcSpec.y, 0});
|
||||||
|
dawn::TextureCopyView dstTextureCopyView = utils::CreateTextureCopyView(
|
||||||
|
dstTexture, dstSpec.level, slice, {dstSpec.x, dstSpec.y, 0});
|
||||||
|
dawn::Extent3D copySize = {copy.width, copy.height, 1};
|
||||||
|
encoder.CopyTextureToTexture(&srcTextureCopyView, &dstTextureCopyView, ©Size);
|
||||||
|
}
|
||||||
|
|
||||||
|
dawn::CommandBuffer commands = encoder.Finish();
|
||||||
|
queue.Submit(1, &commands);
|
||||||
|
|
||||||
|
std::vector<RGBA8> expected(rowPitch / kBytesPerTexel * (copy.height - 1) + copy.width);
|
||||||
|
for (uint32_t slice = 0; slice < srcSpec.arraySize; ++slice) {
|
||||||
|
std::fill(expected.begin(), expected.end(), RGBA8());
|
||||||
|
PackTextureData(
|
||||||
|
&textureArrayData[slice][srcSpec.x + srcSpec.y * (rowPitch / kBytesPerTexel)],
|
||||||
|
copy.width, copy.height, texelsPerRow, expected.data(), copy.width);
|
||||||
|
|
||||||
|
EXPECT_TEXTURE_RGBA8_EQ(expected.data(), dstTexture, dstSpec.x, dstSpec.y, copy.width,
|
||||||
|
copy.height, dstSpec.level, slice)
|
||||||
|
<< "Texture to Texture copy failed copying region [(" << srcSpec.x << ", "
|
||||||
|
<< srcSpec.y << "), (" << srcSpec.x + copy.width << ", " << srcSpec.y + copy.height
|
||||||
|
<< ")) from " << srcSpec.width << " x " << srcSpec.height
|
||||||
|
<< " texture at mip level " << srcSpec.level << " layer " << slice << " to [("
|
||||||
|
<< dstSpec.x << ", " << dstSpec.y << "), (" << dstSpec.x + copy.width << ", "
|
||||||
|
<< dstSpec.y + copy.height << ")) region of " << dstSpec.width << " x "
|
||||||
|
<< dstSpec.height << " texture at mip level " << dstSpec.level << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Test that copying an entire texture with 256-byte aligned dimensions works
|
// Test that copying an entire texture with 256-byte aligned dimensions works
|
||||||
TEST_P(CopyTests_T2B, FullTextureAligned) {
|
TEST_P(CopyTests_T2B, FullTextureAligned) {
|
||||||
constexpr uint32_t kWidth = 256;
|
constexpr uint32_t kWidth = 256;
|
||||||
@ -551,3 +683,51 @@ TEST_P(CopyTests_B2T, RowPitchUnaligned) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
DAWN_INSTANTIATE_TEST(CopyTests_B2T, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend);
|
DAWN_INSTANTIATE_TEST(CopyTests_B2T, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend);
|
||||||
|
|
||||||
|
TEST_P(CopyTests_T2T, Texture) {
|
||||||
|
constexpr uint32_t kWidth = 256;
|
||||||
|
constexpr uint32_t kHeight = 128;
|
||||||
|
DoTest({kWidth, kHeight, 0, 0, 0}, {kWidth, kHeight, 0, 0, 0}, {kWidth, kHeight});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(CopyTests_T2T, TextureRegion) {
|
||||||
|
constexpr uint32_t kWidth = 256;
|
||||||
|
constexpr uint32_t kHeight = 128;
|
||||||
|
for (unsigned int w : {64, 128, 256}) {
|
||||||
|
for (unsigned int h : {16, 32, 48}) {
|
||||||
|
DoTest({kWidth, kHeight, 0, 0, 0, 1}, {kWidth, kHeight, 0, 0, 0, 1}, {w, h});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(CopyTests_T2T, Texture2DArray) {
|
||||||
|
constexpr uint32_t kWidth = 256;
|
||||||
|
constexpr uint32_t kHeight = 128;
|
||||||
|
constexpr uint32_t kLayers = 6u;
|
||||||
|
DoTest({kWidth, kHeight, 0, 0, 0, kLayers}, {kWidth, kHeight, 0, 0, 0, kLayers},
|
||||||
|
{kWidth, kHeight});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(CopyTests_T2T, Texture2DArrayRegion) {
|
||||||
|
constexpr uint32_t kWidth = 256;
|
||||||
|
constexpr uint32_t kHeight = 128;
|
||||||
|
constexpr uint32_t kLayers = 6u;
|
||||||
|
for (unsigned int w : {64, 128, 256}) {
|
||||||
|
for (unsigned int h : {16, 32, 48}) {
|
||||||
|
DoTest({kWidth, kHeight, 0, 0, 0, kLayers}, {kWidth, kHeight, 0, 0, 0, kLayers},
|
||||||
|
{w, h});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(CopyTests_T2T, TextureMip) {
|
||||||
|
constexpr uint32_t kWidth = 256;
|
||||||
|
constexpr uint32_t kHeight = 128;
|
||||||
|
for (unsigned int i = 1; i < 4; ++i) {
|
||||||
|
DoTest({kWidth, kHeight, 0, 0, i}, {kWidth, kHeight, 0, 0, i}, {kWidth >> i, kHeight >> i});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(brandon1.jones@intel.com) Add test for ensuring blitCommandEncoder on Metal.
|
||||||
|
|
||||||
|
DAWN_INSTANTIATE_TEST(CopyTests_T2T, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend);
|
@ -49,6 +49,14 @@ class CopyCommandTest : public ValidationTest {
|
|||||||
return (rowPitch * (height - 1) + width) * depth;
|
return (rowPitch * (height - 1) + width) * depth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ValidateExpectation(dawn::CommandEncoder encoder, utils::Expectation expectation) {
|
||||||
|
if (expectation == utils::Expectation::Success) {
|
||||||
|
encoder.Finish();
|
||||||
|
} else {
|
||||||
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TestB2TCopy(utils::Expectation expectation,
|
void TestB2TCopy(utils::Expectation expectation,
|
||||||
dawn::Buffer srcBuffer,
|
dawn::Buffer srcBuffer,
|
||||||
uint32_t srcOffset,
|
uint32_t srcOffset,
|
||||||
@ -67,11 +75,7 @@ class CopyCommandTest : public ValidationTest {
|
|||||||
dawn::CommandEncoder encoder = device.CreateCommandEncoder();
|
dawn::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, &extent3D);
|
encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, &extent3D);
|
||||||
|
|
||||||
if (expectation == utils::Expectation::Success) {
|
ValidateExpectation(encoder, expectation);
|
||||||
encoder.Finish();
|
|
||||||
} else {
|
|
||||||
ASSERT_DEVICE_ERROR(encoder.Finish());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestT2BCopy(utils::Expectation expectation,
|
void TestT2BCopy(utils::Expectation expectation,
|
||||||
@ -92,11 +96,28 @@ class CopyCommandTest : public ValidationTest {
|
|||||||
dawn::CommandEncoder encoder = device.CreateCommandEncoder();
|
dawn::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
encoder.CopyTextureToBuffer(&textureCopyView, &bufferCopyView, &extent3D);
|
encoder.CopyTextureToBuffer(&textureCopyView, &bufferCopyView, &extent3D);
|
||||||
|
|
||||||
if (expectation == utils::Expectation::Success) {
|
ValidateExpectation(encoder, expectation);
|
||||||
encoder.Finish();
|
}
|
||||||
} else {
|
|
||||||
ASSERT_DEVICE_ERROR(encoder.Finish());
|
void TestT2TCopy(utils::Expectation expectation,
|
||||||
}
|
dawn::Texture srcTexture,
|
||||||
|
uint32_t srcLevel,
|
||||||
|
uint32_t srcSlice,
|
||||||
|
dawn::Origin3D srcOrigin,
|
||||||
|
dawn::Texture dstTexture,
|
||||||
|
uint32_t dstLevel,
|
||||||
|
uint32_t dstSlice,
|
||||||
|
dawn::Origin3D dstOrigin,
|
||||||
|
dawn::Extent3D extent3D) {
|
||||||
|
dawn::TextureCopyView srcTextureCopyView =
|
||||||
|
utils::CreateTextureCopyView(srcTexture, srcLevel, srcSlice, srcOrigin);
|
||||||
|
dawn::TextureCopyView dstTextureCopyView =
|
||||||
|
utils::CreateTextureCopyView(dstTexture, dstLevel, dstSlice, dstOrigin);
|
||||||
|
|
||||||
|
dawn::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
encoder.CopyTextureToTexture(&srcTextureCopyView, &dstTextureCopyView, &extent3D);
|
||||||
|
|
||||||
|
ValidateExpectation(encoder, expectation);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -738,3 +759,193 @@ TEST_F(CopyCommandTest_T2B, BufferOrTextureInErrorState) {
|
|||||||
ASSERT_DEVICE_ERROR(encoder.Finish());
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CopyCommandTest_T2T : public CopyCommandTest {};
|
||||||
|
|
||||||
|
TEST_F(CopyCommandTest_T2T, Success) {
|
||||||
|
dawn::Texture source = Create2DTexture(16, 16, 5, 2, dawn::TextureFormat::R8G8B8A8Unorm,
|
||||||
|
dawn::TextureUsageBit::TransferSrc);
|
||||||
|
dawn::Texture destination = Create2DTexture(16, 16, 5, 2, dawn::TextureFormat::R8G8B8A8Unorm,
|
||||||
|
dawn::TextureUsageBit::TransferDst);
|
||||||
|
|
||||||
|
// Different copies, including some that touch the OOB condition
|
||||||
|
{
|
||||||
|
// Copy a region along top left boundary
|
||||||
|
TestT2TCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, destination, 0, 0,
|
||||||
|
{0, 0, 0}, {4, 4, 1});
|
||||||
|
|
||||||
|
// Copy entire texture
|
||||||
|
TestT2TCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, destination, 0, 0,
|
||||||
|
{0, 0, 0}, {16, 16, 1});
|
||||||
|
|
||||||
|
// Copy a region along bottom right boundary
|
||||||
|
TestT2TCopy(utils::Expectation::Success, source, 0, 0, {8, 8, 0}, destination, 0, 0,
|
||||||
|
{8, 8, 0}, {8, 8, 1});
|
||||||
|
|
||||||
|
// Copy region into mip
|
||||||
|
TestT2TCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, destination, 2, 0,
|
||||||
|
{0, 0, 0}, {4, 4, 1});
|
||||||
|
|
||||||
|
// Copy mip into region
|
||||||
|
TestT2TCopy(utils::Expectation::Success, source, 2, 0, {0, 0, 0}, destination, 0, 0,
|
||||||
|
{0, 0, 0}, {4, 4, 1});
|
||||||
|
|
||||||
|
// Copy between slices
|
||||||
|
TestT2TCopy(utils::Expectation::Success, source, 0, 1, {0, 0, 0}, destination, 0, 1,
|
||||||
|
{0, 0, 0}, {16, 16, 1});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty copies are valid
|
||||||
|
{
|
||||||
|
// An empty copy
|
||||||
|
TestT2TCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, destination, 0, 0,
|
||||||
|
{0, 0, 0}, {0, 0, 1});
|
||||||
|
|
||||||
|
// An empty copy touching the side of the source texture
|
||||||
|
TestT2TCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, destination, 0, 0,
|
||||||
|
{16, 16, 0}, {0, 0, 1});
|
||||||
|
|
||||||
|
// An empty copy touching the side of the destination texture
|
||||||
|
TestT2TCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, destination, 0, 0,
|
||||||
|
{16, 16, 0}, {0, 0, 1});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(CopyCommandTest_T2T, IncorrectUsage) {
|
||||||
|
dawn::Texture source = Create2DTexture(16, 16, 5, 2, dawn::TextureFormat::R8G8B8A8Unorm,
|
||||||
|
dawn::TextureUsageBit::TransferSrc);
|
||||||
|
dawn::Texture destination = Create2DTexture(16, 16, 5, 2, dawn::TextureFormat::R8G8B8A8Unorm,
|
||||||
|
dawn::TextureUsageBit::TransferDst);
|
||||||
|
|
||||||
|
// Incorrect source usage causes failure
|
||||||
|
TestT2TCopy(utils::Expectation::Failure, destination, 0, 0, {0, 0, 0}, destination, 0, 0,
|
||||||
|
{0, 0, 0}, {16, 16, 1});
|
||||||
|
|
||||||
|
// Incorrect destination usage causes failure
|
||||||
|
TestT2TCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, source, 0, 0, {0, 0, 0},
|
||||||
|
{16, 16, 1});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(CopyCommandTest_T2T, OutOfBounds) {
|
||||||
|
dawn::Texture source = Create2DTexture(16, 16, 5, 2, dawn::TextureFormat::R8G8B8A8Unorm,
|
||||||
|
dawn::TextureUsageBit::TransferSrc);
|
||||||
|
dawn::Texture destination = Create2DTexture(16, 16, 5, 2, dawn::TextureFormat::R8G8B8A8Unorm,
|
||||||
|
dawn::TextureUsageBit::TransferDst);
|
||||||
|
|
||||||
|
// OOB on source
|
||||||
|
{
|
||||||
|
// x + width overflows
|
||||||
|
TestT2TCopy(utils::Expectation::Failure, source, 0, 0, {1, 0, 0}, destination, 0, 0,
|
||||||
|
{0, 0, 0}, {16, 16, 1});
|
||||||
|
|
||||||
|
// y + height overflows
|
||||||
|
TestT2TCopy(utils::Expectation::Failure, source, 0, 0, {0, 1, 0}, destination, 0, 0,
|
||||||
|
{0, 0, 0}, {16, 16, 1});
|
||||||
|
|
||||||
|
// non-zero mip overflows
|
||||||
|
TestT2TCopy(utils::Expectation::Failure, source, 1, 0, {0, 0, 0}, destination, 0, 0,
|
||||||
|
{0, 0, 0}, {9, 9, 1});
|
||||||
|
|
||||||
|
// empty copy on non-existent mip fails
|
||||||
|
TestT2TCopy(utils::Expectation::Failure, source, 6, 0, {0, 0, 0}, destination, 0, 0,
|
||||||
|
{0, 0, 0}, {0, 0, 1});
|
||||||
|
|
||||||
|
// empty copy from non-existent slice fails
|
||||||
|
TestT2TCopy(utils::Expectation::Failure, source, 0, 2, {0, 0, 0}, destination, 0, 0,
|
||||||
|
{0, 0, 0}, {0, 0, 1});
|
||||||
|
}
|
||||||
|
|
||||||
|
// OOB on destination
|
||||||
|
{
|
||||||
|
// x + width overflows
|
||||||
|
TestT2TCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 0, 0,
|
||||||
|
{1, 0, 0}, {16, 16, 1});
|
||||||
|
|
||||||
|
// y + height overflows
|
||||||
|
TestT2TCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 0, 0,
|
||||||
|
{0, 1, 0}, {16, 16, 1});
|
||||||
|
|
||||||
|
// non-zero mip overflows
|
||||||
|
TestT2TCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 1, 0,
|
||||||
|
{0, 0, 0}, {9, 9, 1});
|
||||||
|
|
||||||
|
// empty copy on non-existent mip fails
|
||||||
|
TestT2TCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 6, 0,
|
||||||
|
{0, 0, 0}, {0, 0, 1});
|
||||||
|
|
||||||
|
// empty copy on non-existent slice fails
|
||||||
|
TestT2TCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 0, 2,
|
||||||
|
{0, 0, 0}, {0, 0, 1});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(CopyCommandTest_T2T, 2DTextureDepthConstraints) {
|
||||||
|
dawn::Texture source = Create2DTexture(16, 16, 5, 2, dawn::TextureFormat::R8G8B8A8Unorm,
|
||||||
|
dawn::TextureUsageBit::TransferSrc);
|
||||||
|
dawn::Texture destination = Create2DTexture(16, 16, 5, 2, dawn::TextureFormat::R8G8B8A8Unorm,
|
||||||
|
dawn::TextureUsageBit::TransferDst);
|
||||||
|
|
||||||
|
// Empty copy on source with z > 0 fails
|
||||||
|
TestT2TCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 1}, destination, 0, 0, {0, 0, 0},
|
||||||
|
{0, 0, 1});
|
||||||
|
|
||||||
|
// Empty copy on destination with z > 0 fails
|
||||||
|
TestT2TCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 0, 0, {0, 0, 1},
|
||||||
|
{0, 0, 1});
|
||||||
|
|
||||||
|
// Empty copy with depth = 0 fails
|
||||||
|
TestT2TCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 0, 0, {0, 0, 0},
|
||||||
|
{0, 0, 0});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(CopyCommandTest_T2T, 2DTextureDepthStencil) {
|
||||||
|
dawn::Texture source = Create2DTexture(16, 16, 1, 1, dawn::TextureFormat::D32FloatS8Uint,
|
||||||
|
dawn::TextureUsageBit::TransferSrc);
|
||||||
|
dawn::Texture destination = Create2DTexture(16, 16, 1, 1, dawn::TextureFormat::D32FloatS8Uint,
|
||||||
|
dawn::TextureUsageBit::TransferDst);
|
||||||
|
|
||||||
|
// Success when entire depth stencil subresource is copied
|
||||||
|
TestT2TCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, destination, 0, 0, {0, 0, 0},
|
||||||
|
{16, 16, 1});
|
||||||
|
|
||||||
|
// Failure when depth stencil subresource is partially copied
|
||||||
|
TestT2TCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 0, 0, {0, 0, 0},
|
||||||
|
{15, 15, 1});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(CopyCommandTest_T2T, FormatsMismatch) {
|
||||||
|
dawn::Texture source = Create2DTexture(16, 16, 5, 2, dawn::TextureFormat::R8G8B8A8Uint,
|
||||||
|
dawn::TextureUsageBit::TransferSrc);
|
||||||
|
dawn::Texture destination = Create2DTexture(16, 16, 5, 2, dawn::TextureFormat::R8G8B8A8Unorm,
|
||||||
|
dawn::TextureUsageBit::TransferDst);
|
||||||
|
|
||||||
|
// Failure when formats don't match
|
||||||
|
TestT2TCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 0, 0, {0, 0, 0},
|
||||||
|
{0, 0, 1});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(CopyCommandTest_T2T, MultisampledCopies) {
|
||||||
|
dawn::Texture sourceMultiSampled1x = Create2DTexture(
|
||||||
|
16, 16, 1, 1, dawn::TextureFormat::R8G8B8A8Unorm, dawn::TextureUsageBit::TransferSrc, 1);
|
||||||
|
dawn::Texture sourceMultiSampled4x = Create2DTexture(
|
||||||
|
16, 16, 1, 1, dawn::TextureFormat::R8G8B8A8Unorm, dawn::TextureUsageBit::TransferSrc, 4);
|
||||||
|
dawn::Texture destinationMultiSampled4x = Create2DTexture(
|
||||||
|
16, 16, 1, 1, dawn::TextureFormat::R8G8B8A8Unorm, dawn::TextureUsageBit::TransferDst, 4);
|
||||||
|
|
||||||
|
// Success when entire multisampled subresource is copied
|
||||||
|
{
|
||||||
|
TestT2TCopy(utils::Expectation::Success, sourceMultiSampled4x, 0, 0, {0, 0, 0},
|
||||||
|
destinationMultiSampled4x, 0, 0, {0, 0, 0}, {16, 16, 1});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Failures
|
||||||
|
{
|
||||||
|
// An empty copy with mismatched samples fails
|
||||||
|
TestT2TCopy(utils::Expectation::Failure, sourceMultiSampled1x, 0, 0, {0, 0, 0},
|
||||||
|
destinationMultiSampled4x, 0, 0, {0, 0, 0}, {0, 0, 1});
|
||||||
|
|
||||||
|
// A copy fails when samples are greater than 1, and entire subresource isn't copied
|
||||||
|
TestT2TCopy(utils::Expectation::Failure, sourceMultiSampled4x, 0, 0, {0, 0, 0},
|
||||||
|
destinationMultiSampled4x, 0, 0, {0, 0, 0}, {15, 15, 1});
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user