Title: [291571] trunk/Source/WebGPU
Revision
291571
Author
[email protected]
Date
2022-03-21 11:58:31 -0700 (Mon, 21 Mar 2022)

Log Message

[WebGPU] Implement error reporting facilities
https://bugs.webkit.org/show_bug.cgi?id=238131

Reviewed by Kimmo Kinnunen.

This patch implements the GPUDevice.pushErrorScope() and GPUDevice.popErrorScope() functions,
according to the spec.

Now that we can report errors, we should be just about able to pass our first CTS test.

* CommandLinePlayground/main.swift:
* WebGPU/Buffer.mm:
(WebGPU::Buffer::mapAsync):
(WebGPU::Buffer::unmap):
* WebGPU/CommandEncoder.h:
(WebGPU::CommandEncoder::create):
* WebGPU/CommandEncoder.mm:
(WebGPU::Device::createCommandEncoder):
(WebGPU::CommandEncoder::CommandEncoder):
(WebGPU::CommandEncoder::copyBufferToBuffer):
(WebGPU::CommandEncoder::clearBuffer):
(WebGPU::CommandEncoder::finish):
* WebGPU/Device.h:
* WebGPU/Device.mm:
(WebGPU::Device::currentErrorScope):
(WebGPU::Device::generateAValidationError):
(WebGPU::Device::validatePopErrorScope const):
(WebGPU::Device::popErrorScope):
(WebGPU::Device::pushErrorScope):
(WebGPU::Device::setUncapturedErrorCallback):
* WebGPU/Queue.h:
* WebGPU/Queue.mm:
(WebGPU::Queue::submit):
* WebGPU/Sampler.h:
(WebGPU::Sampler::create):
* WebGPU/Sampler.mm:
(WebGPU::Device::createSampler):
(WebGPU::Sampler::Sampler):

Modified Paths

Diff

Modified: trunk/Source/WebGPU/ChangeLog (291570 => 291571)


--- trunk/Source/WebGPU/ChangeLog	2022-03-21 18:48:25 UTC (rev 291570)
+++ trunk/Source/WebGPU/ChangeLog	2022-03-21 18:58:31 UTC (rev 291571)
@@ -1,3 +1,44 @@
+2022-03-21  Myles C. Maxfield  <[email protected]>
+
+        [WebGPU] Implement error reporting facilities
+        https://bugs.webkit.org/show_bug.cgi?id=238131
+
+        Reviewed by Kimmo Kinnunen.
+
+        This patch implements the GPUDevice.pushErrorScope() and GPUDevice.popErrorScope() functions,
+        according to the spec.
+
+        Now that we can report errors, we should be just about able to pass our first CTS test.
+
+        * CommandLinePlayground/main.swift:
+        * WebGPU/Buffer.mm:
+        (WebGPU::Buffer::mapAsync):
+        (WebGPU::Buffer::unmap):
+        * WebGPU/CommandEncoder.h:
+        (WebGPU::CommandEncoder::create):
+        * WebGPU/CommandEncoder.mm:
+        (WebGPU::Device::createCommandEncoder):
+        (WebGPU::CommandEncoder::CommandEncoder):
+        (WebGPU::CommandEncoder::copyBufferToBuffer):
+        (WebGPU::CommandEncoder::clearBuffer):
+        (WebGPU::CommandEncoder::finish):
+        * WebGPU/Device.h:
+        * WebGPU/Device.mm:
+        (WebGPU::Device::currentErrorScope):
+        (WebGPU::Device::generateAValidationError):
+        (WebGPU::Device::validatePopErrorScope const):
+        (WebGPU::Device::popErrorScope):
+        (WebGPU::Device::pushErrorScope):
+        (WebGPU::Device::setUncapturedErrorCallback):
+        * WebGPU/Queue.h:
+        * WebGPU/Queue.mm:
+        (WebGPU::Queue::submit):
+        * WebGPU/Sampler.h:
+        (WebGPU::Sampler::create):
+        * WebGPU/Sampler.mm:
+        (WebGPU::Device::createSampler):
+        (WebGPU::Sampler::Sampler):
+
 2022-03-18  Myles C. Maxfield  <[email protected]>
 
         [WebGPU] Add #pragma marks to strategic places

Modified: trunk/Source/WebGPU/CommandLinePlayground/main.swift (291570 => 291571)


--- trunk/Source/WebGPU/CommandLinePlayground/main.swift	2022-03-21 18:48:25 UTC (rev 291570)
+++ trunk/Source/WebGPU/CommandLinePlayground/main.swift	2022-03-21 18:58:31 UTC (rev 291571)
@@ -58,6 +58,8 @@
 }
 print("Device: \(String(describing: device))")
 
+wgpuDevicePushErrorScope(device, WGPUErrorFilter_Validation)
+
 var uploadBufferDescriptor = WGPUBufferDescriptor(nextInChain: nil, label: nil, usage: WGPUBufferUsage_MapWrite.rawValue | WGPUBufferUsage_CopySrc.rawValue, size: UInt64(MemoryLayout<Int32>.size), mappedAtCreation: false)
 let uploadBuffer = wgpuDeviceCreateBuffer(device, &uploadBufferDescriptor)
 assert(uploadBuffer != nil)
@@ -100,7 +102,18 @@
             let readPointer = wgpuBufferGetMappedRange(downloadBuffer, 0, MemoryLayout<Int32>.size).bindMemory(to: Int32.self, capacity: 1)
             print("Result: \(readPointer[0])")
             wgpuBufferUnmap(downloadBuffer)
-            CFRunLoopStop(CFRunLoopGetMain())
+            wgpuDevicePopErrorScopeWithBlock(device) { (type: WGPUErrorType, message: Optional<UnsafePointer<Int8>>) in
+                if type != WGPUErrorType_NoError {
+                    if message != nil {
+                        print("Message: \(String(cString: message!))")
+                    } else {
+                        print("Empty message.")
+                    }
+                } else {
+                    print("Success!")
+                }
+                CFRunLoopStop(CFRunLoopGetMain())
+            }
         }
     }
 }

Modified: trunk/Source/WebGPU/WebGPU/Buffer.mm (291570 => 291571)


--- trunk/Source/WebGPU/WebGPU/Buffer.mm	2022-03-21 18:48:25 UTC (rev 291570)
+++ trunk/Source/WebGPU/WebGPU/Buffer.mm	2022-03-21 18:58:31 UTC (rev 291571)
@@ -279,7 +279,8 @@
 
     // "If any of the following conditions are unsatisfied:"
     if (!validateMapAsync(mode, offset, rangeSize)) {
-        // FIXME: "Record a validation error on the current scope."
+        // "Record a validation error on the current scope."
+        m_device->generateAValidationError("Validation failure.");
 
         // "Return a promise rejected with an OperationError on the Device timeline."
         callback(WGPUBufferMapAsyncStatus_Error);
@@ -352,7 +353,8 @@
 
     // "If any of the following requirements are unmet"
     if (!validateUnmap()) {
-        // FIXME: "generate a validation error and stop."
+        // "generate a validation error and stop."
+        m_device->generateAValidationError("Validation failure.");
         return;
     }
 

Modified: trunk/Source/WebGPU/WebGPU/CommandEncoder.h (291570 => 291571)


--- trunk/Source/WebGPU/WebGPU/CommandEncoder.h	2022-03-21 18:48:25 UTC (rev 291570)
+++ trunk/Source/WebGPU/WebGPU/CommandEncoder.h	2022-03-21 18:58:31 UTC (rev 291571)
@@ -35,6 +35,7 @@
 class Buffer;
 class CommandBuffer;
 class ComputePassEncoder;
+class Device;
 class QuerySet;
 class RenderPassEncoder;
 
@@ -41,9 +42,9 @@
 class CommandEncoder : public RefCounted<CommandEncoder>, public CommandsMixin {
     WTF_MAKE_FAST_ALLOCATED;
 public:
-    static Ref<CommandEncoder> create(id<MTLCommandBuffer> commandBuffer)
+    static Ref<CommandEncoder> create(id<MTLCommandBuffer> commandBuffer, Device& device)
     {
-        return adoptRef(*new CommandEncoder(commandBuffer));
+        return adoptRef(*new CommandEncoder(commandBuffer, device));
     }
 
     ~CommandEncoder();
@@ -64,7 +65,7 @@
     void setLabel(String&&);
 
 private:
-    CommandEncoder(id<MTLCommandBuffer>);
+    CommandEncoder(id<MTLCommandBuffer>, Device&);
 
     bool validateFinish() const;
     bool validatePopDebugGroup() const;
@@ -76,6 +77,8 @@
     id<MTLBlitCommandEncoder> m_blitCommandEncoder { nil };
 
     uint64_t m_debugGroupStackSize { 0 };
+
+    const Ref<Device> m_device;
 };
 
 } // namespace WebGPU

Modified: trunk/Source/WebGPU/WebGPU/CommandEncoder.mm (291570 => 291571)


--- trunk/Source/WebGPU/WebGPU/CommandEncoder.mm	2022-03-21 18:48:25 UTC (rev 291570)
+++ trunk/Source/WebGPU/WebGPU/CommandEncoder.mm	2022-03-21 18:58:31 UTC (rev 291571)
@@ -51,11 +51,12 @@
 
     commandBuffer.label = fromAPI(descriptor.label);
 
-    return CommandEncoder::create(commandBuffer);
+    return CommandEncoder::create(commandBuffer, *this);
 }
 
-CommandEncoder::CommandEncoder(id<MTLCommandBuffer> commandBuffer)
+CommandEncoder::CommandEncoder(id<MTLCommandBuffer> commandBuffer, Device& device)
     : m_commandBuffer(commandBuffer)
+    , m_device(device)
 {
 }
 
@@ -142,7 +143,8 @@
 
     // "If any of the following conditions are unsatisfied
     if (!validateCopyBufferToBuffer(source, sourceOffset, destination, destinationOffset, size)) {
-        // FIXME: "generate a validation error and stop."
+        // "generate a validation error and stop."
+        m_device->generateAValidationError("Validation failure.");
         return;
     }
 
@@ -212,7 +214,8 @@
 
     // "If any of the following conditions are unsatisfied"
     if (!validateClearBuffer(buffer, offset, size)) {
-        // FIXME: "generate a validation error and stop."
+        // "generate a validation error and stop."
+        m_device->generateAValidationError("Validation failure.");
         return;
     }
 
@@ -253,7 +256,8 @@
 
     // "If validationSucceeded is false, then:"
     if (validationFailed) {
-        // FIXME: "Generate a validation error."
+        // "Generate a validation error."
+        m_device->generateAValidationError("Validation failure.");
 
         // FIXME: "Return a new invalid GPUCommandBuffer."
         return nullptr;

Modified: trunk/Source/WebGPU/WebGPU/Device.h (291570 => 291571)


--- trunk/Source/WebGPU/WebGPU/Device.h	2022-03-21 18:48:25 UTC (rev 291570)
+++ trunk/Source/WebGPU/WebGPU/Device.h	2022-03-21 18:58:31 UTC (rev 291571)
@@ -32,6 +32,8 @@
 #import <wtf/Ref.h>
 #import <wtf/RefCounted.h>
 #import <wtf/RefPtr.h>
+#import <wtf/Vector.h>
+#import <wtf/text/WTFString.h>
 
 namespace WebGPU {
 
@@ -84,6 +86,8 @@
     void setUncapturedErrorCallback(Function<void(WGPUErrorType, String&&)>&&);
     void setLabel(String&&);
 
+    void generateAValidationError(String&& message);
+
     Instance& instance() const { return m_instance; }
     bool hasUnifiedMemory() const { return m_device.hasUnifiedMemory; }
 
@@ -90,9 +94,25 @@
 private:
     Device(id<MTLDevice>, id<MTLCommandQueue> defaultQueue, Instance&);
 
+    struct ErrorScope;
+    ErrorScope* currentErrorScope(WGPUErrorFilter);
+    bool validatePopErrorScope() const;
+
+    struct Error {
+        WGPUErrorType type;
+        String message;
+    };
+    struct ErrorScope {
+        std::optional<Error> error; // "The first GPUError, if any, observed while the GPU error scope was current."
+        const WGPUErrorFilter filter; // Determines what type of GPUError this GPU error scope observes.
+    };
+
     const id<MTLDevice> m_device { nil };
     const Ref<Queue> m_defaultQueue;
     const Ref<Instance> m_instance;
+
+    Function<void(WGPUErrorType, String&&)> m_uncapturedErrorCallback;
+    Vector<ErrorScope> m_errorScopeStack; // "A stack of GPU error scopes that have been pushed to the GPUDevice."
 };
 
 } // namespace WebGPU

Modified: trunk/Source/WebGPU/WebGPU/Device.mm (291570 => 291571)


--- trunk/Source/WebGPU/WebGPU/Device.mm	2022-03-21 18:48:25 UTC (rev 291570)
+++ trunk/Source/WebGPU/WebGPU/Device.mm	2022-03-21 18:58:31 UTC (rev 291571)
@@ -102,17 +102,94 @@
     return false;
 }
 
+auto Device::currentErrorScope(WGPUErrorFilter type) -> ErrorScope*
+{
+    // https://gpuweb.github.io/gpuweb/#abstract-opdef-current-error-scope
+
+    // "Let scope be the last item of device.[[errorScopeStack]]."
+    // "While scope is not undefined:"
+    for (auto iterator = m_errorScopeStack.rbegin(); iterator != m_errorScopeStack.rend(); ++iterator) {
+        // "If scope.[[filter]] is type, return scope."
+        if (iterator->filter == type)
+            return &*iterator;
+        // "Set scope to the previous item of device.[[errorScopeStack]]."
+    }
+    // "Return undefined."
+    return nullptr;
+}
+
+void Device::generateAValidationError(String&& message)
+{
+    // https://gpuweb.github.io/gpuweb/#abstract-opdef-generate-a-validation-error
+
+    // "Let scope be the current error scope for error and device."
+    auto* scope = currentErrorScope(WGPUErrorFilter_Validation);
+
+    // "If scope is not undefined:"
+    if (scope) {
+        // "If scope.[[error]] is null, set scope.[[error]] to error."
+        if (!scope->error)
+            scope->error = Error { WGPUErrorType_Validation, WTFMove(message) };
+        // "Stop."
+        return;
+    }
+
+    // "Otherwise issue the following steps to the Content timeline:"
+    // "If the user agent chooses, queue a task to fire a GPUUncapturedErrorEvent named "uncapturederror" on device with an error of error."
+    if (m_uncapturedErrorCallback) {
+        m_instance->scheduleWork([protectedThis = Ref { *this }, message = WTFMove(message)]() mutable {
+            protectedThis->m_uncapturedErrorCallback(WGPUErrorType_Validation, WTFMove(message));
+        });
+    }
+}
+
+bool Device::validatePopErrorScope() const
+{
+    // FIXME: "this must not be lost."
+
+    // "this.[[errorScopeStack]].size > 0."
+    if (m_errorScopeStack.isEmpty())
+        return false;
+
+    return true;
+}
+
 bool Device::popErrorScope(CompletionHandler<void(WGPUErrorType, String&&)>&& callback)
 {
-    // FIXME: Implement this.
-    UNUSED_PARAM(callback);
-    return false;
+    // https://gpuweb.github.io/gpuweb/#dom-gpudevice-poperrorscope
+
+    // "If any of the following requirements are unmet"
+    if (!validatePopErrorScope()) {
+        // "reject promise with an OperationError and stop."
+        callback(WGPUErrorType_Unknown, "popErrorScope() failed validation."_s);
+        return false;
+    }
+
+    // "Let scope be the result of popping an item off of this.[[errorScopeStack]]."
+    auto scope = m_errorScopeStack.takeLast();
+
+    // "Resolve promise with scope.[[error]]."
+    m_instance->scheduleWork([scope = WTFMove(scope), callback = WTFMove(callback)]() mutable {
+        if (scope.error)
+            callback(scope.error->type, WTFMove(scope.error->message));
+        else
+            callback(WGPUErrorType_NoError, { });
+    });
+
+    // FIXME: Make sure this is the right thing to return.
+    return true;
 }
 
 void Device::pushErrorScope(WGPUErrorFilter filter)
 {
-    // FIXME: Implement this.
-    UNUSED_PARAM(filter);
+    // https://gpuweb.github.io/gpuweb/#dom-gpudevice-pusherrorscope
+
+    // "Let scope be a new GPU error scope."
+    // "Set scope.[[filter]] to filter."
+    ErrorScope scope { std::nullopt, filter };
+
+    // "Push scope onto this.[[errorScopeStack]]."
+    m_errorScopeStack.append(WTFMove(scope));
 }
 
 void Device::setDeviceLostCallback(Function<void(WGPUDeviceLostReason, String&&)>&& callback)
@@ -123,8 +200,7 @@
 
 void Device::setUncapturedErrorCallback(Function<void(WGPUErrorType, String&&)>&& callback)
 {
-    // FIXME: Implement this.
-    UNUSED_PARAM(callback);
+    m_uncapturedErrorCallback = WTFMove(callback);
 }
 
 void Device::setLabel(String&&)

Modified: trunk/Source/WebGPU/WebGPU/Queue.h (291570 => 291571)


--- trunk/Source/WebGPU/WebGPU/Queue.h	2022-03-21 18:48:25 UTC (rev 291570)
+++ trunk/Source/WebGPU/WebGPU/Queue.h	2022-03-21 18:58:31 UTC (rev 291571)
@@ -66,7 +66,7 @@
     void scheduleWork(Instance::WorkItem&&);
 
     const id<MTLCommandQueue> m_commandQueue { nil };
-    const Device& m_device; // The only kind of queues that exist right now are default queues, which are owned by Devices.
+    Device& m_device; // The only kind of queues that exist right now are default queues, which are owned by Devices.
 
     uint64_t m_submittedCommandBufferCount { 0 };
     uint64_t m_completedCommandBufferCount { 0 };

Modified: trunk/Source/WebGPU/WebGPU/Queue.mm (291570 => 291571)


--- trunk/Source/WebGPU/WebGPU/Queue.mm	2022-03-21 18:48:25 UTC (rev 291570)
+++ trunk/Source/WebGPU/WebGPU/Queue.mm	2022-03-21 18:58:31 UTC (rev 291571)
@@ -78,7 +78,8 @@
 
     // "If any of the following conditions are unsatisfied"
     if (!validateSubmit()) {
-        // FIXME: "generate a validation error and stop."
+        // "generate a validation error and stop."
+        m_device.generateAValidationError("Validation failure.");
         return;
     }
 

Modified: trunk/Source/WebGPU/WebGPU/Sampler.h (291570 => 291571)


--- trunk/Source/WebGPU/WebGPU/Sampler.h	2022-03-21 18:48:25 UTC (rev 291570)
+++ trunk/Source/WebGPU/WebGPU/Sampler.h	2022-03-21 18:58:31 UTC (rev 291571)
@@ -31,12 +31,14 @@
 
 namespace WebGPU {
 
+class Device;
+
 class Sampler : public RefCounted<Sampler> {
     WTF_MAKE_FAST_ALLOCATED;
 public:
-    static Ref<Sampler> create(id<MTLSamplerState> samplerState, const WGPUSamplerDescriptor& descriptor)
+    static Ref<Sampler> create(id<MTLSamplerState> samplerState, const WGPUSamplerDescriptor& descriptor, Device& device)
     {
-        return adoptRef(*new Sampler(samplerState, descriptor));
+        return adoptRef(*new Sampler(samplerState, descriptor, device));
     }
 
     ~Sampler();
@@ -51,7 +53,7 @@
     bool isFiltering() const { return descriptor().minFilter == WGPUFilterMode_Linear || descriptor().magFilter == WGPUFilterMode_Linear || descriptor().mipmapFilter == WGPUFilterMode_Linear; }
 
 private:
-    Sampler(id<MTLSamplerState>, const WGPUSamplerDescriptor& descriptor);
+    Sampler(id<MTLSamplerState>, const WGPUSamplerDescriptor&, Device&);
 
     const id<MTLSamplerState> m_samplerState { nil };
 
@@ -58,6 +60,8 @@
     const WGPUSamplerDescriptor m_descriptor { }; // "The GPUSamplerDescriptor with which the GPUSampler was created."
     // "[[isComparison]] of type boolean." This is unnecessary; it's implemented in isComparison().
     // "[[isFiltering]] of type boolean." This is unnecessary; it's implemented in isFiltering().
+
+    const Ref<Device> m_device;
 };
 
 } // namespace WebGPU

Modified: trunk/Source/WebGPU/WebGPU/Sampler.mm (291570 => 291571)


--- trunk/Source/WebGPU/WebGPU/Sampler.mm	2022-03-21 18:48:25 UTC (rev 291570)
+++ trunk/Source/WebGPU/WebGPU/Sampler.mm	2022-03-21 18:58:31 UTC (rev 291571)
@@ -75,7 +75,8 @@
 
     // "If validating GPUSamplerDescriptor(this, descriptor) returns false:"
     if (!validateCreateSampler(*this, descriptor)) {
-        // FIXME: "Generate a validation error."
+        // "Generate a validation error."
+        generateAValidationError("Validation failure.");
 
         // "Create a new invalid GPUSampler and return the result."
         return nullptr;
@@ -201,12 +202,13 @@
     if (!samplerState)
         return nullptr;
 
-    return Sampler::create(samplerState, descriptor);
+    return Sampler::create(samplerState, descriptor, *this);
 }
 
-Sampler::Sampler(id<MTLSamplerState> samplerState, const WGPUSamplerDescriptor& descriptor)
+Sampler::Sampler(id<MTLSamplerState> samplerState, const WGPUSamplerDescriptor& descriptor, Device& device)
     : m_samplerState(samplerState)
     , m_descriptor(descriptor)
+    , m_device(device)
 {
 }
 
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to