@threenative/core 0.3.3 → 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 +555 -21
- package/dist/{assets-CYKk2WTu.d.ts → assets-CqvE429w.d.ts} +45 -3
- package/dist/{canvas-layer-C1SnMoJ-.d.ts → canvas-layer-DDmC_VVF.d.ts} +1 -1
- package/dist/{game-D_6r-k4Y.d.ts → game-CljaDv4D.d.ts} +144 -10
- package/dist/{gpu-readback-CMklJs6r.d.ts → gpu-readback-CqJEfWNQ.d.ts} +16 -6
- package/dist/hot.d.ts +4 -4
- package/dist/index.d.ts +467 -35
- package/dist/index.js +3208 -257
- package/dist/playtest.d.ts +11 -5
- package/dist/playtest.js +55 -5
- package/dist/react.d.ts +2 -2
- package/dist/{renderer-Cy4qeBOA.d.ts → renderer-CfsS2hxi.d.ts} +190 -2
- package/dist/ui-layer.d.ts +5 -3
- 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 +54 -20
- package/mcp/servers.mjs +3 -3
- package/package.json +6 -6
- package/patches/three@0.185.1.patch +2101 -138
- 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..f7de72cffef2eb577abb0b90051a1839
|
|
|
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..f7de72cffef2eb577abb0b90051a1839
|
|
|
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..f7de72cffef2eb577abb0b90051a1839
|
|
|
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..f7de72cffef2eb577abb0b90051a1839
|
|
|
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..f7de72cffef2eb577abb0b90051a1839
|
|
|
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,7 @@ index 2e490796d4d4aa9479b04a5c46ddbf31cc05b1a1..f7de72cffef2eb577abb0b90051a1839
|
|
|
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
|
-
@@ -30098,7 +
|
|
375
|
+
@@ -30098,7 +30211,7 @@ class RenderObject {
|
|
109
376
|
*/
|
|
110
377
|
getChainArray() {
|
|
111
378
|
|
|
@@ -114,7 +381,15 @@ index 2e490796d4d4aa9479b04a5c46ddbf31cc05b1a1..f7de72cffef2eb577abb0b90051a1839
|
|
|
114
381
|
|
|
115
382
|
}
|
|
116
383
|
|
|
117
|
-
@@ -
|
|
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 {
|
|
118
393
|
_chainKeys$3[ 1 ] = material;
|
|
119
394
|
_chainKeys$3[ 2 ] = renderContext;
|
|
120
395
|
_chainKeys$3[ 3 ] = lightsNode;
|
|
@@ -123,7 +398,7 @@ index 2e490796d4d4aa9479b04a5c46ddbf31cc05b1a1..f7de72cffef2eb577abb0b90051a1839
|
|
|
123
398
|
|
|
124
399
|
//
|
|
125
400
|
|
|
126
|
-
@@ -30707,9 +
|
|
401
|
+
@@ -30707,9 +30823,18 @@ class RenderObjects {
|
|
127
402
|
|
|
128
403
|
}
|
|
129
404
|
|
|
@@ -144,7 +419,7 @@ index 2e490796d4d4aa9479b04a5c46ddbf31cc05b1a1..f7de72cffef2eb577abb0b90051a1839
|
|
|
144
419
|
|
|
145
420
|
renderObject.dispose();
|
|
146
421
|
|
|
147
|
-
@@ -30719,6 +
|
|
422
|
+
@@ -30719,6 +30844,8 @@ class RenderObjects {
|
|
148
423
|
|
|
149
424
|
renderObject.version = material.version;
|
|
150
425
|
|
|
@@ -153,7 +428,7 @@ index 2e490796d4d4aa9479b04a5c46ddbf31cc05b1a1..f7de72cffef2eb577abb0b90051a1839
|
|
|
153
428
|
}
|
|
154
429
|
|
|
155
430
|
}
|
|
156
|
-
@@ -30731,6 +
|
|
431
|
+
@@ -30731,6 +30858,7 @@ class RenderObjects {
|
|
157
432
|
_chainKeys$3[ 1 ] = null;
|
|
158
433
|
_chainKeys$3[ 2 ] = null;
|
|
159
434
|
_chainKeys$3[ 3 ] = null;
|
|
@@ -161,7 +436,187 @@ index 2e490796d4d4aa9479b04a5c46ddbf31cc05b1a1..f7de72cffef2eb577abb0b90051a1839
|
|
|
161
436
|
|
|
162
437
|
//
|
|
163
438
|
|
|
164
|
-
@@ -
|
|
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 {
|
|
165
620
|
|
|
166
621
|
const previousModelMatrix = getPreviousMatrix( object );
|
|
167
622
|
|
|
@@ -171,7 +626,7 @@ index 2e490796d4d4aa9479b04a5c46ddbf31cc05b1a1..f7de72cffef2eb577abb0b90051a1839
|
|
|
171
626
|
|
|
172
627
|
//
|
|
173
628
|
|
|
174
|
-
@@ -53841,7 +
|
|
629
|
+
@@ -53841,7 +54070,7 @@ class NodeBuilder {
|
|
175
630
|
|
|
176
631
|
const mrt = this.renderer.getMRT();
|
|
177
632
|
|
|
@@ -180,7 +635,93 @@ index 2e490796d4d4aa9479b04a5c46ddbf31cc05b1a1..f7de72cffef2eb577abb0b90051a1839
|
|
|
180
635
|
|
|
181
636
|
}
|
|
182
637
|
|
|
183
|
-
@@ -
|
|
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 {
|
|
184
725
|
let materialDisplacementMap;
|
|
185
726
|
let materialDisplacementScale;
|
|
186
727
|
let materialDisplacementBias;
|
|
@@ -188,7 +729,7 @@ index 2e490796d4d4aa9479b04a5c46ddbf31cc05b1a1..f7de72cffef2eb577abb0b90051a1839
|
|
|
188
729
|
|
|
189
730
|
const previousSourceMaterial = this._currentSourceMaterial;
|
|
190
731
|
|
|
191
|
-
@@ -62756,6 +
|
|
732
|
+
@@ -62756,6 +63014,7 @@ class Renderer {
|
|
192
733
|
materialDisplacementMap = overrideMaterial.displacementMap;
|
|
193
734
|
materialDisplacementScale = overrideMaterial.displacementScale;
|
|
194
735
|
materialDisplacementBias = overrideMaterial.displacementBias;
|
|
@@ -196,109 +737,1100 @@ index 2e490796d4d4aa9479b04a5c46ddbf31cc05b1a1..f7de72cffef2eb577abb0b90051a1839
|
|
|
196
737
|
|
|
197
738
|
if ( material.positionNode && material.positionNode.isNode ) {
|
|
198
739
|
|
|
199
|
-
@@ -62763,7 +
|
|
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;
|
|
1726
|
+
|
|
1727
|
+
- materialData._equal = false;
|
|
1728
|
+
return false;
|
|
1729
|
+
|
|
1730
|
+
}
|
|
1731
|
+
@@ -448,18 +535,12 @@ class NodeMaterialObserver {
|
|
1732
|
+
renderObjectData.bufferWidth = width;
|
|
1733
|
+
renderObjectData.bufferHeight = height;
|
|
1734
|
+
|
|
1735
|
+
- materialData._equal = false;
|
|
1736
|
+
return false;
|
|
1737
|
+
|
|
1738
|
+
}
|
|
200
1739
|
|
|
201
1740
|
}
|
|
202
1741
|
|
|
203
|
-
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
+ // `RenderObjects.get` and recomputes a full material cache key that always matches.
|
|
209
|
-
+ // Writing the backing field leaves the `alphaTest` getter — and so the cache key — still
|
|
210
|
-
+ // reporting this caster's value, while each render object tracks the staleness signal
|
|
211
|
-
+ // that belongs to it: its own source material's version.
|
|
212
|
-
+ overrideMaterial._alphaTest = material.alphaTest;
|
|
213
|
-
overrideMaterial.alphaMap = material.alphaMap;
|
|
214
|
-
overrideMaterial.displacementMap = material.displacementMap;
|
|
215
|
-
overrideMaterial.displacementScale = material.displacementScale;
|
|
216
|
-
@@ -62825,6 +62879,7 @@ class Renderer {
|
|
217
|
-
scene.overrideMaterial.displacementMap = materialDisplacementMap;
|
|
218
|
-
scene.overrideMaterial.displacementScale = materialDisplacementScale;
|
|
219
|
-
scene.overrideMaterial.displacementBias = materialDisplacementBias;
|
|
220
|
-
+ scene.overrideMaterial._alphaTest = materialAlphaTest;
|
|
1742
|
+
- materialData._equal = true;
|
|
1743
|
+
-
|
|
1744
|
+
- } else {
|
|
1745
|
+
-
|
|
1746
|
+
- if ( materialData._equal === false ) return false;
|
|
221
1747
|
|
|
222
1748
|
}
|
|
223
1749
|
|
|
224
|
-
@@ -
|
|
225
|
-
|
|
1750
|
+
@@ -502,7 +583,6 @@ class NodeMaterialObserver {
|
|
1751
|
+
// attribute was removed
|
|
1752
|
+
delete storedAttributes[ name ];
|
|
226
1753
|
|
|
227
|
-
|
|
1754
|
+
- geometryData._equal = false;
|
|
1755
|
+
return false;
|
|
228
1756
|
|
|
229
|
-
|
|
1757
|
+
}
|
|
1758
|
+
@@ -515,7 +595,6 @@ class NodeMaterialObserver {
|
|
1759
|
+
storedAttributeData.id = id;
|
|
1760
|
+
storedAttributeData.version = version;
|
|
230
1761
|
|
|
231
|
-
|
|
1762
|
+
- geometryData._equal = false;
|
|
1763
|
+
return false;
|
|
232
1764
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
+ // multisampled textureLoad; i32 is accepted by every backend. Mip-level
|
|
236
|
-
+ // operands (including the array form above) stay u32.
|
|
237
|
-
+ const sampleData = texture ? this.renderer?.backend?.utils?.getTextureSampleData?.( texture ) : null;
|
|
238
|
-
+ const backendMultisampled = sampleData !== null && sampleData !== undefined && sampleData.primarySamples > 1;
|
|
239
|
-
+ const targetMultisampled = Boolean( texture && texture.renderTarget && texture.renderTarget.samples > 1 );
|
|
240
|
-
+ const indexType = ( backendMultisampled || targetMultisampled ) ? 'i32' : 'u32';
|
|
241
|
-
+ snippet = `textureLoad( ${ textureProperty }, ${ uvIndexSnippet }, ${ indexType }( ${ levelSnippet } ) )`;
|
|
1765
|
+
}
|
|
1766
|
+
@@ -526,7 +605,6 @@ class NodeMaterialObserver {
|
|
242
1767
|
|
|
243
|
-
|
|
1768
|
+
geometryData.attributes = this.getAttributesData( attributes );
|
|
244
1769
|
|
|
245
|
-
|
|
1770
|
+
- geometryData._equal = false;
|
|
1771
|
+
return false;
|
|
246
1772
|
|
|
247
1773
|
}
|
|
248
|
-
@@ -
|
|
1774
|
+
@@ -544,7 +622,6 @@ class NodeMaterialObserver {
|
|
1775
|
+
geometryData.indexId = currentIndexId;
|
|
1776
|
+
geometryData.indexVersion = currentIndexVersion;
|
|
249
1777
|
|
|
250
|
-
|
|
1778
|
+
- geometryData._equal = false;
|
|
1779
|
+
return false;
|
|
251
1780
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
1781
|
+
}
|
|
1782
|
+
@@ -556,16 +633,10 @@ class NodeMaterialObserver {
|
|
1783
|
+
geometryData.drawRange.start = geometry.drawRange.start;
|
|
1784
|
+
geometryData.drawRange.count = geometry.drawRange.count;
|
|
255
1785
|
|
|
256
|
-
|
|
1786
|
+
- geometryData._equal = false;
|
|
1787
|
+
return false;
|
|
257
1788
|
|
|
258
|
-
|
|
259
|
-
const textureNode = uniform.node;
|
|
260
|
-
const texture = textureNode.value;
|
|
1789
|
+
}
|
|
261
1790
|
|
|
262
|
-
-
|
|
263
|
-
-
|
|
264
|
-
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
+ // disagree with that layout and shift every later binding index.
|
|
268
|
-
+ const nodeData = this.getDataFromNode( textureNode, shaderStage, this.globalCache );
|
|
269
|
-
+ const uniformGPU = nodeData.uniformGPU;
|
|
270
|
-
+ const needsSampler = Array.isArray( uniformGPU ) && uniformGPU[ 0 ].isSampler === true && uniformGPU[ 0 ].isSampledTexture !== true;
|
|
1791
|
+
- geometryData._equal = true;
|
|
1792
|
+
-
|
|
1793
|
+
- } else {
|
|
1794
|
+
-
|
|
1795
|
+
- if ( geometryData._equal === false ) return false;
|
|
271
1796
|
|
|
272
|
-
|
|
1797
|
+
}
|
|
273
1798
|
|
|
274
|
-
@@ -
|
|
275
|
-
|
|
276
|
-
|
|
1799
|
+
@@ -716,7 +787,14 @@ class NodeMaterialObserver {
|
|
1800
|
+
*/
|
|
1801
|
+
needsRefresh( renderObject, nodeFrame ) {
|
|
277
1802
|
|
|
278
|
-
|
|
279
|
-
+
|
|
280
|
-
+ *
|
|
281
|
-
+ * @type {?GPU}
|
|
282
|
-
+ * @default null
|
|
283
|
-
+ */
|
|
284
|
-
+ this.gpu = null;
|
|
1803
|
+
- if ( this.hasNode || this.hasAnimation || this.firstInitialization( renderObject ) || this.needsVelocity( nodeFrame.renderer ) )
|
|
1804
|
+
+ this.renderingVelocity = this.needsVelocity( nodeFrame.renderer );
|
|
285
1805
|
+
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
*
|
|
289
|
-
@@ -83926,7 +83993,9 @@ class WebGPUBackend extends Backend {
|
|
290
|
-
xrCompatible: renderer.xr.enabled
|
|
291
|
-
};
|
|
292
|
-
|
|
293
|
-
- const adapter = ( typeof navigator !== 'undefined' ) ? await navigator.gpu.requestAdapter( adapterOptions ) : null;
|
|
294
|
-
+ this.gpu = ( typeof navigator !== 'undefined' ) ? navigator.gpu : null;
|
|
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.
|
|
295
1808
|
+
|
|
296
|
-
+
|
|
1809
|
+
+ this.updateSettled( renderObject );
|
|
1810
|
+
+
|
|
1811
|
+
+ if ( this.hasNode || this.hasAnimation || this.firstInitialization( renderObject ) || this.renderingVelocity )
|
|
1812
|
+
return true;
|
|
297
1813
|
|
|
298
|
-
|
|
1814
|
+
const { renderId } = nodeFrame;
|
|
1815
|
+
@@ -729,10 +807,14 @@ class NodeMaterialObserver {
|
|
1816
|
+
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
|
+
- const isStatic = renderObject.object.static === true;
|
|
1820
|
+
const isBundle = renderObject.bundle !== null && renderObject.bundle.static === true && this.getRenderObjectData( renderObject ).version === renderObject.bundle.version;
|
|
299
1821
|
|
|
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;
|
|
1830
|
+
|
|
1831
|
+
const lightsData = this.getLights( renderObject.lightsNode, renderId );
|
|
300
1832
|
diff --git a/src/nodes/accessors/Batch.js b/src/nodes/accessors/Batch.js
|
|
301
|
-
index
|
|
1833
|
+
index ab62e98..2cf4bb6 100644
|
|
302
1834
|
--- a/src/nodes/accessors/Batch.js
|
|
303
1835
|
+++ b/src/nodes/accessors/Batch.js
|
|
304
1836
|
@@ -1,6 +1,6 @@
|
|
@@ -344,10 +1876,30 @@ index ab62e98dadab2539d7e1d663fd42267bbc8ebcaa..2cf4bb6039853d5c922e89041419250b
|
|
|
344
1876
|
-
|
|
345
1877
|
-
|
|
346
1878
|
diff --git a/src/nodes/accessors/Instance.js b/src/nodes/accessors/Instance.js
|
|
347
|
-
index
|
|
1879
|
+
index b89834b..ed00e75 100644
|
|
348
1880
|
--- a/src/nodes/accessors/Instance.js
|
|
349
1881
|
+++ b/src/nodes/accessors/Instance.js
|
|
350
|
-
@@ -
|
|
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
|
|
351
1903
|
|
|
352
1904
|
const previousInstanceData = _previousInstanceMatrices.get( object );
|
|
353
1905
|
|
|
@@ -358,7 +1910,7 @@ index b89834b9166c75fd946ade24db265bf8c367d244..c7b7bdd7a31d852a581d4bc75a1e1afc
|
|
|
358
1910
|
} );
|
|
359
1911
|
|
|
360
1912
|
diff --git a/src/nodes/accessors/Skinning.js b/src/nodes/accessors/Skinning.js
|
|
361
|
-
index
|
|
1913
|
+
index 2356aed..aaeea63 100644
|
|
362
1914
|
--- a/src/nodes/accessors/Skinning.js
|
|
363
1915
|
+++ b/src/nodes/accessors/Skinning.js
|
|
364
1916
|
@@ -149,7 +149,8 @@ export const skinning = /*@__PURE__*/ Fn( ( [ skinnedMesh ], builder ) => {
|
|
@@ -382,7 +1934,7 @@ index 2356aed8656dceb1ea74b73ea2e739371a960484..aaeea633a8ece291be5faf868e312867
|
|
|
382
1934
|
}
|
|
383
1935
|
|
|
384
1936
|
diff --git a/src/nodes/accessors/VelocityNode.js b/src/nodes/accessors/VelocityNode.js
|
|
385
|
-
index
|
|
1937
|
+
index 373548b..c48002a 100644
|
|
386
1938
|
--- a/src/nodes/accessors/VelocityNode.js
|
|
387
1939
|
+++ b/src/nodes/accessors/VelocityNode.js
|
|
388
1940
|
@@ -106,7 +106,8 @@ class VelocityNode extends TempNode {
|
|
@@ -396,7 +1948,7 @@ index 373548b0b587e7da3ef0993bfe316af420e7f66c..c48002abbce20ee8b29908cf314d7b03
|
|
|
396
1948
|
//
|
|
397
1949
|
|
|
398
1950
|
diff --git a/src/nodes/core/NodeBuilder.js b/src/nodes/core/NodeBuilder.js
|
|
399
|
-
index
|
|
1951
|
+
index be00b81..1990da8 100644
|
|
400
1952
|
--- a/src/nodes/core/NodeBuilder.js
|
|
401
1953
|
+++ b/src/nodes/core/NodeBuilder.js
|
|
402
1954
|
@@ -3450,7 +3450,7 @@ class NodeBuilder {
|
|
@@ -409,7 +1961,7 @@ index be00b81ef36e1af83b0066603e616f8f1f81a115..1990da8fe8f1f29a7b53030756dfd966
|
|
|
409
1961
|
}
|
|
410
1962
|
|
|
411
1963
|
diff --git a/src/objects/BatchedMesh.js b/src/objects/BatchedMesh.js
|
|
412
|
-
index
|
|
1964
|
+
index 2ef7cb2..35cc0ff 100644
|
|
413
1965
|
--- a/src/objects/BatchedMesh.js
|
|
414
1966
|
+++ b/src/objects/BatchedMesh.js
|
|
415
1967
|
@@ -181,6 +181,8 @@ function copyArrayContents( src, target ) {
|
|
@@ -422,9 +1974,18 @@ index 2ef7cb257438826059eb2408b3756c4891f06a10..35cc0ffe83a69ea9fa5fca6dec2d820c
|
|
|
422
1974
|
* Constructs a new batched mesh.
|
|
423
1975
|
*
|
|
424
1976
|
diff --git a/src/renderers/common/Bindings.js b/src/renderers/common/Bindings.js
|
|
425
|
-
index
|
|
1977
|
+
index 5f3d213..a3cec4e 100644
|
|
426
1978
|
--- a/src/renderers/common/Bindings.js
|
|
427
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
|
+
|
|
428
1989
|
@@ -206,10 +206,14 @@ class Bindings extends DataMap {
|
|
429
1990
|
|
|
430
1991
|
} else if ( binding.isSampledTexture ) {
|
|
@@ -440,8 +2001,165 @@ index 5f3d213a016b37db3b2e5d022da3dd0234d86f6d..7a9a168f29773f37ef9a51ef9f07ade9
|
|
|
440
2001
|
this.textures.updateSampler( binding );
|
|
441
2002
|
|
|
442
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();
|
|
443
2161
|
diff --git a/src/renderers/common/RenderObject.js b/src/renderers/common/RenderObject.js
|
|
444
|
-
index
|
|
2162
|
+
index c900fa4..a3dcba2 100644
|
|
445
2163
|
--- a/src/renderers/common/RenderObject.js
|
|
446
2164
|
+++ b/src/renderers/common/RenderObject.js
|
|
447
2165
|
@@ -299,6 +299,18 @@ class RenderObject {
|
|
@@ -472,8 +2190,16 @@ index c900fa4c9654f8cf3512e96de52a2c1c83e189a6..3a8b0012abf054f50ab66466bab7efa1
|
|
|
472
2190
|
|
|
473
2191
|
}
|
|
474
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
|
+
|
|
475
2201
|
diff --git a/src/renderers/common/RenderObjects.js b/src/renderers/common/RenderObjects.js
|
|
476
|
-
index
|
|
2202
|
+
index 676cc2b..72569d0 100644
|
|
477
2203
|
--- a/src/renderers/common/RenderObjects.js
|
|
478
2204
|
+++ b/src/renderers/common/RenderObjects.js
|
|
479
2205
|
@@ -97,6 +97,8 @@ class RenderObjects {
|
|
@@ -524,7 +2250,7 @@ index 676cc2bd4169427ed420f2467b1e6684bd2aeebc..72569d0339314d26eb7a35ff2a8462a8
|
|
|
524
2250
|
//
|
|
525
2251
|
|
|
526
2252
|
diff --git a/src/renderers/common/Renderer.js b/src/renderers/common/Renderer.js
|
|
527
|
-
index
|
|
2253
|
+
index 8dfc2ae..89b33ef 100644
|
|
528
2254
|
--- a/src/renderers/common/Renderer.js
|
|
529
2255
|
+++ b/src/renderers/common/Renderer.js
|
|
530
2256
|
@@ -3550,6 +3550,7 @@ class Renderer {
|
|
@@ -568,8 +2294,162 @@ index 8dfc2ae055ad7b9d4c00e91f7d52a39e747caebe..e9d27b3ddad3b1f080a717d1f6683b60
|
|
|
568
2294
|
|
|
569
2295
|
}
|
|
570
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
|
+
}
|
|
571
2451
|
diff --git a/src/renderers/webgpu/WebGPUBackend.js b/src/renderers/webgpu/WebGPUBackend.js
|
|
572
|
-
index
|
|
2452
|
+
index 06e2036..69b8b67 100644
|
|
573
2453
|
--- a/src/renderers/webgpu/WebGPUBackend.js
|
|
574
2454
|
+++ b/src/renderers/webgpu/WebGPUBackend.js
|
|
575
2455
|
@@ -102,6 +102,14 @@ class WebGPUBackend extends Backend {
|
|
@@ -598,16 +2478,70 @@ index 06e203693ed9b168a2bddf773cd8ce8dc23196e4..37b5e0be09967e8acbd38e570b71eb20
|
|
|
598
2478
|
|
|
599
2479
|
if ( adapter === null ) {
|
|
600
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(),
|
|
601
2540
|
diff --git a/src/renderers/webgpu/nodes/WGSLNodeBuilder.js b/src/renderers/webgpu/nodes/WGSLNodeBuilder.js
|
|
602
|
-
index
|
|
2541
|
+
index be07536..2acc832 100644
|
|
603
2542
|
--- a/src/renderers/webgpu/nodes/WGSLNodeBuilder.js
|
|
604
2543
|
+++ b/src/renderers/webgpu/nodes/WGSLNodeBuilder.js
|
|
605
|
-
@@ -
|
|
606
|
-
let snippet;
|
|
607
|
-
|
|
608
|
-
if ( depthSnippet ) {
|
|
609
|
-
|
|
610
|
-
snippet = `textureLoad( ${ textureProperty }, ${ uvIndexSnippet }, ${ depthSnippet }, u32( ${ levelSnippet } ) )`;
|
|
2544
|
+
@@ -741,7 +741,14 @@ class WGSLNodeBuilder extends NodeBuilder {
|
|
611
2545
|
|
|
612
2546
|
} else {
|
|
613
2547
|
|
|
@@ -623,10 +2557,7 @@ index be07536301cd47ef2544e4a54f6abe17407f52af..3b064a25a73fb35bcaed653794bda420
|
|
|
623
2557
|
|
|
624
2558
|
if ( this.renderer.backend.compatibilityMode && texture.isDepthTexture ) {
|
|
625
2559
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
}
|
|
629
|
-
@@ -1323,7 +1323,8 @@ class WGSLNodeBuilder extends NodeBuilder {
|
|
2560
|
+
@@ -1323,7 +1330,8 @@ class WGSLNodeBuilder extends NodeBuilder {
|
|
630
2561
|
|
|
631
2562
|
uniformGPU = buffer;
|
|
632
2563
|
|
|
@@ -636,7 +2567,7 @@ index be07536301cd47ef2544e4a54f6abe17407f52af..3b064a25a73fb35bcaed653794bda420
|
|
|
636
2567
|
|
|
637
2568
|
} else {
|
|
638
2569
|
|
|
639
|
-
@@ -2066,9 +
|
|
2570
|
+
@@ -2066,9 +2074,12 @@ ${ flowData.code }
|
|
640
2571
|
const textureNode = uniform.node;
|
|
641
2572
|
const texture = textureNode.value;
|
|
642
2573
|
|
|
@@ -652,31 +2583,63 @@ index be07536301cd47ef2544e4a54f6abe17407f52af..3b064a25a73fb35bcaed653794bda420
|
|
|
652
2583
|
|
|
653
2584
|
if ( needsSampler ) {
|
|
654
2585
|
|
|
655
|
-
diff --git a/
|
|
656
|
-
index
|
|
657
|
-
--- a/
|
|
658
|
-
+++ b/
|
|
659
|
-
@@ -
|
|
660
|
-
|
|
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();
|
|
661
2602
|
|
|
662
|
-
|
|
2603
|
+
- data.buffer.destroy();
|
|
2604
|
+
+ if ( bufferAttribute !== attribute ) backend.delete( bufferAttribute );
|
|
663
2605
|
|
|
664
|
-
|
|
2606
|
+
backend.delete( attribute );
|
|
665
2607
|
|
|
666
|
-
|
|
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 {
|
|
667
2613
|
|
|
668
|
-
|
|
669
|
-
+ // naga v25 (wgpu-native v25.0.2.2) rejects a u32 sample index on a
|
|
670
|
-
+ // multisampled textureLoad; i32 is accepted by every backend. Mip-level
|
|
671
|
-
+ // operands (including the array form above) stay u32.
|
|
672
|
-
+ const sampleData = texture ? this.renderer?.backend?.utils?.getTextureSampleData?.( texture ) : null;
|
|
673
|
-
+ const backendMultisampled = sampleData !== null && sampleData !== undefined && sampleData.primarySamples > 1;
|
|
674
|
-
+ const targetMultisampled = Boolean( texture && texture.renderTarget && texture.renderTarget.samples > 1 );
|
|
675
|
-
+ const indexType = ( backendMultisampled || targetMultisampled ) ? 'i32' : 'u32';
|
|
676
|
-
+ snippet = `textureLoad( ${ textureProperty }, ${ uvIndexSnippet }, ${ indexType }( ${ levelSnippet } ) )`;
|
|
2614
|
+
const bindingsData = backend.get( bindGroup );
|
|
677
2615
|
|
|
678
|
-
|
|
2616
|
+
- // check if the the bind group already has a layout
|
|
2617
|
+
+ const entries = this._createLayoutEntries( bindGroup );
|
|
2618
|
+
+ const bindGroupLayoutKey = hashString( JSON.stringify( entries ) );
|
|
679
2619
|
|
|
680
|
-
|
|
2620
|
+
if ( bindingsData.layout ) {
|
|
681
2621
|
|
|
682
|
-
|
|
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
|
+
/**
|