Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(340)

Side by Side Diff: conformance/resources/webgl-test-utils.js

Issue 42083002: Add ToT WebGL conformance tests : part 10 (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/deps/third_party/webgl/sdk/tests/
Patch Set: Created 7 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « conformance/resources/webgl-test.js ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Property Changes:
Added: svn:eol-style
## -0,0 +1 ##
+LF
\ No newline at end of property
OLDNEW
(Empty)
1 /*
2 ** Copyright (c) 2012 The Khronos Group Inc.
3 **
4 ** Permission is hereby granted, free of charge, to any person obtaining a
5 ** copy of this software and/or associated documentation files (the
6 ** "Materials"), to deal in the Materials without restriction, including
7 ** without limitation the rights to use, copy, modify, merge, publish,
8 ** distribute, sublicense, and/or sell copies of the Materials, and to
9 ** permit persons to whom the Materials are furnished to do so, subject to
10 ** the following conditions:
11 **
12 ** The above copyright notice and this permission notice shall be included
13 ** in all copies or substantial portions of the Materials.
14 **
15 ** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 ** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
22 */
23 var WebGLTestUtils = (function() {
24 "use strict";
25
26 /**
27 * Wrapped logging function.
28 * @param {string} msg The message to log.
29 */
30 var log = function(msg) {
31 if (window.console && window.console.log) {
32 window.console.log(msg);
33 }
34 };
35
36 /**
37 * Wrapped logging function.
38 * @param {string} msg The message to log.
39 */
40 var error = function(msg) {
41 if (window.console) {
42 if (window.console.error) {
43 window.console.error(msg);
44 }
45 else if (window.console.log) {
46 window.console.log(msg);
47 }
48 }
49 };
50
51 /**
52 * Turn off all logging.
53 */
54 var loggingOff = function() {
55 log = function() {};
56 error = function() {};
57 };
58
59 /**
60 * Converts a WebGL enum to a string.
61 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
62 * @param {number} value The enum value.
63 * @return {string} The enum as a string.
64 */
65 var glEnumToString = function(gl, value) {
66 for (var p in gl) {
67 if (gl[p] == value) {
68 return p;
69 }
70 }
71 return "0x" + value.toString(16);
72 };
73
74 var lastError = "";
75
76 /**
77 * Returns the last compiler/linker error.
78 * @return {string} The last compiler/linker error.
79 */
80 var getLastError = function() {
81 return lastError;
82 };
83
84 /**
85 * Whether a haystack ends with a needle.
86 * @param {string} haystack String to search
87 * @param {string} needle String to search for.
88 * @param {boolean} True if haystack ends with needle.
89 */
90 var endsWith = function(haystack, needle) {
91 return haystack.substr(haystack.length - needle.length) === needle;
92 };
93
94 /**
95 * Whether a haystack starts with a needle.
96 * @param {string} haystack String to search
97 * @param {string} needle String to search for.
98 * @param {boolean} True if haystack starts with needle.
99 */
100 var startsWith = function(haystack, needle) {
101 return haystack.substr(0, needle.length) === needle;
102 };
103
104 /**
105 * A vertex shader for a single texture.
106 * @type {string}
107 */
108 var simpleTextureVertexShader = [
109 'attribute vec4 vPosition;',
110 'attribute vec2 texCoord0;',
111 'varying vec2 texCoord;',
112 'void main() {',
113 ' gl_Position = vPosition;',
114 ' texCoord = texCoord0;',
115 '}'].join('\n');
116
117 /**
118 * A fragment shader for a single texture.
119 * @type {string}
120 */
121 var simpleTextureFragmentShader = [
122 'precision mediump float;',
123 'uniform sampler2D tex;',
124 'varying vec2 texCoord;',
125 'void main() {',
126 ' gl_FragData[0] = texture2D(tex, texCoord);',
127 '}'].join('\n');
128
129 /**
130 * A vertex shader for a single texture.
131 * @type {string}
132 */
133 var noTexCoordTextureVertexShader = [
134 'attribute vec4 vPosition;',
135 'varying vec2 texCoord;',
136 'void main() {',
137 ' gl_Position = vPosition;',
138 ' texCoord = vPosition.xy * 0.5 + 0.5;',
139 '}'].join('\n');
140
141 /**
142 * A vertex shader for a uniform color.
143 * @type {string}
144 */
145 var simpleColorVertexShader = [
146 'attribute vec4 vPosition;',
147 'void main() {',
148 ' gl_Position = vPosition;',
149 '}'].join('\n');
150
151 /**
152 * A fragment shader for a uniform color.
153 * @type {string}
154 */
155 var simpleColorFragmentShader = [
156 'precision mediump float;',
157 'uniform vec4 u_color;',
158 'void main() {',
159 ' gl_FragData[0] = u_color;',
160 '}'].join('\n');
161
162 /**
163 * A vertex shader for vertex colors.
164 * @type {string}
165 */
166 var simpleVertexColorVertexShader = [
167 'attribute vec4 vPosition;',
168 'attribute vec4 a_color;',
169 'varying vec4 v_color;',
170 'void main() {',
171 ' gl_Position = vPosition;',
172 ' v_color = a_color;',
173 '}'].join('\n');
174
175 /**
176 * A fragment shader for vertex colors.
177 * @type {string}
178 */
179 var simpleVertexColorFragmentShader = [
180 'precision mediump float;',
181 'varying vec4 v_color;',
182 'void main() {',
183 ' gl_FragData[0] = v_color;',
184 '}'].join('\n');
185
186 /**
187 * Creates a simple texture vertex shader.
188 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
189 * @return {!WebGLShader}
190 */
191 var setupSimpleTextureVertexShader = function(gl) {
192 return loadShader(gl, simpleTextureVertexShader, gl.VERTEX_SHADER);
193 };
194
195 /**
196 * Creates a simple texture fragment shader.
197 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
198 * @return {!WebGLShader}
199 */
200 var setupSimpleTextureFragmentShader = function(gl) {
201 return loadShader(
202 gl, simpleTextureFragmentShader, gl.FRAGMENT_SHADER);
203 };
204
205 /**
206 * Creates a texture vertex shader that doesn't need texcoords.
207 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
208 * @return {!WebGLShader}
209 */
210 var setupNoTexCoordTextureVertexShader = function(gl) {
211 return loadShader(gl, noTexCoordTextureVertexShader, gl.VERTEX_SHADER);
212 };
213
214 /**
215 * Creates a simple vertex color vertex shader.
216 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
217 * @return {!WebGLShader}
218 */
219 var setupSimpleVertexColorVertexShader = function(gl) {
220 return loadShader(gl, simpleVertexColorVertexShader, gl.VERTEX_SHADER);
221 };
222
223 /**
224 * Creates a simple vertex color fragment shader.
225 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
226 * @return {!WebGLShader}
227 */
228 var setupSimpleVertexColorFragmentShader = function(gl) {
229 return loadShader(
230 gl, simpleVertexColorFragmentShader, gl.FRAGMENT_SHADER);
231 };
232
233 /**
234 * Creates a simple color vertex shader.
235 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
236 * @return {!WebGLShader}
237 */
238 var setupSimpleColorVertexShader = function(gl) {
239 return loadShader(gl, simpleColorVertexShader, gl.VERTEX_SHADER);
240 };
241
242 /**
243 * Creates a simple color fragment shader.
244 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
245 * @return {!WebGLShader}
246 */
247 var setupSimpleColorFragmentShader = function(gl) {
248 return loadShader(
249 gl, simpleColorFragmentShader, gl.FRAGMENT_SHADER);
250 };
251
252 /**
253 * Creates a program, attaches shaders, binds attrib locations, links the
254 * program and calls useProgram.
255 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
256 * @param {!Array.<!WebGLShader|string>} shaders The shaders to
257 * attach, or the source, or the id of a script to get
258 * the source from.
259 * @param {!Array.<string>} opt_attribs The attribs names.
260 * @param {!Array.<number>} opt_locations The locations for the attribs.
261 */
262 var setupProgram = function(gl, shaders, opt_attribs, opt_locations) {
263 var realShaders = [];
264 var program = gl.createProgram();
265 var shaderCount = 0;
266 for (var ii = 0; ii < shaders.length; ++ii) {
267 var shader = shaders[ii];
268 var shaderType = undefined;
269 if (typeof shader == 'string') {
270 var element = document.getElementById(shader);
271 if (element) {
272 if (element.type != "x-shader/x-vertex" && element.type != "x-shader/x-f ragment")
273 shaderType = ii ? gl.FRAGMENT_SHADER : gl.VERTEX_SHADER;
274 shader = loadShaderFromScript(gl, shader, shaderType);
275 } else if (endsWith(shader, ".vert")) {
276 shader = loadShaderFromFile(gl, shader, gl.VERTEX_SHADER);
277 } else if (endsWith(shader, ".frag")) {
278 shader = loadShaderFromFile(gl, shader, gl.FRAGMENT_SHADER);
279 } else {
280 shader = loadShader(gl, shader, ii ? gl.FRAGMENT_SHADER : gl.VERTEX_SHAD ER);
281 }
282 }
283 if (shader) {
284 ++shaderCount;
285 gl.attachShader(program, shader);
286 }
287 }
288 if (shaderCount != 2) {
289 error("Error in compiling shader");
290 return null;
291 }
292 if (opt_attribs) {
293 for (var ii = 0; ii < opt_attribs.length; ++ii) {
294 gl.bindAttribLocation(
295 program,
296 opt_locations ? opt_locations[ii] : ii,
297 opt_attribs[ii]);
298 }
299 }
300 gl.linkProgram(program);
301
302 // Check the link status
303 var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
304 if (!linked) {
305 // something went wrong with the link
306 lastError = gl.getProgramInfoLog (program);
307 error("Error in program linking:" + lastError);
308
309 gl.deleteProgram(program);
310 return null;
311 }
312
313 gl.useProgram(program);
314 return program;
315 };
316
317 /**
318 * Creates a simple texture program.
319 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
320 * @return {WebGLProgram}
321 */
322 var setupNoTexCoordTextureProgram = function(gl) {
323 var vs = setupNoTexCoordTextureVertexShader(gl);
324 var fs = setupSimpleTextureFragmentShader(gl);
325 if (!vs || !fs) {
326 return null;
327 }
328 var program = setupProgram(
329 gl,
330 [vs, fs],
331 ['vPosition'],
332 [0]);
333 if (!program) {
334 gl.deleteShader(fs);
335 gl.deleteShader(vs);
336 }
337 gl.useProgram(program);
338 return program;
339 };
340
341 /**
342 * Creates a simple texture program.
343 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
344 * @param {number} opt_positionLocation The attrib location for position.
345 * @param {number} opt_texcoordLocation The attrib location for texture coords.
346 * @return {WebGLProgram}
347 */
348 var setupSimpleTextureProgram = function(
349 gl, opt_positionLocation, opt_texcoordLocation) {
350 opt_positionLocation = opt_positionLocation || 0;
351 opt_texcoordLocation = opt_texcoordLocation || 1;
352 var vs = setupSimpleTextureVertexShader(gl);
353 var fs = setupSimpleTextureFragmentShader(gl);
354 if (!vs || !fs) {
355 return null;
356 }
357 var program = setupProgram(
358 gl,
359 [vs, fs],
360 ['vPosition', 'texCoord0'],
361 [opt_positionLocation, opt_texcoordLocation]);
362 if (!program) {
363 gl.deleteShader(fs);
364 gl.deleteShader(vs);
365 }
366 gl.useProgram(program);
367 return program;
368 };
369
370 /**
371 * Creates a simple vertex color program.
372 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
373 * @param {number} opt_positionLocation The attrib location for position.
374 * @param {number} opt_vertexColorLocation The attrib location
375 * for vertex colors.
376 * @return {WebGLProgram}
377 */
378 var setupSimpleVertexColorProgram = function(
379 gl, opt_positionLocation, opt_vertexColorLocation) {
380 opt_positionLocation = opt_positionLocation || 0;
381 opt_vertexColorLocation = opt_vertexColorLocation || 1;
382 var vs = setupSimpleVertexColorVertexShader(gl);
383 var fs = setupSimpleVertexColorFragmentShader(gl);
384 if (!vs || !fs) {
385 return null;
386 }
387 var program = setupProgram(
388 gl,
389 [vs, fs],
390 ['vPosition', 'a_color'],
391 [opt_positionLocation, opt_vertexColorLocation]);
392 if (!program) {
393 gl.deleteShader(fs);
394 gl.deleteShader(vs);
395 }
396 gl.useProgram(program);
397 return program;
398 };
399
400 /**
401 * Creates a simple color program.
402 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
403 * @param {number} opt_positionLocation The attrib location for position.
404 * @return {WebGLProgram}
405 */
406 var setupSimpleColorProgram = function(gl, opt_positionLocation) {
407 opt_positionLocation = opt_positionLocation || 0;
408 var vs = setupSimpleColorVertexShader(gl);
409 var fs = setupSimpleColorFragmentShader(gl);
410 if (!vs || !fs) {
411 return null;
412 }
413 var program = setupProgram(
414 gl,
415 [vs, fs],
416 ['vPosition'],
417 [opt_positionLocation]);
418 if (!program) {
419 gl.deleteShader(fs);
420 gl.deleteShader(vs);
421 }
422 gl.useProgram(program);
423 return program;
424 };
425
426 /**
427 * Creates buffers for a textured unit quad and attaches them to vertex attribs.
428 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
429 * @param {number} opt_positionLocation The attrib location for position.
430 * @param {number} opt_texcoordLocation The attrib location for texture coords.
431 * @return {!Array.<WebGLBuffer>} The buffer objects that were
432 * created.
433 */
434 var setupUnitQuad = function(gl, opt_positionLocation, opt_texcoordLocation) {
435 return setupUnitQuadWithTexCoords(gl, [ 0.0, 0.0 ], [ 1.0, 1.0 ],
436 opt_positionLocation, opt_texcoordLocation);
437 };
438
439 /**
440 * Creates buffers for a textured unit quad with specified lower left
441 * and upper right texture coordinates, and attaches them to vertex
442 * attribs.
443 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
444 * @param {!Array.<number>} lowerLeftTexCoords The texture coordinates for the l ower left corner.
445 * @param {!Array.<number>} upperRightTexCoords The texture coordinates for the upper right corner.
446 * @param {number} opt_positionLocation The attrib location for position.
447 * @param {number} opt_texcoordLocation The attrib location for texture coords.
448 * @return {!Array.<WebGLBuffer>} The buffer objects that were
449 * created.
450 */
451 var setupUnitQuadWithTexCoords = function(
452 gl, lowerLeftTexCoords, upperRightTexCoords,
453 opt_positionLocation, opt_texcoordLocation) {
454 return setupQuad(gl, {
455 positionLocation: opt_positionLocation || 0,
456 texcoordLocation: opt_texcoordLocation || 1,
457 lowerLeftTexCoords: lowerLeftTexCoords,
458 upperRightTexCoords: upperRightTexCoords,
459 });
460 };
461
462 /**
463 * Makes a quad with various options.
464 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
465 * @param {!Object} options.
466 *
467 * scale: scale to multiple unit quad values by. default 1.0.
468 * positionLocation: attribute location for position.
469 * texcoordLocation: attribute location for texcoords.
470 * If this does not exist no texture coords are created.
471 * lowerLeftTexCoords: an array of 2 values for the
472 * lowerLeftTexCoords.
473 * upperRightTexCoords: an array of 2 values for the
474 * upperRightTexCoords.
475 */
476 var setupQuad = function(gl, options) {
477 var positionLocation = options.positionLocation || 0;
478 var scale = options.scale || 1;
479
480 var objects = [];
481
482 var vertexObject = gl.createBuffer();
483 gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject);
484 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
485 1.0 * scale , 1.0 * scale,
486 -1.0 * scale , 1.0 * scale,
487 -1.0 * scale , -1.0 * scale,
488 1.0 * scale , 1.0 * scale,
489 -1.0 * scale , -1.0 * scale,
490 1.0 * scale , -1.0 * scale,]), gl.STATIC_DRAW);
491 gl.enableVertexAttribArray(positionLocation);
492 gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
493 objects.push(vertexObject);
494
495 if (options.texcoordLocation !== undefined) {
496 var llx = options.lowerLeftTexCoords[0];
497 var lly = options.lowerLeftTexCoords[1];
498 var urx = options.upperRightTexCoords[0];
499 var ury = options.upperRightTexCoords[1];
500
501 var vertexObject = gl.createBuffer();
502 gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject);
503 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
504 urx, ury,
505 llx, ury,
506 llx, lly,
507 urx, ury,
508 llx, lly,
509 urx, lly]), gl.STATIC_DRAW);
510 gl.enableVertexAttribArray(options.texcoordLocation);
511 gl.vertexAttribPointer(options.texcoordLocation, 2, gl.FLOAT, false, 0, 0);
512 objects.push(vertexObject);
513 }
514
515 return objects;
516 };
517
518 /**
519 * Creates a program and buffers for rendering a textured quad.
520 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
521 * @param {number} opt_positionLocation The attrib location for
522 * position. Default = 0.
523 * @param {number} opt_texcoordLocation The attrib location for
524 * texture coords. Default = 1.
525 * @return {!WebGLProgram}
526 */
527 var setupTexturedQuad = function(
528 gl, opt_positionLocation, opt_texcoordLocation) {
529 var program = setupSimpleTextureProgram(
530 gl, opt_positionLocation, opt_texcoordLocation);
531 setupUnitQuad(gl, opt_positionLocation, opt_texcoordLocation);
532 return program;
533 };
534
535 /**
536 * Creates a program and buffers for rendering a color quad.
537 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
538 * @param {number} opt_positionLocation The attrib location for position.
539 * @return {!WebGLProgram}
540 */
541 var setupColorQuad = function(gl, opt_positionLocation) {
542 opt_positionLocation = opt_positionLocation || 0;
543 var program = setupSimpleColorProgram(gl);
544 setupUnitQuad(gl, opt_positionLocation);
545 return program;
546 };
547
548 /**
549 * Creates a program and buffers for rendering a textured quad with
550 * specified lower left and upper right texture coordinates.
551 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
552 * @param {!Array.<number>} lowerLeftTexCoords The texture coordinates for the l ower left corner.
553 * @param {!Array.<number>} upperRightTexCoords The texture coordinates for the upper right corner.
554 * @param {number} opt_positionLocation The attrib location for position.
555 * @param {number} opt_texcoordLocation The attrib location for texture coords.
556 * @return {!WebGLProgram}
557 */
558 var setupTexturedQuadWithTexCoords = function(
559 gl, lowerLeftTexCoords, upperRightTexCoords,
560 opt_positionLocation, opt_texcoordLocation) {
561 var program = setupSimpleTextureProgram(
562 gl, opt_positionLocation, opt_texcoordLocation);
563 setupUnitQuadWithTexCoords(gl, lowerLeftTexCoords, upperRightTexCoords,
564 opt_positionLocation, opt_texcoordLocation);
565 return program;
566 };
567
568 /**
569 * Creates a unit quad with only positions of a given resolution.
570 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
571 * @param {number} gridRes The resolution of the mesh grid,
572 * expressed in the number of quads across and down.
573 * @param {number} opt_positionLocation The attrib location for position.
574 */
575 var setupIndexedQuad = function (
576 gl, gridRes, opt_positionLocation, opt_flipOddTriangles) {
577 return setupIndexedQuadWithOptions(gl,
578 { gridRes: gridRes,
579 positionLocation: opt_positionLocation,
580 flipOddTriangles: opt_flipOddTriangles
581 });
582 };
583
584 /**
585 * Creates a quad with various options.
586 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
587 * @param {!Object) options The options. See below.
588 * @return {!Array.<WebGLBuffer>} The created buffers.
589 * [positions, <colors>, indices]
590 *
591 * Options:
592 * gridRes: number of quads across and down grid.
593 * positionLocation: attrib location for position
594 * flipOddTriangles: reverse order of vertices of every other
595 * triangle
596 * positionOffset: offset added to each vertex
597 * positionMult: multipier for each vertex
598 * colorLocation: attrib location for vertex colors. If
599 * undefined no vertex colors will be created.
600 */
601 var setupIndexedQuadWithOptions = function (gl, options) {
602 var positionLocation = options.positionLocation || 0;
603 var objects = [];
604
605 var gridRes = options.gridRes || 1;
606 var positionOffset = options.positionOffset || 0;
607 var positionMult = options.positionMult || 1;
608 var vertsAcross = gridRes + 1;
609 var numVerts = vertsAcross * vertsAcross;
610 var positions = new Float32Array(numVerts * 3);
611 var indices = new Uint16Array(6 * gridRes * gridRes);
612 var poffset = 0;
613
614 for (var yy = 0; yy <= gridRes; ++yy) {
615 for (var xx = 0; xx <= gridRes; ++xx) {
616 positions[poffset + 0] = (-1 + 2 * xx / gridRes) * positionMult + position Offset;
617 positions[poffset + 1] = (-1 + 2 * yy / gridRes) * positionMult + position Offset;
618 positions[poffset + 2] = 0;
619
620 poffset += 3;
621 }
622 }
623
624 var buf = gl.createBuffer();
625 gl.bindBuffer(gl.ARRAY_BUFFER, buf);
626 gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
627 gl.enableVertexAttribArray(positionLocation);
628 gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0);
629 objects.push(buf);
630
631 if (options.colorLocation !== undefined) {
632 var colors = new Float32Array(numVerts * 4);
633 for (var yy = 0; yy <= gridRes; ++yy) {
634 for (var xx = 0; xx <= gridRes; ++xx) {
635 if (options.color !== undefined) {
636 colors[poffset + 0] = options.color[0];
637 colors[poffset + 1] = options.color[1];
638 colors[poffset + 2] = options.color[2];
639 colors[poffset + 3] = options.color[3];
640 } else {
641 colors[poffset + 0] = xx / gridRes;
642 colors[poffset + 1] = yy / gridRes;
643 colors[poffset + 2] = (xx / gridRes) * (yy / gridRes);
644 colors[poffset + 3] = (yy % 2) * 0.5 + 0.5;
645 }
646 poffset += 4;
647 }
648 }
649
650 var buf = gl.createBuffer();
651 gl.bindBuffer(gl.ARRAY_BUFFER, buf);
652 gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW);
653 gl.enableVertexAttribArray(options.colorLocation);
654 gl.vertexAttribPointer(options.colorLocation, 4, gl.FLOAT, false, 0, 0);
655 objects.push(buf);
656 }
657
658 var tbase = 0;
659 for (var yy = 0; yy < gridRes; ++yy) {
660 var index = yy * vertsAcross;
661 for (var xx = 0; xx < gridRes; ++xx) {
662 indices[tbase + 0] = index + 0;
663 indices[tbase + 1] = index + 1;
664 indices[tbase + 2] = index + vertsAcross;
665 indices[tbase + 3] = index + vertsAcross;
666 indices[tbase + 4] = index + 1;
667 indices[tbase + 5] = index + vertsAcross + 1;
668
669 if (options.flipOddTriangles) {
670 indices[tbase + 4] = index + vertsAcross + 1;
671 indices[tbase + 5] = index + 1;
672 }
673
674 index += 1;
675 tbase += 6;
676 }
677 }
678
679 var buf = gl.createBuffer();
680 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buf);
681 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
682 objects.push(buf);
683
684 return objects;
685 };
686
687 /**
688 * Returns the constructor for an ArrayBuffer that
689 * corresponds to the given WebGL type.
690 * @param {!WebGLRenderingContext} gl A WebGLRenderingContext.
691 * @param {number} type The WebGL type (eg, gl.UNSIGNED_BYTE)
692 * @return {!Constructor} The ArrayBuffer constructor that
693 * corresponds to the given type.
694 */
695 var glTypeToArrayBufferType = function(gl, type) {
696 switch (type) {
697 case gl.BYTE:
698 return window.Int8Array;
699 case gl.UNSIGNED_BYTE:
700 return window.Uint8Array;
701 case gl.SHORT:
702 return window.Int16Array;
703 case gl.UNSIGNED_SHORT:
704 case gl.UNSIGNED_SHORT_5_6_5:
705 case gl.UNSIGNED_SHORT_4_4_4_4:
706 case gl.UNSIGNED_SHORT_5_5_5_1:
707 return window.Uint16Array;
708 case gl.INT:
709 return window.Int32Array;
710 case gl.UNSIGNED_INT:
711 return window.Uint32Array;
712 default:
713 throw 'unknown gl type ' + glEnumToString(gl, type);
714 }
715 };
716
717 /**
718 * Returns the number of bytes per component for a given WebGL
719 * type.
720 * @param {!WebGLRenderingContext} gl A WebGLRenderingContext.
721 * @param {number} type The WebGL type (eg, gl.UNSIGNED_BYTE)
722 * @return {!Constructor} The ArrayBuffer constructor that
723 * corresponds to the given type.
724 */
725 var getBytesPerComponent = function(gl, type) {
726 switch (type) {
727 case gl.BYTE:
728 case gl.UNSIGNED_BYTE:
729 return 1;
730 case gl.SHORT:
731 case gl.UNSIGNED_SHORT:
732 case gl.UNSIGNED_SHORT_5_6_5:
733 case gl.UNSIGNED_SHORT_4_4_4_4:
734 case gl.UNSIGNED_SHORT_5_5_5_1:
735 return 2;
736 case gl.INT:
737 case gl.UNSIGNED_INT:
738 return 4;
739 default:
740 throw 'unknown gl type ' + glEnumToString(gl, type);
741 }
742 };
743
744 /**
745 * Fills the given texture with a solid color.
746 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
747 * @param {!WebGLTexture} tex The texture to fill.
748 * @param {number} width The width of the texture to create.
749 * @param {number} height The height of the texture to create.
750 * @param {!Array.<number>} color The color to fill with.
751 * where each element is in the range 0 to 255.
752 * @param {number} opt_level The level of the texture to fill. Default = 0.
753 * @param {number} opt_format The format for the texture.
754 */
755 var fillTexture = function(gl, tex, width, height, color, opt_level, opt_format, opt_type) {
756 opt_level = opt_level || 0;
757 opt_format = opt_format || gl.RGBA;
758 opt_type = opt_type || gl.UNSIGNED_BYTE;
759 var pack = gl.getParameter(gl.UNPACK_ALIGNMENT);
760 var numComponents = color.length;
761 var bytesPerComponent = getBytesPerComponent(gl, opt_type);
762 var rowSize = numComponents * width * bytesPerComponent;
763 var paddedRowSize = Math.floor((rowSize + pack - 1) / pack) * pack;
764 var size = rowSize + (height - 1) * paddedRowSize;
765 size = Math.floor((size + bytesPerComponent - 1) / bytesPerComponent) * bytesP erComponent;
766 var buf = new (glTypeToArrayBufferType(gl, opt_type))(size);
767 for (var yy = 0; yy < height; ++yy) {
768 var off = yy * paddedRowSize;
769 for (var xx = 0; xx < width; ++xx) {
770 for (var jj = 0; jj < numComponents; ++jj) {
771 buf[off++] = color[jj];
772 }
773 }
774 }
775 gl.bindTexture(gl.TEXTURE_2D, tex);
776 gl.texImage2D(
777 gl.TEXTURE_2D, opt_level, opt_format, width, height, 0,
778 opt_format, opt_type, buf);
779 };
780
781 /**
782 * Creates a texture and fills it with a solid color.
783 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
784 * @param {number} width The width of the texture to create.
785 * @param {number} height The height of the texture to create.
786 * @param {!Array.<number>} color The color to fill with. A 4 element array
787 * where each element is in the range 0 to 255.
788 * @return {!WebGLTexture}
789 */
790 var createColoredTexture = function(gl, width, height, color) {
791 var tex = gl.createTexture();
792 fillTexture(gl, tex, width, height, color);
793 return tex;
794 };
795
796 var ubyteToFloat = function(c) {
797 return c / 255;
798 };
799
800 var ubyteColorToFloatColor = function(color) {
801 var floatColor = [];
802 for (var ii = 0; ii < color.length; ++ii) {
803 floatColor[ii] = ubyteToFloat(color[ii]);
804 }
805 return floatColor;
806 };
807
808 /**
809 * Sets the "u_color" uniform of the current program to color.
810 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
811 * @param {!Array.<number> color 4 element array of 0-1 color
812 * components.
813 */
814 var setFloatDrawColor = function(gl, color) {
815 var program = gl.getParameter(gl.CURRENT_PROGRAM);
816 var colorLocation = gl.getUniformLocation(program, "u_color");
817 gl.uniform4fv(colorLocation, color);
818 };
819
820 /**
821 * Sets the "u_color" uniform of the current program to color.
822 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
823 * @param {!Array.<number> color 4 element array of 0-255 color
824 * components.
825 */
826 var setUByteDrawColor = function(gl, color) {
827 setFloatDrawColor(gl, ubyteColorToFloatColor(color));
828 };
829
830 /**
831 * Draws a previously setup quad in the given color.
832 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
833 * @param {!Array.<number>} color The color to draw with. A 4
834 * element array where each element is in the range 0 to
835 * 1.
836 */
837 var drawFloatColorQuad = function(gl, color) {
838 var program = gl.getParameter(gl.CURRENT_PROGRAM);
839 var colorLocation = gl.getUniformLocation(program, "u_color");
840 gl.uniform4fv(colorLocation, color);
841 gl.drawArrays(gl.TRIANGLES, 0, 6);
842 };
843
844
845 /**
846 * Draws a previously setup quad in the given color.
847 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
848 * @param {!Array.<number>} color The color to draw with. A 4
849 * element array where each element is in the range 0 to
850 * 255.
851 */
852 var drawUByteColorQuad = function(gl, color) {
853 drawFloatColorQuad(gl, ubyteColorToFloatColor(color));
854 };
855
856 /**
857 * Draws a previously setupUnitQuad.
858 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
859 */
860 var drawUnitQuad = function(gl) {
861 gl.drawArrays(gl.TRIANGLES, 0, 6);
862 };
863
864 /**
865 * Clears then Draws a previously setupUnitQuad.
866 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
867 * @param {!Array.<number>} opt_color The color to fill clear with before
868 * drawing. A 4 element array where each element is in the range 0 to
869 * 255. Default [255, 255, 255, 255]
870 */
871 var clearAndDrawUnitQuad = function(gl, opt_color) {
872 opt_color = opt_color || [255, 255, 255, 255];
873 gl.clearColor(
874 opt_color[0] / 255,
875 opt_color[1] / 255,
876 opt_color[2] / 255,
877 opt_color[3] / 255);
878 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
879 drawUnitQuad(gl);
880 };
881
882 /**
883 * Draws a quad previously setup with setupIndexedQuad.
884 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
885 * @param {number} gridRes Resolution of grid.
886 */
887 var drawIndexedQuad = function(gl, gridRes) {
888 gl.drawElements(gl.TRIANGLES, gridRes * gridRes * 6, gl.UNSIGNED_SHORT, 0);
889 };
890
891 /**
892 * Draws a previously setupIndexedQuad
893 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
894 * @param {number} gridRes Resolution of grid.
895 * @param {!Array.<number>} opt_color The color to fill clear with before
896 * drawing. A 4 element array where each element is in the range 0 to
897 * 255. Default [255, 255, 255, 255]
898 */
899 var clearAndDrawIndexedQuad = function(gl, gridRes, opt_color) {
900 opt_color = opt_color || [255, 255, 255, 255];
901 gl.clearColor(
902 opt_color[0] / 255,
903 opt_color[1] / 255,
904 opt_color[2] / 255,
905 opt_color[3] / 255);
906 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
907 drawIndexedQuad(gl, gridRes);
908 };
909
910 /**
911 * Clips a range to min, max
912 * (Eg. clipToRange(-5,7,0,20) would return {value:0,extent:2}
913 * @param {number} value start of range
914 * @param {number} extent extent of range
915 * @param {number} min min.
916 * @param {number} max max.
917 * @return {!{value:number,extent:number} The clipped value.
918 */
919 var clipToRange = function(value, extent, min, max) {
920 if (value < min) {
921 extent -= min - value;
922 value = min;
923 }
924 var end = value + extent;
925 if (end > max) {
926 extent -= end - max;
927 }
928 if (extent < 0) {
929 value = max;
930 extent = 0;
931 }
932 return {value:value, extent: extent};
933 };
934
935 /**
936 * Checks that a portion of a canvas is 1 color.
937 * @param {!WebGLRenderingContext|CanvasRenderingContext2D} gl The
938 * WebGLRenderingContext or 2D context to use.
939 * @param {number} x left corner of region to check.
940 * @param {number} y bottom corner of region to check in case of checking from
941 * a GL context or top corner in case of checking from a 2D context.
942 * @param {number} width width of region to check.
943 * @param {number} height width of region to check.
944 * @param {!Array.<number>} color The color expected. A 4 element array where
945 * each element is in the range 0 to 255.
946 * @param {number} opt_errorRange Optional. Acceptable error in
947 * color checking. 0 by default.
948 * @param {!function()} sameFn Function to call if all pixels
949 * are the same as color.
950 * @param {!function()} differentFn Function to call if a pixel
951 * is different than color
952 * @param {!function()} logFn Function to call for logging.
953 */
954 var checkCanvasRectColor = function(gl, x, y, width, height, color, opt_errorRan ge, sameFn, differentFn, logFn) {
955 if ((gl instanceof WebGLRenderingContext) && !gl.getParameter(gl.FRAMEBUFFER_B INDING)) {
956 // We're reading the backbuffer so clip.
957 var xr = clipToRange(x, width, 0, gl.canvas.width);
958 var yr = clipToRange(y, height, 0, gl.canvas.height);
959 if (!xr.extent || !yr.extent) {
960 logFn("checking rect: effective width or heigh is zero");
961 sameFn();
962 return;
963 }
964 x = xr.value;
965 y = yr.value;
966 width = xr.extent;
967 height = yr.extent;
968 }
969 var errorRange = opt_errorRange || 0;
970 if (!errorRange.length) {
971 errorRange = [errorRange, errorRange, errorRange, errorRange]
972 }
973 var buf;
974 if (gl instanceof WebGLRenderingContext) {
975 buf = new Uint8Array(width * height * 4);
976 gl.readPixels(x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
977 } else {
978 buf = gl.getImageData(x, y, width, height).data;
979 }
980 for (var i = 0; i < width * height; ++i) {
981 var offset = i * 4;
982 for (var j = 0; j < color.length; ++j) {
983 if (Math.abs(buf[offset + j] - color[j]) > errorRange[j]) {
984 differentFn();
985 var was = buf[offset + 0].toString();
986 for (j = 1; j < color.length; ++j) {
987 was += "," + buf[offset + j];
988 }
989 logFn('at (' + (x + (i % width)) + ', ' + (y + Math.floor(i / width)) +
990 ') expected: ' + color + ' was ' + was);
991 return;
992 }
993 }
994 }
995 sameFn();
996 };
997
998 /**
999 * Checks that a portion of a canvas is 1 color.
1000 * @param {!WebGLRenderingContext|CanvasRenderingContext2D} gl The
1001 * WebGLRenderingContext or 2D context to use.
1002 * @param {number} x left corner of region to check.
1003 * @param {number} y bottom corner of region to check in case of checking from
1004 * a GL context or top corner in case of checking from a 2D context.
1005 * @param {number} width width of region to check.
1006 * @param {number} height width of region to check.
1007 * @param {!Array.<number>} color The color expected. A 4 element array where
1008 * each element is in the range 0 to 255.
1009 * @param {string} opt_msg Message to associate with success. Eg
1010 * ("should be red").
1011 * @param {number} opt_errorRange Optional. Acceptable error in
1012 * color checking. 0 by default.
1013 */
1014 var checkCanvasRect = function(gl, x, y, width, height, color, opt_msg, opt_erro rRange) {
1015 var msg = opt_msg;
1016 if (msg === undefined) {
1017 msg = "should be " + color.toString();
1018 }
1019 checkCanvasRectColor(
1020 gl, x, y, width, height, color, opt_errorRange,
1021 function() {
1022 testPassed(msg);
1023 },
1024 function() {
1025 testFailed(msg);
1026 },
1027 debug);
1028 };
1029
1030 /**
1031 * Checks that an entire canvas is 1 color.
1032 * @param {!WebGLRenderingContext|CanvasRenderingContext2D} gl The
1033 * WebGLRenderingContext or 2D context to use.
1034 * @param {!Array.<number>} color The color expected. A 4 element array where
1035 * each element is in the range 0 to 255.
1036 * @param {string} msg Message to associate with success. Eg ("should be red").
1037 * @param {number} errorRange Optional. Acceptable error in
1038 * color checking. 0 by default.
1039 */
1040 var checkCanvas = function(gl, color, msg, errorRange) {
1041 checkCanvasRect(gl, 0, 0, gl.canvas.width, gl.canvas.height, color, msg, error Range);
1042 };
1043
1044 /**
1045 * Checks a rectangular area both inside the area and outside
1046 * the area.
1047 * @param {!WebGLRenderingContext|CanvasRenderingContext2D} gl The
1048 * WebGLRenderingContext or 2D context to use.
1049 * @param {number} x left corner of region to check.
1050 * @param {number} y bottom corner of region to check in case of checking from
1051 * a GL context or top corner in case of checking from a 2D context.
1052 * @param {number} width width of region to check.
1053 * @param {number} height width of region to check.
1054 * @param {!Array.<number>} innerColor The color expected inside
1055 * the area. A 4 element array where each element is in the
1056 * range 0 to 255.
1057 * @param {!Array.<number>} outerColor The color expected
1058 * outside. A 4 element array where each element is in the
1059 * range 0 to 255.
1060 * @param {!number} opt_edgeSize: The number of pixels to skip
1061 * around the edges of the area. Defaut 0.
1062 * @param {!{width:number, height:number}} opt_outerDimensions
1063 * The outer dimensions. Default the size of gl.canvas.
1064 */
1065 var checkAreaInAndOut = function(gl, x, y, width, height, innerColor, outerColor , opt_edgeSize, opt_outerDimensions) {
1066 var outerDimensions = opt_outerDimensions || { width: gl.canvas.width, height: gl.canvas.height };
1067 var edgeSize = opt_edgeSize || 0;
1068 checkCanvasRect(gl, x + edgeSize, y + edgeSize, width - edgeSize * 2, height - edgeSize * 2, innerColor);
1069 checkCanvasRect(gl, 0, 0, x - edgeSize, outerDimensions.height, outerColor);
1070 checkCanvasRect(gl, x + width + edgeSize, 0, outerDimensions.width - x - width - edgeSize, outerDimensions.height, outerColor);
1071 checkCanvasRect(gl, 0, 0, outerDimensions.width, y - edgeSize, outerColor);
1072 checkCanvasRect(gl, 0, y + height + edgeSize, outerDimensions.width, outerDime nsions.height - y - height - edgeSize, outerColor);
1073 };
1074
1075 /**
1076 * Loads a texture, calls callback when finished.
1077 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
1078 * @param {string} url URL of image to load
1079 * @param {function(!Image): void} callback Function that gets called after
1080 * image has loaded
1081 * @return {!WebGLTexture} The created texture.
1082 */
1083 var loadTexture = function(gl, url, callback) {
1084 var texture = gl.createTexture();
1085 gl.bindTexture(gl.TEXTURE_2D, texture);
1086 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
1087 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
1088 var image = new Image();
1089 image.onload = function() {
1090 gl.bindTexture(gl.TEXTURE_2D, texture);
1091 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
1092 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, imag e);
1093 callback(image);
1094 };
1095 image.src = url;
1096 return texture;
1097 };
1098
1099 /**
1100 * Makes a shallow copy of an object.
1101 * @param {!Object) src Object to copy
1102 * @return {!Object} The copy of src.
1103 */
1104 var shallowCopyObject = function(src) {
1105 var dst = {};
1106 for (var attr in src) {
1107 if (src.hasOwnProperty(attr)) {
1108 dst[attr] = src[attr];
1109 }
1110 }
1111 return dst;
1112 };
1113
1114 /**
1115 * Checks if an attribute exists on an object case insensitive.
1116 * @param {!Object) obj Object to check
1117 * @param {string} attr Name of attribute to look for.
1118 * @return {string?} The name of the attribute if it exists,
1119 * undefined if not.
1120 */
1121 var hasAttributeCaseInsensitive = function(obj, attr) {
1122 var lower = attr.toLowerCase();
1123 for (var key in obj) {
1124 if (obj.hasOwnProperty(key) && key.toLowerCase() == lower) {
1125 return key;
1126 }
1127 }
1128 };
1129
1130 /**
1131 * Creates a webgl context.
1132 * @param {!Canvas|string} opt_canvas The canvas tag to get
1133 * context from. If one is not passed in one will be
1134 * created. If it's a string it's assumed to be the id of a
1135 * canvas.
1136 * @param {Object} opt_attributes Context attributes.
1137 * @return {!WebGLRenderingContext} The created context.
1138 */
1139 var create3DContext = function(opt_canvas, opt_attributes) {
1140 if (window.initTestingHarness) {
1141 window.initTestingHarness();
1142 }
1143 var attributes = shallowCopyObject(opt_attributes || {});
1144 if (!hasAttributeCaseInsensitive(attributes, "antialias")) {
1145 attributes.antialias = false;
1146 }
1147
1148 opt_canvas = opt_canvas || document.createElement("canvas");
1149 if (typeof opt_canvas == 'string') {
1150 opt_canvas = document.getElementById(opt_canvas);
1151 }
1152 var context = null;
1153 var names = ["webgl", "experimental-webgl"];
1154 for (var i = 0; i < names.length; ++i) {
1155 try {
1156 context = opt_canvas.getContext(names[i], attributes);
1157 } catch (e) {
1158 }
1159 if (context) {
1160 break;
1161 }
1162 }
1163 if (!context) {
1164 testFailed("Unable to fetch WebGL rendering context for Canvas");
1165 }
1166 return context;
1167 }
1168
1169 /**
1170 * Gets a GLError value as a string.
1171 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
1172 * @param {number} err The webgl error as retrieved from gl.getError().
1173 * @return {string} the error as a string.
1174 */
1175 var getGLErrorAsString = function(gl, err) {
1176 if (err === gl.NO_ERROR) {
1177 return "NO_ERROR";
1178 }
1179 for (var name in gl) {
1180 if (gl[name] === err) {
1181 return name;
1182 }
1183 }
1184 return err.toString();
1185 };
1186
1187 /**
1188 * Wraps a WebGL function with a function that throws an exception if there is
1189 * an error.
1190 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
1191 * @param {string} fname Name of function to wrap.
1192 * @return {function} The wrapped function.
1193 */
1194 var createGLErrorWrapper = function(context, fname) {
1195 return function() {
1196 var rv = context[fname].apply(context, arguments);
1197 var err = context.getError();
1198 if (err != context.NO_ERROR)
1199 throw "GL error " + getGLErrorAsString(context, err) + " in " + fname;
1200 return rv;
1201 };
1202 };
1203
1204 /**
1205 * Creates a WebGL context where all functions are wrapped to throw an exception
1206 * if there is an error.
1207 * @param {!Canvas} canvas The HTML canvas to get a context from.
1208 * @return {!Object} The wrapped context.
1209 */
1210 function create3DContextWithWrapperThatThrowsOnGLError(canvas) {
1211 var context = create3DContext(canvas);
1212 var wrap = {};
1213 for (var i in context) {
1214 try {
1215 if (typeof context[i] == 'function') {
1216 wrap[i] = createGLErrorWrapper(context, i);
1217 } else {
1218 wrap[i] = context[i];
1219 }
1220 } catch (e) {
1221 error("createContextWrapperThatThrowsOnGLError: Error accessing " + i);
1222 }
1223 }
1224 wrap.getError = function() {
1225 return context.getError();
1226 };
1227 return wrap;
1228 };
1229
1230 /**
1231 * Tests that an evaluated expression generates a specific GL error.
1232 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
1233 * @param {number} glError The expected gl error.
1234 * @param {string} evalSTr The string to evaluate.
1235 */
1236 var shouldGenerateGLError = function(gl, glError, evalStr) {
1237 var exception;
1238 try {
1239 eval(evalStr);
1240 } catch (e) {
1241 exception = e;
1242 }
1243 if (exception) {
1244 testFailed(evalStr + " threw exception " + exception);
1245 } else {
1246 var err = gl.getError();
1247 if (err != glError) {
1248 testFailed(evalStr + " expected: " + getGLErrorAsString(gl, glError) + ". Was " + getGLErrorAsString(gl, err) + ".");
1249 } else {
1250 testPassed(evalStr + " was expected value: " + getGLErrorAsString(gl, glEr ror) + ".");
1251 }
1252 }
1253 };
1254
1255 /**
1256 * Tests that the first error GL returns is the specified error.
1257 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
1258 * @param {number} glError The expected gl error.
1259 * @param {string} opt_msg
1260 */
1261 var glErrorShouldBe = function(gl, glError, opt_msg) {
1262 opt_msg = opt_msg || "";
1263 var err = gl.getError();
1264 if (err != glError) {
1265 testFailed("getError expected: " + getGLErrorAsString(gl, glError) +
1266 ". Was " + getGLErrorAsString(gl, err) + " : " + opt_msg);
1267 } else {
1268 testPassed("getError was expected value: " +
1269 getGLErrorAsString(gl, glError) + " : " + opt_msg);
1270 }
1271 };
1272
1273 /**
1274 * Links a WebGL program, throws if there are errors.
1275 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
1276 * @param {!WebGLProgram} program The WebGLProgram to link.
1277 * @param {function(string): void) opt_errorCallback callback for errors.
1278 */
1279 var linkProgram = function(gl, program, opt_errorCallback) {
1280 var errFn = opt_errorCallback || testFailed;
1281 // Link the program
1282 gl.linkProgram(program);
1283
1284 // Check the link status
1285 var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
1286 if (!linked) {
1287 // something went wrong with the link
1288 var error = gl.getProgramInfoLog (program);
1289
1290 errFn("Error in program linking:" + error);
1291
1292 gl.deleteProgram(program);
1293 }
1294 };
1295
1296 /**
1297 * Loads text from an external file. This function is synchronous.
1298 * @param {string} url The url of the external file.
1299 * @param {!function(bool, string): void} callback that is sent a bool for
1300 * success and the string.
1301 */
1302 var loadTextFileAsync = function(url, callback) {
1303 log ("loading: " + url);
1304 var error = 'loadTextFileSynchronous failed to load url "' + url + '"';
1305 var request;
1306 if (window.XMLHttpRequest) {
1307 request = new XMLHttpRequest();
1308 if (request.overrideMimeType) {
1309 request.overrideMimeType('text/plain');
1310 }
1311 } else {
1312 throw 'XMLHttpRequest is disabled';
1313 }
1314 try {
1315 request.open('GET', url, true);
1316 request.onreadystatechange = function() {
1317 if (request.readyState == 4) {
1318 var text = '';
1319 // HTTP reports success with a 200 status. The file protocol reports
1320 // success with zero. HTTP does not use zero as a status code (they
1321 // start at 100).
1322 // https://developer.mozilla.org/En/Using_XMLHttpRequest
1323 var success = request.status == 200 || request.status == 0;
1324 if (success) {
1325 text = request.responseText;
1326 }
1327 log("loaded: " + url);
1328 callback(success, text);
1329 }
1330 };
1331 request.send(null);
1332 } catch (e) {
1333 log("failed to load: " + url);
1334 callback(false, '');
1335 }
1336 };
1337
1338 /**
1339 * Recursively loads a file as a list. Each line is parsed for a relative
1340 * path. If the file ends in .txt the contents of that file is inserted in
1341 * the list.
1342 *
1343 * @param {string} url The url of the external file.
1344 * @param {!function(bool, Array<string>): void} callback that is sent a bool
1345 * for success and the array of strings.
1346 */
1347 var getFileListAsync = function(url, callback) {
1348 var files = [];
1349
1350 var getFileListImpl = function(url, callback) {
1351 var files = [];
1352 if (url.substr(url.length - 4) == '.txt') {
1353 loadTextFileAsync(url, function() {
1354 return function(success, text) {
1355 if (!success) {
1356 callback(false, '');
1357 return;
1358 }
1359 var lines = text.split('\n');
1360 var prefix = '';
1361 var lastSlash = url.lastIndexOf('/');
1362 if (lastSlash >= 0) {
1363 prefix = url.substr(0, lastSlash + 1);
1364 }
1365 var fail = false;
1366 var count = 1;
1367 var index = 0;
1368 for (var ii = 0; ii < lines.length; ++ii) {
1369 var str = lines[ii].replace(/^\s\s*/, '').replace(/\s\s*$/, '');
1370 if (str.length > 4 &&
1371 str[0] != '#' &&
1372 str[0] != ";" &&
1373 str.substr(0, 2) != "//") {
1374 var names = str.split(/ +/);
1375 new_url = prefix + str;
1376 if (names.length == 1) {
1377 new_url = prefix + str;
1378 ++count;
1379 getFileListImpl(new_url, function(index) {
1380 return function(success, new_files) {
1381 log("got files: " + new_files.length);
1382 if (success) {
1383 files[index] = new_files;
1384 }
1385 finish(success);
1386 };
1387 }(index++));
1388 } else {
1389 var s = "";
1390 var p = "";
1391 for (var jj = 0; jj < names.length; ++jj) {
1392 s += p + prefix + names[jj];
1393 p = " ";
1394 }
1395 files[index++] = s;
1396 }
1397 }
1398 }
1399 finish(true);
1400
1401 function finish(success) {
1402 if (!success) {
1403 fail = true;
1404 }
1405 --count;
1406 log("count: " + count);
1407 if (!count) {
1408 callback(!fail, files);
1409 }
1410 }
1411 }
1412 }());
1413
1414 } else {
1415 files.push(url);
1416 callback(true, files);
1417 }
1418 };
1419
1420 getFileListImpl(url, function(success, files) {
1421 // flatten
1422 var flat = [];
1423 flatten(files);
1424 function flatten(files) {
1425 for (var ii = 0; ii < files.length; ++ii) {
1426 var value = files[ii];
1427 if (typeof(value) == "string") {
1428 flat.push(value);
1429 } else {
1430 flatten(value);
1431 }
1432 }
1433 }
1434 callback(success, flat);
1435 });
1436 };
1437
1438 /**
1439 * Gets a file from a file/URL.
1440 * @param {string} file the URL of the file to get.
1441 * @return {string} The contents of the file.
1442 */
1443 var readFile = function(file) {
1444 var xhr = new XMLHttpRequest();
1445 xhr.open("GET", file, false);
1446 xhr.send();
1447 return xhr.responseText.replace(/\r/g, "");
1448 };
1449
1450 var readFileList = function(url) {
1451 var files = [];
1452 if (url.substr(url.length - 4) == '.txt') {
1453 var lines = readFile(url).split('\n');
1454 var prefix = '';
1455 var lastSlash = url.lastIndexOf('/');
1456 if (lastSlash >= 0) {
1457 prefix = url.substr(0, lastSlash + 1);
1458 }
1459 for (var ii = 0; ii < lines.length; ++ii) {
1460 var str = lines[ii].replace(/^\s\s*/, '').replace(/\s\s*$/, '');
1461 if (str.length > 4 &&
1462 str[0] != '#' &&
1463 str[0] != ";" &&
1464 str.substr(0, 2) != "//") {
1465 var names = str.split(/ +/);
1466 if (names.length == 1) {
1467 new_url = prefix + str;
1468 files = files.concat(readFileList(new_url));
1469 } else {
1470 var s = "";
1471 var p = "";
1472 for (var jj = 0; jj < names.length; ++jj) {
1473 s += p + prefix + names[jj];
1474 p = " ";
1475 }
1476 files.push(s);
1477 }
1478 }
1479 }
1480 } else {
1481 files.push(url);
1482 }
1483 return files;
1484 };
1485
1486 /**
1487 * Loads a shader.
1488 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
1489 * @param {string} shaderSource The shader source.
1490 * @param {number} shaderType The type of shader.
1491 * @param {function(string): void) opt_errorCallback callback for errors.
1492 * @return {!WebGLShader} The created shader.
1493 */
1494 var loadShader = function(gl, shaderSource, shaderType, opt_errorCallback) {
1495 var errFn = opt_errorCallback || error;
1496 // Create the shader object
1497 var shader = gl.createShader(shaderType);
1498 if (shader == null) {
1499 errFn("*** Error: unable to create shader '"+shaderSource+"'");
1500 return null;
1501 }
1502
1503 // Load the shader source
1504 gl.shaderSource(shader, shaderSource);
1505 var err = gl.getError();
1506 if (err != gl.NO_ERROR) {
1507 errFn("*** Error loading shader '" + shader + "':" + glEnumToString(gl, err) );
1508 return null;
1509 }
1510
1511 // Compile the shader
1512 gl.compileShader(shader);
1513
1514 // Check the compile status
1515 var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
1516 if (!compiled) {
1517 // Something went wrong during compilation; get the error
1518 lastError = gl.getShaderInfoLog(shader);
1519 errFn("*** Error compiling " + glEnumToString(gl, shaderType) + " '" + shade r + "':" + lastError);
1520 gl.deleteShader(shader);
1521 return null;
1522 }
1523
1524 return shader;
1525 }
1526
1527 /**
1528 * Loads a shader from a URL.
1529 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
1530 * @param {file} file The URL of the shader source.
1531 * @param {number} type The type of shader.
1532 * @param {function(string): void) opt_errorCallback callback for errors.
1533 * @return {!WebGLShader} The created shader.
1534 */
1535 var loadShaderFromFile = function(gl, file, type, opt_errorCallback) {
1536 var shaderSource = readFile(file);
1537 return loadShader(gl, shaderSource, type, opt_errorCallback);
1538 };
1539
1540 /**
1541 * Gets the content of script.
1542 * @param {string} scriptId The id of the script tag.
1543 * @return {string} The content of the script.
1544 */
1545 var getScript = function(scriptId) {
1546 var shaderScript = document.getElementById(scriptId);
1547 if (!shaderScript) {
1548 throw("*** Error: unknown script element" + scriptId);
1549 }
1550 return shaderScript.text;
1551 };
1552
1553 /**
1554 * Loads a shader from a script tag.
1555 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
1556 * @param {string} scriptId The id of the script tag.
1557 * @param {number} opt_shaderType The type of shader. If not passed in it will
1558 * be derived from the type of the script tag.
1559 * @param {function(string): void) opt_errorCallback callback for errors.
1560 * @return {!WebGLShader} The created shader.
1561 */
1562 var loadShaderFromScript = function(
1563 gl, scriptId, opt_shaderType, opt_errorCallback) {
1564 var shaderSource = "";
1565 var shaderScript = document.getElementById(scriptId);
1566 if (!shaderScript) {
1567 throw("*** Error: unknown script element " + scriptId);
1568 }
1569 shaderSource = shaderScript.text;
1570
1571 if (!opt_shaderType) {
1572 if (shaderScript.type == "x-shader/x-vertex") {
1573 opt_shaderType = gl.VERTEX_SHADER;
1574 } else if (shaderScript.type == "x-shader/x-fragment") {
1575 opt_shaderType = gl.FRAGMENT_SHADER;
1576 } else {
1577 throw("*** Error: unknown shader type");
1578 return null;
1579 }
1580 }
1581
1582 return loadShader(
1583 gl, shaderSource, opt_shaderType, opt_errorCallback);
1584 };
1585
1586 var loadStandardProgram = function(gl) {
1587 var program = gl.createProgram();
1588 gl.attachShader(program, loadStandardVertexShader(gl));
1589 gl.attachShader(program, loadStandardFragmentShader(gl));
1590 gl.bindAttribLocation(program, 0, "a_vertex");
1591 gl.bindAttribLocation(program, 1, "a_normal");
1592 linkProgram(gl, program);
1593 return program;
1594 };
1595
1596 /**
1597 * Loads shaders from files, creates a program, attaches the shaders and links.
1598 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
1599 * @param {string} vertexShaderPath The URL of the vertex shader.
1600 * @param {string} fragmentShaderPath The URL of the fragment shader.
1601 * @param {function(string): void) opt_errorCallback callback for errors.
1602 * @return {!WebGLProgram} The created program.
1603 */
1604 var loadProgramFromFile = function(
1605 gl, vertexShaderPath, fragmentShaderPath, opt_errorCallback) {
1606 var program = gl.createProgram();
1607 var vs = loadShaderFromFile(
1608 gl, vertexShaderPath, gl.VERTEX_SHADER, opt_errorCallback);
1609 var fs = loadShaderFromFile(
1610 gl, fragmentShaderPath, gl.FRAGMENT_SHADER, opt_errorCallback);
1611 if (vs && fs) {
1612 gl.attachShader(program, vs);
1613 gl.attachShader(program, fs);
1614 linkProgram(gl, program, opt_errorCallback);
1615 }
1616 if (vs) {
1617 gl.deleteShader(vs);
1618 }
1619 if (fs) {
1620 gl.deleteShader(fs);
1621 }
1622 return program;
1623 };
1624
1625 /**
1626 * Loads shaders from script tags, creates a program, attaches the shaders and
1627 * links.
1628 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
1629 * @param {string} vertexScriptId The id of the script tag that contains the
1630 * vertex shader.
1631 * @param {string} fragmentScriptId The id of the script tag that contains the
1632 * fragment shader.
1633 * @param {function(string): void) opt_errorCallback callback for errors.
1634 * @return {!WebGLProgram} The created program.
1635 */
1636 var loadProgramFromScript = function loadProgramFromScript(
1637 gl, vertexScriptId, fragmentScriptId, opt_errorCallback) {
1638 var program = gl.createProgram();
1639 gl.attachShader(
1640 program,
1641 loadShaderFromScript(
1642 gl, vertexScriptId, gl.VERTEX_SHADER, opt_errorCallback));
1643 gl.attachShader(
1644 program,
1645 loadShaderFromScript(
1646 gl, fragmentScriptId, gl.FRAGMENT_SHADER, opt_errorCallback));
1647 linkProgram(gl, program, opt_errorCallback);
1648 return program;
1649 };
1650
1651 /**
1652 * Loads shaders from source, creates a program, attaches the shaders and
1653 * links.
1654 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
1655 * @param {!WebGLShader} vertexShader The vertex shader.
1656 * @param {!WebGLShader} fragmentShader The fragment shader.
1657 * @param {function(string): void) opt_errorCallback callback for errors.
1658 * @return {!WebGLProgram} The created program.
1659 */
1660 var createProgram = function(gl, vertexShader, fragmentShader, opt_errorCallback ) {
1661 var program = gl.createProgram();
1662 gl.attachShader(program, vertexShader);
1663 gl.attachShader(program, fragmentShader);
1664 linkProgram(gl, program, opt_errorCallback);
1665 return program;
1666 };
1667
1668 /**
1669 * Loads shaders from source, creates a program, attaches the shaders and
1670 * links.
1671 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
1672 * @param {string} vertexShader The vertex shader source.
1673 * @param {string} fragmentShader The fragment shader source.
1674 * @param {function(string): void) opt_errorCallback callback for errors.
1675 * @return {!WebGLProgram} The created program.
1676 */
1677 var loadProgram = function(
1678 gl, vertexShader, fragmentShader, opt_errorCallback) {
1679 var program;
1680 var vs = loadShader(
1681 gl, vertexShader, gl.VERTEX_SHADER, opt_errorCallback);
1682 var fs = loadShader(
1683 gl, fragmentShader, gl.FRAGMENT_SHADER, opt_errorCallback);
1684 if (vs && fs) {
1685 program = createProgram(gl, vs, fs, opt_errorCallback)
1686 }
1687 if (vs) {
1688 gl.deleteShader(vs);
1689 }
1690 if (fs) {
1691 gl.deleteShader(fs);
1692 }
1693 return program;
1694 };
1695
1696 /**
1697 * Loads shaders from source, creates a program, attaches the shaders and
1698 * links but expects error.
1699 *
1700 * GLSL 1.0.17 10.27 effectively says that compileShader can
1701 * always succeed as long as linkProgram fails so we can't
1702 * rely on compileShader failing. This function expects
1703 * one of the shader to fail OR linking to fail.
1704 *
1705 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
1706 * @param {string} vertexShaderScriptId The vertex shader.
1707 * @param {string} fragmentShaderScriptId The fragment shader.
1708 * @return {WebGLProgram} The created program.
1709 */
1710 var loadProgramFromScriptExpectError = function(
1711 gl, vertexShaderScriptId, fragmentShaderScriptId) {
1712 var vertexShader = loadShaderFromScript(gl, vertexShaderScriptId);
1713 if (!vertexShader) {
1714 return null;
1715 }
1716 var fragmentShader = loadShaderFromScript(gl, fragmentShaderScriptId);
1717 if (!fragmentShader) {
1718 return null;
1719 }
1720 var linkSuccess = true;
1721 var program = gl.createProgram();
1722 gl.attachShader(program, vertexShader);
1723 gl.attachShader(program, fragmentShader);
1724 linkSuccess = true;
1725 linkProgram(gl, program, function() {
1726 linkSuccess = false;
1727 });
1728 return linkSuccess ? program : null;
1729 };
1730
1731
1732 var getActiveMap = function(gl, program, typeInfo) {
1733 var numVariables = gl.getProgramParameter(program, gl[typeInfo.param]);
1734 var variables = {};
1735 for (var ii = 0; ii < numVariables; ++ii) {
1736 var info = gl[typeInfo.activeFn](program, ii);
1737 variables[info.name] = {
1738 name: info.name,
1739 size: info.size,
1740 type: info.type,
1741 location: gl[typeInfo.locFn](program, info.name)
1742 };
1743 }
1744 return variables;
1745 };
1746
1747 /**
1748 * Returns a map of attrib names to info about those
1749 * attribs.
1750 *
1751 * eg:
1752 * { "attrib1Name":
1753 * {
1754 * name: "attrib1Name",
1755 * size: 1,
1756 * type: gl.FLOAT_MAT2,
1757 * location: 0
1758 * },
1759 * "attrib2Name[0]":
1760 * {
1761 * name: "attrib2Name[0]",
1762 * size: 4,
1763 * type: gl.FLOAT,
1764 * location: 1
1765 * },
1766 * }
1767 *
1768 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
1769 * @param {WebGLProgram} The program to query for attribs.
1770 * @return the map.
1771 */
1772 var getAttribMap = function(gl, program) {
1773 return getActiveMap(gl, program, {
1774 param: "ACTIVE_ATTRIBUTES",
1775 activeFn: "getActiveAttrib",
1776 locFn: "getAttribLocation"
1777 });
1778 };
1779
1780 /**
1781 * Returns a map of uniform names to info about those uniforms.
1782 *
1783 * eg:
1784 * { "uniform1Name":
1785 * {
1786 * name: "uniform1Name",
1787 * size: 1,
1788 * type: gl.FLOAT_MAT2,
1789 * location: WebGLUniformLocation
1790 * },
1791 * "uniform2Name[0]":
1792 * {
1793 * name: "uniform2Name[0]",
1794 * size: 4,
1795 * type: gl.FLOAT,
1796 * location: WebGLUniformLocation
1797 * },
1798 * }
1799 *
1800 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use.
1801 * @param {WebGLProgram} The program to query for uniforms.
1802 * @return the map.
1803 */
1804 var getUniformMap = function(gl, program) {
1805 return getActiveMap(gl, program, {
1806 param: "ACTIVE_UNIFORMS",
1807 activeFn: "getActiveUniform",
1808 locFn: "getUniformLocation"
1809 });
1810 };
1811
1812 var basePath;
1813 var getBasePath = function() {
1814 if (!basePath) {
1815 var expectedBase = "webgl-test-utils.js";
1816 var scripts = document.getElementsByTagName('script');
1817 for (var script, i = 0; script = scripts[i]; i++) {
1818 var src = script.src;
1819 var l = src.length;
1820 if (src.substr(l - expectedBase.length) == expectedBase) {
1821 basePath = src.substr(0, l - expectedBase.length);
1822 }
1823 }
1824 }
1825 return basePath;
1826 };
1827
1828 var loadStandardVertexShader = function(gl) {
1829 return loadShaderFromFile(
1830 gl, getBasePath() + "vertexShader.vert", gl.VERTEX_SHADER);
1831 };
1832
1833 var loadStandardFragmentShader = function(gl) {
1834 return loadShaderFromFile(
1835 gl, getBasePath() + "fragmentShader.frag", gl.FRAGMENT_SHADER);
1836 };
1837
1838 /**
1839 * Loads an image asynchronously.
1840 * @param {string} url URL of image to load.
1841 * @param {!function(!Element): void} callback Function to call
1842 * with loaded image.
1843 */
1844 var loadImageAsync = function(url, callback) {
1845 var img = document.createElement('img');
1846 img.onload = function() {
1847 callback(img);
1848 };
1849 img.src = url;
1850 };
1851
1852 /**
1853 * Loads an array of images.
1854 * @param {!Array.<string>} urls URLs of images to load.
1855 * @param {!function(!{string, img}): void} callback. Callback
1856 * that gets passed map of urls to img tags.
1857 */
1858 var loadImagesAsync = function(urls, callback) {
1859 var count = 1;
1860 var images = { };
1861 function countDown() {
1862 --count;
1863 if (count == 0) {
1864 callback(images);
1865 }
1866 }
1867 function imageLoaded(url) {
1868 return function(img) {
1869 images[url] = img;
1870 countDown();
1871 }
1872 }
1873 for (var ii = 0; ii < urls.length; ++ii) {
1874 ++count;
1875 loadImageAsync(urls[ii], imageLoaded(urls[ii]));
1876 }
1877 countDown();
1878 };
1879
1880 /**
1881 * Returns a map of key=value values from url.
1882 * @return {!Object.<string, number>} map of keys to values.
1883 */
1884 var getUrlArguments = function() {
1885 var args = {};
1886 try {
1887 var s = window.location.href;
1888 var q = s.indexOf("?");
1889 var e = s.indexOf("#");
1890 if (e < 0) {
1891 e = s.length;
1892 }
1893 var query = s.substring(q + 1, e);
1894 var pairs = query.split("&");
1895 for (var ii = 0; ii < pairs.length; ++ii) {
1896 var keyValue = pairs[ii].split("=");
1897 var key = keyValue[0];
1898 var value = decodeURIComponent(keyValue[1]);
1899 args[key] = value;
1900 }
1901 } catch (e) {
1902 throw "could not parse url";
1903 }
1904 return args;
1905 };
1906
1907 /**
1908 * Makes an image from a canvas.
1909 * @param {!HTMLCanvas} canvas Canvas to make image from.
1910 * @return {!Image} The created image.
1911 */
1912 var makeImage = function(canvas) {
1913 var img = document.createElement('img');
1914 img.src = canvas.toDataURL();
1915 return img;
1916 };
1917
1918 /**
1919 * Inserts an image with a caption into 'element'.
1920 * @param {!HTMLElement} element Element to append image to.
1921 * @param {string} caption caption to associate with image.
1922 * @param {!Image) img image to insert.
1923 */
1924 var insertImage = function(element, caption, img) {
1925 var div = document.createElement("div");
1926 div.appendChild(img);
1927 var label = document.createElement("div");
1928 label.appendChild(document.createTextNode(caption));
1929 div.appendChild(label);
1930 element.appendChild(div);
1931 };
1932
1933 /**
1934 * Inserts a 'label' that when clicked expands to the pre
1935 * formatted text supplied by 'source'.
1936 * @param {!HTMLElement} element element to append label to.
1937 * @param {string} label label for anchor.
1938 * @param {string} source preformatted text to expand to.
1939 * @param {string} opt_url url of source. If provided a 2nd link
1940 * will be added.
1941 */
1942 var addShaderSource = function(element, label, source, opt_url) {
1943 var div = document.createElement("div");
1944 var s = document.createElement("pre");
1945 s.className = "shader-source";
1946 s.style.display = "none";
1947 var ol = document.createElement("ol");
1948 //s.appendChild(document.createTextNode(source));
1949 var lines = source.split("\n");
1950 for (var ii = 0; ii < lines.length; ++ii) {
1951 var line = lines[ii];
1952 var li = document.createElement("li");
1953 li.appendChild(document.createTextNode(line));
1954 ol.appendChild(li);
1955 }
1956 s.appendChild(ol);
1957 var l = document.createElement("a");
1958 l.href = "show-shader-source";
1959 l.appendChild(document.createTextNode(label));
1960 l.addEventListener('click', function(event) {
1961 if (event.preventDefault) {
1962 event.preventDefault();
1963 }
1964 s.style.display = (s.style.display == 'none') ? 'block' : 'none';
1965 return false;
1966 }, false);
1967 div.appendChild(l);
1968 if (opt_url) {
1969 var u = document.createElement("a");
1970 u.href = opt_url;
1971 div.appendChild(document.createTextNode(" "));
1972 u.appendChild(document.createTextNode("(" + opt_url + ")"));
1973 div.appendChild(u);
1974 }
1975 div.appendChild(s);
1976 element.appendChild(div);
1977 };
1978
1979 // Add your prefix here.
1980 var browserPrefixes = [
1981 "",
1982 "MOZ_",
1983 "OP_",
1984 "WEBKIT_"
1985 ];
1986
1987 /**
1988 * Given an extension name like WEBGL_compressed_texture_s3tc
1989 * returns the name of the supported version extension, like
1990 * WEBKIT_WEBGL_compressed_teture_s3tc
1991 * @param {string} name Name of extension to look for.
1992 * @return {string} name of extension found or undefined if not
1993 * found.
1994 */
1995 var getSupportedExtensionWithKnownPrefixes = function(gl, name) {
1996 var supported = gl.getSupportedExtensions();
1997 for (var ii = 0; ii < browserPrefixes.length; ++ii) {
1998 var prefixedName = browserPrefixes[ii] + name;
1999 if (supported.indexOf(prefixedName) >= 0) {
2000 return prefixedName;
2001 }
2002 }
2003 };
2004
2005 /**
2006 * Given an extension name like WEBGL_compressed_texture_s3tc
2007 * returns the supported version extension, like
2008 * WEBKIT_WEBGL_compressed_teture_s3tc
2009 * @param {string} name Name of extension to look for.
2010 * @return {WebGLExtension} The extension or undefined if not
2011 * found.
2012 */
2013 var getExtensionWithKnownPrefixes = function(gl, name) {
2014 for (var ii = 0; ii < browserPrefixes.length; ++ii) {
2015 var prefixedName = browserPrefixes[ii] + name;
2016 var ext = gl.getExtension(prefixedName);
2017 if (ext) {
2018 return ext;
2019 }
2020 }
2021 };
2022
2023
2024 var replaceRE = /\$\((\w+)\)/g;
2025
2026 /**
2027 * Replaces strings with property values.
2028 * Given a string like "hello $(first) $(last)" and an object
2029 * like {first:"John", last:"Smith"} will return
2030 * "hello John Smith".
2031 * @param {string} str String to do replacements in.
2032 * @param {...} 1 or more objects containing properties.
2033 */
2034 var replaceParams = function(str) {
2035 var args = arguments;
2036 return str.replace(replaceRE, function(str, p1, offset, s) {
2037 for (var ii = 1; ii < args.length; ++ii) {
2038 if (args[ii][p1] !== undefined) {
2039 return args[ii][p1];
2040 }
2041 }
2042 throw "unknown string param '" + p1 + "'";
2043 });
2044 };
2045
2046 var upperCaseFirstLetter = function(str) {
2047 return str.substring(0, 1).toUpperCase() + str.substring(1);
2048 };
2049
2050 /**
2051 * Gets a prefixed property. For example,
2052 *
2053 * var fn = getPrefixedProperty(
2054 * window,
2055 * "requestAnimationFrame");
2056 *
2057 * Will return either:
2058 * "window.requestAnimationFrame",
2059 * "window.oRequestAnimationFrame",
2060 * "window.msRequestAnimationFrame",
2061 * "window.mozRequestAnimationFrame",
2062 * "window.webKitRequestAnimationFrame",
2063 * undefined
2064 *
2065 * the non-prefixed function is tried first.
2066 */
2067 var propertyPrefixes = ["", "moz", "ms", "o", "webkit"];
2068 var getPrefixedProperty = function(obj, propertyName) {
2069 for (var ii = 0; ii < propertyPrefixes.length; ++ii) {
2070 var prefix = propertyPrefixes[ii];
2071 var name = prefix + propertyName;
2072 console.log(name);
2073 var property = obj[name];
2074 if (property) {
2075 return property;
2076 }
2077 if (ii == 0) {
2078 propertyName = upperCaseFirstLetter(propertyName);
2079 }
2080 }
2081 return undefined;
2082 };
2083
2084 /**
2085 * Provides requestAnimationFrame in a cross browser way.
2086 */
2087 var requestAnimFrame = getPrefixedProperty(window, "requestAnimationFrame") ||
2088 function(callback, element) {
2089 return window.setTimeout(callback, 1000 / 70);
2090 };
2091
2092 /**
2093 * Provides cancelAnimationFrame in a cross browser way.
2094 */
2095 var cancelAnimFrame = getPrefixedProperty(window, "cancelAnimationFrame") ||
2096 window.clearTimeout;
2097
2098 /**
2099 * Provides requestFullScreen in a cross browser way.
2100 */
2101 var requestFullScreen = function(element) {
2102 var fn = getPrefixedProperty(element, "requestFullScreen");
2103 if (fn) {
2104 fn.call(element);
2105 }
2106 };
2107
2108 /**
2109 * Provides cancelFullScreen in a cross browser way.
2110 */
2111 var cancelFullScreen = function() {
2112 var fn = getPrefixedProperty(document, "cancelFullScreen");
2113 if (fn) {
2114 fn.call(document);
2115 }
2116 };
2117
2118 var fullScreenStateName;
2119 (function() {
2120 var fullScreenStateNames = [
2121 "isFullScreen",
2122 "fullScreen",
2123 ];
2124 for (var ii = 0; ii < fullScreenStateNames.length; ++ii) {
2125 var propertyName = fullScreenStateNames[ii];
2126 for (var jj = 0; jj < propertyPrefixes.length; ++jj) {
2127 var prefix = propertyPrefixes[jj];
2128 if (prefix.length) {
2129 propertyName = upperCaseFirstLetter(propertyName);
2130 fullScreenStateName = prefix + propertyName;
2131 if (document[fullScreenStateName] !== undefined) {
2132 return;
2133 }
2134 }
2135 }
2136 fullScreenStateName = undefined;
2137 }
2138 }());
2139
2140 /**
2141 * @return {boolean} True if fullscreen mode is active.
2142 */
2143 var getFullScreenState = function() {
2144 console.log("fullscreenstatename:" + fullScreenStateName);
2145 console.log(document[fullScreenStateName]);
2146 return document[fullScreenStateName];
2147 };
2148
2149 /**
2150 * @param {!HTMLElement} element The element to go fullscreen.
2151 * @param {!function(boolean)} callback A function that will be called
2152 * when entering/exiting fullscreen. It is passed true if
2153 * entering fullscreen, false if exiting.
2154 */
2155 var onFullScreenChange = function(element, callback) {
2156 propertyPrefixes.forEach(function(prefix) {
2157 var eventName = prefix + "fullscreenchange";
2158 console.log("addevent: " + eventName);
2159 document.addEventListener(eventName, function(event) {
2160 console.log("event: " + eventName);
2161 callback(getFullScreenState());
2162 });
2163 });
2164 };
2165
2166 /**
2167 * @param {!string} buttonId The id of the button that will toggle fullscreen
2168 * mode.
2169 * @param {!string} fullscreenId The id of the element to go fullscreen.
2170 * @param {!function(boolean)} callback A function that will be called
2171 * when entering/exiting fullscreen. It is passed true if
2172 * entering fullscreen, false if exiting.
2173 * @return {boolean} True if fullscreen mode is supported.
2174 */
2175 var setupFullscreen = function(buttonId, fullscreenId, callback) {
2176 if (!fullScreenStateName) {
2177 return false;
2178 }
2179
2180 var fullscreenElement = document.getElementById(fullscreenId);
2181 onFullScreenChange(fullscreenElement, callback);
2182
2183 var toggleFullScreen = function(event) {
2184 if (getFullScreenState()) {
2185 cancelFullScreen(fullscreenElement);
2186 } else {
2187 requestFullScreen(fullscreenElement);
2188 }
2189 event.preventDefault();
2190 return false;
2191 };
2192
2193 var buttonElement = document.getElementById(buttonId);
2194 buttonElement.addEventListener('click', toggleFullScreen);
2195
2196 return true;
2197 };
2198
2199 /**
2200 * Waits for the browser to composite the canvas associated with
2201 * the WebGL context passed in.
2202 * @param {WebGLRenderingContext} gl The WebGLRenderingContext to use.
2203 * @param {function()} callback A function to call after compositing has taken
2204 * place.
2205 */
2206 var waitForComposite = function(gl, callback) {
2207 var frames = 5;
2208 var countDown = function() {
2209 if (frames == 0) {
2210 callback();
2211 } else {
2212 --frames;
2213 requestAnimFrame(countDown);
2214 }
2215 };
2216 countDown();
2217 };
2218
2219 /**
2220 * Runs an array of functions, yielding to the browser between each step.
2221 * If you want to know when all the steps are finished add a last step.
2222 * @param {!Array.<function(): void>} steps. Array of functions.
2223 */
2224 var runSteps = function(steps) {
2225 if (!steps.length) {
2226 return;
2227 }
2228
2229 // copy steps so they can't be modifed.
2230 var stepsToRun = steps.slice();
2231 var currentStep = 0;
2232 var runNextStep = function() {
2233 stepsToRun[currentStep++]();
2234 if (currentStep < stepsToRun.length) {
2235 setTimeout(runNextStep, 1);
2236 }
2237 };
2238 runNextStep();
2239 };
2240
2241 /**
2242 * Starts playing a video and waits for it to be consumable.
2243 * @param {!HTMLVideoElement} video An HTML5 Video element.
2244 * @param {!function(!HTMLVideoElement): void>} callback Function to call when
2245 * video is ready.
2246 */
2247 var startPlayingAndWaitForVideo = function(video, callback) {
2248 var gotPlaying = false;
2249 var gotTimeUpdate = false;
2250
2251 var maybeCallCallback = function() {
2252 if (gotPlaying && gotTimeUpdate && callback) {
2253 callback(video);
2254 callback = undefined;
2255 video.removeEventListener('playing', playingListener, true);
2256 video.removeEventListener('timeupdate', timeupdateListener, true);
2257 }
2258 };
2259
2260 var playingListener = function() {
2261 gotPlaying = true;
2262 maybeCallCallback();
2263 };
2264
2265 var timeupdateListener = function() {
2266 // Checking to make sure the current time has advanced beyond
2267 // the start time seems to be a reliable heuristic that the
2268 // video element has data that can be consumed.
2269 if (video.currentTime > 0.0) {
2270 gotTimeUpdate = true;
2271 maybeCallCallback();
2272 }
2273 };
2274
2275 video.addEventListener('playing', playingListener, true);
2276 video.addEventListener('timeupdate', timeupdateListener, true);
2277 video.loop = true;
2278 video.play();
2279 };
2280
2281 return {
2282 addShaderSource: addShaderSource,
2283 cancelAnimFrame: cancelAnimFrame,
2284 create3DContext: create3DContext,
2285 create3DContextWithWrapperThatThrowsOnGLError:
2286 create3DContextWithWrapperThatThrowsOnGLError,
2287 checkAreaInAndOut: checkAreaInAndOut,
2288 checkCanvas: checkCanvas,
2289 checkCanvasRect: checkCanvasRect,
2290 checkCanvasRectColor: checkCanvasRectColor,
2291 clipToRange: clipToRange,
2292 createColoredTexture: createColoredTexture,
2293 createProgram: createProgram,
2294 clearAndDrawUnitQuad: clearAndDrawUnitQuad,
2295 clearAndDrawIndexedQuad: clearAndDrawIndexedQuad,
2296 drawUnitQuad: drawUnitQuad,
2297 drawIndexedQuad: drawIndexedQuad,
2298 drawUByteColorQuad: drawUByteColorQuad,
2299 drawFloatColorQuad: drawFloatColorQuad,
2300 endsWith: endsWith,
2301 fillTexture: fillTexture,
2302 getBytesPerComponent: getBytesPerComponent,
2303 getExtensionWithKnownPrefixes: getExtensionWithKnownPrefixes,
2304 getFileListAsync: getFileListAsync,
2305 getLastError: getLastError,
2306 getPrefixedProperty: getPrefixedProperty,
2307 getScript: getScript,
2308 getSupportedExtensionWithKnownPrefixes: getSupportedExtensionWithKnownPrefixes ,
2309 getUrlArguments: getUrlArguments,
2310 getAttribMap: getAttribMap,
2311 getUniformMap: getUniformMap,
2312 glEnumToString: glEnumToString,
2313 glErrorShouldBe: glErrorShouldBe,
2314 glTypeToArrayBufferType: glTypeToArrayBufferType,
2315 hasAttributeCaseInsensitive: hasAttributeCaseInsensitive,
2316 insertImage: insertImage,
2317 loadImageAsync: loadImageAsync,
2318 loadImagesAsync: loadImagesAsync,
2319 loadProgram: loadProgram,
2320 loadProgramFromFile: loadProgramFromFile,
2321 loadProgramFromScript: loadProgramFromScript,
2322 loadProgramFromScriptExpectError: loadProgramFromScriptExpectError,
2323 loadShader: loadShader,
2324 loadShaderFromFile: loadShaderFromFile,
2325 loadShaderFromScript: loadShaderFromScript,
2326 loadStandardProgram: loadStandardProgram,
2327 loadStandardVertexShader: loadStandardVertexShader,
2328 loadStandardFragmentShader: loadStandardFragmentShader,
2329 loadTextFileAsync: loadTextFileAsync,
2330 loadTexture: loadTexture,
2331 log: log,
2332 loggingOff: loggingOff,
2333 makeImage: makeImage,
2334 error: error,
2335 shallowCopyObject: shallowCopyObject,
2336 setupColorQuad: setupColorQuad,
2337 setupProgram: setupProgram,
2338 setupQuad: setupQuad,
2339 setupIndexedQuad: setupIndexedQuad,
2340 setupIndexedQuadWithOptions: setupIndexedQuadWithOptions,
2341 setupSimpleColorFragmentShader: setupSimpleColorFragmentShader,
2342 setupSimpleColorVertexShader: setupSimpleColorVertexShader,
2343 setupSimpleColorProgram: setupSimpleColorProgram,
2344 setupSimpleTextureFragmentShader: setupSimpleTextureFragmentShader,
2345 setupSimpleTextureProgram: setupSimpleTextureProgram,
2346 setupSimpleTextureVertexShader: setupSimpleTextureVertexShader,
2347 setupSimpleVertexColorFragmentShader: setupSimpleVertexColorFragmentShader,
2348 setupSimpleVertexColorProgram: setupSimpleVertexColorProgram,
2349 setupSimpleVertexColorVertexShader: setupSimpleVertexColorVertexShader,
2350 setupNoTexCoordTextureProgram: setupNoTexCoordTextureProgram,
2351 setupNoTexCoordTextureVertexShader: setupNoTexCoordTextureVertexShader,
2352 setupTexturedQuad: setupTexturedQuad,
2353 setupTexturedQuadWithTexCoords: setupTexturedQuadWithTexCoords,
2354 setupUnitQuad: setupUnitQuad,
2355 setupUnitQuadWithTexCoords: setupUnitQuadWithTexCoords,
2356 setFloatDrawColor: setFloatDrawColor,
2357 setUByteDrawColor: setUByteDrawColor,
2358 startPlayingAndWaitForVideo: startPlayingAndWaitForVideo,
2359 startsWith: startsWith,
2360 shouldGenerateGLError: shouldGenerateGLError,
2361 readFile: readFile,
2362 readFileList: readFileList,
2363 replaceParams: replaceParams,
2364 requestAnimFrame: requestAnimFrame,
2365 runSteps: runSteps,
2366 waitForComposite: waitForComposite,
2367
2368 // fullscreen api
2369 setupFullscreen: setupFullscreen,
2370
2371 none: false
2372 };
2373
2374 }());
OLDNEW
« no previous file with comments | « conformance/resources/webgl-test.js ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698