DEMIENGINEDOCUMENTATION Everything is free
Project & file formats · Start here

What budgets measure and how they help you ship.

Performance budgets

A performance budget is a number you commit to: “this game will use at most this much”. DemiEngine does not enforce it and never changes quality to hit it. It gives you a measuring stick.

What problem this solves

“It runs fine on my machine” is not a plan. Budgets turn feel into data:

  • A level designer knows the triangle cap before placing the 400th crate.
  • A CI job can fail when a change doubles draw calls.
  • A qualification run has a declared intent to compare against.

Without budgets, regressions hide until a player complains on a low-end phone.

Declare budgets

"performance_budgets": {
  "maximum_frame_ms": 16.7,
  "maximum_draw_calls": 800,
  "maximum_resident_assets": 256,
  "maximum_visible_instances": 4000,
  "maximum_unique_meshes": 120,
  "maximum_triangles": 500000,
  "maximum_texture_memory_mb": 256,
  "maximum_lights": 8,
  "maximum_shadow_lights": 2,
  "maximum_transparent_draws": 200
}

What each number means

FieldUnitWhat it measures
maximum_frame_msmillisecondsWhole frame time. 16.7 ms is 60 FPS; 8.3 ms is 120 FPS.
maximum_draw_callscountRender submissions per frame. Each is CPU + driver overhead.
maximum_resident_assetscountAssets kept loaded at once. Drives memory and load stalls.
maximum_visible_instancescountDrawn model instances after culling.
maximum_unique_meshescountDistinct meshes submitted. High values break batching.
maximum_trianglescountTriangles submitted per frame.
maximum_texture_memory_mbMiBGPU texture memory.
maximum_lightscountLights affecting the frame.
maximum_shadow_lightscountLights allowed to cast shadows — the expensive ones.
maximum_transparent_drawscountTransparent/unsorted draws, which are hard to batch.

Set only the fields you care about. Missing fields are simply not budgeted.

How to use the numbers

1. Pick targets per platform

Desktop 1080p and Android mid-range are different games. Use different project files or different values in CI profiles. A starting point for a 60 FPS 2D game:

"performance_budgets": {
  "maximum_frame_ms": 16.7,
  "maximum_draw_calls": 300,
  "maximum_resident_assets": 128,
  "maximum_texture_memory_mb": 128
}

2. Measure against them

demi asset budget games/my_game/demi.project.json --platform linux

This reports current usage versus each declared limit, so you see headroom or overflow before shipping.

3. Profile the overage

demi run linux \
  --profiler --profile-report report.csv

Budgets tell you that you overflowed; the profiler tells you where. See Replay & profiler.

4. Put it in the pipeline

demi asset budget project/demi.project.json --platform android
# fail the job when the command reports overflow

Combine with a deterministic scene load and a profile CSV so every merge request has a number to compare.

What budgets do not do

  • They never lower quality, hide objects, or change resolution.
  • They never block a build. Exceeding one is a measurement, not a compile error.
  • They are project data, not device auto-detection. You decide the intent per target.

Choosing sensible caps

  1. Build the heaviest scene you expect to ship.
  2. Measure it on your weakest target device.
  3. Leave 20–30% headroom for polish and late content.
  4. Round the number and write it down in the project file.

Budgets are most useful early. Setting them after the game is done only documents the overrun.