2 Copyright (c) 2019 The Khronos Group Inc.
3 Use of this source code is governed by an MIT-style license that can be
4 found in the LICENSE.txt file.
6 GLSLGenerator = (function() {
8 var vertexShaderTemplate = [
9 "attribute vec4 aPosition;",
11 "varying vec4 vColor;",
18 " gl_Position = aPosition;",
19 " vec2 texcoord = vec2(aPosition.xy * 0.5 + vec2(0.5, 0.5));",
20 " vec4 color = vec4(",
22 " texcoord.x * texcoord.y,",
23 " (1.0 - texcoord.x) * texcoord.y * 0.5 + 0.5);",
28 var fragmentShaderTemplate = [
29 "precision mediump float;",
31 "varying vec4 vColor;",
42 var baseVertexShader = [
43 "attribute vec4 aPosition;",
45 "varying vec4 vColor;",
49 " gl_Position = aPosition;",
50 " vec2 texcoord = vec2(aPosition.xy * 0.5 + vec2(0.5, 0.5));",
53 " texcoord.x * texcoord.y,",
54 " (1.0 - texcoord.x) * texcoord.y * 0.5 + 0.5);",
58 var baseVertexShaderWithColor = [
59 "attribute vec4 aPosition;",
60 "attribute vec4 aColor;",
62 "varying vec4 vColor;",
66 " gl_Position = aPosition;",
71 var baseFragmentShader = [
72 "precision mediump float;",
73 "varying vec4 vColor;",
77 " gl_FragColor = vColor;",
84 "float $(func)_emu($(args)) {",
85 " return $(func)_base($(baseArgs));",
90 "vec2 $(func)_emu($(args)) {",
92 " $(func)_base($(baseArgsX)),",
93 " $(func)_base($(baseArgsY)));",
98 "vec3 $(func)_emu($(args)) {",
100 " $(func)_base($(baseArgsX)),",
101 " $(func)_base($(baseArgsY)),",
102 " $(func)_base($(baseArgsZ)));",
107 "vec4 $(func)_emu($(args)) {",
109 " $(func)_base($(baseArgsX)),",
110 " $(func)_base($(baseArgsY)),",
111 " $(func)_base($(baseArgsZ)),",
112 " $(func)_base($(baseArgsW)));",
120 "bvec2 $(func)_emu($(args)) {",
122 " $(func)_base($(baseArgsX)),",
123 " $(func)_base($(baseArgsY)));",
128 "bvec3 $(func)_emu($(args)) {",
130 " $(func)_base($(baseArgsX)),",
131 " $(func)_base($(baseArgsY)),",
132 " $(func)_base($(baseArgsZ)));",
137 "vec4 $(func)_emu($(args)) {",
139 " $(func)_base($(baseArgsX)),",
140 " $(func)_base($(baseArgsY)),",
141 " $(func)_base($(baseArgsZ)),",
142 " $(func)_base($(baseArgsW)));",
147 var replaceRE = /\$\((\w+)\)/g;
149 var replaceParams = function(str) {
150 var args = arguments;
151 return str.replace(replaceRE, function(str, p1, offset, s) {
152 for (var ii = 1; ii < args.length; ++ii) {
153 if (args[ii][p1] !== undefined) {
157 throw "unknown string param '" + p1 + "'";
161 var generateReferenceShader = function(
162 shaderInfo, template, params, typeInfo, test) {
163 var input = shaderInfo.input;
164 var output = shaderInfo.output;
165 var feature = params.feature;
166 var testFunc = params.testFunc;
167 var emuFunc = params.emuFunc || "";
168 var extra = params.extra || '';
169 var args = params.args || "$(type) value";
170 var type = typeInfo.type;
171 var typeCode = typeInfo.code;
173 var baseArgs = params.baseArgs || "value$(field)";
174 var baseArgsX = replaceParams(baseArgs, {field: ".x"});
175 var baseArgsY = replaceParams(baseArgs, {field: ".y"});
176 var baseArgsZ = replaceParams(baseArgs, {field: ".z"});
177 var baseArgsW = replaceParams(baseArgs, {field: ".w"});
178 var baseArgs = replaceParams(baseArgs, {field: ""});
180 test = replaceParams(test, {
183 func: feature + "_emu"
185 emuFunc = replaceParams(emuFunc, {
188 args = replaceParams(args, {
191 typeCode = replaceParams(typeCode, {
196 baseArgsX: baseArgsX,
197 baseArgsY: baseArgsY,
198 baseArgsZ: baseArgsZ,
201 var shader = replaceParams(template, {
203 emu: emuFunc + "\n\n" + typeCode,
209 var generateTestShader = function(
210 shaderInfo, template, params, test) {
211 var input = shaderInfo.input;
212 var output = shaderInfo.output;
213 var feature = params.feature;
214 var testFunc = params.testFunc;
215 var extra = params.extra || '';
217 test = replaceParams(test, {
222 var shader = replaceParams(template, {
230 function _reportResults(refData, refImg, testData, testImg, tolerance,
231 width, height, ctx, imgData, wtu, canvas2d, consoleDiv) {
233 var firstFailure = null;
234 for (var yy = 0; yy < height; ++yy) {
235 for (var xx = 0; xx < width; ++xx) {
236 var offset = (yy * width + xx) * 4;
237 var imgOffset = ((height - yy - 1) * width + xx) * 4;
238 imgData.data[imgOffset + 0] = 0;
239 imgData.data[imgOffset + 1] = 0;
240 imgData.data[imgOffset + 2] = 0;
241 imgData.data[imgOffset + 3] = 255;
242 if (Math.abs(refData[offset + 0] - testData[offset + 0]) > tolerance ||
243 Math.abs(refData[offset + 1] - testData[offset + 1]) > tolerance ||
244 Math.abs(refData[offset + 2] - testData[offset + 2]) > tolerance ||
245 Math.abs(refData[offset + 3] - testData[offset + 3]) > tolerance) {
246 var detail = 'at (' + xx + ',' + yy + '): ref=(' +
247 refData[offset + 0] + ',' +
248 refData[offset + 1] + ',' +
249 refData[offset + 2] + ',' +
250 refData[offset + 3] + ') test=(' +
251 testData[offset + 0] + ',' +
252 testData[offset + 1] + ',' +
253 testData[offset + 2] + ',' +
254 testData[offset + 3] + ') tolerance=' + tolerance;
255 consoleDiv.appendChild(document.createTextNode(detail));
256 consoleDiv.appendChild(document.createElement('br'));
258 firstFailure = ": " + detail;
260 imgData.data[imgOffset] = 255;
268 ctx.putImageData(imgData, 0, 0);
269 diffImg = wtu.makeImageFromCanvas(canvas2d);
272 var div = document.createElement("div");
273 div.className = "testimages";
274 wtu.insertImage(div, "ref", refImg);
275 wtu.insertImage(div, "test", testImg);
277 wtu.insertImage(div, "diff", diffImg);
279 div.appendChild(document.createElement('br'));
281 consoleDiv.appendChild(div);
284 testFailed("images are different" + (firstFailure ? firstFailure : ""));
286 testPassed("images are the same");
289 consoleDiv.appendChild(document.createElement('hr'));
292 var runFeatureTest = function(params) {
293 var wtu = WebGLTestUtils;
294 var gridRes = params.gridRes;
295 var vertexTolerance = params.tolerance || 0;
296 var fragmentTolerance = params.tolerance || 1;
297 if ('fragmentTolerance' in params)
298 fragmentTolerance = params.fragmentTolerance;
300 description("Testing GLSL feature: " + params.feature);
305 var consoleDiv = document.getElementById("console");
306 var canvas = document.createElement('canvas');
307 canvas.width = width;
308 canvas.height = height;
309 var gl = wtu.create3DContext(canvas, { premultipliedAlpha: false });
311 testFailed("context does not exist");
316 var canvas2d = document.createElement('canvas');
317 canvas2d.width = width;
318 canvas2d.height = height;
319 var ctx = canvas2d.getContext("2d");
320 var imgData = ctx.getImageData(0, 0, width, height);
326 vertexShaderTemplate: vertexShaderTemplate,
327 fragmentShaderTemplate: baseFragmentShader,
328 tolerance: vertexTolerance
332 output: "gl_FragColor",
333 vertexShaderTemplate: baseVertexShader,
334 fragmentShaderTemplate: fragmentShaderTemplate,
335 tolerance: fragmentTolerance
338 for (var ss = 0; ss < shaderInfos.length; ++ss) {
339 var shaderInfo = shaderInfos[ss];
340 var tests = params.tests;
341 var testTypes = params.emuFuncs || (params.bvecTest ? bvecTypes : types);
342 // Test vertex shaders
343 for (var ii = 0; ii < tests.length; ++ii) {
344 var type = testTypes[ii];
345 if (params.simpleEmu) {
348 code: params.simpleEmu
352 var str = replaceParams(params.testFunc, {
353 func: params.feature,
357 var passMsg = "Testing: " + str + " in " + shaderInfo.type + " shader";
360 var referenceVertexShaderSource = generateReferenceShader(
362 shaderInfo.vertexShaderTemplate,
366 var referenceFragmentShaderSource = generateReferenceShader(
368 shaderInfo.fragmentShaderTemplate,
372 var testVertexShaderSource = generateTestShader(
374 shaderInfo.vertexShaderTemplate,
377 var testFragmentShaderSource = generateTestShader(
379 shaderInfo.fragmentShaderTemplate,
385 var referenceVertexShader = wtu.loadShader(gl, referenceVertexShaderSource, gl.VERTEX_SHADER, testFailed, true, 'reference');
386 var referenceFragmentShader = wtu.loadShader(gl, referenceFragmentShaderSource, gl.FRAGMENT_SHADER, testFailed, true, 'reference');
387 var testVertexShader = wtu.loadShader(gl, testVertexShaderSource, gl.VERTEX_SHADER, testFailed, true, 'test');
388 var testFragmentShader = wtu.loadShader(gl, testFragmentShaderSource, gl.FRAGMENT_SHADER, testFailed, true, 'test');
391 if (parseInt(wtu.getUrlOptions().dumpShaders)) {
393 shader: referenceVertexShader,
395 label: "reference vertex shader",
396 source: referenceVertexShaderSource
399 shader: referenceFragmentShader,
401 label: "reference fragment shader",
402 source: referenceFragmentShaderSource
404 wtu.dumpShadersInfo(gl, window.location.pathname, passMsg, vRefInfo, fRefInfo);
407 shader: testVertexShader,
409 label: "test vertex shader",
410 source: testVertexShaderSource
413 shader: testFragmentShader,
415 label: "test fragment shader",
416 source: testFragmentShaderSource
418 wtu.dumpShadersInfo(gl, window.location.pathname, passMsg, vTestInfo, fTestInfo);
422 referenceVertexShader, referenceFragmentShader);
423 var refImg = wtu.makeImageFromCanvas(canvas);
426 testVertexShader, referenceFragmentShader);
429 referenceVertexShader, testFragmentShader);
431 var testImg = wtu.makeImageFromCanvas(canvas);
433 _reportResults(refData, refImg, testData, testImg, shaderInfo.tolerance,
434 width, height, ctx, imgData, wtu, canvas2d, consoleDiv);
440 function draw(vertexShader, fragmentShader) {
441 var program = wtu.createProgram(gl, vertexShader, fragmentShader, testFailed);
443 var posLoc = gl.getAttribLocation(program, "aPosition");
444 wtu.setupIndexedQuad(gl, gridRes, posLoc);
446 gl.useProgram(program);
447 wtu.clearAndDrawIndexedQuad(gl, gridRes, [0, 0, 255, 255]);
448 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no errors from draw");
450 var img = new Uint8Array(width * height * 4);
451 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, img);
457 var runBasicTest = function(params) {
458 var wtu = WebGLTestUtils;
459 var gridRes = params.gridRes;
460 var vertexTolerance = params.tolerance || 0;
461 var fragmentTolerance = vertexTolerance;
462 if ('fragmentTolerance' in params)
463 fragmentTolerance = params.fragmentTolerance || 0;
465 description("Testing : " + document.getElementsByTagName("title")[0].innerText);
470 var consoleDiv = document.getElementById("console");
471 var canvas = document.createElement('canvas');
472 canvas.width = width;
473 canvas.height = height;
474 var gl = wtu.create3DContext(canvas);
476 testFailed("context does not exist");
481 var canvas2d = document.createElement('canvas');
482 canvas2d.width = width;
483 canvas2d.height = height;
484 var ctx = canvas2d.getContext("2d");
485 var imgData = ctx.getImageData(0, 0, width, height);
491 vertexShaderTemplate: vertexShaderTemplate,
492 fragmentShaderTemplate: baseFragmentShader,
493 tolerance: vertexTolerance
497 output: "gl_FragColor",
498 vertexShaderTemplate: baseVertexShader,
499 fragmentShaderTemplate: fragmentShaderTemplate,
500 tolerance: fragmentTolerance
503 for (var ss = 0; ss < shaderInfos.length; ++ss) {
504 var shaderInfo = shaderInfos[ss];
505 var tests = params.tests;
506 // var testTypes = params.emuFuncs || (params.bvecTest ? bvecTypes : types);
507 // Test vertex shaders
508 for (var ii = 0; ii < tests.length; ++ii) {
509 var test = tests[ii];
511 var passMsg = "Testing: " + test.name + " in " + shaderInfo.type + " shader";
514 function genShader(shaderInfo, template, shader, subs) {
515 shader = replaceParams(shader, subs, {
516 input: shaderInfo.input,
517 output: shaderInfo.output
519 shader = replaceParams(template, subs, {
527 var referenceVertexShaderSource = genShader(
529 shaderInfo.vertexShaderTemplate,
530 test.reference.shader,
531 test.reference.subs);
532 var referenceFragmentShaderSource = genShader(
534 shaderInfo.fragmentShaderTemplate,
535 test.reference.shader,
536 test.reference.subs);
537 var testVertexShaderSource = genShader(
539 shaderInfo.vertexShaderTemplate,
542 var testFragmentShaderSource = genShader(
544 shaderInfo.fragmentShaderTemplate,
549 var referenceVertexShader = wtu.loadShader(gl, referenceVertexShaderSource, gl.VERTEX_SHADER, testFailed, true, 'reference');
550 var referenceFragmentShader = wtu.loadShader(gl, referenceFragmentShaderSource, gl.FRAGMENT_SHADER, testFailed, true, 'reference');
551 var testVertexShader = wtu.loadShader(gl, testVertexShaderSource, gl.VERTEX_SHADER, testFailed, true, 'test');
552 var testFragmentShader = wtu.loadShader(gl, testFragmentShaderSource, gl.FRAGMENT_SHADER, testFailed, true, 'test');
555 if (parseInt(wtu.getUrlOptions().dumpShaders)) {
557 shader: referenceVertexShader,
559 label: "reference vertex shader",
560 source: referenceVertexShaderSource
563 shader: referenceFragmentShader,
565 label: "reference fragment shader",
566 source: referenceFragmentShaderSource
568 wtu.dumpShadersInfo(gl, window.location.pathname, passMsg, vRefInfo, fRefInfo);
571 shader: testVertexShader,
573 label: "test vertex shader",
574 source: testVertexShaderSource
577 shader: testFragmentShader,
579 label: "test fragment shader",
580 source: testFragmentShaderSource
582 wtu.dumpShadersInfo(gl, window.location.pathname, passMsg, vTestInfo, fTestInfo);
585 var refData = draw(referenceVertexShader, referenceFragmentShader);
586 var refImg = wtu.makeImageFromCanvas(canvas);
588 var testData = draw(testVertexShader, referenceFragmentShader);
590 var testData = draw(referenceVertexShader, testFragmentShader);
592 var testImg = wtu.makeImageFromCanvas(canvas);
594 _reportResults(refData, refImg, testData, testImg, shaderInfo.tolerance,
595 width, height, ctx, imgData, wtu, canvas2d, consoleDiv);
601 function draw(vertexShader, fragmentShader) {
602 var program = wtu.createProgram(gl, vertexShader, fragmentShader, testFailed);
604 var posLoc = gl.getAttribLocation(program, "aPosition");
605 wtu.setupIndexedQuad(gl, gridRes, posLoc);
607 gl.useProgram(program);
608 wtu.clearAndDrawIndexedQuad(gl, gridRes, [0, 0, 255, 255]);
609 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no errors from draw");
611 var img = new Uint8Array(width * height * 4);
612 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, img);
618 var runReferenceImageTest = function(params) {
619 var wtu = WebGLTestUtils;
620 var gridRes = params.gridRes;
621 var vertexTolerance = params.tolerance || 0;
622 var fragmentTolerance = vertexTolerance;
623 if ('fragmentTolerance' in params)
624 fragmentTolerance = params.fragmentTolerance || 0;
626 description("Testing GLSL feature: " + params.feature);
631 var consoleDiv = document.getElementById("console");
632 var canvas = document.createElement('canvas');
633 canvas.width = width;
634 canvas.height = height;
635 var gl = wtu.create3DContext(canvas, { antialias: false, premultipliedAlpha: false });
637 testFailed("context does not exist");
642 var canvas2d = document.createElement('canvas');
643 canvas2d.width = width;
644 canvas2d.height = height;
645 var ctx = canvas2d.getContext("2d");
646 var imgData = ctx.getImageData(0, 0, width, height);
648 // State for reference images for vertex shader tests.
649 // These are drawn with the same tessellated grid as the test vertex
650 // shader so that the interpolation is identical. The grid is reused
651 // from test to test; the colors are changed.
653 var indexedQuadForReferenceVertexShader =
654 wtu.setupIndexedQuad(gl, gridRes, 0);
655 var referenceVertexShaderProgram =
656 wtu.setupProgram(gl, [ baseVertexShaderWithColor, baseFragmentShader ],
657 ["aPosition", "aColor"]);
658 var referenceVertexShaderColorBuffer = gl.createBuffer();
664 vertexShaderTemplate: vertexShaderTemplate,
665 fragmentShaderTemplate: baseFragmentShader,
666 tolerance: vertexTolerance
670 output: "gl_FragColor",
671 vertexShaderTemplate: baseVertexShader,
672 fragmentShaderTemplate: fragmentShaderTemplate,
673 tolerance: fragmentTolerance
676 for (var ss = 0; ss < shaderInfos.length; ++ss) {
677 var shaderInfo = shaderInfos[ss];
678 var tests = params.tests;
679 var testTypes = params.emuFuncs || (params.bvecTest ? bvecTypes : types);
680 // Test vertex shaders
681 for (var ii = 0; ii < tests.length; ++ii) {
682 var type = testTypes[ii];
683 var isVertex = (ss == 0);
685 var str = replaceParams(params.testFunc, {
686 func: params.feature,
690 var passMsg = "Testing: " + str + " in " + shaderInfo.type + " shader";
693 var referenceVertexShaderSource = generateReferenceShader(
695 shaderInfo.vertexShaderTemplate,
699 var referenceFragmentShaderSource = generateReferenceShader(
701 shaderInfo.fragmentShaderTemplate,
705 var testVertexShaderSource = generateTestShader(
707 shaderInfo.vertexShaderTemplate,
710 var testFragmentShaderSource = generateTestShader(
712 shaderInfo.fragmentShaderTemplate,
715 var referenceTextureOrArray = generateReferenceImage(
718 isVertex ? gridRes : width,
719 isVertex ? gridRes : height,
723 var testVertexShader = wtu.loadShader(gl, testVertexShaderSource, gl.VERTEX_SHADER, testFailed, true);
724 var testFragmentShader = wtu.loadShader(gl, testFragmentShaderSource, gl.FRAGMENT_SHADER, testFailed, true);
728 if (parseInt(wtu.getUrlOptions().dumpShaders)) {
730 shader: referenceVertexShader,
732 label: "reference vertex shader",
733 source: referenceVertexShaderSource
736 shader: referenceFragmentShader,
738 label: "reference fragment shader",
739 source: referenceFragmentShaderSource
741 wtu.dumpShadersInfo(gl, window.location.pathname, passMsg, vRefInfo, fRefInfo);
744 shader: testVertexShader,
746 label: "test vertex shader",
747 source: testVertexShaderSource
750 shader: testFragmentShader,
752 label: "test fragment shader",
753 source: testFragmentShaderSource
755 wtu.dumpShadersInfo(gl, window.location.pathname, passMsg, vTestInfo, fTestInfo);
760 refData = drawVertexReferenceImage(referenceTextureOrArray);
762 refData = drawFragmentReferenceImage(referenceTextureOrArray);
764 var refImg = wtu.makeImageFromCanvas(canvas);
767 var referenceFragmentShader = wtu.loadShader(gl, referenceFragmentShaderSource, gl.FRAGMENT_SHADER, testFailed);
769 testVertexShader, referenceFragmentShader);
771 var referenceVertexShader = wtu.loadShader(gl, referenceVertexShaderSource, gl.VERTEX_SHADER, testFailed);
773 referenceVertexShader, testFragmentShader);
775 var testImg = wtu.makeImageFromCanvas(canvas);
776 var testTolerance = shaderInfo.tolerance;
777 // Provide per-test tolerance so that we can increase it only for those desired.
778 if ('tolerance' in tests[ii])
779 testTolerance = tests[ii].tolerance || 0;
780 _reportResults(refData, refImg, testData, testImg, testTolerance,
781 width, height, ctx, imgData, wtu, canvas2d, consoleDiv);
787 function draw(vertexShader, fragmentShader) {
788 var program = wtu.createProgram(gl, vertexShader, fragmentShader, testFailed);
790 var posLoc = gl.getAttribLocation(program, "aPosition");
791 wtu.setupIndexedQuad(gl, gridRes, posLoc);
793 gl.useProgram(program);
794 wtu.clearAndDrawIndexedQuad(gl, gridRes, [0, 0, 255, 255]);
795 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no errors from draw");
797 var img = new Uint8Array(width * height * 4);
798 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, img);
802 function drawVertexReferenceImage(colors) {
803 gl.bindBuffer(gl.ARRAY_BUFFER, indexedQuadForReferenceVertexShader[0]);
804 gl.enableVertexAttribArray(0);
805 gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);
806 gl.bindBuffer(gl.ARRAY_BUFFER, referenceVertexShaderColorBuffer);
807 gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW);
808 gl.enableVertexAttribArray(1);
809 gl.vertexAttribPointer(1, 4, gl.UNSIGNED_BYTE, true, 0, 0);
810 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexedQuadForReferenceVertexShader[1]);
811 gl.useProgram(referenceVertexShaderProgram);
812 wtu.clearAndDrawIndexedQuad(gl, gridRes);
813 gl.disableVertexAttribArray(0);
814 gl.disableVertexAttribArray(1);
815 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no errors from draw");
817 var img = new Uint8Array(width * height * 4);
818 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, img);
822 function drawFragmentReferenceImage(texture) {
823 var program = wtu.setupTexturedQuad(gl);
825 gl.activeTexture(gl.TEXTURE0);
826 gl.bindTexture(gl.TEXTURE_2D, texture);
827 var texLoc = gl.getUniformLocation(program, "tex");
828 gl.uniform1i(texLoc, 0);
829 wtu.clearAndDrawUnitQuad(gl);
830 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no errors from draw");
832 var img = new Uint8Array(width * height * 4);
833 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, img);
838 * Creates and returns either a Uint8Array (for vertex shaders) or
839 * WebGLTexture (for fragment shaders) containing the reference
840 * image for the function being tested. Exactly how the function is
841 * evaluated, and the size of the returned texture or array, depends on
842 * whether we are testing a vertex or fragment shader. If a fragment
843 * shader, the function is evaluated at the pixel centers. If a
844 * vertex shader, the function is evaluated at the triangle's
847 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use to generate texture objects.
848 * @param {!function(number,number,number,number): !Array.<number>} generator The reference image generator function.
849 * @param {number} width The width of the texture to generate if testing a fragment shader; the grid resolution if testing a vertex shader.
850 * @param {number} height The height of the texture to generate if testing a fragment shader; the grid resolution if testing a vertex shader.
851 * @param {boolean} isVertex True if generating a reference image for a vertex shader; false if for a fragment shader.
852 * @return {!WebGLTexture|!Uint8Array} The texture object or array that was generated.
854 function generateReferenceImage(
861 // Note: the math in this function must match that in the vertex and
862 // fragment shader templates above.
863 function computeTexCoord(x) {
864 return x * 0.5 + 0.5;
867 function computeVertexColor(texCoordX, texCoordY) {
870 texCoordX * texCoordY,
871 (1.0 - texCoordX) * texCoordY * 0.5 + 0.5 ];
875 * Computes fragment color according to the algorithm used for interpolation
876 * in OpenGL (GLES 2.0 spec 3.5.1, OpenGL 4.3 spec 14.6.1).
878 function computeInterpolatedColor(texCoordX, texCoordY) {
879 // Calculate grid line indexes below and to the left from texCoord.
880 var gridBottom = Math.floor(texCoordY * gridRes);
881 if (gridBottom == gridRes) {
884 var gridLeft = Math.floor(texCoordX * gridRes);
885 if (gridLeft == gridRes) {
889 // Calculate coordinates relative to the grid cell.
890 var cellX = texCoordX * gridRes - gridLeft;
891 var cellY = texCoordY * gridRes - gridBottom;
893 // Barycentric coordinates inside either triangle ACD or ABC
894 // are used as weights for the vertex colors in the corners:
900 var aColor = computeVertexColor(gridLeft / gridRes, (gridBottom + 1) / gridRes);
901 var bColor = computeVertexColor((gridLeft + 1) / gridRes, (gridBottom + 1) / gridRes);
902 var cColor = computeVertexColor((gridLeft + 1) / gridRes, gridBottom / gridRes);
903 var dColor = computeVertexColor(gridLeft / gridRes, gridBottom / gridRes);
905 // Calculate weights.
908 if (cellX + cellY < 1) {
909 // In bottom triangle ACD.
910 a = cellY; // area of triangle C-D-(cellX, cellY) relative to ACD
911 c = cellX; // area of triangle D-A-(cellX, cellY) relative to ACD
915 // In top triangle ABC.
916 a = 1 - cellX; // area of the triangle B-C-(cellX, cellY) relative to ABC
917 c = 1 - cellY; // area of the triangle A-B-(cellX, cellY) relative to ABC
922 var interpolated = [];
923 for (var ii = 0; ii < aColor.length; ++ii) {
924 interpolated.push(a * aColor[ii] + b * bColor[ii] + c * cColor[ii] + d * dColor[ii]);
929 function clamp(value, minVal, maxVal) {
930 return Math.max(minVal, Math.min(value, maxVal));
933 // Evaluates the function at clip coordinates (px,py), storing the
934 // result in the array "pixel". Each channel's result is clamped
935 // between 0 and 255.
936 function evaluateAtClipCoords(px, py, pixel, colorFunc) {
937 var tcx = computeTexCoord(px);
938 var tcy = computeTexCoord(py);
940 var color = colorFunc(tcx, tcy);
942 var output = generator(color[0], color[1], color[2], color[3]);
944 // Multiply by 256 to get even distribution for all values between 0 and 1.
945 // Use rounding rather than truncation to more closely match the GPU's behavior.
946 pixel[0] = clamp(Math.round(256 * output[0]), 0, 255);
947 pixel[1] = clamp(Math.round(256 * output[1]), 0, 255);
948 pixel[2] = clamp(Math.round(256 * output[2]), 0, 255);
949 pixel[3] = clamp(Math.round(256 * output[3]), 0, 255);
952 function generateFragmentReference() {
953 var data = new Uint8Array(4 * width * height);
955 var horizTexel = 1.0 / width;
956 var vertTexel = 1.0 / height;
957 var halfHorizTexel = 0.5 * horizTexel;
958 var halfVertTexel = 0.5 * vertTexel;
960 var pixel = new Array(4);
962 for (var yi = 0; yi < height; ++yi) {
963 for (var xi = 0; xi < width; ++xi) {
964 // The function must be evaluated at pixel centers.
966 // Compute desired position in clip space
967 var px = -1.0 + 2.0 * (halfHorizTexel + xi * horizTexel);
968 var py = -1.0 + 2.0 * (halfVertTexel + yi * vertTexel);
970 evaluateAtClipCoords(px, py, pixel, computeInterpolatedColor);
971 var index = 4 * (width * yi + xi);
972 data[index + 0] = pixel[0];
973 data[index + 1] = pixel[1];
974 data[index + 2] = pixel[2];
975 data[index + 3] = pixel[3];
979 var texture = gl.createTexture();
980 gl.bindTexture(gl.TEXTURE_2D, texture);
981 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
982 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
983 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
984 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
985 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0,
986 gl.RGBA, gl.UNSIGNED_BYTE, data);
990 function generateVertexReference() {
991 // We generate a Uint8Array which contains the evaluation of the
992 // function at the vertices of the triangle mesh. It is expected
993 // that the width and the height are identical, and equivalent
994 // to the grid resolution.
995 if (width != height) {
996 throw "width and height must be equal";
999 var texSize = 1 + width;
1000 var data = new Uint8Array(4 * texSize * texSize);
1002 var step = 2.0 / width;
1004 var pixel = new Array(4);
1006 for (var yi = 0; yi < texSize; ++yi) {
1007 for (var xi = 0; xi < texSize; ++xi) {
1008 // The function is evaluated at the triangles' vertices.
1010 // Compute desired position in clip space
1011 var px = -1.0 + (xi * step);
1012 var py = -1.0 + (yi * step);
1014 evaluateAtClipCoords(px, py, pixel, computeVertexColor);
1015 var index = 4 * (texSize * yi + xi);
1016 data[index + 0] = pixel[0];
1017 data[index + 1] = pixel[1];
1018 data[index + 2] = pixel[2];
1019 data[index + 3] = pixel[3];
1026 //----------------------------------------------------------------------
1027 // Body of generateReferenceImage
1031 return generateVertexReference();
1033 return generateFragmentReference();
1040 * runs a bunch of GLSL tests using the passed in parameters
1041 * The parameters are:
1044 * the name of the function being tested (eg, sin, dot,
1048 * The prototype of function to be tested not including the
1052 * A base function that can be used to generate emulation
1053 * functions. Example for 'ceil'
1055 * float $(func)_base(float value) {
1056 * float m = mod(value, 1.0);
1057 * return m != 0.0 ? (value + 1.0 - m) : value;
1061 * The arguments to the function
1063 * baseArgs: (optional)
1064 * The arguments when a base function is used to create an
1065 * emulation function. For example 'float sign_base(float v)'
1066 * is used to implemenent vec2 sign_emu(vec2 v).
1069 * if supplied, the code that can be used to generate all
1070 * functions for all types.
1072 * Example for 'normalize':
1074 * $(type) $(func)_emu($(args)) {
1075 * return value / length(value);
1078 * gridRes: (optional)
1079 * The resolution of the mesh to generate. The default is a
1080 * 1x1 grid but many vertex shaders need a higher resolution
1081 * otherwise the only values passed in are the 4 corners
1082 * which often have the same value.
1085 * The code for each test. It is assumed the tests are for
1086 * float, vec2, vec3, vec4 in that order.
1088 * tolerance: (optional)
1089 * Allow some tolerance in the comparisons. The tolerance is applied to
1090 * both vertex and fragment shaders. The default tolerance is 0, meaning
1091 * the values have to be identical.
1093 * fragmentTolerance: (optional)
1094 * Specify a tolerance which only applies to fragment shaders. The
1095 * fragment-only tolerance will override the shared tolerance for
1096 * fragment shaders if both are specified. Fragment shaders usually
1097 * use mediump float precision so they sometimes require higher tolerance
1098 * than vertex shaders which use highp by default.
1100 runFeatureTest: runFeatureTest,
1103 * Runs a bunch of GLSL tests using the passed in parameters
1105 * The parameters are:
1108 * Array of tests. For each test the following parameters are expected
1111 * some description of the test
1113 * parameters for the reference shader (see below)
1115 * parameters for the test shader (see below)
1117 * The parameter for the reference and test shaders are
1119 * shader: the GLSL for the shader
1120 * subs: any substitutions you wish to define for the shader.
1122 * Each shader is created from a basic template that
1123 * defines an input and an output. You can see the
1124 * templates at the top of this file. The input and output
1125 * change depending on whether or not we are generating
1126 * a vertex or fragment shader.
1128 * All this code function does is a bunch of string substitutions.
1129 * A substitution is defined by $(name). If name is found in
1130 * the 'subs' parameter it is replaced. 4 special names exist.
1132 * 'input' the input to your GLSL. Always a vec4. All change
1133 * from 0 to 1 over the quad to be drawn.
1135 * 'output' the output color. Also a vec4
1137 * 'emu' a place to insert extra stuff
1138 * 'extra' a place to insert extra stuff.
1140 * You can think of the templates like this
1146 * // do math to calculate input
1152 * Your shader first has any subs you provided applied as well
1153 * as 'input' and 'output'
1155 * It is then inserted into the template which is also provided
1158 * gridRes: (optional)
1159 * The resolution of the mesh to generate. The default is a
1160 * 1x1 grid but many vertex shaders need a higher resolution
1161 * otherwise the only values passed in are the 4 corners
1162 * which often have the same value.
1164 * tolerance: (optional)
1165 * Allow some tolerance in the comparisons. The tolerance is applied to
1166 * both vertex and fragment shaders. The default tolerance is 0, meaning
1167 * the values have to be identical.
1169 * fragmentTolerance: (optional)
1170 * Specify a tolerance which only applies to fragment shaders. The
1171 * fragment-only tolerance will override the shared tolerance for
1172 * fragment shaders if both are specified. Fragment shaders usually
1173 * use mediump float precision so they sometimes require higher tolerance
1174 * than vertex shaders which use highp.
1176 runBasicTest: runBasicTest,
1179 * Runs a bunch of GLSL tests using the passed in parameters. The
1180 * expected results are computed as a reference image in JavaScript
1181 * instead of on the GPU. The parameters are:
1184 * the name of the function being tested (eg, sin, dot,
1188 * The prototype of function to be tested not including the
1192 * The arguments to the function
1194 * gridRes: (optional)
1195 * The resolution of the mesh to generate. The default is a
1196 * 1x1 grid but many vertex shaders need a higher resolution
1197 * otherwise the only values passed in are the 4 corners
1198 * which often have the same value.
1201 * Array of tests. It is assumed the tests are for float, vec2,
1202 * vec3, vec4 in that order. For each test the following
1203 * parameters are expected:
1205 * source: the GLSL source code for the tests
1207 * generator: a JavaScript function taking four parameters
1208 * which evaluates the same function as the GLSL source,
1209 * returning its result as a newly allocated array.
1211 * tolerance: (optional) a per-test tolerance.
1214 * Extra GLSL code inserted at the top of each test's shader.
1216 * tolerance: (optional)
1217 * Allow some tolerance in the comparisons. The tolerance is applied to
1218 * both vertex and fragment shaders. The default tolerance is 0, meaning
1219 * the values have to be identical.
1221 * fragmentTolerance: (optional)
1222 * Specify a tolerance which only applies to fragment shaders. The
1223 * fragment-only tolerance will override the shared tolerance for
1224 * fragment shaders if both are specified. Fragment shaders usually
1225 * use mediump float precision so they sometimes require higher tolerance
1226 * than vertex shaders which use highp.
1228 runReferenceImageTest: runReferenceImageTest,