@react-three/fiber 10.0.0-canary.0d86c23 → 10.0.0-canary.0f7fc9b

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/legacy.d.mts CHANGED
@@ -38,9 +38,9 @@ function _mergeNamespaces(n, m) {
38
38
  declare const R3F_BUILD_LEGACY = true;
39
39
  declare const R3F_BUILD_WEBGPU = false;
40
40
 
41
- declare const Inspector: {
42
- new (): {};
43
- };
41
+ type Inspector = never;
42
+ /** Not available on the legacy (WebGL-only) entry. */
43
+ declare function loadInspector(): Promise<never>;
44
44
  declare const WebGPURenderer: {
45
45
  new (): {};
46
46
  };
@@ -58,7 +58,8 @@ var THREE = /*#__PURE__*/_mergeNamespaces({
58
58
  R3F_BUILD_WEBGPU: R3F_BUILD_WEBGPU,
59
59
  RenderTarget: RenderTarget,
60
60
  RenderTargetCompat: WebGLRenderTarget,
61
- WebGPURenderer: WebGPURenderer
61
+ WebGPURenderer: WebGPURenderer,
62
+ loadInspector: loadInspector
62
63
  }, [THREE$1]);
63
64
 
64
65
  //* Utility Types ==============================
@@ -496,6 +497,18 @@ interface InternalState {
496
497
  * @see https://threejs.org/docs/#api/en/renderers/common/CanvasTarget
497
498
  */
498
499
  canvasTarget?: CanvasTarget
500
+ /**
501
+ * Set when this root's canvas target has been resized and the backend's cached render pass
502
+ * descriptor (which holds a depth-stencil view built once per canvas) is therefore stale.
503
+ *
504
+ * Flushed by the canvas-target job in the `start` phase, which is the only place this root's
505
+ * target is guaranteed to be the renderer's active one — `backend.updateSize()` operates on
506
+ * whatever `getCanvasTarget()` returns, so calling it from the resize subscription would
507
+ * invalidate some other canvas's descriptor instead.
508
+ *
509
+ * @see https://github.com/pmndrs/react-three-fiber/issues/3847
510
+ */
511
+ canvasTargetSizeDirty?: boolean
499
512
  /**
500
513
  * Whether multi-canvas rendering is active.
501
514
  * True when any canvas uses `renderer={{ primaryCanvas: 'id' }}` to share a renderer.
package/dist/legacy.d.ts CHANGED
@@ -38,9 +38,9 @@ function _mergeNamespaces(n, m) {
38
38
  declare const R3F_BUILD_LEGACY = true;
39
39
  declare const R3F_BUILD_WEBGPU = false;
40
40
 
41
- declare const Inspector: {
42
- new (): {};
43
- };
41
+ type Inspector = never;
42
+ /** Not available on the legacy (WebGL-only) entry. */
43
+ declare function loadInspector(): Promise<never>;
44
44
  declare const WebGPURenderer: {
45
45
  new (): {};
46
46
  };
@@ -58,7 +58,8 @@ var THREE = /*#__PURE__*/_mergeNamespaces({
58
58
  R3F_BUILD_WEBGPU: R3F_BUILD_WEBGPU,
59
59
  RenderTarget: RenderTarget,
60
60
  RenderTargetCompat: WebGLRenderTarget,
61
- WebGPURenderer: WebGPURenderer
61
+ WebGPURenderer: WebGPURenderer,
62
+ loadInspector: loadInspector
62
63
  }, [THREE$1]);
63
64
 
64
65
  //* Utility Types ==============================
@@ -496,6 +497,18 @@ interface InternalState {
496
497
  * @see https://threejs.org/docs/#api/en/renderers/common/CanvasTarget
497
498
  */
498
499
  canvasTarget?: CanvasTarget
500
+ /**
501
+ * Set when this root's canvas target has been resized and the backend's cached render pass
502
+ * descriptor (which holds a depth-stencil view built once per canvas) is therefore stale.
503
+ *
504
+ * Flushed by the canvas-target job in the `start` phase, which is the only place this root's
505
+ * target is guaranteed to be the renderer's active one — `backend.updateSize()` operates on
506
+ * whatever `getCanvasTarget()` returns, so calling it from the resize subscription would
507
+ * invalidate some other canvas's descriptor instead.
508
+ *
509
+ * @see https://github.com/pmndrs/react-three-fiber/issues/3847
510
+ */
511
+ canvasTargetSizeDirty?: boolean
499
512
  /**
500
513
  * Whether multi-canvas rendering is active.
501
514
  * True when any canvas uses `renderer={{ primaryCanvas: 'id' }}` to share a renderer.
package/dist/legacy.mjs CHANGED
@@ -30,11 +30,9 @@ function _mergeNamespaces(n, m) {
30
30
 
31
31
  const R3F_BUILD_LEGACY = true;
32
32
  const R3F_BUILD_WEBGPU = false;
33
- const Inspector = class Inspector2 {
34
- constructor() {
35
- throw new Error("Inspector is not available in legacy builds. Use @react-three/fiber/webgpu instead.");
36
- }
37
- };
33
+ async function loadInspector() {
34
+ throw new Error("Inspector is not available in legacy builds. Use @react-three/fiber/webgpu instead.");
35
+ }
38
36
  const WebGPURenderer = class WebGPURenderer2 {
39
37
  constructor() {
40
38
  throw new Error("WebGPURenderer is not available in legacy builds. Use @react-three/fiber/webgpu instead.");
@@ -47,12 +45,12 @@ const THREE = /*#__PURE__*/_mergeNamespaces({
47
45
  __proto__: null,
48
46
  CubeRenderTarget: CubeRenderTarget,
49
47
  CubeRenderTargetCompat: WebGLCubeRenderTarget,
50
- Inspector: Inspector,
51
48
  R3F_BUILD_LEGACY: R3F_BUILD_LEGACY,
52
49
  R3F_BUILD_WEBGPU: R3F_BUILD_WEBGPU,
53
50
  RenderTarget: RenderTarget,
54
51
  RenderTargetCompat: WebGLRenderTarget,
55
- WebGPURenderer: WebGPURenderer
52
+ WebGPURenderer: WebGPURenderer,
53
+ loadInspector: loadInspector
56
54
  }, [three]);
57
55
 
58
56
  const primaryRegistry = /* @__PURE__ */ new Map();
@@ -1050,17 +1048,10 @@ const createStore = (invalidate, advance) => {
1050
1048
  oldSize = size;
1051
1049
  oldDpr = viewport.dpr;
1052
1050
  updateCamera(camera, size);
1053
- if (internal.isSecondary && canvasTarget) {
1054
- if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
1055
- canvasTarget.setSize(size.width, size.height, false);
1056
- } else {
1057
- if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
1058
- actualRenderer.setSize(size.width, size.height, false);
1059
- if (canvasTarget) {
1060
- if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
1061
- canvasTarget.setSize(size.width, size.height, false);
1062
- }
1063
- }
1051
+ const resizeTarget = canvasTarget ?? actualRenderer;
1052
+ if (viewport.dpr > 0) resizeTarget.setPixelRatio(viewport.dpr);
1053
+ resizeTarget.setSize(size.width, size.height, false);
1054
+ internal.canvasTargetSizeDirty = true;
1064
1055
  }
1065
1056
  if (camera !== oldCamera) {
1066
1057
  oldCamera = camera;
@@ -1122,7 +1113,11 @@ function useLoader(loader, input, extensions, onProgress, cacheKey) {
1122
1113
  const results = cacheKeys.map(
1123
1114
  (key, index) => suspend(() => fn(loader, inputs[index]), [loader, key], { equal: is.equ })
1124
1115
  );
1125
- return Array.isArray(input) ? results : results[0];
1116
+ const stableRef = useRef(null);
1117
+ const previous = stableRef.current;
1118
+ const stable = previous !== null && previous.length === results.length && previous.every((value, i) => value === results[i]) ? previous : results;
1119
+ stableRef.current = stable;
1120
+ return Array.isArray(input) ? stable : stable[0];
1126
1121
  }
1127
1122
  useLoader.preload = function(loader, input, extensions, onProgress, cacheKey) {
1128
1123
  const inputs = Array.isArray(input) ? input : [input];
@@ -1287,6 +1282,11 @@ function getUrls(input) {
1287
1282
  if (Array.isArray(input)) return input;
1288
1283
  return Object.values(input);
1289
1284
  }
1285
+ function inputSignature(input) {
1286
+ if (typeof input === "string") return input;
1287
+ if (Array.isArray(input)) return input.join("\0");
1288
+ return Object.keys(input).sort().map((key) => `${key}\0${input[key]}`).join("");
1289
+ }
1290
1290
  function allUrlsCached(urls, textureCache) {
1291
1291
  return urls.every((url) => textureCache.has(url));
1292
1292
  }
@@ -1312,18 +1312,19 @@ function useTexture(input, optionsOrOnLoad) {
1312
1312
  const onLoadRef = useRef(onLoad);
1313
1313
  onLoadRef.current = onLoad;
1314
1314
  const onLoadCalledForRef = useRef(null);
1315
- const urls = useMemo(() => getUrls(input), [input]);
1315
+ const inputKey = useMemo(() => inputSignature(input), [input]);
1316
+ const stableInput = useMemo(() => input, [inputKey]);
1317
+ const urls = useMemo(() => getUrls(stableInput), [stableInput]);
1316
1318
  const cachedResult = useMemo(() => {
1317
1319
  if (!cache) return null;
1318
1320
  const textures = store.getState().textures;
1319
1321
  if (!allUrlsCached(urls, textures)) return null;
1320
- return buildFromCache(input, textures);
1321
- }, [cache, urls, input, store]);
1322
+ return buildFromCache(stableInput, textures);
1323
+ }, [cache, urls, stableInput, store]);
1322
1324
  const loadedTextures = useLoader(
1323
1325
  TextureLoader,
1324
- IsObject(input) ? Object.values(input) : input
1326
+ IsObject(stableInput) ? Object.values(stableInput) : stableInput
1325
1327
  );
1326
- const inputKey = urls.join("\0");
1327
1328
  useLayoutEffect(() => {
1328
1329
  if (cachedResult) return;
1329
1330
  if (onLoadCalledForRef.current === inputKey) return;
@@ -1350,28 +1351,28 @@ function useTexture(input, optionsOrOnLoad) {
1350
1351
  }, [renderer, loadedTextures, cachedResult]);
1351
1352
  const mappedTextures = useMemo(() => {
1352
1353
  if (cachedResult) return cachedResult;
1353
- if (IsObject(input)) {
1354
+ if (IsObject(stableInput)) {
1354
1355
  const keyed = {};
1355
1356
  const textureArray = loadedTextures;
1356
1357
  let i = 0;
1357
- for (const key in input) keyed[key] = textureArray[i++];
1358
+ for (const key in stableInput) keyed[key] = textureArray[i++];
1358
1359
  return keyed;
1359
1360
  } else {
1360
1361
  return loadedTextures;
1361
1362
  }
1362
- }, [input, loadedTextures, cachedResult]);
1363
+ }, [stableInput, loadedTextures, cachedResult]);
1363
1364
  useEffect(() => {
1364
1365
  if (!cache) return;
1365
1366
  const urlTextureMap = [];
1366
- if (typeof input === "string") {
1367
- urlTextureMap.push([input, mappedTextures]);
1368
- } else if (Array.isArray(input)) {
1367
+ if (typeof stableInput === "string") {
1368
+ urlTextureMap.push([stableInput, mappedTextures]);
1369
+ } else if (Array.isArray(stableInput)) {
1369
1370
  const textureArray = mappedTextures;
1370
- input.forEach((url, i) => urlTextureMap.push([url, textureArray[i]]));
1371
- } else if (IsObject(input)) {
1371
+ stableInput.forEach((url, i) => urlTextureMap.push([url, textureArray[i]]));
1372
+ } else if (IsObject(stableInput)) {
1372
1373
  const textureRecord = mappedTextures;
1373
- for (const key in input) {
1374
- const url = input[key];
1374
+ for (const key in stableInput) {
1375
+ const url = stableInput[key];
1375
1376
  urlTextureMap.push([url, textureRecord[key]]);
1376
1377
  }
1377
1378
  }
@@ -1400,7 +1401,7 @@ function useTexture(input, optionsOrOnLoad) {
1400
1401
  }
1401
1402
  return { _textureRefs: refs };
1402
1403
  });
1403
- }, [cache, input, mappedTextures, store]);
1404
+ }, [cache, stableInput, mappedTextures, store]);
1404
1405
  return mappedTextures;
1405
1406
  }
1406
1407
  useTexture.preload = (url) => useLoader.preload(TextureLoader, url);
@@ -14385,6 +14386,10 @@ function createRoot(canvas) {
14385
14386
  if (state2.internal.isMultiCanvas && state2.internal.canvasTarget) {
14386
14387
  const renderer2 = state2.internal.actualRenderer;
14387
14388
  renderer2.setCanvasTarget(state2.internal.canvasTarget);
14389
+ if (state2.internal.canvasTargetSizeDirty) {
14390
+ state2.internal.canvasTargetSizeDirty = false;
14391
+ renderer2.backend?.updateSize?.();
14392
+ }
14388
14393
  }
14389
14394
  },
14390
14395
  {
@@ -1,7 +1,6 @@
1
1
  'use strict';
2
2
 
3
3
  const webgpu = require('three/webgpu');
4
- const Inspector_js = require('three/addons/inspector/Inspector.js');
5
4
  const jsxRuntime = require('react/jsx-runtime');
6
5
  const React = require('react');
7
6
  const useMeasure = require('react-use-measure');
@@ -54,6 +53,10 @@ const Tb__default = /*#__PURE__*/_interopDefaultCompat(Tb);
54
53
 
55
54
  const R3F_BUILD_LEGACY = false;
56
55
  const R3F_BUILD_WEBGPU = true;
56
+ async function loadInspector() {
57
+ const { Inspector } = await import('three/addons/inspector/Inspector.js');
58
+ return Inspector;
59
+ }
57
60
  const WebGLRenderer = class WebGLRenderer2 {
58
61
  constructor() {
59
62
  throw new Error(
@@ -67,13 +70,13 @@ const WebGLCubeRenderTarget = null;
67
70
  const THREE = /*#__PURE__*/_mergeNamespaces({
68
71
  __proto__: null,
69
72
  CubeRenderTargetCompat: webgpu.CubeRenderTarget,
70
- Inspector: Inspector_js.Inspector,
71
73
  R3F_BUILD_LEGACY: R3F_BUILD_LEGACY,
72
74
  R3F_BUILD_WEBGPU: R3F_BUILD_WEBGPU,
73
75
  RenderTargetCompat: webgpu.RenderTarget,
74
76
  WebGLCubeRenderTarget: WebGLCubeRenderTarget,
75
77
  WebGLRenderTarget: WebGLRenderTarget,
76
- WebGLRenderer: WebGLRenderer
78
+ WebGLRenderer: WebGLRenderer,
79
+ loadInspector: loadInspector
77
80
  }, [webgpu__namespace]);
78
81
 
79
82
  const primaryRegistry = /* @__PURE__ */ new Map();
@@ -1071,17 +1074,10 @@ const createStore = (invalidate, advance) => {
1071
1074
  oldSize = size;
1072
1075
  oldDpr = viewport.dpr;
1073
1076
  updateCamera(camera, size);
1074
- if (internal.isSecondary && canvasTarget) {
1075
- if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
1076
- canvasTarget.setSize(size.width, size.height, false);
1077
- } else {
1078
- if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
1079
- actualRenderer.setSize(size.width, size.height, false);
1080
- if (canvasTarget) {
1081
- if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
1082
- canvasTarget.setSize(size.width, size.height, false);
1083
- }
1084
- }
1077
+ const resizeTarget = canvasTarget ?? actualRenderer;
1078
+ if (viewport.dpr > 0) resizeTarget.setPixelRatio(viewport.dpr);
1079
+ resizeTarget.setSize(size.width, size.height, false);
1080
+ internal.canvasTargetSizeDirty = true;
1085
1081
  }
1086
1082
  if (camera !== oldCamera) {
1087
1083
  oldCamera = camera;
@@ -1143,7 +1139,11 @@ function useLoader(loader, input, extensions, onProgress, cacheKey) {
1143
1139
  const results = cacheKeys.map(
1144
1140
  (key, index) => suspendReact.suspend(() => fn(loader, inputs[index]), [loader, key], { equal: is.equ })
1145
1141
  );
1146
- return Array.isArray(input) ? results : results[0];
1142
+ const stableRef = React.useRef(null);
1143
+ const previous = stableRef.current;
1144
+ const stable = previous !== null && previous.length === results.length && previous.every((value, i) => value === results[i]) ? previous : results;
1145
+ stableRef.current = stable;
1146
+ return Array.isArray(input) ? stable : stable[0];
1147
1147
  }
1148
1148
  useLoader.preload = function(loader, input, extensions, onProgress, cacheKey) {
1149
1149
  const inputs = Array.isArray(input) ? input : [input];
@@ -1158,7 +1158,7 @@ useLoader.clear = function(loader, input, cacheKey) {
1158
1158
  };
1159
1159
  useLoader.loader = getLoader$1;
1160
1160
 
1161
- function useFrame(callback, priorityOrOptions) {
1161
+ function useFrame$1(callback, priorityOrOptions) {
1162
1162
  const store = React__namespace.useContext(context);
1163
1163
  const isInsideCanvas = store !== null;
1164
1164
  const scheduler$1 = scheduler.getScheduler();
@@ -1308,6 +1308,11 @@ function getUrls(input) {
1308
1308
  if (Array.isArray(input)) return input;
1309
1309
  return Object.values(input);
1310
1310
  }
1311
+ function inputSignature(input) {
1312
+ if (typeof input === "string") return input;
1313
+ if (Array.isArray(input)) return input.join("\0");
1314
+ return Object.keys(input).sort().map((key) => `${key}\0${input[key]}`).join("");
1315
+ }
1311
1316
  function allUrlsCached(urls, textureCache) {
1312
1317
  return urls.every((url) => textureCache.has(url));
1313
1318
  }
@@ -1326,25 +1331,26 @@ function buildFromCache(input, textureCache) {
1326
1331
  }
1327
1332
  }
1328
1333
  function useTexture(input, optionsOrOnLoad) {
1329
- const renderer = useThree((state) => state.internal.actualRenderer);
1334
+ const renderer = useThree$1((state) => state.internal.actualRenderer);
1330
1335
  const store = useStore();
1331
1336
  const options = typeof optionsOrOnLoad === "function" ? { onLoad: optionsOrOnLoad } : optionsOrOnLoad ?? {};
1332
1337
  const { onLoad, cache = true } = options;
1333
1338
  const onLoadRef = React.useRef(onLoad);
1334
1339
  onLoadRef.current = onLoad;
1335
1340
  const onLoadCalledForRef = React.useRef(null);
1336
- const urls = React.useMemo(() => getUrls(input), [input]);
1341
+ const inputKey = React.useMemo(() => inputSignature(input), [input]);
1342
+ const stableInput = React.useMemo(() => input, [inputKey]);
1343
+ const urls = React.useMemo(() => getUrls(stableInput), [stableInput]);
1337
1344
  const cachedResult = React.useMemo(() => {
1338
1345
  if (!cache) return null;
1339
1346
  const textures = store.getState().textures;
1340
1347
  if (!allUrlsCached(urls, textures)) return null;
1341
- return buildFromCache(input, textures);
1342
- }, [cache, urls, input, store]);
1348
+ return buildFromCache(stableInput, textures);
1349
+ }, [cache, urls, stableInput, store]);
1343
1350
  const loadedTextures = useLoader(
1344
1351
  webgpu.TextureLoader,
1345
- IsObject(input) ? Object.values(input) : input
1352
+ IsObject(stableInput) ? Object.values(stableInput) : stableInput
1346
1353
  );
1347
- const inputKey = urls.join("\0");
1348
1354
  React.useLayoutEffect(() => {
1349
1355
  if (cachedResult) return;
1350
1356
  if (onLoadCalledForRef.current === inputKey) return;
@@ -1371,28 +1377,28 @@ function useTexture(input, optionsOrOnLoad) {
1371
1377
  }, [renderer, loadedTextures, cachedResult]);
1372
1378
  const mappedTextures = React.useMemo(() => {
1373
1379
  if (cachedResult) return cachedResult;
1374
- if (IsObject(input)) {
1380
+ if (IsObject(stableInput)) {
1375
1381
  const keyed = {};
1376
1382
  const textureArray = loadedTextures;
1377
1383
  let i = 0;
1378
- for (const key in input) keyed[key] = textureArray[i++];
1384
+ for (const key in stableInput) keyed[key] = textureArray[i++];
1379
1385
  return keyed;
1380
1386
  } else {
1381
1387
  return loadedTextures;
1382
1388
  }
1383
- }, [input, loadedTextures, cachedResult]);
1389
+ }, [stableInput, loadedTextures, cachedResult]);
1384
1390
  React.useEffect(() => {
1385
1391
  if (!cache) return;
1386
1392
  const urlTextureMap = [];
1387
- if (typeof input === "string") {
1388
- urlTextureMap.push([input, mappedTextures]);
1389
- } else if (Array.isArray(input)) {
1393
+ if (typeof stableInput === "string") {
1394
+ urlTextureMap.push([stableInput, mappedTextures]);
1395
+ } else if (Array.isArray(stableInput)) {
1390
1396
  const textureArray = mappedTextures;
1391
- input.forEach((url, i) => urlTextureMap.push([url, textureArray[i]]));
1392
- } else if (IsObject(input)) {
1397
+ stableInput.forEach((url, i) => urlTextureMap.push([url, textureArray[i]]));
1398
+ } else if (IsObject(stableInput)) {
1393
1399
  const textureRecord = mappedTextures;
1394
- for (const key in input) {
1395
- const url = input[key];
1400
+ for (const key in stableInput) {
1401
+ const url = stableInput[key];
1396
1402
  urlTextureMap.push([url, textureRecord[key]]);
1397
1403
  }
1398
1404
  }
@@ -1421,7 +1427,7 @@ function useTexture(input, optionsOrOnLoad) {
1421
1427
  }
1422
1428
  return { _textureRefs: refs };
1423
1429
  });
1424
- }, [cache, input, mappedTextures, store]);
1430
+ }, [cache, stableInput, mappedTextures, store]);
1425
1431
  return mappedTextures;
1426
1432
  }
1427
1433
  useTexture.preload = (url) => useLoader.preload(webgpu.TextureLoader, url);
@@ -1492,13 +1498,13 @@ function useTextures(selector) {
1492
1498
  };
1493
1499
  }, [store]);
1494
1500
  const subscribe = selector ? () => selector(registry) : (state) => state.textures;
1495
- const selected = useThree(subscribe);
1501
+ const selected = useThree$1(subscribe);
1496
1502
  return selector ? selected : registry;
1497
1503
  }
1498
1504
 
1499
1505
  function useRenderTarget(widthOrOptions, heightOrOptions, options) {
1500
- const isLegacy = useThree((s) => s.isLegacy);
1501
- const size = useThree((s) => s.size);
1506
+ const isLegacy = useThree$1((s) => s.isLegacy);
1507
+ const size = useThree$1((s) => s.size);
1502
1508
  let width;
1503
1509
  let height;
1504
1510
  let opts;
@@ -1555,7 +1561,7 @@ function useEnvironment({
1555
1561
  const { extension, isCubemap } = getExtension(files);
1556
1562
  const loader = getLoader(extension);
1557
1563
  if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
1558
- const renderer = useThree((state) => state.renderer);
1564
+ const renderer = useThree$1((state) => state.renderer);
1559
1565
  React.useLayoutEffect(() => {
1560
1566
  if (extension !== "webp" && extension !== "jpg" && extension !== "jpeg") return;
1561
1567
  function clearGainmapTexture() {
@@ -1648,7 +1654,7 @@ function useStore() {
1648
1654
  if (!store) throw new Error("R3F: Hooks can only be used within the Canvas component!");
1649
1655
  return store;
1650
1656
  }
1651
- function useThree(selector = (state) => state, equalityFn) {
1657
+ function useThree$1(selector = (state) => state, equalityFn) {
1652
1658
  return useStore()(selector, equalityFn);
1653
1659
  }
1654
1660
  function useInstanceHandle(ref) {
@@ -14435,6 +14441,10 @@ function createRoot(canvas) {
14435
14441
  if (state2.internal.isMultiCanvas && state2.internal.canvasTarget) {
14436
14442
  const renderer2 = state2.internal.actualRenderer;
14437
14443
  renderer2.setCanvasTarget(state2.internal.canvasTarget);
14444
+ if (state2.internal.canvasTargetSizeDirty) {
14445
+ state2.internal.canvasTargetSizeDirty = false;
14446
+ renderer2.backend?.updateSize?.();
14447
+ }
14438
14448
  }
14439
14449
  },
14440
14450
  {
@@ -14739,7 +14749,7 @@ function setEnvProps(background, scene, defaultScene, texture, sceneProps = {})
14739
14749
  };
14740
14750
  }
14741
14751
  function EnvironmentMap({ scene, background = false, map, ...config }) {
14742
- const defaultScene = useThree((state) => state.scene);
14752
+ const defaultScene = useThree$1((state) => state.scene);
14743
14753
  React__namespace.useLayoutEffect(() => {
14744
14754
  if (map) return setEnvProps(background, scene, defaultScene, map, config);
14745
14755
  });
@@ -14757,7 +14767,7 @@ function EnvironmentCube({
14757
14767
  ...rest
14758
14768
  }) {
14759
14769
  const texture = useEnvironment(rest);
14760
- const defaultScene = useThree((state) => state.scene);
14770
+ const defaultScene = useThree$1((state) => state.scene);
14761
14771
  React__namespace.useLayoutEffect(() => {
14762
14772
  return setEnvProps(background, scene, defaultScene, texture, {
14763
14773
  backgroundBlurriness: blur ?? backgroundBlurriness,
@@ -14794,9 +14804,9 @@ function EnvironmentPortal({
14794
14804
  preset = void 0,
14795
14805
  extensions
14796
14806
  }) {
14797
- const renderer = useThree((state) => state.renderer);
14798
- const isLegacy = useThree((state) => state.isLegacy);
14799
- const defaultScene = useThree((state) => state.scene);
14807
+ const renderer = useThree$1((state) => state.renderer);
14808
+ const isLegacy = useThree$1((state) => state.isLegacy);
14809
+ const defaultScene = useThree$1((state) => state.scene);
14800
14810
  const camera = React__namespace.useRef(null);
14801
14811
  const [virtualScene] = React__namespace.useState(() => new webgpu.Scene());
14802
14812
  const fbo = React__namespace.useMemo(() => {
@@ -14840,7 +14850,7 @@ function EnvironmentPortal({
14840
14850
  environmentRotation
14841
14851
  ]);
14842
14852
  let count = 1;
14843
- useFrame(() => {
14853
+ useFrame$1(() => {
14844
14854
  if (frames === Infinity || count < frames) {
14845
14855
  const autoClear = renderer.autoClear;
14846
14856
  renderer.autoClear = true;
@@ -14880,7 +14890,7 @@ function EnvironmentGround(props) {
14880
14890
  ] });
14881
14891
  }
14882
14892
  function EnvironmentColor({ color, scene }) {
14883
- const defaultScene = useThree((state) => state.scene);
14893
+ const defaultScene = useThree$1((state) => state.scene);
14884
14894
  React__namespace.useLayoutEffect(() => {
14885
14895
  if (color === void 0) return;
14886
14896
  const target = resolveScene(scene || defaultScene);
@@ -15474,6 +15484,11 @@ function createTextureOperations(set) {
15474
15484
  removeMultiple: (keys) => removeTextures(set, keys)
15475
15485
  };
15476
15486
  }
15487
+ function scopedNodeName(scope, name) {
15488
+ const raw = scope ? `${scope}_${name}` : name;
15489
+ const safe = raw.replace(/[^A-Za-z0-9_]/g, "_");
15490
+ return /^[A-Za-z_]/.test(safe) ? safe : `_${safe}`;
15491
+ }
15477
15492
  function extractTSLValue(value) {
15478
15493
  if (value === null || value === void 0) return value;
15479
15494
  if (typeof value !== "object") return value;
@@ -15672,8 +15687,7 @@ function createUniform(inName, node, scope) {
15672
15687
  const inValue = vectorize(node);
15673
15688
  const newUniform = tsl.uniform(inValue);
15674
15689
  if (typeof newUniform.setName === "function") {
15675
- const name = scope ? `${scope}_${inName}` : inName;
15676
- newUniform.setName(name);
15690
+ newUniform.setName(scopedNodeName(scope, inName));
15677
15691
  }
15678
15692
  return newUniform;
15679
15693
  }
@@ -15772,7 +15786,7 @@ function useNodes(creatorOrScope, scope) {
15772
15786
  return creatorOrScope(createLazyCreatorState(store.getState(), store));
15773
15787
  },
15774
15788
  prepare: (name, node) => {
15775
- node.setName?.(scope ? `${scope}.${name}` : name);
15789
+ node.setName?.(scopedNodeName(scope, name));
15776
15790
  return node;
15777
15791
  }
15778
15792
  });
@@ -15881,7 +15895,7 @@ function useBuffers(creatorOrScope, scope) {
15881
15895
  },
15882
15896
  prepare: (name, buffer) => {
15883
15897
  if ("setName" in buffer && typeof buffer.setName === "function") {
15884
- buffer.setName(scope ? `${scope}.${name}` : name);
15898
+ buffer.setName(scopedNodeName(scope, name));
15885
15899
  }
15886
15900
  return buffer;
15887
15901
  }
@@ -15952,7 +15966,7 @@ function useGPUStorage(creatorOrScope, scope) {
15952
15966
  return creatorOrScope(createLazyCreatorState(store.getState(), store));
15953
15967
  },
15954
15968
  prepare: (name, storage) => {
15955
- const label = scope ? `${scope}.${name}` : name;
15969
+ const label = scopedNodeName(scope, name);
15956
15970
  if ("setName" in storage && typeof storage.setName === "function") {
15957
15971
  storage.setName(label);
15958
15972
  }
@@ -15983,9 +15997,10 @@ function rebuildAllStorage(store, scope) {
15983
15997
 
15984
15998
  function useRenderPipeline(mainCB, setupCB) {
15985
15999
  const store = useStore();
15986
- const { scene, camera, renderer, isLegacy } = useThree();
16000
+ const { scene, camera, renderer, isLegacy } = useThree$1();
15987
16001
  const callbacksRanRef = React.useRef(false);
15988
16002
  const scenePassCacheRef = React.useRef(null);
16003
+ const forceScenePassRebuildRef = React.useRef(false);
15989
16004
  const mainCBRef = React.useRef(mainCB);
15990
16005
  const setupCBRef = React.useRef(setupCB);
15991
16006
  mainCBRef.current = mainCB;
@@ -15993,7 +16008,7 @@ function useRenderPipeline(mainCB, setupCB) {
15993
16008
  const [rebuildVersion, setRebuildVersion] = React.useState(0);
15994
16009
  React.useEffect(() => {
15995
16010
  callbacksRanRef.current = false;
15996
- scenePassCacheRef.current = null;
16011
+ forceScenePassRebuildRef.current = true;
15997
16012
  }, []);
15998
16013
  const clearPasses = React.useCallback(() => {
15999
16014
  store.setState({ passes: {} });
@@ -16003,12 +16018,14 @@ function useRenderPipeline(mainCB, setupCB) {
16003
16018
  renderPipeline: null,
16004
16019
  passes: {}
16005
16020
  });
16021
+ scenePassCacheRef.current?.scenePass?.dispose?.();
16006
16022
  callbacksRanRef.current = false;
16007
16023
  scenePassCacheRef.current = null;
16024
+ forceScenePassRebuildRef.current = false;
16008
16025
  }, [store]);
16009
16026
  const rebuild = React.useCallback(() => {
16010
16027
  callbacksRanRef.current = false;
16011
- scenePassCacheRef.current = null;
16028
+ forceScenePassRebuildRef.current = true;
16012
16029
  setRebuildVersion((v) => v + 1);
16013
16030
  }, []);
16014
16031
  React.useLayoutEffect(() => {
@@ -16026,16 +16043,23 @@ function useRenderPipeline(mainCB, setupCB) {
16026
16043
  pipeline = new webgpu.RenderPipeline(renderer);
16027
16044
  justCreatedPipeline = true;
16028
16045
  }
16029
- const cacheValid = scenePassCacheRef.current && scenePassCacheRef.current.sceneUuid === scene.uuid && scenePassCacheRef.current.cameraUuid === camera.uuid;
16046
+ const cacheValid = !forceScenePassRebuildRef.current && scenePassCacheRef.current && scenePassCacheRef.current.sceneUuid === scene.uuid && scenePassCacheRef.current.cameraUuid === camera.uuid;
16047
+ let outgoingScenePass = null;
16030
16048
  let scenePass;
16031
16049
  if (cacheValid) {
16032
16050
  scenePass = scenePassCacheRef.current.scenePass;
16033
16051
  } else {
16052
+ outgoingScenePass = scenePassCacheRef.current?.scenePass ?? null;
16034
16053
  scenePass = tsl.pass(scene, camera);
16035
16054
  scenePassCacheRef.current = { sceneUuid: scene.uuid, cameraUuid: camera.uuid, scenePass };
16055
+ forceScenePassRebuildRef.current = false;
16036
16056
  }
16037
16057
  currentPasses.scenePass = scenePass;
16038
- if (!pipeline.outputNode || justCreatedPipeline) pipeline.outputNode = scenePass;
16058
+ let outputNodeChanged = false;
16059
+ if (!pipeline.outputNode || justCreatedPipeline || pipeline.outputNode === outgoingScenePass) {
16060
+ pipeline.outputNode = scenePass;
16061
+ outputNodeChanged = true;
16062
+ }
16039
16063
  set({ renderPipeline: pipeline, passes: currentPasses });
16040
16064
  const shouldRunCallbacks = justCreatedPipeline || !callbacksRanRef.current || !cacheValid;
16041
16065
  if (shouldRunCallbacks) {
@@ -16055,14 +16079,17 @@ function useRenderPipeline(mainCB, setupCB) {
16055
16079
  set({ passes: currentPasses });
16056
16080
  }
16057
16081
  }
16082
+ outputNodeChanged = true;
16058
16083
  callbacksRanRef.current = true;
16059
16084
  }
16085
+ if (outputNodeChanged) pipeline.needsUpdate = true;
16086
+ if (outgoingScenePass && outgoingScenePass !== scenePass) outgoingScenePass.dispose?.();
16060
16087
  } catch (error) {
16061
16088
  console.warn("[useRenderPipeline] Setup failed; the render pipeline was not configured:", error);
16062
16089
  }
16063
16090
  }, [store, renderer, scene, camera, isLegacy, rebuildVersion]);
16064
- const passes = useThree((s) => s.passes);
16065
- const renderPipeline = useThree((s) => s.renderPipeline);
16091
+ const passes = useThree$1((s) => s.passes);
16092
+ const renderPipeline = useThree$1((s) => s.renderPipeline);
16066
16093
  return {
16067
16094
  passes,
16068
16095
  renderPipeline,
@@ -16075,6 +16102,8 @@ function useRenderPipeline(mainCB, setupCB) {
16075
16102
  }
16076
16103
 
16077
16104
  extend(THREE);
16105
+ const useThree = useThree$1;
16106
+ const useFrame = useFrame$1;
16078
16107
 
16079
16108
  exports.Scheduler = scheduler.Scheduler;
16080
16109
  exports.getScheduler = scheduler.getScheduler;