@threenative/core 0.3.2 → 0.3.4
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/capabilities.json +1891 -101
- package/dist/{assets-kyoF7JlJ.d.ts → assets-CqvE429w.d.ts} +52 -3
- package/dist/{audio-BFiGneTL.d.ts → audio-7i3Xl0l3.d.ts} +32 -1
- package/dist/{canvas-layer-BLVijiUJ.d.ts → canvas-layer-DDmC_VVF.d.ts} +1 -1
- package/dist/{game-XGrTzapq.d.ts → game-CljaDv4D.d.ts} +503 -11
- package/dist/{gpu-readback-D2iRvoe9.d.ts → gpu-readback-CqJEfWNQ.d.ts} +16 -6
- package/dist/hot.d.ts +5 -5
- package/dist/hot.js +20 -3
- package/dist/index.d.ts +1496 -43
- package/dist/index.js +8345 -753
- package/dist/playtest.d.ts +11 -5
- package/dist/playtest.js +96 -11
- package/dist/react.d.ts +2 -2
- package/dist/{renderer-C6hqZpoG.d.ts → renderer-CfsS2hxi.d.ts} +436 -14
- package/dist/ui-layer.d.ts +15 -7
- package/dist/ui-layer.js +2 -1
- package/dist/world.d.ts +900 -14
- package/dist/world.js +10876 -126
- package/gpl/fixtures/make_world_fixture.py +184 -0
- package/gpl/recipes/_common.py +11 -0
- package/gpl/recipes/decimate.py +11 -5
- package/gpl/recipes/export_world.py +682 -0
- package/mcp/blender-server.mjs +55 -1
- package/mcp/engine-server.mjs +84 -22
- package/mcp/servers.mjs +3 -3
- package/package.json +6 -6
- package/patches/three@0.185.1.patch +2228 -105
- package/scripts/apply-three-patch.mjs +61 -37
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
diff --git a/build/three.cjs b/build/three.cjs
|
|
2
|
-
index
|
|
2
|
+
index 8939a67..b0f9172 100644
|
|
3
3
|
--- a/build/three.cjs
|
|
4
4
|
+++ b/build/three.cjs
|
|
5
5
|
@@ -26116,6 +26116,8 @@ function copyArrayContents( src, target ) {
|
|
@@ -12,7 +12,7 @@ index 8939a67dd76325ccd25ee8806fc4d22b1caea88a..b0f917241f1f2cd25baa21dec20edda3
|
|
|
12
12
|
* Constructs a new batched mesh.
|
|
13
13
|
*
|
|
14
14
|
diff --git a/build/three.core.js b/build/three.core.js
|
|
15
|
-
index
|
|
15
|
+
index 0bb9262..868a634 100644
|
|
16
16
|
--- a/build/three.core.js
|
|
17
17
|
+++ b/build/three.core.js
|
|
18
18
|
@@ -26136,6 +26136,8 @@ function copyArrayContents( src, target ) {
|
|
@@ -25,10 +25,277 @@ index 0bb9262cd029f411933d077fd44197a51ec5e8e9..868a634b4294be5f32664912a50813d5
|
|
|
25
25
|
* Constructs a new batched mesh.
|
|
26
26
|
*
|
|
27
27
|
diff --git a/build/three.webgpu.js b/build/three.webgpu.js
|
|
28
|
-
index
|
|
28
|
+
index 2e49079..7aa62ad 100644
|
|
29
29
|
--- a/build/three.webgpu.js
|
|
30
30
|
+++ b/build/three.webgpu.js
|
|
31
|
-
@@ -
|
|
31
|
+
@@ -141,6 +141,15 @@ class NodeMaterialObserver {
|
|
32
|
+
*/
|
|
33
|
+
this.renderId = 0;
|
|
34
|
+
|
|
35
|
+
+ /**
|
|
36
|
+
+ * Whether the current render produces motion vectors. Objects keep refreshing
|
|
37
|
+
+ * fully while this is the case.
|
|
38
|
+
+ *
|
|
39
|
+
+ * @type {boolean}
|
|
40
|
+
+ * @default false
|
|
41
|
+
+ */
|
|
42
|
+
+ this.renderingVelocity = false;
|
|
43
|
+
+
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
@@ -240,6 +249,88 @@ class NodeMaterialObserver {
|
|
48
|
+
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
+ /**
|
|
52
|
+
+ * Returns `true` if the given render object is a static object whose per-object state - world
|
|
53
|
+
+ * matrix, material, geometry and the scene environment - did not change since the last
|
|
54
|
+
+ * refresh. A settled object is still drawn every frame and its FRAME and RENDER nodes still
|
|
55
|
+
+ * have to be updated; what it may skip is the work whose inputs it froze: its OBJECT node
|
|
56
|
+
+ * updates, its object uniform group upload and its per-draw attribute scan.
|
|
57
|
+
+ *
|
|
58
|
+
+ * @param {RenderObject} renderObject - The render object.
|
|
59
|
+
+ * @return {boolean} Whether the given render object is settled or not.
|
|
60
|
+
+ */
|
|
61
|
+
+ isSettled( renderObject ) {
|
|
62
|
+
+
|
|
63
|
+
+ const data = this.renderObjects.get( renderObject );
|
|
64
|
+
+
|
|
65
|
+
+ return data !== undefined && data.settledState === true;
|
|
66
|
+
+
|
|
67
|
+
+ }
|
|
68
|
+
+
|
|
69
|
+
+ /**
|
|
70
|
+
+ * Recomputes {@link NodeMaterialObserver#isSettled} for the given render object, once per draw,
|
|
71
|
+
+ * so every step of that draw reads the same answer.
|
|
72
|
+
+ *
|
|
73
|
+
+ * @param {RenderObject} renderObject - The render object.
|
|
74
|
+
+ * @return {boolean} Whether the given render object is settled or not.
|
|
75
|
+
+ */
|
|
76
|
+
+ updateSettled( renderObject ) {
|
|
77
|
+
+
|
|
78
|
+
+ const { object, material, geometry, scene } = renderObject;
|
|
79
|
+
+ const data = this.getRenderObjectData( renderObject );
|
|
80
|
+
+
|
|
81
|
+
+ if ( object.static !== true || this.renderingVelocity === true ) {
|
|
82
|
+
+
|
|
83
|
+
+ data.settledState = false;
|
|
84
|
+
+
|
|
85
|
+
+ return false;
|
|
86
|
+
+
|
|
87
|
+
+ }
|
|
88
|
+
+
|
|
89
|
+
+ let settled = data.settled;
|
|
90
|
+
+
|
|
91
|
+
+ if ( settled === undefined ) {
|
|
92
|
+
+
|
|
93
|
+
+ data.settled = settled = {
|
|
94
|
+
+ matrix: object.matrixWorld.clone(),
|
|
95
|
+
+ environmentRotation: scene.environmentRotation.clone(),
|
|
96
|
+
+ environmentIntensity: scene.environmentIntensity,
|
|
97
|
+
+ materialVersion: material.version,
|
|
98
|
+
+ geometryVersion: geometry.version
|
|
99
|
+
+ };
|
|
100
|
+
+
|
|
101
|
+
+ data.settledState = false;
|
|
102
|
+
+
|
|
103
|
+
+ return false;
|
|
104
|
+
+
|
|
105
|
+
+ }
|
|
106
|
+
+
|
|
107
|
+
+ if ( settled.matrix.equals( object.matrixWorld ) !== true ||
|
|
108
|
+
+ settled.environmentRotation.equals( scene.environmentRotation ) !== true ||
|
|
109
|
+
+ settled.environmentIntensity !== scene.environmentIntensity ||
|
|
110
|
+
+ settled.materialVersion !== material.version ||
|
|
111
|
+
+ settled.geometryVersion !== geometry.version ) {
|
|
112
|
+
+
|
|
113
|
+
+ // re-arm with the current state, so the object settles again once it stops changing
|
|
114
|
+
+
|
|
115
|
+
+ settled.matrix.copy( object.matrixWorld );
|
|
116
|
+
+ settled.environmentRotation.copy( scene.environmentRotation );
|
|
117
|
+
+ settled.environmentIntensity = scene.environmentIntensity;
|
|
118
|
+
+ settled.materialVersion = material.version;
|
|
119
|
+
+ settled.geometryVersion = geometry.version;
|
|
120
|
+
+
|
|
121
|
+
+ data.settledState = false;
|
|
122
|
+
+
|
|
123
|
+
+ return false;
|
|
124
|
+
+
|
|
125
|
+
+ }
|
|
126
|
+
+
|
|
127
|
+
+ data.settledState = true;
|
|
128
|
+
+
|
|
129
|
+
+ return true;
|
|
130
|
+
+
|
|
131
|
+
+ }
|
|
132
|
+
+
|
|
133
|
+
/**
|
|
134
|
+
* Returns an attribute data structure holding the attributes versions for
|
|
135
|
+
* monitoring.
|
|
136
|
+
@@ -306,7 +397,6 @@ class NodeMaterialObserver {
|
|
137
|
+
|
|
138
|
+
data = {
|
|
139
|
+
_renderId: -1,
|
|
140
|
+
- _equal: false,
|
|
141
|
+
|
|
142
|
+
attributes: this.getAttributesData( geometry.attributes ),
|
|
143
|
+
indexId: geometry.index ? geometry.index.id : null,
|
|
144
|
+
@@ -335,7 +425,7 @@ class NodeMaterialObserver {
|
|
145
|
+
|
|
146
|
+
if ( data === undefined ) {
|
|
147
|
+
|
|
148
|
+
- data = { _renderId: -1, _equal: false };
|
|
149
|
+
+ data = { _renderId: -1 };
|
|
150
|
+
|
|
151
|
+
for ( const property of this.refreshUniforms ) {
|
|
152
|
+
|
|
153
|
+
@@ -419,7 +509,6 @@ class NodeMaterialObserver {
|
|
154
|
+
|
|
155
|
+
value.copy( mtlValue );
|
|
156
|
+
|
|
157
|
+
- materialData._equal = false;
|
|
158
|
+
return false;
|
|
159
|
+
|
|
160
|
+
}
|
|
161
|
+
@@ -431,7 +520,6 @@ class NodeMaterialObserver {
|
|
162
|
+
value.id = mtlValue.id;
|
|
163
|
+
value.version = mtlValue.version;
|
|
164
|
+
|
|
165
|
+
- materialData._equal = false;
|
|
166
|
+
return false;
|
|
167
|
+
|
|
168
|
+
}
|
|
169
|
+
@@ -440,7 +528,6 @@ class NodeMaterialObserver {
|
|
170
|
+
|
|
171
|
+
materialData[ property ] = mtlValue;
|
|
172
|
+
|
|
173
|
+
- materialData._equal = false;
|
|
174
|
+
return false;
|
|
175
|
+
|
|
176
|
+
}
|
|
177
|
+
@@ -456,18 +543,12 @@ class NodeMaterialObserver {
|
|
178
|
+
renderObjectData.bufferWidth = width;
|
|
179
|
+
renderObjectData.bufferHeight = height;
|
|
180
|
+
|
|
181
|
+
- materialData._equal = false;
|
|
182
|
+
return false;
|
|
183
|
+
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
- materialData._equal = true;
|
|
189
|
+
-
|
|
190
|
+
- } else {
|
|
191
|
+
-
|
|
192
|
+
- if ( materialData._equal === false ) return false;
|
|
193
|
+
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
@@ -510,7 +591,6 @@ class NodeMaterialObserver {
|
|
197
|
+
// attribute was removed
|
|
198
|
+
delete storedAttributes[ name ];
|
|
199
|
+
|
|
200
|
+
- geometryData._equal = false;
|
|
201
|
+
return false;
|
|
202
|
+
|
|
203
|
+
}
|
|
204
|
+
@@ -523,7 +603,6 @@ class NodeMaterialObserver {
|
|
205
|
+
storedAttributeData.id = id;
|
|
206
|
+
storedAttributeData.version = version;
|
|
207
|
+
|
|
208
|
+
- geometryData._equal = false;
|
|
209
|
+
return false;
|
|
210
|
+
|
|
211
|
+
}
|
|
212
|
+
@@ -534,7 +613,6 @@ class NodeMaterialObserver {
|
|
213
|
+
|
|
214
|
+
geometryData.attributes = this.getAttributesData( attributes );
|
|
215
|
+
|
|
216
|
+
- geometryData._equal = false;
|
|
217
|
+
return false;
|
|
218
|
+
|
|
219
|
+
}
|
|
220
|
+
@@ -552,7 +630,6 @@ class NodeMaterialObserver {
|
|
221
|
+
geometryData.indexId = currentIndexId;
|
|
222
|
+
geometryData.indexVersion = currentIndexVersion;
|
|
223
|
+
|
|
224
|
+
- geometryData._equal = false;
|
|
225
|
+
return false;
|
|
226
|
+
|
|
227
|
+
}
|
|
228
|
+
@@ -564,16 +641,10 @@ class NodeMaterialObserver {
|
|
229
|
+
geometryData.drawRange.start = geometry.drawRange.start;
|
|
230
|
+
geometryData.drawRange.count = geometry.drawRange.count;
|
|
231
|
+
|
|
232
|
+
- geometryData._equal = false;
|
|
233
|
+
return false;
|
|
234
|
+
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
- geometryData._equal = true;
|
|
238
|
+
-
|
|
239
|
+
- } else {
|
|
240
|
+
-
|
|
241
|
+
- if ( geometryData._equal === false ) return false;
|
|
242
|
+
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
@@ -724,7 +795,14 @@ class NodeMaterialObserver {
|
|
246
|
+
*/
|
|
247
|
+
needsRefresh( renderObject, nodeFrame ) {
|
|
248
|
+
|
|
249
|
+
- if ( this.hasNode || this.hasAnimation || this.firstInitialization( renderObject ) || this.needsVelocity( nodeFrame.renderer ) )
|
|
250
|
+
+ this.renderingVelocity = this.needsVelocity( nodeFrame.renderer );
|
|
251
|
+
+
|
|
252
|
+
+ // Answered for the whole draw, so updateBefore, the geometry scan, the node updates and
|
|
253
|
+
+ // the object uniform upload of this object all skip or all refresh.
|
|
254
|
+
+
|
|
255
|
+
+ this.updateSettled( renderObject );
|
|
256
|
+
+
|
|
257
|
+
+ if ( this.hasNode || this.hasAnimation || this.firstInitialization( renderObject ) || this.renderingVelocity )
|
|
258
|
+
return true;
|
|
259
|
+
|
|
260
|
+
const { renderId } = nodeFrame;
|
|
261
|
+
@@ -737,10 +815,14 @@ class NodeMaterialObserver {
|
|
262
|
+
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
- const isStatic = renderObject.object.static === true;
|
|
266
|
+
const isBundle = renderObject.bundle !== null && renderObject.bundle.static === true && this.getRenderObjectData( renderObject ).version === renderObject.bundle.version;
|
|
267
|
+
|
|
268
|
+
- if ( isStatic || isBundle )
|
|
269
|
+
+ // `object.static` is not the question - it is a switch, and a brand new object has it set
|
|
270
|
+
+ // before it has been drawn once, so answering from it skips the draw that creates this
|
|
271
|
+
+ // object's bind groups and leaves the pipeline reading a layout that was never built. A
|
|
272
|
+
+ // static object that moved, or whose material or geometry was edited, has to refresh like
|
|
273
|
+
+ // any other; only a settled one may skip, and nothing is settled before its first refresh.
|
|
274
|
+
+ if ( this.isSettled( renderObject ) === true || isBundle )
|
|
275
|
+
return false;
|
|
276
|
+
|
|
277
|
+
const lightsData = this.getLights( renderObject.lightsNode, renderId );
|
|
278
|
+
@@ -18320,7 +18402,8 @@ function createInstanceMatrixNode( builder, instanceMatrix ) {
|
|
279
|
+
|
|
280
|
+
const uniformBufferSize = matrixCount * 16 * 4;
|
|
281
|
+
|
|
282
|
+
- if ( uniformBufferSize <= builder.getUniformBufferLimit() ) {
|
|
283
|
+
+ // threenative: the uniform path re-uploads every draw; the attribute path uploads on version change
|
|
284
|
+
+ if ( false ) {
|
|
285
|
+
|
|
286
|
+
instanceMatrixNode = buffer( instanceMatrix.array, 'mat4', matrixCount ).element( instanceIndex );
|
|
287
|
+
|
|
288
|
+
@@ -18414,7 +18497,8 @@ const instance = /*@__PURE__*/ Fn( ( [ matrices, colors = null ], builder ) => {
|
|
289
|
+
|
|
290
|
+
const uniformBufferSize = Math.max( matrices.count, 1 ) * 16 * 4;
|
|
291
|
+
|
|
292
|
+
- if ( uniformBufferSize > builder.getUniformBufferLimit() ) {
|
|
293
|
+
+ // threenative: the uniform path re-uploads every draw; the attribute path uploads on version change
|
|
294
|
+
+ if ( true ) {
|
|
295
|
+
|
|
296
|
+
interleavedMatrix = _matrixBuffers.get( matrices );
|
|
297
|
+
|
|
298
|
+
@@ -18500,7 +18584,8 @@ const instance = /*@__PURE__*/ Fn( ( [ matrices, colors = null ], builder ) => {
|
|
32
299
|
|
|
33
300
|
const previousInstanceData = _previousInstanceMatrices.get( object );
|
|
34
301
|
|
|
@@ -38,7 +305,7 @@ index 2e490796d4d4aa9479b04a5c46ddbf31cc05b1a1..d05599255744ccfca19dbfd0433da612
|
|
|
38
305
|
|
|
39
306
|
} );
|
|
40
307
|
|
|
41
|
-
@@ -18599,6 +
|
|
308
|
+
@@ -18599,6 +18684,7 @@ const batch = /*@__PURE__*/ Fn( ( [ batchMesh ], builder ) => {
|
|
42
309
|
const indirectId = getIndirectIndex( batchMesh._indirectTexture, int( batchingIdNode ) );
|
|
43
310
|
|
|
44
311
|
const matricesTexture = batchMesh._matricesTexture;
|
|
@@ -46,7 +313,7 @@ index 2e490796d4d4aa9479b04a5c46ddbf31cc05b1a1..d05599255744ccfca19dbfd0433da612
|
|
|
46
313
|
|
|
47
314
|
const size = int( textureSize( textureLoad( matricesTexture ), 0 ).x ).toConst();
|
|
48
315
|
const j = float( indirectId ).mul( 4 ).toInt().toConst();
|
|
49
|
-
@@ -18626,6 +
|
|
316
|
+
@@ -18626,6 +18712,19 @@ const batch = /*@__PURE__*/ Fn( ( [ batchMesh ], builder ) => {
|
|
50
317
|
|
|
51
318
|
positionLocal.assign( batchingMatrix.mul( positionLocal ) );
|
|
52
319
|
|
|
@@ -66,7 +333,7 @@ index 2e490796d4d4aa9479b04a5c46ddbf31cc05b1a1..d05599255744ccfca19dbfd0433da612
|
|
|
66
333
|
const transformedNormal = normalLocal.div( vec3( bm[ 0 ].dot( bm[ 0 ] ), bm[ 1 ].dot( bm[ 1 ] ), bm[ 2 ].dot( bm[ 2 ] ) ) );
|
|
67
334
|
|
|
68
335
|
const batchingNormal = bm.mul( transformedNormal ).xyz;
|
|
69
|
-
@@ -18777,7 +
|
|
336
|
+
@@ -18777,7 +18876,8 @@ const skinning = /*@__PURE__*/ Fn( ( [ skinnedMesh ], builder ) => {
|
|
70
337
|
|
|
71
338
|
if ( skeletonData !== undefined ) {
|
|
72
339
|
|
|
@@ -76,7 +343,7 @@ index 2e490796d4d4aa9479b04a5c46ddbf31cc05b1a1..d05599255744ccfca19dbfd0433da612
|
|
|
76
343
|
|
|
77
344
|
}
|
|
78
345
|
|
|
79
|
-
@@ -18844,7 +
|
|
346
|
+
@@ -18844,7 +18944,8 @@ const computeSkinning = /*@__PURE__*/ Fn( ( [ skinnedMesh, toPosition = null ],
|
|
80
347
|
|
|
81
348
|
if ( state !== undefined ) {
|
|
82
349
|
|
|
@@ -86,7 +353,7 @@ index 2e490796d4d4aa9479b04a5c46ddbf31cc05b1a1..d05599255744ccfca19dbfd0433da612
|
|
|
86
353
|
|
|
87
354
|
}
|
|
88
355
|
|
|
89
|
-
@@ -29911,6 +
|
|
356
|
+
@@ -29911,6 +30012,18 @@ class RenderObject {
|
|
90
357
|
*/
|
|
91
358
|
this._sourceMaterial = renderer._currentSourceMaterial;
|
|
92
359
|
|
|
@@ -105,7 +372,33 @@ index 2e490796d4d4aa9479b04a5c46ddbf31cc05b1a1..d05599255744ccfca19dbfd0433da612
|
|
|
105
372
|
/**
|
|
106
373
|
* An event listener which is defined by `RenderObjects`. It performs
|
|
107
374
|
* clean up tasks when `dispose()` on this render object.
|
|
108
|
-
@@ -
|
|
375
|
+
@@ -30098,7 +30211,7 @@ class RenderObject {
|
|
376
|
+
*/
|
|
377
|
+
getChainArray() {
|
|
378
|
+
|
|
379
|
+
- return [ this.object, this.material, this.context, this.lightsNode ];
|
|
380
|
+
+ return [ this.object, this.material, this.context, this.lightsNode, this._sourceMaterial ?? this.material ];
|
|
381
|
+
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
@@ -30548,6 +30661,7 @@ class RenderObject {
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
cacheKey = hash$1( cacheKey, this.renderer.contextNode.id, this.renderer.contextNode.version );
|
|
388
|
+
+ cacheKey = hash$1( cacheKey, this.context.sampleCount );
|
|
389
|
+
|
|
390
|
+
return cacheKey;
|
|
391
|
+
|
|
392
|
+
@@ -30680,6 +30794,8 @@ class RenderObjects {
|
|
393
|
+
_chainKeys$3[ 1 ] = material;
|
|
394
|
+
_chainKeys$3[ 2 ] = renderContext;
|
|
395
|
+
_chainKeys$3[ 3 ] = lightsNode;
|
|
396
|
+
+ // Material groups share the shadow override, but must retain separate GPU bindings.
|
|
397
|
+
+ _chainKeys$3[ 4 ] = this.renderer._currentSourceMaterial ?? material;
|
|
398
|
+
|
|
399
|
+
//
|
|
400
|
+
|
|
401
|
+
@@ -30707,9 +30823,18 @@ class RenderObjects {
|
|
109
402
|
|
|
110
403
|
}
|
|
111
404
|
|
|
@@ -118,121 +411,1426 @@ index 2e490796d4d4aa9479b04a5c46ddbf31cc05b1a1..d05599255744ccfca19dbfd0433da612
|
|
|
118
411
|
+ const sourceTracked = sourceMaterial !== null && sourceMaterial !== undefined;
|
|
119
412
|
+ const sourceReplaced = sourceTracked && renderObject._sourceMaterial !== sourceMaterial;
|
|
120
413
|
+ const sourceChanged = sourceTracked && ( sourceReplaced || renderObject.sourceVersion !== sourceMaterial.version );
|
|
414
|
+
+
|
|
415
|
+
+ if ( renderObject.version !== material.version || sourceChanged || renderObject.needsUpdate ) {
|
|
121
416
|
|
|
122
417
|
- if ( renderObject.initialCacheKey !== renderObject.getCacheKey() ) {
|
|
123
|
-
+ if ( renderObject.version !== material.version || sourceChanged || renderObject.needsUpdate ) {
|
|
124
|
-
+
|
|
125
418
|
+ if ( sourceReplaced || renderObject.initialCacheKey !== renderObject.getCacheKey() ) {
|
|
126
419
|
|
|
127
|
-
renderObject.dispose();
|
|
420
|
+
renderObject.dispose();
|
|
421
|
+
|
|
422
|
+
@@ -30719,6 +30844,8 @@ class RenderObjects {
|
|
423
|
+
|
|
424
|
+
renderObject.version = material.version;
|
|
425
|
+
|
|
426
|
+
+ if ( sourceTracked ) renderObject.sourceVersion = sourceMaterial.version;
|
|
427
|
+
+
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
}
|
|
431
|
+
@@ -30731,6 +30858,7 @@ class RenderObjects {
|
|
432
|
+
_chainKeys$3[ 1 ] = null;
|
|
433
|
+
_chainKeys$3[ 2 ] = null;
|
|
434
|
+
_chainKeys$3[ 3 ] = null;
|
|
435
|
+
+ _chainKeys$3[ 4 ] = null;
|
|
436
|
+
|
|
437
|
+
//
|
|
438
|
+
|
|
439
|
+
@@ -31165,6 +31293,14 @@ class Geometries extends DataMap {
|
|
440
|
+
*/
|
|
441
|
+
this._geometryDisposeListeners = new Map();
|
|
442
|
+
|
|
443
|
+
+ /**
|
|
444
|
+
+ * This Weak Map holds the attribute versions a settled render object was last drawn with,
|
|
445
|
+
+ * so a write into one of them is what brings the object back out of the settled path.
|
|
446
|
+
+ *
|
|
447
|
+
+ * @type {WeakMap<RenderObject,Map<BufferAttribute,number>>}
|
|
448
|
+
+ */
|
|
449
|
+
+ this._settledAttributes = new WeakMap();
|
|
450
|
+
+
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
@@ -31190,10 +31326,69 @@ class Geometries extends DataMap {
|
|
455
|
+
|
|
456
|
+
if ( this.has( renderObject ) === false ) this.initGeometry( renderObject );
|
|
457
|
+
|
|
458
|
+
+ // A settled static object has no attribute that could have changed since the last refresh,
|
|
459
|
+
+ // unless a writer bumped one. An InstancedMesh's instance buffer is a vertex attribute of
|
|
460
|
+
+ // this draw, and a BufferGeometry carries no version of its own to announce the write, so
|
|
461
|
+
+ // the attributes' own versions are the signal - and the only one there is.
|
|
462
|
+
+
|
|
463
|
+
+ const monitor = renderObject.getMonitor();
|
|
464
|
+
+
|
|
465
|
+
+ if ( monitor !== undefined && monitor.isSettled( renderObject ) === true && this.attributesChanged( renderObject ) === false ) return;
|
|
466
|
+
+
|
|
467
|
+
this.updateAttributes( renderObject );
|
|
468
|
+
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
+ /**
|
|
472
|
+
+ * Returns `true` if an attribute of the given render object - or its index - was written to
|
|
473
|
+
+ * since the last time its versions were read, and records the versions it holds now.
|
|
474
|
+
+ *
|
|
475
|
+
+ * @param {RenderObject} renderObject - The render object.
|
|
476
|
+
+ * @return {boolean} Whether any of the render object's versions moved.
|
|
477
|
+
+ */
|
|
478
|
+
+ attributesChanged( renderObject ) {
|
|
479
|
+
+
|
|
480
|
+
+ const attributes = renderObject.getAttributes();
|
|
481
|
+
+ const index = this.getIndex( renderObject );
|
|
482
|
+
+ const settled = this._settledAttributes.get( renderObject );
|
|
483
|
+
+
|
|
484
|
+
+ if ( settled !== undefined && settled.size === attributes.length + ( index === null ? 0 : 1 ) ) {
|
|
485
|
+
+
|
|
486
|
+
+ let changed = false;
|
|
487
|
+
+
|
|
488
|
+
+ for ( const attribute of attributes ) {
|
|
489
|
+
+
|
|
490
|
+
+ if ( settled.get( attribute.isInterleavedBufferAttribute ? attribute.data : attribute ) !== attribute.version ) {
|
|
491
|
+
+
|
|
492
|
+
+ changed = true;
|
|
493
|
+
+ break;
|
|
494
|
+
+
|
|
495
|
+
+ }
|
|
496
|
+
+
|
|
497
|
+
+ }
|
|
498
|
+
+
|
|
499
|
+
+ if ( changed === false && ( index === null || settled.get( index ) === index.version ) ) return false;
|
|
500
|
+
+
|
|
501
|
+
+ }
|
|
502
|
+
+
|
|
503
|
+
+ const versions = new Map();
|
|
504
|
+
+
|
|
505
|
+
+ for ( const attribute of attributes ) {
|
|
506
|
+
+
|
|
507
|
+
+ const data = attribute.isInterleavedBufferAttribute ? attribute.data : attribute;
|
|
508
|
+
+
|
|
509
|
+
+ versions.set( data, data.version );
|
|
510
|
+
+
|
|
511
|
+
+ }
|
|
512
|
+
+
|
|
513
|
+
+ if ( index !== null ) versions.set( index, index.version );
|
|
514
|
+
+
|
|
515
|
+
+ this._settledAttributes.set( renderObject, versions );
|
|
516
|
+
+
|
|
517
|
+
+ return true;
|
|
518
|
+
+
|
|
519
|
+
+ }
|
|
520
|
+
+
|
|
521
|
+
/**
|
|
522
|
+
* Initializes the geometry of the given render object.
|
|
523
|
+
*
|
|
524
|
+
@@ -32771,7 +32966,7 @@ class Bindings extends DataMap {
|
|
525
|
+
*/
|
|
526
|
+
updateForRender( renderObject ) {
|
|
527
|
+
|
|
528
|
+
- this._updateBindings( this.getForRender( renderObject ) );
|
|
529
|
+
+ this._updateBindings( this.getForRender( renderObject ), this.nodes.isSettled( renderObject ) );
|
|
530
|
+
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
@@ -32918,12 +33113,13 @@ class Bindings extends DataMap {
|
|
534
|
+
* Updates the given array of bindings.
|
|
535
|
+
*
|
|
536
|
+
* @param {Array<BindGroup>} bindings - The bind groups.
|
|
537
|
+
+ * @param {boolean} [settled] - Whether the render object is a settled static object.
|
|
538
|
+
*/
|
|
539
|
+
- _updateBindings( bindings ) {
|
|
540
|
+
+ _updateBindings( bindings, settled ) {
|
|
541
|
+
|
|
542
|
+
for ( const bindGroup of bindings ) {
|
|
543
|
+
|
|
544
|
+
- this._update( bindGroup, bindings );
|
|
545
|
+
+ this._update( bindGroup, bindings, settled );
|
|
546
|
+
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
@@ -32934,11 +33130,27 @@ class Bindings extends DataMap {
|
|
550
|
+
*
|
|
551
|
+
* @param {BindGroup} bindGroup - The bind group to update.
|
|
552
|
+
* @param {Array<BindGroup>} bindings - The bind groups.
|
|
553
|
+
+ * @param {boolean} [settled] - Whether the render object is a settled static object.
|
|
554
|
+
*/
|
|
555
|
+
- _update( bindGroup, bindings ) {
|
|
556
|
+
+ _update( bindGroup, bindings, settled ) {
|
|
557
|
+
|
|
558
|
+
const { backend } = this;
|
|
559
|
+
|
|
560
|
+
+ // A group a node added after this object's first draw has no buffers and no layout yet, and
|
|
561
|
+
+ // the pipeline reads that layout whatever this draw decides to do with it - so it is built
|
|
562
|
+
+ // here, on every draw. A group that did not exist cannot have been written either, so it is
|
|
563
|
+
+ // written now: `settled` is what tells the loop below to write it, not to skip it.
|
|
564
|
+
+ if ( this.get( bindGroup ).bindGroup === undefined ) {
|
|
565
|
+
+
|
|
566
|
+
+ this._createBindings( [ bindGroup ] );
|
|
567
|
+
+
|
|
568
|
+
+ settled = false;
|
|
569
|
+
+
|
|
570
|
+
+ }
|
|
571
|
+
+
|
|
572
|
+
+ // a settled object keeps the values it uploaded the last time it refreshed, so an object
|
|
573
|
+
+ // group it has already written is the one thing in here it may skip
|
|
574
|
+
+
|
|
575
|
+
let needsBindingsUpdate = false;
|
|
576
|
+
let cacheBindings = true;
|
|
577
|
+
let cacheIndex = 0;
|
|
578
|
+
@@ -32948,7 +33160,7 @@ class Bindings extends DataMap {
|
|
579
|
+
|
|
580
|
+
for ( const binding of bindGroup.bindings ) {
|
|
581
|
+
|
|
582
|
+
- const updatedGroup = this.nodes.updateGroup( binding );
|
|
583
|
+
+ const updatedGroup = this.nodes.updateGroup( binding, settled );
|
|
584
|
+
|
|
585
|
+
// every uniforms group is a uniform buffer. So if no update is required,
|
|
586
|
+
// we move one with the next binding. Otherwise the next if block will update the group.
|
|
587
|
+
@@ -33952,7 +34164,8 @@ class RenderContexts {
|
|
588
|
+
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
- if ( renderTarget !== null ) renderState.sampleCount = renderTarget.samples === 0 ? 1 : renderTarget.samples;
|
|
592
|
+
+ const sampleCount = renderTarget !== null ? renderTarget.samples : this.renderer.currentSamples;
|
|
593
|
+
+ renderState.sampleCount = sampleCount === 0 ? 1 : sampleCount;
|
|
594
|
+
|
|
595
|
+
renderState.clearDepthValue = this.renderer.getClearDepth();
|
|
596
|
+
renderState.clearStencilValue = this.renderer.getClearStencil();
|
|
597
|
+
@@ -34181,6 +34394,21 @@ class Textures extends DataMap {
|
|
598
|
+
|
|
599
|
+
backend.destroyTexture( texture );
|
|
600
|
+
|
|
601
|
+
+ // delete cached bind groups so they don't point to the destroyed texture
|
|
602
|
+
+
|
|
603
|
+
+ if ( textureData.bindGroups ) {
|
|
604
|
+
+
|
|
605
|
+
+ for ( const bindGroup of textureData.bindGroups ) {
|
|
606
|
+
+
|
|
607
|
+
+ const bindingsData = this.backend.get( bindGroup );
|
|
608
|
+
+
|
|
609
|
+
+ bindingsData.groups = undefined;
|
|
610
|
+
+ bindingsData.versions = undefined;
|
|
611
|
+
+
|
|
612
|
+
+ }
|
|
613
|
+
+
|
|
614
|
+
+ }
|
|
615
|
+
+
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
//
|
|
619
|
+
@@ -39725,7 +39953,8 @@ class VelocityNode extends TempNode {
|
|
620
|
+
|
|
621
|
+
const previousModelMatrix = getPreviousMatrix( object );
|
|
622
|
+
|
|
623
|
+
- this.previousModelWorldMatrix.value.copy( previousModelMatrix );
|
|
624
|
+
+ const scheduledPrevious = object[ Symbol.for( 'threenative.velocity.previousWorldMatrix' ) ];
|
|
625
|
+
+ this.previousModelWorldMatrix.value.copy( scheduledPrevious ?? previousModelMatrix );
|
|
626
|
+
|
|
627
|
+
//
|
|
628
|
+
|
|
629
|
+
@@ -53841,7 +54070,7 @@ class NodeBuilder {
|
|
630
|
+
|
|
631
|
+
const mrt = this.renderer.getMRT();
|
|
632
|
+
|
|
633
|
+
- return ( mrt && mrt.has( 'velocity' ) ) || getDataFromObject( this.object ).useVelocity === true;
|
|
634
|
+
+ return ( mrt && mrt.has( 'velocity' ) ) || getDataFromObject( this.object ).useVelocity === true || this.object.userData?.useVelocity === true;
|
|
635
|
+
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
@@ -55244,15 +55473,17 @@ class NodeManager extends DataMap {
|
|
639
|
+
* Returns `true` if the given node uniforms group must be updated or not.
|
|
640
|
+
*
|
|
641
|
+
* @param {NodeUniformsGroup} nodeUniformsGroup - The node uniforms group.
|
|
642
|
+
+ * @param {boolean} [settled] - Whether the render object is a settled static object.
|
|
643
|
+
* @return {boolean} Whether the node uniforms group requires an update or not.
|
|
644
|
+
*/
|
|
645
|
+
- updateGroup( nodeUniformsGroup ) {
|
|
646
|
+
+ updateGroup( nodeUniformsGroup, settled ) {
|
|
647
|
+
|
|
648
|
+
const groupNode = nodeUniformsGroup.groupNode;
|
|
649
|
+
|
|
650
|
+
- // groups that are updated per object always require an update so no further checks are needed
|
|
651
|
+
+ // groups that are updated per object always require an update so no further checks are needed.
|
|
652
|
+
+ // A settled static object keeps the values it uploaded the last time it was refreshed.
|
|
653
|
+
|
|
654
|
+
- if ( groupNode.updateType === NodeUpdateType.OBJECT ) return true;
|
|
655
|
+
+ if ( groupNode.updateType === NodeUpdateType.OBJECT ) return settled !== true;
|
|
656
|
+
|
|
657
|
+
// check for update
|
|
658
|
+
|
|
659
|
+
@@ -56056,6 +56287,21 @@ class NodeManager extends DataMap {
|
|
660
|
+
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
+ /**
|
|
664
|
+
+ * Returns `true` if the per-object updates of the given render object can be skipped
|
|
665
|
+
+ * because the object is static and nothing it depends on changed since its last refresh.
|
|
666
|
+
+ *
|
|
667
|
+
+ * @param {RenderObject} renderObject - The render object.
|
|
668
|
+
+ * @return {boolean} Whether the render object is settled or not.
|
|
669
|
+
+ */
|
|
670
|
+
+ isSettled( renderObject ) {
|
|
671
|
+
+
|
|
672
|
+
+ const monitor = renderObject.getMonitor();
|
|
673
|
+
+
|
|
674
|
+
+ return monitor !== undefined ? monitor.isSettled( renderObject ) : false;
|
|
675
|
+
+
|
|
676
|
+
+ }
|
|
677
|
+
+
|
|
678
|
+
/**
|
|
679
|
+
* Triggers the call of `updateBefore()` methods
|
|
680
|
+
* for all nodes of the given render object.
|
|
681
|
+
@@ -56065,9 +56311,14 @@ class NodeManager extends DataMap {
|
|
682
|
+
updateBefore( renderObject ) {
|
|
683
|
+
|
|
684
|
+
const nodeBuilder = renderObject.getNodeBuilderState();
|
|
685
|
+
+ const settled = this.isSettled( renderObject );
|
|
686
|
+
|
|
687
|
+
for ( const node of nodeBuilder.updateBeforeNodes ) {
|
|
688
|
+
|
|
689
|
+
+ // a settled static object has nothing to update per object
|
|
690
|
+
+
|
|
691
|
+
+ if ( settled === true && node.getUpdateBeforeType() === NodeUpdateType.OBJECT ) continue;
|
|
692
|
+
+
|
|
693
|
+
// update frame state for each node
|
|
694
|
+
|
|
695
|
+
this.getNodeFrameForRender( renderObject ).updateBeforeNode( node );
|
|
696
|
+
@@ -56085,10 +56336,11 @@ class NodeManager extends DataMap {
|
|
697
|
+
updateAfter( renderObject ) {
|
|
698
|
+
|
|
699
|
+
const nodeBuilder = renderObject.getNodeBuilderState();
|
|
700
|
+
+ const settled = this.isSettled( renderObject );
|
|
701
|
+
|
|
702
|
+
for ( const node of nodeBuilder.updateAfterNodes ) {
|
|
703
|
+
|
|
704
|
+
- // update frame state for each node
|
|
705
|
+
+ if ( settled === true && node.getUpdateAfterType() === NodeUpdateType.OBJECT ) continue;
|
|
706
|
+
|
|
707
|
+
this.getNodeFrameForRender( renderObject ).updateAfterNode( node );
|
|
708
|
+
|
|
709
|
+
@@ -56125,9 +56377,14 @@ class NodeManager extends DataMap {
|
|
710
|
+
|
|
711
|
+
const nodeFrame = this.getNodeFrameForRender( renderObject );
|
|
712
|
+
const nodeBuilder = renderObject.getNodeBuilderState();
|
|
713
|
+
+ const settled = this.isSettled( renderObject );
|
|
714
|
+
|
|
715
|
+
for ( const node of nodeBuilder.updateNodes ) {
|
|
716
|
+
|
|
717
|
+
+ // a settled static object keeps the per-object values it uploaded last time
|
|
718
|
+
+
|
|
719
|
+
+ if ( settled === true && node.getUpdateType() === NodeUpdateType.OBJECT ) continue;
|
|
720
|
+
+
|
|
721
|
+
nodeFrame.updateNode( node );
|
|
722
|
+
|
|
723
|
+
}
|
|
724
|
+
@@ -62731,6 +62988,7 @@ class Renderer {
|
|
725
|
+
let materialDisplacementMap;
|
|
726
|
+
let materialDisplacementScale;
|
|
727
|
+
let materialDisplacementBias;
|
|
728
|
+
+ let materialAlphaTest;
|
|
729
|
+
|
|
730
|
+
const previousSourceMaterial = this._currentSourceMaterial;
|
|
731
|
+
|
|
732
|
+
@@ -62756,6 +63014,7 @@ class Renderer {
|
|
733
|
+
materialDisplacementMap = overrideMaterial.displacementMap;
|
|
734
|
+
materialDisplacementScale = overrideMaterial.displacementScale;
|
|
735
|
+
materialDisplacementBias = overrideMaterial.displacementBias;
|
|
736
|
+
+ materialAlphaTest = overrideMaterial.alphaTest;
|
|
737
|
+
|
|
738
|
+
if ( material.positionNode && material.positionNode.isNode ) {
|
|
739
|
+
|
|
740
|
+
@@ -62763,7 +63022,15 @@ class Renderer {
|
|
741
|
+
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
- overrideMaterial.alphaTest = material.alphaTest;
|
|
745
|
+
+ // `Material.alphaTest`'s setter bumps `version` whenever alpha-test-ness flips, and this
|
|
746
|
+
+ // override material is shared by every caster in the pass. A scene that mixes alpha-tested
|
|
747
|
+
+ // casters with opaque ones therefore moves that shared version several times per frame,
|
|
748
|
+
+ // and every OTHER render object drawn with it then fails the version gate in
|
|
749
|
+
+ // `RenderObjects.get` and recomputes a full material cache key that always matches.
|
|
750
|
+
+ // Writing the backing field leaves the `alphaTest` getter — and so the cache key — still
|
|
751
|
+
+ // reporting this caster's value, while each render object tracks the staleness signal
|
|
752
|
+
+ // that belongs to it: its own source material's version.
|
|
753
|
+
+ overrideMaterial._alphaTest = material.alphaTest;
|
|
754
|
+
overrideMaterial.alphaMap = material.alphaMap;
|
|
755
|
+
overrideMaterial.displacementMap = material.displacementMap;
|
|
756
|
+
overrideMaterial.displacementScale = material.displacementScale;
|
|
757
|
+
@@ -62825,6 +63092,7 @@ class Renderer {
|
|
758
|
+
scene.overrideMaterial.displacementMap = materialDisplacementMap;
|
|
759
|
+
scene.overrideMaterial.displacementScale = materialDisplacementScale;
|
|
760
|
+
scene.overrideMaterial.displacementBias = materialDisplacementBias;
|
|
761
|
+
+ scene.overrideMaterial._alphaTest = materialAlphaTest;
|
|
762
|
+
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
@@ -62886,18 +63154,17 @@ class Renderer {
|
|
766
|
+
|
|
767
|
+
//
|
|
768
|
+
|
|
769
|
+
- const needsRefresh = this._nodes.needsRefresh( renderObject );
|
|
770
|
+
+ this._nodes.needsRefresh( renderObject );
|
|
771
|
+
|
|
772
|
+
- if ( needsRefresh ) {
|
|
773
|
+
-
|
|
774
|
+
- this._nodes.updateBefore( renderObject );
|
|
775
|
+
-
|
|
776
|
+
- this._geometries.updateForRender( renderObject );
|
|
777
|
+
-
|
|
778
|
+
- this._nodes.updateForRender( renderObject );
|
|
779
|
+
- this._bindings.updateForRender( renderObject );
|
|
780
|
+
-
|
|
781
|
+
- }
|
|
782
|
+
+ // Every step of a draw runs, settled or not. What a settled static object skips is decided
|
|
783
|
+
+ // inside each of them - its OBJECT node updates, its object uniform group write and its
|
|
784
|
+
+ // attribute scan - because each of those answers for itself, and a step left out here
|
|
785
|
+
+ // cannot: a group a node added after this object's first draw still has to be created, and
|
|
786
|
+
+ // the pipeline reads that group's layout whatever this draw decided to do with it.
|
|
787
|
+
+ this._geometries.updateForRender( renderObject );
|
|
788
|
+
+ this._nodes.updateBefore( renderObject );
|
|
789
|
+
+ this._nodes.updateForRender( renderObject );
|
|
790
|
+
+ this._bindings.updateForRender( renderObject );
|
|
791
|
+
|
|
792
|
+
this._pipelines.updateForRender( renderObject );
|
|
793
|
+
|
|
794
|
+
@@ -62907,7 +63174,7 @@ class Renderer {
|
|
795
|
+
|
|
796
|
+
this.backend.draw( renderObject, this.info );
|
|
797
|
+
|
|
798
|
+
- if ( needsRefresh ) this._nodes.updateAfter( renderObject );
|
|
799
|
+
+ this._nodes.updateAfter( renderObject );
|
|
800
|
+
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
@@ -79004,7 +79271,14 @@ class WGSLNodeBuilder extends NodeBuilder {
|
|
804
|
+
|
|
805
|
+
} else {
|
|
806
|
+
|
|
807
|
+
- snippet = `textureLoad( ${ textureProperty }, ${ uvIndexSnippet }, u32( ${ levelSnippet } ) )`;
|
|
808
|
+
+ // naga v25 (wgpu-native v25.0.2.2) rejects a u32 sample index on a
|
|
809
|
+
+ // multisampled textureLoad; i32 is accepted by every backend. Mip-level
|
|
810
|
+
+ // operands (including the array form above) stay u32.
|
|
811
|
+
+ const sampleData = texture ? this.renderer?.backend?.utils?.getTextureSampleData?.( texture ) : null;
|
|
812
|
+
+ const backendMultisampled = sampleData !== null && sampleData !== undefined && sampleData.primarySamples > 1;
|
|
813
|
+
+ const targetMultisampled = Boolean( texture && texture.renderTarget && texture.renderTarget.samples > 1 );
|
|
814
|
+
+ const indexType = ( backendMultisampled || targetMultisampled ) ? 'i32' : 'u32';
|
|
815
|
+
+ snippet = `textureLoad( ${ textureProperty }, ${ uvIndexSnippet }, ${ indexType }( ${ levelSnippet } ) )`;
|
|
816
|
+
|
|
817
|
+
if ( this.renderer.backend.compatibilityMode && texture.isDepthTexture ) {
|
|
818
|
+
|
|
819
|
+
@@ -79586,7 +79860,8 @@ class WGSLNodeBuilder extends NodeBuilder {
|
|
820
|
+
|
|
821
|
+
uniformGPU = buffer;
|
|
822
|
+
|
|
823
|
+
- uniformNode.name = name ? name : 'NodeBuffer_' + uniformNode.id;
|
|
824
|
+
+ // The base builder supplies a unique name within this program. A process-wide
|
|
825
|
+
+ // allocation id changes otherwise identical shader source for every buffer.
|
|
826
|
+
|
|
827
|
+
} else {
|
|
828
|
+
|
|
829
|
+
@@ -80329,9 +80604,12 @@ ${ flowData.code }
|
|
830
|
+
const textureNode = uniform.node;
|
|
831
|
+
const texture = textureNode.value;
|
|
832
|
+
|
|
833
|
+
- // Cube textures always need samplers (they use textureSampleLevel, not textureLoad)
|
|
834
|
+
- // Also textureGather always need sampler.
|
|
835
|
+
- const needsSampler = texture.isCubeTexture === true || ( this.isUnfilterable( texture ) === false && textureNode.isStorageTextureNode !== true ) || textureNode.gatherNode !== null;
|
|
836
|
+
+ // The bind group layout freezes sampler inclusion once, in getUniformFromNode
|
|
837
|
+
+ // (nodeData.uniformGPU). Re-deriving it here from live texture filter state can
|
|
838
|
+
+ // disagree with that layout and shift every later binding index.
|
|
839
|
+
+ const nodeData = this.getDataFromNode( textureNode, shaderStage, this.globalCache );
|
|
840
|
+
+ const uniformGPU = nodeData.uniformGPU;
|
|
841
|
+
+ const needsSampler = Array.isArray( uniformGPU ) && uniformGPU[ 0 ].isSampler === true && uniformGPU[ 0 ].isSampledTexture !== true;
|
|
842
|
+
|
|
843
|
+
if ( needsSampler ) {
|
|
844
|
+
|
|
845
|
+
@@ -81295,9 +81573,15 @@ class WebGPUAttributeUtils {
|
|
846
|
+
destroyAttribute( attribute ) {
|
|
847
|
+
|
|
848
|
+
const backend = this.backend;
|
|
849
|
+
- const data = backend.get( this._getBufferAttribute( attribute ) );
|
|
850
|
+
+ const bufferAttribute = this._getBufferAttribute( attribute );
|
|
851
|
+
+ const data = backend.get( bufferAttribute );
|
|
852
|
+
|
|
853
|
+
- data.buffer.destroy();
|
|
854
|
+
+ // An interleaved attribute's GPU buffer lives on its InterleavedBuffer, and several attributes
|
|
855
|
+
+ // share it: destroy it once and forget it there too, as the WebGL backend does, or the next
|
|
856
|
+
+ // createAttribute() finds the destroyed buffer and submits it (threenative patch).
|
|
857
|
+
+ if ( data.buffer !== undefined ) data.buffer.destroy();
|
|
858
|
+
+
|
|
859
|
+
+ if ( bufferAttribute !== attribute ) backend.delete( bufferAttribute );
|
|
860
|
+
|
|
861
|
+
backend.delete( attribute );
|
|
862
|
+
|
|
863
|
+
@@ -81598,18 +81882,16 @@ class WebGPUBindingUtils {
|
|
864
|
+
|
|
865
|
+
const bindingsData = backend.get( bindGroup );
|
|
866
|
+
|
|
867
|
+
- // check if the the bind group already has a layout
|
|
868
|
+
+ const entries = this._createLayoutEntries( bindGroup );
|
|
869
|
+
+ const bindGroupLayoutKey = hashString( JSON.stringify( entries ) );
|
|
870
|
+
|
|
871
|
+
if ( bindingsData.layout ) {
|
|
872
|
+
|
|
873
|
+
- return bindingsData.layout.layoutGPU;
|
|
874
|
+
+ if ( bindingsData.layoutKey === bindGroupLayoutKey ) return bindingsData.layout.layoutGPU;
|
|
875
|
+
|
|
876
|
+
- }
|
|
877
|
+
-
|
|
878
|
+
- // if not, assing one
|
|
879
|
+
+ this.deleteBindGroupData( bindGroup );
|
|
880
|
+
|
|
881
|
+
- const entries = this._createLayoutEntries( bindGroup );
|
|
882
|
+
- const bindGroupLayoutKey = hashString( JSON.stringify( entries ) );
|
|
883
|
+
+ }
|
|
884
|
+
|
|
885
|
+
// try to find an existing layout in the cache
|
|
886
|
+
|
|
887
|
+
@@ -82131,6 +82413,9 @@ class WebGPUBindingUtils {
|
|
888
|
+
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
+ bindingsData.groups = [];
|
|
892
|
+
+ bindingsData.versions = [];
|
|
893
|
+
+
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
/**
|
|
897
|
+
@@ -83814,6 +84099,14 @@ class WebGPUBackend extends Backend {
|
|
898
|
+
*/
|
|
899
|
+
this.compatibilityMode = null;
|
|
900
|
+
|
|
901
|
+
+ /**
|
|
902
|
+
+ * A reference to the WebGPU instance that creates adapters.
|
|
903
|
+
+ *
|
|
904
|
+
+ * @type {?GPU}
|
|
905
|
+
+ * @default null
|
|
906
|
+
+ */
|
|
907
|
+
+ this.gpu = null;
|
|
908
|
+
+
|
|
909
|
+
/**
|
|
910
|
+
* A reference to the device.
|
|
911
|
+
*
|
|
912
|
+
@@ -83926,7 +84219,9 @@ class WebGPUBackend extends Backend {
|
|
913
|
+
xrCompatible: renderer.xr.enabled
|
|
914
|
+
};
|
|
915
|
+
|
|
916
|
+
- const adapter = ( typeof navigator !== 'undefined' ) ? await navigator.gpu.requestAdapter( adapterOptions ) : null;
|
|
917
|
+
+ this.gpu = ( typeof navigator !== 'undefined' ) ? navigator.gpu : null;
|
|
918
|
+
+
|
|
919
|
+
+ const adapter = this.gpu !== null ? await this.gpu.requestAdapter( adapterOptions ) : null;
|
|
920
|
+
|
|
921
|
+
if ( adapter === null ) {
|
|
922
|
+
|
|
923
|
+
@@ -85461,13 +85756,12 @@ class WebGPUBackend extends Backend {
|
|
924
|
+
for ( let i = 0, l = bindings.length; i < l; i ++ ) {
|
|
925
|
+
|
|
926
|
+
const bindGroup = bindings[ i ];
|
|
927
|
+
+ const bindingsData = this.get( bindGroup );
|
|
928
|
+
|
|
929
|
+
- if ( currentBindingGroups[ i ] !== bindGroup.id ) {
|
|
930
|
+
-
|
|
931
|
+
- const bindingsData = this.get( bindGroup );
|
|
932
|
+
+ if ( currentBindingGroups[ i ] !== bindingsData.group ) {
|
|
933
|
+
|
|
934
|
+
passEncoderGPU.setBindGroup( i, bindingsData.group );
|
|
935
|
+
- currentBindingGroups[ i ] = bindGroup.id;
|
|
936
|
+
+ currentBindingGroups[ i ] = bindingsData.group;
|
|
937
|
+
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
@@ -85766,6 +86060,7 @@ class WebGPUBackend extends Backend {
|
|
941
|
+
const utils = this.utils;
|
|
942
|
+
|
|
943
|
+
const sampleCount = utils.getSampleCountRenderContext( renderObject.context );
|
|
944
|
+
+ const bindGroupLayoutKeys = renderObject.getBindings().map( bindGroup => this.get( bindGroup ).layoutKey ).join( '|' );
|
|
945
|
+
const colorSpace = utils.getCurrentColorSpace( renderObject.context );
|
|
946
|
+
const colorFormat = utils.getCurrentColorFormat( renderObject.context );
|
|
947
|
+
const depthStencilFormat = utils.getCurrentDepthStencilFormat( renderObject.context );
|
|
948
|
+
@@ -85787,7 +86082,8 @@ class WebGPUBackend extends Backend {
|
|
949
|
+
data.colorFormat !== colorFormat || data.depthStencilFormat !== depthStencilFormat ||
|
|
950
|
+
data.primitiveTopology !== primitiveTopology ||
|
|
951
|
+
data.frontFaceCW !== frontFaceCW ||
|
|
952
|
+
- data.clippingContextCacheKey !== renderObject.clippingContextCacheKey
|
|
953
|
+
+ data.clippingContextCacheKey !== renderObject.clippingContextCacheKey ||
|
|
954
|
+
+ data.bindGroupLayoutKeys !== bindGroupLayoutKeys
|
|
955
|
+
) {
|
|
956
|
+
|
|
957
|
+
data.material = material; data.materialVersion = material.version;
|
|
958
|
+
@@ -85807,6 +86103,7 @@ class WebGPUBackend extends Backend {
|
|
959
|
+
data.primitiveTopology = primitiveTopology;
|
|
960
|
+
data.frontFaceCW = frontFaceCW;
|
|
961
|
+
data.clippingContextCacheKey = renderObject.clippingContextCacheKey;
|
|
962
|
+
+ data.bindGroupLayoutKeys = bindGroupLayoutKeys;
|
|
963
|
+
|
|
964
|
+
needsUpdate = true;
|
|
965
|
+
|
|
966
|
+
@@ -85828,6 +86125,7 @@ class WebGPUBackend extends Backend {
|
|
967
|
+
|
|
968
|
+
const utils = this.utils;
|
|
969
|
+
const renderContext = renderObject.context;
|
|
970
|
+
+ const bindGroupLayoutKeys = renderObject.getBindings().map( bindGroup => this.get( bindGroup ).layoutKey ).join( '|' );
|
|
971
|
+
|
|
972
|
+
// meshes with negative scale have a different frontFace render pipeline
|
|
973
|
+
// descriptor value so the following must be honored in the cache key
|
|
974
|
+
@@ -85846,6 +86144,7 @@ class WebGPUBackend extends Backend {
|
|
975
|
+
material.side,
|
|
976
|
+
frontFaceCW,
|
|
977
|
+
utils.getSampleCountRenderContext( renderContext ),
|
|
978
|
+
+ bindGroupLayoutKeys,
|
|
979
|
+
utils.getCurrentColorSpace( renderContext ), utils.getCurrentColorFormat( renderContext ), utils.getCurrentDepthStencilFormat( renderContext ),
|
|
980
|
+
utils.getPrimitiveTopology( object, material ),
|
|
981
|
+
renderObject.getGeometryCacheKey(),
|
|
982
|
+
diff --git a/build/three.webgpu.nodes.js b/build/three.webgpu.nodes.js
|
|
983
|
+
index f707acc..b8a7b2f 100644
|
|
984
|
+
--- a/build/three.webgpu.nodes.js
|
|
985
|
+
+++ b/build/three.webgpu.nodes.js
|
|
986
|
+
@@ -141,6 +141,15 @@ class NodeMaterialObserver {
|
|
987
|
+
*/
|
|
988
|
+
this.renderId = 0;
|
|
989
|
+
|
|
990
|
+
+ /**
|
|
991
|
+
+ * Whether the current render produces motion vectors. Objects keep refreshing
|
|
992
|
+
+ * fully while this is the case.
|
|
993
|
+
+ *
|
|
994
|
+
+ * @type {boolean}
|
|
995
|
+
+ * @default false
|
|
996
|
+
+ */
|
|
997
|
+
+ this.renderingVelocity = false;
|
|
998
|
+
+
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
/**
|
|
1002
|
+
@@ -240,6 +249,88 @@ class NodeMaterialObserver {
|
|
1003
|
+
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
+ /**
|
|
1007
|
+
+ * Returns `true` if the given render object is a static object whose per-object state - world
|
|
1008
|
+
+ * matrix, material, geometry and the scene environment - did not change since the last
|
|
1009
|
+
+ * refresh. A settled object is still drawn every frame and its FRAME and RENDER nodes still
|
|
1010
|
+
+ * have to be updated; what it may skip is the work whose inputs it froze: its OBJECT node
|
|
1011
|
+
+ * updates, its object uniform group upload and its per-draw attribute scan.
|
|
1012
|
+
+ *
|
|
1013
|
+
+ * @param {RenderObject} renderObject - The render object.
|
|
1014
|
+
+ * @return {boolean} Whether the given render object is settled or not.
|
|
1015
|
+
+ */
|
|
1016
|
+
+ isSettled( renderObject ) {
|
|
1017
|
+
+
|
|
1018
|
+
+ const data = this.renderObjects.get( renderObject );
|
|
1019
|
+
+
|
|
1020
|
+
+ return data !== undefined && data.settledState === true;
|
|
1021
|
+
+
|
|
1022
|
+
+ }
|
|
1023
|
+
+
|
|
1024
|
+
+ /**
|
|
1025
|
+
+ * Recomputes {@link NodeMaterialObserver#isSettled} for the given render object, once per draw,
|
|
1026
|
+
+ * so every step of that draw reads the same answer.
|
|
1027
|
+
+ *
|
|
1028
|
+
+ * @param {RenderObject} renderObject - The render object.
|
|
1029
|
+
+ * @return {boolean} Whether the given render object is settled or not.
|
|
1030
|
+
+ */
|
|
1031
|
+
+ updateSettled( renderObject ) {
|
|
1032
|
+
+
|
|
1033
|
+
+ const { object, material, geometry, scene } = renderObject;
|
|
1034
|
+
+ const data = this.getRenderObjectData( renderObject );
|
|
1035
|
+
+
|
|
1036
|
+
+ if ( object.static !== true || this.renderingVelocity === true ) {
|
|
1037
|
+
+
|
|
1038
|
+
+ data.settledState = false;
|
|
1039
|
+
+
|
|
1040
|
+
+ return false;
|
|
1041
|
+
+
|
|
1042
|
+
+ }
|
|
1043
|
+
+
|
|
1044
|
+
+ let settled = data.settled;
|
|
1045
|
+
+
|
|
1046
|
+
+ if ( settled === undefined ) {
|
|
1047
|
+
+
|
|
1048
|
+
+ data.settled = settled = {
|
|
1049
|
+
+ matrix: object.matrixWorld.clone(),
|
|
1050
|
+
+ environmentRotation: scene.environmentRotation.clone(),
|
|
1051
|
+
+ environmentIntensity: scene.environmentIntensity,
|
|
1052
|
+
+ materialVersion: material.version,
|
|
1053
|
+
+ geometryVersion: geometry.version
|
|
1054
|
+
+ };
|
|
1055
|
+
+
|
|
1056
|
+
+ data.settledState = false;
|
|
1057
|
+
+
|
|
1058
|
+
+ return false;
|
|
1059
|
+
+
|
|
1060
|
+
+ }
|
|
1061
|
+
+
|
|
1062
|
+
+ if ( settled.matrix.equals( object.matrixWorld ) !== true ||
|
|
1063
|
+
+ settled.environmentRotation.equals( scene.environmentRotation ) !== true ||
|
|
1064
|
+
+ settled.environmentIntensity !== scene.environmentIntensity ||
|
|
1065
|
+
+ settled.materialVersion !== material.version ||
|
|
1066
|
+
+ settled.geometryVersion !== geometry.version ) {
|
|
1067
|
+
+
|
|
1068
|
+
+ // re-arm with the current state, so the object settles again once it stops changing
|
|
1069
|
+
+
|
|
1070
|
+
+ settled.matrix.copy( object.matrixWorld );
|
|
1071
|
+
+ settled.environmentRotation.copy( scene.environmentRotation );
|
|
1072
|
+
+ settled.environmentIntensity = scene.environmentIntensity;
|
|
1073
|
+
+ settled.materialVersion = material.version;
|
|
1074
|
+
+ settled.geometryVersion = geometry.version;
|
|
1075
|
+
+
|
|
1076
|
+
+ data.settledState = false;
|
|
1077
|
+
+
|
|
1078
|
+
+ return false;
|
|
1079
|
+
+
|
|
1080
|
+
+ }
|
|
1081
|
+
+
|
|
1082
|
+
+ data.settledState = true;
|
|
1083
|
+
+
|
|
1084
|
+
+ return true;
|
|
1085
|
+
+
|
|
1086
|
+
+ }
|
|
1087
|
+
+
|
|
1088
|
+
/**
|
|
1089
|
+
* Returns an attribute data structure holding the attributes versions for
|
|
1090
|
+
* monitoring.
|
|
1091
|
+
@@ -306,7 +397,6 @@ class NodeMaterialObserver {
|
|
1092
|
+
|
|
1093
|
+
data = {
|
|
1094
|
+
_renderId: -1,
|
|
1095
|
+
- _equal: false,
|
|
1096
|
+
|
|
1097
|
+
attributes: this.getAttributesData( geometry.attributes ),
|
|
1098
|
+
indexId: geometry.index ? geometry.index.id : null,
|
|
1099
|
+
@@ -335,7 +425,7 @@ class NodeMaterialObserver {
|
|
1100
|
+
|
|
1101
|
+
if ( data === undefined ) {
|
|
1102
|
+
|
|
1103
|
+
- data = { _renderId: -1, _equal: false };
|
|
1104
|
+
+ data = { _renderId: -1 };
|
|
1105
|
+
|
|
1106
|
+
for ( const property of this.refreshUniforms ) {
|
|
1107
|
+
|
|
1108
|
+
@@ -419,7 +509,6 @@ class NodeMaterialObserver {
|
|
1109
|
+
|
|
1110
|
+
value.copy( mtlValue );
|
|
1111
|
+
|
|
1112
|
+
- materialData._equal = false;
|
|
1113
|
+
return false;
|
|
1114
|
+
|
|
1115
|
+
}
|
|
1116
|
+
@@ -431,7 +520,6 @@ class NodeMaterialObserver {
|
|
1117
|
+
value.id = mtlValue.id;
|
|
1118
|
+
value.version = mtlValue.version;
|
|
1119
|
+
|
|
1120
|
+
- materialData._equal = false;
|
|
1121
|
+
return false;
|
|
1122
|
+
|
|
1123
|
+
}
|
|
1124
|
+
@@ -440,7 +528,6 @@ class NodeMaterialObserver {
|
|
1125
|
+
|
|
1126
|
+
materialData[ property ] = mtlValue;
|
|
1127
|
+
|
|
1128
|
+
- materialData._equal = false;
|
|
1129
|
+
return false;
|
|
1130
|
+
|
|
1131
|
+
}
|
|
1132
|
+
@@ -456,18 +543,12 @@ class NodeMaterialObserver {
|
|
1133
|
+
renderObjectData.bufferWidth = width;
|
|
1134
|
+
renderObjectData.bufferHeight = height;
|
|
1135
|
+
|
|
1136
|
+
- materialData._equal = false;
|
|
1137
|
+
return false;
|
|
1138
|
+
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
- materialData._equal = true;
|
|
1144
|
+
-
|
|
1145
|
+
- } else {
|
|
1146
|
+
-
|
|
1147
|
+
- if ( materialData._equal === false ) return false;
|
|
1148
|
+
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
@@ -510,7 +591,6 @@ class NodeMaterialObserver {
|
|
1152
|
+
// attribute was removed
|
|
1153
|
+
delete storedAttributes[ name ];
|
|
1154
|
+
|
|
1155
|
+
- geometryData._equal = false;
|
|
1156
|
+
return false;
|
|
1157
|
+
|
|
1158
|
+
}
|
|
1159
|
+
@@ -523,7 +603,6 @@ class NodeMaterialObserver {
|
|
1160
|
+
storedAttributeData.id = id;
|
|
1161
|
+
storedAttributeData.version = version;
|
|
1162
|
+
|
|
1163
|
+
- geometryData._equal = false;
|
|
1164
|
+
return false;
|
|
1165
|
+
|
|
1166
|
+
}
|
|
1167
|
+
@@ -534,7 +613,6 @@ class NodeMaterialObserver {
|
|
1168
|
+
|
|
1169
|
+
geometryData.attributes = this.getAttributesData( attributes );
|
|
1170
|
+
|
|
1171
|
+
- geometryData._equal = false;
|
|
1172
|
+
return false;
|
|
1173
|
+
|
|
1174
|
+
}
|
|
1175
|
+
@@ -552,7 +630,6 @@ class NodeMaterialObserver {
|
|
1176
|
+
geometryData.indexId = currentIndexId;
|
|
1177
|
+
geometryData.indexVersion = currentIndexVersion;
|
|
1178
|
+
|
|
1179
|
+
- geometryData._equal = false;
|
|
1180
|
+
return false;
|
|
1181
|
+
|
|
1182
|
+
}
|
|
1183
|
+
@@ -564,16 +641,10 @@ class NodeMaterialObserver {
|
|
1184
|
+
geometryData.drawRange.start = geometry.drawRange.start;
|
|
1185
|
+
geometryData.drawRange.count = geometry.drawRange.count;
|
|
1186
|
+
|
|
1187
|
+
- geometryData._equal = false;
|
|
1188
|
+
return false;
|
|
1189
|
+
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
- geometryData._equal = true;
|
|
1193
|
+
-
|
|
1194
|
+
- } else {
|
|
1195
|
+
-
|
|
1196
|
+
- if ( geometryData._equal === false ) return false;
|
|
1197
|
+
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
@@ -724,7 +795,14 @@ class NodeMaterialObserver {
|
|
1201
|
+
*/
|
|
1202
|
+
needsRefresh( renderObject, nodeFrame ) {
|
|
1203
|
+
|
|
1204
|
+
- if ( this.hasNode || this.hasAnimation || this.firstInitialization( renderObject ) || this.needsVelocity( nodeFrame.renderer ) )
|
|
1205
|
+
+ this.renderingVelocity = this.needsVelocity( nodeFrame.renderer );
|
|
1206
|
+
+
|
|
1207
|
+
+ // Answered for the whole draw, so updateBefore, the geometry scan, the node updates and
|
|
1208
|
+
+ // the object uniform upload of this object all skip or all refresh.
|
|
1209
|
+
+
|
|
1210
|
+
+ this.updateSettled( renderObject );
|
|
1211
|
+
+
|
|
1212
|
+
+ if ( this.hasNode || this.hasAnimation || this.firstInitialization( renderObject ) || this.renderingVelocity )
|
|
1213
|
+
return true;
|
|
1214
|
+
|
|
1215
|
+
const { renderId } = nodeFrame;
|
|
1216
|
+
@@ -737,10 +815,14 @@ class NodeMaterialObserver {
|
|
1217
|
+
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
- const isStatic = renderObject.object.static === true;
|
|
1221
|
+
const isBundle = renderObject.bundle !== null && renderObject.bundle.static === true && this.getRenderObjectData( renderObject ).version === renderObject.bundle.version;
|
|
1222
|
+
|
|
1223
|
+
- if ( isStatic || isBundle )
|
|
1224
|
+
+ // `object.static` is not the question - it is a switch, and a brand new object has it set
|
|
1225
|
+
+ // before it has been drawn once, so answering from it skips the draw that creates this
|
|
1226
|
+
+ // object's bind groups and leaves the pipeline reading a layout that was never built. A
|
|
1227
|
+
+ // static object that moved, or whose material or geometry was edited, has to refresh like
|
|
1228
|
+
+ // any other; only a settled one may skip, and nothing is settled before its first refresh.
|
|
1229
|
+
+ if ( this.isSettled( renderObject ) === true || isBundle )
|
|
1230
|
+
return false;
|
|
1231
|
+
|
|
1232
|
+
const lightsData = this.getLights( renderObject.lightsNode, renderId );
|
|
1233
|
+
@@ -18320,7 +18402,8 @@ function createInstanceMatrixNode( builder, instanceMatrix ) {
|
|
1234
|
+
|
|
1235
|
+
const uniformBufferSize = matrixCount * 16 * 4;
|
|
1236
|
+
|
|
1237
|
+
- if ( uniformBufferSize <= builder.getUniformBufferLimit() ) {
|
|
1238
|
+
+ // threenative: the uniform path re-uploads every draw; the attribute path uploads on version change
|
|
1239
|
+
+ if ( false ) {
|
|
1240
|
+
|
|
1241
|
+
instanceMatrixNode = buffer( instanceMatrix.array, 'mat4', matrixCount ).element( instanceIndex );
|
|
1242
|
+
|
|
1243
|
+
@@ -18414,7 +18497,8 @@ const instance = /*@__PURE__*/ Fn( ( [ matrices, colors = null ], builder ) => {
|
|
1244
|
+
|
|
1245
|
+
const uniformBufferSize = Math.max( matrices.count, 1 ) * 16 * 4;
|
|
1246
|
+
|
|
1247
|
+
- if ( uniformBufferSize > builder.getUniformBufferLimit() ) {
|
|
1248
|
+
+ // threenative: the uniform path re-uploads every draw; the attribute path uploads on version change
|
|
1249
|
+
+ if ( true ) {
|
|
1250
|
+
|
|
1251
|
+
interleavedMatrix = _matrixBuffers.get( matrices );
|
|
1252
|
+
|
|
1253
|
+
@@ -31165,6 +31249,14 @@ class Geometries extends DataMap {
|
|
1254
|
+
*/
|
|
1255
|
+
this._geometryDisposeListeners = new Map();
|
|
1256
|
+
|
|
1257
|
+
+ /**
|
|
1258
|
+
+ * This Weak Map holds the attribute versions a settled render object was last drawn with,
|
|
1259
|
+
+ * so a write into one of them is what brings the object back out of the settled path.
|
|
1260
|
+
+ *
|
|
1261
|
+
+ * @type {WeakMap<RenderObject,Map<BufferAttribute,number>>}
|
|
1262
|
+
+ */
|
|
1263
|
+
+ this._settledAttributes = new WeakMap();
|
|
1264
|
+
+
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
/**
|
|
1268
|
+
@@ -31190,10 +31282,69 @@ class Geometries extends DataMap {
|
|
1269
|
+
|
|
1270
|
+
if ( this.has( renderObject ) === false ) this.initGeometry( renderObject );
|
|
1271
|
+
|
|
1272
|
+
+ // A settled static object has no attribute that could have changed since the last refresh,
|
|
1273
|
+
+ // unless a writer bumped one. An InstancedMesh's instance buffer is a vertex attribute of
|
|
1274
|
+
+ // this draw, and a BufferGeometry carries no version of its own to announce the write, so
|
|
1275
|
+
+ // the attributes' own versions are the signal - and the only one there is.
|
|
1276
|
+
+
|
|
1277
|
+
+ const monitor = renderObject.getMonitor();
|
|
1278
|
+
+
|
|
1279
|
+
+ if ( monitor !== undefined && monitor.isSettled( renderObject ) === true && this.attributesChanged( renderObject ) === false ) return;
|
|
1280
|
+
+
|
|
1281
|
+
this.updateAttributes( renderObject );
|
|
1282
|
+
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
+ /**
|
|
1286
|
+
+ * Returns `true` if an attribute of the given render object - or its index - was written to
|
|
1287
|
+
+ * since the last time its versions were read, and records the versions it holds now.
|
|
1288
|
+
+ *
|
|
1289
|
+
+ * @param {RenderObject} renderObject - The render object.
|
|
1290
|
+
+ * @return {boolean} Whether any of the render object's versions moved.
|
|
1291
|
+
+ */
|
|
1292
|
+
+ attributesChanged( renderObject ) {
|
|
1293
|
+
+
|
|
1294
|
+
+ const attributes = renderObject.getAttributes();
|
|
1295
|
+
+ const index = this.getIndex( renderObject );
|
|
1296
|
+
+ const settled = this._settledAttributes.get( renderObject );
|
|
1297
|
+
+
|
|
1298
|
+
+ if ( settled !== undefined && settled.size === attributes.length + ( index === null ? 0 : 1 ) ) {
|
|
1299
|
+
+
|
|
1300
|
+
+ let changed = false;
|
|
1301
|
+
+
|
|
1302
|
+
+ for ( const attribute of attributes ) {
|
|
1303
|
+
+
|
|
1304
|
+
+ if ( settled.get( attribute.isInterleavedBufferAttribute ? attribute.data : attribute ) !== attribute.version ) {
|
|
1305
|
+
+
|
|
1306
|
+
+ changed = true;
|
|
1307
|
+
+ break;
|
|
1308
|
+
+
|
|
1309
|
+
+ }
|
|
1310
|
+
+
|
|
1311
|
+
+ }
|
|
1312
|
+
+
|
|
1313
|
+
+ if ( changed === false && ( index === null || settled.get( index ) === index.version ) ) return false;
|
|
1314
|
+
+
|
|
1315
|
+
+ }
|
|
1316
|
+
+
|
|
1317
|
+
+ const versions = new Map();
|
|
1318
|
+
+
|
|
1319
|
+
+ for ( const attribute of attributes ) {
|
|
1320
|
+
+
|
|
1321
|
+
+ const data = attribute.isInterleavedBufferAttribute ? attribute.data : attribute;
|
|
1322
|
+
+
|
|
1323
|
+
+ versions.set( data, data.version );
|
|
1324
|
+
+
|
|
1325
|
+
+ }
|
|
1326
|
+
+
|
|
1327
|
+
+ if ( index !== null ) versions.set( index, index.version );
|
|
1328
|
+
+
|
|
1329
|
+
+ this._settledAttributes.set( renderObject, versions );
|
|
1330
|
+
+
|
|
1331
|
+
+ return true;
|
|
1332
|
+
+
|
|
1333
|
+
+ }
|
|
1334
|
+
+
|
|
1335
|
+
/**
|
|
1336
|
+
* Initializes the geometry of the given render object.
|
|
1337
|
+
*
|
|
1338
|
+
@@ -32771,7 +32922,7 @@ class Bindings extends DataMap {
|
|
1339
|
+
*/
|
|
1340
|
+
updateForRender( renderObject ) {
|
|
1341
|
+
|
|
1342
|
+
- this._updateBindings( this.getForRender( renderObject ) );
|
|
1343
|
+
+ this._updateBindings( this.getForRender( renderObject ), this.nodes.isSettled( renderObject ) );
|
|
1344
|
+
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
@@ -32918,12 +33069,13 @@ class Bindings extends DataMap {
|
|
1348
|
+
* Updates the given array of bindings.
|
|
1349
|
+
*
|
|
1350
|
+
* @param {Array<BindGroup>} bindings - The bind groups.
|
|
1351
|
+
+ * @param {boolean} [settled] - Whether the render object is a settled static object.
|
|
1352
|
+
*/
|
|
1353
|
+
- _updateBindings( bindings ) {
|
|
1354
|
+
+ _updateBindings( bindings, settled ) {
|
|
1355
|
+
|
|
1356
|
+
for ( const bindGroup of bindings ) {
|
|
1357
|
+
|
|
1358
|
+
- this._update( bindGroup, bindings );
|
|
1359
|
+
+ this._update( bindGroup, bindings, settled );
|
|
1360
|
+
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
@@ -32934,11 +33086,27 @@ class Bindings extends DataMap {
|
|
1364
|
+
*
|
|
1365
|
+
* @param {BindGroup} bindGroup - The bind group to update.
|
|
1366
|
+
* @param {Array<BindGroup>} bindings - The bind groups.
|
|
1367
|
+
+ * @param {boolean} [settled] - Whether the render object is a settled static object.
|
|
1368
|
+
*/
|
|
1369
|
+
- _update( bindGroup, bindings ) {
|
|
1370
|
+
+ _update( bindGroup, bindings, settled ) {
|
|
1371
|
+
|
|
1372
|
+
const { backend } = this;
|
|
1373
|
+
|
|
1374
|
+
+ // A group a node added after this object's first draw has no buffers and no layout yet, and
|
|
1375
|
+
+ // the pipeline reads that layout whatever this draw decides to do with it - so it is built
|
|
1376
|
+
+ // here, on every draw. A group that did not exist cannot have been written either, so it is
|
|
1377
|
+
+ // written now: `settled` is what tells the loop below to write it, not to skip it.
|
|
1378
|
+
+ if ( this.get( bindGroup ).bindGroup === undefined ) {
|
|
1379
|
+
+
|
|
1380
|
+
+ this._createBindings( [ bindGroup ] );
|
|
1381
|
+
+
|
|
1382
|
+
+ settled = false;
|
|
1383
|
+
+
|
|
1384
|
+
+ }
|
|
1385
|
+
+
|
|
1386
|
+
+ // a settled object keeps the values it uploaded the last time it refreshed, so an object
|
|
1387
|
+
+ // group it has already written is the one thing in here it may skip
|
|
1388
|
+
+
|
|
1389
|
+
let needsBindingsUpdate = false;
|
|
1390
|
+
let cacheBindings = true;
|
|
1391
|
+
let cacheIndex = 0;
|
|
1392
|
+
@@ -32948,7 +33116,7 @@ class Bindings extends DataMap {
|
|
1393
|
+
|
|
1394
|
+
for ( const binding of bindGroup.bindings ) {
|
|
1395
|
+
|
|
1396
|
+
- const updatedGroup = this.nodes.updateGroup( binding );
|
|
1397
|
+
+ const updatedGroup = this.nodes.updateGroup( binding, settled );
|
|
1398
|
+
|
|
1399
|
+
// every uniforms group is a uniform buffer. So if no update is required,
|
|
1400
|
+
// we move one with the next binding. Otherwise the next if block will update the group.
|
|
1401
|
+
@@ -34181,6 +34349,21 @@ class Textures extends DataMap {
|
|
1402
|
+
|
|
1403
|
+
backend.destroyTexture( texture );
|
|
1404
|
+
|
|
1405
|
+
+ // delete cached bind groups so they don't point to the destroyed texture
|
|
1406
|
+
+
|
|
1407
|
+
+ if ( textureData.bindGroups ) {
|
|
1408
|
+
+
|
|
1409
|
+
+ for ( const bindGroup of textureData.bindGroups ) {
|
|
1410
|
+
+
|
|
1411
|
+
+ const bindingsData = this.backend.get( bindGroup );
|
|
1412
|
+
+
|
|
1413
|
+
+ bindingsData.groups = undefined;
|
|
1414
|
+
+ bindingsData.versions = undefined;
|
|
1415
|
+
+
|
|
1416
|
+
+ }
|
|
1417
|
+
+
|
|
1418
|
+
+ }
|
|
1419
|
+
+
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
//
|
|
1423
|
+
@@ -55244,15 +55427,17 @@ class NodeManager extends DataMap {
|
|
1424
|
+
* Returns `true` if the given node uniforms group must be updated or not.
|
|
1425
|
+
*
|
|
1426
|
+
* @param {NodeUniformsGroup} nodeUniformsGroup - The node uniforms group.
|
|
1427
|
+
+ * @param {boolean} [settled] - Whether the render object is a settled static object.
|
|
1428
|
+
* @return {boolean} Whether the node uniforms group requires an update or not.
|
|
1429
|
+
*/
|
|
1430
|
+
- updateGroup( nodeUniformsGroup ) {
|
|
1431
|
+
+ updateGroup( nodeUniformsGroup, settled ) {
|
|
1432
|
+
|
|
1433
|
+
const groupNode = nodeUniformsGroup.groupNode;
|
|
1434
|
+
|
|
1435
|
+
- // groups that are updated per object always require an update so no further checks are needed
|
|
1436
|
+
+ // groups that are updated per object always require an update so no further checks are needed.
|
|
1437
|
+
+ // A settled static object keeps the values it uploaded the last time it was refreshed.
|
|
1438
|
+
|
|
1439
|
+
- if ( groupNode.updateType === NodeUpdateType.OBJECT ) return true;
|
|
1440
|
+
+ if ( groupNode.updateType === NodeUpdateType.OBJECT ) return settled !== true;
|
|
1441
|
+
|
|
1442
|
+
// check for update
|
|
1443
|
+
|
|
1444
|
+
@@ -56056,6 +56241,21 @@ class NodeManager extends DataMap {
|
|
1445
|
+
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
+ /**
|
|
1449
|
+
+ * Returns `true` if the per-object updates of the given render object can be skipped
|
|
1450
|
+
+ * because the object is static and nothing it depends on changed since its last refresh.
|
|
1451
|
+
+ *
|
|
1452
|
+
+ * @param {RenderObject} renderObject - The render object.
|
|
1453
|
+
+ * @return {boolean} Whether the render object is settled or not.
|
|
1454
|
+
+ */
|
|
1455
|
+
+ isSettled( renderObject ) {
|
|
1456
|
+
+
|
|
1457
|
+
+ const monitor = renderObject.getMonitor();
|
|
1458
|
+
+
|
|
1459
|
+
+ return monitor !== undefined ? monitor.isSettled( renderObject ) : false;
|
|
1460
|
+
+
|
|
1461
|
+
+ }
|
|
1462
|
+
+
|
|
1463
|
+
/**
|
|
1464
|
+
* Triggers the call of `updateBefore()` methods
|
|
1465
|
+
* for all nodes of the given render object.
|
|
1466
|
+
@@ -56065,9 +56265,14 @@ class NodeManager extends DataMap {
|
|
1467
|
+
updateBefore( renderObject ) {
|
|
1468
|
+
|
|
1469
|
+
const nodeBuilder = renderObject.getNodeBuilderState();
|
|
1470
|
+
+ const settled = this.isSettled( renderObject );
|
|
1471
|
+
|
|
1472
|
+
for ( const node of nodeBuilder.updateBeforeNodes ) {
|
|
1473
|
+
|
|
1474
|
+
+ // a settled static object has nothing to update per object
|
|
1475
|
+
+
|
|
1476
|
+
+ if ( settled === true && node.getUpdateBeforeType() === NodeUpdateType.OBJECT ) continue;
|
|
1477
|
+
+
|
|
1478
|
+
// update frame state for each node
|
|
1479
|
+
|
|
1480
|
+
this.getNodeFrameForRender( renderObject ).updateBeforeNode( node );
|
|
1481
|
+
@@ -56085,10 +56290,11 @@ class NodeManager extends DataMap {
|
|
1482
|
+
updateAfter( renderObject ) {
|
|
1483
|
+
|
|
1484
|
+
const nodeBuilder = renderObject.getNodeBuilderState();
|
|
1485
|
+
+ const settled = this.isSettled( renderObject );
|
|
1486
|
+
|
|
1487
|
+
for ( const node of nodeBuilder.updateAfterNodes ) {
|
|
1488
|
+
|
|
1489
|
+
- // update frame state for each node
|
|
1490
|
+
+ if ( settled === true && node.getUpdateAfterType() === NodeUpdateType.OBJECT ) continue;
|
|
1491
|
+
|
|
1492
|
+
this.getNodeFrameForRender( renderObject ).updateAfterNode( node );
|
|
1493
|
+
|
|
1494
|
+
@@ -56125,9 +56331,14 @@ class NodeManager extends DataMap {
|
|
1495
|
+
|
|
1496
|
+
const nodeFrame = this.getNodeFrameForRender( renderObject );
|
|
1497
|
+
const nodeBuilder = renderObject.getNodeBuilderState();
|
|
1498
|
+
+ const settled = this.isSettled( renderObject );
|
|
1499
|
+
|
|
1500
|
+
for ( const node of nodeBuilder.updateNodes ) {
|
|
1501
|
+
|
|
1502
|
+
+ // a settled static object keeps the per-object values it uploaded last time
|
|
1503
|
+
+
|
|
1504
|
+
+ if ( settled === true && node.getUpdateType() === NodeUpdateType.OBJECT ) continue;
|
|
1505
|
+
+
|
|
1506
|
+
nodeFrame.updateNode( node );
|
|
1507
|
+
|
|
1508
|
+
}
|
|
1509
|
+
@@ -62886,18 +63097,17 @@ class Renderer {
|
|
1510
|
+
|
|
1511
|
+
//
|
|
1512
|
+
|
|
1513
|
+
- const needsRefresh = this._nodes.needsRefresh( renderObject );
|
|
1514
|
+
-
|
|
1515
|
+
- if ( needsRefresh ) {
|
|
1516
|
+
-
|
|
1517
|
+
- this._nodes.updateBefore( renderObject );
|
|
1518
|
+
-
|
|
1519
|
+
- this._geometries.updateForRender( renderObject );
|
|
1520
|
+
-
|
|
1521
|
+
- this._nodes.updateForRender( renderObject );
|
|
1522
|
+
- this._bindings.updateForRender( renderObject );
|
|
1523
|
+
+ this._nodes.needsRefresh( renderObject );
|
|
1524
|
+
|
|
1525
|
+
- }
|
|
1526
|
+
+ // Every step of a draw runs, settled or not. What a settled static object skips is decided
|
|
1527
|
+
+ // inside each of them - its OBJECT node updates, its object uniform group write and its
|
|
1528
|
+
+ // attribute scan - because each of those answers for itself, and a step left out here
|
|
1529
|
+
+ // cannot: a group a node added after this object's first draw still has to be created, and
|
|
1530
|
+
+ // the pipeline reads that group's layout whatever this draw decided to do with it.
|
|
1531
|
+
+ this._geometries.updateForRender( renderObject );
|
|
1532
|
+
+ this._nodes.updateBefore( renderObject );
|
|
1533
|
+
+ this._nodes.updateForRender( renderObject );
|
|
1534
|
+
+ this._bindings.updateForRender( renderObject );
|
|
1535
|
+
|
|
1536
|
+
this._pipelines.updateForRender( renderObject );
|
|
1537
|
+
|
|
1538
|
+
@@ -62907,7 +63117,7 @@ class Renderer {
|
|
1539
|
+
|
|
1540
|
+
this.backend.draw( renderObject, this.info );
|
|
1541
|
+
|
|
1542
|
+
- if ( needsRefresh ) this._nodes.updateAfter( renderObject );
|
|
1543
|
+
+ this._nodes.updateAfter( renderObject );
|
|
1544
|
+
|
|
1545
|
+
}
|
|
1546
|
+
|
|
1547
|
+
@@ -79004,7 +79214,14 @@ class WGSLNodeBuilder extends NodeBuilder {
|
|
1548
|
+
|
|
1549
|
+
} else {
|
|
1550
|
+
|
|
1551
|
+
- snippet = `textureLoad( ${ textureProperty }, ${ uvIndexSnippet }, u32( ${ levelSnippet } ) )`;
|
|
1552
|
+
+ // naga v25 (wgpu-native v25.0.2.2) rejects a u32 sample index on a
|
|
1553
|
+
+ // multisampled textureLoad; i32 is accepted by every backend. Mip-level
|
|
1554
|
+
+ // operands (including the array form above) stay u32.
|
|
1555
|
+
+ const sampleData = texture ? this.renderer?.backend?.utils?.getTextureSampleData?.( texture ) : null;
|
|
1556
|
+
+ const backendMultisampled = sampleData !== null && sampleData !== undefined && sampleData.primarySamples > 1;
|
|
1557
|
+
+ const targetMultisampled = Boolean( texture && texture.renderTarget && texture.renderTarget.samples > 1 );
|
|
1558
|
+
+ const indexType = ( backendMultisampled || targetMultisampled ) ? 'i32' : 'u32';
|
|
1559
|
+
+ snippet = `textureLoad( ${ textureProperty }, ${ uvIndexSnippet }, ${ indexType }( ${ levelSnippet } ) )`;
|
|
1560
|
+
|
|
1561
|
+
if ( this.renderer.backend.compatibilityMode && texture.isDepthTexture ) {
|
|
1562
|
+
|
|
1563
|
+
@@ -81295,9 +81512,15 @@ class WebGPUAttributeUtils {
|
|
1564
|
+
destroyAttribute( attribute ) {
|
|
1565
|
+
|
|
1566
|
+
const backend = this.backend;
|
|
1567
|
+
- const data = backend.get( this._getBufferAttribute( attribute ) );
|
|
1568
|
+
+ const bufferAttribute = this._getBufferAttribute( attribute );
|
|
1569
|
+
+ const data = backend.get( bufferAttribute );
|
|
1570
|
+
+
|
|
1571
|
+
+ // An interleaved attribute's GPU buffer lives on its InterleavedBuffer, and several attributes
|
|
1572
|
+
+ // share it: destroy it once and forget it there too, as the WebGL backend does, or the next
|
|
1573
|
+
+ // createAttribute() finds the destroyed buffer and submits it (threenative patch).
|
|
1574
|
+
+ if ( data.buffer !== undefined ) data.buffer.destroy();
|
|
1575
|
+
|
|
1576
|
+
- data.buffer.destroy();
|
|
1577
|
+
+ if ( bufferAttribute !== attribute ) backend.delete( bufferAttribute );
|
|
1578
|
+
|
|
1579
|
+
backend.delete( attribute );
|
|
1580
|
+
|
|
1581
|
+
diff --git a/src/materials/nodes/manager/NodeMaterialObserver.js b/src/materials/nodes/manager/NodeMaterialObserver.js
|
|
1582
|
+
index 1f4c254..7e2cb99 100644
|
|
1583
|
+
--- a/src/materials/nodes/manager/NodeMaterialObserver.js
|
|
1584
|
+
+++ b/src/materials/nodes/manager/NodeMaterialObserver.js
|
|
1585
|
+
@@ -133,6 +133,15 @@ class NodeMaterialObserver {
|
|
1586
|
+
*/
|
|
1587
|
+
this.renderId = 0;
|
|
1588
|
+
|
|
1589
|
+
+ /**
|
|
1590
|
+
+ * Whether the current render produces motion vectors. Objects keep refreshing
|
|
1591
|
+
+ * fully while this is the case.
|
|
1592
|
+
+ *
|
|
1593
|
+
+ * @type {boolean}
|
|
1594
|
+
+ * @default false
|
|
1595
|
+
+ */
|
|
1596
|
+
+ this.renderingVelocity = false;
|
|
1597
|
+
+
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
/**
|
|
1601
|
+
@@ -232,6 +241,88 @@ class NodeMaterialObserver {
|
|
1602
|
+
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
+ /**
|
|
1606
|
+
+ * Returns `true` if the given render object is a static object whose per-object state - world
|
|
1607
|
+
+ * matrix, material, geometry and the scene environment - did not change since the last
|
|
1608
|
+
+ * refresh. A settled object is still drawn every frame and its FRAME and RENDER nodes still
|
|
1609
|
+
+ * have to be updated; what it may skip is the work whose inputs it froze: its OBJECT node
|
|
1610
|
+
+ * updates, its object uniform group upload and its per-draw attribute scan.
|
|
1611
|
+
+ *
|
|
1612
|
+
+ * @param {RenderObject} renderObject - The render object.
|
|
1613
|
+
+ * @return {boolean} Whether the given render object is settled or not.
|
|
1614
|
+
+ */
|
|
1615
|
+
+ isSettled( renderObject ) {
|
|
1616
|
+
+
|
|
1617
|
+
+ const data = this.renderObjects.get( renderObject );
|
|
1618
|
+
+
|
|
1619
|
+
+ return data !== undefined && data.settledState === true;
|
|
1620
|
+
+
|
|
1621
|
+
+ }
|
|
1622
|
+
+
|
|
1623
|
+
+ /**
|
|
1624
|
+
+ * Recomputes {@link NodeMaterialObserver#isSettled} for the given render object, once per draw,
|
|
1625
|
+
+ * so every step of that draw reads the same answer.
|
|
1626
|
+
+ *
|
|
1627
|
+
+ * @param {RenderObject} renderObject - The render object.
|
|
1628
|
+
+ * @return {boolean} Whether the given render object is settled or not.
|
|
1629
|
+
+ */
|
|
1630
|
+
+ updateSettled( renderObject ) {
|
|
1631
|
+
+
|
|
1632
|
+
+ const { object, material, geometry, scene } = renderObject;
|
|
1633
|
+
+ const data = this.getRenderObjectData( renderObject );
|
|
1634
|
+
+
|
|
1635
|
+
+ if ( object.static !== true || this.renderingVelocity === true ) {
|
|
1636
|
+
+
|
|
1637
|
+
+ data.settledState = false;
|
|
1638
|
+
+
|
|
1639
|
+
+ return false;
|
|
1640
|
+
+
|
|
1641
|
+
+ }
|
|
1642
|
+
+
|
|
1643
|
+
+ let settled = data.settled;
|
|
1644
|
+
+
|
|
1645
|
+
+ if ( settled === undefined ) {
|
|
1646
|
+
+
|
|
1647
|
+
+ data.settled = settled = {
|
|
1648
|
+
+ matrix: object.matrixWorld.clone(),
|
|
1649
|
+
+ environmentRotation: scene.environmentRotation.clone(),
|
|
1650
|
+
+ environmentIntensity: scene.environmentIntensity,
|
|
1651
|
+
+ materialVersion: material.version,
|
|
1652
|
+
+ geometryVersion: geometry.version
|
|
1653
|
+
+ };
|
|
1654
|
+
+
|
|
1655
|
+
+ data.settledState = false;
|
|
1656
|
+
+
|
|
1657
|
+
+ return false;
|
|
1658
|
+
+
|
|
1659
|
+
+ }
|
|
1660
|
+
+
|
|
1661
|
+
+ if ( settled.matrix.equals( object.matrixWorld ) !== true ||
|
|
1662
|
+
+ settled.environmentRotation.equals( scene.environmentRotation ) !== true ||
|
|
1663
|
+
+ settled.environmentIntensity !== scene.environmentIntensity ||
|
|
1664
|
+
+ settled.materialVersion !== material.version ||
|
|
1665
|
+
+ settled.geometryVersion !== geometry.version ) {
|
|
1666
|
+
+
|
|
1667
|
+
+ // re-arm with the current state, so the object settles again once it stops changing
|
|
1668
|
+
+
|
|
1669
|
+
+ settled.matrix.copy( object.matrixWorld );
|
|
1670
|
+
+ settled.environmentRotation.copy( scene.environmentRotation );
|
|
1671
|
+
+ settled.environmentIntensity = scene.environmentIntensity;
|
|
1672
|
+
+ settled.materialVersion = material.version;
|
|
1673
|
+
+ settled.geometryVersion = geometry.version;
|
|
1674
|
+
+
|
|
1675
|
+
+ data.settledState = false;
|
|
1676
|
+
+
|
|
1677
|
+
+ return false;
|
|
1678
|
+
+
|
|
1679
|
+
+ }
|
|
1680
|
+
+
|
|
1681
|
+
+ data.settledState = true;
|
|
1682
|
+
+
|
|
1683
|
+
+ return true;
|
|
1684
|
+
+
|
|
1685
|
+
+ }
|
|
1686
|
+
+
|
|
1687
|
+
/**
|
|
1688
|
+
* Returns an attribute data structure holding the attributes versions for
|
|
1689
|
+
* monitoring.
|
|
1690
|
+
@@ -298,7 +389,6 @@ class NodeMaterialObserver {
|
|
1691
|
+
|
|
1692
|
+
data = {
|
|
1693
|
+
_renderId: - 1,
|
|
1694
|
+
- _equal: false,
|
|
1695
|
+
|
|
1696
|
+
attributes: this.getAttributesData( geometry.attributes ),
|
|
1697
|
+
indexId: geometry.index ? geometry.index.id : null,
|
|
1698
|
+
@@ -327,7 +417,7 @@ class NodeMaterialObserver {
|
|
1699
|
+
|
|
1700
|
+
if ( data === undefined ) {
|
|
1701
|
+
|
|
1702
|
+
- data = { _renderId: - 1, _equal: false };
|
|
1703
|
+
+ data = { _renderId: - 1 };
|
|
1704
|
+
|
|
1705
|
+
for ( const property of this.refreshUniforms ) {
|
|
1706
|
+
|
|
1707
|
+
@@ -411,7 +501,6 @@ class NodeMaterialObserver {
|
|
1708
|
+
|
|
1709
|
+
value.copy( mtlValue );
|
|
1710
|
+
|
|
1711
|
+
- materialData._equal = false;
|
|
1712
|
+
return false;
|
|
1713
|
+
|
|
1714
|
+
}
|
|
1715
|
+
@@ -423,7 +512,6 @@ class NodeMaterialObserver {
|
|
1716
|
+
value.id = mtlValue.id;
|
|
1717
|
+
value.version = mtlValue.version;
|
|
1718
|
+
|
|
1719
|
+
- materialData._equal = false;
|
|
1720
|
+
return false;
|
|
1721
|
+
|
|
1722
|
+
}
|
|
1723
|
+
@@ -432,7 +520,6 @@ class NodeMaterialObserver {
|
|
1724
|
+
|
|
1725
|
+
materialData[ property ] = mtlValue;
|
|
128
1726
|
|
|
129
|
-
|
|
1727
|
+
- materialData._equal = false;
|
|
1728
|
+
return false;
|
|
130
1729
|
|
|
131
|
-
|
|
1730
|
+
}
|
|
1731
|
+
@@ -448,18 +535,12 @@ class NodeMaterialObserver {
|
|
1732
|
+
renderObjectData.bufferWidth = width;
|
|
1733
|
+
renderObjectData.bufferHeight = height;
|
|
1734
|
+
|
|
1735
|
+
- materialData._equal = false;
|
|
1736
|
+
return false;
|
|
132
1737
|
|
|
133
|
-
+ if ( sourceTracked ) renderObject.sourceVersion = sourceMaterial.version;
|
|
134
|
-
+
|
|
135
1738
|
}
|
|
136
1739
|
|
|
137
1740
|
}
|
|
138
|
-
@@ -39725,7 +39765,8 @@ class VelocityNode extends TempNode {
|
|
139
|
-
|
|
140
|
-
const previousModelMatrix = getPreviousMatrix( object );
|
|
141
1741
|
|
|
142
|
-
-
|
|
143
|
-
|
|
144
|
-
|
|
1742
|
+
- materialData._equal = true;
|
|
1743
|
+
-
|
|
1744
|
+
- } else {
|
|
1745
|
+
-
|
|
1746
|
+
- if ( materialData._equal === false ) return false;
|
|
145
1747
|
|
|
146
|
-
|
|
1748
|
+
}
|
|
147
1749
|
|
|
148
|
-
@@ -
|
|
1750
|
+
@@ -502,7 +583,6 @@ class NodeMaterialObserver {
|
|
1751
|
+
// attribute was removed
|
|
1752
|
+
delete storedAttributes[ name ];
|
|
149
1753
|
|
|
150
|
-
|
|
1754
|
+
- geometryData._equal = false;
|
|
1755
|
+
return false;
|
|
151
1756
|
|
|
152
|
-
|
|
153
|
-
|
|
1757
|
+
}
|
|
1758
|
+
@@ -515,7 +595,6 @@ class NodeMaterialObserver {
|
|
1759
|
+
storedAttributeData.id = id;
|
|
1760
|
+
storedAttributeData.version = version;
|
|
154
1761
|
|
|
155
|
-
|
|
1762
|
+
- geometryData._equal = false;
|
|
1763
|
+
return false;
|
|
156
1764
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
let materialDisplacementScale;
|
|
160
|
-
let materialDisplacementBias;
|
|
161
|
-
+ let materialAlphaTest;
|
|
1765
|
+
}
|
|
1766
|
+
@@ -526,7 +605,6 @@ class NodeMaterialObserver {
|
|
162
1767
|
|
|
163
|
-
|
|
1768
|
+
geometryData.attributes = this.getAttributesData( attributes );
|
|
164
1769
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
materialDisplacementScale = overrideMaterial.displacementScale;
|
|
168
|
-
materialDisplacementBias = overrideMaterial.displacementBias;
|
|
169
|
-
+ materialAlphaTest = overrideMaterial.alphaTest;
|
|
1770
|
+
- geometryData._equal = false;
|
|
1771
|
+
return false;
|
|
170
1772
|
|
|
171
|
-
|
|
1773
|
+
}
|
|
1774
|
+
@@ -544,7 +622,6 @@ class NodeMaterialObserver {
|
|
1775
|
+
geometryData.indexId = currentIndexId;
|
|
1776
|
+
geometryData.indexVersion = currentIndexVersion;
|
|
172
1777
|
|
|
173
|
-
|
|
1778
|
+
- geometryData._equal = false;
|
|
1779
|
+
return false;
|
|
174
1780
|
|
|
175
1781
|
}
|
|
1782
|
+
@@ -556,16 +633,10 @@ class NodeMaterialObserver {
|
|
1783
|
+
geometryData.drawRange.start = geometry.drawRange.start;
|
|
1784
|
+
geometryData.drawRange.count = geometry.drawRange.count;
|
|
176
1785
|
|
|
177
|
-
-
|
|
178
|
-
|
|
179
|
-
+ // override material is shared by every caster in the pass. A scene that mixes alpha-tested
|
|
180
|
-
+ // casters with opaque ones therefore moves that shared version several times per frame,
|
|
181
|
-
+ // and every OTHER render object drawn with it then fails the version gate in
|
|
182
|
-
+ // `RenderObjects.get` and recomputes a full material cache key that always matches.
|
|
183
|
-
+ // Writing the backing field leaves the `alphaTest` getter — and so the cache key — still
|
|
184
|
-
+ // reporting this caster's value, while each render object tracks the staleness signal
|
|
185
|
-
+ // that belongs to it: its own source material's version.
|
|
186
|
-
+ overrideMaterial._alphaTest = material.alphaTest;
|
|
187
|
-
overrideMaterial.alphaMap = material.alphaMap;
|
|
188
|
-
overrideMaterial.displacementMap = material.displacementMap;
|
|
189
|
-
overrideMaterial.displacementScale = material.displacementScale;
|
|
190
|
-
@@ -62825,6 +62876,7 @@ class Renderer {
|
|
191
|
-
scene.overrideMaterial.displacementMap = materialDisplacementMap;
|
|
192
|
-
scene.overrideMaterial.displacementScale = materialDisplacementScale;
|
|
193
|
-
scene.overrideMaterial.displacementBias = materialDisplacementBias;
|
|
194
|
-
+ scene.overrideMaterial._alphaTest = materialAlphaTest;
|
|
1786
|
+
- geometryData._equal = false;
|
|
1787
|
+
return false;
|
|
195
1788
|
|
|
196
|
-
|
|
1789
|
+
}
|
|
197
1790
|
|
|
198
|
-
|
|
1791
|
+
- geometryData._equal = true;
|
|
1792
|
+
-
|
|
1793
|
+
- } else {
|
|
1794
|
+
-
|
|
1795
|
+
- if ( geometryData._equal === false ) return false;
|
|
199
1796
|
|
|
200
|
-
|
|
1797
|
+
}
|
|
201
1798
|
|
|
202
|
-
-
|
|
203
|
-
|
|
204
|
-
|
|
1799
|
+
@@ -716,7 +787,14 @@ class NodeMaterialObserver {
|
|
1800
|
+
*/
|
|
1801
|
+
needsRefresh( renderObject, nodeFrame ) {
|
|
205
1802
|
|
|
206
|
-
|
|
1803
|
+
- if ( this.hasNode || this.hasAnimation || this.firstInitialization( renderObject ) || this.needsVelocity( nodeFrame.renderer ) )
|
|
1804
|
+
+ this.renderingVelocity = this.needsVelocity( nodeFrame.renderer );
|
|
1805
|
+
+
|
|
1806
|
+
+ // Answered for the whole draw, so updateBefore, the geometry scan, the node updates and
|
|
1807
|
+
+ // the object uniform upload of this object all skip or all refresh.
|
|
1808
|
+
+
|
|
1809
|
+
+ this.updateSettled( renderObject );
|
|
1810
|
+
+
|
|
1811
|
+
+ if ( this.hasNode || this.hasAnimation || this.firstInitialization( renderObject ) || this.renderingVelocity )
|
|
1812
|
+
return true;
|
|
207
1813
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
this.compatibilityMode = null;
|
|
1814
|
+
const { renderId } = nodeFrame;
|
|
1815
|
+
@@ -729,10 +807,14 @@ class NodeMaterialObserver {
|
|
211
1816
|
|
|
212
|
-
|
|
213
|
-
+ * A reference to the WebGPU instance that creates adapters.
|
|
214
|
-
+ *
|
|
215
|
-
+ * @type {?GPU}
|
|
216
|
-
+ * @default null
|
|
217
|
-
+ */
|
|
218
|
-
+ this.gpu = null;
|
|
219
|
-
+
|
|
220
|
-
/**
|
|
221
|
-
* A reference to the device.
|
|
222
|
-
*
|
|
223
|
-
@@ -83926,7 +83987,9 @@ class WebGPUBackend extends Backend {
|
|
224
|
-
xrCompatible: renderer.xr.enabled
|
|
225
|
-
};
|
|
1817
|
+
}
|
|
226
1818
|
|
|
227
|
-
-
|
|
228
|
-
|
|
229
|
-
+
|
|
230
|
-
+ const adapter = this.gpu !== null ? await this.gpu.requestAdapter( adapterOptions ) : null;
|
|
1819
|
+
- const isStatic = renderObject.object.static === true;
|
|
1820
|
+
const isBundle = renderObject.bundle !== null && renderObject.bundle.static === true && this.getRenderObjectData( renderObject ).version === renderObject.bundle.version;
|
|
231
1821
|
|
|
232
|
-
|
|
1822
|
+
- if ( isStatic || isBundle )
|
|
1823
|
+
+ // `object.static` is not the question - it is a switch, and a brand new object has it set
|
|
1824
|
+
+ // before it has been drawn once, so answering from it skips the draw that creates this
|
|
1825
|
+
+ // object's bind groups and leaves the pipeline reading a layout that was never built. A
|
|
1826
|
+
+ // static object that moved, or whose material or geometry was edited, has to refresh like
|
|
1827
|
+
+ // any other; only a settled one may skip, and nothing is settled before its first refresh.
|
|
1828
|
+
+ if ( this.isSettled( renderObject ) === true || isBundle )
|
|
1829
|
+
return false;
|
|
233
1830
|
|
|
1831
|
+
const lightsData = this.getLights( renderObject.lightsNode, renderId );
|
|
234
1832
|
diff --git a/src/nodes/accessors/Batch.js b/src/nodes/accessors/Batch.js
|
|
235
|
-
index
|
|
1833
|
+
index ab62e98..2cf4bb6 100644
|
|
236
1834
|
--- a/src/nodes/accessors/Batch.js
|
|
237
1835
|
+++ b/src/nodes/accessors/Batch.js
|
|
238
1836
|
@@ -1,6 +1,6 @@
|
|
@@ -278,10 +1876,30 @@ index ab62e98dadab2539d7e1d663fd42267bbc8ebcaa..2cf4bb6039853d5c922e89041419250b
|
|
|
278
1876
|
-
|
|
279
1877
|
-
|
|
280
1878
|
diff --git a/src/nodes/accessors/Instance.js b/src/nodes/accessors/Instance.js
|
|
281
|
-
index
|
|
1879
|
+
index b89834b..ed00e75 100644
|
|
282
1880
|
--- a/src/nodes/accessors/Instance.js
|
|
283
1881
|
+++ b/src/nodes/accessors/Instance.js
|
|
284
|
-
@@ -
|
|
1882
|
+
@@ -40,7 +40,8 @@ function createInstanceMatrixNode( builder, instanceMatrix ) {
|
|
1883
|
+
|
|
1884
|
+
const uniformBufferSize = matrixCount * 16 * 4;
|
|
1885
|
+
|
|
1886
|
+
- if ( uniformBufferSize <= builder.getUniformBufferLimit() ) {
|
|
1887
|
+
+ // threenative: the uniform path re-uploads every draw; the attribute path uploads on version change
|
|
1888
|
+
+ if ( false ) {
|
|
1889
|
+
|
|
1890
|
+
instanceMatrixNode = buffer( instanceMatrix.array, 'mat4', matrixCount ).element( instanceIndex );
|
|
1891
|
+
|
|
1892
|
+
@@ -134,7 +135,8 @@ export const instance = /*@__PURE__*/ Fn( ( [ matrices, colors = null ], builder
|
|
1893
|
+
|
|
1894
|
+
const uniformBufferSize = Math.max( matrices.count, 1 ) * 16 * 4;
|
|
1895
|
+
|
|
1896
|
+
- if ( uniformBufferSize > builder.getUniformBufferLimit() ) {
|
|
1897
|
+
+ // threenative: the uniform path re-uploads every draw; the attribute path uploads on version change
|
|
1898
|
+
+ if ( true ) {
|
|
1899
|
+
|
|
1900
|
+
interleavedMatrix = _matrixBuffers.get( matrices );
|
|
1901
|
+
|
|
1902
|
+
@@ -220,7 +222,8 @@ export const instance = /*@__PURE__*/ Fn( ( [ matrices, colors = null ], builder
|
|
285
1903
|
|
|
286
1904
|
const previousInstanceData = _previousInstanceMatrices.get( object );
|
|
287
1905
|
|
|
@@ -292,7 +1910,7 @@ index b89834b9166c75fd946ade24db265bf8c367d244..c7b7bdd7a31d852a581d4bc75a1e1afc
|
|
|
292
1910
|
} );
|
|
293
1911
|
|
|
294
1912
|
diff --git a/src/nodes/accessors/Skinning.js b/src/nodes/accessors/Skinning.js
|
|
295
|
-
index
|
|
1913
|
+
index 2356aed..aaeea63 100644
|
|
296
1914
|
--- a/src/nodes/accessors/Skinning.js
|
|
297
1915
|
+++ b/src/nodes/accessors/Skinning.js
|
|
298
1916
|
@@ -149,7 +149,8 @@ export const skinning = /*@__PURE__*/ Fn( ( [ skinnedMesh ], builder ) => {
|
|
@@ -316,7 +1934,7 @@ index 2356aed8656dceb1ea74b73ea2e739371a960484..aaeea633a8ece291be5faf868e312867
|
|
|
316
1934
|
}
|
|
317
1935
|
|
|
318
1936
|
diff --git a/src/nodes/accessors/VelocityNode.js b/src/nodes/accessors/VelocityNode.js
|
|
319
|
-
index
|
|
1937
|
+
index 373548b..c48002a 100644
|
|
320
1938
|
--- a/src/nodes/accessors/VelocityNode.js
|
|
321
1939
|
+++ b/src/nodes/accessors/VelocityNode.js
|
|
322
1940
|
@@ -106,7 +106,8 @@ class VelocityNode extends TempNode {
|
|
@@ -330,7 +1948,7 @@ index 373548b0b587e7da3ef0993bfe316af420e7f66c..c48002abbce20ee8b29908cf314d7b03
|
|
|
330
1948
|
//
|
|
331
1949
|
|
|
332
1950
|
diff --git a/src/nodes/core/NodeBuilder.js b/src/nodes/core/NodeBuilder.js
|
|
333
|
-
index
|
|
1951
|
+
index be00b81..1990da8 100644
|
|
334
1952
|
--- a/src/nodes/core/NodeBuilder.js
|
|
335
1953
|
+++ b/src/nodes/core/NodeBuilder.js
|
|
336
1954
|
@@ -3450,7 +3450,7 @@ class NodeBuilder {
|
|
@@ -343,7 +1961,7 @@ index be00b81ef36e1af83b0066603e616f8f1f81a115..1990da8fe8f1f29a7b53030756dfd966
|
|
|
343
1961
|
}
|
|
344
1962
|
|
|
345
1963
|
diff --git a/src/objects/BatchedMesh.js b/src/objects/BatchedMesh.js
|
|
346
|
-
index
|
|
1964
|
+
index 2ef7cb2..35cc0ff 100644
|
|
347
1965
|
--- a/src/objects/BatchedMesh.js
|
|
348
1966
|
+++ b/src/objects/BatchedMesh.js
|
|
349
1967
|
@@ -181,6 +181,8 @@ function copyArrayContents( src, target ) {
|
|
@@ -356,9 +1974,18 @@ index 2ef7cb257438826059eb2408b3756c4891f06a10..35cc0ffe83a69ea9fa5fca6dec2d820c
|
|
|
356
1974
|
* Constructs a new batched mesh.
|
|
357
1975
|
*
|
|
358
1976
|
diff --git a/src/renderers/common/Bindings.js b/src/renderers/common/Bindings.js
|
|
359
|
-
index
|
|
1977
|
+
index 5f3d213..a3cec4e 100644
|
|
360
1978
|
--- a/src/renderers/common/Bindings.js
|
|
361
1979
|
+++ b/src/renderers/common/Bindings.js
|
|
1980
|
+
@@ -145,7 +145,7 @@ class Bindings extends DataMap {
|
|
1981
|
+
*/
|
|
1982
|
+
updateForRender( renderObject ) {
|
|
1983
|
+
|
|
1984
|
+
- this._updateBindings( this.getForRender( renderObject ) );
|
|
1985
|
+
+ this._updateBindings( this.getForRender( renderObject ), this.nodes.isSettled( renderObject ) );
|
|
1986
|
+
|
|
1987
|
+
}
|
|
1988
|
+
|
|
362
1989
|
@@ -206,10 +206,14 @@ class Bindings extends DataMap {
|
|
363
1990
|
|
|
364
1991
|
} else if ( binding.isSampledTexture ) {
|
|
@@ -374,8 +2001,165 @@ index 5f3d213a016b37db3b2e5d022da3dd0234d86f6d..7a9a168f29773f37ef9a51ef9f07ade9
|
|
|
374
2001
|
this.textures.updateSampler( binding );
|
|
375
2002
|
|
|
376
2003
|
} else if ( binding.isStorageBuffer ) {
|
|
2004
|
+
@@ -292,12 +296,13 @@ class Bindings extends DataMap {
|
|
2005
|
+
* Updates the given array of bindings.
|
|
2006
|
+
*
|
|
2007
|
+
* @param {Array<BindGroup>} bindings - The bind groups.
|
|
2008
|
+
+ * @param {boolean} [settled] - Whether the render object is a settled static object.
|
|
2009
|
+
*/
|
|
2010
|
+
- _updateBindings( bindings ) {
|
|
2011
|
+
+ _updateBindings( bindings, settled ) {
|
|
2012
|
+
|
|
2013
|
+
for ( const bindGroup of bindings ) {
|
|
2014
|
+
|
|
2015
|
+
- this._update( bindGroup, bindings );
|
|
2016
|
+
+ this._update( bindGroup, bindings, settled );
|
|
2017
|
+
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
@@ -308,11 +313,27 @@ class Bindings extends DataMap {
|
|
2021
|
+
*
|
|
2022
|
+
* @param {BindGroup} bindGroup - The bind group to update.
|
|
2023
|
+
* @param {Array<BindGroup>} bindings - The bind groups.
|
|
2024
|
+
+ * @param {boolean} [settled] - Whether the render object is a settled static object.
|
|
2025
|
+
*/
|
|
2026
|
+
- _update( bindGroup, bindings ) {
|
|
2027
|
+
+ _update( bindGroup, bindings, settled ) {
|
|
2028
|
+
|
|
2029
|
+
const { backend } = this;
|
|
2030
|
+
|
|
2031
|
+
+ // A group a node added after this object's first draw has no buffers and no layout yet, and
|
|
2032
|
+
+ // the pipeline reads that layout whatever this draw decides to do with it - so it is built
|
|
2033
|
+
+ // here, on every draw. A group that did not exist cannot have been written either, so it is
|
|
2034
|
+
+ // written now: `settled` is what tells the loop below to write it, not to skip it.
|
|
2035
|
+
+ if ( this.get( bindGroup ).bindGroup === undefined ) {
|
|
2036
|
+
+
|
|
2037
|
+
+ this._createBindings( [ bindGroup ] );
|
|
2038
|
+
+
|
|
2039
|
+
+ settled = false;
|
|
2040
|
+
+
|
|
2041
|
+
+ }
|
|
2042
|
+
+
|
|
2043
|
+
+ // a settled object keeps the values it uploaded the last time it refreshed, so an object
|
|
2044
|
+
+ // group it has already written is the one thing in here it may skip
|
|
2045
|
+
+
|
|
2046
|
+
let needsBindingsUpdate = false;
|
|
2047
|
+
let cacheBindings = true;
|
|
2048
|
+
let cacheIndex = 0;
|
|
2049
|
+
@@ -322,7 +343,7 @@ class Bindings extends DataMap {
|
|
2050
|
+
|
|
2051
|
+
for ( const binding of bindGroup.bindings ) {
|
|
2052
|
+
|
|
2053
|
+
- const updatedGroup = this.nodes.updateGroup( binding );
|
|
2054
|
+
+ const updatedGroup = this.nodes.updateGroup( binding, settled );
|
|
2055
|
+
|
|
2056
|
+
// every uniforms group is a uniform buffer. So if no update is required,
|
|
2057
|
+
// we move one with the next binding. Otherwise the next if block will update the group.
|
|
2058
|
+
diff --git a/src/renderers/common/Geometries.js b/src/renderers/common/Geometries.js
|
|
2059
|
+
index eb332c4..979da2f 100644
|
|
2060
|
+
--- a/src/renderers/common/Geometries.js
|
|
2061
|
+
+++ b/src/renderers/common/Geometries.js
|
|
2062
|
+
@@ -139,6 +139,14 @@ class Geometries extends DataMap {
|
|
2063
|
+
*/
|
|
2064
|
+
this._geometryDisposeListeners = new Map();
|
|
2065
|
+
|
|
2066
|
+
+ /**
|
|
2067
|
+
+ * This Weak Map holds the attribute versions a settled render object was last drawn with,
|
|
2068
|
+
+ * so a write into one of them is what brings the object back out of the settled path.
|
|
2069
|
+
+ *
|
|
2070
|
+
+ * @type {WeakMap<RenderObject,Map<BufferAttribute,number>>}
|
|
2071
|
+
+ */
|
|
2072
|
+
+ this._settledAttributes = new WeakMap();
|
|
2073
|
+
+
|
|
2074
|
+
}
|
|
2075
|
+
|
|
2076
|
+
/**
|
|
2077
|
+
@@ -164,10 +172,69 @@ class Geometries extends DataMap {
|
|
2078
|
+
|
|
2079
|
+
if ( this.has( renderObject ) === false ) this.initGeometry( renderObject );
|
|
2080
|
+
|
|
2081
|
+
+ // A settled static object has no attribute that could have changed since the last refresh,
|
|
2082
|
+
+ // unless a writer bumped one. An InstancedMesh's instance buffer is a vertex attribute of
|
|
2083
|
+
+ // this draw, and a BufferGeometry carries no version of its own to announce the write, so
|
|
2084
|
+
+ // the attributes' own versions are the signal - and the only one there is.
|
|
2085
|
+
+
|
|
2086
|
+
+ const monitor = renderObject.getMonitor();
|
|
2087
|
+
+
|
|
2088
|
+
+ if ( monitor !== undefined && monitor.isSettled( renderObject ) === true && this.attributesChanged( renderObject ) === false ) return;
|
|
2089
|
+
+
|
|
2090
|
+
this.updateAttributes( renderObject );
|
|
2091
|
+
|
|
2092
|
+
}
|
|
2093
|
+
|
|
2094
|
+
+ /**
|
|
2095
|
+
+ * Returns `true` if an attribute of the given render object - or its index - was written to
|
|
2096
|
+
+ * since the last time its versions were read, and records the versions it holds now.
|
|
2097
|
+
+ *
|
|
2098
|
+
+ * @param {RenderObject} renderObject - The render object.
|
|
2099
|
+
+ * @return {boolean} Whether any of the render object's versions moved.
|
|
2100
|
+
+ */
|
|
2101
|
+
+ attributesChanged( renderObject ) {
|
|
2102
|
+
+
|
|
2103
|
+
+ const attributes = renderObject.getAttributes();
|
|
2104
|
+
+ const index = this.getIndex( renderObject );
|
|
2105
|
+
+ const settled = this._settledAttributes.get( renderObject );
|
|
2106
|
+
+
|
|
2107
|
+
+ if ( settled !== undefined && settled.size === attributes.length + ( index === null ? 0 : 1 ) ) {
|
|
2108
|
+
+
|
|
2109
|
+
+ let changed = false;
|
|
2110
|
+
+
|
|
2111
|
+
+ for ( const attribute of attributes ) {
|
|
2112
|
+
+
|
|
2113
|
+
+ if ( settled.get( attribute.isInterleavedBufferAttribute ? attribute.data : attribute ) !== attribute.version ) {
|
|
2114
|
+
+
|
|
2115
|
+
+ changed = true;
|
|
2116
|
+
+ break;
|
|
2117
|
+
+
|
|
2118
|
+
+ }
|
|
2119
|
+
+
|
|
2120
|
+
+ }
|
|
2121
|
+
+
|
|
2122
|
+
+ if ( changed === false && ( index === null || settled.get( index ) === index.version ) ) return false;
|
|
2123
|
+
+
|
|
2124
|
+
+ }
|
|
2125
|
+
+
|
|
2126
|
+
+ const versions = new Map();
|
|
2127
|
+
+
|
|
2128
|
+
+ for ( const attribute of attributes ) {
|
|
2129
|
+
+
|
|
2130
|
+
+ const data = attribute.isInterleavedBufferAttribute ? attribute.data : attribute;
|
|
2131
|
+
+
|
|
2132
|
+
+ versions.set( data, data.version );
|
|
2133
|
+
+
|
|
2134
|
+
+ }
|
|
2135
|
+
+
|
|
2136
|
+
+ if ( index !== null ) versions.set( index, index.version );
|
|
2137
|
+
+
|
|
2138
|
+
+ this._settledAttributes.set( renderObject, versions );
|
|
2139
|
+
+
|
|
2140
|
+
+ return true;
|
|
2141
|
+
+
|
|
2142
|
+
+ }
|
|
2143
|
+
+
|
|
2144
|
+
/**
|
|
2145
|
+
* Initializes the geometry of the given render object.
|
|
2146
|
+
*
|
|
2147
|
+
diff --git a/src/renderers/common/RenderContexts.js b/src/renderers/common/RenderContexts.js
|
|
2148
|
+
index 64ecdee..7319e25 100644
|
|
2149
|
+
--- a/src/renderers/common/RenderContexts.js
|
|
2150
|
+
+++ b/src/renderers/common/RenderContexts.js
|
|
2151
|
+
@@ -77,7 +77,8 @@ class RenderContexts {
|
|
2152
|
+
|
|
2153
|
+
}
|
|
2154
|
+
|
|
2155
|
+
- if ( renderTarget !== null ) renderState.sampleCount = renderTarget.samples === 0 ? 1 : renderTarget.samples;
|
|
2156
|
+
+ const sampleCount = renderTarget !== null ? renderTarget.samples : this.renderer.currentSamples;
|
|
2157
|
+
+ renderState.sampleCount = sampleCount === 0 ? 1 : sampleCount;
|
|
2158
|
+
|
|
2159
|
+
renderState.clearDepthValue = this.renderer.getClearDepth();
|
|
2160
|
+
renderState.clearStencilValue = this.renderer.getClearStencil();
|
|
377
2161
|
diff --git a/src/renderers/common/RenderObject.js b/src/renderers/common/RenderObject.js
|
|
378
|
-
index
|
|
2162
|
+
index c900fa4..a3dcba2 100644
|
|
379
2163
|
--- a/src/renderers/common/RenderObject.js
|
|
380
2164
|
+++ b/src/renderers/common/RenderObject.js
|
|
381
2165
|
@@ -299,6 +299,18 @@ class RenderObject {
|
|
@@ -397,11 +2181,37 @@ index c900fa4c9654f8cf3512e96de52a2c1c83e189a6..d0a87eabffc8f667ebe9b2f0eccda0dd
|
|
|
397
2181
|
/**
|
|
398
2182
|
* An event listener which is defined by `RenderObjects`. It performs
|
|
399
2183
|
* clean up tasks when `dispose()` on this render object.
|
|
2184
|
+
@@ -486,7 +498,7 @@ class RenderObject {
|
|
2185
|
+
*/
|
|
2186
|
+
getChainArray() {
|
|
2187
|
+
|
|
2188
|
+
- return [ this.object, this.material, this.context, this.lightsNode ];
|
|
2189
|
+
+ return [ this.object, this.material, this.context, this.lightsNode, this._sourceMaterial ?? this.material ];
|
|
2190
|
+
|
|
2191
|
+
}
|
|
2192
|
+
|
|
2193
|
+
@@ -936,6 +948,7 @@ class RenderObject {
|
|
2194
|
+
}
|
|
2195
|
+
|
|
2196
|
+
cacheKey = hash( cacheKey, this.renderer.contextNode.id, this.renderer.contextNode.version );
|
|
2197
|
+
+ cacheKey = hash( cacheKey, this.context.sampleCount );
|
|
2198
|
+
|
|
2199
|
+
return cacheKey;
|
|
2200
|
+
|
|
400
2201
|
diff --git a/src/renderers/common/RenderObjects.js b/src/renderers/common/RenderObjects.js
|
|
401
|
-
index
|
|
2202
|
+
index 676cc2b..72569d0 100644
|
|
402
2203
|
--- a/src/renderers/common/RenderObjects.js
|
|
403
2204
|
+++ b/src/renderers/common/RenderObjects.js
|
|
404
|
-
@@ -
|
|
2205
|
+
@@ -97,6 +97,8 @@ class RenderObjects {
|
|
2206
|
+
_chainKeys[ 1 ] = material;
|
|
2207
|
+
_chainKeys[ 2 ] = renderContext;
|
|
2208
|
+
_chainKeys[ 3 ] = lightsNode;
|
|
2209
|
+
+ // Material groups share the shadow override, but must retain separate GPU bindings.
|
|
2210
|
+
+ _chainKeys[ 4 ] = this.renderer._currentSourceMaterial ?? material;
|
|
2211
|
+
|
|
2212
|
+
//
|
|
2213
|
+
|
|
2214
|
+
@@ -124,9 +126,18 @@ class RenderObjects {
|
|
405
2215
|
|
|
406
2216
|
}
|
|
407
2217
|
|
|
@@ -422,7 +2232,7 @@ index 676cc2bd4169427ed420f2467b1e6684bd2aeebc..0fa661db793548254a15ff43f39207ab
|
|
|
422
2232
|
|
|
423
2233
|
renderObject.dispose();
|
|
424
2234
|
|
|
425
|
-
@@ -136,6 +
|
|
2235
|
+
@@ -136,6 +147,8 @@ class RenderObjects {
|
|
426
2236
|
|
|
427
2237
|
renderObject.version = material.version;
|
|
428
2238
|
|
|
@@ -431,8 +2241,16 @@ index 676cc2bd4169427ed420f2467b1e6684bd2aeebc..0fa661db793548254a15ff43f39207ab
|
|
|
431
2241
|
}
|
|
432
2242
|
|
|
433
2243
|
}
|
|
2244
|
+
@@ -148,6 +161,7 @@ class RenderObjects {
|
|
2245
|
+
_chainKeys[ 1 ] = null;
|
|
2246
|
+
_chainKeys[ 2 ] = null;
|
|
2247
|
+
_chainKeys[ 3 ] = null;
|
|
2248
|
+
+ _chainKeys[ 4 ] = null;
|
|
2249
|
+
|
|
2250
|
+
//
|
|
2251
|
+
|
|
434
2252
|
diff --git a/src/renderers/common/Renderer.js b/src/renderers/common/Renderer.js
|
|
435
|
-
index
|
|
2253
|
+
index 8dfc2ae..89b33ef 100644
|
|
436
2254
|
--- a/src/renderers/common/Renderer.js
|
|
437
2255
|
+++ b/src/renderers/common/Renderer.js
|
|
438
2256
|
@@ -3550,6 +3550,7 @@ class Renderer {
|
|
@@ -476,8 +2294,162 @@ index 8dfc2ae055ad7b9d4c00e91f7d52a39e747caebe..e9d27b3ddad3b1f080a717d1f6683b60
|
|
|
476
2294
|
|
|
477
2295
|
}
|
|
478
2296
|
|
|
2297
|
+
@@ -3705,18 +3716,17 @@ class Renderer {
|
|
2298
|
+
|
|
2299
|
+
//
|
|
2300
|
+
|
|
2301
|
+
- const needsRefresh = this._nodes.needsRefresh( renderObject );
|
|
2302
|
+
+ this._nodes.needsRefresh( renderObject );
|
|
2303
|
+
|
|
2304
|
+
- if ( needsRefresh ) {
|
|
2305
|
+
-
|
|
2306
|
+
- this._nodes.updateBefore( renderObject );
|
|
2307
|
+
-
|
|
2308
|
+
- this._geometries.updateForRender( renderObject );
|
|
2309
|
+
-
|
|
2310
|
+
- this._nodes.updateForRender( renderObject );
|
|
2311
|
+
- this._bindings.updateForRender( renderObject );
|
|
2312
|
+
-
|
|
2313
|
+
- }
|
|
2314
|
+
+ // Every step of a draw runs, settled or not. What a settled static object skips is decided
|
|
2315
|
+
+ // inside each of them - its OBJECT node updates, its object uniform group write and its
|
|
2316
|
+
+ // attribute scan - because each of those answers for itself, and a step left out here
|
|
2317
|
+
+ // cannot: a group a node added after this object's first draw still has to be created, and
|
|
2318
|
+
+ // the pipeline reads that group's layout whatever this draw decided to do with it.
|
|
2319
|
+
+ this._geometries.updateForRender( renderObject );
|
|
2320
|
+
+ this._nodes.updateBefore( renderObject );
|
|
2321
|
+
+ this._nodes.updateForRender( renderObject );
|
|
2322
|
+
+ this._bindings.updateForRender( renderObject );
|
|
2323
|
+
|
|
2324
|
+
this._pipelines.updateForRender( renderObject );
|
|
2325
|
+
|
|
2326
|
+
@@ -3726,7 +3736,7 @@ class Renderer {
|
|
2327
|
+
|
|
2328
|
+
this.backend.draw( renderObject, this.info );
|
|
2329
|
+
|
|
2330
|
+
- if ( needsRefresh ) this._nodes.updateAfter( renderObject );
|
|
2331
|
+
+ this._nodes.updateAfter( renderObject );
|
|
2332
|
+
|
|
2333
|
+
}
|
|
2334
|
+
|
|
2335
|
+
diff --git a/src/renderers/common/Textures.js b/src/renderers/common/Textures.js
|
|
2336
|
+
index f42b90e..be47645 100644
|
|
2337
|
+
--- a/src/renderers/common/Textures.js
|
|
2338
|
+
+++ b/src/renderers/common/Textures.js
|
|
2339
|
+
@@ -215,6 +215,21 @@ class Textures extends DataMap {
|
|
2340
|
+
|
|
2341
|
+
backend.destroyTexture( texture );
|
|
2342
|
+
|
|
2343
|
+
+ // delete cached bind groups so they don't point to the destroyed texture
|
|
2344
|
+
+
|
|
2345
|
+
+ if ( textureData.bindGroups ) {
|
|
2346
|
+
+
|
|
2347
|
+
+ for ( const bindGroup of textureData.bindGroups ) {
|
|
2348
|
+
+
|
|
2349
|
+
+ const bindingsData = this.backend.get( bindGroup );
|
|
2350
|
+
+
|
|
2351
|
+
+ bindingsData.groups = undefined;
|
|
2352
|
+
+ bindingsData.versions = undefined;
|
|
2353
|
+
+
|
|
2354
|
+
+ }
|
|
2355
|
+
+
|
|
2356
|
+
+ }
|
|
2357
|
+
+
|
|
2358
|
+
}
|
|
2359
|
+
|
|
2360
|
+
//
|
|
2361
|
+
diff --git a/src/renderers/common/nodes/NodeManager.js b/src/renderers/common/nodes/NodeManager.js
|
|
2362
|
+
index f6d4f0b..033128c 100644
|
|
2363
|
+
--- a/src/renderers/common/nodes/NodeManager.js
|
|
2364
|
+
+++ b/src/renderers/common/nodes/NodeManager.js
|
|
2365
|
+
@@ -109,15 +109,17 @@ class NodeManager extends DataMap {
|
|
2366
|
+
* Returns `true` if the given node uniforms group must be updated or not.
|
|
2367
|
+
*
|
|
2368
|
+
* @param {NodeUniformsGroup} nodeUniformsGroup - The node uniforms group.
|
|
2369
|
+
+ * @param {boolean} [settled] - Whether the render object is a settled static object.
|
|
2370
|
+
* @return {boolean} Whether the node uniforms group requires an update or not.
|
|
2371
|
+
*/
|
|
2372
|
+
- updateGroup( nodeUniformsGroup ) {
|
|
2373
|
+
+ updateGroup( nodeUniformsGroup, settled ) {
|
|
2374
|
+
|
|
2375
|
+
const groupNode = nodeUniformsGroup.groupNode;
|
|
2376
|
+
|
|
2377
|
+
- // groups that are updated per object always require an update so no further checks are needed
|
|
2378
|
+
+ // groups that are updated per object always require an update so no further checks are needed.
|
|
2379
|
+
+ // A settled static object keeps the values it uploaded the last time it was refreshed.
|
|
2380
|
+
|
|
2381
|
+
- if ( groupNode.updateType === NodeUpdateType.OBJECT ) return true;
|
|
2382
|
+
+ if ( groupNode.updateType === NodeUpdateType.OBJECT ) return settled !== true;
|
|
2383
|
+
|
|
2384
|
+
// check for update
|
|
2385
|
+
|
|
2386
|
+
@@ -921,6 +923,21 @@ class NodeManager extends DataMap {
|
|
2387
|
+
|
|
2388
|
+
}
|
|
2389
|
+
|
|
2390
|
+
+ /**
|
|
2391
|
+
+ * Returns `true` if the per-object updates of the given render object can be skipped
|
|
2392
|
+
+ * because the object is static and nothing it depends on changed since its last refresh.
|
|
2393
|
+
+ *
|
|
2394
|
+
+ * @param {RenderObject} renderObject - The render object.
|
|
2395
|
+
+ * @return {boolean} Whether the render object is settled or not.
|
|
2396
|
+
+ */
|
|
2397
|
+
+ isSettled( renderObject ) {
|
|
2398
|
+
+
|
|
2399
|
+
+ const monitor = renderObject.getMonitor();
|
|
2400
|
+
+
|
|
2401
|
+
+ return monitor !== undefined ? monitor.isSettled( renderObject ) : false;
|
|
2402
|
+
+
|
|
2403
|
+
+ }
|
|
2404
|
+
+
|
|
2405
|
+
/**
|
|
2406
|
+
* Triggers the call of `updateBefore()` methods
|
|
2407
|
+
* for all nodes of the given render object.
|
|
2408
|
+
@@ -930,9 +947,14 @@ class NodeManager extends DataMap {
|
|
2409
|
+
updateBefore( renderObject ) {
|
|
2410
|
+
|
|
2411
|
+
const nodeBuilder = renderObject.getNodeBuilderState();
|
|
2412
|
+
+ const settled = this.isSettled( renderObject );
|
|
2413
|
+
|
|
2414
|
+
for ( const node of nodeBuilder.updateBeforeNodes ) {
|
|
2415
|
+
|
|
2416
|
+
+ // a settled static object has nothing to update per object
|
|
2417
|
+
+
|
|
2418
|
+
+ if ( settled === true && node.getUpdateBeforeType() === NodeUpdateType.OBJECT ) continue;
|
|
2419
|
+
+
|
|
2420
|
+
// update frame state for each node
|
|
2421
|
+
|
|
2422
|
+
this.getNodeFrameForRender( renderObject ).updateBeforeNode( node );
|
|
2423
|
+
@@ -950,10 +972,11 @@ class NodeManager extends DataMap {
|
|
2424
|
+
updateAfter( renderObject ) {
|
|
2425
|
+
|
|
2426
|
+
const nodeBuilder = renderObject.getNodeBuilderState();
|
|
2427
|
+
+ const settled = this.isSettled( renderObject );
|
|
2428
|
+
|
|
2429
|
+
for ( const node of nodeBuilder.updateAfterNodes ) {
|
|
2430
|
+
|
|
2431
|
+
- // update frame state for each node
|
|
2432
|
+
+ if ( settled === true && node.getUpdateAfterType() === NodeUpdateType.OBJECT ) continue;
|
|
2433
|
+
|
|
2434
|
+
this.getNodeFrameForRender( renderObject ).updateAfterNode( node );
|
|
2435
|
+
|
|
2436
|
+
@@ -990,9 +1013,14 @@ class NodeManager extends DataMap {
|
|
2437
|
+
|
|
2438
|
+
const nodeFrame = this.getNodeFrameForRender( renderObject );
|
|
2439
|
+
const nodeBuilder = renderObject.getNodeBuilderState();
|
|
2440
|
+
+ const settled = this.isSettled( renderObject );
|
|
2441
|
+
|
|
2442
|
+
for ( const node of nodeBuilder.updateNodes ) {
|
|
2443
|
+
|
|
2444
|
+
+ // a settled static object keeps the per-object values it uploaded last time
|
|
2445
|
+
+
|
|
2446
|
+
+ if ( settled === true && node.getUpdateType() === NodeUpdateType.OBJECT ) continue;
|
|
2447
|
+
+
|
|
2448
|
+
nodeFrame.updateNode( node );
|
|
2449
|
+
|
|
2450
|
+
}
|
|
479
2451
|
diff --git a/src/renderers/webgpu/WebGPUBackend.js b/src/renderers/webgpu/WebGPUBackend.js
|
|
480
|
-
index
|
|
2452
|
+
index 06e2036..69b8b67 100644
|
|
481
2453
|
--- a/src/renderers/webgpu/WebGPUBackend.js
|
|
482
2454
|
+++ b/src/renderers/webgpu/WebGPUBackend.js
|
|
483
2455
|
@@ -102,6 +102,14 @@ class WebGPUBackend extends Backend {
|
|
@@ -506,11 +2478,86 @@ index 06e203693ed9b168a2bddf773cd8ce8dc23196e4..37b5e0be09967e8acbd38e570b71eb20
|
|
|
506
2478
|
|
|
507
2479
|
if ( adapter === null ) {
|
|
508
2480
|
|
|
2481
|
+
@@ -1749,13 +1759,12 @@ class WebGPUBackend extends Backend {
|
|
2482
|
+
for ( let i = 0, l = bindings.length; i < l; i ++ ) {
|
|
2483
|
+
|
|
2484
|
+
const bindGroup = bindings[ i ];
|
|
2485
|
+
+ const bindingsData = this.get( bindGroup );
|
|
2486
|
+
|
|
2487
|
+
- if ( currentBindingGroups[ i ] !== bindGroup.id ) {
|
|
2488
|
+
-
|
|
2489
|
+
- const bindingsData = this.get( bindGroup );
|
|
2490
|
+
+ if ( currentBindingGroups[ i ] !== bindingsData.group ) {
|
|
2491
|
+
|
|
2492
|
+
passEncoderGPU.setBindGroup( i, bindingsData.group );
|
|
2493
|
+
- currentBindingGroups[ i ] = bindGroup.id;
|
|
2494
|
+
+ currentBindingGroups[ i ] = bindingsData.group;
|
|
2495
|
+
|
|
2496
|
+
}
|
|
2497
|
+
|
|
2498
|
+
@@ -2054,6 +2063,7 @@ class WebGPUBackend extends Backend {
|
|
2499
|
+
const utils = this.utils;
|
|
2500
|
+
|
|
2501
|
+
const sampleCount = utils.getSampleCountRenderContext( renderObject.context );
|
|
2502
|
+
+ const bindGroupLayoutKeys = renderObject.getBindings().map( bindGroup => this.get( bindGroup ).layoutKey ).join( '|' );
|
|
2503
|
+
const colorSpace = utils.getCurrentColorSpace( renderObject.context );
|
|
2504
|
+
const colorFormat = utils.getCurrentColorFormat( renderObject.context );
|
|
2505
|
+
const depthStencilFormat = utils.getCurrentDepthStencilFormat( renderObject.context );
|
|
2506
|
+
@@ -2075,7 +2085,8 @@ class WebGPUBackend extends Backend {
|
|
2507
|
+
data.colorFormat !== colorFormat || data.depthStencilFormat !== depthStencilFormat ||
|
|
2508
|
+
data.primitiveTopology !== primitiveTopology ||
|
|
2509
|
+
data.frontFaceCW !== frontFaceCW ||
|
|
2510
|
+
- data.clippingContextCacheKey !== renderObject.clippingContextCacheKey
|
|
2511
|
+
+ data.clippingContextCacheKey !== renderObject.clippingContextCacheKey ||
|
|
2512
|
+
+ data.bindGroupLayoutKeys !== bindGroupLayoutKeys
|
|
2513
|
+
) {
|
|
2514
|
+
|
|
2515
|
+
data.material = material; data.materialVersion = material.version;
|
|
2516
|
+
@@ -2095,6 +2106,7 @@ class WebGPUBackend extends Backend {
|
|
2517
|
+
data.primitiveTopology = primitiveTopology;
|
|
2518
|
+
data.frontFaceCW = frontFaceCW;
|
|
2519
|
+
data.clippingContextCacheKey = renderObject.clippingContextCacheKey;
|
|
2520
|
+
+ data.bindGroupLayoutKeys = bindGroupLayoutKeys;
|
|
2521
|
+
|
|
2522
|
+
needsUpdate = true;
|
|
2523
|
+
|
|
2524
|
+
@@ -2116,6 +2128,7 @@ class WebGPUBackend extends Backend {
|
|
2525
|
+
|
|
2526
|
+
const utils = this.utils;
|
|
2527
|
+
const renderContext = renderObject.context;
|
|
2528
|
+
+ const bindGroupLayoutKeys = renderObject.getBindings().map( bindGroup => this.get( bindGroup ).layoutKey ).join( '|' );
|
|
2529
|
+
|
|
2530
|
+
// meshes with negative scale have a different frontFace render pipeline
|
|
2531
|
+
// descriptor value so the following must be honored in the cache key
|
|
2532
|
+
@@ -2134,6 +2147,7 @@ class WebGPUBackend extends Backend {
|
|
2533
|
+
material.side,
|
|
2534
|
+
frontFaceCW,
|
|
2535
|
+
utils.getSampleCountRenderContext( renderContext ),
|
|
2536
|
+
+ bindGroupLayoutKeys,
|
|
2537
|
+
utils.getCurrentColorSpace( renderContext ), utils.getCurrentColorFormat( renderContext ), utils.getCurrentDepthStencilFormat( renderContext ),
|
|
2538
|
+
utils.getPrimitiveTopology( object, material ),
|
|
2539
|
+
renderObject.getGeometryCacheKey(),
|
|
509
2540
|
diff --git a/src/renderers/webgpu/nodes/WGSLNodeBuilder.js b/src/renderers/webgpu/nodes/WGSLNodeBuilder.js
|
|
510
|
-
index
|
|
2541
|
+
index be07536..2acc832 100644
|
|
511
2542
|
--- a/src/renderers/webgpu/nodes/WGSLNodeBuilder.js
|
|
512
2543
|
+++ b/src/renderers/webgpu/nodes/WGSLNodeBuilder.js
|
|
513
|
-
@@ -
|
|
2544
|
+
@@ -741,7 +741,14 @@ class WGSLNodeBuilder extends NodeBuilder {
|
|
2545
|
+
|
|
2546
|
+
} else {
|
|
2547
|
+
|
|
2548
|
+
- snippet = `textureLoad( ${ textureProperty }, ${ uvIndexSnippet }, u32( ${ levelSnippet } ) )`;
|
|
2549
|
+
+ // naga v25 (wgpu-native v25.0.2.2) rejects a u32 sample index on a
|
|
2550
|
+
+ // multisampled textureLoad; i32 is accepted by every backend. Mip-level
|
|
2551
|
+
+ // operands (including the array form above) stay u32.
|
|
2552
|
+
+ const sampleData = texture ? this.renderer?.backend?.utils?.getTextureSampleData?.( texture ) : null;
|
|
2553
|
+
+ const backendMultisampled = sampleData !== null && sampleData !== undefined && sampleData.primarySamples > 1;
|
|
2554
|
+
+ const targetMultisampled = Boolean( texture && texture.renderTarget && texture.renderTarget.samples > 1 );
|
|
2555
|
+
+ const indexType = ( backendMultisampled || targetMultisampled ) ? 'i32' : 'u32';
|
|
2556
|
+
+ snippet = `textureLoad( ${ textureProperty }, ${ uvIndexSnippet }, ${ indexType }( ${ levelSnippet } ) )`;
|
|
2557
|
+
|
|
2558
|
+
if ( this.renderer.backend.compatibilityMode && texture.isDepthTexture ) {
|
|
2559
|
+
|
|
2560
|
+
@@ -1323,7 +1330,8 @@ class WGSLNodeBuilder extends NodeBuilder {
|
|
514
2561
|
|
|
515
2562
|
uniformGPU = buffer;
|
|
516
2563
|
|
|
@@ -520,3 +2567,79 @@ index be07536301cd47ef2544e4a54f6abe17407f52af..6144b98ddeb5d53c5cf280bb51401385
|
|
|
520
2567
|
|
|
521
2568
|
} else {
|
|
522
2569
|
|
|
2570
|
+
@@ -2066,9 +2074,12 @@ ${ flowData.code }
|
|
2571
|
+
const textureNode = uniform.node;
|
|
2572
|
+
const texture = textureNode.value;
|
|
2573
|
+
|
|
2574
|
+
- // Cube textures always need samplers (they use textureSampleLevel, not textureLoad)
|
|
2575
|
+
- // Also textureGather always need sampler.
|
|
2576
|
+
- const needsSampler = texture.isCubeTexture === true || ( this.isUnfilterable( texture ) === false && textureNode.isStorageTextureNode !== true ) || textureNode.gatherNode !== null;
|
|
2577
|
+
+ // The bind group layout freezes sampler inclusion once, in getUniformFromNode
|
|
2578
|
+
+ // (nodeData.uniformGPU). Re-deriving it here from live texture filter state can
|
|
2579
|
+
+ // disagree with that layout and shift every later binding index.
|
|
2580
|
+
+ const nodeData = this.getDataFromNode( textureNode, shaderStage, this.globalCache );
|
|
2581
|
+
+ const uniformGPU = nodeData.uniformGPU;
|
|
2582
|
+
+ const needsSampler = Array.isArray( uniformGPU ) && uniformGPU[ 0 ].isSampler === true && uniformGPU[ 0 ].isSampledTexture !== true;
|
|
2583
|
+
|
|
2584
|
+
if ( needsSampler ) {
|
|
2585
|
+
|
|
2586
|
+
diff --git a/src/renderers/webgpu/utils/WebGPUAttributeUtils.js b/src/renderers/webgpu/utils/WebGPUAttributeUtils.js
|
|
2587
|
+
index f683d74..83bbe18 100644
|
|
2588
|
+
--- a/src/renderers/webgpu/utils/WebGPUAttributeUtils.js
|
|
2589
|
+
+++ b/src/renderers/webgpu/utils/WebGPUAttributeUtils.js
|
|
2590
|
+
@@ -361,9 +361,15 @@ class WebGPUAttributeUtils {
|
|
2591
|
+
destroyAttribute( attribute ) {
|
|
2592
|
+
|
|
2593
|
+
const backend = this.backend;
|
|
2594
|
+
- const data = backend.get( this._getBufferAttribute( attribute ) );
|
|
2595
|
+
+ const bufferAttribute = this._getBufferAttribute( attribute );
|
|
2596
|
+
+ const data = backend.get( bufferAttribute );
|
|
2597
|
+
+
|
|
2598
|
+
+ // An interleaved attribute's GPU buffer lives on its InterleavedBuffer, and several attributes
|
|
2599
|
+
+ // share it: destroy it once and forget it there too, as the WebGL backend does, or the next
|
|
2600
|
+
+ // createAttribute() finds the destroyed buffer and submits it (threenative patch).
|
|
2601
|
+
+ if ( data.buffer !== undefined ) data.buffer.destroy();
|
|
2602
|
+
|
|
2603
|
+
- data.buffer.destroy();
|
|
2604
|
+
+ if ( bufferAttribute !== attribute ) backend.delete( bufferAttribute );
|
|
2605
|
+
|
|
2606
|
+
backend.delete( attribute );
|
|
2607
|
+
|
|
2608
|
+
diff --git a/src/renderers/webgpu/utils/WebGPUBindingUtils.js b/src/renderers/webgpu/utils/WebGPUBindingUtils.js
|
|
2609
|
+
index da4bd47..a5da7ca 100644
|
|
2610
|
+
--- a/src/renderers/webgpu/utils/WebGPUBindingUtils.js
|
|
2611
|
+
+++ b/src/renderers/webgpu/utils/WebGPUBindingUtils.js
|
|
2612
|
+
@@ -97,18 +97,16 @@ class WebGPUBindingUtils {
|
|
2613
|
+
|
|
2614
|
+
const bindingsData = backend.get( bindGroup );
|
|
2615
|
+
|
|
2616
|
+
- // check if the the bind group already has a layout
|
|
2617
|
+
+ const entries = this._createLayoutEntries( bindGroup );
|
|
2618
|
+
+ const bindGroupLayoutKey = hashString( JSON.stringify( entries ) );
|
|
2619
|
+
|
|
2620
|
+
if ( bindingsData.layout ) {
|
|
2621
|
+
|
|
2622
|
+
- return bindingsData.layout.layoutGPU;
|
|
2623
|
+
-
|
|
2624
|
+
- }
|
|
2625
|
+
+ if ( bindingsData.layoutKey === bindGroupLayoutKey ) return bindingsData.layout.layoutGPU;
|
|
2626
|
+
|
|
2627
|
+
- // if not, assing one
|
|
2628
|
+
+ this.deleteBindGroupData( bindGroup );
|
|
2629
|
+
|
|
2630
|
+
- const entries = this._createLayoutEntries( bindGroup );
|
|
2631
|
+
- const bindGroupLayoutKey = hashString( JSON.stringify( entries ) );
|
|
2632
|
+
+ }
|
|
2633
|
+
|
|
2634
|
+
// try to find an existing layout in the cache
|
|
2635
|
+
|
|
2636
|
+
@@ -630,6 +628,9 @@ class WebGPUBindingUtils {
|
|
2637
|
+
|
|
2638
|
+
}
|
|
2639
|
+
|
|
2640
|
+
+ bindingsData.groups = [];
|
|
2641
|
+
+ bindingsData.versions = [];
|
|
2642
|
+
+
|
|
2643
|
+
}
|
|
2644
|
+
|
|
2645
|
+
/**
|