@@ -21,13 +21,18 @@ import { GraphNode } from './graph-node.js';
2121import { getDefaultMaterial } from './materials/default-material.js' ;
2222import { LightmapCache } from './graphics/lightmap-cache.js' ;
2323import { DebugGraphics } from '../platform/graphics/debug-graphics.js' ;
24+ import { hash32Fnv1a } from '../core/hash.js' ;
25+ import { array } from '../core/array-utils.js' ;
2426
2527let id = 0 ;
2628const _tmpAabb = new BoundingBox ( ) ;
2729const _tempBoneAabb = new BoundingBox ( ) ;
2830const _tempSphere = new BoundingSphere ( ) ;
2931const _meshSet = new Set ( ) ;
3032
33+ // internal array used to evaluate the hash for the shader instance
34+ const lookupHashes = new Uint32Array ( 3 ) ;
35+
3136/**
3237 * Internal data structure used to store data used by hardware instancing.
3338 *
@@ -72,6 +77,13 @@ class ShaderInstance {
7277 */
7378 uniformBuffer = null ;
7479
80+ /**
81+ * The full array of hashes used to lookup the pipeline, used in case of hash collision.
82+ *
83+ * @type {Uint32Array }
84+ */
85+ hashes ;
86+
7587 /**
7688 * Returns the mesh bind group for the shader.
7789 *
@@ -126,26 +138,6 @@ class ShaderInstance {
126138 }
127139}
128140
129- /**
130- * An entry in the shader cache, representing shaders for this mesh instance and a specific shader
131- * pass.
132- *
133- * @ignore
134- */
135- class ShaderCacheEntry {
136- /**
137- * The shader instances. Looked up by lightHash, which represents an ordered set of lights.
138- *
139- * @type {Map<number, ShaderInstance> }
140- */
141- shaderInstances = new Map ( ) ;
142-
143- destroy ( ) {
144- this . shaderInstances . forEach ( instance => instance . destroy ( ) ) ;
145- this . shaderInstances . clear ( ) ;
146- }
147- }
148-
149141/**
150142 * Callback used by {@link Layer} to calculate the "sort distance" for a {@link MeshInstance},
151143 * which determines its place in the render order.
@@ -198,13 +190,11 @@ class MeshInstance {
198190 _material = null ;
199191
200192 /**
201- * An array of shader cache entries, indexed by the shader pass constant (SHADER_FORWARD..). The
202- * value stores all shaders and bind groups for the shader pass for various light combinations.
193+ * The cache of shaders, indexed by a hash value.
203194 *
204- * @type {Array<ShaderCacheEntry|null> }
205- * @private
195+ * @type {Map<number, ShaderInstance> }
206196 */
207- _shaderCache = [ ] ;
197+ _shaderCache = new Map ( ) ;
208198
209199 /** @ignore */
210200 id = id ++ ;
@@ -511,11 +501,10 @@ class MeshInstance {
511501 * @ignore
512502 */
513503 clearShaders ( ) {
514- const shaderCache = this . _shaderCache ;
515- for ( let i = 0 ; i < shaderCache . length ; i ++ ) {
516- shaderCache [ i ] ?. destroy ( ) ;
517- shaderCache [ i ] = null ;
518- }
504+ this . _shaderCache . forEach ( ( shaderInstance ) => {
505+ shaderInstance . destroy ( ) ;
506+ } ) ;
507+ this . _shaderCache . clear ( ) ;
519508 }
520509
521510 /**
@@ -535,24 +524,26 @@ class MeshInstance {
535524 */
536525 getShaderInstance ( shaderPass , lightHash , scene , viewUniformFormat , viewBindGroupFormat , sortedLights ) {
537526
538- let shaderInstance ;
539- let passEntry = this . _shaderCache [ shaderPass ] ;
540- if ( passEntry ) {
541- shaderInstance = passEntry . shaderInstances . get ( lightHash ) ;
542- } else {
543- passEntry = new ShaderCacheEntry ( ) ;
544- this . _shaderCache [ shaderPass ] = passEntry ;
545- }
527+ const shaderDefs = this . _shaderDefs ;
528+
529+ // unique hash for the required shader
530+ lookupHashes [ 0 ] = shaderPass ;
531+ lookupHashes [ 1 ] = lightHash ;
532+ lookupHashes [ 2 ] = shaderDefs ;
533+ const hash = hash32Fnv1a ( lookupHashes ) ;
534+
535+ // look up the cache
536+ let shaderInstance = this . _shaderCache . get ( hash ) ;
546537
547538 // cache miss in the shader cache of the mesh instance
548539 if ( ! shaderInstance ) {
549540
550- // get the shader from the material
551541 const mat = this . _material ;
552- const shaderDefs = this . _shaderDefs ;
553- const variantKey = shaderPass + '_' + shaderDefs + '_' + lightHash ;
542+
543+ // get the shader from the material
554544 shaderInstance = new ShaderInstance ( ) ;
555- shaderInstance . shader = mat . variants . get ( variantKey ) ;
545+ shaderInstance . shader = mat . variants . get ( hash ) ;
546+ shaderInstance . hashes = new Uint32Array ( lookupHashes ) ;
556547
557548 // cache miss in the material variants
558549 if ( ! shaderInstance . shader ) {
@@ -566,15 +557,24 @@ class MeshInstance {
566557 DebugGraphics . popGpuMarker ( this . mesh . device ) ;
567558
568559 // add it to the material variants cache
569- mat . variants . set ( variantKey , shader ) ;
560+ mat . variants . set ( hash , shader ) ;
570561
571562 shaderInstance . shader = shader ;
572563 }
573564
574565 // add it to the mesh instance cache
575- passEntry . shaderInstances . set ( lightHash , shaderInstance ) ;
566+ this . _shaderCache . set ( hash , shaderInstance ) ;
576567 }
577568
569+ Debug . call ( ( ) => {
570+ // due to a small number of shaders in the cache, and to avoid performance hit, we're not
571+ // handling the hash collision. This is very unlikely but still possible. Check and report
572+ // if it happens in the debug mode, allowing us to fix the issue.
573+ if ( ! array . equals ( shaderInstance . hashes , lookupHashes ) ) {
574+ Debug . errorOnce ( 'Hash collision in the shader cache for mesh instance. This is very unlikely but still possible. Please report this issue.' ) ;
575+ }
576+ } ) ;
577+
578578 return shaderInstance ;
579579 }
580580
0 commit comments