@gnsx/three 0.186.1 → 0.186.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.
@@ -0,0 +1,278 @@
1
+ // WITH_GENESYS
2
+ // Shader and lighting complexity debug views. Costs are a material proxy, not ISA instruction counts.
3
+ // !WITH_GENESYS
4
+
5
+ import Node from '../core/Node.js';
6
+ import { float, vec3, vec4, If, Fn } from '../tsl/TSLCore.js';
7
+ import { fract, min, mix } from '../math/MathNode.js';
8
+
9
+ /**
10
+ * Shaded output. No complexity debug view.
11
+ *
12
+ * @type {string}
13
+ */
14
+ export const DEBUG_VIEW_NONE = 'none';
15
+
16
+ /**
17
+ * Material-cost heatmap. Overlapping draws accumulate, then a fullscreen pass applies the ramp.
18
+ *
19
+ * @type {string}
20
+ */
21
+ export const DEBUG_VIEW_SHADER_COMPLEXITY = 'shaderComplexity';
22
+
23
+ /**
24
+ * Direct-light count heatmap. The visible surface wins.
25
+ *
26
+ * @type {string}
27
+ */
28
+ export const DEBUG_VIEW_LIGHTING_COMPLEXITY = 'lightingComplexity';
29
+
30
+ /**
31
+ * Proxy budget that fills the shader-complexity ramp.
32
+ * A plain lit material stays in the green stops. Transmission plus several
33
+ * texture samples climbs toward red.
34
+ *
35
+ * @type {number}
36
+ */
37
+ export const DEFAULT_SHADER_COMPLEXITY_BUDGET = 800;
38
+
39
+ /** @type {number} */
40
+ export const SHADER_COMPLEXITY_BASE_UNLIT = 20;
41
+
42
+ /** @type {number} */
43
+ export const SHADER_COMPLEXITY_BASE_LIT = 40;
44
+
45
+ /** @type {number} */
46
+ export const SHADER_COMPLEXITY_TEXTURE_COST = 16;
47
+
48
+ /** @type {number} */
49
+ export const SHADER_COMPLEXITY_FEATURE_COST = 30;
50
+
51
+ /** @type {number} */
52
+ export const SHADER_COMPLEXITY_TRANSMISSION_COST = 60;
53
+
54
+ /**
55
+ * Attenuation above this counts as a light that shades the pixel.
56
+ *
57
+ * @type {number}
58
+ */
59
+ export const LIGHTING_COMPLEXITY_EPSILON = 0.001;
60
+
61
+ /**
62
+ * Added on top of 1 when a received light casts a shadow.
63
+ * Applied even where the shadow map is black.
64
+ *
65
+ * @type {number}
66
+ */
67
+ export const LIGHTING_COMPLEXITY_SHADOW_WEIGHT = 0.5;
68
+
69
+ /**
70
+ * Unreal `ShaderComplexityColors` from `BaseEngine.ini`.
71
+ *
72
+ * @type {Array<Array<number>>}
73
+ */
74
+ const SHADER_COMPLEXITY_COLORS = [
75
+ [ 0.0, 1.0, 0.127 ],
76
+ [ 0.0, 1.0, 0.0 ],
77
+ [ 0.046, 0.52, 0.0 ],
78
+ [ 0.215, 0.215, 0.0 ],
79
+ [ 0.52, 0.046, 0.0 ],
80
+ [ 0.7, 0.0, 0.0 ],
81
+ [ 1.0, 0.0, 0.0 ],
82
+ [ 1.0, 0.0, 0.5 ],
83
+ [ 1.0, 0.9, 0.9 ]
84
+ ];
85
+
86
+ /**
87
+ * Unreal `LightComplexityColors` from `BaseEngine.ini`.
88
+ *
89
+ * @type {Array<Array<number>>}
90
+ */
91
+ const LIGHT_COMPLEXITY_COLORS = [
92
+ [ 0.0, 0.0, 0.0 ],
93
+ [ 0.0, 0.0, 0.4 ],
94
+ [ 0.0, 0.3, 1.0 ],
95
+ [ 0.0, 0.7, 0.4 ],
96
+ [ 0.0, 1.0, 0.0 ],
97
+ [ 0.8, 0.8, 0.0 ],
98
+ [ 1.0, 0.3, 0.0 ],
99
+ [ 0.7, 0.0, 0.0 ],
100
+ [ 0.5, 0.0, 0.5 ],
101
+ [ 0.7, 0.3, 0.7 ],
102
+ [ 1.0, 0.9, 0.9 ]
103
+ ];
104
+
105
+ const rgb = ( colors, index ) => vec3( colors[ index ][ 0 ], colors[ index ][ 1 ], colors[ index ][ 2 ] );
106
+
107
+ /**
108
+ * Linear sample of a fixed RGB ramp. `cost` is in steps, and one step is one light.
109
+ *
110
+ * @param {Node<float>} cost - Complexity in ramp steps.
111
+ * @param {Array<Array<number>>} colors - Ramp stops.
112
+ * @return {Node<vec3>} The ramp color.
113
+ */
114
+ const colorizeLinear = ( cost, colors ) => {
115
+
116
+ const steps = colors.length - 1;
117
+ const expanded = cost.div( steps ).clamp( 0, 0.999 ).mul( steps );
118
+ const index = expanded.floor();
119
+ const frac = fract( expanded );
120
+
121
+ let color = rgb( colors, 0 );
122
+
123
+ for ( let i = 0; i < steps; i ++ ) {
124
+
125
+ const sample = mix( rgb( colors, i ), rgb( colors, i + 1 ), frac );
126
+
127
+ color = index.greaterThanEqual( float( i ) ).and( index.lessThan( float( i + 1 ) ) ).select( sample, color );
128
+
129
+ }
130
+
131
+ return color;
132
+
133
+ };
134
+
135
+ /**
136
+ * Maps a direct-light cost through Unreal's light-complexity ramp.
137
+ * Ten lights saturate at white.
138
+ *
139
+ * @param {Node<float>} cost - Accumulated light weight.
140
+ * @return {Node<vec3>} The ramp color.
141
+ */
142
+ export const colorizeLightComplexity = ( cost ) => colorizeLinear( cost, LIGHT_COMPLEXITY_COLORS );
143
+
144
+ /**
145
+ * Unreal's nonlinear shader-complexity ramp (`ColorizeComplexity`).
146
+ * The first third of the normalized cost spreads across the first seven stops.
147
+ *
148
+ * @param {Node<float>} complexity - Accumulated cost divided by the budget.
149
+ * @return {Node<vec3>} The ramp color.
150
+ */
151
+ export const colorizeShaderComplexity = /*@__PURE__*/ Fn( ( [ complexity ] ) => {
152
+
153
+ const c = complexity.clamp( 0, 0.999 );
154
+ const expanded = min( float( SHADER_COMPLEXITY_COLORS.length - 2 ), c.mul( 18 ) );
155
+ const color = vec3( 0 ).toVar();
156
+ const frac = fract( expanded );
157
+
158
+ If( expanded.lessThan( 1 ), () => {
159
+
160
+ color.assign( mix( rgb( SHADER_COMPLEXITY_COLORS, 0 ), rgb( SHADER_COMPLEXITY_COLORS, 1 ), frac ) );
161
+
162
+ } ).ElseIf( expanded.lessThan( 2 ), () => {
163
+
164
+ color.assign( mix( rgb( SHADER_COMPLEXITY_COLORS, 1 ), rgb( SHADER_COMPLEXITY_COLORS, 2 ), frac ) );
165
+
166
+ } ).ElseIf( expanded.lessThan( 3 ), () => {
167
+
168
+ color.assign( mix( rgb( SHADER_COMPLEXITY_COLORS, 2 ), rgb( SHADER_COMPLEXITY_COLORS, 3 ), frac ) );
169
+
170
+ } ).ElseIf( expanded.lessThan( 4 ), () => {
171
+
172
+ color.assign( mix( rgb( SHADER_COMPLEXITY_COLORS, 3 ), rgb( SHADER_COMPLEXITY_COLORS, 4 ), frac ) );
173
+
174
+ } ).ElseIf( expanded.lessThan( 5 ), () => {
175
+
176
+ color.assign( mix( rgb( SHADER_COMPLEXITY_COLORS, 4 ), rgb( SHADER_COMPLEXITY_COLORS, 5 ), frac ) );
177
+
178
+ } ).ElseIf( expanded.lessThan( 6 ), () => {
179
+
180
+ color.assign( mix( rgb( SHADER_COMPLEXITY_COLORS, 5 ), rgb( SHADER_COMPLEXITY_COLORS, 6 ), frac ) );
181
+
182
+ } ).Else( () => {
183
+
184
+ // Nine stops, so the upper range uses the `count > 8` scale of 3.
185
+ const upper = c.sub( 0.33333333 ).mul( 3.0 );
186
+ const upperFrac = fract( upper );
187
+
188
+ If( upper.lessThanEqual( 1 ), () => {
189
+
190
+ color.assign( mix( rgb( SHADER_COMPLEXITY_COLORS, 6 ), rgb( SHADER_COMPLEXITY_COLORS, 7 ), upperFrac ) );
191
+
192
+ } ).Else( () => {
193
+
194
+ color.assign( mix( rgb( SHADER_COMPLEXITY_COLORS, 7 ), rgb( SHADER_COMPLEXITY_COLORS, 8 ), upperFrac ) );
195
+
196
+ } );
197
+
198
+ } );
199
+
200
+ return color;
201
+
202
+ } );
203
+
204
+ /**
205
+ * Reads the builder's shader-complexity proxy at code-generation time,
206
+ * after the real material graph has been generated and texture samples counted.
207
+ *
208
+ * @augments Node
209
+ */
210
+ class ShaderComplexityRatioNode extends Node {
211
+
212
+ static get type() {
213
+
214
+ return 'ShaderComplexityRatioNode';
215
+
216
+ }
217
+
218
+ constructor() {
219
+
220
+ super( 'float' );
221
+
222
+ }
223
+
224
+ generate( builder ) {
225
+
226
+ const budget = builder.renderer.debug.shaderComplexityBudget;
227
+ const safeBudget = ( typeof budget === 'number' && budget > 0 ) ? budget : 1;
228
+ const ratio = builder.getShaderComplexityCost() / safeBudget;
229
+
230
+ return ratio.toFixed( 6 );
231
+
232
+ }
233
+
234
+ }
235
+
236
+ /**
237
+ * Static fragment proxy for a material. Texture samples are counted later, during generation.
238
+ *
239
+ * @param {NodeMaterial} material - The material being built.
240
+ * @return {number} The base cost, excluding texture samples.
241
+ */
242
+ export function getShaderComplexityBase( material ) {
243
+
244
+ let cost = material.lights === true ? SHADER_COMPLEXITY_BASE_LIT : SHADER_COMPLEXITY_BASE_UNLIT;
245
+
246
+ if ( material.useClearcoat === true ) cost += SHADER_COMPLEXITY_FEATURE_COST;
247
+
248
+ if ( material.useSheen === true ) cost += SHADER_COMPLEXITY_FEATURE_COST;
249
+
250
+ if ( material.useIridescence === true ) cost += SHADER_COMPLEXITY_FEATURE_COST;
251
+
252
+ if ( material.useAnisotropy === true ) cost += SHADER_COMPLEXITY_FEATURE_COST;
253
+
254
+ if ( material.useTransmission === true ) cost += SHADER_COMPLEXITY_TRANSMISSION_COST;
255
+
256
+ return cost;
257
+
258
+ }
259
+
260
+ /**
261
+ * Replaces the shaded color with `vec4(cost / budget, 0, 0, 1)`.
262
+ * The real graph is still generated so texture samples are counted first.
263
+ *
264
+ * @param {Node} resultNode - The material's shaded output.
265
+ * @return {Node<vec4>} The complexity ratio in the red channel.
266
+ */
267
+ export function shaderComplexityOutput( resultNode ) {
268
+
269
+ // Generate the shaded graph first so texture samples are counted, then
270
+ // replace the color. A bypass of a value node emits a bare expression
271
+ // statement, which WGSL rejects.
272
+ const shaded = vec4( resultNode ).toVar();
273
+
274
+ const ratio = new ShaderComplexityRatioNode();
275
+
276
+ return vec4( ratio, 0, 0, 1 ).add( shaded.mul( 0 ) );
277
+
278
+ }
@@ -1,8 +1,11 @@
1
1
  import Node from '../core/Node.js';
2
- import { property, vec3 } from '../tsl/TSLBase.js';
2
+ import { float, property, vec3 } from '../tsl/TSLBase.js';
3
3
  import { hashArray } from '../core/NodeUtils.js';
4
4
  import { warn } from '../../utils.js';
5
5
  // WITH_GENESYS
6
+ import { colorizeLightComplexity, DEBUG_VIEW_LIGHTING_COMPLEXITY, LIGHTING_COMPLEXITY_EPSILON, LIGHTING_COMPLEXITY_SHADOW_WEIGHT } from '../display/ComplexityDebug.js';
7
+ // !WITH_GENESYS
8
+ // WITH_GENESYS
6
9
  import LightProbeGridNode from './LightProbeGridNode.js';
7
10
  // !WITH_GENESYS
8
11
 
@@ -27,6 +30,10 @@ const totalSpecular = property( 'vec3', 'totalSpecular' );
27
30
  */
28
31
  const outgoingLight = property( 'vec3', 'outgoingLight' );
29
32
 
33
+ // WITH_GENESYS
34
+ const lightingComplexity = property( 'float', 'lightingComplexity' );
35
+ // !WITH_GENESYS
36
+
30
37
  /**
31
38
  * Sorts an array of lights in ascending order by their IDs.
32
39
  *
@@ -380,6 +387,10 @@ class LightsNode extends Node {
380
387
  reflectedLight
381
388
  }, builder );
382
389
 
390
+ // WITH_GENESYS
391
+ this._addLightingComplexity( builder, lightNode, lightData );
392
+ // !WITH_GENESYS
393
+
383
394
  }
384
395
 
385
396
  /**
@@ -399,7 +410,39 @@ class LightsNode extends Node {
399
410
  reflectedLight
400
411
  }, builder );
401
412
 
413
+ // WITH_GENESYS
414
+ this._addLightingComplexity( builder, lightNode, lightData );
415
+ // !WITH_GENESYS
416
+
417
+ }
418
+
419
+ // WITH_GENESYS
420
+ /**
421
+ * Adds this direct light's weight to the lighting-complexity accumulator.
422
+ * Point, spot, and rect-area lights count only where attenuation is in range.
423
+ * Directional lights have no falloff, so they always count. Shadow-casting
424
+ * lights add extra weight even where the shadow map is black.
425
+ *
426
+ * @private
427
+ * @param {NodeBuilder} builder - The current node builder.
428
+ * @param {LightingNode} lightNode - The light node being shaded.
429
+ * @param {Object} lightData - Direct light terms, including an optional attenuation node.
430
+ */
431
+ _addLightingComplexity( builder, lightNode, lightData ) {
432
+
433
+ if ( builder.renderer.debug.view !== DEBUG_VIEW_LIGHTING_COMPLEXITY ) return;
434
+
435
+ if ( builder.material && builder.material.isShadowPassMaterial === true ) return;
436
+
437
+ const attenuation = lightData.attenuation !== undefined ? lightData.attenuation : float( 1 );
438
+ const light = lightNode.light;
439
+ const castsShadow = light !== undefined && light !== null && light.castShadow === true && builder.object.receiveShadow === true;
440
+ const weight = castsShadow ? float( 1 + LIGHTING_COMPLEXITY_SHADOW_WEIGHT ) : float( 1 );
441
+
442
+ lightingComplexity.addAssign( attenuation.greaterThan( LIGHTING_COMPLEXITY_EPSILON ).select( weight, float( 0 ) ) );
443
+
402
444
  }
445
+ // !WITH_GENESYS
403
446
 
404
447
  /**
405
448
  * Setups the internal lights by building all respective
@@ -463,6 +506,14 @@ class LightsNode extends Node {
463
506
 
464
507
  properties.nodes = stack.nodes;
465
508
 
509
+ // WITH_GENESYS
510
+ if ( builder.renderer.debug.view === DEBUG_VIEW_LIGHTING_COMPLEXITY && ( builder.material === null || builder.material.isShadowPassMaterial !== true ) ) {
511
+
512
+ lightingComplexity.assign( 0 );
513
+
514
+ }
515
+ // !WITH_GENESYS
516
+
466
517
  lightingModel.start( builder );
467
518
 
468
519
  const { backdrop, backdropAlpha } = context;
@@ -487,10 +538,28 @@ class LightsNode extends Node {
487
538
  totalDiffuseNode.assign( totalDiffuse );
488
539
  totalSpecularNode.assign( directSpecular.add( indirectSpecular ) );
489
540
 
490
- outgoingLightNode.assign( totalDiffuseNode.add( totalSpecularNode ) );
541
+ // WITH_GENESYS
542
+ const lightingComplexityView = builder.renderer.debug.view === DEBUG_VIEW_LIGHTING_COMPLEXITY && ( builder.material === null || builder.material.isShadowPassMaterial !== true );
543
+
544
+ if ( lightingComplexityView !== true ) {
545
+
546
+ outgoingLightNode.assign( totalDiffuseNode.add( totalSpecularNode ) );
547
+
548
+ }
549
+ // !WITH_GENESYS
550
+ // outgoingLightNode.assign( totalDiffuseNode.add( totalSpecularNode ) );
491
551
 
492
552
  lightingModel.finish( builder );
493
553
 
554
+ // WITH_GENESYS
555
+ // After finish(), so clearcoat and sheen cannot draw over the heatmap.
556
+ if ( lightingComplexityView === true ) {
557
+
558
+ outgoingLightNode.assign( colorizeLightComplexity( lightingComplexity ) );
559
+
560
+ }
561
+ // !WITH_GENESYS
562
+
494
563
  outgoingLightNode = outgoingLightNode.bypass( builder.removeStack() );
495
564
 
496
565
  } else {
@@ -17,7 +17,10 @@ export const directPointLight = ( { color, lightVector, cutoffDistance, decayExp
17
17
 
18
18
  const lightColor = color.mul( attenuation );
19
19
 
20
- return { lightDirection, lightColor };
20
+ // WITH_GENESYS
21
+ return { lightDirection, lightColor, attenuation };
22
+ // !WITH_GENESYS
23
+ // return { lightDirection, lightColor };
21
24
 
22
25
  };
23
26
 
@@ -88,6 +88,20 @@ class RectAreaLightNode extends AnalyticLightNode {
88
88
 
89
89
  setupDirectRectArea( builder ) {
90
90
 
91
+ // WITH_GENESYS
92
+ // LTC lookups are part of the light loop, not the material's shader cost.
93
+ if ( _ltcLib !== null ) {
94
+
95
+ _ltcLib.LTC_FLOAT_1.isRectAreaLTC = true;
96
+ _ltcLib.LTC_FLOAT_2.isRectAreaLTC = true;
97
+
98
+ if ( _ltcLib.LTC_HALF_1 ) _ltcLib.LTC_HALF_1.isRectAreaLTC = true;
99
+
100
+ if ( _ltcLib.LTC_HALF_2 ) _ltcLib.LTC_HALF_2.isRectAreaLTC = true;
101
+
102
+ }
103
+ // !WITH_GENESYS
104
+
91
105
  let ltc_1, ltc_2;
92
106
 
93
107
  if ( builder.isAvailable( 'float32Filterable' ) ) {
@@ -135,7 +135,12 @@ class SpotLightNode extends AnalyticLightNode {
135
135
  decayExponent: decayExponentNode
136
136
  } );
137
137
 
138
- let lightColor = colorNode.mul( spotAttenuation ).mul( lightAttenuation );
138
+ // WITH_GENESYS
139
+ const attenuation = spotAttenuation.mul( lightAttenuation );
140
+
141
+ let lightColor = colorNode.mul( attenuation );
142
+ // !WITH_GENESYS
143
+ // let lightColor = colorNode.mul( spotAttenuation ).mul( lightAttenuation );
139
144
 
140
145
  let projected, lightCoord;
141
146
 
@@ -159,7 +164,10 @@ class SpotLightNode extends AnalyticLightNode {
159
164
 
160
165
  }
161
166
 
162
- return { lightColor, lightDirection };
167
+ // WITH_GENESYS
168
+ return { lightColor, lightDirection, attenuation };
169
+ // !WITH_GENESYS
170
+ // return { lightColor, lightDirection };
163
171
 
164
172
  }
165
173
 
@@ -191,7 +191,15 @@ class Background extends DataMap {
191
191
 
192
192
  }
193
193
 
194
- renderList.unshift( backgroundMesh, backgroundMesh.geometry, backgroundMesh.material, 0, 0, null, null );
194
+ // WITH_GENESYS
195
+ // The background is depth-tested off, so additive shader complexity would add it to every pixel.
196
+ if ( renderer.debug.view !== 'shaderComplexity' ) {
197
+
198
+ renderList.unshift( backgroundMesh, backgroundMesh.geometry, backgroundMesh.material, 0, 0, null, null );
199
+
200
+ }
201
+ // !WITH_GENESYS
202
+ // renderList.unshift( backgroundMesh, backgroundMesh.geometry, backgroundMesh.material, 0, 0, null, null );
195
203
 
196
204
  } else {
197
205
 
@@ -215,6 +223,14 @@ class Background extends DataMap {
215
223
 
216
224
  //
217
225
 
226
+ // WITH_GENESYS
227
+ if ( renderer.debug.view === 'shaderComplexity' ) {
228
+
229
+ _clearColor.set( 0, 0, 0, 1 );
230
+
231
+ }
232
+ // !WITH_GENESYS
233
+
218
234
  if ( renderer.autoClear === true || forceClear === true ) {
219
235
 
220
236
  const clearColorValue = renderContext.clearColorValue;
@@ -950,6 +950,11 @@ class RenderObject {
950
950
 
951
951
  cacheKey = hash( cacheKey, this.renderer.contextNode.id, this.renderer.contextNode.version );
952
952
 
953
+ // WITH_GENESYS
954
+ // hash() xors each argument as a number, so a view name has to be hashed first.
955
+ cacheKey = hash( cacheKey, hashString( this.renderer.debug.view ), this.renderer.debug.shaderComplexityBudget );
956
+ // !WITH_GENESYS
957
+
953
958
  return cacheKey;
954
959
 
955
960
  }
@@ -37,6 +37,7 @@ import { float, vec3, vec4, Fn } from '../../nodes/tsl/TSLCore.js';
37
37
  import { detachRendererReference } from '../../nodes/accessors/RendererReferenceNode.js';
38
38
  import { toneMappingExposure } from '../../nodes/display/ToneMappingNode.js';
39
39
  import { ProfilerService } from '../../profiler/ProfilerService.js';
40
+ import { DEBUG_VIEW_LIGHTING_COMPLEXITY, DEBUG_VIEW_NONE, DEBUG_VIEW_SHADER_COMPLEXITY, DEFAULT_SHADER_COMPLEXITY_BUDGET } from '../../nodes/display/ComplexityDebug.js';
40
41
  // !WITH_GENESYS
41
42
  import { reference } from '../../nodes/accessors/ReferenceNode.js';
42
43
  import { highpModelNormalViewMatrix, highpModelViewMatrix } from '../../nodes/accessors/ModelNode.js';
@@ -743,6 +744,8 @@ class Renderer {
743
744
  * @property {?Function} onNodeBuilderCreated - A callback function that is executed after a node builder has been created and before it is built.
744
745
  * @property {?Function} onShaderError - A callback function that is executed when a shader error happens. Only supported with WebGL 2 right now.
745
746
  * @property {Function} getShaderAsync - Allows the get the raw shader code for the given scene, camera and 3D object.
747
+ * @property {string} view - Debug view. `shaderComplexity` and `lightingComplexity` replace the shaded color with a heatmap.
748
+ * @property {number} shaderComplexityBudget - Proxy budget that fills the shader-complexity ramp.
746
749
  */
747
750
 
748
751
  /**
@@ -757,6 +760,10 @@ class Renderer {
757
760
  },
758
761
  onNodeBuilderCreated: null,
759
762
  onShaderError: null,
763
+ // WITH_GENESYS
764
+ view: DEBUG_VIEW_NONE,
765
+ shaderComplexityBudget: DEFAULT_SHADER_COMPLEXITY_BUDGET,
766
+ // !WITH_GENESYS
760
767
  getShaderAsync: async ( scene, camera, object ) => {
761
768
 
762
769
  await this.compileAsync( object, camera, scene );
@@ -2691,7 +2698,13 @@ class Renderer {
2691
2698
  const useToneMapping = this.currentToneMapping !== NoToneMapping;
2692
2699
  const useColorSpace = this.currentColorSpace !== ColorManagement.workingColorSpace;
2693
2700
 
2694
- return useToneMapping || useColorSpace;
2701
+ // WITH_GENESYS
2702
+ // Shader complexity writes a scalar into an intermediate target, then the output pass colorizes it.
2703
+ const shaderComplexity = this.debug.view === DEBUG_VIEW_SHADER_COMPLEXITY;
2704
+
2705
+ return useToneMapping || useColorSpace || shaderComplexity;
2706
+ // !WITH_GENESYS
2707
+ // return useToneMapping || useColorSpace;
2695
2708
 
2696
2709
  }
2697
2710
 
@@ -2742,6 +2755,16 @@ class Renderer {
2742
2755
  */
2743
2756
  get currentToneMapping() {
2744
2757
 
2758
+ // WITH_GENESYS
2759
+ const view = this.debug.view;
2760
+
2761
+ if ( view === DEBUG_VIEW_SHADER_COMPLEXITY || view === DEBUG_VIEW_LIGHTING_COMPLEXITY ) {
2762
+
2763
+ return NoToneMapping;
2764
+
2765
+ }
2766
+ // !WITH_GENESYS
2767
+
2745
2768
  return this.isOutputTarget ? this.toneMapping : NoToneMapping;
2746
2769
 
2747
2770
  }
@@ -7,7 +7,11 @@ import { NodeFrame, NodeUpdateType, StackTrace } from '../../../nodes/Nodes.js';
7
7
  import { renderGroup, cubeTexture, texture, fog, rangeFogFactor, densityFogFactor, reference, pmremTexture, screenUV, uniform } from '../../../nodes/TSL.js';
8
8
  import { builtin } from '../../../nodes/accessors/BuiltinNode.js';
9
9
 
10
- import { CubeUVReflectionMapping, EquirectangularReflectionMapping, EquirectangularRefractionMapping } from '../../../constants.js';
10
+ import { CubeUVReflectionMapping, EquirectangularReflectionMapping, EquirectangularRefractionMapping, NoToneMapping } from '../../../constants.js';
11
+ // WITH_GENESYS
12
+ import { vec4 } from '../../../nodes/tsl/TSLCore.js';
13
+ import { colorizeShaderComplexity, DEBUG_VIEW_SHADER_COMPLEXITY } from '../../../nodes/display/ComplexityDebug.js';
14
+ // !WITH_GENESYS
11
15
  import { hashArray } from '../../../nodes/core/NodeUtils.js';
12
16
  import { error } from '../../../utils.js';
13
17
 
@@ -992,7 +996,10 @@ class NodeManager extends DataMap {
992
996
 
993
997
  const renderer = this.renderer;
994
998
 
995
- return renderer.toneMapping + ',' + renderer.currentColorSpace + ',' + renderer.xr.isPresenting;
999
+ // WITH_GENESYS
1000
+ return renderer.toneMapping + ',' + renderer.currentColorSpace + ',' + renderer.xr.isPresenting + ',' + renderer.debug.view;
1001
+ // !WITH_GENESYS
1002
+ // return renderer.toneMapping + ',' + renderer.currentColorSpace + ',' + renderer.xr.isPresenting;
996
1003
 
997
1004
  }
998
1005
 
@@ -1007,27 +1014,35 @@ class NodeManager extends DataMap {
1007
1014
 
1008
1015
  const renderer = this.renderer;
1009
1016
 
1010
- let output;
1017
+ let sampled;
1011
1018
 
1012
1019
  if ( outputTarget.isArrayTexture ) {
1013
1020
 
1014
1021
  if ( this.backend.isWebGLBackend ) {
1015
1022
 
1016
- output = texture( outputTarget, screenUV ).depth( builtin( 'gl_ViewID_OVR' ) ).renderOutput( renderer.toneMapping, renderer.currentColorSpace );
1023
+ sampled = texture( outputTarget, screenUV ).depth( builtin( 'gl_ViewID_OVR' ) );
1017
1024
 
1018
1025
  } else {
1019
1026
 
1020
- output = texture( outputTarget, screenUV ).depth( _outputLayerIndex ).renderOutput( renderer.toneMapping, renderer.currentColorSpace );
1027
+ sampled = texture( outputTarget, screenUV ).depth( _outputLayerIndex );
1021
1028
 
1022
1029
  }
1023
1030
 
1024
1031
  } else {
1025
1032
 
1026
- output = texture( outputTarget, screenUV ).renderOutput( renderer.toneMapping, renderer.currentColorSpace );
1033
+ sampled = texture( outputTarget, screenUV );
1027
1034
 
1028
1035
  }
1029
1036
 
1030
- return output;
1037
+ // WITH_GENESYS
1038
+ if ( renderer.debug.view === DEBUG_VIEW_SHADER_COMPLEXITY ) {
1039
+
1040
+ return vec4( colorizeShaderComplexity( sampled.r ), 1 ).renderOutput( NoToneMapping, renderer.currentColorSpace );
1041
+
1042
+ }
1043
+ // !WITH_GENESYS
1044
+
1045
+ return sampled.renderOutput( renderer.toneMapping, renderer.currentColorSpace );
1031
1046
 
1032
1047
  }
1033
1048
 
@@ -635,6 +635,10 @@ ${ flowData.code }
635
635
  */
636
636
  generateTexture( texture, textureProperty, uvSnippet, depthSnippet, offsetSnippet ) {
637
637
 
638
+ // WITH_GENESYS
639
+ this.recordFragmentTextureSample( texture, this.shaderStage, textureProperty + '|' + uvSnippet );
640
+ // !WITH_GENESYS
641
+
638
642
  if ( depthSnippet ) uvSnippet = `vec3( ${ uvSnippet }, ${ depthSnippet } )`;
639
643
 
640
644
  if ( texture.isDepthTexture ) {
@@ -678,6 +682,10 @@ ${ flowData.code }
678
682
  */
679
683
  generateTextureLevel( texture, textureProperty, uvSnippet, levelSnippet, depthSnippet, offsetSnippet ) {
680
684
 
685
+ // WITH_GENESYS
686
+ this.recordFragmentTextureSample( texture, this.shaderStage, textureProperty + '|' + uvSnippet + '|' + levelSnippet );
687
+ // !WITH_GENESYS
688
+
681
689
  if ( depthSnippet ) uvSnippet = `vec3( ${ uvSnippet }, ${ depthSnippet } )`;
682
690
 
683
691
  if ( offsetSnippet ) {
@@ -934,9 +934,25 @@ class WebGLState {
934
934
 
935
935
  this.setFlipSided( flipSided );
936
936
 
937
- ( material.blending === NormalBlending && material.transparent === false )
938
- ? this.setBlending( NoBlending )
939
- : this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.blendColor, material.blendAlpha, material.premultipliedAlpha );
937
+ // WITH_GENESYS
938
+ // WebGL has no cached render pipeline. One+One is applied on each draw instead.
939
+ if ( this.backend.renderer.debug.view === 'shaderComplexity' && material.isShadowPassMaterial !== true && this.backend.renderer.isOutputTarget !== true ) {
940
+
941
+ this.setBlending( AdditiveBlending, AddEquation, OneFactor, OneFactor, AddEquation, OneFactor, OneFactor, material.blendColor, material.blendAlpha, true );
942
+
943
+ } else if ( material.blending === NormalBlending && material.transparent === false ) {
944
+
945
+ this.setBlending( NoBlending );
946
+
947
+ } else {
948
+
949
+ this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.blendColor, material.blendAlpha, material.premultipliedAlpha );
950
+
951
+ }
952
+ // !WITH_GENESYS
953
+ // ( material.blending === NormalBlending && material.transparent === false )
954
+ // ? this.setBlending( NoBlending )
955
+ // : this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.blendColor, material.blendAlpha, material.premultipliedAlpha );
940
956
 
941
957
  this.setDepthFunc( material.depthFunc );
942
958
  this.setDepthTest( material.depthTest );