From e2d6e1b4ada6888ae597578124cdb57597aebd75 Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Wed, 7 Jan 2026 16:36:33 +0000 Subject: [PATCH] feat(tests): Add Vulkan shader linking test for GUI shader compilation and rendering validation --- CMakeLists.txt | 32 ++++ tests/test_vulkan_shader_linking.cpp | 240 +++++++++++++++++++++++++++ 2 files changed, 272 insertions(+) create mode 100644 tests/test_vulkan_shader_linking.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 63d6f8e..0aa77ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -369,6 +369,38 @@ else() message(FATAL_ERROR "shaderc CMake target not found") endif() add_test(NAME bgfx_gui_service_tests COMMAND bgfx_gui_service_tests) + +# Test: Vulkan shader linking (TDD test for walls/ceiling/floor rendering) +add_executable(vulkan_shader_linking_test + tests/test_vulkan_shader_linking.cpp + src/services/impl/bgfx_graphics_backend.cpp + src/services/impl/bgfx_gui_service.cpp + src/services/impl/bgfx_shader_compiler.cpp + src/services/impl/json_config_service.cpp + src/services/impl/materialx_shader_generator.cpp + src/services/impl/platform_service.cpp + src/services/impl/sdl_window_service.cpp + src/events/event_bus.cpp + src/stb_image.cpp +) +target_include_directories(vulkan_shader_linking_test PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src") +target_link_libraries(vulkan_shader_linking_test PRIVATE + ${SDL_TARGET} + ${SDL3CPP_RENDER_STACK_LIBS} + ${SDL3CPP_FREETYPE_LIBS} + ${SDL3CPP_STB_LIBS} + lunasvg::lunasvg + rapidjson + glm::glm +) +if(TARGET shaderc::shaderc) + target_link_libraries(vulkan_shader_linking_test PRIVATE shaderc::shaderc) +elseif(TARGET shaderc::shaderc_combined) + target_link_libraries(vulkan_shader_linking_test PRIVATE shaderc::shaderc_combined) +else() + message(FATAL_ERROR "shaderc CMake target not found") +endif() +add_test(NAME vulkan_shader_linking_test COMMAND vulkan_shader_linking_test) endif() if(ENABLE_VITA) diff --git a/tests/test_vulkan_shader_linking.cpp b/tests/test_vulkan_shader_linking.cpp new file mode 100644 index 0000000..9f6a080 --- /dev/null +++ b/tests/test_vulkan_shader_linking.cpp @@ -0,0 +1,240 @@ +#include "services/impl/bgfx_graphics_backend.hpp" +#include "services/impl/bgfx_gui_service.hpp" +#include "services/impl/json_config_service.hpp" +#include "services/impl/platform_service.hpp" +#include "services/impl/sdl_window_service.hpp" +#include "services/interfaces/i_logger.hpp" +#include "services/interfaces/gui_types.hpp" +#include "events/event_bus.hpp" + +#include +#include + +#include +#include +#include +#include + +namespace { + +class TestLogger : public sdl3cpp::services::ILogger { +public: + void SetLevel(sdl3cpp::services::LogLevel level) override { level_ = level; } + sdl3cpp::services::LogLevel GetLevel() const override { return level_; } + void SetOutputFile(const std::string& filename) override { (void)filename; } + void SetMaxLinesPerFile(size_t maxLines) override { (void)maxLines; } + void EnableConsoleOutput(bool enable) override { consoleEnabled_ = enable; } + + void Log(sdl3cpp::services::LogLevel level, const std::string& message) override { + if (level < level_) { + return; + } + if (consoleEnabled_) { + std::cout << message << '\n'; + } + if (level == sdl3cpp::services::LogLevel::ERROR) { + errorMessages_.push_back(message); + } + } + + void Trace(const std::string& message) override { Log(sdl3cpp::services::LogLevel::TRACE, message); } + void Trace(const std::string& className, const std::string& methodName, + const std::string& args = "", const std::string& message = "") override { + std::string formattedMessage = className + "::" + methodName; + if (!args.empty()) { + formattedMessage += "(" + args + ")"; + } + if (!message.empty()) { + formattedMessage += ": " + message; + } + Log(sdl3cpp::services::LogLevel::TRACE, formattedMessage); + } + void Debug(const std::string& message) override { Log(sdl3cpp::services::LogLevel::DEBUG, message); } + void Info(const std::string& message) override { Log(sdl3cpp::services::LogLevel::INFO, message); } + void Warn(const std::string& message) override { Log(sdl3cpp::services::LogLevel::WARN, message); } + void Error(const std::string& message) override { Log(sdl3cpp::services::LogLevel::ERROR, message); } + void TraceFunction(const std::string& funcName) override { Trace("Entering " + funcName); } + void TraceVariable(const std::string& name, const std::string& value) override { + Trace(name + " = " + value); + } + void TraceVariable(const std::string& name, int value) override { + TraceVariable(name, std::to_string(value)); + } + void TraceVariable(const std::string& name, size_t value) override { + TraceVariable(name, std::to_string(value)); + } + void TraceVariable(const std::string& name, bool value) override { + TraceVariable(name, std::string(value ? "true" : "false")); + } + void TraceVariable(const std::string& name, float value) override { + TraceVariable(name, std::to_string(value)); + } + void TraceVariable(const std::string& name, double value) override { + TraceVariable(name, std::to_string(value)); + } + + bool HasError(const std::string& substring) const { + for (const auto& msg : errorMessages_) { + if (msg.find(substring) != std::string::npos) { + return true; + } + } + return false; + } + + const std::vector& GetErrors() const { return errorMessages_; } + +private: + sdl3cpp::services::LogLevel level_ = sdl3cpp::services::LogLevel::TRACE; + bool consoleEnabled_ = false; + std::vector errorMessages_; +}; + +void Assert(bool condition, const std::string& message, int& failures) { + if (!condition) { + std::cerr << "TEST FAILED: " << message << '\n'; + ++failures; + } +} + +} // namespace + +// TDD Test: Verify GUI shaders compile and link successfully +// This test validates that walls/ceiling/floor render correctly by ensuring +// the GUI shader program links without errors. +int main() { + int failures = 0; + + auto logger = std::make_shared(); + logger->EnableConsoleOutput(true); + logger->SetLevel(sdl3cpp::services::LogLevel::TRACE); + + // Setup config with seed_runtime.json (production config that was failing) + std::filesystem::path configPath = "config/seed_runtime.json"; + if (!std::filesystem::exists(configPath)) { + std::cerr << "SKIPPED: Config file not found: " << configPath << '\n'; + return 0; + } + + std::cout << "Loading config from: " << configPath << '\n'; + auto configService = std::make_shared(logger, configPath, false); + auto platformService = std::make_shared(logger); + + // Setup window service + auto eventBus = std::make_shared(); + auto windowService = std::make_shared( + logger, platformService, eventBus); + + // Try to create window with available drivers + const char* preferredDrivers[] = {"x11", "wayland", "offscreen", "dummy", nullptr}; + SDL_Window* window = nullptr; + bool windowCreated = false; + + std::cout << "Attempting to create window...\n"; + for (const char* driver : preferredDrivers) { + if (driver) { + SDL_SetHint(SDL_HINT_VIDEO_DRIVER, driver); + } + + try { + windowService->Initialize(); + sdl3cpp::services::WindowConfig config{}; + config.width = configService->GetWindowWidth(); + config.height = configService->GetWindowHeight(); + config.title = configService->GetWindowTitle(); + config.resizable = false; + windowService->CreateWindow(config); + window = windowService->GetNativeHandle(); + windowCreated = true; + std::cout << "Created window with driver: " << (driver ? driver : "default") << '\n'; + break; + } catch (const std::exception& ex) { + std::cerr << "Window creation failed with driver " << (driver ? driver : "default") << ": " << ex.what() << '\n'; + windowService->Shutdown(); + windowService = std::make_shared( + logger, platformService, eventBus); + } + } + + if (!windowCreated) { + std::cerr << "SKIPPED: Could not create SDL window\n"; + return 0; + } + + // Initialize graphics backend (this will init bgfx with production renderer settings) + sdl3cpp::services::impl::BgfxGraphicsBackend backend(configService, platformService, logger); + + std::cout << "Attempting to initialize bgfx...\n"; + try { + sdl3cpp::services::GraphicsConfig graphicsConfig{}; + backend.Initialize(window, graphicsConfig); + std::cout << "bgfx initialized successfully\n"; + } catch (const std::exception& ex) { + std::cerr << "bgfx init exception: " << ex.what() << '\n'; + backend.Shutdown(); + windowService->Shutdown(); + return 0; + } + + const bgfx::RendererType::Enum rendererType = bgfx::getRendererType(); + std::cout << "Testing with renderer: " << bgfx::getRendererName(rendererType) << '\n'; + + if (rendererType == bgfx::RendererType::Noop) { + std::cerr << "SKIPPED: Got Noop renderer\n"; + backend.Shutdown(); + windowService->Shutdown(); + return 0; + } + + // Now test GUI service shader compilation and linking (same as production) + sdl3cpp::services::impl::BgfxGuiService guiService(configService, logger); + + // This triggers shader compilation and program linking + const uint32_t width = configService->GetWindowWidth(); + const uint32_t height = configService->GetWindowHeight(); + guiService.PrepareFrame({}, width, height); + + // CRITICAL TESTS: These are the exact errors from current_problem/README.md + // If these errors occur, walls/ceiling/floor won't render! + + Assert(!logger->HasError("failed to link shaders"), + "FAIL: Shader program linking failed - walls/ceiling/floor won't render!", + failures); + + Assert(!logger->HasError("Failed to create GUI shader program"), + "FAIL: GUI shader program creation failed - walls/ceiling/floor won't render!", + failures); + + Assert(guiService.IsProgramReady(), + "FAIL: GUI program not ready - walls/ceiling/floor won't render!", + failures); + + // Print any errors that occurred + const auto& errors = logger->GetErrors(); + if (!errors.empty()) { + std::cerr << "\nErrors during shader compilation/linking:\n"; + for (const auto& error : errors) { + std::cerr << " - " << error << '\n'; + } + std::cerr << "\nThese errors will cause missing geometry (walls/ceiling/floor) in the rendered scene.\n"; + } + + if (failures == 0) { + std::cout << "\nSUCCESS: GUI shaders compiled and linked successfully with " + << bgfx::getRendererName(rendererType) << '\n'; + std::cout << "Walls, ceiling, and floor should render correctly in production.\n"; + } + + guiService.Shutdown(); + backend.Shutdown(); + windowService->Shutdown(); + + if (failures == 0) { + std::cout << "\nvulkan_shader_linking_test: PASSED\n"; + } else { + std::cerr << "\nvulkan_shader_linking_test: FAILED (" << failures << " errors)\n"; + std::cerr << "Fix: Ensure shader uniforms are properly embedded in shader binaries.\n"; + } + + return failures; +}