@gnsx/three 0.185.4 → 0.185.6

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.
@@ -33,6 +33,7 @@ import {
33
33
  storageTexture,
34
34
  textureStore,
35
35
  uniform,
36
+ uint,
36
37
  uvec3,
37
38
  vec3,
38
39
  vec4
@@ -102,11 +103,11 @@ const ATLAS_PADDING = 1;
102
103
  * Atlas layout (nz = resolution.z, PADDING = 1):
103
104
  * ```
104
105
  * slice 0 : padding (copy of sub-volume 0, data slice 0)
105
- * slices 1 �? nz : sub-volume 0 data
106
+ * slices 1 to nz : sub-volume 0 data
106
107
  * slice nz + 1 : padding (copy of sub-volume 0, data slice nz-1)
107
108
  * slice nz + 2 : padding (copy of sub-volume 1, data slice 0)
108
- * slices nz+3 �? 2*nz+2 : sub-volume 1 data
109
- * �?
109
+ * slices nz+3 to 2*nz+2 : sub-volume 1 data
110
+ * ...
110
111
  * ```
111
112
  * Total atlas depth = `7 * ( nz + 2 )`.
112
113
  *
@@ -279,7 +280,7 @@ class LightProbeGrid extends Object3D {
279
280
  /**
280
281
  * Bakes all probes by rendering cubemaps at each probe position
281
282
  * and projecting to L2 SH. Optionally iterates additional passes to
282
- * capture indirect bounces �? each extra pass samples the previous pass's
283
+ * capture indirect bounces - each extra pass samples the previous pass's
283
284
  * atlas as indirect light, so a grid added to the scene before baking
284
285
  * accumulates one bounce per extra pass.
285
286
  *
@@ -328,7 +329,7 @@ class LightProbeGrid extends Object3D {
328
329
  renderer.getScissor( _currentScissor );
329
330
  const currentScissorTest = renderer.getScissorTest();
330
331
 
331
- // Scene is static across the bake �? update once and disable per-render auto updates.
332
+ // Scene is static across the bake - update once and disable per-render auto updates.
332
333
  const currentMatrixWorldAutoUpdate = scene.matrixWorldAutoUpdate;
333
334
  if ( currentMatrixWorldAutoUpdate === true ) {
334
335
 
@@ -337,7 +338,7 @@ class LightProbeGrid extends Object3D {
337
338
 
338
339
  }
339
340
 
340
- // Disable shadow map auto-update across all passes �? lights don't move.
341
+ // Disable shadow map auto-update across all passes - lights don't move.
341
342
  // Force a single shadow update on the first render so maps are initialized.
342
343
  const currentShadowAutoUpdate = renderer.shadowMap.autoUpdate;
343
344
  renderer.shadowMap.autoUpdate = false;
@@ -351,7 +352,7 @@ class LightProbeGrid extends Object3D {
351
352
  // const t0 = performance.now();
352
353
 
353
354
  // Pass 0 captures direct light only (grid hidden, so probesSH is not sampled
354
- // �? the atlas at this point may be uninitialized or hold a prior bake).
355
+ // the atlas at this point may be uninitialized or hold a prior bake).
355
356
  // Each subsequent pass keeps the grid visible so the cube cameras read the
356
357
  // previous pass's atlas as indirect light, accumulating one bounce per pass.
357
358
  // Phase 1 writes to the batch target and Phase 2 only swaps it into the atlas
@@ -439,11 +440,11 @@ class LightProbeGrid extends Object3D {
439
440
 
440
441
  }
441
442
 
442
- // Phase 2: Repack SH data from batch target into the atlas 3D texture (GPU-to-GPU).
443
+ // Phase 2: Repack SH data from batch target into the atlas 3D texture on the GPU.
443
444
  //
444
445
  // For each of the 7 packed sub-volumes (texture index t) we write:
445
446
  // - A leading padding slice (copy of data slice iz = 0)
446
- // - All nz data slices (iz = 0 �? nz-1)
447
+ // - All nz data slices (iz = 0 to nz-1)
447
448
  // - A trailing padding slice (copy of data slice iz = nz-1)
448
449
  //
449
450
  // In the atlas the slices for sub-volume t occupy the range:
@@ -675,31 +676,6 @@ class LightProbeGrid extends Object3D {
675
676
 
676
677
  renderer.shadowMap.autoUpdate = savedShadowAutoUpdate;
677
678
 
678
- // Padding slices (GPU-to-GPU copy of nearest edge data slices).
679
- const atlas = this.texture;
680
-
681
- for ( let textureIndex = 0; textureIndex < 7; textureIndex ++ ) {
682
-
683
- const baseSlice = textureIndex * paddedSlices;
684
- const dataSlice = baseSlice + ATLAS_PADDING;
685
- const trailingSlice = dataSlice + nz;
686
-
687
- renderer.copyTextureToTexture(
688
- atlas,
689
- atlas,
690
- new Box3( new Vector3( 0, 0, dataSlice ), new Vector3( nx, ny, dataSlice + 1 ) ),
691
- new Vector3( 0, 0, baseSlice )
692
- );
693
-
694
- renderer.copyTextureToTexture(
695
- atlas,
696
- atlas,
697
- new Box3( new Vector3( 0, 0, dataSlice + nz - 1 ), new Vector3( nx, ny, dataSlice + nz ) ),
698
- new Vector3( 0, 0, trailingSlice )
699
- );
700
-
701
- }
702
-
703
679
  await renderer.backend.device.queue.onSubmittedWorkDone();
704
680
 
705
681
  }
@@ -1142,7 +1118,7 @@ function _ensureWebGPUComputeResources( cubemapSize, envMapTexture, atlasTexture
1142
1118
  totalWeight.addAssign( weight );
1143
1119
 
1144
1120
  const dir = coord.normalize();
1145
- // Pass explicit direction �? omitting UV would default to reflectVector (needs camera).
1121
+ // Pass explicit direction; omitting UV would default to reflectVector (needs camera).
1146
1122
  const cw = cubeTexture( envMapTexture, coord ).rgb.mul( weight );
1147
1123
 
1148
1124
  accum0.addAssign( cw.mul( 0.282095 ) );
@@ -1172,35 +1148,56 @@ function _ensureWebGPUComputeResources( cubemapSize, envMapTexture, atlasTexture
1172
1148
 
1173
1149
  Loop( { end: 7, name: 't', type: 'int' }, ( { t } ) => {
1174
1150
 
1175
- const atlasZ = paddedSlices.mul( t ).add( 1 ).add( probeIz );
1151
+ const baseSlice = paddedSlices.mul( t );
1152
+ const atlasZ = baseSlice.add( 1 ).add( probeIz );
1153
+ const trailingSlice = baseSlice.add( paddedSlices ).sub( 1 );
1154
+ const lastDataSlice = paddedSlices.sub( 3 );
1155
+ const storeProbeValue = ( value ) => {
1156
+
1157
+ textureStore( atlas, uvec3( probeIx, probeIy, atlasZ ), value ).toWriteOnly();
1158
+
1159
+ // Write padding during the compute pass; WebGPU validation rejects atlas-to-itself copies.
1160
+ If( probeIz.equal( uint( 0 ) ), () => {
1161
+
1162
+ textureStore( atlas, uvec3( probeIx, probeIy, baseSlice ), value ).toWriteOnly();
1163
+
1164
+ } );
1165
+
1166
+ If( probeIz.equal( lastDataSlice ), () => {
1167
+
1168
+ textureStore( atlas, uvec3( probeIx, probeIy, trailingSlice ), value ).toWriteOnly();
1169
+
1170
+ } );
1171
+
1172
+ };
1176
1173
 
1177
1174
  If( t.equal( int( 0 ) ), () => {
1178
1175
 
1179
- textureStore( atlas, uvec3( probeIx, probeIy, atlasZ ), vec4( c0.x, c0.y, c0.z, c1.x ) ).toWriteOnly();
1176
+ storeProbeValue( vec4( c0.x, c0.y, c0.z, c1.x ) );
1180
1177
 
1181
1178
  } ).ElseIf( t.equal( int( 1 ) ), () => {
1182
1179
 
1183
- textureStore( atlas, uvec3( probeIx, probeIy, atlasZ ), vec4( c1.y, c1.z, c2.x, c2.y ) ).toWriteOnly();
1180
+ storeProbeValue( vec4( c1.y, c1.z, c2.x, c2.y ) );
1184
1181
 
1185
1182
  } ).ElseIf( t.equal( int( 2 ) ), () => {
1186
1183
 
1187
- textureStore( atlas, uvec3( probeIx, probeIy, atlasZ ), vec4( c2.z, c3.x, c3.y, c3.z ) ).toWriteOnly();
1184
+ storeProbeValue( vec4( c2.z, c3.x, c3.y, c3.z ) );
1188
1185
 
1189
1186
  } ).ElseIf( t.equal( int( 3 ) ), () => {
1190
1187
 
1191
- textureStore( atlas, uvec3( probeIx, probeIy, atlasZ ), vec4( c4.x, c4.y, c4.z, c5.x ) ).toWriteOnly();
1188
+ storeProbeValue( vec4( c4.x, c4.y, c4.z, c5.x ) );
1192
1189
 
1193
1190
  } ).ElseIf( t.equal( int( 4 ) ), () => {
1194
1191
 
1195
- textureStore( atlas, uvec3( probeIx, probeIy, atlasZ ), vec4( c5.y, c5.z, c6.x, c6.y ) ).toWriteOnly();
1192
+ storeProbeValue( vec4( c5.y, c5.z, c6.x, c6.y ) );
1196
1193
 
1197
1194
  } ).ElseIf( t.equal( int( 5 ) ), () => {
1198
1195
 
1199
- textureStore( atlas, uvec3( probeIx, probeIy, atlasZ ), vec4( c6.z, c7.x, c7.y, c7.z ) ).toWriteOnly();
1196
+ storeProbeValue( vec4( c6.z, c7.x, c7.y, c7.z ) );
1200
1197
 
1201
1198
  } ).Else( () => {
1202
1199
 
1203
- textureStore( atlas, uvec3( probeIx, probeIy, atlasZ ), vec4( c8.x, c8.y, c8.z, float( 0 ) ) ).toWriteOnly();
1200
+ storeProbeValue( vec4( c8.x, c8.y, c8.z, float( 0 ) ) );
1204
1201
 
1205
1202
  } );
1206
1203
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gnsx/three",
3
- "version": "0.185.4",
3
+ "version": "0.185.6",
4
4
  "description": "JavaScript 3D library",
5
5
  "type": "module",
6
6
  "main": "./build/three.cjs",
@@ -2267,6 +2267,11 @@ class WebGPUBackend extends Backend {
2267
2267
 
2268
2268
  const baseOffset = timestampQueryPool.allocateQueriesForContext( uid );
2269
2269
 
2270
+ // WITH_GENESYS
2271
+ // If query allocation fails, leave timestampWrites unset so WebGPU doesn't receive a null querySet.
2272
+ if ( baseOffset === null || timestampQueryPool.querySet === null ) return;
2273
+ // !WITH_GENESYS
2274
+
2270
2275
  _renderPassTimestampWrites.querySet = timestampQueryPool.querySet;
2271
2276
  _renderPassTimestampWrites.beginningOfPassWriteIndex = baseOffset;
2272
2277
  _renderPassTimestampWrites.endOfPassWriteIndex = baseOffset + 1;
@@ -250,6 +250,17 @@ class WebGPUTextureUtils {
250
250
  if ( bindingData.sampler !== undefined ) {
251
251
 
252
252
  const samplerData = this._samplerCache.get( bindingData.samplerKey );
253
+ // WITH_GENESYS
254
+ // Binding disposal can be reached more than once during renderer teardown; tolerate stale sampler cache data.
255
+ if ( samplerData === undefined ) {
256
+
257
+ bindingData.sampler = undefined;
258
+ bindingData.samplerKey = undefined;
259
+ return;
260
+
261
+ }
262
+
263
+ // !WITH_GENESYS
253
264
  samplerData.usedTimes --;
254
265
 
255
266
  if ( samplerData.usedTimes === 0 ) {