# How the Godot Engine Profiler Tracks Performance Metrics and Frame Timing

> Discover how the Godot Engine profiler tracks performance metrics and frame timing using its singleton, abstraction, and remote debugger for seamless editor integration.

- Repository: [Godot Engine/godot](https://github.com/godotengine/godot)
- Tags: internals
- Published: 2026-02-26

---

**The Godot Engine profiler tracks performance metrics and frame timing through a layered architecture involving the `Performance` singleton for raw data collection, the `EngineProfiler` abstraction for extensibility, and the `RemoteDebugger` for transmitting data to the editor UI.**

The `godotengine/godot` repository implements a sophisticated profiling system that captures real-time performance data from the engine core and streams it to the editor for visualization. Understanding how the profiler tracks performance metrics and frame timing is essential for optimizing games and identifying bottlenecks in both development and production environments.

## Core Data Collection via the Performance Singleton

The foundation of Godot's profiling system rests on the `Performance` singleton, a globally accessible object instantiated at engine startup in [`main/performance.cpp`](https://github.com/godotengine/godot/blob/main/main/performance.cpp).

### Frame Timing Updates in the Main Loop

Every frame, the main loop in [`main/main.cpp`](https://github.com/godotengine/godot/blob/main/main/main.cpp) (lines 18–21) captures timing data and pushes it to the `Performance` singleton:

```cpp
performance->set_process_time( USEC_TO_SEC(process_max) );
performance->set_physics_process_time( USEC_TO_SEC(physics_process_max) );
performance->set_navigation_process_time( USEC_TO_SEC(navigation_process_max) );

```

These calls store the duration of process, physics, and navigation steps as seconds, enabling the profiler to track frame timing with microsecond precision.

### The Monitor Enum and Built-in Metrics

The `Performance` class defines a comprehensive `Monitor` enum in [`main/performance.h`](https://github.com/godotengine/godot/blob/main/main/performance.h) that categorizes metrics into timers, memory usage, object counts, and physics statistics. Built-in monitors include:

- **Time monitors**: `TIME_PROCESS`, `TIME_PHYSICS_PROCESS`, `TIME_NAVIGATION_PROCESS`
- **Memory monitors**: `MEMORY_STATIC`, `MEMORY_DYNAMIC`, `MEMORY_MESSAGE_BUFFER`
- **Object monitors**: `OBJECT_COUNT`, `OBJECT_RESOURCE_COUNT`, `OBJECT_NODE_COUNT`
- **Physics monitors**: `PHYSICS_2D_ACTIVE_OBJECTS`, `PHYSICS_3D_ACTIVE_OBJECTS`

Values are retrieved via `Performance::get_monitor(Monitor p_monitor)`, which the profiler calls to aggregate data for transmission.

### Custom Monitors for Extended Tracking

Developers can extend the profiler's capabilities through `Performance::add_custom_monitor`. This method accepts a name, a `Callable` that returns the metric value, and a `MonitorType` classification:

```cpp
Performance::get_singleton()->add_custom_monitor(
    "live_particles",
    Callable(this, "get_live_particle_count"),
    PackedVector<int>(),
    Performance::MONITOR_TYPE_QUANTITY);

```

Custom monitors automatically appear in the editor's performance panel when the remote debugger is active.

## The EngineProfiler Abstraction Layer

Godot decouples data collection from profiling implementations through the `EngineProfiler` interface defined in [`core/debugger/engine_profiler.h`](https://github.com/godotengine/godot/blob/main/core/debugger/engine_profiler.h).

### Virtual Interface Methods

The `EngineProfiler` class specifies three virtual methods that concrete implementations must override:

```cpp
virtual void toggle(bool p_enable, const Array &p_opts);
virtual void add(const Array &p_data);
virtual void tick(double p_frame_time,
                  double p_process_time,
                  double p_physics_time,
                  double p_physics_frame_time);

```

The `tick` method receives frame timing data every frame, while `toggle` handles activation states and `add` receives arbitrary profiler-specific data arrays.

### Registration with EngineDebugger

Profiler implementations register with the global `EngineDebugger` singleton via `EngineProfiler::bind` and unregister with `unbind`. The `EngineDebugger` maintains a list of active profilers and invokes their `tick` methods once per frame through `EngineDebugger::profiler_tick`, which originates from the main loop after frame timings are stored.

## Remote Debugger Implementation

The `RemoteDebugger` in [`core/debugger/remote_debugger.cpp`](https://github.com/godotengine/godot/blob/main/core/debugger/remote_debugger.cpp) implements the bridge between the running game and the editor's profiler UI.

### PerformanceProfiler Class

Nested within `RemoteDebugger`, the `PerformanceProfiler` class inherits from `EngineProfiler` and overrides the virtual interface to capture and transmit metrics. It maintains internal state to track when data was last sent and what custom monitors were previously registered.

### Data Transmission Protocol

When active, `PerformanceProfiler::tick` executes **once per second** (throttled via `last_perf_time` comparison). During each execution:

1. It retrieves current custom monitor names and types from the `Performance` singleton
2. If the custom monitor list changed, it transmits a `"performance:profile_names"` message to the editor
3. It builds an array containing all built-in monitor values via `Performance::get_monitor(i)` plus all custom monitor values
4. It transmits the array via `EngineDebugger::send_message("performance:profile_frame", arr)`

This protocol ensures minimal overhead during gameplay while providing real-time updates in the editor.

### Throttling and Frame Aggregation

The one-second throttling mechanism prevents network flooding and maintains game performance. The `last_perf_time` variable tracks the last transmission timestamp, and the `tick` method returns early if less than one second has elapsed since the last send.

## Editor-Side Visualization

The editor receives profiler data through `EditorPerformanceProfiler` implemented in [`editor/debugger/editor_performance_profiler.cpp`](https://github.com/godotengine/godot/blob/main/editor/debugger/editor_performance_profiler.cpp).

### EditorPerformanceProfiler UI

This class manages the **Debugger → Performance** panel in the Godot editor. It maintains a hash map of monitor histories (`List<float> history`) indexed by monitor name. When a `"performance:profile_frame"` message arrives, it appends new values to the appropriate history lists and triggers a UI redraw.

### Graph Rendering and History

The `_monitor_draw` method handles visualization:

- It normalizes values based on the monitor's `MonitorType` (quantity, memory, time, percentage)
- It formats labels via `_format_label` to display appropriate units (bytes, milliseconds, percentages)
- It draws the historical data as line graphs with configurable time windows

The UI also provides a **reset** button (calling `EditorPerformanceProfiler::reset`) to clear histories and a **marker** system to flag specific frames for inspection.

## Implementing Custom Performance Monitors

Developers can extend the profiler to track application-specific metrics using the `Performance` singleton's custom monitor API.

### GDScript Example

```gdscript
extends Node

var enemy_count := 0

func _ready():
    Performance.add_custom_monitor(
        "gameplay/enemy_count",
        Callable(self, "_get_enemy_count"),
        [],
        Performance.MONITOR_TYPE_QUANTITY
    )

func _get_enemy_count() -> int:
    return enemy_count

```

### C++ Module Example

```cpp
// In your module's initialization
void MyModule::initialize() {
    Performance *perf = Performance::get_singleton();
    perf->add_custom_monitor(
        "my_module/active_tasks",
        callable_mp(this, &MyModule::get_active_task_count),
        Vector<Variant>(),
        Performance::MONITOR_TYPE_QUANTITY
    );
}

```

Custom monitors appear automatically in the editor's performance panel under the "Custom" category when the remote debugger is connected.

## Summary

- The **Performance singleton** ([`main/performance.cpp`](https://github.com/godotengine/godot/blob/main/main/performance.cpp)) collects frame timing data every frame in [`main/main.cpp`](https://github.com/godotengine/godot/blob/main/main/main.cpp) and maintains built-in monitors for memory, objects, and physics.
- The **EngineProfiler** abstraction ([`core/debugger/engine_profiler.h`](https://github.com/godotengine/godot/blob/main/core/debugger/engine_profiler.h)) defines the interface for profiler implementations, with `tick` called every frame to receive timing data.
- The **RemoteDebugger** ([`core/debugger/remote_debugger.cpp`](https://github.com/godotengine/godot/blob/main/core/debugger/remote_debugger.cpp)) transmits performance data to the editor once per second via the `PerformanceProfiler` nested class.
- The **EditorPerformanceProfiler** ([`editor/debugger/editor_performance_profiler.cpp`](https://github.com/godotengine/godot/blob/main/editor/debugger/editor_performance_profiler.cpp)) visualizes historical data as graphs in the Debugger → Performance panel.
- **Custom monitors** can be added via `Performance::add_custom_monitor` to expose application-specific metrics in the editor.

## Frequently Asked Questions

### How does the Godot profiler collect frame timing data?

The profiler collects frame timing data through the `Performance` singleton, which receives updates every frame from the main loop in [`main/main.cpp`](https://github.com/godotengine/godot/blob/main/main/main.cpp). The main loop calls `set_process_time`, `set_physics_process_time`, and `set_navigation_process_time` with microsecond-precision measurements converted to seconds, storing these values for later retrieval by profiler implementations.

### What is the difference between the Performance singleton and EngineProfiler?

The **Performance singleton** ([`main/performance.h`](https://github.com/godotengine/godot/blob/main/main/performance.h)) is a core engine component that stores raw metric values and frame timings, acting as a centralized data repository. **EngineProfiler** ([`core/debugger/engine_profiler.h`](https://github.com/godotengine/godot/blob/main/core/debugger/engine_profiler.h)) is an abstract interface that defines how profiling data is consumed; concrete implementations like `RemoteDebugger::PerformanceProfiler` register with the engine debugger to receive periodic updates from the Performance singleton and transmit them to the editor.

### How can I add custom metrics to the Godot profiler?

You can add custom metrics by calling `Performance::add_custom_monitor` from GDScript or C++, passing a unique name, a `Callable` that returns the metric value, and a `MonitorType` classification (quantity, memory, time, or percentage). Once registered, these custom monitors automatically appear in the editor's Performance panel when remote debugging is active, updating once per second alongside built-in metrics.

### Why does the profiler data update only once per second in the editor?

The profiler updates once per second to minimize network overhead and runtime performance impact. The `RemoteDebugger::PerformanceProfiler::tick` method implements throttling by tracking the last transmission time (`last_perf_time`) and returning early if less than one second has elapsed. This aggregation ensures that performance monitoring does not interfere with the game's frame rate while still providing sufficiently granular data for optimization work.