diff --git a/apps/example/src/App.tsx b/apps/example/src/App.tsx
index cc85c03b5..08e0b2838 100644
--- a/apps/example/src/App.tsx
+++ b/apps/example/src/App.tsx
@@ -37,6 +37,7 @@ import { AsyncStarvation } from "./Diagnostics/AsyncStarvation";
import { DeviceLostHang } from "./Diagnostics/DeviceLostHang";
import { DiagnosticsList } from "./Diagnostics/DiagnosticsList";
import { WorkletRequestAdapter } from "./Diagnostics/WorkletRequestAdapter";
+import { ReloadLifecycle } from "./Diagnostics/ReloadLifecycle";
import { ContextEdgeCases } from "./Diagnostics/ContextEdgeCases";
import { ViewFormatsUseAfterFree } from "./Diagnostics/ViewFormatsUseAfterFree";
import { RenderAfterUnmount } from "./Diagnostics/RenderAfterUnmount";
@@ -113,6 +114,7 @@ function App() {
name="WorkletRequestAdapter"
component={WorkletRequestAdapter}
/>
+
+ device.createComputePipelineAsync({
+ label: "reload-lifecycle-pending-pipeline",
+ layout: "auto",
+ compute: {
+ module: device.createShaderModule({
+ code: "@compute @workgroup_size(1) fn main() {}",
+ }),
+ entryPoint: "main",
+ },
+ });
+
+export const ReloadLifecycle = () => {
+ const deviceRef = useRef(null);
+ const [status, setStatus] = useState("Requesting adapter…");
+ const [error, setError] = useState(null);
+
+ useEffect(() => {
+ let cancelled = false;
+
+ const prepare = async () => {
+ try {
+ const adapter = await navigator.gpu.requestAdapter();
+ if (!adapter) {
+ throw new Error("Failed to acquire a GPU adapter.");
+ }
+
+ const device = await adapter.requestDevice({
+ label: "reload-lifecycle-probe",
+ });
+ if (cancelled) {
+ return;
+ }
+
+ deviceRef.current = device;
+ setStatus("WebGPU is ready. Reload can now be triggered.");
+
+ // Keep a native callback registered so runtime teardown has to cancel it.
+ void device.lost.catch(() => undefined);
+ } catch (cause) {
+ if (!cancelled) {
+ setError(cause instanceof Error ? cause.message : String(cause));
+ }
+ }
+ };
+
+ void prepare();
+ return () => {
+ cancelled = true;
+ deviceRef.current = null;
+ };
+ }, []);
+
+ const reload = () => {
+ const device = deviceRef.current;
+ if (!device) {
+ return;
+ }
+
+ // Do not await these operations: reload must invalidate their callbacks while
+ // they are still associated with the current JavaScript runtime.
+ void createPendingPipeline(device).catch(() => undefined);
+ void device.queue.onSubmittedWorkDone().catch(() => undefined);
+ DevSettings.reload("react-native-webgpu lifecycle regression");
+ };
+
+ const ready = deviceRef.current !== null;
+
+ return (
+
+
+ Runtime Reload Lifecycle
+ {status}
+ {error ? {error} : null}
+
+ Start async work and reload
+
+
+ After reload, open this screen again. A healthy installation acquires
+ a fresh adapter and device and reaches the ready state.
+
+
+
+ );
+};
diff --git a/apps/example/src/Route.ts b/apps/example/src/Route.ts
index a3c4cea20..329465bf4 100644
--- a/apps/example/src/Route.ts
+++ b/apps/example/src/Route.ts
@@ -30,6 +30,7 @@ export type Routes = {
AsyncStarvation: undefined;
DeviceLostHang: undefined;
WorkletRequestAdapter: undefined;
+ ReloadLifecycle: undefined;
ContextEdgeCases: undefined;
ViewFormatsUseAfterFree: undefined;
RenderAfterUnmount: undefined;