Olympe Engine 2.0
2D Game Engine with ECS Architecture
Loading...
Searching...
No Matches
DebugController.h
Go to the documentation of this file.
1/**
2 * @file DebugController.h
3 * @brief Runtime debug controller for ATS Visual Scripting (Phase 5).
4 * @author Olympe Engine
5 * @date 2026-03-09
6 *
7 * @details
8 * DebugController is a singleton state machine that manages breakpoints,
9 * debug flow (Continue, Pause, Step Next/Into/Out), and the call stack for
10 * SubGraph traversal. It is designed to be called from both the editor UI
11 * thread and the task execution thread, so all methods are mutex-protected.
12 *
13 * C++14 compliant — no std::optional, structured bindings, std::filesystem.
14 */
15
16#pragma once
17
18#include <string>
19#include <vector>
20#include <unordered_map>
21#include <mutex>
22
23#include "../TaskSystem/LocalBlackboard.h"
24#include "../TaskSystem/TaskGraphTypes.h"
25
26namespace Olympe {
27
28// ============================================================================
29// Supporting structures
30// ============================================================================
31
32/**
33 * @struct BreakpointKey
34 * @brief Uniquely identifies a breakpoint by graphID + nodeID.
35 */
37 int graphID = -1;
38 int nodeID = -1;
39
40 bool operator==(const BreakpointKey& o) const
41 {
42 return graphID == o.graphID && nodeID == o.nodeID;
43 }
44};
45
46/**
47 * @struct BreakpointInfo
48 * @brief Describes a single breakpoint.
49 */
51 int graphID = -1;
52 int nodeID = -1;
53 std::string graphName; ///< Human-readable graph file name
54 std::string nodeName; ///< Human-readable node name
55 bool enabled = true;
56};
57
58/**
59 * @struct SubGraphStackFrame
60 * @brief A single frame in the debugger's SubGraph call stack.
61 */
63 int graphID = -1;
64 int nodeID = -1;
65 std::string graphName;
66 std::string nodeName;
67 bool isCurrent = false; ///< true for the top-most (active) frame
68};
69
70// ============================================================================
71// DebugState enum
72// ============================================================================
73
74/**
75 * @enum DebugState
76 * @brief States of the debug controller state machine.
77 */
78enum class DebugState {
79 NotDebugging, ///< No active debug session
80 Running, ///< Normal execution
81 Paused, ///< Stopped at a breakpoint or manual pause
82 StepNext, ///< Execute next node then pause
83 StepInto, ///< Step into a SubGraph on next SubGraph node
84 StepOut ///< Run until the current SubGraph returns
85};
86
87// ============================================================================
88// DebugController
89// ============================================================================
90
91/**
92 * @class DebugController
93 * @brief Singleton that manages runtime debugging of VS graphs.
94 *
95 * @details
96 * Usage:
97 * @code
98 * // Editor side
99 * DebugController::Get().SetBreakpoint(0, 3);
100 * DebugController::Get().StartDebugging(0);
101 *
102 * // TaskSystem side (called from ExecuteFrame)
103 * DebugController::Get().OnNodeExecuting(graphID, nodeID, &blackboard);
104 * @endcode
105 *
106 * Meyers singleton pattern (local static in Get()).
107 * Thread-safe via std::mutex.
108 */
110public:
111
112 /**
113 * @brief Returns the singleton instance (Meyers pattern).
114 */
115 static DebugController& Get();
116
117 // -----------------------------------------------------------------------
118 // Breakpoints
119 // -----------------------------------------------------------------------
120
121 /**
122 * @brief Registers a breakpoint at (graphID, nodeID).
123 * Safe to call multiple times — idempotent.
124 */
125 void SetBreakpoint(int graphID, int nodeID,
126 const std::string& graphName = "",
127 const std::string& nodeName = "");
128
129 /**
130 * @brief Removes the breakpoint at (graphID, nodeID) if it exists.
131 */
132 void ClearBreakpoint(int graphID, int nodeID);
133
134 /**
135 * @brief Toggles the breakpoint at (graphID, nodeID).
136 * Adds it if absent, removes it if present.
137 */
138 void ToggleBreakpoint(int graphID, int nodeID,
139 const std::string& graphName = "",
140 const std::string& nodeName = "");
141
142 /**
143 * @brief Returns true if an enabled breakpoint exists at (graphID, nodeID).
144 */
145 bool HasBreakpoint(int graphID, int nodeID) const;
146
147 /**
148 * @brief Enables or disables an existing breakpoint.
149 */
150 void SetBreakpointEnabled(int graphID, int nodeID, bool enabled);
151
152 /**
153 * @brief Returns all registered breakpoints for @p graphID.
154 */
155 std::vector<BreakpointInfo> GetBreakpoints(int graphID) const;
156
157 /**
158 * @brief Returns all registered breakpoints across all graphs.
159 */
160 std::vector<BreakpointInfo> GetAllBreakpoints() const;
161
162 /**
163 * @brief Removes all breakpoints.
164 */
165 void ClearAllBreakpoints();
166
167 // -----------------------------------------------------------------------
168 // Debug flow
169 // -----------------------------------------------------------------------
170
171 /**
172 * @brief Starts a debug session for the given graph.
173 * Transitions from NotDebugging -> Running.
174 */
175 void StartDebugging(int graphID);
176
177 /**
178 * @brief Stops the current debug session.
179 * Transitions to NotDebugging from any state.
180 */
181 void StopDebugging();
182
183 /**
184 * @brief Resumes execution from Paused state.
185 * Transitions Paused -> Running.
186 */
187 void Continue();
188
189 /**
190 * @brief Pauses execution.
191 * Transitions Running -> Paused.
192 */
193 void Pause();
194
195 /**
196 * @brief Executes the next node then pauses.
197 * Transitions Paused -> StepNext.
198 */
199 void StepNext();
200
201 /**
202 * @brief Steps into a SubGraph if the next node is a SubGraph node.
203 * Transitions Paused -> StepInto.
204 */
205 void StepInto();
206
207 /**
208 * @brief Runs until the current SubGraph returns.
209 * Transitions Paused -> StepOut.
210 */
211 void StepOut();
212
213 // -----------------------------------------------------------------------
214 // Runtime state
215 // -----------------------------------------------------------------------
216
217 /** @brief Returns the current debug state. */
218 DebugState GetState() const;
219
220 /** @brief Returns true when a debug session is active. */
221 bool IsDebugging() const;
222
223 /** @brief Returns the currently debugged graphID (-1 if none). */
224 int GetCurrentGraphID() const;
225
226 /** @brief Returns the node being executed (-1 if none). */
227 int GetCurrentNodeID() const;
228
229 /**
230 * @brief Returns a copy of the blackboard snapshot from the last
231 * OnNodeExecuting() call.
232 */
234
235 /**
236 * @brief Returns the current SubGraph call stack (most recent at front).
237 */
238 std::vector<SubGraphStackFrame> GetCallStack() const;
239
240 // -----------------------------------------------------------------------
241 // Hooks called by TaskGraphExecutor / VSGraphExecutor
242 // -----------------------------------------------------------------------
243
244 /**
245 * @brief Called by VSGraphExecutor before executing a node.
246 *
247 * Updates the current node/graph and blackboard snapshot.
248 * If a breakpoint is hit and state is Running, transitions to Paused.
249 * If state is StepNext, transitions to Paused after the first call.
250 *
251 * @note This method may busy-wait when the state is Paused (in debug
252 * mode with the UI thread calling Continue/StepNext). In a
253 * headless test context, busy-wait is skipped.
254 *
255 * @param graphID Graph being executed.
256 * @param nodeID Node about to be executed.
257 * @param bb Live blackboard pointer (snapshot is copied).
258 */
259 void OnNodeExecuting(int graphID, int nodeID, const LocalBlackboard* bb);
260
261 /**
262 * @brief Push a SubGraph frame onto the call stack.
263 * Called by VSGraphExecutor when entering a SubGraph.
264 */
265 void PushCallFrame(int graphID, int nodeID,
266 const std::string& graphName = "",
267 const std::string& nodeName = "");
268
269 /**
270 * @brief Pop the top SubGraph frame from the call stack.
271 * Called by VSGraphExecutor when returning from a SubGraph.
272 */
273 void PopCallFrame();
274
275 // -----------------------------------------------------------------------
276 // Headless / test mode
277 // -----------------------------------------------------------------------
278
279 /**
280 * @brief When true, OnNodeExecuting() never busy-waits on Paused.
281 * Useful for unit tests where there is no UI thread to call Continue().
282 */
283 void SetHeadlessMode(bool headless);
284
285private:
286
289
290 // Non-copyable, non-movable
293
294 // -----------------------------------------------------------------------
295 // Internal helpers (called with mutex held)
296 // -----------------------------------------------------------------------
297
298 int MakeBreakpointKey(int graphID, int nodeID) const;
299 bool HasBreakpointLocked(int graphID, int nodeID) const;
300
301 // -----------------------------------------------------------------------
302 // State
303 // -----------------------------------------------------------------------
304
305 mutable std::mutex m_mutex;
306
310 bool m_headlessMode = false;
311
313
314 /// Breakpoints keyed by (graphID * 100000 + nodeID)
315 std::unordered_map<int, BreakpointInfo> m_breakpoints;
316
317 /// Call stack (index 0 = bottom / entry, back = current)
318 std::vector<SubGraphStackFrame> m_callStack;
319};
320
321} // namespace Olympe
ComponentTypeID GetComponentTypeID_Static()
Definition ECS_Entity.h:56
Singleton that manages runtime debugging of VS graphs.
void StartDebugging(int graphID)
Starts a debug session for the given graph.
int GetCurrentNodeID() const
Returns the node being executed (-1 if none).
LocalBlackboard GetCurrentBlackboard() const
Returns a copy of the blackboard snapshot from the last OnNodeExecuting() call.
void Continue()
Resumes execution from Paused state.
void ClearBreakpoint(int graphID, int nodeID)
Removes the breakpoint at (graphID, nodeID) if it exists.
std::vector< BreakpointInfo > GetBreakpoints(int graphID) const
Returns all registered breakpoints for graphID.
void StepNext()
Executes the next node then pauses.
std::vector< SubGraphStackFrame > m_callStack
Call stack (index 0 = bottom / entry, back = current)
void PopCallFrame()
Pop the top SubGraph frame from the call stack.
int MakeBreakpointKey(int graphID, int nodeID) const
std::vector< SubGraphStackFrame > GetCallStack() const
Returns the current SubGraph call stack (most recent at front).
void StepOut()
Runs until the current SubGraph returns.
void ToggleBreakpoint(int graphID, int nodeID, const std::string &graphName="", const std::string &nodeName="")
Toggles the breakpoint at (graphID, nodeID).
void SetBreakpointEnabled(int graphID, int nodeID, bool enabled)
Enables or disables an existing breakpoint.
void SetHeadlessMode(bool headless)
When true, OnNodeExecuting() never busy-waits on Paused.
bool HasBreakpointLocked(int graphID, int nodeID) const
void ClearAllBreakpoints()
Removes all breakpoints.
DebugController & operator=(const DebugController &)=delete
static DebugController & Get()
Returns the singleton instance (Meyers pattern).
std::vector< BreakpointInfo > GetAllBreakpoints() const
Returns all registered breakpoints across all graphs.
void StopDebugging()
Stops the current debug session.
DebugState GetState() const
Returns the current debug state.
int GetCurrentGraphID() const
Returns the currently debugged graphID (-1 if none).
void Pause()
Pauses execution.
void PushCallFrame(int graphID, int nodeID, const std::string &graphName="", const std::string &nodeName="")
Push a SubGraph frame onto the call stack.
std::unordered_map< int, BreakpointInfo > m_breakpoints
Breakpoints keyed by (graphID * 100000 + nodeID)
void StepInto()
Steps into a SubGraph if the next node is a SubGraph node.
DebugController(const DebugController &)=delete
bool IsDebugging() const
Returns true when a debug session is active.
bool HasBreakpoint(int graphID, int nodeID) const
Returns true if an enabled breakpoint exists at (graphID, nodeID).
void SetBreakpoint(int graphID, int nodeID, const std::string &graphName="", const std::string &nodeName="")
Registers a breakpoint at (graphID, nodeID).
void OnNodeExecuting(int graphID, int nodeID, const LocalBlackboard *bb)
Called by VSGraphExecutor before executing a node.
LocalBlackboard m_bbSnapshot
Simple map-based blackboard for task graph runtime state.
< Provides AssetID and INVALID_ASSET_ID
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.
std::string graphName
Human-readable graph file name.
std::string nodeName
Human-readable node name.
Uniquely identifies a breakpoint by graphID + nodeID.
bool operator==(const BreakpointKey &o) const
A single frame in the debugger's SubGraph call stack.
bool isCurrent
true for the top-most (active) frame