dawn_node: Add binding/AsyncRunner
Used to poll a wgpu::Device with calls to Tick() while there are asynchronous tasks in flight.
Bug: dawn:1123
Change-Id: Ieee75b983df836a6df09ae4ff81f7382f4be4995
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/64905
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: Austin Eng <enga@chromium.org>
diff --git a/src/dawn_node/binding/AsyncRunner.cpp b/src/dawn_node/binding/AsyncRunner.cpp
new file mode 100644
index 0000000..7162b2e
--- /dev/null
+++ b/src/dawn_node/binding/AsyncRunner.cpp
@@ -0,0 +1,55 @@
+// Copyright 2021 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/dawn_node/binding/AsyncRunner.h"
+
+#include <cassert>
+#include <limits>
+
+namespace wgpu { namespace binding {
+
+ AsyncRunner::AsyncRunner(Napi::Env env, wgpu::Device device) : env_(env), device_(device) {
+ }
+
+ void AsyncRunner::Begin() {
+ assert(count_ != std::numeric_limits<decltype(count_)>::max());
+ if (count_++ == 0) {
+ QueueTick();
+ }
+ }
+
+ void AsyncRunner::End() {
+ assert(count_ > 0);
+ count_--;
+ }
+
+ void AsyncRunner::QueueTick() {
+ // TODO(crbug.com/dawn/1127): We probably want to reduce the frequency at which this gets
+ // called.
+ env_.Global()
+ .Get("setImmediate")
+ .As<Napi::Function>()
+ .Call({
+ // TODO(crbug.com/dawn/1127): Create once, reuse.
+ Napi::Function::New(env_,
+ [this](const Napi::CallbackInfo&) {
+ if (count_ > 0) {
+ device_.Tick();
+ QueueTick();
+ }
+ }),
+ });
+ }
+
+}} // namespace wgpu::binding
diff --git a/src/dawn_node/binding/AsyncRunner.h b/src/dawn_node/binding/AsyncRunner.h
new file mode 100644
index 0000000..83644c0
--- /dev/null
+++ b/src/dawn_node/binding/AsyncRunner.h
@@ -0,0 +1,76 @@
+// Copyright 2021 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef DAWN_NODE_BINDING_ASYNC_RUNNER_H_
+#define DAWN_NODE_BINDING_ASYNC_RUNNER_H_
+
+#include <stdint.h>
+#include <memory>
+
+#include "dawn/webgpu_cpp.h"
+#include "napi.h"
+
+namespace wgpu { namespace binding {
+
+ // AsyncRunner is used to poll a wgpu::Device with calls to Tick() while there are asynchronous
+ // tasks in flight.
+ class AsyncRunner {
+ public:
+ AsyncRunner(Napi::Env env, wgpu::Device device);
+
+ // Begin() should be called when a new asynchronous task is started.
+ // If the number of executing asynchronous tasks transitions from 0 to 1, then a function
+ // will be scheduled on the main JavaScript thread to call wgpu::Device::Tick() whenever the
+ // thread is idle. This will be repeatedly called until the number of executing asynchronous
+ // tasks reaches 0 again.
+ void Begin();
+
+ // End() should be called once the asynchronous task has finished.
+ // Every call to Begin() should eventually result in a call to End().
+ void End();
+
+ private:
+ void QueueTick();
+ Napi::Env env_;
+ wgpu::Device const device_;
+ uint64_t count_ = 0;
+ };
+
+ // AsyncTask is a RAII helper for calling AsyncRunner::Begin() on construction, and
+ // AsyncRunner::End() on destruction.
+ class AsyncTask {
+ public:
+ inline AsyncTask(AsyncTask&&) = default;
+
+ // Constructor.
+ // Calls AsyncRunner::Begin()
+ inline AsyncTask(std::shared_ptr<AsyncRunner> runner) : runner_(std::move(runner)) {
+ runner_->Begin();
+ };
+
+ // Destructor.
+ // Calls AsyncRunner::End()
+ inline ~AsyncTask() {
+ runner_->End();
+ }
+
+ private:
+ AsyncTask(const AsyncTask&) = delete;
+ AsyncTask& operator=(const AsyncTask&) = delete;
+ std::shared_ptr<AsyncRunner> runner_;
+ };
+
+}} // namespace wgpu::binding
+
+#endif // DAWN_NODE_BINDING_ASYNC_RUNNER_H_
diff --git a/src/dawn_node/binding/CMakeLists.txt b/src/dawn_node/binding/CMakeLists.txt
index c12f86e..faad894 100644
--- a/src/dawn_node/binding/CMakeLists.txt
+++ b/src/dawn_node/binding/CMakeLists.txt
@@ -13,6 +13,8 @@
# limitations under the License.
add_library(dawn_node_binding STATIC
+ "AsyncRunner.cpp"
+ "AsyncRunner.h"
"Errors.cpp"
"Errors.h"
)