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
////////////////////////////////////////////////////////////////////////////////
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(
[](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<GPUDevice*>(userdata);
for (auto promise : self->lost_promises_) {
promise.Resolve(
if (self->lost_promise_.GetState() == interop::PromiseState::Pending) {
self->lost_promise_.Resolve(
interop::GPUDeviceLostInfo::Create<DeviceLostInfo>(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<DeviceLostInfo>(
if (lost_promise_.GetState() == interop::PromiseState::Pending) {
lost_promise_.Resolve(interop::GPUDeviceLostInfo::Create<DeviceLostInfo>(
env_, interop::GPUDeviceLostReason::kDestroyed, "device was destroyed"));
}
lost_promises_.clear();
device_.Release();
device_.Destroy();
}
interop::Interface<interop::GPUBuffer> GPUDevice::createBuffer(
@ -422,10 +424,7 @@ namespace wgpu::binding {
interop::Promise<interop::Interface<interop::GPUDeviceLostInfo>> GPUDevice::getLost(
Napi::Env env) {
auto promise =
interop::Promise<interop::Interface<interop::GPUDeviceLostInfo>>(env, PROMISE_INFO);
lost_promises_.emplace_back(promise);
return promise;
return lost_promise_;
}
void GPUDevice::pushErrorScope(Napi::Env env, interop::GPUErrorFilter filter) {

View File

@ -104,8 +104,10 @@ namespace wgpu::binding {
Napi::Env env_;
wgpu::Device device_;
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

View File

@ -168,66 +168,70 @@ namespace wgpu::interop {
int line = 0;
};
enum class PromiseState {
Pending,
Resolved,
Rejected,
};
namespace detail {
// Base class for Promise<T> 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) {
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<T>(value)));
PromiseBase::Resolve(ToJS(state_->deferred.Env(), std::forward<T>(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());
}
};

View File

@ -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