| // Copyright 2021 The Dawn & Tint Authors | |
| // | |
| // Redistribution and use in source and binary forms, with or without | |
| // modification, are permitted provided that the following conditions are met: | |
| // | |
| // 1. Redistributions of source code must retain the above copyright notice, this | |
| // list of conditions and the following disclaimer. | |
| // | |
| // 2. Redistributions in binary form must reproduce the above copyright notice, | |
| // this list of conditions and the following disclaimer in the documentation | |
| // and/or other materials provided with the distribution. | |
| // | |
| // 3. Neither the name of the copyright holder nor the names of its | |
| // contributors may be used to endorse or promote products derived from | |
| // this software without specific prior written permission. | |
| // | |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | |
| // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
| // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| #ifndef SRC_DAWN_NODE_BINDING_ASYNCRUNNER_H_ | |
| #define SRC_DAWN_NODE_BINDING_ASYNCRUNNER_H_ | |
| #include <stdint.h> | |
| #include <webgpu/webgpu_cpp.h> | |
| #include <memory> | |
| #include <utility> | |
| #include "dawn/native/DawnNative.h" | |
| #include "src/dawn/node/interop/Core.h" | |
| #include "src/dawn/node/interop/NodeAPI.h" | |
| namespace wgpu::binding { | |
| // AsyncRunner is used to poll a wgpu::Device with calls to Tick() while there are asynchronous | |
| // tasks in flight. | |
| class AsyncRunner { | |
| public: | |
| // Creates an AsyncRunner to use to process events on the instance. | |
| static std::shared_ptr<AsyncRunner> Create(dawn::native::Instance* instance); | |
| // 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(Napi::Env env); | |
| // End() should be called once the asynchronous task has finished. | |
| // Every call to Begin() should eventually result in a call to End(). | |
| void End(); | |
| // Rejects the promise after the current task in the event loop. This is useful to preserve | |
| // some of the semantics of WebGPU w.r.t. the JavaScript event loop. Reject() can be called | |
| // any time, but callers need to make sure that the Promise is (rejected or resolved) only | |
| // once. | |
| void Reject(Napi::Env env, interop::Promise<void> promise, Napi::Error error); | |
| // Use AsyncRunner::Create instead of this constructor. | |
| explicit AsyncRunner(dawn::native::Instance* instance); | |
| private: | |
| void ScheduleProcessEvents(Napi::Env env); | |
| std::weak_ptr<AsyncRunner> weak_this_; | |
| const dawn::native::Instance* const instance_; | |
| uint64_t tasks_waiting_ = 0; | |
| bool process_events_queued_ = false; | |
| }; | |
| // Whether an asynchronous task needs wgpuInstanceProcessEvents() to be called in order to make | |
| // progress. Tasks whose callbacks are registered with wgpu::CallbackMode::AllowSpontaneous are | |
| // signalled without any polling, so they must not keep the process-events pump running - doing so | |
| // would busy-loop the JavaScript thread for as long as the task is in flight. | |
| enum class AsyncPolling { | |
| kRequired, | |
| kNotRequired, | |
| }; | |
| // AsyncTask is a RAII helper for calling AsyncRunner::Begin() on construction, and | |
| // AsyncRunner::End() on destruction, that also encapsulates the promise generally | |
| // associated with any async task. | |
| template <typename T> | |
| class AsyncContext { | |
| public: | |
| // Constructor. | |
| // Calls AsyncRunner::Begin() if polling is AsyncPolling::kRequired. | |
| inline AsyncContext(Napi::Env env, | |
| const interop::PromiseInfo& info, | |
| std::shared_ptr<AsyncRunner> runner, | |
| AsyncPolling polling = AsyncPolling::kRequired) | |
| : env(env), promise(env, info), runner_(runner), polling_(polling) { | |
| if (polling_ == AsyncPolling::kRequired) { | |
| runner_->Begin(env); | |
| } | |
| } | |
| // Destructor. | |
| // Calls AsyncRunner::End() if polling is AsyncPolling::kRequired. | |
| inline ~AsyncContext() { | |
| if (polling_ == AsyncPolling::kRequired) { | |
| runner_->End(); | |
| } | |
| } | |
| // Note these are public to allow for access for the callbacks that take ownership of this | |
| // context. | |
| Napi::Env env; | |
| interop::Promise<T> promise; | |
| private: | |
| AsyncContext(const AsyncContext&) = delete; | |
| AsyncContext& operator=(const AsyncContext&) = delete; | |
| std::shared_ptr<AsyncRunner> runner_; | |
| const AsyncPolling polling_; | |
| }; | |
| } // namespace wgpu::binding | |
| #endif // SRC_DAWN_NODE_BINDING_ASYNCRUNNER_H_ |