Dawn.node: Emit errors as events
Forward errors to disptchEvent as appropriate.
Bug: 419128706
Depends-On: Ifeeb54bd20342c104b1e8bbd8253010bcf957d67
Change-Id: I1d62fb0c04c9a132d1e7ed1776a364f1ce121470
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/243916
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Gregg Tavares <gman@chromium.org>
diff --git a/src/dawn/node/binding/GPUAdapter.cpp b/src/dawn/node/binding/GPUAdapter.cpp
index 0f180ab..9914431 100644
--- a/src/dawn/node/binding/GPUAdapter.cpp
+++ b/src/dawn/node/binding/GPUAdapter.cpp
@@ -119,42 +119,6 @@
return interop::GPUAdapterInfo::Create<GPUAdapterInfo>(env, info);
}
-namespace {
-// Returns a string representation of the wgpu::ErrorType
-const char* str(wgpu::ErrorType ty) {
- switch (ty) {
- case wgpu::ErrorType::NoError:
- return "no error";
- case wgpu::ErrorType::Validation:
- return "validation";
- case wgpu::ErrorType::OutOfMemory:
- return "out of memory";
- case wgpu::ErrorType::Internal:
- return "internal";
- case wgpu::ErrorType::Unknown:
- default:
- return "unknown";
- }
-}
-
-// There's something broken with Node when attempting to write more than 65536 bytes to cout.
-// Split the string up into writes of 4k chunks.
-// Likely related: https://github.com/nodejs/node/issues/12921
-void chunkedWrite(wgpu::StringView msg) {
- while (msg.length != 0) {
- int n;
- if (msg.length > 4096) {
- n = printf("%.4096s", msg.data);
- } else {
- n = printf("%.*s", static_cast<int>(msg.length), msg.data);
- }
- msg.data += n;
- msg.length -= n;
- }
-}
-
-} // namespace
-
interop::Promise<interop::Interface<interop::GPUDevice>> GPUAdapter::requestDevice(
Napi::Env env,
interop::GPUDeviceDescriptor descriptor) {
@@ -239,11 +203,7 @@
}
},
device_lost_ctx);
- desc.SetUncapturedErrorCallback(
- [](const wgpu::Device&, ErrorType type, wgpu::StringView message) {
- printf("%s:\n", str(type));
- chunkedWrite(message);
- });
+ desc.SetUncapturedErrorCallback(GPUDevice::handleUncapturedErrorCallback);
// Propagate enabled/disabled dawn features
TogglesLoader togglesLoader(flags_);
diff --git a/src/dawn/node/binding/GPUDevice.cpp b/src/dawn/node/binding/GPUDevice.cpp
index 0c182ef..c33522d 100644
--- a/src/dawn/node/binding/GPUDevice.cpp
+++ b/src/dawn/node/binding/GPUDevice.cpp
@@ -30,6 +30,7 @@
#include <cassert>
#include <memory>
#include <type_traits>
+#include <unordered_map>
#include <utility>
#include <vector>
@@ -74,6 +75,23 @@
}
}
+// Returns a string representation of the wgpu::ErrorType
+const char* str(wgpu::ErrorType ty) {
+ switch (ty) {
+ case wgpu::ErrorType::NoError:
+ return "no error";
+ case wgpu::ErrorType::Validation:
+ return "validation";
+ case wgpu::ErrorType::OutOfMemory:
+ return "out of memory";
+ case wgpu::ErrorType::Internal:
+ return "internal";
+ case wgpu::ErrorType::Unknown:
+ default:
+ return "unknown";
+ }
+}
+
// There's something broken with Node when attempting to write more than 65536 bytes to cout.
// Split the string up into writes of 4k chunks.
// Likely related: https://github.com/nodejs/node/issues/12921
@@ -90,35 +108,43 @@
}
}
-class OOMError : public interop::GPUOutOfMemoryError {
- public:
- explicit OOMError(std::string message) : message_(std::move(message)) {}
+std::optional<interop::Interface<interop::GPUError>>
+createErrorFromWGPUError(Napi::Env env, wgpu::ErrorType type, wgpu::StringView message) {
+ auto constructors = interop::ConstructorsFor(env);
+ auto msg = Napi::String::New(env, std::string(message.data, message.length));
- std::string getMessage(Napi::Env) override { return message_; }
+ switch (type) {
+ case wgpu::ErrorType::NoError:
+ return {};
+ case wgpu::ErrorType::OutOfMemory:
+ return interop::Interface<interop::GPUError>(
+ constructors->GPUOutOfMemoryError_ctor.New({msg}));
+ case wgpu::ErrorType::Validation:
+ return interop::Interface<interop::GPUError>(
+ constructors->GPUValidationError_ctor.New({msg}));
+ case wgpu::ErrorType::Internal:
+ return interop::Interface<interop::GPUError>(
+ constructors->GPUInternalError_ctor.New({msg}));
+ case wgpu::ErrorType::Unknown:
+ // This error type is reserved for when translating an error type from a newer
+ // implementation (e.g. the browser added a new error type) to another (e.g.
+ // you're using an older version of Emscripten). It shouldn't happen in Dawn.
+ assert(false);
+ return {};
+ }
+}
- private:
- std::string message_;
-};
+static std::mutex s_device_to_js_map_mutex_;
+static std::unordered_map<WGPUDevice, GPUDevice*> s_device_to_js_map_;
-class ValidationError : public interop::GPUValidationError {
- public:
- explicit ValidationError(std::string message) : message_(std::move(message)) {}
-
- std::string getMessage(Napi::Env) override { return message_; }
-
- private:
- std::string message_;
-};
-
-class InternalError : public interop::GPUInternalError {
- public:
- explicit InternalError(std::string message) : message_(std::move(message)) {}
-
- std::string getMessage(Napi::Env) override { return message_; }
-
- private:
- std::string message_;
-};
+GPUDevice* lookupGPUDeviceFromWGPUDevice(wgpu::Device device) {
+ std::lock_guard<std::mutex> lock(s_device_to_js_map_mutex_);
+ auto it = s_device_to_js_map_.find(device.Get());
+ if (it != s_device_to_js_map_.end()) {
+ return it->second;
+ }
+ return nullptr;
+}
} // namespace
@@ -154,6 +180,10 @@
printf("%s:\n", str(type));
chunkedWrite(message);
});
+ {
+ std::lock_guard<std::mutex> lock(s_device_to_js_map_mutex_);
+ s_device_to_js_map_.insert({device_.Get(), this});
+ }
}
GPUDevice::~GPUDevice() {
@@ -166,6 +196,44 @@
device_.Destroy();
destroyed_ = true;
}
+ {
+ std::lock_guard<std::mutex> lock(s_device_to_js_map_mutex_);
+ s_device_to_js_map_.erase(device_.Get());
+ }
+}
+
+void GPUDevice::handleUncapturedError(ErrorType type, wgpu::StringView message) {
+ Napi::HandleScope scope(env_);
+
+ auto error = createErrorFromWGPUError(env_, type, message);
+ if (!error.has_value()) {
+ fprintf(stderr,
+ "GPUDevice::handleUncapturedError: Failed to create GPUError object for error type "
+ "%s.\n",
+ str(type));
+ return;
+ }
+
+ Napi::Object event_init_dict = Napi::Object::New(env_);
+ event_init_dict.Set("error", error.value());
+ event_init_dict.Set("cancelable", Napi::Boolean::New(env_, true));
+
+ auto constructors = interop::ConstructorsFor(env_);
+ Napi::Object eventObj = constructors->GPUUncapturedErrorEvent_ctor.New(
+ {Napi::String::New(env_, "uncapturederror"), event_init_dict});
+
+ bool doDefault = dispatchEvent(env_, eventObj);
+ if (doDefault) {
+ printf("%s:\n", str(type));
+ chunkedWrite(message);
+ }
+}
+
+void GPUDevice::handleUncapturedErrorCallback(const wgpu::Device& device,
+ ErrorType type,
+ wgpu::StringView message) {
+ auto gpuDevice = lookupGPUDeviceFromWGPUDevice(device.Get());
+ gpuDevice->handleUncapturedError(type, message);
}
void GPUDevice::ForceLoss(wgpu::DeviceLostReason reason, const char* message) {
@@ -551,37 +619,7 @@
break;
}
- switch (type) {
- case wgpu::ErrorType::NoError:
- ctx->promise.Resolve({});
- break;
- case wgpu::ErrorType::OutOfMemory: {
- interop::Interface<interop::GPUError> err{
- interop::GPUOutOfMemoryError::Create<OOMError>(env, std::string(message))};
- ctx->promise.Resolve(err);
- break;
- }
- case wgpu::ErrorType::Validation: {
- interop::Interface<interop::GPUError> err{
- interop::GPUValidationError::Create<ValidationError>(env,
- std::string(message))};
- ctx->promise.Resolve(err);
- break;
- }
- case wgpu::ErrorType::Internal: {
- interop::Interface<interop::GPUError> err{
- interop::GPUInternalError::Create<InternalError>(env,
- std::string(message))};
- ctx->promise.Resolve(err);
- break;
- }
- case wgpu::ErrorType::Unknown:
- // This error type is reserved for when translating an error type from a newer
- // implementation (e.g. the browser added a new error type) to another (e.g.
- // you're using an older version of Emscripten). It shouldn't happen in Dawn.
- assert(false);
- break;
- }
+ ctx->promise.Resolve(createErrorFromWGPUError(env, type, message));
});
return promise;
diff --git a/src/dawn/node/binding/GPUDevice.h b/src/dawn/node/binding/GPUDevice.h
index 1514889..80a7d69 100644
--- a/src/dawn/node/binding/GPUDevice.h
+++ b/src/dawn/node/binding/GPUDevice.h
@@ -125,6 +125,11 @@
interop::Interface<interop::EventHandler> getOnuncapturederror(Napi::Env) override;
void setOnuncapturederror(Napi::Env, interop::Interface<interop::EventHandler> value) override;
+ void handleUncapturedError(ErrorType type, wgpu::StringView message);
+ static void handleUncapturedErrorCallback(const wgpu::Device& device,
+ ErrorType type,
+ wgpu::StringView message);
+
private:
Napi::Env env_;
wgpu::Device device_;
diff --git a/src/dawn/node/interop/WebGPU.cpp.tmpl b/src/dawn/node/interop/WebGPU.cpp.tmpl
index 9704e70..e11217d 100644
--- a/src/dawn/node/interop/WebGPU.cpp.tmpl
+++ b/src/dawn/node/interop/WebGPU.cpp.tmpl
@@ -64,6 +64,10 @@
} // namespace
+const Constructors* ConstructorsFor(Napi::Env env) {
+ return Wrappers::For(env);
+}
+
{{ range $ := .Declarations}}
{{- if IsDictionary $}}{{template "Dictionary" $}}
{{- else if IsInterface $}}