@react-three/fiber 10.0.0-canary.5c62eb9 → 10.0.0-canary.6d6e5ab

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
  });
@@ -450,7 +454,6 @@ function Environment(props) {
450
454
  var __defProp = Object.defineProperty;
451
455
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
452
456
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
453
- const act = React__namespace["act"];
454
457
  const useIsomorphicLayoutEffect = /* @__PURE__ */ (() => typeof window !== "undefined" && (window.document?.createElement || window.navigator?.product === "ReactNative"))() ? React__namespace.useLayoutEffect : React__namespace.useEffect;
455
458
  function useMutableCallback(fn) {
456
459
  const ref = React__namespace.useRef(fn);
@@ -626,8 +629,7 @@ function prepare(target, root, type, props) {
626
629
  object,
627
630
  eventCount: 0,
628
631
  handlers: {},
629
- isHidden: false,
630
- deferredRefs: []
632
+ isHidden: false
631
633
  };
632
634
  if (object) object.__r3f = instance;
633
635
  }
@@ -784,6 +786,17 @@ function unregisterVisibility(store, object) {
784
786
  internal.visibilityRegistry.delete(object.uuid);
785
787
  internal.occlusionCache.delete(object);
786
788
  }
789
+ function updateVisibilityHandlers(store, object, handlers) {
790
+ const { internal } = store.getState();
791
+ const entry = internal.visibilityRegistry.get(object.uuid);
792
+ if (!entry) return false;
793
+ entry.handlers = handlers;
794
+ if ((handlers.onOccluded || handlers.onVisible) && !internal.occlusionEnabled) {
795
+ object.occlusionTest = true;
796
+ enableOcclusion(store);
797
+ }
798
+ return true;
799
+ }
787
800
  function checkVisibility(state) {
788
801
  const { internal, camera } = state;
789
802
  const registry = internal.visibilityRegistry;
@@ -944,9 +957,9 @@ function diffProps(instance, newProps) {
944
957
  const { root, key } = resolve(instance.object, prop);
945
958
  if (root.constructor && root.constructor.length === 0) {
946
959
  const ctor = getMemoizedPrototype(root);
947
- if (!is.und(ctor)) changedProps[key] = ctor[key];
960
+ if (!is.und(ctor)) changedProps[prop] = ctor[key];
948
961
  } else {
949
- changedProps[key] = 0;
962
+ changedProps[prop] = 0;
950
963
  }
951
964
  }
952
965
  return changedProps;
@@ -971,10 +984,7 @@ function applyProps(object, props) {
971
984
  continue;
972
985
  }
973
986
  if (value === void 0) continue;
974
- if (isFromRef(value)) {
975
- instance?.deferredRefs?.push({ prop, ref: value[FROM_REF] });
976
- continue;
977
- }
987
+ if (isFromRef(value)) continue;
978
988
  if (isOnce(value)) {
979
989
  if (instance?.appliedOnce?.has(prop)) continue;
980
990
  if (instance) {
@@ -1010,6 +1020,19 @@ function applyProps(object, props) {
1010
1020
  } else if (isVectorLike(target) && is.num(value)) {
1011
1021
  if ("setScalar" in target && typeof target.setScalar === "function") target.setScalar(value);
1012
1022
  else target.set(value);
1023
+ } else if (root instanceof webgpu.ShaderMaterial && key === "uniforms" && is.obj(value)) {
1024
+ if (!is.obj(root.uniforms)) root.uniforms = {};
1025
+ const uniforms = root.uniforms;
1026
+ const nextUniforms = value;
1027
+ for (const name in nextUniforms) {
1028
+ const uniform = nextUniforms[name];
1029
+ const targetUniform = uniforms[name];
1030
+ if (targetUniform) Object.assign(targetUniform, uniform);
1031
+ else {
1032
+ const nextUniform = uniform instanceof webgpu.Uniform ? uniform.clone() : new webgpu.Uniform(uniform.value);
1033
+ uniforms[name] = nextUniform;
1034
+ }
1035
+ }
1013
1036
  } else {
1014
1037
  root[key] = value;
1015
1038
  if (key.endsWith("Node") && root.isMaterial) {
@@ -1028,6 +1051,9 @@ function applyProps(object, props) {
1028
1051
  if (instance.eventCount && object2.raycast !== null) {
1029
1052
  rootState.internal.interaction.push(object2);
1030
1053
  }
1054
+ }
1055
+ if (instance?.parent && rootState?.internal && instance.object?.isObject3D) {
1056
+ const object2 = instance.object;
1031
1057
  const root = findInitialRoot(instance);
1032
1058
  const visibilityHandlers = {
1033
1059
  onFramed: instance.handlers.onFramed,
@@ -1035,7 +1061,9 @@ function applyProps(object, props) {
1035
1061
  onVisible: instance.handlers.onVisible
1036
1062
  };
1037
1063
  if (hasVisibilityHandlers(visibilityHandlers)) {
1038
- registerVisibility(root, object2, visibilityHandlers);
1064
+ if (!updateVisibilityHandlers(root, object2, visibilityHandlers)) {
1065
+ registerVisibility(root, object2, visibilityHandlers);
1066
+ }
1039
1067
  } else {
1040
1068
  unregisterVisibility(root, object2);
1041
1069
  }
@@ -1563,6 +1591,7 @@ function createPointerEvents(store) {
1563
1591
  set((state) => ({ events: { ...state.events, connected: void 0 } }));
1564
1592
  }
1565
1593
  },
1594
+ // PREVIEW (A8): the XR pointer registration API may change before stable.
1566
1595
  registerPointer: (config) => {
1567
1596
  const pointerId = nextXRPointerId++;
1568
1597
  xrPointers.set(pointerId, config);
@@ -1650,11 +1679,10 @@ function notifyAlpha({ message, link }) {
1650
1679
  }
1651
1680
 
1652
1681
  const INTERNAL_LIB_MARKERS = ["zustand", "setState", "Object.assign"];
1653
- const INTERNAL_FIBER_MARKERS = ["packages/fiber/src", "packages/fiber/dist", "@react-three/fiber", "@react-three_fiber"];
1654
1682
  function isInternalRendererAccess(stack, callerDepth = 2) {
1655
1683
  if (!stack) return false;
1656
1684
  const caller = stack.split("\n")[callerDepth] ?? "";
1657
- return INTERNAL_LIB_MARKERS.some((m) => caller.includes(m)) || INTERNAL_FIBER_MARKERS.some((m) => caller.includes(m));
1685
+ return INTERNAL_LIB_MARKERS.some((m) => caller.includes(m));
1658
1686
  }
1659
1687
 
1660
1688
  const R3F_CONTEXT = Symbol.for("@react-three/fiber.context");
@@ -1789,6 +1817,7 @@ const createStore = (invalidate, advance) => {
1789
1817
  }),
1790
1818
  setFrameloop: (frameloop = "always") => {
1791
1819
  set(() => ({ frameloop }));
1820
+ scheduler.getScheduler().frameloop = frameloop;
1792
1821
  },
1793
1822
  setError: (error) => set(() => ({ error })),
1794
1823
  error: null,
@@ -1969,19 +1998,25 @@ function loadingFn(extensions, onProgress) {
1969
1998
  );
1970
1999
  };
1971
2000
  }
1972
- function useLoader(loader, input, extensions, onProgress) {
1973
- const keys = Array.isArray(input) ? input : [input];
2001
+ function useLoader(loader, input, extensions, onProgress, cacheKey) {
2002
+ const inputs = Array.isArray(input) ? input : [input];
2003
+ const cacheKeys = Array.isArray(cacheKey ?? input) ? cacheKey ?? input : [cacheKey ?? input];
1974
2004
  const fn = loadingFn(extensions, onProgress);
1975
- const results = keys.map((key) => suspendReact.suspend(fn, [loader, key], { equal: is.equ }));
2005
+ const results = cacheKeys.map(
2006
+ (key, index) => suspendReact.suspend(() => fn(loader, inputs[index]), [loader, key], { equal: is.equ })
2007
+ );
1976
2008
  return Array.isArray(input) ? results : results[0];
1977
2009
  }
1978
- useLoader.preload = function(loader, input, extensions, onProgress) {
1979
- const keys = Array.isArray(input) ? input : [input];
1980
- keys.forEach((key) => suspendReact.preload(loadingFn(extensions, onProgress), [loader, key]));
2010
+ useLoader.preload = function(loader, input, extensions, onProgress, cacheKey) {
2011
+ const inputs = Array.isArray(input) ? input : [input];
2012
+ const cacheKeys = Array.isArray(cacheKey ?? input) ? cacheKey ?? input : [cacheKey ?? input];
2013
+ cacheKeys.forEach(
2014
+ (key, index) => suspendReact.preload(() => loadingFn(extensions, onProgress)(loader, inputs[index]), [loader, key])
2015
+ );
1981
2016
  };
1982
- useLoader.clear = function(loader, input) {
1983
- const keys = Array.isArray(input) ? input : [input];
1984
- keys.forEach((key) => suspendReact.clear([loader, key]));
2017
+ useLoader.clear = function(loader, input, cacheKey) {
2018
+ const cacheKeys = Array.isArray(cacheKey ?? input) ? cacheKey ?? input : [cacheKey ?? input];
2019
+ cacheKeys.forEach((key) => suspendReact.clear([loader, key]));
1985
2020
  };
1986
2021
  useLoader.loader = getLoader;
1987
2022
 
@@ -2403,7 +2438,7 @@ function advance(timestamp) {
2403
2438
  scheduler.getScheduler().step(timestamp);
2404
2439
  }
2405
2440
 
2406
- const version = "10.0.0-alpha.2";
2441
+ const version = "10.0.0-alpha.3";
2407
2442
  const packageData = {
2408
2443
  version: version};
2409
2444
 
@@ -13659,7 +13694,7 @@ function n0() {
13659
13694
  var t0 = n0();
13660
13695
  const r0 = Xb(t0);
13661
13696
 
13662
- const t = 1, o = 8, r = 32, e = 2;
13697
+ const t = 1, o = 8, r = 32, e = 2, i$1 = 268435456;
13663
13698
 
13664
13699
  function createReconciler(config) {
13665
13700
  const reconciler2 = r0(config);
@@ -13824,7 +13859,15 @@ function setFiberRef(fiber, publicInstance) {
13824
13859
  }
13825
13860
  }
13826
13861
  const reconstructed = [];
13827
- function swapInstances() {
13862
+ function flushReconstructedInstances() {
13863
+ if (reconstructed.length === 0) return;
13864
+ try {
13865
+ swapReconstructedInstances();
13866
+ } finally {
13867
+ reconstructed.length = 0;
13868
+ }
13869
+ }
13870
+ function swapReconstructedInstances() {
13828
13871
  for (const [instance] of reconstructed) {
13829
13872
  const parent = instance.parent;
13830
13873
  if (parent) {
@@ -13842,6 +13885,7 @@ function swapInstances() {
13842
13885
  }
13843
13886
  }
13844
13887
  if (instance.isHidden) unhideInstance(instance);
13888
+ if (isObject3D(instance.object)) removeInteractivity(findInitialRoot(instance), instance.object);
13845
13889
  if (instance.object.__r3f) delete instance.object.__r3f;
13846
13890
  if (instance.type !== "primitive") disposeOnIdle(instance.object);
13847
13891
  }
@@ -13854,6 +13898,8 @@ function swapInstances() {
13854
13898
  instance.object.__r3f = instance;
13855
13899
  setFiberRef(fiber, instance.object);
13856
13900
  delete instance.appliedOnce;
13901
+ instance.eventCount = 0;
13902
+ instance.handlers = {};
13857
13903
  applyProps(instance.object, instance.props);
13858
13904
  if (instance.props.attach) {
13859
13905
  attach(parent, instance);
@@ -13870,14 +13916,114 @@ function swapInstances() {
13870
13916
  invalidateInstance(instance);
13871
13917
  }
13872
13918
  }
13873
- reconstructed.length = 0;
13874
13919
  }
13875
13920
  const handleTextInstance = () => {
13876
13921
  };
13877
13922
  const NO_CONTEXT = {};
13878
13923
  let currentUpdatePriority = NoEventPriority;
13879
- const NoFlags = 0;
13880
- const Update = 4;
13924
+ function getEventPriority(type) {
13925
+ switch (type) {
13926
+ case "beforetoggle":
13927
+ case "cancel":
13928
+ case "click":
13929
+ case "close":
13930
+ case "contextmenu":
13931
+ case "copy":
13932
+ case "cut":
13933
+ case "auxclick":
13934
+ case "dblclick":
13935
+ case "dragend":
13936
+ case "dragstart":
13937
+ case "drop":
13938
+ case "focusin":
13939
+ case "focusout":
13940
+ case "input":
13941
+ case "invalid":
13942
+ case "keydown":
13943
+ case "keypress":
13944
+ case "keyup":
13945
+ case "mousedown":
13946
+ case "mouseup":
13947
+ case "paste":
13948
+ case "pause":
13949
+ case "play":
13950
+ case "pointercancel":
13951
+ case "pointerdown":
13952
+ case "pointerup":
13953
+ case "ratechange":
13954
+ case "reset":
13955
+ case "resize":
13956
+ case "seeked":
13957
+ case "submit":
13958
+ case "touchcancel":
13959
+ case "touchend":
13960
+ case "touchstart":
13961
+ case "volumechange":
13962
+ case "change":
13963
+ case "selectionchange":
13964
+ case "compositionstart":
13965
+ case "compositionend":
13966
+ case "compositionupdate":
13967
+ case "beforeinput":
13968
+ case "blur":
13969
+ case "fullscreenchange":
13970
+ case "focus":
13971
+ case "hashchange":
13972
+ case "popstate":
13973
+ case "select":
13974
+ case "selectstart":
13975
+ return e;
13976
+ case "drag":
13977
+ case "dragenter":
13978
+ case "dragexit":
13979
+ case "dragleave":
13980
+ case "dragover":
13981
+ case "mousemove":
13982
+ case "mouseout":
13983
+ case "mouseover":
13984
+ case "pointermove":
13985
+ case "pointerout":
13986
+ case "pointerover":
13987
+ case "scroll":
13988
+ case "touchmove":
13989
+ case "wheel":
13990
+ case "mouseenter":
13991
+ case "mouseleave":
13992
+ case "pointerenter":
13993
+ case "pointerleave":
13994
+ return o;
13995
+ case "message": {
13996
+ switch (Tb.unstable_getCurrentPriorityLevel()) {
13997
+ case Tb.unstable_ImmediatePriority:
13998
+ return e;
13999
+ case Tb.unstable_UserBlockingPriority:
14000
+ return o;
14001
+ case Tb.unstable_NormalPriority:
14002
+ case Tb.unstable_LowPriority:
14003
+ return r;
14004
+ case Tb.unstable_IdlePriority:
14005
+ return i$1;
14006
+ default:
14007
+ return r;
14008
+ }
14009
+ }
14010
+ default:
14011
+ return r;
14012
+ }
14013
+ }
14014
+ function scheduleMicrotask(callback) {
14015
+ if (typeof queueMicrotask === "function") {
14016
+ queueMicrotask(callback);
14017
+ } else if (typeof Promise !== "undefined") {
14018
+ Promise.resolve().then(callback).catch((error) => {
14019
+ setTimeout(() => {
14020
+ throw error;
14021
+ });
14022
+ });
14023
+ } else {
14024
+ setTimeout(callback);
14025
+ }
14026
+ }
13881
14027
  const reconciler = /* @__PURE__ */ createReconciler({
13882
14028
  isPrimaryRenderer: false,
13883
14029
  warnsIfNotActing: false,
@@ -13916,16 +14062,15 @@ const reconciler = /* @__PURE__ */ createReconciler({
13916
14062
  else if (newProps.args?.length !== oldProps.args?.length) reconstruct = true;
13917
14063
  else if (newProps.args?.some((value, index) => value !== oldProps.args?.[index])) reconstruct = true;
13918
14064
  if (reconstruct) {
13919
- reconstructed.push([instance, { ...newProps }, fiber]);
14065
+ reconstructed.push([instance, getInstanceProps(newProps), fiber]);
13920
14066
  } else {
13921
14067
  const changedProps = diffProps(instance, newProps);
13922
- if (Object.keys(changedProps).length) {
13923
- Object.assign(instance.props, changedProps);
13924
- applyProps(instance.object, changedProps);
13925
- }
14068
+ const attach2 = instance.props.attach;
14069
+ instance.props = getInstanceProps(newProps);
14070
+ if (attach2 !== void 0) instance.props.attach = attach2;
14071
+ else delete instance.props.attach;
14072
+ if (Object.keys(changedProps).length) applyProps(instance.object, changedProps);
13926
14073
  }
13927
- const isTailSibling = fiber.sibling === null || (fiber.flags & Update) === NoFlags;
13928
- if (isTailSibling) swapInstances();
13929
14074
  },
13930
14075
  finalizeInitialChildren: (instance) => {
13931
14076
  for (const prop in instance.props) {
@@ -13950,8 +14095,9 @@ const reconciler = /* @__PURE__ */ createReconciler({
13950
14095
  const target = container.getState().internal.container ?? container.getState().scene;
13951
14096
  return prepare(target, container, "", {});
13952
14097
  },
13953
- resetAfterCommit: () => {
13954
- },
14098
+ // Reconstructed instances are swapped once all mutations are committed,
14099
+ // before layout effects run so refs point to the new objects
14100
+ resetAfterCommit: flushReconstructedInstances,
13955
14101
  shouldSetTextContent: () => false,
13956
14102
  clearContainer: () => false,
13957
14103
  hideInstance,
@@ -13959,6 +14105,9 @@ const reconciler = /* @__PURE__ */ createReconciler({
13959
14105
  createTextInstance: handleTextInstance,
13960
14106
  hideTextInstance: handleTextInstance,
13961
14107
  unhideTextInstance: handleTextInstance,
14108
+ // Mirrors react-dom, which flushes discrete work in microtasks
14109
+ supportsMicrotasks: true,
14110
+ scheduleMicrotask,
13962
14111
  scheduleTimeout: typeof setTimeout === "function" ? setTimeout : void 0,
13963
14112
  cancelTimeout: typeof clearTimeout === "function" ? clearTimeout : void 0,
13964
14113
  noTimeout: -1,
@@ -13999,28 +14148,9 @@ const reconciler = /* @__PURE__ */ createReconciler({
13999
14148
  },
14000
14149
  resolveUpdatePriority() {
14001
14150
  if (currentUpdatePriority !== NoEventPriority) return currentUpdatePriority;
14002
- switch (typeof window !== "undefined" && window.event?.type) {
14003
- case "click":
14004
- case "contextmenu":
14005
- case "dblclick":
14006
- case "dragenter":
14007
- case "dragleave":
14008
- case "drop":
14009
- case "pointercancel":
14010
- case "pointerdown":
14011
- case "pointerup":
14012
- return e;
14013
- case "dragover":
14014
- case "pointermove":
14015
- case "pointerout":
14016
- case "pointerover":
14017
- case "pointerenter":
14018
- case "pointerleave":
14019
- case "wheel":
14020
- return o;
14021
- default:
14022
- return r;
14023
- }
14151
+ const eventType = typeof window !== "undefined" ? window.event?.type : void 0;
14152
+ if (eventType === void 0) return r;
14153
+ return getEventPriority(eventType);
14024
14154
  },
14025
14155
  resetFormInstance() {
14026
14156
  },
@@ -14069,11 +14199,15 @@ const reconciler = /* @__PURE__ */ createReconciler({
14069
14199
  cloneMutableTextInstance(textInstance) {
14070
14200
  return textInstance;
14071
14201
  },
14202
+ // React 19.2 added these for root-level view transitions. They only run for hosts that opt into
14203
+ // view transitions by returning a container from cloneRootViewTransitionContainer; R3F renders
14204
+ // into a WebGL/WebGPU canvas, where cloning the root has no meaning, so we never opt in and
14205
+ // React never calls them. They stay as no-ops rather than throws: the host config is shared by
14206
+ // every root, and a throw here would turn a React-internal probe into a hard crash for users.
14207
+ // Guarded by a test asserting a full render + unmount never reaches them.
14072
14208
  cloneRootViewTransitionContainer(_rootContainer) {
14073
- throw new Error("Not implemented.");
14074
14209
  },
14075
14210
  removeRootViewTransitionClone(_rootContainer, _clone) {
14076
- throw new Error("Not implemented.");
14077
14211
  },
14078
14212
  // https://github.com/facebook/react/pull/32465
14079
14213
  createFragmentInstance: (_fiber) => null,
@@ -14158,6 +14292,7 @@ function createRoot(canvas) {
14158
14292
  const lastConfiguredProps = {};
14159
14293
  let configured = false;
14160
14294
  let pending = null;
14295
+ let rendererSetup = null;
14161
14296
  return {
14162
14297
  async configure(props = {}) {
14163
14298
  let resolve;
@@ -14188,7 +14323,7 @@ function createRoot(canvas) {
14188
14323
  forceEven
14189
14324
  } = props;
14190
14325
  const textureColorSpace = is.obj(glConfig) && !is.fun(glConfig) && !isRenderer(glConfig) && glConfig.textureColorSpace || is.obj(rendererConfig) && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && rendererConfig.textureColorSpace || webgpu.SRGBColorSpace;
14191
- const state = store.getState();
14326
+ let state = store.getState();
14192
14327
  const defaultGLProps = {
14193
14328
  canvas,
14194
14329
  powerPreference: "high-performance",
@@ -14216,56 +14351,68 @@ function createRoot(canvas) {
14216
14351
  "The `primaryCanvas` prop for multi-canvas rendering cannot be used with WebGL. Remove the `gl` prop or use WebGPU."
14217
14352
  );
14218
14353
  }
14219
- if (wantsGL && !state.internal.actualRenderer) {
14220
- renderer = await resolveRenderer(glConfig, defaultGLProps, three.WebGLRenderer);
14221
- state.internal.actualRenderer = renderer;
14222
- state.set({ isLegacy: true, gl: renderer, renderer, primaryStore: store });
14223
- } else if (!wantsGL && primaryCanvas && !state.internal.actualRenderer) {
14224
- const primary = await waitForPrimary(primaryCanvas);
14225
- renderer = primary.renderer;
14226
- state.internal.actualRenderer = renderer;
14227
- const canvasTarget = new webgpu.CanvasTarget(canvas);
14228
- primary.store.setState((prev) => ({
14229
- internal: { ...prev.internal, isMultiCanvas: true }
14230
- }));
14231
- state.set((prev) => ({
14232
- webGPUSupported: primary.store.getState().webGPUSupported,
14233
- renderer,
14234
- primaryStore: primary.store,
14235
- internal: {
14236
- ...prev.internal,
14237
- canvasTarget,
14238
- isMultiCanvas: true,
14239
- isSecondary: true,
14240
- targetId: primaryCanvas
14241
- }
14242
- }));
14243
- } else if (!wantsGL && !state.internal.actualRenderer) {
14244
- renderer = await resolveRenderer(rendererConfig, defaultGPUProps, webgpu.WebGPURenderer);
14245
- if (!renderer.hasInitialized?.()) {
14246
- const size2 = computeInitialSize(canvas, propsSize);
14247
- if (size2.width > 0 && size2.height > 0) {
14248
- const pixelRatio = calculateDpr(dpr);
14249
- canvas.width = size2.width * pixelRatio;
14250
- canvas.height = size2.height * pixelRatio;
14251
- }
14252
- await renderer.init();
14253
- }
14254
- const backend = renderer.backend;
14255
- const isWebGPUBackend = backend && "isWebGPUBackend" in backend;
14256
- state.internal.actualRenderer = renderer;
14257
- state.set({ webGPUSupported: isWebGPUBackend, renderer, primaryStore: store });
14258
- if (canvasId && !state.internal.isSecondary) {
14259
- const canvasTarget = new webgpu.CanvasTarget(canvas);
14260
- const unregisterPrimary = registerPrimary(canvasId, renderer, store);
14261
- state.set((prev) => ({
14262
- internal: {
14263
- ...prev.internal,
14264
- canvasTarget,
14265
- unregisterPrimary
14354
+ if (!state.internal.actualRenderer) {
14355
+ if (!rendererSetup) {
14356
+ rendererSetup = (async () => {
14357
+ if (wantsGL) {
14358
+ renderer = await resolveRenderer(glConfig, defaultGLProps, three.WebGLRenderer);
14359
+ state.internal.actualRenderer = renderer;
14360
+ state.set({ isLegacy: true, gl: renderer, renderer, primaryStore: store });
14361
+ } else if (!wantsGL && primaryCanvas) {
14362
+ const primary = await waitForPrimary(primaryCanvas);
14363
+ renderer = primary.renderer;
14364
+ state.internal.actualRenderer = renderer;
14365
+ const canvasTarget = new webgpu.CanvasTarget(canvas);
14366
+ primary.store.setState((prev) => ({
14367
+ internal: { ...prev.internal, isMultiCanvas: true }
14368
+ }));
14369
+ state.set((prev) => ({
14370
+ webGPUSupported: primary.store.getState().webGPUSupported,
14371
+ renderer,
14372
+ primaryStore: primary.store,
14373
+ internal: {
14374
+ ...prev.internal,
14375
+ canvasTarget,
14376
+ isMultiCanvas: true,
14377
+ isSecondary: true,
14378
+ targetId: primaryCanvas
14379
+ }
14380
+ }));
14381
+ } else if (!wantsGL) {
14382
+ renderer = await resolveRenderer(rendererConfig, defaultGPUProps, webgpu.WebGPURenderer);
14383
+ if (!renderer.hasInitialized?.()) {
14384
+ const size2 = computeInitialSize(canvas, propsSize);
14385
+ if (size2.width > 0 && size2.height > 0) {
14386
+ const pixelRatio = calculateDpr(dpr);
14387
+ canvas.width = size2.width * pixelRatio;
14388
+ canvas.height = size2.height * pixelRatio;
14389
+ }
14390
+ await renderer.init();
14391
+ }
14392
+ const backend = renderer.backend;
14393
+ const isWebGPUBackend = backend && "isWebGPUBackend" in backend;
14394
+ state.internal.actualRenderer = renderer;
14395
+ state.set({ webGPUSupported: isWebGPUBackend, renderer, primaryStore: store });
14396
+ if (canvasId && !state.internal.isSecondary) {
14397
+ const canvasTarget = new webgpu.CanvasTarget(canvas);
14398
+ const unregisterPrimary = registerPrimary(canvasId, renderer, store);
14399
+ state.set((prev) => ({
14400
+ internal: {
14401
+ ...prev.internal,
14402
+ canvasTarget,
14403
+ unregisterPrimary
14404
+ }
14405
+ }));
14406
+ }
14266
14407
  }
14267
- }));
14408
+ })().catch((err) => {
14409
+ rendererSetup = null;
14410
+ throw err;
14411
+ });
14268
14412
  }
14413
+ await rendererSetup;
14414
+ state = store.getState();
14415
+ renderer = state.internal.actualRenderer;
14269
14416
  }
14270
14417
  let raycaster = state.raycaster;
14271
14418
  if (!raycaster) state.set({ raycaster: raycaster = new webgpu.Raycaster() });
@@ -14615,7 +14762,8 @@ function unmountComponentAtNode(canvas, callback) {
14615
14762
  dispose(state.scene);
14616
14763
  _roots.delete(canvas);
14617
14764
  if (callback) callback(canvas);
14618
- } catch {
14765
+ } catch (error) {
14766
+ console.warn("[R3F] Error while unmounting root; teardown may be incomplete:", error);
14619
14767
  }
14620
14768
  }, 500);
14621
14769
  }
@@ -14678,6 +14826,7 @@ function PortalInner({ state = {}, children, container }) {
14678
14826
  viewport = rootState.viewport.getCurrentViewport(camera, new webgpu.Vector3(), resolvedSize);
14679
14827
  if (camera !== rootState.camera) updateCamera(camera, resolvedSize);
14680
14828
  }
14829
+ const set = injectState.set;
14681
14830
  return {
14682
14831
  // The intersect consists of the previous root state
14683
14832
  ...rootState,
@@ -14696,7 +14845,7 @@ function PortalInner({ state = {}, children, container }) {
14696
14845
  size: resolvedSize,
14697
14846
  viewport: { ...rootState.viewport, ...viewport },
14698
14847
  // Layers are allowed to override events
14699
- setEvents: (events2) => injectState.set((state2) => ({ ...state2, events: { ...state2.events, ...events2 } })),
14848
+ setEvents: (events2) => set((state2) => ({ ...state2, events: { ...state2.events, ...events2 } })),
14700
14849
  // Container for child attachment - the portalScene (injected or container itself)
14701
14850
  internal: { ...rootState.internal, ...injectState.internal, container: portalScene }
14702
14851
  };
@@ -14768,14 +14917,36 @@ function parseRendererConfig(rendererProp) {
14768
14917
  return { primaryCanvas: void 0, scheduler: void 0, renderer: rendererProp };
14769
14918
  }
14770
14919
 
14920
+ const RESOURCE_KINDS = ["nodes", "uniforms", "buffers", "gpuStorage"];
14921
+ const registries = /* @__PURE__ */ new WeakMap();
14922
+ function getKindRegistry(store, kind) {
14923
+ let registry = registries.get(store);
14924
+ if (!registry) {
14925
+ registry = {
14926
+ nodes: { valid: /* @__PURE__ */ new Set(), staged: /* @__PURE__ */ new Map() },
14927
+ uniforms: { valid: /* @__PURE__ */ new Set(), staged: /* @__PURE__ */ new Map() },
14928
+ buffers: { valid: /* @__PURE__ */ new Set(), staged: /* @__PURE__ */ new Map() },
14929
+ gpuStorage: { valid: /* @__PURE__ */ new Set(), staged: /* @__PURE__ */ new Map() }
14930
+ };
14931
+ registries.set(store, registry);
14932
+ }
14933
+ return registry[kind];
14934
+ }
14935
+ function invalidateResource(store, kind, scopeKey) {
14936
+ const registry = getKindRegistry(store, kind);
14937
+ {
14938
+ registry.valid.clear();
14939
+ registry.staged.clear();
14940
+ }
14941
+ }
14942
+ function invalidateAllResources(store) {
14943
+ for (const kind of RESOURCE_KINDS) invalidateResource(store, kind);
14944
+ }
14945
+
14771
14946
  function clearHmrCaches(store) {
14772
- store.setState((state) => ({
14773
- nodes: {},
14774
- uniforms: {},
14775
- buffers: {},
14776
- gpuStorage: {},
14777
- _hmrVersion: state._hmrVersion + 1
14778
- }));
14947
+ const primary = store.getState().primaryStore ?? store;
14948
+ invalidateAllResources(primary);
14949
+ primary.setState((state) => ({ _hmrVersion: state._hmrVersion + 1 }));
14779
14950
  }
14780
14951
 
14781
14952
  function CanvasImpl({
@@ -14856,6 +15027,7 @@ function CanvasImpl({
14856
15027
  const handleDropMissed = useMutableCallback(onDropMissed);
14857
15028
  const [block, setBlock] = React__namespace.useState(false);
14858
15029
  const [error, setError] = React__namespace.useState(false);
15030
+ const [fallbackVisible, setFallbackVisible] = React__namespace.useState(false);
14859
15031
  if (block) throw block;
14860
15032
  if (error) throw error;
14861
15033
  const root = React__namespace.useRef(null);
@@ -14863,6 +15035,7 @@ function CanvasImpl({
14863
15035
  const unsubscribeErrorRef = React__namespace.useRef(null);
14864
15036
  useIsomorphicLayoutEffect(() => {
14865
15037
  effectActiveRef.current = true;
15038
+ if (fallbackVisible) return;
14866
15039
  const canvas = canvasRef.current;
14867
15040
  if (effectiveSize.width > 0 && effectiveSize.height > 0 && canvas) {
14868
15041
  if (!root.current) {
@@ -14883,7 +15056,7 @@ function CanvasImpl({
14883
15056
  }
14884
15057
  async function run() {
14885
15058
  if (!effectActiveRef.current || !root.current) return;
14886
- await root.current.configure({
15059
+ const configured = await root.current.configure({
14887
15060
  id,
14888
15061
  primaryCanvas,
14889
15062
  scheduler,
@@ -14924,8 +15097,20 @@ function CanvasImpl({
14924
15097
  }
14925
15098
  onCreated?.(state);
14926
15099
  }
14927
- });
14928
- if (!effectActiveRef.current || !root.current) return;
15100
+ }).then(
15101
+ () => true,
15102
+ // Renderer setup failed (e.g. no WebGL/WebGPU support). The `fallback` prop lives
15103
+ // inside <canvas>, which browsers don't display, so surface it as visible DOM
15104
+ // instead; with no fallback, rethrow to an external error boundary. (#3757)
15105
+ (setupError) => {
15106
+ if (effectActiveRef.current) {
15107
+ if (fallback != null) setFallbackVisible(true);
15108
+ else setError(setupError);
15109
+ }
15110
+ return false;
15111
+ }
15112
+ );
15113
+ if (!configured || !effectActiveRef.current || !root.current) return;
14929
15114
  root.current.render(
14930
15115
  /* @__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: [
14931
15116
  backgroundProps && /* @__PURE__ */ jsxRuntime.jsx(Environment, { ...backgroundProps }),
@@ -14988,7 +15173,11 @@ function CanvasImpl({
14988
15173
  ...style
14989
15174
  },
14990
15175
  ...props,
14991
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx(
15176
+ children: fallbackVisible ? (
15177
+ // Renderer setup failed: render the fallback as visible DOM. Inside <canvas> (below)
15178
+ // it exists in the tree but browsers never paint it, which is the whole bug (#3757).
15179
+ fallback
15180
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx(
14992
15181
  "canvas",
14993
15182
  {
14994
15183
  ref: canvasRef,
@@ -15026,7 +15215,6 @@ exports.REACT_INTERNAL_PROPS = REACT_INTERNAL_PROPS;
15026
15215
  exports.RESERVED_PROPS = RESERVED_PROPS;
15027
15216
  exports.Texture = Texture;
15028
15217
  exports._roots = _roots;
15029
- exports.act = act;
15030
15218
  exports.addAfterEffect = addAfterEffect;
15031
15219
  exports.addEffect = addEffect;
15032
15220
  exports.addTail = addTail;