Преглед на файлове

Add support for shader hot swapping

David Catuhe преди 6 години
родител
ревизия
8a03ee338f

+ 1 - 0
dist/preview release/what's new.md

@@ -15,6 +15,7 @@
 - Refactor of the SolidParticleSystem code for performance and code quality improvement ([barroij](https://github.com/barroij))
 - Added utility function `Tools.BuildArray` for array initialisation ([barroij](https://github.com/barroij))
 - Introduced a new `IOfflineSupport` interface to hide IndexedDB ([Deltakosh](https://github.com/deltakosh))
+- `PBRMaterial` and `StandardMaterial` now use hot swapping feature for shaders. This means they can keep using a previous shader while a new one is being compiled ([Deltakosh](https://github.com/deltakosh))
 
 ### glTF Loader
 

+ 18 - 11
src/Materials/PBR/babylon.pbrBaseMaterial.ts

@@ -861,13 +861,21 @@ module BABYLON {
             if (!engine.getCaps().standardDerivatives && !mesh.isVerticesDataPresent(VertexBuffer.NormalKind)) {
                 mesh.createNormals(true);
                 Tools.Warn("PBRMaterial: Normals have been created for the mesh: " + mesh.name);
-                }
+            }
+
+            let previousEffect = subMesh.effect;
+            let effect = this._prepareEffect(mesh, defines, this.onCompiled, this.onError, useInstances);
 
-            const effect = this._prepareEffect(mesh, defines, this.onCompiled, this.onError, useInstances);
             if (effect) {
-                scene.resetCachedMaterial();
-                subMesh.setEffect(effect, defines);
-                this.buildUniformLayout();
+                // Use previous effect while new one is compiling
+                if (this.allowShaderHotSwapping && previousEffect && !effect.isReady()) {
+                    effect = previousEffect;
+                    defines.markAsUnprocessed();
+                } else {
+                    scene.resetCachedMaterial();
+                    subMesh.setEffect(effect, defines);
+                    this.buildUniformLayout();
+                }
             }
 
             if (!subMesh.effect || !subMesh.effect.isReady()) {
@@ -1134,10 +1142,10 @@ module BABYLON {
                             case Texture.CUBIC_MODE:
                             case Texture.INVCUBIC_MODE:
                             default:
-                                    defines.REFLECTIONMAP_CUBIC = true;
-                                    defines.USE_LOCAL_REFLECTIONMAP_CUBIC = (<any>reflectionTexture).boundingBoxSize ? true : false;
-                                    break;
-                            }
+                                defines.REFLECTIONMAP_CUBIC = true;
+                                defines.USE_LOCAL_REFLECTIONMAP_CUBIC = (<any>reflectionTexture).boundingBoxSize ? true : false;
+                                break;
+                        }
 
                         if (reflectionTexture.coordinatesMode !== Texture.SKYBOX_MODE) {
                             if (reflectionTexture.sphericalPolynomial) {
@@ -1424,8 +1432,7 @@ module BABYLON {
             this.bindOnlyWorldMatrix(world);
 
             // Normal Matrix
-            if (defines.OBJECTSPACE_NORMALMAP)
-            {
+            if (defines.OBJECTSPACE_NORMALMAP) {
                 world.toNormalMatrix(this._normalMatrix);
                 this.bindOnlyNormalMatrix(this._normalMatrix);
             }

+ 13 - 6
src/Materials/babylon.pushMaterial.ts

@@ -7,7 +7,14 @@ module BABYLON {
 
         protected _activeEffect: Effect;
 
-        protected _normalMatrix : Matrix = new Matrix();
+        protected _normalMatrix: Matrix = new Matrix();
+
+        /**
+         * Gets or sets a boolean indicating that the material is allowed to do shader hot swapping. 
+         * This means that the material can keep using a previous shader while a new one is being compiled.
+         * This is mostly used when shader parallel compilation is supported (true by default)
+         */
+        public allowShaderHotSwapping = true;
 
         constructor(name: string, scene: Scene) {
             super(name, scene);
@@ -30,11 +37,11 @@ module BABYLON {
             return this.isReadyForSubMesh(mesh, mesh.subMeshes[0], useInstances);
         }
 
-         /**
-         * Binds the given world matrix to the active effect
-         *
-         * @param world the matrix to bind
-         */
+        /**
+        * Binds the given world matrix to the active effect
+        *
+        * @param world the matrix to bind
+        */
         public bindOnlyWorldMatrix(world: Matrix): void {
             this._activeEffect.setMatrix("world", world);
         }

+ 15 - 4
src/Materials/babylon.standardMaterial.ts

@@ -983,7 +983,6 @@ module BABYLON {
             // Get correct effect
             if (defines.isDirty) {
                 defines.markAsProcessed();
-                scene.resetCachedMaterial();
 
                 // Fallbacks
                 var fallbacks = new EffectFallbacks();
@@ -1106,7 +1105,9 @@ module BABYLON {
                 }
 
                 var join = defines.toString();
-                subMesh.setEffect(scene.getEngine().createEffect(shaderName, <EffectCreationOptions>{
+
+                let previousEffect = subMesh.effect;
+                let effect = scene.getEngine().createEffect(shaderName, <EffectCreationOptions>{
                     attributes: attribs,
                     uniformsNames: uniforms,
                     uniformBuffersNames: uniformBuffers,
@@ -1116,9 +1117,19 @@ module BABYLON {
                     onCompiled: this.onCompiled,
                     onError: this.onError,
                     indexParameters: { maxSimultaneousLights: this._maxSimultaneousLights, maxSimultaneousMorphTargets: defines.NUM_MORPH_INFLUENCERS }
-                }, engine), defines);
+                }, engine);
 
-                this.buildUniformLayout();
+                if (effect) {
+                    // Use previous effect while new one is compiling
+                    if (this.allowShaderHotSwapping && previousEffect && !effect.isReady()) {
+                        effect = previousEffect;
+                        defines.markAsUnprocessed();
+                    } else {
+                        scene.resetCachedMaterial();
+                        subMesh.setEffect(effect, defines);
+                        this.buildUniformLayout();
+                    }
+                }
             }
 
             if (!subMesh.effect || !subMesh.effect.isReady()) {