diff --git a/packages/webgpu/cpp/rnwgpu/api/GPUAdapter.cpp b/packages/webgpu/cpp/rnwgpu/api/GPUAdapter.cpp index 7bdb21840..085b582dc 100644 --- a/packages/webgpu/cpp/rnwgpu/api/GPUAdapter.cpp +++ b/packages/webgpu/cpp/rnwgpu/api/GPUAdapter.cpp @@ -135,9 +135,35 @@ async::AsyncTaskHandle GPUAdapter::requestDevice( deviceLostBinding, creationRuntime](const async::AsyncTaskHandle::ResolveFunction &resolve, const async::AsyncTaskHandle::RejectFunction &reject) { - (void)descriptor; + // Build a local mutable copy so we can chain Dawn's device toggles. + // The toggle name strings are owned by `descriptor` (captured above), + // and the const char* / DawnTogglesDescriptor locals live for the + // whole synchronous RequestDevice call below, which is when Dawn reads + // the chained struct. + wgpu::DeviceDescriptor deviceDesc = aDescriptor; + wgpu::DawnTogglesDescriptor toggles{}; + std::vector enabledToggles; + std::vector disabledToggles; + if (descriptor.has_value() && descriptor.value()->dawnToggles) { + const auto &dawnToggles = descriptor.value()->dawnToggles.value(); + if (dawnToggles->enabledToggles) { + for (const auto &t : dawnToggles->enabledToggles.value()) { + enabledToggles.push_back(t.c_str()); + } + toggles.enabledToggleCount = enabledToggles.size(); + toggles.enabledToggles = enabledToggles.data(); + } + if (dawnToggles->disabledToggles) { + for (const auto &t : dawnToggles->disabledToggles.value()) { + disabledToggles.push_back(t.c_str()); + } + toggles.disabledToggleCount = disabledToggles.size(); + toggles.disabledToggles = disabledToggles.data(); + } + deviceDesc.nextInChain = &toggles; + } _instance.RequestDevice( - &aDescriptor, wgpu::CallbackMode::AllowProcessEvents, + &deviceDesc, 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 new file mode 100644 index 000000000..59d4cdfc2 --- /dev/null +++ b/packages/webgpu/cpp/rnwgpu/api/descriptors/GPUDawnTogglesDescriptor.h @@ -0,0 +1,57 @@ +#pragma once + +#include +#include +#include + +#include "webgpu/webgpu_cpp.h" + +#include "JSIConverter.h" +#include "WGPULogger.h" + +namespace jsi = facebook::jsi; + +namespace rnwgpu { + +// Non-standard, Dawn-only. Mirrors wgpu::DawnTogglesDescriptor field-for-field +// so the mapping to the native chained struct is 1:1. Chained onto the +// wgpu::DeviceDescriptor in GPUAdapter::requestDevice. +struct GPUDawnTogglesDescriptor { + std::optional> enabledToggles; // Iterable + std::optional> disabledToggles; // Iterable +}; + +} // namespace rnwgpu + +namespace rnwgpu { + +template <> +struct JSIConverter> { + static std::shared_ptr + fromJSI(jsi::Runtime &runtime, const jsi::Value &arg, bool outOfBounds) { + auto result = std::make_unique(); + if (!outOfBounds && arg.isObject()) { + auto value = arg.getObject(runtime); + if (value.hasProperty(runtime, "enabledToggles")) { + auto prop = value.getProperty(runtime, "enabledToggles"); + result->enabledToggles = + JSIConverter>>::fromJSI( + runtime, prop, false); + } + if (value.hasProperty(runtime, "disabledToggles")) { + auto prop = value.getProperty(runtime, "disabledToggles"); + result->disabledToggles = + JSIConverter>>::fromJSI( + runtime, prop, false); + } + } + return result; + } + static jsi::Value + toJSI(jsi::Runtime &runtime, + std::shared_ptr arg) { + throw std::runtime_error("Invalid GPUDawnTogglesDescriptor::toJSI()"); + } +}; + +} // namespace rnwgpu diff --git a/packages/webgpu/cpp/rnwgpu/api/descriptors/GPUDeviceDescriptor.h b/packages/webgpu/cpp/rnwgpu/api/descriptors/GPUDeviceDescriptor.h index e18f73e16..21c43fd36 100644 --- a/packages/webgpu/cpp/rnwgpu/api/descriptors/GPUDeviceDescriptor.h +++ b/packages/webgpu/cpp/rnwgpu/api/descriptors/GPUDeviceDescriptor.h @@ -11,6 +11,7 @@ #include "RnFeatures.h" #include "WGPULogger.h" +#include "GPUDawnTogglesDescriptor.h" #include "GPUQueueDescriptor.h" namespace jsi = facebook::jsi; @@ -25,6 +26,9 @@ struct GPUDeviceDescriptor { std::optional> defaultQueue; // GPUQueueDescriptor std::optional label; // string + // Non-standard Dawn-only device toggles, chained onto the wgpu::Device + // descriptor in GPUAdapter::requestDevice. + std::optional> dawnToggles; }; } // namespace rnwgpu diff --git a/packages/webgpu/src/__tests__/DawnToggles.spec.ts b/packages/webgpu/src/__tests__/DawnToggles.spec.ts new file mode 100644 index 000000000..574a985bf --- /dev/null +++ b/packages/webgpu/src/__tests__/DawnToggles.spec.ts @@ -0,0 +1,41 @@ +import { client } from "./setup"; + +describe("Dawn toggles", () => { + it("requests a device with enabled and disabled dawnToggles", async () => { + const result = await client.eval(({ gpu }) => { + return gpu.requestAdapter().then((adapter) => + adapter! + .requestDevice({ + dawnToggles: { + enabledToggles: ["disable_symbol_renaming"], + disabledToggles: ["lazy_clear_resource_on_first_use"], + }, + }) + .then((device) => !!device), + ); + }); + expect(result).toBe(true); + }); + + it("requests a device with no dawnToggles (unchanged behavior)", async () => { + const result = await client.eval(({ gpu }) => { + return gpu + .requestAdapter() + .then((adapter) => adapter!.requestDevice().then((device) => !!device)); + }); + expect(result).toBe(true); + }); + + it("ignores unknown toggle names without failing device creation", async () => { + const result = await client.eval(({ gpu }) => { + return gpu.requestAdapter().then((adapter) => + adapter! + .requestDevice({ + dawnToggles: { enabledToggles: ["this_toggle_does_not_exist"] }, + }) + .then((device) => !!device), + ); + }); + expect(result).toBe(true); + }); +}); diff --git a/packages/webgpu/src/index.tsx b/packages/webgpu/src/index.tsx index 6ac631a48..cf9e9902b 100644 --- a/packages/webgpu/src/index.tsx +++ b/packages/webgpu/src/index.tsx @@ -1,5 +1,6 @@ /// import type { + GPUDawnTogglesDescriptor, GPUSharedTextureMemory, GPUSharedTextureMemoryDescriptor, NativeCanvas, @@ -17,6 +18,7 @@ export type { CreateVideoPlayerOptions, GPUSharedTextureMemory, GPUSharedTextureMemoryDescriptor, + GPUDawnTogglesDescriptor, } from "./types"; declare global { @@ -56,6 +58,11 @@ declare global { ): GPUSharedTextureMemory; } + // Non-standard, Dawn-only. Lets callers set Dawn device-stage toggles at + // device creation: adapter.requestDevice({ dawnToggles: { ... } }). + interface GPUDeviceDescriptor { + dawnToggles?: GPUDawnTogglesDescriptor; + } // Non-spec extension: camera frames arrive in the sensor's native // orientation, which differs between iOS and Android. `rotation` (degrees, // one of 0/90/180/270) and `mirrored` (horizontal flip) are baked into the diff --git a/packages/webgpu/src/types.ts b/packages/webgpu/src/types.ts index a982763a5..c03f92b4b 100644 --- a/packages/webgpu/src/types.ts +++ b/packages/webgpu/src/types.ts @@ -75,6 +75,17 @@ export interface GPUSharedTextureMemoryDescriptor { label?: string; } +// Non-standard, Dawn-only device toggles. Mirrors Dawn's DawnTogglesDescriptor +// and is chained onto the native device descriptor at requestDevice time. +// Pass it via the (augmented) GPUDeviceDescriptor: adapter.requestDevice({ +// dawnToggles: { enabledToggles: ["dump_shaders"] } }). Toggle names are open +// strings (see Dawn's Toggles.cpp); these flags are Dawn-specific and +// non-portable. +export interface GPUDawnTogglesDescriptor { + enabledToggles?: string[]; + disabledToggles?: string[]; +} + // A piece of shared GPU memory backed by a native surface. Use createTexture() // to obtain a regular GPUTexture that aliases the surface's pixels. The // returned texture must be bracketed by beginAccess/endAccess around any