Modified: trunk/Source/WebGPU/ChangeLog (292198 => 292199)
--- trunk/Source/WebGPU/ChangeLog 2022-04-01 03:39:53 UTC (rev 292198)
+++ trunk/Source/WebGPU/ChangeLog 2022-04-01 04:48:09 UTC (rev 292199)
@@ -1,5 +1,24 @@
2022-03-31 Myles C. Maxfield <[email protected]>
+ [WebGPU] Implement CommandEncoder::copyTextureToBuffer() according to the spec.
+ https://bugs.webkit.org/show_bug.cgi?id=238430
+
+ Reviewed by Dean Jackson.
+
+ This implements CommandEncoder::copyTextureToBuffer() according to the spec.
+ It re-uses many of the helper functions added in
+ https://bugs.webkit.org/show_bug.cgi?id=238428. There are links and quotes from
+ the spec where appropriate.
+
+ * WebGPU/CommandEncoder.mm:
+ (WebGPU::validateCopyTextureToBuffer):
+ (WebGPU::CommandEncoder::copyTextureToBuffer):
+ * WebGPU/Texture.h:
+ * WebGPU/Texture.mm:
+ (WebGPU::Texture::isValidImageCopySource):
+
+2022-03-31 Myles C. Maxfield <[email protected]>
+
[WebGPU] Implement CommandEncoder::copyBufferToTexture() according to the spec
https://bugs.webkit.org/show_bug.cgi?id=238428
Modified: trunk/Source/WebGPU/WebGPU/CommandEncoder.mm (292198 => 292199)
--- trunk/Source/WebGPU/WebGPU/CommandEncoder.mm 2022-04-01 03:39:53 UTC (rev 292198)
+++ trunk/Source/WebGPU/WebGPU/CommandEncoder.mm 2022-04-01 04:48:09 UTC (rev 292199)
@@ -354,11 +354,187 @@
}
}
+static bool validateCopyTextureToBuffer(const WGPUImageCopyTexture& source, const WGPUImageCopyBuffer& destination, const WGPUExtent3D& copySize)
+{
+ // "Let srcTextureDesc be source.texture.[[descriptor]]."
+ const auto& srcTextureDesc = fromAPI(source.texture).descriptor();
+
+ // "validating GPUImageCopyTexture(source, copySize) returns true."
+ if (!Texture::validateImageCopyTexture(source, copySize))
+ return false;
+
+ // "srcTextureDesc.usage contains COPY_SRC."
+ if (!(srcTextureDesc.usage & WGPUBufferUsage_CopySrc))
+ return false;
+
+ // "srcTextureDesc.sampleCount is 1."
+ if (srcTextureDesc.sampleCount != 1)
+ return false;
+
+ // "Let aspectSpecificFormat = dstTextureDesc.format."
+ WGPUTextureFormat aspectSpecificFormat = srcTextureDesc.format;
+
+ // "If srcTextureDesc.format is a depth-stencil format:"
+ if (Texture::isDepthOrStencilFormat(srcTextureDesc.format)) {
+ // "source.aspect must refer to a single aspect of srcTextureDesc.format"
+ if (!Texture::refersToSingleAspect(srcTextureDesc.format, source.aspect))
+ return false;
+
+ // "and that aspect must be a valid image copy source according to § 25.1.2 Depth-stencil formats."
+ if (!Texture::isValidImageCopySource(srcTextureDesc.format, source.aspect))
+ return false;
+
+ // "Set aspectSpecificFormat to the aspect-specific format according to § 25.1.2 Depth-stencil formats."
+ aspectSpecificFormat = Texture::aspectSpecificFormat(srcTextureDesc.format, source.aspect);
+ }
+
+ // "validating GPUImageCopyBuffer(destination) returns true."
+ if (!validateImageCopyBuffer(destination))
+ return false;
+
+ // "destination.buffer.[[usage]] contains COPY_DST."
+ if (!(fromAPI(destination.buffer).usage() & WGPUBufferUsage_CopyDst))
+ return false;
+
+ // "validating texture copy range(source, copySize) returns true."
+ if (!Texture::validateTextureCopyRange(source, copySize))
+ return false;
+
+ // "If srcTextureDesc.format is not a depth-or-stencil format:"
+ if (!Texture::isDepthOrStencilFormat(srcTextureDesc.format)) {
+ // "destination.offset is a multiple of the texel block size of srcTextureDesc.format."
+ auto texelBlockSize = Texture::texelBlockSize(srcTextureDesc.format);
+ if (destination.layout.offset % texelBlockSize)
+ return false;
+ }
+
+ // "If srcTextureDesc.format is a depth-or-stencil format:"
+ if (Texture::isDepthOrStencilFormat(srcTextureDesc.format)) {
+ // "destination.offset is a multiple of 4."
+ if (destination.layout.offset % 4)
+ return false;
+ }
+
+ // "validating linear texture data(destination, destination.buffer.[[size]], aspectSpecificFormat, copySize) succeeds."
+ if (!Texture::validateLinearTextureData(destination.layout, fromAPI(destination.buffer).size(), aspectSpecificFormat, copySize))
+ return false;
+
+ return true;
+}
+
void CommandEncoder::copyTextureToBuffer(const WGPUImageCopyTexture& source, const WGPUImageCopyBuffer& destination, const WGPUExtent3D& copySize)
{
- UNUSED_PARAM(source);
- UNUSED_PARAM(destination);
- UNUSED_PARAM(copySize);
+ if (source.nextInChain || destination.nextInChain || destination.layout.nextInChain)
+ return;
+
+ // https://gpuweb.github.io/gpuweb/#dom-gpucommandencoder-copytexturetobuffer
+
+ // "Prepare the encoder state of this. If it returns false, stop."
+ if (!prepareTheEncoderState())
+ return;
+
+ // "If any of the following conditions are unsatisfied"
+ if (!validateCopyTextureToBuffer(source, destination, copySize)) {
+ // "generate a validation error and stop."
+ m_device->generateAValidationError("Validation failure.");
+ return;
+ }
+
+ ensureBlitCommandEncoder();
+
+ NSUInteger destinationBytesPerRow = destination.layout.bytesPerRow;
+
+ // FIXME: Use checked arithmetic
+ NSUInteger destinationBytesPerImage = destination.layout.rowsPerImage * destination.layout.bytesPerRow;
+
+ MTLBlitOption options = MTLBlitOptionNone;
+ switch (source.aspect) {
+ case WGPUTextureAspect_All:
+ options = MTLBlitOptionNone;
+ break;
+ case WGPUTextureAspect_StencilOnly:
+ options = MTLBlitOptionStencilFromDepthStencil;
+ break;
+ case WGPUTextureAspect_DepthOnly:
+ options = MTLBlitOptionDepthFromDepthStencil;
+ break;
+ case WGPUTextureAspect_Force32:
+ return;
+ }
+
+ auto logicalSize = fromAPI(source.texture).logicalMiplevelSpecificTextureExtent(source.mipLevel);
+ auto widthForMetal = std::min(copySize.width, logicalSize.width);
+ auto heightForMetal = std::min(copySize.height, logicalSize.height);
+ auto depthForMetal = std::min(copySize.depthOrArrayLayers, logicalSize.depthOrArrayLayers);
+
+ auto& sourceDescriptor = fromAPI(source.texture).descriptor();
+ switch (sourceDescriptor.dimension) {
+ case WGPUTextureDimension_1D: {
+ // https://developer.apple.com/documentation/metal/mtlblitcommandencoder/1400756-copyfromtexture?language=objc
+ // "When you copy to a 1D texture, height and depth must be 1."
+ auto sourceSize = MTLSizeMake(widthForMetal, 1, 1);
+ auto sourceOrigin = MTLOriginMake(source.origin.x, 1, 1);
+ for (uint32_t layer = 0; layer < copySize.depthOrArrayLayers; ++layer) {
+ // FIXME: Use checked arithmetic.
+ auto destinationOffset = static_cast<NSUInteger>(destination.layout.offset + layer * destinationBytesPerImage);
+ NSUInteger sourceSlice = source.origin.z + layer;
+ [m_blitCommandEncoder
+ copyFromTexture:fromAPI(source.texture).texture()
+ sourceSlice:sourceSlice
+ sourceLevel:source.mipLevel
+ sourceOrigin:sourceOrigin
+ sourceSize:sourceSize
+ toBuffer:fromAPI(destination.buffer).buffer()
+ destinationOffset:destinationOffset
+ destinationBytesPerRow:destinationBytesPerRow
+ destinationBytesPerImage:destinationBytesPerImage
+ options:options];
+ }
+ break;
+ }
+ case WGPUTextureDimension_2D: {
+ // https://developer.apple.com/documentation/metal/mtlblitcommandencoder/1400756-copyfromtexture?language=objc
+ // "When you copy to a 2D texture, depth must be 1."
+ auto sourceSize = MTLSizeMake(widthForMetal, heightForMetal, 1);
+ auto sourceOrigin = MTLOriginMake(source.origin.x, source.origin.y, 1);
+ for (uint32_t layer = 0; layer < copySize.depthOrArrayLayers; ++layer) {
+ // FIXME: Use checked arithmetic.
+ auto destinationOffset = static_cast<NSUInteger>(destination.layout.offset + layer * destinationBytesPerImage);
+ NSUInteger sourceSlice = source.origin.z + layer;
+ [m_blitCommandEncoder
+ copyFromTexture:fromAPI(source.texture).texture()
+ sourceSlice:sourceSlice
+ sourceLevel:source.mipLevel
+ sourceOrigin:sourceOrigin
+ sourceSize:sourceSize
+ toBuffer:fromAPI(destination.buffer).buffer()
+ destinationOffset:destinationOffset
+ destinationBytesPerRow:destinationBytesPerRow
+ destinationBytesPerImage:destinationBytesPerImage
+ options:options];
+ }
+ break;
+ }
+ case WGPUTextureDimension_3D: {
+ auto sourceSize = MTLSizeMake(widthForMetal, heightForMetal, depthForMetal);
+ auto sourceOrigin = MTLOriginMake(source.origin.x, source.origin.y, source.origin.z);
+ auto destinationOffset = static_cast<NSUInteger>(destination.layout.offset);
+ [m_blitCommandEncoder
+ copyFromTexture:fromAPI(source.texture).texture()
+ sourceSlice:0
+ sourceLevel:source.mipLevel
+ sourceOrigin:sourceOrigin
+ sourceSize:sourceSize
+ toBuffer:fromAPI(destination.buffer).buffer()
+ destinationOffset:destinationOffset
+ destinationBytesPerRow:destinationBytesPerRow
+ destinationBytesPerImage:destinationBytesPerImage
+ options:options];
+ break;
+ }
+ case WGPUTextureDimension_Force32:
+ return;
+ }
}
void CommandEncoder::copyTextureToTexture(const WGPUImageCopyTexture& source, const WGPUImageCopyTexture& destination, const WGPUExtent3D& copySize)
Modified: trunk/Source/WebGPU/WebGPU/Texture.h (292198 => 292199)
--- trunk/Source/WebGPU/WebGPU/Texture.h 2022-04-01 03:39:53 UTC (rev 292198)
+++ trunk/Source/WebGPU/WebGPU/Texture.h 2022-04-01 04:48:09 UTC (rev 292199)
@@ -64,6 +64,7 @@
static bool validateImageCopyTexture(const WGPUImageCopyTexture&, const WGPUExtent3D&);
static bool validateTextureCopyRange(const WGPUImageCopyTexture&, const WGPUExtent3D&);
static bool refersToSingleAspect(WGPUTextureFormat, WGPUTextureAspect);
+ static bool isValidImageCopySource(WGPUTextureFormat, WGPUTextureAspect);
static bool isValidImageCopyDestination(WGPUTextureFormat, WGPUTextureAspect);
static bool validateLinearTextureData(const WGPUTextureDataLayout&, uint64_t, WGPUTextureFormat, WGPUExtent3D);
Modified: trunk/Source/WebGPU/WebGPU/Texture.mm (292198 => 292199)
--- trunk/Source/WebGPU/WebGPU/Texture.mm 2022-04-01 03:39:53 UTC (rev 292198)
+++ trunk/Source/WebGPU/WebGPU/Texture.mm 2022-04-01 04:48:09 UTC (rev 292199)
@@ -2424,6 +2424,119 @@
return true;
}
+bool Texture::isValidImageCopySource(WGPUTextureFormat format, WGPUTextureAspect aspect)
+{
+ // https://gpuweb.github.io/gpuweb/#depth-formats
+
+ switch (format) {
+ case WGPUTextureFormat_Undefined:
+ case WGPUTextureFormat_R8Unorm:
+ case WGPUTextureFormat_R8Snorm:
+ case WGPUTextureFormat_R8Uint:
+ case WGPUTextureFormat_R8Sint:
+ case WGPUTextureFormat_R16Uint:
+ case WGPUTextureFormat_R16Sint:
+ case WGPUTextureFormat_R16Float:
+ case WGPUTextureFormat_RG8Unorm:
+ case WGPUTextureFormat_RG8Snorm:
+ case WGPUTextureFormat_RG8Uint:
+ case WGPUTextureFormat_RG8Sint:
+ case WGPUTextureFormat_R32Float:
+ case WGPUTextureFormat_R32Uint:
+ case WGPUTextureFormat_R32Sint:
+ case WGPUTextureFormat_RG16Uint:
+ case WGPUTextureFormat_RG16Sint:
+ case WGPUTextureFormat_RG16Float:
+ case WGPUTextureFormat_RGBA8Unorm:
+ case WGPUTextureFormat_RGBA8UnormSrgb:
+ case WGPUTextureFormat_RGBA8Snorm:
+ case WGPUTextureFormat_RGBA8Uint:
+ case WGPUTextureFormat_RGBA8Sint:
+ case WGPUTextureFormat_BGRA8Unorm:
+ case WGPUTextureFormat_BGRA8UnormSrgb:
+ case WGPUTextureFormat_RGB10A2Unorm:
+ case WGPUTextureFormat_RG11B10Ufloat:
+ case WGPUTextureFormat_RGB9E5Ufloat:
+ case WGPUTextureFormat_RG32Float:
+ case WGPUTextureFormat_RG32Uint:
+ case WGPUTextureFormat_RG32Sint:
+ case WGPUTextureFormat_RGBA16Uint:
+ case WGPUTextureFormat_RGBA16Sint:
+ case WGPUTextureFormat_RGBA16Float:
+ case WGPUTextureFormat_RGBA32Float:
+ case WGPUTextureFormat_RGBA32Uint:
+ case WGPUTextureFormat_RGBA32Sint:
+ ASSERT_NOT_REACHED();
+ return false;
+ case WGPUTextureFormat_Stencil8:
+ case WGPUTextureFormat_Depth16Unorm:
+ return true;
+ case WGPUTextureFormat_Depth24Plus:
+ return false;
+ case WGPUTextureFormat_Depth24PlusStencil8:
+ case WGPUTextureFormat_Depth24UnormStencil8:
+ return aspect == WGPUTextureAspect_StencilOnly;
+ case WGPUTextureFormat_Depth32Float:
+ case WGPUTextureFormat_Depth32FloatStencil8:
+ return true;
+ case WGPUTextureFormat_BC1RGBAUnorm:
+ case WGPUTextureFormat_BC1RGBAUnormSrgb:
+ case WGPUTextureFormat_BC2RGBAUnorm:
+ case WGPUTextureFormat_BC2RGBAUnormSrgb:
+ case WGPUTextureFormat_BC3RGBAUnorm:
+ case WGPUTextureFormat_BC3RGBAUnormSrgb:
+ case WGPUTextureFormat_BC4RUnorm:
+ case WGPUTextureFormat_BC4RSnorm:
+ case WGPUTextureFormat_BC5RGUnorm:
+ case WGPUTextureFormat_BC5RGSnorm:
+ case WGPUTextureFormat_BC6HRGBUfloat:
+ case WGPUTextureFormat_BC6HRGBFloat:
+ case WGPUTextureFormat_BC7RGBAUnorm:
+ case WGPUTextureFormat_BC7RGBAUnormSrgb:
+ case WGPUTextureFormat_ETC2RGB8Unorm:
+ case WGPUTextureFormat_ETC2RGB8UnormSrgb:
+ case WGPUTextureFormat_ETC2RGB8A1Unorm:
+ case WGPUTextureFormat_ETC2RGB8A1UnormSrgb:
+ case WGPUTextureFormat_ETC2RGBA8Unorm:
+ case WGPUTextureFormat_ETC2RGBA8UnormSrgb:
+ case WGPUTextureFormat_EACR11Unorm:
+ case WGPUTextureFormat_EACR11Snorm:
+ case WGPUTextureFormat_EACRG11Unorm:
+ case WGPUTextureFormat_EACRG11Snorm:
+ case WGPUTextureFormat_ASTC4x4Unorm:
+ case WGPUTextureFormat_ASTC4x4UnormSrgb:
+ case WGPUTextureFormat_ASTC5x4Unorm:
+ case WGPUTextureFormat_ASTC5x4UnormSrgb:
+ case WGPUTextureFormat_ASTC5x5Unorm:
+ case WGPUTextureFormat_ASTC5x5UnormSrgb:
+ case WGPUTextureFormat_ASTC6x5Unorm:
+ case WGPUTextureFormat_ASTC6x5UnormSrgb:
+ case WGPUTextureFormat_ASTC6x6Unorm:
+ case WGPUTextureFormat_ASTC6x6UnormSrgb:
+ case WGPUTextureFormat_ASTC8x5Unorm:
+ case WGPUTextureFormat_ASTC8x5UnormSrgb:
+ case WGPUTextureFormat_ASTC8x6Unorm:
+ case WGPUTextureFormat_ASTC8x6UnormSrgb:
+ case WGPUTextureFormat_ASTC8x8Unorm:
+ case WGPUTextureFormat_ASTC8x8UnormSrgb:
+ case WGPUTextureFormat_ASTC10x5Unorm:
+ case WGPUTextureFormat_ASTC10x5UnormSrgb:
+ case WGPUTextureFormat_ASTC10x6Unorm:
+ case WGPUTextureFormat_ASTC10x6UnormSrgb:
+ case WGPUTextureFormat_ASTC10x8Unorm:
+ case WGPUTextureFormat_ASTC10x8UnormSrgb:
+ case WGPUTextureFormat_ASTC10x10Unorm:
+ case WGPUTextureFormat_ASTC10x10UnormSrgb:
+ case WGPUTextureFormat_ASTC12x10Unorm:
+ case WGPUTextureFormat_ASTC12x10UnormSrgb:
+ case WGPUTextureFormat_ASTC12x12Unorm:
+ case WGPUTextureFormat_ASTC12x12UnormSrgb:
+ case WGPUTextureFormat_Force32:
+ ASSERT_NOT_REACHED();
+ return false;
+ }
+}
+
bool Texture::isValidImageCopyDestination(WGPUTextureFormat format, WGPUTextureAspect aspect)
{
// https://gpuweb.github.io/gpuweb/#depth-formats