UNPKG

@itwin/core-frontend

Version:
922 lines • 43.4 kB
"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ /** @packageDocumentation * @module WebGL */ Object.defineProperty(exports, "__esModule", { value: true }); exports.ProgramBuilder = exports.FragmentShaderBuilder = exports.VertexShaderBuilder = exports.ShaderBuilder = exports.SourceBuilder = exports.ShaderVariables = exports.ShaderVariable = void 0; const core_bentley_1 = require("@itwin/core-bentley"); const Instancing_1 = require("./glsl/Instancing"); const PlanarClassification_1 = require("./glsl/PlanarClassification"); const Vertex_1 = require("./glsl/Vertex"); const ShaderProgram_1 = require("./ShaderProgram"); /** @internal */ var Convert; (function (Convert) { function typeToString(type) { switch (type) { case 0 /* VariableType.Boolean */: return "bool"; case 1 /* VariableType.Int */: return "int"; case 2 /* VariableType.Float */: return "float"; case 3 /* VariableType.Vec2 */: return "vec2"; case 4 /* VariableType.Vec3 */: return "vec3"; case 5 /* VariableType.Vec4 */: return "vec4"; case 6 /* VariableType.Mat3 */: return "mat3"; case 7 /* VariableType.Mat4 */: return "mat4"; case 8 /* VariableType.Sampler2D */: return "sampler2D"; case 9 /* VariableType.SamplerCube */: return "samplerCube"; case 10 /* VariableType.Uint */: return "uint"; case 11 /* VariableType.BVec2 */: return "bvec2"; default: (0, core_bentley_1.assert)(false); return "undefined"; } } Convert.typeToString = typeToString; function scopeToString(scope, isVertexShader) { switch (scope) { case 0 /* VariableScope.Global */: return ""; case 1 /* VariableScope.Varying */: return (isVertexShader ? "out" : "in"); case 2 /* VariableScope.Uniform */: return "uniform"; default: (0, core_bentley_1.assert)(false); return "undefined"; } } Convert.scopeToString = scopeToString; function precisionToString(precision) { switch (precision) { case 0 /* VariablePrecision.Default */: return ""; case 1 /* VariablePrecision.Low */: return "lowp"; case 2 /* VariablePrecision.Medium */: return "mediump"; case 3 /* VariablePrecision.High */: return "highp"; default: (0, core_bentley_1.assert)(false); return "undefined"; } } Convert.precisionToString = precisionToString; })(Convert || (Convert = {})); /** Represents a variable within a fragment or vertex shader. * @internal */ class ShaderVariable { _addBinding; name; value; // for global variables only type; scope; precision; isConst = false; // for global variables only length; // for uniform arrays only constructor(name, type, scope, precision, isConst, addBinding, value, length = 0) { this._addBinding = addBinding; this.name = name; this.value = value; this.type = type; this.scope = scope; this.precision = precision; this.isConst = isConst; this.length = length; } static create(name, type, scope, addBinding, precision = 0 /* VariablePrecision.Default */) { return new ShaderVariable(name, type, scope, precision, false, addBinding, undefined); } static createArray(name, type, length, scope, addBinding, precision = 0 /* VariablePrecision.Default */) { return new ShaderVariable(name, type, scope, precision, false, addBinding, undefined, length); } static createGlobal(name, type, value, isConst = false) { return new ShaderVariable(name, type, 0 /* VariableScope.Global */, 0 /* VariablePrecision.Default */, isConst, undefined, value); } get hasBinding() { return undefined !== this._addBinding; } addBinding(prog) { if (undefined !== this._addBinding) this._addBinding(prog); } get typeName() { return Convert.typeToString(this.type); } getScopeName(isVertexShader) { return Convert.scopeToString(this.scope, isVertexShader); } get precisionName() { return Convert.precisionToString(this.precision); } /** Constructs the single-line declaration of this variable */ buildDeclaration(isVertexShader) { const parts = new Array(); if (this.isConst) parts.push("const"); const scopeName = this.getScopeName(isVertexShader); if (0 < scopeName.length) parts.push(scopeName); const precisionName = this.precisionName; if (0 < precisionName.length) parts.push(precisionName); parts.push(this.typeName); if (this.length > 0) parts.push(`${this.name}[${this.length.toFixed(0)}]`); else parts.push(this.name); if (undefined !== this.value && 0 < this.value.length) { parts.push("="); parts.push(this.value); } return `${parts.join(" ")};`; } } exports.ShaderVariable = ShaderVariable; /** * Represents the set of variables defined and used within a fragment or vertex shader. * If the same variable is used in both the fragment and vertex shader (e.g., a varying variable), it should be defined in both ShaderBuilders' ShaderVariables object. * @internal */ class ShaderVariables { _list = new Array(); /** Find an existing variable with the specified name */ find(name) { return this._list.find((v) => v.name === name); } /** Add a new variable, if a variable with the same name does not already exist. return true if added */ addVariable(v) { const found = this.find(v.name); if (undefined !== found) { (0, core_bentley_1.assert)(found.type === v.type); // assume same binding etc... return false; } else { this._list.push(v); return true; } } addUniform(name, type, binding, precision = 0 /* VariablePrecision.Default */) { this.addVariable(ShaderVariable.create(name, type, 2 /* VariableScope.Uniform */, binding, precision)); } addUniformArray(name, type, length, binding) { this.addVariable(ShaderVariable.createArray(name, type, length, 2 /* VariableScope.Uniform */, binding)); } addVarying(name, type) { return this.addVariable(ShaderVariable.create(name, type, 1 /* VariableScope.Varying */)); } addGlobal(name, type, value, isConst = false) { this.addVariable(ShaderVariable.createGlobal(name, type, value, isConst)); } addConstant(name, type, value) { this.addGlobal(name, type, value, true); } addBitFlagConstant(name, value) { this.addGlobal(name, 10 /* VariableType.Uint */, `${(2 ** value).toFixed(0)}u`, true); } /** Constructs the lines of glsl code declaring the variables of a certain scope. */ buildScopeDeclarations(isVertexShader, scope, constants) { let decls = ""; for (const v of this._list) { if (scope === v.scope && (undefined === constants ? true : v.isConst === constants)) decls += `${v.buildDeclaration(isVertexShader)}\n`; } return decls; } /** Constructs the lines of glsl code declaring all of the variables. */ buildDeclarations(isVertexShader) { let decls = ""; if (isVertexShader) { decls += this.buildScopeDeclarations(isVertexShader, 2 /* VariableScope.Uniform */); decls += this.buildScopeDeclarations(isVertexShader, 0 /* VariableScope.Global */, true); decls += this.buildScopeDeclarations(isVertexShader, 0 /* VariableScope.Global */, false); decls += this.buildScopeDeclarations(isVertexShader, 1 /* VariableScope.Varying */); } else { decls += this.buildScopeDeclarations(isVertexShader, 1 /* VariableScope.Varying */); decls += this.buildScopeDeclarations(isVertexShader, 2 /* VariableScope.Uniform */); decls += this.buildScopeDeclarations(isVertexShader, 0 /* VariableScope.Global */, true); decls += this.buildScopeDeclarations(isVertexShader, 0 /* VariableScope.Global */, false); } return decls; } /** * For every uniform and attribute variable not contained in the optional 'predefined' list, invokes the associated binding function * to add the corresponding Uniform or Attribute object to the ShaderProgram. */ addBindings(prog, predefined) { for (const v of this._list) { // Some variables exist in both frag and vert shaders - only add them to the program once. if (v.hasBinding && (undefined === predefined || undefined === predefined.find(v.name))) { v.addBinding(prog); } } } get length() { return this._list.length; } findSlot(variableSize, loopSize, registers) { // Find the first available slot into which to insert this variable for (let i = 0; i < loopSize; i++) { const newSize = registers[i] + variableSize; if (newSize <= 4) { registers[i] = newSize; return i; } } return -1; } // Return string of varying types with their theoretical slot numbers checkMaxVaryingVectors(fragSource) { // Varyings go into a matrix of 4 columns and GL_MAX_VARYING_VECTORS rows of floats. // The packing rules are defined by the standard. Specifically each row can contain one of: // vec4 // vec3 (+ float) // vec2 (+ vec2) // vec2 (+ float (+ float)) // float (+ float (+ float (+ float))) // Varyings are packed in order of size from largest to smallest const loopSize = 64; const registers = Array(loopSize + 1).fill(0); let outStr = ""; let slot = 0; const varyings = this._list.filter((variable) => 1 /* VariableScope.Varying */ === variable.scope); // Add in any built in vars that count as varyings if they are used if (fragSource.includes("gl_FragCoord")) { varyings.push(ShaderVariable.create("gl_FragCoord", 5 /* VariableType.Vec4 */, 1 /* VariableScope.Varying */)); } if (fragSource.includes("gl_PointCoord")) { varyings.push(ShaderVariable.create("gl_PointCoord", 3 /* VariableType.Vec2 */, 1 /* VariableScope.Varying */)); } if (fragSource.includes("gl_FrontFacing")) { varyings.push(ShaderVariable.create("gl_FrontFacing", 0 /* VariableType.Boolean */, 1 /* VariableScope.Varying */)); } // Need to process in size order (largest first) varyings.sort((a, b) => b.type - a.type); // this is good enough to sort by for (const variable of varyings) { let variableSize = 0; switch (variable.type) { case 0 /* VariableType.Boolean */: case 1 /* VariableType.Int */: case 2 /* VariableType.Float */: variableSize = 1; break; case 3 /* VariableType.Vec2 */: case 11 /* VariableType.BVec2 */: variableSize = 2; break; case 4 /* VariableType.Vec3 */: variableSize = 3; break; case 5 /* VariableType.Vec4 */: variableSize = 4; break; default: (0, core_bentley_1.assert)(false, "Invalid varying variable type"); continue; } slot = this.findSlot(variableSize, loopSize, registers); outStr += `// varying slot ${slot} ${Convert.typeToString(variable.type)} ${variable.name}\n`; } const slotsUsed = registers.indexOf(0); registers.length = slotsUsed; outStr += `// Slots used: ${slotsUsed} [${registers.toString()}]\n`; // debug output modes const outputAll = true; // false just outputs varyings that use more than 8 if (outputAll) { return outStr; } else { if (slotsUsed > 8) return outStr; else return ""; } } // Return the number of varying vectors used by the shader. // Varyings go into a matrix of 4 columns and GL_MAX_VARYING_VECTORS rows of floats. // The packing rules are defined by the standard. Specifically each row can contain one of: // vec4 // vec3 (+ float) // vec2 (+ vec2) // vec2 (+ float (+ float)) // float (+ float (+ float (+ float))) // Varyings are packed in order of size from largest to smallest computeNumVaryingVectors(fragSource) { const loopSize = 64; const registers = Array(loopSize + 1).fill(0); const varyings = this._list.filter((variable) => 1 /* VariableScope.Varying */ === variable.scope); // Add in any built in vars that count as varyings if they are used if (fragSource.includes("gl_FragCoord")) { varyings.push(ShaderVariable.create("gl_FragCoord", 5 /* VariableType.Vec4 */, 1 /* VariableScope.Varying */)); } if (fragSource.includes("gl_PointCoord")) { varyings.push(ShaderVariable.create("gl_PointCoord", 3 /* VariableType.Vec2 */, 1 /* VariableScope.Varying */)); } if (fragSource.includes("gl_FrontFacing")) { varyings.push(ShaderVariable.create("gl_FrontFacing", 0 /* VariableType.Boolean */, 1 /* VariableScope.Varying */)); } // Need to process in size order (largest first) varyings.sort((a, b) => b.type - a.type); // this is good enough to sort by for (const variable of varyings) { if (1 /* VariableScope.Varying */ !== variable.scope) continue; let variableSize = 0; switch (variable.type) { case 0 /* VariableType.Boolean */: case 1 /* VariableType.Int */: case 2 /* VariableType.Float */: variableSize = 1; break; case 3 /* VariableType.Vec2 */: case 11 /* VariableType.BVec2 */: variableSize = 2; break; case 4 /* VariableType.Vec3 */: variableSize = 3; break; case 5 /* VariableType.Vec4 */: variableSize = 4; break; default: (0, core_bentley_1.assert)(false, "Invalid varying variable type"); continue; } this.findSlot(variableSize, loopSize, registers); } const slotsUsed = registers.indexOf(0); return slotsUsed; } } exports.ShaderVariables = ShaderVariables; /** Convenience API for assembling glsl source code. * @internal */ class SourceBuilder { source = ""; /* Append the specified string to the glsl source */ add(what) { this.source += what; } /* Append a new-line to the glsl source */ newline() { this.add("\n"); } /* Append the specified string to the glsl source, followed by a new-line */ addline(what) { this.add(what); this.newline(); } /** * Construct a function definition given the function signature and body. For example: * buildFunctionDefinition("float average(float a, float b)", "\n return (a + b) / 2.0;\n"); * will produce: * "float average(float a, float b) { * return (a + b) / 2.0; * }" * For an inline function: * buildFunctionDefinition("float average(float a, float b)", "return (a + b) / 2.0;"); * will produce: * "float average(float a, float b) { return (a + b) / 2.0; }" */ static buildFunctionDefinition(declaration, implementation) { // If implementation does not start with a newline then assume it is an inline function & add spaces between braces. if ("\n" === implementation.charAt(0)) return `${declaration} {${implementation}}\n\n`; else return `${declaration} { ${implementation} }\n\n`; } /** Constructs a function definition as described by buildFunctionDefinition() and appends it to the glsl source. */ addFunction(declaration, implementation) { this.add(SourceBuilder.buildFunctionDefinition(declaration, implementation)); } /** Constructs the definition of the main() function using the supplied function body and appends it to the glsl source. */ addMain(implementation) { this.addFunction("void main()", implementation); } } exports.SourceBuilder = SourceBuilder; /** * Represents a fragment or vertex shader under construction. The shader consists of a set of defined variables, * plus a set of code snippets which can be concatenated together to form the shader source. * @internal */ class ShaderBuilder extends ShaderVariables { _components = new Array(); _functions = []; _extensions = []; _macros = []; _fragOutputs = []; _version = ""; headerComment = ""; _flags; _initializers = new Array(); get usesInstancedGeometry() { return !!this._flags.instanced; } addInitializer(initializer) { if (-1 === this._initializers.indexOf(initializer)) this._initializers.push(initializer); } constructor(maxComponents, flags) { super(); this._components.length = maxComponents; this._flags = flags; this._version = "#version 300 es"; this.addDefine("TEXTURE", "texture"); this.addDefine("TEXTURE_CUBE", "texture"); this.addDefine("TEXTURE_PROJ", "textureProj"); } addComponent(index, component) { (0, core_bentley_1.assert)(index < this._components.length); // assume if caller is replacing an existing component, they know what they're doing... this._components[index] = component; } removeComponent(index) { (0, core_bentley_1.assert)(index < this._components.length); this._components[index] = undefined; } getComponent(index) { (0, core_bentley_1.assert)(index < this._components.length); return this._components[index]; } addFunction(declarationOrFull, implementation) { let def = declarationOrFull; if (undefined !== implementation) def = SourceBuilder.buildFunctionDefinition(`\n${declarationOrFull}`, implementation); if (undefined === this.findFunction(def)) this._functions.push(def); } replaceFunction(existing, replacement) { const index = this._functions.indexOf(existing); if (-1 !== index) { this._functions[index] = replacement; } (0, core_bentley_1.assert)(-1 !== index); return -1 !== index; } findFunction(func) { return this._functions.find((f) => f === func); } addExtension(extName) { if (-1 === this._extensions.indexOf(extName)) this._extensions.push(extName); } addMacro(macro) { if (-1 === this._macros.indexOf(macro)) this._macros.push(macro); } addDefine(name, value) { const defineName = `#define ${name} `; const macro = defineName + value; const index = this._macros.findIndex((x) => x.startsWith(defineName)); if (-1 !== index) this._macros[index] = macro; else this._macros.push(defineName + value); } clearFragOutput() { while (this._fragOutputs.length > 0) this._fragOutputs.pop(); } addFragOutput(name, value) { if (-1 === value) { this._fragOutputs.push("out vec4 FragColor;"); return; } this._fragOutputs.push(`layout(location = ${value}) out vec4 ${name};`); } buildPreludeCommon(attrMap, isVertexShader) { const src = new SourceBuilder(); // Version number (must be first thing for GLSL 300) src.addline(this._version); // Header comment src.newline(); if ("" !== this.headerComment) { src.addline(this.headerComment); src.newline(); } // Macros for (const macro of this._macros) src.addline(macro); // Extensions for (const ext of this._extensions) src.addline(`#extension ${ext} : enable`); // Default precisions src.addline("precision highp float;"); src.addline("precision highp int;"); src.newline(); // Attribute declarations if (attrMap !== undefined) { attrMap.forEach((attr, key) => { src.addline(`in ${Convert.typeToString(attr.type)} ${key};`); }); } // Variable declarations src.add(this.buildDeclarations(isVertexShader)); // Layouts for (const layout of this._fragOutputs) src.addline(layout); // Functions for (const func of this._functions) { src.add(func); } if (!src.source.endsWith("\n\n")) src.newline(); return src; } copyCommon(src) { this._version = src._version; this.headerComment = src.headerComment; this._initializers = [...src._initializers]; this._components = [...src._components]; this._functions = [...src._functions]; this._extensions = [...src._extensions]; this._list = [...src._list]; this._macros = [...src._macros]; this._fragOutputs = [...src._fragOutputs]; } } exports.ShaderBuilder = ShaderBuilder; /** Assembles the source code for a vertex shader from a set of modular components. * @internal */ class VertexShaderBuilder extends ShaderBuilder { _computedVarying = new Array(); buildPrelude(attrMap) { return this.buildPreludeCommon(attrMap, true); } constructor(flags = {}) { super(14 /* VertexShaderComponent.COUNT */, flags); this.addDefine("MAT_NORM", "g_nmx"); if (this.usesInstancedGeometry) { (0, Instancing_1.addInstancedModelMatrixRTC)(this); this.addDefine("MAT_MV", "g_mv"); this.addDefine("MAT_MVP", "g_mvp"); } else { this.addDefine("MAT_MV", "u_mv"); this.addDefine("MAT_MVP", "u_mvp"); } (0, Vertex_1.addPosition)(this, this.usesVertexTable); } get usesVertexTable() { return undefined !== this._flags.positionType; } get positionType() { (0, core_bentley_1.assert)(undefined !== this._flags.positionType); return this._flags.positionType; } get(id) { return this.getComponent(id); } set(id, component) { this.addComponent(id, component); } unset(id) { this.removeComponent(id); } addComputedVarying(name, type, computation) { this.addVarying(name, type); this._computedVarying.push(computation); } buildSource(attrMap) { const prelude = this.buildPrelude(attrMap); const main = new SourceBuilder(); main.newline(); const computePosition = this.get(10 /* VertexShaderComponent.ComputePosition */); (0, core_bentley_1.assert)(undefined !== computePosition); if (undefined !== computePosition) { prelude.addFunction("vec4 computePosition(vec4 rawPos)", computePosition); } const computeQPos = this.get(0 /* VertexShaderComponent.ComputeQuantizedPosition */) ?? "return a_pos;"; prelude.addFunction("vec3 computeQuantizedPosition()", computeQPos); main.addline(" vec3 qpos = computeQuantizedPosition();"); // Initialization logic that should occur at start of main() - primarily global variables whose values // are too complex to compute inline or which depend on uniforms and/or other globals. for (const init of this._initializers) { if ("\n" === init.charAt(0)) main.addline(` {${init} }\n`); else main.addline(` { ${init} }\n`); } main.addline(" vec4 rawPosition = computeVertexPosition(qpos);"); const adjustRawPosition = this.get(1 /* VertexShaderComponent.AdjustRawPosition */); if (undefined !== adjustRawPosition) { prelude.addFunction("vec4 adjustRawPosition(vec4 rawPos)", adjustRawPosition); main.addline(" rawPosition = adjustRawPosition(rawPosition);"); } const checkForEarlyDiscard = this.get(2 /* VertexShaderComponent.CheckForEarlyDiscard */); if (undefined !== checkForEarlyDiscard) { prelude.addFunction("bool checkForEarlyDiscard(vec4 rawPos)", checkForEarlyDiscard); main.add(Vertex_1.earlyVertexDiscard); } const computeFeatureOverrides = this.get(3 /* VertexShaderComponent.ComputeFeatureOverrides */); if (undefined !== computeFeatureOverrides) { prelude.addFunction("void computeFeatureOverrides()", computeFeatureOverrides); main.addline(" computeFeatureOverrides();"); } const computeMaterial = this.get(4 /* VertexShaderComponent.ComputeMaterial */); if (undefined !== computeMaterial) { prelude.addFunction("void computeMaterial()", computeMaterial); main.addline(" computeMaterial();"); } const computeBaseColor = this.get(5 /* VertexShaderComponent.ComputeBaseColor */); if (undefined !== computeBaseColor) { (0, core_bentley_1.assert)(undefined !== this.find("v_color")); prelude.addFunction("vec4 computeBaseColor()", computeBaseColor); main.addline(" vec4 baseColor = computeBaseColor();"); const applyMaterialColor = this.get(6 /* VertexShaderComponent.ApplyMaterialColor */); if (undefined !== applyMaterialColor) { prelude.addFunction("vec4 applyMaterialColor(vec4 baseColor)", applyMaterialColor); main.addline(" baseColor = applyMaterialColor(baseColor);"); } const applyFeatureColor = this.get(7 /* VertexShaderComponent.ApplyFeatureColor */); if (undefined !== applyFeatureColor) { prelude.addFunction("vec4 applyFeatureColor(vec4 baseColor)", applyFeatureColor); main.addline(" baseColor = applyFeatureColor(baseColor);"); } const adjustContrast = this.get(8 /* VertexShaderComponent.AdjustContrast */); if (adjustContrast) { prelude.addFunction("vec4 adjustContrast(vec4 baseColor)", adjustContrast); main.addline(" baseColor = adjustContrast(baseColor);"); } main.addline(" v_color = baseColor;"); } const checkForDiscard = this.get(9 /* VertexShaderComponent.CheckForDiscard */); if (undefined !== checkForDiscard) { prelude.addFunction("bool checkForDiscard()", checkForDiscard); main.add(Vertex_1.vertexDiscard); } main.addline(" gl_Position = computePosition(rawPosition);"); const finalizePos = this.get(13 /* VertexShaderComponent.FinalizePosition */); if (undefined !== finalizePos) { prelude.addFunction("vec4 finalizePosition(vec4 pos)", finalizePos); main.addline(" gl_Position = finalizePosition(gl_Position);"); } for (const comp of this._computedVarying) { main.addline(` ${comp}`); } const computeAtmosphericScatteringVaryings = this.get(11 /* VertexShaderComponent.ComputeAtmosphericScatteringVaryings */); if (undefined !== computeAtmosphericScatteringVaryings) { prelude.addFunction("void computeAtmosphericScatteringVaryings()", computeAtmosphericScatteringVaryings); main.addline(" computeAtmosphericScatteringVaryings();"); } const checkForLateDiscard = this.get(12 /* VertexShaderComponent.CheckForLateDiscard */); if (undefined !== checkForLateDiscard) { prelude.addFunction("bool checkForLateDiscard()", checkForLateDiscard); main.addline(Vertex_1.lateVertexDiscard); } prelude.addMain(main.source); return prelude.source; } copyFrom(src) { this.copyCommon(src); this._computedVarying = [...src._computedVarying]; } } exports.VertexShaderBuilder = VertexShaderBuilder; /** Assembles the source code for a fragment shader from a set of modular components. * @internal */ class FragmentShaderBuilder extends ShaderBuilder { requiresEarlyZWorkaround = false; constructor(flags = {}) { super(26 /* FragmentShaderComponent.COUNT */, flags); this.addFragOutput("FragColor", -1); } get(id) { return this.getComponent(id); } set(id, component) { this.addComponent(id, component); } unset(id) { this.removeComponent(id); } addDrawBuffersExtension(n) { this.clearFragOutput(); for (let i = 0; i < n; i++) this.addFragOutput(`FragColor${i}`, i); } buildSource() { const applyLighting = this.get(8 /* FragmentShaderComponent.ApplyLighting */); const prelude = this.buildPrelude(undefined); const computeBaseColor = this.get(1 /* FragmentShaderComponent.ComputeBaseColor */); (0, core_bentley_1.assert)(undefined !== computeBaseColor); if (undefined !== computeBaseColor) { prelude.addFunction("vec4 computeBaseColor()", computeBaseColor); } const main = new SourceBuilder(); main.newline(); // Initialization logic that should occur at start of main() - primarily global variables whose values // are too complex to compute inline or which depend on uniforms and/or other globals. for (const init of this._initializers) { if ("\n" === init.charAt(0)) main.addline(` {${init} }\n`); else main.addline(` { ${init} }\n`); } const checkForEarlyDiscard = this.get(0 /* FragmentShaderComponent.CheckForEarlyDiscard */); if (undefined !== checkForEarlyDiscard) { prelude.addFunction("bool checkForEarlyDiscard()", checkForEarlyDiscard); main.addline(" if (checkForEarlyDiscard()) { discard; return; }"); } const finalizeNormal = this.get(25 /* FragmentShaderComponent.FinalizeNormal */); if (undefined !== finalizeNormal) { prelude.addFunction("vec3 finalizeNormal()", finalizeNormal); main.addline(" g_normal = finalizeNormal();"); } main.addline(" vec4 baseColor = computeBaseColor();"); const finalizeDepth = this.get(20 /* FragmentShaderComponent.FinalizeDepth */); if (undefined !== finalizeDepth) { prelude.addFunction("float finalizeDepth()", finalizeDepth); main.addline(" float finalDepth = finalizeDepth();"); main.addline(" gl_FragDepth = finalDepth;"); } let clipIndent = ""; const applyClipping = this.get(10 /* FragmentShaderComponent.ApplyClipping */); if (undefined !== applyClipping) { prelude.addline("vec3 g_clipColor;\n"); prelude.addFunction("bvec2 applyClipping(vec4 baseColor)", applyClipping); main.addline(" g_hasClipColor = applyClipping(baseColor);"); main.addline(" if (g_hasClipColor.x) { baseColor.rgb = g_clipColor; } else {"); clipIndent = " "; } const applyMaterialOverrides = this.get(2 /* FragmentShaderComponent.ApplyMaterialOverrides */); if (undefined !== applyMaterialOverrides) { prelude.addFunction("vec4 applyMaterialOverrides(vec4 baseColor)", applyMaterialOverrides); main.addline(`${clipIndent} baseColor = applyMaterialOverrides(baseColor);`); } const applyThematicDisplay = this.get(7 /* FragmentShaderComponent.ApplyThematicDisplay */); if (undefined !== applyThematicDisplay) { prelude.addFunction("vec4 applyThematicDisplay(vec4 baseColor)", applyThematicDisplay); main.addline(`${clipIndent} if (u_renderPass != kRenderPass_PlanarClassification)`); main.addline(`${clipIndent} baseColor = applyThematicDisplay(baseColor);`); } const applyPlanarClassifier = this.get(13 /* FragmentShaderComponent.ApplyPlanarClassifier */); if (undefined !== applyPlanarClassifier) { if (undefined === finalizeDepth) { if (this.findFunction(PlanarClassification_1.volClassOpaqueColor)) main.addline(`${clipIndent} float finalDepth = gl_FragCoord.z;`); else main.addline(`${clipIndent} float finalDepth = 1.0;`); } prelude.addFunction("vec4 applyPlanarClassifications(vec4 baseColor, float depth)", applyPlanarClassifier); main.addline(`${clipIndent} baseColor = applyPlanarClassifications(baseColor, finalDepth);`); } const applySolarShadowMap = this.get(15 /* FragmentShaderComponent.ApplySolarShadowMap */); if (undefined !== applySolarShadowMap) { prelude.addFunction("vec4 applySolarShadowMap(vec4 baseColor)", applySolarShadowMap); main.addline(`${clipIndent} baseColor = applySolarShadowMap(baseColor);`); } const finalize = this.get(3 /* FragmentShaderComponent.FinalizeBaseColor */); if (undefined !== finalize) { prelude.addFunction("vec4 finalizeBaseColor(vec4 baseColor)", finalize); main.addline(`${clipIndent} baseColor = finalizeBaseColor(baseColor);`); } const checkForDiscard = this.get(4 /* FragmentShaderComponent.CheckForDiscard */); if (undefined !== checkForDiscard) { prelude.addFunction("bool checkForDiscard(vec4 baseColor)", checkForDiscard); main.addline(`${clipIndent} if (checkForDiscard(baseColor)) { discard; return; }`); } const discardByAlpha = this.get(5 /* FragmentShaderComponent.DiscardByAlpha */); if (undefined !== discardByAlpha) { prelude.addFunction("bool discardByAlpha(float alpha)", discardByAlpha); main.addline(`${clipIndent} if (discardByAlpha(baseColor.a)) { discard; return; }`); } if (undefined !== applyClipping) main.addline(" }"); const applyMonochrome = this.get(6 /* FragmentShaderComponent.ApplyMonochrome */); if (undefined !== applyMonochrome) { prelude.addFunction("vec4 applyMonochrome(vec4 baseColor)", applyMonochrome); main.addline(" baseColor = applyMonochrome(baseColor);"); } if (undefined !== applyLighting) { prelude.addFunction("vec4 applyLighting(vec4 baseColor)", applyLighting); main.addline(" baseColor = applyLighting(baseColor);"); } if (undefined !== applyClipping) { main.addline(" if (g_hasClipColor.y) { baseColor.rgba = vec4(g_clipColor, 1.0); } "); } const reverseWoW = this.get(9 /* FragmentShaderComponent.ReverseWhiteOnWhite */); if (undefined !== reverseWoW) { prelude.addFunction("vec4 reverseWhiteOnWhite(vec4 baseColor)", reverseWoW); main.addline(" baseColor = reverseWhiteOnWhite(baseColor);"); } const applyContours = this.get(11 /* FragmentShaderComponent.ApplyContours */); if (undefined !== applyContours) { prelude.addFunction("vec4 applyContours(vec4 baseColor)", applyContours); main.addline(" baseColor = applyContours(baseColor);"); } const applyFlash = this.get(12 /* FragmentShaderComponent.ApplyFlash */); if (undefined !== applyFlash) { prelude.addFunction("vec4 applyFlash(vec4 baseColor)", applyFlash); main.addline(" baseColor = applyFlash(baseColor);"); } const applyWiremesh = this.get(16 /* FragmentShaderComponent.ApplyWiremesh */); if (applyWiremesh) { prelude.addFunction("vec4 applyWiremesh(vec4 baseColor)", applyWiremesh); main.addline(" baseColor = applyWiremesh(baseColor);"); } const applyAtmosphericScattering = this.get(24 /* FragmentShaderComponent.ApplyAtmosphericScattering */); if (applyAtmosphericScattering) { prelude.addFunction("vec4 applyAtmosphericScattering(vec4 baseColor)", applyAtmosphericScattering); main.addline(" baseColor = applyAtmosphericScattering(baseColor);"); } const applyDraping = this.get(14 /* FragmentShaderComponent.ApplyDraping */); if (undefined !== applyDraping) { prelude.addFunction("vec4 applyDraping(vec4 baseColor)", applyDraping); main.addline(`${clipIndent} baseColor = applyDraping(baseColor);`); } const applyDebug = this.get(17 /* FragmentShaderComponent.ApplyDebugColor */); if (undefined !== applyDebug) { prelude.addFunction("vec4 applyDebugColor(vec4 baseColor)", applyDebug); main.addline(" baseColor = applyDebugColor(baseColor);"); } const assignFragData = this.get(18 /* FragmentShaderComponent.AssignFragData */); (0, core_bentley_1.assert)(undefined !== assignFragData); if (undefined !== assignFragData) { prelude.addFunction("void assignFragData(vec4 baseColor)", assignFragData); main.addline(" assignFragData(baseColor);"); } if (this.requiresEarlyZWorkaround) { // Add a conditional discard that never executes to force buggy Intel driver to skip early Z despite our fragment shader writing depth. main.addline("if (v_eyeSpace.z == 9999999.0) discard;"); } prelude.addMain(main.source); return prelude.source; } buildPrelude(attrMap) { return this.buildPreludeCommon(attrMap, false); } copyFrom(src) { this.copyCommon(src); this.requiresEarlyZWorkaround = src.requiresEarlyZWorkaround; } } exports.FragmentShaderBuilder = FragmentShaderBuilder; /** * Assembles vertex and fragment shaders from a set of modular components to produce a compiled ShaderProgram. * Be very careful with components which use samplers to ensure that no conflicts exist with texture units used by other components (see TextureUnit enum). * @internal */ class ProgramBuilder { vert; frag; _flags; _attrMap; constructor(attrMap, flags = {}) { this._attrMap = attrMap; this.vert = new VertexShaderBuilder(flags); this.frag = new FragmentShaderBuilder(flags); this._flags = flags; // only needed for clone - though could look up from vert or frag shader. } addVariable(v, which) { if (which & 1 /* ShaderType.Fragment */) { this.frag.addVariable(v); } if (which & 2 /* ShaderType.Vertex */) { this.vert.addVariable(v); } } addUniform(name, type, binding, which = 3 /* ShaderType.Both */) { this.addVariable(ShaderVariable.create(name, type, 2 /* VariableScope.Uniform */, binding), which); } addUniformArray(name, type, length, binding, which = 3 /* ShaderType.Both */) { this.addVariable(ShaderVariable.createArray(name, type, length, 2 /* VariableScope.Uniform */, binding), which); } addVarying(name, type) { this.addVariable(ShaderVariable.create(name, type, 1 /* VariableScope.Varying */), 3 /* ShaderType.Both */); } addGlobal(name, type, which = 3 /* ShaderType.Both */, value, isConst = false) { this.addVariable(ShaderVariable.createGlobal(name, type, value, isConst), which); } addInlineComputedVarying(name, type, inlineComputation) { if (this.frag.addVarying(name, type)) this.vert.addComputedVarying(name, type, inlineComputation); } addFunctionComputedVarying(name, type, funcName, funcBody) { let funcDecl = `\n${Convert.typeToString(type)} ${funcName}()`; funcDecl = SourceBuilder.buildFunctionDefinition(funcDecl, funcBody); const funcCall = `${funcName}()`; this.addFunctionComputedVaryingWithArgs(name, type, funcCall, funcDecl); } addFunctionComputedVaryingWithArgs(name, type, funcCall, funcDef) { this.vert.addFunction(funcDef); const computation = `${name} = ${funcCall};`; this.addInlineComputedVarying(name, type, computation); } /** Assembles the vertex and fragment shader code and returns a ready-to-compile shader program */ buildProgram(gl) { const vertSource = this.vert.buildSource(this._attrMap); const fragSource = this.frag.buildSource(); // NB: frag has no need to specify attributes, only vertex does. // Debug output const debugVaryings = false; if (debugVaryings) { const dbgLog = (x) => console.log(x); // eslint-disable-line no-console const outSrc = false; // true for source out, false for just varying info if (this.frag.headerComment) { let tStr = ""; if (!outSrc) { tStr = `${this.frag.headerComment}\n`; } const tStr2 = this.vert.checkMaxVaryingVectors(fragSource); if (tStr2) { if (outSrc) { dbgLog("//==============================================================================================================="); dbgLog(vertSource); dbgLog("//========= Varying Info ========="); dbgLog(tStr2); dbgLog(fragSource); } else { dbgLog(tStr + tStr2); } } } } const prog = new ShaderProgram_1.ShaderProgram(gl, vertSource, fragSource, this._attrMap, this.vert.headerComment, this.frag.headerComment); this.vert.addBindings(prog); this.frag.addBindings(prog, this.vert); return prog; } setDebugDescription(description) { this.vert.headerComment = (`//!V! ${description}`); this.frag.headerComment = (`//!F! ${description}`); } /** Returns a deep copy of this program builder. */ clone() { const clone = new ProgramBuilder(this._attrMap, this._flags); clone.vert.copyFrom(this.vert); clone.frag.copyFrom(this.frag); return clone; } } exports.ProgramBuilder = ProgramBuilder; //# sourceMappingURL=ShaderBuilder.js.map