OLD | NEW |
(Empty) | |
| 1 GLSLGenerator = (function() { |
| 2 |
| 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"); |
| 22 |
| 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"); |
| 38 |
| 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"); |
| 54 |
| 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"); |
| 66 |
| 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 ]; |
| 102 |
| 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 ]; |
| 132 |
| 133 var replaceRE = /\$\((\w+)\)/g; |
| 134 |
| 135 var replaceParams = function(str, params) { |
| 136 return str.replace(replaceRE, function(str, p1, offset, s) { |
| 137 if (params[p1] === undefined) { |
| 138 throw "unknown string param '" + p1 + "'"; |
| 139 } |
| 140 return params[p1]; |
| 141 }); |
| 142 }; |
| 143 |
| 144 var generateReferenceShader = function( |
| 145 shaderInfo, template, params, typeInfo, test) { |
| 146 var input = shaderInfo.input; |
| 147 var output = shaderInfo.output; |
| 148 var feature = params.feature; |
| 149 var testFunc = params.testFunc; |
| 150 var emuFunc = params.emuFunc || ""; |
| 151 var extra = params.extra || ''; |
| 152 var args = params.args || "$(type) value"; |
| 153 var type = typeInfo.type; |
| 154 var typeCode = typeInfo.code; |
| 155 |
| 156 var baseArgs = params.baseArgs || "value$(field)"; |
| 157 var baseArgsX = replaceParams(baseArgs, {field: ".x"}); |
| 158 var baseArgsY = replaceParams(baseArgs, {field: ".y"}); |
| 159 var baseArgsZ = replaceParams(baseArgs, {field: ".z"}); |
| 160 var baseArgsW = replaceParams(baseArgs, {field: ".w"}); |
| 161 var baseArgs = replaceParams(baseArgs, {field: ""}); |
| 162 |
| 163 test = replaceParams(test, { |
| 164 input: input, |
| 165 output: output, |
| 166 func: feature + "_emu" |
| 167 }); |
| 168 emuFunc = replaceParams(emuFunc, { |
| 169 func: feature |
| 170 }); |
| 171 args = replaceParams(args, { |
| 172 type: type |
| 173 }); |
| 174 typeCode = replaceParams(typeCode, { |
| 175 func: feature, |
| 176 type: type, |
| 177 args: args, |
| 178 baseArgs: baseArgs, |
| 179 baseArgsX: baseArgsX, |
| 180 baseArgsY: baseArgsY, |
| 181 baseArgsZ: baseArgsZ, |
| 182 baseArgsW: baseArgsW |
| 183 }); |
| 184 var shader = replaceParams(template, { |
| 185 extra: extra, |
| 186 emu: emuFunc + "\n\n" + typeCode, |
| 187 test: test |
| 188 }); |
| 189 return shader; |
| 190 }; |
| 191 |
| 192 var generateTestShader = function( |
| 193 shaderInfo, template, params, test) { |
| 194 var input = shaderInfo.input; |
| 195 var output = shaderInfo.output; |
| 196 var feature = params.feature; |
| 197 var testFunc = params.testFunc; |
| 198 var extra = params.extra || ''; |
| 199 |
| 200 test = replaceParams(test, { |
| 201 input: input, |
| 202 output: output, |
| 203 func: feature |
| 204 }); |
| 205 var shader = replaceParams(template, { |
| 206 extra: extra, |
| 207 emu: '', |
| 208 test: test |
| 209 }); |
| 210 return shader; |
| 211 }; |
| 212 |
| 213 var makeImage = function(canvas) { |
| 214 var img = document.createElement('img'); |
| 215 img.src = canvas.toDataURL(); |
| 216 return img; |
| 217 }; |
| 218 |
| 219 var runFeatureTest = function(params) { |
| 220 if (window.initNonKhronosFramework) { |
| 221 window.initNonKhronosFramework(false); |
| 222 } |
| 223 |
| 224 var wtu = WebGLTestUtils; |
| 225 var gridRes = params.gridRes; |
| 226 var tolerance = params.tolerance || 0; |
| 227 |
| 228 description("Testing GLSL feature: " + params.feature); |
| 229 |
| 230 var width = 32; |
| 231 var height = 32; |
| 232 |
| 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 } |
| 243 |
| 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); |
| 249 |
| 250 var shaderInfos = [ |
| 251 { type: "vertex", |
| 252 input: "color", |
| 253 output: "vColor", |
| 254 vertexShaderTemplate: vertexShaderTemplate, |
| 255 fragmentShaderTemplate: baseFragmentShader |
| 256 }, |
| 257 { type: "fragment", |
| 258 input: "vColor", |
| 259 output: "gl_FragColor", |
| 260 vertexShaderTemplate: baseVertexShader, |
| 261 fragmentShaderTemplate: fragmentShaderTemplate |
| 262 } |
| 263 ]; |
| 264 for (var ss = 0; ss < shaderInfos.length; ++ss) { |
| 265 var shaderInfo = shaderInfos[ss]; |
| 266 var tests = params.tests; |
| 267 var testTypes = params.emuFuncs || (params.bvecTest ? bvecTypes : types); |
| 268 // Test vertex shaders |
| 269 for (var ii = 0; ii < tests.length; ++ii) { |
| 270 var type = testTypes[ii]; |
| 271 if (params.simpleEmu) { |
| 272 type = { |
| 273 type: type.type, |
| 274 code: params.simpleEmu |
| 275 }; |
| 276 } |
| 277 debug(""); |
| 278 var str = replaceParams(params.testFunc, { |
| 279 func: params.feature, |
| 280 type: type.type, |
| 281 arg0: type.type |
| 282 }); |
| 283 debug("Testing: " + str + " in " + shaderInfo.type + " shader"); |
| 284 |
| 285 var referenceVertexShaderSource = generateReferenceShader( |
| 286 shaderInfo, |
| 287 shaderInfo.vertexShaderTemplate, |
| 288 params, |
| 289 type, |
| 290 tests[ii]); |
| 291 var referenceFragmentShaderSource = generateReferenceShader( |
| 292 shaderInfo, |
| 293 shaderInfo.fragmentShaderTemplate, |
| 294 params, |
| 295 type, |
| 296 tests[ii]); |
| 297 var testVertexShaderSource = generateTestShader( |
| 298 shaderInfo, |
| 299 shaderInfo.vertexShaderTemplate, |
| 300 params, |
| 301 tests[ii]); |
| 302 var testFragmentShaderSource = generateTestShader( |
| 303 shaderInfo, |
| 304 shaderInfo.fragmentShaderTemplate, |
| 305 params, |
| 306 tests[ii]); |
| 307 |
| 308 debug(""); |
| 309 addShaderSource( |
| 310 "reference vertex shader", referenceVertexShaderSource); |
| 311 addShaderSource( |
| 312 "reference fragment shader", referenceFragmentShaderSource); |
| 313 addShaderSource( |
| 314 "test vertex shader", testVertexShaderSource); |
| 315 addShaderSource( |
| 316 "test fragment shader", testFragmentShaderSource); |
| 317 debug(""); |
| 318 |
| 319 var refData = draw( |
| 320 canvas, referenceVertexShaderSource, referenceFragmentShaderSource); |
| 321 var refImg = makeImage(canvas); |
| 322 if (ss == 0) { |
| 323 var testData = draw( |
| 324 canvas, testVertexShaderSource, referenceFragmentShaderSource); |
| 325 } else { |
| 326 var testData = draw( |
| 327 canvas, referenceVertexShaderSource, testFragmentShaderSource); |
| 328 } |
| 329 var testImg = makeImage(canvas); |
| 330 |
| 331 reportResults(refData, refImg, testData, testImg); |
| 332 } |
| 333 } |
| 334 |
| 335 finishTest(); |
| 336 |
| 337 function addShaderSource(label, source) { |
| 338 var div = document.createElement("div"); |
| 339 var s = document.createElement("pre"); |
| 340 s.className = "shader-source"; |
| 341 s.style.display = "none"; |
| 342 var ol = document.createElement("ol"); |
| 343 //s.appendChild(document.createTextNode(source)); |
| 344 var lines = source.split("\n"); |
| 345 for (var ii = 0; ii < lines.length; ++ii) { |
| 346 var line = lines[ii]; |
| 347 var li = document.createElement("li"); |
| 348 li.appendChild(document.createTextNode(line)); |
| 349 ol.appendChild(li); |
| 350 } |
| 351 s.appendChild(ol); |
| 352 var l = document.createElement("a"); |
| 353 l.href = "show-shader-source"; |
| 354 l.appendChild(document.createTextNode(label)); |
| 355 l.addEventListener('click', function(event) { |
| 356 if (event.preventDefault) { |
| 357 event.preventDefault(); |
| 358 } |
| 359 s.style.display = (s.style.display == 'none') ? 'block' : 'none'; |
| 360 return false; |
| 361 }, false); |
| 362 div.appendChild(l); |
| 363 div.appendChild(s); |
| 364 console.appendChild(div); |
| 365 } |
| 366 |
| 367 function reportResults(refData, refImage, testData, testImage) { |
| 368 var same = true; |
| 369 for (var yy = 0; yy < height; ++yy) { |
| 370 for (var xx = 0; xx < width; ++xx) { |
| 371 var offset = (yy * width + xx) * 4; |
| 372 var imgOffset = ((height - yy - 1) * width + xx) * 4; |
| 373 imgData.data[imgOffset + 0] = 0; |
| 374 imgData.data[imgOffset + 1] = 0; |
| 375 imgData.data[imgOffset + 2] = 0; |
| 376 imgData.data[imgOffset + 3] = 255; |
| 377 if (Math.abs(refData[offset + 0] - testData[offset + 0]) > tolerance || |
| 378 Math.abs(refData[offset + 1] - testData[offset + 1]) > tolerance || |
| 379 Math.abs(refData[offset + 2] - testData[offset + 2]) > tolerance || |
| 380 Math.abs(refData[offset + 3] - testData[offset + 3]) > tolerance) { |
| 381 imgData.data[imgOffset] = 255; |
| 382 same = false; |
| 383 } |
| 384 } |
| 385 } |
| 386 |
| 387 var diffImg = null; |
| 388 if (!same) { |
| 389 ctx.putImageData(imgData, 0, 0); |
| 390 diffImg = makeImage(canvas2d); |
| 391 } |
| 392 |
| 393 var div = document.createElement("div"); |
| 394 div.className = "testimages"; |
| 395 insertImg(div, "ref", refImg); |
| 396 insertImg(div, "test", testImg); |
| 397 if (diffImg) { |
| 398 insertImg(div, "diff", diffImg); |
| 399 } |
| 400 div.appendChild(document.createElement('br')); |
| 401 |
| 402 function insertImg(element, caption, img) { |
| 403 var div = document.createElement("div"); |
| 404 div.appendChild(img); |
| 405 var label = document.createElement("div"); |
| 406 label.appendChild(document.createTextNode(caption)); |
| 407 div.appendChild(label); |
| 408 element.appendChild(div); |
| 409 } |
| 410 |
| 411 console.appendChild(div); |
| 412 |
| 413 if (!same) { |
| 414 testFailed("images are different"); |
| 415 } else { |
| 416 testPassed("images are the same"); |
| 417 } |
| 418 |
| 419 console.appendChild(document.createElement('hr')); |
| 420 } |
| 421 |
| 422 function draw(canvas, vsSource, fsSource) { |
| 423 var program = wtu.loadProgram(gl, vsSource, fsSource, testFailed); |
| 424 |
| 425 var posLoc = gl.getAttribLocation(program, "aPosition"); |
| 426 WebGLTestUtils.setupQuad(gl, gridRes, posLoc); |
| 427 |
| 428 gl.useProgram(program); |
| 429 gl.clearColor(0, 0, 1, 1); |
| 430 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); |
| 431 gl.drawElements(gl.TRIANGLES, gridRes * gridRes * 6, gl.UNSIGNED_SHORT, 0); |
| 432 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no errors from draw"); |
| 433 |
| 434 var img = new Uint8Array(width * height * 4); |
| 435 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, img); |
| 436 return img; |
| 437 } |
| 438 |
| 439 }; |
| 440 |
| 441 return { |
| 442 /** |
| 443 * runs a bunch of GLSL tests using the passed in parameters |
| 444 * The parameters are: |
| 445 * |
| 446 * feature: |
| 447 * the name of the function being tested (eg, sin, dot, |
| 448 * normalize) |
| 449 * |
| 450 * testFunc: |
| 451 * The prototype of function to be tested not including the |
| 452 * return type. |
| 453 * |
| 454 * emuFunc: |
| 455 * A base function that can be used to generate emulation |
| 456 * functions. Example for 'ceil' |
| 457 * |
| 458 * float $(func)_base(float value) { |
| 459 * float m = mod(value, 1.0); |
| 460 * return m != 0.0 ? (value + 1.0 - m) : value; |
| 461 * } |
| 462 * |
| 463 * args: |
| 464 * The arguments to the function |
| 465 * |
| 466 * baseArgs: (optional) |
| 467 * The arguments when a base function is used to create an |
| 468 * emulation function. For example 'float sign_base(float v)' |
| 469 * is used to implemenent vec2 sign_emu(vec2 v). |
| 470 * |
| 471 * simpleEmu: |
| 472 * if supplied, the code that can be used to generate all |
| 473 * functions for all types. |
| 474 * |
| 475 * Example for 'normalize': |
| 476 * |
| 477 * $(type) $(func)_emu($(args)) { |
| 478 * return value / length(value); |
| 479 * } |
| 480 * |
| 481 * gridRes: (optional) |
| 482 * The resolution of the mesh to generate. The default is a |
| 483 * 1x1 grid but many vertex shaders need a higher resolution |
| 484 * otherwise the only values passed in are the 4 corners |
| 485 * which often have the same value. |
| 486 * |
| 487 * tests: |
| 488 * The code for each test. It is assumed the tests are for |
| 489 * float, vec2, vec3, vec4 in that order. |
| 490 */ |
| 491 runFeatureTest: runFeatureTest, |
| 492 |
| 493 none: false |
| 494 }; |
| 495 |
| 496 }()); |
| 497 |
OLD | NEW |