2 ** Copyright (c) 2012 The Khronos Group Inc.
4 ** Permission is hereby granted, free of charge, to any person obtaining a
5 ** copy of this software and/or associated documentation files (the
6 ** "Materials"), to deal in the Materials without restriction, including
7 ** without limitation the rights to use, copy, modify, merge, publish,
8 ** distribute, sublicense, and/or sell copies of the Materials, and to
9 ** permit persons to whom the Materials are furnished to do so, subject to
10 ** the following conditions:
12 ** The above copyright notice and this permission notice shall be included
13 ** in all copies or substantial portions of the Materials.
15 ** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 ** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
23 GLSLGenerator = (function() {
25 var vertexShaderTemplate = [
26 "attribute vec4 aPosition;",
28 "varying vec4 vColor;",
35 " gl_Position = aPosition;",
36 " vec2 texcoord = vec2(aPosition.xy * 0.5 + vec2(0.5, 0.5));",
37 " vec4 color = vec4(",
39 " texcoord.x * texcoord.y,",
40 " (1.0 - texcoord.x) * texcoord.y * 0.5 + 0.5);",
45 var fragmentShaderTemplate = [
47 "precision mediump float;",
50 "varying vec4 vColor;",
61 var baseVertexShader = [
62 "attribute vec4 aPosition;",
64 "varying vec4 vColor;",
68 " gl_Position = aPosition;",
69 " vec2 texcoord = vec2(aPosition.xy * 0.5 + vec2(0.5, 0.5));",
72 " texcoord.x * texcoord.y,",
73 " (1.0 - texcoord.x) * texcoord.y * 0.5 + 0.5);",
77 var baseVertexShaderWithColor = [
78 "attribute vec4 aPosition;",
79 "attribute vec4 aColor;",
81 "varying vec4 vColor;",
85 " gl_Position = aPosition;",
90 var baseFragmentShader = [
92 "precision mediump float;",
94 "varying vec4 vColor;",
98 " gl_FragColor = vColor;",
105 "float $(func)_emu($(args)) {",
106 " return $(func)_base($(baseArgs));",
111 "vec2 $(func)_emu($(args)) {",
113 " $(func)_base($(baseArgsX)),",
114 " $(func)_base($(baseArgsY)));",
119 "vec3 $(func)_emu($(args)) {",
121 " $(func)_base($(baseArgsX)),",
122 " $(func)_base($(baseArgsY)),",
123 " $(func)_base($(baseArgsZ)));",
128 "vec4 $(func)_emu($(args)) {",
130 " $(func)_base($(baseArgsX)),",
131 " $(func)_base($(baseArgsY)),",
132 " $(func)_base($(baseArgsZ)),",
133 " $(func)_base($(baseArgsW)));",
141 "bvec2 $(func)_emu($(args)) {",
143 " $(func)_base($(baseArgsX)),",
144 " $(func)_base($(baseArgsY)));",
149 "bvec3 $(func)_emu($(args)) {",
151 " $(func)_base($(baseArgsX)),",
152 " $(func)_base($(baseArgsY)),",
153 " $(func)_base($(baseArgsZ)));",
158 "vec4 $(func)_emu($(args)) {",
160 " $(func)_base($(baseArgsX)),",
161 " $(func)_base($(baseArgsY)),",
162 " $(func)_base($(baseArgsZ)),",
163 " $(func)_base($(baseArgsW)));",
168 var replaceRE = /\$\((\w+)\)/g;
170 var replaceParams = function(str) {
171 var args = arguments;
172 return str.replace(replaceRE, function(str, p1, offset, s) {
173 for (var ii = 1; ii < args.length; ++ii) {
174 if (args[ii][p1] !== undefined) {
178 throw "unknown string param '" + p1 + "'";
182 var generateReferenceShader = function(
183 shaderInfo, template, params, typeInfo, test) {
184 var input = shaderInfo.input;
185 var output = shaderInfo.output;
186 var feature = params.feature;
187 var testFunc = params.testFunc;
188 var emuFunc = params.emuFunc || "";
189 var extra = params.extra || '';
190 var args = params.args || "$(type) value";
191 var type = typeInfo.type;
192 var typeCode = typeInfo.code;
194 var baseArgs = params.baseArgs || "value$(field)";
195 var baseArgsX = replaceParams(baseArgs, {field: ".x"});
196 var baseArgsY = replaceParams(baseArgs, {field: ".y"});
197 var baseArgsZ = replaceParams(baseArgs, {field: ".z"});
198 var baseArgsW = replaceParams(baseArgs, {field: ".w"});
199 var baseArgs = replaceParams(baseArgs, {field: ""});
201 test = replaceParams(test, {
204 func: feature + "_emu"
206 emuFunc = replaceParams(emuFunc, {
209 args = replaceParams(args, {
212 typeCode = replaceParams(typeCode, {
217 baseArgsX: baseArgsX,
218 baseArgsY: baseArgsY,
219 baseArgsZ: baseArgsZ,
222 var shader = replaceParams(template, {
224 emu: emuFunc + "\n\n" + typeCode,
230 var generateTestShader = function(
231 shaderInfo, template, params, test) {
232 var input = shaderInfo.input;
233 var output = shaderInfo.output;
234 var feature = params.feature;
235 var testFunc = params.testFunc;
236 var extra = params.extra || '';
238 test = replaceParams(test, {
243 var shader = replaceParams(template, {
251 function _reportResults(refData, refImg, testData, testImg, tolerance,
252 width, height, ctx, imgData, wtu, canvas2d, consoleDiv) {
254 var firstFailure = null;
255 for (var yy = 0; yy < height; ++yy) {
256 for (var xx = 0; xx < width; ++xx) {
257 var offset = (yy * width + xx) * 4;
258 var imgOffset = ((height - yy - 1) * width + xx) * 4;
259 imgData.data[imgOffset + 0] = 0;
260 imgData.data[imgOffset + 1] = 0;
261 imgData.data[imgOffset + 2] = 0;
262 imgData.data[imgOffset + 3] = 255;
263 if (Math.abs(refData[offset + 0] - testData[offset + 0]) > tolerance ||
264 Math.abs(refData[offset + 1] - testData[offset + 1]) > tolerance ||
265 Math.abs(refData[offset + 2] - testData[offset + 2]) > tolerance ||
266 Math.abs(refData[offset + 3] - testData[offset + 3]) > tolerance) {
267 var detail = 'at (' + xx + ',' + yy + '): ref=(' +
268 refData[offset + 0] + ',' +
269 refData[offset + 1] + ',' +
270 refData[offset + 2] + ',' +
271 refData[offset + 3] + ') test=(' +
272 testData[offset + 0] + ',' +
273 testData[offset + 1] + ',' +
274 testData[offset + 2] + ',' +
275 testData[offset + 3] + ') tolerance=' + tolerance;
276 consoleDiv.appendChild(document.createTextNode(detail));
277 consoleDiv.appendChild(document.createElement('br'));
279 firstFailure = ": " + detail;
281 imgData.data[imgOffset] = 255;
289 ctx.putImageData(imgData, 0, 0);
290 diffImg = wtu.makeImageFromCanvas(canvas2d);
293 var div = document.createElement("div");
294 div.className = "testimages";
295 wtu.insertImage(div, "ref", refImg);
296 wtu.insertImage(div, "test", testImg);
298 wtu.insertImage(div, "diff", diffImg);
300 div.appendChild(document.createElement('br'));
302 consoleDiv.appendChild(div);
305 testFailed("images are different" + (firstFailure ? firstFailure : ""));
307 testPassed("images are the same");
310 consoleDiv.appendChild(document.createElement('hr'));
313 var runFeatureTest = function(params) {
314 var wtu = WebGLTestUtils;
315 var gridRes = params.gridRes;
316 var vertexTolerance = params.tolerance || 0;
317 var fragmentTolerance = vertexTolerance;
318 if ('fragmentTolerance' in params)
319 fragmentTolerance = params.fragmentTolerance || 0;
321 description("Testing GLSL feature: " + params.feature);
326 var consoleDiv = document.getElementById("console");
327 var canvas = document.createElement('canvas');
328 canvas.width = width;
329 canvas.height = height;
330 var gl = wtu.create3DContext(canvas, { premultipliedAlpha: false });
332 testFailed("context does not exist");
337 var canvas2d = document.createElement('canvas');
338 canvas2d.width = width;
339 canvas2d.height = height;
340 var ctx = canvas2d.getContext("2d");
341 var imgData = ctx.getImageData(0, 0, width, height);
347 vertexShaderTemplate: vertexShaderTemplate,
348 fragmentShaderTemplate: baseFragmentShader,
349 tolerance: vertexTolerance
353 output: "gl_FragColor",
354 vertexShaderTemplate: baseVertexShader,
355 fragmentShaderTemplate: fragmentShaderTemplate,
356 tolerance: fragmentTolerance
359 for (var ss = 0; ss < shaderInfos.length; ++ss) {
360 var shaderInfo = shaderInfos[ss];
361 var tests = params.tests;
362 var testTypes = params.emuFuncs || (params.bvecTest ? bvecTypes : types);
363 // Test vertex shaders
364 for (var ii = 0; ii < tests.length; ++ii) {
365 var type = testTypes[ii];
366 if (params.simpleEmu) {
369 code: params.simpleEmu
373 var str = replaceParams(params.testFunc, {
374 func: params.feature,
378 debug("Testing: " + str + " in " + shaderInfo.type + " shader");
380 var referenceVertexShaderSource = generateReferenceShader(
382 shaderInfo.vertexShaderTemplate,
386 var referenceFragmentShaderSource = generateReferenceShader(
388 shaderInfo.fragmentShaderTemplate,
392 var testVertexShaderSource = generateTestShader(
394 shaderInfo.vertexShaderTemplate,
397 var testFragmentShaderSource = generateTestShader(
399 shaderInfo.fragmentShaderTemplate,
405 var referenceVertexShader = wtu.loadShader(gl, referenceVertexShaderSource, gl.VERTEX_SHADER, testFailed, true, 'reference');
406 var referenceFragmentShader = wtu.loadShader(gl, referenceFragmentShaderSource, gl.FRAGMENT_SHADER, testFailed, true, 'reference');
407 var testVertexShader = wtu.loadShader(gl, testVertexShaderSource, gl.VERTEX_SHADER, testFailed, true, 'test');
408 var testFragmentShader = wtu.loadShader(gl, testFragmentShaderSource, gl.FRAGMENT_SHADER, testFailed, true, 'test');
412 referenceVertexShader, referenceFragmentShader);
413 var refImg = wtu.makeImageFromCanvas(canvas);
416 testVertexShader, referenceFragmentShader);
419 referenceVertexShader, testFragmentShader);
421 var testImg = wtu.makeImageFromCanvas(canvas);
423 _reportResults(refData, refImg, testData, testImg, shaderInfo.tolerance,
424 width, height, ctx, imgData, wtu, canvas2d, consoleDiv);
430 function draw(vertexShader, fragmentShader) {
431 var program = wtu.createProgram(gl, vertexShader, fragmentShader, testFailed);
433 var posLoc = gl.getAttribLocation(program, "aPosition");
434 wtu.setupIndexedQuad(gl, gridRes, posLoc);
436 gl.useProgram(program);
437 wtu.clearAndDrawIndexedQuad(gl, gridRes, [0, 0, 255, 255]);
438 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no errors from draw");
440 var img = new Uint8Array(width * height * 4);
441 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, img);
447 var runBasicTest = function(params) {
448 var wtu = WebGLTestUtils;
449 var gridRes = params.gridRes;
450 var vertexTolerance = params.tolerance || 0;
451 var fragmentTolerance = vertexTolerance;
452 if ('fragmentTolerance' in params)
453 fragmentTolerance = params.fragmentTolerance || 0;
455 description("Testing : " + document.getElementsByTagName("title")[0].innerText);
460 var consoleDiv = document.getElementById("console");
461 var canvas = document.createElement('canvas');
462 canvas.width = width;
463 canvas.height = height;
464 var gl = wtu.create3DContext(canvas);
466 testFailed("context does not exist");
471 var canvas2d = document.createElement('canvas');
472 canvas2d.width = width;
473 canvas2d.height = height;
474 var ctx = canvas2d.getContext("2d");
475 var imgData = ctx.getImageData(0, 0, width, height);
481 vertexShaderTemplate: vertexShaderTemplate,
482 fragmentShaderTemplate: baseFragmentShader,
483 tolerance: vertexTolerance
487 output: "gl_FragColor",
488 vertexShaderTemplate: baseVertexShader,
489 fragmentShaderTemplate: fragmentShaderTemplate,
490 tolerance: fragmentTolerance
493 for (var ss = 0; ss < shaderInfos.length; ++ss) {
494 var shaderInfo = shaderInfos[ss];
495 var tests = params.tests;
496 // var testTypes = params.emuFuncs || (params.bvecTest ? bvecTypes : types);
497 // Test vertex shaders
498 for (var ii = 0; ii < tests.length; ++ii) {
499 var test = tests[ii];
501 debug("Testing: " + test.name + " in " + shaderInfo.type + " shader");
503 function genShader(shaderInfo, template, shader, subs) {
504 shader = replaceParams(shader, subs, {
505 input: shaderInfo.input,
506 output: shaderInfo.output
508 shader = replaceParams(template, subs, {
516 var referenceVertexShaderSource = genShader(
518 shaderInfo.vertexShaderTemplate,
519 test.reference.shader,
520 test.reference.subs);
521 var referenceFragmentShaderSource = genShader(
523 shaderInfo.fragmentShaderTemplate,
524 test.reference.shader,
525 test.reference.subs);
526 var testVertexShaderSource = genShader(
528 shaderInfo.vertexShaderTemplate,
531 var testFragmentShaderSource = genShader(
533 shaderInfo.fragmentShaderTemplate,
538 var referenceVertexShader = wtu.loadShader(gl, referenceVertexShaderSource, gl.VERTEX_SHADER, testFailed, true, 'reference');
539 var referenceFragmentShader = wtu.loadShader(gl, referenceFragmentShaderSource, gl.FRAGMENT_SHADER, testFailed, true, 'reference');
540 var testVertexShader = wtu.loadShader(gl, testVertexShaderSource, gl.VERTEX_SHADER, testFailed, true, 'test');
541 var testFragmentShader = wtu.loadShader(gl, testFragmentShaderSource, gl.FRAGMENT_SHADER, testFailed, true, 'test');
544 var refData = draw(referenceVertexShader, referenceFragmentShader);
545 var refImg = wtu.makeImageFromCanvas(canvas);
547 var testData = draw(testVertexShader, referenceFragmentShader);
549 var testData = draw(referenceVertexShader, testFragmentShader);
551 var testImg = wtu.makeImageFromCanvas(canvas);
553 _reportResults(refData, refImg, testData, testImg, shaderInfo.tolerance,
554 width, height, ctx, imgData, wtu, canvas2d, consoleDiv);
560 function draw(vertexShader, fragmentShader) {
561 var program = wtu.createProgram(gl, vertexShader, fragmentShader, testFailed);
563 var posLoc = gl.getAttribLocation(program, "aPosition");
564 wtu.setupIndexedQuad(gl, gridRes, posLoc);
566 gl.useProgram(program);
567 wtu.clearAndDrawIndexedQuad(gl, gridRes, [0, 0, 255, 255]);
568 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no errors from draw");
570 var img = new Uint8Array(width * height * 4);
571 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, img);
577 var runReferenceImageTest = function(params) {
578 var wtu = WebGLTestUtils;
579 var gridRes = params.gridRes;
580 var vertexTolerance = params.tolerance || 0;
581 var fragmentTolerance = vertexTolerance;
582 if ('fragmentTolerance' in params)
583 fragmentTolerance = params.fragmentTolerance || 0;
585 description("Testing GLSL feature: " + params.feature);
590 var consoleDiv = document.getElementById("console");
591 var canvas = document.createElement('canvas');
592 canvas.width = width;
593 canvas.height = height;
594 var gl = wtu.create3DContext(canvas, { antialias: false, premultipliedAlpha: false });
596 testFailed("context does not exist");
601 var canvas2d = document.createElement('canvas');
602 canvas2d.width = width;
603 canvas2d.height = height;
604 var ctx = canvas2d.getContext("2d");
605 var imgData = ctx.getImageData(0, 0, width, height);
607 // State for reference images for vertex shader tests.
608 // These are drawn with the same tessellated grid as the test vertex
609 // shader so that the interpolation is identical. The grid is reused
610 // from test to test; the colors are changed.
612 var indexedQuadForReferenceVertexShader =
613 wtu.setupIndexedQuad(gl, gridRes, 0);
614 var referenceVertexShaderProgram =
615 wtu.setupProgram(gl, [ baseVertexShaderWithColor, baseFragmentShader ],
616 ["aPosition", "aColor"]);
617 var referenceVertexShaderColorBuffer = gl.createBuffer();
623 vertexShaderTemplate: vertexShaderTemplate,
624 fragmentShaderTemplate: baseFragmentShader,
625 tolerance: vertexTolerance
629 output: "gl_FragColor",
630 vertexShaderTemplate: baseVertexShader,
631 fragmentShaderTemplate: fragmentShaderTemplate,
632 tolerance: fragmentTolerance
635 for (var ss = 0; ss < shaderInfos.length; ++ss) {
636 var shaderInfo = shaderInfos[ss];
637 var tests = params.tests;
638 var testTypes = params.emuFuncs || (params.bvecTest ? bvecTypes : types);
639 // Test vertex shaders
640 for (var ii = 0; ii < tests.length; ++ii) {
641 var type = testTypes[ii];
642 var isVertex = (ss == 0);
644 var str = replaceParams(params.testFunc, {
645 func: params.feature,
649 debug("Testing: " + str + " in " + shaderInfo.type + " shader");
651 var referenceVertexShaderSource = generateReferenceShader(
653 shaderInfo.vertexShaderTemplate,
657 var referenceFragmentShaderSource = generateReferenceShader(
659 shaderInfo.fragmentShaderTemplate,
663 var testVertexShaderSource = generateTestShader(
665 shaderInfo.vertexShaderTemplate,
668 var testFragmentShaderSource = generateTestShader(
670 shaderInfo.fragmentShaderTemplate,
673 var referenceTextureOrArray = generateReferenceImage(
676 isVertex ? gridRes : width,
677 isVertex ? gridRes : height,
681 var testVertexShader = wtu.loadShader(gl, testVertexShaderSource, gl.VERTEX_SHADER, testFailed, true);
682 var testFragmentShader = wtu.loadShader(gl, testFragmentShaderSource, gl.FRAGMENT_SHADER, testFailed, true);
687 refData = drawVertexReferenceImage(referenceTextureOrArray);
689 refData = drawFragmentReferenceImage(referenceTextureOrArray);
691 var refImg = wtu.makeImageFromCanvas(canvas);
694 var referenceFragmentShader = wtu.loadShader(gl, referenceFragmentShaderSource, gl.FRAGMENT_SHADER, testFailed);
696 testVertexShader, referenceFragmentShader);
698 var referenceVertexShader = wtu.loadShader(gl, referenceVertexShaderSource, gl.VERTEX_SHADER, testFailed);
700 referenceVertexShader, testFragmentShader);
702 var testImg = wtu.makeImageFromCanvas(canvas);
703 var testTolerance = shaderInfo.tolerance;
704 // Provide per-test tolerance so that we can increase it only for those desired.
705 if ('tolerance' in tests[ii])
706 testTolerance = tests[ii].tolerance || 0;
707 _reportResults(refData, refImg, testData, testImg, testTolerance,
708 width, height, ctx, imgData, wtu, canvas2d, consoleDiv);
714 function draw(vertexShader, fragmentShader) {
715 var program = wtu.createProgram(gl, vertexShader, fragmentShader, testFailed);
717 var posLoc = gl.getAttribLocation(program, "aPosition");
718 wtu.setupIndexedQuad(gl, gridRes, posLoc);
720 gl.useProgram(program);
721 wtu.clearAndDrawIndexedQuad(gl, gridRes, [0, 0, 255, 255]);
722 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no errors from draw");
724 var img = new Uint8Array(width * height * 4);
725 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, img);
729 function drawVertexReferenceImage(colors) {
730 gl.bindBuffer(gl.ARRAY_BUFFER, indexedQuadForReferenceVertexShader[0]);
731 gl.enableVertexAttribArray(0);
732 gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);
733 gl.bindBuffer(gl.ARRAY_BUFFER, referenceVertexShaderColorBuffer);
734 gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW);
735 gl.enableVertexAttribArray(1);
736 gl.vertexAttribPointer(1, 4, gl.UNSIGNED_BYTE, true, 0, 0);
737 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexedQuadForReferenceVertexShader[1]);
738 gl.useProgram(referenceVertexShaderProgram);
739 wtu.clearAndDrawIndexedQuad(gl, gridRes);
740 gl.disableVertexAttribArray(0);
741 gl.disableVertexAttribArray(1);
742 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no errors from draw");
744 var img = new Uint8Array(width * height * 4);
745 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, img);
749 function drawFragmentReferenceImage(texture) {
750 var program = wtu.setupTexturedQuad(gl);
752 gl.activeTexture(gl.TEXTURE0);
753 gl.bindTexture(gl.TEXTURE_2D, texture);
754 var texLoc = gl.getUniformLocation(program, "tex");
755 gl.uniform1i(texLoc, 0);
756 wtu.clearAndDrawUnitQuad(gl);
757 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no errors from draw");
759 var img = new Uint8Array(width * height * 4);
760 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, img);
765 * Creates and returns either a Uint8Array (for vertex shaders) or
766 * WebGLTexture (for fragment shaders) containing the reference
767 * image for the function being tested. Exactly how the function is
768 * evaluated, and the size of the returned texture or array, depends on
769 * whether we are testing a vertex or fragment shader. If a fragment
770 * shader, the function is evaluated at the pixel centers. If a
771 * vertex shader, the function is evaluated at the triangle's
774 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use to generate texture objects.
775 * @param {!function(number,number,number,number): !Array.<number>} generator The reference image generator function.
776 * @param {number} width The width of the texture to generate if testing a fragment shader; the grid resolution if testing a vertex shader.
777 * @param {number} height The height of the texture to generate if testing a fragment shader; the grid resolution if testing a vertex shader.
778 * @param {boolean} isVertex True if generating a reference image for a vertex shader; false if for a fragment shader.
779 * @return {!WebGLTexture|!Uint8Array} The texture object or array that was generated.
781 function generateReferenceImage(
788 // Note: the math in this function must match that in the vertex and
789 // fragment shader templates above.
790 function computeTexCoord(x) {
791 return x * 0.5 + 0.5;
794 function computeVertexColor(texCoordX, texCoordY) {
797 texCoordX * texCoordY,
798 (1.0 - texCoordX) * texCoordY * 0.5 + 0.5 ];
802 * Computes fragment color according to the algorithm used for interpolation
803 * in OpenGL (GLES 2.0 spec 3.5.1, OpenGL 4.3 spec 14.6.1).
805 function computeInterpolatedColor(texCoordX, texCoordY) {
806 // Calculate grid line indexes below and to the left from texCoord.
807 var gridBottom = Math.floor(texCoordY * gridRes);
808 if (gridBottom == gridRes) {
811 var gridLeft = Math.floor(texCoordX * gridRes);
812 if (gridLeft == gridRes) {
816 // Calculate coordinates relative to the grid cell.
817 var cellX = texCoordX * gridRes - gridLeft;
818 var cellY = texCoordY * gridRes - gridBottom;
820 // Barycentric coordinates inside either triangle ACD or ABC
821 // are used as weights for the vertex colors in the corners:
827 var aColor = computeVertexColor(gridLeft / gridRes, (gridBottom + 1) / gridRes);
828 var bColor = computeVertexColor((gridLeft + 1) / gridRes, (gridBottom + 1) / gridRes);
829 var cColor = computeVertexColor((gridLeft + 1) / gridRes, gridBottom / gridRes);
830 var dColor = computeVertexColor(gridLeft / gridRes, gridBottom / gridRes);
832 // Calculate weights.
835 if (cellX + cellY < 1) {
836 // In bottom triangle ACD.
837 a = cellY; // area of triangle C-D-(cellX, cellY) relative to ACD
838 c = cellX; // area of triangle D-A-(cellX, cellY) relative to ACD
842 // In top triangle ABC.
843 a = 1 - cellX; // area of the triangle B-C-(cellX, cellY) relative to ABC
844 c = 1 - cellY; // area of the triangle A-B-(cellX, cellY) relative to ABC
849 var interpolated = [];
850 for (var ii = 0; ii < aColor.length; ++ii) {
851 interpolated.push(a * aColor[ii] + b * bColor[ii] + c * cColor[ii] + d * dColor[ii]);
856 function clamp(value, minVal, maxVal) {
857 return Math.max(minVal, Math.min(value, maxVal));
860 // Evaluates the function at clip coordinates (px,py), storing the
861 // result in the array "pixel". Each channel's result is clamped
862 // between 0 and 255.
863 function evaluateAtClipCoords(px, py, pixel, colorFunc) {
864 var tcx = computeTexCoord(px);
865 var tcy = computeTexCoord(py);
867 var color = colorFunc(tcx, tcy);
869 var output = generator(color[0], color[1], color[2], color[3]);
871 // Multiply by 256 to get even distribution for all values between 0 and 1.
872 // Use rounding rather than truncation to more closely match the GPU's behavior.
873 pixel[0] = clamp(Math.round(256 * output[0]), 0, 255);
874 pixel[1] = clamp(Math.round(256 * output[1]), 0, 255);
875 pixel[2] = clamp(Math.round(256 * output[2]), 0, 255);
876 pixel[3] = clamp(Math.round(256 * output[3]), 0, 255);
879 function generateFragmentReference() {
880 var data = new Uint8Array(4 * width * height);
882 var horizTexel = 1.0 / width;
883 var vertTexel = 1.0 / height;
884 var halfHorizTexel = 0.5 * horizTexel;
885 var halfVertTexel = 0.5 * vertTexel;
887 var pixel = new Array(4);
889 for (var yi = 0; yi < height; ++yi) {
890 for (var xi = 0; xi < width; ++xi) {
891 // The function must be evaluated at pixel centers.
893 // Compute desired position in clip space
894 var px = -1.0 + 2.0 * (halfHorizTexel + xi * horizTexel);
895 var py = -1.0 + 2.0 * (halfVertTexel + yi * vertTexel);
897 evaluateAtClipCoords(px, py, pixel, computeInterpolatedColor);
898 var index = 4 * (width * yi + xi);
899 data[index + 0] = pixel[0];
900 data[index + 1] = pixel[1];
901 data[index + 2] = pixel[2];
902 data[index + 3] = pixel[3];
906 var texture = gl.createTexture();
907 gl.bindTexture(gl.TEXTURE_2D, texture);
908 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
909 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
910 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
911 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
912 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0,
913 gl.RGBA, gl.UNSIGNED_BYTE, data);
917 function generateVertexReference() {
918 // We generate a Uint8Array which contains the evaluation of the
919 // function at the vertices of the triangle mesh. It is expected
920 // that the width and the height are identical, and equivalent
921 // to the grid resolution.
922 if (width != height) {
923 throw "width and height must be equal";
926 var texSize = 1 + width;
927 var data = new Uint8Array(4 * texSize * texSize);
929 var step = 2.0 / width;
931 var pixel = new Array(4);
933 for (var yi = 0; yi < texSize; ++yi) {
934 for (var xi = 0; xi < texSize; ++xi) {
935 // The function is evaluated at the triangles' vertices.
937 // Compute desired position in clip space
938 var px = -1.0 + (xi * step);
939 var py = -1.0 + (yi * step);
941 evaluateAtClipCoords(px, py, pixel, computeVertexColor);
942 var index = 4 * (texSize * yi + xi);
943 data[index + 0] = pixel[0];
944 data[index + 1] = pixel[1];
945 data[index + 2] = pixel[2];
946 data[index + 3] = pixel[3];
953 //----------------------------------------------------------------------
954 // Body of generateReferenceImage
958 return generateVertexReference();
960 return generateFragmentReference();
967 * runs a bunch of GLSL tests using the passed in parameters
968 * The parameters are:
971 * the name of the function being tested (eg, sin, dot,
975 * The prototype of function to be tested not including the
979 * A base function that can be used to generate emulation
980 * functions. Example for 'ceil'
982 * float $(func)_base(float value) {
983 * float m = mod(value, 1.0);
984 * return m != 0.0 ? (value + 1.0 - m) : value;
988 * The arguments to the function
990 * baseArgs: (optional)
991 * The arguments when a base function is used to create an
992 * emulation function. For example 'float sign_base(float v)'
993 * is used to implemenent vec2 sign_emu(vec2 v).
996 * if supplied, the code that can be used to generate all
997 * functions for all types.
999 * Example for 'normalize':
1001 * $(type) $(func)_emu($(args)) {
1002 * return value / length(value);
1005 * gridRes: (optional)
1006 * The resolution of the mesh to generate. The default is a
1007 * 1x1 grid but many vertex shaders need a higher resolution
1008 * otherwise the only values passed in are the 4 corners
1009 * which often have the same value.
1012 * The code for each test. It is assumed the tests are for
1013 * float, vec2, vec3, vec4 in that order.
1015 * tolerance: (optional)
1016 * Allow some tolerance in the comparisons. The tolerance is applied to
1017 * both vertex and fragment shaders. The default tolerance is 0, meaning
1018 * the values have to be identical.
1020 * fragmentTolerance: (optional)
1021 * Specify a tolerance which only applies to fragment shaders. The
1022 * fragment-only tolerance will override the shared tolerance for
1023 * fragment shaders if both are specified. Fragment shaders usually
1024 * use mediump float precision so they sometimes require higher tolerance
1025 * than vertex shaders which use highp by default.
1027 runFeatureTest: runFeatureTest,
1030 * Runs a bunch of GLSL tests using the passed in parameters
1032 * The parameters are:
1035 * Array of tests. For each test the following parameters are expected
1038 * some description of the test
1040 * parameters for the reference shader (see below)
1042 * parameters for the test shader (see below)
1044 * The parameter for the reference and test shaders are
1046 * shader: the GLSL for the shader
1047 * subs: any substitutions you wish to define for the shader.
1049 * Each shader is created from a basic template that
1050 * defines an input and an output. You can see the
1051 * templates at the top of this file. The input and output
1052 * change depending on whether or not we are generating
1053 * a vertex or fragment shader.
1055 * All this code function does is a bunch of string substitutions.
1056 * A substitution is defined by $(name). If name is found in
1057 * the 'subs' parameter it is replaced. 4 special names exist.
1059 * 'input' the input to your GLSL. Always a vec4. All change
1060 * from 0 to 1 over the quad to be drawn.
1062 * 'output' the output color. Also a vec4
1064 * 'emu' a place to insert extra stuff
1065 * 'extra' a place to insert extra stuff.
1067 * You can think of the templates like this
1073 * // do math to calculate input
1079 * Your shader first has any subs you provided applied as well
1080 * as 'input' and 'output'
1082 * It is then inserted into the template which is also provided
1085 * gridRes: (optional)
1086 * The resolution of the mesh to generate. The default is a
1087 * 1x1 grid but many vertex shaders need a higher resolution
1088 * otherwise the only values passed in are the 4 corners
1089 * which often have the same value.
1091 * tolerance: (optional)
1092 * Allow some tolerance in the comparisons. The tolerance is applied to
1093 * both vertex and fragment shaders. The default tolerance is 0, meaning
1094 * the values have to be identical.
1096 * fragmentTolerance: (optional)
1097 * Specify a tolerance which only applies to fragment shaders. The
1098 * fragment-only tolerance will override the shared tolerance for
1099 * fragment shaders if both are specified. Fragment shaders usually
1100 * use mediump float precision so they sometimes require higher tolerance
1101 * than vertex shaders which use highp.
1103 runBasicTest: runBasicTest,
1106 * Runs a bunch of GLSL tests using the passed in parameters. The
1107 * expected results are computed as a reference image in JavaScript
1108 * instead of on the GPU. The parameters are:
1111 * the name of the function being tested (eg, sin, dot,
1115 * The prototype of function to be tested not including the
1119 * The arguments to the function
1121 * gridRes: (optional)
1122 * The resolution of the mesh to generate. The default is a
1123 * 1x1 grid but many vertex shaders need a higher resolution
1124 * otherwise the only values passed in are the 4 corners
1125 * which often have the same value.
1128 * Array of tests. It is assumed the tests are for float, vec2,
1129 * vec3, vec4 in that order. For each test the following
1130 * parameters are expected:
1132 * source: the GLSL source code for the tests
1134 * generator: a JavaScript function taking four parameters
1135 * which evaluates the same function as the GLSL source,
1136 * returning its result as a newly allocated array.
1138 * tolerance: (optional) a per-test tolerance.
1141 * Extra GLSL code inserted at the top of each test's shader.
1143 * tolerance: (optional)
1144 * Allow some tolerance in the comparisons. The tolerance is applied to
1145 * both vertex and fragment shaders. The default tolerance is 0, meaning
1146 * the values have to be identical.
1148 * fragmentTolerance: (optional)
1149 * Specify a tolerance which only applies to fragment shaders. The
1150 * fragment-only tolerance will override the shared tolerance for
1151 * fragment shaders if both are specified. Fragment shaders usually
1152 * use mediump float precision so they sometimes require higher tolerance
1153 * than vertex shaders which use highp.
1155 runReferenceImageTest: runReferenceImageTest,