[wgpu-headers] Introduce 2nd userdata to onSubmittedWorkDone callback.

- We need to differentiate between 1 userdata and 2 userdata because
  they use different callback mechanisms that won't work with one
  another.
- Note that usages will be updated in a follow up after
  mapAsync has been updated as well since there's some inter-dependency
  between these two calls.

Bug: 42241461
Change-Id: I961bcb96620bee3b6fe5654769e80a6058ce011f
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/188225
Reviewed-by: Austin Eng <enga@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Loko Kung <lokokung@google.com>
diff --git a/src/dawn/dawn.json b/src/dawn/dawn.json
index 4b71a1a..02bf724 100644
--- a/src/dawn/dawn.json
+++ b/src/dawn/dawn.json
@@ -2786,6 +2786,15 @@
                 ]
             },
             {
+                "name": "on submitted work done 2",
+                "_comment": "TODO(crbug.com/dawn/2021): This is dawn/emscripten-only until we rename it to replace the old API. See bug for details.",
+                "tags": ["dawn", "emscripten"],
+                "returns": "future",
+                "args": [
+                    {"name": "callback info", "type": "queue work done callback info 2"}
+                ]
+            },
+            {
                 "name": "write buffer",
                 "args": [
                     {"name": "buffer", "type": "buffer"},
@@ -2849,6 +2858,12 @@
             {"name": "userdata", "type": "void *"}
         ]
     },
+    "queue work done callback 2": {
+        "category": "callback function",
+        "args": [
+            {"name": "status", "type": "queue work done status"}
+        ]
+    },
     "queue work done callback info": {
         "category": "structure",
         "extensible": "in",
@@ -2858,6 +2873,12 @@
             {"name": "userdata", "type": "void *"}
         ]
     },
+    "queue work done callback info 2": {
+        "category": "callback info",
+        "members": [
+            {"name": "callback", "type": "queue work done callback 2"}
+        ]
+    },
     "queue work done status": {
         "category": "enum",
         "emscripten_no_enum_table": true,
diff --git a/src/dawn/dawn_wire.json b/src/dawn/dawn_wire.json
index 212d7ba..85fbcf0 100644
--- a/src/dawn/dawn_wire.json
+++ b/src/dawn/dawn_wire.json
@@ -81,7 +81,8 @@
         "queue on submitted work done": [
             { "name": "queue id", "type": "ObjectId", "id_type": "queue" },
             { "name": "event manager handle", "type": "ObjectHandle" },
-            { "name": "future", "type": "future" }
+            { "name": "future", "type": "future" },
+            { "name": "userdata count", "type": "uint8_t", "_comment": "TODO(crbug.com/dawn/2509): Remove this once Chromium overrides the correct functions in the proc table."}
         ],
         "queue write buffer": [
             {"name": "queue id", "type": "ObjectId", "id_type": "queue" },
@@ -246,6 +247,7 @@
             "QuerySetGetCount",
             "QueueOnSubmittedWorkDone",
             "QueueOnSubmittedWorkDoneF",
+            "QueueOnSubmittedWorkDone2",
             "QueueWriteBuffer",
             "QueueWriteTexture",
             "SurfaceGetCapabilities",
diff --git a/src/dawn/native/Queue.cpp b/src/dawn/native/Queue.cpp
index 8109c1f..9c5e924 100644
--- a/src/dawn/native/Queue.cpp
+++ b/src/dawn/native/Queue.cpp
@@ -200,43 +200,6 @@
     MaybeError WaitForIdleForDestruction() override { DAWN_UNREACHABLE(); }
 };
 
-struct WorkDoneEvent final : public EventManager::TrackedEvent {
-    std::optional<wgpu::QueueWorkDoneStatus> mEarlyStatus;
-    WGPUQueueWorkDoneCallback mCallback;
-    raw_ptr<void> mUserdata;
-
-    // Create an event backed by the given queue execution serial.
-    WorkDoneEvent(const QueueWorkDoneCallbackInfo& callbackInfo,
-                  QueueBase* queue,
-                  ExecutionSerial serial)
-        : TrackedEvent(callbackInfo.mode, queue, serial),
-          mCallback(callbackInfo.callback),
-          mUserdata(callbackInfo.userdata) {}
-
-    // Create an event that's ready at creation (for errors, etc.)
-    WorkDoneEvent(const QueueWorkDoneCallbackInfo& callbackInfo,
-                  QueueBase* queue,
-                  wgpu::QueueWorkDoneStatus earlyStatus)
-        : TrackedEvent(callbackInfo.mode, queue, kBeginningOfGPUTime),
-          mEarlyStatus(earlyStatus),
-          mCallback(callbackInfo.callback),
-          mUserdata(callbackInfo.userdata) {}
-
-    ~WorkDoneEvent() override { EnsureComplete(EventCompletionType::Shutdown); }
-
-    void Complete(EventCompletionType completionType) override {
-        // WorkDoneEvent has no error cases other than the mEarlyStatus ones.
-        wgpu::QueueWorkDoneStatus status = wgpu::QueueWorkDoneStatus::Success;
-        if (completionType == EventCompletionType::Shutdown) {
-            status = wgpu::QueueWorkDoneStatus::InstanceDropped;
-        } else if (mEarlyStatus) {
-            status = mEarlyStatus.value();
-        }
-
-        mCallback(ToAPI(status), mUserdata.ExtractAsDangling());
-    }
-};
-
 }  // namespace
 
 // TrackTaskCallback
@@ -316,6 +279,58 @@
 }
 
 Future QueueBase::APIOnSubmittedWorkDoneF(const QueueWorkDoneCallbackInfo& callbackInfo) {
+    return APIOnSubmittedWorkDone2(
+        {ToAPI(callbackInfo.nextInChain), ToAPI(callbackInfo.mode),
+         [](WGPUQueueWorkDoneStatus status, void* callback, void* userdata) {
+             auto cb = reinterpret_cast<WGPUQueueWorkDoneCallback>(callback);
+             cb(status, userdata);
+         },
+         reinterpret_cast<void*>(callbackInfo.callback), callbackInfo.userdata});
+}
+
+Future QueueBase::APIOnSubmittedWorkDone2(const WGPUQueueWorkDoneCallbackInfo2& callbackInfo) {
+    struct WorkDoneEvent final : public EventManager::TrackedEvent {
+        std::optional<WGPUQueueWorkDoneStatus> mEarlyStatus;
+        WGPUQueueWorkDoneCallback2 mCallback;
+        raw_ptr<void> mUserdata1;
+        raw_ptr<void> mUserdata2;
+
+        // Create an event backed by the given queue execution serial.
+        WorkDoneEvent(const WGPUQueueWorkDoneCallbackInfo2& callbackInfo,
+                      QueueBase* queue,
+                      ExecutionSerial serial)
+            : TrackedEvent(static_cast<wgpu::CallbackMode>(callbackInfo.mode), queue, serial),
+              mCallback(callbackInfo.callback),
+              mUserdata1(callbackInfo.userdata1),
+              mUserdata2(callbackInfo.userdata2) {}
+
+        // Create an event that's ready at creation (for errors, etc.)
+        WorkDoneEvent(const WGPUQueueWorkDoneCallbackInfo2& callbackInfo,
+                      QueueBase* queue,
+                      wgpu::QueueWorkDoneStatus earlyStatus)
+            : TrackedEvent(static_cast<wgpu::CallbackMode>(callbackInfo.mode),
+                           queue,
+                           kBeginningOfGPUTime),
+              mEarlyStatus(ToAPI(earlyStatus)),
+              mCallback(callbackInfo.callback),
+              mUserdata1(callbackInfo.userdata1),
+              mUserdata2(callbackInfo.userdata2) {}
+
+        ~WorkDoneEvent() override { EnsureComplete(EventCompletionType::Shutdown); }
+
+        void Complete(EventCompletionType completionType) override {
+            // WorkDoneEvent has no error cases other than the mEarlyStatus ones.
+            WGPUQueueWorkDoneStatus status = WGPUQueueWorkDoneStatus_Success;
+            if (completionType == EventCompletionType::Shutdown) {
+                status = WGPUQueueWorkDoneStatus_InstanceDropped;
+            } else if (mEarlyStatus) {
+                status = mEarlyStatus.value();
+            }
+
+            mCallback(status, mUserdata1.ExtractAsDangling(), mUserdata2.ExtractAsDangling());
+        }
+    };
+
     // TODO(crbug.com/dawn/2052): Once we always return a future, change this to log to the instance
     // (note, not raise a validation error to the device) and return the null future.
     DAWN_ASSERT(callbackInfo.nextInChain == nullptr);
diff --git a/src/dawn/native/Queue.h b/src/dawn/native/Queue.h
index a8d40b6..9f6c3ef 100644
--- a/src/dawn/native/Queue.h
+++ b/src/dawn/native/Queue.h
@@ -75,6 +75,7 @@
     void APISubmit(uint32_t commandCount, CommandBufferBase* const* commands);
     void APIOnSubmittedWorkDone(WGPUQueueWorkDoneCallback callback, void* userdata);
     Future APIOnSubmittedWorkDoneF(const QueueWorkDoneCallbackInfo& callbackInfo);
+    Future APIOnSubmittedWorkDone2(const WGPUQueueWorkDoneCallbackInfo2& callbackInfo);
     void APIWriteBuffer(BufferBase* buffer, uint64_t bufferOffset, const void* data, size_t size);
     void APIWriteTexture(const ImageCopyTexture* destination,
                          const void* data,
diff --git a/src/dawn/wire/client/Queue.cpp b/src/dawn/wire/client/Queue.cpp
index cfdf86b..2680e16 100644
--- a/src/dawn/wire/client/Queue.cpp
+++ b/src/dawn/wire/client/Queue.cpp
@@ -41,10 +41,11 @@
   public:
     static constexpr EventType kType = EventType::WorkDone;
 
-    explicit WorkDoneEvent(const WGPUQueueWorkDoneCallbackInfo& callbackInfo)
+    explicit WorkDoneEvent(const WGPUQueueWorkDoneCallbackInfo2& callbackInfo)
         : TrackedEvent(callbackInfo.mode),
           mCallback(callbackInfo.callback),
-          mUserdata(callbackInfo.userdata) {}
+          mUserdata1(callbackInfo.userdata1),
+          mUserdata2(callbackInfo.userdata2) {}
 
     EventType GetType() override { return kType; }
 
@@ -61,13 +62,16 @@
         if (mStatus == WGPUQueueWorkDoneStatus_DeviceLost) {
             mStatus = WGPUQueueWorkDoneStatus_Success;
         }
+        void* userdata1 = mUserdata1.ExtractAsDangling();
+        void* userdata2 = mUserdata2.ExtractAsDangling();
         if (mCallback) {
-            mCallback(mStatus, mUserdata.ExtractAsDangling());
+            mCallback(mStatus, userdata1, userdata2);
         }
     }
 
-    WGPUQueueWorkDoneCallback mCallback;
-    raw_ptr<void> mUserdata;
+    WGPUQueueWorkDoneCallback2 mCallback;
+    raw_ptr<void> mUserdata1;
+    raw_ptr<void> mUserdata2;
 
     WGPUQueueWorkDoneStatus mStatus = WGPUQueueWorkDoneStatus_Success;
 };
@@ -87,11 +91,7 @@
 }
 
 void Queue::OnSubmittedWorkDone(WGPUQueueWorkDoneCallback callback, void* userdata) {
-    WGPUQueueWorkDoneCallbackInfo callbackInfo = {};
-    callbackInfo.mode = WGPUCallbackMode_AllowSpontaneous;
-    callbackInfo.callback = callback;
-    callbackInfo.userdata = userdata;
-    OnSubmittedWorkDoneF(callbackInfo);
+    OnSubmittedWorkDoneF({nullptr, WGPUCallbackMode_AllowSpontaneous, callback, userdata});
 }
 
 WGPUFuture Queue::OnSubmittedWorkDoneF(const WGPUQueueWorkDoneCallbackInfo& callbackInfo) {
@@ -101,6 +101,36 @@
 
     Client* client = GetClient();
     auto [futureIDInternal, tracked] =
+        GetEventManager().TrackEvent(std::make_unique<WorkDoneEvent>(WGPUQueueWorkDoneCallbackInfo2{
+            callbackInfo.nextInChain, callbackInfo.mode,
+            [](WGPUQueueWorkDoneStatus status, void* callback, void* userdata) {
+                auto cb = reinterpret_cast<WGPUQueueWorkDoneCallback>(callback);
+                cb(status, userdata);
+            },
+            reinterpret_cast<void*>(callbackInfo.callback != nullptr ? callbackInfo.callback
+                                                                     : nullptr),
+            callbackInfo.userdata}));
+    if (!tracked) {
+        return {futureIDInternal};
+    }
+
+    QueueOnSubmittedWorkDoneCmd cmd;
+    cmd.queueId = GetWireId();
+    cmd.eventManagerHandle = GetEventManagerHandle();
+    cmd.future = {futureIDInternal};
+    cmd.userdataCount = 1;
+
+    client->SerializeCommand(cmd);
+    return {futureIDInternal};
+}
+
+WGPUFuture Queue::OnSubmittedWorkDone2(const WGPUQueueWorkDoneCallbackInfo2& callbackInfo) {
+    // TODO(crbug.com/dawn/2052): Once we always return a future, change this to log to the instance
+    // (note, not raise a validation error to the device) and return the null future.
+    DAWN_ASSERT(callbackInfo.nextInChain == nullptr);
+
+    Client* client = GetClient();
+    auto [futureIDInternal, tracked] =
         GetEventManager().TrackEvent(std::make_unique<WorkDoneEvent>(callbackInfo));
     if (!tracked) {
         return {futureIDInternal};
@@ -110,6 +140,7 @@
     cmd.queueId = GetWireId();
     cmd.eventManagerHandle = GetEventManagerHandle();
     cmd.future = {futureIDInternal};
+    cmd.userdataCount = 2;
 
     client->SerializeCommand(cmd);
     return {futureIDInternal};
diff --git a/src/dawn/wire/client/Queue.h b/src/dawn/wire/client/Queue.h
index 6708c03..0566f08 100644
--- a/src/dawn/wire/client/Queue.h
+++ b/src/dawn/wire/client/Queue.h
@@ -45,6 +45,7 @@
     // Dawn API
     void OnSubmittedWorkDone(WGPUQueueWorkDoneCallback callback, void* userdata);
     WGPUFuture OnSubmittedWorkDoneF(const WGPUQueueWorkDoneCallbackInfo& callbackInfo);
+    WGPUFuture OnSubmittedWorkDone2(const WGPUQueueWorkDoneCallbackInfo2& callbackInfo);
     void WriteBuffer(WGPUBuffer cBuffer, uint64_t bufferOffset, const void* data, size_t size);
     void WriteTexture(const WGPUImageCopyTexture* destination,
                       const void* data,
diff --git a/src/dawn/wire/server/ServerQueue.cpp b/src/dawn/wire/server/ServerQueue.cpp
index 807861b..338a3b5 100644
--- a/src/dawn/wire/server/ServerQueue.cpp
+++ b/src/dawn/wire/server/ServerQueue.cpp
@@ -43,14 +43,22 @@
 
 WireResult Server::DoQueueOnSubmittedWorkDone(Known<WGPUQueue> queue,
                                               ObjectHandle eventManager,
-                                              WGPUFuture future) {
+                                              WGPUFuture future,
+                                              uint8_t userdataCount) {
     auto userdata = MakeUserdata<QueueWorkDoneUserdata>();
     userdata->queue = queue.AsHandle();
     userdata->eventManager = eventManager;
     userdata->future = future;
 
-    mProcs.queueOnSubmittedWorkDone(queue->handle, ForwardToServer<&Server::OnQueueWorkDone>,
-                                    userdata.release());
+    if (userdataCount == 1) {
+        mProcs.queueOnSubmittedWorkDone(queue->handle, ForwardToServer<&Server::OnQueueWorkDone>,
+                                        userdata.release());
+    } else {
+        mProcs.queueOnSubmittedWorkDone2(
+            queue->handle,
+            {nullptr, WGPUCallbackMode_AllowProcessEvents,
+             ForwardToServer2<&Server::OnQueueWorkDone>, userdata.release(), nullptr});
+    }
     return WireResult::Success;
 }