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