Skip to content

Commit ac6b8ec

Browse files
committed
feat(ui): add runtime debug overlay
1 parent dc0520c commit ac6b8ec

49 files changed

Lines changed: 3243 additions & 27 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/windows-ci.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,25 @@ jobs:
170170
shell: msys2 {0}
171171
run: ./build/model-consumer/${{ matrix.name }}/PyramidModelConsumer.exe
172172

173+
- name: Configure standalone UI consumer
174+
shell: msys2 {0}
175+
run: >-
176+
cmake -S Tests/UIConsumer
177+
-B build/ui-consumer/${{ matrix.name }}
178+
-G Ninja
179+
-DCMAKE_BUILD_TYPE=${{ matrix.configuration }}
180+
-DCMAKE_C_COMPILER=${{ matrix.compiler_c }}
181+
-DCMAKE_CXX_COMPILER=${{ matrix.compiler_cxx }}
182+
-DCMAKE_PREFIX_PATH="$PWD/build/install/${{ matrix.name }}"
183+
184+
- name: Build standalone UI consumer
185+
shell: msys2 {0}
186+
run: cmake --build build/ui-consumer/${{ matrix.name }} --parallel
187+
188+
- name: Run standalone UI consumer
189+
shell: msys2 {0}
190+
run: ./build/ui-consumer/${{ matrix.name }}/PyramidUIConsumer.exe
191+
173192
- name: Upload binaries
174193
if: always()
175194
uses: actions/upload-artifact@v4

AGENTS.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## Scope
44

5-
- `Engine/` builds the C++17 `PyramidEngine` library. `Libraries/` owns independently testable `PyramidFoundation`, `PyramidMath`, `PyramidInput`, `PyramidImage`, and `PyramidModel` packages.
5+
- `Engine/` builds the C++17 `PyramidEngine` library. `Libraries/` owns independently testable `PyramidFoundation`, `PyramidMath`, `PyramidInput`, `PyramidImage`, `PyramidModel`, `PyramidText`, and `PyramidUI` packages.
66
- Engine-owned modules are Core, Graphics, and Win32/WGL Platform. Foundational types/logging, math, physical/action input, image codecs, and CPU asset-format parsing must remain outside the engine binary.
77
- `Examples/BasicGame` and `Examples/BasicRendering` are the graphical references; `Examples/RTSReference` is reusable game-side support and is not part of the installed engine API.
88
- `Tests/PublicApiLinkage.cpp` protects selected public symbols. Focused tests cover platform input state, generic action mapping, reusable camera controllers, game-side RTS interaction, resize behavior, camera/frustum logic, framebuffers, texture loading, spatial updates/queries/configuration/compaction, render bounds, resources/caches, registry handles/manifests, the authoritative entity scene, and scene serialization. `Tests/EntitySceneTests.cpp` protects stable entity IDs, cycle-safe hierarchy transforms, inherited visibility, component attachment, generated renderer/light proxies, and recursive destruction. `Tests/SceneSerializationTests.cpp` protects deterministic version-2 entity/component round trips, exact manifest references, hierarchy validation, and missing/stale resource diagnostics.
@@ -32,7 +32,7 @@ ctest --preset test-gcc-release
3232

3333
- C++17, four spaces, braces on new lines.
3434
- Types/public methods use `PascalCase`; locals/parameters use `camelCase`; fields use `m_`.
35-
- Prefer RAII, explicit ownership, and `PYRAMID_LOG_*` diagnostics. CPU model parsing belongs in `Pyramid::Model`; it must not depend on graphics, Win32, OpenGL, or GLAD. Graphics publication must use `ModelResourceImporter` and the existing mesh cache rather than parallel upload ownership. OBJ/MTL changes require positive/negative index, malformed-input, limit, file-resolution, and transactional upload tests. Geometry passed to scenes must use `Mesh`; do not reintroduce raw vertex-array fields on `RenderObject`. Shared reusable geometry should be acquired through the game-owned `ResourceRegistry::Meshes()` cache; do not create parallel uploads for byte-identical mesh specifications. Shared shader programs should be acquired through `ResourceRegistry::Shaders()`; do not mutate cached `ShaderProgram` instances directly or compile identical stage source repeatedly. Shared sampled textures should use `ResourceRegistry::Textures()`; cached `TextureResource` instances are immutable, color space is part of identity, and file changes must be published through transactional reload. Scene renderables should acquire immutable `Material` resources through `ResourceRegistry::Materials()` rather than ad-hoc shader/texture fields; per-draw matrices belong in command-buffer uniforms, not material identity. Platform input must remain backend-neutral at the public API boundary: native messages feed `InputState`, `Game` evaluates generic `InputActionSystem` contexts before updates, and focus loss must release held controls. Never hard-code RTS or other game-specific action names into the input module. Camera controllers must consume configurable named action references, keep physical bindings in examples/games, and distinguish per-frame delta input from time-scaled rate input; reference profiles belong in examples or games. RTS selection, command, unit, ownership, and edge-scroll semantics must remain in game/reference layers rather than `Pyramid::Engine`; focused behavior belongs in `Tests/RTSInteractionTests.cpp`. Long-lived scene and serialized references should use typed registry handles. Handles must stay non-owning, every alias bind/remap/removal must advance its generation, direct cache mutation must invalidate existing handles, and stale handles must resolve to null rather than a replacement resource.
35+
- Prefer RAII, explicit ownership, and `PYRAMID_LOG_*` diagnostics. CPU model parsing belongs in `Pyramid::Model`; it must not depend on graphics, Win32, OpenGL, or GLAD. Graphics publication must use `ModelResourceImporter` and the existing mesh cache rather than parallel upload ownership. OBJ/MTL changes require positive/negative index, malformed-input, limit, file-resolution, and transactional upload tests. Geometry passed to scenes must use `Mesh`; do not reintroduce raw vertex-array fields on `RenderObject`. Shared reusable geometry should be acquired through the game-owned `ResourceRegistry::Meshes()` cache; do not create parallel uploads for byte-identical mesh specifications. Shared shader programs should be acquired through `ResourceRegistry::Shaders()`; do not mutate cached `ShaderProgram` instances directly or compile identical stage source repeatedly. Shared sampled textures should use `ResourceRegistry::Textures()`; cached `TextureResource` instances are immutable, color space is part of identity, and file changes must be published through transactional reload. Scene renderables should acquire immutable `Material` resources through `ResourceRegistry::Materials()` rather than ad-hoc shader/texture fields; per-draw matrices belong in command-buffer uniforms, not material identity. Platform input must remain backend-neutral at the public API boundary: native messages feed `InputState`, `Game` evaluates generic `InputActionSystem` contexts before updates, and focus loss must release held controls. Never hard-code RTS or other game-specific action names into the input module. Camera controllers must consume configurable named action references, keep physical bindings in examples/games, and distinguish per-frame delta input from time-scaled rate input; reference profiles belong in examples or games. UI layout, widget state, hit testing, focus, pointer capture, and draw-list generation belong in `Pyramid::UI`; the library must remain independent of OpenGL, Win32, scenes, and game semantics. Debug and game UI must share this runtime rather than introduce parallel systems. `Pyramid::Text` owns renderer-independent glyph metrics and glyph-run generation. UI layers must reserve handled controls with `InputConsumptionMask` before action-context evaluation, and graphics publication must go through `UIRenderer` rather than placing OpenGL calls in `Pyramid::UI`. RTS selection, command, unit, ownership, and edge-scroll semantics must remain in game/reference layers rather than `Pyramid::Engine`; focused behavior belongs in `Tests/RTSInteractionTests.cpp`. Long-lived scene and serialized references should use typed registry handles. Handles must stay non-owning, every alias bind/remap/removal must advance its generation, direct cache mutation must invalidate existing handles, and stale handles must resolve to null rather than a replacement resource.
3636
- Scene authoring must use `Entity` plus components; do not reintroduce `SceneNode` or make `RenderObject` transforms authoritative. Renderer/light proxies are generated from the scene. Stable entity IDs and hierarchy invariants must remain serialization-safe.
3737
- Do not add required interface methods with silent no-op defaults.
3838
- Do not expose source-tree absolute paths through installed target interfaces.

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,18 @@ All notable changes to Pyramid Engine are documented here. The project is pre-al
44

55
## [Unreleased]
66

7+
### Owned UI foundation and runtime debug overlay
8+
9+
- Added independently installable `PyramidText` and `PyramidUI` targets exported as `Pyramid::Text` and `Pyramid::UI`; both remain platform and renderer independent.
10+
- Added a deterministic embedded ASCII debug atlas, glyph metrics, text measurement, multiline glyph-run generation, and a white atlas texel for solid UI geometry without runtime font files.
11+
- Added hybrid UI contexts whose immediate widget facade reconciles retained element state, stable scoped IDs, vertical/horizontal flow layout, clipping, focus, pointer capture, keyboard navigation, DPI metadata, and batched renderer-independent draw lists.
12+
- Added panels, labels, value rows, separators, spacers, buttons, checkboxes, float sliders, progress bars, and textured-image widgets with configurable themes.
13+
- Added `InputConsumptionMask` and pre-action UI-context registration in `Game`, preventing handled mouse, wheel, drag, and focused keyboard controls from leaking into gameplay action contexts.
14+
- Added graphics-device scissor operations plus `UIRenderer` for batched colored/textured quads, embedded-font rendering, conservative DPI-scaled clip rectangles, alpha blending, registered UI textures, and deterministic baseline-state restoration.
15+
- Added resource-specific UI-renderer initialization rollback and non-canonical `ShaderCache::RemoveAlias()`, preserving unrelated cache-only shaders and textures when GPU buffer allocation fails.
16+
- Added an F1 `BasicGame` overlay with live frame, render, resource, input, UI, animation, and camera controls.
17+
- Added standalone installed `Pyramid::UI` consumer validation and focused `Text.DebugFont`, `UI.Context`, input-consumption, and `Graphics.UIRenderer` coverage; CTest now registers 46 targets.
18+
719
### Build fixes
820

921
- Fixed `WindowResizeEventTests` after the input-library extraction by linking it to `Pyramid::Input`, which propagates the required input and foundation headers.

CMake/PyramidEngineConfig.cmake.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ find_dependency(PyramidMath CONFIG)
77
find_dependency(PyramidImage CONFIG)
88
find_dependency(PyramidInput CONFIG)
99
find_dependency(PyramidModel CONFIG)
10+
find_dependency(PyramidText CONFIG)
11+
find_dependency(PyramidUI CONFIG)
1012

1113
include("${CMAKE_CURRENT_LIST_DIR}/PyramidEngineTargets.cmake")
1214
check_required_components(PyramidEngine)

CMake/PyramidTextConfig.cmake.in

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
@PACKAGE_INIT@
2+
3+
include(CMakeFindDependencyMacro)
4+
find_dependency(PyramidFoundation CONFIG)
5+
find_dependency(PyramidMath CONFIG)
6+
7+
include("${CMAKE_CURRENT_LIST_DIR}/PyramidTextTargets.cmake")
8+
check_required_components(PyramidText)

CMake/PyramidUIConfig.cmake.in

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
@PACKAGE_INIT@
2+
3+
include(CMakeFindDependencyMacro)
4+
find_dependency(PyramidFoundation CONFIG)
5+
find_dependency(PyramidMath CONFIG)
6+
find_dependency(PyramidInput CONFIG)
7+
find_dependency(PyramidText CONFIG)
8+
9+
include("${CMAKE_CURRENT_LIST_DIR}/PyramidUITargets.cmake")
10+
check_required_components(PyramidUI)

CMakeLists.txt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ add_subdirectory(Libraries/PyramidMath)
4040
add_subdirectory(Libraries/PyramidImage)
4141
add_subdirectory(Libraries/PyramidInput)
4242
add_subdirectory(Libraries/PyramidModel)
43+
add_subdirectory(Libraries/PyramidText)
44+
add_subdirectory(Libraries/PyramidUI)
4345
add_subdirectory(Engine)
4446

4547
if(PYRAMID_BUILD_EXAMPLES OR PYRAMID_BUILD_TESTS)
@@ -70,7 +72,7 @@ install(
7072
PATTERN "*.hpp"
7173
)
7274

73-
foreach(library IN ITEMS PyramidFoundation PyramidMath PyramidImage PyramidInput PyramidModel)
75+
foreach(library IN ITEMS PyramidFoundation PyramidMath PyramidImage PyramidInput PyramidModel PyramidText PyramidUI)
7476
install(
7577
DIRECTORY "${PROJECT_SOURCE_DIR}/Libraries/${library}/include/"
7678
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
@@ -89,7 +91,7 @@ install(
8991
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT PyramidEngine
9092
)
9193

92-
foreach(library IN ITEMS PyramidFoundation PyramidMath PyramidImage PyramidInput PyramidModel)
94+
foreach(library IN ITEMS PyramidFoundation PyramidMath PyramidImage PyramidInput PyramidModel PyramidText PyramidUI)
9395
install(
9496
TARGETS ${library}
9597
EXPORT ${library}Targets
@@ -107,7 +109,7 @@ install(
107109
COMPONENT PyramidEngine
108110
)
109111

110-
foreach(library IN ITEMS PyramidFoundation PyramidMath PyramidImage PyramidInput PyramidModel)
112+
foreach(library IN ITEMS PyramidFoundation PyramidMath PyramidImage PyramidInput PyramidModel PyramidText PyramidUI)
111113
install(
112114
EXPORT ${library}Targets
113115
FILE ${library}Targets.cmake
@@ -119,7 +121,7 @@ endforeach()
119121

120122
include(CMakePackageConfigHelpers)
121123

122-
foreach(package IN ITEMS PyramidEngine PyramidFoundation PyramidMath PyramidImage PyramidInput PyramidModel)
124+
foreach(package IN ITEMS PyramidEngine PyramidFoundation PyramidMath PyramidImage PyramidInput PyramidModel PyramidText PyramidUI)
123125
configure_package_config_file(
124126
"${PROJECT_SOURCE_DIR}/CMake/${package}Config.cmake.in"
125127
"${PROJECT_BINARY_DIR}/${package}Config.cmake"

Engine/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ target_link_libraries(PyramidEngine
3030
Pyramid::Input
3131
Pyramid::Image
3232
Pyramid::Model
33+
Pyramid::Text
34+
Pyramid::UI
3335
)
3436

3537
if(WIN32)

Engine/Core/include/Pyramid/Core/Game.hpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
#include <Pyramid/Graphics/GraphicsDevice.hpp>
33
#include <Pyramid/Platform/Window.hpp> // Added for std::unique_ptr<Window>
44
#include <Pyramid/Input/InputActions.hpp>
5+
#include <Pyramid/UI/UI.hpp>
56
#include <memory>
7+
#include <vector>
68

79
namespace Pyramid {
810

@@ -143,6 +145,13 @@ class Game
143145

144146
Renderer::RenderSystem* GetRenderSystem() const { return m_renderSystem; }
145147

148+
/**
149+
* Register a non-owning UI context for pre-action input capture. The context
150+
* must outlive its registration and should be unregistered before destruction.
151+
*/
152+
void RegisterUIContext(UI::Context* context);
153+
void UnregisterUIContext(UI::Context* context);
154+
146155
/**
147156
* @brief Whether the window currently has a non-zero renderable client area.
148157
*/
@@ -155,6 +164,7 @@ class Game
155164
std::unique_ptr<IGraphicsDevice> m_graphicsDevice;
156165
std::unique_ptr<ResourceRegistry> m_resourceRegistry;
157166
InputActionSystem m_inputActions;
167+
std::vector<UI::Context*> m_uiContexts;
158168
Camera* m_activeCamera;
159169
Renderer::RenderSystem* m_renderSystem;
160170
bool m_isRunning;

Engine/Core/source/Game.cpp

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,22 @@ namespace Pyramid
196196
}
197197
}
198198

199+
void Game::RegisterUIContext(UI::Context* context)
200+
{
201+
if (!context || std::find(m_uiContexts.begin(), m_uiContexts.end(), context) != m_uiContexts.end())
202+
{
203+
return;
204+
}
205+
m_uiContexts.push_back(context);
206+
}
207+
208+
void Game::UnregisterUIContext(UI::Context* context)
209+
{
210+
m_uiContexts.erase(
211+
std::remove(m_uiContexts.begin(), m_uiContexts.end(), context),
212+
m_uiContexts.end());
213+
}
214+
199215
void Game::HandleWindowResize(const WindowResizeEvent& event)
200216
{
201217
m_renderSurfaceAvailable = event.HasRenderableArea();
@@ -282,9 +298,21 @@ namespace Pyramid
282298
// Clamp delta time to prevent large jumps (e.g., when debugging or system lag)
283299
deltaTime = (std::min)(deltaTime, maxDeltaTime);
284300

301+
// Give retained UI contexts first refusal over physical controls so
302+
// clicks, wheel input and focused keyboard navigation do not leak into
303+
// lower-priority gameplay action contexts.
304+
InputConsumptionMask uiConsumption;
305+
for (UI::Context* context : m_uiContexts)
306+
{
307+
if (context)
308+
{
309+
uiConsumption.Merge(context->PrepareInput(GetInput()));
310+
}
311+
}
312+
285313
// Evaluate engine-generic action contexts from the completed native
286314
// input snapshot before game logic reads named actions.
287-
m_inputActions.Update(GetInput());
315+
m_inputActions.Update(GetInput(), uiConsumption);
288316

289317
// Update game logic
290318
onUpdate(deltaTime);

0 commit comments

Comments
 (0)