diff --git a/Source/Renderer/Context.js b/Source/Renderer/Context.js
index 7eff7dc3fa52..5ab984919331 100644
--- a/Source/Renderer/Context.js
+++ b/Source/Renderer/Context.js
@@ -269,12 +269,15 @@ define([
// Query and initialize extensions
this._standardDerivatives = !!getExtension(gl, ['OES_standard_derivatives']);
+ this._blendMinmax = !!getExtension(gl, ['EXT_blend_minmax']);
this._elementIndexUint = !!getExtension(gl, ['OES_element_index_uint']);
this._depthTexture = !!getExtension(gl, ['WEBGL_depth_texture', 'WEBKIT_WEBGL_depth_texture']);
this._textureFloat = !!getExtension(gl, ['OES_texture_float']);
this._fragDepth = !!getExtension(gl, ['EXT_frag_depth']);
this._debugShaders = getExtension(gl, ['WEBGL_debug_shaders']);
+ this._colorBufferFloat = this._webgl2 && !!getExtension(gl, ['EXT_color_buffer_float']);
+
this._s3tc = !!getExtension(gl, ['WEBGL_compressed_texture_s3tc', 'MOZ_WEBGL_compressed_texture_s3tc', 'WEBKIT_WEBGL_compressed_texture_s3tc']);
this._pvrtc = !!getExtension(gl, ['WEBGL_compressed_texture_pvrtc', 'WEBKIT_WEBGL_compressed_texture_pvrtc']);
this._etc1 = !!getExtension(gl, ['WEBGL_compressed_texture_etc1']);
@@ -483,7 +486,21 @@ define([
*/
standardDerivatives : {
get : function() {
- return this._standardDerivatives;
+ return this._standardDerivatives || this._webgl2;
+ }
+ },
+
+ /**
+ * true
if the EXT_blend_minmax extension is supported. This
+ * extension extends blending capabilities by adding two new blend equations:
+ * the minimum or maximum color components of the source and destination colors.
+ * @memberof Context.prototype
+ * @type {Boolean}
+ * @see {@link https://www.khronos.org/registry/webgl/extensions/EXT_blend_minmax/}
+ */
+ blendMinmax : {
+ get : function() {
+ return this._blendMinmax || this._webgl2;
}
},
@@ -510,7 +527,7 @@ define([
*/
depthTexture : {
get : function() {
- return this._depthTexture;
+ return this._depthTexture || this._webgl2;
}
},
@@ -523,7 +540,7 @@ define([
*/
floatingPointTexture : {
get : function() {
- return this._textureFloat;
+ return this._textureFloat || this._colorBufferFloat;
}
},
@@ -597,7 +614,7 @@ define([
*/
fragmentDepth : {
get : function() {
- return this._fragDepth;
+ return this._fragDepth || this._webgl2;
}
},
@@ -614,6 +631,20 @@ define([
}
},
+ /**
+ * true
if the EXT_color_buffer_float extension is supported. This
+ * extension makes the formats gl.R16F, gl.RG16F, gl.RGBA16F, gl.R32F, gl.RG32F,
+ * gl.RGBA32F, gl.R11F_G11F_B10F color renderable.
+ * @memberof Context.prototype
+ * @type {Boolean}
+ * @see {@link https://www.khronos.org/registry/webgl/extensions/EXT_color_buffer_float/}
+ */
+ colorBufferFloat : {
+ get : function() {
+ return this._colorBufferFloat;
+ }
+ },
+
/**
* true
if the WEBGL_draw_buffers extension is supported. This
* extensions provides support for multiple render targets. The framebuffer object can have mutiple
@@ -1076,7 +1107,8 @@ define([
}),
uniformMap : overrides.uniformMap,
owner : overrides.owner,
- framebuffer : overrides.framebuffer
+ framebuffer : overrides.framebuffer,
+ pass : overrides.pass
});
};
diff --git a/Source/Renderer/RenderState.js b/Source/Renderer/RenderState.js
index 3b30bebbd290..07ab7633fe12 100644
--- a/Source/Renderer/RenderState.js
+++ b/Source/Renderer/RenderState.js
@@ -21,7 +21,9 @@ define([
function validateBlendEquation(blendEquation) {
return ((blendEquation === WebGLConstants.FUNC_ADD) ||
(blendEquation === WebGLConstants.FUNC_SUBTRACT) ||
- (blendEquation === WebGLConstants.FUNC_REVERSE_SUBTRACT));
+ (blendEquation === WebGLConstants.FUNC_REVERSE_SUBTRACT) ||
+ (blendEquation === WebGLConstants.MIN) ||
+ (blendEquation === WebGLConstants.MAX));
}
function validateBlendFunction(blendFunction) {
diff --git a/Source/Renderer/ShaderCache.js b/Source/Renderer/ShaderCache.js
index d8635213de6d..a2a9e86d7358 100644
--- a/Source/Renderer/ShaderCache.js
+++ b/Source/Renderer/ShaderCache.js
@@ -96,8 +96,8 @@ define([
});
}
- var vertexShaderText = vertexShaderSource.createCombinedVertexShader();
- var fragmentShaderText = fragmentShaderSource.createCombinedFragmentShader();
+ var vertexShaderText = vertexShaderSource.createCombinedVertexShader(this._context);
+ var fragmentShaderText = fragmentShaderSource.createCombinedFragmentShader(this._context);
var keyword = vertexShaderText + fragmentShaderText + JSON.stringify(attributeLocations);
var cachedShader;
@@ -169,10 +169,11 @@ define([
});
}
- var vertexShaderText = vertexShaderSource.createCombinedVertexShader();
- var fragmentShaderText = fragmentShaderSource.createCombinedFragmentShader();
-
var context = this._context;
+
+ var vertexShaderText = vertexShaderSource.createCombinedVertexShader(context);
+ var fragmentShaderText = fragmentShaderSource.createCombinedFragmentShader(context);
+
var derivedShaderProgram = new ShaderProgram({
gl : context._gl,
logShaderCompilation : context.logShaderCompilation,
diff --git a/Source/Renderer/ShaderSource.js b/Source/Renderer/ShaderSource.js
index 36bb4637145b..b1f4684b93df 100644
--- a/Source/Renderer/ShaderSource.js
+++ b/Source/Renderer/ShaderSource.js
@@ -2,12 +2,14 @@ define([
'../Core/defaultValue',
'../Core/defined',
'../Core/DeveloperError',
+ '../Renderer/modernizeShader',
'../Shaders/Builtin/CzmBuiltins',
'./AutomaticUniforms'
], function(
defaultValue,
defined,
DeveloperError,
+ modernizeShader,
CzmBuiltins,
AutomaticUniforms) {
'use strict';
@@ -151,7 +153,7 @@ define([
return builtinsSource.replace(root.glslSource, '');
}
- function combineShader(shaderSource, isFragmentShader) {
+ function combineShader(shaderSource, isFragmentShader, context) {
var i;
var length;
@@ -186,6 +188,17 @@ define([
return '\n';
});
+ // Extract shader extensions from sources
+ var extensions = [];
+ combinedSources = combinedSources.replace(/#extension.*\n/gm, function(match) {
+ // Extract extension to put at the top
+ extensions.push(match);
+
+ // Replace original #extension directive with a new line so the line numbers
+ // are not off by one.
+ return '\n';
+ });
+
// Remove precision qualifier
combinedSources = combinedSources.replace(/precision\s(lowp|mediump|highp)\s(float|int);/, '');
@@ -204,6 +217,11 @@ define([
result = '#version ' + version + '\n';
}
+ var extensionsLength = extensions.length;
+ for (i = 0; i < extensionsLength; i++) {
+ result += extensions[i];
+ }
+
if (isFragmentShader) {
result += '\
#ifdef GL_FRAGMENT_PRECISION_HIGH\n\
@@ -224,6 +242,12 @@ define([
}
}
+ // GLSLModernizer inserts its own layout qualifiers
+ // at this position in the source
+ if (context.webgl2) {
+ result += '#define OUTPUT_DECLARATION\n\n';
+ }
+
// append built-ins
if (shaderSource.includeBuiltIns) {
result += getBuiltinsAndAutomaticUniforms(combinedSources);
@@ -235,6 +259,11 @@ define([
// append actual source
result += combinedSources;
+ // modernize the source
+ if (context.webgl2) {
+ result = modernizeShader(result, isFragmentShader, true);
+ }
+
return result;
}
@@ -297,19 +326,23 @@ define([
/**
* Create a single string containing the full, combined vertex shader with all dependencies and defines.
*
+ * @param {Context} context The current rendering context
+ *
* @returns {String} The combined shader string.
*/
- ShaderSource.prototype.createCombinedVertexShader = function() {
- return combineShader(this, false);
+ ShaderSource.prototype.createCombinedVertexShader = function(context) {
+ return combineShader(this, false, context);
};
/**
* Create a single string containing the full, combined fragment shader with all dependencies and defines.
*
+ * @param {Context} context The current rendering context
+ *
* @returns {String} The combined shader string.
*/
- ShaderSource.prototype.createCombinedFragmentShader = function() {
- return combineShader(this, true);
+ ShaderSource.prototype.createCombinedFragmentShader = function(context) {
+ return combineShader(this, true, context);
};
/**
diff --git a/Source/Renderer/Texture.js b/Source/Renderer/Texture.js
index 2de068ea749e..34d71a6d56a6 100644
--- a/Source/Renderer/Texture.js
+++ b/Source/Renderer/Texture.js
@@ -71,6 +71,23 @@ define([
internalFormat = WebGLConstants.DEPTH_COMPONENT24;
}
}
+
+ if (pixelDatatype === PixelDatatype.FLOAT) {
+ switch (pixelFormat) {
+ case PixelFormat.RGBA:
+ internalFormat = WebGLConstants.RGBA32F;
+ break;
+ case PixelFormat.RGB:
+ internalFormat = WebGLConstants.RGB32F;
+ break;
+ case PixelFormat.RG:
+ internalFormat = WebGLConstants.RG32F;
+ break;
+ case PixelFormat.R:
+ internalFormat = WebGLConstants.R32F;
+ break;
+ }
+ }
}
//>>includeStart('debug', pragmas.debug);
diff --git a/Source/Renderer/modernizeShader.js b/Source/Renderer/modernizeShader.js
new file mode 100644
index 000000000000..b2accdf7e62a
--- /dev/null
+++ b/Source/Renderer/modernizeShader.js
@@ -0,0 +1,227 @@
+define([
+ '../Core/defined',
+ '../Core/DeveloperError',
+ ], function(
+ defined,
+ DeveloperError) {
+ 'use strict';
+
+ /**
+ * A function to port GLSL shaders from GLSL ES 1.00 to GLSL ES 3.00
+ *
+ * This function is nowhere near comprehensive or complete. It just
+ * handles some common cases.
+ *
+ * Note that this function requires the presence of the
+ * "#define OUTPUT_DECLARATION" line that is appended
+ * by ShaderSource.
+ *
+ * @private
+ */
+ function modernizeShader(source, isFragmentShader) {
+ var outputDeclarationRegex = /#define OUTPUT_DECLARATION/;
+ var splitSource = source.split('\n');
+
+ if (/#version 300 es/g.test(source)) {
+ return source;
+ }
+
+ var outputDeclarationLine = -1;
+ var i, line;
+ for (i = 0; i < splitSource.length; ++i) {
+ line = splitSource[i];
+ if (outputDeclarationRegex.test(line)) {
+ outputDeclarationLine = i;
+ break;
+ }
+ }
+
+ if (outputDeclarationLine === -1) {
+ throw new DeveloperError('Could not find a #define OUTPUT_DECLARATION!');
+ }
+
+ var outputVariables = [];
+
+ for (i = 0; i < 10; i++) {
+ var fragDataString = 'gl_FragData\\[' + i + '\\]';
+ var newOutput = 'czm_out' + i;
+ var regex = new RegExp(fragDataString, 'g');
+ if (regex.test(source)) {
+ setAdd(newOutput, outputVariables);
+ replaceInSourceString(fragDataString, newOutput, splitSource);
+ splitSource.splice(outputDeclarationLine, 0, 'layout(location = ' + i + ') out vec4 ' + newOutput + ';');
+ outputDeclarationLine += 1;
+ }
+ }
+
+ var czmFragColor = 'czm_fragColor';
+ if (findInSource('gl_FragColor', splitSource)) {
+ setAdd(czmFragColor, outputVariables);
+ replaceInSourceString('gl_FragColor', czmFragColor, splitSource);
+ splitSource.splice(outputDeclarationLine, 0, 'layout(location = 0) out vec4 czm_fragColor;');
+ outputDeclarationLine += 1;
+ }
+
+ var variableMap = getVariablePreprocessorBranch(outputVariables, splitSource);
+ var lineAdds = {};
+ for (i = 0; i < splitSource.length; i++) {
+ line = splitSource[i];
+ for (var variable in variableMap) {
+ if (variableMap.hasOwnProperty(variable)) {
+ var matchVar = new RegExp('(layout)[^]+(out)[^]+(' + variable + ')[^]+', 'g');
+ if (matchVar.test(line)) {
+ lineAdds[line] = variable;
+ }
+ }
+ }
+ }
+
+ for (var layoutDeclaration in lineAdds) {
+ if (lineAdds.hasOwnProperty(layoutDeclaration)) {
+ var variableName = lineAdds[layoutDeclaration];
+ var lineNumber = splitSource.indexOf(layoutDeclaration);
+ var entry = variableMap[variableName];
+ var depth = entry.length;
+ var d;
+ for (d = 0; d < depth; d++) {
+ splitSource.splice(lineNumber, 0, entry[d]);
+ }
+ lineNumber += depth + 1;
+ for (d = depth - 1; d >= 0; d--) {
+ splitSource.splice(lineNumber, 0, '#endif //' + entry[d]);
+ }
+ }
+ }
+
+ var versionThree = '#version 300 es';
+ var foundVersion = false;
+ for (i = 0; i < splitSource.length; i++) {
+ if (/#version/.test(splitSource[i])) {
+ splitSource[i] = versionThree;
+ foundVersion = true;
+ }
+ }
+
+ if (!foundVersion) {
+ splitSource.splice(0, 0, versionThree);
+ }
+
+ removeExtension('EXT_draw_buffers', splitSource);
+ removeExtension('EXT_frag_depth', splitSource);
+
+ replaceInSourceString('texture2D', 'texture', splitSource);
+ replaceInSourceString('texture3D', 'texture', splitSource);
+ replaceInSourceString('textureCube', 'texture', splitSource);
+ replaceInSourceString('gl_FragDepthEXT', 'gl_FragDepth', splitSource);
+
+ if (isFragmentShader) {
+ replaceInSourceString('varying', 'in', splitSource);
+ } else {
+ replaceInSourceString('attribute', 'in', splitSource);
+ replaceInSourceString('varying', 'out', splitSource);
+ }
+
+ return compileSource(splitSource);
+ }
+
+ // Note that this fails if your string looks like
+ // searchString[singleCharacter]searchString
+ function replaceInSourceString(str, replacement, splitSource) {
+ var regexStr = '(^|[^\\w])(' + str + ')($|[^\\w])';
+ var regex = new RegExp(regexStr, 'g');
+
+ var splitSourceLength = splitSource.length;
+ for (var i = 0; i < splitSourceLength; ++i) {
+ var line = splitSource[i];
+ splitSource[i] = line.replace(regex, '$1' + replacement + '$3');
+ }
+ }
+
+ function replaceInSourceRegex(regex, replacement, splitSource) {
+ var splitSourceLength = splitSource.length;
+ for (var i = 0; i < splitSourceLength; ++i) {
+ var line = splitSource[i];
+ splitSource[i] = line.replace(regex, replacement);
+ }
+ }
+
+ function findInSource(str, splitSource) {
+ var regexStr = '(^|[^\\w])(' + str + ')($|[^\\w])';
+ var regex = new RegExp(regexStr, 'g');
+
+ var splitSourceLength = splitSource.length;
+ for (var i = 0; i < splitSourceLength; ++i) {
+ var line = splitSource[i];
+ if (regex.test(line)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ function compileSource(splitSource) {
+ var wholeSource = '';
+
+ var splitSourceLength = splitSource.length;
+ for (var i = 0; i < splitSourceLength; ++i) {
+ wholeSource += splitSource[i] + '\n';
+ }
+ return wholeSource;
+ }
+
+ function setAdd(variable, set) {
+ if (set.indexOf(variable) === -1) {
+ set.push(variable);
+ }
+ }
+
+ function getVariablePreprocessorBranch(layoutVariables, splitSource) {
+ var variableMap = {};
+
+ var numLayoutVariables = layoutVariables.length;
+
+ var stack = [];
+ for (var i = 0; i < splitSource.length; ++i) {
+ var line = splitSource[i];
+ var hasIF = /(#ifdef|#if)/g.test(line);
+ var hasELSE = /#else/g.test(line);
+ var hasENDIF = /#endif/g.test(line);
+
+ if (hasIF) {
+ stack.push(line);
+ } else if (hasELSE) {
+ var top = stack[stack.length - 1];
+ var op = top.replace('ifdef', 'ifndef');
+ if (/if/g.test(op)) {
+ op = op.replace(/(#if\s+)(\S*)([^]*)/, '$1!($2)$3');
+ }
+ stack.pop();
+ stack.push(op);
+ } else if (hasENDIF) {
+ stack.pop();
+ } else if (!/layout/g.test(line)) {
+ for (var varIndex = 0; varIndex < numLayoutVariables; ++varIndex) {
+ var varName = layoutVariables[varIndex];
+ if (line.indexOf(varName) !== -1) {
+ if (!defined(variableMap[varName])) {
+ variableMap[varName] = stack.slice();
+ } else {
+ variableMap[varName] = variableMap[varName].filter(function(x) {
+ return stack.indexOf(x) >= 0;
+ });
+ }
+ }
+ }
+ }
+ }
+
+ return variableMap;
+ }
+
+ function removeExtension(name, splitSource) {
+ var regex = '#extension\\s+GL_' + name + '\\s+:\\s+[a-zA-Z0-9]+\\s*$';
+ replaceInSourceRegex(new RegExp(regex, 'g'), '', splitSource);
+ }
+
+ return modernizeShader;
+});
diff --git a/Source/Scene/BlendEquation.js b/Source/Scene/BlendEquation.js
index 27bf019d21eb..e589beeae418 100644
--- a/Source/Scene/BlendEquation.js
+++ b/Source/Scene/BlendEquation.js
@@ -34,9 +34,27 @@ define([
* @type {Number}
* @constant
*/
- REVERSE_SUBTRACT : WebGLConstants.FUNC_REVERSE_SUBTRACT
+ REVERSE_SUBTRACT : WebGLConstants.FUNC_REVERSE_SUBTRACT,
- // No min and max like in ColladaFX GLES2 profile
+ /**
+ * Pixel values are given to the minimum function (min(source, destination)).
+ *
+ * This equation operates on each pixel color component.
+ *
+ * @type {Number}
+ * @constant
+ */
+ MIN : WebGLConstants.MIN,
+
+ /**
+ * Pixel values are given to the maximum function (max(source, destination)).
+ *
+ * This equation operates on each pixel color component.
+ *
+ * @type {Number}
+ * @constant
+ */
+ MAX : WebGLConstants.MAX
};
return freezeObject(BlendEquation);
diff --git a/Source/Shaders/GlobeFS.glsl b/Source/Shaders/GlobeFS.glsl
index c36fc9737286..34f8bc02c4ca 100644
--- a/Source/Shaders/GlobeFS.glsl
+++ b/Source/Shaders/GlobeFS.glsl
@@ -66,7 +66,7 @@ varying vec3 v_mieColor;
vec4 sampleAndBlend(
vec4 previousColor,
- sampler2D texture,
+ sampler2D textureToSample,
vec2 tileTextureCoordinates,
vec4 textureCoordinateRectangle,
vec4 textureCoordinateTranslationAndScale,
@@ -94,7 +94,7 @@ vec4 sampleAndBlend(
vec2 translation = textureCoordinateTranslationAndScale.xy;
vec2 scale = textureCoordinateTranslationAndScale.zw;
vec2 textureCoordinates = tileTextureCoordinates * scale + translation;
- vec4 value = texture2D(texture, textureCoordinates);
+ vec4 value = texture2D(textureToSample, textureCoordinates);
vec3 color = value.rgb;
float alpha = value.a;
diff --git a/Specs/Renderer/ShaderProgramSpec.js b/Specs/Renderer/ShaderProgramSpec.js
index e2934ed94b66..0fe33e0acc0b 100644
--- a/Specs/Renderer/ShaderProgramSpec.js
+++ b/Specs/Renderer/ShaderProgramSpec.js
@@ -65,13 +65,13 @@ defineSuite([
var expectedVSText = new ShaderSource({
sources : [vs]
- }).createCombinedVertexShader();
+ }).createCombinedVertexShader(context);
expect(sp._vertexShaderText).toEqual(expectedVSText);
var expectedFSText = new ShaderSource({
sources : [fs]
- }).createCombinedFragmentShader();
+ }).createCombinedFragmentShader(context);
expect(sp._fragmentShaderText).toEqual(expectedFSText);
});
diff --git a/Specs/Renderer/ShaderSourceSpec.js b/Specs/Renderer/ShaderSourceSpec.js
index b9105e4b28ed..80b4f4fbe9c4 100644
--- a/Specs/Renderer/ShaderSourceSpec.js
+++ b/Specs/Renderer/ShaderSourceSpec.js
@@ -4,12 +4,16 @@ defineSuite([
ShaderSource) {
'use strict';
+ var mockContext = {
+ webgl2 : false
+ };
+
it('combines #defines', function() {
var source = new ShaderSource({
defines : ['A', 'B', '']
});
- var shaderText = source.createCombinedVertexShader();
+ var shaderText = source.createCombinedVertexShader(mockContext);
expect(shaderText).toContain('#define A');
expect(shaderText).toContain('#define B');
expect(shaderText.match(/#define/g).length).toEqual(2);
@@ -19,7 +23,7 @@ defineSuite([
var source = new ShaderSource({
sources : ['void func() {}', 'void main() {}']
});
- var shaderText = source.createCombinedVertexShader();
+ var shaderText = source.createCombinedVertexShader(mockContext);
expect(shaderText).toContain('#line 0\nvoid func() {}');
expect(shaderText).toContain('#line 0\nvoid main() {}');
});
@@ -29,7 +33,7 @@ defineSuite([
defines : ['A', 'B', ''],
sources : ['void func() {}', 'void main() {}']
});
- var shaderText = source.createCombinedVertexShader();
+ var shaderText = source.createCombinedVertexShader(mockContext);
expect(shaderText).toContain('#define A');
expect(shaderText).toContain('#define B');
expect(shaderText.match(/#define/g).length).toEqual(2);
@@ -42,7 +46,7 @@ defineSuite([
sources : ['void main() { gl_FragColor = vec4(1.0); }'],
pickColorQualifier : 'uniform'
});
- var shaderText = source.createCombinedVertexShader();
+ var shaderText = source.createCombinedVertexShader(mockContext);
expect(shaderText).toContain('uniform vec4 czm_pickColor;');
expect(shaderText).toContain('gl_FragColor = czm_pickColor;');
});
@@ -52,7 +56,7 @@ defineSuite([
sources : ['void main() { gl_FragColor = vec4(1.0); }'],
pickColorQualifier : 'varying'
});
- var shaderText = source.createCombinedVertexShader();
+ var shaderText = source.createCombinedVertexShader(mockContext);
expect(shaderText).toContain('varying vec4 czm_pickColor;');
expect(shaderText).toContain('gl_FragColor = czm_pickColor;');
});
@@ -69,7 +73,7 @@ defineSuite([
var source = new ShaderSource({
sources : ['#version 300 es\nvoid main() {gl_FragColor = vec4(1.0); }']
});
- var shaderText = source.createCombinedVertexShader();
+ var shaderText = source.createCombinedVertexShader(mockContext);
expect(shaderText).toStartWith('#version 300 es\n');
});
});
diff --git a/Specs/Renderer/modernizeShaderSpec.js b/Specs/Renderer/modernizeShaderSpec.js
new file mode 100644
index 000000000000..b915fd6461e7
--- /dev/null
+++ b/Specs/Renderer/modernizeShaderSpec.js
@@ -0,0 +1,317 @@
+defineSuite([
+ 'Renderer/modernizeShader'
+ ], function(
+ modernizeShader) {
+ 'use strict';
+
+ it('adds version string', function() {
+ var simple =
+ '#define OUTPUT_DECLARATION \n' +
+ 'void main() \n' +
+ '{ \n' +
+ '} \n';
+ var output = modernizeShader(simple, true);
+ var expected = '#version 300 es';
+ expect(output.split('\n')[0]).toEqual(expected);
+ });
+
+ it('removes extensions', function() {
+ var extensions =
+ '#define OUTPUT_DECLARATION \n' +
+ '#extension GL_EXT_draw_buffers : enable \n' +
+ 'void main() \n' +
+ '{ \n' +
+ '} \n';
+ var output = modernizeShader(extensions, true);
+ var notExpected = '#extension GL_EXT_draw_buffers : enable \n';
+ expect(output).not.toContain(notExpected);
+ });
+
+ it('throws exception if no output declaration', function() {
+ var noOutputDeclaration =
+ 'void main() \n' +
+ '{ \n' +
+ '} \n';
+ var runFunc = function() {
+ modernizeShader(noOutputDeclaration, true);
+ };
+ expect(runFunc).toThrowDeveloperError();
+ });
+
+ it('creates layout qualifier for gl_FragColor', function() {
+ var noQualifiers =
+ '#define OUTPUT_DECLARATION \n' +
+ 'void main() \n' +
+ '{ \n' +
+ ' gl_FragColor = vec4(0.0); \n' +
+ '} \n';
+ var output = modernizeShader(noQualifiers, true);
+ var expected = 'layout(location = 0) out vec4 czm_fragColor;';
+ expect(output).toContain(expected);
+ });
+
+ it('creates layout qualifier for gl_FragData', function() {
+ var noQualifiers =
+ '#define OUTPUT_DECLARATION \n' +
+ 'void main() \n' +
+ '{ \n' +
+ ' gl_FragData[0] = vec4(0.0); \n' +
+ '} \n';
+ var output = modernizeShader(noQualifiers, true);
+ var expected = 'layout(location = 0) out vec4 czm_out0;';
+ expect(output).toContain(expected);
+ });
+
+ it('creates layout qualifier for MRT', function() {
+ var noQualifiers =
+ '#define OUTPUT_DECLARATION \n' +
+ '#extension GL_EXT_draw_buffers : enable \n' +
+ 'void main() \n' +
+ '{ \n' +
+ ' gl_FragData[0] = vec4(0.0); \n' +
+ ' gl_FragData[1] = vec4(1.0); \n' +
+ '} \n';
+ var output = modernizeShader(noQualifiers, true);
+ var expected0 = 'layout(location = 0) out vec4 czm_out0;';
+ var expected1 = 'layout(location = 1) out vec4 czm_out1;';
+ expect(output).toContain(expected0);
+ expect(output).toContain(expected1);
+ });
+
+ it('does not create layout qualifier for reserved word lookalike variables', function() {
+ var noQualifiers =
+ '#define OUTPUT_DECLARATION \n' +
+ 'uniform sampler2D example; \n' +
+ 'void main() \n' +
+ '{ \n' +
+ ' vec4 gl_FragData_ = vec4(0.0); \n' +
+ ' vec4 a_gl_FragData = vec4(0.0); \n' +
+ ' vec2 thisIsNotAGoodNameForAtexture2D = vec2(0.0); \n' +
+ ' vec4 gl_FragColor = texture2D(example, thisIsNotAGoodNameForAtexture2D); \n' +
+ '} \n';
+ var output = modernizeShader(noQualifiers, true);
+ var expectedBadName = 'vec2 thisIsNotAGoodNameForAtexture2D';
+ var expectedVariable = 'vec4 a_gl_FragData = vec4(0.0);';
+ var expectedTextureCall = 'texture(example, thisIsNotAGoodNameForAtexture2D)';
+ var notExpectedLayout = 'layout(location = 0) out czm_out';
+ expect(output).toContain(expectedBadName);
+ expect(output).toContain(expectedVariable);
+ expect(output).toContain(expectedTextureCall);
+ expect(output).not.toContain(notExpectedLayout);
+ });
+
+ it('creates layout qualifier with swizzle', function() {
+ var noQualifiers =
+ '#define OUTPUT_DECLARATION \n' +
+ '#extension GL_EXT_draw_buffers : enable \n' +
+ 'void main() \n' +
+ '{ \n' +
+ ' gl_FragData[0] = vec4(0.0); \n' +
+ ' gl_FragData[1].xyz = vec3(1.0); \n' +
+ '} \n';
+ var output = modernizeShader(noQualifiers, true);
+ var expected0 = 'layout(location = 0) out vec4 czm_out0;';
+ var expected1 = 'layout(location = 1) out vec4 czm_out1;';
+ expect(output).toContain(expected0);
+ expect(output).toContain(expected1);
+ });
+
+ it('removes old functions/variables from fragment shader', function() {
+ var old_fragment =
+ '#define OUTPUT_DECLARATION \n' +
+ '#extension GL_EXT_draw_buffers : enable \n' +
+ 'uniform sampler2D example; \n' +
+ 'uniform sampler2D exampleCube; \n' +
+ 'uniform sampler2D example3D; \n' +
+ 'varying vec2 v_textureCoordinates; \n' +
+ 'void main() \n' +
+ '{ \n' +
+ ' gl_FragData[0] = texture2D(example, v_textureCoordinates); \n' +
+ ' gl_FragData[1] = textureCube(exampleCube, v_textureCoordinates); \n' +
+ ' gl_FragData[2] = texture3D(example3D, v_textureCoordinates); \n' +
+ ' gl_FragDepthEXT = 0.0; \n' +
+ '} \n';
+
+ var output = modernizeShader(old_fragment, true);
+
+ var expectedDepth = 'gl_FragDepth = 0.0;';
+ var expectedTexture2D = 'texture(example, v_textureCoordinates);';
+ var expectedTextureCube = 'texture(exampleCube, v_textureCoordinates);';
+ var expectedTexture3D = 'texture(example3D, v_textureCoordinates);';
+ var expectedIn = 'in vec2 v_textureCoordinates;';
+
+ var notExpectedDepth = 'gl_FragDepthEXT = 0.0;';
+ var notExpectedTexture2D = 'texture2D(example, v_textureCoordinates);';
+ var notExpectedTextureCube = 'textureCube(exampleCube, v_textureCoordinates);';
+ var notExpectedTexture3D = 'texture3D(example3D, v_textureCoordinates);';
+ var notExpectedVarying = 'varying vec2 v_textureCoordinates;';
+
+ expect(output).toContain(expectedDepth);
+ expect(output).toContain(expectedTexture2D);
+ expect(output).toContain(expectedTextureCube);
+ expect(output).toContain(expectedTexture3D);
+ expect(output).toContain(expectedIn);
+
+ expect(output).not.toContain(notExpectedDepth);
+ expect(output).not.toContain(notExpectedTexture2D);
+ expect(output).not.toContain(notExpectedTextureCube);
+ expect(output).not.toContain(notExpectedTexture3D);
+ expect(output).not.toContain(notExpectedVarying);
+ });
+
+ it('removes old functions/variables from vertex shader', function() {
+ var old_vertex =
+ '#define OUTPUT_DECLARATION \n' +
+ 'attribute vec4 position; \n' +
+ 'varying vec4 varyingVariable; \n' +
+ 'void main() \n' +
+ '{ \n' +
+ ' gl_Position = position; \n' +
+ ' varyingVariable = position.wzyx; \n' +
+ '} \n';
+
+ var output = modernizeShader(old_vertex, false);
+
+ var expectedOut = 'out vec4 varyingVariable;';
+ var expectedIn = 'in vec4 position;';
+
+ var notExpectedAttribute = 'attribute vec4 varyingVariable;';
+ var notExpectedVarying = 'varying vec2 varyingVariable;';
+
+ expect(output).toContain(expectedOut);
+ expect(output).toContain(expectedIn);
+
+ expect(output).not.toContain(notExpectedAttribute);
+ expect(output).not.toContain(notExpectedVarying);
+ });
+
+ it('creates single layout qualifier under single branch, single condition', function() {
+ var noQualifiers =
+ '#define OUTPUT_DECLARATION \n' +
+ '#define EXAMPLE_BRANCH \n' +
+ 'void main() \n' +
+ '{ \n' +
+ ' #ifdef EXAMPLE_BRANCH \n' +
+ ' gl_FragData[0] = vec4(0.0); \n' +
+ ' #endif //EXAMPLE_BRANCH \n' +
+ '} \n';
+ var output = modernizeShader(noQualifiers, true);
+ var expected = '#ifdef EXAMPLE_BRANCH \nlayout(location = 0) out vec4 czm_out0;\n#endif';
+ expect(output).toContain(expected);
+ });
+
+ it('creates multiple layout qualifiers under single branch, single condition', function() {
+ var noQualifiers =
+ '#define OUTPUT_DECLARATION \n' +
+ '#define EXAMPLE_BRANCH \n' +
+ 'void main() \n' +
+ '{ \n' +
+ ' #ifdef EXAMPLE_BRANCH \n' +
+ ' gl_FragData[0] = vec4(0.0); \n' +
+ ' gl_FragData[1] = vec4(1.0); \n' +
+ ' #endif //EXAMPLE_BRANCH \n' +
+ '} \n';
+ var output = modernizeShader(noQualifiers, true);
+ var expected0 = '#ifdef EXAMPLE_BRANCH \nlayout(location = 0) out vec4 czm_out0;\n#endif';
+ var expected1 = '#ifdef EXAMPLE_BRANCH \nlayout(location = 1) out vec4 czm_out1;\n#endif';
+ expect(output).toContain(expected0);
+ expect(output).toContain(expected1);
+ });
+
+ it('creates multiple layout qualifiers under multiple branches, single condition (cancels)', function() {
+ var noQualifiers =
+ '#define OUTPUT_DECLARATION \n' +
+ '#define EXAMPLE_BRANCH \n' +
+ 'void main() \n' +
+ '{ \n' +
+ ' #ifdef EXAMPLE_BRANCH \n' +
+ ' gl_FragData[0] = vec4(0.0); \n' +
+ ' gl_FragData[1] = vec4(1.0); \n' +
+ ' #endif //EXAMPLE_BRANCH \n' +
+ ' #ifndef EXAMPLE_BRANCH \n' +
+ ' gl_FragData[0] = vec4(0.0); \n' +
+ ' gl_FragData[1] = vec4(1.0); \n' +
+ ' #endif //!EXAMPLE_BRANCH \n' +
+ '} \n';
+ var output = modernizeShader(noQualifiers, true);
+ var notExpected = '#ifdef EXAMPLE_BRANCH \nlayout(location = 0) out vec4 czm_out0;\n#endif';
+ expect(output).not.toContain(notExpected);
+ });
+
+ it('creates single layout qualifier under multiple branches, multiple conditions (cancels)', function() {
+ var noQualifiers =
+ '#define OUTPUT_DECLARATION \n' +
+ '#define EXAMPLE_BRANCH \n' +
+ '#define EXAMPLE_BRANCH1 \n' +
+ 'void main() \n' +
+ '{ \n' +
+ ' #ifdef EXAMPLE_BRANCH \n' +
+ ' gl_FragData[0] = vec4(0.0); \n' +
+ ' #endif //EXAMPLE_BRANCH \n' +
+ ' #ifdef EXAMPLE_BRANCH1 \n' +
+ ' gl_FragData[0] = vec4(0.0); \n' +
+ ' #endif //EXAMPLE_BRANCH1 \n' +
+ '} \n';
+ var output = modernizeShader(noQualifiers, true);
+ var notExpected = '#ifdef EXAMPLE_BRANCH \nlayout(location = 0) out vec4 czm_out0;\n#endif';
+ expect(output).not.toContain(notExpected);
+ });
+
+ it('creates multiple layout qualifiers under multiple branches, multiple conditions (cascades)', function() {
+ var noQualifiers =
+ '#define OUTPUT_DECLARATION \n' +
+ '#define EXAMPLE_BRANCH \n' +
+ '#define EXAMPLE_BRANCH1 \n' +
+ 'void main() \n' +
+ '{ \n' +
+ ' #ifdef EXAMPLE_BRANCH \n' +
+ ' gl_FragData[0] = vec4(0.0); \n' +
+ ' #ifdef EXAMPLE_BRANCH1 \n' +
+ ' gl_FragData[1] = vec4(1.0); \n' +
+ ' #endif //EXAMPLE_BRANCH1 \n' +
+ ' #endif //EXAMPLE_BRANCH \n' +
+ '} \n';
+ var output = modernizeShader(noQualifiers, true);
+ var expected0 = '#ifdef EXAMPLE_BRANCH \nlayout(location = 0) out vec4 czm_out0;\n#endif';
+ var expected1 = /#ifdef (EXAMPLE_BRANCH|EXAMPLE_BRANCH1)\s*\n\s*#ifdef (EXAMPLE_BRANCH1|EXAMPLE_BRANCH)\s*\n\s*layout\(location = 1\) out vec4 czm_out1;/g;
+ var containsExpected0 = expected1.test(output);
+ expect(output).toContain(expected0);
+ expect(containsExpected0).toBe(true);
+ });
+
+ it('creates single layout qualifier under multiple branches, single condition (else)', function() {
+ var noQualifiers =
+ '#define OUTPUT_DECLARATION \n' +
+ '#define EXAMPLE_BRANCH \n' +
+ 'void main() \n' +
+ '{ \n' +
+ ' #ifdef EXAMPLE_BRANCH \n' +
+ ' gl_FragData[0] = vec4(0.0); \n' +
+ ' #else \n' +
+ ' gl_FragData[0] = vec4(1.0); \n' +
+ ' #endif //EXAMPLE_BRANCH \n' +
+ '} \n';
+ var output = modernizeShader(noQualifiers, true);
+ var notExpected = '#ifdef EXAMPLE_BRANCH \nlayout(location = 0) out vec4 czm_out0;\n#endif';
+ expect(output).not.toContain(notExpected);
+ });
+
+ it('creates branched layout qualifiers for gl_FragColor and gl_FragData', function() {
+ var noQualifiers =
+ '#define OUTPUT_DECLARATION \n' +
+ '#define EXAMPLE_BRANCH \n' +
+ 'void main() \n' +
+ '{ \n' +
+ ' #ifdef EXAMPLE_BRANCH \n' +
+ ' gl_FragData[0] = vec4(0.0); \n' +
+ ' #else \n' +
+ ' gl_FragColor = vec4(1.0); \n' +
+ ' #endif //EXAMPLE_BRANCH \n' +
+ '} \n';
+ var output = modernizeShader(noQualifiers, true);
+ var expected0 = '#ifdef EXAMPLE_BRANCH \nlayout(location = 0) out vec4 czm_out0;\n#endif';
+ var expected1 = '#ifndef EXAMPLE_BRANCH \nlayout(location = 0) out vec4 czm_fragColor;\n#endif';
+ expect(output).toContain(expected0);
+ expect(output).toContain(expected1);
+ });
+});