Implement Queue::OnSubmittedWorkDone

This is the replacement for Fence in the single-queue WebGPU world. To
keep this CL focused, it doesn't deprecate the fences yet.

Bug: chromium:1177476

Change-Id: I09d60732ec67bc1deb49f7a9d57699c049475acf
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/41723
Auto-Submit: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/dawn_wire/client/ClientDoers.cpp b/src/dawn_wire/client/ClientDoers.cpp
index a5ef7f6..2c6e23f 100644
--- a/src/dawn_wire/client/ClientDoers.cpp
+++ b/src/dawn_wire/client/ClientDoers.cpp
@@ -66,7 +66,6 @@
         if (buffer == nullptr) {
             return true;
         }
-
         return buffer->OnMapAsyncCallback(requestSerial, status, readInitialDataInfoLength,
                                           readInitialDataInfo);
     }
@@ -88,9 +87,17 @@
         if (fence == nullptr) {
             return true;
         }
+        return fence->OnCompletionCallback(requestSerial, status);
+    }
 
-        fence->OnCompletionCallback(requestSerial, status);
-        return true;
+    bool Client::DoQueueWorkDoneCallback(Queue* queue,
+                                         uint64_t requestSerial,
+                                         WGPUQueueWorkDoneStatus status) {
+        // The queue might have been deleted or recreated so this isn't an error.
+        if (queue == nullptr) {
+            return true;
+        }
+        return queue->OnWorkDoneCallback(requestSerial, status);
     }
 
     bool Client::DoDeviceCreateComputePipelineAsyncCallback(Device* device,
diff --git a/src/dawn_wire/client/Fence.cpp b/src/dawn_wire/client/Fence.cpp
index d9a858a..563f13f1 100644
--- a/src/dawn_wire/client/Fence.cpp
+++ b/src/dawn_wire/client/Fence.cpp
@@ -46,7 +46,8 @@
                              WGPUFenceOnCompletionCallback callback,
                              void* userdata) {
         if (client->IsDisconnected()) {
-            return callback(WGPUFenceCompletionStatus_DeviceLost, userdata);
+            callback(WGPUFenceCompletionStatus_DeviceLost, userdata);
+            return;
         }
 
         uint32_t serial = mOnCompletionRequestSerial++;
diff --git a/src/dawn_wire/client/Queue.cpp b/src/dawn_wire/client/Queue.cpp
index f5f68f7..8c9f78b 100644
--- a/src/dawn_wire/client/Queue.cpp
+++ b/src/dawn_wire/client/Queue.cpp
@@ -19,6 +19,47 @@
 
 namespace dawn_wire { namespace client {
 
+    Queue::~Queue() {
+        ClearAllCallbacks(WGPUQueueWorkDoneStatus_Unknown);
+    }
+
+    bool Queue::OnWorkDoneCallback(uint64_t requestSerial, WGPUQueueWorkDoneStatus status) {
+        auto requestIt = mOnWorkDoneRequests.find(requestSerial);
+        if (requestIt == mOnWorkDoneRequests.end()) {
+            return false;
+        }
+
+        // Remove the request data so that the callback cannot be called again.
+        // ex.) inside the callback: if the queue is deleted (when there are multiple queues),
+        // all callbacks reject.
+        OnWorkDoneData request = std::move(requestIt->second);
+        mOnWorkDoneRequests.erase(requestIt);
+
+        request.callback(status, request.userdata);
+        return true;
+    }
+
+    void Queue::OnSubmittedWorkDone(uint64_t signalValue,
+                                    WGPUQueueWorkDoneCallback callback,
+                                    void* userdata) {
+        if (client->IsDisconnected()) {
+            callback(WGPUQueueWorkDoneStatus_DeviceLost, userdata);
+            return;
+        }
+
+        uint32_t serial = mOnWorkDoneSerial++;
+        ASSERT(mOnWorkDoneRequests.find(serial) == mOnWorkDoneRequests.end());
+
+        QueueOnSubmittedWorkDoneCmd cmd;
+        cmd.queueId = this->id;
+        cmd.signalValue = signalValue;
+        cmd.requestSerial = serial;
+
+        mOnWorkDoneRequests[serial] = {callback, userdata};
+
+        client->SerializeCommand(cmd);
+    }
+
     WGPUFence Queue::CreateFence(WGPUFenceDescriptor const* descriptor) {
         auto* allocation = client->FenceAllocator().New(client);
 
@@ -65,4 +106,17 @@
         client->SerializeCommand(cmd);
     }
 
+    void Queue::CancelCallbacksForDisconnect() {
+        ClearAllCallbacks(WGPUQueueWorkDoneStatus_DeviceLost);
+    }
+
+    void Queue::ClearAllCallbacks(WGPUQueueWorkDoneStatus status) {
+        for (auto& it : mOnWorkDoneRequests) {
+            if (it.second.callback) {
+                it.second.callback(status, it.second.userdata);
+            }
+        }
+        mOnWorkDoneRequests.clear();
+    }
+
 }}  // namespace dawn_wire::client
diff --git a/src/dawn_wire/client/Queue.h b/src/dawn_wire/client/Queue.h
index 91c9393..f14fae1 100644
--- a/src/dawn_wire/client/Queue.h
+++ b/src/dawn_wire/client/Queue.h
@@ -27,7 +27,14 @@
     class Queue final : public ObjectBase {
       public:
         using ObjectBase::ObjectBase;
+        ~Queue();
 
+        bool OnWorkDoneCallback(uint64_t requestSerial, WGPUQueueWorkDoneStatus status);
+
+        // Dawn API
+        void OnSubmittedWorkDone(uint64_t signalValue,
+                                 WGPUQueueWorkDoneCallback callback,
+                                 void* userdata);
         WGPUFence CreateFence(const WGPUFenceDescriptor* descriptor);
         void WriteBuffer(WGPUBuffer cBuffer, uint64_t bufferOffset, const void* data, size_t size);
         void WriteTexture(const WGPUTextureCopyView* destination,
@@ -35,6 +42,18 @@
                           size_t dataSize,
                           const WGPUTextureDataLayout* dataLayout,
                           const WGPUExtent3D* writeSize);
+
+      private:
+        void CancelCallbacksForDisconnect() override;
+
+        void ClearAllCallbacks(WGPUQueueWorkDoneStatus status);
+
+        struct OnWorkDoneData {
+            WGPUQueueWorkDoneCallback callback = nullptr;
+            void* userdata = nullptr;
+        };
+        uint64_t mOnWorkDoneSerial = 0;
+        std::map<uint64_t, OnWorkDoneData> mOnWorkDoneRequests;
     };
 
 }}  // namespace dawn_wire::client
diff --git a/src/dawn_wire/server/Server.h b/src/dawn_wire/server/Server.h
index bcedeaa..1124cba 100644
--- a/src/dawn_wire/server/Server.h
+++ b/src/dawn_wire/server/Server.h
@@ -141,6 +141,13 @@
         uint64_t requestSerial;
     };
 
+    struct QueueWorkDoneUserdata : CallbackUserdata {
+        using CallbackUserdata::CallbackUserdata;
+
+        ObjectHandle queue;
+        uint64_t requestSerial;
+    };
+
     struct CreatePipelineAsyncUserData : CallbackUserdata {
         using CallbackUserdata::CallbackUserdata;
 
@@ -202,6 +209,7 @@
                                           FenceCompletionUserdata* userdata);
         void OnFenceOnCompletion(WGPUFenceCompletionStatus status,
                                  FenceOnCompletionUserdata* userdata);
+        void OnQueueWorkDone(WGPUQueueWorkDoneStatus status, QueueWorkDoneUserdata* userdata);
         void OnCreateComputePipelineAsyncCallback(WGPUCreatePipelineAsyncStatus status,
                                                   WGPUComputePipeline pipeline,
                                                   const char* message,
diff --git a/src/dawn_wire/server/ServerQueue.cpp b/src/dawn_wire/server/ServerQueue.cpp
index d798d41..9ab8bc0 100644
--- a/src/dawn_wire/server/ServerQueue.cpp
+++ b/src/dawn_wire/server/ServerQueue.cpp
@@ -40,6 +40,34 @@
         return true;
     }
 
+    void Server::OnQueueWorkDone(WGPUQueueWorkDoneStatus status, QueueWorkDoneUserdata* data) {
+        ReturnQueueWorkDoneCallbackCmd cmd;
+        cmd.queue = data->queue;
+        cmd.requestSerial = data->requestSerial;
+        cmd.status = status;
+
+        SerializeCommand(cmd);
+    }
+
+    bool Server::DoQueueOnSubmittedWorkDone(ObjectId queueId,
+                                            uint64_t signalValue,
+                                            uint64_t requestSerial) {
+        auto* queue = QueueObjects().Get(queueId);
+        if (queue == nullptr) {
+            return false;
+        }
+
+        auto userdata = MakeUserdata<QueueWorkDoneUserdata>();
+        userdata->queue = ObjectHandle{queueId, queue->generation};
+        userdata->requestSerial = requestSerial;
+
+        mProcs.queueOnSubmittedWorkDone(
+            queue->handle, signalValue,
+            ForwardToServer<decltype(&Server::OnQueueWorkDone)>::Func<&Server::OnQueueWorkDone>(),
+            userdata.release());
+        return true;
+    }
+
     bool Server::DoQueueWriteBufferInternal(ObjectId queueId,
                                             ObjectId bufferId,
                                             uint64_t bufferOffset,