Manuals
Manuals




Profiling

Defold includes profiling tools integrated with the engine and build pipeline. They help find performance, memory, and resource-usage problems. Runtime profiling data can be consumed by several facilities:

  • The basic profiler and in-game visual profiler are available on all platforms.
  • The Remotery profiler and interactive web frame profiler are available on desktop and mobile platforms.
  • macOS debug builds using Metal can save GPU traces for inspection in Xcode.
  • HTML5 builds can publish Defold scopes to the browser’s Web Performance API.

The Profiler setting in the App Manifest controls whether profiler code is linked into a build. Debug Only is the default, None excludes it, and Always includes it in both debug and release builds. The profiler settings in game.project control runtime behavior but do not link excluded profiler code back into a build. In particular, Track CPU controls CPU usage sampling; it is separate from the App Manifest choice.

The runtime visual profiler

Builds that include profiler support feature a runtime visual profiler that displays live information overlaid on the running application:

function on_reload(self)
    -- Toggle the visual profiler on hot reload.
    profiler.enable_ui(true)
end

Visual profiler

The visual profiler provides a number of different function that can be used to change the way the visual profiler presents its data:


profiler.set_ui_mode()
profiler.set_ui_view_mode()
profiler.view_recorded_frame()

Refer to a the profiler API reference for more information about the profiler functions.

The web profiler

While running a desktop or mobile build that includes profiler support, interactive frame and resource profilers can be accessed through a browser.

Remotery frame profiler

The Frame profiler allows you to sample your game while it is running and analyze individual frames in detail. To access the profiler:

  1. Start your game on your target device.
  2. Select Debug ▸ Open Web Profiler menu.

The frame profiler is divided into several sections that all give different views into the running game. Press the Pause button in the top right corner to temporarily stop the profiler from updating the views.

Web profiler

When you use multiple targets simultaneously, you can manually switch between them by changing the Connection Address field at the top of the page to match the Remotery profiler URL shown in the console when the target started:

INFO:ENGINE: Defold Engine 1.3.4 (80b1b73)
INFO:DLIB: Initialized Remotery (ws://127.0.0.1:17815/rmt)
INFO:ENGINE: Loading data from: build/default
Sample Timeline
The Sample Timeline will show the frames of data captured in the engine, one horizontal timeline per Thread. Main is the main thread where all of the game logic and most of the engine code is run. Remotery is for the profiler itself and Sound is for the sound mixing and playback thread. You can zoom in and out (using the mouse wheel) and select individual frames to see the details of a frame in the Frame Data view.

Sample Timeline

Frame Data
The Frame Data view is a table where all data for the currently selected frame is broken down into detail. You can view how many milliseconds are spent in each engine scope.

Frame data

Global Properties
The Global Properties view shows a table of counters. They make it is easy to, for instance, track the number of draw calls or the number of components of a certain type.

Global Properties

The LuaMem value is the amount of memory in kilobytes used by the Lua VM as reported by the Lua garbage collector. Memory is the amount of memory in kilobytes used by the engine.

The Max Sample Count setting limits the number of profiler samples recorded per thread per frame. If the profiler reports that the limit was exceeded, first check native-extension profiling code for an unmatched scope begin/end pair. Raise the cap only when a legitimate frame contains more scopes than the configured limit.

Resource profiler

The Resource profiler allows you to inspect your game while it is running and analyze resource use in detail. To access the profiler:

  1. Start a debug build of your game on your target device.
  2. Open the engine service address in a browser: http://<address>:<port>/. Use the address and port reported by the running engine in the console.

The resource profiler is divided into 2 sections, one showing a hierarchical view of the collections, game objects and components currently instantiated in your game, and the other showing all currently loaded resources.

Resource profiler

Collection view
The collection view shows hierarchical list of all game objects and components currently instantiated in the game and from which collection they originate. This is a very useful tool when you need to dig into and understand what you have instanced in your game at any given time and from where the objects originate.
Resources view
The resources view shows all resources currently loaded into memory, their size and the number of references to each resource. This is useful when optimizing memory usage in your application when you need to understand what is loaded into memory at any given time.

Resource loading times

You can also inspect how long individual resources take to load. Enable Resource → Profile in game.project before launching the game. In the text view, this setting is:

[resource]
profile = 1

The setting is disabled by default. Restart the game after enabling it to measure startup loads. Each completed or failed load writes an INFO-level log entry with the resource path, total time, and times for preload, read, create, post-create, queue and wait stages. Cache hits do not produce new loading records.

In the resource profiler, click Capture, then Show load times to reveal the timing columns. Capture refreshes the view of retained measurements; it does not enable profiling or measure earlier loads retroactively.

Column Meaning
Load (ms) Total elapsed time for the initial load, including dependencies, post-create work, queueing and waiting.
Preload (ms) Time spent preparing the resource in its preload callback.
Read (ms) Time spent reading the resource data.
Create (ms) Time spent creating the runtime resource in its create callback.

Showing load times sorts the table by total load time, with the slowest resources first. Click a timing column heading to sort by that stage; click it again to reverse the order. The bars in the Load column show each total relative to the slowest visible resource. Click Hide load times to hide the columns again.

Load and callback times include any nested dependency work. Do not sum totals across resources to calculate startup time, since the same work can appear in more than one resource’s measurement. The Preload, Read and Create columns also do not account for all of the total load time; the log provides the additional stages.

An em dash (—) means no completed measurement is available, for example because profiling was disabled or the initial load has not finished. A measured zero is displayed as 0.000. Measurements describe the initial load: cache hits and hot reloads leave them unchanged, and unloading a resource removes its retained measurement.

Metal GPU trace capture

The engine HTTP service can capture Metal GPU commands to a .gputrace document on macOS. Open the completed trace in Xcode to inspect the captured frames. This feature is built into the debug engine.

Launch and check support

Create a macOS Debug bundle with the native Metal graphics backend included. If you use an App Manifest, select a Graphics (macOS) option that includes Metal; see graphics backends.

Launch the bundle’s executable from a terminal with METAL_CAPTURE_ENABLED=1 and select the Metal adapter. Replace the bundle path and executable name in this example:

METAL_CAPTURE_ENABLED=1 \
    "/absolute/path/MyGame.app/Contents/MacOS/MyGame" --graphics-adapter=metal

The environment variable must be set when the engine starts. Setting it only in a terminal used for HTTP requests does not enable capture in an already running engine.

In a second terminal, use the engine service port printed in the log. The following examples use curl and jq:

ENGINE_URL="http://127.0.0.1:<port>"
curl -fsS "$ENGINE_URL/openapi.json" | jq '.paths["/profiler/gputrace"]'
curl -sS -i "$ENGINE_URL/profiler/gputrace"

The route is registered only on macOS. A null entry in the OpenAPI document means this engine does not expose it. Even when the route is present, capture support depends on the graphics adapter and launch environment. GET /profiler/gputrace returns HTTP 501 with error.code: "gputrace_unsupported" when Metal capture is unavailable. Check that the engine is using Metal and was launched with METAL_CAPTURE_ENABLED=1 before proceeding. A supported request returns HTTP 200 with capture status.

Capture and inspect frames

Choose a writable absolute output path ending in .gputrace, with an existing parent directory and no trace already at that path. The path is on the computer running the engine. For a local engine, this example creates a fresh temporary directory and requests one frame:

GPU_TRACE_DIR=$(mktemp -d /tmp/defold-gputrace.XXXXXX)
GPU_TRACE_PATH="$GPU_TRACE_DIR/frame.gputrace"
GPU_TRACE_REQUEST=$(jq -n --arg path "$GPU_TRACE_PATH" '{path: $path, frames: 1}')
curl -fsS -X POST \
    -H 'Content-Type: application/json' \
    --data "$GPU_TRACE_REQUEST" \
    "$ENGINE_URL/profiler/gputrace" | jq

frames is optional, defaults to 1, and accepts integers from 1 to 10000. Capture starts before the next rendered frame and stops after the requested number of complete frames have been submitted. Keep the game rendering while waiting. Only one capture can be pending or active; another start request returns HTTP 409 with error.code: "gputrace_active".

POST returns HTTP 200 with ok: true and data.state: "pending" when the capture is scheduled. Capture completion is reported by later status requests. Repeat the status request until data.state becomes complete, canceled or failed:

curl -fsS "$ENGINE_URL/profiler/gputrace" | jq
data.state Meaning
idle No capture has been scheduled.
pending Capture is waiting for rendering to begin.
capturing Capture is active; data.frames_captured reports completed frames.
complete Capture has finished and the trace can be opened.
canceled A pending capture was canceled before it started.
failed Capture could not start; inspect data.error for the native error.

GET describes the current or most recent capture. Check that data.path matches your requested path. A failed capture still returns HTTP 200 with ok: true; inspect data.state and data.error to determine the result. For an automated client, poll with a delay and a timeout.

After the state is complete, open the trace in Xcode on the engine’s Mac:

open -a Xcode "$GPU_TRACE_PATH"

The HTTP endpoint returns status; the trace is saved at data.path on the engine host. For a remote engine, retrieve the completed document from that host before opening it locally.

Cancel or stop a capture

To cancel a pending capture or finish an active capture early:

curl -fsS -X DELETE "$ENGINE_URL/profiler/gputrace" | jq

A pending capture changes to canceled. For an active capture, data.stop_requested becomes true, and capture finishes after the current rendered frame is submitted. Continue polling GET until it reaches a terminal state. DELETE leaves any trace already written on disk. If no capture is pending or active, it returns HTTP 409 with error.code: "gputrace_inactive".

HTML5 browser performance timeline

HTML5 uses the Web Performance API instead of Remotery for its browser timeline. To record Defold scopes:

  1. Make sure the selected App Manifest profiler mode includes profiler support in the build variant you are running.
  2. Enable Performance Timeline Enabled (profiler.performance_timeline_enabled) in game.project.
  3. Launch the HTML5 build and open the browser developer tools.
  4. Record a session in the browser’s Performance panel and inspect the Defold scopes in the resulting timeline.

This browser timeline is separate from both the in-game visual profiler and the interactive Remotery web profiler.

Build reports

When bundling your game there is an option to create a build report. This is very useful to get a grip on the size of all the assets that are part of your game bundle. Simply check the Generate build report checkbox when bundling the game.

build report

The builder will produce a file called report.html alongside the game bundle. Open the file in a web browser to inspect the report:

build report

The Overview gives an over all visual breakdown of the project size based on resource type.

Resources shows a detailed list of resources that you can sort based on size, compression ratio, encryption, type and directory name. Use the “search” field to filter the resource entries displayed.

The Structure section shows sizes based on how resources are organized in the project file structure. Entries are color coded from green (light) to blue (heavy) according to the relative size of the file and directory content.

External tools

In addition to the built-in tools, there is a wide range of free high quality tracing and profiling tools available. Here is a selection:

ProFi (Lua)
We do not ship any built-in Lua profiler but there are external libraries that are easy enough to use. To find where your scripts spend time, either insert time measures in your code yourself, or use a Lua profiling library like ProFi.

Note that pure Lua profilers add quite a lot of overhead with each hook they install. For this reason you should be a bit wary of the timing profiles you get from such a tool. Counting profiles are accurate enough though.

Instruments (macOS and iOS)
This is a performance analyzer and visualizer that is part of Xcode. It allows you to trace and inspect the behavior of one or more apps or processes, examine device specific features (like Wi-Fi and Bluetooth) and much more.

instruments

OpenGL profiler (macOS)
Part of the package “Additional Tools for Xcode” that you can download from Apple (select Xcode ▸ Open Developer Tool ▸ More Developer Tools... in the Xcode menu).

This tool allows you to inspect a running Defold application and see how it uses OpenGL. It allows you to do traces of OpenGL function calls, set breakpoints on OpenGL functions, investigate application resources (textures, programs, shaders etc), look at buffer contents, and check other aspects of the OpenGL state.

opengl profiler

Android Profiler (Android)
https://developer.android.com/studio/profile/android-profiler.html

A set of profiling tools that captures real-time data of your game’s CPU, memory, and network activity. You can perform sample-based method tracing of code execution, capture heap dumps, view memory allocations, and inspect the details of network-transmitted files. Using the tool requires that you set android:debuggable="true" in AndroidManifest.xml.

android profiler

Note: Since Android Studio 4.1 it is also possible to run the profiling tools without starting Android Studio.

Graphics API Debugger (Android)
https://github.com/google/gapid

This is a collection of tools that allows you to inspect, tweak and replay calls from an application to a graphics driver. To use the tool requires that you set android:debuggable="true" in AndroidManifest.xml.

graphics api debugger