@react-three/fiber 10.0.0-canary.2c50459 → 10.0.0-canary.4a21acb

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -56,10 +56,13 @@ const R3F_BUILD_WEBGPU = true;
56
56
 
57
57
  const THREE = /*#__PURE__*/_mergeNamespaces({
58
58
  __proto__: null,
59
+ CubeRenderTarget: webgpu.CubeRenderTarget,
60
+ CubeRenderTargetCompat: webgpu.CubeRenderTarget,
59
61
  Inspector: Inspector_js.Inspector,
60
62
  R3F_BUILD_LEGACY: R3F_BUILD_LEGACY,
61
63
  R3F_BUILD_WEBGPU: R3F_BUILD_WEBGPU,
62
64
  RenderTargetCompat: webgpu.RenderTarget,
65
+ WebGLCubeRenderTarget: three.WebGLCubeRenderTarget,
63
66
  WebGLRenderTarget: three.WebGLRenderTarget,
64
67
  WebGLRenderer: three.WebGLRenderer
65
68
  }, [webgpu__namespace]);
@@ -333,15 +336,16 @@ function EnvironmentPortal({
333
336
  preset = void 0,
334
337
  extensions
335
338
  }) {
336
- const gl = fiber.useThree((state) => state.gl);
339
+ const renderer = fiber.useThree((state) => state.renderer);
340
+ const isLegacy = fiber.useThree((state) => state.isLegacy);
337
341
  const defaultScene = fiber.useThree((state) => state.scene);
338
342
  const camera = React__namespace.useRef(null);
339
343
  const [virtualScene] = React__namespace.useState(() => new webgpu.Scene());
340
344
  const fbo = React__namespace.useMemo(() => {
341
- const fbo2 = new webgpu.WebGLCubeRenderTarget(resolution);
345
+ const fbo2 = isLegacy ? new three.WebGLCubeRenderTarget(resolution) : new webgpu.CubeRenderTarget(resolution);
342
346
  fbo2.texture.type = webgpu.HalfFloatType;
343
347
  return fbo2;
344
- }, [resolution]);
348
+ }, [resolution, isLegacy]);
345
349
  React__namespace.useEffect(() => {
346
350
  return () => {
347
351
  fbo.dispose();
@@ -349,10 +353,10 @@ function EnvironmentPortal({
349
353
  }, [fbo]);
350
354
  React__namespace.useLayoutEffect(() => {
351
355
  if (frames === 1) {
352
- const autoClear = gl.autoClear;
353
- gl.autoClear = true;
354
- camera.current.update(gl, virtualScene);
355
- gl.autoClear = autoClear;
356
+ const autoClear = renderer.autoClear;
357
+ renderer.autoClear = true;
358
+ camera.current.update(renderer, virtualScene);
359
+ renderer.autoClear = autoClear;
356
360
  }
357
361
  return setEnvProps(background, scene, defaultScene, fbo.texture, {
358
362
  backgroundBlurriness: blur ?? backgroundBlurriness,
@@ -369,7 +373,7 @@ function EnvironmentPortal({
369
373
  defaultScene,
370
374
  background,
371
375
  frames,
372
- gl,
376
+ renderer,
373
377
  blur,
374
378
  backgroundBlurriness,
375
379
  backgroundIntensity,
@@ -380,10 +384,10 @@ function EnvironmentPortal({
380
384
  let count = 1;
381
385
  fiber.useFrame(() => {
382
386
  if (frames === Infinity || count < frames) {
383
- const autoClear = gl.autoClear;
384
- gl.autoClear = true;
385
- camera.current.update(gl, virtualScene);
386
- gl.autoClear = autoClear;
387
+ const autoClear = renderer.autoClear;
388
+ renderer.autoClear = true;
389
+ camera.current.update(renderer, virtualScene);
390
+ renderer.autoClear = autoClear;
387
391
  count++;
388
392
  }
389
393
  });
@@ -626,8 +630,7 @@ function prepare(target, root, type, props) {
626
630
  object,
627
631
  eventCount: 0,
628
632
  handlers: {},
629
- isHidden: false,
630
- deferredRefs: []
633
+ isHidden: false
631
634
  };
632
635
  if (object) object.__r3f = instance;
633
636
  }
@@ -784,6 +787,17 @@ function unregisterVisibility(store, object) {
784
787
  internal.visibilityRegistry.delete(object.uuid);
785
788
  internal.occlusionCache.delete(object);
786
789
  }
790
+ function updateVisibilityHandlers(store, object, handlers) {
791
+ const { internal } = store.getState();
792
+ const entry = internal.visibilityRegistry.get(object.uuid);
793
+ if (!entry) return false;
794
+ entry.handlers = handlers;
795
+ if ((handlers.onOccluded || handlers.onVisible) && !internal.occlusionEnabled) {
796
+ object.occlusionTest = true;
797
+ enableOcclusion(store);
798
+ }
799
+ return true;
800
+ }
787
801
  function checkVisibility(state) {
788
802
  const { internal, camera } = state;
789
803
  const registry = internal.visibilityRegistry;
@@ -971,10 +985,7 @@ function applyProps(object, props) {
971
985
  continue;
972
986
  }
973
987
  if (value === void 0) continue;
974
- if (isFromRef(value)) {
975
- instance?.deferredRefs?.push({ prop, ref: value[FROM_REF] });
976
- continue;
977
- }
988
+ if (isFromRef(value)) continue;
978
989
  if (isOnce(value)) {
979
990
  if (instance?.appliedOnce?.has(prop)) continue;
980
991
  if (instance) {
@@ -1010,6 +1021,19 @@ function applyProps(object, props) {
1010
1021
  } else if (isVectorLike(target) && is.num(value)) {
1011
1022
  if ("setScalar" in target && typeof target.setScalar === "function") target.setScalar(value);
1012
1023
  else target.set(value);
1024
+ } else if (root instanceof webgpu.ShaderMaterial && key === "uniforms" && is.obj(value)) {
1025
+ if (!is.obj(root.uniforms)) root.uniforms = {};
1026
+ const uniforms = root.uniforms;
1027
+ const nextUniforms = value;
1028
+ for (const name in nextUniforms) {
1029
+ const uniform = nextUniforms[name];
1030
+ const targetUniform = uniforms[name];
1031
+ if (targetUniform) Object.assign(targetUniform, uniform);
1032
+ else {
1033
+ const nextUniform = uniform instanceof webgpu.Uniform ? uniform.clone() : new webgpu.Uniform(uniform.value);
1034
+ uniforms[name] = nextUniform;
1035
+ }
1036
+ }
1013
1037
  } else {
1014
1038
  root[key] = value;
1015
1039
  if (key.endsWith("Node") && root.isMaterial) {
@@ -1028,6 +1052,9 @@ function applyProps(object, props) {
1028
1052
  if (instance.eventCount && object2.raycast !== null) {
1029
1053
  rootState.internal.interaction.push(object2);
1030
1054
  }
1055
+ }
1056
+ if (instance?.parent && rootState?.internal && instance.object?.isObject3D) {
1057
+ const object2 = instance.object;
1031
1058
  const root = findInitialRoot(instance);
1032
1059
  const visibilityHandlers = {
1033
1060
  onFramed: instance.handlers.onFramed,
@@ -1035,7 +1062,9 @@ function applyProps(object, props) {
1035
1062
  onVisible: instance.handlers.onVisible
1036
1063
  };
1037
1064
  if (hasVisibilityHandlers(visibilityHandlers)) {
1038
- registerVisibility(root, object2, visibilityHandlers);
1065
+ if (!updateVisibilityHandlers(root, object2, visibilityHandlers)) {
1066
+ registerVisibility(root, object2, visibilityHandlers);
1067
+ }
1039
1068
  } else {
1040
1069
  unregisterVisibility(root, object2);
1041
1070
  }
@@ -1563,6 +1592,7 @@ function createPointerEvents(store) {
1563
1592
  set((state) => ({ events: { ...state.events, connected: void 0 } }));
1564
1593
  }
1565
1594
  },
1595
+ // PREVIEW (A8): the XR pointer registration API may change before stable.
1566
1596
  registerPointer: (config) => {
1567
1597
  const pointerId = nextXRPointerId++;
1568
1598
  xrPointers.set(pointerId, config);
@@ -1649,6 +1679,13 @@ function notifyAlpha({ message, link }) {
1649
1679
  }
1650
1680
  }
1651
1681
 
1682
+ const INTERNAL_LIB_MARKERS = ["zustand", "setState", "Object.assign"];
1683
+ function isInternalRendererAccess(stack, callerDepth = 2) {
1684
+ if (!stack) return false;
1685
+ const caller = stack.split("\n")[callerDepth] ?? "";
1686
+ return INTERNAL_LIB_MARKERS.some((m) => caller.includes(m));
1687
+ }
1688
+
1652
1689
  const R3F_CONTEXT = Symbol.for("@react-three/fiber.context");
1653
1690
  const context = globalThis[R3F_CONTEXT] ?? (globalThis[R3F_CONTEXT] = React__namespace.createContext(null));
1654
1691
  const createStore = (invalidate, advance) => {
@@ -1781,6 +1818,7 @@ const createStore = (invalidate, advance) => {
1781
1818
  }),
1782
1819
  setFrameloop: (frameloop = "always") => {
1783
1820
  set(() => ({ frameloop }));
1821
+ scheduler.getScheduler().frameloop = frameloop;
1784
1822
  },
1785
1823
  setError: (error) => set(() => ({ error })),
1786
1824
  error: null,
@@ -1848,8 +1886,7 @@ const createStore = (invalidate, advance) => {
1848
1886
  const currentState = rootStore.getState();
1849
1887
  if (!currentState.isLegacy && currentState.internal.actualRenderer) {
1850
1888
  const stack = new Error().stack || "";
1851
- const isInternalAccess = stack.includes("zustand") || stack.includes("setState") || stack.includes("Object.assign") || stack.includes("react-three-fiber/packages/fiber/src/core");
1852
- if (!isInternalAccess) {
1889
+ if (!isInternalRendererAccess(stack)) {
1853
1890
  const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
1854
1891
  notifyDepreciated({
1855
1892
  heading: "Accessing state.gl in WebGPU mode",
@@ -1962,19 +1999,25 @@ function loadingFn(extensions, onProgress) {
1962
1999
  );
1963
2000
  };
1964
2001
  }
1965
- function useLoader(loader, input, extensions, onProgress) {
1966
- const keys = Array.isArray(input) ? input : [input];
2002
+ function useLoader(loader, input, extensions, onProgress, cacheKey) {
2003
+ const inputs = Array.isArray(input) ? input : [input];
2004
+ const cacheKeys = Array.isArray(cacheKey ?? input) ? cacheKey ?? input : [cacheKey ?? input];
1967
2005
  const fn = loadingFn(extensions, onProgress);
1968
- const results = keys.map((key) => suspendReact.suspend(fn, [loader, key], { equal: is.equ }));
2006
+ const results = cacheKeys.map(
2007
+ (key, index) => suspendReact.suspend(() => fn(loader, inputs[index]), [loader, key], { equal: is.equ })
2008
+ );
1969
2009
  return Array.isArray(input) ? results : results[0];
1970
2010
  }
1971
- useLoader.preload = function(loader, input, extensions, onProgress) {
1972
- const keys = Array.isArray(input) ? input : [input];
1973
- keys.forEach((key) => suspendReact.preload(loadingFn(extensions, onProgress), [loader, key]));
2011
+ useLoader.preload = function(loader, input, extensions, onProgress, cacheKey) {
2012
+ const inputs = Array.isArray(input) ? input : [input];
2013
+ const cacheKeys = Array.isArray(cacheKey ?? input) ? cacheKey ?? input : [cacheKey ?? input];
2014
+ cacheKeys.forEach(
2015
+ (key, index) => suspendReact.preload(() => loadingFn(extensions, onProgress)(loader, inputs[index]), [loader, key])
2016
+ );
1974
2017
  };
1975
- useLoader.clear = function(loader, input) {
1976
- const keys = Array.isArray(input) ? input : [input];
1977
- keys.forEach((key) => suspendReact.clear([loader, key]));
2018
+ useLoader.clear = function(loader, input, cacheKey) {
2019
+ const cacheKeys = Array.isArray(cacheKey ?? input) ? cacheKey ?? input : [cacheKey ?? input];
2020
+ cacheKeys.forEach((key) => suspendReact.clear([loader, key]));
1978
2021
  };
1979
2022
  useLoader.loader = getLoader;
1980
2023
 
@@ -2396,7 +2439,7 @@ function advance(timestamp) {
2396
2439
  scheduler.getScheduler().step(timestamp);
2397
2440
  }
2398
2441
 
2399
- const version = "10.0.0-alpha.2";
2442
+ const version = "10.0.0-alpha.3";
2400
2443
  const packageData = {
2401
2444
  version: version};
2402
2445
 
@@ -13835,6 +13878,7 @@ function swapInstances() {
13835
13878
  }
13836
13879
  }
13837
13880
  if (instance.isHidden) unhideInstance(instance);
13881
+ if (isObject3D(instance.object)) removeInteractivity(findInitialRoot(instance), instance.object);
13838
13882
  if (instance.object.__r3f) delete instance.object.__r3f;
13839
13883
  if (instance.type !== "primitive") disposeOnIdle(instance.object);
13840
13884
  }
@@ -13847,6 +13891,8 @@ function swapInstances() {
13847
13891
  instance.object.__r3f = instance;
13848
13892
  setFiberRef(fiber, instance.object);
13849
13893
  delete instance.appliedOnce;
13894
+ instance.eventCount = 0;
13895
+ instance.handlers = {};
13850
13896
  applyProps(instance.object, instance.props);
13851
13897
  if (instance.props.attach) {
13852
13898
  attach(parent, instance);
@@ -14062,11 +14108,15 @@ const reconciler = /* @__PURE__ */ createReconciler({
14062
14108
  cloneMutableTextInstance(textInstance) {
14063
14109
  return textInstance;
14064
14110
  },
14111
+ // React 19.2 added these for root-level view transitions. They only run for hosts that opt into
14112
+ // view transitions by returning a container from cloneRootViewTransitionContainer; R3F renders
14113
+ // into a WebGL/WebGPU canvas, where cloning the root has no meaning, so we never opt in and
14114
+ // React never calls them. They stay as no-ops rather than throws: the host config is shared by
14115
+ // every root, and a throw here would turn a React-internal probe into a hard crash for users.
14116
+ // Guarded by a test asserting a full render + unmount never reaches them.
14065
14117
  cloneRootViewTransitionContainer(_rootContainer) {
14066
- throw new Error("Not implemented.");
14067
14118
  },
14068
14119
  removeRootViewTransitionClone(_rootContainer, _clone) {
14069
- throw new Error("Not implemented.");
14070
14120
  },
14071
14121
  // https://github.com/facebook/react/pull/32465
14072
14122
  createFragmentInstance: (_fiber) => null,
@@ -14151,6 +14201,7 @@ function createRoot(canvas) {
14151
14201
  const lastConfiguredProps = {};
14152
14202
  let configured = false;
14153
14203
  let pending = null;
14204
+ let rendererSetup = null;
14154
14205
  return {
14155
14206
  async configure(props = {}) {
14156
14207
  let resolve;
@@ -14181,7 +14232,7 @@ function createRoot(canvas) {
14181
14232
  forceEven
14182
14233
  } = props;
14183
14234
  const textureColorSpace = is.obj(glConfig) && !is.fun(glConfig) && !isRenderer(glConfig) && glConfig.textureColorSpace || is.obj(rendererConfig) && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && rendererConfig.textureColorSpace || webgpu.SRGBColorSpace;
14184
- const state = store.getState();
14235
+ let state = store.getState();
14185
14236
  const defaultGLProps = {
14186
14237
  canvas,
14187
14238
  powerPreference: "high-performance",
@@ -14209,56 +14260,68 @@ function createRoot(canvas) {
14209
14260
  "The `primaryCanvas` prop for multi-canvas rendering cannot be used with WebGL. Remove the `gl` prop or use WebGPU."
14210
14261
  );
14211
14262
  }
14212
- if (wantsGL && !state.internal.actualRenderer) {
14213
- renderer = await resolveRenderer(glConfig, defaultGLProps, three.WebGLRenderer);
14214
- state.internal.actualRenderer = renderer;
14215
- state.set({ isLegacy: true, gl: renderer, renderer, primaryStore: store });
14216
- } else if (!wantsGL && primaryCanvas && !state.internal.actualRenderer) {
14217
- const primary = await waitForPrimary(primaryCanvas);
14218
- renderer = primary.renderer;
14219
- state.internal.actualRenderer = renderer;
14220
- const canvasTarget = new webgpu.CanvasTarget(canvas);
14221
- primary.store.setState((prev) => ({
14222
- internal: { ...prev.internal, isMultiCanvas: true }
14223
- }));
14224
- state.set((prev) => ({
14225
- webGPUSupported: primary.store.getState().webGPUSupported,
14226
- renderer,
14227
- primaryStore: primary.store,
14228
- internal: {
14229
- ...prev.internal,
14230
- canvasTarget,
14231
- isMultiCanvas: true,
14232
- isSecondary: true,
14233
- targetId: primaryCanvas
14234
- }
14235
- }));
14236
- } else if (!wantsGL && !state.internal.actualRenderer) {
14237
- renderer = await resolveRenderer(rendererConfig, defaultGPUProps, webgpu.WebGPURenderer);
14238
- if (!renderer.hasInitialized?.()) {
14239
- const size2 = computeInitialSize(canvas, propsSize);
14240
- if (size2.width > 0 && size2.height > 0) {
14241
- const pixelRatio = calculateDpr(dpr);
14242
- canvas.width = size2.width * pixelRatio;
14243
- canvas.height = size2.height * pixelRatio;
14244
- }
14245
- await renderer.init();
14246
- }
14247
- const backend = renderer.backend;
14248
- const isWebGPUBackend = backend && "isWebGPUBackend" in backend;
14249
- state.internal.actualRenderer = renderer;
14250
- state.set({ webGPUSupported: isWebGPUBackend, renderer, primaryStore: store });
14251
- if (canvasId && !state.internal.isSecondary) {
14252
- const canvasTarget = new webgpu.CanvasTarget(canvas);
14253
- const unregisterPrimary = registerPrimary(canvasId, renderer, store);
14254
- state.set((prev) => ({
14255
- internal: {
14256
- ...prev.internal,
14257
- canvasTarget,
14258
- unregisterPrimary
14263
+ if (!state.internal.actualRenderer) {
14264
+ if (!rendererSetup) {
14265
+ rendererSetup = (async () => {
14266
+ if (wantsGL) {
14267
+ renderer = await resolveRenderer(glConfig, defaultGLProps, three.WebGLRenderer);
14268
+ state.internal.actualRenderer = renderer;
14269
+ state.set({ isLegacy: true, gl: renderer, renderer, primaryStore: store });
14270
+ } else if (!wantsGL && primaryCanvas) {
14271
+ const primary = await waitForPrimary(primaryCanvas);
14272
+ renderer = primary.renderer;
14273
+ state.internal.actualRenderer = renderer;
14274
+ const canvasTarget = new webgpu.CanvasTarget(canvas);
14275
+ primary.store.setState((prev) => ({
14276
+ internal: { ...prev.internal, isMultiCanvas: true }
14277
+ }));
14278
+ state.set((prev) => ({
14279
+ webGPUSupported: primary.store.getState().webGPUSupported,
14280
+ renderer,
14281
+ primaryStore: primary.store,
14282
+ internal: {
14283
+ ...prev.internal,
14284
+ canvasTarget,
14285
+ isMultiCanvas: true,
14286
+ isSecondary: true,
14287
+ targetId: primaryCanvas
14288
+ }
14289
+ }));
14290
+ } else if (!wantsGL) {
14291
+ renderer = await resolveRenderer(rendererConfig, defaultGPUProps, webgpu.WebGPURenderer);
14292
+ if (!renderer.hasInitialized?.()) {
14293
+ const size2 = computeInitialSize(canvas, propsSize);
14294
+ if (size2.width > 0 && size2.height > 0) {
14295
+ const pixelRatio = calculateDpr(dpr);
14296
+ canvas.width = size2.width * pixelRatio;
14297
+ canvas.height = size2.height * pixelRatio;
14298
+ }
14299
+ await renderer.init();
14300
+ }
14301
+ const backend = renderer.backend;
14302
+ const isWebGPUBackend = backend && "isWebGPUBackend" in backend;
14303
+ state.internal.actualRenderer = renderer;
14304
+ state.set({ webGPUSupported: isWebGPUBackend, renderer, primaryStore: store });
14305
+ if (canvasId && !state.internal.isSecondary) {
14306
+ const canvasTarget = new webgpu.CanvasTarget(canvas);
14307
+ const unregisterPrimary = registerPrimary(canvasId, renderer, store);
14308
+ state.set((prev) => ({
14309
+ internal: {
14310
+ ...prev.internal,
14311
+ canvasTarget,
14312
+ unregisterPrimary
14313
+ }
14314
+ }));
14315
+ }
14259
14316
  }
14260
- }));
14317
+ })().catch((err) => {
14318
+ rendererSetup = null;
14319
+ throw err;
14320
+ });
14261
14321
  }
14322
+ await rendererSetup;
14323
+ state = store.getState();
14324
+ renderer = state.internal.actualRenderer;
14262
14325
  }
14263
14326
  let raycaster = state.raycaster;
14264
14327
  if (!raycaster) state.set({ raycaster: raycaster = new webgpu.Raycaster() });
@@ -14608,7 +14671,8 @@ function unmountComponentAtNode(canvas, callback) {
14608
14671
  dispose(state.scene);
14609
14672
  _roots.delete(canvas);
14610
14673
  if (callback) callback(canvas);
14611
- } catch {
14674
+ } catch (error) {
14675
+ console.warn("[R3F] Error while unmounting root; teardown may be incomplete:", error);
14612
14676
  }
14613
14677
  }, 500);
14614
14678
  }
@@ -14671,6 +14735,7 @@ function PortalInner({ state = {}, children, container }) {
14671
14735
  viewport = rootState.viewport.getCurrentViewport(camera, new webgpu.Vector3(), resolvedSize);
14672
14736
  if (camera !== rootState.camera) updateCamera(camera, resolvedSize);
14673
14737
  }
14738
+ const set = injectState.set;
14674
14739
  return {
14675
14740
  // The intersect consists of the previous root state
14676
14741
  ...rootState,
@@ -14689,7 +14754,7 @@ function PortalInner({ state = {}, children, container }) {
14689
14754
  size: resolvedSize,
14690
14755
  viewport: { ...rootState.viewport, ...viewport },
14691
14756
  // Layers are allowed to override events
14692
- setEvents: (events2) => injectState.set((state2) => ({ ...state2, events: { ...state2.events, ...events2 } })),
14757
+ setEvents: (events2) => set((state2) => ({ ...state2, events: { ...state2.events, ...events2 } })),
14693
14758
  // Container for child attachment - the portalScene (injected or container itself)
14694
14759
  internal: { ...rootState.internal, ...injectState.internal, container: portalScene }
14695
14760
  };
@@ -14748,14 +14813,49 @@ function parseBackground(background) {
14748
14813
  return null;
14749
14814
  }
14750
14815
 
14816
+ function parseRendererConfig(rendererProp) {
14817
+ const isRendererConfig = typeof rendererProp === "object" && rendererProp !== null && !("render" in rendererProp) && ("primaryCanvas" in rendererProp || "scheduler" in rendererProp);
14818
+ if (isRendererConfig) {
14819
+ const { primaryCanvas: pc, scheduler: sc, ...rest } = rendererProp;
14820
+ return {
14821
+ primaryCanvas: pc,
14822
+ scheduler: sc,
14823
+ renderer: Object.keys(rest).length > 0 ? rest : rendererProp
14824
+ };
14825
+ }
14826
+ return { primaryCanvas: void 0, scheduler: void 0, renderer: rendererProp };
14827
+ }
14828
+
14829
+ const RESOURCE_KINDS = ["nodes", "uniforms", "buffers", "gpuStorage"];
14830
+ const registries = /* @__PURE__ */ new WeakMap();
14831
+ function getKindRegistry(store, kind) {
14832
+ let registry = registries.get(store);
14833
+ if (!registry) {
14834
+ registry = {
14835
+ nodes: { valid: /* @__PURE__ */ new Set(), staged: /* @__PURE__ */ new Map() },
14836
+ uniforms: { valid: /* @__PURE__ */ new Set(), staged: /* @__PURE__ */ new Map() },
14837
+ buffers: { valid: /* @__PURE__ */ new Set(), staged: /* @__PURE__ */ new Map() },
14838
+ gpuStorage: { valid: /* @__PURE__ */ new Set(), staged: /* @__PURE__ */ new Map() }
14839
+ };
14840
+ registries.set(store, registry);
14841
+ }
14842
+ return registry[kind];
14843
+ }
14844
+ function invalidateResource(store, kind, scopeKey) {
14845
+ const registry = getKindRegistry(store, kind);
14846
+ {
14847
+ registry.valid.clear();
14848
+ registry.staged.clear();
14849
+ }
14850
+ }
14851
+ function invalidateAllResources(store) {
14852
+ for (const kind of RESOURCE_KINDS) invalidateResource(store, kind);
14853
+ }
14854
+
14751
14855
  function clearHmrCaches(store) {
14752
- store.setState((state) => ({
14753
- nodes: {},
14754
- uniforms: {},
14755
- buffers: {},
14756
- gpuStorage: {},
14757
- _hmrVersion: state._hmrVersion + 1
14758
- }));
14856
+ const primary = store.getState().primaryStore ?? store;
14857
+ invalidateAllResources(primary);
14858
+ primary.setState((state) => ({ _hmrVersion: state._hmrVersion + 1 }));
14759
14859
  }
14760
14860
 
14761
14861
  function CanvasImpl({
@@ -14791,18 +14891,7 @@ function CanvasImpl({
14791
14891
  forceEven,
14792
14892
  ...props
14793
14893
  }) {
14794
- const isRendererConfig = typeof rendererProp === "object" && rendererProp !== null && !("render" in rendererProp) && ("primaryCanvas" in rendererProp || "scheduler" in rendererProp);
14795
- let primaryCanvas;
14796
- let scheduler;
14797
- let renderer;
14798
- if (isRendererConfig) {
14799
- const { primaryCanvas: pc, scheduler: sc, ...rest } = rendererProp;
14800
- primaryCanvas = pc;
14801
- scheduler = sc;
14802
- renderer = Object.keys(rest).length > 0 ? rest : rendererProp;
14803
- } else {
14804
- renderer = rendererProp;
14805
- }
14894
+ const { primaryCanvas, scheduler, renderer } = parseRendererConfig(rendererProp);
14806
14895
  React__namespace.useMemo(() => extend(THREE), []);
14807
14896
  const Bridge = useBridge();
14808
14897
  const backgroundProps = React__namespace.useMemo(() => parseBackground(background), [background]);
@@ -14847,6 +14936,7 @@ function CanvasImpl({
14847
14936
  const handleDropMissed = useMutableCallback(onDropMissed);
14848
14937
  const [block, setBlock] = React__namespace.useState(false);
14849
14938
  const [error, setError] = React__namespace.useState(false);
14939
+ const [fallbackVisible, setFallbackVisible] = React__namespace.useState(false);
14850
14940
  if (block) throw block;
14851
14941
  if (error) throw error;
14852
14942
  const root = React__namespace.useRef(null);
@@ -14854,6 +14944,7 @@ function CanvasImpl({
14854
14944
  const unsubscribeErrorRef = React__namespace.useRef(null);
14855
14945
  useIsomorphicLayoutEffect(() => {
14856
14946
  effectActiveRef.current = true;
14947
+ if (fallbackVisible) return;
14857
14948
  const canvas = canvasRef.current;
14858
14949
  if (effectiveSize.width > 0 && effectiveSize.height > 0 && canvas) {
14859
14950
  if (!root.current) {
@@ -14874,7 +14965,7 @@ function CanvasImpl({
14874
14965
  }
14875
14966
  async function run() {
14876
14967
  if (!effectActiveRef.current || !root.current) return;
14877
- await root.current.configure({
14968
+ const configured = await root.current.configure({
14878
14969
  id,
14879
14970
  primaryCanvas,
14880
14971
  scheduler,
@@ -14915,8 +15006,20 @@ function CanvasImpl({
14915
15006
  }
14916
15007
  onCreated?.(state);
14917
15008
  }
14918
- });
14919
- if (!effectActiveRef.current || !root.current) return;
15009
+ }).then(
15010
+ () => true,
15011
+ // Renderer setup failed (e.g. no WebGL/WebGPU support). The `fallback` prop lives
15012
+ // inside <canvas>, which browsers don't display, so surface it as visible DOM
15013
+ // instead; with no fallback, rethrow to an external error boundary. (#3757)
15014
+ (setupError) => {
15015
+ if (effectActiveRef.current) {
15016
+ if (fallback != null) setFallbackVisible(true);
15017
+ else setError(setupError);
15018
+ }
15019
+ return false;
15020
+ }
15021
+ );
15022
+ if (!configured || !effectActiveRef.current || !root.current) return;
14920
15023
  root.current.render(
14921
15024
  /* @__PURE__ */ jsxRuntime.jsx(Bridge, { children: /* @__PURE__ */ jsxRuntime.jsx(ErrorBoundary, { set: setError, children: /* @__PURE__ */ jsxRuntime.jsxs(React__namespace.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(Block, { set: setBlock }), children: [
14922
15025
  backgroundProps && /* @__PURE__ */ jsxRuntime.jsx(Environment, { ...backgroundProps }),
@@ -14979,7 +15082,11 @@ function CanvasImpl({
14979
15082
  ...style
14980
15083
  },
14981
15084
  ...props,
14982
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx(
15085
+ children: fallbackVisible ? (
15086
+ // Renderer setup failed: render the fallback as visible DOM. Inside <canvas> (below)
15087
+ // it exists in the tree but browsers never paint it, which is the whole bug (#3757).
15088
+ fallback
15089
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx(
14983
15090
  "canvas",
14984
15091
  {
14985
15092
  ref: canvasRef,