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