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.
Files changed (67) hide show
  1. package/android/CMakeLists.txt +7 -0
  2. package/android/build.gradle +4 -4
  3. package/android/cpp/cpp-adapter.cpp +10 -4
  4. package/android/cpp/platform/ThreadUtils.cpp +41 -0
  5. package/apple/ApplePlatformContext.h +19 -0
  6. package/apple/ApplePlatformContext.mm +86 -0
  7. package/apple/MetalView.h +13 -0
  8. package/apple/MetalView.mm +58 -0
  9. package/apple/WebGPUModule.h +19 -0
  10. package/apple/WebGPUModule.mm +93 -0
  11. package/apple/WebGPUView.h +15 -0
  12. package/apple/WebGPUView.mm +68 -0
  13. package/apple/WebGPUViewManager.mm +23 -0
  14. package/apple/platform/ThreadUtils.cpp +33 -0
  15. package/cpp/jsi/RNFJSIConverter.h +47 -28
  16. package/cpp/platform/ThreadUtils.h +30 -0
  17. package/cpp/rnwgpu/RNWebGPUManager.cpp +8 -0
  18. package/cpp/rnwgpu/api/Convertors.h +13 -14
  19. package/cpp/rnwgpu/api/GPU.cpp +4 -4
  20. package/cpp/rnwgpu/api/GPUAdapter.cpp +15 -14
  21. package/cpp/rnwgpu/api/GPUAdapterInfo.h +25 -4
  22. package/cpp/rnwgpu/api/GPUCanvasContext.cpp +6 -4
  23. package/cpp/rnwgpu/api/GPUDevice.cpp +5 -5
  24. package/cpp/rnwgpu/api/GPUDevice.h +7 -1
  25. package/cpp/rnwgpu/api/GPUFeatures.h +4 -4
  26. package/cpp/rnwgpu/api/GPUShaderModule.cpp +2 -1
  27. package/cpp/rnwgpu/api/descriptors/GPUCanvasConfiguration.h +9 -0
  28. package/cpp/threading/CallInvokerDispatcher.h +37 -0
  29. package/cpp/threading/Dispatcher.cpp +54 -0
  30. package/cpp/threading/Dispatcher.h +93 -0
  31. package/cpp/threading/ThreadPool.cpp +86 -0
  32. package/cpp/threading/ThreadPool.h +53 -0
  33. package/cpp/webgpu/webgpu.h +762 -758
  34. package/cpp/webgpu/webgpu_cpp.h +1827 -1626
  35. package/cpp/webgpu/webgpu_cpp_chained_struct.h +2 -0
  36. package/lib/commonjs/hooks.js +4 -2
  37. package/lib/commonjs/hooks.js.map +1 -1
  38. package/lib/module/hooks.js +4 -2
  39. package/lib/module/hooks.js.map +1 -1
  40. package/lib/typescript/lib/commonjs/hooks.d.ts.map +1 -1
  41. package/lib/typescript/lib/module/hooks.d.ts.map +1 -1
  42. package/lib/typescript/src/__tests__/Alpha.spec.d.ts +2 -0
  43. package/lib/typescript/src/__tests__/Alpha.spec.d.ts.map +1 -0
  44. package/lib/typescript/src/hooks.d.ts.map +1 -1
  45. package/libs/android/arm64-v8a/libwebgpu_dawn.so +0 -0
  46. package/libs/android/armeabi-v7a/libwebgpu_dawn.so +0 -0
  47. package/libs/android/x86/libwebgpu_dawn.so +0 -0
  48. package/libs/android/x86_64/libwebgpu_dawn.so +0 -0
  49. package/libs/apple/arm64_iphoneos/libwebgpu_dawn.a +0 -0
  50. package/libs/apple/arm64_iphonesimulator/libwebgpu_dawn.a +0 -0
  51. package/libs/apple/arm64_xros/libwebgpu_dawn.a +0 -0
  52. package/libs/apple/arm64_xrsimulator/libwebgpu_dawn.a +0 -0
  53. package/libs/apple/iphonesimulator/libwebgpu_dawn.a +0 -0
  54. package/libs/apple/libwebgpu_dawn.xcframework/Info.plist +10 -10
  55. package/libs/apple/libwebgpu_dawn.xcframework/ios-arm64/libwebgpu_dawn.a +0 -0
  56. package/libs/apple/libwebgpu_dawn.xcframework/ios-arm64_x86_64-simulator/libwebgpu_dawn.a +0 -0
  57. package/libs/apple/libwebgpu_dawn.xcframework/macos-arm64_x86_64/libwebgpu_dawn.a +0 -0
  58. package/libs/apple/libwebgpu_dawn.xcframework/xros-arm64/libwebgpu_dawn.a +0 -0
  59. package/libs/apple/libwebgpu_dawn.xcframework/xros-arm64-simulator/libwebgpu_dawn.a +0 -0
  60. package/libs/apple/universal_macosx/libwebgpu_dawn.a +0 -0
  61. package/libs/apple/x86_64_iphonesimulator/libwebgpu_dawn.a +0 -0
  62. package/libs/dawn.json +270 -251
  63. package/package.json +2 -2
  64. package/src/__tests__/Alpha.spec.ts +28 -0
  65. package/src/__tests__/Device.spec.ts +31 -0
  66. package/src/__tests__/snapshots/semi-opaque-cyan.png +0 -0
  67. 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
- wgpu::PrimitiveDepthClipControl *depthClip =
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) &&
@@ -28,10 +28,10 @@ GPU::requestAdapter(
28
28
  wgpu::Adapter adapter = nullptr;
29
29
  _instance.RequestAdapter(
30
30
  &aOptions,
31
- [](WGPURequestAdapterStatus, WGPUAdapter cAdapter, const char *message,
32
- void *userdata) {
33
- if (message != nullptr) {
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
- char const *message, void *userdata) {
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, message);
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 char *message,
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.c_str());
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 char *message, void *userdata) {
71
- if (message != nullptr) {
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 char *message, void *userdata) {
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::AdapterProperties adapterProperties = {};
143
- _instance.GetProperties(&adapterProperties);
144
- return adapterProperties.adapterType == wgpu::AdapterType::DiscreteGPU;
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() { return _instance.vendor; }
27
- std::string getArchitecture() { return _instance.architecture; }
28
- std::string getDevice() { return _instance.device; }
29
- std::string getDescription() { return _instance.description; }
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.format, configuration->format)) {
20
- throw std::runtime_error("Error with SurfaceConfiguration");
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
- return std::make_shared<GPUShaderModule>(
80
- _instance.CreateErrorShaderModule(
81
- &sm_desc, "The WGSL shader contains an illegal character '\\0'"),
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 m_lostPromise->get_future();
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 = wgpuMessage.message ? wgpuMessage.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