react-native-wgpu 0.1.12 → 0.1.14
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 +7 -0
- package/android/build.gradle +4 -4
- package/android/cpp/cpp-adapter.cpp +10 -4
- package/android/cpp/platform/ThreadUtils.cpp +41 -0
- package/apple/ApplePlatformContext.h +19 -0
- package/apple/ApplePlatformContext.mm +86 -0
- package/apple/MetalView.h +13 -0
- package/apple/MetalView.mm +58 -0
- package/apple/WebGPUModule.h +19 -0
- package/apple/WebGPUModule.mm +93 -0
- package/apple/WebGPUView.h +15 -0
- package/apple/WebGPUView.mm +68 -0
- package/apple/WebGPUViewManager.mm +23 -0
- package/apple/platform/ThreadUtils.cpp +33 -0
- package/cpp/jsi/RNFJSIConverter.h +47 -28
- package/cpp/platform/ThreadUtils.h +30 -0
- package/cpp/rnwgpu/RNWebGPUManager.cpp +8 -0
- package/cpp/rnwgpu/api/Convertors.h +13 -14
- package/cpp/rnwgpu/api/GPU.cpp +4 -4
- package/cpp/rnwgpu/api/GPUAdapter.cpp +15 -14
- package/cpp/rnwgpu/api/GPUAdapterInfo.h +25 -4
- package/cpp/rnwgpu/api/GPUCanvasContext.cpp +6 -4
- package/cpp/rnwgpu/api/GPUDevice.cpp +5 -5
- package/cpp/rnwgpu/api/GPUDevice.h +7 -1
- package/cpp/rnwgpu/api/GPUFeatures.h +4 -4
- package/cpp/rnwgpu/api/GPUShaderModule.cpp +2 -1
- package/cpp/rnwgpu/api/descriptors/GPUCanvasConfiguration.h +9 -0
- package/cpp/threading/CallInvokerDispatcher.h +37 -0
- package/cpp/threading/Dispatcher.cpp +54 -0
- package/cpp/threading/Dispatcher.h +93 -0
- package/cpp/threading/ThreadPool.cpp +86 -0
- package/cpp/threading/ThreadPool.h +53 -0
- package/cpp/webgpu/webgpu.h +762 -758
- package/cpp/webgpu/webgpu_cpp.h +1827 -1626
- package/cpp/webgpu/webgpu_cpp_chained_struct.h +2 -0
- package/lib/commonjs/hooks.js +4 -2
- package/lib/commonjs/hooks.js.map +1 -1
- package/lib/module/hooks.js +4 -2
- package/lib/module/hooks.js.map +1 -1
- package/lib/typescript/lib/commonjs/hooks.d.ts.map +1 -1
- package/lib/typescript/lib/module/hooks.d.ts.map +1 -1
- package/lib/typescript/src/__tests__/Alpha.spec.d.ts +2 -0
- package/lib/typescript/src/__tests__/Alpha.spec.d.ts.map +1 -0
- package/lib/typescript/src/hooks.d.ts.map +1 -1
- package/libs/android/arm64-v8a/libwebgpu_dawn.so +0 -0
- package/libs/android/armeabi-v7a/libwebgpu_dawn.so +0 -0
- package/libs/android/x86/libwebgpu_dawn.so +0 -0
- package/libs/android/x86_64/libwebgpu_dawn.so +0 -0
- package/libs/apple/arm64_iphoneos/libwebgpu_dawn.a +0 -0
- package/libs/apple/arm64_iphonesimulator/libwebgpu_dawn.a +0 -0
- package/libs/apple/arm64_xros/libwebgpu_dawn.a +0 -0
- package/libs/apple/arm64_xrsimulator/libwebgpu_dawn.a +0 -0
- package/libs/apple/iphonesimulator/libwebgpu_dawn.a +0 -0
- package/libs/apple/libwebgpu_dawn.xcframework/Info.plist +10 -10
- package/libs/apple/libwebgpu_dawn.xcframework/ios-arm64/libwebgpu_dawn.a +0 -0
- package/libs/apple/libwebgpu_dawn.xcframework/ios-arm64_x86_64-simulator/libwebgpu_dawn.a +0 -0
- package/libs/apple/libwebgpu_dawn.xcframework/macos-arm64_x86_64/libwebgpu_dawn.a +0 -0
- package/libs/apple/libwebgpu_dawn.xcframework/xros-arm64/libwebgpu_dawn.a +0 -0
- package/libs/apple/libwebgpu_dawn.xcframework/xros-arm64-simulator/libwebgpu_dawn.a +0 -0
- package/libs/apple/universal_macosx/libwebgpu_dawn.a +0 -0
- package/libs/apple/x86_64_iphonesimulator/libwebgpu_dawn.a +0 -0
- package/libs/dawn.json +270 -251
- package/package.json +2 -2
- package/src/__tests__/Alpha.spec.ts +28 -0
- package/src/__tests__/Device.spec.ts +31 -0
- package/src/__tests__/snapshots/semi-opaque-cyan.png +0 -0
- package/src/hooks.tsx +3 -2
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
//
|
|
2
|
+
// ThreadUtils.hpp
|
|
3
|
+
// react-native-nitro
|
|
4
|
+
//
|
|
5
|
+
// Created by Marc Rousavy on 14.07.24.
|
|
6
|
+
//
|
|
7
|
+
#pragma once
|
|
8
|
+
|
|
9
|
+
#include <string>
|
|
10
|
+
|
|
11
|
+
namespace margelo {
|
|
12
|
+
|
|
13
|
+
class ThreadUtils final {
|
|
14
|
+
public:
|
|
15
|
+
ThreadUtils() = delete;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Get the current Thread's name.
|
|
19
|
+
* This is implemented differently on iOS and Android.
|
|
20
|
+
*/
|
|
21
|
+
static std::string getThreadName();
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Set the current Thread's name.
|
|
25
|
+
* This is implemented differently on iOS and Android.
|
|
26
|
+
*/
|
|
27
|
+
static void setThreadName(const std::string &name);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
} // namespace margelo
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
#include "RNWebGPUManager.h"
|
|
2
2
|
|
|
3
|
+
#include "CallInvokerDispatcher.h"
|
|
4
|
+
#include "Dispatcher.h"
|
|
3
5
|
#include "GPU.h"
|
|
4
6
|
#include "RNWebGPU.h"
|
|
5
7
|
|
|
@@ -22,6 +24,12 @@ RNWebGPUManager::RNWebGPUManager(
|
|
|
22
24
|
: _jsRuntime(jsRuntime), _jsCallInvoker(jsCallInvoker),
|
|
23
25
|
_platformContext(platformContext) {
|
|
24
26
|
|
|
27
|
+
// Installs the global Dispatcher mechanism into this Runtime.
|
|
28
|
+
// This allows creating Promises and calling back to JS.
|
|
29
|
+
auto dispatcher =
|
|
30
|
+
std::make_shared<margelo::CallInvokerDispatcher>(_jsCallInvoker);
|
|
31
|
+
margelo::Dispatcher::installRuntimeGlobalDispatcher(*_jsRuntime, dispatcher);
|
|
32
|
+
|
|
25
33
|
auto gpu = std::make_shared<GPU>();
|
|
26
34
|
auto rnWebGPU = std::make_shared<RNWebGPU>(gpu, _platformContext);
|
|
27
35
|
_gpu = gpu->get();
|
|
@@ -175,6 +175,17 @@ public:
|
|
|
175
175
|
return true;
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
+
[[nodiscard]] bool Convert(wgpu::OptionalBool &out,
|
|
179
|
+
const std::optional<bool> &in) {
|
|
180
|
+
out = in;
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
[[nodiscard]] bool Convert(wgpu::StringView &out, const std::string &in) {
|
|
185
|
+
out = {in.data(), in.size()};
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
|
|
178
189
|
[[nodiscard]] bool Convert(const char *&out, const std::string &in) {
|
|
179
190
|
out = in.c_str();
|
|
180
191
|
return true;
|
|
@@ -280,15 +291,6 @@ public:
|
|
|
280
291
|
Convert(out.label, in.label);
|
|
281
292
|
}
|
|
282
293
|
|
|
283
|
-
// [[nodiscard]] bool Convert(wgpu::CanvasConfiguration &out,
|
|
284
|
-
// const GPUCanvasConfiguration &in) {
|
|
285
|
-
// return Convert(out.device, in.device) && Convert(out.format, in.format)
|
|
286
|
-
// &&
|
|
287
|
-
// Convert(out.usage, in.usage) && Convert(out.viewFormats,
|
|
288
|
-
// in.viewFormats) && Convert(out.colorSpace, in.colorSpace) &&
|
|
289
|
-
// Convert(out.alphaMode, in.alphaMode);
|
|
290
|
-
// }
|
|
291
|
-
|
|
292
294
|
[[nodiscard]] bool Convert(wgpu::Color &out, const GPUColor &in) {
|
|
293
295
|
return Convert(out.r, in.r) && Convert(out.g, in.g) &&
|
|
294
296
|
Convert(out.b, in.b) && Convert(out.a, in.a);
|
|
@@ -490,11 +492,8 @@ public:
|
|
|
490
492
|
const GPUPrimitiveState &in) {
|
|
491
493
|
out = {};
|
|
492
494
|
|
|
493
|
-
if (in.unclippedDepth) {
|
|
494
|
-
|
|
495
|
-
Allocate<wgpu::PrimitiveDepthClipControl>();
|
|
496
|
-
depthClip->unclippedDepth = true;
|
|
497
|
-
out.nextInChain = depthClip;
|
|
495
|
+
if (in.unclippedDepth.has_value()) {
|
|
496
|
+
out.unclippedDepth = in.unclippedDepth.value();
|
|
498
497
|
}
|
|
499
498
|
|
|
500
499
|
return Convert(out.topology, in.topology) &&
|
package/cpp/rnwgpu/api/GPU.cpp
CHANGED
|
@@ -28,10 +28,10 @@ GPU::requestAdapter(
|
|
|
28
28
|
wgpu::Adapter adapter = nullptr;
|
|
29
29
|
_instance.RequestAdapter(
|
|
30
30
|
&aOptions,
|
|
31
|
-
[](WGPURequestAdapterStatus, WGPUAdapter cAdapter,
|
|
32
|
-
void *userdata) {
|
|
33
|
-
if (message
|
|
34
|
-
fprintf(stderr, "%s", message);
|
|
31
|
+
[](WGPURequestAdapterStatus, WGPUAdapter cAdapter,
|
|
32
|
+
const WGPUStringView message, void *userdata) {
|
|
33
|
+
if (message.length) {
|
|
34
|
+
fprintf(stderr, "%s", message.data);
|
|
35
35
|
return;
|
|
36
36
|
}
|
|
37
37
|
*static_cast<wgpu::Adapter *>(userdata) =
|
|
@@ -23,7 +23,7 @@ std::future<std::shared_ptr<GPUDevice>> GPUAdapter::requestDevice(
|
|
|
23
23
|
}
|
|
24
24
|
wgpu::DeviceLostCallbackInfo info = {
|
|
25
25
|
.callback = [](WGPUDevice const *device, WGPUDeviceLostReason reason,
|
|
26
|
-
|
|
26
|
+
const WGPUStringView message, void *userdata) {
|
|
27
27
|
const char *lostReason = "";
|
|
28
28
|
switch (reason) {
|
|
29
29
|
case WGPUDeviceLostReason_Destroyed:
|
|
@@ -35,12 +35,13 @@ std::future<std::shared_ptr<GPUDevice>> GPUAdapter::requestDevice(
|
|
|
35
35
|
default:
|
|
36
36
|
lostReason = "Unknown";
|
|
37
37
|
}
|
|
38
|
-
Logger::logToConsole("GPU Device Lost (%s): %s", lostReason,
|
|
38
|
+
Logger::logToConsole("GPU Device Lost (%s): %s", lostReason,
|
|
39
|
+
message.data);
|
|
39
40
|
}};
|
|
40
41
|
aDescriptor.deviceLostCallbackInfo = info;
|
|
41
42
|
wgpu::UncapturedErrorCallbackInfo errorInfo;
|
|
42
43
|
errorInfo.userdata = static_cast<void *>(_creationRuntime);
|
|
43
|
-
errorInfo.callback = [](WGPUErrorType type, const
|
|
44
|
+
errorInfo.callback = [](WGPUErrorType type, const WGPUStringView message,
|
|
44
45
|
void *userdata) {
|
|
45
46
|
auto creationRuntime = static_cast<jsi::Runtime *>(userdata);
|
|
46
47
|
const char *errorType = "";
|
|
@@ -60,16 +61,16 @@ std::future<std::shared_ptr<GPUDevice>> GPUAdapter::requestDevice(
|
|
|
60
61
|
default:
|
|
61
62
|
errorType = "Unknown";
|
|
62
63
|
}
|
|
63
|
-
std::string fullMessage = std::string(errorType) + ": " + message;
|
|
64
|
-
Logger::errorToJavascriptConsole(*creationRuntime, fullMessage
|
|
64
|
+
std::string fullMessage = std::string(errorType) + ": " + message.data;
|
|
65
|
+
Logger::errorToJavascriptConsole(*creationRuntime, fullMessage);
|
|
65
66
|
};
|
|
66
67
|
aDescriptor.uncapturedErrorCallbackInfo = errorInfo;
|
|
67
68
|
_instance.RequestDevice(
|
|
68
69
|
&aDescriptor,
|
|
69
70
|
[](WGPURequestDeviceStatus status, WGPUDevice cDevice,
|
|
70
|
-
const
|
|
71
|
-
if (message
|
|
72
|
-
fprintf(stderr, "%s", message);
|
|
71
|
+
const WGPUStringView message, void *userdata) {
|
|
72
|
+
if (message.length) {
|
|
73
|
+
fprintf(stderr, "%s", message.data);
|
|
73
74
|
return;
|
|
74
75
|
}
|
|
75
76
|
*static_cast<wgpu::Device *>(userdata) = wgpu::Device::Acquire(cDevice);
|
|
@@ -80,17 +81,17 @@ std::future<std::shared_ptr<GPUDevice>> GPUAdapter::requestDevice(
|
|
|
80
81
|
throw std::runtime_error("Failed to request device");
|
|
81
82
|
}
|
|
82
83
|
device.SetLoggingCallback(
|
|
83
|
-
[](WGPULoggingType type, const
|
|
84
|
+
[](WGPULoggingType type, const WGPUStringView message, void *userdata) {
|
|
84
85
|
auto creationRuntime = static_cast<jsi::Runtime *>(userdata);
|
|
85
86
|
const char *logLevel = "";
|
|
86
87
|
switch (type) {
|
|
87
88
|
case WGPULoggingType_Warning:
|
|
88
89
|
logLevel = "Warning";
|
|
89
|
-
Logger::warnToJavascriptConsole(*creationRuntime, message);
|
|
90
|
+
Logger::warnToJavascriptConsole(*creationRuntime, message.data);
|
|
90
91
|
break;
|
|
91
92
|
case WGPULoggingType_Error:
|
|
92
93
|
logLevel = "Error";
|
|
93
|
-
Logger::errorToJavascriptConsole(*creationRuntime, message);
|
|
94
|
+
Logger::errorToJavascriptConsole(*creationRuntime, message.data);
|
|
94
95
|
break;
|
|
95
96
|
case WGPULoggingType_Verbose:
|
|
96
97
|
logLevel = "Verbose";
|
|
@@ -139,9 +140,9 @@ std::shared_ptr<GPUAdapterInfo> GPUAdapter::getInfo() {
|
|
|
139
140
|
}
|
|
140
141
|
|
|
141
142
|
bool GPUAdapter::getIsFallbackAdapter() {
|
|
142
|
-
wgpu::
|
|
143
|
-
_instance.
|
|
144
|
-
return
|
|
143
|
+
wgpu::AdapterInfo adapterInfo = {};
|
|
144
|
+
_instance.GetInfo(&adapterInfo);
|
|
145
|
+
return adapterInfo.adapterType == wgpu::AdapterType::CPU;
|
|
145
146
|
}
|
|
146
147
|
|
|
147
148
|
} // namespace rnwgpu
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
#include "RNFHybridObject.h"
|
|
9
9
|
|
|
10
10
|
#include "AsyncRunner.h"
|
|
11
|
+
#include "Convertors.h"
|
|
11
12
|
|
|
12
13
|
#include "webgpu/webgpu_cpp.h"
|
|
13
14
|
|
|
@@ -23,10 +24,30 @@ public:
|
|
|
23
24
|
public:
|
|
24
25
|
std::string getBrand() { return _name; }
|
|
25
26
|
|
|
26
|
-
std::string getVendor() {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
std::string getVendor() {
|
|
28
|
+
if (_instance.vendor.length) {
|
|
29
|
+
return _instance.vendor.data;
|
|
30
|
+
}
|
|
31
|
+
return "";
|
|
32
|
+
}
|
|
33
|
+
std::string getArchitecture() {
|
|
34
|
+
if (_instance.architecture.length) {
|
|
35
|
+
return _instance.architecture.data;
|
|
36
|
+
}
|
|
37
|
+
return "";
|
|
38
|
+
}
|
|
39
|
+
std::string getDevice() {
|
|
40
|
+
if (_instance.device.length) {
|
|
41
|
+
return _instance.device.data;
|
|
42
|
+
}
|
|
43
|
+
return "";
|
|
44
|
+
}
|
|
45
|
+
std::string getDescription() {
|
|
46
|
+
if (_instance.device.length) {
|
|
47
|
+
return _instance.device.data;
|
|
48
|
+
}
|
|
49
|
+
return "";
|
|
50
|
+
}
|
|
30
51
|
|
|
31
52
|
void loadHybridMethods() override {
|
|
32
53
|
registerHybridGetter("__brand", &GPUAdapterInfo::getBrand, this);
|
|
@@ -16,12 +16,14 @@ void GPUCanvasContext::configure(
|
|
|
16
16
|
throw std::runtime_error("Error with SurfaceConfiguration");
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
|
-
if (!conv(surfaceConfiguration.
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
if (!conv(surfaceConfiguration.usage, configuration->usage)) {
|
|
19
|
+
if (!conv(surfaceConfiguration.usage, configuration->usage) ||
|
|
20
|
+
!conv(surfaceConfiguration.format, configuration->format)) {
|
|
23
21
|
throw std::runtime_error("Error with SurfaceConfiguration");
|
|
24
22
|
}
|
|
23
|
+
|
|
24
|
+
#ifdef __APPLE__
|
|
25
|
+
surfaceConfiguration.alphaMode = configuration->alphaMode;
|
|
26
|
+
#endif
|
|
25
27
|
_surfaceInfo->configure(surfaceConfiguration);
|
|
26
28
|
}
|
|
27
29
|
|
|
@@ -76,10 +76,9 @@ std::shared_ptr<GPUShaderModule> GPUDevice::createShaderModule(
|
|
|
76
76
|
}
|
|
77
77
|
sm_desc.nextInChain = &wgsl_desc;
|
|
78
78
|
if (descriptor->code.find('\0') != std::string::npos) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
_async, sm_desc.label);
|
|
79
|
+
auto mod = _instance.CreateErrorShaderModule(
|
|
80
|
+
&sm_desc, "The WGSL shader contains an illegal character '\\0'");
|
|
81
|
+
return std::make_shared<GPUShaderModule>(mod, _async, sm_desc.label.data);
|
|
83
82
|
}
|
|
84
83
|
auto module = _instance.CreateShaderModule(&sm_desc);
|
|
85
84
|
return std::make_shared<GPUShaderModule>(module, _async,
|
|
@@ -337,6 +336,7 @@ std::unordered_set<std::string> GPUDevice::getFeatures() {
|
|
|
337
336
|
}
|
|
338
337
|
|
|
339
338
|
std::future<std::shared_ptr<GPUDeviceLostInfo>> GPUDevice::getLost() {
|
|
340
|
-
return
|
|
339
|
+
return std::async(std::launch::async,
|
|
340
|
+
[=]() { return m_lostSharedFuture->get(); });
|
|
341
341
|
}
|
|
342
342
|
} // namespace rnwgpu
|
|
@@ -57,6 +57,10 @@ public:
|
|
|
57
57
|
_label(label) {
|
|
58
58
|
m_lostPromise =
|
|
59
59
|
std::make_shared<std::promise<std::shared_ptr<GPUDeviceLostInfo>>>();
|
|
60
|
+
|
|
61
|
+
auto sharedFuture = m_lostPromise->get_future().share();
|
|
62
|
+
m_lostSharedFuture = std::make_shared<
|
|
63
|
+
std::shared_future<std::shared_ptr<GPUDeviceLostInfo>>>(sharedFuture);
|
|
60
64
|
}
|
|
61
65
|
|
|
62
66
|
public:
|
|
@@ -154,6 +158,8 @@ private:
|
|
|
154
158
|
std::string _label;
|
|
155
159
|
std::shared_ptr<std::promise<std::shared_ptr<GPUDeviceLostInfo>>>
|
|
156
160
|
m_lostPromise;
|
|
161
|
+
std::shared_ptr<std::shared_future<std::shared_ptr<GPUDeviceLostInfo>>>
|
|
162
|
+
m_lostSharedFuture;
|
|
157
163
|
};
|
|
158
164
|
|
|
159
|
-
} // namespace rnwgpu
|
|
165
|
+
} // namespace rnwgpu
|
|
@@ -65,9 +65,9 @@ static void convertEnumToJSUnion(wgpu::FeatureName inEnum,
|
|
|
65
65
|
case wgpu::FeatureName::ImplicitDeviceSynchronization:
|
|
66
66
|
*outUnion = "implicit-device-synchronization";
|
|
67
67
|
break;
|
|
68
|
-
case wgpu::FeatureName::SurfaceCapabilities:
|
|
69
|
-
*outUnion = "surface-capabilities";
|
|
70
|
-
break;
|
|
68
|
+
// case wgpu::FeatureName::SurfaceCapabilities:
|
|
69
|
+
// *outUnion = "surface-capabilities";
|
|
70
|
+
// break;
|
|
71
71
|
case wgpu::FeatureName::TransientAttachments:
|
|
72
72
|
*outUnion = "transient-attachments";
|
|
73
73
|
break;
|
|
@@ -218,4 +218,4 @@ static void convertEnumToJSUnion(wgpu::FeatureName inEnum,
|
|
|
218
218
|
}
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
-
} // namespace rnwgpu
|
|
221
|
+
} // namespace rnwgpu
|
|
@@ -18,7 +18,8 @@ GPUShaderModule::getCompilationInfo() {
|
|
|
18
18
|
for (size_t i = 0; i < compilationInfo->messageCount; ++i) {
|
|
19
19
|
const auto &wgpuMessage = compilationInfo->messages[i];
|
|
20
20
|
GPUCompilationMessage message;
|
|
21
|
-
message.message =
|
|
21
|
+
message.message =
|
|
22
|
+
wgpuMessage.message.length ? wgpuMessage.message.data : "";
|
|
22
23
|
message.type = wgpuMessage.type;
|
|
23
24
|
message.lineNum = wgpuMessage.lineNum;
|
|
24
25
|
message.linePos = wgpuMessage.linePos;
|
|
@@ -21,6 +21,7 @@ struct GPUCanvasConfiguration {
|
|
|
21
21
|
std::optional<double> usage; // GPUTextureUsageFlags
|
|
22
22
|
std::optional<std::vector<wgpu::TextureFormat>>
|
|
23
23
|
viewFormats; // Iterable<GPUTextureFormat>
|
|
24
|
+
wgpu::CompositeAlphaMode alphaMode = wgpu::CompositeAlphaMode::Opaque;
|
|
24
25
|
};
|
|
25
26
|
|
|
26
27
|
} // namespace rnwgpu
|
|
@@ -58,6 +59,14 @@ struct JSIConverter<std::shared_ptr<rnwgpu::GPUCanvasConfiguration>> {
|
|
|
58
59
|
prop,
|
|
59
60
|
false);
|
|
60
61
|
}
|
|
62
|
+
if (value.hasProperty(runtime, "alphaMode")) {
|
|
63
|
+
auto prop = value.getProperty(runtime, "alphaMode")
|
|
64
|
+
.asString(runtime)
|
|
65
|
+
.utf8(runtime);
|
|
66
|
+
if (prop == "premultiplied") {
|
|
67
|
+
result->alphaMode = wgpu::CompositeAlphaMode::Premultiplied;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
61
70
|
}
|
|
62
71
|
|
|
63
72
|
return result;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
|
|
2
|
+
//
|
|
3
|
+
// Created by Marc Rousavy on 27.03.24.
|
|
4
|
+
//
|
|
5
|
+
|
|
6
|
+
#pragma once
|
|
7
|
+
|
|
8
|
+
#include "Dispatcher.h"
|
|
9
|
+
|
|
10
|
+
#include <utility>
|
|
11
|
+
#include <memory>
|
|
12
|
+
#include <ReactCommon/CallInvoker.h>
|
|
13
|
+
|
|
14
|
+
namespace margelo {
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* A Dispatcher that uses react::CallInvoker for it's implementation
|
|
18
|
+
*/
|
|
19
|
+
class CallInvokerDispatcher final : public Dispatcher {
|
|
20
|
+
public:
|
|
21
|
+
explicit CallInvokerDispatcher(
|
|
22
|
+
std::shared_ptr<react::CallInvoker> callInvoker)
|
|
23
|
+
: _callInvoker(callInvoker) {}
|
|
24
|
+
|
|
25
|
+
void runAsync(std::function<void()> &&function) override {
|
|
26
|
+
_callInvoker->invokeAsync(std::move(function));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
void runSync(std::function<void()> &&function) override {
|
|
30
|
+
_callInvoker->invokeSync(std::move(function));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
private:
|
|
34
|
+
std::shared_ptr<react::CallInvoker> _callInvoker;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
} // namespace margelo
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Created by Marc Rousavy on 12.03.24.
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
#include "Dispatcher.h"
|
|
6
|
+
|
|
7
|
+
#include <memory>
|
|
8
|
+
#include "RNFJSIHelper.h"
|
|
9
|
+
|
|
10
|
+
namespace margelo {
|
|
11
|
+
|
|
12
|
+
namespace jsi = facebook::jsi;
|
|
13
|
+
|
|
14
|
+
static constexpr auto GLOBAL_DISPATCHER_HOLDER_NAME = "__nitroDispatcher";
|
|
15
|
+
|
|
16
|
+
std::unordered_map<jsi::Runtime *, std::weak_ptr<Dispatcher>>
|
|
17
|
+
Dispatcher::_globalCache;
|
|
18
|
+
|
|
19
|
+
void Dispatcher::installRuntimeGlobalDispatcher(
|
|
20
|
+
jsi::Runtime &runtime, std::shared_ptr<Dispatcher> dispatcher) {
|
|
21
|
+
|
|
22
|
+
// Store a weak reference in global cache
|
|
23
|
+
_globalCache[&runtime] = std::weak_ptr<Dispatcher>(dispatcher);
|
|
24
|
+
|
|
25
|
+
// Inject the dispatcher into Runtime global (runtime will hold a strong
|
|
26
|
+
// reference)
|
|
27
|
+
jsi::Object dispatcherHolder(runtime);
|
|
28
|
+
dispatcherHolder.setNativeState(runtime, dispatcher);
|
|
29
|
+
runtime.global().setProperty(runtime, GLOBAL_DISPATCHER_HOLDER_NAME,
|
|
30
|
+
dispatcherHolder);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
std::shared_ptr<Dispatcher>
|
|
34
|
+
Dispatcher::getRuntimeGlobalDispatcher(jsi::Runtime &runtime) {
|
|
35
|
+
if (auto search = _globalCache.find(&runtime); search != _globalCache.end()) {
|
|
36
|
+
// the runtime is known - we have something in cache
|
|
37
|
+
std::weak_ptr<Dispatcher> weakDispatcher = _globalCache[&runtime];
|
|
38
|
+
std::shared_ptr<Dispatcher> strongDispatcher = weakDispatcher.lock();
|
|
39
|
+
if (strongDispatcher) {
|
|
40
|
+
// the weak reference we cached is still valid - return it!
|
|
41
|
+
return strongDispatcher;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
jsi::Value dispatcherHolderValue = getRuntimeGlobalDispatcherHolder(runtime);
|
|
46
|
+
jsi::Object dispatcherHolder = dispatcherHolderValue.getObject(runtime);
|
|
47
|
+
return dispatcherHolder.getNativeState<Dispatcher>(runtime);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
jsi::Value Dispatcher::getRuntimeGlobalDispatcherHolder(jsi::Runtime &runtime) {
|
|
51
|
+
return runtime.global().getProperty(runtime, GLOBAL_DISPATCHER_HOLDER_NAME);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
} // namespace margelo
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Created by Marc Rousavy on 12.03.24.
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
#pragma once
|
|
6
|
+
|
|
7
|
+
#include <functional>
|
|
8
|
+
#include <future>
|
|
9
|
+
#include <jsi/jsi.h>
|
|
10
|
+
#include <queue>
|
|
11
|
+
#include <unordered_map>
|
|
12
|
+
#include <utility>
|
|
13
|
+
#include <memory>
|
|
14
|
+
|
|
15
|
+
namespace margelo {
|
|
16
|
+
|
|
17
|
+
namespace jsi = facebook::jsi;
|
|
18
|
+
|
|
19
|
+
class Dispatcher : public jsi::NativeState {
|
|
20
|
+
public:
|
|
21
|
+
/**
|
|
22
|
+
Installs the Dispatcher into the given Runtime.
|
|
23
|
+
It can be accessed using `getRuntimeGlobalDispatcher` later.
|
|
24
|
+
*/
|
|
25
|
+
static void
|
|
26
|
+
installRuntimeGlobalDispatcher(jsi::Runtime &runtime,
|
|
27
|
+
std::shared_ptr<Dispatcher> dispatcher);
|
|
28
|
+
/**
|
|
29
|
+
Gets the global Dispatcher in the given Runtime, or throws an error if not
|
|
30
|
+
found.
|
|
31
|
+
*/
|
|
32
|
+
static std::shared_ptr<Dispatcher>
|
|
33
|
+
getRuntimeGlobalDispatcher(jsi::Runtime &runtime);
|
|
34
|
+
|
|
35
|
+
private:
|
|
36
|
+
static jsi::Value getRuntimeGlobalDispatcherHolder(jsi::Runtime &runtime);
|
|
37
|
+
|
|
38
|
+
public:
|
|
39
|
+
/**
|
|
40
|
+
* Run the given void function synchronously on the Thread this Dispatcher is
|
|
41
|
+
* managing.
|
|
42
|
+
*/
|
|
43
|
+
virtual void runSync(std::function<void()> &&function) = 0;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Run the given void function asynchronously on the Thread this Dispatcher is
|
|
47
|
+
* managing.
|
|
48
|
+
*/
|
|
49
|
+
virtual void runAsync(std::function<void()> &&function) = 0;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Run the given function asynchronously on the Thread this Dispatcher is
|
|
53
|
+
* managing, and return a future that will hold the result of the function.
|
|
54
|
+
*/
|
|
55
|
+
template <typename T>
|
|
56
|
+
std::future<T> runAsyncAwaitable(std::function<T()> &&function) {
|
|
57
|
+
// 1. Create Promise that can be shared between this and dispatcher thread
|
|
58
|
+
auto promise = std::make_shared<std::promise<T>>();
|
|
59
|
+
std::future<T> future = promise->get_future();
|
|
60
|
+
|
|
61
|
+
runAsync([function = std::move(function), promise]() {
|
|
62
|
+
try {
|
|
63
|
+
if constexpr (std::is_void_v<T>) {
|
|
64
|
+
// 4. Call the actual function on the new Thread
|
|
65
|
+
function();
|
|
66
|
+
// 5.a. Resolve the Promise if we succeeded
|
|
67
|
+
promise->set_value();
|
|
68
|
+
} else {
|
|
69
|
+
// 4. Call the actual function on the new Thread
|
|
70
|
+
T result = function();
|
|
71
|
+
// 5.a. Resolve the Promise if we succeeded
|
|
72
|
+
promise->set_value(std::move(result));
|
|
73
|
+
}
|
|
74
|
+
} catch (...) {
|
|
75
|
+
// 5.b. Reject the Promise if the call failed
|
|
76
|
+
promise->set_exception(std::current_exception());
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// 3. Return an open future that gets resolved later by the dispatcher
|
|
81
|
+
// Thread
|
|
82
|
+
return future;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private:
|
|
86
|
+
static std::unordered_map<jsi::Runtime *, std::weak_ptr<Dispatcher>>
|
|
87
|
+
_globalCache;
|
|
88
|
+
|
|
89
|
+
private:
|
|
90
|
+
static constexpr auto TAG = "Dispatcher";
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
} // namespace margelo
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
//
|
|
2
|
+
// ThreadPool.cpp
|
|
3
|
+
// NitroModules
|
|
4
|
+
//
|
|
5
|
+
// Created by Marc Rousavy on 21.06.24.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#include "ThreadPool.h"
|
|
9
|
+
#include "ThreadUtils.h"
|
|
10
|
+
|
|
11
|
+
#include <utility>
|
|
12
|
+
#include <algorithm>
|
|
13
|
+
|
|
14
|
+
namespace margelo {
|
|
15
|
+
|
|
16
|
+
ThreadPool::ThreadPool(const char *name, size_t numThreads)
|
|
17
|
+
: _isAlive(true), _name(name) {
|
|
18
|
+
for (size_t i = 0; i < numThreads; ++i) {
|
|
19
|
+
std::string threadName = std::string(name) + "-" + std::to_string(i + 1);
|
|
20
|
+
_workers.emplace_back([this, threadName] {
|
|
21
|
+
// Set the Thread's name
|
|
22
|
+
ThreadUtils::setThreadName(threadName);
|
|
23
|
+
|
|
24
|
+
// Start the run-loop
|
|
25
|
+
while (true) {
|
|
26
|
+
std::function<void()> task;
|
|
27
|
+
{
|
|
28
|
+
// Lock on the mutex so only one Worker receives the condition signal
|
|
29
|
+
// at a time
|
|
30
|
+
std::unique_lock<std::mutex> lock(_queueMutex);
|
|
31
|
+
this->_condition.wait(
|
|
32
|
+
lock, [this] { return !_isAlive || !_tasks.empty(); });
|
|
33
|
+
if (!_isAlive && _tasks.empty()) {
|
|
34
|
+
// ThreadPool is dead - stop run-loop.
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
// Schedule the oldest task
|
|
38
|
+
task = std::move(_tasks.front());
|
|
39
|
+
_tasks.pop();
|
|
40
|
+
}
|
|
41
|
+
// Run it (outside of the mutex so others can run in parallel)
|
|
42
|
+
task();
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
void ThreadPool::run(std::function<void()> &&task) {
|
|
49
|
+
{
|
|
50
|
+
// lock on the mutex - we want to emplace the task back in the queue
|
|
51
|
+
std::unique_lock<std::mutex> lock(_queueMutex);
|
|
52
|
+
if (!_isAlive) {
|
|
53
|
+
throw std::runtime_error("Cannot queue the given task - the ThreadPool "
|
|
54
|
+
"has already been stopped!");
|
|
55
|
+
}
|
|
56
|
+
_tasks.emplace(std::move(task));
|
|
57
|
+
}
|
|
58
|
+
// Notify about a new task - one of the workers will pick it up
|
|
59
|
+
_condition.notify_one();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
ThreadPool::~ThreadPool() {
|
|
63
|
+
{
|
|
64
|
+
// Lock and set `_isAlive` to false.
|
|
65
|
+
std::unique_lock<std::mutex> lock(_queueMutex);
|
|
66
|
+
_isAlive = false;
|
|
67
|
+
}
|
|
68
|
+
// Notify all workers - they will stop the work since `_isAlive` is false.
|
|
69
|
+
_condition.notify_all();
|
|
70
|
+
for (std::thread &worker : _workers) {
|
|
71
|
+
// Wait for each worker to exit.
|
|
72
|
+
worker.join();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
std::shared_ptr<ThreadPool> ThreadPool::getSharedPool() {
|
|
77
|
+
static std::shared_ptr<ThreadPool> shared;
|
|
78
|
+
if (shared == nullptr) {
|
|
79
|
+
int availableThreads = std::thread::hardware_concurrency();
|
|
80
|
+
auto numThreads = std::min(availableThreads, 3);
|
|
81
|
+
shared = std::make_shared<ThreadPool>("nitro-thread", numThreads);
|
|
82
|
+
}
|
|
83
|
+
return shared;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
} // namespace margelo
|