-
Notifications
You must be signed in to change notification settings - Fork 1
Coroutine System
SparkEngine provides a cooperative coroutine scheduler for gameplay code that needs delayed, yielding, or repeating tasks. The system offers two complementary APIs: a builder-pattern API for simple sequenced actions and a C++20 coroutine API for natural async-style control flow using co_await, co_yield, and co_return.
All coroutines run cooperatively on the main thread. They yield control back to the scheduler each frame and are resumed when their yield condition is satisfied.
Source: SparkEngine/Source/Engine/Coroutine/CoroutineScheduler.h
Namespace: Spark
Tests: Tests/TestCoroutineScheduler.cpp (10 test cases)
- Overview
- Yield Instructions
- Builder-Pattern API
- C++20 Coroutine API
- CoroutineScheduler
- NativeCoroutineWrapper
- Convenience Free Functions
- Integration with EventBus
- Practical Examples
- Threading Model
- Testing
- See Also
The coroutine system solves a common gameplay programming problem: expressing sequences of actions separated by time delays, frame waits, or condition checks without blocking the game loop. Instead of scattering state machines across update functions, you describe the full sequence in one place and let the scheduler handle timing.
The two APIs serve different use cases:
| Feature | Builder Pattern | C++20 Coroutines |
|---|---|---|
| Syntax | Fluent method chaining |
co_await / co_yield / co_return
|
| Control flow | Linear sequences only | Loops, branches, try/catch |
| Local variables | Captured by lambda | Naturally preserved across yields |
| Compiler support | Any C++17+ compiler | Requires C++20 coroutine support |
| Best for | Simple action-delay chains | Complex async logic |
Both APIs share the same YieldInstruction types and are managed by the same CoroutineScheduler.
All yield instructions derive from the YieldInstruction base class, which defines the interface the scheduler uses to check whether a coroutine should resume.
class YieldInstruction
{
public:
virtual ~YieldInstruction() = default;
/// Called each frame. Returns true when the coroutine should resume.
virtual bool IsReady(float deltaTime) = 0;
/// Reset the instruction for reuse (e.g., in repeating coroutines).
virtual void Reset() {}
};Pauses execution for a specified duration in seconds. The instruction accumulates elapsed time each frame via IsReady(deltaTime) and resumes once the elapsed time meets or exceeds the target.
class WaitForSeconds : public YieldInstruction
{
public:
explicit WaitForSeconds(float seconds);
bool IsReady(float deltaTime) override;
void Reset() override;
};Parameters:
-
seconds-- The number of seconds to wait before resuming.
Behavior: Accumulates deltaTime across frames. Returns true from IsReady() once m_elapsed >= m_target.
Pauses execution for a specified number of frames. Each call to IsReady() increments an internal counter, and the instruction becomes ready when the count reaches the target.
class WaitForFrames : public YieldInstruction
{
public:
explicit WaitForFrames(int frames);
bool IsReady(float deltaTime) override;
void Reset() override;
};Parameters:
-
frames-- The number of frames to wait. The coroutine resumes on the frame where the count reaches this value.
Pauses execution until a user-supplied predicate returns true. The predicate is evaluated every frame.
class WaitUntil : public YieldInstruction
{
public:
explicit WaitUntil(std::function<bool()> predicate);
bool IsReady(float deltaTime) override;
};Parameters:
-
predicate-- A callable returningbool. The coroutine resumes when it returnstrue.
Note: The predicate is called every frame, so keep it lightweight. Avoid expensive computations or I/O inside the predicate.
Pauses execution for exactly one frame. The coroutine resumes on the next Update() call after the one that first encounters this instruction.
class WaitForEndOfFrame : public YieldInstruction
{
public:
bool IsReady(float deltaTime) override;
void Reset() override;
};Behavior: Returns false on the first call to IsReady(), then true on all subsequent calls. This guarantees at least one frame of delay.
Pauses execution until a shared atomic boolean flag is set to true. This is the primary mechanism for integrating coroutines with the EventBus system without introducing a direct include dependency.
class WaitForEvent : public YieldInstruction
{
public:
explicit WaitForEvent(std::shared_ptr<std::atomic<bool>> eventFlag);
bool IsReady(float deltaTime) override;
void Reset() override;
};Parameters:
-
eventFlag-- A shared pointer to an atomic boolean. The coroutine resumes when this flag istrue.
Memory ordering: Uses memory_order_acquire for reading and memory_order_release for resetting, ensuring correct synchronization if the flag is set from another thread.
Reset behavior: Calling Reset() sets the flag back to false, which is useful for repeating coroutines that need to wait for the same event multiple times.
The builder-pattern API lets you describe a coroutine as a chain of Do() actions and yield steps using fluent method syntax. This is ideal for simple linear sequences of actions separated by delays.
class Coroutine
{
public:
explicit Coroutine(const std::string& name);
const std::string& GetName() const;
bool IsFinished() const;
bool IsCancelled() const;
void Cancel();
void Update(float deltaTime);
// Builder methods (all return *this for chaining)
Coroutine& Do(std::function<void()> action);
Coroutine& WaitForSeconds(float seconds);
Coroutine& WaitForFrames(int frames);
Coroutine& WaitUntil(std::function<bool()> predicate);
Coroutine& WaitForEvent(std::shared_ptr<std::atomic<bool>> eventFlag);
Coroutine& YieldFrame();
};| Method | Description |
|---|---|
Do(action) |
Enqueue an action step. Runs immediately when reached (no delay). |
WaitForSeconds(seconds) |
Enqueue a time-based yield. Resumes after seconds have elapsed. |
WaitForFrames(frames) |
Enqueue a frame-count yield. Resumes after frames update ticks. |
WaitUntil(predicate) |
Enqueue a predicate yield. Resumes when predicate() returns true. |
WaitForEvent(flag) |
Enqueue an event yield. Resumes when the shared atomic bool is true. |
YieldFrame() |
Enqueue a single-frame yield. Equivalent to WaitForFrames(1) but uses WaitForEndOfFrame internally. |
Execution model: When Update(deltaTime) is called, the coroutine processes steps sequentially. Action steps execute immediately and the coroutine advances to the next step in the same frame. Yield steps check their condition; if satisfied, the coroutine advances. If not, processing stops until the next frame. This means multiple consecutive Do() steps all execute in a single frame, while yield steps introduce pauses.
Simple delayed action:
auto& scheduler = Spark::CoroutineScheduler::GetInstance();
scheduler.StartCoroutine("greet")
.Do([]() { Logger::Info("Hello..."); })
.WaitForSeconds(2.0f)
.Do([]() { Logger::Info("...World!"); });Multi-step sequence with different yield types:
scheduler.StartCoroutine("intro_sequence")
.Do([&]() { camera.FadeToBlack(); })
.WaitForSeconds(1.0f)
.Do([&]() { camera.SetPosition(introPos); })
.YieldFrame() // let the renderer pick up the new position
.Do([&]() { camera.FadeFromBlack(); })
.WaitForSeconds(2.0f)
.Do([&]() { hud.ShowObjective("Find the exit"); });Waiting for a condition:
bool doorUnlocked = false;
scheduler.StartCoroutine("wait_for_door")
.Do([&]() { hud.ShowPrompt("Find the key"); })
.WaitUntil([&]() { return doorUnlocked; })
.Do([&]() { door.Open(); hud.HidePrompt(); });For more complex asynchronous logic involving loops, conditionals, or deeply nested sequences, the C++20 coroutine API provides a natural programming model using co_await, co_yield, and co_return.
Any function that returns GameCoroutine can use coroutine keywords. The GameCoroutine object owns the coroutine handle and destroys it in its destructor (RAII).
class GameCoroutine
{
public:
struct promise_type;
using handle_type = std::coroutine_handle<promise_type>;
GameCoroutine();
explicit GameCoroutine(handle_type h);
// Move-only (no copy)
GameCoroutine(GameCoroutine&& other) noexcept;
GameCoroutine& operator=(GameCoroutine&& other) noexcept;
~GameCoroutine();
bool IsFinished() const;
bool Update(float deltaTime);
handle_type GetHandle() const;
};Key properties:
-
Move-only:
GameCoroutinecannot be copied. Usestd::move()when passing to the scheduler. -
RAII: The destructor calls
m_handle.destroy()if the handle is valid, preventing coroutine frame leaks. -
Initial suspend: Coroutines start in a suspended state (
initial_suspendreturnssuspend_always). They do not execute any code until the firstUpdate()call.
The promise_type nested inside GameCoroutine drives the C++20 coroutine machinery:
struct promise_type
{
std::unique_ptr<YieldInstruction> currentYield;
bool finished = false;
GameCoroutine get_return_object();
std::suspend_always initial_suspend() noexcept;
std::suspend_always final_suspend() noexcept;
void return_void();
void unhandled_exception();
std::suspend_always yield_value(std::nullptr_t);
};-
initial_suspend()returnssuspend_always-- coroutines do not auto-start. -
final_suspend()returnssuspend_alwaysand setsfinished = true. -
return_void()setsfinished = true(coroutines must useco_return;or fall off the end). -
unhandled_exception()setsfinished = true(exceptions terminate the coroutine silently). -
yield_value(nullptr)supportsco_yield nullptrfor yielding one frame.
The following operator co_await overloads are provided, each creating a YieldAwaiter<T> that stores the yield instruction in the promise:
| Expression | Effect |
|---|---|
co_await Spark::WaitForSeconds(2.0f) |
Suspend for 2 seconds |
co_await Spark::WaitForFrames(5) |
Suspend for 5 frames |
co_await Spark::WaitUntil([&]{ return ready; }) |
Suspend until predicate is true |
co_await Spark::WaitForEndOfFrame() |
Suspend for one frame |
co_await Spark::WaitForEvent(flag) |
Suspend until event flag is set |
co_yield nullptr |
Suspend for one frame (shorthand) |
co_return |
Complete the coroutine |
The YieldAwaiter<T> template (constrained with std::derived_from<T, YieldInstruction>) bridges yield instructions to the C++20 awaiter protocol:
template <typename T>
requires std::derived_from<T, YieldInstruction>
struct YieldAwaiter
{
T instruction;
explicit YieldAwaiter(T inst);
bool await_ready() const noexcept; // always returns false
void await_suspend(std::coroutine_handle<GameCoroutine::promise_type> handle) const;
void await_resume() const noexcept;
};Basic timed sequence:
Spark::GameCoroutine FlashDamageIndicator(HUD& hud)
{
hud.SetDamageFlash(1.0f);
co_await Spark::WaitForSeconds(0.2f);
hud.FadeDamageFlash(1.0f);
co_await Spark::WaitForSeconds(1.0f);
hud.SetDamageFlash(0.0f);
co_return;
}
// Schedule it:
Spark::CoroutineScheduler::GetInstance().Schedule("damage_flash",
FlashDamageIndicator(hud));Loop with delay (wave spawner):
Spark::GameCoroutine SpawnWaves(WaveManager& waves, int count)
{
for (int i = 0; i < count; ++i)
{
waves.SpawnWave(i);
co_await Spark::WaitForSeconds(10.0f);
}
co_return;
}Conditional logic:
Spark::GameCoroutine PatrolAndChase(AIController& ai)
{
while (ai.IsAlive())
{
// Patrol until player is spotted
ai.StartPatrol();
co_await Spark::WaitUntil([&]() { return ai.CanSeePlayer(); });
// Chase the player
ai.StartChase();
co_await Spark::WaitUntil([&]() { return !ai.CanSeePlayer(); });
// Lost the player, wait before resuming patrol
ai.StopChase();
co_await Spark::WaitForSeconds(3.0f);
}
co_return;
}Using co_yield for frame-by-frame work:
Spark::GameCoroutine SmoothLerp(Transform& transform, Vector3 target, float duration)
{
Vector3 start = transform.GetPosition();
float elapsed = 0.0f;
while (elapsed < duration)
{
float t = elapsed / duration;
transform.SetPosition(Vector3::Lerp(start, target, t));
elapsed += Time::GetDeltaTime();
co_yield nullptr; // wait one frame
}
transform.SetPosition(target);
co_return;
}The CoroutineScheduler is a singleton that manages all active coroutines (both builder-pattern and C++20 native). It should be ticked once per frame from the game loop.
Spark::CoroutineScheduler& scheduler = Spark::CoroutineScheduler::GetInstance();The instance is created on first access (Meyer's singleton) and lives for the duration of the program.
Coroutine& StartCoroutine(const std::string& name);Creates a new builder-pattern coroutine with the given name and returns a reference for chaining Do()/Wait calls. The coroutine begins executing on the next Update() call.
Parameters:
-
name-- A string identifier used for cancellation and debugging. Does not need to be unique; multiple coroutines can share a name (all will be cancelled byStopCoroutine).
void Schedule(const std::string& name, GameCoroutine coroutine);Registers a C++20 GameCoroutine with the scheduler. The coroutine is wrapped in a NativeCoroutineWrapper for unified management.
Parameters:
-
name-- A string identifier for cancellation and debugging. -
coroutine-- AGameCoroutineobject (must be moved in, as it is move-only).
void StopCoroutine(const std::string& name);Cancels all coroutines (both builder and native) that match the given name. Cancelled coroutines are removed during the next Update() call.
void StopAll();Cancels every active coroutine. Useful for scene transitions or shutdown.
bool IsRunning(const std::string& name) const;Returns true if at least one non-cancelled, non-finished coroutine with the given name exists.
size_t ActiveCount() const;Returns the total number of active coroutines (builder + native combined). Finished and cancelled coroutines are not counted after the next Update() removes them.
void Update(float deltaTime);Ticks all active coroutines forward by one frame. This method:
- Calls
Update(deltaTime)on each active builder-pattern coroutine. - Calls
Update(deltaTime)on each active native coroutine wrapper. - Removes all finished or cancelled coroutines from both lists (erase-remove idiom).
Call CoroutineScheduler::Update() once per frame from your game loop, after physics and before rendering:
void GameLoop(float deltaTime)
{
// Physics, AI, etc.
PhysicsSystem::Update(deltaTime);
AISystem::Update(deltaTime);
// Coroutines
Spark::CoroutineScheduler::GetInstance().Update(deltaTime);
// Rendering
GraphicsEngine::Render();
}The NativeCoroutineWrapper class adapts a GameCoroutine so the scheduler can manage it with the same interface as builder-pattern coroutines.
class NativeCoroutineWrapper
{
public:
NativeCoroutineWrapper(const std::string& name, GameCoroutine coroutine);
const std::string& GetName() const;
bool IsFinished() const;
bool IsCancelled() const;
void Cancel();
void Update(float deltaTime);
};You do not normally interact with this class directly. It is used internally by CoroutineScheduler::Schedule().
Three free functions in the Spark namespace provide shorthand access to the global scheduler:
/// Start a builder-pattern coroutine on the global scheduler.
Spark::Coroutine& Spark::StartCoroutine(const std::string& name);
/// Stop a coroutine by name on the global scheduler.
void Spark::StopCoroutine(const std::string& name);
/// Schedule a C++20 coroutine on the global scheduler.
void Spark::ScheduleCoroutine(const std::string& name, GameCoroutine coroutine);Usage:
// Builder pattern via free function
Spark::StartCoroutine("flash")
.Do([]() { /* ... */ })
.WaitForSeconds(1.0f)
.Do([]() { /* ... */ });
// C++20 via free function
Spark::ScheduleCoroutine("waves", SpawnWaves(5));
// Cancel via free function
Spark::StopCoroutine("flash");The WaitForEvent yield instruction uses a std::shared_ptr<std::atomic<bool>> flag to decouple the coroutine system from the EventBus include hierarchy. To wait for an event inside a coroutine:
- Create a shared atomic boolean flag.
- Subscribe to the event on the
EventBus, setting the flag totruein the handler. - Use
WaitForEvent(flag)in the coroutine.
auto& scheduler = Spark::CoroutineScheduler::GetInstance();
auto& eventBus = EngineContext::GetEventBus();
// Create a shared flag
auto doorOpenFlag = std::make_shared<std::atomic<bool>>(false);
// Subscribe to the event
auto subID = eventBus.Subscribe<DoorOpenEvent>(
[doorOpenFlag](const DoorOpenEvent& e)
{
doorOpenFlag->store(true, std::memory_order_release);
});
// Coroutine waits for the event
scheduler.StartCoroutine("on_door_open")
.WaitForEvent(doorOpenFlag)
.Do([&]()
{
PlaySound("door_creak");
TriggerCutscene("enter_dungeon");
});Spark::GameCoroutine WaitForDoorAndEnter(
EventBus& eventBus,
std::shared_ptr<std::atomic<bool>> doorFlag)
{
co_await Spark::WaitForEvent(doorFlag);
PlaySound("door_creak");
co_await Spark::WaitForSeconds(1.5f);
TriggerCutscene("enter_dungeon");
co_return;
}
// Setup and schedule:
auto doorFlag = std::make_shared<std::atomic<bool>>(false);
eventBus.Subscribe<DoorOpenEvent>(
[doorFlag](const DoorOpenEvent&) { doorFlag->store(true); });
Spark::ScheduleCoroutine("door_enter", WaitForDoorAndEnter(eventBus, doorFlag));Important: Remember to unsubscribe from the EventBus when the coroutine completes or when the flag is no longer needed, to avoid dangling subscriptions.
A common FPS pattern: flash the screen red when the player takes damage, then fade it out.
// Builder pattern
Spark::StartCoroutine("damage_flash")
.Do([&]() { hud.SetDamageFlash(1.0f); })
.WaitForSeconds(0.2f)
.Do([&]() { hud.FadeDamageFlash(1.0f); })
.WaitForSeconds(1.0f)
.Do([&]() { hud.SetDamageFlash(0.0f); });Spawn multiple waves with a delay between each, using C++20 coroutines for natural loop syntax.
Spark::GameCoroutine SpawnWaves(WaveManager& waves, int waveCount)
{
for (int i = 0; i < waveCount; ++i)
{
waves.SpawnWave(i);
Logger::Info("Wave {} spawned", i + 1);
co_await Spark::WaitForSeconds(10.0f);
}
Logger::Info("All waves complete");
co_return;
}
Spark::ScheduleCoroutine("waves", SpawnWaves(waveManager, 5));A multi-step scripted sequence combining delays, frame yields, and actions.
Spark::StartCoroutine("door_sequence")
.Do([&]() { player.DisableInput(); })
.Do([&]() { camera.LookAt(door.GetPosition()); })
.YieldFrame()
.Do([&]() { door.PlayAnimation("open"); })
.WaitForSeconds(1.5f)
.Do([&]() { camera.ResetToPlayer(); })
.YieldFrame()
.Do([&]() { player.EnableInput(); });Display a countdown before a match starts.
Spark::GameCoroutine MatchCountdown(HUD& hud)
{
for (int i = 3; i > 0; --i)
{
hud.ShowCenterText(std::to_string(i));
co_await Spark::WaitForSeconds(1.0f);
}
hud.ShowCenterText("GO!");
co_await Spark::WaitForSeconds(0.5f);
hud.HideCenterText();
co_return;
}Use WaitUntil to pause a tutorial sequence until the player performs an action.
Spark::StartCoroutine("tutorial_step1")
.Do([&]() { hud.ShowTutorial("Press SPACE to jump"); })
.WaitUntil([&]() { return input.WasKeyPressed(Key::Space); })
.Do([&]() { hud.ShowTutorial("Great! Now press SHIFT to sprint"); })
.WaitUntil([&]() { return input.WasKeyPressed(Key::Shift); })
.Do([&]() { hud.HideTutorial(); });The coroutine system is main-thread only. All coroutine execution, yield checking, and scheduler updates happen on the thread that calls CoroutineScheduler::Update(). This means:
- Coroutine actions can safely access game state without synchronization.
- The
WaitForEventflag can be set from any thread (it usesstd::atomic<bool>with appropriate memory ordering), but the coroutine itself resumes on the main thread. - Do not call
Update()from multiple threads simultaneously. - Do not store references to
Coroutine&across threads.
The coroutine system is covered by Tests/TestCoroutineScheduler.cpp with 10 test cases:
| Test Case | Description |
|---|---|
Coroutine_DoExecutesImmediately |
Verifies that consecutive Do() actions execute in a single Update() call |
Coroutine_WaitForSecondsDelays |
Verifies time-based yielding with accumulated delta time |
Coroutine_WaitForFramesDelays |
Verifies frame-count yielding over multiple updates |
Coroutine_WaitUntilPredicate |
Verifies predicate-based yielding |
Coroutine_CancelStopsExecution |
Verifies that Cancel() prevents further step execution |
Scheduler_ManagesMultipleCoroutines |
Verifies concurrent coroutines with independent timelines |
Scheduler_StopByName |
Verifies cancellation by name through the scheduler |
Scheduler_StopAll |
Verifies bulk cancellation |
Scheduler_IsRunning |
Verifies runtime status queries |
Coroutine_ChainedWaits |
Verifies multiple consecutive wait-action-wait sequences |
Run tests with:
cd build && ctest --output-on-failure-
Event System --
EventBusfor publish/subscribe events, used withWaitForEvent - Entity Component System -- ECS architecture and system execution order
- Gameplay Systems -- Higher-level gameplay logic that uses coroutines
- Architecture Overview -- Overall engine architecture and subsystem relationships
- Animation -- Animation state machines that may coordinate with coroutines
- AI and Navigation -- AI behavior trees that can use coroutines for scripted sequences
-
Testing -- Test framework and conventions used by
TestCoroutineScheduler
Published from 8fd2c5bdc5cd. Edit the canonical source in wiki/.
- Documentation
- Docs route
- Wiki index
- Guides
- Tutorials
- Samples
- Examples
- API Reference
- API route
- Reference
- Build Guide
- Dependencies
- FAQ
- Changelog
- Roadmap
- Contributing
- Code of Conduct
- Home
- FAQ
- Getting Started
- Quick-Start Tutorial
- Making Your First Game
- Making Your First Multiplayer Game
- Artist Workflow Guide
- Editor Walkthrough
- Migration Guide
- How SparkEngine Works
- Architecture Overview
- Engine Architecture Flowchart
- Creating a Game Module
- Game Modules (catalog)
- Entity Component System
- Rendering and Graphics
- Physics
- Cloth Simulation
- Audio
- Input System
- Camera System
- Scripting with AngelScript
- Visual Scripting
- AI and Navigation
- Animation
- 2D Systems
- Networking
- Dedicated Server
- Multiplayer Quick Start
- Area Server Architecture
- Scene Management
- Large World Support
- Collaborative Editing
- Coroutine System
- Event System
- Event Response System
- Job System
- UI System
- UI Layout Extensions
- Localization
- Dialogue System
- Destruction System
- Replay System
- Achievement System
- Loading System
- Mod System
- Content Delivery
- Tween System
- Memory Integrity
- Gameplay Systems
- Terrain and Procedural Generation
- Save System
- Persistence System
- Day Night Cycle and Weather
- Cinematic Sequencer
- Runtime Prefabs
- SparkEditor
- Editor Tutorials
- SparkConsole
- SparkDaemon
- Shader Pipeline
- Asset Pipeline
- Asset Validation
- Asset Migration
- Game Packaging
- Online Services
- DataTable System
- Loot and Crafting System
- CSG System
- Font System
- Timer Manager
- Movie Render Pipeline
- HLOD and World Partition
- Remote Debug System
- Selection Manager
- Asset Dependency Graph
- Editor Automation
- File Watcher
- Project Templates
- System Requirements
- VR Support
- Mobile Platform
- Accessibility
- Platform Input
- Cross-Compilation: Wine Testing
- RHI Abstraction Layer
- D3D11 Backend
- D3D12 Backend
- Vulkan Backend
- OpenGL Backend
- Metal Backend
- DXR Raytracing
- Hybrid Ray Tracing
- Upscaling (DLSS/FSR)
- Render Graph
- Shader Graph
- GPU Particles
- GPU-Driven Rendering
- Volumetric Fog
- Volumetric Clouds
- Global Illumination
- Virtual Texturing
- Water Rendering
- Clustered Lighting
- Material System
- Post-Processing
- Shadow System
- Particle System
- Decal System
- Sky and Atmosphere
- Foliage System
- Mesh Shaders
- Neural Rendering
- Configuration Reference
- Performance Tips
- Benchmark Framework
- Threading Model
- Memory Safety
- Memory Management Patterns
- Build System and CMake Modules
- Profiler and Debugging
- Performance Profiling Guide
- Telemetry System
- Golden Image Testing
- Utilities
- Testing
- Codebase Statistics
- Codebase Health
- Error Handling Patterns
- Hot Reload Overview
- Troubleshooting
- Contributing
- Workflow Patterns
- Build Optimizations
- CI Reproducible Builds
- GitHub API and PR Checks
- Git Rebase Conflicts
- Clang-Format
- Code Quality Violations
- AI Bloat Pattern
- MinGW + Wine Cross-Compilation
- Live Editor Testing
- Engine & Renderer Landscape
- DuetOS Portability Catalog
- Five-Engine Analysis
- Eleven-Engine Analysis
- ThorVG / Unity Graphics Analysis
- Advanced Techniques Catalog
- Third-Party Library Evaluation
- Engine Viability Evaluation
- Engine Feature Recommendations
- Project Recommendations
- Mac Compatibility Analysis
- Codebase Observations
- Codebase Bloat Audit
- Test Suite Audit
- Documentation Coverage Audit
- ThirdParty Dependencies Audit
- Load Test Baseline
- Gameplay Systems Status
- SparkGame Module Status
- Stub and Abandoned Features
- Memory Integrity System
- Memory Safety Evaluation
- Hardware Acceleration Systems
- Jolt Physics Integration
- GPU/CPU Separation Plan
- Daemon Services Architecture
- Reflection & Polymorphism Refactoring Plan
- SparkBuild In-Tree
- Wine No-JobSystem Breakthrough
- Wine Role and Fallback Tiers