Bug 1932347 - Adjust positioning of tab preview for vertical tabs r=tabbrowser-review...
[gecko.git] / dom / canvas / test / webgl-conf / checkout / js / glsl-generator.js
blobd0b65bcb4b87015cb114b9fb269320c7ea585a03
1 /*
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.
5 */
6 GLSLGenerator = (function() {
8 var vertexShaderTemplate = [
9   "attribute vec4 aPosition;",
10   "",
11   "varying vec4 vColor;",
12   "",
13   "$(extra)",
14   "$(emu)",
15   "",
16   "void main()",
17   "{",
18   "   gl_Position = aPosition;",
19   "   vec2 texcoord = vec2(aPosition.xy * 0.5 + vec2(0.5, 0.5));",
20   "   vec4 color = vec4(",
21   "       texcoord,",
22   "       texcoord.x * texcoord.y,",
23   "       (1.0 - texcoord.x) * texcoord.y * 0.5 + 0.5);",
24   "   $(test)",
25   "}"
26 ].join("\n");
28 var fragmentShaderTemplate = [
29   "precision mediump float;",
30   "",
31   "varying vec4 vColor;",
32   "",
33   "$(extra)",
34   "$(emu)",
35   "",
36   "void main()",
37   "{",
38   "   $(test)",
39   "}"
40 ].join("\n");
42 var baseVertexShader = [
43   "attribute vec4 aPosition;",
44   "",
45   "varying vec4 vColor;",
46   "",
47   "void main()",
48   "{",
49   "   gl_Position = aPosition;",
50   "   vec2 texcoord = vec2(aPosition.xy * 0.5 + vec2(0.5, 0.5));",
51   "   vColor = vec4(",
52   "       texcoord,",
53   "       texcoord.x * texcoord.y,",
54   "       (1.0 - texcoord.x) * texcoord.y * 0.5 + 0.5);",
55   "}"
56 ].join("\n");
58 var baseVertexShaderWithColor = [
59   "attribute vec4 aPosition;",
60   "attribute vec4 aColor;",
61   "",
62   "varying vec4 vColor;",
63   "",
64   "void main()",
65   "{",
66   "   gl_Position = aPosition;",
67   "   vColor = aColor;",
68   "}"
69 ].join("\n");
71 var baseFragmentShader = [
72   "precision mediump float;",
73   "varying vec4 vColor;",
74   "",
75   "void main()",
76   "{",
77   "   gl_FragColor = vColor;",
78   "}"
79 ].join("\n");
81 var types = [
82   { type: "float",
83     code: [
84       "float $(func)_emu($(args)) {",
85       "  return $(func)_base($(baseArgs));",
86       "}"].join("\n")
87   },
88   { type: "vec2",
89     code: [
90       "vec2 $(func)_emu($(args)) {",
91       "  return vec2(",
92       "      $(func)_base($(baseArgsX)),",
93       "      $(func)_base($(baseArgsY)));",
94       "}"].join("\n")
95   },
96   { type: "vec3",
97     code: [
98       "vec3 $(func)_emu($(args)) {",
99       "  return vec3(",
100       "      $(func)_base($(baseArgsX)),",
101       "      $(func)_base($(baseArgsY)),",
102       "      $(func)_base($(baseArgsZ)));",
103       "}"].join("\n")
104   },
105   { type: "vec4",
106     code: [
107       "vec4 $(func)_emu($(args)) {",
108       "  return vec4(",
109       "      $(func)_base($(baseArgsX)),",
110       "      $(func)_base($(baseArgsY)),",
111       "      $(func)_base($(baseArgsZ)),",
112       "      $(func)_base($(baseArgsW)));",
113       "}"].join("\n")
114   }
117 var bvecTypes = [
118   { type: "bvec2",
119     code: [
120       "bvec2 $(func)_emu($(args)) {",
121       "  return bvec2(",
122       "      $(func)_base($(baseArgsX)),",
123       "      $(func)_base($(baseArgsY)));",
124       "}"].join("\n")
125   },
126   { type: "bvec3",
127     code: [
128       "bvec3 $(func)_emu($(args)) {",
129       "  return bvec3(",
130       "      $(func)_base($(baseArgsX)),",
131       "      $(func)_base($(baseArgsY)),",
132       "      $(func)_base($(baseArgsZ)));",
133       "}"].join("\n")
134   },
135   { type: "bvec4",
136     code: [
137       "vec4 $(func)_emu($(args)) {",
138       "  return bvec4(",
139       "      $(func)_base($(baseArgsX)),",
140       "      $(func)_base($(baseArgsY)),",
141       "      $(func)_base($(baseArgsZ)),",
142       "      $(func)_base($(baseArgsW)));",
143       "}"].join("\n")
144   }
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) {
154         return args[ii][p1];
155       }
156     }
157     throw "unknown string param '" + p1 + "'";
158   });
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, {
181     input: input,
182     output: output,
183     func: feature + "_emu"
184   });
185   emuFunc = replaceParams(emuFunc, {
186     func: feature
187   });
188   args = replaceParams(args, {
189     type: type
190   });
191   typeCode = replaceParams(typeCode, {
192     func: feature,
193     type: type,
194     args: args,
195     baseArgs: baseArgs,
196     baseArgsX: baseArgsX,
197     baseArgsY: baseArgsY,
198     baseArgsZ: baseArgsZ,
199     baseArgsW: baseArgsW
200   });
201   var shader = replaceParams(template, {
202     extra: extra,
203     emu: emuFunc + "\n\n" + typeCode,
204     test: test
205   });
206   return shader;
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, {
218     input: input,
219     output: output,
220     func: feature
221   });
222   var shader = replaceParams(template, {
223     extra: extra,
224     emu: '',
225     test: test
226   });
227   return shader;
230 function _reportResults(refData, refImg, testData, testImg, tolerance,
231                         width, height, ctx, imgData, wtu, canvas2d, consoleDiv) {
232   var same = true;
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'));
257         if (!firstFailure) {
258           firstFailure = ": " + detail;
259         }
260         imgData.data[imgOffset] = 255;
261         same = false;
262       }
263     }
264   }
266   var diffImg = null;
267   if (!same) {
268     ctx.putImageData(imgData, 0, 0);
269     diffImg = wtu.makeImageFromCanvas(canvas2d);
270   }
272   var div = document.createElement("div");
273   div.className = "testimages";
274   wtu.insertImage(div, "ref", refImg);
275   wtu.insertImage(div, "test", testImg);
276   if (diffImg) {
277     wtu.insertImage(div, "diff", diffImg);
278   }
279   div.appendChild(document.createElement('br'));
281   consoleDiv.appendChild(div);
283   if (!same) {
284     testFailed("images are different" + (firstFailure ? firstFailure : ""));
285   } else {
286     testPassed("images are the same");
287   }
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);
302   var width = 32;
303   var height = 32;
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 });
310   if (!gl) {
311     testFailed("context does not exist");
312     finishTest();
313     return;
314   }
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);
322   var shaderInfos = [
323     { type: "vertex",
324       input: "color",
325       output: "vColor",
326       vertexShaderTemplate: vertexShaderTemplate,
327       fragmentShaderTemplate: baseFragmentShader,
328       tolerance: vertexTolerance
329     },
330     { type: "fragment",
331       input: "vColor",
332       output: "gl_FragColor",
333       vertexShaderTemplate: baseVertexShader,
334       fragmentShaderTemplate: fragmentShaderTemplate,
335       tolerance: fragmentTolerance
336     }
337   ];
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) {
346         type = {
347           type: type.type,
348           code: params.simpleEmu
349         };
350       }
351       debug("");
352       var str = replaceParams(params.testFunc, {
353         func: params.feature,
354         type: type.type,
355         arg0: type.type
356       });
357       var passMsg = "Testing: " + str + " in " + shaderInfo.type + " shader";
358       debug(passMsg);
360       var referenceVertexShaderSource = generateReferenceShader(
361           shaderInfo,
362           shaderInfo.vertexShaderTemplate,
363           params,
364           type,
365           tests[ii]);
366       var referenceFragmentShaderSource = generateReferenceShader(
367           shaderInfo,
368           shaderInfo.fragmentShaderTemplate,
369           params,
370           type,
371           tests[ii]);
372       var testVertexShaderSource = generateTestShader(
373           shaderInfo,
374           shaderInfo.vertexShaderTemplate,
375           params,
376           tests[ii]);
377       var testFragmentShaderSource = generateTestShader(
378           shaderInfo,
379           shaderInfo.fragmentShaderTemplate,
380           params,
381           tests[ii]);
384       debug("");
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');
389       debug("");
391       if (parseInt(wtu.getUrlOptions().dumpShaders)) {
392         var vRefInfo = {
393           shader: referenceVertexShader,
394           shaderSuccess: true,
395           label: "reference vertex shader",
396           source: referenceVertexShaderSource
397         };
398         var fRefInfo = {
399           shader: referenceFragmentShader,
400           shaderSuccess: true,
401           label: "reference fragment shader",
402           source: referenceFragmentShaderSource
403         };
404         wtu.dumpShadersInfo(gl, window.location.pathname, passMsg, vRefInfo, fRefInfo);
406         var vTestInfo = {
407           shader: testVertexShader,
408           shaderSuccess: true,
409           label: "test vertex shader",
410           source: testVertexShaderSource
411         };
412         var fTestInfo = {
413           shader: testFragmentShader,
414           shaderSuccess: true,
415           label: "test fragment shader",
416           source: testFragmentShaderSource
417         };
418         wtu.dumpShadersInfo(gl, window.location.pathname, passMsg, vTestInfo, fTestInfo);
419       }
421       var refData = draw(
422           referenceVertexShader, referenceFragmentShader);
423       var refImg = wtu.makeImageFromCanvas(canvas);
424       if (ss == 0) {
425         var testData = draw(
426             testVertexShader, referenceFragmentShader);
427       } else {
428         var testData = draw(
429             referenceVertexShader, testFragmentShader);
430       }
431       var testImg = wtu.makeImageFromCanvas(canvas);
433       _reportResults(refData, refImg, testData, testImg, shaderInfo.tolerance,
434                      width, height, ctx, imgData, wtu, canvas2d, consoleDiv);
435     }
436   }
438   finishTest();
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);
452     return img;
453   }
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);
467   var width = 32;
468   var height = 32;
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);
475   if (!gl) {
476     testFailed("context does not exist");
477     finishTest();
478     return;
479   }
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);
487   var shaderInfos = [
488     { type: "vertex",
489       input: "color",
490       output: "vColor",
491       vertexShaderTemplate: vertexShaderTemplate,
492       fragmentShaderTemplate: baseFragmentShader,
493       tolerance: vertexTolerance
494     },
495     { type: "fragment",
496       input: "vColor",
497       output: "gl_FragColor",
498       vertexShaderTemplate: baseVertexShader,
499       fragmentShaderTemplate: fragmentShaderTemplate,
500       tolerance: fragmentTolerance
501     }
502   ];
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];
510       debug("");
511       var passMsg = "Testing: " + test.name + " in " + shaderInfo.type + " shader";
512       debug(passMsg);
514       function genShader(shaderInfo, template, shader, subs) {
515         shader = replaceParams(shader, subs, {
516             input: shaderInfo.input,
517             output: shaderInfo.output
518           });
519         shader = replaceParams(template, subs, {
520             test: shader,
521             emu: "",
522             extra: ""
523           });
524         return shader;
525       }
527       var referenceVertexShaderSource = genShader(
528           shaderInfo,
529           shaderInfo.vertexShaderTemplate,
530           test.reference.shader,
531           test.reference.subs);
532       var referenceFragmentShaderSource = genShader(
533           shaderInfo,
534           shaderInfo.fragmentShaderTemplate,
535           test.reference.shader,
536           test.reference.subs);
537       var testVertexShaderSource = genShader(
538           shaderInfo,
539           shaderInfo.vertexShaderTemplate,
540           test.test.shader,
541           test.test.subs);
542       var testFragmentShaderSource = genShader(
543           shaderInfo,
544           shaderInfo.fragmentShaderTemplate,
545           test.test.shader,
546           test.test.subs);
548       debug("");
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');
553       debug("");
555       if (parseInt(wtu.getUrlOptions().dumpShaders)) {
556         var vRefInfo = {
557           shader: referenceVertexShader,
558           shaderSuccess: true,
559           label: "reference vertex shader",
560           source: referenceVertexShaderSource
561         };
562         var fRefInfo = {
563           shader: referenceFragmentShader,
564           shaderSuccess: true,
565           label: "reference fragment shader",
566           source: referenceFragmentShaderSource
567         };
568         wtu.dumpShadersInfo(gl, window.location.pathname, passMsg, vRefInfo, fRefInfo);
570         var vTestInfo = {
571           shader: testVertexShader,
572           shaderSuccess: true,
573           label: "test vertex shader",
574           source: testVertexShaderSource
575         };
576         var fTestInfo = {
577           shader: testFragmentShader,
578           shaderSuccess: true,
579           label: "test fragment shader",
580           source: testFragmentShaderSource
581         };
582         wtu.dumpShadersInfo(gl, window.location.pathname, passMsg, vTestInfo, fTestInfo);
583       }
585       var refData = draw(referenceVertexShader, referenceFragmentShader);
586       var refImg = wtu.makeImageFromCanvas(canvas);
587       if (ss == 0) {
588         var testData = draw(testVertexShader, referenceFragmentShader);
589       } else {
590         var testData = draw(referenceVertexShader, testFragmentShader);
591       }
592       var testImg = wtu.makeImageFromCanvas(canvas);
594       _reportResults(refData, refImg, testData, testImg, shaderInfo.tolerance,
595                      width, height, ctx, imgData, wtu, canvas2d, consoleDiv);
596     }
597   }
599   finishTest();
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);
613     return img;
614   }
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);
628   var width = 32;
629   var height = 32;
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 });
636   if (!gl) {
637     testFailed("context does not exist");
638     finishTest();
639     return;
640   }
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();
660   var shaderInfos = [
661     { type: "vertex",
662       input: "color",
663       output: "vColor",
664       vertexShaderTemplate: vertexShaderTemplate,
665       fragmentShaderTemplate: baseFragmentShader,
666       tolerance: vertexTolerance
667     },
668     { type: "fragment",
669       input: "vColor",
670       output: "gl_FragColor",
671       vertexShaderTemplate: baseVertexShader,
672       fragmentShaderTemplate: fragmentShaderTemplate,
673       tolerance: fragmentTolerance
674     }
675   ];
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);
684       debug("");
685       var str = replaceParams(params.testFunc, {
686         func: params.feature,
687         type: type.type,
688         arg0: type.type
689       });
690       var passMsg = "Testing: " + str + " in " + shaderInfo.type + " shader";
691       debug(passMsg);
693       var referenceVertexShaderSource = generateReferenceShader(
694           shaderInfo,
695           shaderInfo.vertexShaderTemplate,
696           params,
697           type,
698           tests[ii].source);
699       var referenceFragmentShaderSource = generateReferenceShader(
700           shaderInfo,
701           shaderInfo.fragmentShaderTemplate,
702           params,
703           type,
704           tests[ii].source);
705       var testVertexShaderSource = generateTestShader(
706           shaderInfo,
707           shaderInfo.vertexShaderTemplate,
708           params,
709           tests[ii].source);
710       var testFragmentShaderSource = generateTestShader(
711           shaderInfo,
712           shaderInfo.fragmentShaderTemplate,
713           params,
714           tests[ii].source);
715       var referenceTextureOrArray = generateReferenceImage(
716           gl,
717           tests[ii].generator,
718           isVertex ? gridRes : width,
719           isVertex ? gridRes : height,
720           isVertex);
722       debug("");
723       var testVertexShader = wtu.loadShader(gl, testVertexShaderSource, gl.VERTEX_SHADER, testFailed, true);
724       var testFragmentShader = wtu.loadShader(gl, testFragmentShaderSource, gl.FRAGMENT_SHADER, testFailed, true);
725       debug("");
728       if (parseInt(wtu.getUrlOptions().dumpShaders)) {
729         var vRefInfo = {
730           shader: referenceVertexShader,
731           shaderSuccess: true,
732           label: "reference vertex shader",
733           source: referenceVertexShaderSource
734         };
735         var fRefInfo = {
736           shader: referenceFragmentShader,
737           shaderSuccess: true,
738           label: "reference fragment shader",
739           source: referenceFragmentShaderSource
740         };
741         wtu.dumpShadersInfo(gl, window.location.pathname, passMsg, vRefInfo, fRefInfo);
743         var vTestInfo = {
744           shader: testVertexShader,
745           shaderSuccess: true,
746           label: "test vertex shader",
747           source: testVertexShaderSource
748         };
749         var fTestInfo = {
750           shader: testFragmentShader,
751           shaderSuccess: true,
752           label: "test fragment shader",
753           source: testFragmentShaderSource
754         };
755         wtu.dumpShadersInfo(gl, window.location.pathname, passMsg, vTestInfo, fTestInfo);
756       }
758       var refData;
759       if (isVertex) {
760         refData = drawVertexReferenceImage(referenceTextureOrArray);
761       } else {
762         refData = drawFragmentReferenceImage(referenceTextureOrArray);
763       }
764       var refImg = wtu.makeImageFromCanvas(canvas);
765       var testData;
766       if (isVertex) {
767         var referenceFragmentShader = wtu.loadShader(gl, referenceFragmentShaderSource, gl.FRAGMENT_SHADER, testFailed);
768         testData = draw(
769           testVertexShader, referenceFragmentShader);
770       } else {
771         var referenceVertexShader = wtu.loadShader(gl, referenceVertexShaderSource, gl.VERTEX_SHADER, testFailed);
772         testData = draw(
773           referenceVertexShader, testFragmentShader);
774       }
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);
782     }
783   }
785   finishTest();
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);
799     return img;
800   }
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);
819     return img;
820   }
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);
834     return img;
835   }
837   /**
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
845    * vertices.
846    *
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.
853    */
854   function generateReferenceImage(
855     gl,
856     generator,
857     width,
858     height,
859     isVertex) {
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;
865     }
867     function computeVertexColor(texCoordX, texCoordY) {
868       return [ texCoordX,
869                texCoordY,
870                texCoordX * texCoordY,
871                (1.0 - texCoordX) * texCoordY * 0.5 + 0.5 ];
872     }
874     /**
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).
877      */
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) {
882         --gridBottom;
883       }
884       var gridLeft = Math.floor(texCoordX * gridRes);
885       if (gridLeft == gridRes) {
886         --gridLeft;
887       }
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:
895       // A--B
896       // |\ |
897       // | \|
898       // D--C
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.
906       var a, b, c, d;
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
912         d = 1 - a - c;
913         b = 0;
914       } else {
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
918         b = 1 - a - c;
919         d = 0;
920       }
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]);
925       }
926       return interpolated;
927     }
929     function clamp(value, minVal, maxVal) {
930       return Math.max(minVal, Math.min(value, maxVal));
931     }
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);
950     }
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];
976         }
977       }
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);
987       return texture;
988     }
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";
997       }
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];
1020         }
1021       }
1023       return data;
1024     }
1026     //----------------------------------------------------------------------
1027     // Body of generateReferenceImage
1028     //
1030     if (isVertex) {
1031       return generateVertexReference();
1032     } else {
1033       return generateFragmentReference();
1034     }
1035   }
1038 return {
1039   /**
1040    * runs a bunch of GLSL tests using the passed in parameters
1041    * The parameters are:
1042    *
1043    * feature:
1044    *    the name of the function being tested (eg, sin, dot,
1045    *    normalize)
1046    *
1047    * testFunc:
1048    *    The prototype of function to be tested not including the
1049    *    return type.
1050    *
1051    * emuFunc:
1052    *    A base function that can be used to generate emulation
1053    *    functions. Example for 'ceil'
1054    *
1055    *      float $(func)_base(float value) {
1056    *        float m = mod(value, 1.0);
1057    *        return m != 0.0 ? (value + 1.0 - m) : value;
1058    *      }
1059    *
1060    * args:
1061    *    The arguments to the function
1062    *
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).
1067    *
1068    * simpleEmu:
1069    *    if supplied, the code that can be used to generate all
1070    *    functions for all types.
1071    *
1072    *    Example for 'normalize':
1073    *
1074    *        $(type) $(func)_emu($(args)) {
1075    *           return value / length(value);
1076    *        }
1077    *
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.
1083    *
1084    * tests:
1085    *    The code for each test. It is assumed the tests are for
1086    *    float, vec2, vec3, vec4 in that order.
1087    *
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.
1092    *
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.
1099    */
1100   runFeatureTest: runFeatureTest,
1102   /*
1103    * Runs a bunch of GLSL tests using the passed in parameters
1104    *
1105    * The parameters are:
1106    *
1107    * tests:
1108    *    Array of tests. For each test the following parameters are expected
1109    *
1110    *    name:
1111    *       some description of the test
1112    *    reference:
1113    *       parameters for the reference shader (see below)
1114    *    test:
1115    *       parameters for the test shader (see below)
1116    *
1117    *    The parameter for the reference and test shaders are
1118    *
1119    *    shader: the GLSL for the shader
1120    *    subs: any substitutions you wish to define for the shader.
1121    *
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.
1127    *
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.
1131    *
1132    *    'input' the input to your GLSL. Always a vec4. All change
1133    *    from 0 to 1 over the quad to be drawn.
1134    *
1135    *    'output' the output color. Also a vec4
1136    *
1137    *    'emu' a place to insert extra stuff
1138    *    'extra' a place to insert extra stuff.
1139    *
1140    *    You can think of the templates like this
1141    *
1142    *       $(extra)
1143    *       $(emu)
1144    *
1145    *       void main() {
1146    *          // do math to calculate input
1147    *          ...
1148    *
1149    *          $(shader)
1150    *       }
1151    *
1152    *    Your shader first has any subs you provided applied as well
1153    *    as 'input' and 'output'
1154    *
1155    *    It is then inserted into the template which is also provided
1156    *    with your subs.
1157    *
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.
1163    *
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.
1168    *
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.
1175    */
1176   runBasicTest: runBasicTest,
1178   /**
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:
1182    *
1183    * feature:
1184    *    the name of the function being tested (eg, sin, dot,
1185    *    normalize)
1186    *
1187    * testFunc:
1188    *    The prototype of function to be tested not including the
1189    *    return type.
1190    *
1191    * args:
1192    *    The arguments to the function
1193    *
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.
1199    *
1200    * tests:
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:
1204    *
1205    *       source: the GLSL source code for the tests
1206    *
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.
1210    *
1211    *       tolerance: (optional) a per-test tolerance.
1212    *
1213    * extra: (optional)
1214    *    Extra GLSL code inserted at the top of each test's shader.
1215    *
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.
1220    *
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.
1227    */
1228   runReferenceImageTest: runReferenceImageTest,
1230   none: false
1233 }());