diff --git a/Source/Renderer/Context.js b/Source/Renderer/Context.js index f2d23f2927c2..a893efa14980 100644 --- a/Source/Renderer/Context.js +++ b/Source/Renderer/Context.js @@ -929,11 +929,11 @@ define([ }); Context.prototype.replaceShaderProgram = function(shaderProgram, vertexShaderSource, fragmentShaderSource, attributeLocations) { - return this.shaderCache.replaceShaderProgram(shaderProgram, vertexShaderSource, fragmentShaderSource, attributeLocations); + return this._shaderCache.replaceShaderProgram(shaderProgram, vertexShaderSource, fragmentShaderSource, attributeLocations); }; Context.prototype.createShaderProgram = function(vertexShaderSource, fragmentShaderSource, attributeLocations) { - return this.shaderCache.getShaderProgram(vertexShaderSource, fragmentShaderSource, attributeLocations); + return this._shaderCache.getShaderProgram(vertexShaderSource, fragmentShaderSource, attributeLocations); }; function createBuffer(gl, bufferTarget, typedArrayOrSizeInBytes, usage) { diff --git a/Source/Renderer/ShaderCache.js b/Source/Renderer/ShaderCache.js index 1fc5b9977ada..7372497d8040 100644 --- a/Source/Renderer/ShaderCache.js +++ b/Source/Renderer/ShaderCache.js @@ -2,11 +2,13 @@ define([ '../Core/defined', '../Core/destroyObject', - './ShaderProgram' + './ShaderProgram', + './ShaderSource' ], function( defined, destroyObject, - ShaderProgram) { + ShaderProgram, + ShaderSource) { "use strict"; /** @@ -27,16 +29,15 @@ define([ *

* * @param {ShaderProgram} shaderProgram The shader program that is being reassigned. This can be undefined. - * @param {String} vertexShaderSource The GLSL source for the vertex shader. - * @param {String} fragmentShaderSource The GLSL source for the fragment shader. + * @param {String|ShaderSource} vertexShaderSource The GLSL source for the vertex shader. + * @param {String|ShaderSource} fragmentShaderSource The GLSL source for the fragment shader. * @param {Object} attributeLocations Indices for the attribute inputs to the vertex shader. * @returns {ShaderProgram} The cached or newly created shader program. * * @see ShaderCache#getShaderProgram * * @example - * this._shaderProgram = context.shaderCache.replaceShaderProgram( - * this._shaderProgram, vs, fs, attributeLocations); + * this._shaderProgram = context.shaderCache.replaceShaderProgram(this._shaderProgram, vs, fs, attributeLocations); */ ShaderCache.prototype.replaceShaderProgram = function(shaderProgram, vertexShaderSource, fragmentShaderSource, attributeLocations) { if (defined(shaderProgram)) { @@ -46,8 +47,36 @@ define([ return this.getShaderProgram(vertexShaderSource, fragmentShaderSource, attributeLocations); }; + /** + * Returns a shader program from the cache, or creates and caches a new shader program, + * given the GLSL vertex and fragment shader source and attribute locations. + * + * @param {String|ShaderSource} vertexShaderSource The GLSL source for the vertex shader. + * @param {String|ShaderSource} fragmentShaderSource The GLSL source for the fragment shader. + * @param {Object} attributeLocations Indices for the attribute inputs to the vertex shader. + * + * @returns {ShaderProgram} The cached or newly created shader program. + */ ShaderCache.prototype.getShaderProgram = function(vertexShaderSource, fragmentShaderSource, attributeLocations) { - var keyword = vertexShaderSource + fragmentShaderSource + JSON.stringify(attributeLocations); + // convert shaders which are provided as strings into ShaderSource objects + // because ShaderSource handles all the automatic including of built-in functions, etc. + + if (typeof vertexShaderSource === 'string') { + vertexShaderSource = new ShaderSource({ + sources : [vertexShaderSource] + }); + } + + if (typeof fragmentShaderSource === 'string') { + fragmentShaderSource = new ShaderSource({ + sources : [fragmentShaderSource] + }); + } + + var vertexShaderText = vertexShaderSource.getCombinedShader(false); + var fragmentShaderText = fragmentShaderSource.getCombinedShader(true); + + var keyword = vertexShaderText + fragmentShaderText + JSON.stringify(attributeLocations); var cachedShader; if (this._shaders[keyword]) { @@ -57,17 +86,25 @@ define([ delete this._shadersToRelease[keyword]; } else { var context = this._context; - var sp = new ShaderProgram(context._gl, context.logShaderCompilation, vertexShaderSource, fragmentShaderSource, attributeLocations); + var shaderProgram = new ShaderProgram({ + gl : context._gl, + logShaderCompilation : context.logShaderCompilation, + vertexShaderSource : vertexShaderSource, + vertexShaderText : vertexShaderText, + fragmentShaderSource : fragmentShaderSource, + fragmentShaderText : fragmentShaderText, + attributeLocations : attributeLocations + }); cachedShader = { cache : this, - shaderProgram : sp, + shaderProgram : shaderProgram, keyword : keyword, count : 0 }; // A shader can't be in more than one cache. - sp._cachedShader = cachedShader; + shaderProgram._cachedShader = cachedShader; this._shaders[keyword] = cachedShader; } diff --git a/Source/Renderer/ShaderProgram.js b/Source/Renderer/ShaderProgram.js index af73d8d5e3ff..c8155a034476 100644 --- a/Source/Renderer/ShaderProgram.js +++ b/Source/Renderer/ShaderProgram.js @@ -9,7 +9,6 @@ define([ '../Core/Matrix3', '../Core/Matrix4', '../Core/RuntimeError', - '../Shaders/Builtin/CzmBuiltins', './AutomaticUniforms' ], function( defined, @@ -21,7 +20,6 @@ define([ Matrix3, Matrix4, RuntimeError, - CzmBuiltins, AutomaticUniforms) { "use strict"; /*global console*/ @@ -34,7 +32,7 @@ define([ scratchUniformMatrix3 = new Float32Array(9); scratchUniformMatrix4 = new Float32Array(16); } - function setUniform (uniform) { + function setUniform(uniform) { var gl = uniform._gl; var location = uniform._location; switch (uniform._activeUniform.type) { @@ -340,10 +338,10 @@ define([ /** * @private */ - var ShaderProgram = function(gl, logShaderCompilation, vertexShaderSource, fragmentShaderSource, attributeLocations) { - this._gl = gl; - this._logShaderCompilation = logShaderCompilation; - this._attributeLocations = attributeLocations; + var ShaderProgram = function(options) { + this._gl = options.gl; + this._logShaderCompilation = options.logShaderCompilation; + this._attributeLocations = options.attributeLocations; this._program = undefined; this._numberOfVertexAttributes = undefined; @@ -352,15 +350,17 @@ define([ this._uniforms = undefined; this._automaticUniforms = undefined; this._manualUniforms = undefined; - this._cachedShader = undefined; // Used by ShaderCache + this._cachedShader = undefined; // Used by ShaderCache /** * @private */ this.maximumTextureUnitIndex = undefined; - this._vertexShaderSource = vertexShaderSource; - this._fragmentShaderSource = fragmentShaderSource; + this._vertexShaderSource = options.vertexShaderSource; + this._vertexShaderText = options.vertexShaderText; + this._fragmentShaderSource = options.fragmentShaderSource; + this._fragmentShaderText = options.fragmentShaderText; /** * @private @@ -370,36 +370,30 @@ define([ defineProperties(ShaderProgram.prototype, { /** - * GLSL source for the shader program's vertex shader. This is the version of - * the source provided when the shader program was created, not the final - * source provided to WebGL, which includes Cesium bulit-ins. - * + * GLSL source for the shader program's vertex shader. * @memberof ShaderProgram.prototype * - * @type {String} + * @type {ShaderSource} * @readonly */ - vertexShaderSource: { + vertexShaderSource : { get : function() { return this._vertexShaderSource; } }, /** - * GLSL source for the shader program's fragment shader. This is the version of - * the source provided when the shader program was created, not the final - * source provided to WebGL, which includes Cesium bulit-ins. - * + * GLSL source for the shader program's fragment shader. * @memberof ShaderProgram.prototype * - * @type {String} + * @type {ShaderSource} * @readonly */ - fragmentShaderSource: { + fragmentShaderSource : { get : function() { return this._fragmentShaderSource; } }, - vertexAttributes: { + vertexAttributes : { get : function() { initialize(this); return this._vertexAttributes; @@ -411,13 +405,13 @@ define([ return this._numberOfVertexAttributes; } }, - allUniforms: { + allUniforms : { get : function() { initialize(this); return this._uniformsByName; } }, - manualUniforms: { + manualUniforms : { get : function() { initialize(this); return this._manualUniforms; @@ -425,221 +419,9 @@ define([ } }); - /** - * For ShaderProgram testing - * @private - */ - ShaderProgram._czmBuiltinsAndUniforms = {}; - - // combine automatic uniforms and Cesium built-ins - for ( var builtinName in CzmBuiltins) { - if (CzmBuiltins.hasOwnProperty(builtinName)) { - ShaderProgram._czmBuiltinsAndUniforms[builtinName] = CzmBuiltins[builtinName]; - } - } - for ( var uniformName in AutomaticUniforms) { - if (AutomaticUniforms.hasOwnProperty(uniformName)) { - var uniform = AutomaticUniforms[uniformName]; - if (typeof uniform.getDeclaration === 'function') { - ShaderProgram._czmBuiltinsAndUniforms[uniformName] = uniform.getDeclaration(uniformName); - } - } - } - - function extractShaderVersion(source) { - // This will fail if the first #version is actually in a comment. - var index = source.indexOf('#version'); - if (index !== -1) { - var newLineIndex = source.indexOf('\n', index); - - // We could throw an exception if there is not a new line after - // #version, but the GLSL compiler will catch it. - if (index !== -1) { - // Extract #version directive, including the new line. - var version = source.substring(index, newLineIndex + 1); - - // Comment out original #version directive so the line numbers - // are not off by one. There can be only one #version directive - // and it must appear at the top of the source, only preceded by - // whitespace and comments. - var modified = source.substring(0, index) + '//' + source.substring(index); - - return { - version : version, - source : modified - }; - } - } - - return { - version : '', // defaults to #version 100 - source : source // no modifications required - }; - } - - function getDependencyNode(name, glslSource, nodes) { - var dependencyNode; - - // check if already loaded - for (var i = 0; i < nodes.length; ++i) { - if (nodes[i].name === name) { - dependencyNode = nodes[i]; - } - } - - if (!defined(dependencyNode)) { - // strip doc comments so we don't accidentally try to determine a dependency for something found - // in a comment - var commentBlocks = glslSource.match(/\/\*\*[\s\S]*?\*\//gm); - if (defined(commentBlocks) && commentBlocks !== null) { - for (i = 0; i < commentBlocks.length; ++i) { - var commentBlock = commentBlocks[i]; - - // preserve the number of lines in the comment block so the line numbers will be correct when debugging shaders - var numberOfLines = commentBlock.match(/\n/gm).length; - var modifiedComment = ''; - for (var lineNumber = 0; lineNumber < numberOfLines; ++lineNumber) { - if (lineNumber === 0) { - modifiedComment += '// Comment replaced to prevent problems when determining dependencies on built-in functions\n'; - } else { - modifiedComment += '//\n'; - } - } - - glslSource = glslSource.replace(commentBlock, modifiedComment); - } - } - - // create new node - dependencyNode = { - name : name, - glslSource : glslSource, - dependsOn : [], - requiredBy : [], - evaluated : false - }; - nodes.push(dependencyNode); - } - - return dependencyNode; - } - - function generateDependencies(currentNode, dependencyNodes) { - if (currentNode.evaluated) { - return; - } - - currentNode.evaluated = true; - - // identify all dependencies that are referenced from this glsl source code - var czmMatches = currentNode.glslSource.match(/\bczm_[a-zA-Z0-9_]*/g); - if (defined(czmMatches) && czmMatches !== null) { - // remove duplicates - czmMatches = czmMatches.filter(function(elem, pos) { - return czmMatches.indexOf(elem) === pos; - }); - - czmMatches.forEach(function(element, index, array) { - if (element !== currentNode.name && ShaderProgram._czmBuiltinsAndUniforms.hasOwnProperty(element)) { - var referencedNode = getDependencyNode(element, ShaderProgram._czmBuiltinsAndUniforms[element], dependencyNodes); - currentNode.dependsOn.push(referencedNode); - referencedNode.requiredBy.push(currentNode); - - // recursive call to find any dependencies of the new node - generateDependencies(referencedNode, dependencyNodes); - } - }); - } - } - - function sortDependencies(dependencyNodes) { - var nodesWithoutIncomingEdges = []; - var allNodes = []; - - while (dependencyNodes.length > 0) { - var node = dependencyNodes.pop(); - allNodes.push(node); - - if (node.requiredBy.length === 0) { - nodesWithoutIncomingEdges.push(node); - } - } - - while (nodesWithoutIncomingEdges.length > 0) { - var currentNode = nodesWithoutIncomingEdges.shift(); - - dependencyNodes.push(currentNode); - - for (var i = 0; i < currentNode.dependsOn.length; ++i) { - // remove the edge from the graph - var referencedNode = currentNode.dependsOn[i]; - var index = referencedNode.requiredBy.indexOf(currentNode); - referencedNode.requiredBy.splice(index, 1); - - // if referenced node has no more incoming edges, add to list - if (referencedNode.requiredBy.length === 0) { - nodesWithoutIncomingEdges.push(referencedNode); - } - } - } - - // if there are any nodes left with incoming edges, then there was a circular dependency somewhere in the graph - var badNodes = []; - for (var j = 0; j < allNodes.length; ++j) { - if (allNodes[j].requiredBy.length !== 0) { - badNodes.push(allNodes[j]); - } - } - if (badNodes.length !== 0) { - var message = 'A circular dependency was found in the following built-in functions/structs/constants: \n'; - for (j = 0; j < badNodes.length; ++j) { - message = message + badNodes[j].name + '\n'; - } - throw new DeveloperError(message); - } - } - - function getBuiltinsAndAutomaticUniforms(shaderSource) { - // generate a dependency graph for builtin functions - var dependencyNodes = []; - var root = getDependencyNode('main', shaderSource, dependencyNodes); - generateDependencies(root, dependencyNodes); - sortDependencies(dependencyNodes); - - // Concatenate the source code for the function dependencies. - // Iterate in reverse so that dependent items are declared before they are used. - var builtinsSource = ''; - for (var i = dependencyNodes.length - 1; i >= 0; --i) { - builtinsSource = builtinsSource + dependencyNodes[i].glslSource + '\n'; - } - - return builtinsSource.replace(root.glslSource, ''); - } - - function getFragmentShaderPrecision() { - return '#ifdef GL_FRAGMENT_PRECISION_HIGH \n' + - ' precision highp float; \n' + - '#else \n' + - ' precision mediump float; \n' + - '#endif \n\n'; - } - - function createAndLinkProgram(gl, logShaderCompilation, vertexShaderSource, fragmentShaderSource, attributeLocations) { - var vsSourceVersioned = extractShaderVersion(vertexShaderSource); - var fsSourceVersioned = extractShaderVersion(fragmentShaderSource); - - var vsSource = - vsSourceVersioned.version + - getBuiltinsAndAutomaticUniforms(vsSourceVersioned.source) + - '\n#line 0\n' + - vsSourceVersioned.source; - var fsSource = - fsSourceVersioned.version + - getFragmentShaderPrecision() + - getBuiltinsAndAutomaticUniforms(fsSourceVersioned.source) + - '\n#line 0\n' + - fsSourceVersioned.source; - var log; + function createAndLinkProgram(gl, shader) { + var vsSource = shader._vertexShaderText; + var fsSource = shader._fragmentShaderText; var vertexShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertexShader, vsSource); @@ -656,6 +438,7 @@ define([ gl.deleteShader(vertexShader); gl.deleteShader(fragmentShader); + var attributeLocations = shader._attributeLocations; if (defined(attributeLocations)) { for ( var attribute in attributeLocations) { if (attributeLocations.hasOwnProperty(attribute)) { @@ -666,6 +449,7 @@ define([ gl.linkProgram(program); + var log; if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { // For performance, only check compile errors if there is a linker error. if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) { @@ -688,6 +472,8 @@ define([ throw new RuntimeError('Program failed to link. Link log: ' + log); } + var logShaderCompilation = shader._logShaderCompilation; + if (logShaderCompilation) { log = gl.getShaderInfoLog(vertexShader); if (defined(log) && (log.length > 0)) { @@ -799,7 +585,7 @@ define([ } else { locations = []; value = []; - for ( var j = 0; j < activeUniform.size; ++j) { + for (var j = 0; j < activeUniform.size; ++j) { loc = gl.getUniformLocation(program, uniformName + '[' + j + ']'); // Workaround for IE 11.0.9. See above. @@ -858,7 +644,7 @@ define([ } var gl = shader._gl; - var program = createAndLinkProgram(gl, shader._logShaderCompilation, shader.vertexShaderSource, shader.fragmentShaderSource, shader._attributeLocations); + var program = createAndLinkProgram(gl, shader); var numberOfVertexAttributes = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES); var uniforms = findUniforms(gl, program); var partitionedUniforms = partitionUniforms(uniforms.uniformsByName); diff --git a/Source/Renderer/ShaderSource.js b/Source/Renderer/ShaderSource.js new file mode 100644 index 000000000000..5aeb12745860 --- /dev/null +++ b/Source/Renderer/ShaderSource.js @@ -0,0 +1,340 @@ +/*global define*/ +define([ + '../Core/defaultValue', + '../Core/defined', + '../Core/defineProperties', + '../Core/destroyObject', + '../Core/DeveloperError', + '../Core/FeatureDetection', + '../Core/RuntimeError', + '../Shaders/Builtin/CzmBuiltins', + './AutomaticUniforms' + ], function( + defaultValue, + defined, + defineProperties, + destroyObject, + DeveloperError, + FeatureDetection, + RuntimeError, + CzmBuiltins, + AutomaticUniforms) { + "use strict"; + /*global console*/ + + function removeComments(source) { + return source.replace(/\/\*\*[\s\S]*?\*\//gm, function(match) { + // preserve the number of lines in the comment block so the line numbers will be correct when debugging shaders + var numberOfLines = match.match(/\n/gm).length; + var replacement = ''; + for (var lineNumber = 0; lineNumber < numberOfLines; ++lineNumber) { + replacement += '\n'; + } + return replacement; + }); + } + + function getDependencyNode(name, glslSource, nodes) { + var dependencyNode; + + // check if already loaded + for (var i = 0; i < nodes.length; ++i) { + if (nodes[i].name === name) { + dependencyNode = nodes[i]; + } + } + + if (!defined(dependencyNode)) { + // strip doc comments so we don't accidentally try to determine a dependency for something found + // in a comment + glslSource = removeComments(glslSource); + + // create new node + dependencyNode = { + name : name, + glslSource : glslSource, + dependsOn : [], + requiredBy : [], + evaluated : false + }; + nodes.push(dependencyNode); + } + + return dependencyNode; + } + + function generateDependencies(currentNode, dependencyNodes) { + if (currentNode.evaluated) { + return; + } + + currentNode.evaluated = true; + + // identify all dependencies that are referenced from this glsl source code + var czmMatches = currentNode.glslSource.match(/\bczm_[a-zA-Z0-9_]*/g); + if (defined(czmMatches) && czmMatches !== null) { + // remove duplicates + czmMatches = czmMatches.filter(function(elem, pos) { + return czmMatches.indexOf(elem) === pos; + }); + + czmMatches.forEach(function(element, index, array) { + if (element !== currentNode.name && ShaderSource._czmBuiltinsAndUniforms.hasOwnProperty(element)) { + var referencedNode = getDependencyNode(element, ShaderSource._czmBuiltinsAndUniforms[element], dependencyNodes); + currentNode.dependsOn.push(referencedNode); + referencedNode.requiredBy.push(currentNode); + + // recursive call to find any dependencies of the new node + generateDependencies(referencedNode, dependencyNodes); + } + }); + } + } + + function sortDependencies(dependencyNodes) { + var nodesWithoutIncomingEdges = []; + var allNodes = []; + + while (dependencyNodes.length > 0) { + var node = dependencyNodes.pop(); + allNodes.push(node); + + if (node.requiredBy.length === 0) { + nodesWithoutIncomingEdges.push(node); + } + } + + while (nodesWithoutIncomingEdges.length > 0) { + var currentNode = nodesWithoutIncomingEdges.shift(); + + dependencyNodes.push(currentNode); + + for (var i = 0; i < currentNode.dependsOn.length; ++i) { + // remove the edge from the graph + var referencedNode = currentNode.dependsOn[i]; + var index = referencedNode.requiredBy.indexOf(currentNode); + referencedNode.requiredBy.splice(index, 1); + + // if referenced node has no more incoming edges, add to list + if (referencedNode.requiredBy.length === 0) { + nodesWithoutIncomingEdges.push(referencedNode); + } + } + } + + // if there are any nodes left with incoming edges, then there was a circular dependency somewhere in the graph + var badNodes = []; + for (var j = 0; j < allNodes.length; ++j) { + if (allNodes[j].requiredBy.length !== 0) { + badNodes.push(allNodes[j]); + } + } + + if (badNodes.length !== 0) { + var message = 'A circular dependency was found in the following built-in functions/structs/constants: \n'; + for (j = 0; j < badNodes.length; ++j) { + message = message + badNodes[j].name + '\n'; + } + throw new DeveloperError(message); + } + } + + function getBuiltinsAndAutomaticUniforms(shaderSource) { + // generate a dependency graph for builtin functions + var dependencyNodes = []; + var root = getDependencyNode('main', shaderSource, dependencyNodes); + generateDependencies(root, dependencyNodes); + sortDependencies(dependencyNodes); + + // Concatenate the source code for the function dependencies. + // Iterate in reverse so that dependent items are declared before they are used. + var builtinsSource = ''; + for (var i = dependencyNodes.length - 1; i >= 0; --i) { + builtinsSource = builtinsSource + dependencyNodes[i].glslSource + '\n'; + } + + return builtinsSource.replace(root.glslSource, ''); + } + + function combineShader(shaderSource, isFragmentShader) { + var i; + var length; + + // Combine shader sources, generally for pseudo-polymorphism, e.g., czm_getMaterial. + var combinedSources = ''; + var sources = shaderSource.sources; + if (defined(sources)) { + for (i = 0, length = sources.length; i < length; ++i) { + // #line needs to be on its own line. + combinedSources += '\n#line 0\n' + sources[i]; + } + } + + combinedSources = removeComments(combinedSources); + + // Extract existing shader version from sources + var version; + combinedSources = combinedSources.replace(/#version\s+(.*?)\n/gm, function(match, group1) { + if (defined(version) && version !== group1) { + throw new DeveloperError('inconsistent versions found: ' + version + ' and ' + group1); + } + // Extract #version to put at the top + version = group1; + + // Replace original #version directive with a new line so the line numbers + // are not off by one. There can be only one #version directive + // and it must appear at the top of the source, only preceded by + // whitespace and comments. + return '\n'; + }); + + // Replace main() for picked if desired. + var pickColorQualifier = shaderSource.pickColorQualifier; + if (defined(pickColorQualifier)) { + combinedSources = combinedSources.replace(/void\s+main\s*\(\s*(?:void)?\s*\)/g, 'void czm_old_main()'); + combinedSources += '\ +\n' + pickColorQualifier + ' vec4 czm_pickColor;\n\ +void main()\n\ +{\n\ + czm_old_main();\n\ + if (gl_FragColor.a == 0.0) {\n\ + discard;\n\ + }\n\ + gl_FragColor = czm_pickColor;\n\ +}'; + } + + // combine into single string + var result = ''; + + // #version must be first + // defaults to #version 100 if not specified + if (defined(version)) { + result = '#version ' + version; + } + + if (isFragmentShader) { + result += '\ +#ifdef GL_FRAGMENT_PRECISION_HIGH\n\ + precision highp float;\n\ +#else\n\ + precision mediump float;\n\ +#endif\n\n'; + } + + // Prepend #defines for uber-shaders + var defines = shaderSource.defines; + if (defined(defines)) { + for (i = 0, length = defines.length; i < length; ++i) { + var define = defines[i]; + if (define.length !== 0) { + result += '#define ' + define + '\n'; + } + } + } + + // append built-ins + if (shaderSource.includeBuiltIns) { + result += getBuiltinsAndAutomaticUniforms(combinedSources); + } + + // reset line number + result += '\n#line 0\n'; + + // append actual source + result += combinedSources; + + return result; + } + + /** + * An object containing various inputs that will be combined to form a final GLSL shader string. + * + * @param {Object} [options] Object with the following properties: + * @param {String[]} [options.sources] An array of strings to combine containing GLSL code for the shader. + * @param {String[]} [options.defines] An array of strings containing GLSL identifiers to #define. + * @param {String} [options.pickColorQualifier] The GLSL qualifier, uniform or varying, for the input czm_pickColor. When defined, a pick fragment shader is generated. + * @param {Boolean} [options.includeBuiltIns=true] If true, referenced built-in functions will be included with the combined shader. Set to false if this shader will become a source in another shader, to avoid duplicating functions. + * + * @exception {DeveloperError} options.pickColorQualifier must be 'uniform' or 'varying'. + * + * @example + * // 1. Prepend #defines to a shader + * var source = new Cesium.ShaderSource({ + * defines : ['WHITE'], + * so + * urces : ['void main() { \n#ifdef WHITE\n gl_FragColor = vec4(1.0); \n#else\n gl_FragColor = vec4(0.0); \n#endif\n }'] + * }); + * + * // 2. Modify a fragment shader for picking + * var source = new Cesium.ShaderSource({ + * sources : ['void main() { gl_FragColor = vec4(1.0); }'], + * pickColorQualifier : 'uniform' + * }); + * + * @private + */ + var ShaderSource = function(options) { + options = defaultValue(options, defaultValue.EMPTY_OBJECT); + var pickColorQualifier = options.pickColorQualifier; + + //>>includeStart('debug', pragmas.debug); + if (defined(pickColorQualifier) && pickColorQualifier !== 'uniform' && pickColorQualifier !== 'varying') { + throw new DeveloperError('options.pickColorQualifier must be \'uniform\' or \'varying\'.'); + } + //>>includeEnd('debug'); + + this.defines = defined(options.defines) ? options.defines.slice(0) : []; + this.sources = defined(options.sources) ? options.sources.slice(0) : []; + this.pickColorQualifier = pickColorQualifier; + this.includeBuiltIns = defaultValue(options.includeBuiltIns, true); + }; + + ShaderSource.prototype.clone = function() { + return new ShaderSource({ + sources : this.sources, + defines : this.defines, + pickColorQuantifier : this.pickColorQualifier, + includeBuiltIns : this.includeBuiltIns + }); + }; + + /** + * Create a single string containing the full, combined shader with all dependencies and defines. + * + * @param {Boolean} isFragmentShader True if this shader will be a fragment shader. + * @returns {String} The combined shader string. + */ + ShaderSource.prototype.getCombinedShader = function(isFragmentShader) { + //>>includeStart('debug', pragmas.debug); + if (!defined(isFragmentShader)) { + throw new DeveloperError('isFragmentShader is required.'); + } + //>>includeEnd('debug'); + + return combineShader(this, isFragmentShader); + }; + + /** + * For ShaderProgram testing + * @private + */ + ShaderSource._czmBuiltinsAndUniforms = {}; + + // combine automatic uniforms and Cesium built-ins + for ( var builtinName in CzmBuiltins) { + if (CzmBuiltins.hasOwnProperty(builtinName)) { + ShaderSource._czmBuiltinsAndUniforms[builtinName] = CzmBuiltins[builtinName]; + } + } + for ( var uniformName in AutomaticUniforms) { + if (AutomaticUniforms.hasOwnProperty(uniformName)) { + var uniform = AutomaticUniforms[uniformName]; + if (typeof uniform.getDeclaration === 'function') { + ShaderSource._czmBuiltinsAndUniforms[uniformName] = uniform.getDeclaration(uniformName); + } + } + } + + return ShaderSource; +}); diff --git a/Source/Renderer/createShaderSource.js b/Source/Renderer/createShaderSource.js deleted file mode 100644 index 6d4eeac8cda6..000000000000 --- a/Source/Renderer/createShaderSource.js +++ /dev/null @@ -1,104 +0,0 @@ -/*global define*/ -define([ - '../Core/defaultValue', - '../Core/defined', - '../Core/DeveloperError' - ], function( - defaultValue, - defined, - DeveloperError) { - "use strict"; - - /** - * Creates a GLSL shader source string by sending the input through three stages: - * - * - * @exports createShaderSource - * - * @param {Object} [options] Object with the following properties: - * @param {String[]} [options.defines] An array of strings to combine containing GLSL identifiers to #define. - * @param {String[]} [options.sources] An array of strings to combine containing GLSL code for the shader. - * @param {String} [options.pickColorQualifier] The GLSL qualifier, uniform or varying, for the input czm_pickColor. When defined, a pick fragment shader is generated. - * @returns {String} The generated GLSL shader source. - * - * @exception {DeveloperError} options.pickColorQualifier must be 'uniform' or 'varying'. - * - * @example - * // 1. Prepend #defines to a shader - * var source = Cesium.createShaderSource({ - * defines : ['WHITE'], - * sources : ['void main() { \n#ifdef WHITE\n gl_FragColor = vec4(1.0); \n#else\n gl_FragColor = vec4(0.0); \n#endif\n }'] - * }); - * - * // 2. Modify a fragment shader for picking - * var source = createShaderSource({ - * sources : ['void main() { gl_FragColor = vec4(1.0); }'], - * pickColorQualifier : 'uniform' - * }); - * - * @private - */ - function createShaderSource(options) { - options = defaultValue(options, defaultValue.EMPTY_OBJECT); - var defines = options.defines; - var sources = options.sources; - var pickColorQualifier = options.pickColorQualifier; - - if (defined(pickColorQualifier) && (pickColorQualifier !== 'uniform') && (pickColorQualifier !== 'varying')) { - throw new DeveloperError('options.pickColorQualifier must be \'uniform\' or \'varying\'.'); - } - - var source = ''; - var i; - var length; - - // Stage 1. Prepend #defines for uber-shaders - if (defined(defines) && defines.length > 0) { - length = defines.length; - for (i = 0; i < length; ++i) { - if (defines[i].length !== 0) { - source += '#define ' + defines[i] + '\n'; - } - } - } - - // Stage 2. Combine shader sources, generally for pseudo-polymorphism, e.g., czm_getMaterial. - if (defined(sources) && sources.length > 0) { - length = sources.length; - for (i = 0; i < length; ++i) { - // #line needs to be on its own line. - source += '\n#line 0\n' + sources[i]; - } - } - - // Stage 3. Replace main() for picked if desired. - if (defined(pickColorQualifier)) { - var renamedFS = source.replace(/void\s+main\s*\(\s*(?:void)?\s*\)/g, 'void czm_old_main()'); - var pickMain = - pickColorQualifier + ' vec4 czm_pickColor; \n' + - 'void main() \n' + - '{ \n' + - ' czm_old_main(); \n' + - ' if (gl_FragColor.a == 0.0) { \n' + - ' discard; \n' + - ' } \n' + - ' gl_FragColor = czm_pickColor; \n' + - '}'; - - source = renamedFS + '\n' + pickMain; - } - - return source; - } - - return createShaderSource; -}); diff --git a/Source/Scene/Appearance.js b/Source/Scene/Appearance.js index dda8b50d8951..f8c424be2c9f 100644 --- a/Source/Scene/Appearance.js +++ b/Source/Scene/Appearance.js @@ -4,7 +4,7 @@ define([ '../Core/defaultValue', '../Core/defined', '../Core/defineProperties', - '../Renderer/createShaderSource', + '../Renderer/ShaderSource', './BlendingState', './CullFace' ], function( @@ -12,7 +12,7 @@ define([ defaultValue, defined, defineProperties, - createShaderSource, + ShaderSource, BlendingState, CullFace) { "use strict"; @@ -139,10 +139,28 @@ define([ * @returns {String} The full GLSL fragment shader source. */ Appearance.prototype.getFragmentShaderSource = function() { - return createShaderSource({ - defines : [this.flat ? 'FLAT' : '', this.faceForward ? 'FACE_FORWARD' : ''], - sources : [defined(this.material) ? this.material.shaderSource : '', this.fragmentShaderSource] + var defines = []; + if (this.flat) { + defines.push('FLAT'); + } + if (this.faceForward) { + defines.push('FACE_FORWARD'); + } + + var sources = []; + if (defined(this.material)) { + sources.push(this.material.shaderSource); + } + sources.push(this.fragmentShaderSource); + + // includeBuiltIns is false because the primitive will create its own ShaderSource containing our source + var shaderSource = new ShaderSource({ + defines : defines, + sources : sources, + includeBuiltIns : false }); + + return shaderSource.getCombinedShader(true); }; /** diff --git a/Source/Scene/BillboardCollection.js b/Source/Scene/BillboardCollection.js index bb155c5d7b3e..49fd09342c23 100644 --- a/Source/Scene/BillboardCollection.js +++ b/Source/Scene/BillboardCollection.js @@ -14,8 +14,8 @@ define([ '../Core/IndexDatatype', '../Core/Matrix4', '../Renderer/BufferUsage', - '../Renderer/createShaderSource', '../Renderer/DrawCommand', + '../Renderer/ShaderSource', '../Renderer/VertexArrayFacade', '../Shaders/BillboardCollectionFS', '../Shaders/BillboardCollectionVS', @@ -40,8 +40,8 @@ define([ IndexDatatype, Matrix4, BufferUsage, - createShaderSource, DrawCommand, + ShaderSource, VertexArrayFacade, BillboardCollectionFS, BillboardCollectionVS, @@ -1213,6 +1213,9 @@ define([ var vaLength; var command; var j; + var defines; + var vs; + var fs; if (pass.render) { var colorList = this._colorCommands; @@ -1231,17 +1234,30 @@ define([ (this._shaderScaleByDistance && !this._compiledShaderScaleByDistance) || (this._shaderTranslucencyByDistance && !this._compiledShaderTranslucencyByDistance) || (this._shaderPixelOffsetScaleByDistance && !this._compiledShaderPixelOffsetScaleByDistance)) { - this._sp = context.replaceShaderProgram( - this._sp, - createShaderSource({ - defines : [this._shaderRotation ? 'ROTATION' : '', - this._shaderScaleByDistance ? 'EYE_DISTANCE_SCALING' : '', - this._shaderTranslucencyByDistance ? 'EYE_DISTANCE_TRANSLUCENCY' : '', - this._shaderPixelOffsetScaleByDistance ? 'EYE_DISTANCE_PIXEL_OFFSET' : ''], - sources : [BillboardCollectionVS] - }), - BillboardCollectionFS, - attributeLocations); + + defines = []; + if (this._shaderRotation) { + defines.push('ROTATION'); + } + if (this._shaderScaleByDistance) { + defines.push('EYE_DISTANCE_SCALING'); + } + if (this._shaderTranslucencyByDistance) { + defines.push('EYE_DISTANCE_TRANSLUCENCY'); + } + if (this._shaderPixelOffsetScaleByDistance) { + defines.push('EYE_DISTANCE_PIXEL_OFFSET'); + } + + vs = new ShaderSource({ + defines : defines, + sources : [BillboardCollectionVS] + }); + fs = new ShaderSource({ + sources : [BillboardCollectionFS] + }); + + this._sp = context.replaceShaderProgram(this._sp, vs, fs, attributeLocations); this._compiledShaderRotation = this._shaderRotation; this._compiledShaderScaleByDistance = this._shaderScaleByDistance; this._compiledShaderTranslucencyByDistance = this._shaderTranslucencyByDistance; @@ -1274,7 +1290,6 @@ define([ } } - if (picking) { var pickList = this._pickCommands; @@ -1283,21 +1298,31 @@ define([ (this._shaderScaleByDistance && !this._compiledShaderScaleByDistancePick) || (this._shaderTranslucencyByDistance && !this._compiledShaderTranslucencyByDistancePick) || (this._shaderPixelOffsetScaleByDistance && !this._compiledShaderPixelOffsetScaleByDistancePick)) { - this._spPick = context.replaceShaderProgram( - this._spPick, - createShaderSource({ - defines : ['RENDER_FOR_PICK', - this._shaderRotation ? 'ROTATION' : '', - this._shaderScaleByDistance ? 'EYE_DISTANCE_SCALING' : '', - this._shaderTranslucencyByDistance ? 'EYE_DISTANCE_TRANSLUCENCY' : '', - this._shaderPixelOffsetScaleByDistance ? 'EYE_DISTANCE_PIXEL_OFFSET' : ''], - sources : [BillboardCollectionVS] - }), - createShaderSource({ - defines : ['RENDER_FOR_PICK'], - sources : [BillboardCollectionFS] - }), - attributeLocations); + + defines = ['RENDER_FOR_PICK']; + if (this._shaderRotation) { + defines.push('ROTATION'); + } + if (this._shaderScaleByDistance) { + defines.push('EYE_DISTANCE_SCALING'); + } + if (this._shaderTranslucencyByDistance) { + defines.push('EYE_DISTANCE_TRANSLUCENCY'); + } + if (this._shaderPixelOffsetScaleByDistance) { + defines.push('EYE_DISTANCE_PIXEL_OFFSET'); + } + + vs = new ShaderSource({ + defines : defines, + sources : [BillboardCollectionVS] + }); + fs = new ShaderSource({ + defines : ['RENDER_FOR_PICK'], + sources : [BillboardCollectionFS] + }); + + this._spPick = context.replaceShaderProgram(this._spPick, vs, fs, attributeLocations); this._compiledShaderRotationPick = this._shaderRotation; this._compiledShaderScaleByDistancePick = this._shaderScaleByDistance; this._compiledShaderTranslucencyByDistancePick = this._shaderTranslucencyByDistance; diff --git a/Source/Scene/EllipsoidPrimitive.js b/Source/Scene/EllipsoidPrimitive.js index c6b3ec367b7f..08db5b8c1860 100644 --- a/Source/Scene/EllipsoidPrimitive.js +++ b/Source/Scene/EllipsoidPrimitive.js @@ -11,8 +11,8 @@ define([ '../Core/Matrix4', '../Core/VertexFormat', '../Renderer/BufferUsage', - '../Renderer/createShaderSource', '../Renderer/DrawCommand', + '../Renderer/ShaderSource', '../Shaders/EllipsoidFS', '../Shaders/EllipsoidVS', './BlendingState', @@ -32,8 +32,8 @@ define([ Matrix4, VertexFormat, BufferUsage, - createShaderSource, DrawCommand, + ShaderSource, EllipsoidFS, EllipsoidVS, BlendingState, @@ -347,17 +347,28 @@ define([ var colorCommand = this._colorCommand; - // Recompile shader when material, lighting, or transluceny changes + var defines; + + // Recompile shader when material, lighting, or translucency changes if (materialChanged || lightingChanged || translucencyChanged) { - var colorFS = createShaderSource({ - defines : [ - this.onlySunLighting ? 'ONLY_SUN_LIGHTING' : '', - (!translucent && context.fragmentDepth) ? 'WRITE_DEPTH' : '' - ], - sources : [this.material.shaderSource, EllipsoidFS] } - ); + var colorVS = new ShaderSource({ + sources : [EllipsoidVS] + }); + + defines = []; + if (this.onlySunLighting) { + defines.push('ONLY_SUN_LIGHTING'); + } + if (!translucent && context.fragmentDepth) { + defines.push('WRITE_DEPTH'); + } - this._sp = context.replaceShaderProgram(this._sp, EllipsoidVS, colorFS, attributeLocations); + var colorFS = new ShaderSource({ + defines : defines, + sources : [this.material.shaderSource, EllipsoidFS] + }); + + this._sp = context.replaceShaderProgram(this._sp, colorVS, colorFS, attributeLocations); colorCommand.vertexArray = this._va; colorCommand.renderState = this._rs; @@ -391,16 +402,25 @@ define([ // Recompile shader when material changes if (materialChanged || lightingChanged || !defined(this._pickSP)) { - var pickFS = createShaderSource({ - defines : [ - this.onlySunLighting ? 'ONLY_SUN_LIGHTING' : '', - (!translucent && context.fragmentDepth) ? 'WRITE_DEPTH' : '' - ], + var pickVS = new ShaderSource({ + sources : [EllipsoidVS] + }); + + defines = []; + if (this.onlySunLighting) { + defines.push('ONLY_SUN_LIGHTING'); + } + if (!translucent && context.fragmentDepth) { + defines.push('WRITE_DEPTH'); + } + + var pickFS = new ShaderSource({ + defines : defines, sources : [this.material.shaderSource, EllipsoidFS], pickColorQualifier : 'uniform' }); - this._pickSP = context.replaceShaderProgram(this._pickSP, EllipsoidVS, pickFS, attributeLocations); + this._pickSP = context.replaceShaderProgram(this._pickSP, pickVS, pickFS, attributeLocations); pickCommand.vertexArray = this._va; pickCommand.renderState = this._rs; diff --git a/Source/Scene/FXAA.js b/Source/Scene/FXAA.js index 403dd4fd8d1b..16accc2605bb 100644 --- a/Source/Scene/FXAA.js +++ b/Source/Scene/FXAA.js @@ -6,7 +6,7 @@ define([ '../Core/destroyObject', '../Core/PixelFormat', '../Renderer/ClearCommand', - '../Renderer/createShaderSource', + '../Renderer/ShaderSource', '../Renderer/PixelDatatype', '../Renderer/RenderbufferFormat', '../Shaders/PostProcessFilters/FXAA' @@ -17,7 +17,7 @@ define([ destroyObject, PixelFormat, ClearCommand, - createShaderSource, + ShaderSource, PixelDatatype, RenderbufferFormat, FXAAFS) { @@ -104,11 +104,7 @@ define([ } if (!defined(this._command)) { - var fs = createShaderSource({ - sources : [FXAAFS] - }); - - this._command = context.createViewportQuadCommand(fs, { + this._command = context.createViewportQuadCommand(FXAAFS, { renderState : context.createRenderState(), owner : this }); diff --git a/Source/Scene/Globe.js b/Source/Scene/Globe.js index 5318c8021fb1..c78d5143eb07 100644 --- a/Source/Scene/Globe.js +++ b/Source/Scene/Globe.js @@ -31,8 +31,8 @@ define([ '../Core/Transforms', '../Renderer/BufferUsage', '../Renderer/ClearCommand', - '../Renderer/createShaderSource', '../Renderer/DrawCommand', + '../Renderer/ShaderSource', '../Shaders/GlobeFS', '../Shaders/GlobeFSDepth', '../Shaders/GlobeFSPole', @@ -80,8 +80,8 @@ define([ Transforms, BufferUsage, ClearCommand, - createShaderSource, DrawCommand, + ShaderSource, GlobeFS, GlobeFSDepth, GlobeFSPole, @@ -925,12 +925,21 @@ define([ } } - surfaceShaderSet.baseVertexShaderString = createShaderSource({ + // Firefox 33-34 has a regression that prevents the CORDIC implementation from compiling + // https://github.com/AnalyticalGraphicsInc/cesium/issues/2197 + if (FeatureDetection.isFirefox()) { + var firefoxVersion = FeatureDetection.firefoxVersion(); + if (firefoxVersion[0] >= 33 && firefoxVersion[0] <= 34) { + shaderDefines.push('DISABLE_CORDIC'); + } + } + + surfaceShaderSet.baseVertexShaderSource = new ShaderSource({ defines : shaderDefines, sources : [GlobeVS, getPositionMode, get2DYPositionFraction] }); - surfaceShaderSet.baseFragmentShaderString = createShaderSource({ + surfaceShaderSet.baseFragmentShaderSource = new ShaderSource({ defines : shaderDefines, sources : [GlobeFS] }); diff --git a/Source/Scene/GlobeSurfaceShaderSet.js b/Source/Scene/GlobeSurfaceShaderSet.js index 45bce6953040..ca5a91742bb6 100644 --- a/Source/Scene/GlobeSurfaceShaderSet.js +++ b/Source/Scene/GlobeSurfaceShaderSet.js @@ -16,8 +16,8 @@ define([ * @private */ function GlobeSurfaceShaderSet() { - this.baseVertexShaderString = undefined; - this.baseFragmentShaderString = undefined; + this.baseVertexShaderSource = undefined; + this.baseFragmentShaderSource = undefined; this._attributeLocations = terrainAttributeLocations; this._shaders = {}; } @@ -68,39 +68,57 @@ define([ var key = getShaderKey(textureCount, applyBrightness, applyContrast, applyHue, applySaturation, applyGamma, applyAlpha); var shader = this._shaders[key]; if (!defined(shader)) { - var vs = this.baseVertexShaderString; - var fs = - (applyBrightness ? '#define APPLY_BRIGHTNESS\n' : '') + - (applyContrast ? '#define APPLY_CONTRAST\n' : '') + - (applyHue ? '#define APPLY_HUE\n' : '') + - (applySaturation ? '#define APPLY_SATURATION\n' : '') + - (applyGamma ? '#define APPLY_GAMMA\n' : '') + - (applyAlpha ? '#define APPLY_ALPHA\n' : '') + - '#define TEXTURE_UNITS ' + textureCount + '\n' + - this.baseFragmentShaderString + '\n' + - 'vec4 computeDayColor(vec4 initialColor, vec2 textureCoordinates)\n' + - '{\n' + - ' vec4 color = initialColor;\n'; + var vs = this.baseVertexShaderSource; + + var fs = this.baseFragmentShaderSource.clone(); + fs.defines.push('TEXTURE_UNITS ' + textureCount); + + if (applyBrightness) { + fs.defines.push('APPLY_BRIGHTNESS'); + } + if (applyContrast) { + fs.defines.push('APPLY_CONTRAST'); + } + if (applyHue) { + fs.defines.push('APPLY_HUE'); + } + if (applySaturation) { + fs.defines.push('APPLY_SATURATION'); + } + if (applyGamma) { + fs.defines.push('APPLY_GAMMA'); + } + if (applyAlpha) { + fs.defines.push('APPLY_ALPHA'); + } + + var computeDayColor = '\ +vec4 computeDayColor(vec4 initialColor, vec2 textureCoordinates)\n\ +{\n\ + vec4 color = initialColor;\n'; for (var i = 0; i < textureCount; ++i) { - fs += - 'color = sampleAndBlend(\n' + - ' color,\n' + - ' u_dayTextures[' + i + '],\n' + - ' textureCoordinates,\n' + - ' u_dayTextureTexCoordsRectangle[' + i + '],\n' + - ' u_dayTextureTranslationAndScale[' + i + '],\n' + - (applyAlpha ? ' u_dayTextureAlpha[' + i + '],\n' : '1.0,\n') + - (applyBrightness ? ' u_dayTextureBrightness[' + i + '],\n' : '0.0,\n') + - (applyContrast ? ' u_dayTextureContrast[' + i + '],\n' : '0.0,\n') + - (applyHue ? ' u_dayTextureHue[' + i + '],\n' : '0.0,\n') + - (applySaturation ? ' u_dayTextureSaturation[' + i + '],\n' : '0.0,\n') + - (applyGamma ? ' u_dayTextureOneOverGamma[' + i + ']);\n' : '0.0);\n') ; + computeDayColor += '\ + color = sampleAndBlend(\n\ + color,\n\ + u_dayTextures[' + i + '],\n\ + textureCoordinates,\n\ + u_dayTextureTexCoordsRectangle[' + i + '],\n\ + u_dayTextureTranslationAndScale[' + i + '],\n\ + ' + (applyAlpha ? 'u_dayTextureAlpha[' + i + ']' : '1.0') + ',\n\ + ' + (applyBrightness ? 'u_dayTextureBrightness[' + i + ']' : '0.0') + ',\n\ + ' + (applyContrast ? 'u_dayTextureContrast[' + i + ']' : '0.0') + ',\n\ + ' + (applyHue ? 'u_dayTextureHue[' + i + ']' : '0.0') + ',\n\ + ' + (applySaturation ? 'u_dayTextureSaturation[' + i + ']' : '0.0') + ',\n\ + ' + (applyGamma ? 'u_dayTextureOneOverGamma[' + i + ']' : '0.0') + '\n\ + );\n'; } - fs += - ' return color;\n' + - '}'; + computeDayColor += '\ + return color;\n\ +}'; + + fs.sources.push(computeDayColor); shader = context.createShaderProgram(vs, fs, this._attributeLocations); this._shaders[key] = shader; diff --git a/Source/Scene/ImageryLayer.js b/Source/Scene/ImageryLayer.js index 033372ed0e03..0052562cd560 100644 --- a/Source/Scene/ImageryLayer.js +++ b/Source/Scene/ImageryLayer.js @@ -23,6 +23,7 @@ define([ '../Renderer/ClearCommand', '../Renderer/DrawCommand', '../Renderer/MipmapHint', + '../Renderer/ShaderSource', '../Renderer/TextureMagnificationFilter', '../Renderer/TextureMinificationFilter', '../Renderer/TextureWrap', @@ -56,6 +57,7 @@ define([ ClearCommand, DrawCommand, MipmapHint, + ShaderSource, TextureMagnificationFilter, TextureMinificationFilter, TextureWrap, @@ -831,10 +833,23 @@ define([ bufferUsage : BufferUsage.STATIC_DRAW }); - reproject.shaderProgram = context.createShaderProgram( - ReprojectWebMercatorVS, - ReprojectWebMercatorFS, - reprojectAttribInds); + var vs = new ShaderSource({ + sources : [ReprojectWebMercatorVS] + }); + var fs = new ShaderSource({ + sources : [ReprojectWebMercatorFS] + }); + + // Firefox 33-34 has a regression that prevents the CORDIC implementation from compiling + // https://github.com/AnalyticalGraphicsInc/cesium/issues/2197 + if (FeatureDetection.isFirefox()) { + var firefoxVersion = FeatureDetection.firefoxVersion(); + if (firefoxVersion[0] >= 33 && firefoxVersion[0] <= 34) { + vs.defines.push('DISABLE_CORDIC'); + } + } + + reproject.shaderProgram = context.createShaderProgram(vs, fs, reprojectAttribInds); var maximumSupportedAnisotropy = context.maximumTextureFilterAnisotropy; reproject.sampler = context.createSampler({ diff --git a/Source/Scene/Model.js b/Source/Scene/Model.js index bb86dd83e151..ef98fa633a58 100644 --- a/Source/Scene/Model.js +++ b/Source/Scene/Model.js @@ -24,8 +24,8 @@ define([ '../Core/Queue', '../Core/RuntimeError', '../Renderer/BufferUsage', - '../Renderer/createShaderSource', '../Renderer/DrawCommand', + '../Renderer/ShaderSource', '../Renderer/TextureMinificationFilter', '../Renderer/TextureWrap', '../ThirdParty/gltfDefaults', @@ -63,8 +63,8 @@ define([ Queue, RuntimeError, BufferUsage, - createShaderSource, DrawCommand, + ShaderSource, TextureMinificationFilter, TextureWrap, gltfDefaults, @@ -967,7 +967,7 @@ define([ if (model.allowPicking) { // PERFORMANCE_IDEA: Can optimize this shader with a glTF hint. https://github.com/KhronosGroup/glTF/issues/181 - var pickFS = createShaderSource({ + var pickFS = new ShaderSource({ sources : [fs], pickColorQualifier : 'uniform' }); diff --git a/Source/Scene/OIT.js b/Source/Scene/OIT.js index d2c0c46ee3ec..868c110b46de 100644 --- a/Source/Scene/OIT.js +++ b/Source/Scene/OIT.js @@ -5,9 +5,9 @@ define([ '../Core/destroyObject', '../Core/PixelFormat', '../Renderer/ClearCommand', - '../Renderer/createShaderSource', '../Renderer/PixelDatatype', '../Renderer/RenderState', + '../Renderer/ShaderSource', '../Shaders/AdjustTranslucentFS', '../Shaders/CompositeOITFS', './BlendEquation', @@ -18,9 +18,9 @@ define([ destroyObject, PixelFormat, ClearCommand, - createShaderSource, PixelDatatype, RenderState, + ShaderSource, AdjustTranslucentFS, CompositeOITFS, BlendEquation, @@ -218,8 +218,12 @@ define([ var uniformMap; if (!defined(this._compositeCommand)) { - fs = createShaderSource({ - defines : [this._translucentMRTSupport ? 'MRT' : ''], + var defines = []; + if (this._translucentMRTSupport) { + defines.push('MRT'); + } + fs = new ShaderSource({ + defines : defines, sources : [CompositeOITFS] }); @@ -243,7 +247,7 @@ define([ if (!defined(this._adjustTranslucentCommand)) { if (this._translucentMRTSupport) { - fs = createShaderSource({ + fs = new ShaderSource({ defines : ['MRT'], sources : [AdjustTranslucentFS] }); @@ -263,7 +267,7 @@ define([ owner : this }); } else if (this._translucentMultipassSupport) { - fs = createShaderSource({ + fs = new ShaderSource({ sources : [AdjustTranslucentFS] }); @@ -312,7 +316,8 @@ define([ }; var translucentColorBlend = { - enabled : true,color : new Color(0.0, 0.0, 0.0, 0.0), + enabled : true, + color : new Color(0.0, 0.0, 0.0, 0.0), equationRgb : BlendEquation.ADD, equationAlpha : BlendEquation.ADD, functionSourceRgb : BlendFunction.ONE, @@ -322,7 +327,8 @@ define([ }; var translucentAlphaBlend = { - enabled : true,color : new Color(0.0, 0.0, 0.0, 0.0), + enabled : true, + color : new Color(0.0, 0.0, 0.0, 0.0), equationRgb : BlendEquation.ADD, equationAlpha : BlendEquation.ADD, functionSourceRgb : BlendFunction.ZERO, @@ -379,32 +385,37 @@ define([ var shader = cache[id]; if (!defined(shader)) { var attributeLocations = shaderProgram._attributeLocations; - var vs = shaderProgram.vertexShaderSource; - var fs = shaderProgram.fragmentShaderSource; - var renamedFS = fs.replace(/void\s+main\s*\(\s*(?:void)?\s*\)/g, 'void czm_translucent_main()'); - renamedFS = renamedFS.replace(/gl_FragColor/g, 'czm_gl_FragColor'); - renamedFS = renamedFS.replace(/\bdiscard\b/g, 'czm_discard = true'); - renamedFS = renamedFS.replace(/czm_phong/g, 'czm_translucentPhong'); + var fs = shaderProgram.fragmentShaderSource.clone(); + + fs.sources = fs.sources.map(function(source) { + source = source.replace(/void\s+main\s*\(\s*(?:void)?\s*\)/g, 'void czm_translucent_main()'); + source = source.replace(/gl_FragColor/g, 'czm_gl_FragColor'); + source = source.replace(/\bdiscard\b/g, 'czm_discard = true'); + source = source.replace(/czm_phong/g, 'czm_translucentPhong'); + return source; + }); // Discarding the fragment in main is a workaround for ANGLE D3D9 // shader compilation errors. - var newSourceFS = - (source.indexOf('gl_FragData') !== -1 ? '#extension GL_EXT_draw_buffers : enable \n' : '') + - 'vec4 czm_gl_FragColor;\n' + - 'bool czm_discard = false;\n' + - renamedFS + '\n\n' + - 'void main()\n' + - '{\n' + - ' czm_translucent_main();\n' + - ' if (czm_discard)\n' + - ' {\n' + - ' discard;\n' + - ' }\n' + - source + - '}\n'; - - shader = context.createShaderProgram(vs, newSourceFS, attributeLocations); + + fs.sources.splice(0, 0, + (source.indexOf('gl_FragData') !== -1 ? '#extension GL_EXT_draw_buffers : enable \n' : '') + + 'vec4 czm_gl_FragColor;\n' + + 'bool czm_discard = false;\n'); + + fs.sources.push( + 'void main()\n' + + '{\n' + + ' czm_translucent_main();\n' + + ' if (czm_discard)\n' + + ' {\n' + + ' discard;\n' + + ' }\n' + + source + + '}\n'); + + shader = context.createShaderProgram(shaderProgram.vertexShaderSource, fs, attributeLocations); cache[id] = shader; } diff --git a/Source/Scene/PolylineCollection.js b/Source/Scene/PolylineCollection.js index dbb4c39469a5..08224a3b3686 100644 --- a/Source/Scene/PolylineCollection.js +++ b/Source/Scene/PolylineCollection.js @@ -16,8 +16,8 @@ define([ '../Core/Math', '../Core/Matrix4', '../Renderer/BufferUsage', - '../Renderer/createShaderSource', '../Renderer/DrawCommand', + '../Renderer/ShaderSource', '../Shaders/PolylineCommon', '../Shaders/PolylineFS', '../Shaders/PolylineVS', @@ -43,8 +43,8 @@ define([ CesiumMath, Matrix4, BufferUsage, - createShaderSource, DrawCommand, + ShaderSource, PolylineCommon, PolylineFS, PolylineVS, @@ -1028,11 +1028,18 @@ define([ return; } - var vsSource = createShaderSource({ sources : [PolylineCommon, PolylineVS] }); - var fsSource = createShaderSource({ sources : [this.material.shaderSource, PolylineFS] }); - var fsPick = createShaderSource({ sources : [fsSource], pickColorQualifier : 'varying' }); - this.shaderProgram = context.createShaderProgram(vsSource, fsSource, attributeLocations); - this.pickShaderProgram = context.createShaderProgram(vsSource, fsPick, attributeLocations); + var vs = new ShaderSource({ + sources : [PolylineCommon, PolylineVS] + }); + var fs = new ShaderSource({ + sources : [this.material.shaderSource, PolylineFS] + }); + var fsPick = new ShaderSource({ + sources : fs.sources, + pickColorQualifier : 'varying' + }); + this.shaderProgram = context.createShaderProgram(vs, fs, attributeLocations); + this.pickShaderProgram = context.createShaderProgram(vs, fsPick, attributeLocations); }; function intersectsIDL(polyline) { diff --git a/Source/Scene/PolylineColorAppearance.js b/Source/Scene/PolylineColorAppearance.js index 9204add7172a..fb13cf6f9b9a 100644 --- a/Source/Scene/PolylineColorAppearance.js +++ b/Source/Scene/PolylineColorAppearance.js @@ -3,7 +3,6 @@ define([ '../Core/defaultValue', '../Core/defineProperties', '../Core/VertexFormat', - '../Renderer/createShaderSource', '../Shaders/Appearances/PerInstanceFlatColorAppearanceFS', '../Shaders/Appearances/PolylineColorAppearanceVS', '../Shaders/PolylineCommon', @@ -12,13 +11,15 @@ define([ defaultValue, defineProperties, VertexFormat, - createShaderSource, PerInstanceFlatColorAppearanceFS, PolylineColorAppearanceVS, PolylineCommon, Appearance) { "use strict"; + var defaultVertexShaderSource = PolylineCommon + '\n' + PolylineColorAppearanceVS; + var defaultFragmentShaderSource = PerInstanceFlatColorAppearanceFS; + /** * An appearance for {@link GeometryInstance} instances with color attributes and {@link PolylineGeometry}. * This allows several geometry instances, each with a different color, to @@ -61,8 +62,6 @@ define([ var translucent = defaultValue(options.translucent, true); var closed = false; - var vs = createShaderSource({ sources : [PolylineCommon, PolylineColorAppearanceVS] }); - var fs = PerInstanceFlatColorAppearanceFS; var vertexFormat = PolylineColorAppearance.VERTEX_FORMAT; /** @@ -85,8 +84,8 @@ define([ */ this.translucent = translucent; - this._vertexShaderSource = defaultValue(options.vertexShaderSource, vs); - this._fragmentShaderSource = defaultValue(options.fragmentShaderSource, fs); + this._vertexShaderSource = defaultValue(options.vertexShaderSource, defaultVertexShaderSource); + this._fragmentShaderSource = defaultValue(options.fragmentShaderSource, defaultFragmentShaderSource); this._renderState = defaultValue(options.renderState, Appearance.getDefaultRenderState(translucent, closed)); this._closed = closed; diff --git a/Source/Scene/PolylineMaterialAppearance.js b/Source/Scene/PolylineMaterialAppearance.js index 718a3551fb1e..debc9bf75dc3 100644 --- a/Source/Scene/PolylineMaterialAppearance.js +++ b/Source/Scene/PolylineMaterialAppearance.js @@ -4,7 +4,6 @@ define([ '../Core/defined', '../Core/defineProperties', '../Core/VertexFormat', - '../Renderer/createShaderSource', '../Shaders/Appearances/PolylineMaterialAppearanceVS', '../Shaders/PolylineCommon', '../Shaders/PolylineFS', @@ -15,7 +14,6 @@ define([ defined, defineProperties, VertexFormat, - createShaderSource, PolylineMaterialAppearanceVS, PolylineCommon, PolylineFS, @@ -23,6 +21,9 @@ define([ Material) { "use strict"; + var defaultVertexShaderSource = PolylineCommon + '\n' + PolylineMaterialAppearanceVS; + var defaultFragmentShaderSource = PolylineFS; + /** * An appearance for {@link PolylineGeometry} that supports shading with materials. * @@ -61,8 +62,6 @@ define([ var translucent = defaultValue(options.translucent, true); var closed = false; - var vs = createShaderSource({ sources : [PolylineCommon, PolylineMaterialAppearanceVS] }); - var fs = PolylineFS; var vertexFormat = PolylineMaterialAppearance.VERTEX_FORMAT; /** @@ -87,8 +86,8 @@ define([ */ this.translucent = translucent; - this._vertexShaderSource = defaultValue(options.vertexShaderSource, vs); - this._fragmentShaderSource = defaultValue(options.fragmentShaderSource, fs); + this._vertexShaderSource = defaultValue(options.vertexShaderSource, defaultVertexShaderSource); + this._fragmentShaderSource = defaultValue(options.fragmentShaderSource, defaultFragmentShaderSource); this._renderState = defaultValue(options.renderState, Appearance.getDefaultRenderState(translucent, closed)); this._closed = closed; diff --git a/Source/Scene/Primitive.js b/Source/Scene/Primitive.js index 75669f6d0ccb..8f9c743120be 100644 --- a/Source/Scene/Primitive.js +++ b/Source/Scene/Primitive.js @@ -19,8 +19,8 @@ define([ '../Core/subdivideArray', '../Core/TaskProcessor', '../Renderer/BufferUsage', - '../Renderer/createShaderSource', '../Renderer/DrawCommand', + '../Renderer/ShaderSource', '../ThirdParty/when', './CullFace', './Pass', @@ -47,8 +47,8 @@ define([ subdivideArray, TaskProcessor, BufferUsage, - createShaderSource, DrawCommand, + ShaderSource, when, CullFace, Pass, @@ -485,7 +485,7 @@ define([ } } - return createShaderSource({ sources : [forwardDecl, attributes, vertexShaderSource, computeFunctions] }); + return [forwardDecl, attributes, vertexShaderSource, computeFunctions].join('\n'); } function createPickVertexShaderSource(vertexShaderSource) { @@ -853,7 +853,10 @@ define([ validateShaderMatching(this._sp, attributeLocations); if (allowPicking) { - var pickFS = createShaderSource({ sources : [fs], pickColorQualifier : 'varying' }); + var pickFS = new ShaderSource({ + sources : [fs], + pickColorQualifier : 'varying' + }); this._pickSP = context.replaceShaderProgram(this._pickSP, createPickVertexShaderSource(vs), pickFS, attributeLocations); } else { this._pickSP = context.createShaderProgram(vs, fs, attributeLocations); diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js index 97cef8db1290..d3373613446d 100644 --- a/Source/Scene/Scene.js +++ b/Source/Scene/Scene.js @@ -985,8 +985,12 @@ define([ function createDebugFragmentShaderProgram(command, scene, shaderProgram) { var context = scene.context; var sp = defaultValue(shaderProgram, command.shaderProgram); - var fragmentShaderSource = sp.fragmentShaderSource; - var renamedFS = fragmentShaderSource.replace(/void\s+main\s*\(\s*(?:void)?\s*\)/g, 'void czm_Debug_main()'); + var fs = sp.fragmentShaderSource.clone(); + + fs.sources = fs.sources.map(function(source) { + source = source.replace(/void\s+main\s*\(\s*(?:void)?\s*\)/g, 'void czm_Debug_main()'); + return source; + }); var newMain = 'void main() \n' + @@ -1012,9 +1016,10 @@ define([ newMain += '}'; - var source = renamedFS + '\n' + newMain; + fs.sources.push(newMain); + var attributeLocations = getAttributeLocations(sp); - return context.createShaderProgram(sp.vertexShaderSource, source, attributeLocations); + return context.createShaderProgram(sp.vertexShaderSource, fs, attributeLocations); } function executeDebugCommand(command, scene, passState, renderState, shaderProgram) { diff --git a/Source/Scene/SkyAtmosphere.js b/Source/Scene/SkyAtmosphere.js index 1e678707d082..3404b57446c9 100644 --- a/Source/Scene/SkyAtmosphere.js +++ b/Source/Scene/SkyAtmosphere.js @@ -10,8 +10,8 @@ define([ '../Core/GeometryPipeline', '../Core/VertexFormat', '../Renderer/BufferUsage', - '../Renderer/createShaderSource', '../Renderer/DrawCommand', + '../Renderer/ShaderSource', '../Shaders/SkyAtmosphereFS', '../Shaders/SkyAtmosphereVS', './BlendingState', @@ -28,8 +28,8 @@ define([ GeometryPipeline, VertexFormat, BufferUsage, - createShaderSource, DrawCommand, + ShaderSource, SkyAtmosphereFS, SkyAtmosphereVS, BlendingState, @@ -164,13 +164,13 @@ define([ blending : BlendingState.ALPHA_BLEND }); - var vs = createShaderSource({ + var vs = new ShaderSource({ defines : ['SKY_FROM_SPACE'], sources : [SkyAtmosphereVS] }); this._spSkyFromSpace = context.createShaderProgram(vs, SkyAtmosphereFS); - vs = createShaderSource({ + vs = new ShaderSource({ defines : ['SKY_FROM_ATMOSPHERE'], sources : [SkyAtmosphereVS] }); diff --git a/Source/Scene/ViewportQuad.js b/Source/Scene/ViewportQuad.js index e588075586d6..96645a36342a 100644 --- a/Source/Scene/ViewportQuad.js +++ b/Source/Scene/ViewportQuad.js @@ -5,7 +5,7 @@ define([ '../Core/defined', '../Core/destroyObject', '../Core/DeveloperError', - '../Renderer/createShaderSource', + '../Renderer/ShaderSource', '../Shaders/ViewportQuadFS', './BlendingState', './Material', @@ -16,7 +16,7 @@ define([ defined, destroyObject, DeveloperError, - createShaderSource, + ShaderSource, ViewportQuadFS, BlendingState, Material, @@ -133,7 +133,9 @@ define([ this._overlayCommand.shaderProgram.destroy(); } - var fsSource = createShaderSource({ sources : [this._material.shaderSource, ViewportQuadFS] }); + var fsSource = new ShaderSource({ + sources : [this._material.shaderSource, ViewportQuadFS] + }); this._overlayCommand = context.createViewportQuadCommand(fsSource, { renderState : this._rs, uniformMap : this._material._uniforms, diff --git a/Source/Shaders/Builtin/Functions/cosineAndSine.glsl b/Source/Shaders/Builtin/Functions/cosineAndSine.glsl index 5821de1367a5..57e8f7314cfd 100644 --- a/Source/Shaders/Builtin/Functions/cosineAndSine.glsl +++ b/Source/Shaders/Builtin/Functions/cosineAndSine.glsl @@ -1,3 +1,6 @@ +// Firefox 33-34 has a regression that prevents the CORDIC implementation from compiling +#ifndef DISABLE_CORDIC + /** * @private */ @@ -208,4 +211,6 @@ vec2 czm_cosineAndSine(float angle) { return cordic(angle); } -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/Source/Shaders/Builtin/Functions/latitudeToWebMercatorFraction.glsl b/Source/Shaders/Builtin/Functions/latitudeToWebMercatorFraction.glsl index c51f7e038f22..0a69e21db6b2 100644 --- a/Source/Shaders/Builtin/Functions/latitudeToWebMercatorFraction.glsl +++ b/Source/Shaders/Builtin/Functions/latitudeToWebMercatorFraction.glsl @@ -15,7 +15,12 @@ */ float czm_latitudeToWebMercatorFraction(float latitude, float southMercatorYLow, float southMercatorYHigh, float oneOverMercatorHeight) { +// Firefox 33-34 has a regression that prevents the CORDIC implementation from compiling +#ifdef DISABLE_CORDIC + float sinLatitude = sin(latitude); +#else float sinLatitude = czm_cosineAndSine(latitude).y; +#endif float mercatorY = 0.5 * log((1.0 + sinLatitude) / (1.0 - sinLatitude)); // mercatorY - southMercatorY in simulated double precision. diff --git a/Specs/Renderer/ShaderProgramSpec.js b/Specs/Renderer/ShaderProgramSpec.js index df8729bb49fb..c55b4a78b1e9 100644 --- a/Specs/Renderer/ShaderProgramSpec.js +++ b/Specs/Renderer/ShaderProgramSpec.js @@ -11,6 +11,7 @@ defineSuite([ 'Renderer/BufferUsage', 'Renderer/ClearCommand', 'Renderer/DrawCommand', + 'Renderer/ShaderSource', 'Specs/createContext', 'Specs/destroyContext' ], function( @@ -25,6 +26,7 @@ defineSuite([ BufferUsage, ClearCommand, DrawCommand, + ShaderSource, createContext, destroyContext) { "use strict"; @@ -53,9 +55,9 @@ defineSuite([ beforeAll(function() { context = createContext(); - for(var functionName in injectedTestFunctions) { - if(injectedTestFunctions.hasOwnProperty(functionName)) { - ShaderProgram._czmBuiltinsAndUniforms[functionName] = injectedTestFunctions[functionName]; + for ( var functionName in injectedTestFunctions) { + if (injectedTestFunctions.hasOwnProperty(functionName)) { + ShaderSource._czmBuiltinsAndUniforms[functionName] = injectedTestFunctions[functionName]; } } @@ -66,7 +68,7 @@ defineSuite([ for ( var functionName in injectedTestFunctions) { if (injectedTestFunctions.hasOwnProperty(functionName)) { - delete ShaderProgram._czmBuiltinsAndUniforms[functionName]; + delete ShaderSource._czmBuiltinsAndUniforms[functionName]; } } }); @@ -96,8 +98,17 @@ defineSuite([ var fs = 'void main() { gl_FragColor = vec4(1.0); }'; sp = context.createShaderProgram(vs, fs); - expect(sp.vertexShaderSource).toEqual(vs); - expect(sp.fragmentShaderSource).toEqual(fs); + var expectedVSText = new ShaderSource({ + sources : [vs] + }).getCombinedShader(false); + + expect(sp._vertexShaderText).toEqual(expectedVSText); + + var expectedFSText = new ShaderSource({ + sources : [fs] + }).getCombinedShader(true); + + expect(sp._fragmentShaderText).toEqual(expectedFSText); }); it('has a position vertex attribute', function() { @@ -649,8 +660,8 @@ defineSuite([ it('fails with built-in function circular dependency', function() { var vs = 'void main() { gl_Position = vec4(0.0); }'; var fs = 'void main() { czm_circularDependency1(); gl_FragColor = vec4(1.0); }'; - sp = context.createShaderProgram(vs, fs); expect(function() { + sp = context.createShaderProgram(vs, fs); sp._bind(); }).toThrowDeveloperError(); }); diff --git a/Specs/Renderer/ShaderSourceSpec.js b/Specs/Renderer/ShaderSourceSpec.js new file mode 100644 index 000000000000..ab7a1a1037f6 --- /dev/null +++ b/Specs/Renderer/ShaderSourceSpec.js @@ -0,0 +1,69 @@ +/*global defineSuite*/ +defineSuite([ + 'Renderer/ShaderSource' + ], function( + ShaderSource) { + "use strict"; + /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/ + + it('combines #defines', function() { + var source = new ShaderSource({ + defines : ['A', 'B', ''] + }); + + var shaderText = source.getCombinedShader(false); + expect(shaderText).toContain('#define A'); + expect(shaderText).toContain('#define B'); + expect(shaderText.match(/#define/g).length).toEqual(2); + }); + + it('combines sources', function() { + var source = new ShaderSource({ + sources : ['void func() {}', 'void main() {}'] + }); + var shaderText = source.getCombinedShader(false); + expect(shaderText).toContain('#line 0\nvoid func() {}'); + expect(shaderText).toContain('#line 0\nvoid main() {}'); + }); + + it('combines #defines and sources', function() { + var source = new ShaderSource({ + defines : ['A', 'B', ''], + sources : ['void func() {}', 'void main() {}'] + }); + var shaderText = source.getCombinedShader(false); + expect(shaderText).toContain('#define A'); + expect(shaderText).toContain('#define B'); + expect(shaderText.match(/#define/g).length).toEqual(2); + expect(shaderText).toContain('#line 0\nvoid func() {}'); + expect(shaderText).toContain('#line 0\nvoid main() {}'); + }); + + it('creates a pick shader with a uniform', function() { + var source = new ShaderSource({ + sources : ['void main() { gl_FragColor = vec4(1.0); }'], + pickColorQualifier : 'uniform' + }); + var shaderText = source.getCombinedShader(false); + expect(shaderText).toContain('uniform vec4 czm_pickColor;'); + expect(shaderText).toContain('gl_FragColor = czm_pickColor;'); + }); + + it('creates a pick shader with a varying', function() { + var source = new ShaderSource({ + sources : ['void main() { gl_FragColor = vec4(1.0); }'], + pickColorQualifier : 'varying' + }); + var shaderText = source.getCombinedShader(false); + expect(shaderText).toContain('varying vec4 czm_pickColor;'); + expect(shaderText).toContain('gl_FragColor = czm_pickColor;'); + }); + + it('throws with invalid qualifier', function() { + expect(function() { + var source = new ShaderSource({ + pickColorQualifier : 'const' + }); + }).toThrowDeveloperError(); + }); +}); \ No newline at end of file diff --git a/Specs/Renderer/createShaderSourceSpec.js b/Specs/Renderer/createShaderSourceSpec.js deleted file mode 100644 index 6ba0c7b6f18c..000000000000 --- a/Specs/Renderer/createShaderSourceSpec.js +++ /dev/null @@ -1,59 +0,0 @@ -/*global defineSuite*/ -defineSuite([ - 'Renderer/createShaderSource' - ], function( - createShaderSource) { - "use strict"; - /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/ - - it('combines #defines', function() { - var source = createShaderSource({ defines : ['A', 'B', '' ] }); - expect(source).toContain('#define A'); - expect(source).toContain('#define B'); - expect(source.match(/#define/g).length).toEqual(2); - }); - - it('combines sources', function() { - var source = createShaderSource({ sources : ['void func() {}', 'void main() {}'] }); - expect(source).toContain('void func() {}'); - expect(source).toContain('void main() {}'); - expect(source.match(/#line/g).length).toEqual(2); - }); - - it('combines #defines and sources', function() { - var source = createShaderSource({ - defines : ['A', 'B', '' ], - sources : ['void func() {}', 'void main() {}'] - }); - expect(source).toContain('#define A'); - expect(source).toContain('#define B'); - expect(source.match(/#define/g).length).toEqual(2); - expect(source).toContain('void func() {}'); - expect(source).toContain('void main() {}'); - expect(source.match(/#line/g).length).toEqual(2); - }); - - it('creates a pick shader with a uniform', function() { - var source = createShaderSource({ - sources : ['void main() { gl_FragColor = vec4(1.0); }'], - pickColorQualifier : 'uniform' - }); - expect(source).toContain('uniform vec4 czm_pickColor;'); - expect(source).toContain('gl_FragColor = czm_pickColor;'); - }); - - it('creates a pick shader with a varying', function() { - var source = createShaderSource({ - sources : ['void main() { gl_FragColor = vec4(1.0); }'], - pickColorQualifier : 'varying' - }); - expect(source).toContain('varying vec4 czm_pickColor;'); - expect(source).toContain('gl_FragColor = czm_pickColor;'); - }); - - it('throws with invalid qualifier', function() { - expect(function() { - createShaderSource({ pickColorQualifier : 'const' }); - }).toThrowDeveloperError(); - }); -}); \ No newline at end of file