admin管理员组文章数量:1189212
I am trying to compare performance for 3d applications on mobile devices. I have a 3d solar system set up in webGL and im trying to record or at least display the FPS. So far this is what i Have:
in the body
<script language="javascript">
var x, message;
x = Time;
message = "fps is equal to ";
document.write (message); // prints the value of the message variable
document.write (x); //prints the value of x
</script>
and to get The Time Var in the draw function of canvas i have this
var Time = 0;
function drawScene() {
var startTime = new Date();
//draw scene here
var endTime = new Date();
Time = (endTime - startTime)
}
the output i get at the bottom of the canvas is "fps is equal to null"
any help would be great!
I am trying to compare performance for 3d applications on mobile devices. I have a 3d solar system set up in webGL and im trying to record or at least display the FPS. So far this is what i Have:
in the body
<script language="javascript">
var x, message;
x = Time;
message = "fps is equal to ";
document.write (message); // prints the value of the message variable
document.write (x); //prints the value of x
</script>
and to get The Time Var in the draw function of canvas i have this
var Time = 0;
function drawScene() {
var startTime = new Date();
//draw scene here
var endTime = new Date();
Time = (endTime - startTime)
}
the output i get at the bottom of the canvas is "fps is equal to null"
any help would be great!
Share Improve this question asked May 8, 2013 at 4:41 timtim 3191 gold badge5 silver badges15 bronze badges 1- 1 take a look at github.com/mrdoob/stats.js – gaitat Commented Nov 21, 2017 at 22:08
5 Answers
Reset to default 19Displaying FPSs is pretty simple and has really nothing to do with WebGL other than it's common to want to know. Here's a small FPS display
const fpsElem = document.querySelector("#fps");
let then = 0;
function render(now) {
now *= 0.001; // convert to seconds
const deltaTime = now - then; // compute time since last frame
then = now; // remember time for next frame
const fps = 1 / deltaTime; // compute frames per second
fpsElem.textContent = fps.toFixed(1); // update fps display
requestAnimationFrame(render);
}
requestAnimationFrame(render);
<div>fps: <span id="fps"></span></div>
Use requestAnimationFrame for animation because that's what it's for. Browsers can sync to the screen refresh to give you buttery smooth animation. They can also stop processing if your page is not visible. setTimeout on the other hand is not designed for animation, will not be synchronised to the browser's page drawing.
You should probably not use Date.now()
for computing FPS as Date.now()
only returns milliseconds. Also using (new Date()).getTime()
is especially bad since it's generating a new Date
object every frame.
requestAnimationFrame
already gets passed the time in microseconds since the page loaded so just use that.
It's also common to average the FPS across frames.
const fpsElem = document.querySelector("#fps");
const avgElem = document.querySelector("#avg");
const frameTimes = [];
let frameCursor = 0;
let numFrames = 0;
const maxFrames = 20;
let totalFPS = 0;
let then = 0;
function render(now) {
now *= 0.001; // convert to seconds
const deltaTime = now - then; // compute time since last frame
then = now; // remember time for next frame
const fps = 1 / deltaTime; // compute frames per second
fpsElem.textContent = fps.toFixed(1); // update fps display
// add the current fps and remove the oldest fps
totalFPS += fps - (frameTimes[frameCursor] || 0);
// record the newest fps
frameTimes[frameCursor++] = fps;
// needed so the first N frames, before we have maxFrames, is correct.
numFrames = Math.max(numFrames, frameCursor);
// wrap the cursor
frameCursor %= maxFrames;
const averageFPS = totalFPS / numFrames;
avgElem.textContent = averageFPS.toFixed(1); // update avg display
requestAnimationFrame(render);
}
requestAnimationFrame(render);
body { font-family: monospace; }
<div> fps: <span id="fps"></span></div>
<div>average fps: <span id="avg"></span></div>
I assume you are calling drawScene repeatedly but if you are setting x
only once then it will not update every time drawScene is called. Also what you are storing in Time
is elapsed time and not frames per second.
How about something like the below? The idea is to count the number of frames rendered and once one second has passed store that in the fps variable.
<script>
var elapsedTime = 0;
var frameCount = 0;
var lastTime = 0;
function drawScene() {
// draw scene here
var now = new Date().getTime();
frameCount++;
elapsedTime += (now - lastTime);
lastTime = now;
if(elapsedTime >= 1000) {
fps = frameCount;
frameCount = 0;
elapsedTime -= 1000;
document.getElementById('test').innerHTML = fps;
}
}
lastTime = new Date().getTime();
setInterval(drawScene,33);
</script>
<div id="test">
</div>
I created an object oriented version of Barış Uşaklı's answer. It also tracks the average fps over the last minute.
Usage:
global Variable:
var fpsCounter;
Create the object somewhere when starting your program:
fpsCounter = new FpsCounter();
Call the update method in your draw() funktion & update the fps-displays:
function drawScene() {
fpsCounter.update();
document.getElementById('fpsDisplay').innerHTML = fpsCounter.getCountPerSecond();
document.getElementById('fpsMinuteDisplay').innerHTML = fpsCounter.getCountPerMinute();
// Code
}
Note: I only put the fps-display updates in the draw function for simplicity. With 60fps it gets set 60 times per second, even though once a second is enough.
FpsCounter Code:
function FpsCounter(){
this.count = 0;
this.fps = 0;
this.prevSecond;
this.minuteBuffer = new OverrideRingBuffer(60);
}
FpsCounter.prototype.update = function(){
if (!this.prevSecond) {
this.prevSecond = new Date().getTime();
this.count = 1;
}
else {
var currentTime = new Date().getTime();
var difference = currentTime - this.prevSecond;
if (difference > 1000) {
this.prevSecond = currentTime;
this.fps = this.count;
this.minuteBuffer.push(this.count);
this.count = 0;
}
else{
this.count++;
}
}
};
FpsCounter.prototype.getCountPerMinute = function(){
return this.minuteBuffer.getAverage();
};
FpsCounter.prototype.getCountPerSecond = function(){
return this.fps;
};
OverrideBuffer Code:
function OverrideRingBuffer(size){
this.size = size;
this.head = 0;
this.buffer = new Array();
};
OverrideRingBuffer.prototype.push = function(value){
if(this.head >= this.size) this.head -= this.size;
this.buffer[this.head] = value;
this.head++;
};
OverrideRingBuffer.prototype.getAverage = function(){
if(this.buffer.length === 0) return 0;
var sum = 0;
for(var i = 0; i < this.buffer.length; i++){
sum += this.buffer[i];
}
return (sum / this.buffer.length).toFixed(1);
};
Since none of the other answers addressed the "in WebGL" part of the question, I'll add the following important details when measuring FPS in WebGL correctly.
window.console.time('custom-timer-id'); // start timer
/* webgl draw call here */ // e.g., gl.drawElements();
gl.finish(); // ensure the GPU is ready
window.console.timeEnd('custom-timer-id'); // end timer
For simplicity I used the console timer. I'm trying to make the point to always use WebGLRenderingContext.finish() to ensure the correct time is measured as all WebGL calls to the GPU are asynchronous!
Using a rotating array can do better. with dom element:
<div id="fps">
the following script do the trick:
var fpsLastTick = new Date().getTime();
var fpsTri = [15, 15, 15]; // aims for 60fps
function animate() {
// your rendering logic blahh blahh.....
// update fps at last
var now = new Date().getTime();
var frameTime = (now - fpsLastTick);
fpsTri.shift(); // drop one
fpsTri.push(frameTime); // append one
fpsLastTick = now;
fps = Math.floor(3000 / (fpsTri[0] + fpsTri[1] + fpsTri[2])); // mean of 3
var fpsElement = document.getElementById('fps')
if (fpsElement) {
fpsElement.innerHTML = fps;
}
}
本文标签: javascriptRecording FPS in webGLStack Overflow
版权声明:本文标题:javascript - Recording FPS in webGL - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1738408892a2085202.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论