Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / webgl / src / sdk / tests / conformance / resources / glsl-generator.js
1 /*
2 ** Copyright (c) 2012 The Khronos Group Inc.
3 **
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:
11 **
12 ** The above copyright notice and this permission notice shall be included
13 ** in all copies or substantial portions of the Materials.
14 **
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.
22 */
23 GLSLGenerator = (function() {
24
25 var vertexShaderTemplate = [
26   "attribute vec4 aPosition;",
27   "",
28   "varying vec4 vColor;",
29   "",
30   "$(extra)",
31   "$(emu)",
32   "",
33   "void main()",
34   "{",
35   "   gl_Position = aPosition;",
36   "   vec2 texcoord = vec2(aPosition.xy * 0.5 + vec2(0.5, 0.5));",
37   "   vec4 color = vec4(",
38   "       texcoord,",
39   "       texcoord.x * texcoord.y,",
40   "       (1.0 - texcoord.x) * texcoord.y * 0.5 + 0.5);",
41   "   $(test)",
42   "}"
43 ].join("\n");
44
45 var fragmentShaderTemplate = [
46   "precision mediump float;",
47   "",
48   "varying vec4 vColor;",
49   "",
50   "$(extra)",
51   "$(emu)",
52   "",
53   "void main()",
54   "{",
55   "   $(test)",
56   "}"
57 ].join("\n");
58
59 var baseVertexShader = [
60   "attribute vec4 aPosition;",
61   "",
62   "varying vec4 vColor;",
63   "",
64   "void main()",
65   "{",
66   "   gl_Position = aPosition;",
67   "   vec2 texcoord = vec2(aPosition.xy * 0.5 + vec2(0.5, 0.5));",
68   "   vColor = vec4(",
69   "       texcoord,",
70   "       texcoord.x * texcoord.y,",
71   "       (1.0 - texcoord.x) * texcoord.y * 0.5 + 0.5);",
72   "}"
73 ].join("\n");
74
75 var baseVertexShaderWithColor = [
76   "attribute vec4 aPosition;",
77   "attribute vec4 aColor;",
78   "",
79   "varying vec4 vColor;",
80   "",
81   "void main()",
82   "{",
83   "   gl_Position = aPosition;",
84   "   vColor = aColor;",
85   "}"
86 ].join("\n");
87
88 var baseFragmentShader = [
89   "precision mediump float;",
90   "varying vec4 vColor;",
91   "",
92   "void main()",
93   "{",
94   "   gl_FragColor = vColor;",
95   "}"
96 ].join("\n");
97
98 var types = [
99   { type: "float",
100     code: [
101       "float $(func)_emu($(args)) {",
102       "  return $(func)_base($(baseArgs));",
103       "}"].join("\n")
104   },
105   { type: "vec2",
106     code: [
107       "vec2 $(func)_emu($(args)) {",
108       "  return vec2(",
109       "      $(func)_base($(baseArgsX)),",
110       "      $(func)_base($(baseArgsY)));",
111       "}"].join("\n")
112   },
113   { type: "vec3",
114     code: [
115       "vec3 $(func)_emu($(args)) {",
116       "  return vec3(",
117       "      $(func)_base($(baseArgsX)),",
118       "      $(func)_base($(baseArgsY)),",
119       "      $(func)_base($(baseArgsZ)));",
120       "}"].join("\n")
121   },
122   { type: "vec4",
123     code: [
124       "vec4 $(func)_emu($(args)) {",
125       "  return vec4(",
126       "      $(func)_base($(baseArgsX)),",
127       "      $(func)_base($(baseArgsY)),",
128       "      $(func)_base($(baseArgsZ)),",
129       "      $(func)_base($(baseArgsW)));",
130       "}"].join("\n")
131   }
132 ];
133
134 var bvecTypes = [
135   { type: "bvec2",
136     code: [
137       "bvec2 $(func)_emu($(args)) {",
138       "  return bvec2(",
139       "      $(func)_base($(baseArgsX)),",
140       "      $(func)_base($(baseArgsY)));",
141       "}"].join("\n")
142   },
143   { type: "bvec3",
144     code: [
145       "bvec3 $(func)_emu($(args)) {",
146       "  return bvec3(",
147       "      $(func)_base($(baseArgsX)),",
148       "      $(func)_base($(baseArgsY)),",
149       "      $(func)_base($(baseArgsZ)));",
150       "}"].join("\n")
151   },
152   { type: "bvec4",
153     code: [
154       "vec4 $(func)_emu($(args)) {",
155       "  return bvec4(",
156       "      $(func)_base($(baseArgsX)),",
157       "      $(func)_base($(baseArgsY)),",
158       "      $(func)_base($(baseArgsZ)),",
159       "      $(func)_base($(baseArgsW)));",
160       "}"].join("\n")
161   }
162 ];
163
164 var replaceRE = /\$\((\w+)\)/g;
165
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) {
171         return args[ii][p1];
172       }
173     }
174     throw "unknown string param '" + p1 + "'";
175   });
176 };
177
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;
189
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: ""});
196
197   test = replaceParams(test, {
198     input: input,
199     output: output,
200     func: feature + "_emu"
201   });
202   emuFunc = replaceParams(emuFunc, {
203     func: feature
204   });
205   args = replaceParams(args, {
206     type: type
207   });
208   typeCode = replaceParams(typeCode, {
209     func: feature,
210     type: type,
211     args: args,
212     baseArgs: baseArgs,
213     baseArgsX: baseArgsX,
214     baseArgsY: baseArgsY,
215     baseArgsZ: baseArgsZ,
216     baseArgsW: baseArgsW
217   });
218   var shader = replaceParams(template, {
219     extra: extra,
220     emu: emuFunc + "\n\n" + typeCode,
221     test: test
222   });
223   return shader;
224 };
225
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 || '';
233
234   test = replaceParams(test, {
235     input: input,
236     output: output,
237     func: feature
238   });
239   var shader = replaceParams(template, {
240     extra: extra,
241     emu: '',
242     test: test
243   });
244   return shader;
245 };
246
247 function _reportResults(refData, refImg, testData, testImg, tolerance,
248                         width, height, ctx, imgData, wtu, canvas2d, consoleDiv) {
249   var same = true;
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'));
274         if (!firstFailure) {
275           firstFailure = ": " + detail;
276         }
277         imgData.data[imgOffset] = 255;
278         same = false;
279       }
280     }
281   }
282
283   var diffImg = null;
284   if (!same) {
285     ctx.putImageData(imgData, 0, 0);
286     diffImg = wtu.makeImageFromCanvas(canvas2d);
287   }
288
289   var div = document.createElement("div");
290   div.className = "testimages";
291   wtu.insertImage(div, "ref", refImg);
292   wtu.insertImage(div, "test", testImg);
293   if (diffImg) {
294     wtu.insertImage(div, "diff", diffImg);
295   }
296   div.appendChild(document.createElement('br'));
297
298   consoleDiv.appendChild(div);
299
300   if (!same) {
301     testFailed("images are different" + (firstFailure ? firstFailure : ""));
302   } else {
303     testPassed("images are the same");
304   }
305
306   consoleDiv.appendChild(document.createElement('hr'));
307 }
308
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;
316
317   description("Testing GLSL feature: " + params.feature);
318
319   var width = 32;
320   var height = 32;
321
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 });
327   if (!gl) {
328     testFailed("context does not exist");
329     finishTest();
330     return;
331   }
332
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);
338
339   var shaderInfos = [
340     { type: "vertex",
341       input: "color",
342       output: "vColor",
343       vertexShaderTemplate: vertexShaderTemplate,
344       fragmentShaderTemplate: baseFragmentShader,
345       tolerance: vertexTolerance
346     },
347     { type: "fragment",
348       input: "vColor",
349       output: "gl_FragColor",
350       vertexShaderTemplate: baseVertexShader,
351       fragmentShaderTemplate: fragmentShaderTemplate,
352       tolerance: fragmentTolerance
353     }
354   ];
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) {
363         type = {
364           type: type.type,
365           code: params.simpleEmu
366         };
367       }
368       debug("");
369       var str = replaceParams(params.testFunc, {
370         func: params.feature,
371         type: type.type,
372         arg0: type.type
373       });
374       var passMsg = "Testing: " + str + " in " + shaderInfo.type + " shader";
375       debug(passMsg);
376
377       var referenceVertexShaderSource = generateReferenceShader(
378           shaderInfo,
379           shaderInfo.vertexShaderTemplate,
380           params,
381           type,
382           tests[ii]);
383       var referenceFragmentShaderSource = generateReferenceShader(
384           shaderInfo,
385           shaderInfo.fragmentShaderTemplate,
386           params,
387           type,
388           tests[ii]);
389       var testVertexShaderSource = generateTestShader(
390           shaderInfo,
391           shaderInfo.vertexShaderTemplate,
392           params,
393           tests[ii]);
394       var testFragmentShaderSource = generateTestShader(
395           shaderInfo,
396           shaderInfo.fragmentShaderTemplate,
397           params,
398           tests[ii]);
399
400
401       debug("");
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');
406       debug("");
407
408       if (parseInt(wtu.getUrlOptions().dumpShaders)) {
409         var vRefInfo = {
410           shader: referenceVertexShader,
411           shaderSuccess: true,
412           label: "reference vertex shader",
413           source: referenceVertexShaderSource
414         };
415         var fRefInfo = {
416           shader: referenceFragmentShader,
417           shaderSuccess: true,
418           label: "reference fragment shader",
419           source: referenceFragmentShaderSource
420         };
421         wtu.dumpShadersInfo(gl, window.location.pathname, passMsg, vRefInfo, fRefInfo);
422
423         var vTestInfo = {
424           shader: testVertexShader,
425           shaderSuccess: true,
426           label: "test vertex shader",
427           source: testVertexShaderSource
428         };
429         var fTestInfo = {
430           shader: testFragmentShader,
431           shaderSuccess: true,
432           label: "test fragment shader",
433           source: testFragmentShaderSource
434         };
435         wtu.dumpShadersInfo(gl, window.location.pathname, passMsg, vTestInfo, fTestInfo);
436       }
437
438       var refData = draw(
439           referenceVertexShader, referenceFragmentShader);
440       var refImg = wtu.makeImageFromCanvas(canvas);
441       if (ss == 0) {
442         var testData = draw(
443             testVertexShader, referenceFragmentShader);
444       } else {
445         var testData = draw(
446             referenceVertexShader, testFragmentShader);
447       }
448       var testImg = wtu.makeImageFromCanvas(canvas);
449
450       _reportResults(refData, refImg, testData, testImg, shaderInfo.tolerance,
451                      width, height, ctx, imgData, wtu, canvas2d, consoleDiv);
452     }
453   }
454
455   finishTest();
456
457   function draw(vertexShader, fragmentShader) {
458     var program = wtu.createProgram(gl, vertexShader, fragmentShader, testFailed);
459
460     var posLoc = gl.getAttribLocation(program, "aPosition");
461     wtu.setupIndexedQuad(gl, gridRes, posLoc);
462
463     gl.useProgram(program);
464     wtu.clearAndDrawIndexedQuad(gl, gridRes, [0, 0, 255, 255]);
465     wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no errors from draw");
466
467     var img = new Uint8Array(width * height * 4);
468     gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, img);
469     return img;
470   }
471
472 };
473
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;
481
482   description("Testing : " + document.getElementsByTagName("title")[0].innerText);
483
484   var width = 32;
485   var height = 32;
486
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);
492   if (!gl) {
493     testFailed("context does not exist");
494     finishTest();
495     return;
496   }
497
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);
503
504   var shaderInfos = [
505     { type: "vertex",
506       input: "color",
507       output: "vColor",
508       vertexShaderTemplate: vertexShaderTemplate,
509       fragmentShaderTemplate: baseFragmentShader,
510       tolerance: vertexTolerance
511     },
512     { type: "fragment",
513       input: "vColor",
514       output: "gl_FragColor",
515       vertexShaderTemplate: baseVertexShader,
516       fragmentShaderTemplate: fragmentShaderTemplate,
517       tolerance: fragmentTolerance
518     }
519   ];
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];
527       debug("");
528       var passMsg = "Testing: " + test.name + " in " + shaderInfo.type + " shader";
529       debug(passMsg);
530
531       function genShader(shaderInfo, template, shader, subs) {
532         shader = replaceParams(shader, subs, {
533             input: shaderInfo.input,
534             output: shaderInfo.output
535           });
536         shader = replaceParams(template, subs, {
537             test: shader,
538             emu: "",
539             extra: ""
540           });
541         return shader;
542       }
543
544       var referenceVertexShaderSource = genShader(
545           shaderInfo,
546           shaderInfo.vertexShaderTemplate,
547           test.reference.shader,
548           test.reference.subs);
549       var referenceFragmentShaderSource = genShader(
550           shaderInfo,
551           shaderInfo.fragmentShaderTemplate,
552           test.reference.shader,
553           test.reference.subs);
554       var testVertexShaderSource = genShader(
555           shaderInfo,
556           shaderInfo.vertexShaderTemplate,
557           test.test.shader,
558           test.test.subs);
559       var testFragmentShaderSource = genShader(
560           shaderInfo,
561           shaderInfo.fragmentShaderTemplate,
562           test.test.shader,
563           test.test.subs);
564
565       debug("");
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');
570       debug("");
571
572       if (parseInt(wtu.getUrlOptions().dumpShaders)) {
573         var vRefInfo = {
574           shader: referenceVertexShader,
575           shaderSuccess: true,
576           label: "reference vertex shader",
577           source: referenceVertexShaderSource
578         };
579         var fRefInfo = {
580           shader: referenceFragmentShader,
581           shaderSuccess: true,
582           label: "reference fragment shader",
583           source: referenceFragmentShaderSource
584         };
585         wtu.dumpShadersInfo(gl, window.location.pathname, passMsg, vRefInfo, fRefInfo);
586
587         var vTestInfo = {
588           shader: testVertexShader,
589           shaderSuccess: true,
590           label: "test vertex shader",
591           source: testVertexShaderSource
592         };
593         var fTestInfo = {
594           shader: testFragmentShader,
595           shaderSuccess: true,
596           label: "test fragment shader",
597           source: testFragmentShaderSource
598         };
599         wtu.dumpShadersInfo(gl, window.location.pathname, passMsg, vTestInfo, fTestInfo);
600       }
601
602       var refData = draw(referenceVertexShader, referenceFragmentShader);
603       var refImg = wtu.makeImageFromCanvas(canvas);
604       if (ss == 0) {
605         var testData = draw(testVertexShader, referenceFragmentShader);
606       } else {
607         var testData = draw(referenceVertexShader, testFragmentShader);
608       }
609       var testImg = wtu.makeImageFromCanvas(canvas);
610
611       _reportResults(refData, refImg, testData, testImg, shaderInfo.tolerance,
612                      width, height, ctx, imgData, wtu, canvas2d, consoleDiv);
613     }
614   }
615
616   finishTest();
617
618   function draw(vertexShader, fragmentShader) {
619     var program = wtu.createProgram(gl, vertexShader, fragmentShader, testFailed);
620
621     var posLoc = gl.getAttribLocation(program, "aPosition");
622     wtu.setupIndexedQuad(gl, gridRes, posLoc);
623
624     gl.useProgram(program);
625     wtu.clearAndDrawIndexedQuad(gl, gridRes, [0, 0, 255, 255]);
626     wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no errors from draw");
627
628     var img = new Uint8Array(width * height * 4);
629     gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, img);
630     return img;
631   }
632
633 };
634
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;
642
643   description("Testing GLSL feature: " + params.feature);
644
645   var width = 32;
646   var height = 32;
647
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 });
653   if (!gl) {
654     testFailed("context does not exist");
655     finishTest();
656     return;
657   }
658
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);
664
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.
669
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();
676
677   var shaderInfos = [
678     { type: "vertex",
679       input: "color",
680       output: "vColor",
681       vertexShaderTemplate: vertexShaderTemplate,
682       fragmentShaderTemplate: baseFragmentShader,
683       tolerance: vertexTolerance
684     },
685     { type: "fragment",
686       input: "vColor",
687       output: "gl_FragColor",
688       vertexShaderTemplate: baseVertexShader,
689       fragmentShaderTemplate: fragmentShaderTemplate,
690       tolerance: fragmentTolerance
691     }
692   ];
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);
701       debug("");
702       var str = replaceParams(params.testFunc, {
703         func: params.feature,
704         type: type.type,
705         arg0: type.type
706       });
707       var passMsg = "Testing: " + str + " in " + shaderInfo.type + " shader";
708       debug(passMsg);
709
710       var referenceVertexShaderSource = generateReferenceShader(
711           shaderInfo,
712           shaderInfo.vertexShaderTemplate,
713           params,
714           type,
715           tests[ii].source);
716       var referenceFragmentShaderSource = generateReferenceShader(
717           shaderInfo,
718           shaderInfo.fragmentShaderTemplate,
719           params,
720           type,
721           tests[ii].source);
722       var testVertexShaderSource = generateTestShader(
723           shaderInfo,
724           shaderInfo.vertexShaderTemplate,
725           params,
726           tests[ii].source);
727       var testFragmentShaderSource = generateTestShader(
728           shaderInfo,
729           shaderInfo.fragmentShaderTemplate,
730           params,
731           tests[ii].source);
732       var referenceTextureOrArray = generateReferenceImage(
733           gl,
734           tests[ii].generator,
735           isVertex ? gridRes : width,
736           isVertex ? gridRes : height,
737           isVertex);
738
739       debug("");
740       var testVertexShader = wtu.loadShader(gl, testVertexShaderSource, gl.VERTEX_SHADER, testFailed, true);
741       var testFragmentShader = wtu.loadShader(gl, testFragmentShaderSource, gl.FRAGMENT_SHADER, testFailed, true);
742       debug("");
743
744
745       if (parseInt(wtu.getUrlOptions().dumpShaders)) {
746         var vRefInfo = {
747           shader: referenceVertexShader,
748           shaderSuccess: true,
749           label: "reference vertex shader",
750           source: referenceVertexShaderSource
751         };
752         var fRefInfo = {
753           shader: referenceFragmentShader,
754           shaderSuccess: true,
755           label: "reference fragment shader",
756           source: referenceFragmentShaderSource
757         };
758         wtu.dumpShadersInfo(gl, window.location.pathname, passMsg, vRefInfo, fRefInfo);
759
760         var vTestInfo = {
761           shader: testVertexShader,
762           shaderSuccess: true,
763           label: "test vertex shader",
764           source: testVertexShaderSource
765         };
766         var fTestInfo = {
767           shader: testFragmentShader,
768           shaderSuccess: true,
769           label: "test fragment shader",
770           source: testFragmentShaderSource
771         };
772         wtu.dumpShadersInfo(gl, window.location.pathname, passMsg, vTestInfo, fTestInfo);
773       }
774
775       var refData;
776       if (isVertex) {
777         refData = drawVertexReferenceImage(referenceTextureOrArray);
778       } else {
779         refData = drawFragmentReferenceImage(referenceTextureOrArray);
780       }
781       var refImg = wtu.makeImageFromCanvas(canvas);
782       var testData;
783       if (isVertex) {
784         var referenceFragmentShader = wtu.loadShader(gl, referenceFragmentShaderSource, gl.FRAGMENT_SHADER, testFailed);
785         testData = draw(
786           testVertexShader, referenceFragmentShader);
787       } else {
788         var referenceVertexShader = wtu.loadShader(gl, referenceVertexShaderSource, gl.VERTEX_SHADER, testFailed);
789         testData = draw(
790           referenceVertexShader, testFragmentShader);
791       }
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);
799     }
800   }
801
802   finishTest();
803
804   function draw(vertexShader, fragmentShader) {
805     var program = wtu.createProgram(gl, vertexShader, fragmentShader, testFailed);
806
807     var posLoc = gl.getAttribLocation(program, "aPosition");
808     wtu.setupIndexedQuad(gl, gridRes, posLoc);
809
810     gl.useProgram(program);
811     wtu.clearAndDrawIndexedQuad(gl, gridRes, [0, 0, 255, 255]);
812     wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no errors from draw");
813
814     var img = new Uint8Array(width * height * 4);
815     gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, img);
816     return img;
817   }
818
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");
833
834     var img = new Uint8Array(width * height * 4);
835     gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, img);
836     return img;
837   }
838
839   function drawFragmentReferenceImage(texture) {
840     var program = wtu.setupTexturedQuad(gl);
841
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");
848
849     var img = new Uint8Array(width * height * 4);
850     gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, img);
851     return img;
852   }
853
854   /**
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
862    * vertices.
863    *
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.
870    */
871   function generateReferenceImage(
872     gl,
873     generator,
874     width,
875     height,
876     isVertex) {
877
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;
882     }
883
884     function computeVertexColor(texCoordX, texCoordY) {
885       return [ texCoordX,
886                texCoordY,
887                texCoordX * texCoordY,
888                (1.0 - texCoordX) * texCoordY * 0.5 + 0.5 ];
889     }
890
891     /**
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).
894      */
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) {
899         --gridBottom;
900       }
901       var gridLeft = Math.floor(texCoordX * gridRes);
902       if (gridLeft == gridRes) {
903         --gridLeft;
904       }
905
906       // Calculate coordinates relative to the grid cell.
907       var cellX = texCoordX * gridRes - gridLeft;
908       var cellY = texCoordY * gridRes - gridBottom;
909
910       // Barycentric coordinates inside either triangle ACD or ABC
911       // are used as weights for the vertex colors in the corners:
912       // A--B
913       // |\ |
914       // | \|
915       // D--C
916
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);
921
922       // Calculate weights.
923       var a, b, c, d;
924
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
929         d = 1 - a - c;
930         b = 0;
931       } else {
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
935         b = 1 - a - c;
936         d = 0;
937       }
938
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]);
942       }
943       return interpolated;
944     }
945
946     function clamp(value, minVal, maxVal) {
947       return Math.max(minVal, Math.min(value, maxVal));
948     }
949
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);
956
957       var color = colorFunc(tcx, tcy);
958
959       var output = generator(color[0], color[1], color[2], color[3]);
960
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);
967     }
968
969     function generateFragmentReference() {
970       var data = new Uint8Array(4 * width * height);
971
972       var horizTexel = 1.0 / width;
973       var vertTexel = 1.0 / height;
974       var halfHorizTexel = 0.5 * horizTexel;
975       var halfVertTexel = 0.5 * vertTexel;
976
977       var pixel = new Array(4);
978
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.
982
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);
986
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];
993         }
994       }
995
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);
1004       return texture;
1005     }
1006
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";
1014       }
1015
1016       var texSize = 1 + width;
1017       var data = new Uint8Array(4 * texSize * texSize);
1018
1019       var step = 2.0 / width;
1020
1021       var pixel = new Array(4);
1022
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.
1026
1027           // Compute desired position in clip space
1028           var px = -1.0 + (xi * step);
1029           var py = -1.0 + (yi * step);
1030
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];
1037         }
1038       }
1039
1040       return data;
1041     }
1042
1043     //----------------------------------------------------------------------
1044     // Body of generateReferenceImage
1045     //
1046
1047     if (isVertex) {
1048       return generateVertexReference();
1049     } else {
1050       return generateFragmentReference();
1051     }
1052   }
1053 };
1054
1055 return {
1056   /**
1057    * runs a bunch of GLSL tests using the passed in parameters
1058    * The parameters are:
1059    *
1060    * feature:
1061    *    the name of the function being tested (eg, sin, dot,
1062    *    normalize)
1063    *
1064    * testFunc:
1065    *    The prototype of function to be tested not including the
1066    *    return type.
1067    *
1068    * emuFunc:
1069    *    A base function that can be used to generate emulation
1070    *    functions. Example for 'ceil'
1071    *
1072    *      float $(func)_base(float value) {
1073    *        float m = mod(value, 1.0);
1074    *        return m != 0.0 ? (value + 1.0 - m) : value;
1075    *      }
1076    *
1077    * args:
1078    *    The arguments to the function
1079    *
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).
1084    *
1085    * simpleEmu:
1086    *    if supplied, the code that can be used to generate all
1087    *    functions for all types.
1088    *
1089    *    Example for 'normalize':
1090    *
1091    *        $(type) $(func)_emu($(args)) {
1092    *           return value / length(value);
1093    *        }
1094    *
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.
1100    *
1101    * tests:
1102    *    The code for each test. It is assumed the tests are for
1103    *    float, vec2, vec3, vec4 in that order.
1104    *
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.
1109    *
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.
1116    */
1117   runFeatureTest: runFeatureTest,
1118
1119   /*
1120    * Runs a bunch of GLSL tests using the passed in parameters
1121    *
1122    * The parameters are:
1123    *
1124    * tests:
1125    *    Array of tests. For each test the following parameters are expected
1126    *
1127    *    name:
1128    *       some description of the test
1129    *    reference:
1130    *       parameters for the reference shader (see below)
1131    *    test:
1132    *       parameters for the test shader (see below)
1133    *
1134    *    The parameter for the reference and test shaders are
1135    *
1136    *    shader: the GLSL for the shader
1137    *    subs: any substitutions you wish to define for the shader.
1138    *
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.
1144    *
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.
1148    *
1149    *    'input' the input to your GLSL. Always a vec4. All change
1150    *    from 0 to 1 over the quad to be drawn.
1151    *
1152    *    'output' the output color. Also a vec4
1153    *
1154    *    'emu' a place to insert extra stuff
1155    *    'extra' a place to insert extra stuff.
1156    *
1157    *    You can think of the templates like this
1158    *
1159    *       $(extra)
1160    *       $(emu)
1161    *
1162    *       void main() {
1163    *          // do math to calculate input
1164    *          ...
1165    *
1166    *          $(shader)
1167    *       }
1168    *
1169    *    Your shader first has any subs you provided applied as well
1170    *    as 'input' and 'output'
1171    *
1172    *    It is then inserted into the template which is also provided
1173    *    with your subs.
1174    *
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.
1180    *
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.
1185    *
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.
1192    */
1193   runBasicTest: runBasicTest,
1194
1195   /**
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:
1199    *
1200    * feature:
1201    *    the name of the function being tested (eg, sin, dot,
1202    *    normalize)
1203    *
1204    * testFunc:
1205    *    The prototype of function to be tested not including the
1206    *    return type.
1207    *
1208    * args:
1209    *    The arguments to the function
1210    *
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.
1216    *
1217    * tests:
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:
1221    *
1222    *       source: the GLSL source code for the tests
1223    *
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.
1227    *
1228    *       tolerance: (optional) a per-test tolerance.
1229    *
1230    * extra: (optional)
1231    *    Extra GLSL code inserted at the top of each test's shader.
1232    * 
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.
1237    *
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.
1244    */
1245   runReferenceImageTest: runReferenceImageTest,
1246
1247   none: false
1248 };
1249
1250 }());
1251