Olympe Engine 2.0
2D Game Engine with ECS Architecture
Loading...
Searching...
No Matches
DebugPanel.cpp
Go to the documentation of this file.
1/**
2 * @file DebugPanel.cpp
3 * @brief Implementation of DebugPanel (Phase 5).
4 * @author Olympe Engine
5 * @date 2026-03-09
6 *
7 * @details C++14 compliant.
8 */
9
10#include "DebugPanel.h"
11#include "DebugController.h"
12#include "PerformanceProfiler.h"
13
14#include "../third_party/imgui/imgui.h"
15#include "../TaskSystem/TaskGraphTypes.h"
16
17#include <string>
18#include <vector>
19
20namespace Olympe {
21
22// ============================================================================
23// Constructor / Destructor
24// ============================================================================
25
29
33
37
39{
40}
41
42// ============================================================================
43// Render
44// ============================================================================
45
47{
48 if (!m_visible)
49 return;
50
51 ImGui::Begin("Debugger", &m_visible);
52
54 ImGui::Separator();
55
56 if (ImGui::CollapsingHeader("Breakpoints", ImGuiTreeNodeFlags_DefaultOpen))
58
59 if (ImGui::CollapsingHeader("Call Stack", ImGuiTreeNodeFlags_DefaultOpen))
61
62 if (ImGui::CollapsingHeader("Watch Variables", ImGuiTreeNodeFlags_DefaultOpen))
64
65 if (ImGui::CollapsingHeader("Profiler Summary"))
67
68 ImGui::End();
69}
70
71// ============================================================================
72// Toolbar
73// ============================================================================
74
76{
78 DebugState state = dc.GetState();
79
81 bool isPaused = state == DebugState::Paused;
82 bool isRunning = state == DebugState::Running;
83
84 // [▶ Continue]
85 if (!isPaused)
86 ImGui::BeginDisabled();
87 if (ImGui::Button("▶ Continue (F5)") || (isPaused && ImGui::IsKeyPressed(ImGuiKey_F5)))
88 {
89 dc.Continue();
90 }
91 if (!isPaused)
92 ImGui::EndDisabled();
93
94 ImGui::SameLine();
95
96 // [⏸ Pause]
97 if (!isRunning)
98 ImGui::BeginDisabled();
99 if (ImGui::Button("⏸ Pause"))
100 {
101 dc.Pause();
102 }
103 if (!isRunning)
104 ImGui::EndDisabled();
105
106 ImGui::SameLine();
107
108 // [⏭ Step Next] F10
109 if (!isPaused)
110 ImGui::BeginDisabled();
111 if (ImGui::Button("⏭ Step (F10)") || (isPaused && ImGui::IsKeyPressed(ImGuiKey_F10)))
112 {
113 dc.StepNext();
114 }
115 if (!isPaused)
116 ImGui::EndDisabled();
117
118 ImGui::SameLine();
119
120 // [⤵ Step Into] F11
121 if (!isPaused)
122 ImGui::BeginDisabled();
123 if (ImGui::Button("⤵ Into (F11)") || (isPaused && ImGui::IsKeyPressed(ImGuiKey_F11)))
124 {
125 dc.StepInto();
126 }
127 if (!isPaused)
128 ImGui::EndDisabled();
129
130 ImGui::SameLine();
131
132 // [⤴ Step Out] Shift+F11
133 if (!isPaused)
134 ImGui::BeginDisabled();
135 bool shiftF11 = isPaused &&
136 ImGui::IsKeyPressed(ImGuiKey_F11) &&
137 ImGui::GetIO().KeyShift;
138 if (ImGui::Button("⤴ Out") || shiftF11)
139 {
140 dc.StepOut();
141 }
142 if (!isPaused)
143 ImGui::EndDisabled();
144
145 ImGui::SameLine();
146
147 // [⏹ Stop]
148 if (!isDebugging)
149 ImGui::BeginDisabled();
150 if (ImGui::Button("⏹ Stop"))
151 {
152 dc.StopDebugging();
153 }
154 if (!isDebugging)
155 ImGui::EndDisabled();
156
157 // State indicator
158 ImGui::SameLine(0, 20);
159 const char* stateStr = "●";
160 ImVec4 stateCol = ImVec4(0.5f, 0.5f, 0.5f, 1.0f);
161 switch (state)
162 {
163 case DebugState::Running: stateStr = "● Running"; stateCol = {0.2f,0.8f,0.2f,1.f}; break;
164 case DebugState::Paused: stateStr = "⏸ Paused"; stateCol = {1.0f,0.8f,0.0f,1.f}; break;
165 case DebugState::StepNext: stateStr = "⏭ Step"; stateCol = {0.8f,0.8f,0.2f,1.f}; break;
166 case DebugState::StepInto: stateStr = "⤵ Into"; stateCol = {0.2f,0.8f,0.8f,1.f}; break;
167 case DebugState::StepOut: stateStr = "⤴ Out"; stateCol = {0.2f,0.6f,0.8f,1.f}; break;
168 case DebugState::NotDebugging: stateStr = "○ Idle"; stateCol = {0.5f,0.5f,0.5f,1.f}; break;
169 }
170 ImGui::TextColored(stateCol, "%s", stateStr);
171
172 // Graph/node info
173 int gid = dc.GetCurrentGraphID();
174 int nid = dc.GetCurrentNodeID();
175 if (gid >= 0 && nid >= 0)
176 {
177 ImGui::SameLine();
178 ImGui::TextDisabled(" graph=%d node=%d", gid, nid);
179 }
180}
181
182// ============================================================================
183// Breakpoints list
184// ============================================================================
185
187{
189 auto bps = dc.GetAllBreakpoints();
190
191 if (bps.empty())
192 {
193 ImGui::TextDisabled("(no breakpoints)");
194 return;
195 }
196
197 if (ImGui::SmallButton("Clear All##bpClearAll"))
198 dc.ClearAllBreakpoints();
199
200 ImGui::BeginChild("BPList", ImVec2(0, 140), true);
201
202 for (size_t i = 0; i < bps.size(); ++i)
203 {
205 ImGui::PushID(static_cast<int>(i));
206
207 bool enabled = bp.enabled;
208 if (ImGui::Checkbox("##bpEnabled", &enabled))
209 dc.SetBreakpointEnabled(bp.graphID, bp.nodeID, enabled);
210
211 ImGui::SameLine();
212 ImGui::Text("%s : Node %d \"%s\"",
213 bp.graphName.empty() ? "(graph)" : bp.graphName.c_str(),
214 bp.nodeID,
215 bp.nodeName.empty() ? "" : bp.nodeName.c_str());
216
217 ImGui::SameLine();
218 if (ImGui::SmallButton("×##bpDel"))
219 dc.ClearBreakpoint(bp.graphID, bp.nodeID);
220
221 ImGui::PopID();
222 }
223
224 ImGui::EndChild();
225}
226
227// ============================================================================
228// Call stack
229// ============================================================================
230
232{
234 auto stack = dc.GetCallStack();
235
236 if (stack.empty())
237 {
238 ImGui::TextDisabled("(no active call stack)");
239 return;
240 }
241
242 ImGui::BeginChild("CallStack", ImVec2(0, 100), true);
243
244 for (int idx = static_cast<int>(stack.size()) - 1; idx >= 0; --idx)
245 {
246 const SubGraphStackFrame& frame = stack[static_cast<size_t>(idx)];
247 if (frame.isCurrent)
248 {
249 ImGui::TextColored(ImVec4(0.3f, 1.0f, 0.3f, 1.0f),
250 "-> %s : Node %d \"%s\" [CURRENT]",
251 frame.graphName.empty() ? "(graph)" : frame.graphName.c_str(),
252 frame.nodeID,
253 frame.nodeName.empty() ? "" : frame.nodeName.c_str());
254 }
255 else
256 {
257 ImGui::Text(" %s : Node %d \"%s\"",
258 frame.graphName.empty() ? "(graph)" : frame.graphName.c_str(),
259 frame.nodeID,
260 frame.nodeName.empty() ? "" : frame.nodeName.c_str());
261 }
262 }
263
264 ImGui::EndChild();
265}
266
267// ============================================================================
268// Watch variables
269// ============================================================================
270
272{
274 if (!dc.IsDebugging())
275 {
276 ImGui::TextDisabled("(not debugging)");
277 return;
278 }
279
280 LocalBlackboard bb = dc.GetCurrentBlackboard();
281 auto keys = bb.GetVariableNames();
282
283 if (keys.empty())
284 {
285 ImGui::TextDisabled("(empty blackboard)");
286 return;
287 }
288
289 ImGui::BeginChild("WatchVars", ImVec2(0, 180), true);
290 ImGui::TextDisabled("Local Blackboard:");
291 ImGui::Separator();
292
293 for (size_t i = 0; i < keys.size(); ++i)
294 {
295 const std::string& key = keys[i];
297 try { val = bb.GetValue(key); }
298 catch (...) { continue; }
299
300 switch (val.GetType())
301 {
303 ImGui::Text(" %-22s : Bool(%s)", key.c_str(), val.AsBool() ? "true" : "false");
304 break;
306 ImGui::Text(" %-22s : Int(%d)", key.c_str(), val.AsInt());
307 break;
309 ImGui::Text(" %-22s : Float(%.3f)",key.c_str(), val.AsFloat());
310 break;
312 ImGui::Text(" %-22s : String(\"%s\")", key.c_str(), val.AsString().c_str());
313 break;
315 ImGui::Text(" %-22s : EntityID(%llu)",
316 key.c_str(),
317 static_cast<unsigned long long>(val.AsEntityID()));
318 break;
320 {
321 ::Vector v = val.AsVector();
322 ImGui::Text(" %-22s : Vec(%.2f,%.2f,%.2f)",
323 key.c_str(), v.x, v.y, v.z);
324 break;
325 }
326 default:
327 ImGui::Text(" %-22s : (unknown)", key.c_str());
328 break;
329 }
330 }
331
332 ImGui::EndChild();
333}
334
335// ============================================================================
336// Profiler summary
337// ============================================================================
338
340{
342
343 if (!prof.IsProfiling())
344 {
345 if (ImGui::Button("Start Profiling"))
346 prof.BeginProfiling();
347 return;
348 }
349
350 if (ImGui::Button("Stop Profiling"))
351 prof.StopProfiling();
352
353 auto hotspots = prof.GetHotspots();
354 if (hotspots.empty())
355 {
356 ImGui::TextDisabled("(no data yet)");
357 return;
358 }
359
360 ImGui::BeginChild("ProfSummary", ImVec2(0, 120), true);
361 ImGui::Text("%-6s %-24s %8s %8s %8s",
362 "NodeID", "Name", "AvgMs", "MaxMs", "Count");
363 ImGui::Separator();
364
365 for (size_t i = 0; i < hotspots.size() && i < 8; ++i)
366 {
369 if (isHot)
370 ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f),
371 "%-6d %-24s %8.3f %8.3f %8llu",
372 m.nodeID, m.nodeName.c_str(),
373 m.avgTimeMs, m.maxTimeMs, m.executionCount);
374 else
375 ImGui::Text("%-6d %-24s %8.3f %8.3f %8llu",
376 m.nodeID, m.nodeName.c_str(),
377 m.avgTimeMs, m.maxTimeMs, m.executionCount);
378 }
379
380 ImGui::EndChild();
381}
382
383} // namespace Olympe
Runtime debug controller for ATS Visual Scripting (Phase 5).
ImGui debug panel for ATS VS runtime debugging (Phase 5).
ComponentTypeID GetComponentTypeID_Static()
Definition ECS_Entity.h:56
Performance profiler for ATS Visual Scripting node execution (Phase 5).
Singleton that manages runtime debugging of VS graphs.
static DebugController & Get()
Returns the singleton instance (Meyers pattern).
void Render()
Renders the debug panel window.
Simple map-based blackboard for task graph runtime state.
std::vector< std::string > GetVariableNames() const
Returns all registered variable names (useful for debugging / editor).
Singleton performance profiler for VS graph node execution.
static PerformanceProfiler & Get()
Returns the singleton instance (Meyers pattern).
static constexpr float HOTSPOT_THRESHOLD_MS
Hotspot threshold (ms) — nodes above this are flagged.
C++14-compliant type-safe value container for task parameters.
< Provides AssetID and INVALID_ASSET_ID
@ Int
32-bit signed integer
@ Float
Single-precision float.
@ String
std::string
@ Vector
3-component vector (Vector from vector.h)
@ EntityID
Entity identifier (uint64_t)
DebugState
States of the debug controller state machine.
@ NotDebugging
No active debug session.
@ StepNext
Execute next node then pause.
@ StepOut
Run until the current SubGraph returns.
@ Running
Normal execution.
@ StepInto
Step into a SubGraph on next SubGraph node.
@ Paused
Stopped at a breakpoint or manual pause.
Describes a single breakpoint.
Accumulated per-node execution statistics.
float avgTimeMs
Running average execution time (ms)
A single frame in the debugger's SubGraph call stack.
bool isCurrent
true for the top-most (active) frame