Jelajahi Sumber

Merge pull request #7737 from Popov72/effect-error

Allow logging of shader code when a compilation error occurs
David Catuhe 5 tahun lalu
induk
melakukan
fa8c078389

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

@@ -8,6 +8,8 @@
 
 ### Engine
 
+- Allow logging of shader code when a compilation error occurs ([Popov72](https://github.com/Popov72))
+
 ### Physics
 - Ammo.js IDL exposed property update and raycast vehicle stablization support ([MackeyK24](https://github.com/MackeyK24))
 

+ 6 - 0
src/Engines/IPipelineContext.ts

@@ -13,5 +13,11 @@ export interface IPipelineContext {
     isReady: boolean;
 
     /** @hidden */
+    _getVertexShaderCode(): string | null;
+
+    /** @hidden */
+    _getFragmentShaderCode(): string | null;
+
+    /** @hidden */
     _handlesSpectorRebuildCallback(onCompiled: (compiledObject: any) => void): void;
 }

+ 8 - 0
src/Engines/WebGL/webGLPipelineContext.ts

@@ -38,4 +38,12 @@ export class WebGLPipelineContext implements IPipelineContext {
             onCompiled(this.program);
         }
     }
+
+    public _getVertexShaderCode(): string | null {
+        return this.vertexShader ? this.engine._getShaderSource(this.vertexShader) : null;
+    }
+
+    public _getFragmentShaderCode(): string | null {
+        return this.fragmentShader ? this.engine._getShaderSource(this.fragmentShader) : null;
+    }
 }

+ 8 - 0
src/Engines/nativeEngine.ts

@@ -107,6 +107,14 @@ class NativePipelineContext implements IPipelineContext {
     public isAsync = false;
     public isReady = false;
 
+    public _getVertexShaderCode(): string | null {
+        return null;
+    }
+
+    public _getFragmentShaderCode(): string | null {
+        return null;
+    }
+
     // TODO: what should this do?
     public _handlesSpectorRebuildCallback(onCompiled: (compiledObject: any) => void): void {
         throw new Error("Not implemented");

+ 5 - 0
src/Engines/thinEngine.ts

@@ -2081,6 +2081,11 @@ export class ThinEngine {
         return shader;
     }
 
+    /** @hidden */
+    public _getShaderSource(shader: WebGLShader): Nullable<string> {
+        return this._gl.getShaderSource(shader);
+    }
+
     /**
      * Directly creates a webGL program
      * @param pipelineContext  defines the pipeline context to attach to

+ 46 - 0
src/Materials/effect.ts

@@ -76,6 +76,10 @@ export class Effect implements IDisposable {
      */
     public static ShadersRepository = "src/Shaders/";
     /**
+     * Enable logging of the shader code when a compilation error occurs
+     */
+    public static LogShaderCodeOnCompilationError = true;
+    /**
      * Name of the effect.
      */
     public name: any = null;
@@ -612,6 +616,25 @@ export class Effect implements IDisposable {
         }
     }
 
+    private _getShaderCodeAndErrorLine(code: Nullable<string>, error: Nullable<string>, isFragment: boolean): [Nullable<string>, Nullable<string>] {
+        const regexp = isFragment ? /FRAGMENT SHADER ERROR: 0:(\d+?):/ : /VERTEX SHADER ERROR: 0:(\d+?):/;
+
+        let errorLine = null;
+
+        if (error && code) {
+            const res = error.match(regexp);
+            if (res && res.length === 2) {
+                const lineNumber = parseInt(res[1]);
+                const lines = code.split("\n", -1);
+                if (lines.length >= lineNumber) {
+                    errorLine = `Offending line [${lineNumber}] in ${isFragment ? "fragment" : "vertex"} code: ${lines[lineNumber - 1]}`;
+                }
+            }
+        }
+
+        return [code, errorLine];
+    }
+
     private _processCompilationErrors(e: any, previousPipelineContext: Nullable<IPipelineContext> = null) {
         this._compilationError = e.message;
         let attributesNames = this._attributesNames;
@@ -626,6 +649,29 @@ export class Effect implements IDisposable {
             return " " + attribute;
         }));
         Logger.Error("Defines:\r\n" + this.defines);
+        if (Effect.LogShaderCodeOnCompilationError) {
+            let lineErrorVertex = null, lineErrorFragment = null, code = null;
+            if (this._pipelineContext?._getVertexShaderCode()) {
+                [code, lineErrorVertex] = this._getShaderCodeAndErrorLine(this._pipelineContext._getVertexShaderCode(), this._compilationError, false);
+                if (code) {
+                    Logger.Error("Vertex code:");
+                    Logger.Error(code);
+                }
+            }
+            if (this._pipelineContext?._getFragmentShaderCode()) {
+                [code, lineErrorFragment] = this._getShaderCodeAndErrorLine(this._pipelineContext?._getFragmentShaderCode(), this._compilationError, true);
+                if (code) {
+                    Logger.Error("Fragment code:");
+                    Logger.Error(code);
+                }
+            }
+            if (lineErrorVertex) {
+                Logger.Error(lineErrorVertex);
+            }
+            if (lineErrorFragment) {
+                Logger.Error(lineErrorFragment);
+            }
+        }
         Logger.Error("Error: " + this._compilationError);
         if (previousPipelineContext) {
             this._pipelineContext = previousPipelineContext;