local fps = {}
do
cvars.RemoveChangeCallback("fps_graph", "fps_graph")
cvars.RemoveChangeCallback("fps_graph_width", "fps_graph_width")
cvars.RemoveChangeCallback("fps_graph_points", "fps_graph_points")
fps.X, fps.Y = 20, 20
fps.W, fps.H = 0, 150
fps.PointDistance = 0
fps.max = GetConVar("fps_max")
fps.graph = CreateClientConVar("fps_graph", "0", nil, nil, "Draw the FPS counter and/or graph, = 1 draws counter, = 2 draws bar graph and counter, = 3 draws line graph and counter.")
fps.graph_points = CreateClientConVar("fps_graph_points", "60", nil, nil, "The number of points/bars the FPS graph will have.")
fps.graph_width = CreateClientConVar("fps_graph_width", "500", nil, nil, "The FPS graph's target width. Will change to match the points.")
fps.Gradient = Material("vgui/gradient-d")
fps.Cache = setmetatable({}, {__index = function() return 0 end})
fps.NextCount = 0
fps.SinceLastUpdate = 0
fps.LastFrameNumber = FrameNumber()
end
local function CountFrames()
if (SysTime() < fps.NextCount) then return end
fps.NextCount = SysTime() + 1
local frames = FrameNumber()
local diff = frames - fps.LastFrameNumber
local limit = math.max(fps.max:GetInt(), 60)
table.insert(fps.Cache, 1, math.Clamp(diff / limit, 0, 1))
if (#fps.Cache > fps.graph_points:GetInt() + 1) then
table.remove(fps.Cache)
end
fps.SinceLastUpdate = diff
fps.LastFrameNumber = frames
end
local function DrawCounter()
local x, y = fps.X, fps.Y
local w, h = fps.W, fps.H
local FPS = string.format("%i FPS", fps.SinceLastUpdate)
if (fps.graph:GetInt() == 1) then
draw.SimpleText(FPS, "DebugOverlay", x, y)
else
local mult = fps.Cache[1]
local tx = x + w + 2
local ty = y + (h - (h * mult))
draw.SimpleText(FPS, "DebugOverlay", tx, ty, _, _, TEXT_ALIGN_CENTER)
end
end
local function DrawBarGraph()
local x, y = fps.X, fps.Y
local w, h = fps.W, fps.H
local pd = fps.PointDistance
local points = fps.graph_points:GetInt()
render.SetScissorRect(x, y, x + w, y + h, true)
surface.SetDrawColor(0, 0, 0, 200)
surface.DrawRect(x, y, w, h)
for i = 1, points do
local mult = fps.Cache[i]
local bh = math.floor(h * mult)
local bx = (x + w) - (i * pd)
local by = y + (h - bh)
surface.SetDrawColor(Color(255 - 255 * mult, 255 * mult, 0))
surface.DrawRect(bx, by, pd, bh)
end
render.SetScissorRect(0, 0, 0, 0, false)
end
local function DrawLineGraph()
local x, y = fps.X, fps.Y
local w, h = fps.W, fps.H
local pd = fps.PointDistance
local points = fps.graph_points:GetInt()
render.SetScissorRect(x, y, x + w, y + h, true)
surface.SetDrawColor(0, 0, 0, 200)
surface.DrawRect(x, y, w, h)
render.ClearStencil()
render.SetStencilEnable(true)
render.SetStencilTestMask(0xFF)
render.SetStencilWriteMask(0xFF)
render.SetStencilReferenceValue(0x01)
render.SetStencilCompareFunction(STENCIL_ALWAYS)
render.SetStencilPassOperation(STENCIL_REPLACE)
local first = fps.Cache[1]
local pph = math.floor(h * first)
local ppx = (x + w) - 1
local ppy = y + (h - pph)
for i = 2, points + 1 do
local mult = fps.Cache[i]
local ph = math.floor(h * mult)
local px = ((x + w) - ((i - 1) * pd)) - 1
local py = y + (h - ph)
surface.SetDrawColor(0, 255, 0)
surface.DrawLine(ppx, ppy, px, py)
ppx, ppy = px, py
end
render.SetStencilCompareFunction(STENCIL_LESSEQUAL)
render.SetStencilFailOperation(STENCIL_KEEP)
surface.SetMaterial(fps.Gradient)
surface.SetDrawColor(255, 0, 0)
surface.DrawTexturedRect(x, y, w, h)
render.SetStencilEnable(false)
render.SetScissorRect(0, 0, 0, 0, false)
end
local function GraphChanged(_, old, new)
old, new = tonumber(old) or 0, tonumber(new) or 0
if (new > 0) then
if (old <= 0) then
fps.SinceLastUpdate = 0
fps.LastFrameNumber = FrameNumber()
end
hook.Add("PreRender", "FPS Counter", CountFrames)
if (new >= 1) then
hook.Add("PostDrawHUD", "FPS Counter", DrawCounter)
if (new == 2) then
hook.Add("PostDrawHUD", "FPS Graph", DrawBarGraph)
elseif (new >= 3) then
hook.Add("PostDrawHUD", "FPS Graph", DrawLineGraph)
else
hook.Remove("PostDrawHUD", "FPS Graph")
end
end
else
table.Empty(fps.Cache)
hook.Remove("PreRender", "FPS Counter")
hook.Remove("PostDrawHUD", "FPS Counter")
hook.Remove("PostDrawHUD", "FPS Graph")
end
end
cvars.AddChangeCallback("fps_graph", GraphChanged, "fps_graph")
local function FindClosest(width, points)
local low = width % points
local high = (width + low) % points
return low <= high and width - low or width + high
end
local function WidthChanged(_, _, new)
local points = fps.graph_points:GetInt()
fps.W = FindClosest(new, points)
fps.PointDistance = fps.W / points
end
cvars.AddChangeCallback("fps_graph_width", WidthChanged, "fps_graph_width")
local function PointsChanged(_, old, new)
old, new = tonumber(old) or 0, tonumber(new) or 60
local target = fps.graph_width:GetInt()
fps.W = FindClosest(target, new)
fps.PointDistance = fps.W / new
if (old < new) then return end
local diff = old - new
if (#fps.Cache - diff > new) then
for i = 1, diff do
table.remove(fps.Cache)
end
end
end
cvars.AddChangeCallback("fps_graph_points", PointsChanged, "fps_graph_points")
GraphChanged(_, _, fps.graph:GetInt())
WidthChanged(_, _, fps.graph_width:GetInt())
Comments