diff --git a/modules/BgfxRenderer/BgfxRendererModule.cpp b/modules/BgfxRenderer/BgfxRendererModule.cpp index d209007..7030fec 100644 --- a/modules/BgfxRenderer/BgfxRendererModule.cpp +++ b/modules/BgfxRenderer/BgfxRendererModule.cpp @@ -6,6 +6,7 @@ #include "RenderGraph/RenderGraph.h" #include "Scene/SceneCollector.h" #include "Resources/ResourceCache.h" +#include "Debug/DebugOverlay.h" #include "Passes/ClearPass.h" #include "Passes/SpritePass.h" #include "Passes/DebugPass.h" @@ -104,6 +105,14 @@ void BgfxRendererModule::setConfiguration(const IDataNode& config, IIO* io, ITas // Setup resource cache m_resourceCache = std::make_unique(); + // Setup debug overlay + m_debugOverlay = std::make_unique(); + bool debugEnabled = config.getBool("debugOverlay", false); + m_debugOverlay->setEnabled(debugEnabled); + if (debugEnabled) { + m_logger->info("Debug overlay enabled"); + } + // Load default texture if specified in config std::string defaultTexturePath = config.getString("defaultTexture", ""); if (!defaultTexturePath.empty()) { @@ -138,10 +147,16 @@ void BgfxRendererModule::process(const IDataNode& input) { // 4. Execute render graph m_renderGraph->execute(frame, *m_device); - // 5. Present + // 5. Update and render debug overlay + if (m_debugOverlay) { + m_debugOverlay->update(deltaTime, static_cast(frame.spriteCount), 1); + m_debugOverlay->render(m_width, m_height); + } + + // 6. Present m_device->frame(); - // 6. Cleanup for next frame + // 7. Cleanup for next frame m_sceneCollector->clear(); m_frameCount++; } diff --git a/modules/BgfxRenderer/BgfxRendererModule.h b/modules/BgfxRenderer/BgfxRendererModule.h index 6feb140..9e7a965 100644 --- a/modules/BgfxRenderer/BgfxRendererModule.h +++ b/modules/BgfxRenderer/BgfxRendererModule.h @@ -17,6 +17,7 @@ class SceneCollector; class ResourceCache; class ShaderManager; class SpritePass; +class DebugOverlay; // ============================================================================ // BgfxRenderer Module - 2D rendering via bgfx @@ -55,6 +56,7 @@ private: std::unique_ptr m_renderGraph; std::unique_ptr m_sceneCollector; std::unique_ptr m_resourceCache; + std::unique_ptr m_debugOverlay; // Pass references (non-owning, owned by RenderGraph) SpritePass* m_spritePass = nullptr; diff --git a/modules/BgfxRenderer/CMakeLists.txt b/modules/BgfxRenderer/CMakeLists.txt index 814e797..40161a9 100644 --- a/modules/BgfxRenderer/CMakeLists.txt +++ b/modules/BgfxRenderer/CMakeLists.txt @@ -58,6 +58,9 @@ add_library(BgfxRenderer SHARED # Resources Resources/ResourceCache.cpp Resources/TextureLoader.cpp + + # Debug + Debug/DebugOverlay.cpp ) target_include_directories(BgfxRenderer PRIVATE diff --git a/modules/BgfxRenderer/Debug/DebugOverlay.cpp b/modules/BgfxRenderer/Debug/DebugOverlay.cpp new file mode 100644 index 0000000..5069d61 --- /dev/null +++ b/modules/BgfxRenderer/Debug/DebugOverlay.cpp @@ -0,0 +1,73 @@ +#include "DebugOverlay.h" +#include + +namespace grove { + +void DebugOverlay::update(float deltaTime, uint32_t spriteCount, uint32_t drawCalls) { + m_deltaTime = deltaTime; + m_frameTimeMs = deltaTime * 1000.0f; + m_fps = deltaTime > 0.0f ? 1.0f / deltaTime : 0.0f; + m_spriteCount = spriteCount; + m_drawCalls = drawCalls; + + // Smooth FPS over time + m_fpsAccum += deltaTime; + m_fpsFrameCount++; + + if (m_fpsAccum >= FPS_UPDATE_INTERVAL) { + m_smoothedFps = static_cast(m_fpsFrameCount) / m_fpsAccum; + m_fpsAccum = 0.0f; + m_fpsFrameCount = 0; + } +} + +void DebugOverlay::render(uint16_t screenWidth, uint16_t screenHeight) { + if (!m_enabled) { + return; + } + + // Enable debug text rendering + bgfx::setDebug(BGFX_DEBUG_TEXT); + + // Clear debug text buffer + bgfx::dbgTextClear(); + + // Calculate text columns based on screen width (8 pixels per char typically) + // uint16_t cols = screenWidth / 8; + (void)screenWidth; + (void)screenHeight; + + // Header + bgfx::dbgTextPrintf(1, 1, 0x0f, "GroveEngine Debug Overlay"); + bgfx::dbgTextPrintf(1, 2, 0x07, "========================"); + + // FPS and frame time + uint8_t fpsColor = 0x0a; // Green + if (m_smoothedFps < 30.0f) { + fpsColor = 0x0c; // Red + } else if (m_smoothedFps < 55.0f) { + fpsColor = 0x0e; // Yellow + } + bgfx::dbgTextPrintf(1, 4, fpsColor, "FPS: %.1f", m_smoothedFps); + bgfx::dbgTextPrintf(1, 5, 0x07, "Frame: %.2f ms", m_frameTimeMs); + + // Rendering stats + bgfx::dbgTextPrintf(1, 7, 0x07, "Sprites: %u", m_spriteCount); + bgfx::dbgTextPrintf(1, 8, 0x07, "Draw calls: %u", m_drawCalls); + + // bgfx stats + const bgfx::Stats* stats = bgfx::getStats(); + if (stats) { + bgfx::dbgTextPrintf(1, 10, 0x07, "GPU time: %.2f ms", + double(stats->gpuTimeEnd - stats->gpuTimeBegin) * 1000.0 / stats->gpuTimerFreq); + bgfx::dbgTextPrintf(1, 11, 0x07, "CPU submit: %.2f ms", + double(stats->cpuTimeEnd - stats->cpuTimeBegin) * 1000.0 / stats->cpuTimerFreq); + bgfx::dbgTextPrintf(1, 12, 0x07, "Primitives: %u", stats->numPrims[bgfx::Topology::TriList]); + bgfx::dbgTextPrintf(1, 13, 0x07, "Textures: %u / %u", stats->numTextures, stats->textureMemoryUsed / 1024); + } + + // Instructions + bgfx::dbgTextPrintf(1, 15, 0x08, "Press F3 to toggle overlay"); +} + +} // namespace grove diff --git a/modules/BgfxRenderer/Debug/DebugOverlay.h b/modules/BgfxRenderer/Debug/DebugOverlay.h new file mode 100644 index 0000000..64b317d --- /dev/null +++ b/modules/BgfxRenderer/Debug/DebugOverlay.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include + +namespace grove { + +/** + * @brief Debug overlay for displaying runtime stats + * + * Uses bgfx debug text to display FPS, frame time, sprite count, etc. + * Can be toggled on/off at runtime. + */ +class DebugOverlay { +public: + DebugOverlay() = default; + + // Enable/disable overlay + void setEnabled(bool enabled) { m_enabled = enabled; } + bool isEnabled() const { return m_enabled; } + void toggle() { m_enabled = !m_enabled; } + + // Update stats (call each frame) + void update(float deltaTime, uint32_t spriteCount, uint32_t drawCalls); + + // Render the overlay (call after bgfx::frame setup, before submit) + void render(uint16_t screenWidth, uint16_t screenHeight); + +private: + bool m_enabled = false; + + // Stats tracking + float m_deltaTime = 0.0f; + float m_fps = 0.0f; + float m_frameTimeMs = 0.0f; + uint32_t m_spriteCount = 0; + uint32_t m_drawCalls = 0; + + // FPS smoothing + float m_fpsAccum = 0.0f; + int m_fpsFrameCount = 0; + float m_smoothedFps = 0.0f; + static constexpr float FPS_UPDATE_INTERVAL = 0.25f; // Update FPS display every 250ms +}; + +} // namespace grove diff --git a/tests/visual/test_23_bgfx_sprites_visual.cpp b/tests/visual/test_23_bgfx_sprites_visual.cpp index 1846fa7..1afb2c7 100644 --- a/tests/visual/test_23_bgfx_sprites_visual.cpp +++ b/tests/visual/test_23_bgfx_sprites_visual.cpp @@ -123,6 +123,9 @@ int main(int argc, char* argv[]) { // Load texture from assets folder config.setString("defaultTexture", "../../assets/textures/1f440.png"); + // Enable debug overlay + config.setBool("debugOverlay", true); + module->setConfiguration(config, rendererIO.get(), nullptr); std::cout << "Module configured\n";