d3d: reuse event objects

Bug: chromium:335553337
Change-Id: I26c68b2a76d1885be3fdb7e9b72831185690221e
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/184622
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Peng Huang <penghuang@chromium.org>
diff --git a/src/dawn/native/SystemEvent.cpp b/src/dawn/native/SystemEvent.cpp
index dd0d4b1..747f2bd 100644
--- a/src/dawn/native/SystemEvent.cpp
+++ b/src/dawn/native/SystemEvent.cpp
@@ -60,6 +60,10 @@
     return receiver;
 }
 
+const SystemHandle& SystemEventReceiver::GetPrimitive() const {
+    return mPrimitive;
+}
+
 // SystemEventPipeSender
 
 SystemEventPipeSender::~SystemEventPipeSender() {
diff --git a/src/dawn/native/SystemEvent.h b/src/dawn/native/SystemEvent.h
index f641cf7..391d430 100644
--- a/src/dawn/native/SystemEvent.h
+++ b/src/dawn/native/SystemEvent.h
@@ -59,6 +59,8 @@
     SystemEventReceiver(SystemEventReceiver&&) = default;
     SystemEventReceiver& operator=(SystemEventReceiver&&) = default;
 
+    const SystemHandle& GetPrimitive() const;
+
   private:
     template <typename It>
     friend bool WaitAnySystemEvent(It begin, It end, Nanoseconds timeout);
diff --git a/src/dawn/native/d3d/QueueD3D.cpp b/src/dawn/native/d3d/QueueD3D.cpp
index f8615dc..5f07c57 100644
--- a/src/dawn/native/d3d/QueueD3D.cpp
+++ b/src/dawn/native/d3d/QueueD3D.cpp
@@ -27,6 +27,7 @@
 
 #include "dawn/native/d3d/QueueD3D.h"
 
+#include <algorithm>
 #include <utility>
 
 #include "dawn/native/WaitAnySystemEvent.h"
@@ -35,6 +36,44 @@
 
 Queue::~Queue() = default;
 
+ResultOrError<SystemEventReceiver> Queue::GetSystemEventReceiver() {
+    SystemEventReceiver receiver;
+    bool result = mAvailableEventReceivers.Use([&](auto receivers) -> auto {
+        if (receivers->empty()) {
+            return false;
+        }
+        receiver = std::move(receivers->back());
+        receivers->pop_back();
+        return true;
+    });
+    if (!result) {
+        HANDLE fenceEvent =
+            ::CreateEvent(nullptr, /*bManualReset=*/true, /*bInitialState=*/false, nullptr);
+        if (fenceEvent == nullptr) {
+            return DAWN_INTERNAL_ERROR("CreateEvent failed");
+        }
+        receiver = SystemEventReceiver(SystemHandle::Acquire(fenceEvent));
+    }
+
+    return receiver;
+}
+
+MaybeError Queue::ReturnSystemEventReceivers(std::vector<SystemEventReceiver> receivers) {
+    for (const auto& receiver : receivers) {
+        if (!ResetEvent(receiver.GetPrimitive().Get())) {
+            return DAWN_INTERNAL_ERROR("ResetEvent failed");
+        }
+    }
+    mAvailableEventReceivers.Use([&](auto availableEventReceivers) {
+        size_t count =
+            std::min(receivers.size(), kMaxEventReceivers - availableEventReceivers->size());
+        availableEventReceivers->insert(availableEventReceivers->end(),
+                                        std::make_move_iterator(receivers.begin()),
+                                        std::make_move_iterator(receivers.begin() + count));
+    });
+    return {};
+}
+
 ResultOrError<bool> Queue::WaitForQueueSerial(ExecutionSerial serial, Nanoseconds timeout) {
     ExecutionSerial completedSerial = GetCompletedCommandSerial();
     if (serial <= completedSerial) {
@@ -43,16 +82,8 @@
 
     auto receiver = mSystemEventReceivers->TakeOne(serial);
     if (!receiver) {
-        // Anytime we may create an event, clear out any completed receivers so the list doesn't
-        // grow forever.
-        mSystemEventReceivers->ClearUpTo(completedSerial);
-
-        HANDLE fenceEvent =
-            ::CreateEvent(nullptr, /*bManualReset=*/true, /*bInitialState=*/false, nullptr);
-        DAWN_INVALID_IF(fenceEvent == nullptr, "CreateEvent failed");
-        SetEventOnCompletion(serial, fenceEvent);
-
-        receiver = SystemEventReceiver(SystemHandle::Acquire(fenceEvent));
+        DAWN_TRY_ASSIGN(receiver, GetSystemEventReceiver());
+        SetEventOnCompletion(serial, receiver->GetPrimitive().Get());
     }
 
     bool ready = false;
@@ -60,12 +91,26 @@
         {{*receiver, &ready}}};
     DAWN_ASSERT(serial <= GetLastSubmittedCommandSerial());
     bool didComplete = WaitAnySystemEvent(events.begin(), events.end(), timeout);
-    if (!didComplete) {
-        // Return the SystemEventReceiver to the pool of receivers so it can be re-waited in the
-        // future.
-        mSystemEventReceivers->Enqueue(std::move(*receiver), serial);
-    }
+    // Return the SystemEventReceiver to the pool of receivers so it can be re-waited in the
+    // future.
+    // The caller should call CheckPassedSerials() which will clear passed system events.
+    mSystemEventReceivers->Enqueue(std::move(*receiver), serial);
+
     return didComplete;
 }
 
+MaybeError Queue::RecycleSystemEventReceivers(ExecutionSerial completedSerial) {
+    std::vector<SystemEventReceiver> receivers;
+    mSystemEventReceivers.Use([&](auto systemEventReceivers) {
+        for (auto& receiver : systemEventReceivers->IterateUpTo(completedSerial)) {
+            receivers.emplace_back(std::move(receiver));
+        }
+        systemEventReceivers->ClearUpTo(completedSerial);
+    });
+
+    DAWN_TRY(ReturnSystemEventReceivers(std::move(receivers)));
+
+    return {};
+}
+
 }  // namespace dawn::native::d3d
diff --git a/src/dawn/native/d3d/QueueD3D.h b/src/dawn/native/d3d/QueueD3D.h
index ee9e7a4..05e8d03 100644
--- a/src/dawn/native/d3d/QueueD3D.h
+++ b/src/dawn/native/d3d/QueueD3D.h
@@ -28,6 +28,8 @@
 #ifndef SRC_DAWN_NATIVE_D3D_QUEUED3D_H_
 #define SRC_DAWN_NATIVE_D3D_QUEUED3D_H_
 
+#include <vector>
+
 #include "dawn/common/MutexProtected.h"
 #include "dawn/common/SerialMap.h"
 #include "dawn/common/windows_with_undefs.h"
@@ -45,11 +47,20 @@
 
     virtual ResultOrError<Ref<SharedFence>> GetOrCreateSharedFence() = 0;
 
+  protected:
     ResultOrError<bool> WaitForQueueSerial(ExecutionSerial serial, Nanoseconds timeout) override;
 
+    ResultOrError<SystemEventReceiver> GetSystemEventReceiver();
+    MaybeError ReturnSystemEventReceivers(std::vector<SystemEventReceiver> receivers);
+    MaybeError RecycleSystemEventReceivers(ExecutionSerial completeSerial);
+
   private:
     virtual void SetEventOnCompletion(ExecutionSerial serial, HANDLE event) = 0;
 
+    // Available event receivers which can be reused.
+    static constexpr size_t kMaxEventReceivers = 32;
+    MutexProtected<std::vector<SystemEventReceiver>> mAvailableEventReceivers;
+
     MutexProtected<SerialMap<ExecutionSerial, SystemEventReceiver>> mSystemEventReceivers;
 };
 
diff --git a/src/dawn/native/d3d11/QueueD3D11.cpp b/src/dawn/native/d3d11/QueueD3D11.cpp
index a368d92..42070e5 100644
--- a/src/dawn/native/d3d11/QueueD3D11.cpp
+++ b/src/dawn/native/d3d11/QueueD3D11.cpp
@@ -222,6 +222,8 @@
 
     DAWN_TRY(CheckAndMapReadyBuffers(completedSerial));
 
+    DAWN_TRY(RecycleSystemEventReceivers(completedSerial));
+
     return completedSerial;
 }
 
diff --git a/src/dawn/native/d3d12/QueueD3D12.cpp b/src/dawn/native/d3d12/QueueD3D12.cpp
index a358f85..6833eff 100644
--- a/src/dawn/native/d3d12/QueueD3D12.cpp
+++ b/src/dawn/native/d3d12/QueueD3D12.cpp
@@ -185,6 +185,8 @@
         return ExecutionSerial(0);
     }
 
+    DAWN_TRY(RecycleSystemEventReceivers(completedSerial));
+
     return completedSerial;
 }