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