Upstream version 10.39.225.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   "#if defined(GL_ES)",
47   "precision mediump float;",
48   "#endif",
49   "",
50   "varying vec4 vColor;",
51   "",
52   "$(extra)",
53   "$(emu)",
54   "",
55   "void main()",
56   "{",
57   "   $(test)",
58   "}"
59 ].join("\n");
60
61 var baseVertexShader = [
62   "attribute vec4 aPosition;",
63   "",
64   "varying vec4 vColor;",
65   "",
66   "void main()",
67   "{",
68   "   gl_Position = aPosition;",
69   "   vec2 texcoord = vec2(aPosition.xy * 0.5 + vec2(0.5, 0.5));",
70   "   vColor = vec4(",
71   "       texcoord,",
72   "       texcoord.x * texcoord.y,",
73   "       (1.0 - texcoord.x) * texcoord.y * 0.5 + 0.5);",
74   "}"
75 ].join("\n");
76
77 var baseVertexShaderWithColor = [
78   "attribute vec4 aPosition;",
79   "attribute vec4 aColor;",
80   "",
81   "varying vec4 vColor;",
82   "",
83   "void main()",
84   "{",
85   "   gl_Position = aPosition;",
86   "   vColor = aColor;",
87   "}"
88 ].join("\n");
89
90 var baseFragmentShader = [
91   "#if defined(GL_ES)",
92   "precision mediump float;",
93   "#endif",
94   "varying vec4 vColor;",
95   "",
96   "void main()",
97   "{",
98   "   gl_FragColor = vColor;",
99   "}"
100 ].join("\n");
101
102 var types = [
103   { type: "float",
104     code: [
105       "float $(func)_emu($(args)) {",
106       "  return $(func)_base($(baseArgs));",
107       "}"].join("\n")
108   },
109   { type: "vec2",
110     code: [
111       "vec2 $(func)_emu($(args)) {",
112       "  return vec2(",
113       "      $(func)_base($(baseArgsX)),",
114       "      $(func)_base($(baseArgsY)));",
115       "}"].join("\n")
116   },
117   { type: "vec3",
118     code: [
119       "vec3 $(func)_emu($(args)) {",
120       "  return vec3(",
121       "      $(func)_base($(baseArgsX)),",
122       "      $(func)_base($(baseArgsY)),",
123       "      $(func)_base($(baseArgsZ)));",
124       "}"].join("\n")
125   },
126   { type: "vec4",
127     code: [
128       "vec4 $(func)_emu($(args)) {",
129       "  return vec4(",
130       "      $(func)_base($(baseArgsX)),",
131       "      $(func)_base($(baseArgsY)),",
132       "      $(func)_base($(baseArgsZ)),",
133       "      $(func)_base($(baseArgsW)));",
134       "}"].join("\n")
135   }
136 ];
137
138 var bvecTypes = [
139   { type: "bvec2",
140     code: [
141       "bvec2 $(func)_emu($(args)) {",
142       "  return bvec2(",
143       "      $(func)_base($(baseArgsX)),",
144       "      $(func)_base($(baseArgsY)));",
145       "}"].join("\n")
146   },
147   { type: "bvec3",
148     code: [
149       "bvec3 $(func)_emu($(args)) {",
150       "  return bvec3(",
151       "      $(func)_base($(baseArgsX)),",
152       "      $(func)_base($(baseArgsY)),",
153       "      $(func)_base($(baseArgsZ)));",
154       "}"].join("\n")
155   },
156   { type: "bvec4",
157     code: [
158       "vec4 $(func)_emu($(args)) {",
159       "  return bvec4(",
160       "      $(func)_base($(baseArgsX)),",
161       "      $(func)_base($(baseArgsY)),",
162       "      $(func)_base($(baseArgsZ)),",
163       "      $(func)_base($(baseArgsW)));",
164       "}"].join("\n")
165   }
166 ];
167
168 var replaceRE = /\$\((\w+)\)/g;
169
170 var replaceParams = function(str) {
171   var args = arguments;
172   return str.replace(replaceRE, function(str, p1, offset, s) {
173     for (var ii = 1; ii < args.length; ++ii) {
174       if (args[ii][p1] !== undefined) {
175         return args[ii][p1];
176       }
177     }
178     throw "unknown string param '" + p1 + "'";
179   });
180 };
181
182 var generateReferenceShader = function(
183     shaderInfo, template, params, typeInfo, test) {
184   var input = shaderInfo.input;
185   var output = shaderInfo.output;
186   var feature = params.feature;
187   var testFunc = params.testFunc;
188   var emuFunc = params.emuFunc || "";
189   var extra = params.extra || '';
190   var args = params.args || "$(type) value";
191   var type = typeInfo.type;
192   var typeCode = typeInfo.code;
193
194   var baseArgs = params.baseArgs || "value$(field)";
195   var baseArgsX = replaceParams(baseArgs, {field: ".x"});
196   var baseArgsY = replaceParams(baseArgs, {field: ".y"});
197   var baseArgsZ = replaceParams(baseArgs, {field: ".z"});
198   var baseArgsW = replaceParams(baseArgs, {field: ".w"});
199   var baseArgs = replaceParams(baseArgs, {field: ""});
200
201   test = replaceParams(test, {
202     input: input,
203     output: output,
204     func: feature + "_emu"
205   });
206   emuFunc = replaceParams(emuFunc, {
207     func: feature
208   });
209   args = replaceParams(args, {
210     type: type
211   });
212   typeCode = replaceParams(typeCode, {
213     func: feature,
214     type: type,
215     args: args,
216     baseArgs: baseArgs,
217     baseArgsX: baseArgsX,
218     baseArgsY: baseArgsY,
219     baseArgsZ: baseArgsZ,
220     baseArgsW: baseArgsW
221   });
222   var shader = replaceParams(template, {
223     extra: extra,
224     emu: emuFunc + "\n\n" + typeCode,
225     test: test
226   });
227   return shader;
228 };
229
230 var generateTestShader = function(
231     shaderInfo, template, params, test) {
232   var input = shaderInfo.input;
233   var output = shaderInfo.output;
234   var feature = params.feature;
235   var testFunc = params.testFunc;
236   var extra = params.extra || '';
237
238   test = replaceParams(test, {
239     input: input,
240     output: output,
241     func: feature
242   });
243   var shader = replaceParams(template, {
244     extra: extra,
245     emu: '',
246     test: test
247   });
248   return shader;
249 };
250
251 function _reportResults(refData, refImg, testData, testImg, tolerance,
252                         width, height, ctx, imgData, wtu, canvas2d, consoleDiv) {
253   var same = true;
254   var firstFailure = null;
255   for (var yy = 0; yy < height; ++yy) {
256     for (var xx = 0; xx < width; ++xx) {
257       var offset = (yy * width + xx) * 4;
258       var imgOffset = ((height - yy - 1) * width + xx) * 4;
259       imgData.data[imgOffset + 0] = 0;
260       imgData.data[imgOffset + 1] = 0;
261       imgData.data[imgOffset + 2] = 0;
262       imgData.data[imgOffset + 3] = 255;
263       if (Math.abs(refData[offset + 0] - testData[offset + 0]) > tolerance ||
264           Math.abs(refData[offset + 1] - testData[offset + 1]) > tolerance ||
265           Math.abs(refData[offset + 2] - testData[offset + 2]) > tolerance ||
266           Math.abs(refData[offset + 3] - testData[offset + 3]) > tolerance) {
267         var detail = 'at (' + xx + ',' + yy + '): ref=(' +
268             refData[offset + 0] + ',' +
269             refData[offset + 1] + ',' +
270             refData[offset + 2] + ',' +
271             refData[offset + 3] + ')  test=(' +
272             testData[offset + 0] + ',' +
273             testData[offset + 1] + ',' +
274             testData[offset + 2] + ',' +
275             testData[offset + 3] + ') tolerance=' + tolerance;
276         consoleDiv.appendChild(document.createTextNode(detail));
277         consoleDiv.appendChild(document.createElement('br'));
278         if (!firstFailure) {
279           firstFailure = ": " + detail;
280         }
281         imgData.data[imgOffset] = 255;
282         same = false;
283       }
284     }
285   }
286
287   var diffImg = null;
288   if (!same) {
289     ctx.putImageData(imgData, 0, 0);
290     diffImg = wtu.makeImageFromCanvas(canvas2d);
291   }
292
293   var div = document.createElement("div");
294   div.className = "testimages";
295   wtu.insertImage(div, "ref", refImg);
296   wtu.insertImage(div, "test", testImg);
297   if (diffImg) {
298     wtu.insertImage(div, "diff", diffImg);
299   }
300   div.appendChild(document.createElement('br'));
301
302   consoleDiv.appendChild(div);
303
304   if (!same) {
305     testFailed("images are different" + (firstFailure ? firstFailure : ""));
306   } else {
307     testPassed("images are the same");
308   }
309
310   consoleDiv.appendChild(document.createElement('hr'));
311 }
312
313 var runFeatureTest = function(params) {
314   var wtu = WebGLTestUtils;
315   var gridRes = params.gridRes;
316   var vertexTolerance = params.tolerance || 0;
317   var fragmentTolerance = vertexTolerance;
318   if ('fragmentTolerance' in params)
319     fragmentTolerance = params.fragmentTolerance || 0;
320
321   description("Testing GLSL feature: " + params.feature);
322
323   var width = 32;
324   var height = 32;
325
326   var consoleDiv = document.getElementById("console");
327   var canvas = document.createElement('canvas');
328   canvas.width = width;
329   canvas.height = height;
330   var gl = wtu.create3DContext(canvas, { premultipliedAlpha: false });
331   if (!gl) {
332     testFailed("context does not exist");
333     finishTest();
334     return;
335   }
336
337   var canvas2d = document.createElement('canvas');
338   canvas2d.width = width;
339   canvas2d.height = height;
340   var ctx = canvas2d.getContext("2d");
341   var imgData = ctx.getImageData(0, 0, width, height);
342
343   var shaderInfos = [
344     { type: "vertex",
345       input: "color",
346       output: "vColor",
347       vertexShaderTemplate: vertexShaderTemplate,
348       fragmentShaderTemplate: baseFragmentShader,
349       tolerance: vertexTolerance
350     },
351     { type: "fragment",
352       input: "vColor",
353       output: "gl_FragColor",
354       vertexShaderTemplate: baseVertexShader,
355       fragmentShaderTemplate: fragmentShaderTemplate,
356       tolerance: fragmentTolerance
357     }
358   ];
359   for (var ss = 0; ss < shaderInfos.length; ++ss) {
360     var shaderInfo = shaderInfos[ss];
361     var tests = params.tests;
362     var testTypes = params.emuFuncs || (params.bvecTest ? bvecTypes : types);
363     // Test vertex shaders
364     for (var ii = 0; ii < tests.length; ++ii) {
365       var type = testTypes[ii];
366       if (params.simpleEmu) {
367         type = {
368           type: type.type,
369           code: params.simpleEmu
370         };
371       }
372       debug("");
373       var str = replaceParams(params.testFunc, {
374         func: params.feature,
375         type: type.type,
376         arg0: type.type
377       });
378       debug("Testing: " + str + " in " + shaderInfo.type + " shader");
379
380       var referenceVertexShaderSource = generateReferenceShader(
381           shaderInfo,
382           shaderInfo.vertexShaderTemplate,
383           params,
384           type,
385           tests[ii]);
386       var referenceFragmentShaderSource = generateReferenceShader(
387           shaderInfo,
388           shaderInfo.fragmentShaderTemplate,
389           params,
390           type,
391           tests[ii]);
392       var testVertexShaderSource = generateTestShader(
393           shaderInfo,
394           shaderInfo.vertexShaderTemplate,
395           params,
396           tests[ii]);
397       var testFragmentShaderSource = generateTestShader(
398           shaderInfo,
399           shaderInfo.fragmentShaderTemplate,
400           params,
401           tests[ii]);
402
403
404       debug("");
405       var referenceVertexShader = wtu.loadShader(gl, referenceVertexShaderSource, gl.VERTEX_SHADER, testFailed, true, 'reference');
406       var referenceFragmentShader = wtu.loadShader(gl, referenceFragmentShaderSource, gl.FRAGMENT_SHADER, testFailed, true, 'reference');
407       var testVertexShader = wtu.loadShader(gl, testVertexShaderSource, gl.VERTEX_SHADER, testFailed, true, 'test');
408       var testFragmentShader = wtu.loadShader(gl, testFragmentShaderSource, gl.FRAGMENT_SHADER, testFailed, true, 'test');
409       debug("");
410
411       var refData = draw(
412           referenceVertexShader, referenceFragmentShader);
413       var refImg = wtu.makeImageFromCanvas(canvas);
414       if (ss == 0) {
415         var testData = draw(
416             testVertexShader, referenceFragmentShader);
417       } else {
418         var testData = draw(
419             referenceVertexShader, testFragmentShader);
420       }
421       var testImg = wtu.makeImageFromCanvas(canvas);
422
423       _reportResults(refData, refImg, testData, testImg, shaderInfo.tolerance,
424                      width, height, ctx, imgData, wtu, canvas2d, consoleDiv);
425     }
426   }
427
428   finishTest();
429
430   function draw(vertexShader, fragmentShader) {
431     var program = wtu.createProgram(gl, vertexShader, fragmentShader, testFailed);
432
433     var posLoc = gl.getAttribLocation(program, "aPosition");
434     wtu.setupIndexedQuad(gl, gridRes, posLoc);
435
436     gl.useProgram(program);
437     wtu.clearAndDrawIndexedQuad(gl, gridRes, [0, 0, 255, 255]);
438     wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no errors from draw");
439
440     var img = new Uint8Array(width * height * 4);
441     gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, img);
442     return img;
443   }
444
445 };
446
447 var runBasicTest = function(params) {
448   var wtu = WebGLTestUtils;
449   var gridRes = params.gridRes;
450   var vertexTolerance = params.tolerance || 0;
451   var fragmentTolerance = vertexTolerance;
452   if ('fragmentTolerance' in params)
453     fragmentTolerance = params.fragmentTolerance || 0;
454
455   description("Testing : " + document.getElementsByTagName("title")[0].innerText);
456
457   var width = 32;
458   var height = 32;
459
460   var consoleDiv = document.getElementById("console");
461   var canvas = document.createElement('canvas');
462   canvas.width = width;
463   canvas.height = height;
464   var gl = wtu.create3DContext(canvas);
465   if (!gl) {
466     testFailed("context does not exist");
467     finishTest();
468     return;
469   }
470
471   var canvas2d = document.createElement('canvas');
472   canvas2d.width = width;
473   canvas2d.height = height;
474   var ctx = canvas2d.getContext("2d");
475   var imgData = ctx.getImageData(0, 0, width, height);
476
477   var shaderInfos = [
478     { type: "vertex",
479       input: "color",
480       output: "vColor",
481       vertexShaderTemplate: vertexShaderTemplate,
482       fragmentShaderTemplate: baseFragmentShader,
483       tolerance: vertexTolerance
484     },
485     { type: "fragment",
486       input: "vColor",
487       output: "gl_FragColor",
488       vertexShaderTemplate: baseVertexShader,
489       fragmentShaderTemplate: fragmentShaderTemplate,
490       tolerance: fragmentTolerance
491     }
492   ];
493   for (var ss = 0; ss < shaderInfos.length; ++ss) {
494     var shaderInfo = shaderInfos[ss];
495     var tests = params.tests;
496 //    var testTypes = params.emuFuncs || (params.bvecTest ? bvecTypes : types);
497     // Test vertex shaders
498     for (var ii = 0; ii < tests.length; ++ii) {
499       var test = tests[ii];
500       debug("");
501       debug("Testing: " + test.name + " in " + shaderInfo.type + " shader");
502
503       function genShader(shaderInfo, template, shader, subs) {
504         shader = replaceParams(shader, subs, {
505             input: shaderInfo.input,
506             output: shaderInfo.output
507           });
508         shader = replaceParams(template, subs, {
509             test: shader,
510             emu: "",
511             extra: ""
512           });
513         return shader;
514       }
515
516       var referenceVertexShaderSource = genShader(
517           shaderInfo,
518           shaderInfo.vertexShaderTemplate,
519           test.reference.shader,
520           test.reference.subs);
521       var referenceFragmentShaderSource = genShader(
522           shaderInfo,
523           shaderInfo.fragmentShaderTemplate,
524           test.reference.shader,
525           test.reference.subs);
526       var testVertexShaderSource = genShader(
527           shaderInfo,
528           shaderInfo.vertexShaderTemplate,
529           test.test.shader,
530           test.test.subs);
531       var testFragmentShaderSource = genShader(
532           shaderInfo,
533           shaderInfo.fragmentShaderTemplate,
534           test.test.shader,
535           test.test.subs);
536
537       debug("");
538       var referenceVertexShader = wtu.loadShader(gl, referenceVertexShaderSource, gl.VERTEX_SHADER, testFailed, true, 'reference');
539       var referenceFragmentShader = wtu.loadShader(gl, referenceFragmentShaderSource, gl.FRAGMENT_SHADER, testFailed, true, 'reference');
540       var testVertexShader = wtu.loadShader(gl, testVertexShaderSource, gl.VERTEX_SHADER, testFailed, true, 'test');
541       var testFragmentShader = wtu.loadShader(gl, testFragmentShaderSource, gl.FRAGMENT_SHADER, testFailed, true, 'test');
542       debug("");
543
544       var refData = draw(referenceVertexShader, referenceFragmentShader);
545       var refImg = wtu.makeImageFromCanvas(canvas);
546       if (ss == 0) {
547         var testData = draw(testVertexShader, referenceFragmentShader);
548       } else {
549         var testData = draw(referenceVertexShader, testFragmentShader);
550       }
551       var testImg = wtu.makeImageFromCanvas(canvas);
552
553       _reportResults(refData, refImg, testData, testImg, shaderInfo.tolerance,
554                      width, height, ctx, imgData, wtu, canvas2d, consoleDiv);
555     }
556   }
557
558   finishTest();
559
560   function draw(vertexShader, fragmentShader) {
561     var program = wtu.createProgram(gl, vertexShader, fragmentShader, testFailed);
562
563     var posLoc = gl.getAttribLocation(program, "aPosition");
564     wtu.setupIndexedQuad(gl, gridRes, posLoc);
565
566     gl.useProgram(program);
567     wtu.clearAndDrawIndexedQuad(gl, gridRes, [0, 0, 255, 255]);
568     wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no errors from draw");
569
570     var img = new Uint8Array(width * height * 4);
571     gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, img);
572     return img;
573   }
574
575 };
576
577 var runReferenceImageTest = function(params) {
578   var wtu = WebGLTestUtils;
579   var gridRes = params.gridRes;
580   var vertexTolerance = params.tolerance || 0;
581   var fragmentTolerance = vertexTolerance;
582   if ('fragmentTolerance' in params)
583     fragmentTolerance = params.fragmentTolerance || 0;
584
585   description("Testing GLSL feature: " + params.feature);
586
587   var width = 32;
588   var height = 32;
589
590   var consoleDiv = document.getElementById("console");
591   var canvas = document.createElement('canvas');
592   canvas.width = width;
593   canvas.height = height;
594   var gl = wtu.create3DContext(canvas, { antialias: false, premultipliedAlpha: false });
595   if (!gl) {
596     testFailed("context does not exist");
597     finishTest();
598     return;
599   }
600
601   var canvas2d = document.createElement('canvas');
602   canvas2d.width = width;
603   canvas2d.height = height;
604   var ctx = canvas2d.getContext("2d");
605   var imgData = ctx.getImageData(0, 0, width, height);
606
607   // State for reference images for vertex shader tests.
608   // These are drawn with the same tessellated grid as the test vertex
609   // shader so that the interpolation is identical. The grid is reused
610   // from test to test; the colors are changed.
611
612   var indexedQuadForReferenceVertexShader =
613     wtu.setupIndexedQuad(gl, gridRes, 0);
614   var referenceVertexShaderProgram =
615     wtu.setupProgram(gl, [ baseVertexShaderWithColor, baseFragmentShader ],
616                      ["aPosition", "aColor"]);
617   var referenceVertexShaderColorBuffer = gl.createBuffer();
618
619   var shaderInfos = [
620     { type: "vertex",
621       input: "color",
622       output: "vColor",
623       vertexShaderTemplate: vertexShaderTemplate,
624       fragmentShaderTemplate: baseFragmentShader,
625       tolerance: vertexTolerance
626     },
627     { type: "fragment",
628       input: "vColor",
629       output: "gl_FragColor",
630       vertexShaderTemplate: baseVertexShader,
631       fragmentShaderTemplate: fragmentShaderTemplate,
632       tolerance: fragmentTolerance
633     }
634   ];
635   for (var ss = 0; ss < shaderInfos.length; ++ss) {
636     var shaderInfo = shaderInfos[ss];
637     var tests = params.tests;
638     var testTypes = params.emuFuncs || (params.bvecTest ? bvecTypes : types);
639     // Test vertex shaders
640     for (var ii = 0; ii < tests.length; ++ii) {
641       var type = testTypes[ii];
642       var isVertex = (ss == 0);
643       debug("");
644       var str = replaceParams(params.testFunc, {
645         func: params.feature,
646         type: type.type,
647         arg0: type.type
648       });
649       debug("Testing: " + str + " in " + shaderInfo.type + " shader");
650
651       var referenceVertexShaderSource = generateReferenceShader(
652           shaderInfo,
653           shaderInfo.vertexShaderTemplate,
654           params,
655           type,
656           tests[ii].source);
657       var referenceFragmentShaderSource = generateReferenceShader(
658           shaderInfo,
659           shaderInfo.fragmentShaderTemplate,
660           params,
661           type,
662           tests[ii].source);
663       var testVertexShaderSource = generateTestShader(
664           shaderInfo,
665           shaderInfo.vertexShaderTemplate,
666           params,
667           tests[ii].source);
668       var testFragmentShaderSource = generateTestShader(
669           shaderInfo,
670           shaderInfo.fragmentShaderTemplate,
671           params,
672           tests[ii].source);
673       var referenceTextureOrArray = generateReferenceImage(
674           gl,
675           tests[ii].generator,
676           isVertex ? gridRes : width,
677           isVertex ? gridRes : height,
678           isVertex);
679
680       debug("");
681       var testVertexShader = wtu.loadShader(gl, testVertexShaderSource, gl.VERTEX_SHADER, testFailed, true);
682       var testFragmentShader = wtu.loadShader(gl, testFragmentShaderSource, gl.FRAGMENT_SHADER, testFailed, true);
683       debug("");
684
685       var refData;
686       if (isVertex) {
687         refData = drawVertexReferenceImage(referenceTextureOrArray);
688       } else {
689         refData = drawFragmentReferenceImage(referenceTextureOrArray);
690       }
691       var refImg = wtu.makeImageFromCanvas(canvas);
692       var testData;
693       if (isVertex) {
694         var referenceFragmentShader = wtu.loadShader(gl, referenceFragmentShaderSource, gl.FRAGMENT_SHADER, testFailed);
695         testData = draw(
696           testVertexShader, referenceFragmentShader);
697       } else {
698         var referenceVertexShader = wtu.loadShader(gl, referenceVertexShaderSource, gl.VERTEX_SHADER, testFailed);
699         testData = draw(
700           referenceVertexShader, testFragmentShader);
701       }
702       var testImg = wtu.makeImageFromCanvas(canvas);
703       var testTolerance = shaderInfo.tolerance;
704       // Provide per-test tolerance so that we can increase it only for those desired.
705       if ('tolerance' in tests[ii])
706         testTolerance = tests[ii].tolerance || 0;
707       _reportResults(refData, refImg, testData, testImg, testTolerance,
708                      width, height, ctx, imgData, wtu, canvas2d, consoleDiv);
709     }
710   }
711
712   finishTest();
713
714   function draw(vertexShader, fragmentShader) {
715     var program = wtu.createProgram(gl, vertexShader, fragmentShader, testFailed);
716
717     var posLoc = gl.getAttribLocation(program, "aPosition");
718     wtu.setupIndexedQuad(gl, gridRes, posLoc);
719
720     gl.useProgram(program);
721     wtu.clearAndDrawIndexedQuad(gl, gridRes, [0, 0, 255, 255]);
722     wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no errors from draw");
723
724     var img = new Uint8Array(width * height * 4);
725     gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, img);
726     return img;
727   }
728
729   function drawVertexReferenceImage(colors) {
730     gl.bindBuffer(gl.ARRAY_BUFFER, indexedQuadForReferenceVertexShader[0]);
731     gl.enableVertexAttribArray(0);
732     gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);
733     gl.bindBuffer(gl.ARRAY_BUFFER, referenceVertexShaderColorBuffer);
734     gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW);
735     gl.enableVertexAttribArray(1);
736     gl.vertexAttribPointer(1, 4, gl.UNSIGNED_BYTE, true, 0, 0);
737     gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexedQuadForReferenceVertexShader[1]);
738     gl.useProgram(referenceVertexShaderProgram);
739     wtu.clearAndDrawIndexedQuad(gl, gridRes);
740     gl.disableVertexAttribArray(0);
741     gl.disableVertexAttribArray(1);
742     wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no errors from draw");
743
744     var img = new Uint8Array(width * height * 4);
745     gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, img);
746     return img;
747   }
748
749   function drawFragmentReferenceImage(texture) {
750     var program = wtu.setupTexturedQuad(gl);
751
752     gl.activeTexture(gl.TEXTURE0);
753     gl.bindTexture(gl.TEXTURE_2D, texture);
754     var texLoc = gl.getUniformLocation(program, "tex");
755     gl.uniform1i(texLoc, 0);
756     wtu.clearAndDrawUnitQuad(gl);
757     wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no errors from draw");
758
759     var img = new Uint8Array(width * height * 4);
760     gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, img);
761     return img;
762   }
763
764   /**
765    * Creates and returns either a Uint8Array (for vertex shaders) or
766    * WebGLTexture (for fragment shaders) containing the reference
767    * image for the function being tested. Exactly how the function is
768    * evaluated, and the size of the returned texture or array, depends on
769    * whether we are testing a vertex or fragment shader. If a fragment
770    * shader, the function is evaluated at the pixel centers. If a
771    * vertex shader, the function is evaluated at the triangle's
772    * vertices.
773    *
774    * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use to generate texture objects.
775    * @param {!function(number,number,number,number): !Array.<number>} generator The reference image generator function.
776    * @param {number} width The width of the texture to generate if testing a fragment shader; the grid resolution if testing a vertex shader.
777    * @param {number} height The height of the texture to generate if testing a fragment shader; the grid resolution if testing a vertex shader.
778    * @param {boolean} isVertex True if generating a reference image for a vertex shader; false if for a fragment shader.
779    * @return {!WebGLTexture|!Uint8Array} The texture object or array that was generated.
780    */
781   function generateReferenceImage(
782     gl,
783     generator,
784     width,
785     height,
786     isVertex) {
787
788     // Note: the math in this function must match that in the vertex and
789     // fragment shader templates above.
790     function computeTexCoord(x) {
791       return x * 0.5 + 0.5;
792     }
793
794     function computeVertexColor(texCoordX, texCoordY) {
795       return [ texCoordX,
796                texCoordY,
797                texCoordX * texCoordY,
798                (1.0 - texCoordX) * texCoordY * 0.5 + 0.5 ];
799     }
800
801     /**
802      * Computes fragment color according to the algorithm used for interpolation
803      * in OpenGL (GLES 2.0 spec 3.5.1, OpenGL 4.3 spec 14.6.1).
804      */
805     function computeInterpolatedColor(texCoordX, texCoordY) {
806       // Calculate grid line indexes below and to the left from texCoord.
807       var gridBottom = Math.floor(texCoordY * gridRes);
808       if (gridBottom == gridRes) {
809         --gridBottom;
810       }
811       var gridLeft = Math.floor(texCoordX * gridRes);
812       if (gridLeft == gridRes) {
813         --gridLeft;
814       }
815
816       // Calculate coordinates relative to the grid cell.
817       var cellX = texCoordX * gridRes - gridLeft;
818       var cellY = texCoordY * gridRes - gridBottom;
819
820       // Barycentric coordinates inside either triangle ACD or ABC
821       // are used as weights for the vertex colors in the corners:
822       // A--B
823       // |\ |
824       // | \|
825       // D--C
826
827       var aColor = computeVertexColor(gridLeft / gridRes, (gridBottom + 1) / gridRes);
828       var bColor = computeVertexColor((gridLeft + 1) / gridRes, (gridBottom + 1) / gridRes);
829       var cColor = computeVertexColor((gridLeft + 1) / gridRes, gridBottom / gridRes);
830       var dColor = computeVertexColor(gridLeft / gridRes, gridBottom / gridRes);
831
832       // Calculate weights.
833       var a, b, c, d;
834
835       if (cellX + cellY < 1) {
836         // In bottom triangle ACD.
837         a = cellY; // area of triangle C-D-(cellX, cellY) relative to ACD
838         c = cellX; // area of triangle D-A-(cellX, cellY) relative to ACD
839         d = 1 - a - c;
840         b = 0;
841       } else {
842         // In top triangle ABC.
843         a = 1 - cellX; // area of the triangle B-C-(cellX, cellY) relative to ABC
844         c = 1 - cellY; // area of the triangle A-B-(cellX, cellY) relative to ABC
845         b = 1 - a - c;
846         d = 0;
847       }
848
849       var interpolated = [];
850       for (var ii = 0; ii < aColor.length; ++ii) {
851         interpolated.push(a * aColor[ii] + b * bColor[ii] + c * cColor[ii] + d * dColor[ii]);
852       }
853       return interpolated;
854     }
855
856     function clamp(value, minVal, maxVal) {
857       return Math.max(minVal, Math.min(value, maxVal));
858     }
859
860     // Evaluates the function at clip coordinates (px,py), storing the
861     // result in the array "pixel". Each channel's result is clamped
862     // between 0 and 255.
863     function evaluateAtClipCoords(px, py, pixel, colorFunc) {
864       var tcx = computeTexCoord(px);
865       var tcy = computeTexCoord(py);
866
867       var color = colorFunc(tcx, tcy);
868
869       var output = generator(color[0], color[1], color[2], color[3]);
870
871       // Multiply by 256 to get even distribution for all values between 0 and 1.
872       // Use rounding rather than truncation to more closely match the GPU's behavior.
873       pixel[0] = clamp(Math.round(256 * output[0]), 0, 255);
874       pixel[1] = clamp(Math.round(256 * output[1]), 0, 255);
875       pixel[2] = clamp(Math.round(256 * output[2]), 0, 255);
876       pixel[3] = clamp(Math.round(256 * output[3]), 0, 255);
877     }
878
879     function generateFragmentReference() {
880       var data = new Uint8Array(4 * width * height);
881
882       var horizTexel = 1.0 / width;
883       var vertTexel = 1.0 / height;
884       var halfHorizTexel = 0.5 * horizTexel;
885       var halfVertTexel = 0.5 * vertTexel;
886
887       var pixel = new Array(4);
888
889       for (var yi = 0; yi < height; ++yi) {
890         for (var xi = 0; xi < width; ++xi) {
891           // The function must be evaluated at pixel centers.
892
893           // Compute desired position in clip space
894           var px = -1.0 + 2.0 * (halfHorizTexel + xi * horizTexel);
895           var py = -1.0 + 2.0 * (halfVertTexel + yi * vertTexel);
896
897           evaluateAtClipCoords(px, py, pixel, computeInterpolatedColor);
898           var index = 4 * (width * yi + xi);
899           data[index + 0] = pixel[0];
900           data[index + 1] = pixel[1];
901           data[index + 2] = pixel[2];
902           data[index + 3] = pixel[3];
903         }
904       }
905
906       var texture = gl.createTexture();
907       gl.bindTexture(gl.TEXTURE_2D, texture);
908       gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
909       gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
910       gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
911       gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
912       gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0,
913                     gl.RGBA, gl.UNSIGNED_BYTE, data);
914       return texture;
915     }
916
917     function generateVertexReference() {
918       // We generate a Uint8Array which contains the evaluation of the
919       // function at the vertices of the triangle mesh. It is expected
920       // that the width and the height are identical, and equivalent
921       // to the grid resolution.
922       if (width != height) {
923         throw "width and height must be equal";
924       }
925
926       var texSize = 1 + width;
927       var data = new Uint8Array(4 * texSize * texSize);
928
929       var step = 2.0 / width;
930
931       var pixel = new Array(4);
932
933       for (var yi = 0; yi < texSize; ++yi) {
934         for (var xi = 0; xi < texSize; ++xi) {
935           // The function is evaluated at the triangles' vertices.
936
937           // Compute desired position in clip space
938           var px = -1.0 + (xi * step);
939           var py = -1.0 + (yi * step);
940
941           evaluateAtClipCoords(px, py, pixel, computeVertexColor);
942           var index = 4 * (texSize * yi + xi);
943           data[index + 0] = pixel[0];
944           data[index + 1] = pixel[1];
945           data[index + 2] = pixel[2];
946           data[index + 3] = pixel[3];
947         }
948       }
949
950       return data;
951     }
952
953     //----------------------------------------------------------------------
954     // Body of generateReferenceImage
955     //
956
957     if (isVertex) {
958       return generateVertexReference();
959     } else {
960       return generateFragmentReference();
961     }
962   }
963 };
964
965 return {
966   /**
967    * runs a bunch of GLSL tests using the passed in parameters
968    * The parameters are:
969    *
970    * feature:
971    *    the name of the function being tested (eg, sin, dot,
972    *    normalize)
973    *
974    * testFunc:
975    *    The prototype of function to be tested not including the
976    *    return type.
977    *
978    * emuFunc:
979    *    A base function that can be used to generate emulation
980    *    functions. Example for 'ceil'
981    *
982    *      float $(func)_base(float value) {
983    *        float m = mod(value, 1.0);
984    *        return m != 0.0 ? (value + 1.0 - m) : value;
985    *      }
986    *
987    * args:
988    *    The arguments to the function
989    *
990    * baseArgs: (optional)
991    *    The arguments when a base function is used to create an
992    *    emulation function. For example 'float sign_base(float v)'
993    *    is used to implemenent vec2 sign_emu(vec2 v).
994    *
995    * simpleEmu:
996    *    if supplied, the code that can be used to generate all
997    *    functions for all types.
998    *
999    *    Example for 'normalize':
1000    *
1001    *        $(type) $(func)_emu($(args)) {
1002    *           return value / length(value);
1003    *        }
1004    *
1005    * gridRes: (optional)
1006    *    The resolution of the mesh to generate. The default is a
1007    *    1x1 grid but many vertex shaders need a higher resolution
1008    *    otherwise the only values passed in are the 4 corners
1009    *    which often have the same value.
1010    *
1011    * tests:
1012    *    The code for each test. It is assumed the tests are for
1013    *    float, vec2, vec3, vec4 in that order.
1014    *
1015    * tolerance: (optional)
1016    *    Allow some tolerance in the comparisons. The tolerance is applied to 
1017    *    both vertex and fragment shaders. The default tolerance is 0, meaning 
1018    *    the values have to be identical.
1019    *
1020    * fragmentTolerance: (optional)
1021    *    Specify a tolerance which only applies to fragment shaders. The 
1022    *    fragment-only tolerance will override the shared tolerance for 
1023    *    fragment shaders if both are specified. Fragment shaders usually
1024    *    use mediump float precision so they sometimes require higher tolerance
1025    *    than vertex shaders which use highp by default.
1026    */
1027   runFeatureTest: runFeatureTest,
1028
1029   /*
1030    * Runs a bunch of GLSL tests using the passed in parameters
1031    *
1032    * The parameters are:
1033    *
1034    * tests:
1035    *    Array of tests. For each test the following parameters are expected
1036    *
1037    *    name:
1038    *       some description of the test
1039    *    reference:
1040    *       parameters for the reference shader (see below)
1041    *    test:
1042    *       parameters for the test shader (see below)
1043    *
1044    *    The parameter for the reference and test shaders are
1045    *
1046    *    shader: the GLSL for the shader
1047    *    subs: any substitutions you wish to define for the shader.
1048    *
1049    *    Each shader is created from a basic template that
1050    *    defines an input and an output. You can see the
1051    *    templates at the top of this file. The input and output
1052    *    change depending on whether or not we are generating
1053    *    a vertex or fragment shader.
1054    *
1055    *    All this code function does is a bunch of string substitutions.
1056    *    A substitution is defined by $(name). If name is found in
1057    *    the 'subs' parameter it is replaced. 4 special names exist.
1058    *
1059    *    'input' the input to your GLSL. Always a vec4. All change
1060    *    from 0 to 1 over the quad to be drawn.
1061    *
1062    *    'output' the output color. Also a vec4
1063    *
1064    *    'emu' a place to insert extra stuff
1065    *    'extra' a place to insert extra stuff.
1066    *
1067    *    You can think of the templates like this
1068    *
1069    *       $(extra)
1070    *       $(emu)
1071    *
1072    *       void main() {
1073    *          // do math to calculate input
1074    *          ...
1075    *
1076    *          $(shader)
1077    *       }
1078    *
1079    *    Your shader first has any subs you provided applied as well
1080    *    as 'input' and 'output'
1081    *
1082    *    It is then inserted into the template which is also provided
1083    *    with your subs.
1084    *
1085    * gridRes: (optional)
1086    *    The resolution of the mesh to generate. The default is a
1087    *    1x1 grid but many vertex shaders need a higher resolution
1088    *    otherwise the only values passed in are the 4 corners
1089    *    which often have the same value.
1090    *
1091    * tolerance: (optional)
1092    *    Allow some tolerance in the comparisons. The tolerance is applied to
1093    *    both vertex and fragment shaders. The default tolerance is 0, meaning
1094    *    the values have to be identical.
1095    *
1096    * fragmentTolerance: (optional)
1097    *    Specify a tolerance which only applies to fragment shaders. The
1098    *    fragment-only tolerance will override the shared tolerance for
1099    *    fragment shaders if both are specified. Fragment shaders usually
1100    *    use mediump float precision so they sometimes require higher tolerance
1101    *    than vertex shaders which use highp.
1102    */
1103   runBasicTest: runBasicTest,
1104
1105   /**
1106    * Runs a bunch of GLSL tests using the passed in parameters. The
1107    * expected results are computed as a reference image in JavaScript
1108    * instead of on the GPU. The parameters are:
1109    *
1110    * feature:
1111    *    the name of the function being tested (eg, sin, dot,
1112    *    normalize)
1113    *
1114    * testFunc:
1115    *    The prototype of function to be tested not including the
1116    *    return type.
1117    *
1118    * args:
1119    *    The arguments to the function
1120    *
1121    * gridRes: (optional)
1122    *    The resolution of the mesh to generate. The default is a
1123    *    1x1 grid but many vertex shaders need a higher resolution
1124    *    otherwise the only values passed in are the 4 corners
1125    *    which often have the same value.
1126    *
1127    * tests:
1128    *    Array of tests. It is assumed the tests are for float, vec2,
1129    *    vec3, vec4 in that order. For each test the following
1130    *    parameters are expected:
1131    *
1132    *       source: the GLSL source code for the tests
1133    *
1134    *       generator: a JavaScript function taking four parameters
1135    *       which evaluates the same function as the GLSL source,
1136    *       returning its result as a newly allocated array.
1137    *
1138    *       tolerance: (optional) a per-test tolerance.
1139    *
1140    * extra: (optional)
1141    *    Extra GLSL code inserted at the top of each test's shader.
1142    * 
1143    * tolerance: (optional)
1144    *    Allow some tolerance in the comparisons. The tolerance is applied to 
1145    *    both vertex and fragment shaders. The default tolerance is 0, meaning 
1146    *    the values have to be identical.
1147    *
1148    * fragmentTolerance: (optional)
1149    *    Specify a tolerance which only applies to fragment shaders. The 
1150    *    fragment-only tolerance will override the shared tolerance for 
1151    *    fragment shaders if both are specified. Fragment shaders usually
1152    *    use mediump float precision so they sometimes require higher tolerance
1153    *    than vertex shaders which use highp.
1154    */
1155   runReferenceImageTest: runReferenceImageTest,
1156
1157   none: false
1158 };
1159
1160 }());
1161