Lesson 9.2Lesson 9.2 · Optimization & Performance
Profiling & Frame Budgets
Stop guessing - measure the frame, find whether the CPU or the GPU is the wall, and fix the right thing
You cannot optimize what you have not measured. Halve the polycount to fix a stutter that was really the CPU, and you fixed nothing.
Every smooth real-time experience is really a promise kept: the whole frame will be finished in time. At 60fps that is about 16 milliseconds; for comfortable VR at 90fps it is about 11. Miss it and the picture stutters, and in VR that stutter can make people ill.
The trap is optimizing by superstition - decimating meshes, lowering textures, deleting lights - without knowing which one is actually the wall. Profiling replaces guesswork with a reading. A few commands tell you your frame time, break it into CPU and GPU, and point straight at the bottleneck. This lesson teaches you to take that reading and interpret it.
Measure -> hypothesise -> change ONE thing -> re-measure. Never lower settings before you know if the GPU is even the wall.
The frame budget: 16ms at 60fps, 11ms at 90fps
Frame rate is just the inverse of frame time, and turning it into milliseconds is the single most useful mental move in optimization. Sixty frames per second means each frame must finish in 1000/60, about 16.7ms. Thirty fps is 33.3ms; 90fps - the comfortable floor for VR - is 11.1ms; 120fps is 8.3ms. That number is your budget, and everything the frame does has to fit inside it: game logic, animation, physics, culling, issuing draw calls, and the GPU actually drawing the pixels.
Working in milliseconds beats working in fps because milliseconds add up linearly and fps does not. Going from 60 to 50fps sounds small but is +3.3ms; going from 30 to 20fps is a huge +16.7ms. When you shave a feature that costs '2ms', you can add that straight into your budget arithmetic. Frame rate figures hide this; millisecond figures make optimization a spreadsheet.
VR makes the budget unforgiving in two ways at once. It demands a higher frame rate (90fps and up, so a shorter budget), and it renders the scene twice, once per eye. So the VR frame budget is not just tighter - it is tighter while doing roughly double the work. That is why Module 9's last lesson treats VR performance as its own discipline; here, just hold the numbers: 16ms desktop, 11ms VR, and VR pays it twice.
fps to ms: 1000 / fps. 60->16.7, 90->11.1, 120->8.3. Think in milliseconds; they add up, fps does not.
stat fps and stat unit - your first read
The fastest way to see the budget in Unreal is the stat console commands. Press the backtick key to open the console and type stat fps for a frame rate and frame time readout, or better, stat unit - the single most important profiling display you have. It shows four numbers in milliseconds: Frame (the total), Game (the CPU game thread - your Blueprints, logic, animation, physics), Draw (the CPU render/draw thread - preparing and issuing draw calls), and GPU (how long the graphics card took to draw the frame).
Read them together and the scene tells you where it hurts. The three workers - game thread, draw thread and GPU - run largely in parallel, so your frame time is set by the slowest of them, not their sum. If Frame is 21ms and GPU is 12ms while Draw is 20ms, the draw thread is your wall. Cut the GPU cost and nothing improves, because the draw thread was already the long pole.
Add stat unitgraph to see those numbers plotted over time, which exposes spikes a single reading hides - a stutter every few seconds usually means something intermittent (garbage collection, a level stream, a heavy Blueprint tick). stat rhi reports the raw draw-call count and triangles drawn this frame, the two numbers you most often need when the draw thread is the problem. Learn these four commands and you are already ahead of most people optimizing by feel.
stat unit = Frame / Game / Draw / GPU in ms. The frame is only as fast as the SLOWEST of Game, Draw and GPU.
CPU-bound or GPU-bound - the question that decides everything
Almost every optimization decision hinges on one diagnosis: are you CPU-bound or GPU-bound? Optimize the wrong side and you spend hours for no frame-rate gain, because you were never touching the bottleneck.
The read from stat unit is direct. If GPU is the largest number and close to Frame, you are GPU-bound - the graphics card is the wall. Fixes live on the pixel side: lower the resolution or resolution scale, reduce shadow quality, dial down Lumen (global-illumination) cost, simplify materials and translucency, cut post-processing. If instead Game or Draw is the largest, you are CPU-bound. A high Draw points at too many draw calls - the CPU is spending its time describing thousands of separate objects to the GPU - so you instance, merge and cull (the next lesson). A high Game points at expensive logic - heavy Blueprint ticks, physics, animation - so you optimize or throttle that code.
A useful confirming trick: the console command r.ScreenPercentage 50 roughly halves the pixels the GPU draws. If your frame rate jumps, you were GPU-bound; if it barely moves, the GPU was never the wall and you are CPU-bound. This one-line test cuts through a lot of guessing. Diagnose first, then reach for the matching fix - never the other way around.
GPU biggest -> GPU-bound -> cut pixels/shadows/Lumen. Draw or Game biggest -> CPU-bound -> cut draw calls / heavy logic.
Going deeper: GPU Visualizer and Unreal Insights
When stat unit tells you which worker is slow but not why, two deeper tools break the frame open. The GPU Visualizer (ProfileGPU, bound to Ctrl+Shift+comma by default) captures a single frame and shows a breakdown of GPU time by pass - base pass, shadows, Lumen, translucency, post-processing - so a GPU-bound scene reveals exactly which pass is eating the budget. If shadows are 4ms and Lumen reflections are 3ms, you now know precisely where to spend your effort.
For the CPU side and for intermittent problems, Unreal Insights is the professional-grade tool: it records a full timeline trace of the game and render threads, letting you zoom into a specific hitch and see which functions, Blueprints or systems consumed the time. It is heavier to set up than a stat command, but it is how you catch the once-a-second spike that stat unit only hints at.
Build the habit as a ladder. Start with stat unit for the one-glance diagnosis; add stat rhi for draw calls and triangles; run r.ScreenPercentage 50 to confirm CPU-versus-GPU; open the GPU Visualizer to find the guilty GPU pass; and reach for Unreal Insights when you need a full trace of a hitch. Measure, form a hypothesis, change one thing, and measure again. That disciplined loop - not blanket setting-lowering - is what separates real optimization from cargo-cult tweaking, and it is the mindset the rest of this module puts to work.
Ladder: stat unit -> stat rhi -> r.ScreenPercentage 50 test -> GPU Visualizer -> Unreal Insights. Change ONE thing, re-measure.
A worked read - turning numbers into a plan
Put it together with a concrete example, because the whole skill is translating a reading into a decision. You open a heavy interior scene, press backtick and type stat unit. It reports Frame 24.0ms, Game 8.5ms, Draw 23.2ms, GPU 13.0ms. First, convert Frame to fps: 1000 / 24 is about 42fps - well short of your 60fps (16.7ms) target. Second, find the long pole: Draw at 23.2ms is by far the largest and sits right under Frame, while GPU is only 13ms. That single comparison tells you the story - you are CPU-bound on the draw thread, and the GPU has spare capacity.
You confirm it in one line: type r.ScreenPercentage 50 to roughly halve the pixels the GPU draws. The frame rate barely moves - proof the GPU was never the wall. Set it back to 100. Now type stat rhi: it reports something like 9,400 draw calls. That number is enormous for an interior and explains everything - the scene was imported piece by piece, so the CPU is spending its whole budget describing thousands of separate objects. Your plan writes itself: instance the repeated furniture and fittings, merge the fixed clusters, and consolidate materials. None of it touches resolution, shadows or Lumen, because those live on the GPU side you already ruled out.
Contrast the opposite reading. If stat unit had shown Frame 20ms, GPU 18ms, Draw 6ms, and r.ScreenPercentage 50 had leapt the frame rate, you would be GPU-bound, and the GPU Visualizer would tell you whether shadows, Lumen or translucency was the culprit. Same tools, opposite conclusion, opposite fix. That is why the reading comes before the wrench every single time - the numbers, not your instinct, choose the lever.
Frame 24 / Game 8.5 / Draw 23.2 / GPU 13 -> Draw is the wall -> CPU-bound -> stat rhi shows 9400 calls -> instance and merge. The numbers decide.
stat unit
Console command showing Frame, Game, Draw and GPU in milliseconds
The one-glance diagnosis of where a frame's time goes; the frame is only as fast as the slowest of the three workers.
stat rhi
Console command reporting draw calls and triangles drawn this frame
The two numbers you most need when the draw thread is the bottleneck; high draw calls point to instancing and merging.
GPU Visualizer (ProfileGPU)
Single-frame GPU breakdown by rendering pass
Bound to Ctrl+Shift+comma; shows which pass - shadows, Lumen, translucency, post - is eating a GPU-bound frame.
Unreal Insights
Full timeline trace of game and render threads
The professional CPU profiler; catches intermittent hitches a single stat reading hides. Heavier to set up than a stat command.
Workshop - profile a scene and diagnose the bottleneck
You will take a real reading of a scene, translate it into a budget, and diagnose whether it is CPU-bound or GPU-bound using the one-line screen-percentage test. The goal is the diagnosis, not the fix - because the right fix follows automatically once the diagnosis is correct.
Unreal Engine 5, one moderately heavy scene, and the console (backtick). No extra installs - stat commands and the GPU Visualizer are built in.
Goal: read a frame budget and name your bottleneck with evidence Inputs: Unreal Engine 5 and any moderately heavy scene (an archviz sample, your own import, or a populated level) Time: ~30 minutes
- 1Open the console (backtick) and type stat unit. Write down Frame, Game, Draw and GPU in ms. Convert Frame to fps (1000 / Frame) and compare it to your target budget (16.7ms for 60fps, 11.1ms for 90fps VR).
- 2Identify the largest of Game, Draw and GPU - that is your candidate bottleneck. Add stat unitgraph and move around the scene for 20 seconds, watching for spikes that a single reading missed.
- 3Run the confirming test: type r.ScreenPercentage 50 and watch the frame rate. Big jump = GPU-bound; little change = CPU-bound. Then set it back to 100. Record what this told you.
- 4Type stat rhi and note the draw-call and triangle counts. If you diagnosed CPU-bound with a high Draw, high draw calls confirm it. If GPU-bound, open the GPU Visualizer (Ctrl+Shift+comma) and note which pass costs the most.
- 5Write one sentence: 'This scene is [CPU/GPU]-bound because [evidence], so the right fix is [matching lever].' That sentence is the whole skill.
You’ll walk away with
A one-page profile of one scene: stat unit numbers, the frame time converted to fps against budget, the r.ScreenPercentage 50 result, the stat rhi draw-call/triangle counts, and a single evidence-based sentence naming the bottleneck and the matching fix.
Three altitudes on the same idea
Read the band that fits you — or all three.
Profiling is how you make a promise you can keep to a client presentation. Nothing undermines a review like a walkthrough that stutters on the office machine. Take a stat unit reading on the hardware you will actually present on, find whether the model is stressing the GPU or the CPU, and fix that specific thing. A measured 60fps you can rely on beats a hopeful 90fps that hitches when the client turns a corner.
Your scenes tend to be GPU-heavy - lots of materials, soft light, translucency, reflections - so learn to read the GPU side. When a richly lit room drops frames, stat unit usually shows GPU as the wall, and the GPU Visualizer will point at shadows, Lumen reflections or translucency. Knowing that lets you soften a setting deliberately rather than stripping the room bare and losing the very richness that sells the interior.
The single most senior habit in real-time work is refusing to optimize before measuring, and it is easy to learn early. Get fluent with stat unit and the CPU-versus-GPU diagnosis and you will out-reason people with years more tool time who still tweak by feel. Practise the loop - measure, hypothesise, change one thing, re-measure - on every scene you build, and it becomes second nature by the time it counts.
“If the frame rate is low, just turn down the graphics settings and lower the poly count until it is smooth.”
Do it yourself
Read the numbers, do not guess - that is the entire point.
- 1How many milliseconds is one frame at 60fps? At 90fps? Show the arithmetic.
- 2In stat unit, which four numbers do you read, and why is the frame time set by the slowest rather than the sum?
- 3You see Frame 22ms, Game 9ms, Draw 21ms, GPU 12ms. Are you CPU- or GPU-bound, and what do you fix?
- 4What does r.ScreenPercentage 50 tell you, and how do you interpret a big jump versus no change?
- 5When would you reach past stat unit for the GPU Visualizer, and when for Unreal Insights?
The one line to carry out
Peer-reviewed journals & authoritative standards
- 01Frame rate — Wikipedia, 2026.
- 02Unreal Engine 5 Documentation — Epic Games Developer Documentation, 2026.
- 03Graphics processing unit — Wikipedia, 2026.
- 04Real-time computer graphics — Wikipedia, 2026.
Once you have measured and found you are CPU-bound on the draw thread, the fix is almost always the same family of levers: fewer draw calls and less geometry sent to the GPU. Next we work those classic levers - LODs, instancing, merging and culling - in detail.
The author
Amogh N P
Architect, interior designer, and creative polymath. Studio Matrx began in his notebooks — his vision of design made honest, useful, and open to everyone. Its Academy is written and taught in his memory, and free, forever.
More about Amogh →