Using the Performance service

The Performance service continuously measures runtime performance metrics from the Kanzi Engine and stores them as profiler samples. These metrics are exported to the trace file by the Trace service, where they appear as counter tracks in the Perfetto trace viewer. The service provides built-in metrics for frame timing, rendering statistics, resource usage, and scheduling, and you can register custom metrics through the API.

Platform support

The Performance service builds from sources on all known Kanzi target platforms. All built-in metrics are platform-independent.

How the Performance service works

The Performance service attaches measurement tasks to the Kanzi main loop scheduler. These tasks run at specific stages of the main loop (user stage, render stage) and sample the current values from the Kanzi Engine. Each measurement is stored in a typed profiler (float or integer) with a nanosecond-precision timestamp.

The Performance service defines its own profiling category (PROFILING_CATEGORY_PERFORMANCEINFO) which is always enabled at compile time, independent of the build configuration of the Kanzi Engine. This means performance metrics are included in the trace in all build configurations (Debug, Release, and Profiling).

Built-in metrics

The following metrics are measured automatically when the Performance service is enabled:

Frame timing

  • Frames per second (FPS)

  • App frames per second (requires Application registration)

  • Frametime Total (ms) – Last frame duration

  • Frametime Animation (ms) – Timeline clock duration

Rendering statistics

  • Draw Count – Draw calls per frame (draw + drawIndirect)

  • Drawn Elements – Elements (triangles/vertices) rendered per frame

  • Images – Live GPU image object count

  • Framebuffers – Live GPU framebuffer object count

  • Render Pipelines – Live GPU render pipeline object count

  • Constant Data – Constant data (uniform) updates per frame

  • Render Pass Count – Render pass begin commands per frame

  • Render Pipeline Binds – Render pipeline bind commands per frame

  • Compute Dispatches – Compute dispatch commands per frame

GPU object statistics

Live object counts for all 16 GPU object types tracked by the gfx backend: buffers, images, framebuffers, vertex input states, depth/stencil states, blend states, raster states, samplers, render resource sets, compute resource sets, shaders, compute pipelines, render pipelines, render passes, command buffers, and GPU fences. Each reports current, maximum, lifetime created, and increase/decrease counts.

Note

Rendering statistics and GPU object statistics require the Kanzi graphics statistics layer to be enabled. In Debug builds, the statistics layer is enabled by default. In Release and Profiling builds, you must enable it explicitly by setting GraphicsStatisticEnabled = true in application.cfg, or configuration.graphicsStatisticsEnabled = true in onConfigure().

Without this setting, these metrics remain zero. FPS, frame time, resource counts, and memory usage are not affected and work in all build configurations.

See GraphicsStatisticEnabled in the Kanzi application configuration reference.

Staging buffers

Per-staging-buffer memory usage (used bytes and total size) from the command recorder.

Resource management

  • Resource Count – Total loaded resources

  • Resource CPU Mem – CPU-side memory usage

  • Resource GPU Mem – GPU-side memory usage

  • Resource AcquireQueue Size – Pending resource acquisitions

  • Resource DeployQueue Size – Resources waiting for deployment

  • Resource LoadQueue Size – Resources in loading queue

Animation and scheduling

  • Animations Active Count – Currently active timeline playbacks

  • Animations Total Count – Total timeline playback instances

  • Timers Count – Active timer subscriptions

  • Tasks Total Count – Total recurring task count

Viewing performance metrics in the trace

The performance metrics are primarily consumed through the trace output:

  1. Enable the Performance service (ServicePerformanceEnabled, enabled by default).

  2. Run the application and trigger a trace write. See Using the Profiling Trace service.

  3. Open the trace file in the Perfetto trace viewer. The performance metrics appear as counter tracks.

The perfinfo2 command provides a quick FPS reading from the console.

Trace integration

The Performance service registers a collection task (registry_performanceinfo_profiling) with the Trace service. When a trace is written, all performance profiler samples are exported as counter events on the main thread. In the Perfetto trace viewer, each metric appears as a separate counter track showing the value over time.

Adding custom metrics

You can register custom performance profilers through the Performance service API. Custom profilers support both float and size_t (integer) data types and appear alongside the built-in metrics in the trace output.

To register a custom profiler and sample it periodically:

KanziMonitorModule* module = getKanziMonitorModule(domain);
PerformanceService* perfService = module->getPerformanceService();

// Create a custom float profiler.
auto myProfiler = profilinghelper::createPerformanceInfoProfiler<float>(
    "My Custom Metric",
    kzProfilingGetCategoryRuntimeReference(PROFILING_CATEGORY_PERFORMANCEINFO),
    PROFILING_PERFORMANCEINFO_DEFAULT_BUFFER_SIZE);

// Register it in the Performance service profiler registry.
perfService->getPerformanceInfoProfilerRegistry().registerProfiler(myProfiler);

// Store an initial measurement.
updateMeasureAndStore(myProfilerWithValue, false, initialValue);

// Set up periodic sampling using a main loop timer.
domain->getMainLoopScheduler()->appendTimer(
    kanzi::UserStage,
    kzMakeFixedString("MyPlugin.MeasureMyMetric"),
    kanzi::MainLoopScheduler::TimerRecurrence::Recurring,
    chrono::milliseconds(500),
    bind(myMeasureFunction, placeholders::_1, placeholders::_2,
         &myProfilerWithValue));

The updateMeasureAndStore helper records a new sample with the current timestamp. The main loop timer calls the measurement function at the specified interval, which calls updateMeasureAndStore with the latest value. The profiler samples are automatically included in the trace output through the Performance service collection task.

Reading profiler data

You can query the profiler registry to find profilers by name and read their current values:

KanziMonitorModule* module = getKanziMonitorModule(domain);
PerformanceService* perfService = module->getPerformanceService();
auto& registry = perfService->getPerformanceInfoProfilerRegistry();

// Find a profiler by name.
auto it = std::find_if(
    registry.beginProfilers(), registry.endProfilers(),
    [](AbstractProfilerSharedPtr p) {
        return p->getName() == "My Custom Metric";
    });

if (it != registry.endProfilers())
{
    AbstractProfilerSharedPtr profiler = *it;

    // Read aggregate data (for example, average value at field index 5).
    string fieldName = profiler->getAggregateDataFieldName(5);
    AbstractProfiler::Value value = profiler->getAggregateDataFieldValue(5);

    if (AbstractProfiler::getDataType(value) == AbstractProfiler::DataTypeFloat)
    {
        float floatValue = get<float>(value);
    }
}

This is useful for displaying custom metrics on-screen, logging them, or using them to drive application behavior.

Using performance watchers

Performance watchers let you attach threshold-based triggers to metrics. When a metric crosses the threshold, the watcher automatically executes a command and then deactivates (one-shot behavior). This prevents repeated triggers while the metric stays beyond the threshold.

The primary use case is automated trace capture: when FPS drops below 30, write a trace file.

Adding a watcher

Use the watch add command to create a watcher:

watch add <metric> <below|above> <threshold> <command> [args]

For example:

watch add fps below 30 trace
watch add batches above 500 trace
watch add frametime above 33.3 trace

The watcher evaluates the metric every frame. When the condition is met, it executes the command and deactivates. A deactivated watcher shows as [triggered] in the list.

Viewing available metrics

Use watch metrics to see all available metric names and their current values:

watch metrics

Alias

Description

fps

Frames per second

appfps

Application frames per second

frametime

Frame time total (ms)

animtime

Frame time animation (ms)

batches

Draw count (draw + drawIndirect)

triangles

Drawn elements count

images

Live GPU image object count

framebuffers

Live GPU framebuffer object count

renderpipelines

Live GPU render pipeline object count

constantdata

Constant data updates per frame

renderpasses

Render pass begin commands per frame

renderpipelinebinds

Render pipeline bind commands per frame

computedispatches

Compute dispatch commands per frame

gpu.<type>

Live GPU object count for a specific type (for example, gpu.buffers, gpu.shaders, gpu.samplers). See the full list of 16 types with watch metrics.

texswitches

Alias for images (backward compatible)

fbswitches

Alias for framebuffers (backward compatible)

shaderswitches

Alias for renderpipelines (backward compatible)

uniforms

Alias for constantdata (backward compatible)

resources

Total resource count

cpumem

CPU memory usage (bytes)

gpumem

GPU memory usage (bytes)

animations

Active animation count

Managing watchers

List all watchers and their status:

watch list

Example output:

#1 fps below 30 -> trace [active]
#2 batches above 500 -> trace [triggered]

Remove a specific watcher by its ID:

watch remove 1

Remove all watchers:

watch clear

Re-arm a triggered watcher so it can fire again:

watch reset 2

Example workflow

A typical workflow for capturing a trace when FPS drops:

watch add fps below 30 trace       Add the watcher
fpslimit 15                         Simulate low FPS (for testing)
watch list                          Verify watcher triggered
fpslimit 0                          Remove the FPS limit
watch reset 1                       Re-arm the watcher for next time

When a watcher triggers, it logs the event and the command output at Info level.

Available commands

Command

Description

perfinfo2

Shows Kanzi Monitor Performance Service information.

watch

Manages performance watchers. Usage: watch [add|list|remove|clear|reset|metrics]

The perfinfo2 command displays the current FPS as measured by the Performance service.

Note

The built-in perfinfo command (from the Command Processor service) shows the last frame duration in milliseconds. Both commands warn if SuspendWhenIdle is enabled, as it affects timing accuracy.

See also

Using the Profiling Trace service

Using the Overwatch service

Configuring Kanzi Monitor