react-native-webgpu 0.13.1 → 0.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/CMakeLists.txt +1 -0
- package/android/cpp/AndroidPlatformContext.h +0 -9
- package/android/cpp/cpp-adapter.cpp +6 -4
- package/android/src/main/java/com/webgpu/WebGPUView.java +8 -0
- package/apple/ApplePlatformContext.h +0 -3
- package/apple/ApplePlatformContext.mm +0 -10
- package/apple/MetalView.mm +4 -3
- package/cpp/jsi/JSIConverter.h +6 -11
- package/cpp/jsi/NativeObject.h +130 -18
- package/cpp/rnwgpu/EnforceRange.h +68 -0
- package/cpp/rnwgpu/PlatformContext.h +0 -2
- package/cpp/rnwgpu/RNWebGPUManager.cpp +7 -95
- package/cpp/rnwgpu/RNWebGPUManager.h +4 -3
- package/cpp/rnwgpu/WebGPURuntime.cpp +91 -0
- package/cpp/rnwgpu/WebGPURuntime.h +36 -0
- package/cpp/rnwgpu/api/Convertors.h +10 -1
- package/cpp/rnwgpu/api/DestroyedError.h +26 -0
- package/cpp/rnwgpu/api/GPU.cpp +3 -57
- package/cpp/rnwgpu/api/GPUDevice.cpp +17 -9
- package/cpp/rnwgpu/api/GPUError.h +15 -0
- package/cpp/rnwgpu/api/GPUInternalError.h +2 -0
- package/cpp/rnwgpu/api/GPUOutOfMemoryError.h +2 -0
- package/cpp/rnwgpu/api/GPUQueue.cpp +7 -21
- package/cpp/rnwgpu/api/GPUSharedFence.cpp +39 -12
- package/cpp/rnwgpu/api/GPUSharedFence.h +21 -4
- package/cpp/rnwgpu/api/GPUSharedTextureMemory.cpp +80 -4
- package/cpp/rnwgpu/api/GPUSharedTextureMemory.h +29 -6
- package/cpp/rnwgpu/api/GPUTexture.h +6 -0
- package/cpp/rnwgpu/api/GPUValidationError.h +2 -0
- package/cpp/rnwgpu/api/descriptors/Unions.h +80 -0
- package/lib/commonjs/external/reanimated/registerWebGPUForReanimated.js +13 -3
- package/lib/commonjs/external/reanimated/registerWebGPUForReanimated.js.map +1 -1
- package/lib/module/external/reanimated/registerWebGPUForReanimated.js +13 -3
- package/lib/module/external/reanimated/registerWebGPUForReanimated.js.map +1 -1
- package/lib/typescript/lib/commonjs/external/reanimated/registerWebGPUForReanimated.d.ts.map +1 -1
- package/lib/typescript/lib/module/external/reanimated/registerWebGPUForReanimated.d.ts.map +1 -1
- package/lib/typescript/src/external/reanimated/registerWebGPUForReanimated.d.ts +12 -0
- package/lib/typescript/src/external/reanimated/registerWebGPUForReanimated.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +2 -0
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/external/reanimated/registerWebGPUForReanimated.ts +29 -7
- package/src/types.ts +14 -1
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#include "WebGPURuntime.h"
|
|
2
|
+
|
|
3
|
+
#include <dlfcn.h>
|
|
4
|
+
|
|
5
|
+
#include <iterator>
|
|
6
|
+
|
|
7
|
+
namespace rnwgpu {
|
|
8
|
+
|
|
9
|
+
namespace {
|
|
10
|
+
|
|
11
|
+
wgpu::Instance createInstance() {
|
|
12
|
+
// If a Graphite build of react-native-skia is loaded in this
|
|
13
|
+
// process, adopt its wgpu::Instance instead of creating our own, so
|
|
14
|
+
// Graphite's device and every JS-created device live on one instance. That
|
|
15
|
+
// makes pointer-based handoff (RNWebGPU.importDevice of Skia's device)
|
|
16
|
+
// sound: the async pump serves the shared instance, so callbacks on
|
|
17
|
+
// imported devices settle. Discovered at runtime via dlsym so there is no
|
|
18
|
+
// build-time coupling and native-module init order does not matter. Both
|
|
19
|
+
// packages must link the same single Dawn copy (they vendor the same
|
|
20
|
+
// artifact) for the handle to be meaningful.
|
|
21
|
+
using GetInstanceFn = WGPUInstance (*)();
|
|
22
|
+
if (auto getSkiaInstance = reinterpret_cast<GetInstanceFn>(
|
|
23
|
+
dlsym(RTLD_DEFAULT, "rnskia_getWGPUInstance"))) {
|
|
24
|
+
if (WGPUInstance external = getSkiaInstance()) {
|
|
25
|
+
wgpuInstanceAddRef(external);
|
|
26
|
+
return wgpu::Instance::Acquire(external);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
static const auto kTimedWaitAny = wgpu::InstanceFeatureName::TimedWaitAny;
|
|
31
|
+
wgpu::InstanceDescriptor instanceDesc{.requiredFeatureCount = 1,
|
|
32
|
+
.requiredFeatures = &kTimedWaitAny};
|
|
33
|
+
|
|
34
|
+
wgpu::InstanceLimits limits{.timedWaitAnyMaxCount = 64};
|
|
35
|
+
instanceDesc.requiredLimits = &limits;
|
|
36
|
+
|
|
37
|
+
// Expose Dawn's experimental adapter features. Several features needed by
|
|
38
|
+
// our Android external-texture path (YCbCrVulkanSamplers,
|
|
39
|
+
// OpaqueYCbCrAndroidForExternalTexture) are tagged Experimental in Dawn's
|
|
40
|
+
// feature table and are otherwise filtered out of adapter.features by
|
|
41
|
+
// PhysicalDeviceBase::GetSupportedFeatures. The allow_unsafe_apis toggle
|
|
42
|
+
// disables that filter so the features become visible;
|
|
43
|
+
// expose_wgsl_experimental_features is the parallel toggle for WGSL language
|
|
44
|
+
// features.
|
|
45
|
+
//
|
|
46
|
+
// Trade-off: these are instance-level Dawn toggles, so they apply to every
|
|
47
|
+
// adapter/device created from this instance, not just the external-texture
|
|
48
|
+
// path. There is no finer-grained, per-feature mechanism to surface these.
|
|
49
|
+
// The exposure is acceptable because the toggle only *un-hides* experimental
|
|
50
|
+
// features in adapter.features; it does not enable any of them. Nothing
|
|
51
|
+
// experimental becomes active unless application code explicitly lists that
|
|
52
|
+
// feature in requiredFeatures at device creation, so the default behavior of
|
|
53
|
+
// a device that asks for no experimental features is unchanged.
|
|
54
|
+
static const char *const kEnabledToggles[] = {
|
|
55
|
+
"allow_unsafe_apis",
|
|
56
|
+
"expose_wgsl_experimental_features",
|
|
57
|
+
};
|
|
58
|
+
wgpu::DawnTogglesDescriptor toggles;
|
|
59
|
+
toggles.enabledToggleCount = std::size(kEnabledToggles);
|
|
60
|
+
toggles.enabledToggles = kEnabledToggles;
|
|
61
|
+
instanceDesc.nextInChain = &toggles;
|
|
62
|
+
|
|
63
|
+
return wgpu::CreateInstance(&instanceDesc);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
} // namespace
|
|
67
|
+
|
|
68
|
+
WebGPURuntime &WebGPURuntime::shared() {
|
|
69
|
+
static auto *runtime = new WebGPURuntime();
|
|
70
|
+
return *runtime;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
wgpu::Instance WebGPURuntime::instance() {
|
|
74
|
+
std::call_once(_instanceOnce, [this] { _instance = createInstance(); });
|
|
75
|
+
return _instance;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
wgpu::Surface WebGPURuntime::makeSurface(void *nativeSurface) {
|
|
79
|
+
#if defined(__ANDROID__)
|
|
80
|
+
wgpu::SurfaceSourceAndroidNativeWindow source;
|
|
81
|
+
source.window = nativeSurface;
|
|
82
|
+
#else
|
|
83
|
+
wgpu::SurfaceSourceMetalLayer source;
|
|
84
|
+
source.layer = nativeSurface;
|
|
85
|
+
#endif
|
|
86
|
+
wgpu::SurfaceDescriptor descriptor;
|
|
87
|
+
descriptor.nextInChain = &source;
|
|
88
|
+
return instance().CreateSurface(&descriptor);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
} // namespace rnwgpu
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <mutex>
|
|
4
|
+
|
|
5
|
+
#include "webgpu/webgpu_cpp.h"
|
|
6
|
+
|
|
7
|
+
namespace rnwgpu {
|
|
8
|
+
|
|
9
|
+
// Process-lifetime GPU state shared by every React instance, JS runtime and
|
|
10
|
+
// thread. It holds nothing tied to one React instance (no jsi::Runtime,
|
|
11
|
+
// CallInvoker or BlobModule): those stay with the per-instance module.
|
|
12
|
+
// Intentionally never destroyed, so there is no static-destruction ordering to
|
|
13
|
+
// get wrong and it outlives a React reload.
|
|
14
|
+
class WebGPURuntime {
|
|
15
|
+
public:
|
|
16
|
+
static WebGPURuntime &shared();
|
|
17
|
+
|
|
18
|
+
// The process-wide instance: Skia Graphite's when that is loaded, otherwise
|
|
19
|
+
// our own. Created on first use. Safe to call from any thread.
|
|
20
|
+
wgpu::Instance instance();
|
|
21
|
+
|
|
22
|
+
// Creates a surface on the shared instance for a CAMetalLayer (Apple) or an
|
|
23
|
+
// ANativeWindow (Android). Safe to call from any thread.
|
|
24
|
+
wgpu::Surface makeSurface(void *nativeSurface);
|
|
25
|
+
|
|
26
|
+
WebGPURuntime(const WebGPURuntime &) = delete;
|
|
27
|
+
WebGPURuntime &operator=(const WebGPURuntime &) = delete;
|
|
28
|
+
|
|
29
|
+
private:
|
|
30
|
+
WebGPURuntime() = default;
|
|
31
|
+
|
|
32
|
+
std::once_flag _instanceOnce;
|
|
33
|
+
wgpu::Instance _instance;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
} // namespace rnwgpu
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
#include "webgpu/webgpu_cpp.h"
|
|
12
12
|
|
|
13
|
+
#include "EnforceRange.h"
|
|
13
14
|
#include "GPUBindGroupDescriptor.h"
|
|
14
15
|
#include "GPUBindGroupEntry.h"
|
|
15
16
|
#include "GPUBindGroupLayoutDescriptor.h"
|
|
@@ -161,11 +162,19 @@ public:
|
|
|
161
162
|
return true;
|
|
162
163
|
}
|
|
163
164
|
|
|
165
|
+
// JS numbers destined for an integer field are WebIDL [EnforceRange]
|
|
166
|
+
// integers in WebGPU, so an out-of-range value is a TypeError rather than a
|
|
167
|
+
// silent wrap (and an undefined cast). Flags are enums here and keep the
|
|
168
|
+
// plain cast, as GPUFlagsConstant is not [EnforceRange].
|
|
164
169
|
template <typename T>
|
|
165
170
|
[[nodiscard]] typename std::enable_if<
|
|
166
171
|
(std::is_arithmetic<T>::value || std::is_enum<T>::value), bool>::type
|
|
167
172
|
Convert(T &out, const double &in) {
|
|
168
|
-
|
|
173
|
+
if constexpr (std::is_integral_v<T> && !std::is_same_v<T, bool>) {
|
|
174
|
+
out = enforceRange<T>(in);
|
|
175
|
+
} else {
|
|
176
|
+
out = static_cast<T>(in);
|
|
177
|
+
}
|
|
169
178
|
return true;
|
|
170
179
|
}
|
|
171
180
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <string>
|
|
4
|
+
|
|
5
|
+
#include "webgpu/webgpu_cpp.h"
|
|
6
|
+
|
|
7
|
+
namespace rnwgpu {
|
|
8
|
+
|
|
9
|
+
// 'GPUSharedFence "label"', or just the class name when unlabeled.
|
|
10
|
+
inline std::string describeDestroyed(const char *className,
|
|
11
|
+
const std::string &label) {
|
|
12
|
+
return label.empty() ? std::string(className)
|
|
13
|
+
: std::string(className) + " \"" + label + "\"";
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Reports a use of a destroyed object the way Dawn reports an invalid one: a
|
|
17
|
+
// validation error, caught by error scopes or else delivered to the device's
|
|
18
|
+
// uncapturederror event.
|
|
19
|
+
inline void reportDestroyedUse(wgpu::Device &device, const char *className,
|
|
20
|
+
const std::string &label, const char *method) {
|
|
21
|
+
auto message = "Destroyed " + describeDestroyed(className, label) +
|
|
22
|
+
" used in " + method + "().";
|
|
23
|
+
device.InjectError(wgpu::ErrorType::Validation, message.c_str());
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
} // namespace rnwgpu
|
package/cpp/rnwgpu/api/GPU.cpp
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
#include "GPU.h"
|
|
2
2
|
|
|
3
|
-
#include <dlfcn.h>
|
|
4
|
-
|
|
5
3
|
#include <cstdio>
|
|
6
4
|
#include <memory>
|
|
7
5
|
#include <string>
|
|
@@ -11,65 +9,13 @@
|
|
|
11
9
|
|
|
12
10
|
#include "Convertors.h"
|
|
13
11
|
#include "JSIConverter.h"
|
|
12
|
+
#include "WebGPURuntime.h"
|
|
14
13
|
#include "rnwgpu/async/RuntimeContext.h"
|
|
15
14
|
|
|
16
15
|
namespace rnwgpu {
|
|
17
16
|
|
|
18
|
-
GPU::GPU(jsi::Runtime & /*runtime*/)
|
|
19
|
-
|
|
20
|
-
// process, adopt its wgpu::Instance instead of creating our own, so
|
|
21
|
-
// Graphite's device and every JS-created device live on one instance. That
|
|
22
|
-
// makes pointer-based handoff (RNWebGPU.importDevice of Skia's device)
|
|
23
|
-
// sound: the async pump serves the shared instance, so callbacks on
|
|
24
|
-
// imported devices settle. Discovered at runtime via dlsym so there is no
|
|
25
|
-
// build-time coupling and native-module init order does not matter. Both
|
|
26
|
-
// packages must link the same single Dawn copy (they vendor the same
|
|
27
|
-
// artifact) for the handle to be meaningful.
|
|
28
|
-
using GetInstanceFn = WGPUInstance (*)();
|
|
29
|
-
if (auto getSkiaInstance = reinterpret_cast<GetInstanceFn>(
|
|
30
|
-
dlsym(RTLD_DEFAULT, "rnskia_getWGPUInstance"))) {
|
|
31
|
-
if (WGPUInstance external = getSkiaInstance()) {
|
|
32
|
-
wgpuInstanceAddRef(external);
|
|
33
|
-
_instance = wgpu::Instance::Acquire(external);
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
static const auto kTimedWaitAny = wgpu::InstanceFeatureName::TimedWaitAny;
|
|
39
|
-
wgpu::InstanceDescriptor instanceDesc{.requiredFeatureCount = 1,
|
|
40
|
-
.requiredFeatures = &kTimedWaitAny};
|
|
41
|
-
|
|
42
|
-
wgpu::InstanceLimits limits{.timedWaitAnyMaxCount = 64};
|
|
43
|
-
instanceDesc.requiredLimits = &limits;
|
|
44
|
-
|
|
45
|
-
// Expose Dawn's experimental adapter features. Several features needed by
|
|
46
|
-
// our Android external-texture path (YCbCrVulkanSamplers,
|
|
47
|
-
// OpaqueYCbCrAndroidForExternalTexture) are tagged Experimental in Dawn's
|
|
48
|
-
// feature table and are otherwise filtered out of adapter.features by
|
|
49
|
-
// PhysicalDeviceBase::GetSupportedFeatures. The allow_unsafe_apis toggle
|
|
50
|
-
// disables that filter so the features become visible;
|
|
51
|
-
// expose_wgsl_experimental_features is the parallel toggle for WGSL language
|
|
52
|
-
// features.
|
|
53
|
-
//
|
|
54
|
-
// Trade-off: these are instance-level Dawn toggles, so they apply to every
|
|
55
|
-
// adapter/device created from this instance, not just the external-texture
|
|
56
|
-
// path. There is no finer-grained, per-feature mechanism to surface these.
|
|
57
|
-
// The exposure is acceptable because the toggle only *un-hides* experimental
|
|
58
|
-
// features in adapter.features; it does not enable any of them. Nothing
|
|
59
|
-
// experimental becomes active unless application code explicitly lists that
|
|
60
|
-
// feature in requiredFeatures at device creation, so the default behavior of
|
|
61
|
-
// a device that asks for no experimental features is unchanged.
|
|
62
|
-
static const char *const kEnabledToggles[] = {
|
|
63
|
-
"allow_unsafe_apis",
|
|
64
|
-
"expose_wgsl_experimental_features",
|
|
65
|
-
};
|
|
66
|
-
wgpu::DawnTogglesDescriptor toggles;
|
|
67
|
-
toggles.enabledToggleCount = std::size(kEnabledToggles);
|
|
68
|
-
toggles.enabledToggles = kEnabledToggles;
|
|
69
|
-
instanceDesc.nextInChain = &toggles;
|
|
70
|
-
|
|
71
|
-
_instance = wgpu::CreateInstance(&instanceDesc);
|
|
72
|
-
}
|
|
17
|
+
GPU::GPU(jsi::Runtime & /*runtime*/)
|
|
18
|
+
: NativeObject(CLASS_NAME), _instance(WebGPURuntime::shared().instance()) {}
|
|
73
19
|
|
|
74
20
|
async::AsyncTaskHandle GPU::requestAdapter(
|
|
75
21
|
jsi::Runtime &runtime,
|
|
@@ -345,14 +345,21 @@ std::shared_ptr<GPUSharedTextureMemory> GPUDevice::importSharedTextureMemory(
|
|
|
345
345
|
#endif
|
|
346
346
|
|
|
347
347
|
auto memory = _instance.ImportSharedTextureMemory(&desc);
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
348
|
+
// Dawn reports a failed import (feature missing, unusable handle) as a
|
|
349
|
+
// validation error and returns an error object rather than null, with no
|
|
350
|
+
// format in its properties: the format getter of any texture created from
|
|
351
|
+
// it would throw. Fail the import instead.
|
|
352
|
+
wgpu::SharedTextureMemoryProperties properties{};
|
|
353
|
+
if (memory == nullptr ||
|
|
354
|
+
memory.GetProperties(&properties) != wgpu::Status::Success ||
|
|
355
|
+
properties.format == wgpu::TextureFormat::Undefined) {
|
|
356
|
+
throw std::runtime_error(
|
|
357
|
+
"GPUDevice::importSharedTextureMemory(): the import failed - is the "
|
|
358
|
+
"'shared-texture-memory-iosurface' (Apple) or "
|
|
359
|
+
"'shared-texture-memory-ahardware-buffer' (Android) feature enabled "
|
|
360
|
+
"on the device, and the handle a usable IOSurface / AHardwareBuffer?");
|
|
361
|
+
}
|
|
362
|
+
return std::make_shared<GPUSharedTextureMemory>(_instance, std::move(memory),
|
|
356
363
|
std::move(label));
|
|
357
364
|
}
|
|
358
365
|
|
|
@@ -402,7 +409,8 @@ std::shared_ptr<GPUSharedFence> GPUDevice::importSharedFence(
|
|
|
402
409
|
"GPUDevice::importSharedFence(): ImportSharedFence returned null - is "
|
|
403
410
|
"the matching 'shared-fence-*' feature enabled on the device?");
|
|
404
411
|
}
|
|
405
|
-
return std::make_shared<GPUSharedFence>(std::move(fence),
|
|
412
|
+
return std::make_shared<GPUSharedFence>(_instance, std::move(fence),
|
|
413
|
+
std::move(label));
|
|
406
414
|
}
|
|
407
415
|
|
|
408
416
|
async::AsyncTaskHandle GPUDevice::createComputePipelineAsync(
|
|
@@ -24,6 +24,21 @@ public:
|
|
|
24
24
|
installGetter(runtime, prototype, "message", &GPUError::getMessage);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
// GPUValidationError, GPUOutOfMemoryError and GPUInternalError extend
|
|
28
|
+
// GPUError in the spec: chain their prototype to GPUError.prototype so
|
|
29
|
+
// `error instanceof GPUError` holds.
|
|
30
|
+
static void inheritPrototype(jsi::Runtime &runtime, jsi::Object &prototype) {
|
|
31
|
+
installPrototype(runtime);
|
|
32
|
+
auto *parent = getCachedPrototype(runtime);
|
|
33
|
+
if (parent == nullptr) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
auto objectCtor = runtime.global().getPropertyAsObject(runtime, "Object");
|
|
37
|
+
auto setPrototypeOf =
|
|
38
|
+
objectCtor.getPropertyAsFunction(runtime, "setPrototypeOf");
|
|
39
|
+
setPrototypeOf.call(runtime, prototype, *parent);
|
|
40
|
+
}
|
|
41
|
+
|
|
27
42
|
protected:
|
|
28
43
|
// Protected constructor for subclasses
|
|
29
44
|
GPUError(const char *className, std::string message)
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
#include <string>
|
|
4
4
|
|
|
5
|
+
#include "GPUError.h"
|
|
5
6
|
#include "NativeObject.h"
|
|
6
7
|
|
|
7
8
|
namespace rnwgpu {
|
|
@@ -20,6 +21,7 @@ public:
|
|
|
20
21
|
std::string getMessage() { return _message; }
|
|
21
22
|
|
|
22
23
|
static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) {
|
|
24
|
+
GPUError::inheritPrototype(runtime, prototype);
|
|
23
25
|
installGetter(runtime, prototype, "__brand", &GPUInternalError::getBrand);
|
|
24
26
|
installGetter(runtime, prototype, "message", &GPUInternalError::getMessage);
|
|
25
27
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
#include <string>
|
|
4
4
|
|
|
5
|
+
#include "GPUError.h"
|
|
5
6
|
#include "NativeObject.h"
|
|
6
7
|
|
|
7
8
|
namespace rnwgpu {
|
|
@@ -20,6 +21,7 @@ public:
|
|
|
20
21
|
std::string getMessage() { return _message; }
|
|
21
22
|
|
|
22
23
|
static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) {
|
|
24
|
+
GPUError::inheritPrototype(runtime, prototype);
|
|
23
25
|
installGetter(runtime, prototype, "__brand",
|
|
24
26
|
&GPUOutOfMemoryError::getBrand);
|
|
25
27
|
installGetter(runtime, prototype, "message",
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
#include <vector>
|
|
10
10
|
|
|
11
11
|
#include "Convertors.h"
|
|
12
|
+
#include "EnforceRange.h"
|
|
12
13
|
#include "ImageBitmap.h"
|
|
13
14
|
#include "PlatformContext.h"
|
|
14
15
|
|
|
@@ -126,15 +127,9 @@ void GPUQueue::copyVideoFrameToTexture(
|
|
|
126
127
|
throw std::runtime_error(
|
|
127
128
|
"copyExternalImageToTexture: a native frame is a single 2D image");
|
|
128
129
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
if (!(value >= 0) || value > kMaxCoordinate) {
|
|
133
|
-
throw std::runtime_error(std::string("copyExternalImageToTexture: ") +
|
|
134
|
-
name +
|
|
135
|
-
" must be a non-negative integer coordinate");
|
|
136
|
-
}
|
|
137
|
-
return static_cast<uint32_t>(value);
|
|
130
|
+
// GPUOrigin2D coordinates are [EnforceRange] unsigned long.
|
|
131
|
+
auto coordinate = [](double value, const char * /*name*/) {
|
|
132
|
+
return enforceRange<uint32_t>(value);
|
|
138
133
|
};
|
|
139
134
|
const double rotation = source.rotation.value_or(0);
|
|
140
135
|
if (rotation != 0 && rotation != 90 && rotation != 180 && rotation != 270) {
|
|
@@ -208,18 +203,9 @@ void GPUQueue::copyExternalImageToTexture(
|
|
|
208
203
|
}
|
|
209
204
|
|
|
210
205
|
const auto origin = source->origin.value_or(nullptr);
|
|
211
|
-
// GPUOrigin2D coordinates are [EnforceRange] unsigned long
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
constexpr double kMaxOrigin =
|
|
215
|
-
static_cast<double>(std::numeric_limits<uint32_t>::max());
|
|
216
|
-
if (origin && (!(origin->x >= 0) || !(origin->y >= 0) ||
|
|
217
|
-
origin->x > kMaxOrigin || origin->y > kMaxOrigin)) {
|
|
218
|
-
throw std::runtime_error(
|
|
219
|
-
"The source origin must be a non-negative integer coordinate.");
|
|
220
|
-
}
|
|
221
|
-
const size_t sourceOriginX = origin ? static_cast<size_t>(origin->x) : 0;
|
|
222
|
-
const size_t sourceOriginY = origin ? static_cast<size_t>(origin->y) : 0;
|
|
206
|
+
// GPUOrigin2D coordinates are [EnforceRange] unsigned long.
|
|
207
|
+
const size_t sourceOriginX = origin ? enforceRange<uint32_t>(origin->x) : 0;
|
|
208
|
+
const size_t sourceOriginY = origin ? enforceRange<uint32_t>(origin->y) : 0;
|
|
223
209
|
if (sourceOriginX > source->source->getWidth() ||
|
|
224
210
|
sz.width > source->source->getWidth() - sourceOriginX ||
|
|
225
211
|
sourceOriginY > source->source->getHeight() ||
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
#include <cstdint>
|
|
4
4
|
#include <string>
|
|
5
5
|
|
|
6
|
+
#include "DestroyedError.h"
|
|
7
|
+
|
|
6
8
|
#if defined(__ANDROID__)
|
|
7
9
|
#include <fcntl.h>
|
|
8
10
|
#endif
|
|
@@ -32,10 +34,30 @@ std::string sharedFenceTypeToString(wgpu::SharedFenceType type) {
|
|
|
32
34
|
}
|
|
33
35
|
}
|
|
34
36
|
|
|
37
|
+
// Dawn reports a fence it can't export (an invalid one) as a validation error
|
|
38
|
+
// and leaves the type at 0. Fail the call too, like a destroyed fence does,
|
|
39
|
+
// rather than returning a record with no type and no handle.
|
|
40
|
+
void throwIfNotExported(jsi::Runtime &runtime,
|
|
41
|
+
const wgpu::SharedFenceExportInfo &info) {
|
|
42
|
+
if (info.type == wgpu::SharedFenceType(0)) {
|
|
43
|
+
throw jsi::JSError(runtime, "GPUSharedFence::export() failed");
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
35
47
|
} // namespace
|
|
36
48
|
|
|
49
|
+
void GPUSharedFence::injectDestroyedError(const char *method) {
|
|
50
|
+
reportDestroyedUse(_device, CLASS_NAME, _label, method);
|
|
51
|
+
}
|
|
52
|
+
|
|
37
53
|
jsi::Value GPUSharedFence::exportInfo(jsi::Runtime &runtime, const jsi::Value &,
|
|
38
54
|
const jsi::Value *, size_t) {
|
|
55
|
+
// Fails like an export Dawn rejects: a validation error, then the throw.
|
|
56
|
+
if (!_instance) {
|
|
57
|
+
injectDestroyedError("export");
|
|
58
|
+
throw jsi::JSError(runtime,
|
|
59
|
+
"GPUSharedFence::export(): the fence was destroyed");
|
|
60
|
+
}
|
|
39
61
|
wgpu::SharedFenceExportInfo info{};
|
|
40
62
|
uint64_t handle = 0;
|
|
41
63
|
|
|
@@ -44,25 +66,30 @@ jsi::Value GPUSharedFence::exportInfo(jsi::Runtime &runtime, const jsi::Value &,
|
|
|
44
66
|
wgpu::SharedFenceMTLSharedEventExportInfo mtlInfo{};
|
|
45
67
|
info.nextInChain = &mtlInfo;
|
|
46
68
|
_instance.ExportInfo(&info);
|
|
69
|
+
throwIfNotExported(runtime, info);
|
|
47
70
|
handle = reinterpret_cast<uint64_t>(mtlInfo.sharedEvent);
|
|
48
71
|
#elif defined(__ANDROID__)
|
|
49
|
-
// Android: the handle is an OS file descriptor (sync_fd). Dawn's ExportInfo
|
|
50
|
-
// owned by the SharedFence and closed when the
|
|
51
|
-
//
|
|
52
|
-
//
|
|
72
|
+
// Android: the handle is an OS file descriptor (sync_fd). Dawn's ExportInfo
|
|
73
|
+
// returns a BORROWED fd — it is owned by the SharedFence and closed when the
|
|
74
|
+
// fence is destroyed. This exported handle is documented as caller-owned (the
|
|
75
|
+
// caller must close() it), so dup() it. Without the dup the same fd is closed
|
|
76
|
+
// twice — once by the caller and once by Dawn on fence destruction — tripping
|
|
77
|
+
// Android's fdsan (double-close abort).
|
|
53
78
|
wgpu::SharedFenceSyncFDExportInfo fdInfo{};
|
|
54
79
|
info.nextInChain = &fdInfo;
|
|
55
80
|
_instance.ExportInfo(&info);
|
|
56
|
-
|
|
57
|
-
|
|
81
|
+
throwIfNotExported(runtime, info);
|
|
82
|
+
int exportedFd = fdInfo.handle >= 0
|
|
83
|
+
? ::fcntl(fdInfo.handle, F_DUPFD_CLOEXEC, 0)
|
|
84
|
+
: fdInfo.handle;
|
|
58
85
|
handle = static_cast<uint64_t>(static_cast<uint32_t>(exportedFd));
|
|
59
86
|
#else
|
|
60
|
-
// react-native-webgpu only targets Apple (Metal) and Android (Vulkan). On any
|
|
61
|
-
// other platform there is no native handle convention to expose, so fail
|
|
62
|
-
// rather than handing back a meaningless handle of 0.
|
|
63
|
-
throw jsi::JSError(runtime,
|
|
64
|
-
|
|
65
|
-
|
|
87
|
+
// react-native-webgpu only targets Apple (Metal) and Android (Vulkan). On any
|
|
88
|
+
// other platform there is no native handle convention to expose, so fail
|
|
89
|
+
// loudly rather than handing back a meaningless handle of 0.
|
|
90
|
+
throw jsi::JSError(runtime,
|
|
91
|
+
"GPUSharedFence::export(): unsupported platform (only "
|
|
92
|
+
"Apple/Metal and Android/Vulkan are supported)");
|
|
66
93
|
#endif
|
|
67
94
|
|
|
68
95
|
jsi::Object result(runtime);
|
|
@@ -17,9 +17,10 @@ class GPUSharedFence : public NativeObject<GPUSharedFence> {
|
|
|
17
17
|
public:
|
|
18
18
|
static constexpr const char *CLASS_NAME = "GPUSharedFence";
|
|
19
19
|
|
|
20
|
-
explicit GPUSharedFence(wgpu::
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
explicit GPUSharedFence(wgpu::Device device, wgpu::SharedFence instance,
|
|
21
|
+
std::string label)
|
|
22
|
+
: NativeObject(CLASS_NAME), _device(std::move(device)),
|
|
23
|
+
_instance(std::move(instance)), _label(std::move(label)) {}
|
|
23
24
|
|
|
24
25
|
public:
|
|
25
26
|
std::string getBrand() { return CLASS_NAME; }
|
|
@@ -33,19 +34,35 @@ public:
|
|
|
33
34
|
std::string getLabel() { return _label; }
|
|
34
35
|
void setLabel(const std::string &label) {
|
|
35
36
|
_label = label;
|
|
36
|
-
_instance
|
|
37
|
+
if (_instance) {
|
|
38
|
+
_instance.SetLabel(_label.c_str());
|
|
39
|
+
}
|
|
37
40
|
}
|
|
38
41
|
|
|
42
|
+
// Releases the fence now (closing its sync fd on Android) rather than when
|
|
43
|
+
// the JS object is garbage collected, which a runtime making fences every
|
|
44
|
+
// frame may not do for a while. Dawn keeps its own reference as long as it
|
|
45
|
+
// needs the fence (a beginAccess wait). A later use is a validation error
|
|
46
|
+
// and a throw.
|
|
47
|
+
void destroy() { _instance = nullptr; }
|
|
48
|
+
|
|
39
49
|
static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) {
|
|
40
50
|
installGetter(runtime, prototype, "__brand", &GPUSharedFence::getBrand);
|
|
41
51
|
installMethod(runtime, prototype, "export", &GPUSharedFence::exportInfo);
|
|
52
|
+
installMethod(runtime, prototype, "destroy", &GPUSharedFence::destroy);
|
|
42
53
|
installGetterSetter(runtime, prototype, "label", &GPUSharedFence::getLabel,
|
|
43
54
|
&GPUSharedFence::setLabel);
|
|
44
55
|
}
|
|
45
56
|
|
|
46
57
|
inline wgpu::SharedFence get() { return _instance; }
|
|
47
58
|
|
|
59
|
+
// Reports a use of the destroyed fence the way Dawn reports an invalid
|
|
60
|
+
// object: a validation error, caught by error scopes or else delivered to
|
|
61
|
+
// the device's uncapturederror event.
|
|
62
|
+
void injectDestroyedError(const char *method);
|
|
63
|
+
|
|
48
64
|
private:
|
|
65
|
+
wgpu::Device _device;
|
|
49
66
|
wgpu::SharedFence _instance;
|
|
50
67
|
std::string _label;
|
|
51
68
|
};
|