Browse Source

Added first step shader processor

David Catuhe 6 years ago
parent
commit
5211378f98
3 changed files with 202 additions and 172 deletions
  1. 175 0
      src/Engines/Processors/shaderProcessor.ts
  2. 1 0
      src/Engines/index.ts
  3. 26 172
      src/Materials/effect.ts

+ 175 - 0
src/Engines/Processors/shaderProcessor.ts

@@ -0,0 +1,175 @@
+import { Effect } from '../../Materials/effect';
+import { Tools } from '../../Misc/tools';
+
+interface ProcessingOptions {
+    defines: string,
+    indexParameters: any,
+    isFragment: boolean,
+    shouldUseHighPrecisionShader: boolean,
+    needProcessing: boolean,
+    supportsUniformBuffers: boolean
+}
+
+/** @hidden */
+export class ShaderProcessor {
+    public static Process(sourceCode: string, options: ProcessingOptions, callback: (migratedCode: string) => void) {
+        this._ProcessIncludes(sourceCode, options, (codeWithIncludes) => {
+            let migratedCode = this._ProcessShaderConversion(codeWithIncludes, options);
+            callback(migratedCode);
+        });
+    }
+
+    private static _ProcessPrecision(source: string, options: ProcessingOptions): string {
+        const shouldUseHighPrecisionShader = options.shouldUseHighPrecisionShader;
+
+        if (source.indexOf("precision highp float") === -1) {
+            if (!shouldUseHighPrecisionShader) {
+                source = "precision mediump float;\n" + source;
+            } else {
+                source = "precision highp float;\n" + source;
+            }
+        } else {
+            if (!shouldUseHighPrecisionShader) { // Moving highp to mediump
+                source = source.replace("precision highp float", "precision mediump float");
+            }
+        }
+
+        return source;
+    }
+
+    private static _ProcessShaderConversion(sourceCode: string, options: ProcessingOptions): string {
+
+        var preparedSourceCode = this._ProcessPrecision(sourceCode, options);
+
+        if (!options.needProcessing) {
+            return preparedSourceCode;
+        }
+
+        // Already converted
+        if (preparedSourceCode.indexOf("#version 3") !== -1) {
+            return preparedSourceCode.replace("#version 300 es", "");
+        }
+
+        let preprocessors = options.defines.split("\n");
+
+        var hasDrawBuffersExtension = preparedSourceCode.search(/#extension.+GL_EXT_draw_buffers.+require/) !== -1;
+
+        // Remove extensions
+        // #extension GL_OES_standard_derivatives : enable
+        // #extension GL_EXT_shader_texture_lod : enable
+        // #extension GL_EXT_frag_depth : enable
+        // #extension GL_EXT_draw_buffers : require
+        var regex = /#extension.+(GL_OVR_multiview2|GL_OES_standard_derivatives|GL_EXT_shader_texture_lod|GL_EXT_frag_depth|GL_EXT_draw_buffers).+(enable|require)/g;
+        var result = preparedSourceCode.replace(regex, "");
+
+        // Migrate to GLSL v300
+        let isFragment = options.isFragment;
+        result = result.replace(/varying(?![\n\r])\s/g, isFragment ? "in " : "out ");
+        result = result.replace(/attribute[ \t]/g, "in ");
+        result = result.replace(/[ \t]attribute/g, " in");
+
+        result = result.replace(/texture2D\s*\(/g, "texture(");
+        if (isFragment) {
+            result = result.replace(/texture2DLodEXT\s*\(/g, "textureLod(");
+            result = result.replace(/textureCubeLodEXT\s*\(/g, "textureLod(");
+            result = result.replace(/textureCube\s*\(/g, "texture(");
+            result = result.replace(/gl_FragDepthEXT/g, "gl_FragDepth");
+            result = result.replace(/gl_FragColor/g, "glFragColor");
+            result = result.replace(/gl_FragData/g, "glFragData");
+            result = result.replace(/void\s+?main\s*\(/g, (hasDrawBuffersExtension ? "" : "out vec4 glFragColor;\n") + "void main(");
+        }
+
+        // Add multiview setup to top of file when defined
+        var hasMultiviewExtension = preprocessors.indexOf("#define MULTIVIEW") !== -1;
+        if (hasMultiviewExtension && !isFragment) {
+            result = "#extension GL_OVR_multiview2 : require\nlayout (num_views = 2) in;\n" + result;
+        }
+
+        return result;
+    }
+
+    private static _ProcessIncludes(sourceCode: string, options: ProcessingOptions, callback: (data: any) => void): void {
+        var regex = /#include<(.+)>(\((.*)\))*(\[(.*)\])*/g;
+        var match = regex.exec(sourceCode);
+
+        var returnValue = new String(sourceCode);
+
+        while (match != null) {
+            var includeFile = match[1];
+
+            // Uniform declaration
+            if (includeFile.indexOf("__decl__") !== -1) {
+                includeFile = includeFile.replace(/__decl__/, "");
+                if (options.supportsUniformBuffers) {
+                    includeFile = includeFile.replace(/Vertex/, "Ubo");
+                    includeFile = includeFile.replace(/Fragment/, "Ubo");
+                }
+                includeFile = includeFile + "Declaration";
+            }
+
+            if (Effect.IncludesShadersStore[includeFile]) {
+                // Substitution
+                var includeContent = Effect.IncludesShadersStore[includeFile];
+                if (match[2]) {
+                    var splits = match[3].split(",");
+
+                    for (var index = 0; index < splits.length; index += 2) {
+                        var source = new RegExp(splits[index], "g");
+                        var dest = splits[index + 1];
+
+                        includeContent = includeContent.replace(source, dest);
+                    }
+                }
+
+                if (match[4]) {
+                    var indexString = match[5];
+
+                    if (indexString.indexOf("..") !== -1) {
+                        var indexSplits = indexString.split("..");
+                        var minIndex = parseInt(indexSplits[0]);
+                        var maxIndex = parseInt(indexSplits[1]);
+                        var sourceIncludeContent = includeContent.slice(0);
+                        includeContent = "";
+
+                        if (isNaN(maxIndex)) {
+                            maxIndex = options.indexParameters[indexSplits[1]];
+                        }
+
+                        for (var i = minIndex; i < maxIndex; i++) {
+                            if (!options.supportsUniformBuffers) {
+                                // Ubo replacement
+                                sourceIncludeContent = sourceIncludeContent.replace(/light\{X\}.(\w*)/g, (str: string, p1: string) => {
+                                    return p1 + "{X}";
+                                });
+                            }
+                            includeContent += sourceIncludeContent.replace(/\{X\}/g, i.toString()) + "\n";
+                        }
+                    } else {
+                        if (!options.supportsUniformBuffers) {
+                            // Ubo replacement
+                            includeContent = includeContent.replace(/light\{X\}.(\w*)/g, (str: string, p1: string) => {
+                                return p1 + "{X}";
+                            });
+                        }
+                        includeContent = includeContent.replace(/\{X\}/g, indexString);
+                    }
+                }
+
+                // Replace
+                returnValue = returnValue.replace(match[0], includeContent);
+            } else {
+                var includeShaderUrl = Effect.ShadersRepository + "ShadersInclude/" + includeFile + ".fx";
+
+                Tools.LoadFile(includeShaderUrl, (fileContent) => {
+                    Effect.IncludesShadersStore[includeFile] = fileContent as string;
+                    this._ProcessIncludes(<string>returnValue, options, callback);
+                });
+                return;
+            }
+
+            match = regex.exec(sourceCode);
+        }
+
+        callback(returnValue);
+    }
+}

+ 1 - 0
src/Engines/index.ts

@@ -4,4 +4,5 @@ export * from "./engineStore";
 export * from "./nullEngine";
 export * from "./nullEngine";
 export * from "./Extensions/index";
 export * from "./Extensions/index";
 export * from "./IPipelineContext";
 export * from "./IPipelineContext";
+export * from "./Processors/shaderProcessor";
 export * from "./WebGL/webGLPipelineContext";
 export * from "./WebGL/webGLPipelineContext";

+ 26 - 172
src/Materials/effect.ts

@@ -7,6 +7,7 @@ import { Logger } from "../Misc/logger";
 import { IDisposable } from '../scene';
 import { IDisposable } from '../scene';
 import { IPipelineContext } from '../Engines/IPipelineContext';
 import { IPipelineContext } from '../Engines/IPipelineContext';
 import { DataBuffer } from '../Meshes/dataBuffer';
 import { DataBuffer } from '../Meshes/dataBuffer';
+import { ShaderProcessor } from '../Engines/Processors/shaderProcessor';
 
 
 declare type Engine = import("../Engines/engine").Engine;
 declare type Engine = import("../Engines/engine").Engine;
 declare type InternalTexture = import("../Materials/Textures/internalTexture").InternalTexture;
 declare type InternalTexture = import("../Materials/Textures/internalTexture").InternalTexture;
@@ -78,7 +79,7 @@ export class EffectFallbacks {
     }
     }
 
 
     /**
     /**
-     * Removes the defines that shoould be removed when falling back.
+     * Removes the defines that should be removed when falling back.
      * @param currentDefines defines the current define statements for the shader.
      * @param currentDefines defines the current define statements for the shader.
      * @param effect defines the current effect we try to compile
      * @param effect defines the current effect we try to compile
      * @returns The resulting defines with defines of the current rank removed.
      * @returns The resulting defines with defines of the current rank removed.
@@ -347,25 +348,31 @@ export class Effect implements IDisposable {
             fragmentSource = baseName.fragment || baseName;
             fragmentSource = baseName.fragment || baseName;
         }
         }
 
 
+        let processorOptions = {
+            defines: this.defines,
+            indexParameters: this._indexParameters,
+            isFragment: false,
+            shouldUseHighPrecisionShader: this._engine._shouldUseHighPrecisionShader,
+            needProcessing: this._engine.webGLVersion > 1,
+            supportsUniformBuffers: this._engine.supportsUniformBuffers
+        };
+
         this._loadVertexShader(vertexSource, (vertexCode) => {
         this._loadVertexShader(vertexSource, (vertexCode) => {
-            this._processIncludes(vertexCode, (vertexCodeWithIncludes) => {
-                this._processShaderConversion(vertexCodeWithIncludes, false, (migratedVertexCode) => {
-                    this._loadFragmentShader(fragmentSource, (fragmentCode) => {
-                        this._processIncludes(fragmentCode, (fragmentCodeWithIncludes) => {
-                            this._processShaderConversion(fragmentCodeWithIncludes, true, (migratedFragmentCode) => {
-                                if (baseName) {
-                                    var vertex = baseName.vertexElement || baseName.vertex || baseName;
-                                    var fragment = baseName.fragmentElement || baseName.fragment || baseName;
-
-                                    this._vertexSourceCode = "#define SHADER_NAME vertex:" + vertex + "\n" + migratedVertexCode;
-                                    this._fragmentSourceCode = "#define SHADER_NAME fragment:" + fragment + "\n" + migratedFragmentCode;
-                                } else {
-                                    this._vertexSourceCode = migratedVertexCode;
-                                    this._fragmentSourceCode = migratedFragmentCode;
-                                }
-                                this._prepareEffect();
-                            });
-                        });
+            this._loadFragmentShader(fragmentSource, (fragmentCode) => {
+                ShaderProcessor.Process(vertexCode, processorOptions, (migratedVertexCode) => {
+                    processorOptions.isFragment = true;
+                    ShaderProcessor.Process(fragmentCode, processorOptions, (migratedFragmentCode) => {
+                        if (baseName) {
+                            var vertex = baseName.vertexElement || baseName.vertex || baseName;
+                            var fragment = baseName.fragmentElement || baseName.fragment || baseName;
+
+                            this._vertexSourceCode = "#define SHADER_NAME vertex:" + vertex + "\n" + migratedVertexCode;
+                            this._fragmentSourceCode = "#define SHADER_NAME fragment:" + fragment + "\n" + migratedFragmentCode;
+                        } else {
+                            this._vertexSourceCode = migratedVertexCode;
+                            this._fragmentSourceCode = migratedFragmentCode;
+                        }
+                        this._prepareEffect();
                     });
                     });
                 });
                 });
             });
             });
@@ -616,159 +623,6 @@ export class Effect implements IDisposable {
         }
         }
     }
     }
 
 
-    private _processShaderConversion(sourceCode: string, isFragment: boolean, callback: (data: any) => void): void {
-
-        var preparedSourceCode = this._processPrecision(sourceCode);
-
-        if (this._engine.webGLVersion == 1) {
-            callback(preparedSourceCode);
-            return;
-        }
-
-        // Already converted
-        if (preparedSourceCode.indexOf("#version 3") !== -1) {
-            callback(preparedSourceCode.replace("#version 300 es", ""));
-            return;
-        }
-
-        var hasDrawBuffersExtension = preparedSourceCode.search(/#extension.+GL_EXT_draw_buffers.+require/) !== -1;
-
-        // Remove extensions
-        // #extension GL_OES_standard_derivatives : enable
-        // #extension GL_EXT_shader_texture_lod : enable
-        // #extension GL_EXT_frag_depth : enable
-        // #extension GL_EXT_draw_buffers : require
-        var regex = /#extension.+(GL_OVR_multiview2|GL_OES_standard_derivatives|GL_EXT_shader_texture_lod|GL_EXT_frag_depth|GL_EXT_draw_buffers).+(enable|require)/g;
-        var result = preparedSourceCode.replace(regex, "");
-
-        // Migrate to GLSL v300
-        result = result.replace(/varying(?![\n\r])\s/g, isFragment ? "in " : "out ");
-        result = result.replace(/attribute[ \t]/g, "in ");
-        result = result.replace(/[ \t]attribute/g, " in");
-
-        result = result.replace(/texture2D\s*\(/g, "texture(");
-        if (isFragment) {
-            result = result.replace(/texture2DLodEXT\s*\(/g, "textureLod(");
-            result = result.replace(/textureCubeLodEXT\s*\(/g, "textureLod(");
-            result = result.replace(/textureCube\s*\(/g, "texture(");
-            result = result.replace(/gl_FragDepthEXT/g, "gl_FragDepth");
-            result = result.replace(/gl_FragColor/g, "glFragColor");
-            result = result.replace(/gl_FragData/g, "glFragData");
-            result = result.replace(/void\s+?main\s*\(/g, (hasDrawBuffersExtension ? "" : "out vec4 glFragColor;\n") + "void main(");
-        }
-
-        // Add multiview setup to top of file when defined
-        var hasMultiviewExtension = this.defines.indexOf("#define MULTIVIEW\n") !== -1;
-        if (hasMultiviewExtension && !isFragment) {
-            result = "#extension GL_OVR_multiview2 : require\nlayout (num_views = 2) in;\n" + result;
-        }
-
-        callback(result);
-    }
-
-    private _processIncludes(sourceCode: string, callback: (data: any) => void): void {
-        var regex = /#include<(.+)>(\((.*)\))*(\[(.*)\])*/g;
-        var match = regex.exec(sourceCode);
-
-        var returnValue = new String(sourceCode);
-
-        while (match != null) {
-            var includeFile = match[1];
-
-            // Uniform declaration
-            if (includeFile.indexOf("__decl__") !== -1) {
-                includeFile = includeFile.replace(/__decl__/, "");
-                if (this._engine.supportsUniformBuffers) {
-                    includeFile = includeFile.replace(/Vertex/, "Ubo");
-                    includeFile = includeFile.replace(/Fragment/, "Ubo");
-                }
-                includeFile = includeFile + "Declaration";
-            }
-
-            if (Effect.IncludesShadersStore[includeFile]) {
-                // Substitution
-                var includeContent = Effect.IncludesShadersStore[includeFile];
-                if (match[2]) {
-                    var splits = match[3].split(",");
-
-                    for (var index = 0; index < splits.length; index += 2) {
-                        var source = new RegExp(splits[index], "g");
-                        var dest = splits[index + 1];
-
-                        includeContent = includeContent.replace(source, dest);
-                    }
-                }
-
-                if (match[4]) {
-                    var indexString = match[5];
-
-                    if (indexString.indexOf("..") !== -1) {
-                        var indexSplits = indexString.split("..");
-                        var minIndex = parseInt(indexSplits[0]);
-                        var maxIndex = parseInt(indexSplits[1]);
-                        var sourceIncludeContent = includeContent.slice(0);
-                        includeContent = "";
-
-                        if (isNaN(maxIndex)) {
-                            maxIndex = this._indexParameters[indexSplits[1]];
-                        }
-
-                        for (var i = minIndex; i < maxIndex; i++) {
-                            if (!this._engine.supportsUniformBuffers) {
-                                // Ubo replacement
-                                sourceIncludeContent = sourceIncludeContent.replace(/light\{X\}.(\w*)/g, (str: string, p1: string) => {
-                                    return p1 + "{X}";
-                                });
-                            }
-                            includeContent += sourceIncludeContent.replace(/\{X\}/g, i.toString()) + "\n";
-                        }
-                    } else {
-                        if (!this._engine.supportsUniformBuffers) {
-                            // Ubo replacement
-                            includeContent = includeContent.replace(/light\{X\}.(\w*)/g, (str: string, p1: string) => {
-                                return p1 + "{X}";
-                            });
-                        }
-                        includeContent = includeContent.replace(/\{X\}/g, indexString);
-                    }
-                }
-
-                // Replace
-                returnValue = returnValue.replace(match[0], includeContent);
-            } else {
-                var includeShaderUrl = Effect.ShadersRepository + "ShadersInclude/" + includeFile + ".fx";
-
-                this._engine._loadFile(includeShaderUrl, (fileContent) => {
-                    Effect.IncludesShadersStore[includeFile] = fileContent as string;
-                    this._processIncludes(<string>returnValue, callback);
-                });
-                return;
-            }
-
-            match = regex.exec(sourceCode);
-        }
-
-        callback(returnValue);
-    }
-
-    private _processPrecision(source: string): string {
-        const shouldUseHighPrecisionShader = this._engine._shouldUseHighPrecisionShader;
-
-        if (source.indexOf("precision highp float") === -1) {
-            if (!shouldUseHighPrecisionShader) {
-                source = "precision mediump float;\n" + source;
-            } else {
-                source = "precision highp float;\n" + source;
-            }
-        } else {
-            if (!shouldUseHighPrecisionShader) { // Moving highp to mediump
-                source = source.replace("precision highp float", "precision mediump float");
-            }
-        }
-
-        return source;
-    }
-
     /**
     /**
      * Recompiles the webGL program
      * Recompiles the webGL program
      * @param vertexSourceCode The source code for the vertex shader.
      * @param vertexSourceCode The source code for the vertex shader.