Performance

Press F3 to see what a frame costs and where its time goes. In the editor it opens the Performance window. In a game started from the runtime with a project file it shows a box over the game. Both use the same numbers.

The editor's Performance window in play mode

Opening it

WhereHow
EditorF3, or View > Performance. It works while the game plays, like F5 and F6. --stats opens it at startup
Game, run with a project fileF3. --stats starts with it shown
Exported gameNot there. A finished game carries no debug tools

The game sees F3 too. If your game uses F3 for something, run it without the overlay.

What it shows

The numbers change twice a second, each an average over the last half second, so they are steady enough to read.

PartMeaning
FrameThe average frame time, in milliseconds
FPSFrames per second. Green while it keeps up with 60 Hz, yellow below
CPU workWhat a frame costs the main thread: updating, and recording what the GPU should draw. Waiting for the display and for the GPU is left out. With vsync on the frame time is the display's, so this is the number to watch
GPUWhat the viewport's passes cost the GPU, added up
The graphThe last 240 frames, newest on the right. The yellow line across it is 16.6 ms, the budget of one frame at 60 Hz
The rowsWhere the time goes, part by part: CPU parts first, then GPU passes, each with a bar against the 16.6 ms budget. See below
Memory, threadsThe process's own memory and its threads. On macOS memory is Activity Monitor's Memory column
Draw calls, lightsDraw calls of the viewport's last frame in every pass (shadows draw a mesh again for each cascade), and the lights drawn out of the 16 one frame supports
This machineOpens to show CPU use of the whole process (100% is one core kept busy), the machine's cores, memory now, at its peak and in all, the GPU's name and how much of its memory the process uses, and every count the editor keeps

Where the time goes

The CPU rows list the main thread's time by part:

PartWhat it is
GameIn the editor, everything the playing game did. Its parts are listed under it. Only in play mode
ScriptsEvery OnFixedUpdate, OnUpdate and collision or trigger callback, with timers and coroutines
PhysicsThe physics steps. At 60 fixed steps a second, a frame can hold none, one or several
Animation, Particles, Audio, Game UI, InputThose systems
Scene changesCarrying out Scene.Load and the others
Render, or Viewport in the editorRecording the frame's commands for the GPU. The GPU's own time is in the next list
Editor update, Editor UIThe editor's own work: commands, reloading scripts, and drawing its panels
GPU submitHanding the frame to the GPU. It waits when the GPU is frames behind, so a large number here means the GPU is what holds the frame up

The GPU rows, marked GPU, list how long the GPU took over each pass of the picture, measured by the GPU itself:

PassWhat it is
Skinning and shadowsUploading skinned meshes, then the shadow maps of the sun and of point and spot lights
Depth prepassThe depth of every opaque mesh
Ambient occlusionThe soft shadow in corners. It costs more the more pixels there are
Lit passEvery mesh, lit
Sky and particlesThe sky, then the particles
Post-processingBloom, exposure, tonemapping and anti-aliasing
Lines and UIDebug lines and the game's UI
Editor UIIn the editor, its own panels

GPU times are a few frames old when shown, because the GPU finishes a frame after the CPU has moved on.

The stats overlay over a game

Finding what is slow

  1. Look at GPU submit first. When it is large, the GPU is the limit: look down the GPU list for the most expensive pass. When it is small and CPU work is near the frame time, the CPU is the limit: look down the CPU list.
  2. A slow GPU pass usually has a setting behind it. Ambient occlusion and post-processing cost more the more pixels there are. Ambient occlusion, bloom and anti-aliasing are in the scene's render settings (the inspector with nothing selected), so turn one off and compare. Shadows cost more with each shadow-casting light, and a point light draws six shadow views.
  3. A slow Scripts line is usually one script doing too much in OnUpdate. Switch entities off one at a time, or move work into a timer that runs less often.
  4. A slow Physics line comes with many bodies, or with mesh colliders on things that move. Simple shapes are much cheaper.
  5. Watch the graph while you play. Single tall bars are hitches, often from loading something the first time it is used.

Recording a run

To compare one version of a game with another, record the numbers instead of reading them:

./build-release/Cairn-Runtime/Cairn-Runtime MyGame/MyGame.cairnproject --stats-log run.csv

Each time the stats change, twice a second, the runtime writes every number as a row of its own:

time,scene,group,name,value
12.50,Workshop,frame,fps,118.200
12.50,Workshop,cpu,Physics,2.310
12.50,Workshop,gpu,Lit pass,1.080

time is seconds since the run started, scene is the scene that was running, and group is frame, machine, counter, cpu or gpu. A row for each number keeps the file the same shape when sections come and go, as the game's do between levels. A pivot table in a spreadsheet, with name across and scene down, gives the average of each number in each scene. Like the overlay, it works only for a game run with a project file.

A run that is the same every time

Samples/FeatureTour in the repository is made for this. Left on its title screen for six seconds, it starts its benchmark: an autopilot walks the player through six levels, each of which leans on one part of the engine (lights, physics, particles, animation, scripts), in about a minute and a half. Record a run before a change and one after, and compare the levels:

./build-release/Cairn-Runtime/Cairn-Runtime Samples/FeatureTour/Game/Game.cairnproject --no-vsync --stats-log before.csv

Judge speed in a release build (make release). A debug build has no optimisation and asserts on, and its numbers say little about the finished game. --validation slows things down too.

For a frame broken down function by function, build with the Tracy profiler, as the Profiling section of AGENTS.md in the repository describes. The Performance window is for seeing at a glance where to look.