dawn.node: Make GPUDevice.lost [SameObject]

Previously a new promise was created and new promises were never
resolved on creation, only on loss/destruction of the device. This made
the following code wait forever:

  device.destroy();
  await device.lost();

Bug: dawn:1123
Change-Id: I1e31cf9ccd466672eed4cad464c38cb9f8b3d724
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/85362
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
Corentin Wallez 2022-04-01 12:22:36 +00:00 committed by Dawn LUCI CQ
parent ecc3fe6f0f
commit 63fe6e11cd
4 changed files with 57 additions and 38 deletions

View File

@ -77,7 +77,10 @@ namespace wgpu::binding {
// wgpu::bindings::GPUDevice // wgpu::bindings::GPUDevice
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
GPUDevice::GPUDevice(Napi::Env env, wgpu::Device device) GPUDevice::GPUDevice(Napi::Env env, wgpu::Device device)
: env_(env), device_(device), async_(std::make_shared<AsyncRunner>(env, device)) { : env_(env),
device_(device),
async_(std::make_shared<AsyncRunner>(env, device)),
lost_promise_(env, PROMISE_INFO) {
device_.SetLoggingCallback( device_.SetLoggingCallback(
[](WGPULoggingType type, char const* message, void* userdata) { [](WGPULoggingType type, char const* message, void* userdata) {
std::cout << type << ": " << message << std::endl; std::cout << type << ": " << message << std::endl;
@ -102,8 +105,8 @@ namespace wgpu::binding {
break; break;
} }
auto* self = static_cast<GPUDevice*>(userdata); auto* self = static_cast<GPUDevice*>(userdata);
for (auto promise : self->lost_promises_) { if (self->lost_promise_.GetState() == interop::PromiseState::Pending) {
promise.Resolve( self->lost_promise_.Resolve(
interop::GPUDeviceLostInfo::Create<DeviceLostInfo>(self->env_, r, message)); interop::GPUDeviceLostInfo::Create<DeviceLostInfo>(self->env_, r, message));
} }
}, },
@ -139,12 +142,11 @@ namespace wgpu::binding {
} }
void GPUDevice::destroy(Napi::Env env) { void GPUDevice::destroy(Napi::Env env) {
for (auto promise : lost_promises_) { if (lost_promise_.GetState() == interop::PromiseState::Pending) {
promise.Resolve(interop::GPUDeviceLostInfo::Create<DeviceLostInfo>( lost_promise_.Resolve(interop::GPUDeviceLostInfo::Create<DeviceLostInfo>(
env_, interop::GPUDeviceLostReason::kDestroyed, "device was destroyed")); env_, interop::GPUDeviceLostReason::kDestroyed, "device was destroyed"));
} }
lost_promises_.clear(); device_.Destroy();
device_.Release();
} }
interop::Interface<interop::GPUBuffer> GPUDevice::createBuffer( interop::Interface<interop::GPUBuffer> GPUDevice::createBuffer(
@ -422,10 +424,7 @@ namespace wgpu::binding {
interop::Promise<interop::Interface<interop::GPUDeviceLostInfo>> GPUDevice::getLost( interop::Promise<interop::Interface<interop::GPUDeviceLostInfo>> GPUDevice::getLost(
Napi::Env env) { Napi::Env env) {
auto promise = return lost_promise_;
interop::Promise<interop::Interface<interop::GPUDeviceLostInfo>>(env, PROMISE_INFO);
lost_promises_.emplace_back(promise);
return promise;
} }
void GPUDevice::pushErrorScope(Napi::Env env, interop::GPUErrorFilter filter) { void GPUDevice::pushErrorScope(Napi::Env env, interop::GPUErrorFilter filter) {

View File

@ -104,8 +104,10 @@ namespace wgpu::binding {
Napi::Env env_; Napi::Env env_;
wgpu::Device device_; wgpu::Device device_;
std::shared_ptr<AsyncRunner> async_; std::shared_ptr<AsyncRunner> async_;
std::vector<interop::Promise<interop::Interface<interop::GPUDeviceLostInfo>>>
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<interop::Interface<interop::GPUDeviceLostInfo>> lost_promise_;
}; };
} // namespace wgpu::binding } // namespace wgpu::binding

View File

@ -168,66 +168,70 @@ namespace wgpu::interop {
int line = 0; int line = 0;
}; };
enum class PromiseState {
Pending,
Resolved,
Rejected,
};
namespace detail { namespace detail {
// Base class for Promise<T> specializations. // Base class for Promise<T> specializations.
class PromiseBase { class PromiseBase {
public: public:
// Implicit conversion operators to Napi promises. // Implicit conversion operators to Napi promises.
inline operator napi_value() const { inline operator napi_value() const {
return state->deferred.Promise(); return state_->deferred.Promise();
} }
inline operator Napi::Value() const { inline operator Napi::Value() const {
return state->deferred.Promise(); return state_->deferred.Promise();
} }
inline operator Napi::Promise() const { inline operator Napi::Promise() const {
return state->deferred.Promise(); return state_->deferred.Promise();
} }
// Reject() rejects the promise with the given failure value. // Reject() rejects the promise with the given failure value.
void Reject(Napi::Value value) const { void Reject(Napi::Value value) const {
state->deferred.Reject(value); state_->deferred.Reject(value);
state->resolved_or_rejected = true; state_->state = PromiseState::Rejected;
} }
void Reject(Napi::Error err) const { void Reject(Napi::Error err) const {
Reject(err.Value()); Reject(err.Value());
} }
void Reject(std::string err) const { 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: protected:
void Resolve(Napi::Value value) const { void Resolve(Napi::Value value) const {
state->deferred.Resolve(value); state_->deferred.Resolve(value);
state->resolved_or_rejected = true; state_->state = PromiseState::Resolved;
} }
struct State { struct State {
Napi::Promise::Deferred deferred; Napi::Promise::Deferred deferred;
PromiseInfo info; PromiseInfo info;
bool resolved_or_rejected = false; PromiseState state = PromiseState::Pending;
}; };
PromiseBase(Napi::Env env, const PromiseInfo& info) PromiseBase(Napi::Env env, const PromiseInfo& info)
: state(new State{Napi::Promise::Deferred::New(env), info}) { : state_(new State{Napi::Promise::Deferred::New(env), info}) {
state->deferred.Promise().AddFinalizer( state_->deferred.Promise().AddFinalizer(
[](Napi::Env, State* state) { [](Napi::Env, State* state) {
// TODO(https://github.com/gpuweb/cts/issues/784): if (state->state == PromiseState::Pending) {
// Devices are never destroyed, so we always end up ::wgpu::utils::Fatal("Promise not resolved or rejected",
// leaking the Device.lost promise. Enable this once state->info.file, state->info.line,
// fixed. state->info.function);
if ((false)) {
if (!state->resolved_or_rejected) {
::wgpu::utils::Fatal("Promise not resolved or rejected",
state->info.file, state->info.line,
state->info.function);
}
} }
delete state; delete state;
}, },
state); state_);
} }
State* const state; State* const state_;
}; };
} // namespace detail } // namespace detail
@ -242,7 +246,7 @@ namespace wgpu::interop {
// Resolve() fulfills the promise with the given value. // Resolve() fulfills the promise with the given value.
void Resolve(T&& value) const { void Resolve(T&& value) const {
PromiseBase::Resolve(ToJS(state->deferred.Env(), std::forward<T>(value))); PromiseBase::Resolve(ToJS(state_->deferred.Env(), std::forward<T>(value)));
} }
}; };
@ -256,7 +260,7 @@ namespace wgpu::interop {
// Resolve() fulfills the promise. // Resolve() fulfills the promise.
void Resolve() const { void Resolve() const {
PromiseBase::Resolve(state->deferred.Env().Undefined()); PromiseBase::Resolve(state_->deferred.Env().Undefined());
} }
}; };

View File

@ -142,6 +142,9 @@ func run() error {
// simplify the definitions in the WebIDL before passing this to the template // simplify the definitions in the WebIDL before passing this to the template
idl, declarations := simplify(idl) idl, declarations := simplify(idl)
// Patch the IDL for the differences we need compared to the upstream IDL.
patch(idl, declarations)
g.declarations = declarations g.declarations = declarations
// Write the file header // 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 // generator holds the template generator state
type generator struct { type generator struct {
// the root template // the root template