# Class to calculate and view an average frame rate and a FPS history graph

As the framerate changes too quick when I just display 1.0 / DeltaTime each frame, I wanted to show an average frame rate over a number of frames to stabilize it more. Here you find the source code to this small class including a draw function.

Simplest usage is to initialize the class as framerate=FrameRateC(25, 100). Then call framerate:calc() and framerate:draw(1, 255) in your draw code, that shows the FPS as average over 25 frames and a history of 100 FPS. Initialize as FrameRateC(25, 0) to only show the number.

You can also retrieve the current average and frame number with getAverageFrameRate() and getCurrentFrameNr(). The average is calculated over 20 frames in this sample which I found good for my use cases.

The algorithm used is called Simple Moving Average. It avoids calculating the median over the whole table each frame, so the runtime is the same independent of the number of frames you average. But showing a large history will take time.

Have fun,
Kilam Malik

Source of Main function to test it:

``````-- Main.lua

frameRate = nil
frameNr = 1
frameAvg = 0

--displayMode(FULLSCREEN)

-- Use this function to perform your initial setup
function setup()
frameRate = FrameRateC(20, 100)

watch("frameNr")
watch("frameAvg")
iparameter("nrObjects", 1, 500, 50)
iparameter("position", 1, 4, 2)
iparameter("alpha", 50, 255, 127)
iparameter("bgcol", 0, 255, 255)
end

-- This function gets called once every frame
function draw()
frameNr = frameRate:getCurrentFrameNr()
frameAvg = frameRate:getAverageFrameRate()

-- This sets a dark background color
background(bgcol, bgcol, bgcol, 255)

-- This sets the line thickness
strokeWidth(5)

stroke(0, 0, 0, 127)
strokeWidth(1)
for i = 1, nrObjects do
local r = 200 + 100*math.sin(i*0.180)
local inner = (ElapsedTime*20+i)/20
local cs = math.sin(inner) + 1
local cc = math.cos(inner*0.3429495) + 1
fill(127*cs, 127*cc, 127*cs*cc, 50)
ellipse(WIDTH/2 + r*math.sin(inner),HEIGHT/2 + r*math.cos(inner),30)
end

-- Calc and draw the frame rate:
frameRate:calc()
frameRate:draw(position, alpha)
end
``````

Source of the Framerate class:

``````-- Framerate.lua
--
-- Framerate calculation and display
-- =================================
--
-- This class calculates the average framerate over a given number
-- of frames. It uses the Simple Moving Average algorithm to avoid
-- calculating the median over the whole table all the time.
--
-- The higher you choose the number of frames to average, the more
-- stable the framerate will be. But also it will react slower on
-- changes.
--
-- Additionaly, there is a framerate history graph. It has its own table,
-- as the average is e.g. fine with 20 values, but the graph makes sense with more.
--
-- A large graph will slow down your program.
--
-- Usage:
--     frameRate = nil
--
-- Initialize the class:
--     frameRate = FrameRateC(25, 100)
--
-- Call this in the draw function. Call it as last to overlay everything else:
--     frameRate:calc()
--     frameRate:draw(position, alpha) -- position = 1-4 for the corners and alpha 1-255.
--
-- Have fun,
--   Kilam Malik.

local frameNr = 1 -- Frame counter.
local frameRateAvg = -1 -- Average frame rate.
local frameRateTable = {} -- Table the recent frame rates.
local tabIdx = 1 -- Current table index.
local tabSize = 0 -- Currently used size of table. Only for startup.
local tabMaxSize = 25 -- Table size.
local tabSum = 0 -- Current sum of all values in table.

local frameRateGraphTable = {} -- Table for history graph.
local graphTableIdx = 1 -- Current position.
local graphWidth = 100 -- Width of graph.
local graphHeight = 50-- Height of graph.

FrameRateC = class()

-- avgSize: # frames to average.
-- gw: Width of graph, means # frames in graph.
-- gh: Height of graph shown.
function FrameRateC:init(avgSize, gw, gh)
local i
tabMaxSize = avgSize
for i = 1,tabMaxSize do
frameRateTable[i] = 0
end

graphWidth = gw
graphHeight = gh
for i = 1,graphWidth do
frameRateGraphTable[i] = 0
end
end

-- Call this every frame in your draw function to calculate the rate.
function FrameRateC:calc()
-- Calc current frame rate
local thisFrameRate = 1.0 / DeltaTime

-- Calc average frame rate using simple moving average
tabSum = tabSum - frameRateTable[tabIdx] -- Subtract remove value from sum
tabSum = tabSum + thisFrameRate -- Add new value to sum
frameRateTable[tabIdx] = thisFrameRate -- Set value in table
if tabIdx < tabMaxSize then
tabIdx = tabIdx + 1
else
tabIdx = 1
end

-- To have a correct framerate when the table is filled the first time,
-- the tabSize variable is incremented until it is tabMaxSize
if tabSize < tabMaxSize then
tabSize = tabSize + 1
end

-- Calc Framerate over table sum
frameRateAvg = tabSum / tabSize

-- Increase frame counter
frameNr = frameNr + 1

-- Fill graph
frameRateGraphTable[graphTableIdx] = thisFrameRate -- Set value in table
if graphTableIdx < graphWidth then
graphTableIdx = graphTableIdx + 1
else
graphTableIdx = 1
end
end

-- Retrieve average frame rate.
function FrameRateC:getAverageFrameRate()
return math.floor(frameRateAvg)
end

-- Retrieve current frame number.
function FrameRateC:getCurrentFrameNr()
return frameNr
end

function FrameRateC:draw(pos, alpha)
pushStyle()
local h = 50
local center = vec2()
local hMul = h/60
local cMul = 255/60
rectMode(CENTER)
noSmooth()
if pos == 1 then
center=vec2(WIDTH - graphWidth - 5, HEIGHT - h - 5)
elseif pos == 2 then
center=vec2(WIDTH - graphWidth - 5, 5)
elseif pos == 3 then
center=vec2(5, 5)
elseif pos == 4 then
center=vec2(5, HEIGHT - h - 5)
end

local i
local basePosX = center.x
local basePosY = center.y
strokeWidth(1)
for i = 1,graphWidth do
local p = basePosX + i
local idx = (graphTableIdx + (i-2))%graphWidth+1
local f = frameRateGraphTable[idx]
stroke(255-f*cMul,f*cMul,0, alpha)
line(p, basePosY, p, basePosY + f * hMul)
end

fontSize(20)
font("Inconsolata")
local s = string.format("%d", frameRateAvg)
local wf, hf = textSize("00")

if pos <= 2 then
basePosX = basePosX - wf - 5
else
basePosX = basePosX + graphWidth + 5
end
textMode(CORNER)
fill(255, 255, 255, alpha)
text(s,basePosX, basePosY)
fill(0, 0, 0, alpha)
text(s,basePosX + 1, basePosY + 1)
popStyle()
end
``````

Your code demonstrates more than just stable FPS calculation, but if the main issue to be dealt with is stability, a lighter solution would be to reduce the frequency at which the FPS is recalculated. For example:

Use:

``````
function setup()
myFPSReporter = FPSReporter(4)
iparameter("nrObjects", 1, 1500, 1)
iparameter("position", 1, 4, 1)
dim = math.min(WIDTH, HEIGHT)
end

function draw()
background(0)
myFPSReporter:draw(position)
for i = 1, nrObjects do
local d = i/nrObjects * 8 * math.pi
local r = i/nrObjects * dim/2
ellipse(WIDTH/2 + r*math.sin(ElapsedTime+d),
HEIGHT/2 + r*math.cos(ElapsedTime+d),10)
end
end

``````

Class:

``````
FPSReporter = class()

function FPSReporter:init(rate)
self.spacing = 1/rate
self.last = ElapsedTime
self.count = 0
self.rate = 60
end

function FPSReporter:draw(pos)
self.count = self.count + 1
local now = ElapsedTime
local delta = now - self.last
if delta >= self.spacing then
self.rate = self.count/delta
self.count = 0
self.last = now
end
pushStyle()
local s = string.format("%d", self.rate)
local w, h = textSize(s)
textMode(CENTER)
fontSize(18)
font("Inconsolata")
fill(255)
pos = pos - 1
local x = math.floor(pos/2) % 2 * (WIDTH - w) + w/2
local y = pos % 2 * (HEIGHT - h) + h/2
text(s, x, y)
popStyle()
end

``````

The calculation of x and y from pos above is inspired by Andrew Stacey’s AddStar() method from the Roller Coaster example project.

Right, thats a simpler calculation by showing the framerate only every 1/4 or 1/2 second. I like that too, think it depends on what you need it for. Thanks.

After seeing @mpilgrem’s post about his framerate test with visualizing the history, I also added some history to my FrameRateC class. Source changed above in comment 1 and 2.

A Picture, bottom right corner shows the FPS and history graph:
https://dl.dropbox.com/u/80475380/CodeaForumPictures/framerate_history.JPG

The framerate is a bit jerky when you show the display in standard mode, I have seen that in my other apps too. But with this graph I’m now sure it is something with the display mode and not with my code. When you open the display in full screen, then the framerate is stable.