content/canvas/test/webgl-conformance/conformance/resources/webgl-test-utils.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.

michael@0 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
michael@0 2 // Use of this source code is governed by a BSD-style license that can be
michael@0 3 // found in the LICENSE file.
michael@0 4
michael@0 5 WebGLTestUtils = (function() {
michael@0 6
michael@0 7 /**
michael@0 8 * Wrapped logging function.
michael@0 9 * @param {string} msg The message to log.
michael@0 10 */
michael@0 11 var log = function(msg) {
michael@0 12 if (window.console && window.console.log) {
michael@0 13 window.console.log(msg);
michael@0 14 }
michael@0 15 };
michael@0 16
michael@0 17 /**
michael@0 18 * Wrapped logging function.
michael@0 19 * @param {string} msg The message to log.
michael@0 20 */
michael@0 21 var error = function(msg) {
michael@0 22 if (window.console) {
michael@0 23 if (window.console.error) {
michael@0 24 window.console.error(msg);
michael@0 25 }
michael@0 26 else if (window.console.log) {
michael@0 27 window.console.log(msg);
michael@0 28 }
michael@0 29 }
michael@0 30 };
michael@0 31
michael@0 32 /**
michael@0 33 * Turn off all logging.
michael@0 34 */
michael@0 35 var loggingOff = function() {
michael@0 36 log = function() {};
michael@0 37 error = function() {};
michael@0 38 };
michael@0 39
michael@0 40 /**
michael@0 41 * Converts a WebGL enum to a string
michael@0 42 * @param {!WebGLContext} gl The WebGLContext to use.
michael@0 43 * @param {number} value The enum value.
michael@0 44 * @return {string} The enum as a string.
michael@0 45 */
michael@0 46 var glEnumToString = function(gl, value) {
michael@0 47 for (var p in gl) {
michael@0 48 if (gl[p] == value) {
michael@0 49 return p;
michael@0 50 }
michael@0 51 }
michael@0 52 return "0x" + value.toString(16);
michael@0 53 };
michael@0 54
michael@0 55 var lastError = "";
michael@0 56
michael@0 57 /**
michael@0 58 * Returns the last compiler/linker error.
michael@0 59 * @return {string} The last compiler/linker error.
michael@0 60 */
michael@0 61 var getLastError = function() {
michael@0 62 return lastError;
michael@0 63 };
michael@0 64
michael@0 65 /**
michael@0 66 * Whether a haystack ends with a needle.
michael@0 67 * @param {string} haystack String to search
michael@0 68 * @param {string} needle String to search for.
michael@0 69 * @param {boolean} True if haystack ends with needle.
michael@0 70 */
michael@0 71 var endsWith = function(haystack, needle) {
michael@0 72 return haystack.substr(haystack.length - needle.length) === needle;
michael@0 73 };
michael@0 74
michael@0 75 /**
michael@0 76 * Whether a haystack starts with a needle.
michael@0 77 * @param {string} haystack String to search
michael@0 78 * @param {string} needle String to search for.
michael@0 79 * @param {boolean} True if haystack starts with needle.
michael@0 80 */
michael@0 81 var startsWith = function(haystack, needle) {
michael@0 82 return haystack.substr(0, needle.length) === needle;
michael@0 83 };
michael@0 84
michael@0 85 /**
michael@0 86 * A vertex shader for a single texture.
michael@0 87 * @type {string}
michael@0 88 */
michael@0 89 var simpleTextureVertexShader = [
michael@0 90 'attribute vec4 vPosition;',
michael@0 91 'attribute vec2 texCoord0;',
michael@0 92 'varying vec2 texCoord;',
michael@0 93 'void main() {',
michael@0 94 ' gl_Position = vPosition;',
michael@0 95 ' texCoord = texCoord0;',
michael@0 96 '}'].join('\n');
michael@0 97
michael@0 98 /**
michael@0 99 * A fragment shader for a single texture.
michael@0 100 * @type {string}
michael@0 101 */
michael@0 102 var simpleTextureFragmentShader = [
michael@0 103 'precision mediump float;',
michael@0 104 'uniform sampler2D tex;',
michael@0 105 'varying vec2 texCoord;',
michael@0 106 'void main() {',
michael@0 107 ' gl_FragData[0] = texture2D(tex, texCoord);',
michael@0 108 '}'].join('\n');
michael@0 109
michael@0 110 /**
michael@0 111 * Creates a simple texture vertex shader.
michael@0 112 * @param {!WebGLContext} gl The WebGLContext to use.
michael@0 113 * @return {!WebGLShader}
michael@0 114 */
michael@0 115 var setupSimpleTextureVertexShader = function(gl) {
michael@0 116 return loadShader(gl, simpleTextureVertexShader, gl.VERTEX_SHADER);
michael@0 117 };
michael@0 118
michael@0 119 /**
michael@0 120 * Creates a simple texture fragment shader.
michael@0 121 * @param {!WebGLContext} gl The WebGLContext to use.
michael@0 122 * @return {!WebGLShader}
michael@0 123 */
michael@0 124 var setupSimpleTextureFragmentShader = function(gl) {
michael@0 125 return loadShader(
michael@0 126 gl, simpleTextureFragmentShader, gl.FRAGMENT_SHADER);
michael@0 127 };
michael@0 128
michael@0 129 /**
michael@0 130 * Creates a program, attaches shaders, binds attrib locations, links the
michael@0 131 * program and calls useProgram.
michael@0 132 * @param {!Array.<!WebGLShader|string>} shaders The shaders to
michael@0 133 * attach, or the source, or the id of a script to get
michael@0 134 * the source from.
michael@0 135 * @param {!Array.<string>} opt_attribs The attribs names.
michael@0 136 * @param {!Array.<number>} opt_locations The locations for the attribs.
michael@0 137 */
michael@0 138 var setupProgram = function(gl, shaders, opt_attribs, opt_locations) {
michael@0 139 var realShaders = [];
michael@0 140 var program = gl.createProgram();
michael@0 141 for (var ii = 0; ii < shaders.length; ++ii) {
michael@0 142 var shader = shaders[ii];
michael@0 143 if (typeof shader == 'string') {
michael@0 144 var element = document.getElementById(shader);
michael@0 145 if (element) {
michael@0 146 shader = loadShaderFromScript(gl, shader);
michael@0 147 } else {
michael@0 148 shader = loadShader(gl, shader, ii ? gl.FRAGMENT_SHADER : gl.VERTEX_SHADER);
michael@0 149 }
michael@0 150 }
michael@0 151 gl.attachShader(program, shader);
michael@0 152 }
michael@0 153 if (opt_attribs) {
michael@0 154 for (var ii = 0; ii < opt_attribs.length; ++ii) {
michael@0 155 gl.bindAttribLocation(
michael@0 156 program,
michael@0 157 opt_locations ? opt_locations[ii] : ii,
michael@0 158 opt_attribs[ii]);
michael@0 159 }
michael@0 160 }
michael@0 161 gl.linkProgram(program);
michael@0 162
michael@0 163 // Check the link status
michael@0 164 var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
michael@0 165 if (!linked) {
michael@0 166 // something went wrong with the link
michael@0 167 lastError = gl.getProgramInfoLog (program);
michael@0 168 error("Error in program linking:" + lastError);
michael@0 169
michael@0 170 gl.deleteProgram(program);
michael@0 171 return null;
michael@0 172 }
michael@0 173
michael@0 174 gl.useProgram(program);
michael@0 175 return program;
michael@0 176 };
michael@0 177
michael@0 178 /**
michael@0 179 * Creates a simple texture program.
michael@0 180 * @param {!WebGLContext} gl The WebGLContext to use.
michael@0 181 * @param {number} opt_positionLocation The attrib location for position.
michael@0 182 * @param {number} opt_texcoordLocation The attrib location for texture coords.
michael@0 183 * @return {WebGLProgram}
michael@0 184 */
michael@0 185 var setupSimpleTextureProgram = function(
michael@0 186 gl, opt_positionLocation, opt_texcoordLocation) {
michael@0 187 opt_positionLocation = opt_positionLocation || 0;
michael@0 188 opt_texcoordLocation = opt_texcoordLocation || 1;
michael@0 189 var vs = setupSimpleTextureVertexShader(gl);
michael@0 190 var fs = setupSimpleTextureFragmentShader(gl);
michael@0 191 if (!vs || !fs) {
michael@0 192 return null;
michael@0 193 }
michael@0 194 var program = setupProgram(
michael@0 195 gl,
michael@0 196 [vs, fs],
michael@0 197 ['vPosition', 'texCoord0'],
michael@0 198 [opt_positionLocation, opt_texcoordLocation]);
michael@0 199 if (!program) {
michael@0 200 gl.deleteShader(fs);
michael@0 201 gl.deleteShader(vs);
michael@0 202 }
michael@0 203 gl.useProgram(program);
michael@0 204 return program;
michael@0 205 };
michael@0 206
michael@0 207 /**
michael@0 208 * Creates buffers for a textured unit quad and attaches them to vertex attribs.
michael@0 209 * @param {!WebGLContext} gl The WebGLContext to use.
michael@0 210 * @param {number} opt_positionLocation The attrib location for position.
michael@0 211 * @param {number} opt_texcoordLocation The attrib location for texture coords.
michael@0 212 * @return {!Array.<WebGLBuffer>} The buffer objects that were
michael@0 213 * created.
michael@0 214 */
michael@0 215 var setupUnitQuad = function(gl, opt_positionLocation, opt_texcoordLocation) {
michael@0 216 return setupUnitQuadWithTexCoords(gl, [ 0.0, 0.0 ], [ 1.0, 1.0 ],
michael@0 217 opt_positionLocation, opt_texcoordLocation);
michael@0 218 };
michael@0 219
michael@0 220 /**
michael@0 221 * Draws a previously setupUnitQuad.
michael@0 222 * @param {!WebGLContext} gl The WebGLContext to use.
michael@0 223 */
michael@0 224 var drawUnitQuad = function(gl) {
michael@0 225 gl.drawArrays(gl.TRIANGLES, 0, 6);
michael@0 226 };
michael@0 227
michael@0 228 /**
michael@0 229 * Clears then Draws a previously setupUnitQuad.
michael@0 230 * @param {!WebGLContext} gl The WebGLContext to use.
michael@0 231 * @param {!Array.<number>} opt_color The color to fill clear with before
michael@0 232 * drawing. A 4 element array where each element is in the range 0 to
michael@0 233 * 255. Default [255, 255, 255, 255]
michael@0 234 */
michael@0 235 var clearAndDrawUnitQuad = function(gl, opt_color) {
michael@0 236 opt_color = opt_color || [255, 255, 255, 255];
michael@0 237
michael@0 238 // Save and restore.
michael@0 239 var prevClearColor = gl.getParameter(gl.COLOR_CLEAR_VALUE);
michael@0 240
michael@0 241 gl.clearColor(opt_color[0] / 255,
michael@0 242 opt_color[1] / 255,
michael@0 243 opt_color[2] / 255,
michael@0 244 opt_color[3] / 255);
michael@0 245 gl.clear(gl.COLOR_BUFFER_BIT);
michael@0 246 drawUnitQuad(gl);
michael@0 247
michael@0 248 gl.clearColor(prevClearColor[0],
michael@0 249 prevClearColor[1],
michael@0 250 prevClearColor[2],
michael@0 251 prevClearColor[3]);
michael@0 252 };
michael@0 253
michael@0 254 /**
michael@0 255 * Creates buffers for a textured unit quad with specified lower left
michael@0 256 * and upper right texture coordinates, and attaches them to vertex
michael@0 257 * attribs.
michael@0 258 * @param {!WebGLContext} gl The WebGLContext to use.
michael@0 259 * @param {!Array.<number>} lowerLeftTexCoords The texture coordinates for the lower left corner.
michael@0 260 * @param {!Array.<number>} upperRightTexCoords The texture coordinates for the upper right corner.
michael@0 261 * @param {number} opt_positionLocation The attrib location for position.
michael@0 262 * @param {number} opt_texcoordLocation The attrib location for texture coords.
michael@0 263 * @return {!Array.<WebGLBuffer>} The buffer objects that were
michael@0 264 * created.
michael@0 265 */
michael@0 266 var setupUnitQuadWithTexCoords = function(
michael@0 267 gl, lowerLeftTexCoords, upperRightTexCoords,
michael@0 268 opt_positionLocation, opt_texcoordLocation) {
michael@0 269 opt_positionLocation = opt_positionLocation || 0;
michael@0 270 opt_texcoordLocation = opt_texcoordLocation || 1;
michael@0 271 var objects = [];
michael@0 272
michael@0 273 var vertexObject = gl.createBuffer();
michael@0 274 gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject);
michael@0 275 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
michael@0 276 1.0, 1.0, 0.0,
michael@0 277 -1.0, 1.0, 0.0,
michael@0 278 -1.0, -1.0, 0.0,
michael@0 279 1.0, 1.0, 0.0,
michael@0 280 -1.0, -1.0, 0.0,
michael@0 281 1.0, -1.0, 0.0]), gl.STATIC_DRAW);
michael@0 282 gl.enableVertexAttribArray(opt_positionLocation);
michael@0 283 gl.vertexAttribPointer(opt_positionLocation, 3, gl.FLOAT, false, 0, 0);
michael@0 284 objects.push(vertexObject);
michael@0 285
michael@0 286 var llx = lowerLeftTexCoords[0];
michael@0 287 var lly = lowerLeftTexCoords[1];
michael@0 288 var urx = upperRightTexCoords[0];
michael@0 289 var ury = upperRightTexCoords[1];
michael@0 290
michael@0 291 var vertexObject = gl.createBuffer();
michael@0 292 gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject);
michael@0 293 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
michael@0 294 urx, ury,
michael@0 295 llx, ury,
michael@0 296 llx, lly,
michael@0 297 urx, ury,
michael@0 298 llx, lly,
michael@0 299 urx, lly]), gl.STATIC_DRAW);
michael@0 300 gl.enableVertexAttribArray(opt_texcoordLocation);
michael@0 301 gl.vertexAttribPointer(opt_texcoordLocation, 2, gl.FLOAT, false, 0, 0);
michael@0 302 objects.push(vertexObject);
michael@0 303 return objects;
michael@0 304 };
michael@0 305
michael@0 306 /**
michael@0 307 * Creates a program and buffers for rendering a textured quad.
michael@0 308 * @param {!WebGLContext} gl The WebGLContext to use.
michael@0 309 * @param {number} opt_positionLocation The attrib location for position.
michael@0 310 * @param {number} opt_texcoordLocation The attrib location for texture coords.
michael@0 311 * @return {!WebGLProgram}
michael@0 312 */
michael@0 313 var setupTexturedQuad = function(
michael@0 314 gl, opt_positionLocation, opt_texcoordLocation) {
michael@0 315 var program = setupSimpleTextureProgram(
michael@0 316 gl, opt_positionLocation, opt_texcoordLocation);
michael@0 317 setupUnitQuad(gl, opt_positionLocation, opt_texcoordLocation);
michael@0 318 return program;
michael@0 319 };
michael@0 320
michael@0 321 /**
michael@0 322 * Creates a program and buffers for rendering a textured quad with
michael@0 323 * specified lower left and upper right texture coordinates.
michael@0 324 * @param {!WebGLContext} gl The WebGLContext to use.
michael@0 325 * @param {!Array.<number>} lowerLeftTexCoords The texture coordinates for the lower left corner.
michael@0 326 * @param {!Array.<number>} upperRightTexCoords The texture coordinates for the upper right corner.
michael@0 327 * @param {number} opt_positionLocation The attrib location for position.
michael@0 328 * @param {number} opt_texcoordLocation The attrib location for texture coords.
michael@0 329 * @return {!WebGLProgram}
michael@0 330 */
michael@0 331 var setupTexturedQuadWithTexCoords = function(
michael@0 332 gl, lowerLeftTexCoords, upperRightTexCoords,
michael@0 333 opt_positionLocation, opt_texcoordLocation) {
michael@0 334 var program = setupSimpleTextureProgram(
michael@0 335 gl, opt_positionLocation, opt_texcoordLocation);
michael@0 336 setupUnitQuadWithTexCoords(gl, lowerLeftTexCoords, upperRightTexCoords,
michael@0 337 opt_positionLocation, opt_texcoordLocation);
michael@0 338 return program;
michael@0 339 };
michael@0 340
michael@0 341 /**
michael@0 342 * Creates a unit quad with only positions of a given resolution.
michael@0 343 * @param {!WebGLContext} gl The WebGLContext to use.
michael@0 344 * @param {number} gridRes The resolution of the mesh grid, expressed in the number of triangles across and down.
michael@0 345 * @param {number} opt_positionLocation The attrib location for position.
michael@0 346 */
michael@0 347 var setupQuad = function (
michael@0 348 gl, gridRes, opt_positionLocation, opt_flipOddTriangles) {
michael@0 349 var positionLocation = opt_positionLocation || 0;
michael@0 350 var objects = [];
michael@0 351
michael@0 352 var vertsAcross = gridRes + 1;
michael@0 353 var numVerts = vertsAcross * vertsAcross;
michael@0 354 var positions = new Float32Array(numVerts * 3);
michael@0 355 var indices = new Uint16Array(6 * gridRes * gridRes);
michael@0 356
michael@0 357 var poffset = 0;
michael@0 358
michael@0 359 for (var yy = 0; yy <= gridRes; ++yy) {
michael@0 360 for (var xx = 0; xx <= gridRes; ++xx) {
michael@0 361 positions[poffset + 0] = -1 + 2 * xx / gridRes;
michael@0 362 positions[poffset + 1] = -1 + 2 * yy / gridRes;
michael@0 363 positions[poffset + 2] = 0;
michael@0 364
michael@0 365 poffset += 3;
michael@0 366 }
michael@0 367 }
michael@0 368
michael@0 369 var tbase = 0;
michael@0 370 for (var yy = 0; yy < gridRes; ++yy) {
michael@0 371 var index = yy * vertsAcross;
michael@0 372 for (var xx = 0; xx < gridRes; ++xx) {
michael@0 373 indices[tbase + 0] = index + 0;
michael@0 374 indices[tbase + 1] = index + 1;
michael@0 375 indices[tbase + 2] = index + vertsAcross;
michael@0 376 indices[tbase + 3] = index + vertsAcross;
michael@0 377 indices[tbase + 4] = index + 1;
michael@0 378 indices[tbase + 5] = index + vertsAcross + 1;
michael@0 379
michael@0 380 if (opt_flipOddTriangles) {
michael@0 381 indices[tbase + 4] = index + vertsAcross + 1;
michael@0 382 indices[tbase + 5] = index + 1;
michael@0 383 }
michael@0 384
michael@0 385 index += 1;
michael@0 386 tbase += 6;
michael@0 387 }
michael@0 388 }
michael@0 389
michael@0 390 var buf = gl.createBuffer();
michael@0 391 gl.bindBuffer(gl.ARRAY_BUFFER, buf);
michael@0 392 gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
michael@0 393 gl.enableVertexAttribArray(positionLocation);
michael@0 394 gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0);
michael@0 395 objects.push(buf);
michael@0 396
michael@0 397 var buf = gl.createBuffer();
michael@0 398 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buf);
michael@0 399 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
michael@0 400 objects.push(buf);
michael@0 401
michael@0 402 return objects;
michael@0 403 };
michael@0 404
michael@0 405 /**
michael@0 406 * Fills the given texture with a solid color
michael@0 407 * @param {!WebGLContext} gl The WebGLContext to use.
michael@0 408 * @param {!WebGLTexture} tex The texture to fill.
michael@0 409 * @param {number} width The width of the texture to create.
michael@0 410 * @param {number} height The height of the texture to create.
michael@0 411 * @param {!Array.<number>} color The color to fill with. A 4 element array
michael@0 412 * where each element is in the range 0 to 255.
michael@0 413 * @param {number} opt_level The level of the texture to fill. Default = 0.
michael@0 414 */
michael@0 415 var fillTexture = function(gl, tex, width, height, color, opt_level) {
michael@0 416 opt_level = opt_level || 0;
michael@0 417 var numPixels = width * height;
michael@0 418 var size = numPixels * 4;
michael@0 419 var buf = new Uint8Array(size);
michael@0 420 for (var ii = 0; ii < numPixels; ++ii) {
michael@0 421 var off = ii * 4;
michael@0 422 buf[off + 0] = color[0];
michael@0 423 buf[off + 1] = color[1];
michael@0 424 buf[off + 2] = color[2];
michael@0 425 buf[off + 3] = color[3];
michael@0 426 }
michael@0 427 gl.bindTexture(gl.TEXTURE_2D, tex);
michael@0 428 gl.texImage2D(
michael@0 429 gl.TEXTURE_2D, opt_level, gl.RGBA, width, height, 0,
michael@0 430 gl.RGBA, gl.UNSIGNED_BYTE, buf);
michael@0 431 };
michael@0 432
michael@0 433 /**
michael@0 434 * Creates a textures and fills it with a solid color
michael@0 435 * @param {!WebGLContext} gl The WebGLContext to use.
michael@0 436 * @param {number} width The width of the texture to create.
michael@0 437 * @param {number} height The height of the texture to create.
michael@0 438 * @param {!Array.<number>} color The color to fill with. A 4 element array
michael@0 439 * where each element is in the range 0 to 255.
michael@0 440 * @return {!WebGLTexture}
michael@0 441 */
michael@0 442 var createColoredTexture = function(gl, width, height, color) {
michael@0 443 var tex = gl.createTexture();
michael@0 444 fillTexture(gl, tex, width, height, color);
michael@0 445 return tex;
michael@0 446 };
michael@0 447
michael@0 448 /**
michael@0 449 * Draws a previously setup quad.
michael@0 450 * @param {!WebGLContext} gl The WebGLContext to use.
michael@0 451 * @param {!Array.<number>} opt_color The color to fill clear with before
michael@0 452 * drawing. A 4 element array where each element is in the range 0 to
michael@0 453 * 255. Default [255, 255, 255, 255]
michael@0 454 */
michael@0 455 var drawQuad = function(gl, opt_color) {
michael@0 456 opt_color = opt_color || [255, 255, 255, 255];
michael@0 457 gl.clearColor(
michael@0 458 opt_color[0] / 255,
michael@0 459 opt_color[1] / 255,
michael@0 460 opt_color[2] / 255,
michael@0 461 opt_color[3] / 255);
michael@0 462 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
michael@0 463 gl.drawArrays(gl.TRIANGLES, 0, 6);
michael@0 464 };
michael@0 465
michael@0 466 /**
michael@0 467 * Checks that a portion of a canvas is 1 color.
michael@0 468 * @param {!WebGLContext} gl The WebGLContext to use.
michael@0 469 * @param {number} x left corner of region to check.
michael@0 470 * @param {number} y bottom corner of region to check.
michael@0 471 * @param {number} width width of region to check.
michael@0 472 * @param {number} height width of region to check.
michael@0 473 * @param {!Array.<number>} color The color to fill clear with before drawing. A
michael@0 474 * 4 element array where each element is in the range 0 to 255.
michael@0 475 * @param {string} msg Message to associate with success. Eg ("should be red").
michael@0 476 * @param {number} errorRange Optional. Acceptable error in
michael@0 477 * color checking. 0 by default.
michael@0 478 */
michael@0 479 var checkCanvasRect = function(gl, x, y, width, height, color, msg, errorRange) {
michael@0 480 errorRange = errorRange || 0;
michael@0 481 var buf = new Uint8Array(width * height * 4);
michael@0 482 gl.readPixels(x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
michael@0 483 for (var i = 0; i < width * height; ++i) {
michael@0 484 var offset = i * 4;
michael@0 485 for (var j = 0; j < color.length; ++j) {
michael@0 486 if (Math.abs(buf[offset + j] - color[j]) > errorRange) {
michael@0 487 var was = buf[offset + 0].toString();
michael@0 488 for (j = 1; j < color.length; ++j) {
michael@0 489 was += "," + buf[offset + j];
michael@0 490 }
michael@0 491
michael@0 492 var cv = document.createElement('canvas');
michael@0 493 cv.height = height;
michael@0 494 cv.width = width;
michael@0 495 var ctx = cv.getContext('2d');
michael@0 496 ctx.fillStyle="rgba(" + color[0] + ", " + color[1] + ", " + color[2] + ", 255)";
michael@0 497 ctx.fillRect(0, 0, width, height);
michael@0 498 testFailedRender(msg, ctx, buf, width, height);
michael@0 499
michael@0 500 debug('at (' + (i % width) + ', ' + Math.floor(i / width) +
michael@0 501 ') expected: ' + color + ' was ' + was);
michael@0 502 return;
michael@0 503 }
michael@0 504 }
michael@0 505 }
michael@0 506 testPassed(msg);
michael@0 507 };
michael@0 508
michael@0 509 /**
michael@0 510 * Checks that an entire canvas is 1 color.
michael@0 511 * @param {!WebGLContext} gl The WebGLContext to use.
michael@0 512 * @param {!Array.<number>} color The color to fill clear with before drawing. A
michael@0 513 * 4 element array where each element is in the range 0 to 255.
michael@0 514 * @param {string} msg Message to associate with success. Eg ("should be red").
michael@0 515 * @param {number} errorRange Optional. Acceptable error in
michael@0 516 * color checking. 0 by default.
michael@0 517 */
michael@0 518 var checkCanvas = function(gl, color, msg, errorRange) {
michael@0 519 checkCanvasRect(gl, 0, 0, gl.canvas.width, gl.canvas.height, color, msg, errorRange);
michael@0 520 };
michael@0 521
michael@0 522 /**
michael@0 523 * Loads a texture, calls callback when finished.
michael@0 524 * @param {!WebGLContext} gl The WebGLContext to use.
michael@0 525 * @param {string} url URL of image to load
michael@0 526 * @param {function(!Image): void} callback Function that gets called after
michael@0 527 * image has loaded
michael@0 528 * @return {!WebGLTexture} The created texture.
michael@0 529 */
michael@0 530 var loadTexture = function(gl, url, callback) {
michael@0 531 var texture = gl.createTexture();
michael@0 532 gl.bindTexture(gl.TEXTURE_2D, texture);
michael@0 533 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
michael@0 534 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
michael@0 535 var image = new Image();
michael@0 536 image.onload = function() {
michael@0 537 gl.bindTexture(gl.TEXTURE_2D, texture);
michael@0 538 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
michael@0 539 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
michael@0 540 callback(image);
michael@0 541 };
michael@0 542 image.src = url;
michael@0 543 return texture;
michael@0 544 };
michael@0 545
michael@0 546 /**
michael@0 547 * Creates a webgl context.
michael@0 548 * @param {!Canvas|string} opt_canvas The canvas tag to get
michael@0 549 * context from. If one is not passed in one will be
michael@0 550 * created. If it's a string it's assumed to be the id of a
michael@0 551 * canvas.
michael@0 552 * @return {!WebGLContext} The created context.
michael@0 553 */
michael@0 554 var create3DContext = function(opt_canvas, opt_attributes) {
michael@0 555 opt_canvas = opt_canvas || document.createElement("canvas");
michael@0 556 if (typeof opt_canvas == 'string') {
michael@0 557 opt_canvas = document.getElementById(opt_canvas);
michael@0 558 }
michael@0 559 var context = null;
michael@0 560 var names = ["webgl", "experimental-webgl"];
michael@0 561 for (var i = 0; i < names.length; ++i) {
michael@0 562 try {
michael@0 563 context = opt_canvas.getContext(names[i], opt_attributes);
michael@0 564 } catch (e) {
michael@0 565 }
michael@0 566 if (context) {
michael@0 567 break;
michael@0 568 }
michael@0 569 }
michael@0 570 if (!context) {
michael@0 571 testFailed("Unable to fetch WebGL rendering context for Canvas");
michael@0 572 }
michael@0 573 return context;
michael@0 574 }
michael@0 575
michael@0 576 /**
michael@0 577 * Gets a GLError value as a string.
michael@0 578 * @param {!WebGLContext} gl The WebGLContext to use.
michael@0 579 * @param {number} err The webgl error as retrieved from gl.getError().
michael@0 580 * @return {string} the error as a string.
michael@0 581 */
michael@0 582 var getGLErrorAsString = function(gl, err) {
michael@0 583 if (err === gl.NO_ERROR) {
michael@0 584 return "NO_ERROR";
michael@0 585 }
michael@0 586 for (var name in gl) {
michael@0 587 if (gl[name] === err) {
michael@0 588 return name;
michael@0 589 }
michael@0 590 }
michael@0 591 return err.toString();
michael@0 592 };
michael@0 593
michael@0 594 /**
michael@0 595 * Wraps a WebGL function with a function that throws an exception if there is
michael@0 596 * an error.
michael@0 597 * @param {!WebGLContext} gl The WebGLContext to use.
michael@0 598 * @param {string} fname Name of function to wrap.
michael@0 599 * @return {function} The wrapped function.
michael@0 600 */
michael@0 601 var createGLErrorWrapper = function(context, fname) {
michael@0 602 return function() {
michael@0 603 var rv = context[fname].apply(context, arguments);
michael@0 604 var err = context.getError();
michael@0 605 if (err != 0)
michael@0 606 throw "GL error " + getGLErrorAsString(err) + " in " + fname;
michael@0 607 return rv;
michael@0 608 };
michael@0 609 };
michael@0 610
michael@0 611 /**
michael@0 612 * Creates a WebGL context where all functions are wrapped to throw an exception
michael@0 613 * if there is an error.
michael@0 614 * @param {!Canvas} canvas The HTML canvas to get a context from.
michael@0 615 * @return {!Object} The wrapped context.
michael@0 616 */
michael@0 617 function create3DContextWithWrapperThatThrowsOnGLError(canvas) {
michael@0 618 var context = create3DContext(canvas);
michael@0 619 var wrap = {};
michael@0 620 for (var i in context) {
michael@0 621 try {
michael@0 622 if (typeof context[i] == 'function') {
michael@0 623 wrap[i] = createGLErrorWrapper(context, i);
michael@0 624 } else {
michael@0 625 wrap[i] = context[i];
michael@0 626 }
michael@0 627 } catch (e) {
michael@0 628 error("createContextWrapperThatThrowsOnGLError: Error accessing " + i);
michael@0 629 }
michael@0 630 }
michael@0 631 wrap.getError = function() {
michael@0 632 return context.getError();
michael@0 633 };
michael@0 634 return wrap;
michael@0 635 };
michael@0 636
michael@0 637 /**
michael@0 638 * Tests that an evaluated expression generates a specific GL error.
michael@0 639 * @param {!WebGLContext} gl The WebGLContext to use.
michael@0 640 * @param {number} glError The expected gl error.
michael@0 641 * @param {string} evalSTr The string to evaluate.
michael@0 642 */
michael@0 643 var shouldGenerateGLError = function(gl, glError, evalStr) {
michael@0 644 var exception;
michael@0 645 try {
michael@0 646 eval(evalStr);
michael@0 647 } catch (e) {
michael@0 648 exception = e;
michael@0 649 }
michael@0 650 if (exception) {
michael@0 651 testFailed(evalStr + " threw exception " + exception);
michael@0 652 } else {
michael@0 653 var err = gl.getError();
michael@0 654 if (err != glError) {
michael@0 655 testFailed(evalStr + " expected: " + getGLErrorAsString(gl, glError) + ". Was " + getGLErrorAsString(gl, err) + ".");
michael@0 656 } else {
michael@0 657 testPassed(evalStr + " was expected value: " + getGLErrorAsString(gl, glError) + ".");
michael@0 658 }
michael@0 659 }
michael@0 660 };
michael@0 661
michael@0 662 /**
michael@0 663 * Tests that the first error GL returns is the specified error.
michael@0 664 * @param {!WebGLContext} gl The WebGLContext to use.
michael@0 665 * @param {number} glError The expected gl error.
michael@0 666 * @param {string} opt_msg
michael@0 667 */
michael@0 668 var glErrorShouldBe = function(gl, glError, opt_msg) {
michael@0 669 opt_msg = opt_msg || "";
michael@0 670 var err = gl.getError();
michael@0 671 if (err != glError) {
michael@0 672 testFailed("getError expected: " + getGLErrorAsString(gl, glError) +
michael@0 673 ". Was " + getGLErrorAsString(gl, err) + " : " + opt_msg);
michael@0 674 } else {
michael@0 675 testPassed("getError was expected value: " +
michael@0 676 getGLErrorAsString(gl, glError) + " : " + opt_msg);
michael@0 677 }
michael@0 678 };
michael@0 679
michael@0 680 /**
michael@0 681 * Links a WebGL program, throws if there are errors.
michael@0 682 * @param {!WebGLContext} gl The WebGLContext to use.
michael@0 683 * @param {!WebGLProgram} program The WebGLProgram to link.
michael@0 684 * @param {function(string): void) opt_errorCallback callback for errors.
michael@0 685 */
michael@0 686 var linkProgram = function(gl, program, opt_errorCallback) {
michael@0 687 errFn = opt_errorCallback || testFailed;
michael@0 688 // Link the program
michael@0 689 gl.linkProgram(program);
michael@0 690
michael@0 691 // Check the link status
michael@0 692 var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
michael@0 693 if (!linked) {
michael@0 694 // something went wrong with the link
michael@0 695 var error = gl.getProgramInfoLog (program);
michael@0 696
michael@0 697 errFn("Error in program linking:" + error);
michael@0 698
michael@0 699 gl.deleteProgram(program);
michael@0 700 }
michael@0 701 };
michael@0 702
michael@0 703 /**
michael@0 704 * Sets up WebGL with shaders.
michael@0 705 * @param {string} canvasName The id of the canvas.
michael@0 706 * @param {string} vshader The id of the script tag that contains the vertex
michael@0 707 * shader source.
michael@0 708 * @param {string} fshader The id of the script tag that contains the fragment
michael@0 709 * shader source.
michael@0 710 * @param {!Array.<string>} attribs An array of attrib names used to bind
michael@0 711 * attribs to the ordinal of the name in this array.
michael@0 712 * @param {!Array.<number>} opt_clearColor The color to cla
michael@0 713 * @return {!WebGLContext} The created WebGLContext.
michael@0 714 */
michael@0 715 var setupWebGLWithShaders = function(
michael@0 716 canvasName, vshader, fshader, attribs) {
michael@0 717 var canvas = document.getElementById(canvasName);
michael@0 718 var gl = create3DContext(canvas);
michael@0 719 if (!gl) {
michael@0 720 testFailed("No WebGL context found");
michael@0 721 }
michael@0 722
michael@0 723 // create our shaders
michael@0 724 var vertexShader = loadShaderFromScript(gl, vshader);
michael@0 725 var fragmentShader = loadShaderFromScript(gl, fshader);
michael@0 726
michael@0 727 if (!vertexShader || !fragmentShader) {
michael@0 728 return null;
michael@0 729 }
michael@0 730
michael@0 731 // Create the program object
michael@0 732 program = gl.createProgram();
michael@0 733
michael@0 734 if (!program) {
michael@0 735 return null;
michael@0 736 }
michael@0 737
michael@0 738 // Attach our two shaders to the program
michael@0 739 gl.attachShader (program, vertexShader);
michael@0 740 gl.attachShader (program, fragmentShader);
michael@0 741
michael@0 742 // Bind attributes
michael@0 743 for (var i in attribs) {
michael@0 744 gl.bindAttribLocation (program, i, attribs[i]);
michael@0 745 }
michael@0 746
michael@0 747 linkProgram(gl, program);
michael@0 748
michael@0 749 gl.useProgram(program);
michael@0 750
michael@0 751 gl.clearColor(0,0,0,1);
michael@0 752 gl.clearDepth(1);
michael@0 753
michael@0 754 gl.enable(gl.DEPTH_TEST);
michael@0 755 gl.enable(gl.BLEND);
michael@0 756 gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
michael@0 757
michael@0 758 gl.program = program;
michael@0 759 return gl;
michael@0 760 };
michael@0 761
michael@0 762 /**
michael@0 763 * Loads text from an external file. This function is synchronous.
michael@0 764 * @param {string} url The url of the external file.
michael@0 765 * @param {!function(bool, string): void} callback that is sent a bool for
michael@0 766 * success and the string.
michael@0 767 */
michael@0 768 var loadTextFileAsync = function(url, callback) {
michael@0 769 log ("loading: " + url);
michael@0 770 var error = 'loadTextFileSynchronous failed to load url "' + url + '"';
michael@0 771 var request;
michael@0 772 if (window.XMLHttpRequest) {
michael@0 773 request = new XMLHttpRequest();
michael@0 774 if (request.overrideMimeType) {
michael@0 775 request.overrideMimeType('text/plain');
michael@0 776 }
michael@0 777 } else {
michael@0 778 throw 'XMLHttpRequest is disabled';
michael@0 779 }
michael@0 780 try {
michael@0 781 request.open('GET', url, true);
michael@0 782 request.onreadystatechange = function() {
michael@0 783 if (request.readyState == 4) {
michael@0 784 var text = '';
michael@0 785 // HTTP reports success with a 200 status. The file protocol reports
michael@0 786 // success with zero. HTTP does not use zero as a status code (they
michael@0 787 // start at 100).
michael@0 788 // https://developer.mozilla.org/En/Using_XMLHttpRequest
michael@0 789 var success = request.status == 200 || request.status == 0;
michael@0 790 if (success) {
michael@0 791 text = request.responseText;
michael@0 792 }
michael@0 793 log("loaded: " + url);
michael@0 794 callback(success, text);
michael@0 795 }
michael@0 796 };
michael@0 797 request.send(null);
michael@0 798 } catch (e) {
michael@0 799 log("failed to load: " + url);
michael@0 800 callback(false, '');
michael@0 801 }
michael@0 802 };
michael@0 803
michael@0 804 // Add your prefix here.
michael@0 805 var browserPrefixes = [
michael@0 806 "",
michael@0 807 "MOZ_",
michael@0 808 "OP_",
michael@0 809 "WEBKIT_"
michael@0 810 ];
michael@0 811
michael@0 812 /**
michael@0 813 * Given an extension name like WEBGL_compressed_texture_s3tc
michael@0 814 * returns the name of the supported version extension, like
michael@0 815 * WEBKIT_WEBGL_compressed_teture_s3tc
michael@0 816 * @param {string} name Name of extension to look for
michael@0 817 * @return {string} name of extension found or undefined if not
michael@0 818 * found.
michael@0 819 */
michael@0 820 var getSupportedExtensionWithKnownPrefixes = function(gl, name) {
michael@0 821 var supported = gl.getSupportedExtensions();
michael@0 822 for (var ii = 0; ii < browserPrefixes.length; ++ii) {
michael@0 823 var prefixedName = browserPrefixes[ii] + name;
michael@0 824 if (supported.indexOf(prefixedName) >= 0) {
michael@0 825 return prefixedName;
michael@0 826 }
michael@0 827 }
michael@0 828 };
michael@0 829
michael@0 830 /**
michael@0 831 * Given an extension name like WEBGL_compressed_texture_s3tc
michael@0 832 * returns the supported version extension, like
michael@0 833 * WEBKIT_WEBGL_compressed_teture_s3tc
michael@0 834 * @param {string} name Name of extension to look for
michael@0 835 * @return {WebGLExtension} The extension or undefined if not
michael@0 836 * found.
michael@0 837 */
michael@0 838 var getExtensionWithKnownPrefixes = function(gl, name) {
michael@0 839 for (var ii = 0; ii < browserPrefixes.length; ++ii) {
michael@0 840 var prefixedName = browserPrefixes[ii] + name;
michael@0 841 var ext = gl.getExtension(prefixedName);
michael@0 842 if (ext) {
michael@0 843 return ext;
michael@0 844 }
michael@0 845 }
michael@0 846 };
michael@0 847
michael@0 848 /**
michael@0 849 * Recursively loads a file as a list. Each line is parsed for a relative
michael@0 850 * path. If the file ends in .txt the contents of that file is inserted in
michael@0 851 * the list.
michael@0 852 *
michael@0 853 * @param {string} url The url of the external file.
michael@0 854 * @param {!function(bool, Array<string>): void} callback that is sent a bool
michael@0 855 * for success and the array of strings.
michael@0 856 */
michael@0 857 var getFileListAsync = function(url, callback) {
michael@0 858 var files = [];
michael@0 859
michael@0 860 var getFileListImpl = function(url, callback) {
michael@0 861 var files = [];
michael@0 862 if (url.substr(url.length - 4) == '.txt') {
michael@0 863 loadTextFileAsync(url, function() {
michael@0 864 return function(success, text) {
michael@0 865 if (!success) {
michael@0 866 callback(false, '');
michael@0 867 return;
michael@0 868 }
michael@0 869 var lines = text.split('\n');
michael@0 870 var prefix = '';
michael@0 871 var lastSlash = url.lastIndexOf('/');
michael@0 872 if (lastSlash >= 0) {
michael@0 873 prefix = url.substr(0, lastSlash + 1);
michael@0 874 }
michael@0 875 var fail = false;
michael@0 876 var count = 1;
michael@0 877 var index = 0;
michael@0 878 for (var ii = 0; ii < lines.length; ++ii) {
michael@0 879 var str = lines[ii].replace(/^\s\s*/, '').replace(/\s\s*$/, '');
michael@0 880 if (str.length > 4 &&
michael@0 881 str[0] != '#' &&
michael@0 882 str[0] != ";" &&
michael@0 883 str.substr(0, 2) != "//") {
michael@0 884 var names = str.split(/ +/);
michael@0 885 new_url = prefix + str;
michael@0 886 if (names.length == 1) {
michael@0 887 new_url = prefix + str;
michael@0 888 ++count;
michael@0 889 getFileListImpl(new_url, function(index) {
michael@0 890 return function(success, new_files) {
michael@0 891 log("got files: " + new_files.length);
michael@0 892 if (success) {
michael@0 893 files[index] = new_files;
michael@0 894 }
michael@0 895 finish(success);
michael@0 896 };
michael@0 897 }(index++));
michael@0 898 } else {
michael@0 899 var s = "";
michael@0 900 var p = "";
michael@0 901 for (var jj = 0; jj < names.length; ++jj) {
michael@0 902 s += p + prefix + names[jj];
michael@0 903 p = " ";
michael@0 904 }
michael@0 905 files[index++] = s;
michael@0 906 }
michael@0 907 }
michael@0 908 }
michael@0 909 finish(true);
michael@0 910
michael@0 911 function finish(success) {
michael@0 912 if (!success) {
michael@0 913 fail = true;
michael@0 914 }
michael@0 915 --count;
michael@0 916 log("count: " + count);
michael@0 917 if (!count) {
michael@0 918 callback(!fail, files);
michael@0 919 }
michael@0 920 }
michael@0 921 }
michael@0 922 }());
michael@0 923
michael@0 924 } else {
michael@0 925 files.push(url);
michael@0 926 callback(true, files);
michael@0 927 }
michael@0 928 };
michael@0 929
michael@0 930 getFileListImpl(url, function(success, files) {
michael@0 931 // flatten
michael@0 932 var flat = [];
michael@0 933 flatten(files);
michael@0 934 function flatten(files) {
michael@0 935 for (var ii = 0; ii < files.length; ++ii) {
michael@0 936 var value = files[ii];
michael@0 937 if (typeof(value) == "string") {
michael@0 938 flat.push(value);
michael@0 939 } else {
michael@0 940 flatten(value);
michael@0 941 }
michael@0 942 }
michael@0 943 }
michael@0 944 callback(success, flat);
michael@0 945 });
michael@0 946 };
michael@0 947
michael@0 948 /**
michael@0 949 * Gets a file from a file/URL
michael@0 950 * @param {string} file the URL of the file to get.
michael@0 951 * @return {string} The contents of the file.
michael@0 952 */
michael@0 953 var readFile = function(file) {
michael@0 954 var xhr = new XMLHttpRequest();
michael@0 955 xhr.open("GET", file, false);
michael@0 956 xhr.send();
michael@0 957 return xhr.responseText.replace(/\r/g, "");
michael@0 958 };
michael@0 959
michael@0 960 var readFileList = function(url) {
michael@0 961 var files = [];
michael@0 962 if (url.substr(url.length - 4) == '.txt') {
michael@0 963 var lines = readFile(url).split('\n');
michael@0 964 var prefix = '';
michael@0 965 var lastSlash = url.lastIndexOf('/');
michael@0 966 if (lastSlash >= 0) {
michael@0 967 prefix = url.substr(0, lastSlash + 1);
michael@0 968 }
michael@0 969 for (var ii = 0; ii < lines.length; ++ii) {
michael@0 970 var str = lines[ii].replace(/^\s\s*/, '').replace(/\s\s*$/, '');
michael@0 971 if (str.length > 4 &&
michael@0 972 str[0] != '#' &&
michael@0 973 str[0] != ";" &&
michael@0 974 str.substr(0, 2) != "//") {
michael@0 975 var names = str.split(/ +/);
michael@0 976 if (names.length == 1) {
michael@0 977 new_url = prefix + str;
michael@0 978 files = files.concat(readFileList(new_url));
michael@0 979 } else {
michael@0 980 var s = "";
michael@0 981 var p = "";
michael@0 982 for (var jj = 0; jj < names.length; ++jj) {
michael@0 983 s += p + prefix + names[jj];
michael@0 984 p = " ";
michael@0 985 }
michael@0 986 files.push(s);
michael@0 987 }
michael@0 988 }
michael@0 989 }
michael@0 990 } else {
michael@0 991 files.push(url);
michael@0 992 }
michael@0 993 return files;
michael@0 994 };
michael@0 995
michael@0 996 /**
michael@0 997 * Loads a shader.
michael@0 998 * @param {!WebGLContext} gl The WebGLContext to use.
michael@0 999 * @param {string} shaderSource The shader source.
michael@0 1000 * @param {number} shaderType The type of shader.
michael@0 1001 * @param {function(string): void) opt_errorCallback callback for errors.
michael@0 1002 * @return {!WebGLShader} The created shader.
michael@0 1003 */
michael@0 1004 var loadShader = function(gl, shaderSource, shaderType, opt_errorCallback) {
michael@0 1005 var errFn = opt_errorCallback || error;
michael@0 1006 // Create the shader object
michael@0 1007 var shader = gl.createShader(shaderType);
michael@0 1008 if (shader == null) {
michael@0 1009 errFn("*** Error: unable to create shader '"+shaderSource+"'");
michael@0 1010 return null;
michael@0 1011 }
michael@0 1012
michael@0 1013 // Load the shader source
michael@0 1014 gl.shaderSource(shader, shaderSource);
michael@0 1015 var err = gl.getError();
michael@0 1016 if (err != gl.NO_ERROR) {
michael@0 1017 errFn("*** Error loading shader '" + shader + "':" + glEnumToString(gl, err));
michael@0 1018 return null;
michael@0 1019 }
michael@0 1020
michael@0 1021 // Compile the shader
michael@0 1022 gl.compileShader(shader);
michael@0 1023
michael@0 1024 // Check the compile status
michael@0 1025 var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
michael@0 1026 if (!compiled) {
michael@0 1027 // Something went wrong during compilation; get the error
michael@0 1028 lastError = gl.getShaderInfoLog(shader);
michael@0 1029 errFn("*** Error compiling shader '" + shader + "':" + lastError);
michael@0 1030 gl.deleteShader(shader);
michael@0 1031 return null;
michael@0 1032 }
michael@0 1033
michael@0 1034 return shader;
michael@0 1035 }
michael@0 1036
michael@0 1037 /**
michael@0 1038 * Loads a shader from a URL.
michael@0 1039 * @param {!WebGLContext} gl The WebGLContext to use.
michael@0 1040 * @param {file} file The URL of the shader source.
michael@0 1041 * @param {number} type The type of shader.
michael@0 1042 * @param {function(string): void) opt_errorCallback callback for errors.
michael@0 1043 * @return {!WebGLShader} The created shader.
michael@0 1044 */
michael@0 1045 var loadShaderFromFile = function(gl, file, type, opt_errorCallback) {
michael@0 1046 var shaderSource = readFile(file);
michael@0 1047 return loadShader(gl, shaderSource, type, opt_errorCallback);
michael@0 1048 };
michael@0 1049
michael@0 1050 /**
michael@0 1051 * Gets the content of script.
michael@0 1052 */
michael@0 1053 var getScript = function(scriptId) {
michael@0 1054 var shaderScript = document.getElementById(scriptId);
michael@0 1055 if (!shaderScript) {
michael@0 1056 throw("*** Error: unknown script element" + scriptId);
michael@0 1057 }
michael@0 1058 return shaderScript.text;
michael@0 1059 };
michael@0 1060
michael@0 1061 /**
michael@0 1062 * Loads a shader from a script tag.
michael@0 1063 * @param {!WebGLContext} gl The WebGLContext to use.
michael@0 1064 * @param {string} scriptId The id of the script tag.
michael@0 1065 * @param {number} opt_shaderType The type of shader. If not passed in it will
michael@0 1066 * be derived from the type of the script tag.
michael@0 1067 * @param {function(string): void) opt_errorCallback callback for errors.
michael@0 1068 * @return {!WebGLShader} The created shader.
michael@0 1069 */
michael@0 1070 var loadShaderFromScript = function(
michael@0 1071 gl, scriptId, opt_shaderType, opt_errorCallback) {
michael@0 1072 var shaderSource = "";
michael@0 1073 var shaderType;
michael@0 1074 var shaderScript = document.getElementById(scriptId);
michael@0 1075 if (!shaderScript) {
michael@0 1076 throw("*** Error: unknown script element " + scriptId);
michael@0 1077 }
michael@0 1078 shaderSource = shaderScript.text;
michael@0 1079
michael@0 1080 if (!opt_shaderType) {
michael@0 1081 if (shaderScript.type == "x-shader/x-vertex") {
michael@0 1082 shaderType = gl.VERTEX_SHADER;
michael@0 1083 } else if (shaderScript.type == "x-shader/x-fragment") {
michael@0 1084 shaderType = gl.FRAGMENT_SHADER;
michael@0 1085 } else if (shaderType != gl.VERTEX_SHADER && shaderType != gl.FRAGMENT_SHADER) {
michael@0 1086 throw("*** Error: unknown shader type");
michael@0 1087 return null;
michael@0 1088 }
michael@0 1089 }
michael@0 1090
michael@0 1091 return loadShader(
michael@0 1092 gl, shaderSource, opt_shaderType ? opt_shaderType : shaderType,
michael@0 1093 opt_errorCallback);
michael@0 1094 };
michael@0 1095
michael@0 1096 var loadStandardProgram = function(gl) {
michael@0 1097 var program = gl.createProgram();
michael@0 1098 gl.attachShader(program, loadStandardVertexShader(gl));
michael@0 1099 gl.attachShader(program, loadStandardFragmentShader(gl));
michael@0 1100 linkProgram(gl, program);
michael@0 1101 return program;
michael@0 1102 };
michael@0 1103
michael@0 1104 /**
michael@0 1105 * Loads shaders from files, creates a program, attaches the shaders and links.
michael@0 1106 * @param {!WebGLContext} gl The WebGLContext to use.
michael@0 1107 * @param {string} vertexShaderPath The URL of the vertex shader.
michael@0 1108 * @param {string} fragmentShaderPath The URL of the fragment shader.
michael@0 1109 * @param {function(string): void) opt_errorCallback callback for errors.
michael@0 1110 * @return {!WebGLProgram} The created program.
michael@0 1111 */
michael@0 1112 var loadProgramFromFile = function(
michael@0 1113 gl, vertexShaderPath, fragmentShaderPath, opt_errorCallback) {
michael@0 1114 var program = gl.createProgram();
michael@0 1115 gl.attachShader(
michael@0 1116 program,
michael@0 1117 loadShaderFromFile(
michael@0 1118 gl, vertexShaderPath, gl.VERTEX_SHADER, opt_errorCallback));
michael@0 1119 gl.attachShader(
michael@0 1120 program,
michael@0 1121 loadShaderFromFile(
michael@0 1122 gl, fragmentShaderPath, gl.FRAGMENT_SHADER, opt_errorCallback));
michael@0 1123 linkProgram(gl, program, opt_errorCallback);
michael@0 1124 return program;
michael@0 1125 };
michael@0 1126
michael@0 1127 /**
michael@0 1128 * Loads shaders from script tags, creates a program, attaches the shaders and
michael@0 1129 * links.
michael@0 1130 * @param {!WebGLContext} gl The WebGLContext to use.
michael@0 1131 * @param {string} vertexScriptId The id of the script tag that contains the
michael@0 1132 * vertex shader.
michael@0 1133 * @param {string} fragmentScriptId The id of the script tag that contains the
michael@0 1134 * fragment shader.
michael@0 1135 * @param {function(string): void) opt_errorCallback callback for errors.
michael@0 1136 * @return {!WebGLProgram} The created program.
michael@0 1137 */
michael@0 1138 var loadProgramFromScript = function loadProgramFromScript(
michael@0 1139 gl, vertexScriptId, fragmentScriptId, opt_errorCallback) {
michael@0 1140 var program = gl.createProgram();
michael@0 1141 gl.attachShader(
michael@0 1142 program,
michael@0 1143 loadShaderFromScript(
michael@0 1144 gl, vertexScriptId, gl.VERTEX_SHADER, opt_errorCallback));
michael@0 1145 gl.attachShader(
michael@0 1146 program,
michael@0 1147 loadShaderFromScript(
michael@0 1148 gl, fragmentScriptId, gl.FRAGMENT_SHADER, opt_errorCallback));
michael@0 1149 linkProgram(gl, program, opt_errorCallback);
michael@0 1150 return program;
michael@0 1151 };
michael@0 1152
michael@0 1153 /**
michael@0 1154 * Loads shaders from source, creates a program, attaches the shaders and
michael@0 1155 * links.
michael@0 1156 * @param {!WebGLContext} gl The WebGLContext to use.
michael@0 1157 * @param {string} vertexShader The vertex shader.
michael@0 1158 * @param {string} fragmentShader The fragment shader.
michael@0 1159 * @param {function(string): void) opt_errorCallback callback for errors.
michael@0 1160 * @return {!WebGLProgram} The created program.
michael@0 1161 */
michael@0 1162 var loadProgram = function(
michael@0 1163 gl, vertexShader, fragmentShader, opt_errorCallback) {
michael@0 1164 var program = gl.createProgram();
michael@0 1165 gl.attachShader(
michael@0 1166 program,
michael@0 1167 loadShader(
michael@0 1168 gl, vertexShader, gl.VERTEX_SHADER, opt_errorCallback));
michael@0 1169 gl.attachShader(
michael@0 1170 program,
michael@0 1171 loadShader(
michael@0 1172 gl, fragmentShader, gl.FRAGMENT_SHADER, opt_errorCallback));
michael@0 1173 linkProgram(gl, program, opt_errorCallback);
michael@0 1174 return program;
michael@0 1175 };
michael@0 1176
michael@0 1177 /**
michael@0 1178 * Loads shaders from source, creates a program, attaches the shaders and
michael@0 1179 * links but expects error.
michael@0 1180 *
michael@0 1181 * GLSL 1.0.17 10.27 effectively says that compileShader can
michael@0 1182 * always succeed as long as linkProgram fails so we can't
michael@0 1183 * rely on compileShader failing. This function expects
michael@0 1184 * one of the shader to fail OR linking to fail.
michael@0 1185 *
michael@0 1186 * @param {!WebGLContext} gl The WebGLContext to use.
michael@0 1187 * @param {string} vertexShaderScriptId The vertex shader.
michael@0 1188 * @param {string} fragmentShaderScriptId The fragment shader.
michael@0 1189 * @return {WebGLProgram} The created program.
michael@0 1190 */
michael@0 1191 var loadProgramFromScriptExpectError = function(
michael@0 1192 gl, vertexShaderScriptId, fragmentShaderScriptId) {
michael@0 1193 var vertexShader = loadShaderFromScript(gl, vertexShaderScriptId);
michael@0 1194 if (!vertexShader) {
michael@0 1195 return null;
michael@0 1196 }
michael@0 1197 var fragmentShader = loadShaderFromScript(gl, fragmentShaderScriptId);
michael@0 1198 if (!fragmentShader) {
michael@0 1199 return null;
michael@0 1200 }
michael@0 1201 var linkSuccess = true;
michael@0 1202 var program = gl.createProgram();
michael@0 1203 gl.attachShader(program, vertexShader);
michael@0 1204 gl.attachShader(program, fragmentShader);
michael@0 1205 linkSuccess = true;
michael@0 1206 linkProgram(gl, program, function() {
michael@0 1207 linkSuccess = false;
michael@0 1208 });
michael@0 1209 return linkSuccess ? program : null;
michael@0 1210 };
michael@0 1211
michael@0 1212 var basePath;
michael@0 1213 var getBasePath = function() {
michael@0 1214 if (!basePath) {
michael@0 1215 var expectedBase = "webgl-test-utils.js";
michael@0 1216 var scripts = document.getElementsByTagName('script');
michael@0 1217 for (var script, i = 0; script = scripts[i]; i++) {
michael@0 1218 var src = script.src;
michael@0 1219 var l = src.length;
michael@0 1220 if (src.substr(l - expectedBase.length) == expectedBase) {
michael@0 1221 basePath = src.substr(0, l - expectedBase.length);
michael@0 1222 }
michael@0 1223 }
michael@0 1224 }
michael@0 1225 return basePath;
michael@0 1226 };
michael@0 1227
michael@0 1228 var loadStandardVertexShader = function(gl) {
michael@0 1229 return loadShaderFromFile(
michael@0 1230 gl, getBasePath() + "vertexShader.vert", gl.VERTEX_SHADER);
michael@0 1231 };
michael@0 1232
michael@0 1233 var loadStandardFragmentShader = function(gl) {
michael@0 1234 return loadShaderFromFile(
michael@0 1235 gl, getBasePath() + "fragmentShader.frag", gl.FRAGMENT_SHADER);
michael@0 1236 };
michael@0 1237
michael@0 1238 /**
michael@0 1239 * Loads an image asynchronously.
michael@0 1240 * @param {string} url URL of image to load.
michael@0 1241 * @param {!function(!Element): void} callback Function to call
michael@0 1242 * with loaded image.
michael@0 1243 */
michael@0 1244 var loadImageAsync = function(url, callback) {
michael@0 1245 var img = document.createElement('img');
michael@0 1246 img.onload = function() {
michael@0 1247 callback(img);
michael@0 1248 };
michael@0 1249 img.src = url;
michael@0 1250 };
michael@0 1251
michael@0 1252 /**
michael@0 1253 * Loads an array of images.
michael@0 1254 * @param {!Array.<string>} urls URLs of images to load.
michael@0 1255 * @param {!function(!{string, img}): void} callback. Callback
michael@0 1256 * that gets passed map of urls to img tags.
michael@0 1257 */
michael@0 1258 var loadImagesAsync = function(urls, callback) {
michael@0 1259 var count = 1;
michael@0 1260 var images = { };
michael@0 1261 function countDown() {
michael@0 1262 --count;
michael@0 1263 if (count == 0) {
michael@0 1264 callback(images);
michael@0 1265 }
michael@0 1266 }
michael@0 1267 function imageLoaded(url) {
michael@0 1268 return function(img) {
michael@0 1269 images[url] = img;
michael@0 1270 countDown();
michael@0 1271 }
michael@0 1272 }
michael@0 1273 for (var ii = 0; ii < urls.length; ++ii) {
michael@0 1274 ++count;
michael@0 1275 loadImageAsync(urls[ii], imageLoaded(urls[ii]));
michael@0 1276 }
michael@0 1277 countDown();
michael@0 1278 };
michael@0 1279
michael@0 1280 var getUrlArguments = function() {
michael@0 1281 var args = {};
michael@0 1282 try {
michael@0 1283 var s = window.location.href;
michael@0 1284 var q = s.indexOf("?");
michael@0 1285 var e = s.indexOf("#");
michael@0 1286 if (e < 0) {
michael@0 1287 e = s.length;
michael@0 1288 }
michael@0 1289 var query = s.substring(q + 1, e);
michael@0 1290 var pairs = query.split("&");
michael@0 1291 for (var ii = 0; ii < pairs.length; ++ii) {
michael@0 1292 var keyValue = pairs[ii].split("=");
michael@0 1293 var key = keyValue[0];
michael@0 1294 var value = decodeURIComponent(keyValue[1]);
michael@0 1295 args[key] = value;
michael@0 1296 }
michael@0 1297 } catch (e) {
michael@0 1298 throw "could not parse url";
michael@0 1299 }
michael@0 1300 return args;
michael@0 1301 };
michael@0 1302
michael@0 1303 var makeImage = function(canvas) {
michael@0 1304 var img = document.createElement('img');
michael@0 1305 img.src = canvas.toDataURL();
michael@0 1306 return img;
michael@0 1307 };
michael@0 1308
michael@0 1309 var insertImage = function(element, caption, img) {
michael@0 1310 var div = document.createElement("div");
michael@0 1311 div.appendChild(img);
michael@0 1312 var label = document.createElement("div");
michael@0 1313 label.appendChild(document.createTextNode(caption));
michael@0 1314 div.appendChild(label);
michael@0 1315 element.appendChild(div);
michael@0 1316 };
michael@0 1317
michael@0 1318 var addShaderSource = function(element, label, source) {
michael@0 1319 var div = document.createElement("div");
michael@0 1320 var s = document.createElement("pre");
michael@0 1321 s.className = "shader-source";
michael@0 1322 s.style.display = "none";
michael@0 1323 var ol = document.createElement("ol");
michael@0 1324 //s.appendChild(document.createTextNode(source));
michael@0 1325 var lines = source.split("\n");
michael@0 1326 for (var ii = 0; ii < lines.length; ++ii) {
michael@0 1327 var line = lines[ii];
michael@0 1328 var li = document.createElement("li");
michael@0 1329 li.appendChild(document.createTextNode(line));
michael@0 1330 ol.appendChild(li);
michael@0 1331 }
michael@0 1332 s.appendChild(ol);
michael@0 1333 var l = document.createElement("a");
michael@0 1334 l.href = "show-shader-source";
michael@0 1335 l.appendChild(document.createTextNode(label));
michael@0 1336 l.addEventListener('click', function(event) {
michael@0 1337 if (event.preventDefault) {
michael@0 1338 event.preventDefault();
michael@0 1339 }
michael@0 1340 s.style.display = (s.style.display == 'none') ? 'block' : 'none';
michael@0 1341 return false;
michael@0 1342 }, false);
michael@0 1343 div.appendChild(l);
michael@0 1344 div.appendChild(s);
michael@0 1345 element.appendChild(div);
michael@0 1346 }
michael@0 1347
michael@0 1348 return {
michael@0 1349 addShaderSource: addShaderSource,
michael@0 1350 clearAndDrawUnitQuad : clearAndDrawUnitQuad,
michael@0 1351 create3DContext: create3DContext,
michael@0 1352 create3DContextWithWrapperThatThrowsOnGLError:
michael@0 1353 create3DContextWithWrapperThatThrowsOnGLError,
michael@0 1354 checkCanvas: checkCanvas,
michael@0 1355 checkCanvasRect: checkCanvasRect,
michael@0 1356 createColoredTexture: createColoredTexture,
michael@0 1357 drawQuad: drawQuad,
michael@0 1358 drawUnitQuad: drawUnitQuad,
michael@0 1359 endsWith: endsWith,
michael@0 1360 getExtensionWithKnownPrefixes: getExtensionWithKnownPrefixes,
michael@0 1361 getFileListAsync: getFileListAsync,
michael@0 1362 getLastError: getLastError,
michael@0 1363 getScript: getScript,
michael@0 1364 getSupportedExtensionWithKnownPrefixes: getSupportedExtensionWithKnownPrefixes,
michael@0 1365 getUrlArguments: getUrlArguments,
michael@0 1366 glEnumToString: glEnumToString,
michael@0 1367 glErrorShouldBe: glErrorShouldBe,
michael@0 1368 fillTexture: fillTexture,
michael@0 1369 insertImage: insertImage,
michael@0 1370 loadImageAsync: loadImageAsync,
michael@0 1371 loadImagesAsync: loadImagesAsync,
michael@0 1372 loadProgram: loadProgram,
michael@0 1373 loadProgramFromFile: loadProgramFromFile,
michael@0 1374 loadProgramFromScript: loadProgramFromScript,
michael@0 1375 loadProgramFromScriptExpectError: loadProgramFromScriptExpectError,
michael@0 1376 loadShader: loadShader,
michael@0 1377 loadShaderFromFile: loadShaderFromFile,
michael@0 1378 loadShaderFromScript: loadShaderFromScript,
michael@0 1379 loadStandardProgram: loadStandardProgram,
michael@0 1380 loadStandardVertexShader: loadStandardVertexShader,
michael@0 1381 loadStandardFragmentShader: loadStandardFragmentShader,
michael@0 1382 loadTextFileAsync: loadTextFileAsync,
michael@0 1383 loadTexture: loadTexture,
michael@0 1384 log: log,
michael@0 1385 loggingOff: loggingOff,
michael@0 1386 makeImage: makeImage,
michael@0 1387 error: error,
michael@0 1388 setupProgram: setupProgram,
michael@0 1389 setupQuad: setupQuad,
michael@0 1390 setupSimpleTextureFragmentShader: setupSimpleTextureFragmentShader,
michael@0 1391 setupSimpleTextureProgram: setupSimpleTextureProgram,
michael@0 1392 setupSimpleTextureVertexShader: setupSimpleTextureVertexShader,
michael@0 1393 setupTexturedQuad: setupTexturedQuad,
michael@0 1394 setupTexturedQuadWithTexCoords: setupTexturedQuadWithTexCoords,
michael@0 1395 setupUnitQuad: setupUnitQuad,
michael@0 1396 setupUnitQuadWithTexCoords: setupUnitQuadWithTexCoords,
michael@0 1397 setupWebGLWithShaders: setupWebGLWithShaders,
michael@0 1398 startsWith: startsWith,
michael@0 1399 shouldGenerateGLError: shouldGenerateGLError,
michael@0 1400 readFile: readFile,
michael@0 1401 readFileList: readFileList,
michael@0 1402
michael@0 1403 none: false
michael@0 1404 };
michael@0 1405
michael@0 1406 }());
michael@0 1407
michael@0 1408

mercurial