From e235b3717f1e564cf0a79bd2ab3461b97b32c42b Mon Sep 17 00:00:00 2001 From: HALBY Date: Sun, 8 Mar 2026 14:49:20 +0900 Subject: [PATCH 1/5] feat: expose Dawn debug toggles via requestDevice() dawnToggles option Allow enabling/disabling Dawn debug toggles (e.g. dump_shaders, use_user_defined_labels_in_backend) from JS via requestDevice(). Added as a non-standard extension field to GPUDeviceDescriptor. Co-Authored-By: Claude Sonnet 4.6 --- packages/webgpu/cpp/rnwgpu/api/GPUAdapter.cpp | 36 +++++++++++++++++- .../descriptors/GPUDawnTogglesDescriptor.h | 37 +++++++++++++++++++ .../api/descriptors/GPUDeviceDescriptor.h | 8 ++++ packages/webgpu/src/types.ts | 19 ++++++++++ 4 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 packages/webgpu/cpp/rnwgpu/api/descriptors/GPUDawnTogglesDescriptor.h diff --git a/packages/webgpu/cpp/rnwgpu/api/GPUAdapter.cpp b/packages/webgpu/cpp/rnwgpu/api/GPUAdapter.cpp index 27bf14f8b..8e962b665 100644 --- a/packages/webgpu/cpp/rnwgpu/api/GPUAdapter.cpp +++ b/packages/webgpu/cpp/rnwgpu/api/GPUAdapter.cpp @@ -86,15 +86,45 @@ async::AsyncTaskHandle GPUAdapter::requestDevice( std::string label = descriptor.has_value() ? descriptor.value()->label.value_or("") : ""; + // Extract Dawn toggle data by value before the async lambda captures it + std::optional dawnTogglesData; + if (descriptor.has_value() && descriptor.value()->dawnToggles.has_value()) { + dawnTogglesData = *descriptor.value()->dawnToggles.value(); + } + auto creationRuntime = getCreationRuntime(); return _async->postTask( [this, aDescriptor, descriptor, label = std::move(label), - deviceLostBinding, + deviceLostBinding, dawnTogglesData = std::move(dawnTogglesData), creationRuntime](const async::AsyncTaskHandle::ResolveFunction &resolve, const async::AsyncTaskHandle::RejectFunction &reject) { (void)descriptor; + + // Build Dawn toggles chain if provided + wgpu::DawnTogglesDescriptor togglesDesc; + std::vector enablePtrs, disablePtrs; + auto localDescriptor = aDescriptor; + + if (dawnTogglesData.has_value()) { + const auto &td = *dawnTogglesData; + if (td.enable.has_value() && !td.enable->empty()) { + for (const auto &s : *td.enable) enablePtrs.push_back(s.c_str()); + togglesDesc.enabledToggles = enablePtrs.data(); + togglesDesc.enabledTogglesCount = enablePtrs.size(); + } + if (td.disable.has_value() && !td.disable->empty()) { + for (const auto &s : *td.disable) disablePtrs.push_back(s.c_str()); + togglesDesc.disabledToggles = disablePtrs.data(); + togglesDesc.disabledTogglesCount = disablePtrs.size(); + } + if (!enablePtrs.empty() || !disablePtrs.empty()) { + togglesDesc.nextInChain = localDescriptor.nextInChain; + localDescriptor.nextInChain = &togglesDesc; + } + } + _instance.RequestDevice( - &aDescriptor, wgpu::CallbackMode::AllowProcessEvents, + &localDescriptor, wgpu::CallbackMode::AllowProcessEvents, [asyncRunner = _async, resolve, reject, label, creationRuntime, deviceLostBinding](wgpu::RequestDeviceStatus status, wgpu::Device device, @@ -131,6 +161,8 @@ async::AsyncTaskHandle GPUAdapter::requestDevice( break; case wgpu::LoggingType::Verbose: logLevel = "Verbose"; + Logger::logToConsole("%s: %.*s", logLevel, + static_cast(msg.length), msg.data); break; case wgpu::LoggingType::Info: logLevel = "Info"; diff --git a/packages/webgpu/cpp/rnwgpu/api/descriptors/GPUDawnTogglesDescriptor.h b/packages/webgpu/cpp/rnwgpu/api/descriptors/GPUDawnTogglesDescriptor.h new file mode 100644 index 000000000..ff5d0a282 --- /dev/null +++ b/packages/webgpu/cpp/rnwgpu/api/descriptors/GPUDawnTogglesDescriptor.h @@ -0,0 +1,37 @@ +#pragma once +#include +#include +#include +#include "JSIConverter.h" + +namespace rnwgpu { + +struct GPUDawnTogglesDescriptor { + std::optional> enable; + std::optional> disable; +}; + +template <> +struct JSIConverter> { + static std::shared_ptr + fromJSI(jsi::Runtime &runtime, const jsi::Value &arg, bool outOfBounds) { + auto result = std::make_shared(); + if (outOfBounds || arg.isNull() || arg.isUndefined()) return result; + auto obj = arg.asObject(runtime); + if (obj.hasProperty(runtime, "enable")) { + result->enable = JSIConverter>::fromJSI( + runtime, obj.getProperty(runtime, "enable"), false); + } + if (obj.hasProperty(runtime, "disable")) { + result->disable = JSIConverter>::fromJSI( + runtime, obj.getProperty(runtime, "disable"), false); + } + return result; + } + static jsi::Value toJSI(jsi::Runtime &, + std::shared_ptr) { + throw std::runtime_error("GPUDawnTogglesDescriptor: toJSI not supported"); + } +}; + +} // namespace rnwgpu diff --git a/packages/webgpu/cpp/rnwgpu/api/descriptors/GPUDeviceDescriptor.h b/packages/webgpu/cpp/rnwgpu/api/descriptors/GPUDeviceDescriptor.h index 5ec068b48..49bb1a0b7 100644 --- a/packages/webgpu/cpp/rnwgpu/api/descriptors/GPUDeviceDescriptor.h +++ b/packages/webgpu/cpp/rnwgpu/api/descriptors/GPUDeviceDescriptor.h @@ -11,6 +11,7 @@ #include "WGPULogger.h" #include "GPUQueueDescriptor.h" +#include "GPUDawnTogglesDescriptor.h" namespace jsi = facebook::jsi; @@ -24,6 +25,7 @@ struct GPUDeviceDescriptor { std::optional> defaultQueue; // GPUQueueDescriptor std::optional label; // string + std::optional> dawnToggles; }; } // namespace rnwgpu @@ -87,6 +89,12 @@ template <> struct JSIConverter> { result->label = JSIConverter>::fromJSI( runtime, prop, false); } + if (value.hasProperty(runtime, "dawnToggles")) { + auto prop = value.getProperty(runtime, "dawnToggles"); + result->dawnToggles = + JSIConverter>>:: + fromJSI(runtime, prop, false); + } } return result; diff --git a/packages/webgpu/src/types.ts b/packages/webgpu/src/types.ts index af4684cfa..3153863d9 100644 --- a/packages/webgpu/src/types.ts +++ b/packages/webgpu/src/types.ts @@ -1,5 +1,24 @@ type SurfacePointer = bigint; +/** Dawn固有のデバッグトグル設定 (WebGPU仕様外) */ +export interface DawnTogglesDescriptor { + /** + * 有効化するトグル名の配列 + * 例: 'dump_shaders', 'use_user_defined_labels_in_backend', + * 'disable_symbol_renaming', 'emit_hlsl_debug_symbols' + */ + enable?: string[]; + /** 無効化するトグル名の配列 */ + disable?: string[]; +} + +declare global { + interface GPUDeviceDescriptor { + /** Dawn固有のトグル設定。非Dawn環境では無視される。 */ + dawnToggles?: DawnTogglesDescriptor; + } +} + export interface NativeCanvas { surface: SurfacePointer; width: number; From 1a06e1986c290c2981cf9da99d753941b76d1445 Mon Sep 17 00:00:00 2001 From: HALBY Date: Sun, 8 Mar 2026 16:56:50 +0900 Subject: [PATCH 2/5] test: add dawnToggles tests and fix Dawn API naming MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add dawnToggles option tests to Device.spec.ts (covers enable/disable/both/empty cases) - Fix Dawn API naming in GPUAdapter.cpp: enabledTogglesCount → enabledToggleCount disabledTogglesCount → disabledToggleCount Co-Authored-By: Claude Sonnet 4.6 --- packages/webgpu/cpp/rnwgpu/api/GPUAdapter.cpp | 4 +- packages/webgpu/src/__tests__/Device.spec.ts | 55 +++++++++++++++++++ packages/webgpu/src/types.ts | 12 ++-- 3 files changed, 63 insertions(+), 8 deletions(-) diff --git a/packages/webgpu/cpp/rnwgpu/api/GPUAdapter.cpp b/packages/webgpu/cpp/rnwgpu/api/GPUAdapter.cpp index 8e962b665..27821f0ee 100644 --- a/packages/webgpu/cpp/rnwgpu/api/GPUAdapter.cpp +++ b/packages/webgpu/cpp/rnwgpu/api/GPUAdapter.cpp @@ -110,12 +110,12 @@ async::AsyncTaskHandle GPUAdapter::requestDevice( if (td.enable.has_value() && !td.enable->empty()) { for (const auto &s : *td.enable) enablePtrs.push_back(s.c_str()); togglesDesc.enabledToggles = enablePtrs.data(); - togglesDesc.enabledTogglesCount = enablePtrs.size(); + togglesDesc.enabledToggleCount = enablePtrs.size(); } if (td.disable.has_value() && !td.disable->empty()) { for (const auto &s : *td.disable) disablePtrs.push_back(s.c_str()); togglesDesc.disabledToggles = disablePtrs.data(); - togglesDesc.disabledTogglesCount = disablePtrs.size(); + togglesDesc.disabledToggleCount = disablePtrs.size(); } if (!enablePtrs.empty() || !disablePtrs.empty()) { togglesDesc.nextInChain = localDescriptor.nextInChain; diff --git a/packages/webgpu/src/__tests__/Device.spec.ts b/packages/webgpu/src/__tests__/Device.spec.ts index d02dd57b7..dc1beb2e9 100644 --- a/packages/webgpu/src/__tests__/Device.spec.ts +++ b/packages/webgpu/src/__tests__/Device.spec.ts @@ -191,6 +191,61 @@ describe("Device", () => { expect(result).toBe(true); }); + describe("dawnToggles", () => { + it("should request device with enable toggles", async () => { + const result = await client.eval(({ gpu }) => + gpu.requestAdapter().then((adapter) => + adapter! + .requestDevice({ + dawnToggles: { enable: ["use_user_defined_labels_in_backend"] }, + }) + .then((device) => device !== null), + ), + ); + expect(result).toBe(true); + }); + + it("should request device with disable toggles", async () => { + const result = await client.eval(({ gpu }) => + gpu.requestAdapter().then((adapter) => + adapter! + .requestDevice({ + dawnToggles: { disable: ["use_user_defined_labels_in_backend"] }, + }) + .then((device) => device !== null), + ), + ); + expect(result).toBe(true); + }); + + it("should request device with both enable and disable toggles", async () => { + const result = await client.eval(({ gpu }) => + gpu.requestAdapter().then((adapter) => + adapter! + .requestDevice({ + dawnToggles: { + enable: ["use_user_defined_labels_in_backend"], + disable: ["disable_symbol_renaming"], + }, + }) + .then((device) => device !== null), + ), + ); + expect(result).toBe(true); + }); + + it("should request device with empty dawnToggles", async () => { + const result = await client.eval(({ gpu }) => + gpu.requestAdapter().then((adapter) => + adapter! + .requestDevice({ dawnToggles: {} }) + .then((device) => device !== null), + ), + ); + expect(result).toBe(true); + }); + }); + it("should receive uncapturederror event when validation error occurs", async () => { const result = await client.eval(({ gpu }) => gpu.requestAdapter().then((adapter) => diff --git a/packages/webgpu/src/types.ts b/packages/webgpu/src/types.ts index 3153863d9..15444fd13 100644 --- a/packages/webgpu/src/types.ts +++ b/packages/webgpu/src/types.ts @@ -1,20 +1,20 @@ type SurfacePointer = bigint; -/** Dawn固有のデバッグトグル設定 (WebGPU仕様外) */ +/** Dawn-specific debug toggle configuration (non-standard WebGPU extension) */ export interface DawnTogglesDescriptor { /** - * 有効化するトグル名の配列 - * 例: 'dump_shaders', 'use_user_defined_labels_in_backend', - * 'disable_symbol_renaming', 'emit_hlsl_debug_symbols' + * List of toggle names to enable. + * e.g. 'dump_shaders', 'use_user_defined_labels_in_backend', + * 'disable_symbol_renaming', 'emit_hlsl_debug_symbols' */ enable?: string[]; - /** 無効化するトグル名の配列 */ + /** List of toggle names to disable. */ disable?: string[]; } declare global { interface GPUDeviceDescriptor { - /** Dawn固有のトグル設定。非Dawn環境では無視される。 */ + /** Dawn-specific toggles. Ignored in non-Dawn environments. */ dawnToggles?: DawnTogglesDescriptor; } } From 8ecd4d156face7e4ac0c01f86160b304aee7e9fd Mon Sep 17 00:00:00 2001 From: HALBY Date: Sun, 8 Mar 2026 19:02:06 +0900 Subject: [PATCH 3/5] refactor: move dawnToggles from requestDevice() to installWebGPU() options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dawn toggles are now specified at the Instance level via installWebGPU(), matching node-webgpu's create() design. This avoids polluting the standard GPUDeviceDescriptor type with a non-standard Dawn-specific field. - Add RNWebGPUInstallOptions / DawnTogglesDescriptor types (exported) - Export installWebGPU(options?) from the package entry point - Remove declare global extension of GPUDeviceDescriptor - Remove GPUDawnTogglesDescriptor.h and dawnToggles from GPUDeviceDescriptor - Pass toggles through install() → RNWebGPUManager → GPU constructor - Apply DawnTogglesDescriptor to both CreateInstance and RequestAdapter - Remove dawnToggles handling from GPUAdapter::requestDevice() - Update iOS/Android native install() to accept and parse options - Web entry point exports a no-op installWebGPU() that ignores toggles BREAKING CHANGE: requestDevice({ dawnToggles }) is no longer supported. Use installWebGPU({ dawnToggles }) instead. Co-Authored-By: Claude Sonnet 4.6 --- packages/webgpu/android/cpp/cpp-adapter.cpp | 33 ++++++++- .../main/java/com/webgpu/WebGPUModule.java | 47 +++++++++++-- packages/webgpu/apple/WebGPUModule.mm | 32 ++++++++- .../webgpu/cpp/rnwgpu/RNWebGPUManager.cpp | 8 ++- packages/webgpu/cpp/rnwgpu/RNWebGPUManager.h | 6 +- packages/webgpu/cpp/rnwgpu/api/GPU.cpp | 49 +++++++++++-- packages/webgpu/cpp/rnwgpu/api/GPU.h | 7 +- packages/webgpu/cpp/rnwgpu/api/GPUAdapter.cpp | 33 +-------- .../descriptors/GPUDawnTogglesDescriptor.h | 37 ---------- .../api/descriptors/GPUDeviceDescriptor.h | 8 --- packages/webgpu/src/NativeWebGPUModule.ts | 2 +- packages/webgpu/src/__tests__/Device.spec.ts | 55 --------------- packages/webgpu/src/main/index.tsx | 68 ++++++++++++------- packages/webgpu/src/main/index.web.tsx | 8 +++ packages/webgpu/src/types.ts | 9 ++- 15 files changed, 220 insertions(+), 182 deletions(-) delete mode 100644 packages/webgpu/cpp/rnwgpu/api/descriptors/GPUDawnTogglesDescriptor.h diff --git a/packages/webgpu/android/cpp/cpp-adapter.cpp b/packages/webgpu/android/cpp/cpp-adapter.cpp index 2a441c218..18c05cd5f 100644 --- a/packages/webgpu/android/cpp/cpp-adapter.cpp +++ b/packages/webgpu/android/cpp/cpp-adapter.cpp @@ -19,7 +19,8 @@ std::shared_ptr manager; extern "C" JNIEXPORT void JNICALL Java_com_webgpu_WebGPUModule_initializeNative( JNIEnv *env, jobject /* this */, jlong jsRuntime, - jobject jsCallInvokerHolder, jobject blobModule) { + jobject jsCallInvokerHolder, jobject blobModule, + jobjectArray enableToggles, jobjectArray disableToggles) { auto runtime = reinterpret_cast(jsRuntime); jobject globalBlobModule = env->NewGlobalRef(blobModule); auto jsCallInvoker{ @@ -29,8 +30,34 @@ extern "C" JNIEXPORT void JNICALL Java_com_webgpu_WebGPUModule_initializeNative( ->getCallInvoker()}; auto platformContext = std::make_shared(globalBlobModule); - manager = std::make_shared(runtime, jsCallInvoker, - platformContext); + + // Convert Java string arrays to std::vector + std::vector enableVec; + std::vector disableVec; + if (enableToggles != nullptr) { + jsize len = env->GetArrayLength(enableToggles); + for (jsize i = 0; i < len; i++) { + auto jstr = (jstring)env->GetObjectArrayElement(enableToggles, i); + const char *cstr = env->GetStringUTFChars(jstr, nullptr); + enableVec.emplace_back(cstr); + env->ReleaseStringUTFChars(jstr, cstr); + env->DeleteLocalRef(jstr); + } + } + if (disableToggles != nullptr) { + jsize len = env->GetArrayLength(disableToggles); + for (jsize i = 0; i < len; i++) { + auto jstr = (jstring)env->GetObjectArrayElement(disableToggles, i); + const char *cstr = env->GetStringUTFChars(jstr, nullptr); + disableVec.emplace_back(cstr); + env->ReleaseStringUTFChars(jstr, cstr); + env->DeleteLocalRef(jstr); + } + } + + manager = std::make_shared( + runtime, jsCallInvoker, platformContext, + std::move(enableVec), std::move(disableVec)); } extern "C" JNIEXPORT void JNICALL Java_com_webgpu_WebGPUView_onSurfaceChanged( diff --git a/packages/webgpu/android/src/main/java/com/webgpu/WebGPUModule.java b/packages/webgpu/android/src/main/java/com/webgpu/WebGPUModule.java index 75375700e..82b0bcd1a 100644 --- a/packages/webgpu/android/src/main/java/com/webgpu/WebGPUModule.java +++ b/packages/webgpu/android/src/main/java/com/webgpu/WebGPUModule.java @@ -2,19 +2,22 @@ import android.util.Log; +import androidx.annotation.Nullable; import androidx.annotation.OptIn; -import java.util.HashSet; -import java.util.Set; +import java.util.ArrayList; +import java.util.List; import com.facebook.proguard.annotations.DoNotStrip; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.JavaScriptContextHolder; +import com.facebook.react.bridge.ReactMap; import com.facebook.react.bridge.ReactMethod; +import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.bridge.ReadableMap; import com.facebook.react.common.annotations.FrameworkAPI; import com.facebook.react.module.annotations.ReactModule; import com.facebook.react.modules.blob.BlobModule; -import com.facebook.react.modules.blob.BlobProvider; import com.facebook.react.turbomodule.core.CallInvokerHolderImpl; import com.facebook.react.turbomodule.core.interfaces.CallInvokerHolder; @@ -32,7 +35,7 @@ public WebGPUModule(ReactApplicationContext reactContext) { @OptIn(markerClass = FrameworkAPI.class) @ReactMethod(isBlockingSynchronousMethod = true) - public boolean install() { + public boolean install(@Nullable ReadableMap options) { ReactApplicationContext context = getReactApplicationContext(); JavaScriptContextHolder jsContext = context.getJavaScriptContextHolder(); CallInvokerHolder callInvokerHolder = context.getCatalystInstance().getJSCallInvokerHolder(); @@ -40,11 +43,43 @@ public boolean install() { if (blobModule == null) { throw new RuntimeException("React Native's BlobModule was not found!"); } - initializeNative(jsContext.get(), (CallInvokerHolderImpl) callInvokerHolder, blobModule); + + // Extract dawnToggles from options + String[] enableToggles = new String[0]; + String[] disableToggles = new String[0]; + if (options != null && options.hasKey("dawnToggles")) { + ReadableMap dawnToggles = options.getMap("dawnToggles"); + if (dawnToggles != null) { + if (dawnToggles.hasKey("enable")) { + ReadableArray enableArray = dawnToggles.getArray("enable"); + if (enableArray != null) { + List list = new ArrayList<>(); + for (int i = 0; i < enableArray.size(); i++) { + list.add(enableArray.getString(i)); + } + enableToggles = list.toArray(new String[0]); + } + } + if (dawnToggles.hasKey("disable")) { + ReadableArray disableArray = dawnToggles.getArray("disable"); + if (disableArray != null) { + List list = new ArrayList<>(); + for (int i = 0; i < disableArray.size(); i++) { + list.add(disableArray.getString(i)); + } + disableToggles = list.toArray(new String[0]); + } + } + } + } + + initializeNative(jsContext.get(), (CallInvokerHolderImpl) callInvokerHolder, blobModule, + enableToggles, disableToggles); return true; } @OptIn(markerClass = FrameworkAPI.class) @DoNotStrip - private native void initializeNative(long jsRuntime, CallInvokerHolderImpl jsInvoker, BlobModule blobModule); + private native void initializeNative(long jsRuntime, CallInvokerHolderImpl jsInvoker, + BlobModule blobModule, String[] enableToggles, String[] disableToggles); } diff --git a/packages/webgpu/apple/WebGPUModule.mm b/packages/webgpu/apple/WebGPUModule.mm index d3ca8c392..cc76ada1d 100644 --- a/packages/webgpu/apple/WebGPUModule.mm +++ b/packages/webgpu/apple/WebGPUModule.mm @@ -49,7 +49,7 @@ - (void)invalidate { return webgpuManager; } -RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(install) { +RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(install:(NSDictionary* __nullable)options) { if (webgpuManager != nil) { // Already initialized, ignore call. return @true; @@ -72,10 +72,36 @@ - (void)invalidate { return [NSNumber numberWithBool:NO]; } + // Extract dawnToggles from options + std::vector enableToggles; + std::vector disableToggles; + if (options != nil) { + NSDictionary *dawnToggles = options[@"dawnToggles"]; + if ([dawnToggles isKindOfClass:[NSDictionary class]]) { + NSArray *enable = dawnToggles[@"enable"]; + if ([enable isKindOfClass:[NSArray class]]) { + for (NSString *toggle in enable) { + if ([toggle isKindOfClass:[NSString class]]) { + enableToggles.push_back(toggle.UTF8String); + } + } + } + NSArray *disable = dawnToggles[@"disable"]; + if ([disable isKindOfClass:[NSArray class]]) { + for (NSString *toggle in disable) { + if ([toggle isKindOfClass:[NSString class]]) { + disableToggles.push_back(toggle.UTF8String); + } + } + } + } + } + std::shared_ptr platformContext = std::make_shared(); - webgpuManager = std::make_shared(runtime, jsInvoker, - platformContext); + webgpuManager = std::make_shared( + runtime, jsInvoker, platformContext, + std::move(enableToggles), std::move(disableToggles)); return @true; } diff --git a/packages/webgpu/cpp/rnwgpu/RNWebGPUManager.cpp b/packages/webgpu/cpp/rnwgpu/RNWebGPUManager.cpp index 5a2decc09..914a7e75a 100644 --- a/packages/webgpu/cpp/rnwgpu/RNWebGPUManager.cpp +++ b/packages/webgpu/cpp/rnwgpu/RNWebGPUManager.cpp @@ -53,14 +53,18 @@ namespace rnwgpu { RNWebGPUManager::RNWebGPUManager( jsi::Runtime *jsRuntime, std::shared_ptr jsCallInvoker, - std::shared_ptr platformContext) + std::shared_ptr platformContext, + std::vector enableToggles, + std::vector disableToggles) : _jsRuntime(jsRuntime), _jsCallInvoker(jsCallInvoker), _platformContext(platformContext) { // Register main runtime for RuntimeAwareCache BaseRuntimeAwareCache::setMainJsRuntime(_jsRuntime); - auto gpu = std::make_shared(*_jsRuntime); + auto gpu = std::make_shared(*_jsRuntime, + std::move(enableToggles), + std::move(disableToggles)); auto rnWebGPU = std::make_shared(gpu, _platformContext, _jsCallInvoker); _gpu = gpu->get(); diff --git a/packages/webgpu/cpp/rnwgpu/RNWebGPUManager.h b/packages/webgpu/cpp/rnwgpu/RNWebGPUManager.h index 2043c9658..9bb7603bc 100644 --- a/packages/webgpu/cpp/rnwgpu/RNWebGPUManager.h +++ b/packages/webgpu/cpp/rnwgpu/RNWebGPUManager.h @@ -1,6 +1,8 @@ #pragma once #include +#include +#include #include "GPU.h" #include "PlatformContext.h" @@ -24,7 +26,9 @@ class RNWebGPUManager { public: RNWebGPUManager(jsi::Runtime *jsRuntime, std::shared_ptr jsCallInvoker, - std::shared_ptr platformContext); + std::shared_ptr platformContext, + std::vector enableToggles = {}, + std::vector disableToggles = {}); ~RNWebGPUManager(); /** diff --git a/packages/webgpu/cpp/rnwgpu/api/GPU.cpp b/packages/webgpu/cpp/rnwgpu/api/GPU.cpp index 764a9aa32..c208eadc4 100644 --- a/packages/webgpu/cpp/rnwgpu/api/GPU.cpp +++ b/packages/webgpu/cpp/rnwgpu/api/GPU.cpp @@ -13,13 +13,33 @@ namespace rnwgpu { -GPU::GPU(jsi::Runtime &runtime) : NativeObject(CLASS_NAME) { +GPU::GPU(jsi::Runtime &runtime, + std::vector enableToggles, + std::vector disableToggles) + : NativeObject(CLASS_NAME), + _enableToggles(std::move(enableToggles)), + _disableToggles(std::move(disableToggles)) { static const auto kTimedWaitAny = wgpu::InstanceFeatureName::TimedWaitAny; wgpu::InstanceDescriptor instanceDesc{.requiredFeatureCount = 1, .requiredFeatures = &kTimedWaitAny}; wgpu::InstanceLimits limits{.timedWaitAnyMaxCount = 64}; instanceDesc.requiredLimits = &limits; + + // Build Dawn toggles descriptor and chain it if any toggles are specified + std::vector enablePtrs, disablePtrs; + wgpu::DawnTogglesDescriptor togglesDesc; + if (!_enableToggles.empty() || !_disableToggles.empty()) { + for (const auto &s : _enableToggles) enablePtrs.push_back(s.c_str()); + for (const auto &s : _disableToggles) disablePtrs.push_back(s.c_str()); + togglesDesc.enabledToggles = enablePtrs.empty() ? nullptr : enablePtrs.data(); + togglesDesc.enabledToggleCount = enablePtrs.size(); + togglesDesc.disabledToggles = disablePtrs.empty() ? nullptr : disablePtrs.data(); + togglesDesc.disabledToggleCount = disablePtrs.size(); + togglesDesc.nextInChain = instanceDesc.nextInChain; + instanceDesc.nextInChain = &togglesDesc; + } + _instance = wgpu::CreateInstance(&instanceDesc); auto dispatcher = std::make_shared(runtime); @@ -39,11 +59,32 @@ async::AsyncTaskHandle GPU::requestAdapter( constexpr auto kDefaultBackendType = wgpu::BackendType::Vulkan; #endif aOptions.backendType = kDefaultBackendType; + + // Capture toggle strings by value so the lambda owns them + auto enableToggles = _enableToggles; + auto disableToggles = _disableToggles; + return _async->postTask( - [this, aOptions](const async::AsyncTaskHandle::ResolveFunction &resolve, - const async::AsyncTaskHandle::RejectFunction &reject) { + [this, aOptions, enableToggles = std::move(enableToggles), + disableToggles = std::move(disableToggles)]( + const async::AsyncTaskHandle::ResolveFunction &resolve, + const async::AsyncTaskHandle::RejectFunction &reject) { + // Build Dawn toggles chain inside the task so pointers remain valid + std::vector enablePtrs, disablePtrs; + wgpu::DawnTogglesDescriptor togglesDesc; + auto localOptions = aOptions; + if (!enableToggles.empty() || !disableToggles.empty()) { + for (const auto &s : enableToggles) enablePtrs.push_back(s.c_str()); + for (const auto &s : disableToggles) disablePtrs.push_back(s.c_str()); + togglesDesc.enabledToggles = enablePtrs.empty() ? nullptr : enablePtrs.data(); + togglesDesc.enabledToggleCount = enablePtrs.size(); + togglesDesc.disabledToggles = disablePtrs.empty() ? nullptr : disablePtrs.data(); + togglesDesc.disabledToggleCount = disablePtrs.size(); + togglesDesc.nextInChain = localOptions.nextInChain; + localOptions.nextInChain = &togglesDesc; + } _instance.RequestAdapter( - &aOptions, wgpu::CallbackMode::AllowProcessEvents, + &localOptions, wgpu::CallbackMode::AllowProcessEvents, [asyncRunner = _async, resolve, reject](wgpu::RequestAdapterStatus status, wgpu::Adapter adapter, wgpu::StringView message) { diff --git a/packages/webgpu/cpp/rnwgpu/api/GPU.h b/packages/webgpu/cpp/rnwgpu/api/GPU.h index f6bb4ede3..5093c7e40 100644 --- a/packages/webgpu/cpp/rnwgpu/api/GPU.h +++ b/packages/webgpu/cpp/rnwgpu/api/GPU.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "Unions.h" @@ -27,7 +28,9 @@ class GPU : public NativeObject { public: static constexpr const char *CLASS_NAME = "GPU"; - explicit GPU(jsi::Runtime &runtime); + explicit GPU(jsi::Runtime &runtime, + std::vector enableToggles = {}, + std::vector disableToggles = {}); public: std::string getBrand() { return CLASS_NAME; } @@ -53,6 +56,8 @@ class GPU : public NativeObject { private: wgpu::Instance _instance; std::shared_ptr _async; + std::vector _enableToggles; + std::vector _disableToggles; }; } // namespace rnwgpu diff --git a/packages/webgpu/cpp/rnwgpu/api/GPUAdapter.cpp b/packages/webgpu/cpp/rnwgpu/api/GPUAdapter.cpp index 27821f0ee..c4cdbdf47 100644 --- a/packages/webgpu/cpp/rnwgpu/api/GPUAdapter.cpp +++ b/packages/webgpu/cpp/rnwgpu/api/GPUAdapter.cpp @@ -86,45 +86,16 @@ async::AsyncTaskHandle GPUAdapter::requestDevice( std::string label = descriptor.has_value() ? descriptor.value()->label.value_or("") : ""; - // Extract Dawn toggle data by value before the async lambda captures it - std::optional dawnTogglesData; - if (descriptor.has_value() && descriptor.value()->dawnToggles.has_value()) { - dawnTogglesData = *descriptor.value()->dawnToggles.value(); - } - auto creationRuntime = getCreationRuntime(); return _async->postTask( [this, aDescriptor, descriptor, label = std::move(label), - deviceLostBinding, dawnTogglesData = std::move(dawnTogglesData), + deviceLostBinding, creationRuntime](const async::AsyncTaskHandle::ResolveFunction &resolve, const async::AsyncTaskHandle::RejectFunction &reject) { (void)descriptor; - // Build Dawn toggles chain if provided - wgpu::DawnTogglesDescriptor togglesDesc; - std::vector enablePtrs, disablePtrs; - auto localDescriptor = aDescriptor; - - if (dawnTogglesData.has_value()) { - const auto &td = *dawnTogglesData; - if (td.enable.has_value() && !td.enable->empty()) { - for (const auto &s : *td.enable) enablePtrs.push_back(s.c_str()); - togglesDesc.enabledToggles = enablePtrs.data(); - togglesDesc.enabledToggleCount = enablePtrs.size(); - } - if (td.disable.has_value() && !td.disable->empty()) { - for (const auto &s : *td.disable) disablePtrs.push_back(s.c_str()); - togglesDesc.disabledToggles = disablePtrs.data(); - togglesDesc.disabledToggleCount = disablePtrs.size(); - } - if (!enablePtrs.empty() || !disablePtrs.empty()) { - togglesDesc.nextInChain = localDescriptor.nextInChain; - localDescriptor.nextInChain = &togglesDesc; - } - } - _instance.RequestDevice( - &localDescriptor, wgpu::CallbackMode::AllowProcessEvents, + &aDescriptor, wgpu::CallbackMode::AllowProcessEvents, [asyncRunner = _async, resolve, reject, label, creationRuntime, deviceLostBinding](wgpu::RequestDeviceStatus status, wgpu::Device device, diff --git a/packages/webgpu/cpp/rnwgpu/api/descriptors/GPUDawnTogglesDescriptor.h b/packages/webgpu/cpp/rnwgpu/api/descriptors/GPUDawnTogglesDescriptor.h deleted file mode 100644 index ff5d0a282..000000000 --- a/packages/webgpu/cpp/rnwgpu/api/descriptors/GPUDawnTogglesDescriptor.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once -#include -#include -#include -#include "JSIConverter.h" - -namespace rnwgpu { - -struct GPUDawnTogglesDescriptor { - std::optional> enable; - std::optional> disable; -}; - -template <> -struct JSIConverter> { - static std::shared_ptr - fromJSI(jsi::Runtime &runtime, const jsi::Value &arg, bool outOfBounds) { - auto result = std::make_shared(); - if (outOfBounds || arg.isNull() || arg.isUndefined()) return result; - auto obj = arg.asObject(runtime); - if (obj.hasProperty(runtime, "enable")) { - result->enable = JSIConverter>::fromJSI( - runtime, obj.getProperty(runtime, "enable"), false); - } - if (obj.hasProperty(runtime, "disable")) { - result->disable = JSIConverter>::fromJSI( - runtime, obj.getProperty(runtime, "disable"), false); - } - return result; - } - static jsi::Value toJSI(jsi::Runtime &, - std::shared_ptr) { - throw std::runtime_error("GPUDawnTogglesDescriptor: toJSI not supported"); - } -}; - -} // namespace rnwgpu diff --git a/packages/webgpu/cpp/rnwgpu/api/descriptors/GPUDeviceDescriptor.h b/packages/webgpu/cpp/rnwgpu/api/descriptors/GPUDeviceDescriptor.h index 49bb1a0b7..5ec068b48 100644 --- a/packages/webgpu/cpp/rnwgpu/api/descriptors/GPUDeviceDescriptor.h +++ b/packages/webgpu/cpp/rnwgpu/api/descriptors/GPUDeviceDescriptor.h @@ -11,7 +11,6 @@ #include "WGPULogger.h" #include "GPUQueueDescriptor.h" -#include "GPUDawnTogglesDescriptor.h" namespace jsi = facebook::jsi; @@ -25,7 +24,6 @@ struct GPUDeviceDescriptor { std::optional> defaultQueue; // GPUQueueDescriptor std::optional label; // string - std::optional> dawnToggles; }; } // namespace rnwgpu @@ -89,12 +87,6 @@ template <> struct JSIConverter> { result->label = JSIConverter>::fromJSI( runtime, prop, false); } - if (value.hasProperty(runtime, "dawnToggles")) { - auto prop = value.getProperty(runtime, "dawnToggles"); - result->dawnToggles = - JSIConverter>>:: - fromJSI(runtime, prop, false); - } } return result; diff --git a/packages/webgpu/src/NativeWebGPUModule.ts b/packages/webgpu/src/NativeWebGPUModule.ts index 79305482f..f41720e97 100644 --- a/packages/webgpu/src/NativeWebGPUModule.ts +++ b/packages/webgpu/src/NativeWebGPUModule.ts @@ -2,7 +2,7 @@ import { TurboModuleRegistry } from "react-native"; import type { TurboModule } from "react-native"; export interface Spec extends TurboModule { - install: () => boolean; + install: (options?: { [key: string]: unknown } | null) => boolean; } // eslint-disable-next-line import/no-default-export diff --git a/packages/webgpu/src/__tests__/Device.spec.ts b/packages/webgpu/src/__tests__/Device.spec.ts index dc1beb2e9..d02dd57b7 100644 --- a/packages/webgpu/src/__tests__/Device.spec.ts +++ b/packages/webgpu/src/__tests__/Device.spec.ts @@ -191,61 +191,6 @@ describe("Device", () => { expect(result).toBe(true); }); - describe("dawnToggles", () => { - it("should request device with enable toggles", async () => { - const result = await client.eval(({ gpu }) => - gpu.requestAdapter().then((adapter) => - adapter! - .requestDevice({ - dawnToggles: { enable: ["use_user_defined_labels_in_backend"] }, - }) - .then((device) => device !== null), - ), - ); - expect(result).toBe(true); - }); - - it("should request device with disable toggles", async () => { - const result = await client.eval(({ gpu }) => - gpu.requestAdapter().then((adapter) => - adapter! - .requestDevice({ - dawnToggles: { disable: ["use_user_defined_labels_in_backend"] }, - }) - .then((device) => device !== null), - ), - ); - expect(result).toBe(true); - }); - - it("should request device with both enable and disable toggles", async () => { - const result = await client.eval(({ gpu }) => - gpu.requestAdapter().then((adapter) => - adapter! - .requestDevice({ - dawnToggles: { - enable: ["use_user_defined_labels_in_backend"], - disable: ["disable_symbol_renaming"], - }, - }) - .then((device) => device !== null), - ), - ); - expect(result).toBe(true); - }); - - it("should request device with empty dawnToggles", async () => { - const result = await client.eval(({ gpu }) => - gpu.requestAdapter().then((adapter) => - adapter! - .requestDevice({ dawnToggles: {} }) - .then((device) => device !== null), - ), - ); - expect(result).toBe(true); - }); - }); - it("should receive uncapturederror event when validation error occurs", async () => { const result = await client.eval(({ gpu }) => gpu.requestAdapter().then((adapter) => diff --git a/packages/webgpu/src/main/index.tsx b/packages/webgpu/src/main/index.tsx index df21bd780..49d4d2268 100644 --- a/packages/webgpu/src/main/index.tsx +++ b/packages/webgpu/src/main/index.tsx @@ -1,40 +1,58 @@ import { registerWebGPUForReanimated } from "../external"; import WebGPUModule from "../NativeWebGPUModule"; +import type { RNWebGPUInstallOptions } from "../types"; export * from "../Canvas"; export * from "../Offscreen"; export * from "../WebGPUViewNativeComponent"; export * from "../hooks"; +export type { RNWebGPUInstallOptions, DawnTogglesDescriptor } from "../types"; export { default as WebGPUModule } from "../NativeWebGPUModule"; -const _installOk = WebGPUModule.install(); - -registerWebGPUForReanimated(); - -if (typeof RNWebGPU !== "undefined") { - if (!navigator) { - // @ts-expect-error Navigation object is more complex than this, setting it to an empty object to add gpu property - navigator = { - gpu: RNWebGPU.gpu, - userAgent: "react-native", - }; - } else { - navigator.gpu = RNWebGPU.gpu; - if (typeof navigator.userAgent !== "string") { - try { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - Hermes navigator may not include a userAgent, align with the polyfill if needed - navigator.userAgent = "react-native"; - } catch { - // navigator.userAgent can be read-only; ignore if assignment fails +function setupNavigatorGpu(_installOk: boolean) { + if (typeof RNWebGPU !== "undefined") { + if (!navigator) { + // @ts-expect-error Navigation object is more complex than this, setting it to an empty object to add gpu property + navigator = { + gpu: RNWebGPU.gpu, + userAgent: "react-native", + }; + } else { + navigator.gpu = RNWebGPU.gpu; + if (typeof navigator.userAgent !== "string") { + try { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore - Hermes navigator may not include a userAgent, align with the polyfill if needed + navigator.userAgent = "react-native"; + } catch { + // navigator.userAgent can be read-only; ignore if assignment fails + } } } + global.createImageBitmap = + global.createImageBitmap ?? RNWebGPU.createImageBitmap.bind(RNWebGPU); + } else { + console.warn( + `[react-native-wgpu] install() returned ${_installOk} but RNWebGPU global is not available`, + ); } - global.createImageBitmap = - global.createImageBitmap ?? RNWebGPU.createImageBitmap.bind(RNWebGPU); -} else { - console.warn( - `[react-native-wgpu] install() returned ${_installOk} but RNWebGPU global is not available`, +} + +/** + * Explicitly install react-native-webgpu with optional Dawn toggles. + * Toggles are applied at the Instance level (equivalent to node-webgpu's create() flags). + * On web, dawnToggles are silently ignored. + */ +export function installWebGPU(options?: RNWebGPUInstallOptions): void { + const _installOk = WebGPUModule.install( + (options as { [key: string]: unknown }) ?? null, ); + setupNavigatorGpu(_installOk); } + +// Auto-install on module load (backward compatible, no toggles) +const _installOk = WebGPUModule.install(null); +setupNavigatorGpu(_installOk); + +registerWebGPUForReanimated(); diff --git a/packages/webgpu/src/main/index.web.tsx b/packages/webgpu/src/main/index.web.tsx index b848bbc69..1a7443154 100644 --- a/packages/webgpu/src/main/index.web.tsx +++ b/packages/webgpu/src/main/index.web.tsx @@ -4,5 +4,13 @@ export * from "../Canvas"; export * from "../Offscreen"; export * from "../WebGPUViewNativeComponent"; export * from "../hooks"; +export type { RNWebGPUInstallOptions, DawnTogglesDescriptor } from "../types"; // We don't need to set all global properties on web, webgpu is already available globally + +/** No-op on web — dawnToggles are Dawn-specific and are silently ignored. */ +export function installWebGPU( + _options?: import("../types").RNWebGPUInstallOptions, +): void { + // no-op on web +} diff --git a/packages/webgpu/src/types.ts b/packages/webgpu/src/types.ts index 15444fd13..44447e840 100644 --- a/packages/webgpu/src/types.ts +++ b/packages/webgpu/src/types.ts @@ -12,11 +12,10 @@ export interface DawnTogglesDescriptor { disable?: string[]; } -declare global { - interface GPUDeviceDescriptor { - /** Dawn-specific toggles. Ignored in non-Dawn environments. */ - dawnToggles?: DawnTogglesDescriptor; - } +/** Options for installWebGPU() - React Native WebGPU specific */ +export interface RNWebGPUInstallOptions { + /** Dawn-specific toggles applied at the Instance level (ignored on web) */ + dawnToggles?: DawnTogglesDescriptor; } export interface NativeCanvas { From 83ec66f97f84fa5dac3cde5a0185b34a16e7fc42 Mon Sep 17 00:00:00 2001 From: HALBY Date: Mon, 9 Mar 2026 13:40:12 +0900 Subject: [PATCH 4/5] refactor: move dawn toggles to native config --- packages/webgpu/README.md | 35 +++++++++ packages/webgpu/android/cpp/cpp-adapter.cpp | 16 +++- .../main/java/com/webgpu/WebGPUModule.java | 75 +++++++++++-------- packages/webgpu/apple/WebGPUModule.mm | 55 +++++++------- packages/webgpu/src/NativeWebGPUModule.ts | 2 +- packages/webgpu/src/main/index.tsx | 68 +++++++---------- packages/webgpu/src/main/index.web.tsx | 8 -- packages/webgpu/src/types.ts | 18 ----- 8 files changed, 148 insertions(+), 129 deletions(-) diff --git a/packages/webgpu/README.md b/packages/webgpu/README.md index 0ae1359b4..b70d20367 100644 --- a/packages/webgpu/README.md +++ b/packages/webgpu/README.md @@ -151,6 +151,41 @@ const style = StyleSheet.create({ }); ``` +## Dawn Debug Toggles + +Dawn instance toggles are configured natively before WebGPU is installed. +This keeps initialization order deterministic and avoids JS import-order issues. + +On iOS, add the following keys to your app's `Info.plist`: + +```xml +RNWebGPUEnableToggles + + allow_unsafe_apis + +RNWebGPUDisableToggles + + disallow_spirv + +``` + +On Android, add `meta-data` entries to your app's `AndroidManifest.xml`: + +```xml + + + + +``` + +Android toggle lists are comma-separated strings. Empty entries are ignored. +If you inject these values from build-time environment variables or config plugins, +emit them into `Info.plist` and `AndroidManifest.xml`. + ## Example App To run the example app you first need to [install Dawn](#installing-dawn). diff --git a/packages/webgpu/android/cpp/cpp-adapter.cpp b/packages/webgpu/android/cpp/cpp-adapter.cpp index 18c05cd5f..2fe1b3aaa 100644 --- a/packages/webgpu/android/cpp/cpp-adapter.cpp +++ b/packages/webgpu/android/cpp/cpp-adapter.cpp @@ -38,7 +38,14 @@ extern "C" JNIEXPORT void JNICALL Java_com_webgpu_WebGPUModule_initializeNative( jsize len = env->GetArrayLength(enableToggles); for (jsize i = 0; i < len; i++) { auto jstr = (jstring)env->GetObjectArrayElement(enableToggles, i); + if (jstr == nullptr) { + continue; + } const char *cstr = env->GetStringUTFChars(jstr, nullptr); + if (cstr == nullptr) { + env->DeleteLocalRef(jstr); + continue; + } enableVec.emplace_back(cstr); env->ReleaseStringUTFChars(jstr, cstr); env->DeleteLocalRef(jstr); @@ -48,7 +55,14 @@ extern "C" JNIEXPORT void JNICALL Java_com_webgpu_WebGPUModule_initializeNative( jsize len = env->GetArrayLength(disableToggles); for (jsize i = 0; i < len; i++) { auto jstr = (jstring)env->GetObjectArrayElement(disableToggles, i); + if (jstr == nullptr) { + continue; + } const char *cstr = env->GetStringUTFChars(jstr, nullptr); + if (cstr == nullptr) { + env->DeleteLocalRef(jstr); + continue; + } disableVec.emplace_back(cstr); env->ReleaseStringUTFChars(jstr, cstr); env->DeleteLocalRef(jstr); @@ -95,4 +109,4 @@ extern "C" JNIEXPORT void JNICALL Java_com_webgpu_WebGPUView_onSurfaceDestroy( JNIEnv *env, jobject thiz, jint contextId) { auto ®istry = rnwgpu::SurfaceRegistry::getInstance(); registry.removeSurfaceInfo(contextId); -} \ No newline at end of file +} diff --git a/packages/webgpu/android/src/main/java/com/webgpu/WebGPUModule.java b/packages/webgpu/android/src/main/java/com/webgpu/WebGPUModule.java index 82b0bcd1a..86e6e6593 100644 --- a/packages/webgpu/android/src/main/java/com/webgpu/WebGPUModule.java +++ b/packages/webgpu/android/src/main/java/com/webgpu/WebGPUModule.java @@ -1,6 +1,8 @@ package com.webgpu; -import android.util.Log; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.os.Bundle; import androidx.annotation.Nullable; import androidx.annotation.OptIn; @@ -11,10 +13,7 @@ import com.facebook.proguard.annotations.DoNotStrip; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.JavaScriptContextHolder; -import com.facebook.react.bridge.ReactMap; import com.facebook.react.bridge.ReactMethod; -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.bridge.ReadableMap; import com.facebook.react.common.annotations.FrameworkAPI; import com.facebook.react.module.annotations.ReactModule; import com.facebook.react.modules.blob.BlobModule; @@ -35,7 +34,7 @@ public WebGPUModule(ReactApplicationContext reactContext) { @OptIn(markerClass = FrameworkAPI.class) @ReactMethod(isBlockingSynchronousMethod = true) - public boolean install(@Nullable ReadableMap options) { + public boolean install() { ReactApplicationContext context = getReactApplicationContext(); JavaScriptContextHolder jsContext = context.getJavaScriptContextHolder(); CallInvokerHolder callInvokerHolder = context.getCatalystInstance().getJSCallInvokerHolder(); @@ -44,40 +43,50 @@ public boolean install(@Nullable ReadableMap options) { throw new RuntimeException("React Native's BlobModule was not found!"); } - // Extract dawnToggles from options - String[] enableToggles = new String[0]; - String[] disableToggles = new String[0]; - if (options != null && options.hasKey("dawnToggles")) { - ReadableMap dawnToggles = options.getMap("dawnToggles"); - if (dawnToggles != null) { - if (dawnToggles.hasKey("enable")) { - ReadableArray enableArray = dawnToggles.getArray("enable"); - if (enableArray != null) { - List list = new ArrayList<>(); - for (int i = 0; i < enableArray.size(); i++) { - list.add(enableArray.getString(i)); - } - enableToggles = list.toArray(new String[0]); - } - } - if (dawnToggles.hasKey("disable")) { - ReadableArray disableArray = dawnToggles.getArray("disable"); - if (disableArray != null) { - List list = new ArrayList<>(); - for (int i = 0; i < disableArray.size(); i++) { - list.add(disableArray.getString(i)); - } - disableToggles = list.toArray(new String[0]); - } - } - } - } + String[] enableToggles = readToggleMetadata("com.webgpu.enable_toggles"); + String[] disableToggles = readToggleMetadata("com.webgpu.disable_toggles"); initializeNative(jsContext.get(), (CallInvokerHolderImpl) callInvokerHolder, blobModule, enableToggles, disableToggles); return true; } + private String[] readToggleMetadata(String key) { + Bundle metadata = getApplicationMetadata(); + if (metadata == null) { + return new String[0]; + } + return parseToggleList(metadata.getString(key)); + } + + @Nullable + private Bundle getApplicationMetadata() { + ReactApplicationContext context = getReactApplicationContext(); + try { + ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo( + context.getPackageName(), PackageManager.GET_META_DATA); + return appInfo.metaData; + } catch (PackageManager.NameNotFoundException e) { + return null; + } + } + + private static String[] parseToggleList(@Nullable String rawValue) { + if (rawValue == null || rawValue.trim().isEmpty()) { + return new String[0]; + } + + List toggles = new ArrayList<>(); + String[] parts = rawValue.split(","); + for (String part : parts) { + String toggle = part.trim(); + if (!toggle.isEmpty()) { + toggles.add(toggle); + } + } + return toggles.toArray(new String[0]); + } + @OptIn(markerClass = FrameworkAPI.class) @DoNotStrip private native void initializeNative(long jsRuntime, CallInvokerHolderImpl jsInvoker, diff --git a/packages/webgpu/apple/WebGPUModule.mm b/packages/webgpu/apple/WebGPUModule.mm index cc76ada1d..11a85767e 100644 --- a/packages/webgpu/apple/WebGPUModule.mm +++ b/packages/webgpu/apple/WebGPUModule.mm @@ -20,6 +20,30 @@ @interface RCTBridge (JSIRuntime) - (void *)runtime; @end +static std::vector ReadToggleArray(NSDictionary *infoDictionary, + NSString *key) { + std::vector toggles; + id value = infoDictionary[key]; + if (![value isKindOfClass:[NSArray class]]) { + return toggles; + } + + for (id item in (NSArray *)value) { + if (![item isKindOfClass:[NSString class]]) { + continue; + } + NSString *toggle = [(NSString *)item + stringByTrimmingCharactersInSet: + [NSCharacterSet whitespaceAndNewlineCharacterSet]]; + if (toggle.length == 0) { + continue; + } + toggles.push_back(toggle.UTF8String); + } + + return toggles; +} + @implementation WebGPUModule RCT_EXPORT_MODULE(WebGPUModule) @@ -49,7 +73,7 @@ - (void)invalidate { return webgpuManager; } -RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(install:(NSDictionary* __nullable)options) { +RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(install) { if (webgpuManager != nil) { // Already initialized, ignore call. return @true; @@ -72,30 +96,11 @@ - (void)invalidate { return [NSNumber numberWithBool:NO]; } - // Extract dawnToggles from options - std::vector enableToggles; - std::vector disableToggles; - if (options != nil) { - NSDictionary *dawnToggles = options[@"dawnToggles"]; - if ([dawnToggles isKindOfClass:[NSDictionary class]]) { - NSArray *enable = dawnToggles[@"enable"]; - if ([enable isKindOfClass:[NSArray class]]) { - for (NSString *toggle in enable) { - if ([toggle isKindOfClass:[NSString class]]) { - enableToggles.push_back(toggle.UTF8String); - } - } - } - NSArray *disable = dawnToggles[@"disable"]; - if ([disable isKindOfClass:[NSArray class]]) { - for (NSString *toggle in disable) { - if ([toggle isKindOfClass:[NSString class]]) { - disableToggles.push_back(toggle.UTF8String); - } - } - } - } - } + NSDictionary *infoDictionary = NSBundle.mainBundle.infoDictionary ?: @{}; + auto enableToggles = + ReadToggleArray(infoDictionary, @"RNWebGPUEnableToggles"); + auto disableToggles = + ReadToggleArray(infoDictionary, @"RNWebGPUDisableToggles"); std::shared_ptr platformContext = std::make_shared(); diff --git a/packages/webgpu/src/NativeWebGPUModule.ts b/packages/webgpu/src/NativeWebGPUModule.ts index f41720e97..79305482f 100644 --- a/packages/webgpu/src/NativeWebGPUModule.ts +++ b/packages/webgpu/src/NativeWebGPUModule.ts @@ -2,7 +2,7 @@ import { TurboModuleRegistry } from "react-native"; import type { TurboModule } from "react-native"; export interface Spec extends TurboModule { - install: (options?: { [key: string]: unknown } | null) => boolean; + install: () => boolean; } // eslint-disable-next-line import/no-default-export diff --git a/packages/webgpu/src/main/index.tsx b/packages/webgpu/src/main/index.tsx index 49d4d2268..df21bd780 100644 --- a/packages/webgpu/src/main/index.tsx +++ b/packages/webgpu/src/main/index.tsx @@ -1,58 +1,40 @@ import { registerWebGPUForReanimated } from "../external"; import WebGPUModule from "../NativeWebGPUModule"; -import type { RNWebGPUInstallOptions } from "../types"; export * from "../Canvas"; export * from "../Offscreen"; export * from "../WebGPUViewNativeComponent"; export * from "../hooks"; -export type { RNWebGPUInstallOptions, DawnTogglesDescriptor } from "../types"; export { default as WebGPUModule } from "../NativeWebGPUModule"; -function setupNavigatorGpu(_installOk: boolean) { - if (typeof RNWebGPU !== "undefined") { - if (!navigator) { - // @ts-expect-error Navigation object is more complex than this, setting it to an empty object to add gpu property - navigator = { - gpu: RNWebGPU.gpu, - userAgent: "react-native", - }; - } else { - navigator.gpu = RNWebGPU.gpu; - if (typeof navigator.userAgent !== "string") { - try { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - Hermes navigator may not include a userAgent, align with the polyfill if needed - navigator.userAgent = "react-native"; - } catch { - // navigator.userAgent can be read-only; ignore if assignment fails - } +const _installOk = WebGPUModule.install(); + +registerWebGPUForReanimated(); + +if (typeof RNWebGPU !== "undefined") { + if (!navigator) { + // @ts-expect-error Navigation object is more complex than this, setting it to an empty object to add gpu property + navigator = { + gpu: RNWebGPU.gpu, + userAgent: "react-native", + }; + } else { + navigator.gpu = RNWebGPU.gpu; + if (typeof navigator.userAgent !== "string") { + try { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore - Hermes navigator may not include a userAgent, align with the polyfill if needed + navigator.userAgent = "react-native"; + } catch { + // navigator.userAgent can be read-only; ignore if assignment fails } } - global.createImageBitmap = - global.createImageBitmap ?? RNWebGPU.createImageBitmap.bind(RNWebGPU); - } else { - console.warn( - `[react-native-wgpu] install() returned ${_installOk} but RNWebGPU global is not available`, - ); } -} - -/** - * Explicitly install react-native-webgpu with optional Dawn toggles. - * Toggles are applied at the Instance level (equivalent to node-webgpu's create() flags). - * On web, dawnToggles are silently ignored. - */ -export function installWebGPU(options?: RNWebGPUInstallOptions): void { - const _installOk = WebGPUModule.install( - (options as { [key: string]: unknown }) ?? null, + global.createImageBitmap = + global.createImageBitmap ?? RNWebGPU.createImageBitmap.bind(RNWebGPU); +} else { + console.warn( + `[react-native-wgpu] install() returned ${_installOk} but RNWebGPU global is not available`, ); - setupNavigatorGpu(_installOk); } - -// Auto-install on module load (backward compatible, no toggles) -const _installOk = WebGPUModule.install(null); -setupNavigatorGpu(_installOk); - -registerWebGPUForReanimated(); diff --git a/packages/webgpu/src/main/index.web.tsx b/packages/webgpu/src/main/index.web.tsx index 1a7443154..b848bbc69 100644 --- a/packages/webgpu/src/main/index.web.tsx +++ b/packages/webgpu/src/main/index.web.tsx @@ -4,13 +4,5 @@ export * from "../Canvas"; export * from "../Offscreen"; export * from "../WebGPUViewNativeComponent"; export * from "../hooks"; -export type { RNWebGPUInstallOptions, DawnTogglesDescriptor } from "../types"; // We don't need to set all global properties on web, webgpu is already available globally - -/** No-op on web — dawnToggles are Dawn-specific and are silently ignored. */ -export function installWebGPU( - _options?: import("../types").RNWebGPUInstallOptions, -): void { - // no-op on web -} diff --git a/packages/webgpu/src/types.ts b/packages/webgpu/src/types.ts index 44447e840..af4684cfa 100644 --- a/packages/webgpu/src/types.ts +++ b/packages/webgpu/src/types.ts @@ -1,23 +1,5 @@ type SurfacePointer = bigint; -/** Dawn-specific debug toggle configuration (non-standard WebGPU extension) */ -export interface DawnTogglesDescriptor { - /** - * List of toggle names to enable. - * e.g. 'dump_shaders', 'use_user_defined_labels_in_backend', - * 'disable_symbol_renaming', 'emit_hlsl_debug_symbols' - */ - enable?: string[]; - /** List of toggle names to disable. */ - disable?: string[]; -} - -/** Options for installWebGPU() - React Native WebGPU specific */ -export interface RNWebGPUInstallOptions { - /** Dawn-specific toggles applied at the Instance level (ignored on web) */ - dawnToggles?: DawnTogglesDescriptor; -} - export interface NativeCanvas { surface: SurfacePointer; width: number; From 6b8302e9009d474da8c229a3a24fd410674dde05 Mon Sep 17 00:00:00 2001 From: HALBY Date: Tue, 10 Mar 2026 22:05:59 +0900 Subject: [PATCH 5/5] feat: add Expo Config Plugin for Dawn debug toggles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Expo managed/bare workflow ユーザーが app.json の plugins フィールドから enableToggles / disableToggles を設定できる Config Plugin を追加。 plugin/src/app.plugin.ts → app.plugin.js にコンパイルして配布。 Co-Authored-By: Claude Sonnet 4.6 --- packages/webgpu/app.plugin.js | 44 ++++ packages/webgpu/package.json | 7 + packages/webgpu/plugin/src/app.plugin.ts | 67 +++++++ packages/webgpu/plugin/tsconfig.json | 14 ++ yarn.lock | 243 ++++++++++++++++++++++- 5 files changed, 369 insertions(+), 6 deletions(-) create mode 100644 packages/webgpu/app.plugin.js create mode 100644 packages/webgpu/plugin/src/app.plugin.ts create mode 100644 packages/webgpu/plugin/tsconfig.json diff --git a/packages/webgpu/app.plugin.js b/packages/webgpu/app.plugin.js new file mode 100644 index 000000000..6f0bb1c4e --- /dev/null +++ b/packages/webgpu/app.plugin.js @@ -0,0 +1,44 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const config_plugins_1 = require("@expo/config-plugins"); +const withWebGPUAndroid = (config, { enableToggles = [], disableToggles = [] } = {}) => { + return (0, config_plugins_1.withAndroidManifest)(config, (config) => { + const app = config_plugins_1.AndroidConfig.Manifest.getMainApplicationOrThrow(config.modResults); + if (!app['meta-data']) + app['meta-data'] = []; + if (enableToggles.length > 0) { + app['meta-data'].push({ + $: { + 'android:name': 'com.webgpu.enable_toggles', + 'android:value': enableToggles.join(','), + }, + }); + } + if (disableToggles.length > 0) { + app['meta-data'].push({ + $: { + 'android:name': 'com.webgpu.disable_toggles', + 'android:value': disableToggles.join(','), + }, + }); + } + return config; + }); +}; +const withWebGPUIos = (config, { enableToggles = [], disableToggles = [] } = {}) => { + return (0, config_plugins_1.withInfoPlist)(config, (config) => { + if (enableToggles.length > 0) { + config.modResults['RNWebGPUEnableToggles'] = enableToggles; + } + if (disableToggles.length > 0) { + config.modResults['RNWebGPUDisableToggles'] = disableToggles; + } + return config; + }); +}; +const withWebGPU = (config, options = {}) => { + config = withWebGPUAndroid(config, options); + config = withWebGPUIos(config, options); + return config; +}; +exports.default = withWebGPU; diff --git a/packages/webgpu/package.json b/packages/webgpu/package.json index dd7992f21..3dcc8e4cc 100644 --- a/packages/webgpu/package.json +++ b/packages/webgpu/package.json @@ -7,9 +7,14 @@ "types": "lib/typescript/src/index.d.ts", "react-native": "src/index", "source": "src/index", + "expo": { + "plugin": "./app.plugin" + }, "files": [ "src/**", "lib/**", + "app.plugin.js", + "plugin/src/**", "!**/__tests__/**", "android/build.gradle", "android/CMakeLists.txt", @@ -26,6 +31,7 @@ "test:ref": "REFERENCE=true NODE_OPTIONS='--experimental-require-module' jest -i", "lint": "eslint . --ext .ts,.tsx --max-warnings 0 --cache --fix", "tsc": "tsc --noEmit", + "build:plugin": "tsc -p plugin/tsconfig.json", "build": "bob build", "build-dawn": "tsx scripts/build/dawn.ts", "clean-dawn": "rimraf ./libs && rimraf ../../externals/dawn/out", @@ -56,6 +62,7 @@ "registry": "https://registry.npmjs.org/" }, "devDependencies": { + "@expo/config-plugins": "^9.0.0", "@types/jest": "^29.5.12", "@types/lodash": "^4.17.5", "@types/node": "^20.14.7", diff --git a/packages/webgpu/plugin/src/app.plugin.ts b/packages/webgpu/plugin/src/app.plugin.ts new file mode 100644 index 000000000..3724eb819 --- /dev/null +++ b/packages/webgpu/plugin/src/app.plugin.ts @@ -0,0 +1,67 @@ +import { + ConfigPlugin, + withAndroidManifest, + withInfoPlist, + AndroidConfig, +} from '@expo/config-plugins'; + +type WebGPUPluginOptions = { + enableToggles?: string[]; + disableToggles?: string[]; +}; + +const withWebGPUAndroid: ConfigPlugin = ( + config, + { enableToggles = [], disableToggles = [] } = {} +) => { + return withAndroidManifest(config, (config) => { + const app = AndroidConfig.Manifest.getMainApplicationOrThrow( + config.modResults + ); + if (!app['meta-data']) app['meta-data'] = []; + + if (enableToggles.length > 0) { + app['meta-data'].push({ + $: { + 'android:name': 'com.webgpu.enable_toggles', + 'android:value': enableToggles.join(','), + }, + }); + } + if (disableToggles.length > 0) { + app['meta-data'].push({ + $: { + 'android:name': 'com.webgpu.disable_toggles', + 'android:value': disableToggles.join(','), + }, + }); + } + return config; + }); +}; + +const withWebGPUIos: ConfigPlugin = ( + config, + { enableToggles = [], disableToggles = [] } = {} +) => { + return withInfoPlist(config, (config) => { + if (enableToggles.length > 0) { + config.modResults['RNWebGPUEnableToggles'] = enableToggles; + } + if (disableToggles.length > 0) { + config.modResults['RNWebGPUDisableToggles'] = disableToggles; + } + return config; + }); +}; + +const withWebGPU: ConfigPlugin = ( + config, + options = {} +) => { + config = withWebGPUAndroid(config, options); + config = withWebGPUIos(config, options); + return config; +}; + +export default withWebGPU; diff --git a/packages/webgpu/plugin/tsconfig.json b/packages/webgpu/plugin/tsconfig.json new file mode 100644 index 000000000..f6f5a8fad --- /dev/null +++ b/packages/webgpu/plugin/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ES2019", + "module": "commonjs", + "lib": ["ES2019"], + "strict": true, + "outDir": "../", + "rootDir": "./src", + "declaration": false, + "skipLibCheck": true + }, + "include": ["./src"], + "exclude": ["../node_modules"] +} diff --git a/yarn.lock b/yarn.lock index 959df01a9..df53c694b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16,6 +16,15 @@ __metadata: languageName: node linkType: hard +"@babel/code-frame@npm:~7.10.4": + version: 7.10.4 + resolution: "@babel/code-frame@npm:7.10.4" + dependencies: + "@babel/highlight": ^7.10.4 + checksum: feb4543c8a509fe30f0f6e8d7aa84f82b41148b963b826cd330e34986f649a85cb63b2f13dd4effdf434ac555d16f14940b8ea5f4433297c2f5ff85486ded019 + languageName: node + linkType: hard + "@babel/compat-data@npm:^7.20.5, @babel/compat-data@npm:^7.27.7, @babel/compat-data@npm:^7.28.6": version: 7.28.6 resolution: "@babel/compat-data@npm:7.28.6" @@ -248,7 +257,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-validator-identifier@npm:^7.28.5": +"@babel/helper-validator-identifier@npm:^7.25.9, @babel/helper-validator-identifier@npm:^7.28.5": version: 7.28.5 resolution: "@babel/helper-validator-identifier@npm:7.28.5" checksum: 5a251a6848e9712aea0338f659a1a3bd334d26219d5511164544ca8ec20774f098c3a6661e9da65a0d085c745c00bb62c8fada38a62f08fa1f8053bc0aeb57e4 @@ -283,6 +292,18 @@ __metadata: languageName: node linkType: hard +"@babel/highlight@npm:^7.10.4": + version: 7.25.9 + resolution: "@babel/highlight@npm:7.25.9" + dependencies: + "@babel/helper-validator-identifier": ^7.25.9 + chalk: ^2.4.2 + js-tokens: ^4.0.0 + picocolors: ^1.0.0 + checksum: a6e0ac0a1c4bef7401915ca3442ab2b7ae4adf360262ca96b91396bfb9578abb28c316abf5e34460b780696db833b550238d9256bdaca60fade4ba7a67645064 + languageName: node + linkType: hard + "@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.13.16, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.0, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.23.9, @babel/parser@npm:^7.25.3, @babel/parser@npm:^7.28.6": version: 7.28.6 resolution: "@babel/parser@npm:7.28.6" @@ -2060,6 +2081,63 @@ __metadata: languageName: node linkType: hard +"@expo/config-plugins@npm:^9.0.0": + version: 9.1.7 + resolution: "@expo/config-plugins@npm:9.1.7" + dependencies: + "@expo/config-types": ^53.0.0 + "@expo/json-file": ~9.1.3 + "@expo/plist": ^0.3.3 + "@expo/sdk-runtime-versions": ^1.0.0 + chalk: ^4.1.2 + debug: ^4.3.5 + getenv: ^1.0.0 + glob: ^10.4.2 + resolve-from: ^5.0.0 + semver: ^7.5.4 + slash: ^3.0.0 + slugify: ^1.6.6 + xcode: ^3.0.1 + xml2js: 0.6.0 + checksum: 8eba71d88100c1c0572307256900b10b8375d3fac2496d0486e7d9d66d8dc4a979e907a12e5dfb3a7811871bef4ba5cc906559ceb371517b7308bd909f17730a + languageName: node + linkType: hard + +"@expo/config-types@npm:^53.0.0": + version: 53.0.5 + resolution: "@expo/config-types@npm:53.0.5" + checksum: 3f4db2d9590c18fb178f7395739ee2200512ad7c253655be0bad7f3f0d948df89c1c69c7e949f202faa98eecbd4bbae3edfcf9264f97f49d4f7087f85c68af5d + languageName: node + linkType: hard + +"@expo/json-file@npm:~9.1.3": + version: 9.1.5 + resolution: "@expo/json-file@npm:9.1.5" + dependencies: + "@babel/code-frame": ~7.10.4 + json5: ^2.2.3 + checksum: beedf9077dcff476acd895219e391e18079d3c375e58bb4902a4147fffe9774e11d4d1607cfc3488f8e9daec2c30e4d7c17c46fb701035ad7aabacaaf6d465b4 + languageName: node + linkType: hard + +"@expo/plist@npm:^0.3.3": + version: 0.3.5 + resolution: "@expo/plist@npm:0.3.5" + dependencies: + "@xmldom/xmldom": ^0.8.8 + base64-js: ^1.2.3 + xmlbuilder: ^15.1.1 + checksum: b78fda216c63ab553b24e75e87021be09155cd16e0fcf666846c1a5ea33defa2d7f631ea504788a8e2c5d67b5f59b35d6a46fa79a244d5890ee26870caffec22 + languageName: node + linkType: hard + +"@expo/sdk-runtime-versions@npm:^1.0.0": + version: 1.0.0 + resolution: "@expo/sdk-runtime-versions@npm:1.0.0" + checksum: 0942d5a356f590e8dc795761456cc48b3e2d6a38ad2a02d6774efcdc5a70424e05623b4e3e5d2fec0cdc30f40dde05c14391c781607eed3971bf8676518bfd9d + languageName: node + linkType: hard + "@hapi/hoek@npm:^9.0.0, @hapi/hoek@npm:^9.3.0": version: 9.3.0 resolution: "@hapi/hoek@npm:9.3.0" @@ -4924,6 +5002,13 @@ __metadata: languageName: node linkType: hard +"@xmldom/xmldom@npm:^0.8.8": + version: 0.8.11 + resolution: "@xmldom/xmldom@npm:0.8.11" + checksum: 72020f3d5c74b54e25d19f2cd7b2d87484926cc7febdf02347dc3e06364186641d54e9e94baaaaba30e99528e6727adcd1baef6d0809e7460aee3a5be890b132 + languageName: node + linkType: hard + "Example@workspace:apps/example": version: 0.0.0-use.local resolution: "Example@workspace:apps/example" @@ -5121,7 +5206,7 @@ __metadata: languageName: node linkType: hard -"ansi-styles@npm:^3.2.0": +"ansi-styles@npm:^3.2.0, ansi-styles@npm:^3.2.1": version: 3.2.1 resolution: "ansi-styles@npm:3.2.1" dependencies: @@ -5650,7 +5735,7 @@ __metadata: languageName: node linkType: hard -"base64-js@npm:^1.3.1, base64-js@npm:^1.5.1": +"base64-js@npm:^1.2.3, base64-js@npm:^1.3.1, base64-js@npm:^1.5.1": version: 1.5.1 resolution: "base64-js@npm:1.5.1" checksum: 669632eb3745404c2f822a18fc3a0122d2f9a7a13f7fb8b5823ee19d1d2ff9ee5b52c53367176ea4ad093c332fd5ab4bd0ebae5a8e27917a4105a4cfc86b1005 @@ -5673,6 +5758,13 @@ __metadata: languageName: node linkType: hard +"big-integer@npm:1.6.x": + version: 1.6.52 + resolution: "big-integer@npm:1.6.52" + checksum: 6e86885787a20fed96521958ae9086960e4e4b5e74d04f3ef7513d4d0ad631a9f3bde2730fc8aaa4b00419fc865f6ec573e5320234531ef37505da7da192c40b + languageName: node + linkType: hard + "bl@npm:^4.1.0": version: 4.1.0 resolution: "bl@npm:4.1.0" @@ -5691,6 +5783,24 @@ __metadata: languageName: node linkType: hard +"bplist-creator@npm:0.1.1": + version: 0.1.1 + resolution: "bplist-creator@npm:0.1.1" + dependencies: + stream-buffers: 2.2.x + checksum: b0d40d1d1623f1afdbb575cfc8075d742d2c4f0eb458574be809e3857752d1042a39553b3943d2d7f505dde92bcd43e1d7bdac61c9cd44475d696deb79f897ce + languageName: node + linkType: hard + +"bplist-parser@npm:0.3.2": + version: 0.3.2 + resolution: "bplist-parser@npm:0.3.2" + dependencies: + big-integer: 1.6.x + checksum: fad0f6eb155a9b636b4096a1725ce972a0386490d7d38df7be11a3a5645372446b7c44aacbc6626d24d2c17d8b837765361520ebf2960aeffcaf56765811620e + languageName: node + linkType: hard + "brace-expansion@npm:^1.1.7": version: 1.1.12 resolution: "brace-expansion@npm:1.1.12" @@ -5909,6 +6019,17 @@ __metadata: languageName: node linkType: hard +"chalk@npm:^2.4.2": + version: 2.4.2 + resolution: "chalk@npm:2.4.2" + dependencies: + ansi-styles: ^3.2.1 + escape-string-regexp: ^1.0.5 + supports-color: ^5.3.0 + checksum: ec3661d38fe77f681200f878edbd9448821924e0f93a9cefc0e26a33b145f1027a2084bf19967160d11e1f03bfe4eaffcabf5493b89098b2782c3fe0b03d80c2 + languageName: node + linkType: hard + "chalk@npm:^4.0.0, chalk@npm:^4.1.0, chalk@npm:^4.1.2": version: 4.1.2 resolution: "chalk@npm:4.1.2" @@ -6796,7 +6917,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.4.0, debug@npm:^4.4.1, debug@npm:^4.4.3": +"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.3.5, debug@npm:^4.4.0, debug@npm:^4.4.1, debug@npm:^4.4.3": version: 4.4.3 resolution: "debug@npm:4.4.3" dependencies: @@ -8529,6 +8650,13 @@ __metadata: languageName: node linkType: hard +"getenv@npm:^1.0.0": + version: 1.0.0 + resolution: "getenv@npm:1.0.0" + checksum: 19ae5cad603a1cf1bcb8fa3bed48e00d062eb0572a4404c02334b67f3b3499f238383082b064bb42515e9e25c2b08aef1a3e3d2b6852347721aa8b174825bd56 + languageName: node + linkType: hard + "glamor@npm:~2.20.40": version: 2.20.40 resolution: "glamor@npm:2.20.40" @@ -8560,7 +8688,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:^10.3.7": +"glob@npm:^10.3.7, glob@npm:^10.4.2": version: 10.5.0 resolution: "glob@npm:10.5.0" dependencies: @@ -8693,6 +8821,13 @@ __metadata: languageName: node linkType: hard +"has-flag@npm:^3.0.0": + version: 3.0.0 + resolution: "has-flag@npm:3.0.0" + checksum: 4a15638b454bf086c8148979aae044dd6e39d63904cd452d970374fa6a87623423da485dfb814e7be882e05c096a7ccf1ebd48e7e7501d0208d8384ff4dea73b + languageName: node + linkType: hard + "has-flag@npm:^4.0.0": version: 4.0.0 resolution: "has-flag@npm:4.0.0" @@ -12247,7 +12382,7 @@ __metadata: languageName: node linkType: hard -"picocolors@npm:^1.1.1": +"picocolors@npm:^1.0.0, picocolors@npm:^1.1.1": version: 1.1.1 resolution: "picocolors@npm:1.1.1" checksum: e1cf46bf84886c79055fdfa9dcb3e4711ad259949e3565154b004b260cd356c5d54b31a1437ce9782624bf766272fe6b0154f5f0c744fb7af5d454d2b60db045 @@ -12311,6 +12446,17 @@ __metadata: languageName: node linkType: hard +"plist@npm:^3.0.5": + version: 3.1.0 + resolution: "plist@npm:3.1.0" + dependencies: + "@xmldom/xmldom": ^0.8.8 + base64-js: ^1.5.1 + xmlbuilder: ^15.1.1 + checksum: c8ea013da8646d4c50dff82f9be39488054621cc229957621bb00add42b5d4ce3657cf58d4b10c50f7dea1a81118f825838f838baeb4e6f17fab453ecf91d424 + languageName: node + linkType: hard + "pngjs@npm:^6.0.0": version: 6.0.0 resolution: "pngjs@npm:6.0.0" @@ -12881,6 +13027,7 @@ __metadata: version: 0.0.0-use.local resolution: "react-native-wgpu@workspace:packages/webgpu" dependencies: + "@expo/config-plugins": ^9.0.0 "@types/jest": ^29.5.12 "@types/lodash": ^4.17.5 "@types/node": ^20.14.7 @@ -13537,6 +13684,13 @@ __metadata: languageName: node linkType: hard +"sax@npm:>=0.6.0": + version: 1.5.0 + resolution: "sax@npm:1.5.0" + checksum: e143cbeb6ca59345db142d97d1bbb620553beb8b166eda36ffeaba4b01af47dcf0423cd8781d0805b75feea69462cf80d0db7bf0a7e6dd87b711fda51b9153df + languageName: node + linkType: hard + "scheduler@npm:0.24.0-canary-efb381bbf-20230505": version: 0.24.0-canary-efb381bbf-20230505 resolution: "scheduler@npm:0.24.0-canary-efb381bbf-20230505" @@ -13812,6 +13966,17 @@ __metadata: languageName: node linkType: hard +"simple-plist@npm:^1.1.0": + version: 1.4.0 + resolution: "simple-plist@npm:1.4.0" + dependencies: + bplist-creator: 0.1.1 + bplist-parser: 0.3.2 + plist: ^3.0.5 + checksum: fa8086f6b781c289f1abad21306481dda4af6373b32a5d998a70e53c2b7218a1d21ebb5ae3e736baae704c21d311d3d39d01d0e6a2387eda01b4020b9ebd909e + languageName: node + linkType: hard + "simple-swizzle@npm:^0.2.2": version: 0.2.4 resolution: "simple-swizzle@npm:0.2.4" @@ -13853,6 +14018,13 @@ __metadata: languageName: node linkType: hard +"slugify@npm:^1.6.6": + version: 1.6.6 + resolution: "slugify@npm:1.6.6" + checksum: 04773c2d3b7aea8d2a61fa47cc7e5d29ce04e1a96cbaec409da57139df906acb3a449fac30b167d203212c806e73690abd4ff94fbad0a9a7b7ea109a2a638ae9 + languageName: node + linkType: hard + "smart-buffer@npm:^4.2.0": version: 4.2.0 resolution: "smart-buffer@npm:4.2.0" @@ -14035,6 +14207,13 @@ __metadata: languageName: node linkType: hard +"stream-buffers@npm:2.2.x": + version: 2.2.0 + resolution: "stream-buffers@npm:2.2.0" + checksum: 4587d9e8f050d689fb38b4295e73408401b16de8edecc12026c6f4ae92956705ecfd995ae3845d7fa3ebf19502d5754df9143d91447fd881d86e518f43882c1c + languageName: node + linkType: hard + "streamx@npm:^2.15.0, streamx@npm:^2.21.0": version: 2.23.0 resolution: "streamx@npm:2.23.0" @@ -14262,6 +14441,15 @@ __metadata: languageName: node linkType: hard +"supports-color@npm:^5.3.0": + version: 5.5.0 + resolution: "supports-color@npm:5.5.0" + dependencies: + has-flag: ^3.0.0 + checksum: 95f6f4ba5afdf92f495b5a912d4abee8dcba766ae719b975c56c084f5004845f6f5a5f7769f52d53f40e21952a6d87411bafe34af4a01e65f9926002e38e1dac + languageName: node + linkType: hard + "supports-color@npm:^7.1.0": version: 7.2.0 resolution: "supports-color@npm:7.2.0" @@ -15073,6 +15261,15 @@ __metadata: languageName: node linkType: hard +"uuid@npm:^7.0.3": + version: 7.0.3 + resolution: "uuid@npm:7.0.3" + bin: + uuid: dist/bin/uuid + checksum: f5b7b5cc28accac68d5c083fd51cca64896639ebd4cca88c6cfb363801aaa83aa439c86dfc8446ea250a7a98d17afd2ad9e88d9d4958c79a412eccb93bae29de + languageName: node + linkType: hard + "v8-to-istanbul@npm:^9.0.1": version: 9.3.0 resolution: "v8-to-istanbul@npm:9.3.0" @@ -15963,6 +16160,40 @@ __metadata: languageName: node linkType: hard +"xcode@npm:^3.0.1": + version: 3.0.1 + resolution: "xcode@npm:3.0.1" + dependencies: + simple-plist: ^1.1.0 + uuid: ^7.0.3 + checksum: 908ff85851f81aec6e36ca24427db092e1cc068f052716e14de5e762196858039efabbe053a1abe8920184622501049e74a93618e8692b982f7604a9847db108 + languageName: node + linkType: hard + +"xml2js@npm:0.6.0": + version: 0.6.0 + resolution: "xml2js@npm:0.6.0" + dependencies: + sax: ">=0.6.0" + xmlbuilder: ~11.0.0 + checksum: 437f353fd66d367bf158e9555a0625df9965d944e499728a5c6bc92a54a2763179b144f14b7e1c725040f56bbd22b0fa6cfcb09ec4faf39c45ce01efe631f40b + languageName: node + linkType: hard + +"xmlbuilder@npm:^15.1.1": + version: 15.1.1 + resolution: "xmlbuilder@npm:15.1.1" + checksum: 14f7302402e28d1f32823583d121594a9dca36408d40320b33f598bd589ca5163a352d076489c9c64d2dc1da19a790926a07bf4191275330d4de2b0d85bb1843 + languageName: node + linkType: hard + +"xmlbuilder@npm:~11.0.0": + version: 11.0.1 + resolution: "xmlbuilder@npm:11.0.1" + checksum: 7152695e16f1a9976658215abab27e55d08b1b97bca901d58b048d2b6e106b5af31efccbdecf9b07af37c8377d8e7e821b494af10b3a68b0ff4ae60331b415b0 + languageName: node + linkType: hard + "xtend@npm:~4.0.1": version: 4.0.2 resolution: "xtend@npm:4.0.2"