From 0417fbf5ba2e74b2517a6dd140c30f7deb748066 Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Thu, 8 Jan 2026 00:33:17 +0000 Subject: [PATCH] feat(tests): add unit tests for render coordinator initialization order --- CMakeLists.txt | 12 ++ .../impl/render_coordinator_service.cpp | 18 +++ tests/render_coordinator_init_order_test.cpp | 109 ++++++++++++++++++ 3 files changed, 139 insertions(+) create mode 100644 tests/render_coordinator_init_order_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2db2537..1c6af19 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -552,6 +552,18 @@ target_link_libraries(bgfx_frame_requirement_test PRIVATE SDL3::SDL3 ) add_test(NAME bgfx_frame_requirement_test COMMAND bgfx_frame_requirement_test) + +# Test: Render coordinator initialization order (TDD guard for shader load timing) +add_executable(render_coordinator_init_order_test + tests/render_coordinator_init_order_test.cpp + src/services/impl/render_coordinator_service.cpp +) +target_include_directories(render_coordinator_init_order_test PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src") +target_link_libraries(render_coordinator_init_order_test PRIVATE + GTest::gtest + GTest::gtest_main +) +add_test(NAME render_coordinator_init_order_test COMMAND render_coordinator_init_order_test) endif() if(ENABLE_VITA) diff --git a/src/services/impl/render_coordinator_service.cpp b/src/services/impl/render_coordinator_service.cpp index 3642831..b210d25 100644 --- a/src/services/impl/render_coordinator_service.cpp +++ b/src/services/impl/render_coordinator_service.cpp @@ -48,6 +48,24 @@ void RenderCoordinatorService::RenderFrame(float time) { } return; } + if (logger_) { + logger_->Trace("RenderCoordinatorService", "RenderFrame", + "Priming bgfx with a dummy frame before shader load"); + } + if (!graphicsService_->BeginFrame()) { + if (logger_) { + logger_->Warn("RenderCoordinatorService::RenderFrame: Swapchain out of date during shader pre-frame"); + } + graphicsService_->RecreateSwapchain(); + return; + } + if (!graphicsService_->EndFrame()) { + if (logger_) { + logger_->Warn("RenderCoordinatorService::RenderFrame: Swapchain out of date during shader pre-frame"); + } + graphicsService_->RecreateSwapchain(); + return; + } if (logger_) { logger_->Trace("RenderCoordinatorService", "RenderFrame", "Loading shaders from Lua"); } diff --git a/tests/render_coordinator_init_order_test.cpp b/tests/render_coordinator_init_order_test.cpp new file mode 100644 index 0000000..0227485 --- /dev/null +++ b/tests/render_coordinator_init_order_test.cpp @@ -0,0 +1,109 @@ +#include + +#include "services/impl/render_coordinator_service.hpp" +#include "services/interfaces/i_graphics_service.hpp" +#include "services/interfaces/i_shader_script_service.hpp" + +#include +#include +#include +#include +#include + +namespace { + +class CallOrderGraphicsService : public sdl3cpp::services::IGraphicsService { +public: + std::vector calls; + bool beginFrameResult = true; + bool endFrameResult = true; + + void InitializeDevice(SDL_Window*, const sdl3cpp::services::GraphicsConfig&) override {} + void InitializeSwapchain() override {} + void RecreateSwapchain() override { calls.push_back("RecreateSwapchain"); } + void Shutdown() noexcept override {} + void LoadShaders(const std::unordered_map&) override { + calls.push_back("LoadShaders"); + } + void UploadVertexData(const std::vector&) override {} + void UploadIndexData(const std::vector&) override {} + bool BeginFrame() override { + calls.push_back("BeginFrame"); + return beginFrameResult; + } + void RenderScene(const std::vector&, + const sdl3cpp::services::ViewState&) override {} + bool EndFrame() override { + calls.push_back("EndFrame"); + return endFrameResult; + } + void WaitIdle() override {} + sdl3cpp::services::GraphicsDeviceHandle GetDevice() const override { return nullptr; } + sdl3cpp::services::GraphicsDeviceHandle GetPhysicalDevice() const override { return nullptr; } + std::pair GetSwapchainExtent() const override { return {0, 0}; } + uint32_t GetSwapchainFormat() const override { return 0; } + void* GetCurrentCommandBuffer() const override { return nullptr; } + void* GetGraphicsQueue() const override { return nullptr; } +}; + +class StubShaderScriptService : public sdl3cpp::services::IShaderScriptService { +public: + std::unordered_map LoadShaderPathsMap() override { + return {}; + } +}; + +std::string JoinCalls(const std::vector& calls) { + std::string joined; + for (size_t index = 0; index < calls.size(); ++index) { + if (index > 0) { + joined += " -> "; + } + joined += calls[index]; + } + return joined; +} + +bool HasEndFrameBeforeLoadShaders(const std::vector& calls) { + for (const auto& call : calls) { + if (call == "LoadShaders") { + return false; + } + if (call == "EndFrame") { + return true; + } + } + return false; +} + +TEST(RenderCoordinatorInitOrderTest, LoadsShadersOnlyAfterFirstFrame) { + auto graphicsService = std::make_shared(); + auto shaderScriptService = std::make_shared(); + + sdl3cpp::services::impl::RenderCoordinatorService service( + nullptr, + graphicsService, + nullptr, + shaderScriptService, + nullptr, + nullptr, + nullptr); + + service.RenderFrame(0.0f); + + const bool hasLoadShaders = std::find( + graphicsService->calls.begin(), + graphicsService->calls.end(), + "LoadShaders") != graphicsService->calls.end(); + + if (!hasLoadShaders) { + SUCCEED() << "RenderFrame did not load shaders; initialization is expected elsewhere."; + return; + } + + EXPECT_TRUE(HasEndFrameBeforeLoadShaders(graphicsService->calls)) + << "LoadShaders was called before a completed frame. Calls: " + << JoinCalls(graphicsService->calls); +} + +} // namespace