Writing FPS in webGL

I am trying to compare the performance for 3d applications on mobile devices. I have a 3D solar system created in webGL and I'm trying to record or at least display FPS. So far this is what I have:

in 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 get the Time Var in the canvas drawing function, 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 null"

any help would be great!

+1
javascript webgl
source share
4 answers

I assume that you call drawScene several times, but if you set x only once, then it will not be updated every time drawScene is called. Also, what you store in Time is the elapsed time, not frames per second.

How about something like below? The idea is to count the number of frames displayed and after one second the storage has passed, which is 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> 
+4
source share

FPS mapping is pretty simple and has nothing to do with WebGL other than what it wants to know. Here is 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 is for. Browsers can sync with screen updates to give you an oily smooth animation. They may also stop processing if your page is not displayed. setTimeout, on the other hand, is not intended for animation; it will not synchronize with the picture of the browser page.

You should probably not use Date.now() to calculate FPS, as Date.now() returns milliseconds. Also, using (new Date()).getTime() especially bad since it generates a new Date object for each frame.

requestAnimationFrame already receives time in microseconds from the moment the page loads, so just use this.

It is also common for the average FPS for 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> 
+8
source share

Since none of the other answers touched part of the “in WebGL” question, I will add the following important data with the correct FPS measurement in WebGL.

 window.console.time('custom-timer-id'); // start timer /* webgl draw call here */ // eg, gl.drawElements(); gl.finish(); // ensure the GPU is ready window.console.timeEnd('custom-timer-id'); // end timer 

For simplicity, I used a console timer. I try to always use WebGLRenderingContext.finish () to ensure that the correct time is measured, since all calls to WebGL on the GPU are asynchronous!

+1
source share

I created an object oriented version of Barış Uşaklı's answer. It also tracks the average fps for the last minute.

Application:

global variable:

 var fpsCounter; 

Create an object somewhere at the start of your program:

  fpsCounter = new FpsCounter(); 

Call the update method in draw () and update fps-display:

 function drawScene() { fpsCounter.update(); document.getElementById('fpsDisplay').innerHTML = fpsCounter.getCountPerSecond(); document.getElementById('fpsMinuteDisplay').innerHTML = fpsCounter.getCountPerMinute(); // Code } 

Note. I just add fps-display updates to the draw function for simplicity. At 60fps, it is set 60 times per second, although once per 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); }; 
0
source share

All Articles