1 //----------------------------------------------------------------------
2 // Differences with respect to Khronos version of webgl-test.js
4 testRunner
.overridePreference("WebKitWebGLEnabled", "1");
6 function assertMsg(assertion
, msg
) {
14 function initNonKhronosFramework(waitUntilDone
) {
15 if (window
.testRunner
) {
16 testRunner
.overridePreference("WebKitWebGLEnabled", "1");
17 testRunner
.dumpAsText();
19 window
.jsTestIsAsync
= true;
20 testRunner
.waitUntilDone();
25 function nonKhronosFrameworkNotifyDone() {
26 if (window
.testRunner
) {
27 testRunner
.notifyDone();
31 function finishTest() {
36 //----------------------------------------------------------------------
38 function webglTestLog(msg
) {
39 if (window
.console
&& window
.console
.log
) {
40 window
.console
.log(msg
);
42 if (document
.getElementById("console")) {
43 var log
= document
.getElementById("console");
44 log
.innerHTML
+= msg
+ "<br>";
51 // Returns the WebGLRenderingContext for any known implementation.
53 function create3DContext(canvas
, attributes
)
56 canvas
= document
.createElement("canvas");
59 context
= canvas
.getContext("webgl", attributes
);
63 context
= canvas
.getContext("webkit-3d", attributes
);
68 context
= canvas
.getContext("moz-webgl", attributes
);
72 throw "Unable to fetch WebGL rendering context for Canvas";
77 function createGLErrorWrapper(context
, fname
) {
79 var rv
= context
[fname
].apply(context
, arguments
);
80 var err
= context
.getError();
82 throw "GL error " + err
+ " in " + fname
;
87 function create3DContextWithWrapperThatThrowsOnGLError(canvas
, attributes
) {
88 var context
= create3DContext(canvas
, attributes
);
89 // Thanks to Ilmari Heikkinen for the idea on how to implement this so elegantly.
91 for (var i
in context
) {
93 if (typeof context
[i
] == 'function') {
94 wrap
[i
] = createGLErrorWrapper(context
, i
);
99 webglTestLog("createContextWrapperThatThrowsOnGLError: Error accessing " + i
);
102 wrap
.getError = function() {
103 return context
.getError();
108 function getGLErrorAsString(ctx
, err
) {
109 if (err
=== ctx
.NO_ERROR
) {
112 for (var name
in ctx
) {
113 if (ctx
[name
] === err
) {
117 return "0x" + err
.toString(16);
120 // Pass undefined for glError to test that it at least throws some error
121 function shouldGenerateGLError(ctx
, glErrors
, evalStr
) {
122 if (!glErrors
.length
) {
123 glErrors
= [glErrors
];
132 testFailed(evalStr
+ " threw exception " + exception
);
134 var err
= ctx
.getError();
136 for (var ii
= 0; ii
< glErrors
.length
; ++ii
) {
137 errStrs
.push(getGLErrorAsString(ctx
, glErrors
[ii
]));
139 var expected
= errStrs
.join(" or ");
140 if (glErrors
.indexOf(err
) < 0) {
141 testFailed(evalStr
+ " expected: " + expected
+ ". Was " + getGLErrorAsString(ctx
, err
) + ".");
143 var msg
= (glErrors
.length
== 1) ? " generated expected GL error: " :
144 " generated one of expected GL errors: ";
145 testPassed(evalStr
+ msg
+ expected
+ ".");
151 * Tests that the first error GL returns is the specified error.
152 * @param {!WebGLContext} gl The WebGLContext to use.
153 * @param {number|!Array.<number>} glError The expected gl
154 * error. Multiple errors can be passed in using an
156 * @param {string} opt_msg Optional additional message.
158 function glErrorShouldBe(gl
, glErrors
, opt_msg
) {
159 if (!glErrors
.length
) {
160 glErrors
= [glErrors
];
162 opt_msg
= opt_msg
|| "";
163 var err
= gl
.getError();
164 var ndx
= glErrors
.indexOf(err
);
166 for (var ii
= 0; ii
< glErrors
.length
; ++ii
) {
167 errStrs
.push(getGLErrorAsString(gl
, glErrors
[ii
]));
169 var expected
= errStrs
.join(" or ");
171 var msg
= "getError expected" + ((glErrors
.length
> 1) ? " one of: " : ": ");
172 testFailed(msg
+ expected
+ ". Was " + getGLErrorAsString(gl
, err
) + " : " + opt_msg
);
174 var msg
= "getError was " + ((glErrors
.length
> 1) ? "one of: " : "expected value: ");
175 testPassed(msg
+ expected
+ " : " + opt_msg
);
182 // Create and return a program object, attaching each of the given shaders.
184 // If attribs are given, bind an attrib with that name at that index.
186 function createProgram(gl
, vshaders
, fshaders
, attribs
)
188 if (typeof(vshaders
) == "string")
189 vshaders
= [vshaders
];
190 if (typeof(fshaders
) == "string")
191 fshaders
= [fshaders
];
196 for (i
= 0; i
< vshaders
.length
; ++i
) {
197 var shader
= loadShader(gl
, vshaders
[i
], gl
.VERTEX_SHADER
);
200 shaders
.push(shader
);
203 for (i
= 0; i
< fshaders
.length
; ++i
) {
204 var shader
= loadShader(gl
, fshaders
[i
], gl
.FRAGMENT_SHADER
);
207 shaders
.push(shader
);
210 var prog
= gl
.createProgram();
211 for (i
= 0; i
< shaders
.length
; ++i
) {
212 gl
.attachShader(prog
, shaders
[i
]);
216 for (var i
in attribs
) {
217 gl
.bindAttribLocation (prog
, parseInt(i
), attribs
[i
]);
221 gl
.linkProgram(prog
);
223 // Check the link status
224 var linked
= gl
.getProgramParameter(prog
, gl
.LINK_STATUS
);
226 // something went wrong with the link
227 var error
= gl
.getProgramInfoLog(prog
);
228 webglTestLog("Error in program linking:" + error
);
230 gl
.deleteProgram(prog
);
231 for (i
= 0; i
< shaders
.length
; ++i
)
232 gl
.deleteShader(shaders
[i
]);
242 // Initialize the Canvas element with the passed name as a WebGL object and return the
243 // WebGLRenderingContext.
245 // Load shaders with the passed names and create a program with them. Return this program
246 // in the 'program' property of the returned context.
248 // For each string in the passed attribs array, bind an attrib with that name at that index.
249 // Once the attribs are bound, link the program and then use it.
251 // Set the clear color to the passed array (4 values) and set the clear depth to the passed value.
252 // Enable depth testing and blending with a blend func of (SRC_ALPHA, ONE_MINUS_SRC_ALPHA)
254 function initWebGL(canvasName
, vshader
, fshader
, attribs
, clearColor
, clearDepth
, contextAttribs
)
256 var canvas
= document
.getElementById(canvasName
);
257 var gl
= create3DContext(canvas
, contextAttribs
);
259 alert("No WebGL context found");
263 // Create the program object
264 gl
.program
= createProgram(gl
, vshader
, fshader
, attribs
);
268 gl
.useProgram(gl
.program
);
270 gl
.clearColor(clearColor
[0], clearColor
[1], clearColor
[2], clearColor
[3]);
271 gl
.clearDepth(clearDepth
);
273 gl
.enable(gl
.DEPTH_TEST
);
275 gl
.blendFunc(gl
.SRC_ALPHA
, gl
.ONE_MINUS_SRC_ALPHA
);
283 // Load the source from the passed shader file.
285 function getShaderSource(file
)
287 var xhr
= new XMLHttpRequest();
288 xhr
.open("GET", file
, false);
290 return xhr
.responseText
;
297 // 'shader' is either the id of a <script> element containing the shader source
298 // string, the shader string itself, or the URL of a file containing the shader
299 // source. Load this shader and return the WebGLShader object corresponding to
302 function loadShader(ctx
, shaderId
, shaderType
, isFile
)
304 var shaderSource
= "";
307 shaderSource
= getShaderSource(shaderId
);
309 var shaderScript
= document
.getElementById(shaderId
);
311 shaderSource
= shaderId
;
313 if (shaderScript
.type
== "x-shader/x-vertex") {
314 shaderType
= ctx
.VERTEX_SHADER
;
315 } else if (shaderScript
.type
== "x-shader/x-fragment") {
316 shaderType
= ctx
.FRAGMENT_SHADER
;
317 } else if (shaderType
!= ctx
.VERTEX_SHADER
&& shaderType
!= ctx
.FRAGMENT_SHADER
) {
318 webglTestLog("*** Error: unknown shader type");
322 shaderSource
= shaderScript
.text
;
326 // Create the shader object
327 var shader
= ctx
.createShader(shaderType
);
328 if (shader
== null) {
329 webglTestLog("*** Error: unable to create shader '"+shaderId
+"'");
333 // Load the shader source
334 ctx
.shaderSource(shader
, shaderSource
);
336 // Compile the shader
337 ctx
.compileShader(shader
);
339 // Check the compile status
340 var compiled
= ctx
.getShaderParameter(shader
, ctx
.COMPILE_STATUS
);
342 // Something went wrong during compilation; get the error
343 var error
= ctx
.getShaderInfoLog(shader
);
344 webglTestLog("*** Error compiling shader '"+shader
+"':"+error
);
345 ctx
.deleteShader(shader
);
352 function loadShaderFromFile(ctx
, file
, type
)
354 return loadShader(ctx
, file
, type
, true);
357 function loadShaderFromScript(ctx
, script
)
359 return loadShader(ctx
, script
, 0, false);
362 function loadStandardProgram(context
) {
363 var program
= context
.createProgram();
364 context
.attachShader(program
, loadStandardVertexShader(context
));
365 context
.attachShader(program
, loadStandardFragmentShader(context
));
366 context
.linkProgram(program
);
370 function loadProgram(context
, vertexShaderPath
, fragmentShaderPath
, isFile
) {
371 isFile
= (isFile
=== undefined) ? true : isFile
;
372 var program
= context
.createProgram();
373 context
.attachShader(program
, loadShader(context
, vertexShaderPath
, context
.VERTEX_SHADER
, isFile
));
374 context
.attachShader(program
, loadShader(context
, fragmentShaderPath
, context
.FRAGMENT_SHADER
, isFile
));
375 context
.linkProgram(program
);
379 function loadStandardVertexShader(context
) {
380 return loadShader(context
, "resources/vertexShader.vert", context
.VERTEX_SHADER
, true);
383 function loadStandardFragmentShader(context
) {
384 return loadShader(context
, "resources/fragmentShader.frag", context
.FRAGMENT_SHADER
, true);
390 // Create a box with vertices, normals and texCoords. Create VBOs for each as well as the index array.
391 // Return an object with the following properties:
393 // normalObject WebGLBuffer object for normals
394 // texCoordObject WebGLBuffer object for texCoords
395 // vertexObject WebGLBuffer object for vertices
396 // indexObject WebGLBuffer object for indices
397 // numIndices The number of indices in the indexObject
399 function makeBox(ctx
)
410 // vertex coords array
411 var vertices
= new Float32Array(
412 [ 1, 1, 1, -1, 1, 1, -1,-1, 1, 1,-1, 1, // v0-v1-v2-v3 front
413 1, 1, 1, 1,-1, 1, 1,-1,-1, 1, 1,-1, // v0-v3-v4-v5 right
414 1, 1, 1, 1, 1,-1, -1, 1,-1, -1, 1, 1, // v0-v5-v6-v1 top
415 -1, 1, 1, -1, 1,-1, -1,-1,-1, -1,-1, 1, // v1-v6-v7-v2 left
416 -1,-1,-1, 1,-1,-1, 1,-1, 1, -1,-1, 1, // v7-v4-v3-v2 bottom
417 1,-1,-1, -1,-1,-1, -1, 1,-1, 1, 1,-1 ] // v4-v7-v6-v5 back
421 var normals
= new Float32Array(
422 [ 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, // v0-v1-v2-v3 front
423 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v0-v3-v4-v5 right
424 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, // v0-v5-v6-v1 top
425 -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, // v1-v6-v7-v2 left
426 0,-1, 0, 0,-1, 0, 0,-1, 0, 0,-1, 0, // v7-v4-v3-v2 bottom
427 0, 0,-1, 0, 0,-1, 0, 0,-1, 0, 0,-1 ] // v4-v7-v6-v5 back
432 var texCoords
= new Float32Array(
433 [ 1, 1, 0, 1, 0, 0, 1, 0, // v0-v1-v2-v3 front
434 0, 1, 0, 0, 1, 0, 1, 1, // v0-v3-v4-v5 right
435 1, 0, 1, 1, 0, 1, 0, 0, // v0-v5-v6-v1 top
436 1, 1, 0, 1, 0, 0, 1, 0, // v1-v6-v7-v2 left
437 0, 0, 1, 0, 1, 1, 0, 1, // v7-v4-v3-v2 bottom
438 0, 0, 1, 0, 1, 1, 0, 1 ] // v4-v7-v6-v5 back
442 var indices
= new Uint8Array(
443 [ 0, 1, 2, 0, 2, 3, // front
444 4, 5, 6, 4, 6, 7, // right
445 8, 9,10, 8,10,11, // top
446 12,13,14, 12,14,15, // left
447 16,17,18, 16,18,19, // bottom
448 20,21,22, 20,22,23 ] // back
453 retval
.normalObject
= ctx
.createBuffer();
454 ctx
.bindBuffer(ctx
.ARRAY_BUFFER
, retval
.normalObject
);
455 ctx
.bufferData(ctx
.ARRAY_BUFFER
, normals
, ctx
.STATIC_DRAW
);
457 retval
.texCoordObject
= ctx
.createBuffer();
458 ctx
.bindBuffer(ctx
.ARRAY_BUFFER
, retval
.texCoordObject
);
459 ctx
.bufferData(ctx
.ARRAY_BUFFER
, texCoords
, ctx
.STATIC_DRAW
);
461 retval
.vertexObject
= ctx
.createBuffer();
462 ctx
.bindBuffer(ctx
.ARRAY_BUFFER
, retval
.vertexObject
);
463 ctx
.bufferData(ctx
.ARRAY_BUFFER
, vertices
, ctx
.STATIC_DRAW
);
465 ctx
.bindBuffer(ctx
.ARRAY_BUFFER
, 0);
467 retval
.indexObject
= ctx
.createBuffer();
468 ctx
.bindBuffer(ctx
.ELEMENT_ARRAY_BUFFER
, retval
.indexObject
);
469 ctx
.bufferData(ctx
.ELEMENT_ARRAY_BUFFER
, indices
, ctx
.STATIC_DRAW
);
470 ctx
.bindBuffer(ctx
.ELEMENT_ARRAY_BUFFER
, 0);
472 retval
.numIndices
= indices
.length
;
480 // Create a sphere with the passed number of latitude and longitude bands and the passed radius.
481 // Sphere has vertices, normals and texCoords. Create VBOs for each as well as the index array.
482 // Return an object with the following properties:
484 // normalObject WebGLBuffer object for normals
485 // texCoordObject WebGLBuffer object for texCoords
486 // vertexObject WebGLBuffer object for vertices
487 // indexObject WebGLBuffer object for indices
488 // numIndices The number of indices in the indexObject
490 function makeSphere(ctx
, radius
, lats
, longs
)
492 var geometryData
= [ ];
493 var normalData
= [ ];
494 var texCoordData
= [ ];
497 for (var latNumber
= 0; latNumber
<= lats
; ++latNumber
) {
498 for (var longNumber
= 0; longNumber
<= longs
; ++longNumber
) {
499 var theta
= latNumber
* Math
.PI
/ lats
;
500 var phi
= longNumber
* 2 * Math
.PI
/ longs
;
501 var sinTheta
= Math
.sin(theta
);
502 var sinPhi
= Math
.sin(phi
);
503 var cosTheta
= Math
.cos(theta
);
504 var cosPhi
= Math
.cos(phi
);
506 var x
= cosPhi
* sinTheta
;
508 var z
= sinPhi
* sinTheta
;
509 var u
= 1-(longNumber
/longs
);
510 var v
= latNumber
/lats
;
515 texCoordData
.push(u
);
516 texCoordData
.push(v
);
517 geometryData
.push(radius
* x
);
518 geometryData
.push(radius
* y
);
519 geometryData
.push(radius
* z
);
524 for (var latNumber
= 0; latNumber
< lats
; ++latNumber
) {
525 for (var longNumber
= 0; longNumber
< longs
; ++longNumber
) {
526 var first
= (latNumber
* longs
) + (longNumber
% longs
);
527 var second
= first
+ longs
;
528 indexData
.push(first
);
529 indexData
.push(second
);
530 indexData
.push(first
+1);
532 indexData
.push(second
);
533 indexData
.push(second
+1);
534 indexData
.push(first
+1);
540 retval
.normalObject
= ctx
.createBuffer();
541 ctx
.bindBuffer(ctx
.ARRAY_BUFFER
, retval
.normalObject
);
542 ctx
.bufferData(ctx
.ARRAY_BUFFER
, new Float32Array(normalData
), ctx
.STATIC_DRAW
);
544 retval
.texCoordObject
= ctx
.createBuffer();
545 ctx
.bindBuffer(ctx
.ARRAY_BUFFER
, retval
.texCoordObject
);
546 ctx
.bufferData(ctx
.ARRAY_BUFFER
, new Float32Array(texCoordData
), ctx
.STATIC_DRAW
);
548 retval
.vertexObject
= ctx
.createBuffer();
549 ctx
.bindBuffer(ctx
.ARRAY_BUFFER
, retval
.vertexObject
);
550 ctx
.bufferData(ctx
.ARRAY_BUFFER
, new Float32Array(geometryData
), ctx
.STATIC_DRAW
);
552 retval
.numIndices
= indexData
.length
;
553 retval
.indexObject
= ctx
.createBuffer();
554 ctx
.bindBuffer(ctx
.ELEMENT_ARRAY_BUFFER
, retval
.indexObject
);
555 ctx
.bufferData(ctx
.ELEMENT_ARRAY_BUFFER
, new Uint16Array(indexData
), ctx
.STREAM_DRAW
);
563 // Load a .obj file from the passed URL. Return an object with a 'loaded' property set to false.
564 // When the object load is complete, the 'loaded' property becomes true and the following
565 // properties are set:
567 // normalObject WebGLBuffer object for normals
568 // texCoordObject WebGLBuffer object for texCoords
569 // vertexObject WebGLBuffer object for vertices
570 // indexObject WebGLBuffer object for indices
571 // numIndices The number of indices in the indexObject
573 function loadObj(ctx
, url
)
575 var obj
= { loaded
: false };
577 var req
= new XMLHttpRequest();
579 req
.onreadystatechange = function () { processLoadObj(req
) };
580 req
.open("GET", url
, true);
585 function processLoadObj(req
)
587 webglTestLog("req="+req
)
588 // only if req shows "complete"
589 if (req
.readyState
== 4) {
590 doLoadObj(req
.obj
, req
.responseText
);
594 function doLoadObj(obj
, text
)
607 var lines
= text
.split("\n");
608 for (var lineIndex
in lines
) {
609 var line
= lines
[lineIndex
].replace(/[ \t]+/g, " ").replace(/\s\s*$/, "");
615 var array
= line
.split(" ");
616 if (array
[0] == "v") {
618 vertex
.push(parseFloat(array
[1]));
619 vertex
.push(parseFloat(array
[2]));
620 vertex
.push(parseFloat(array
[3]));
622 else if (array
[0] == "vt") {
624 texture
.push(parseFloat(array
[1]));
625 texture
.push(parseFloat(array
[2]));
627 else if (array
[0] == "vn") {
629 normal
.push(parseFloat(array
[1]));
630 normal
.push(parseFloat(array
[2]));
631 normal
.push(parseFloat(array
[3]));
633 else if (array
[0] == "f") {
635 if (array
.length
!= 4) {
636 webglTestLog("*** Error: face '"+line
+"' not handled");
640 for (var i
= 1; i
< 4; ++i
) {
641 if (!(array
[i
] in facemap
)) {
642 // add a new entry to the map and arrays
643 var f
= array
[i
].split("/");
647 vtx
= parseInt(f
[0]) - 1;
651 else if (f
.length
= 3) {
652 vtx
= parseInt(f
[0]) - 1;
653 tex
= parseInt(f
[1]) - 1;
654 nor
= parseInt(f
[2]) - 1;
657 webglTestLog("*** Error: did not understand face '"+array
[i
]+"'");
665 if (vtx
* 3 + 2 < vertex
.length
) {
677 if (tex
* 2 + 1 < texture
.length
) {
679 y
= texture
[tex
*2+1];
681 textureArray
.push(x
);
682 textureArray
.push(y
);
688 if (nor
* 3 + 2 < normal
.length
) {
697 facemap
[array
[i
]] = index
++;
700 indexArray
.push(facemap
[array
[i
]]);
706 obj
.normalObject
= obj
.ctx
.createBuffer();
707 obj
.ctx
.bindBuffer(obj
.ctx
.ARRAY_BUFFER
, obj
.normalObject
);
708 obj
.ctx
.bufferData(obj
.ctx
.ARRAY_BUFFER
, new Float32Array(normalArray
), obj
.ctx
.STATIC_DRAW
);
710 obj
.texCoordObject
= obj
.ctx
.createBuffer();
711 obj
.ctx
.bindBuffer(obj
.ctx
.ARRAY_BUFFER
, obj
.texCoordObject
);
712 obj
.ctx
.bufferData(obj
.ctx
.ARRAY_BUFFER
, new Float32Array(textureArray
), obj
.ctx
.STATIC_DRAW
);
714 obj
.vertexObject
= obj
.ctx
.createBuffer();
715 obj
.ctx
.bindBuffer(obj
.ctx
.ARRAY_BUFFER
, obj
.vertexObject
);
716 obj
.ctx
.bufferData(obj
.ctx
.ARRAY_BUFFER
, new Float32Array(vertexArray
), obj
.ctx
.STATIC_DRAW
);
718 obj
.numIndices
= indexArray
.length
;
719 obj
.indexObject
= obj
.ctx
.createBuffer();
720 obj
.ctx
.bindBuffer(obj
.ctx
.ELEMENT_ARRAY_BUFFER
, obj
.indexObject
);
721 obj
.ctx
.bufferData(obj
.ctx
.ELEMENT_ARRAY_BUFFER
, new Uint16Array(indexArray
), obj
.ctx
.STREAM_DRAW
);
729 // Load the image at the passed url, place it in a new WebGLTexture object and return the WebGLTexture.
731 function loadImageTexture(ctx
, url
)
733 var texture
= ctx
.createTexture();
734 texture
.image
= new Image();
735 texture
.image
.onload = function() { doLoadImageTexture(ctx
, texture
.image
, texture
) }
736 texture
.image
.src
= url
;
740 function doLoadImageTexture(ctx
, image
, texture
)
742 ctx
.enable(ctx
.TEXTURE_2D
);
743 ctx
.bindTexture(ctx
.TEXTURE_2D
, texture
);
744 ctx
.texImage2D(ctx
.TEXTURE_2D
, 0, gl
.RGBA
, gl
.RGBA
, gl
.UNSIGNED_BYTE
, image
);
745 ctx
.texParameteri(ctx
.TEXTURE_2D
, ctx
.TEXTURE_MAG_FILTER
, ctx
.LINEAR
);
746 ctx
.texParameteri(ctx
.TEXTURE_2D
, ctx
.TEXTURE_MIN_FILTER
, ctx
.LINEAR_MIPMAP_LINEAR
);
747 ctx
.texParameteri(ctx
.TEXTURE_2D
, ctx
.TEXTURE_WRAP_S
, ctx
.CLAMP_TO_EDGE
);
748 ctx
.texParameteri(ctx
.TEXTURE_2D
, ctx
.TEXTURE_WRAP_T
, ctx
.CLAMP_TO_EDGE
);
749 ctx
.generateMipmap(ctx
.TEXTURE_2D
)
750 ctx
.bindTexture(ctx
.TEXTURE_2D
, 0);
756 // This object keeps track of framerate and displays it as the innerHTML text of the
757 // HTML element with the passed id. Once created you call snapshot at the end
758 // of every rendering cycle. Every 500ms the framerate is updated in the HTML element.
760 Framerate = function(id
)
762 this.numFramerates
= 10;
763 this.framerateUpdateInterval
= 500;
766 this.renderTime
= -1;
767 this.framerates
= [ ];
769 var fr = function() { self
.updateFramerate() }
770 setInterval(fr
, this.framerateUpdateInterval
);
773 Framerate
.prototype.updateFramerate = function()
776 for (var i
= 0; i
< this.framerates
.length
; ++i
)
777 tot
+= this.framerates
[i
];
779 var framerate
= tot
/ this.framerates
.length
;
780 framerate
= Math
.round(framerate
);
781 document
.getElementById(this.id
).innerHTML
= "Framerate:"+framerate
+"fps";
784 Framerate
.prototype.snapshot = function()
786 if (this.renderTime
< 0)
787 this.renderTime
= new Date().getTime();
789 var newTime
= new Date().getTime();
790 var t
= newTime
- this.renderTime
;
791 var framerate
= 1000/t
;
792 this.framerates
.push(framerate
);
793 while (this.framerates
.length
> this.numFramerates
)
794 this.framerates
.shift();
795 this.renderTime
= newTime
;