Title: [291313] trunk/Source
Revision
291313
Author
[email protected]
Date
2022-03-15 14:22:35 -0700 (Tue, 15 Mar 2022)

Log Message

[WebGPU] Allow for scheduling asynchronous work
https://bugs.webkit.org/show_bug.cgi?id=237755

Reviewed by Kimmo Kinnunen.

Source/WebCore/PAL:

WebGPU doesn't know how to schedule asynchronous work, so that will have to be handled
by dependency injection from the caller. The way it works is Instance creation takes
a block, which is retained. Whenever asynchronous work needs to be scheduled on the
relevant thread, this block is called, and is passed another block for the work to be
performed.

The API contract is: callers of WebGPU must call its functions in a non-racey way. The
schedule function will execute on a background thread, and it must schedule the block
it's passed to be run in a non-racey way with regards to all the other WebGPU calls.
The schedule function can be NULL, in which case the asynchronous tasks will just be
internally queued up inside WebGPU, and then users will have to call
wgpuInstanceProcessEvents() periodically to synchronously call the queued callbacks.

* pal/graphics/WebGPU/Impl/WebGPUImpl.cpp:
(PAL::WebGPU::GPUImpl::create):
* pal/graphics/WebGPU/Impl/WebGPUImpl.h:

Source/WebGPU:

* WebGPU/Instance.h:
(WebGPU::Instance::runLoop const): Deleted.
* WebGPU/Instance.mm:
(WebGPU::Instance::create):
(WebGPU::Instance::Instance):
(WebGPU::Instance::scheduleWork):
(WebGPU::Instance::defaultScheduleWork):
(WebGPU::Instance::processEvents):
* WebGPU/WebGPUExt.h:

Source/WebKit:

* GPUProcess/graphics/WebGPU/RemoteGPU.cpp:
(WebKit::RemoteGPU::workQueueInitialize):
(WebKit::RemoteGPU::workQueueUninitialize):
* WebProcess/WebCoreSupport/WebChromeClient.cpp:
(WebKit::WebChromeClient::createGPUForWebGPU const):

Source/WebKitLegacy/mac:

* WebCoreSupport/WebChromeClient.mm:
(WebChromeClient::createGPUForWebGPU const):

Modified Paths

Diff

Modified: trunk/Source/WebCore/PAL/ChangeLog (291312 => 291313)


--- trunk/Source/WebCore/PAL/ChangeLog	2022-03-15 21:17:33 UTC (rev 291312)
+++ trunk/Source/WebCore/PAL/ChangeLog	2022-03-15 21:22:35 UTC (rev 291313)
@@ -1,3 +1,27 @@
+2022-03-15  Myles C. Maxfield  <[email protected]>
+
+        [WebGPU] Allow for scheduling asynchronous work
+        https://bugs.webkit.org/show_bug.cgi?id=237755
+
+        Reviewed by Kimmo Kinnunen.
+
+        WebGPU doesn't know how to schedule asynchronous work, so that will have to be handled
+        by dependency injection from the caller. The way it works is Instance creation takes
+        a block, which is retained. Whenever asynchronous work needs to be scheduled on the
+        relevant thread, this block is called, and is passed another block for the work to be
+        performed.
+
+        The API contract is: callers of WebGPU must call its functions in a non-racey way. The
+        schedule function will execute on a background thread, and it must schedule the block
+        it's passed to be run in a non-racey way with regards to all the other WebGPU calls.
+        The schedule function can be NULL, in which case the asynchronous tasks will just be
+        internally queued up inside WebGPU, and then users will have to call
+        wgpuInstanceProcessEvents() periodically to synchronously call the queued callbacks.
+
+        * pal/graphics/WebGPU/Impl/WebGPUImpl.cpp:
+        (PAL::WebGPU::GPUImpl::create):
+        * pal/graphics/WebGPU/Impl/WebGPUImpl.h:
+
 2022-03-15  Jer Noble  <[email protected]>
 
         [Cocoa] Adopt AVAssetPrefersSandboxedParsingOptionKey

Modified: trunk/Source/WebCore/PAL/pal/graphics/WebGPU/Impl/WebGPUImpl.cpp (291312 => 291313)


--- trunk/Source/WebCore/PAL/pal/graphics/WebGPU/Impl/WebGPUImpl.cpp	2022-03-15 21:17:33 UTC (rev 291312)
+++ trunk/Source/WebCore/PAL/pal/graphics/WebGPU/Impl/WebGPUImpl.cpp	2022-03-15 21:22:35 UTC (rev 291313)
@@ -31,6 +31,7 @@
 #include "WebGPUAdapterImpl.h"
 #include "WebGPUDowncastConvertToBackingContext.h"
 #include <WebGPU/WebGPUExt.h>
+#include <wtf/BlockPtr.h>
 
 #if PLATFORM(COCOA)
 #include <wtf/darwin/WeakLinking.h>
@@ -40,9 +41,21 @@
 
 namespace PAL::WebGPU {
 
-RefPtr<GPUImpl> GPUImpl::create()
+RefPtr<GPUImpl> GPUImpl::create(ScheduleWorkFunction&& scheduleWorkFunction)
 {
-    WGPUInstanceDescriptor descriptor = { nullptr };
+    auto scheduleWorkBlock = makeBlockPtr([scheduleWorkFunction = WTFMove(scheduleWorkFunction)](WGPUWorkItem workItem)
+    {
+        scheduleWorkFunction(makeBlockPtr(WTFMove(workItem)));
+    });
+    WGPUInstanceCocoaDescriptor cocoaDescriptor {
+        {
+            nullptr,
+            static_cast<WGPUSType>(WGPUSTypeExtended_InstanceCocoaDescriptor),
+        },
+        scheduleWorkBlock.get(),
+    };
+    WGPUInstanceDescriptor descriptor = { &cocoaDescriptor.chain };
+
     if (!&wgpuCreateInstance)
         return nullptr;
     auto instance = wgpuCreateInstance(&descriptor);

Modified: trunk/Source/WebCore/PAL/pal/graphics/WebGPU/Impl/WebGPUImpl.h (291312 => 291313)


--- trunk/Source/WebCore/PAL/pal/graphics/WebGPU/Impl/WebGPUImpl.h	2022-03-15 21:17:33 UTC (rev 291312)
+++ trunk/Source/WebCore/PAL/pal/graphics/WebGPU/Impl/WebGPUImpl.h	2022-03-15 21:22:35 UTC (rev 291313)
@@ -29,7 +29,9 @@
 
 #include "WebGPU.h"
 #include <WebGPU/WebGPU.h>
+#include <functional>
 #include <wtf/Deque.h>
+#include <wtf/Function.h>
 
 namespace PAL::WebGPU {
 
@@ -38,7 +40,9 @@
 class GPUImpl final : public GPU {
     WTF_MAKE_FAST_ALLOCATED;
 public:
-    PAL_EXPORT static RefPtr<GPUImpl> create();
+    using WorkItem = Function<void(void)>;
+    using ScheduleWorkFunction = Function<void(WorkItem&&)>;
+    PAL_EXPORT static RefPtr<GPUImpl> create(ScheduleWorkFunction&&);
 
     static Ref<GPUImpl> create(WGPUInstance instance, ConvertToBackingContext& convertToBackingContext)
     {

Modified: trunk/Source/WebGPU/ChangeLog (291312 => 291313)


--- trunk/Source/WebGPU/ChangeLog	2022-03-15 21:17:33 UTC (rev 291312)
+++ trunk/Source/WebGPU/ChangeLog	2022-03-15 21:22:35 UTC (rev 291313)
@@ -1,3 +1,20 @@
+2022-03-15  Myles C. Maxfield  <[email protected]>
+
+        [WebGPU] Allow for scheduling asynchronous work
+        https://bugs.webkit.org/show_bug.cgi?id=237755
+
+        Reviewed by Kimmo Kinnunen.
+
+        * WebGPU/Instance.h:
+        (WebGPU::Instance::runLoop const): Deleted.
+        * WebGPU/Instance.mm:
+        (WebGPU::Instance::create):
+        (WebGPU::Instance::Instance):
+        (WebGPU::Instance::scheduleWork):
+        (WebGPU::Instance::defaultScheduleWork):
+        (WebGPU::Instance::processEvents):
+        * WebGPU/WebGPUExt.h:
+
 2022-03-12  Tim Horton  <[email protected]>
 
         Adopt FALLBACK_PLATFORM_NAME in place of FALLBACK_PLATFORM

Modified: trunk/Source/WebGPU/WebGPU/Instance.h (291312 => 291313)


--- trunk/Source/WebGPU/WebGPU/Instance.h	2022-03-15 21:17:33 UTC (rev 291312)
+++ trunk/Source/WebGPU/WebGPU/Instance.h	2022-03-15 21:22:35 UTC (rev 291313)
@@ -25,12 +25,13 @@
 
 #pragma once
 
-#import <Foundation/Foundation.h>
+#import <wtf/Deque.h>
 #import <wtf/FastMalloc.h>
 #import <wtf/Function.h>
+#import <wtf/Lock.h>
 #import <wtf/Ref.h>
-#import <wtf/RefCounted.h>
 #import <wtf/RefPtr.h>
+#import <wtf/ThreadSafeRefCounted.h>
 
 namespace WebGPU {
 
@@ -37,7 +38,7 @@
 class Adapter;
 class Surface;
 
-class Instance : public RefCounted<Instance> {
+class Instance : public ThreadSafeRefCounted<Instance> {
     WTF_MAKE_FAST_ALLOCATED;
 public:
     static RefPtr<Instance> create(const WGPUInstanceDescriptor&);
@@ -48,12 +49,20 @@
     void processEvents();
     void requestAdapter(const WGPURequestAdapterOptions&, WTF::Function<void(WGPURequestAdapterStatus, RefPtr<Adapter>&&, const char*)>&& callback);
 
-    NSRunLoop *runLoop() const { return m_runLoop; }
+    // This can be called on a background thread.
+    using WorkItem = Function<void(void)>;
+    void scheduleWork(WorkItem&&);
 
 private:
-    Instance(NSRunLoop *);
+    Instance(WGPUScheduleWorkBlock);
 
-    NSRunLoop *m_runLoop;
+    // This can be called on a background thread.
+    void defaultScheduleWork(WGPUWorkItem&&);
+
+    // This can be used on a background thread.
+    Deque<WGPUWorkItem> m_pendingWork WTF_GUARDED_BY_LOCK(m_lock);
+    WGPUScheduleWorkBlock m_scheduleWorkBlock;
+    Lock m_lock;
 };
 
 } // namespace WebGPU

Modified: trunk/Source/WebGPU/WebGPU/Instance.mm (291312 => 291313)


--- trunk/Source/WebGPU/WebGPU/Instance.mm	2022-03-15 21:17:33 UTC (rev 291312)
+++ trunk/Source/WebGPU/WebGPU/Instance.mm	2022-03-15 21:22:35 UTC (rev 291313)
@@ -29,26 +29,29 @@
 #import "Adapter.h"
 #import "Surface.h"
 #import <cstring>
+#import <wtf/BlockPtr.h>
 #import <wtf/StdLibExtras.h>
 
 namespace WebGPU {
 
-static constexpr NSString *runLoopMode = @"kCFRunLoopWebGPUMode";
-
 RefPtr<Instance> Instance::create(const WGPUInstanceDescriptor& descriptor)
 {
-    if (descriptor.nextInChain)
+    if (!descriptor.nextInChain)
+        return adoptRef(*new Instance(nullptr));
+
+    if (descriptor.nextInChain->sType != static_cast<WGPUSType>(WGPUSTypeExtended_InstanceCocoaDescriptor))
         return nullptr;
 
-    NSRunLoop *runLoop = NSRunLoop.currentRunLoop;
-    if (!runLoop)
+    const WGPUInstanceCocoaDescriptor& cocoaDescriptor = reinterpret_cast<const WGPUInstanceCocoaDescriptor&>(*descriptor.nextInChain);
+
+    if (cocoaDescriptor.chain.next)
         return nullptr;
 
-    return adoptRef(*new Instance(runLoop));
+    return adoptRef(*new Instance(cocoaDescriptor.scheduleWorkBlock));
 }
 
-Instance::Instance(NSRunLoop *runLoop)
-    : m_runLoop(runLoop)
+Instance::Instance(WGPUScheduleWorkBlock scheduleWorkBlock)
+    : m_scheduleWorkBlock(scheduleWorkBlock ? WTFMove(scheduleWorkBlock) : ^(WGPUWorkItem workItem) { defaultScheduleWork(WTFMove(workItem)); })
 {
 }
 
@@ -61,17 +64,30 @@
     return Surface::create();
 }
 
+void Instance::scheduleWork(WorkItem&& workItem)
+{
+    m_scheduleWorkBlock(makeBlockPtr(WTFMove(workItem)).get());
+}
+
+void Instance::defaultScheduleWork(WGPUWorkItem&& workItem)
+{
+    LockHolder lockHolder(m_lock);
+    m_pendingWork.append(WTFMove(workItem));
+}
+
 void Instance::processEvents()
 {
-    // NSRunLoops are not thread-safe, but then again neither is WebGPU.
-    // If the caller does all the necessary synchronization themselves, this will be safe.
-    // In that situation, we have to make sure we're using the run loop we were created on,
-    // so tasks that were queued up on one thread actually get executed here, even if
-    // this is executing on a different thread.
-    BOOL result;
-    do {
-        result = [m_runLoop runMode:runLoopMode beforeDate:[NSDate date]];
-    } while (result);
+    while (true) {
+        Deque<WGPUWorkItem> localWork;
+        {
+            LockHolder lockHolder(m_lock);
+            std::swap(m_pendingWork, localWork);
+        }
+        if (localWork.isEmpty())
+            return;
+        for (auto& workItem : localWork)
+            workItem();
+    }
 }
 
 static NSArray<id<MTLDevice>> *sortedDevices(NSArray<id<MTLDevice>> *devices, WGPUPowerPreference powerPreference)

Modified: trunk/Source/WebGPU/WebGPU/WebGPUExt.h (291312 => 291313)


--- trunk/Source/WebGPU/WebGPU/WebGPUExt.h	2022-03-15 21:17:33 UTC (rev 291312)
+++ trunk/Source/WebGPU/WebGPU/WebGPUExt.h	2022-03-15 21:22:35 UTC (rev 291313)
@@ -39,13 +39,26 @@
 typedef void (^WGPUQueueWorkDoneBlockCallback)(WGPUQueueWorkDoneStatus status);
 typedef void (^WGPURequestAdapterBlockCallback)(WGPURequestAdapterStatus status, WGPUAdapter adapter, char const * message);
 typedef void (^WGPURequestDeviceBlockCallback)(WGPURequestDeviceStatus status, WGPUDevice device, char const * message);
+typedef void (^WGPUWorkItem)(void);
+typedef void (^WGPUScheduleWorkBlock)(WGPUWorkItem workItem);
 
 typedef enum WGPUSTypeExtended {
     WGPUSTypeExtended_ShaderModuleDescriptorHints = 0x348970F3, // Random
     WGPUSTypeExtended_TextureDescriptorViewFormats = 0x1D5BC57, // Random
+    WGPUSTypeExtended_InstanceCocoaDescriptor = 0x151BBC00, // Random
     WGPUSTypeExtended_Force32 = 0x7FFFFFFF
 } WGPUSTypeExtended;
 
+typedef struct WGPUInstanceCocoaDescriptor {
+    WGPUChainedStruct chain;
+    // The API contract is: callers must call WebGPU's functions in a non-racey way with respect
+    // to each other. This scheduleWorkBlock will execute on a background thread, and it must
+    // schedule the block it's passed to be run in a non-racey way with regards to all the other
+    // WebGPU calls. It's fine to pass NULL here, but if you do, you must periodically call
+    // wgpuInstanceProcessEvents() to synchronously run the queued callbacks.
+    WGPUScheduleWorkBlock scheduleWorkBlock;
+} WGPUInstanceCocoaDescriptor;
+
 typedef struct WGPUShaderModuleCompilationHint {
     WGPUPipelineLayout layout;
 } WGPUShaderModuleCompilationHint;

Modified: trunk/Source/WebKit/ChangeLog (291312 => 291313)


--- trunk/Source/WebKit/ChangeLog	2022-03-15 21:17:33 UTC (rev 291312)
+++ trunk/Source/WebKit/ChangeLog	2022-03-15 21:22:35 UTC (rev 291313)
@@ -1,3 +1,16 @@
+2022-03-15  Myles C. Maxfield  <[email protected]>
+
+        [WebGPU] Allow for scheduling asynchronous work
+        https://bugs.webkit.org/show_bug.cgi?id=237755
+
+        Reviewed by Kimmo Kinnunen.
+
+        * GPUProcess/graphics/WebGPU/RemoteGPU.cpp:
+        (WebKit::RemoteGPU::workQueueInitialize):
+        (WebKit::RemoteGPU::workQueueUninitialize):
+        * WebProcess/WebCoreSupport/WebChromeClient.cpp:
+        (WebKit::WebChromeClient::createGPUForWebGPU const):
+
 2022-03-15  Chris Dumez  <[email protected]>
 
         Fix logging in GPUProcessProxy::didCreateContextForVisibilityPropagation()

Modified: trunk/Source/WebKit/GPUProcess/graphics/WebGPU/RemoteGPU.cpp (291312 => 291313)


--- trunk/Source/WebKit/GPUProcess/graphics/WebGPU/RemoteGPU.cpp	2022-03-15 21:17:33 UTC (rev 291312)
+++ trunk/Source/WebKit/GPUProcess/graphics/WebGPU/RemoteGPU.cpp	2022-03-15 21:22:35 UTC (rev 291313)
@@ -79,7 +79,14 @@
 {
     assertIsCurrent(workQueue());
 #if HAVE(WEBGPU_IMPLEMENTATION)
-    auto backing = PAL::WebGPU::GPUImpl::create();
+    // BEWARE: This is a retain cycle.
+    // this owns m_backing, but m_backing contains a callback which has a stong reference to this.
+    // The retain cycle is required because callbacks need to execute even if this is disowned
+    // (because the callbacks handle resource cleanup, etc.).
+    // The retain cycle is broken in workQueueUninitialize().
+    auto backing = PAL::WebGPU::GPUImpl::create([protectedThis = Ref { *this }](PAL::WebGPU::GPUImpl::WorkItem&& workItem) {
+        protectedThis->workQueue().dispatch(WTFMove(workItem));
+    });
 #else
     RefPtr<PAL::WebGPU::GPU> backing;
 #endif
@@ -95,6 +102,7 @@
     assertIsCurrent(workQueue());
     m_streamConnection = nullptr;
     m_objectHeap->clear();
+    m_backing = nullptr;
 }
 
 void RemoteGPU::requestAdapter(const WebGPU::RequestAdapterOptions& options, WebGPUIdentifier identifier, WTF::CompletionHandler<void(std::optional<RequestAdapterResponse>&&)>&& callback)

Modified: trunk/Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.cpp (291312 => 291313)


--- trunk/Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.cpp	2022-03-15 21:17:33 UTC (rev 291312)
+++ trunk/Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.cpp	2022-03-15 21:22:35 UTC (rev 291313)
@@ -957,7 +957,9 @@
 #if ENABLE(GPU_PROCESS)
     return RemoteGPUProxy::create(WebProcess::singleton().ensureGPUProcessConnection(), WebGPU::DowncastConvertToBackingContext::create(), WebGPUIdentifier::generate(), m_page.ensureRemoteRenderingBackendProxy().ensureBackendCreated());
 #else
-    return PAL::WebGPU::GPUImpl::create();
+    return PAL::WebGPU::GPUImpl::create([](PAL::WebGPU::GPUImpl::WorkItem&& workItem) {
+        callOnMainRunLoop(WTFMove(workItem));
+    });
 #endif
 #else
     return nullptr;

Modified: trunk/Source/WebKitLegacy/mac/ChangeLog (291312 => 291313)


--- trunk/Source/WebKitLegacy/mac/ChangeLog	2022-03-15 21:17:33 UTC (rev 291312)
+++ trunk/Source/WebKitLegacy/mac/ChangeLog	2022-03-15 21:22:35 UTC (rev 291313)
@@ -1,3 +1,13 @@
+2022-03-15  Myles C. Maxfield  <[email protected]>
+
+        [WebGPU] Allow for scheduling asynchronous work
+        https://bugs.webkit.org/show_bug.cgi?id=237755
+
+        Reviewed by Kimmo Kinnunen.
+
+        * WebCoreSupport/WebChromeClient.mm:
+        (WebChromeClient::createGPUForWebGPU const):
+
 2022-03-15  Wenson Hsieh  <[email protected]>
 
         [macOS] Tooltip no longer disappears after leaving hovered element

Modified: trunk/Source/WebKitLegacy/mac/WebCoreSupport/WebChromeClient.mm (291312 => 291313)


--- trunk/Source/WebKitLegacy/mac/WebCoreSupport/WebChromeClient.mm	2022-03-15 21:17:33 UTC (rev 291312)
+++ trunk/Source/WebKitLegacy/mac/WebCoreSupport/WebChromeClient.mm	2022-03-15 21:22:35 UTC (rev 291313)
@@ -1163,7 +1163,9 @@
 RefPtr<PAL::WebGPU::GPU> WebChromeClient::createGPUForWebGPU() const
 {
 #if HAVE(WEBGPU_IMPLEMENTATION)
-    return PAL::WebGPU::GPUImpl::create();
+    return PAL::WebGPU::GPUImpl::create([](PAL::WebGPU::GPUImpl::WorkItem&& workItem) {
+        callOnMainRunLoop(WTFMove(workItem));
+    });
 #else
     return nullptr;
 #endif
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to