From 0d9fce100df39ef2ae0254c991bd454bcdaa700c Mon Sep 17 00:00:00 2001 From: Austin Eng Date: Thu, 30 Jul 2020 15:29:57 +0000 Subject: [PATCH] Add texture aspect to texture copy view and validation tests Bug: dawn:439 Change-Id: I0ca283f58fe2b63ac3a8c468f8ea1bb2d300856f Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/24683 Commit-Queue: Austin Eng Reviewed-by: Jiawei Shao Reviewed-by: Corentin Wallez --- dawn.json | 3 +- src/dawn_native/CommandEncoder.cpp | 69 ++++++- src/dawn_native/CommandValidation.cpp | 74 +++++++- src/dawn_native/CommandValidation.h | 6 +- src/dawn_native/Format.cpp | 41 ++++ src/dawn_native/Format.h | 14 +- src/dawn_native/Queue.cpp | 6 +- .../CopyCommandsValidationTests.cpp | 179 +++++++++++++++++- .../QueueWriteTextureValidationTests.cpp | 70 ++++++- src/utils/WGPUHelpers.cpp | 4 +- src/utils/WGPUHelpers.h | 8 +- 11 files changed, 436 insertions(+), 38 deletions(-) diff --git a/dawn.json b/dawn.json index 109eeae865..e2cd79a407 100644 --- a/dawn.json +++ b/dawn.json @@ -1592,7 +1592,8 @@ {"name": "texture", "type": "texture"}, {"name": "mip level", "type": "uint32_t", "default": "0"}, {"name": "array layer", "type": "uint32_t", "default": "0"}, - {"name": "origin", "type": "origin 3D"} + {"name": "origin", "type": "origin 3D"}, + {"name": "aspect", "type": "texture aspect", "default": "all"} ] }, "texture data layout": { diff --git a/src/dawn_native/CommandEncoder.cpp b/src/dawn_native/CommandEncoder.cpp index 1a387b7e4f..71a66fce4a 100644 --- a/src/dawn_native/CommandEncoder.cpp +++ b/src/dawn_native/CommandEncoder.cpp @@ -100,6 +100,12 @@ namespace dawn_native { return DAWN_VALIDATION_ERROR("Source and destination texture formats must match."); } + if (src.aspect != wgpu::TextureAspect::All || dst.aspect != wgpu::TextureAspect::All) { + // Metal cannot select a single aspect for texture-to-texture copies + return DAWN_VALIDATION_ERROR( + "Texture aspect must be \"all\" for texture to texture copies"); + } + if (src.texture->GetFormat().HasDepthOrStencil()) { // D3D12 requires entire subresource to be copied when using CopyTextureRegion is // used with depth/stencil. @@ -119,6 +125,55 @@ namespace dawn_native { return {}; } + MaybeError ValidateTextureToBufferCopyRestrictions(const TextureCopyView& src) { + const Format& format = src.texture->GetFormat(); + + bool depthSelected = false; + switch (src.aspect) { + case wgpu::TextureAspect::All: + switch (format.aspects) { + case Aspect::Color: + case Aspect::Stencil: + break; + case Aspect::Depth: + depthSelected = true; + break; + default: + return DAWN_VALIDATION_ERROR( + "A single aspect must be selected for multi planar formats in " + "texture to buffer copies"); + } + break; + case wgpu::TextureAspect::DepthOnly: + ASSERT(format.aspects & Aspect::Depth); + depthSelected = true; + break; + case wgpu::TextureAspect::StencilOnly: + ASSERT(format.aspects & Aspect::Stencil); + break; + default: + UNREACHABLE(); + } + + if (depthSelected) { + switch (format.format) { + case wgpu::TextureFormat::Depth24Plus: + case wgpu::TextureFormat::Depth24PlusStencil8: + return DAWN_VALIDATION_ERROR( + "The depth aspect of depth24plus texture cannot be selected in a " + "texture to buffer copy"); + break; + case wgpu::TextureFormat::Depth32Float: + break; + default: + UNREACHABLE(); + break; + } + } + + return {}; + } + MaybeError ValidateCanUseAs(const BufferBase* buffer, wgpu::BufferUsage usage) { ASSERT(wgpu::HasZeroOrOneBits(usage)); if (!(buffer->GetUsage() & usage)) { @@ -661,8 +716,11 @@ namespace dawn_native { // copyExtent.height by blockHeight while the divisibility conditions are // checked in validating texture copy range. DAWN_TRY(ValidateTextureCopyRange(*destination, *copySize)); - DAWN_TRY(ValidateLinearTextureData(source->layout, source->buffer->GetSize(), - destination->texture->GetFormat(), *copySize)); + DAWN_TRY(ValidateBufferToTextureCopyRestrictions(*destination)); + DAWN_TRY(ValidateLinearTextureData( + source->layout, source->buffer->GetSize(), + destination->texture->GetFormat().GetTexelBlockInfo(destination->aspect), + *copySize)); mTopLevelBuffers.insert(source->buffer); mTopLevelTextures.insert(destination->texture); @@ -718,9 +776,10 @@ namespace dawn_native { // copyExtent.height by blockHeight while the divisibility conditions are // checked in validating texture copy range. DAWN_TRY(ValidateTextureCopyRange(*source, *copySize)); - DAWN_TRY(ValidateLinearTextureData(destination->layout, - destination->buffer->GetSize(), - source->texture->GetFormat(), *copySize)); + DAWN_TRY(ValidateTextureToBufferCopyRestrictions(*source)); + DAWN_TRY(ValidateLinearTextureData( + destination->layout, destination->buffer->GetSize(), + source->texture->GetFormat().GetTexelBlockInfo(source->aspect), *copySize)); mTopLevelTextures.insert(source->texture); mTopLevelBuffers.insert(destination->buffer); diff --git a/src/dawn_native/CommandValidation.cpp b/src/dawn_native/CommandValidation.cpp index 11a363a1ca..095cf61a83 100644 --- a/src/dawn_native/CommandValidation.cpp +++ b/src/dawn_native/CommandValidation.cpp @@ -370,7 +370,7 @@ namespace dawn_native { static_cast(maxStart); } - uint32_t ComputeRequiredBytesInCopy(const Format& textureFormat, + uint32_t ComputeRequiredBytesInCopy(const TexelBlockInfo& blockInfo, const Extent3D& copySize, uint32_t bytesPerRow, uint32_t rowsPerImage) { @@ -386,11 +386,11 @@ namespace dawn_native { ASSERT(copySize.height >= 1); ASSERT(copySize.depth >= 1); - uint64_t texelBlockRowsPerImage = rowsPerImage / textureFormat.blockHeight; + uint64_t texelBlockRowsPerImage = rowsPerImage / blockInfo.blockHeight; uint64_t bytesPerImage = bytesPerRow * texelBlockRowsPerImage; uint64_t bytesInLastSlice = - bytesPerRow * (copySize.height / textureFormat.blockHeight - 1) + - (copySize.width / textureFormat.blockWidth * textureFormat.blockByteSize); + bytesPerRow * (copySize.height / blockInfo.blockHeight - 1) + + (copySize.width / blockInfo.blockWidth * blockInfo.blockByteSize); return bytesPerImage * (copySize.depth - 1) + bytesInLastSlice; } @@ -408,15 +408,15 @@ namespace dawn_native { MaybeError ValidateLinearTextureData(const TextureDataLayout& layout, uint64_t byteSize, - const Format& format, + const TexelBlockInfo& blockInfo, const Extent3D& copyExtent) { // Validation for the texel block alignments: - if (layout.rowsPerImage % format.blockHeight != 0) { + if (layout.rowsPerImage % blockInfo.blockHeight != 0) { return DAWN_VALIDATION_ERROR( "rowsPerImage must be a multiple of compressed texture format block height"); } - if (layout.offset % format.blockByteSize != 0) { + if (layout.offset % blockInfo.blockByteSize != 0) { return DAWN_VALIDATION_ERROR("Offset must be a multiple of the texel or block size"); } @@ -429,8 +429,8 @@ namespace dawn_native { // because the divisibility conditions are necessary for the algorithm to be valid. // TODO(tommek@google.com): to match the spec this should only be checked when // copyExtent.depth > 1. - uint32_t requiredBytesInCopy = - ComputeRequiredBytesInCopy(format, copyExtent, layout.bytesPerRow, layout.rowsPerImage); + uint32_t requiredBytesInCopy = ComputeRequiredBytesInCopy( + blockInfo, copyExtent, layout.bytesPerRow, layout.rowsPerImage); bool fitsInData = layout.offset <= byteSize && (requiredBytesInCopy <= (byteSize - layout.offset)); @@ -440,7 +440,8 @@ namespace dawn_native { } // Validation for other members in layout: - if (layout.bytesPerRow < copyExtent.width / format.blockWidth * format.blockByteSize) { + if (layout.bytesPerRow < + copyExtent.width / blockInfo.blockWidth * blockInfo.blockByteSize) { return DAWN_VALIDATION_ERROR( "bytesPerRow must not be less than the number of bytes per row"); } @@ -482,6 +483,26 @@ namespace dawn_native { "Offset.y must be a multiple of compressed texture format block height"); } + switch (textureCopy.aspect) { + case wgpu::TextureAspect::All: + break; + case wgpu::TextureAspect::DepthOnly: + if ((textureCopy.texture->GetFormat().aspects & Aspect::Depth) == 0) { + return DAWN_VALIDATION_ERROR( + "Texture does not have depth aspect for texture copy"); + } + break; + case wgpu::TextureAspect::StencilOnly: + if ((textureCopy.texture->GetFormat().aspects & Aspect::Stencil) == 0) { + return DAWN_VALIDATION_ERROR( + "Texture does not have stencil aspect for texture copy"); + } + break; + default: + UNREACHABLE(); + break; + } + return {}; } @@ -523,4 +544,37 @@ namespace dawn_native { return {}; } + MaybeError ValidateBufferToTextureCopyRestrictions(const TextureCopyView& dst) { + const Format& format = dst.texture->GetFormat(); + + bool depthSelected = false; + switch (dst.aspect) { + case wgpu::TextureAspect::All: + switch (format.aspects) { + case Aspect::Color: + case Aspect::Stencil: + break; + case Aspect::Depth: + depthSelected = true; + break; + default: + return DAWN_VALIDATION_ERROR( + "A single aspect must be selected for multi planar formats in buffer " + "to texture copies"); + } + break; + case wgpu::TextureAspect::DepthOnly: + ASSERT(format.aspects & Aspect::Depth); + depthSelected = true; + break; + case wgpu::TextureAspect::StencilOnly: + ASSERT(format.aspects & Aspect::Stencil); + break; + } + if (depthSelected) { + return DAWN_VALIDATION_ERROR("Cannot copy into the depth aspect of a texture"); + } + return {}; + } + } // namespace dawn_native diff --git a/src/dawn_native/CommandValidation.h b/src/dawn_native/CommandValidation.h index ae0464c0ac..719ce17998 100644 --- a/src/dawn_native/CommandValidation.h +++ b/src/dawn_native/CommandValidation.h @@ -27,6 +27,7 @@ namespace dawn_native { class QuerySetBase; struct BeginRenderPassCmd; struct PassResourceUsage; + struct TexelBlockInfo; MaybeError ValidateCanPopDebugGroup(uint64_t debugGroupStackSize); MaybeError ValidateFinalDebugGroupStackSize(uint64_t debugGroupStackSize); @@ -40,17 +41,18 @@ namespace dawn_native { MaybeError ValidateTimestampQuery(QuerySetBase* querySet, uint32_t queryIndex); - uint32_t ComputeRequiredBytesInCopy(const Format& textureFormat, + uint32_t ComputeRequiredBytesInCopy(const TexelBlockInfo& blockInfo, const Extent3D& copySize, uint32_t bytesPerRow, uint32_t rowsPerImage); MaybeError ValidateLinearTextureData(const TextureDataLayout& layout, uint64_t byteSize, - const Format& format, + const TexelBlockInfo& blockInfo, const Extent3D& copyExtent); MaybeError ValidateTextureCopyRange(const TextureCopyView& textureCopyView, const Extent3D& copySize); + MaybeError ValidateBufferToTextureCopyRestrictions(const TextureCopyView& dst); MaybeError ValidateBufferCopyView(DeviceBase const* device, const BufferCopyView& bufferCopyView); diff --git a/src/dawn_native/Format.cpp b/src/dawn_native/Format.cpp index d80db78857..92a8a606d5 100644 --- a/src/dawn_native/Format.cpp +++ b/src/dawn_native/Format.cpp @@ -79,6 +79,47 @@ namespace dawn_native { return componentType == type; } + TexelBlockInfo Format::GetTexelBlockInfo(wgpu::TextureAspect aspect) const { + switch (aspect) { + case wgpu::TextureAspect::All: + switch (aspects) { + case Aspect::Color: + case Aspect::Depth: + case Aspect::Stencil: + break; + default: + UNREACHABLE(); + } + return *this; + + case wgpu::TextureAspect::DepthOnly: + ASSERT(HasDepth()); + switch (format) { + case wgpu::TextureFormat::Depth32Float: + return *this; + default: + UNREACHABLE(); + break; + } + break; + + case wgpu::TextureAspect::StencilOnly: + ASSERT(HasStencil()); + switch (format) { + case wgpu::TextureFormat::Depth24PlusStencil8: + return {1, 1, 1}; + default: + UNREACHABLE(); + break; + } + break; + + default: + UNREACHABLE(); + break; + } + } + size_t Format::GetIndex() const { return ComputeFormatIndex(format); } diff --git a/src/dawn_native/Format.h b/src/dawn_native/Format.h index a8907ad0cf..f57370ea80 100644 --- a/src/dawn_native/Format.h +++ b/src/dawn_native/Format.h @@ -29,12 +29,18 @@ namespace dawn_native { enum class Aspect : uint8_t; class DeviceBase; + struct TexelBlockInfo { + uint32_t blockByteSize; + uint32_t blockWidth; + uint32_t blockHeight; + }; + // The number of formats Dawn knows about. Asserts in BuildFormatTable ensure that this is the // exact number of known format. static constexpr size_t kKnownFormatCount = 52; // A wgpu::TextureFormat along with all the information about it necessary for validation. - struct Format { + struct Format : TexelBlockInfo { enum class Type { Float, Sint, @@ -51,10 +57,6 @@ namespace dawn_native { Type type; Aspect aspects; - uint32_t blockByteSize; - uint32_t blockWidth; - uint32_t blockHeight; - static Type TextureComponentTypeToFormatType(wgpu::TextureComponentType componentType); static wgpu::TextureComponentType FormatTypeToTextureComponentType(Type type); @@ -64,6 +66,8 @@ namespace dawn_native { bool HasDepthOrStencil() const; bool HasComponentType(Type componentType) const; + TexelBlockInfo GetTexelBlockInfo(wgpu::TextureAspect aspect) const; + // The index of the format in the list of all known formats: a unique number for each format // in [0, kKnownFormatCount) size_t GetIndex() const; diff --git a/src/dawn_native/Queue.cpp b/src/dawn_native/Queue.cpp index 372885bd92..900b92a553 100644 --- a/src/dawn_native/Queue.cpp +++ b/src/dawn_native/Queue.cpp @@ -275,8 +275,10 @@ namespace dawn_native { // copyExtent.height by blockHeight while the divisibility conditions are // checked in validating texture copy range. DAWN_TRY(ValidateTextureCopyRange(*destination, *writeSize)); - DAWN_TRY(ValidateLinearTextureData(*dataLayout, dataSize, destination->texture->GetFormat(), - *writeSize)); + DAWN_TRY(ValidateBufferToTextureCopyRestrictions(*destination)); + DAWN_TRY(ValidateLinearTextureData( + *dataLayout, dataSize, + destination->texture->GetFormat().GetTexelBlockInfo(destination->aspect), *writeSize)); return {}; } diff --git a/src/tests/unittests/validation/CopyCommandsValidationTests.cpp b/src/tests/unittests/validation/CopyCommandsValidationTests.cpp index e5877af1f4..f5443462c4 100644 --- a/src/tests/unittests/validation/CopyCommandsValidationTests.cpp +++ b/src/tests/unittests/validation/CopyCommandsValidationTests.cpp @@ -75,11 +75,12 @@ class CopyCommandTest : public ValidationTest { wgpu::Texture destTexture, uint32_t destLevel, wgpu::Origin3D destOrigin, - wgpu::Extent3D extent3D) { + wgpu::Extent3D extent3D, + wgpu::TextureAspect aspect = wgpu::TextureAspect::All) { wgpu::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(srcBuffer, srcOffset, srcBytesPerRow, srcRowsPerImage); wgpu::TextureCopyView textureCopyView = - utils::CreateTextureCopyView(destTexture, destLevel, destOrigin); + utils::CreateTextureCopyView(destTexture, destLevel, destOrigin, aspect); wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, &extent3D); @@ -95,11 +96,12 @@ class CopyCommandTest : public ValidationTest { uint64_t destOffset, uint32_t destBytesPerRow, uint32_t destRowsPerImage, - wgpu::Extent3D extent3D) { + wgpu::Extent3D extent3D, + wgpu::TextureAspect aspect = wgpu::TextureAspect::All) { wgpu::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(destBuffer, destOffset, destBytesPerRow, destRowsPerImage); wgpu::TextureCopyView textureCopyView = - utils::CreateTextureCopyView(srcTexture, srcLevel, srcOrigin); + utils::CreateTextureCopyView(srcTexture, srcLevel, srcOrigin, aspect); wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyTextureToBuffer(&textureCopyView, &bufferCopyView, &extent3D); @@ -114,11 +116,12 @@ class CopyCommandTest : public ValidationTest { wgpu::Texture dstTexture, uint32_t dstLevel, wgpu::Origin3D dstOrigin, - wgpu::Extent3D extent3D) { + wgpu::Extent3D extent3D, + wgpu::TextureAspect aspect = wgpu::TextureAspect::All) { wgpu::TextureCopyView srcTextureCopyView = - utils::CreateTextureCopyView(srcTexture, srcLevel, srcOrigin); + utils::CreateTextureCopyView(srcTexture, srcLevel, srcOrigin, aspect); wgpu::TextureCopyView dstTextureCopyView = - utils::CreateTextureCopyView(dstTexture, dstLevel, dstOrigin); + utils::CreateTextureCopyView(dstTexture, dstLevel, dstOrigin, aspect); wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyTextureToTexture(&srcTextureCopyView, &dstTextureCopyView, &extent3D); @@ -682,6 +685,83 @@ TEST_F(CopyCommandTest_B2T, CopyToMipmapOfNonSquareTexture) { {0, 0, 0}, {2, 2, 1}); } +// Test it is invalid to copy to a depth texture +TEST_F(CopyCommandTest_B2T, CopyToDepthAspect) { + // Test it is invalid to copy from a buffer into Depth32Float + { + uint64_t bufferSize = BufferSizeForTextureCopy(16, 16, 1, wgpu::TextureFormat::R32Float); + wgpu::Buffer source = CreateBuffer(bufferSize, wgpu::BufferUsage::CopySrc); + + wgpu::Texture destination = Create2DTexture(16, 16, 1, 1, wgpu::TextureFormat::Depth32Float, + wgpu::TextureUsage::CopyDst); + + TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, destination, 0, {0, 0, 0}, + {16, 16, 1}, wgpu::TextureAspect::All); + + TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, destination, 0, {0, 0, 0}, + {16, 16, 1}, wgpu::TextureAspect::DepthOnly); + } + + // Test it is invalid to copy from a buffer into Depth24Plus + { + uint64_t bufferSize = BufferSizeForTextureCopy(16, 16, 1, wgpu::TextureFormat::R32Float); + wgpu::Buffer source = CreateBuffer(bufferSize, wgpu::BufferUsage::CopySrc); + + wgpu::Texture destination = Create2DTexture(16, 16, 1, 1, wgpu::TextureFormat::Depth24Plus, + wgpu::TextureUsage::CopyDst); + + TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, destination, 0, {0, 0, 0}, + {16, 16, 1}, wgpu::TextureAspect::All); + + TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, destination, 0, {0, 0, 0}, + {16, 16, 1}, wgpu::TextureAspect::DepthOnly); + } +} + +// Test copy to only the stencil aspect of a texture +TEST_F(CopyCommandTest_B2T, CopyToStencilAspect) { + // Test it is valid to copy from a buffer into the stencil aspect of Depth24PlusStencil8 + { + uint64_t bufferSize = BufferSizeForTextureCopy(16, 16, 1, wgpu::TextureFormat::R8Uint); + wgpu::Buffer source = CreateBuffer(bufferSize, wgpu::BufferUsage::CopySrc); + + wgpu::Texture destination = Create2DTexture( + 16, 16, 1, 1, wgpu::TextureFormat::Depth24PlusStencil8, wgpu::TextureUsage::CopyDst); + + TestB2TCopy(utils::Expectation::Success, source, 0, 256, 0, destination, 0, {0, 0, 0}, + {16, 16, 1}, wgpu::TextureAspect::StencilOnly); + + // And that it fails if the buffer is one byte too small + wgpu::Buffer sourceSmall = CreateBuffer(bufferSize - 1, wgpu::BufferUsage::CopySrc); + TestB2TCopy(utils::Expectation::Failure, sourceSmall, 0, 256, 0, destination, 0, {0, 0, 0}, + {16, 16, 1}, wgpu::TextureAspect::StencilOnly); + } + + // Test it is invalid to copy from a buffer into the stencil aspect of Depth24Plus (no stencil) + { + uint64_t bufferSize = BufferSizeForTextureCopy(16, 16, 1, wgpu::TextureFormat::R8Uint); + wgpu::Buffer source = CreateBuffer(bufferSize, wgpu::BufferUsage::CopySrc); + + wgpu::Texture destination = Create2DTexture(16, 16, 1, 1, wgpu::TextureFormat::Depth24Plus, + wgpu::TextureUsage::CopyDst); + + TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, destination, 0, {0, 0, 0}, + {16, 16, 1}, wgpu::TextureAspect::StencilOnly); + } + + // Test it is invalid to copy from a buffer into the stencil aspect of a color texture + { + uint64_t bufferSize = BufferSizeForTextureCopy(16, 16, 1, wgpu::TextureFormat::R8Uint); + wgpu::Buffer source = CreateBuffer(bufferSize, wgpu::BufferUsage::CopySrc); + + wgpu::Texture destination = Create2DTexture(16, 16, 1, 1, wgpu::TextureFormat::RGBA8Uint, + wgpu::TextureUsage::CopyDst); + + TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, destination, 0, {0, 0, 0}, + {16, 16, 1}, wgpu::TextureAspect::StencilOnly); + } +} + class CopyCommandTest_T2B : public CopyCommandTest {}; // Test a successfull T2B copy @@ -1010,6 +1090,83 @@ TEST_F(CopyCommandTest_T2B, CopyFromMipmapOfNonSquareTexture) { 256, 0, {2, 1, 1}); } +// Test copy from only the depth aspect of a texture +TEST_F(CopyCommandTest_T2B, CopyFromDepthAspect) { + uint64_t bufferSize = BufferSizeForTextureCopy(16, 16, 1, wgpu::TextureFormat::R32Float); + wgpu::Buffer destination = CreateBuffer(bufferSize, wgpu::BufferUsage::CopyDst); + { + wgpu::Texture source = Create2DTexture(16, 16, 1, 1, wgpu::TextureFormat::Depth32Float, + wgpu::TextureUsage::CopySrc); + + // Test "all" of a depth texture which is only the depth aspect. + TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, 256, 0, + {16, 16, 1}, wgpu::TextureAspect::All); + + // Test it is valid to copy the depth aspect of a depth texture + TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, 256, 0, + {16, 16, 1}, wgpu::TextureAspect::DepthOnly); + } + { + wgpu::Texture source = Create2DTexture(16, 16, 1, 1, wgpu::TextureFormat::Depth24Plus, + wgpu::TextureUsage::CopySrc); + + // Test it is invalid to copy from the depth aspect of depth24plus + TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, 256, 0, + {16, 16, 1}, wgpu::TextureAspect::DepthOnly); + } + { + wgpu::Texture source = Create2DTexture( + 16, 16, 1, 1, wgpu::TextureFormat::Depth24PlusStencil8, wgpu::TextureUsage::CopySrc); + + // Test it is invalid to copy from the depth aspect of depth24plus-stencil8 + TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, 256, 0, + {16, 16, 1}, wgpu::TextureAspect::DepthOnly); + } + { + wgpu::Texture source = Create2DTexture(16, 16, 1, 1, wgpu::TextureFormat::R32Float, + wgpu::TextureUsage::CopySrc); + + // Test it is invalid to copy from the depth aspect of a color texture + TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, 256, 0, + {16, 16, 1}, wgpu::TextureAspect::DepthOnly); + } +} + +// Test copy from only the stencil aspect of a texture +TEST_F(CopyCommandTest_T2B, CopyFromStencilAspect) { + uint64_t bufferSize = BufferSizeForTextureCopy(16, 16, 1, wgpu::TextureFormat::R8Uint); + wgpu::Buffer destination = CreateBuffer(bufferSize, wgpu::BufferUsage::CopyDst); + { + wgpu::Texture source = Create2DTexture( + 16, 16, 1, 1, wgpu::TextureFormat::Depth24PlusStencil8, wgpu::TextureUsage::CopySrc); + + // Test it is valid to copy from the stencil aspect of a depth24plus-stencil8 texture + TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, 256, 0, + {16, 16, 1}, wgpu::TextureAspect::StencilOnly); + + // Test it is invalid if the buffer is too small + wgpu::Buffer destinationSmall = CreateBuffer(bufferSize - 1, wgpu::BufferUsage::CopyDst); + TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destinationSmall, 0, 256, 0, + {16, 16, 1}, wgpu::TextureAspect::StencilOnly); + } + { + wgpu::Texture source = + Create2DTexture(16, 16, 1, 1, wgpu::TextureFormat::R8Uint, wgpu::TextureUsage::CopySrc); + + // Test it is invalid to copy from the stencil aspect of a color texture + TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, 256, 0, + {16, 16, 1}, wgpu::TextureAspect::StencilOnly); + } + { + wgpu::Texture source = Create2DTexture(16, 16, 1, 1, wgpu::TextureFormat::Depth24Plus, + wgpu::TextureUsage::CopySrc); + + // Test it is invalid to copy from the stencil aspect of a depth-only texture + TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, 256, 0, + {16, 16, 1}, wgpu::TextureAspect::StencilOnly); + } +} + class CopyCommandTest_T2T : public CopyCommandTest {}; TEST_F(CopyCommandTest_T2T, Success) { @@ -1164,6 +1321,14 @@ TEST_F(CopyCommandTest_T2T, 2DTextureDepthStencil) { // Failure when depth stencil subresource is partially copied TestT2TCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, {0, 0, 0}, {15, 15, 1}); + + // Failure when selecting the depth aspect (not all) + TestT2TCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, {0, 0, 0}, + {16, 16, 1}, wgpu::TextureAspect::DepthOnly); + + // Failure when selecting the stencil aspect (not all) + TestT2TCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, {0, 0, 0}, + {16, 16, 1}, wgpu::TextureAspect::StencilOnly); } TEST_F(CopyCommandTest_T2T, 2DTextureArrayDepthStencil) { diff --git a/src/tests/unittests/validation/QueueWriteTextureValidationTests.cpp b/src/tests/unittests/validation/QueueWriteTextureValidationTests.cpp index c238acf3e7..c689f58e05 100644 --- a/src/tests/unittests/validation/QueueWriteTextureValidationTests.cpp +++ b/src/tests/unittests/validation/QueueWriteTextureValidationTests.cpp @@ -53,7 +53,8 @@ namespace { wgpu::Texture texture, uint32_t texLevel, wgpu::Origin3D texOrigin, - wgpu::Extent3D size) { + wgpu::Extent3D size, + wgpu::TextureAspect aspect = wgpu::TextureAspect::All) { std::vector data(dataSize); wgpu::TextureDataLayout textureDataLayout; @@ -62,7 +63,7 @@ namespace { textureDataLayout.rowsPerImage = dataRowsPerImage; wgpu::TextureCopyView textureCopyView = - utils::CreateTextureCopyView(texture, texLevel, texOrigin); + utils::CreateTextureCopyView(texture, texLevel, texOrigin, aspect); queue.WriteTexture(&textureCopyView, data.data(), dataSize, &textureDataLayout, &size); } @@ -402,6 +403,71 @@ namespace { {0, 0, 1}, {4, 2, 3}); } + // Test it is invalid to write into a depth texture. + TEST_F(QueueWriteTextureValidationTest, WriteToDepthAspect) { + uint32_t bytesPerRow = sizeof(float) * 4; + const uint64_t dataSize = utils::RequiredBytesInCopy(bytesPerRow, 0, {4, 4, 1}, + wgpu::TextureFormat::Depth32Float); + + // Invalid to write into depth32float + { + wgpu::Texture destination = QueueWriteTextureValidationTest::Create2DTexture( + {4, 4, 1}, 1, wgpu::TextureFormat::Depth32Float, wgpu::TextureUsage::CopyDst); + + ASSERT_DEVICE_ERROR(TestWriteTexture(dataSize, 0, bytesPerRow, 0, destination, 0, + {0, 0, 0}, {4, 4, 1}, wgpu::TextureAspect::All)); + + ASSERT_DEVICE_ERROR(TestWriteTexture(dataSize, 0, bytesPerRow, 0, destination, 0, + {0, 0, 0}, {4, 4, 1}, + wgpu::TextureAspect::DepthOnly)); + } + + // Invalid to write into depth24plus + { + wgpu::Texture destination = QueueWriteTextureValidationTest::Create2DTexture( + {4, 4, 1}, 1, wgpu::TextureFormat::Depth24Plus, wgpu::TextureUsage::CopyDst); + + ASSERT_DEVICE_ERROR(TestWriteTexture(dataSize, 0, bytesPerRow, 0, destination, 0, + {0, 0, 0}, {4, 4, 1}, wgpu::TextureAspect::All)); + + ASSERT_DEVICE_ERROR(TestWriteTexture(dataSize, 0, bytesPerRow, 0, destination, 0, + {0, 0, 0}, {4, 4, 1}, + wgpu::TextureAspect::DepthOnly)); + } + } + + // Test write texture to the stencil aspect + TEST_F(QueueWriteTextureValidationTest, WriteToStencilAspect) { + uint32_t bytesPerRow = 4; + const uint64_t dataSize = + utils::RequiredBytesInCopy(bytesPerRow, 0, {4, 4, 1}, wgpu::TextureFormat::R8Uint); + + // It is valid to write into the stencil aspect of depth24plus-stencil8 + { + wgpu::Texture destination = QueueWriteTextureValidationTest::Create2DTexture( + {4, 4, 1}, 1, wgpu::TextureFormat::Depth24PlusStencil8, + wgpu::TextureUsage::CopyDst); + + TestWriteTexture(dataSize, 0, bytesPerRow, 0, destination, 0, {0, 0, 0}, {4, 4, 1}, + wgpu::TextureAspect::StencilOnly); + + // And that it fails if the buffer is one byte too small + ASSERT_DEVICE_ERROR(TestWriteTexture(dataSize - 1, 0, bytesPerRow, 0, destination, 0, + {0, 0, 0}, {4, 4, 1}, + wgpu::TextureAspect::StencilOnly)); + } + + // It is invalid to write into the stencil aspect of depth24plus (no stencil) + { + wgpu::Texture destination = QueueWriteTextureValidationTest::Create2DTexture( + {4, 4, 1}, 1, wgpu::TextureFormat::Depth24Plus, wgpu::TextureUsage::CopyDst); + + ASSERT_DEVICE_ERROR(TestWriteTexture(dataSize, 0, bytesPerRow, 0, destination, 0, + {0, 0, 0}, {4, 4, 1}, + wgpu::TextureAspect::StencilOnly)); + } + } + class WriteTextureTest_CompressedTextureFormats : public QueueWriteTextureValidationTest { public: WriteTextureTest_CompressedTextureFormats() : QueueWriteTextureValidationTest() { diff --git a/src/utils/WGPUHelpers.cpp b/src/utils/WGPUHelpers.cpp index 0a47b8d709..b9a51bbec0 100644 --- a/src/utils/WGPUHelpers.cpp +++ b/src/utils/WGPUHelpers.cpp @@ -276,11 +276,13 @@ namespace utils { wgpu::TextureCopyView CreateTextureCopyView(wgpu::Texture texture, uint32_t mipLevel, - wgpu::Origin3D origin) { + wgpu::Origin3D origin, + wgpu::TextureAspect aspect) { wgpu::TextureCopyView textureCopyView; textureCopyView.texture = texture; textureCopyView.mipLevel = mipLevel; textureCopyView.origin = origin; + textureCopyView.aspect = aspect; return textureCopyView; } diff --git a/src/utils/WGPUHelpers.h b/src/utils/WGPUHelpers.h index 0c63c4de97..03e618cc2c 100644 --- a/src/utils/WGPUHelpers.h +++ b/src/utils/WGPUHelpers.h @@ -52,9 +52,11 @@ namespace utils { uint64_t offset, uint32_t bytesPerRow, uint32_t rowsPerImage); - wgpu::TextureCopyView CreateTextureCopyView(wgpu::Texture texture, - uint32_t level, - wgpu::Origin3D origin); + wgpu::TextureCopyView CreateTextureCopyView( + wgpu::Texture texture, + uint32_t level, + wgpu::Origin3D origin, + wgpu::TextureAspect aspect = wgpu::TextureAspect::All); wgpu::TextureDataLayout CreateTextureDataLayout(uint64_t offset, uint32_t bytesPerRow, uint32_t rowsPerImage);