Forráskód Böngészése

Start optimizer block

David Catuhe 6 éve
szülő
commit
621c179899

+ 3 - 3
src/Engines/engine.ts

@@ -5643,7 +5643,7 @@ export class Engine {
         if (multiviewTexture._colorTextureArray && multiviewTexture._depthStencilTextureArray) {
             ext.framebufferTextureMultiviewWEBGL(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, multiviewTexture._colorTextureArray, 0, 0, 2);
             ext.framebufferTextureMultiviewWEBGL(gl.DRAW_FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, multiviewTexture._depthStencilTextureArray, 0, 0, 2);
-        }else {
+        } else {
             throw "Invalid multiview frame buffer";
         }
     }
@@ -6510,7 +6510,7 @@ export class Engine {
 
             if (texture && texture.isMultiview) {
                 this._gl.bindTexture(target, texture ? texture._colorTextureArray : null);
-            }else {
+            } else {
                 this._gl.bindTexture(target, texture ? texture._webGLTexture : null);
             }
 
@@ -6708,7 +6708,7 @@ export class Engine {
             if (needToBind) {
                 this._bindTextureDirectly(this._gl.TEXTURE_2D_ARRAY, internalTexture, isPartOfTextureArray);
             }
-        }else if (internalTexture && internalTexture.is3D) {
+        } else if (internalTexture && internalTexture.is3D) {
             if (needToBind) {
                 this._bindTextureDirectly(this._gl.TEXTURE_3D, internalTexture, isPartOfTextureArray);
             }

+ 2 - 1
src/Materials/Node/Blocks/Fragment/index.ts

@@ -4,4 +4,5 @@ export * from "./alphaTestBlock";
 export * from "./rgbaMergerBlock";
 export * from "./rgbMergerBlock";
 export * from "./rgbaSplitterBlock";
-export * from "./rgbSplitterBlock";
+export * from "./rgbSplitterBlock";
+export * from "./textureBlock";

+ 165 - 0
src/Materials/Node/Blocks/Fragment/textureBlock.ts

@@ -0,0 +1,165 @@
+import { NodeMaterialBlock } from '../../nodeMaterialBlock';
+import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
+import { NodeMaterialBuildState } from '../../nodeMaterialBuildState';
+import { NodeMaterialBlockTargets } from '../../nodeMaterialBlockTargets';
+import { NodeMaterialConnectionPoint } from '../../nodeMaterialBlockConnectionPoint';
+import { BaseTexture } from '../../../Textures/baseTexture';
+import { AbstractMesh } from '../../../../Meshes/abstractMesh';
+import { NodeMaterial } from '../../nodeMaterial';
+import { MaterialDefines } from '../../../materialDefines';
+
+/**
+ * Block used to read a texture from a sampler
+ */
+export class TextureBlock extends NodeMaterialBlock {
+    private _defineName: string;
+
+    /**
+     * Gets or sets a boolean indicating that the block can automatically fetch the texture matrix
+     */
+    public autoConnectTextureMatrix = true;
+
+    /**
+     * Gets or sets a boolean indicating that the block can automatically select the uv channel based on texture
+     */
+    public autoSelectUV = true;
+
+    /**
+     * Create a new TextureBlock
+     * @param name defines the block name
+     */
+    public constructor(name: string) {
+        super(name, NodeMaterialBlockTargets.Fragment);
+
+        this.registerInput("uv", NodeMaterialBlockConnectionPointTypes.Vector2);
+        this.registerInput("textureInfo", NodeMaterialBlockConnectionPointTypes.Vector2, true);
+
+        this.registerInput("transformedUV", NodeMaterialBlockConnectionPointTypes.Vector2, false, NodeMaterialBlockTargets.Vertex);
+        this.registerInput("texture", NodeMaterialBlockConnectionPointTypes.Texture, false, NodeMaterialBlockTargets.Fragment);
+        this.registerInput("textureTransform", NodeMaterialBlockConnectionPointTypes.Matrix, true, NodeMaterialBlockTargets.Vertex);
+
+        this.registerOutput("color", NodeMaterialBlockConnectionPointTypes.Color4);
+
+        // Auto configuration
+        this._inputs[0].setAsAttribute();
+        this._inputs[0].connectTo(this._inputs[2]);
+    }
+
+    /**
+     * Gets the current class name
+     * @returns the class name
+     */
+    public getClassName() {
+        return "TextureBlock";
+    }
+
+    /**
+     * Gets the uv input component
+     */
+    public get uv(): NodeMaterialConnectionPoint {
+        return this._inputs[0];
+    }
+
+    /**
+     * Gets the texture information input component
+     */
+    public get textureInfo(): NodeMaterialConnectionPoint {
+        return this._inputs[1];
+    }
+
+    /**
+     * Gets the transformed uv input component
+     */
+    public get transformedUV(): NodeMaterialConnectionPoint {
+        return this._inputs[2];
+    }
+
+    /**
+     * Gets the texture input component
+     */
+    public get texture(): NodeMaterialConnectionPoint {
+        return this._inputs[3];
+    }
+
+    /**
+     * Gets the texture transform input component
+     */
+    public get textureTransform(): NodeMaterialConnectionPoint {
+        return this._inputs[4];
+    }
+
+    public initialize(state: NodeMaterialBuildState) {
+        if (this.texture.value && this.texture.value.getTextureMatrix) {
+            const texture = this.texture.value as BaseTexture;
+
+            if (this.autoConnectTextureMatrix) {
+                this.textureTransform.valueCallback = () => texture.getTextureMatrix();
+            }
+            if (this.autoSelectUV) {
+                this.uv.setAsAttribute("uv" + (texture.coordinatesIndex ? (texture.coordinatesIndex + 1) : ""));
+            }
+        }
+    }
+
+    public prepareDefines(mesh: AbstractMesh, nodeMaterial: NodeMaterial, defines: MaterialDefines) {
+        if (!this.texture.value || !this.texture.value.getTextureMatrix) {
+            return;
+        }
+
+        const texture = this.texture.value as BaseTexture;
+
+        if (!texture.getTextureMatrix().isIdentityAs3x2()) {
+            defines[this._defineName] = true;
+        } else {
+            defines[this._defineName] = false;
+        }
+    }
+
+    private _injectVertexCode(state: NodeMaterialBuildState) {
+        let uvInput = this.uv;
+        let transformedUV = this.transformedUV;
+        let textureTransform = this.textureTransform;
+        let isTextureTransformConnected = textureTransform.connectedPoint != null || textureTransform.isUniform;
+
+        // Inject code in vertex
+        transformedUV.associatedVariableName = state._getFreeVariableName(transformedUV.name);
+        state._emitVaryings(transformedUV, true);
+
+        textureTransform.associatedVariableName = state._getFreeVariableName(textureTransform.name);
+        state._emitUniformOrAttributes(textureTransform);
+
+        this._defineName = state._getFreeDefineName("UVTRANSFORM");
+
+        if (isTextureTransformConnected) {
+            state.compilationString += `#ifdef ${this._defineName}\r\n`;
+            state.compilationString += `${transformedUV.associatedVariableName} = vec2(${textureTransform.associatedVariableName} * vec4(${uvInput.associatedVariableName}, 1.0, 0.0));\r\n`;
+            state.compilationString += `#else\r\n`;
+            state.compilationString += `${transformedUV.associatedVariableName} = ${uvInput.associatedVariableName};\r\n`;
+            state.compilationString += `#endif\r\n`;
+        } else {
+            state.compilationString += `${transformedUV.associatedVariableName} = ${uvInput.associatedVariableName};\r\n`;
+        }
+    }
+
+    protected _buildBlock(state: NodeMaterialBuildState) {
+        super._buildBlock(state);
+
+        // Vertex
+        this._injectVertexCode(state._vertexState);
+
+        // Fragment
+        state.sharedData.blocksWithDefines.push(this);
+
+        let transformedUV = this.transformedUV;
+        let textureInfo = this.textureInfo;
+        let samplerInput = this.texture;
+        let output = this._outputs[0];
+        let isTextureInfoConnected = textureInfo.connectedPoint != null || textureInfo.isUniform;
+        const complement = isTextureInfoConnected ? ` * ${textureInfo.associatedVariableName}.y` : "";
+
+        state.compilationString += `vec4 ${output.associatedVariableName} = texture2D(${samplerInput.associatedVariableName}, ${transformedUV.associatedVariableName})${complement};\r\n`;
+
+
+        return this;
+    }
+}

+ 0 - 1
src/Materials/Node/Blocks/index.ts

@@ -4,7 +4,6 @@ export * from "./Dual/index";
 export * from "./multiplyBlock";
 export * from "./addBlock";
 export * from "./clampBlock";
-export * from "./textureBlock";
 export * from "./vector2TransformBlock";
 export * from "./vector3TransformBlock";
 export * from "./vector4TransformBlock";

+ 0 - 130
src/Materials/Node/Blocks/textureBlock.ts

@@ -1,130 +0,0 @@
-import { NodeMaterialBlock } from '../nodeMaterialBlock';
-import { NodeMaterialBlockConnectionPointTypes } from '../nodeMaterialBlockConnectionPointTypes';
-import { NodeMaterialBuildState } from '../nodeMaterialBuildState';
-import { NodeMaterialBlockTargets } from '../nodeMaterialBlockTargets';
-import { NodeMaterialConnectionPoint } from '../nodeMaterialBlockConnectionPoint';
-import { BaseTexture } from '../../Textures/baseTexture';
-
-/**
- * Block used to read a texture from a sampler
- */
-export class TextureBlock extends NodeMaterialBlock {
-
-    /**
-     * Gets or sets a boolean indicating that the block can automatically fetch the texture matrix
-     */
-    public autoConnectTextureMatrix = true;
-
-    /**
-     * Gets or sets a boolean indicating that the block can automatically select the uv channel based on texture
-     */
-    public autoSelectUV = true;
-
-    /**
-     * Create a new TextureBlock
-     * @param name defines the block name
-     */
-    public constructor(name: string) {
-        super(name, NodeMaterialBlockTargets.VertexAndFragment);
-
-        this.registerInput("uv", NodeMaterialBlockConnectionPointTypes.Vector2);
-        this.registerInput("textureInfo", NodeMaterialBlockConnectionPointTypes.Vector2, true);
-
-        this.registerInput("transformedUV", NodeMaterialBlockConnectionPointTypes.Vector2);
-        this.registerInput("texture", NodeMaterialBlockConnectionPointTypes.Texture);
-        this.registerInput("textureTransform", NodeMaterialBlockConnectionPointTypes.Matrix, true);
-
-        this.registerOutput("color", NodeMaterialBlockConnectionPointTypes.Color4);
-
-        // Auto configuration
-        this._inputs[0].setAsAttribute();
-        this._inputs[0].connectTo(this._inputs[2]);
-    }
-
-    /**
-     * Gets the current class name
-     * @returns the class name
-     */
-    public getClassName() {
-        return "TextureBlock";
-    }
-
-    /**
-     * Gets the uv input component
-     */
-    public get uv(): NodeMaterialConnectionPoint {
-        return this._inputs[0];
-    }
-
-    /**
-     * Gets the texture information input component
-     */
-    public get textureInfo(): NodeMaterialConnectionPoint {
-        return this._inputs[1];
-    }
-
-    /**
-     * Gets the transformed uv input component
-     */
-    public get transformedUV(): NodeMaterialConnectionPoint {
-        return this._inputs[2];
-    }
-
-    /**
-     * Gets the texture input component
-     */
-    public get texture(): NodeMaterialConnectionPoint {
-        return this._inputs[3];
-    }
-
-    /**
-     * Gets the texture transform input component
-     */
-    public get textureTransform(): NodeMaterialConnectionPoint {
-        return this._inputs[4];
-    }
-
-    public initialize(state: NodeMaterialBuildState) {
-        if (this.texture.value && this.texture.value.getTextureMatrix) {
-            const texture = this.texture.value as BaseTexture;
-
-            if (this.autoConnectTextureMatrix) {
-                this.textureTransform.valueCallback = () => texture.getTextureMatrix();
-            }
-            if (this.autoSelectUV) {
-                this.uv.setAsAttribute("uv" + (texture.coordinatesIndex ? (texture.coordinatesIndex + 1) : ""));
-            }
-        }
-    }
-
-    protected _buildBlock(state: NodeMaterialBuildState) {
-        super._buildBlock(state);
-
-        let uvInput = this.uv;
-        let transformedUV = this.transformedUV;
-        let textureInfo = this.textureInfo;
-        let textureTransform = this.textureTransform;
-        let isTextureInfoConnected = textureInfo.connectedPoint != null || textureInfo.isUniform;
-        let isTextureTransformConnected = textureTransform.connectedPoint != null || textureTransform.isUniform;
-
-        if (state.target === NodeMaterialBlockTargets.Fragment) { // Fragment
-            let samplerInput = this.texture;
-
-            let output = this._outputs[0];
-
-            const complement = isTextureInfoConnected ? ` * ${textureInfo.associatedVariableName}.y` : "";
-
-            state.compilationString += `vec4 ${output.associatedVariableName} = texture2D(${samplerInput.associatedVariableName}, ${transformedUV.associatedVariableName})${complement};\r\n`;
-        } else { // Vertex
-            transformedUV.associatedVariableName = state._getFreeVariableName(transformedUV.name);
-            state._emitVaryings(transformedUV, true);
-
-            if (isTextureTransformConnected) {
-                state.compilationString += `${transformedUV.associatedVariableName} =  vec2(${textureTransform.associatedVariableName} * vec4(${uvInput.associatedVariableName}, 1.0, 0.0));`;
-            } else {
-                state.compilationString += `${transformedUV.associatedVariableName} =  ${uvInput.associatedVariableName};`;
-            }
-        }
-        return this;
-    }
-}

+ 2 - 0
src/Materials/Node/Optimizers/index.ts

@@ -0,0 +1,2 @@
+export * from "./nodeMaterialOptimizer";
+export * from "./uvVaryingOptimizer";

+ 15 - 0
src/Materials/Node/Optimizers/nodeMaterialOptimizer.ts

@@ -0,0 +1,15 @@
+import { NodeMaterialBlock } from '../nodeMaterialBlock';
+
+/**
+ * Root class for all node material optimizers
+ */
+export class NodeMaterialOptimizer {
+    /**
+     * Function used to optimize a NodeMaterial graph
+     * @param vertexShaderBuildState 
+     * @param fragmentShaderBuildState 
+     */
+    public optimize(vertexOutputNodes: NodeMaterialBlock[], fragmentOutputNodes: NodeMaterialBlock[]) {
+        // Do nothing by default
+    }
+}

+ 11 - 0
src/Materials/Node/Optimizers/uvVaryingOptimizer.ts

@@ -0,0 +1,11 @@
+import { NodeMaterialOptimizer } from './nodeMaterialOptimizer';
+import { NodeMaterialBlock } from '../nodeMaterialBlock';
+
+/**
+ * Optimizer that will try to reuse uv varyings across the node graph
+ */
+export class UVVaryingOptimizer extends NodeMaterialOptimizer {
+    public optimize(vertexOutputNodes: NodeMaterialBlock[], fragmentOutputNodes: NodeMaterialBlock[]) {
+
+    }
+}

+ 2 - 1
src/Materials/Node/index.ts

@@ -4,4 +4,5 @@ export * from "./nodeMaterialBlockConnectionPoint";
 export * from "./nodeMaterialBlock";
 export * from "./nodeMaterial";
 export * from "./nodeMaterialWellKnownValues";
-export * from "./Blocks/index";
+export * from "./Blocks/index";
+export * from "./Optimizers/index";

+ 60 - 8
src/Materials/Node/nodeMaterial.ts

@@ -15,6 +15,8 @@ import { NodeMaterialBlockTargets } from './nodeMaterialBlockTargets';
 import { NodeMaterialBuildStateSharedData } from './NodeMaterialBuildStateSharedData';
 import { SubMesh } from '../../Meshes/subMesh';
 import { MaterialDefines } from '../../Materials/materialDefines';
+import { NodeMaterialOptimizer } from './Optimizers/nodeMaterialOptimizer';
+import { UVVaryingOptimizer } from './Optimizers/uvVaryingOptimizer';
 
 /** @hidden */
 export class NodeMaterialDefines extends MaterialDefines {
@@ -56,6 +58,7 @@ export class NodeMaterial extends PushMaterial {
     private _cachedWorldViewMatrix = new Matrix();
     private _cachedWorldViewProjectionMatrix = new Matrix();
     private _textureConnectionPoints = new Array<NodeMaterialConnectionPoint>();
+    private _optimizers = new Array<NodeMaterialOptimizer>();
 
     /**
      * Observable raised when the material is built
@@ -94,6 +97,9 @@ export class NodeMaterial extends PushMaterial {
             emitComments: false,
             ...options
         };
+
+        // Registers some optimizers
+        this.registerOptimizer(new UVVaryingOptimizer());
     }
 
     /**
@@ -105,6 +111,40 @@ export class NodeMaterial extends PushMaterial {
     }
 
     /**
+     * Adds a new optimizer to the list of optimizers
+     * @param optimizer defines the optimizers to add
+     * @returns the current material
+     */
+    public registerOptimizer(optimizer: NodeMaterialOptimizer) {
+        let index = this._optimizers.indexOf(optimizer);
+
+        if (index > -1) {
+            return;
+        }
+
+        this._optimizers.push(optimizer);
+
+        return this;
+    }
+
+    /**
+     * Remove an optimizer from the list of optimizers
+     * @param optimizer defines the optimizers to remove
+     * @returns the current material
+     */
+    public unregisterOptimizer(optimizer: NodeMaterialOptimizer) {
+        let index = this._optimizers.indexOf(optimizer);
+
+        if (index === -1) {
+            return;
+        }
+
+        this._optimizers.splice(index, 1);
+
+        return this;
+    }
+
+    /**
      * Add a new block to the list of output nodes
      * @param node defines the node to add
      * @returns the current material
@@ -273,6 +313,9 @@ export class NodeMaterial extends PushMaterial {
             this._initializeBlock(fragmentOutputNode, this._fragmentCompilationState);
         }
 
+        // Optimize
+        this.optimize();
+
         // Vertex
         for (var vertexOutputNode of this._vertexOutputNodes) {
             vertexOutputNode.build(this._vertexCompilationState);
@@ -312,14 +355,23 @@ export class NodeMaterial extends PushMaterial {
         this.onBuildObservable.notifyObservers(this);
     }
 
-   /**
-     * Get if the submesh is ready to be used and all its information available.
-     * Child classes can use it to update shaders
-     * @param mesh defines the mesh to check
-     * @param subMesh defines which submesh to check
-     * @param useInstances specifies that instances should be used
-     * @returns a boolean indicating that the submesh is ready or not
+    /**
+     * Runs an otpimization phase to try to improve the shader code
      */
+    public optimize() {
+        for (var optimizer of this._optimizers) {
+            optimizer.optimize(this._vertexOutputNodes, this._fragmentOutputNodes);
+        }
+    }
+
+    /**
+      * Get if the submesh is ready to be used and all its information available.
+      * Child classes can use it to update shaders
+      * @param mesh defines the mesh to check
+      * @param subMesh defines which submesh to check
+      * @param useInstances specifies that instances should be used
+      * @returns a boolean indicating that the submesh is ready or not
+      */
     public isReadyForSubMesh(mesh: AbstractMesh, subMesh: SubMesh, useInstances: boolean = false): boolean {
         if (!this._buildWasSuccessful) {
             return false;
@@ -347,7 +399,7 @@ export class NodeMaterial extends PushMaterial {
         // Textures
         for (var connectionPoint of this._textureConnectionPoints) {
             let texture = connectionPoint.value as BaseTexture;
-            if (texture && !texture.isReady()) {
+            if (texture && !texture.isReadyOrNotBlocking()) {
                 return false;
             }
         }

+ 7 - 0
src/Materials/Node/nodeMaterialBlock.ts

@@ -310,6 +310,10 @@ export class NodeMaterialBlock {
                 continue;
             }
 
+            if ((input.target & state.target!) === 0) {
+                continue;
+            }
+
             let block = input.connectedPoint.ownerBlock;
             if (block && block !== this && block.buildId !== state.sharedData.buildId) {
                 block.build(state);
@@ -339,6 +343,9 @@ export class NodeMaterialBlock {
             if ((input.target & this.target!) === 0) {
                 continue;
             }
+            if ((input.target & state.target!) === 0) {
+                continue;
+            }
             state._emitUniformOrAttributes(input);
         }
 

+ 1 - 7
src/Materials/Node/nodeMaterialBlockConnectionPoint.ts

@@ -106,12 +106,6 @@ export class NodeMaterialConnectionPoint {
      * Can only be set on exit points
      */
     public get isVarying(): boolean {
-        for (var connectedBlock of this.connectedBlocks) {
-            if (connectedBlock.target && this.ownerBlock.target && (connectedBlock.target & this.ownerBlock.target) === 0) {
-                return true;
-            }
-        }
-
         return this._isVarying;
     }
 
@@ -322,7 +316,7 @@ export class NodeMaterialConnectionPoint {
             case NodeMaterialBlockConnectionPointTypes.Color3:
                 effect.setColor3(this.name, value);
                 break;
-                case NodeMaterialBlockConnectionPointTypes.Color4:
+            case NodeMaterialBlockConnectionPointTypes.Color4:
                 effect.setDirectColor4(this.name, value);
                 break;
             case NodeMaterialBlockConnectionPointTypes.Vector2:

+ 20 - 4
src/Materials/Node/nodeMaterialBuildState.ts

@@ -98,6 +98,17 @@ export class NodeMaterialBuildState {
     }
 
     /** @hidden */
+    public _getFreeDefineName(prefix: string): string {
+        if (this.sharedData.defineNames[prefix] === undefined) {
+            this.sharedData.defineNames[prefix] = 0;
+        } else {
+            this.sharedData.defineNames[prefix]++;
+        }
+
+        return prefix + this.sharedData.defineNames[prefix];
+    }
+
+    /** @hidden */
     public _excludeVariableName(name: string) {
         this.sharedData.variableNames[name] = 0;
     }
@@ -240,16 +251,18 @@ export class NodeMaterialBuildState {
             return;
         }
 
-        point.associatedVariableName = point.name;
+        if (!point.associatedVariableName) {
+            point.associatedVariableName = point.name;
+        }
 
         // Uniforms
         if (point.isUniform) {
-            if (this.uniforms.indexOf(point.name) !== -1) {
+            if (this.uniforms.indexOf(point.associatedVariableName) !== -1) {
                 return;
             }
 
-            this.uniforms.push(point.name);
-            this._uniformDeclaration += `uniform ${this._getGLType(point.type)} ${point.name};\r\n`;
+            this.uniforms.push(point.associatedVariableName);
+            this._uniformDeclaration += `uniform ${this._getGLType(point.type)} ${point.associatedVariableName};\r\n`;
 
             // well known
             let hints = this.sharedData.hints;
@@ -272,6 +285,9 @@ export class NodeMaterialBuildState {
         if (point.isAttribute) {
             if (this.target === NodeMaterialBlockTargets.Fragment) { // Attribute for fragment need to be carried over by varyings
                 this._vertexState._emitUniformOrAttributes(point);
+                if (point.associatedVariableName) {
+                    return;
+                }
                 point.associatedVariableName = this._getFreeVariableName(point.name);
                 this._emitVaryings(point, true);
                 this._vertexState._emitVaryings(point, true, true);

+ 13 - 0
src/Materials/Node/nodeMaterialBuildStateSharedData.ts

@@ -43,6 +43,9 @@ export class NodeMaterialBuildStateSharedData {
     /** List of emitted variables */
     public variableNames: { [key: string]: number } = {};
 
+    /** List of emitted defines */
+    public defineNames: { [key: string]: number } = {};
+
     /** Should emit comments? */
     public emitComments: boolean;
 
@@ -86,6 +89,16 @@ export class NodeMaterialBuildStateSharedData {
         this.variableNames["matricesWeights"] = 0;
         this.variableNames["matricesIndicesExtra"] = 0;
         this.variableNames["matricesWeightsExtra"] = 0;
+
+        // Exclude defines
+        this.defineNames["MAINUV0"] = 0;
+        this.defineNames["MAINUV1"] = 0;
+        this.defineNames["MAINUV2"] = 0;
+        this.defineNames["MAINUV3"] = 0;
+        this.defineNames["MAINUV4"] = 0;
+        this.defineNames["MAINUV5"] = 0;
+        this.defineNames["MAINUV6"] = 0;
+        this.defineNames["MAINUV7"] = 0;
     }
 
     /**