Skip to content

Refactor Event System #33

Description

@renanbomtempo

Problem

The current event system is built on virtual inheritance and RTTI. Every event carries a vtable pointer and a virtual destructor, dispatching goes through dynamic_cast, and event categories are represented as bitfield enums that layers test at runtime. This adds overhead at every dispatch site, forces all event types into a single inheritance hierarchy, and makes it impossible for user-defined events to participate cleanly in the same system.

The goal of this refactor is to eliminate all of that with a design that has zero runtime overhead for type identification, no heap allocation in the hot path, and a clean separation between hardware/OS events (which need ordered, synchronous propagation) and other subsystem events (which benefit from a decoupled pub/sub model).

Proposed Solution

Event types become plain structs satisfying an IsEvent concept (standard layout, trivially copyable, named). There are no base classes, no RTTI, and no global type registries.

Replace the current system with a hybrid architecture:

  • Input events use a closed std::variant (InputEvent) dispatched back-to-front through the LayerStack. This preserves the existing propagation semantics with no virtual calls.
  • Non-input events use a new EventBus with a subscriber registry,
  • compile-time sequential type IDs, supporting both immediate dispatch and deferred dispatch with a double-buffered queue safe for cross-thread use.

Key Design Decisions

Area Decision
Input/Window routing std::variant-based InputEventData, dispatched through Layer::OnEvent() back-to-front
Gameplay/Subsystem routing EventBus with pub/sub, immediate + double-buffered queued
Type identification consteval FNV-1a hash of T::Name (deterministic, no global state)
Event categories Removed; ImGuiLayer uses IsMouseEvent() / IsKeyboardEvent() helpers
Logging fmt::formatter<T> specializations, external to event structs
Queue buffer size 64 KB default per buffer

Tasks

Core

  • Event.cppm: complete rewrite with plain structs, IsEvent concept,
  • InputEvents.cppm: InputEvent variant, RoutedInputEvent, InputEventDispatcher, IsMouseEvent/IsKeyboardEvent helpers, fmt formatters
  • EventBus.cppm / EventBus.cpp: new pub/sub system with compile-time sequential type IDs, Subscribe, Unsubscribe, Dispatch, QueueEvent, Flush, double-buffered queue write buffer
  • CMakeLists.txt: add EventBus.cppm / EventBus.cpp, remove AsyncEventBus files

Interface updates (depend on Event.cppm)

  • Layer.cppm: OnEvent(Event&)OnEvent(InputEventData&)
  • Window.cppm: EventCallbackFn type updated to std::function<void(InputEventData&)>
  • GlfwWindow.cpp: GLFW callbacks construct plain structs wrapped in InputEventData
  • Application.cppm / Application.cpp: update OnEvent, add import Nodens.EventBus, add EventBus::Get().flush() to main loop
  • ImGuiLayer.cppm / ImGuiLayer.cpp: OnEvent(InputEventData&), use IsMouseEvent / IsKeyboardEvent helpers
  • Nodens.cppm: replace export import Nodens.AsyncEventBus with export import Nodens.EventBus

Examples

  • Rename 'AsyncEvent' example to 'PlanetaryScan'
  • PlanetaryScanLayer.cppm / .cpp: PlanetaryScanEvent becomes a plain struct; subscribe via EventBus
  • CircularWave3DLayer.cppm / .cpp: OnEvent updated to RoutedInputEvent&, dispatch via new EventDispatcher

Cleanup

  • Delete AsyncEventBus.cppm and AsyncEventBus.cpp

Breaking Changes

This is a breaking change for any downstream code using the Layer interface.

Acceptance Criteria

  • All targets build without errors
  • example-asyncevent: scatter plot and latency monitor populate correctly at runtime
  • example-circularwave3d: rendering is correct and window resize works
  • ImGui input blocking: mouse/keyboard events captured by ImGui do not leak to game layers
  • No references to AsyncEventBus, EventType, EventCategory, or the Event base class remain in the codebase
  • Wiki entry about the new architecture

Metadata

Metadata

Assignees

Type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions