Title: [292201] trunk/Source/WebGPU
Revision
292201
Author
[email protected]
Date
2022-03-31 22:00:52 -0700 (Thu, 31 Mar 2022)

Log Message

[WebGPU] Implement Queue::writeTexture() according to the spec
https://bugs.webkit.org/show_bug.cgi?id=238432

Reviewed by Dean Jackson.

This implements Queue::writeTexture() 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/Queue.mm:
(WebGPU::Queue::writeBuffer):
(WebGPU::validateWriteTexture):
(WebGPU::Queue::writeTexture):

Modified Paths

Diff

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


--- trunk/Source/WebGPU/ChangeLog	2022-04-01 04:50:58 UTC (rev 292200)
+++ trunk/Source/WebGPU/ChangeLog	2022-04-01 05:00:52 UTC (rev 292201)
@@ -1,5 +1,22 @@
 2022-03-31  Myles C. Maxfield  <[email protected]>
 
+        [WebGPU] Implement Queue::writeTexture() according to the spec
+        https://bugs.webkit.org/show_bug.cgi?id=238432
+
+        Reviewed by Dean Jackson.
+
+        This implements Queue::writeTexture() 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/Queue.mm:
+        (WebGPU::Queue::writeBuffer):
+        (WebGPU::validateWriteTexture):
+        (WebGPU::Queue::writeTexture):
+
+2022-03-31  Myles C. Maxfield  <[email protected]>
+
         [WebGPU] Implement CommandEncoder::copyTextureToTexture() according to the spec
         https://bugs.webkit.org/show_bug.cgi?id=238431
 

Modified: trunk/Source/WebGPU/WebGPU/Queue.mm (292200 => 292201)


--- trunk/Source/WebGPU/WebGPU/Queue.mm	2022-04-01 04:50:58 UTC (rev 292200)
+++ trunk/Source/WebGPU/WebGPU/Queue.mm	2022-04-01 05:00:52 UTC (rev 292201)
@@ -30,6 +30,7 @@
 #import "Buffer.h"
 #import "CommandBuffer.h"
 #import "Device.h"
+#import "Texture.h"
 
 namespace WebGPU {
 
@@ -236,21 +237,257 @@
 
     ensureBlitCommandEncoder();
     // FIXME(PERFORMANCE): Suballocate, so the common case doesn't need to hit the kernel.
+    // FIXME(PERFORMANCE): Should this temporary buffer really be shared?
     id<MTLBuffer> temporaryBuffer = [m_device.device() newBufferWithBytes:data length:static_cast<NSUInteger>(size) options:MTLResourceStorageModeShared];
     if (!temporaryBuffer)
         return;
-    [m_blitCommandEncoder copyFromBuffer:temporaryBuffer sourceOffset:0 toBuffer:buffer.buffer() destinationOffset:static_cast<NSUInteger>(bufferOffset) size:static_cast<NSUInteger>(size)];
+    [m_blitCommandEncoder
+        copyFromBuffer:temporaryBuffer
+        sourceOffset:0
+        toBuffer:buffer.buffer()
+        destinationOffset:static_cast<NSUInteger>(bufferOffset)
+        size:static_cast<NSUInteger>(size)];
 }
 
-void Queue::writeTexture(const WGPUImageCopyTexture& destination, const void* data, size_t dataSize, const WGPUTextureDataLayout& dataLayout, const WGPUExtent3D& writeSize)
+static bool validateWriteTexture(const WGPUImageCopyTexture& destination, const WGPUTextureDataLayout& dataLayout, const WGPUExtent3D& size, size_t dataByteSize, const WGPUTextureDescriptor& textureDesc)
 {
-    UNUSED_PARAM(destination);
-    UNUSED_PARAM(data);
-    UNUSED_PARAM(dataSize);
-    UNUSED_PARAM(dataLayout);
-    UNUSED_PARAM(writeSize);
+    // "validating GPUImageCopyTexture(destination, size) returns true."
+    if (!Texture::validateImageCopyTexture(destination, size))
+        return false;
+
+    // "textureDesc.usage includes COPY_DST."
+    if (!(textureDesc.usage & WGPUTextureUsage_CopyDst))
+        return false;
+
+    // "textureDesc.sampleCount is 1."
+    if (textureDesc.sampleCount != 1)
+        return false;
+
+    // "validating texture copy range(destination, size) return true."
+    if (!Texture::validateTextureCopyRange(destination, size))
+        return false;
+
+    // "destination.aspect must refer to a single aspect of textureDesc.format."
+    if (!Texture::refersToSingleAspect(textureDesc.format, destination.aspect))
+        return false;
+
+    // "That aspect must be a valid image copy destination according to § 25.1.2 Depth-stencil formats."
+    if (!Texture::isValidImageCopyDestination(textureDesc.format, destination.aspect))
+        return false;
+
+    // "Let aspectSpecificFormat = textureDesc.format."
+    auto aspectSpecificFormat = textureDesc.format;
+
+    // "If textureDesc.format is a depth-or-stencil format:"
+    if (Texture::isDepthOrStencilFormat(textureDesc.format)) {
+        // "Set aspectSpecificFormat to the aspect-specific format of textureDesc.format according to § 25.1.2 Depth-stencil formats."
+        aspectSpecificFormat = Texture::aspectSpecificFormat(textureDesc.format, destination.aspect);
+    }
+
+    // "validating linear texture data(dataLayout, dataByteSize, aspectSpecificFormat, size) succeeds."
+    if (!Texture::validateLinearTextureData(dataLayout, dataByteSize, aspectSpecificFormat, size))
+        return false;
+
+    return true;
 }
 
+void Queue::writeTexture(const WGPUImageCopyTexture& destination, const void* data, size_t dataSize, const WGPUTextureDataLayout& dataLayout, const WGPUExtent3D& size)
+{
+    if (destination.nextInChain || dataLayout.nextInChain)
+        return;
+
+    // https://gpuweb.github.io/gpuweb/#dom-gpuqueue-writetexture
+
+    // "Let dataBytes be a copy of the bytes held by the buffer source data."
+    // "Let dataByteSize be the number of bytes in dataBytes."
+    auto dataByteSize = dataSize;
+
+    // "Let textureDesc be destination.texture.[[descriptor]]."
+    const auto& textureDesc = fromAPI(destination.texture).descriptor();
+
+    // "Let contents be the contents of the images seen by viewing dataBytes with dataLayout and size."
+
+    // "If any of the following conditions are unsatisfied"
+    if (!validateWriteTexture(destination, dataLayout, size, dataByteSize, textureDesc)) {
+        // "generate a validation error and stop."
+        m_device.generateAValidationError("Validation failure."_s);
+        return;
+    }
+
+    // "Write contents into destination."
+    if (!dataSize)
+        return;
+
+    NSUInteger bytesPerRow = dataLayout.bytesPerRow;
+
+    // FIXME: Use checked arithmetic.
+    NSUInteger bytesPerImage = dataLayout.bytesPerRow * dataLayout.rowsPerImage;
+
+    MTLBlitOption options = MTLBlitOptionNone;
+    switch (destination.aspect) {
+    case WGPUTextureAspect_All:
+        options = MTLBlitOptionNone;
+        break;
+    case WGPUTextureAspect_StencilOnly:
+        options = MTLBlitOptionStencilFromDepthStencil;
+        break;
+    case WGPUTextureAspect_DepthOnly:
+        options = MTLBlitOptionDepthFromDepthStencil;
+        break;
+    case WGPUTextureAspect_Force32:
+        return;
+    }
+
+    // FIXME(PERFORMANCE): Instead of checking whether or not the whole queue is idle,
+    // we could detect whether this specific resource is idle, if we tracked every resource.
+    if (isIdle() && options == MTLBlitOptionNone) {
+        switch (fromAPI(destination.texture).texture().storageMode) {
+        case MTLStorageModeShared:
+#if PLATFORM(MAC) || PLATFORM(MACCATALYST)
+        case MTLStorageModeManaged:
+#endif
+            {
+                switch (textureDesc.dimension) {
+                case WGPUTextureDimension_1D: {
+                    auto region = MTLRegionMake1D(destination.origin.x, size.width);
+                    for (uint32_t layer = 0; layer < size.depthOrArrayLayers; ++layer) {
+                        // FIXME: Use checked arithmetic.
+                        auto sourceOffset = static_cast<NSUInteger>(dataLayout.offset + layer * bytesPerImage);
+                        NSUInteger destinationSlice = destination.origin.z + layer;
+                        [fromAPI(destination.texture).texture()
+                            replaceRegion:region
+                            mipmapLevel:destination.mipLevel
+                            slice:destinationSlice
+                            withBytes:static_cast<const char*>(data) + sourceOffset
+                            bytesPerRow:bytesPerRow
+                            bytesPerImage:bytesPerImage];
+                    }
+                    break;
+                }
+                case WGPUTextureDimension_2D: {
+                    auto region = MTLRegionMake2D(destination.origin.x, destination.origin.y, size.width, size.height);
+                    for (uint32_t layer = 0; layer < size.depthOrArrayLayers; ++layer) {
+                        // FIXME: Use checked arithmetic.
+                        auto sourceOffset = static_cast<NSUInteger>(dataLayout.offset + layer * bytesPerImage);
+                        NSUInteger destinationSlice = destination.origin.z + layer;
+                        [fromAPI(destination.texture).texture()
+                            replaceRegion:region
+                            mipmapLevel:destination.mipLevel
+                            slice:destinationSlice
+                            withBytes:static_cast<const char*>(data) + sourceOffset
+                            bytesPerRow:bytesPerRow
+                            bytesPerImage:bytesPerImage];
+                    }
+                    break;
+                }
+                case WGPUTextureDimension_3D: {
+                    auto region = MTLRegionMake3D(destination.origin.x, destination.origin.y, destination.origin.z, size.width, size.height, size.depthOrArrayLayers);
+                    auto sourceOffset = static_cast<NSUInteger>(dataLayout.offset);
+                    [fromAPI(destination.texture).texture()
+                        replaceRegion:region
+                        mipmapLevel:destination.mipLevel
+                        slice:0
+                        withBytes:static_cast<const char*>(data) + sourceOffset
+                        bytesPerRow:bytesPerRow
+                        bytesPerImage:bytesPerImage];
+                    break;
+                }
+                case WGPUTextureDimension_Force32:
+                    ASSERT_NOT_REACHED();
+                    return;
+                }
+                return;
+            }
+        case MTLStorageModePrivate:
+            // The only way to get data into a private resource is to tell the GPU to copy it in.
+            break;
+        default:
+            ASSERT_NOT_REACHED();
+            return;
+        }
+    }
+
+    ensureBlitCommandEncoder();
+    // FIXME(PERFORMANCE): Suballocate, so the common case doesn't need to hit the kernel.
+    // FIXME(PERFORMANCE): Should this temporary buffer really be shared?
+    id<MTLBuffer> temporaryBuffer = [m_device.device() newBufferWithBytes:static_cast<const char*>(data) + dataLayout.offset length:static_cast<NSUInteger>(dataByteSize) options:MTLResourceStorageModeShared];
+    if (!temporaryBuffer)
+        return;
+
+    auto logicalSize = fromAPI(destination.texture).logicalMiplevelSpecificTextureExtent(destination.mipLevel);
+    auto widthForMetal = std::min(size.width, logicalSize.width);
+    auto heightForMetal = std::min(size.height, logicalSize.height);
+    auto depthForMetal = std::min(size.depthOrArrayLayers, logicalSize.depthOrArrayLayers);
+
+    switch (textureDesc.dimension) {
+    case WGPUTextureDimension_1D: {
+        // https://developer.apple.com/documentation/metal/mtlblitcommandencoder/1400771-copyfrombuffer?language=objc
+        // "When you copy to a 1D texture, height and depth must be 1."
+        auto sourceSize = MTLSizeMake(widthForMetal, 1, 1);
+        auto destinationOrigin = MTLOriginMake(destination.origin.x, 1, 1);
+        for (uint32_t layer = 0; layer < size.depthOrArrayLayers; ++layer) {
+            // FIXME: Use checked arithmetic.
+            NSUInteger sourceOffset = layer * bytesPerImage;
+            NSUInteger destinationSlice = destination.origin.z + layer;
+            [m_blitCommandEncoder
+                copyFromBuffer:temporaryBuffer
+                sourceOffset:sourceOffset
+                sourceBytesPerRow:bytesPerRow
+                sourceBytesPerImage:bytesPerImage
+                sourceSize:sourceSize
+                toTexture:fromAPI(destination.texture).texture()
+                destinationSlice:destinationSlice
+                destinationLevel:destination.mipLevel
+                destinationOrigin:destinationOrigin
+                options:options];
+        }
+        break;
+    }
+    case WGPUTextureDimension_2D: {
+        // https://developer.apple.com/documentation/metal/mtlblitcommandencoder/1400771-copyfrombuffer?language=objc
+        // "When you copy to a 2D texture, depth must be 1."
+        auto sourceSize = MTLSizeMake(widthForMetal, heightForMetal, 1);
+        auto destinationOrigin = MTLOriginMake(destination.origin.x, destination.origin.y, 1);
+        for (uint32_t layer = 0; layer < size.depthOrArrayLayers; ++layer) {
+            // FIXME: Use checked arithmetic.
+            NSUInteger sourceOffset = layer * bytesPerImage;
+            NSUInteger destinationSlice = destination.origin.z + layer;
+            [m_blitCommandEncoder
+                copyFromBuffer:temporaryBuffer
+                sourceOffset:sourceOffset
+                sourceBytesPerRow:bytesPerRow
+                sourceBytesPerImage:bytesPerImage
+                sourceSize:sourceSize
+                toTexture:fromAPI(destination.texture).texture()
+                destinationSlice:destinationSlice
+                destinationLevel:destination.mipLevel
+                destinationOrigin:destinationOrigin
+                options:options];
+        }
+        break;
+    }
+    case WGPUTextureDimension_3D: {
+        auto sourceSize = MTLSizeMake(widthForMetal, heightForMetal, depthForMetal);
+        auto destinationOrigin = MTLOriginMake(destination.origin.x, destination.origin.y, destination.origin.z);
+        NSUInteger sourceOffset = 0;
+        [m_blitCommandEncoder
+            copyFromBuffer:temporaryBuffer
+            sourceOffset:sourceOffset
+            sourceBytesPerRow:bytesPerRow
+            sourceBytesPerImage:bytesPerImage
+            sourceSize:sourceSize
+            toTexture:fromAPI(destination.texture).texture()
+            destinationSlice:0
+            destinationLevel:destination.mipLevel
+            destinationOrigin:destinationOrigin
+            options:options];
+        break;
+    }
+    case WGPUTextureDimension_Force32:
+        return;
+    }
+}
+
 void Queue::setLabel(String&& label)
 {
     m_commandQueue.label = label;
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to