Skip to content

Commit 864b4bd

Browse files
mvaligurskyMartin Valigursky
and
Martin Valigursky
authored
[Fix] Avoid additional shaders creation caused by out of order lights (#5550)
* [Fix] Avoid additional shaders creation caused by out of order lights * don’t include vertex format to be part of processing key on WebGL * updates --------- Co-authored-by: Martin Valigursky <[email protected]>
1 parent 773a48d commit 864b4bd

File tree

6 files changed

+46
-22
lines changed

6 files changed

+46
-22
lines changed

src/platform/graphics/shader-processor-options.js

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,16 +75,22 @@ class ShaderProcessorOptions {
7575
}
7676

7777
/**
78-
* Generate unique key represending the processing options.
78+
* Generate unique key representing the processing options.
7979
*
80+
* @param {import('./graphics-device.js').GraphicsDevice} device - The device.
8081
* @returns {string} - Returns the key.
8182
*/
82-
generateKey() {
83+
generateKey(device) {
8384
// TODO: Optimize. Uniform and BindGroup formats should have their keys evaluated in their
8485
// constructors, and here we should simply concatenate those.
85-
return JSON.stringify(this.uniformFormats) +
86-
JSON.stringify(this.bindGroupFormats) +
87-
this.vertexFormat?.renderingHashString;
86+
let key = JSON.stringify(this.uniformFormats) + JSON.stringify(this.bindGroupFormats);
87+
88+
// WebGPU shaders are processed per vertex format
89+
if (device.isWebGPU) {
90+
key += this.vertexFormat?.renderingHashString;
91+
}
92+
93+
return key;
8894
}
8995
}
9096

src/scene/composition/layer-composition.js

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -157,17 +157,25 @@ class LayerComposition extends EventHandler {
157157

158158
// function which splits list of lights on a a target object into separate lists of lights based on light type
159159
_splitLightsArray(target) {
160-
const lights = target._lights;
161-
target._splitLights[LIGHTTYPE_DIRECTIONAL].length = 0;
162-
target._splitLights[LIGHTTYPE_OMNI].length = 0;
163-
target._splitLights[LIGHTTYPE_SPOT].length = 0;
164160

161+
const splitLights = target._splitLights;
162+
splitLights[LIGHTTYPE_DIRECTIONAL].length = 0;
163+
splitLights[LIGHTTYPE_OMNI].length = 0;
164+
splitLights[LIGHTTYPE_SPOT].length = 0;
165+
166+
const lights = target._lights;
165167
for (let i = 0; i < lights.length; i++) {
166168
const light = lights[i];
167169
if (light.enabled) {
168-
target._splitLights[light._type].push(light);
170+
splitLights[light._type].push(light);
169171
}
170172
}
173+
174+
// sort the lights by their key, as the order of lights is used to generate shader generation key,
175+
// and this avoids new shaders being generated when lights are reordered
176+
splitLights[LIGHTTYPE_DIRECTIONAL].sort((a, b) => a.key - b.key);
177+
splitLights[LIGHTTYPE_OMNI].sort((a, b) => a.key - b.key);
178+
splitLights[LIGHTTYPE_SPOT].sort((a, b) => a.key - b.key);
171179
}
172180

173181
_update(device, clusteredLightingEnabled = false) {

src/scene/shader-lib/program-library.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Debug } from '../../core/debug.js';
2+
import { hashCode } from '../../core/hash.js';
23
import { version, revision } from '../../core/core.js';
34

45
import { Shader } from '../../platform/graphics/shader.js';
@@ -119,8 +120,12 @@ class ProgramLibrary {
119120

120121
// we have a key for shader source code generation, a key for its further processing to work with
121122
// uniform buffers, and a final key to get the processed shader from the cache
122-
const generationKey = generator.generateKey(options);
123-
const processingKey = processingOptions.generateKey();
123+
const generationKeyString = generator.generateKey(options);
124+
const generationKey = hashCode(generationKeyString);
125+
126+
const processingKeyString = processingOptions.generateKey(this._device);
127+
const processingKey = hashCode(processingKeyString);
128+
124129
const totalKey = `${generationKey}#${processingKey}`;
125130

126131
// do we have final processed shader
@@ -158,6 +163,13 @@ class ProgramLibrary {
158163

159164
// add new shader to the processed cache
160165
processedShader = new Shader(this._device, shaderDefinition);
166+
167+
// keep the keys in the debug mode
168+
Debug.call(() => {
169+
processedShader._generationKey = generationKeyString;
170+
processedShader._processingKey = processingKeyString;
171+
});
172+
161173
this.setCachedShader(totalKey, processedShader);
162174
}
163175

src/scene/shader-lib/programs/lit-options-utils.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,17 @@ const LitOptionsUtils = {
1414
}
1515
return key + options[key];
1616
})
17-
.join("");
17+
.join("\n");
1818
},
1919

2020
generateLightsKey(options) {
21-
return options.lights.map((light) => {
22-
return (!options.clusteredLightingEnabled || light._type === LIGHTTYPE_DIRECTIONAL) ? light.key : "";
21+
return 'lights:' + options.lights.map((light) => {
22+
return (!options.clusteredLightingEnabled || light._type === LIGHTTYPE_DIRECTIONAL) ? `${light.key},` : '';
2323
}).join("");
2424
},
2525

2626
generateChunksKey(options) {
27-
return Object.keys(options.chunks ?? {})
27+
return 'chunks:\n' + Object.keys(options.chunks ?? {})
2828
.sort()
2929
.map(key => key + options.chunks[key])
3030
.join("");

src/scene/shader-lib/programs/lit.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { hashCode } from '../../../core/hash.js';
21
import { ChunkBuilder } from '../chunk-builder.js';
32
import { LitShader } from './lit-shader.js';
43
import { LitOptionsUtils } from './lit-options-utils.js';
@@ -19,7 +18,7 @@ const lit = {
1918
options.shaderChunk +
2019
LitOptionsUtils.generateKey(options.litOptions);
2120

22-
return hashCode(key);
21+
return key;
2322
},
2423

2524
/**

src/scene/shader-lib/programs/standard.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { hashCode } from '../../../core/hash.js';
21
import { Debug } from '../../../core/debug.js';
32

43
import {
@@ -40,11 +39,11 @@ const standard = {
4039
props = buildPropertiesList(options);
4140
}
4241

43-
const key = "standard" +
44-
props.map(prop => prop + options[prop]).join("") +
42+
const key = "standard:\n" +
43+
props.map(prop => prop + options[prop]).join('\n') +
4544
LitOptionsUtils.generateKey(options.litOptions);
4645

47-
return hashCode(key);
46+
return key;
4847
},
4948

5049
// get the value to replace $UV with in Map Shader functions

0 commit comments

Comments
 (0)