Profiling Guide — Tracy Zones in MitiruEngine
This guide explains how to enable Tracy profiling in MitiruEngine, capture a
live session, and interpret zones added to the hot path. The macro set lives in
include/mitiru/debug/TracyZones.hpp;
all zone macros are no-ops when Tracy is not compiled in, so instrumented
shipping builds carry zero overhead.
Enabling Tracy
Tracy support is auto-detected by the root CMakeLists.txt. The build enables
Tracy only when the vendored source is present:
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/external/tracy/public/TracyClient.cpp")
option(TRACY_ENABLE "" ON)
add_subdirectory(external/tracy)
target_compile_definitions(mitiru ${MITIRU_TARGET_SCOPE} MITIRU_HAS_TRACY=1)
target_link_libraries(mitiru ${MITIRU_TARGET_SCOPE} TracyClient)
endif()
To enable Tracy for a local capture session:
- Place a working Tracy checkout under
external/tracy/(the directory must containpublic/TracyClient.cpp). Most users vendor the tracy repository directly. - Re-run CMake configuration. The status output should say
Tracy found - enabling profiler. - Rebuild —
MITIRU_HAS_TRACY=1is now defined for engine targets, and the zone macros expand to realZoneScopedN/ZoneScopedNCinstances.
If external/tracy/ is absent, CMake reports
Tracy not found - profiling macros will be no-ops and the build proceeds
with zero overhead.
Capturing a Session
- Launch the Tracy GUI (
tracyorTracy.exe) on the host machine. - Start the MitiruEngine application built with
MITIRU_HAS_TRACY=1. - In Tracy, click Connect and select the running process (Tracy listens
on
127.0.0.1:8086by default). - Drive the application through the scenario you want to profile — the timeline view will populate with zones as they execute.
- Stop the capture and save it to
.tracyfor later analysis.
Known Zones
The following named zones are emitted by engine code. All zones expand to
no-ops when MITIRU_HAS_TRACY is undefined.
| Zone name | Source | Macro used | Notes |
|---|---|---|---|
Engine::Frame |
include/mitiru/core/detail/Engine_Frame.hpp:32 |
MITIRU_ZONE_NAMED |
外側のフレームゾーン。tickOneFrame() 全体を包む。子ゾーンはすべてこの下に並ぶ。 |
Engine::Input |
include/mitiru/core/detail/Engine_Frame.hpp:54 |
MITIRU_ZONE_NAMED |
m_window->pollEvents() と注入入力の適用。Emscripten 終了判定もここ。 |
Engine::MouseScaling |
include/mitiru/core/detail/Engine_Frame.hpp:83 |
MITIRU_ZONE_NAMED |
Win32 RAW マウス座標を Screen 論理座標へ毎フレームスケーリングする処理。 |
Engine::FixedUpdate |
include/mitiru/core/detail/Engine_Frame.hpp:125 |
MITIRU_ZONE_NAMED |
固定タイムステップアキュムレータループ。game.update() と現在シーンの onUpdate() を含む。 |
Engine::Render |
include/mitiru/core/detail/Engine_Frame.hpp:161 |
MITIRU_ZONE_NAMED |
Screen::clear から game.draw()、Scene::onDraw() までの 2D/3D 描画蓄積。device->beginFrame() も含む。 |
Engine::Present |
include/mitiru/core/detail/Engine_Frame.hpp:206 |
MITIRU_ZONE_NAMED |
Screen::present() と PostFX、3D レンダラーの finalizeFrame()。GPU コマンド送信が中心。 |
Engine::CefComposite |
include/mitiru/core/detail/Engine_Frame.hpp:245 |
MITIRU_ZONE_NAMED |
CEF UI レイヤーのメッセージループ処理、入力転送、テクスチャアップロード、バックバッファ合成。Mode B 専用。 |
Engine::AutoCapture |
include/mitiru/core/detail/Engine_Frame.hpp:271 |
MITIRU_ZONE_NAMED |
自律テストモードのスクリーンショット保存と device->endFrame()。通常運用では endFrame() のみで軽量。 |
Engine::HttpPoll |
include/mitiru/core/detail/Engine_Frame.hpp:311 |
MITIRU_ZONE_NAMED |
HTTP API サーバーのポーリングと、vsync OFF 時のフレームレートキャップ用 sleep_for。 |
SmallFunction::invoke |
include/mitiru/time/detail/SmallFunction.hpp:79 |
MITIRU_ZONE_NAMED |
Wraps every call of the type-erased callable. Hot path. |
Sequence::action |
include/mitiru/time/Sequence.hpp:75 |
MITIRU_ZONE_NAMED |
One zone per action step fired inside Sequence::tick. |
Search the codebase for additional zones with:
rg "MITIRU_ZONE" include/mitiru/
The full macro set is documented in
include/mitiru/debug/TracyZones.hpp
and includes:
MITIRU_ZONE— anonymous scoped zone (function name auto-captured).MITIRU_ZONE_NAMED(name)— named scoped zone;namemust be a compile-timeconst char*literal.MITIRU_ZONE_COLOR(name, color)— named zone with an explicit color.MITIRU_ZONE_RENDER / PHYSICS / AUDIO / SCRIPT / UI— category shortcuts using the colors declared inmitiru::debug::ZoneColors.MITIRU_FRAME_MARK— frame boundary marker.MITIRU_PLOT(name, value)— numeric plot.MITIRU_MESSAGE(text)— annotation message.
SBO vs Heap Profile Recipe
mitiru::time::detail::SmallFunction uses a 48-byte inline buffer (SBO);
captures larger than 48 bytes fall back to heap allocation. The
SmallFunction::invoke zone exposes the per-call cost of both paths in real
gameplay loads.
- Run the game with Tracy connected; exercise the system that allocates the
callables you care about (e.g.
Sequencechains). - In Tracy, filter the Find Zone panel to
SmallFunction::invoke. - Inspect the Histogram and Time columns:
- Average µs near the synthetic SBO baseline → captures are inline.
- Average µs near the synthetic heap baseline → captures are spilling.
- Wide histogram tail → mixed populations; group by parent zone to identify which call sites use heap captures.
- Cross-reference with the Catch2 benchmark numbers produced by
tests/mitiru/TestSmallFunctionBench.cpp. The benchmark records SBO vs heap timings on synthetic captures; use those as the calibration baseline when interpreting in-app Tracy averages. - To zoom in on a specific timeline section, parent the
SmallFunction::invokezone underSequence::action(Tracy displays the call hierarchy automatically), then inspect just the action-driven invocations.
If average µs for SmallFunction::invoke significantly exceeds the SBO
benchmark, capture the offending call sites’ captures and consider:
- Shrinking the capture below 48 bytes (e.g. capture indices into a shared context object instead of large values directly).
- Splitting the callable into a smaller closure plus a lookup.
Interpreting Engine_Frame Zones
Engine::Frame は外側で 1 フレーム全体を包むので、Tracy の Statistics ビューで
これを基準に他ゾーンの相対比率を見るのがいちばん速い。以下、各ゾーンの定性的な
期待値を記す。実機ベースラインの数値は未計測 (本ドキュメント末尾の Known
Limitations 参照) のため、ここでは「どこに重点的に時間を使っているはずか」と
いう構造的な見方だけを示す。
Engine::Render— 通常はフレーム時間の大部分を占める。game.draw()と Scene のonDraw()がここに集約されるので、コンテンツが重ければ最初に膨らむ のはここ。Tracy で子コール (drawSprite 等) が出ない場合はMITIRU_ZONE_NAMEDを計装したい draw メソッドに足すと深掘りできる。Engine::Present— GPU コマンド送信と PostFX チェーン。vsync ON のとき はScreen::present()ないしdevice->endFrame()で vsync 待ちが入るため 実時間が膨らみがちだが、これは「GPU 待ち時間」であってエンジン側の処理コスト ではない。CPU の純粋なコストはEngine::Renderを見るほうが正確。Engine::FixedUpdate— 固定タイムステップでは複数 step が走り得る (例: 144Hz vsync + 60Hz update)。スパイラルオブデス防止でkMaxFrameSkipに クリップされている。長フレームの後に幅広いゾーンを見たら累積アキュムレータ 起因。Engine::Input—pollEventsが支配的。ネイティブ window のキューが 大きいフレームではここが伸びる。Emscripten では shouldClose 判定もここで 実行される。Engine::MouseScaling— Win32 のみで意味のある軽量フェーズ。ほぼ常に サブマイクロ秒オーダーで完結する。dynamic_cast<Win32Window*>が支配的なら これは設計上正常 (代替手段は ABI 変更を伴うため温存)。Engine::CefComposite— Mode B 専用。m_cefContext.isInitialized()が false の場合は早期 return するため、Mode A 純ネイティブ運用ではほぼ ゼロ。Mode B でも CEF が dirty frame を持たない静的画面ではアップロードが スキップされ軽量。Engine::AutoCapture— 自律テストモード以外ではdevice->endFrame()の呼び出しだけ。通常運用ではEngine::Presentと並ぶ軽量ゾーン。Engine::HttpPoll—m_httpServerが動いていない or アイドルなら ほぼゼロ。vsync OFF +targetFps>0のフレームレートキャップが効くとsleep_forの待機時間がここに乗るので、Tracy 上では「Engine::HttpPoll が 長い = 余裕で targetFps を達成している」と読める。
子ゾーンの合計は Engine::Frame よりわずかに小さくなる (helper 関数呼び出し
の薄いシーケンサ部分が外側に残るため)。乖離が大きい場合は計装されていない
処理が tickOneFrame() 内に紛れていないか確認する。
Adding New Zones
新規ホットパスを計装したいときは、対象関数の先頭で MITIRU_ZONE_NAMED を呼ぶ
だけでよい。MITIRU_HAS_TRACY が未定義のビルドでは展開結果が ((void)0) に
なるため、リリースビルドへの混入も気にしなくてよい。
#include "mitiru/debug/TracyZones.hpp"
void MySystem::update(float dt) {
MITIRU_ZONE_NAMED("MySystem::update");
// ... per-frame work ...
}
カテゴリ別の色分けをしたい場合は MITIRU_ZONE_RENDER / MITIRU_ZONE_PHYSICS
/ MITIRU_ZONE_AUDIO / MITIRU_ZONE_SCRIPT / MITIRU_ZONE_UI のいずれかを
使うと、mitiru::debug::ZoneColors に定義された色が自動で割り当てられる。
完全に独自色にしたい場合は MITIRU_ZONE_COLOR("Name", 0xRRGGBB) を使う。
命名規約は Subsystem::Operation を推奨。Tracy の Statistics ビューで
Engine::* や MySystem::* でフィルタしやすくするため。
Known Limitations
- 本番ハードウェアでの計測は未実施。エンジンコアの主要フェーズは 計装済みで no-op gate も検証済みだが、実機キャプチャによるベースライン 数値はまだ公開できる形に揃っていない。
- GPU 側のゾーンは未計装。Tracy には GPU タイムスタンプ機構があるが、 現状の DX11/DX12/Vulkan/OpenGL バックエンドにはまだ統合されていない。 GPU 時間を見たい場合は当面 PIX / RenderDoc / NSight などのベンダーツールを 併用する。
- Mode B (CEF) のレンダリングは外部プロセスで動く。
Engine::CefCompositeはホスト側のアップロード/コンポジットしか見えないので、CEF サブプロセス内の HTML レイアウトコストはこの計装からは可視化できない。Chrome DevTools の Performance パネルで別途プロファイルすること。
See Also
include/mitiru/debug/TracyZones.hpp— full macro reference and category colors.include/mitiru/debug/TracyIntegration.hpp— engine-levelMITIRU_PROFILE_*helpers (frame markers, plots).tests/mitiru/TestSmallFunctionBench.cpp— Catch2 synthetic benchmark for SBO vs heap.tests/mitiru/TestSmallFunctionTracy.cpp— regression test ensuring the zone macros stay no-op-safe.