content/canvas/test/webgl-conformance/conformance/resources/glsl-generator.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

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    *        }
  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.
  1007    * tests:
  1008    *    The code for each test. It is assumed the tests are for
  1009    *    float, vec2, vec3, vec4 in that order.
  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.
  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
  1028    * The parameters are:
  1030    * tests:
  1031    *    Array of tests. For each test the following parameters are expected
  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)
  1040    *    The parameter for the reference and test shaders are
  1042    *    shader: the GLSL for the shader
  1043    *    subs: any substitutions you wish to define for the shader.
  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.
  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.
  1055    *    'input' the input to your GLSL. Always a vec4. All change
  1056    *    from 0 to 1 over the quad to be drawn.
  1058    *    'output' the output color. Also a vec4
  1060    *    'emu' a place to insert extra stuff
  1061    *    'extra' a place to insert extra stuff.
  1063    *    You can think of the templates like this
  1065    *       $(extra)
  1066    *       $(emu)
  1068    *       void main() {
  1069    *          // do math to calculate input
  1070    *          ...
  1072    *          $(shader)
  1073    *       }
  1075    *    Your shader first has any subs you provided applied as well
  1076    *    as 'input' and 'output'
  1078    *    It is then inserted into the template which is also provided
  1079    *    with your subs.
  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.
  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.
  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:
  1106    * feature:
  1107    *    the name of the function being tested (eg, sin, dot,
  1108    *    normalize)
  1110    * testFunc:
  1111    *    The prototype of function to be tested not including the
  1112    *    return type.
  1114    * args:
  1115    *    The arguments to the function
  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.
  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:
  1128    *       source: the GLSL source code for the tests
  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.
  1134    *       tolerance: (optional) a per-test tolerance.
  1136    * extra: (optional)
  1137    *    Extra GLSL code inserted at the top of each test's shader.
  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.
  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 }());

mercurial