diff --git a/src/dawn/node/binding/GPUDevice.cpp b/src/dawn/node/binding/GPUDevice.cpp index bd8d2677a8..ffd898d220 100644 --- a/src/dawn/node/binding/GPUDevice.cpp +++ b/src/dawn/node/binding/GPUDevice.cpp @@ -77,7 +77,10 @@ namespace wgpu::binding { // wgpu::bindings::GPUDevice //////////////////////////////////////////////////////////////////////////////// GPUDevice::GPUDevice(Napi::Env env, wgpu::Device device) - : env_(env), device_(device), async_(std::make_shared(env, device)) { + : env_(env), + device_(device), + async_(std::make_shared(env, device)), + lost_promise_(env, PROMISE_INFO) { device_.SetLoggingCallback( [](WGPULoggingType type, char const* message, void* userdata) { std::cout << type << ": " << message << std::endl; @@ -102,8 +105,8 @@ namespace wgpu::binding { break; } auto* self = static_cast(userdata); - for (auto promise : self->lost_promises_) { - promise.Resolve( + if (self->lost_promise_.GetState() == interop::PromiseState::Pending) { + self->lost_promise_.Resolve( interop::GPUDeviceLostInfo::Create(self->env_, r, message)); } }, @@ -139,12 +142,11 @@ namespace wgpu::binding { } void GPUDevice::destroy(Napi::Env env) { - for (auto promise : lost_promises_) { - promise.Resolve(interop::GPUDeviceLostInfo::Create( + if (lost_promise_.GetState() == interop::PromiseState::Pending) { + lost_promise_.Resolve(interop::GPUDeviceLostInfo::Create( env_, interop::GPUDeviceLostReason::kDestroyed, "device was destroyed")); } - lost_promises_.clear(); - device_.Release(); + device_.Destroy(); } interop::Interface GPUDevice::createBuffer( @@ -422,10 +424,7 @@ namespace wgpu::binding { interop::Promise> GPUDevice::getLost( Napi::Env env) { - auto promise = - interop::Promise>(env, PROMISE_INFO); - lost_promises_.emplace_back(promise); - return promise; + return lost_promise_; } void GPUDevice::pushErrorScope(Napi::Env env, interop::GPUErrorFilter filter) { diff --git a/src/dawn/node/binding/GPUDevice.h b/src/dawn/node/binding/GPUDevice.h index 583439c8f6..e3215bdee3 100644 --- a/src/dawn/node/binding/GPUDevice.h +++ b/src/dawn/node/binding/GPUDevice.h @@ -104,8 +104,10 @@ namespace wgpu::binding { Napi::Env env_; wgpu::Device device_; std::shared_ptr async_; - std::vector>> - lost_promises_; + + // This promise's JS object lives as long as the device because it is stored in .lost + // of the wrapper JS object. + interop::Promise> lost_promise_; }; } // namespace wgpu::binding diff --git a/src/dawn/node/interop/Core.h b/src/dawn/node/interop/Core.h index 8ea62d1ae6..1c98036e0b 100644 --- a/src/dawn/node/interop/Core.h +++ b/src/dawn/node/interop/Core.h @@ -168,66 +168,70 @@ namespace wgpu::interop { int line = 0; }; + enum class PromiseState { + Pending, + Resolved, + Rejected, + }; + namespace detail { // Base class for Promise specializations. class PromiseBase { public: // Implicit conversion operators to Napi promises. inline operator napi_value() const { - return state->deferred.Promise(); + return state_->deferred.Promise(); } inline operator Napi::Value() const { - return state->deferred.Promise(); + return state_->deferred.Promise(); } inline operator Napi::Promise() const { - return state->deferred.Promise(); + return state_->deferred.Promise(); } // Reject() rejects the promise with the given failure value. void Reject(Napi::Value value) const { - state->deferred.Reject(value); - state->resolved_or_rejected = true; + state_->deferred.Reject(value); + state_->state = PromiseState::Rejected; } void Reject(Napi::Error err) const { Reject(err.Value()); } void Reject(std::string err) const { - Reject(Napi::Error::New(state->deferred.Env(), err)); + Reject(Napi::Error::New(state_->deferred.Env(), err)); + } + + PromiseState GetState() const { + return state_->state; } protected: void Resolve(Napi::Value value) const { - state->deferred.Resolve(value); - state->resolved_or_rejected = true; + state_->deferred.Resolve(value); + state_->state = PromiseState::Resolved; } struct State { Napi::Promise::Deferred deferred; PromiseInfo info; - bool resolved_or_rejected = false; + PromiseState state = PromiseState::Pending; }; PromiseBase(Napi::Env env, const PromiseInfo& info) - : state(new State{Napi::Promise::Deferred::New(env), info}) { - state->deferred.Promise().AddFinalizer( + : state_(new State{Napi::Promise::Deferred::New(env), info}) { + state_->deferred.Promise().AddFinalizer( [](Napi::Env, State* state) { - // TODO(https://github.com/gpuweb/cts/issues/784): - // Devices are never destroyed, so we always end up - // leaking the Device.lost promise. Enable this once - // fixed. - if ((false)) { - if (!state->resolved_or_rejected) { - ::wgpu::utils::Fatal("Promise not resolved or rejected", - state->info.file, state->info.line, - state->info.function); - } + if (state->state == PromiseState::Pending) { + ::wgpu::utils::Fatal("Promise not resolved or rejected", + state->info.file, state->info.line, + state->info.function); } delete state; }, - state); + state_); } - State* const state; + State* const state_; }; } // namespace detail @@ -242,7 +246,7 @@ namespace wgpu::interop { // Resolve() fulfills the promise with the given value. void Resolve(T&& value) const { - PromiseBase::Resolve(ToJS(state->deferred.Env(), std::forward(value))); + PromiseBase::Resolve(ToJS(state_->deferred.Env(), std::forward(value))); } }; @@ -256,7 +260,7 @@ namespace wgpu::interop { // Resolve() fulfills the promise. void Resolve() const { - PromiseBase::Resolve(state->deferred.Env().Undefined()); + PromiseBase::Resolve(state_->deferred.Env().Undefined()); } }; diff --git a/src/dawn/node/tools/src/cmd/idlgen/main.go b/src/dawn/node/tools/src/cmd/idlgen/main.go index 9be1209292..bcf69287b8 100644 --- a/src/dawn/node/tools/src/cmd/idlgen/main.go +++ b/src/dawn/node/tools/src/cmd/idlgen/main.go @@ -142,6 +142,9 @@ func run() error { // simplify the definitions in the WebIDL before passing this to the template idl, declarations := simplify(idl) + + // Patch the IDL for the differences we need compared to the upstream IDL. + patch(idl, declarations) g.declarations = declarations // Write the file header @@ -369,6 +372,17 @@ func (s *simplifier) visitType(t ast.Type) { } } +func patch(idl *ast.File, decl declarations) { + // Add [SameObject] to GPUDevice.lost + for _, member := range decl["GPUDevice"].(*ast.Interface).Members { + if m := member.(*ast.Member); m != nil && m.Name == "lost" { + annotation := &ast.Annotation{} + annotation.Name = "SameObject" + m.Annotations = append(m.Annotations, annotation) + } + } +} + // generator holds the template generator state type generator struct { // the root template