Title: [292200] trunk/Source/WebGPU
Revision
292200
Author
mmaxfi...@apple.com
Date
2022-03-31 21:50:58 -0700 (Thu, 31 Mar 2022)

Log Message

[WebGPU] Implement CommandEncoder::copyTextureToTexture() according to the spec
https://bugs.webkit.org/show_bug.cgi?id=238431

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::refersToAllAspects):
(WebGPU::isSRGBCompatible):
(WebGPU::areCopyCompatible):
(WebGPU::validateCopyTextureToTexture):
(WebGPU::CommandEncoder::copyTextureToTexture):

Modified Paths

Diff

Modified: trunk/Source/WebGPU/ChangeLog (292199 => 292200)


--- trunk/Source/WebGPU/ChangeLog	2022-04-01 04:48:09 UTC (rev 292199)
+++ trunk/Source/WebGPU/ChangeLog	2022-04-01 04:50:58 UTC (rev 292200)
@@ -1,5 +1,24 @@
 2022-03-31  Myles C. Maxfield  <mmaxfi...@apple.com>
 
+        [WebGPU] Implement CommandEncoder::copyTextureToTexture() according to the spec
+        https://bugs.webkit.org/show_bug.cgi?id=238431
+
+        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::refersToAllAspects):
+        (WebGPU::isSRGBCompatible):
+        (WebGPU::areCopyCompatible):
+        (WebGPU::validateCopyTextureToTexture):
+        (WebGPU::CommandEncoder::copyTextureToTexture):
+
+2022-03-31  Myles C. Maxfield  <mmaxfi...@apple.com>
+
         [WebGPU] Implement CommandEncoder::copyTextureToBuffer() according to the spec.
         https://bugs.webkit.org/show_bug.cgi?id=238430
 

Modified: trunk/Source/WebGPU/WebGPU/CommandEncoder.mm (292199 => 292200)


--- trunk/Source/WebGPU/WebGPU/CommandEncoder.mm	2022-04-01 04:48:09 UTC (rev 292199)
+++ trunk/Source/WebGPU/WebGPU/CommandEncoder.mm	2022-04-01 04:50:58 UTC (rev 292200)
@@ -171,6 +171,21 @@
     return false;
 }
 
+static bool refersToAllAspects(WGPUTextureFormat format, WGPUTextureAspect aspect)
+{
+    switch (aspect) {
+    case WGPUTextureAspect_All:
+        return true;
+    case WGPUTextureAspect_StencilOnly:
+        return Texture::containsStencilAspect(format) && !Texture::containsDepthAspect(format);
+    case WGPUTextureAspect_DepthOnly:
+        return Texture::containsDepthAspect(format) && !Texture::containsStencilAspect(format);
+    case WGPUTextureAspect_Force32:
+        ASSERT_NOT_REACHED();
+        return false;
+    }
+}
+
 static bool validateCopyBufferToTexture(const WGPUImageCopyBuffer& source, const WGPUImageCopyTexture& destination, const WGPUExtent3D& copySize)
 {
     // "Let dstTextureDesc be destination.texture.[[descriptor]]."
@@ -537,11 +552,327 @@
     }
 }
 
+static std::optional<WGPUTextureFormat> isSRGBCompatible(WGPUTextureFormat format)
+{
+    switch (format) {
+    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:
+        return std::nullopt;
+    case WGPUTextureFormat_RGBA8Unorm:
+    case WGPUTextureFormat_RGBA8UnormSrgb:
+        return WGPUTextureFormat_RGBA8Unorm;
+    case WGPUTextureFormat_RGBA8Snorm:
+    case WGPUTextureFormat_RGBA8Uint:
+    case WGPUTextureFormat_RGBA8Sint:
+        return std::nullopt;
+    case WGPUTextureFormat_BGRA8Unorm:
+    case WGPUTextureFormat_BGRA8UnormSrgb:
+        return WGPUTextureFormat_BGRA8Unorm;
+    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:
+    case WGPUTextureFormat_Stencil8:
+    case WGPUTextureFormat_Depth16Unorm:
+    case WGPUTextureFormat_Depth24Plus:
+    case WGPUTextureFormat_Depth24PlusStencil8:
+    case WGPUTextureFormat_Depth24UnormStencil8:
+    case WGPUTextureFormat_Depth32Float:
+    case WGPUTextureFormat_Depth32FloatStencil8:
+        return std::nullopt;
+    case WGPUTextureFormat_BC1RGBAUnorm:
+    case WGPUTextureFormat_BC1RGBAUnormSrgb:
+        return WGPUTextureFormat_BC1RGBAUnorm;
+    case WGPUTextureFormat_BC2RGBAUnorm:
+    case WGPUTextureFormat_BC2RGBAUnormSrgb:
+        return WGPUTextureFormat_BC2RGBAUnorm;
+    case WGPUTextureFormat_BC3RGBAUnorm:
+    case WGPUTextureFormat_BC3RGBAUnormSrgb:
+        return WGPUTextureFormat_BC3RGBAUnorm;
+    case WGPUTextureFormat_BC4RUnorm:
+    case WGPUTextureFormat_BC4RSnorm:
+    case WGPUTextureFormat_BC5RGUnorm:
+    case WGPUTextureFormat_BC5RGSnorm:
+    case WGPUTextureFormat_BC6HRGBUfloat:
+    case WGPUTextureFormat_BC6HRGBFloat:
+        return std::nullopt;
+    case WGPUTextureFormat_BC7RGBAUnorm:
+    case WGPUTextureFormat_BC7RGBAUnormSrgb:
+        return WGPUTextureFormat_BC7RGBAUnorm;
+    case WGPUTextureFormat_ETC2RGB8Unorm:
+    case WGPUTextureFormat_ETC2RGB8UnormSrgb:
+        return WGPUTextureFormat_ETC2RGB8Unorm;
+    case WGPUTextureFormat_ETC2RGB8A1Unorm:
+    case WGPUTextureFormat_ETC2RGB8A1UnormSrgb:
+        return WGPUTextureFormat_ETC2RGB8A1Unorm;
+    case WGPUTextureFormat_ETC2RGBA8Unorm:
+    case WGPUTextureFormat_ETC2RGBA8UnormSrgb:
+        return WGPUTextureFormat_ETC2RGBA8Unorm;
+    case WGPUTextureFormat_EACR11Unorm:
+    case WGPUTextureFormat_EACR11Snorm:
+    case WGPUTextureFormat_EACRG11Unorm:
+    case WGPUTextureFormat_EACRG11Snorm:
+        return std::nullopt;
+    case WGPUTextureFormat_ASTC4x4Unorm:
+    case WGPUTextureFormat_ASTC4x4UnormSrgb:
+        return WGPUTextureFormat_ASTC4x4Unorm;
+    case WGPUTextureFormat_ASTC5x4Unorm:
+    case WGPUTextureFormat_ASTC5x4UnormSrgb:
+        return WGPUTextureFormat_ASTC5x4Unorm;
+    case WGPUTextureFormat_ASTC5x5Unorm:
+    case WGPUTextureFormat_ASTC5x5UnormSrgb:
+        return WGPUTextureFormat_ASTC5x5Unorm;
+    case WGPUTextureFormat_ASTC6x5Unorm:
+    case WGPUTextureFormat_ASTC6x5UnormSrgb:
+        return WGPUTextureFormat_ASTC6x5Unorm;
+    case WGPUTextureFormat_ASTC6x6Unorm:
+    case WGPUTextureFormat_ASTC6x6UnormSrgb:
+        return WGPUTextureFormat_ASTC6x6Unorm;
+    case WGPUTextureFormat_ASTC8x5Unorm:
+    case WGPUTextureFormat_ASTC8x5UnormSrgb:
+        return WGPUTextureFormat_ASTC8x5Unorm;
+    case WGPUTextureFormat_ASTC8x6Unorm:
+    case WGPUTextureFormat_ASTC8x6UnormSrgb:
+        return WGPUTextureFormat_ASTC8x6Unorm;
+    case WGPUTextureFormat_ASTC8x8Unorm:
+    case WGPUTextureFormat_ASTC8x8UnormSrgb:
+        return WGPUTextureFormat_ASTC8x8Unorm;
+    case WGPUTextureFormat_ASTC10x5Unorm:
+    case WGPUTextureFormat_ASTC10x5UnormSrgb:
+        return WGPUTextureFormat_ASTC10x5Unorm;
+    case WGPUTextureFormat_ASTC10x6Unorm:
+    case WGPUTextureFormat_ASTC10x6UnormSrgb:
+        return WGPUTextureFormat_ASTC10x6Unorm;
+    case WGPUTextureFormat_ASTC10x8Unorm:
+    case WGPUTextureFormat_ASTC10x8UnormSrgb:
+        return WGPUTextureFormat_ASTC10x8Unorm;
+    case WGPUTextureFormat_ASTC10x10Unorm:
+    case WGPUTextureFormat_ASTC10x10UnormSrgb:
+        return WGPUTextureFormat_ASTC10x10Unorm;
+    case WGPUTextureFormat_ASTC12x10Unorm:
+    case WGPUTextureFormat_ASTC12x10UnormSrgb:
+        return WGPUTextureFormat_ASTC12x10Unorm;
+    case WGPUTextureFormat_ASTC12x12Unorm:
+    case WGPUTextureFormat_ASTC12x12UnormSrgb:
+        return WGPUTextureFormat_ASTC12x12Unorm;
+    case WGPUTextureFormat_Undefined:
+    case WGPUTextureFormat_Force32:
+        ASSERT_NOT_REACHED();
+        return std::nullopt;
+    }
+}
+
+static bool areCopyCompatible(WGPUTextureFormat format1, WGPUTextureFormat format2)
+{
+    // https://gpuweb.github.io/gpuweb/#copy-compatible
+
+    // "format1 equals format2"
+    if (format1 == format2)
+        return true;
+
+    // "format1 and format2 differ only in whether they are srgb formats (have the -srgb suffix)."
+    auto canonicalizedFormat1 = isSRGBCompatible(format1);
+    auto canonicalizedFormat2 = isSRGBCompatible(format2);
+    if (!canonicalizedFormat1 || !canonicalizedFormat2)
+        return false;
+    if (canonicalizedFormat1 != canonicalizedFormat1)
+        return false;
+
+    return true;
+}
+
+static bool validateCopyTextureToTexture(const WGPUImageCopyTexture& source, const WGPUImageCopyTexture& destination, const WGPUExtent3D& copySize)
+{
+    // "Let srcTextureDesc be source.texture.[[descriptor]]."
+    const auto& srcTextureDesc = fromAPI(source.texture).descriptor();
+
+    // "Let dstTextureDesc be destination.texture.[[descriptor]]."
+    const auto& dstTextureDesc = fromAPI(destination.texture).descriptor();
+
+    // "validating GPUImageCopyTexture(source, copySize) returns true."
+    if (!Texture::validateImageCopyTexture(source, copySize))
+        return false;
+
+    // "srcTextureDesc.usage contains COPY_SRC."
+    if (!(srcTextureDesc.usage & WGPUTextureUsage_CopySrc))
+        return false;
+
+    // "validating GPUImageCopyTexture(destination, copySize) returns true."
+    if (!Texture::validateImageCopyTexture(destination, copySize))
+        return false;
+
+    // "dstTextureDesc.usage contains COPY_DST."
+    if (!(dstTextureDesc.usage & WGPUTextureUsage_CopyDst))
+        return false;
+
+    // "srcTextureDesc.sampleCount is equal to dstTextureDesc.sampleCount."
+    if (srcTextureDesc.sampleCount != dstTextureDesc.sampleCount)
+        return false;
+
+    // "srcTextureDesc.format and dstTextureDesc.format must be copy-compatible."
+    if (!areCopyCompatible(srcTextureDesc.format, dstTextureDesc.format))
+        return false;
+
+    // "If srcTextureDesc.format is a depth-stencil format:"
+    if (Texture::isDepthOrStencilFormat(srcTextureDesc.format)) {
+        // "source.aspect and destination.aspect must both refer to all aspects of srcTextureDesc.format and dstTextureDesc.format, respectively."
+        if (!refersToAllAspects(srcTextureDesc.format, source.aspect)
+            || !refersToAllAspects(dstTextureDesc.format, destination.aspect))
+            return false;
+    }
+
+    // "validating texture copy range(source, copySize) returns true."
+    if (!Texture::validateTextureCopyRange(source, copySize))
+        return false;
+
+    // "validating texture copy range(destination, copySize) returns true."
+    if (!Texture::validateTextureCopyRange(destination, copySize))
+        return false;
+
+    // "The set of subresources for texture copy(source, copySize) and the set of subresources for texture copy(destination, copySize) is disjoint."
+    // https://gpuweb.github.io/gpuweb/#abstract-opdef-set-of-subresources-for-texture-copy
+    if (source.texture == destination.texture) {
+        // Mip levels are never ranges.
+        if (source.mipLevel == destination.mipLevel) {
+            switch (fromAPI(source.texture).descriptor().dimension) {
+            case WGPUTextureDimension_1D:
+                // "The subresource of imageCopyTexture.texture at mipmap level imageCopyTexture.mipLevel."
+                return false;
+            case WGPUTextureDimension_2D: {
+                // "For each arrayLayer of the copySize.depthOrArrayLayers array layers starting at imageCopyTexture.origin.z:"
+                // "The subresource of imageCopyTexture.texture at mipmap level imageCopyTexture.mipLevel and array layer arrayLayer."
+                Range<uint32_t> sourceRange(source.origin.z, source.origin.z + copySize.depthOrArrayLayers);
+                Range<uint32_t> destinationRange(destination.origin.z, source.origin.z + copySize.depthOrArrayLayers);
+                if (sourceRange.overlaps(destinationRange))
+                    return false;
+                break;
+            }
+            case WGPUTextureDimension_3D:
+                // "The subresource of imageCopyTexture.texture at mipmap level imageCopyTexture.mipLevel."
+                return false;
+            case WGPUTextureDimension_Force32:
+                ASSERT_NOT_REACHED();
+                return false;
+            }
+        }
+    }
+
+    return true;
+}
+
 void CommandEncoder::copyTextureToTexture(const WGPUImageCopyTexture& source, const WGPUImageCopyTexture& destination, const WGPUExtent3D& copySize)
 {
-    UNUSED_PARAM(source);
-    UNUSED_PARAM(destination);
-    UNUSED_PARAM(copySize);
+    if (source.nextInChain || destination.nextInChain)
+        return;
+
+    // https://gpuweb.github.io/gpuweb/#dom-gpucommandencoder-copytexturetotexture
+
+    // "Prepare the encoder state of this. If it returns false, stop."
+    if (!prepareTheEncoderState())
+        return;
+
+    // "If any of the following conditions are unsatisfied"
+    if (!validateCopyTextureToTexture(source, destination, copySize)) {
+        // "generate a validation error and stop."
+        m_device->generateAValidationError("Validation failure.");
+        return;
+    }
+
+    ensureBlitCommandEncoder();
+
+    auto& sourceDescriptor = fromAPI(source.texture).descriptor();
+    // FIXME(PERFORMANCE): Is it actually faster to use the -[MTLBlitCommandEncoder copyFromTexture:...toTexture:...levelCount:]
+    // variant, where possible, rather than calling the other variant in a loop?
+    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(copySize.width, 1, 1);
+        auto sourceOrigin = MTLOriginMake(source.origin.x, 1, 1);
+        auto destinationOrigin = MTLOriginMake(destination.origin.x, 1, 1);
+        for (uint32_t layer = 0; layer < copySize.depthOrArrayLayers; ++layer) {
+            // FIXME: Use checked arithmetic.
+            NSUInteger sourceSlice = source.origin.z + layer;
+            NSUInteger destinationSlice = destination.origin.z + layer;
+            [m_blitCommandEncoder
+                copyFromTexture:fromAPI(source.texture).texture()
+                sourceSlice:sourceSlice
+                sourceLevel:source.mipLevel
+                sourceOrigin:sourceOrigin
+                sourceSize:sourceSize
+                toTexture:fromAPI(destination.texture).texture()
+                destinationSlice:destinationSlice
+                destinationLevel:destination.mipLevel
+                destinationOrigin:destinationOrigin];
+        }
+        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(copySize.width, copySize.height, 1);
+        auto sourceOrigin = MTLOriginMake(source.origin.x, source.origin.y, 1);
+        auto destinationOrigin = MTLOriginMake(destination.origin.x, destination.origin.y, 1);
+        for (uint32_t layer = 0; layer < copySize.depthOrArrayLayers; ++layer) {
+            // FIXME: Use checked arithmetic.
+            NSUInteger sourceSlice = source.origin.z + layer;
+            NSUInteger destinationSlice = destination.origin.z + layer;
+            [m_blitCommandEncoder
+                copyFromTexture:fromAPI(source.texture).texture()
+                sourceSlice:sourceSlice
+                sourceLevel:source.mipLevel
+                sourceOrigin:sourceOrigin
+                sourceSize:sourceSize
+                toTexture:fromAPI(destination.texture).texture()
+                destinationSlice:destinationSlice
+                destinationLevel:destination.mipLevel
+                destinationOrigin:destinationOrigin];
+        }
+        break;
+    }
+    case WGPUTextureDimension_3D: {
+        auto sourceSize = MTLSizeMake(copySize.width, copySize.height, copySize.depthOrArrayLayers);
+        auto sourceOrigin = MTLOriginMake(source.origin.x, source.origin.y, source.origin.z);
+        auto destinationOrigin = MTLOriginMake(destination.origin.x, destination.origin.y, destination.origin.z);
+        [m_blitCommandEncoder
+            copyFromTexture:fromAPI(source.texture).texture()
+            sourceSlice:0
+            sourceLevel:source.mipLevel
+            sourceOrigin:sourceOrigin
+            sourceSize:sourceSize
+            toTexture:fromAPI(destination.texture).texture()
+            destinationSlice:0
+            destinationLevel:destination.mipLevel
+            destinationOrigin:destinationOrigin];
+        break;
+    }
+    case WGPUTextureDimension_Force32:
+        ASSERT_NOT_REACHED();
+        return;
+    }
 }
 
 static bool validateClearBuffer(const Buffer& buffer, uint64_t offset, uint64_t size)
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to