Index: conformance/resources/glsl-generator.js |
=================================================================== |
--- conformance/resources/glsl-generator.js (revision 0) |
+++ conformance/resources/glsl-generator.js (revision 0) |
@@ -0,0 +1,497 @@ |
+GLSLGenerator = (function() { |
+ |
+var vertexShaderTemplate = [ |
+ "attribute vec4 aPosition;", |
+ "", |
+ "varying vec4 vColor;", |
+ "", |
+ "$(extra)", |
+ "$(emu)", |
+ "", |
+ "void main()", |
+ "{", |
+ " gl_Position = aPosition;", |
+ " vec2 texcoord = vec2(aPosition.xy * 0.5 + vec2(0.5, 0.5));", |
+ " vec4 color = vec4(", |
+ " texcoord,", |
+ " texcoord.x * texcoord.y,", |
+ " (1.0 - texcoord.x) * texcoord.y * 0.5 + 0.5);", |
+ " $(test)", |
+ "}" |
+].join("\n"); |
+ |
+var fragmentShaderTemplate = [ |
+ "#if defined(GL_ES)", |
+ "precision mediump float;", |
+ "#endif", |
+ "", |
+ "varying vec4 vColor;", |
+ "", |
+ "$(extra)", |
+ "$(emu)", |
+ "", |
+ "void main()", |
+ "{", |
+ " $(test)", |
+ "}" |
+].join("\n"); |
+ |
+var baseVertexShader = [ |
+ "attribute vec4 aPosition;", |
+ "", |
+ "varying vec4 vColor;", |
+ "", |
+ "void main()", |
+ "{", |
+ " gl_Position = aPosition;", |
+ " vec2 texcoord = vec2(aPosition.xy * 0.5 + vec2(0.5, 0.5));", |
+ " vColor = vec4(", |
+ " texcoord,", |
+ " texcoord.x * texcoord.y,", |
+ " (1.0 - texcoord.x) * texcoord.y * 0.5 + 0.5);", |
+ "}" |
+].join("\n"); |
+ |
+var baseFragmentShader = [ |
+ "#if defined(GL_ES)", |
+ "precision mediump float;", |
+ "#endif", |
+ "varying vec4 vColor;", |
+ "", |
+ "void main()", |
+ "{", |
+ " gl_FragColor = vColor;", |
+ "}" |
+].join("\n"); |
+ |
+var types = [ |
+ { type: "float", |
+ code: [ |
+ "float $(func)_emu($(args)) {", |
+ " return $(func)_base($(baseArgs));", |
+ "}"].join("\n") |
+ }, |
+ { type: "vec2", |
+ code: [ |
+ "vec2 $(func)_emu($(args)) {", |
+ " return vec2(", |
+ " $(func)_base($(baseArgsX)),", |
+ " $(func)_base($(baseArgsY)));", |
+ "}"].join("\n") |
+ }, |
+ { type: "vec3", |
+ code: [ |
+ "vec3 $(func)_emu($(args)) {", |
+ " return vec3(", |
+ " $(func)_base($(baseArgsX)),", |
+ " $(func)_base($(baseArgsY)),", |
+ " $(func)_base($(baseArgsZ)));", |
+ "}"].join("\n") |
+ }, |
+ { type: "vec4", |
+ code: [ |
+ "vec4 $(func)_emu($(args)) {", |
+ " return vec4(", |
+ " $(func)_base($(baseArgsX)),", |
+ " $(func)_base($(baseArgsY)),", |
+ " $(func)_base($(baseArgsZ)),", |
+ " $(func)_base($(baseArgsW)));", |
+ "}"].join("\n") |
+ } |
+]; |
+ |
+var bvecTypes = [ |
+ { type: "bvec2", |
+ code: [ |
+ "bvec2 $(func)_emu($(args)) {", |
+ " return bvec2(", |
+ " $(func)_base($(baseArgsX)),", |
+ " $(func)_base($(baseArgsY)));", |
+ "}"].join("\n") |
+ }, |
+ { type: "bvec3", |
+ code: [ |
+ "bvec3 $(func)_emu($(args)) {", |
+ " return bvec3(", |
+ " $(func)_base($(baseArgsX)),", |
+ " $(func)_base($(baseArgsY)),", |
+ " $(func)_base($(baseArgsZ)));", |
+ "}"].join("\n") |
+ }, |
+ { type: "bvec4", |
+ code: [ |
+ "vec4 $(func)_emu($(args)) {", |
+ " return bvec4(", |
+ " $(func)_base($(baseArgsX)),", |
+ " $(func)_base($(baseArgsY)),", |
+ " $(func)_base($(baseArgsZ)),", |
+ " $(func)_base($(baseArgsW)));", |
+ "}"].join("\n") |
+ } |
+]; |
+ |
+var replaceRE = /\$\((\w+)\)/g; |
+ |
+var replaceParams = function(str, params) { |
+ return str.replace(replaceRE, function(str, p1, offset, s) { |
+ if (params[p1] === undefined) { |
+ throw "unknown string param '" + p1 + "'"; |
+ } |
+ return params[p1]; |
+ }); |
+}; |
+ |
+var generateReferenceShader = function( |
+ shaderInfo, template, params, typeInfo, test) { |
+ var input = shaderInfo.input; |
+ var output = shaderInfo.output; |
+ var feature = params.feature; |
+ var testFunc = params.testFunc; |
+ var emuFunc = params.emuFunc || ""; |
+ var extra = params.extra || ''; |
+ var args = params.args || "$(type) value"; |
+ var type = typeInfo.type; |
+ var typeCode = typeInfo.code; |
+ |
+ var baseArgs = params.baseArgs || "value$(field)"; |
+ var baseArgsX = replaceParams(baseArgs, {field: ".x"}); |
+ var baseArgsY = replaceParams(baseArgs, {field: ".y"}); |
+ var baseArgsZ = replaceParams(baseArgs, {field: ".z"}); |
+ var baseArgsW = replaceParams(baseArgs, {field: ".w"}); |
+ var baseArgs = replaceParams(baseArgs, {field: ""}); |
+ |
+ test = replaceParams(test, { |
+ input: input, |
+ output: output, |
+ func: feature + "_emu" |
+ }); |
+ emuFunc = replaceParams(emuFunc, { |
+ func: feature |
+ }); |
+ args = replaceParams(args, { |
+ type: type |
+ }); |
+ typeCode = replaceParams(typeCode, { |
+ func: feature, |
+ type: type, |
+ args: args, |
+ baseArgs: baseArgs, |
+ baseArgsX: baseArgsX, |
+ baseArgsY: baseArgsY, |
+ baseArgsZ: baseArgsZ, |
+ baseArgsW: baseArgsW |
+ }); |
+ var shader = replaceParams(template, { |
+ extra: extra, |
+ emu: emuFunc + "\n\n" + typeCode, |
+ test: test |
+ }); |
+ return shader; |
+}; |
+ |
+var generateTestShader = function( |
+ shaderInfo, template, params, test) { |
+ var input = shaderInfo.input; |
+ var output = shaderInfo.output; |
+ var feature = params.feature; |
+ var testFunc = params.testFunc; |
+ var extra = params.extra || ''; |
+ |
+ test = replaceParams(test, { |
+ input: input, |
+ output: output, |
+ func: feature |
+ }); |
+ var shader = replaceParams(template, { |
+ extra: extra, |
+ emu: '', |
+ test: test |
+ }); |
+ return shader; |
+}; |
+ |
+var makeImage = function(canvas) { |
+ var img = document.createElement('img'); |
+ img.src = canvas.toDataURL(); |
+ return img; |
+}; |
+ |
+var runFeatureTest = function(params) { |
+ if (window.initNonKhronosFramework) { |
+ window.initNonKhronosFramework(false); |
+ } |
+ |
+ var wtu = WebGLTestUtils; |
+ var gridRes = params.gridRes; |
+ var tolerance = params.tolerance || 0; |
+ |
+ description("Testing GLSL feature: " + params.feature); |
+ |
+ var width = 32; |
+ var height = 32; |
+ |
+ var console = document.getElementById("console"); |
+ var canvas = document.createElement('canvas'); |
+ canvas.width = width; |
+ canvas.height = height; |
+ var gl = wtu.create3DContext(canvas); |
+ if (!gl) { |
+ testFailed("context does not exist"); |
+ finishTest(); |
+ return; |
+ } |
+ |
+ var canvas2d = document.createElement('canvas'); |
+ canvas2d.width = width; |
+ canvas2d.height = height; |
+ var ctx = canvas2d.getContext("2d"); |
+ var imgData = ctx.getImageData(0, 0, width, height); |
+ |
+ var shaderInfos = [ |
+ { type: "vertex", |
+ input: "color", |
+ output: "vColor", |
+ vertexShaderTemplate: vertexShaderTemplate, |
+ fragmentShaderTemplate: baseFragmentShader |
+ }, |
+ { type: "fragment", |
+ input: "vColor", |
+ output: "gl_FragColor", |
+ vertexShaderTemplate: baseVertexShader, |
+ fragmentShaderTemplate: fragmentShaderTemplate |
+ } |
+ ]; |
+ for (var ss = 0; ss < shaderInfos.length; ++ss) { |
+ var shaderInfo = shaderInfos[ss]; |
+ var tests = params.tests; |
+ var testTypes = params.emuFuncs || (params.bvecTest ? bvecTypes : types); |
+ // Test vertex shaders |
+ for (var ii = 0; ii < tests.length; ++ii) { |
+ var type = testTypes[ii]; |
+ if (params.simpleEmu) { |
+ type = { |
+ type: type.type, |
+ code: params.simpleEmu |
+ }; |
+ } |
+ debug(""); |
+ var str = replaceParams(params.testFunc, { |
+ func: params.feature, |
+ type: type.type, |
+ arg0: type.type |
+ }); |
+ debug("Testing: " + str + " in " + shaderInfo.type + " shader"); |
+ |
+ var referenceVertexShaderSource = generateReferenceShader( |
+ shaderInfo, |
+ shaderInfo.vertexShaderTemplate, |
+ params, |
+ type, |
+ tests[ii]); |
+ var referenceFragmentShaderSource = generateReferenceShader( |
+ shaderInfo, |
+ shaderInfo.fragmentShaderTemplate, |
+ params, |
+ type, |
+ tests[ii]); |
+ var testVertexShaderSource = generateTestShader( |
+ shaderInfo, |
+ shaderInfo.vertexShaderTemplate, |
+ params, |
+ tests[ii]); |
+ var testFragmentShaderSource = generateTestShader( |
+ shaderInfo, |
+ shaderInfo.fragmentShaderTemplate, |
+ params, |
+ tests[ii]); |
+ |
+ debug(""); |
+ addShaderSource( |
+ "reference vertex shader", referenceVertexShaderSource); |
+ addShaderSource( |
+ "reference fragment shader", referenceFragmentShaderSource); |
+ addShaderSource( |
+ "test vertex shader", testVertexShaderSource); |
+ addShaderSource( |
+ "test fragment shader", testFragmentShaderSource); |
+ debug(""); |
+ |
+ var refData = draw( |
+ canvas, referenceVertexShaderSource, referenceFragmentShaderSource); |
+ var refImg = makeImage(canvas); |
+ if (ss == 0) { |
+ var testData = draw( |
+ canvas, testVertexShaderSource, referenceFragmentShaderSource); |
+ } else { |
+ var testData = draw( |
+ canvas, referenceVertexShaderSource, testFragmentShaderSource); |
+ } |
+ var testImg = makeImage(canvas); |
+ |
+ reportResults(refData, refImg, testData, testImg); |
+ } |
+ } |
+ |
+ finishTest(); |
+ |
+ function addShaderSource(label, source) { |
+ var div = document.createElement("div"); |
+ var s = document.createElement("pre"); |
+ s.className = "shader-source"; |
+ s.style.display = "none"; |
+ var ol = document.createElement("ol"); |
+ //s.appendChild(document.createTextNode(source)); |
+ var lines = source.split("\n"); |
+ for (var ii = 0; ii < lines.length; ++ii) { |
+ var line = lines[ii]; |
+ var li = document.createElement("li"); |
+ li.appendChild(document.createTextNode(line)); |
+ ol.appendChild(li); |
+ } |
+ s.appendChild(ol); |
+ var l = document.createElement("a"); |
+ l.href = "show-shader-source"; |
+ l.appendChild(document.createTextNode(label)); |
+ l.addEventListener('click', function(event) { |
+ if (event.preventDefault) { |
+ event.preventDefault(); |
+ } |
+ s.style.display = (s.style.display == 'none') ? 'block' : 'none'; |
+ return false; |
+ }, false); |
+ div.appendChild(l); |
+ div.appendChild(s); |
+ console.appendChild(div); |
+ } |
+ |
+ function reportResults(refData, refImage, testData, testImage) { |
+ var same = true; |
+ for (var yy = 0; yy < height; ++yy) { |
+ for (var xx = 0; xx < width; ++xx) { |
+ var offset = (yy * width + xx) * 4; |
+ var imgOffset = ((height - yy - 1) * width + xx) * 4; |
+ imgData.data[imgOffset + 0] = 0; |
+ imgData.data[imgOffset + 1] = 0; |
+ imgData.data[imgOffset + 2] = 0; |
+ imgData.data[imgOffset + 3] = 255; |
+ if (Math.abs(refData[offset + 0] - testData[offset + 0]) > tolerance || |
+ Math.abs(refData[offset + 1] - testData[offset + 1]) > tolerance || |
+ Math.abs(refData[offset + 2] - testData[offset + 2]) > tolerance || |
+ Math.abs(refData[offset + 3] - testData[offset + 3]) > tolerance) { |
+ imgData.data[imgOffset] = 255; |
+ same = false; |
+ } |
+ } |
+ } |
+ |
+ var diffImg = null; |
+ if (!same) { |
+ ctx.putImageData(imgData, 0, 0); |
+ diffImg = makeImage(canvas2d); |
+ } |
+ |
+ var div = document.createElement("div"); |
+ div.className = "testimages"; |
+ insertImg(div, "ref", refImg); |
+ insertImg(div, "test", testImg); |
+ if (diffImg) { |
+ insertImg(div, "diff", diffImg); |
+ } |
+ div.appendChild(document.createElement('br')); |
+ |
+ function insertImg(element, caption, img) { |
+ var div = document.createElement("div"); |
+ div.appendChild(img); |
+ var label = document.createElement("div"); |
+ label.appendChild(document.createTextNode(caption)); |
+ div.appendChild(label); |
+ element.appendChild(div); |
+ } |
+ |
+ console.appendChild(div); |
+ |
+ if (!same) { |
+ testFailed("images are different"); |
+ } else { |
+ testPassed("images are the same"); |
+ } |
+ |
+ console.appendChild(document.createElement('hr')); |
+ } |
+ |
+ function draw(canvas, vsSource, fsSource) { |
+ var program = wtu.loadProgram(gl, vsSource, fsSource, testFailed); |
+ |
+ var posLoc = gl.getAttribLocation(program, "aPosition"); |
+ WebGLTestUtils.setupQuad(gl, gridRes, posLoc); |
+ |
+ gl.useProgram(program); |
+ gl.clearColor(0, 0, 1, 1); |
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); |
+ gl.drawElements(gl.TRIANGLES, gridRes * gridRes * 6, gl.UNSIGNED_SHORT, 0); |
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no errors from draw"); |
+ |
+ var img = new Uint8Array(width * height * 4); |
+ gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, img); |
+ return img; |
+ } |
+ |
+}; |
+ |
+return { |
+ /** |
+ * runs a bunch of GLSL tests using the passed in parameters |
+ * The parameters are: |
+ * |
+ * feature: |
+ * the name of the function being tested (eg, sin, dot, |
+ * normalize) |
+ * |
+ * testFunc: |
+ * The prototype of function to be tested not including the |
+ * return type. |
+ * |
+ * emuFunc: |
+ * A base function that can be used to generate emulation |
+ * functions. Example for 'ceil' |
+ * |
+ * float $(func)_base(float value) { |
+ * float m = mod(value, 1.0); |
+ * return m != 0.0 ? (value + 1.0 - m) : value; |
+ * } |
+ * |
+ * args: |
+ * The arguments to the function |
+ * |
+ * baseArgs: (optional) |
+ * The arguments when a base function is used to create an |
+ * emulation function. For example 'float sign_base(float v)' |
+ * is used to implemenent vec2 sign_emu(vec2 v). |
+ * |
+ * simpleEmu: |
+ * if supplied, the code that can be used to generate all |
+ * functions for all types. |
+ * |
+ * Example for 'normalize': |
+ * |
+ * $(type) $(func)_emu($(args)) { |
+ * return value / length(value); |
+ * } |
+ * |
+ * gridRes: (optional) |
+ * The resolution of the mesh to generate. The default is a |
+ * 1x1 grid but many vertex shaders need a higher resolution |
+ * otherwise the only values passed in are the 4 corners |
+ * which often have the same value. |
+ * |
+ * tests: |
+ * The code for each test. It is assumed the tests are for |
+ * float, vec2, vec3, vec4 in that order. |
+ */ |
+ runFeatureTest: runFeatureTest, |
+ |
+ none: false |
+}; |
+ |
+}()); |
+ |
Property changes on: conformance/resources/glsl-generator.js |
___________________________________________________________________ |
Added: svn:executable |
+ * |
Added: svn:eol-style |
+ LF |