@react-three/fiber 10.0.0-canary.c6846c3 → 10.0.0-canary.ce4bd5f

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
@@ -2,7 +2,6 @@
2
2
 
3
3
  const webgpu = require('three/webgpu');
4
4
  const three = require('three');
5
- const Inspector_js = require('three/addons/inspector/Inspector.js');
6
5
  const jsxRuntime = require('react/jsx-runtime');
7
6
  const React = require('react');
8
7
  const useMeasure = require('react-use-measure');
@@ -52,18 +51,22 @@ const Tb__default = /*#__PURE__*/_interopDefaultCompat(Tb);
52
51
 
53
52
  const R3F_BUILD_LEGACY = true;
54
53
  const R3F_BUILD_WEBGPU = true;
54
+ async function loadInspector() {
55
+ const { Inspector } = await import('three/addons/inspector/Inspector.js');
56
+ return Inspector;
57
+ }
55
58
 
56
59
  const THREE = /*#__PURE__*/_mergeNamespaces({
57
60
  __proto__: null,
58
61
  CubeRenderTarget: webgpu.CubeRenderTarget,
59
62
  CubeRenderTargetCompat: webgpu.CubeRenderTarget,
60
- Inspector: Inspector_js.Inspector,
61
63
  R3F_BUILD_LEGACY: R3F_BUILD_LEGACY,
62
64
  R3F_BUILD_WEBGPU: R3F_BUILD_WEBGPU,
63
65
  RenderTargetCompat: webgpu.RenderTarget,
64
66
  WebGLCubeRenderTarget: three.WebGLCubeRenderTarget,
65
67
  WebGLRenderTarget: three.WebGLRenderTarget,
66
- WebGLRenderer: three.WebGLRenderer
68
+ WebGLRenderer: three.WebGLRenderer,
69
+ loadInspector: loadInspector
67
70
  }, [webgpu__namespace]);
68
71
 
69
72
  const primaryRegistry = /* @__PURE__ */ new Map();
@@ -922,7 +925,7 @@ const createStore = (invalidate, advance) => {
922
925
  size: newSize,
923
926
  viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, newSize) }
924
927
  }));
925
- scheduler.getScheduler().invalidate();
928
+ get().invalidate();
926
929
  }
927
930
  }
928
931
  return;
@@ -937,15 +940,16 @@ const createStore = (invalidate, advance) => {
937
940
  viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, size) },
938
941
  _sizeImperative: true
939
942
  }));
940
- scheduler.getScheduler().invalidate();
943
+ get().invalidate();
941
944
  },
942
945
  setDpr: (dpr) => set((state2) => {
943
946
  const resolved = calculateDpr(dpr);
944
947
  return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
945
948
  }),
946
949
  setFrameloop: (frameloop = "always") => {
950
+ const rootId = get().internal.rootId;
951
+ if (rootId) scheduler.getScheduler().setRootFrameloop(rootId, frameloop);
947
952
  set(() => ({ frameloop }));
948
- scheduler.getScheduler().frameloop = frameloop;
949
953
  },
950
954
  setError: (error) => set(() => ({ error })),
951
955
  error: null,
@@ -1061,17 +1065,10 @@ const createStore = (invalidate, advance) => {
1061
1065
  oldSize = size;
1062
1066
  oldDpr = viewport.dpr;
1063
1067
  updateCamera(camera, size);
1064
- if (internal.isSecondary && canvasTarget) {
1065
- if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
1066
- canvasTarget.setSize(size.width, size.height, false);
1067
- } else {
1068
- if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
1069
- actualRenderer.setSize(size.width, size.height, false);
1070
- if (canvasTarget) {
1071
- if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
1072
- canvasTarget.setSize(size.width, size.height, false);
1073
- }
1074
- }
1068
+ const resizeTarget = canvasTarget ?? actualRenderer;
1069
+ if (viewport.dpr > 0) resizeTarget.setPixelRatio(viewport.dpr);
1070
+ resizeTarget.setSize(size.width, size.height, false);
1071
+ internal.canvasTargetSizeDirty = true;
1075
1072
  }
1076
1073
  if (camera !== oldCamera) {
1077
1074
  oldCamera = camera;
@@ -1133,7 +1130,11 @@ function useLoader(loader, input, extensions, onProgress, cacheKey) {
1133
1130
  const results = cacheKeys.map(
1134
1131
  (key, index) => suspendReact.suspend(() => fn(loader, inputs[index]), [loader, key], { equal: is.equ })
1135
1132
  );
1136
- return Array.isArray(input) ? results : results[0];
1133
+ const stableRef = React.useRef(null);
1134
+ const previous = stableRef.current;
1135
+ const stable = previous !== null && previous.length === results.length && previous.every((value, i) => value === results[i]) ? previous : results;
1136
+ stableRef.current = stable;
1137
+ return Array.isArray(input) ? stable : stable[0];
1137
1138
  }
1138
1139
  useLoader.preload = function(loader, input, extensions, onProgress, cacheKey) {
1139
1140
  const inputs = Array.isArray(input) ? input : [input];
@@ -1253,6 +1254,13 @@ function useFrame(callback, priorityOrOptions) {
1253
1254
  * Use for controlling the entire frame loop, adding phases, etc.
1254
1255
  */
1255
1256
  scheduler: scheduler2,
1257
+ /**
1258
+ * The root that currently owns this job. Resolve on access because ambient
1259
+ * jobs can be adopted by a Canvas after the controls object is created.
1260
+ */
1261
+ get rootId() {
1262
+ return scheduler.getScheduler().getJobRootId(id);
1263
+ },
1256
1264
  /**
1257
1265
  * Manually step this job only.
1258
1266
  * Bypasses FPS limiting - always runs.
@@ -1269,6 +1277,14 @@ function useFrame(callback, priorityOrOptions) {
1269
1277
  stepAll: (timestamp) => {
1270
1278
  scheduler.getScheduler().step(timestamp);
1271
1279
  },
1280
+ /**
1281
+ * Request frames for this job's owning root without waking sibling roots.
1282
+ */
1283
+ invalidate: (frames, stackFrames) => {
1284
+ const scheduler3 = scheduler.getScheduler();
1285
+ const rootId = scheduler3.getJobRootId(id);
1286
+ if (rootId) scheduler3.invalidateRoot(rootId, frames, stackFrames);
1287
+ },
1272
1288
  /**
1273
1289
  * Pause this job (set enabled=false).
1274
1290
  * Job remains registered but won't run.
@@ -1298,6 +1314,11 @@ function getUrls(input) {
1298
1314
  if (Array.isArray(input)) return input;
1299
1315
  return Object.values(input);
1300
1316
  }
1317
+ function inputSignature(input) {
1318
+ if (typeof input === "string") return input;
1319
+ if (Array.isArray(input)) return input.join("\0");
1320
+ return Object.keys(input).sort().map((key) => `${key}\0${input[key]}`).join("");
1321
+ }
1301
1322
  function allUrlsCached(urls, textureCache) {
1302
1323
  return urls.every((url) => textureCache.has(url));
1303
1324
  }
@@ -1323,18 +1344,19 @@ function useTexture(input, optionsOrOnLoad) {
1323
1344
  const onLoadRef = React.useRef(onLoad);
1324
1345
  onLoadRef.current = onLoad;
1325
1346
  const onLoadCalledForRef = React.useRef(null);
1326
- const urls = React.useMemo(() => getUrls(input), [input]);
1347
+ const inputKey = React.useMemo(() => inputSignature(input), [input]);
1348
+ const stableInput = React.useMemo(() => input, [inputKey]);
1349
+ const urls = React.useMemo(() => getUrls(stableInput), [stableInput]);
1327
1350
  const cachedResult = React.useMemo(() => {
1328
1351
  if (!cache) return null;
1329
1352
  const textures = store.getState().textures;
1330
1353
  if (!allUrlsCached(urls, textures)) return null;
1331
- return buildFromCache(input, textures);
1332
- }, [cache, urls, input, store]);
1354
+ return buildFromCache(stableInput, textures);
1355
+ }, [cache, urls, stableInput, store]);
1333
1356
  const loadedTextures = useLoader(
1334
1357
  webgpu.TextureLoader,
1335
- IsObject(input) ? Object.values(input) : input
1358
+ IsObject(stableInput) ? Object.values(stableInput) : stableInput
1336
1359
  );
1337
- const inputKey = urls.join("\0");
1338
1360
  React.useLayoutEffect(() => {
1339
1361
  if (cachedResult) return;
1340
1362
  if (onLoadCalledForRef.current === inputKey) return;
@@ -1361,28 +1383,28 @@ function useTexture(input, optionsOrOnLoad) {
1361
1383
  }, [renderer, loadedTextures, cachedResult]);
1362
1384
  const mappedTextures = React.useMemo(() => {
1363
1385
  if (cachedResult) return cachedResult;
1364
- if (IsObject(input)) {
1386
+ if (IsObject(stableInput)) {
1365
1387
  const keyed = {};
1366
1388
  const textureArray = loadedTextures;
1367
1389
  let i = 0;
1368
- for (const key in input) keyed[key] = textureArray[i++];
1390
+ for (const key in stableInput) keyed[key] = textureArray[i++];
1369
1391
  return keyed;
1370
1392
  } else {
1371
1393
  return loadedTextures;
1372
1394
  }
1373
- }, [input, loadedTextures, cachedResult]);
1395
+ }, [stableInput, loadedTextures, cachedResult]);
1374
1396
  React.useEffect(() => {
1375
1397
  if (!cache) return;
1376
1398
  const urlTextureMap = [];
1377
- if (typeof input === "string") {
1378
- urlTextureMap.push([input, mappedTextures]);
1379
- } else if (Array.isArray(input)) {
1399
+ if (typeof stableInput === "string") {
1400
+ urlTextureMap.push([stableInput, mappedTextures]);
1401
+ } else if (Array.isArray(stableInput)) {
1380
1402
  const textureArray = mappedTextures;
1381
- input.forEach((url, i) => urlTextureMap.push([url, textureArray[i]]));
1382
- } else if (IsObject(input)) {
1403
+ stableInput.forEach((url, i) => urlTextureMap.push([url, textureArray[i]]));
1404
+ } else if (IsObject(stableInput)) {
1383
1405
  const textureRecord = mappedTextures;
1384
- for (const key in input) {
1385
- const url = input[key];
1406
+ for (const key in stableInput) {
1407
+ const url = stableInput[key];
1386
1408
  urlTextureMap.push([url, textureRecord[key]]);
1387
1409
  }
1388
1410
  }
@@ -1411,7 +1433,7 @@ function useTexture(input, optionsOrOnLoad) {
1411
1433
  }
1412
1434
  return { _textureRefs: refs };
1413
1435
  });
1414
- }, [cache, input, mappedTextures, store]);
1436
+ }, [cache, stableInput, mappedTextures, store]);
1415
1437
  return mappedTextures;
1416
1438
  }
1417
1439
  useTexture.preload = (url) => useLoader.preload(webgpu.TextureLoader, url);
@@ -1682,13 +1704,19 @@ function addTail(callback) {
1682
1704
  return scheduler.getScheduler().onIdle(callback);
1683
1705
  }
1684
1706
  function invalidate(state, frames = 1, stackFrames = false) {
1685
- scheduler.getScheduler().invalidate(frames, stackFrames);
1707
+ const rootId = state?.internal?.rootId;
1708
+ const scheduler$1 = scheduler.getScheduler();
1709
+ if (rootId) scheduler$1.invalidateRoot(rootId, frames, stackFrames);
1710
+ else scheduler$1.invalidate(frames, stackFrames);
1686
1711
  }
1687
- function advance(timestamp) {
1688
- scheduler.getScheduler().step(timestamp);
1712
+ function advance(timestamp, runGlobalEffects, state) {
1713
+ const rootId = state?.internal?.rootId;
1714
+ const scheduler$1 = scheduler.getScheduler();
1715
+ if (rootId) scheduler$1.stepRoot(rootId, timestamp);
1716
+ else scheduler$1.step(timestamp);
1689
1717
  }
1690
1718
 
1691
- const version = "10.0.0-alpha.3";
1719
+ const version = "10.0.0-alpha.4";
1692
1720
  const packageData = {
1693
1721
  version: version};
1694
1722
 
@@ -14347,7 +14375,7 @@ function createRoot(canvas) {
14347
14375
  const handleXRFrame = (timestamp, _frame) => {
14348
14376
  const state2 = store.getState();
14349
14377
  if (state2.frameloop === "never") return;
14350
- advance(timestamp);
14378
+ advance(timestamp, true, state2);
14351
14379
  };
14352
14380
  const actualRenderer = state.internal.actualRenderer;
14353
14381
  const handleSessionChange = () => {
@@ -14355,7 +14383,7 @@ function createRoot(canvas) {
14355
14383
  const renderer2 = state2.internal.actualRenderer;
14356
14384
  actualRenderer.xr.enabled = actualRenderer.xr.isPresenting;
14357
14385
  renderer2.xr.setAnimationLoop(renderer2.xr.isPresenting ? handleXRFrame : null);
14358
- if (!renderer2.xr.isPresenting) invalidate();
14386
+ if (!renderer2.xr.isPresenting) invalidate(state2);
14359
14387
  };
14360
14388
  const xr = {
14361
14389
  connect() {
@@ -14437,7 +14465,11 @@ function createRoot(canvas) {
14437
14465
  const newRootId = canvasId || scheduler$1.generateRootId();
14438
14466
  const unregisterRoot = scheduler$1.registerRoot(newRootId, {
14439
14467
  getState: () => store.getState(),
14440
- onError: (err) => store.getState().setError(err)
14468
+ onError: (err) => store.getState().setError(err),
14469
+ frameloop: store.getState().frameloop,
14470
+ before: schedulerConfig?.before,
14471
+ after: schedulerConfig?.after,
14472
+ order: schedulerConfig?.order
14441
14473
  });
14442
14474
  const unregisterCanvasTarget = scheduler$1.register(
14443
14475
  () => {
@@ -14445,6 +14477,10 @@ function createRoot(canvas) {
14445
14477
  if (state2.internal.isMultiCanvas && state2.internal.canvasTarget) {
14446
14478
  const renderer2 = state2.internal.actualRenderer;
14447
14479
  renderer2.setCanvasTarget(state2.internal.canvasTarget);
14480
+ if (state2.internal.canvasTargetSizeDirty) {
14481
+ state2.internal.canvasTargetSizeDirty = false;
14482
+ renderer2.backend?.updateSize?.();
14483
+ }
14448
14484
  }
14449
14485
  },
14450
14486
  {
@@ -14513,8 +14549,7 @@ function createRoot(canvas) {
14513
14549
  phase: "render",
14514
14550
  system: true,
14515
14551
  // Internal flag: this is a system job, not user-controlled
14516
- // Apply scheduler config for render ordering and rate limiting
14517
- ...schedulerConfig?.after && { after: schedulerConfig.after },
14552
+ // FPS throttles only the default render job; Canvas ordering belongs to the root.
14518
14553
  ...schedulerConfig?.fps && { fps: schedulerConfig.fps }
14519
14554
  }
14520
14555
  );
@@ -14533,8 +14568,21 @@ function createRoot(canvas) {
14533
14568
  scheduler: scheduler$1
14534
14569
  }
14535
14570
  }));
14571
+ } else {
14572
+ const constraintsChanged = !is.equ(schedulerConfig?.before, lastConfiguredProps.schedulerBefore, shallowLoose) || !is.equ(schedulerConfig?.after, lastConfiguredProps.schedulerAfter, shallowLoose);
14573
+ if (constraintsChanged) {
14574
+ scheduler$1.setRootConstraints(rootId, {
14575
+ before: schedulerConfig?.before,
14576
+ after: schedulerConfig?.after
14577
+ });
14578
+ }
14579
+ if (schedulerConfig?.order !== lastConfiguredProps.schedulerOrder) {
14580
+ scheduler$1.setRootOrder(rootId, schedulerConfig?.order ?? 0);
14581
+ }
14536
14582
  }
14537
- scheduler$1.frameloop = frameloop;
14583
+ lastConfiguredProps.schedulerBefore = schedulerConfig?.before;
14584
+ lastConfiguredProps.schedulerAfter = schedulerConfig?.after;
14585
+ lastConfiguredProps.schedulerOrder = schedulerConfig?.order;
14538
14586
  onCreated = onCreatedCallback;
14539
14587
  configured = true;
14540
14588
  resolve();
@@ -14576,14 +14624,19 @@ function unmountComponentAtNode(canvas, callback) {
14576
14624
  const fiber = root?.fiber;
14577
14625
  if (fiber) {
14578
14626
  const state = root?.store.getState();
14579
- if (state) state.internal.active = false;
14627
+ if (state) {
14628
+ state.internal.active = false;
14629
+ const unregisterRoot = state.internal.unregisterRoot;
14630
+ if (unregisterRoot) {
14631
+ unregisterRoot();
14632
+ state.internal.unregisterRoot = void 0;
14633
+ }
14634
+ }
14580
14635
  reconciler.updateContainer(null, fiber, null, () => {
14581
14636
  if (state) {
14582
14637
  setTimeout(() => {
14583
14638
  try {
14584
14639
  const renderer = state.internal.actualRenderer;
14585
- const unregisterRoot = state.internal.unregisterRoot;
14586
- if (unregisterRoot) unregisterRoot();
14587
14640
  const unregisterPrimary = state.internal.unregisterPrimary;
14588
14641
  if (unregisterPrimary) unregisterPrimary();
14589
14642
  const canvasTarget = state.internal.canvasTarget;
@@ -15031,9 +15084,9 @@ function CanvasImpl({
15031
15084
  React__namespace.useMemo(() => extend(THREE), []);
15032
15085
  const Bridge = useBridge();
15033
15086
  const backgroundProps = React__namespace.useMemo(() => parseBackground(background), [background]);
15034
- const hasInitialSizeRef = React__namespace.useRef(false);
15087
+ const [hasInitialSize, setHasInitialSize] = React__namespace.useState(false);
15035
15088
  const measureConfig = React__namespace.useMemo(() => {
15036
- if (!hasInitialSizeRef.current) {
15089
+ if (!hasInitialSize) {
15037
15090
  return {
15038
15091
  ...resize,
15039
15092
  scroll: resize?.scroll ?? true,
@@ -15045,7 +15098,7 @@ function CanvasImpl({
15045
15098
  debounce: { scroll: 50, resize: 0 },
15046
15099
  ...resize
15047
15100
  };
15048
- }, [resize, hasInitialSizeRef.current]);
15101
+ }, [hasInitialSize, resize]);
15049
15102
  const [containerRef, containerRect] = useMeasure__default(measureConfig);
15050
15103
  const effectiveSize = React__namespace.useMemo(() => {
15051
15104
  let w = width ?? containerRect.width;
@@ -15061,9 +15114,7 @@ function CanvasImpl({
15061
15114
  left: containerRect.left
15062
15115
  };
15063
15116
  }, [width, height, containerRect, forceEven]);
15064
- if (!hasInitialSizeRef.current && effectiveSize.width > 0 && effectiveSize.height > 0) {
15065
- hasInitialSizeRef.current = true;
15066
- }
15117
+ if (!hasInitialSize && effectiveSize.width > 0 && effectiveSize.height > 0) setHasInitialSize(true);
15067
15118
  const canvasRef = React__namespace.useRef(null);
15068
15119
  const divRef = React__namespace.useRef(null);
15069
15120
  React__namespace.useImperativeHandle(ref, () => canvasRef.current);
@@ -15177,6 +15228,7 @@ function CanvasImpl({
15177
15228
  const canvas = canvasRef.current;
15178
15229
  if (canvas) {
15179
15230
  return () => {
15231
+ if (canvas.isConnected) return;
15180
15232
  unmountComponentAtNode(canvas);
15181
15233
  root.current = null;
15182
15234
  };
package/dist/index.d.cts CHANGED
@@ -1,3 +1,4 @@
1
+ import * as three_examples_jsm_inspector_Inspector_js from 'three/examples/jsm/inspector/Inspector.js';
1
2
  import * as three_webgpu from 'three/webgpu';
2
3
  import { CubeRenderTarget, RenderTarget, WebGPURenderer, Node, StorageTexture, Data3DTexture, CanvasTarget, ShaderNodeObject, Euler as Euler$2, Color as Color$2, ColorRepresentation as ColorRepresentation$1, Layers as Layers$1, Raycaster, Intersection as Intersection$1, BufferGeometry, Matrix4 as Matrix4$1, Quaternion as Quaternion$1, Vector2 as Vector2$1, Vector3 as Vector3$1, Vector4 as Vector4$1, Matrix3 as Matrix3$1, Loader as Loader$1, ColorSpace, Texture as Texture$1, CubeTexture, Scene, Object3D, Frustum, OrthographicCamera } from 'three/webgpu';
3
4
  import * as THREE$1 from 'three';
@@ -47,6 +48,9 @@ function _mergeNamespaces(n, m) {
47
48
  declare const R3F_BUILD_LEGACY = true;
48
49
  declare const R3F_BUILD_WEBGPU = true;
49
50
 
51
+ /** Lazily load the three Inspector. Keeps it out of the eager module graph — see note above. */
52
+ declare function loadInspector(): Promise<typeof three_examples_jsm_inspector_Inspector_js.Inspector>;
53
+
50
54
  var THREE = /*#__PURE__*/_mergeNamespaces({
51
55
  __proto__: null,
52
56
  CubeRenderTarget: CubeRenderTarget,
@@ -58,7 +62,8 @@ var THREE = /*#__PURE__*/_mergeNamespaces({
58
62
  WebGLCubeRenderTarget: WebGLCubeRenderTarget,
59
63
  WebGLRenderTarget: WebGLRenderTarget,
60
64
  WebGLRenderer: WebGLRenderer,
61
- WebGLRendererParameters: WebGLRendererParameters
65
+ WebGLRendererParameters: WebGLRendererParameters,
66
+ loadInspector: loadInspector
62
67
  }, [three_webgpu]);
63
68
 
64
69
  //* Utility Types ==============================
@@ -496,6 +501,18 @@ interface InternalState {
496
501
  * @see https://threejs.org/docs/#api/en/renderers/common/CanvasTarget
497
502
  */
498
503
  canvasTarget?: CanvasTarget
504
+ /**
505
+ * Set when this root's canvas target has been resized and the backend's cached render pass
506
+ * descriptor (which holds a depth-stencil view built once per canvas) is therefore stale.
507
+ *
508
+ * Flushed by the canvas-target job in the `start` phase, which is the only place this root's
509
+ * target is guaranteed to be the renderer's active one — `backend.updateSize()` operates on
510
+ * whatever `getCanvasTarget()` returns, so calling it from the resize subscription would
511
+ * invalidate some other canvas's descriptor instead.
512
+ *
513
+ * @see https://github.com/pmndrs/react-three-fiber/issues/3847
514
+ */
515
+ canvasTargetSizeDirty?: boolean
499
516
  /**
500
517
  * Whether multi-canvas rendering is active.
501
518
  * True when any canvas uses `renderer={{ primaryCanvas: 'id' }}` to share a renderer.
@@ -705,12 +722,19 @@ type DefaultRendererProps = {
705
722
  */
706
723
  interface CanvasSchedulerConfig {
707
724
  /**
708
- * Render this canvas after another canvas completes.
709
- * Pass the `id` of another canvas.
725
+ * Run this Canvas root before the referenced Canvas root id(s).
726
+ */
727
+ before?: string | string[]
728
+ /**
729
+ * Run this Canvas root after the referenced Canvas root id(s).
710
730
  */
711
- after?: string
731
+ after?: string | string[]
712
732
  /**
713
- * Limit this canvas's render rate (frames per second).
733
+ * Numeric root order. Lower values run first.
734
+ */
735
+ order?: number
736
+ /**
737
+ * Limit this Canvas's default render job (frames per second).
714
738
  */
715
739
  fps?: number
716
740
  }
@@ -1314,11 +1338,16 @@ declare global {
1314
1338
  /** Pass record - stores TSL pass nodes for render pipeline */
1315
1339
  type PassRecord = Record<string, any>
1316
1340
 
1341
+ /** State passed to pipeline callbacks after the active pipeline has been created */
1342
+ type RenderPipelineCallbackState = RootState & {
1343
+ renderPipeline: ThreeRenderPipeline
1344
+ }
1345
+
1317
1346
  /** Setup callback - runs first to configure MRT, create additional passes */
1318
- type RenderPipelineSetupCallback = (state: RootState) => PassRecord | void
1347
+ type RenderPipelineSetupCallback = (state: RenderPipelineCallbackState) => PassRecord | void
1319
1348
 
1320
1349
  /** Main callback - runs second to configure outputNode, create effect passes */
1321
- type RenderPipelineMainCallback = (state: RootState) => PassRecord | void
1350
+ type RenderPipelineMainCallback = (state: RenderPipelineCallbackState) => PassRecord | void
1322
1351
 
1323
1352
  /** Return type for useRenderPipeline hook */
1324
1353
  interface UseRenderPipelineReturn {
@@ -1878,7 +1907,7 @@ declare namespace useLoader {
1878
1907
  *
1879
1908
  * @param callback - Function called each frame with (state, delta). Optional if you only need scheduler access.
1880
1909
  * @param priorityOrOptions - Either a priority number (backwards compat) or options object
1881
- * @returns Controls object with step(), stepAll(), pause(), resume(), isPaused, id, scheduler
1910
+ * @returns Controls object with step(), stepAll(), invalidate(), pause(), resume(), isPaused, id, rootId, scheduler
1882
1911
  *
1883
1912
  * @example
1884
1913
  * // Simple priority (backwards compat)
@@ -2174,7 +2203,10 @@ declare function addTail(callback: GlobalRenderCallback): () => void;
2174
2203
  * Invalidates the view, requesting a frame to be rendered.
2175
2204
  * In demand mode, this triggers the scheduler to run frames.
2176
2205
  *
2177
- * @param state - Optional root state (ignored in new scheduler, kept for backwards compat)
2206
+ * With a state argument, only that root's jobs run on the requested frames; other
2207
+ * roots on 'demand' stay idle. Without one, every registered root is invalidated.
2208
+ *
2209
+ * @param state - Optional root state; targets the invalidation at that root
2178
2210
  * @param frames - Number of frames to request (default: 1)
2179
2211
  * @param stackFrames - If false, sets pendingFrames to frames. If true, adds to existing pendingFrames (default: false)
2180
2212
  *
@@ -2185,14 +2217,16 @@ declare function invalidate(state?: RootState, frames?: number, stackFrames?: bo
2185
2217
  * Advances the frameloop and runs render effects.
2186
2218
  * Useful for when manually rendering via `frameloop="never"`.
2187
2219
  *
2220
+ * With a state argument, only that root is stepped. Without one, every
2221
+ * registered root is stepped for backwards compatibility.
2222
+ *
2188
2223
  * @param timestamp - The timestamp to use for this frame
2189
2224
  * @param runGlobalEffects - Ignored (kept for backwards compat, global effects always run)
2190
- * @param state - Ignored (kept for backwards compat)
2191
- * @param frame - Ignored (kept for backwards compat)
2225
+ * @param state - Optional root state; targets the manual step at that root
2192
2226
  *
2193
2227
  * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#advance
2194
2228
  */
2195
- declare function advance(timestamp: number): void;
2229
+ declare function advance(timestamp: number, runGlobalEffects?: boolean, state?: RootState): void;
2196
2230
 
2197
2231
  /* eslint-disable @definitelytyped/no-unnecessary-generics */
2198
2232
  declare function ReactReconciler<
package/dist/index.d.mts CHANGED
@@ -1,3 +1,4 @@
1
+ import * as three_examples_jsm_inspector_Inspector_js from 'three/examples/jsm/inspector/Inspector.js';
1
2
  import * as three_webgpu from 'three/webgpu';
2
3
  import { CubeRenderTarget, RenderTarget, WebGPURenderer, Node, StorageTexture, Data3DTexture, CanvasTarget, ShaderNodeObject, Euler as Euler$2, Color as Color$2, ColorRepresentation as ColorRepresentation$1, Layers as Layers$1, Raycaster, Intersection as Intersection$1, BufferGeometry, Matrix4 as Matrix4$1, Quaternion as Quaternion$1, Vector2 as Vector2$1, Vector3 as Vector3$1, Vector4 as Vector4$1, Matrix3 as Matrix3$1, Loader as Loader$1, ColorSpace, Texture as Texture$1, CubeTexture, Scene, Object3D, Frustum, OrthographicCamera } from 'three/webgpu';
3
4
  import * as THREE$1 from 'three';
@@ -47,6 +48,9 @@ function _mergeNamespaces(n, m) {
47
48
  declare const R3F_BUILD_LEGACY = true;
48
49
  declare const R3F_BUILD_WEBGPU = true;
49
50
 
51
+ /** Lazily load the three Inspector. Keeps it out of the eager module graph — see note above. */
52
+ declare function loadInspector(): Promise<typeof three_examples_jsm_inspector_Inspector_js.Inspector>;
53
+
50
54
  var THREE = /*#__PURE__*/_mergeNamespaces({
51
55
  __proto__: null,
52
56
  CubeRenderTarget: CubeRenderTarget,
@@ -58,7 +62,8 @@ var THREE = /*#__PURE__*/_mergeNamespaces({
58
62
  WebGLCubeRenderTarget: WebGLCubeRenderTarget,
59
63
  WebGLRenderTarget: WebGLRenderTarget,
60
64
  WebGLRenderer: WebGLRenderer,
61
- WebGLRendererParameters: WebGLRendererParameters
65
+ WebGLRendererParameters: WebGLRendererParameters,
66
+ loadInspector: loadInspector
62
67
  }, [three_webgpu]);
63
68
 
64
69
  //* Utility Types ==============================
@@ -496,6 +501,18 @@ interface InternalState {
496
501
  * @see https://threejs.org/docs/#api/en/renderers/common/CanvasTarget
497
502
  */
498
503
  canvasTarget?: CanvasTarget
504
+ /**
505
+ * Set when this root's canvas target has been resized and the backend's cached render pass
506
+ * descriptor (which holds a depth-stencil view built once per canvas) is therefore stale.
507
+ *
508
+ * Flushed by the canvas-target job in the `start` phase, which is the only place this root's
509
+ * target is guaranteed to be the renderer's active one — `backend.updateSize()` operates on
510
+ * whatever `getCanvasTarget()` returns, so calling it from the resize subscription would
511
+ * invalidate some other canvas's descriptor instead.
512
+ *
513
+ * @see https://github.com/pmndrs/react-three-fiber/issues/3847
514
+ */
515
+ canvasTargetSizeDirty?: boolean
499
516
  /**
500
517
  * Whether multi-canvas rendering is active.
501
518
  * True when any canvas uses `renderer={{ primaryCanvas: 'id' }}` to share a renderer.
@@ -705,12 +722,19 @@ type DefaultRendererProps = {
705
722
  */
706
723
  interface CanvasSchedulerConfig {
707
724
  /**
708
- * Render this canvas after another canvas completes.
709
- * Pass the `id` of another canvas.
725
+ * Run this Canvas root before the referenced Canvas root id(s).
726
+ */
727
+ before?: string | string[]
728
+ /**
729
+ * Run this Canvas root after the referenced Canvas root id(s).
710
730
  */
711
- after?: string
731
+ after?: string | string[]
712
732
  /**
713
- * Limit this canvas's render rate (frames per second).
733
+ * Numeric root order. Lower values run first.
734
+ */
735
+ order?: number
736
+ /**
737
+ * Limit this Canvas's default render job (frames per second).
714
738
  */
715
739
  fps?: number
716
740
  }
@@ -1314,11 +1338,16 @@ declare global {
1314
1338
  /** Pass record - stores TSL pass nodes for render pipeline */
1315
1339
  type PassRecord = Record<string, any>
1316
1340
 
1341
+ /** State passed to pipeline callbacks after the active pipeline has been created */
1342
+ type RenderPipelineCallbackState = RootState & {
1343
+ renderPipeline: ThreeRenderPipeline
1344
+ }
1345
+
1317
1346
  /** Setup callback - runs first to configure MRT, create additional passes */
1318
- type RenderPipelineSetupCallback = (state: RootState) => PassRecord | void
1347
+ type RenderPipelineSetupCallback = (state: RenderPipelineCallbackState) => PassRecord | void
1319
1348
 
1320
1349
  /** Main callback - runs second to configure outputNode, create effect passes */
1321
- type RenderPipelineMainCallback = (state: RootState) => PassRecord | void
1350
+ type RenderPipelineMainCallback = (state: RenderPipelineCallbackState) => PassRecord | void
1322
1351
 
1323
1352
  /** Return type for useRenderPipeline hook */
1324
1353
  interface UseRenderPipelineReturn {
@@ -1878,7 +1907,7 @@ declare namespace useLoader {
1878
1907
  *
1879
1908
  * @param callback - Function called each frame with (state, delta). Optional if you only need scheduler access.
1880
1909
  * @param priorityOrOptions - Either a priority number (backwards compat) or options object
1881
- * @returns Controls object with step(), stepAll(), pause(), resume(), isPaused, id, scheduler
1910
+ * @returns Controls object with step(), stepAll(), invalidate(), pause(), resume(), isPaused, id, rootId, scheduler
1882
1911
  *
1883
1912
  * @example
1884
1913
  * // Simple priority (backwards compat)
@@ -2174,7 +2203,10 @@ declare function addTail(callback: GlobalRenderCallback): () => void;
2174
2203
  * Invalidates the view, requesting a frame to be rendered.
2175
2204
  * In demand mode, this triggers the scheduler to run frames.
2176
2205
  *
2177
- * @param state - Optional root state (ignored in new scheduler, kept for backwards compat)
2206
+ * With a state argument, only that root's jobs run on the requested frames; other
2207
+ * roots on 'demand' stay idle. Without one, every registered root is invalidated.
2208
+ *
2209
+ * @param state - Optional root state; targets the invalidation at that root
2178
2210
  * @param frames - Number of frames to request (default: 1)
2179
2211
  * @param stackFrames - If false, sets pendingFrames to frames. If true, adds to existing pendingFrames (default: false)
2180
2212
  *
@@ -2185,14 +2217,16 @@ declare function invalidate(state?: RootState, frames?: number, stackFrames?: bo
2185
2217
  * Advances the frameloop and runs render effects.
2186
2218
  * Useful for when manually rendering via `frameloop="never"`.
2187
2219
  *
2220
+ * With a state argument, only that root is stepped. Without one, every
2221
+ * registered root is stepped for backwards compatibility.
2222
+ *
2188
2223
  * @param timestamp - The timestamp to use for this frame
2189
2224
  * @param runGlobalEffects - Ignored (kept for backwards compat, global effects always run)
2190
- * @param state - Ignored (kept for backwards compat)
2191
- * @param frame - Ignored (kept for backwards compat)
2225
+ * @param state - Optional root state; targets the manual step at that root
2192
2226
  *
2193
2227
  * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#advance
2194
2228
  */
2195
- declare function advance(timestamp: number): void;
2229
+ declare function advance(timestamp: number, runGlobalEffects?: boolean, state?: RootState): void;
2196
2230
 
2197
2231
  /* eslint-disable @definitelytyped/no-unnecessary-generics */
2198
2232
  declare function ReactReconciler<