@gnsx/three 0.186.3 → 0.186.5

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.
Files changed (34) hide show
  1. package/build/three.webgpu.js +1326 -18
  2. package/build/three.webgpu.nodes.js +1326 -18
  3. package/examples/jsm/inspector/tabs/Settings.js +97 -0
  4. package/package.json +1 -1
  5. package/src/materials/nodes/MeshBasicNodeMaterial.js +13 -0
  6. package/src/materials/nodes/MeshLambertNodeMaterial.js +13 -0
  7. package/src/materials/nodes/MeshMatcapNodeMaterial.js +13 -0
  8. package/src/materials/nodes/MeshPhongNodeMaterial.js +13 -0
  9. package/src/materials/nodes/MeshPhysicalNodeMaterial.js +25 -0
  10. package/src/materials/nodes/MeshSSSNodeMaterial.js +15 -0
  11. package/src/materials/nodes/MeshStandardNodeMaterial.js +13 -0
  12. package/src/materials/nodes/MeshToonNodeMaterial.js +13 -0
  13. package/src/materials/nodes/NodeMaterial.js +106 -1
  14. package/src/materials/nodes/ShadowNodeMaterial.js +13 -0
  15. package/src/materials/nodes/VolumeNodeMaterial.js +13 -0
  16. package/src/nodes/core/NodeBuilder.js +74 -0
  17. package/src/nodes/display/BufferDebug.js +143 -0
  18. package/src/nodes/display/ComplexityDebug.js +400 -0
  19. package/src/nodes/display/DrawCallDebug.js +118 -0
  20. package/src/nodes/display/LightingDebug.js +108 -0
  21. package/src/nodes/lighting/LightsNode.js +71 -2
  22. package/src/nodes/lighting/PointLightNode.js +4 -1
  23. package/src/nodes/lighting/RectAreaLightNode.js +14 -0
  24. package/src/nodes/lighting/SpotLightNode.js +10 -2
  25. package/src/renderers/common/Background.js +20 -1
  26. package/src/renderers/common/RenderObject.js +5 -0
  27. package/src/renderers/common/Renderer.js +27 -1
  28. package/src/renderers/common/nodes/NodeManager.js +32 -7
  29. package/src/renderers/webgl-fallback/nodes/GLSLNodeBuilder.js +8 -0
  30. package/src/renderers/webgl-fallback/utils/WebGLState.js +22 -3
  31. package/src/renderers/webgpu/WebGPUBackend.js +14 -2
  32. package/src/renderers/webgpu/nodes/WGSLNodeBuilder.js +16 -0
  33. package/src/renderers/webgpu/utils/WebGPUPipelineUtils.js +23 -0
  34. package/build/three.cjs +0 -17
@@ -134,10 +134,86 @@ class Settings extends Parameters {
134
134
 
135
135
  } ).info( 'Shows how many times each pixel is shaded.' );
136
136
 
137
+ // WITH_GENESYS
138
+ // Values match Renderer.debug.view. Not stored with inspector settings.
139
+ const debugViewState = { view: 'none' };
140
+ const bufferState = { buffer: 'baseColor' };
141
+
142
+ this._debugViewState = debugViewState;
143
+ this._bufferState = bufferState;
144
+ this._debugViewControl = modesGroup.add( debugViewState, 'view', {
145
+ 'Shaded': 'none',
146
+ 'Shader Complexity': 'shaderComplexity',
147
+ 'Lighting Complexity': 'lightingComplexity',
148
+ 'Quad Overdraw': 'quadOverdraw',
149
+ 'Shader Complexity & Quads': 'shaderComplexityAndQuads',
150
+ 'Buffer Visualization': 'bufferVisualization',
151
+ 'Lighting Only': 'lightingOnly',
152
+ 'Detail Lighting': 'detailLighting',
153
+ 'Draw Call': 'drawCall'
154
+ } ).name( 'Debug View' ).onChange( ( view ) => {
155
+
156
+ const renderer = this.inspector.getRenderer();
157
+
158
+ if ( renderer === null || renderer.debug === undefined ) return;
159
+
160
+ renderer.debug.view = view;
161
+ this._syncBufferRow( view );
162
+ window.dispatchEvent( new Event( 'genesys-debug-view' ) );
163
+
164
+ } ).info( 'Shaded color, a cost heatmap, per-pixel overdraw, shader cost times overdraw, one material channel, lighting on a flat gray surface, or one lit color per draw. The Overdraw toggle above draws on top when both are enabled.' );
165
+
166
+ this._bufferControl = modesGroup.add( bufferState, 'buffer', {
167
+ 'Base Color': 'baseColor',
168
+ 'World Normal': 'worldNormal',
169
+ 'Roughness': 'roughness',
170
+ 'Metallic': 'metallic',
171
+ 'Ambient Occlusion': 'ambientOcclusion',
172
+ 'Emissive': 'emissive'
173
+ } ).name( 'Buffer' ).onChange( ( buffer ) => {
174
+
175
+ const renderer = this.inspector.getRenderer();
176
+
177
+ if ( renderer === null || renderer.debug === undefined ) return;
178
+
179
+ renderer.debug.buffer = buffer;
180
+ window.dispatchEvent( new Event( 'genesys-debug-view' ) );
181
+
182
+ } ).info( 'Channel drawn by Buffer Visualization. Base color, world normal, roughness, metallic, ambient occlusion, or emissive.' );
183
+
184
+ this._bufferControl.hide();
185
+ // !WITH_GENESYS
186
+
137
187
  }
138
188
 
139
189
  init() {
140
190
 
191
+ // WITH_GENESYS
192
+ const renderer = this.inspector.getRenderer();
193
+ const view = renderer !== null && renderer.debug !== undefined ? renderer.debug.view : null;
194
+
195
+ if ( view === 'none' || view === 'shaderComplexity' || view === 'lightingComplexity' || view === 'quadOverdraw' || view === 'shaderComplexityAndQuads' || view === 'bufferVisualization' || view === 'lightingOnly' || view === 'detailLighting' || view === 'drawCall' ) {
196
+
197
+ if ( this._debugViewState.view !== view ) {
198
+
199
+ this._debugViewControl.setValue( view );
200
+
201
+ }
202
+
203
+ this._syncBufferRow( view );
204
+
205
+ }
206
+
207
+ const buffer = renderer !== null && renderer.debug !== undefined ? renderer.debug.buffer : null;
208
+ const buffers = [ 'baseColor', 'worldNormal', 'roughness', 'metallic', 'ambientOcclusion', 'emissive' ];
209
+
210
+ if ( buffers.includes( buffer ) && this._bufferState.buffer !== buffer ) {
211
+
212
+ this._bufferControl.setValue( buffer );
213
+
214
+ }
215
+ // !WITH_GENESYS
216
+
141
217
  const extensionsGroup = this.createGroup( 'Extensions' );
142
218
 
143
219
  const storageGroup = this.createGroup( 'Storage' );
@@ -221,6 +297,27 @@ Shares the same state across any page within the current origin.` );
221
297
 
222
298
  }
223
299
 
300
+ // WITH_GENESYS
301
+ /**
302
+ * Shows the buffer channel row only while buffer visualization is the debug view.
303
+ *
304
+ * @param {string} view - `renderer.debug.view`.
305
+ */
306
+ _syncBufferRow( view ) {
307
+
308
+ if ( view === 'bufferVisualization' ) {
309
+
310
+ this._bufferControl.show();
311
+
312
+ } else {
313
+
314
+ this._bufferControl.hide();
315
+
316
+ }
317
+
318
+ }
319
+ // !WITH_GENESYS
320
+
224
321
  async setActiveExtension( name, value ) {
225
322
 
226
323
  const extension = this.extensions[ name ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gnsx/three",
3
- "version": "0.186.3",
3
+ "version": "0.186.5",
4
4
  "description": "JavaScript 3D library",
5
5
  "type": "module",
6
6
  "main": "./build/three.cjs",
@@ -118,6 +118,19 @@ class MeshBasicNodeMaterial extends NodeMaterial {
118
118
 
119
119
  }
120
120
 
121
+ // WITH_GENESYS
122
+ /**
123
+ * Shader-complexity proxy for {@link BasicLightingModel}.
124
+ *
125
+ * @return {number} The base cost, excluding texture samples.
126
+ */
127
+ getShaderComplexity() {
128
+
129
+ return 80;
130
+
131
+ }
132
+ // !WITH_GENESYS
133
+
121
134
  /**
122
135
  * Setups the lighting model.
123
136
  *
@@ -66,6 +66,19 @@ class MeshLambertNodeMaterial extends NodeMaterial {
66
66
 
67
67
  }
68
68
 
69
+ // WITH_GENESYS
70
+ /**
71
+ * Shader-complexity proxy for Lambert. Specular is forced off.
72
+ *
73
+ * @return {number} The base cost, excluding texture samples.
74
+ */
75
+ getShaderComplexity() {
76
+
77
+ return 140;
78
+
79
+ }
80
+ // !WITH_GENESYS
81
+
69
82
  /**
70
83
  * Setups the lighting model.
71
84
  *
@@ -46,6 +46,19 @@ class MeshMatcapNodeMaterial extends NodeMaterial {
46
46
 
47
47
  }
48
48
 
49
+ // WITH_GENESYS
50
+ /**
51
+ * Shader-complexity proxy for matcap UV shading.
52
+ *
53
+ * @return {number} The base cost, excluding texture samples.
54
+ */
55
+ getShaderComplexity() {
56
+
57
+ return 50;
58
+
59
+ }
60
+ // !WITH_GENESYS
61
+
49
62
  /**
50
63
  * Setups the matcap specific node variables.
51
64
  *
@@ -95,6 +95,19 @@ class MeshPhongNodeMaterial extends NodeMaterial {
95
95
 
96
96
  }
97
97
 
98
+ // WITH_GENESYS
99
+ /**
100
+ * Shader-complexity proxy for Blinn-Phong.
101
+ *
102
+ * @return {number} The base cost, excluding texture samples.
103
+ */
104
+ getShaderComplexity() {
105
+
106
+ return 200;
107
+
108
+ }
109
+ // !WITH_GENESYS
110
+
98
111
  /**
99
112
  * Setups the lighting model.
100
113
  *
@@ -383,6 +383,31 @@ class MeshPhysicalNodeMaterial extends MeshStandardNodeMaterial {
383
383
 
384
384
  }
385
385
 
386
+ // WITH_GENESYS
387
+ /**
388
+ * GGX base from {@link MeshStandardNodeMaterial}, plus each enabled physical feature.
389
+ *
390
+ * @return {number} The base cost, excluding texture samples.
391
+ */
392
+ getShaderComplexity() {
393
+
394
+ let cost = super.getShaderComplexity();
395
+
396
+ if ( this.useClearcoat === true ) cost += 30;
397
+
398
+ if ( this.useSheen === true ) cost += 30;
399
+
400
+ if ( this.useIridescence === true ) cost += 30;
401
+
402
+ if ( this.useAnisotropy === true ) cost += 30;
403
+
404
+ if ( this.useTransmission === true ) cost += 60;
405
+
406
+ return cost;
407
+
408
+ }
409
+ // !WITH_GENESYS
410
+
386
411
  /**
387
412
  * Setups the lighting model.
388
413
  *
@@ -146,6 +146,21 @@ class MeshSSSNodeMaterial extends MeshPhysicalNodeMaterial {
146
146
 
147
147
  }
148
148
 
149
+ // WITH_GENESYS
150
+ /**
151
+ * Physical complexity, plus the subsurface term when it is evaluated.
152
+ *
153
+ * @return {number} The base cost, excluding texture samples.
154
+ */
155
+ getShaderComplexity() {
156
+
157
+ const cost = super.getShaderComplexity();
158
+
159
+ return this.useSSS === true ? cost + 40 : cost;
160
+
161
+ }
162
+ // !WITH_GENESYS
163
+
149
164
  /**
150
165
  * Setups the lighting model.
151
166
  *
@@ -117,6 +117,19 @@ class MeshStandardNodeMaterial extends NodeMaterial {
117
117
 
118
118
  }
119
119
 
120
+ // WITH_GENESYS
121
+ /**
122
+ * Shader-complexity proxy for the Cook-Torrance GGX model.
123
+ *
124
+ * @return {number} The base cost, excluding texture samples.
125
+ */
126
+ getShaderComplexity() {
127
+
128
+ return 240;
129
+
130
+ }
131
+ // !WITH_GENESYS
132
+
120
133
  /**
121
134
  * Setups the lighting model.
122
135
  *
@@ -50,6 +50,19 @@ class MeshToonNodeMaterial extends NodeMaterial {
50
50
 
51
51
  }
52
52
 
53
+ // WITH_GENESYS
54
+ /**
55
+ * Shader-complexity proxy for toon banding.
56
+ *
57
+ * @return {number} The base cost, excluding texture samples.
58
+ */
59
+ getShaderComplexity() {
60
+
61
+ return 170;
62
+
63
+ }
64
+ // !WITH_GENESYS
65
+
53
66
  /**
54
67
  * Setups the lighting model.
55
68
  *
@@ -25,6 +25,12 @@ import { modelViewMatrix } from '../../nodes/accessors/ModelNode.js';
25
25
  import { vertexColor } from '../../nodes/accessors/VertexColorNode.js';
26
26
  import { premultiplyAlpha } from '../../nodes/display/PremultiplyAlphaFunctions.js';
27
27
  import { subBuild } from '../../nodes/core/SubBuildNode.js';
28
+ // WITH_GENESYS
29
+ import { DEBUG_VIEW_LIGHTING_COMPLEXITY, DEBUG_VIEW_QUAD_OVERDRAW, DEBUG_VIEW_SHADER_COMPLEXITY, DEBUG_VIEW_SHADER_COMPLEXITY_AND_QUADS, quadOverdrawOutput, shaderComplexityAndQuadsOutput, shaderComplexityOutput } from '../../nodes/display/ComplexityDebug.js';
30
+ import { DEBUG_VIEW_BUFFER, assignBufferDefaults, bufferVisualizationOutput } from '../../nodes/display/BufferDebug.js';
31
+ import { DEBUG_VIEW_DETAIL_LIGHTING, DEBUG_VIEW_LIGHTING_ONLY, applyLightingDebug, lightingOnlyNormal } from '../../nodes/display/LightingDebug.js';
32
+ import { DEBUG_VIEW_DRAW_CALL, applyDrawCallDebug } from '../../nodes/display/DrawCallDebug.js';
33
+ // !WITH_GENESYS
28
34
 
29
35
  /**
30
36
  * Base class for all node materials.
@@ -524,10 +530,33 @@ class NodeMaterial extends Material {
524
530
 
525
531
  if ( this.fragmentNode === null ) {
526
532
 
533
+ // WITH_GENESYS
534
+ // Fallback for materials that never write the channel. Lit materials overwrite these.
535
+ if ( renderer.debug.view === DEBUG_VIEW_BUFFER && this.isShadowPassMaterial !== true ) {
536
+
537
+ assignBufferDefaults( renderer.debug.buffer );
538
+
539
+ }
540
+ // !WITH_GENESYS
541
+
527
542
  this.setupDiffuseColor( builder );
528
543
  this.setupAmbientOcclusion( builder );
529
544
  this.setupVariants( builder );
530
545
 
546
+ // WITH_GENESYS
547
+ // After the material writes its channels, so roughness and normals already exist
548
+ // for detail lighting and can be replaced for lighting only.
549
+ if ( ( renderer.debug.view === DEBUG_VIEW_LIGHTING_ONLY || renderer.debug.view === DEBUG_VIEW_DETAIL_LIGHTING ) && this.isShadowPassMaterial !== true ) {
550
+
551
+ applyLightingDebug( builder, renderer.debug.view );
552
+
553
+ } else if ( renderer.debug.view === DEBUG_VIEW_DRAW_CALL && this.isShadowPassMaterial !== true ) {
554
+
555
+ applyDrawCallDebug( builder );
556
+
557
+ }
558
+ // !WITH_GENESYS
559
+
531
560
  const outgoingLightNode = this.setupLighting( builder );
532
561
 
533
562
  if ( clippingNode !== null ) builder.stack.addToStack( clippingNode );
@@ -597,6 +626,32 @@ class NodeMaterial extends Material {
597
626
 
598
627
  }
599
628
 
629
+ // WITH_GENESYS
630
+ // The screen output pass colorizes the accumulated ratio. Wrapping that pass
631
+ // would replace the heatmap with this material's own constant cost.
632
+ if ( renderer.debug.view === DEBUG_VIEW_SHADER_COMPLEXITY && this.isShadowPassMaterial !== true && renderer.isOutputTarget !== true ) {
633
+
634
+ builder.shaderComplexityBase = this.getShaderComplexity();
635
+ resultNode = shaderComplexityOutput( resultNode );
636
+
637
+ } else if ( renderer.debug.view === DEBUG_VIEW_SHADER_COMPLEXITY_AND_QUADS && this.isShadowPassMaterial !== true && renderer.isOutputTarget !== true ) {
638
+
639
+ builder.shaderComplexityBase = this.getShaderComplexity();
640
+ resultNode = shaderComplexityAndQuadsOutput( resultNode );
641
+
642
+ } else if ( renderer.debug.view === DEBUG_VIEW_QUAD_OVERDRAW && this.isShadowPassMaterial !== true && renderer.isOutputTarget !== true ) {
643
+
644
+ resultNode = quadOverdrawOutput( resultNode );
645
+
646
+ } else if ( renderer.debug.view === DEBUG_VIEW_BUFFER && this.isShadowPassMaterial !== true && this.fragmentNode === null ) {
647
+
648
+ // The fullscreen output pass sets fragmentNode. Scene materials do not, including
649
+ // when the scene is drawn straight to the canvas.
650
+ resultNode = bufferVisualizationOutput( renderer.debug.buffer );
651
+
652
+ }
653
+ // !WITH_GENESYS
654
+
600
655
  builder.stack.outputNode = resultNode;
601
656
 
602
657
  builder.addFlow( 'fragment', builder.removeStack() );
@@ -607,6 +662,21 @@ class NodeMaterial extends Material {
607
662
 
608
663
  }
609
664
 
665
+ // WITH_GENESYS
666
+ /**
667
+ * Static shader-complexity proxy for this material's lighting model.
668
+ * Fragment texture samples are counted later, while the shader is generated.
669
+ * Derived materials override this instead of registering a type flag.
670
+ *
671
+ * @return {number} The base cost, excluding texture samples.
672
+ */
673
+ getShaderComplexity() {
674
+
675
+ return this.lights === true ? 40 : 20;
676
+
677
+ }
678
+ // !WITH_GENESYS
679
+
610
680
  /**
611
681
  * Setups the clipping node.
612
682
  *
@@ -931,9 +1001,18 @@ class NodeMaterial extends Material {
931
1001
  /**
932
1002
  * Setups the normal node from the material.
933
1003
  *
1004
+ * @param {NodeBuilder} builder - The current node builder.
934
1005
  * @return {Node<vec3>} The normal node.
935
1006
  */
936
- setupNormal() {
1007
+ setupNormal( builder ) {
1008
+
1009
+ // WITH_GENESYS
1010
+ if ( ( builder.renderer.debug.view === DEBUG_VIEW_LIGHTING_ONLY || builder.renderer.debug.view === DEBUG_VIEW_DRAW_CALL ) && this.isShadowPassMaterial !== true && this.fragmentNode === null ) {
1011
+
1012
+ return lightingOnlyNormal();
1013
+
1014
+ }
1015
+ // !WITH_GENESYS
937
1016
 
938
1017
  return this.normalNode ? vec3( this.normalNode ) : materialNormal;
939
1018
 
@@ -1110,10 +1189,36 @@ class NodeMaterial extends Material {
1110
1189
 
1111
1190
  }
1112
1191
 
1192
+ // WITH_GENESYS
1193
+ if ( builder.renderer.debug.view === DEBUG_VIEW_LIGHTING_COMPLEXITY && this.isShadowPassMaterial !== true ) {
1194
+
1195
+ const hasLightLoop = lightsNode && ( materialLightings.length > 0 || lightsNode.getScope().hasLights );
1196
+
1197
+ if ( hasLightLoop !== true ) {
1198
+
1199
+ outgoingLightNode = vec3( 0 );
1200
+
1201
+ }
1202
+
1203
+ return outgoingLightNode;
1204
+
1205
+ }
1206
+ // !WITH_GENESYS
1207
+
1113
1208
  // EMISSIVE
1114
1209
 
1115
1210
  if ( ( emissiveNode && emissiveNode.isNode === true ) || ( material.emissive && material.emissive.isColor === true ) ) {
1116
1211
 
1212
+ // WITH_GENESYS
1213
+ // Lighting only and draw call substitute a plain lit material, so emissive is dropped.
1214
+ // Detail lighting keeps it.
1215
+ if ( ( builder.renderer.debug.view === DEBUG_VIEW_LIGHTING_ONLY || builder.renderer.debug.view === DEBUG_VIEW_DRAW_CALL ) && this.isShadowPassMaterial !== true ) {
1216
+
1217
+ return outgoingLightNode;
1218
+
1219
+ }
1220
+ // !WITH_GENESYS
1221
+
1117
1222
  emissive.assign( vec3( emissiveNode ? emissiveNode : materialEmissive ) );
1118
1223
 
1119
1224
  outgoingLightNode = outgoingLightNode.add( emissive );
@@ -60,6 +60,19 @@ class ShadowNodeMaterial extends NodeMaterial {
60
60
 
61
61
  }
62
62
 
63
+ // WITH_GENESYS
64
+ /**
65
+ * Shader-complexity proxy for the shadow mask.
66
+ *
67
+ * @return {number} The base cost, excluding texture samples.
68
+ */
69
+ getShaderComplexity() {
70
+
71
+ return 110;
72
+
73
+ }
74
+ // !WITH_GENESYS
75
+
63
76
  /**
64
77
  * Setups the lighting model.
65
78
  *
@@ -70,6 +70,19 @@ class VolumeNodeMaterial extends NodeMaterial {
70
70
 
71
71
  }
72
72
 
73
+ // WITH_GENESYS
74
+ /**
75
+ * Shader-complexity proxy for volumetric ray integration.
76
+ *
77
+ * @return {number} The base cost, excluding texture samples.
78
+ */
79
+ getShaderComplexity() {
80
+
81
+ return 280;
82
+
83
+ }
84
+ // !WITH_GENESYS
85
+
73
86
  setupLightingModel() {
74
87
 
75
88
  return new VolumetricLightingModel();
@@ -24,6 +24,9 @@ import CubeRenderTarget from '../../renderers/common/CubeRenderTarget.js';
24
24
  import BindGroup from '../../renderers/common/BindGroup.js';
25
25
 
26
26
  import { REVISION, IntType, UnsignedIntType, LinearFilter, LinearMipmapNearestFilter, NearestMipmapLinearFilter, LinearMipmapLinearFilter, NormalBlending } from '../../constants.js';
27
+ // WITH_GENESYS
28
+ import { SHADER_COMPLEXITY_TEXTURE_COST } from '../display/ComplexityDebug.js';
29
+ // !WITH_GENESYS
27
30
  import { RenderTarget } from '../../core/RenderTarget.js';
28
31
  import { Color } from '../../math/Color.js';
29
32
  import { Vector2 } from '../../math/Vector2.js';
@@ -268,6 +271,31 @@ class NodeBuilder {
268
271
  */
269
272
  this.fragmentShader = null;
270
273
 
274
+ // WITH_GENESYS
275
+ /**
276
+ * Static material-feature cost for the shader-complexity debug view.
277
+ *
278
+ * @type {number}
279
+ * @default 0
280
+ */
281
+ this.shaderComplexityBase = 0;
282
+
283
+ /**
284
+ * Fragment texture samples counted while generating the shader.
285
+ *
286
+ * @type {number}
287
+ * @default 0
288
+ */
289
+ this.fragmentTextureSamples = 0;
290
+
291
+ /**
292
+ * Sample sites already counted, so a node that is generated twice is not double-counted.
293
+ *
294
+ * @type {Set<string>}
295
+ */
296
+ this.shaderComplexitySampleKeys = new Set();
297
+ // !WITH_GENESYS
298
+
271
299
  /**
272
300
  * The generated compute shader.
273
301
  *
@@ -1296,6 +1324,46 @@ class NodeBuilder {
1296
1324
 
1297
1325
  }
1298
1326
 
1327
+ // WITH_GENESYS
1328
+ /**
1329
+ * Counts one fragment texture sample toward the shader-complexity proxy.
1330
+ * Depth and cube textures are shadow maps and environment maps, and rect-area
1331
+ * LTC tables belong to the light loop, so they are not material cost.
1332
+ *
1333
+ * @param {?Texture} texture - The texture being sampled.
1334
+ * @param {string} [shaderStage=this.shaderStage] - The stage the sample is emitted for.
1335
+ * @param {string} [key=''] - Stable id for this sample site. Repeated generation of the same site counts once.
1336
+ */
1337
+ recordFragmentTextureSample( texture, shaderStage = this.shaderStage, key = '' ) {
1338
+
1339
+ if ( shaderStage !== 'fragment' ) return;
1340
+
1341
+ if ( texture && ( texture.isDepthTexture === true || texture.isCubeTexture === true || texture.isRectAreaLTC === true ) ) return;
1342
+
1343
+ if ( key !== '' ) {
1344
+
1345
+ if ( this.shaderComplexitySampleKeys.has( key ) ) return;
1346
+
1347
+ this.shaderComplexitySampleKeys.add( key );
1348
+
1349
+ }
1350
+
1351
+ this.fragmentTextureSamples ++;
1352
+
1353
+ }
1354
+
1355
+ /**
1356
+ * Returns the shader-complexity proxy for the material currently being built.
1357
+ *
1358
+ * @return {number} Base feature cost plus weighted fragment texture samples.
1359
+ */
1360
+ getShaderComplexityCost() {
1361
+
1362
+ return this.shaderComplexityBase + this.fragmentTextureSamples * SHADER_COMPLEXITY_TEXTURE_COST;
1363
+
1364
+ }
1365
+ // !WITH_GENESYS
1366
+
1299
1367
  /**
1300
1368
  * Generates a texture LOD shader string for the given texture data.
1301
1369
  *
@@ -3119,6 +3187,12 @@ class NodeBuilder {
3119
3187
  */
3120
3188
  prebuild() {
3121
3189
 
3190
+ // WITH_GENESYS
3191
+ this.shaderComplexityBase = 0;
3192
+ this.fragmentTextureSamples = 0;
3193
+ this.shaderComplexitySampleKeys.clear();
3194
+ // !WITH_GENESYS
3195
+
3122
3196
  const { renderer, material } = this;
3123
3197
 
3124
3198
  // < renderer.contextNode >