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 = [
46 "precision mediump float;",
48 "varying vec4 vColor;",
59 var baseVertexShader = [
60 "attribute vec4 aPosition;",
62 "varying vec4 vColor;",
66 " gl_Position = aPosition;",
67 " vec2 texcoord = vec2(aPosition.xy * 0.5 + vec2(0.5, 0.5));",
70 " texcoord.x * texcoord.y,",
71 " (1.0 - texcoord.x) * texcoord.y * 0.5 + 0.5);",
75 var baseVertexShaderWithColor = [
76 "attribute vec4 aPosition;",
77 "attribute vec4 aColor;",
79 "varying vec4 vColor;",
83 " gl_Position = aPosition;",
88 var baseFragmentShader = [
89 "precision mediump float;",
90 "varying vec4 vColor;",
94 " gl_FragColor = vColor;",
101 "float $(func)_emu($(args)) {",
102 " return $(func)_base($(baseArgs));",
107 "vec2 $(func)_emu($(args)) {",
109 " $(func)_base($(baseArgsX)),",
110 " $(func)_base($(baseArgsY)));",
115 "vec3 $(func)_emu($(args)) {",
117 " $(func)_base($(baseArgsX)),",
118 " $(func)_base($(baseArgsY)),",
119 " $(func)_base($(baseArgsZ)));",
124 "vec4 $(func)_emu($(args)) {",
126 " $(func)_base($(baseArgsX)),",
127 " $(func)_base($(baseArgsY)),",
128 " $(func)_base($(baseArgsZ)),",
129 " $(func)_base($(baseArgsW)));",
137 "bvec2 $(func)_emu($(args)) {",
139 " $(func)_base($(baseArgsX)),",
140 " $(func)_base($(baseArgsY)));",
145 "bvec3 $(func)_emu($(args)) {",
147 " $(func)_base($(baseArgsX)),",
148 " $(func)_base($(baseArgsY)),",
149 " $(func)_base($(baseArgsZ)));",
154 "vec4 $(func)_emu($(args)) {",
156 " $(func)_base($(baseArgsX)),",
157 " $(func)_base($(baseArgsY)),",
158 " $(func)_base($(baseArgsZ)),",
159 " $(func)_base($(baseArgsW)));",
164 var replaceRE = /\$\((\w+)\)/g;
166 var replaceParams = function(str) {
167 var args = arguments;
168 return str.replace(replaceRE, function(str, p1, offset, s) {
169 for (var ii = 1; ii < args.length; ++ii) {
170 if (args[ii][p1] !== undefined) {
174 throw "unknown string param '" + p1 + "'";
178 var generateReferenceShader = function(
179 shaderInfo, template, params, typeInfo, test) {
180 var input = shaderInfo.input;
181 var output = shaderInfo.output;
182 var feature = params.feature;
183 var testFunc = params.testFunc;
184 var emuFunc = params.emuFunc || "";
185 var extra = params.extra || '';
186 var args = params.args || "$(type) value";
187 var type = typeInfo.type;
188 var typeCode = typeInfo.code;
190 var baseArgs = params.baseArgs || "value$(field)";
191 var baseArgsX = replaceParams(baseArgs, {field: ".x"});
192 var baseArgsY = replaceParams(baseArgs, {field: ".y"});
193 var baseArgsZ = replaceParams(baseArgs, {field: ".z"});
194 var baseArgsW = replaceParams(baseArgs, {field: ".w"});
195 var baseArgs = replaceParams(baseArgs, {field: ""});
197 test = replaceParams(test, {
200 func: feature + "_emu"
202 emuFunc = replaceParams(emuFunc, {
205 args = replaceParams(args, {
208 typeCode = replaceParams(typeCode, {
213 baseArgsX: baseArgsX,
214 baseArgsY: baseArgsY,
215 baseArgsZ: baseArgsZ,
218 var shader = replaceParams(template, {
220 emu: emuFunc + "\n\n" + typeCode,
226 var generateTestShader = function(
227 shaderInfo, template, params, test) {
228 var input = shaderInfo.input;
229 var output = shaderInfo.output;
230 var feature = params.feature;
231 var testFunc = params.testFunc;
232 var extra = params.extra || '';
234 test = replaceParams(test, {
239 var shader = replaceParams(template, {
247 function _reportResults(refData, refImg, testData, testImg, tolerance,
248 width, height, ctx, imgData, wtu, canvas2d, consoleDiv) {
250 var firstFailure = null;
251 for (var yy = 0; yy < height; ++yy) {
252 for (var xx = 0; xx < width; ++xx) {
253 var offset = (yy * width + xx) * 4;
254 var imgOffset = ((height - yy - 1) * width + xx) * 4;
255 imgData.data[imgOffset + 0] = 0;
256 imgData.data[imgOffset + 1] = 0;
257 imgData.data[imgOffset + 2] = 0;
258 imgData.data[imgOffset + 3] = 255;
259 if (Math.abs(refData[offset + 0] - testData[offset + 0]) > tolerance ||
260 Math.abs(refData[offset + 1] - testData[offset + 1]) > tolerance ||
261 Math.abs(refData[offset + 2] - testData[offset + 2]) > tolerance ||
262 Math.abs(refData[offset + 3] - testData[offset + 3]) > tolerance) {
263 var detail = 'at (' + xx + ',' + yy + '): ref=(' +
264 refData[offset + 0] + ',' +
265 refData[offset + 1] + ',' +
266 refData[offset + 2] + ',' +
267 refData[offset + 3] + ') test=(' +
268 testData[offset + 0] + ',' +
269 testData[offset + 1] + ',' +
270 testData[offset + 2] + ',' +
271 testData[offset + 3] + ') tolerance=' + tolerance;
272 consoleDiv.appendChild(document.createTextNode(detail));
273 consoleDiv.appendChild(document.createElement('br'));
275 firstFailure = ": " + detail;
277 imgData.data[imgOffset] = 255;
285 ctx.putImageData(imgData, 0, 0);
286 diffImg = wtu.makeImageFromCanvas(canvas2d);
289 var div = document.createElement("div");
290 div.className = "testimages";
291 wtu.insertImage(div, "ref", refImg);
292 wtu.insertImage(div, "test", testImg);
294 wtu.insertImage(div, "diff", diffImg);
296 div.appendChild(document.createElement('br'));
298 consoleDiv.appendChild(div);
301 testFailed("images are different" + (firstFailure ? firstFailure : ""));
303 testPassed("images are the same");
306 consoleDiv.appendChild(document.createElement('hr'));
309 var runFeatureTest = function(params) {
310 var wtu = WebGLTestUtils;
311 var gridRes = params.gridRes;
312 var vertexTolerance = params.tolerance || 0;
313 var fragmentTolerance = vertexTolerance;
314 if ('fragmentTolerance' in params)
315 fragmentTolerance = params.fragmentTolerance || 0;
317 description("Testing GLSL feature: " + params.feature);
322 var consoleDiv = document.getElementById("console");
323 var canvas = document.createElement('canvas');
324 canvas.width = width;
325 canvas.height = height;
326 var gl = wtu.create3DContext(canvas, { premultipliedAlpha: false });
328 testFailed("context does not exist");
333 var canvas2d = document.createElement('canvas');
334 canvas2d.width = width;
335 canvas2d.height = height;
336 var ctx = canvas2d.getContext("2d");
337 var imgData = ctx.getImageData(0, 0, width, height);
343 vertexShaderTemplate: vertexShaderTemplate,
344 fragmentShaderTemplate: baseFragmentShader,
345 tolerance: vertexTolerance
349 output: "gl_FragColor",
350 vertexShaderTemplate: baseVertexShader,
351 fragmentShaderTemplate: fragmentShaderTemplate,
352 tolerance: fragmentTolerance
355 for (var ss = 0; ss < shaderInfos.length; ++ss) {
356 var shaderInfo = shaderInfos[ss];
357 var tests = params.tests;
358 var testTypes = params.emuFuncs || (params.bvecTest ? bvecTypes : types);
359 // Test vertex shaders
360 for (var ii = 0; ii < tests.length; ++ii) {
361 var type = testTypes[ii];
362 if (params.simpleEmu) {
365 code: params.simpleEmu
369 var str = replaceParams(params.testFunc, {
370 func: params.feature,
374 var passMsg = "Testing: " + str + " in " + shaderInfo.type + " shader";
377 var referenceVertexShaderSource = generateReferenceShader(
379 shaderInfo.vertexShaderTemplate,
383 var referenceFragmentShaderSource = generateReferenceShader(
385 shaderInfo.fragmentShaderTemplate,
389 var testVertexShaderSource = generateTestShader(
391 shaderInfo.vertexShaderTemplate,
394 var testFragmentShaderSource = generateTestShader(
396 shaderInfo.fragmentShaderTemplate,
402 var referenceVertexShader = wtu.loadShader(gl, referenceVertexShaderSource, gl.VERTEX_SHADER, testFailed, true, 'reference');
403 var referenceFragmentShader = wtu.loadShader(gl, referenceFragmentShaderSource, gl.FRAGMENT_SHADER, testFailed, true, 'reference');
404 var testVertexShader = wtu.loadShader(gl, testVertexShaderSource, gl.VERTEX_SHADER, testFailed, true, 'test');
405 var testFragmentShader = wtu.loadShader(gl, testFragmentShaderSource, gl.FRAGMENT_SHADER, testFailed, true, 'test');
408 if (parseInt(wtu.getUrlOptions().dumpShaders)) {
410 shader: referenceVertexShader,
412 label: "reference vertex shader",
413 source: referenceVertexShaderSource
416 shader: referenceFragmentShader,
418 label: "reference fragment shader",
419 source: referenceFragmentShaderSource
421 wtu.dumpShadersInfo(gl, window.location.pathname, passMsg, vRefInfo, fRefInfo);
424 shader: testVertexShader,
426 label: "test vertex shader",
427 source: testVertexShaderSource
430 shader: testFragmentShader,
432 label: "test fragment shader",
433 source: testFragmentShaderSource
435 wtu.dumpShadersInfo(gl, window.location.pathname, passMsg, vTestInfo, fTestInfo);
439 referenceVertexShader, referenceFragmentShader);
440 var refImg = wtu.makeImageFromCanvas(canvas);
443 testVertexShader, referenceFragmentShader);
446 referenceVertexShader, testFragmentShader);
448 var testImg = wtu.makeImageFromCanvas(canvas);
450 _reportResults(refData, refImg, testData, testImg, shaderInfo.tolerance,
451 width, height, ctx, imgData, wtu, canvas2d, consoleDiv);
457 function draw(vertexShader, fragmentShader) {
458 var program = wtu.createProgram(gl, vertexShader, fragmentShader, testFailed);
460 var posLoc = gl.getAttribLocation(program, "aPosition");
461 wtu.setupIndexedQuad(gl, gridRes, posLoc);
463 gl.useProgram(program);
464 wtu.clearAndDrawIndexedQuad(gl, gridRes, [0, 0, 255, 255]);
465 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no errors from draw");
467 var img = new Uint8Array(width * height * 4);
468 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, img);
474 var runBasicTest = function(params) {
475 var wtu = WebGLTestUtils;
476 var gridRes = params.gridRes;
477 var vertexTolerance = params.tolerance || 0;
478 var fragmentTolerance = vertexTolerance;
479 if ('fragmentTolerance' in params)
480 fragmentTolerance = params.fragmentTolerance || 0;
482 description("Testing : " + document.getElementsByTagName("title")[0].innerText);
487 var consoleDiv = document.getElementById("console");
488 var canvas = document.createElement('canvas');
489 canvas.width = width;
490 canvas.height = height;
491 var gl = wtu.create3DContext(canvas);
493 testFailed("context does not exist");
498 var canvas2d = document.createElement('canvas');
499 canvas2d.width = width;
500 canvas2d.height = height;
501 var ctx = canvas2d.getContext("2d");
502 var imgData = ctx.getImageData(0, 0, width, height);
508 vertexShaderTemplate: vertexShaderTemplate,
509 fragmentShaderTemplate: baseFragmentShader,
510 tolerance: vertexTolerance
514 output: "gl_FragColor",
515 vertexShaderTemplate: baseVertexShader,
516 fragmentShaderTemplate: fragmentShaderTemplate,
517 tolerance: fragmentTolerance
520 for (var ss = 0; ss < shaderInfos.length; ++ss) {
521 var shaderInfo = shaderInfos[ss];
522 var tests = params.tests;
523 // var testTypes = params.emuFuncs || (params.bvecTest ? bvecTypes : types);
524 // Test vertex shaders
525 for (var ii = 0; ii < tests.length; ++ii) {
526 var test = tests[ii];
528 var passMsg = "Testing: " + test.name + " in " + shaderInfo.type + " shader";
531 function genShader(shaderInfo, template, shader, subs) {
532 shader = replaceParams(shader, subs, {
533 input: shaderInfo.input,
534 output: shaderInfo.output
536 shader = replaceParams(template, subs, {
544 var referenceVertexShaderSource = genShader(
546 shaderInfo.vertexShaderTemplate,
547 test.reference.shader,
548 test.reference.subs);
549 var referenceFragmentShaderSource = genShader(
551 shaderInfo.fragmentShaderTemplate,
552 test.reference.shader,
553 test.reference.subs);
554 var testVertexShaderSource = genShader(
556 shaderInfo.vertexShaderTemplate,
559 var testFragmentShaderSource = genShader(
561 shaderInfo.fragmentShaderTemplate,
566 var referenceVertexShader = wtu.loadShader(gl, referenceVertexShaderSource, gl.VERTEX_SHADER, testFailed, true, 'reference');
567 var referenceFragmentShader = wtu.loadShader(gl, referenceFragmentShaderSource, gl.FRAGMENT_SHADER, testFailed, true, 'reference');
568 var testVertexShader = wtu.loadShader(gl, testVertexShaderSource, gl.VERTEX_SHADER, testFailed, true, 'test');
569 var testFragmentShader = wtu.loadShader(gl, testFragmentShaderSource, gl.FRAGMENT_SHADER, testFailed, true, 'test');
572 if (parseInt(wtu.getUrlOptions().dumpShaders)) {
574 shader: referenceVertexShader,
576 label: "reference vertex shader",
577 source: referenceVertexShaderSource
580 shader: referenceFragmentShader,
582 label: "reference fragment shader",
583 source: referenceFragmentShaderSource
585 wtu.dumpShadersInfo(gl, window.location.pathname, passMsg, vRefInfo, fRefInfo);
588 shader: testVertexShader,
590 label: "test vertex shader",
591 source: testVertexShaderSource
594 shader: testFragmentShader,
596 label: "test fragment shader",
597 source: testFragmentShaderSource
599 wtu.dumpShadersInfo(gl, window.location.pathname, passMsg, vTestInfo, fTestInfo);
602 var refData = draw(referenceVertexShader, referenceFragmentShader);
603 var refImg = wtu.makeImageFromCanvas(canvas);
605 var testData = draw(testVertexShader, referenceFragmentShader);
607 var testData = draw(referenceVertexShader, testFragmentShader);
609 var testImg = wtu.makeImageFromCanvas(canvas);
611 _reportResults(refData, refImg, testData, testImg, shaderInfo.tolerance,
612 width, height, ctx, imgData, wtu, canvas2d, consoleDiv);
618 function draw(vertexShader, fragmentShader) {
619 var program = wtu.createProgram(gl, vertexShader, fragmentShader, testFailed);
621 var posLoc = gl.getAttribLocation(program, "aPosition");
622 wtu.setupIndexedQuad(gl, gridRes, posLoc);
624 gl.useProgram(program);
625 wtu.clearAndDrawIndexedQuad(gl, gridRes, [0, 0, 255, 255]);
626 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no errors from draw");
628 var img = new Uint8Array(width * height * 4);
629 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, img);
635 var runReferenceImageTest = function(params) {
636 var wtu = WebGLTestUtils;
637 var gridRes = params.gridRes;
638 var vertexTolerance = params.tolerance || 0;
639 var fragmentTolerance = vertexTolerance;
640 if ('fragmentTolerance' in params)
641 fragmentTolerance = params.fragmentTolerance || 0;
643 description("Testing GLSL feature: " + params.feature);
648 var consoleDiv = document.getElementById("console");
649 var canvas = document.createElement('canvas');
650 canvas.width = width;
651 canvas.height = height;
652 var gl = wtu.create3DContext(canvas, { antialias: false, premultipliedAlpha: false });
654 testFailed("context does not exist");
659 var canvas2d = document.createElement('canvas');
660 canvas2d.width = width;
661 canvas2d.height = height;
662 var ctx = canvas2d.getContext("2d");
663 var imgData = ctx.getImageData(0, 0, width, height);
665 // State for reference images for vertex shader tests.
666 // These are drawn with the same tessellated grid as the test vertex
667 // shader so that the interpolation is identical. The grid is reused
668 // from test to test; the colors are changed.
670 var indexedQuadForReferenceVertexShader =
671 wtu.setupIndexedQuad(gl, gridRes, 0);
672 var referenceVertexShaderProgram =
673 wtu.setupProgram(gl, [ baseVertexShaderWithColor, baseFragmentShader ],
674 ["aPosition", "aColor"]);
675 var referenceVertexShaderColorBuffer = gl.createBuffer();
681 vertexShaderTemplate: vertexShaderTemplate,
682 fragmentShaderTemplate: baseFragmentShader,
683 tolerance: vertexTolerance
687 output: "gl_FragColor",
688 vertexShaderTemplate: baseVertexShader,
689 fragmentShaderTemplate: fragmentShaderTemplate,
690 tolerance: fragmentTolerance
693 for (var ss = 0; ss < shaderInfos.length; ++ss) {
694 var shaderInfo = shaderInfos[ss];
695 var tests = params.tests;
696 var testTypes = params.emuFuncs || (params.bvecTest ? bvecTypes : types);
697 // Test vertex shaders
698 for (var ii = 0; ii < tests.length; ++ii) {
699 var type = testTypes[ii];
700 var isVertex = (ss == 0);
702 var str = replaceParams(params.testFunc, {
703 func: params.feature,
707 var passMsg = "Testing: " + str + " in " + shaderInfo.type + " shader";
710 var referenceVertexShaderSource = generateReferenceShader(
712 shaderInfo.vertexShaderTemplate,
716 var referenceFragmentShaderSource = generateReferenceShader(
718 shaderInfo.fragmentShaderTemplate,
722 var testVertexShaderSource = generateTestShader(
724 shaderInfo.vertexShaderTemplate,
727 var testFragmentShaderSource = generateTestShader(
729 shaderInfo.fragmentShaderTemplate,
732 var referenceTextureOrArray = generateReferenceImage(
735 isVertex ? gridRes : width,
736 isVertex ? gridRes : height,
740 var testVertexShader = wtu.loadShader(gl, testVertexShaderSource, gl.VERTEX_SHADER, testFailed, true);
741 var testFragmentShader = wtu.loadShader(gl, testFragmentShaderSource, gl.FRAGMENT_SHADER, testFailed, true);
745 if (parseInt(wtu.getUrlOptions().dumpShaders)) {
747 shader: referenceVertexShader,
749 label: "reference vertex shader",
750 source: referenceVertexShaderSource
753 shader: referenceFragmentShader,
755 label: "reference fragment shader",
756 source: referenceFragmentShaderSource
758 wtu.dumpShadersInfo(gl, window.location.pathname, passMsg, vRefInfo, fRefInfo);
761 shader: testVertexShader,
763 label: "test vertex shader",
764 source: testVertexShaderSource
767 shader: testFragmentShader,
769 label: "test fragment shader",
770 source: testFragmentShaderSource
772 wtu.dumpShadersInfo(gl, window.location.pathname, passMsg, vTestInfo, fTestInfo);
777 refData = drawVertexReferenceImage(referenceTextureOrArray);
779 refData = drawFragmentReferenceImage(referenceTextureOrArray);
781 var refImg = wtu.makeImageFromCanvas(canvas);
784 var referenceFragmentShader = wtu.loadShader(gl, referenceFragmentShaderSource, gl.FRAGMENT_SHADER, testFailed);
786 testVertexShader, referenceFragmentShader);
788 var referenceVertexShader = wtu.loadShader(gl, referenceVertexShaderSource, gl.VERTEX_SHADER, testFailed);
790 referenceVertexShader, testFragmentShader);
792 var testImg = wtu.makeImageFromCanvas(canvas);
793 var testTolerance = shaderInfo.tolerance;
794 // Provide per-test tolerance so that we can increase it only for those desired.
795 if ('tolerance' in tests[ii])
796 testTolerance = tests[ii].tolerance || 0;
797 _reportResults(refData, refImg, testData, testImg, testTolerance,
798 width, height, ctx, imgData, wtu, canvas2d, consoleDiv);
804 function draw(vertexShader, fragmentShader) {
805 var program = wtu.createProgram(gl, vertexShader, fragmentShader, testFailed);
807 var posLoc = gl.getAttribLocation(program, "aPosition");
808 wtu.setupIndexedQuad(gl, gridRes, posLoc);
810 gl.useProgram(program);
811 wtu.clearAndDrawIndexedQuad(gl, gridRes, [0, 0, 255, 255]);
812 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no errors from draw");
814 var img = new Uint8Array(width * height * 4);
815 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, img);
819 function drawVertexReferenceImage(colors) {
820 gl.bindBuffer(gl.ARRAY_BUFFER, indexedQuadForReferenceVertexShader[0]);
821 gl.enableVertexAttribArray(0);
822 gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);
823 gl.bindBuffer(gl.ARRAY_BUFFER, referenceVertexShaderColorBuffer);
824 gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW);
825 gl.enableVertexAttribArray(1);
826 gl.vertexAttribPointer(1, 4, gl.UNSIGNED_BYTE, true, 0, 0);
827 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexedQuadForReferenceVertexShader[1]);
828 gl.useProgram(referenceVertexShaderProgram);
829 wtu.clearAndDrawIndexedQuad(gl, gridRes);
830 gl.disableVertexAttribArray(0);
831 gl.disableVertexAttribArray(1);
832 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no errors from draw");
834 var img = new Uint8Array(width * height * 4);
835 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, img);
839 function drawFragmentReferenceImage(texture) {
840 var program = wtu.setupTexturedQuad(gl);
842 gl.activeTexture(gl.TEXTURE0);
843 gl.bindTexture(gl.TEXTURE_2D, texture);
844 var texLoc = gl.getUniformLocation(program, "tex");
845 gl.uniform1i(texLoc, 0);
846 wtu.clearAndDrawUnitQuad(gl);
847 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no errors from draw");
849 var img = new Uint8Array(width * height * 4);
850 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, img);
855 * Creates and returns either a Uint8Array (for vertex shaders) or
856 * WebGLTexture (for fragment shaders) containing the reference
857 * image for the function being tested. Exactly how the function is
858 * evaluated, and the size of the returned texture or array, depends on
859 * whether we are testing a vertex or fragment shader. If a fragment
860 * shader, the function is evaluated at the pixel centers. If a
861 * vertex shader, the function is evaluated at the triangle's
864 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use to generate texture objects.
865 * @param {!function(number,number,number,number): !Array.<number>} generator The reference image generator function.
866 * @param {number} width The width of the texture to generate if testing a fragment shader; the grid resolution if testing a vertex shader.
867 * @param {number} height The height of the texture to generate if testing a fragment shader; the grid resolution if testing a vertex shader.
868 * @param {boolean} isVertex True if generating a reference image for a vertex shader; false if for a fragment shader.
869 * @return {!WebGLTexture|!Uint8Array} The texture object or array that was generated.
871 function generateReferenceImage(
878 // Note: the math in this function must match that in the vertex and
879 // fragment shader templates above.
880 function computeTexCoord(x) {
881 return x * 0.5 + 0.5;
884 function computeVertexColor(texCoordX, texCoordY) {
887 texCoordX * texCoordY,
888 (1.0 - texCoordX) * texCoordY * 0.5 + 0.5 ];
892 * Computes fragment color according to the algorithm used for interpolation
893 * in OpenGL (GLES 2.0 spec 3.5.1, OpenGL 4.3 spec 14.6.1).
895 function computeInterpolatedColor(texCoordX, texCoordY) {
896 // Calculate grid line indexes below and to the left from texCoord.
897 var gridBottom = Math.floor(texCoordY * gridRes);
898 if (gridBottom == gridRes) {
901 var gridLeft = Math.floor(texCoordX * gridRes);
902 if (gridLeft == gridRes) {
906 // Calculate coordinates relative to the grid cell.
907 var cellX = texCoordX * gridRes - gridLeft;
908 var cellY = texCoordY * gridRes - gridBottom;
910 // Barycentric coordinates inside either triangle ACD or ABC
911 // are used as weights for the vertex colors in the corners:
917 var aColor = computeVertexColor(gridLeft / gridRes, (gridBottom + 1) / gridRes);
918 var bColor = computeVertexColor((gridLeft + 1) / gridRes, (gridBottom + 1) / gridRes);
919 var cColor = computeVertexColor((gridLeft + 1) / gridRes, gridBottom / gridRes);
920 var dColor = computeVertexColor(gridLeft / gridRes, gridBottom / gridRes);
922 // Calculate weights.
925 if (cellX + cellY < 1) {
926 // In bottom triangle ACD.
927 a = cellY; // area of triangle C-D-(cellX, cellY) relative to ACD
928 c = cellX; // area of triangle D-A-(cellX, cellY) relative to ACD
932 // In top triangle ABC.
933 a = 1 - cellX; // area of the triangle B-C-(cellX, cellY) relative to ABC
934 c = 1 - cellY; // area of the triangle A-B-(cellX, cellY) relative to ABC
939 var interpolated = [];
940 for (var ii = 0; ii < aColor.length; ++ii) {
941 interpolated.push(a * aColor[ii] + b * bColor[ii] + c * cColor[ii] + d * dColor[ii]);
946 function clamp(value, minVal, maxVal) {
947 return Math.max(minVal, Math.min(value, maxVal));
950 // Evaluates the function at clip coordinates (px,py), storing the
951 // result in the array "pixel". Each channel's result is clamped
952 // between 0 and 255.
953 function evaluateAtClipCoords(px, py, pixel, colorFunc) {
954 var tcx = computeTexCoord(px);
955 var tcy = computeTexCoord(py);
957 var color = colorFunc(tcx, tcy);
959 var output = generator(color[0], color[1], color[2], color[3]);
961 // Multiply by 256 to get even distribution for all values between 0 and 1.
962 // Use rounding rather than truncation to more closely match the GPU's behavior.
963 pixel[0] = clamp(Math.round(256 * output[0]), 0, 255);
964 pixel[1] = clamp(Math.round(256 * output[1]), 0, 255);
965 pixel[2] = clamp(Math.round(256 * output[2]), 0, 255);
966 pixel[3] = clamp(Math.round(256 * output[3]), 0, 255);
969 function generateFragmentReference() {
970 var data = new Uint8Array(4 * width * height);
972 var horizTexel = 1.0 / width;
973 var vertTexel = 1.0 / height;
974 var halfHorizTexel = 0.5 * horizTexel;
975 var halfVertTexel = 0.5 * vertTexel;
977 var pixel = new Array(4);
979 for (var yi = 0; yi < height; ++yi) {
980 for (var xi = 0; xi < width; ++xi) {
981 // The function must be evaluated at pixel centers.
983 // Compute desired position in clip space
984 var px = -1.0 + 2.0 * (halfHorizTexel + xi * horizTexel);
985 var py = -1.0 + 2.0 * (halfVertTexel + yi * vertTexel);
987 evaluateAtClipCoords(px, py, pixel, computeInterpolatedColor);
988 var index = 4 * (width * yi + xi);
989 data[index + 0] = pixel[0];
990 data[index + 1] = pixel[1];
991 data[index + 2] = pixel[2];
992 data[index + 3] = pixel[3];
996 var texture = gl.createTexture();
997 gl.bindTexture(gl.TEXTURE_2D, texture);
998 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
999 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
1000 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
1001 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
1002 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0,
1003 gl.RGBA, gl.UNSIGNED_BYTE, data);
1007 function generateVertexReference() {
1008 // We generate a Uint8Array which contains the evaluation of the
1009 // function at the vertices of the triangle mesh. It is expected
1010 // that the width and the height are identical, and equivalent
1011 // to the grid resolution.
1012 if (width != height) {
1013 throw "width and height must be equal";
1016 var texSize = 1 + width;
1017 var data = new Uint8Array(4 * texSize * texSize);
1019 var step = 2.0 / width;
1021 var pixel = new Array(4);
1023 for (var yi = 0; yi < texSize; ++yi) {
1024 for (var xi = 0; xi < texSize; ++xi) {
1025 // The function is evaluated at the triangles' vertices.
1027 // Compute desired position in clip space
1028 var px = -1.0 + (xi * step);
1029 var py = -1.0 + (yi * step);
1031 evaluateAtClipCoords(px, py, pixel, computeVertexColor);
1032 var index = 4 * (texSize * yi + xi);
1033 data[index + 0] = pixel[0];
1034 data[index + 1] = pixel[1];
1035 data[index + 2] = pixel[2];
1036 data[index + 3] = pixel[3];
1043 //----------------------------------------------------------------------
1044 // Body of generateReferenceImage
1048 return generateVertexReference();
1050 return generateFragmentReference();
1057 * runs a bunch of GLSL tests using the passed in parameters
1058 * The parameters are:
1061 * the name of the function being tested (eg, sin, dot,
1065 * The prototype of function to be tested not including the
1069 * A base function that can be used to generate emulation
1070 * functions. Example for 'ceil'
1072 * float $(func)_base(float value) {
1073 * float m = mod(value, 1.0);
1074 * return m != 0.0 ? (value + 1.0 - m) : value;
1078 * The arguments to the function
1080 * baseArgs: (optional)
1081 * The arguments when a base function is used to create an
1082 * emulation function. For example 'float sign_base(float v)'
1083 * is used to implemenent vec2 sign_emu(vec2 v).
1086 * if supplied, the code that can be used to generate all
1087 * functions for all types.
1089 * Example for 'normalize':
1091 * $(type) $(func)_emu($(args)) {
1092 * return value / length(value);
1095 * gridRes: (optional)
1096 * The resolution of the mesh to generate. The default is a
1097 * 1x1 grid but many vertex shaders need a higher resolution
1098 * otherwise the only values passed in are the 4 corners
1099 * which often have the same value.
1102 * The code for each test. It is assumed the tests are for
1103 * float, vec2, vec3, vec4 in that order.
1105 * tolerance: (optional)
1106 * Allow some tolerance in the comparisons. The tolerance is applied to
1107 * both vertex and fragment shaders. The default tolerance is 0, meaning
1108 * the values have to be identical.
1110 * fragmentTolerance: (optional)
1111 * Specify a tolerance which only applies to fragment shaders. The
1112 * fragment-only tolerance will override the shared tolerance for
1113 * fragment shaders if both are specified. Fragment shaders usually
1114 * use mediump float precision so they sometimes require higher tolerance
1115 * than vertex shaders which use highp by default.
1117 runFeatureTest: runFeatureTest,
1120 * Runs a bunch of GLSL tests using the passed in parameters
1122 * The parameters are:
1125 * Array of tests. For each test the following parameters are expected
1128 * some description of the test
1130 * parameters for the reference shader (see below)
1132 * parameters for the test shader (see below)
1134 * The parameter for the reference and test shaders are
1136 * shader: the GLSL for the shader
1137 * subs: any substitutions you wish to define for the shader.
1139 * Each shader is created from a basic template that
1140 * defines an input and an output. You can see the
1141 * templates at the top of this file. The input and output
1142 * change depending on whether or not we are generating
1143 * a vertex or fragment shader.
1145 * All this code function does is a bunch of string substitutions.
1146 * A substitution is defined by $(name). If name is found in
1147 * the 'subs' parameter it is replaced. 4 special names exist.
1149 * 'input' the input to your GLSL. Always a vec4. All change
1150 * from 0 to 1 over the quad to be drawn.
1152 * 'output' the output color. Also a vec4
1154 * 'emu' a place to insert extra stuff
1155 * 'extra' a place to insert extra stuff.
1157 * You can think of the templates like this
1163 * // do math to calculate input
1169 * Your shader first has any subs you provided applied as well
1170 * as 'input' and 'output'
1172 * It is then inserted into the template which is also provided
1175 * gridRes: (optional)
1176 * The resolution of the mesh to generate. The default is a
1177 * 1x1 grid but many vertex shaders need a higher resolution
1178 * otherwise the only values passed in are the 4 corners
1179 * which often have the same value.
1181 * tolerance: (optional)
1182 * Allow some tolerance in the comparisons. The tolerance is applied to
1183 * both vertex and fragment shaders. The default tolerance is 0, meaning
1184 * the values have to be identical.
1186 * fragmentTolerance: (optional)
1187 * Specify a tolerance which only applies to fragment shaders. The
1188 * fragment-only tolerance will override the shared tolerance for
1189 * fragment shaders if both are specified. Fragment shaders usually
1190 * use mediump float precision so they sometimes require higher tolerance
1191 * than vertex shaders which use highp.
1193 runBasicTest: runBasicTest,
1196 * Runs a bunch of GLSL tests using the passed in parameters. The
1197 * expected results are computed as a reference image in JavaScript
1198 * instead of on the GPU. The parameters are:
1201 * the name of the function being tested (eg, sin, dot,
1205 * The prototype of function to be tested not including the
1209 * The arguments to the function
1211 * gridRes: (optional)
1212 * The resolution of the mesh to generate. The default is a
1213 * 1x1 grid but many vertex shaders need a higher resolution
1214 * otherwise the only values passed in are the 4 corners
1215 * which often have the same value.
1218 * Array of tests. It is assumed the tests are for float, vec2,
1219 * vec3, vec4 in that order. For each test the following
1220 * parameters are expected:
1222 * source: the GLSL source code for the tests
1224 * generator: a JavaScript function taking four parameters
1225 * which evaluates the same function as the GLSL source,
1226 * returning its result as a newly allocated array.
1228 * tolerance: (optional) a per-test tolerance.
1231 * Extra GLSL code inserted at the top of each test's shader.
1233 * tolerance: (optional)
1234 * Allow some tolerance in the comparisons. The tolerance is applied to
1235 * both vertex and fragment shaders. The default tolerance is 0, meaning
1236 * the values have to be identical.
1238 * fragmentTolerance: (optional)
1239 * Specify a tolerance which only applies to fragment shaders. The
1240 * fragment-only tolerance will override the shared tolerance for
1241 * fragment shaders if both are specified. Fragment shaders usually
1242 * use mediump float precision so they sometimes require higher tolerance
1243 * than vertex shaders which use highp.
1245 runReferenceImageTest: runReferenceImageTest,