refactor: Restructure GUI renderer implementation and update service dependencies

This commit is contained in:
2026-01-04 23:59:09 +00:00
parent c727d7ff38
commit bb497aaae2
7 changed files with 38 additions and 43 deletions

View File

@@ -152,7 +152,7 @@ if(BUILD_SDL3_APP)
src/services/impl/scene_service.cpp
src/services/impl/graphics_service.cpp
src/app/service_based_app.cpp
src/gui/gui_renderer.cpp
src/services/impl/gui_renderer.cpp
)
target_include_directories(sdl3_app PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src")
target_link_libraries(sdl3_app PRIVATE ${SDL_TARGET} Vulkan::Vulkan lua::lua CLI11::CLI11 rapidjson assimp::assimp Bullet::Bullet glm::glm Vorbis::vorbisfile Vorbis::vorbis)

View File

@@ -1,4 +1,4 @@
#include "gui/gui_renderer.hpp"
#include "gui_renderer.hpp"
#include <algorithm>
#include <array>
@@ -14,14 +14,9 @@
#include "../../third_party/font8x8_basic.h"
namespace services = sdl3cpp::services;
namespace sdl3cpp::gui {
namespace sdl3cpp::services::impl {
namespace {
using ParsedSvg = sdl3cpp::gui::ParsedSvg;
using SvgCircle = sdl3cpp::gui::SvgCircle;
bool ExtractAttribute(const std::string& source, const char* name, std::string& outValue) {
std::string key = name;
size_t pos = source.find(key);
@@ -61,7 +56,7 @@ float ParseFloatValue(const std::string& text) {
}
}
services::GuiColor ParseColorString(const std::string& text, const services::GuiColor& fallback) {
GuiColor ParseColorString(const std::string& text, const GuiColor& fallback) {
if (text.empty() || text[0] != '#') {
return fallback;
}
@@ -141,9 +136,9 @@ ParsedSvg ParseSvgFile(const std::filesystem::path& path) {
return result;
}
services::GuiCommand::RectData IntersectRect(const services::GuiCommand::RectData& a,
const services::GuiCommand::RectData& b) {
services::GuiCommand::RectData result;
GuiCommand::RectData IntersectRect(const GuiCommand::RectData& a,
const GuiCommand::RectData& b) {
GuiCommand::RectData result;
result.x = std::max(a.x, b.x);
result.y = std::max(a.y, b.y);
float right = std::min(a.x + a.width, b.x + b.width);
@@ -161,7 +156,7 @@ int ClampToRange(int value, int minimum, int maximum) {
class GuiRenderer::Canvas {
public:
using RectData = services::GuiCommand::RectData;
using RectData = GuiCommand::RectData;
void Resize(uint32_t width, uint32_t height) {
width_ = width;
@@ -187,8 +182,8 @@ public:
}
}
void FillRect(const RectData& rect, const services::GuiColor& fillColor,
const services::GuiColor& borderColor, float borderWidth) {
void FillRect(const RectData& rect, const GuiColor& fillColor,
const GuiColor& borderColor, float borderWidth) {
DrawFilledRect(rect, fillColor);
if (borderWidth > 0.0f && borderColor.a > 0.0f) {
DrawFilledRect({rect.x, rect.y, rect.width, borderWidth}, borderColor);
@@ -199,7 +194,7 @@ public:
}
}
void DrawText(const std::string& text, const services::GuiColor& color, const RectData& bounds,
void DrawText(const std::string& text, const GuiColor& color, const RectData& bounds,
const std::string& alignX, const std::string& alignY, float fontSize) {
if (text.empty() || width_ == 0 || height_ == 0) {
return;
@@ -244,7 +239,7 @@ public:
}
}
void DrawSvg(const ParsedSvg& svg, const RectData& target, const services::GuiColor& tint) {
void DrawSvg(const ParsedSvg& svg, const RectData& target, const GuiColor& tint) {
if (svg.circles.empty() || svg.viewWidth <= 0.0f || svg.viewHeight <= 0.0f || width_ == 0 ||
height_ == 0) {
return;
@@ -260,7 +255,7 @@ public:
float cx = clipped.x + circle.cx * scaleX;
float cy = clipped.y + circle.cy * scaleY;
float radius = circle.r * scale;
services::GuiColor color = circle.color;
GuiColor color = circle.color;
if (tint.a > 0.0f) {
color.r *= tint.r;
color.g *= tint.g;
@@ -300,7 +295,7 @@ private:
return clipped;
}
void DrawFilledRect(const RectData& rect, const services::GuiColor& color) {
void DrawFilledRect(const RectData& rect, const GuiColor& color) {
if (rect.width <= 0.0f || rect.height <= 0.0f) {
return;
}
@@ -319,7 +314,7 @@ private:
}
}
void BlendPixel(int x, int y, const services::GuiColor& color) {
void BlendPixel(int x, int y, const GuiColor& color) {
size_t index = (static_cast<size_t>(y) * width_ + static_cast<size_t>(x)) * 4;
auto clampByte = [](float value) -> uint8_t {
return static_cast<uint8_t>(std::clamp(value, 0.0f, 1.0f) * 255.0f);
@@ -348,7 +343,7 @@ private:
GuiRenderer::GuiRenderer(VkDevice device, VkPhysicalDevice physicalDevice, VkFormat swapchainFormat,
const std::filesystem::path& scriptDirectory,
std::shared_ptr<services::IBufferService> bufferService)
std::shared_ptr<IBufferService> bufferService)
: device_(device),
physicalDevice_(physicalDevice),
swapchainFormat_(swapchainFormat),
@@ -365,7 +360,7 @@ GuiRenderer::GuiRenderer(VkDevice device, VkPhysicalDevice physicalDevice, VkFor
return canvasWidth_ > 0 && canvasHeight_ > 0 && stagingBuffer_ != VK_NULL_HANDLE;
}
void GuiRenderer::Prepare(const std::vector<services::GuiCommand>& commands, uint32_t width,
void GuiRenderer::Prepare(const std::vector<GuiCommand>& commands, uint32_t width,
uint32_t height) {
if (width == 0 || height == 0 || !canvas_) {
return;
@@ -374,10 +369,10 @@ GuiRenderer::GuiRenderer(VkDevice device, VkPhysicalDevice physicalDevice, VkFor
canvas_->Clear();
for (const auto& command : commands) {
switch (command.type) {
case services::GuiCommand::Type::Rect:
case GuiCommand::Type::Rect:
canvas_->FillRect(command.rect, command.color, command.borderColor, command.borderWidth);
break;
case services::GuiCommand::Type::Text: {
case GuiCommand::Type::Text: {
if (command.hasClipRect) {
canvas_->PushClip(command.clipRect);
}
@@ -385,7 +380,7 @@ GuiRenderer::GuiRenderer(VkDevice device, VkPhysicalDevice physicalDevice, VkFor
canvas_->DrawText(command.text, command.color, command.bounds, command.alignX,
command.alignY, command.fontSize);
} else {
services::GuiCommand::RectData fallback{
GuiCommand::RectData fallback{
command.rect.x, command.rect.y,
command.fontSize * static_cast<float>(std::max<size_t>(1, command.text.size())), command.fontSize};
canvas_->DrawText(command.text, command.color, fallback, command.alignX,
@@ -396,13 +391,13 @@ GuiRenderer::GuiRenderer(VkDevice device, VkPhysicalDevice physicalDevice, VkFor
}
break;
}
case services::GuiCommand::Type::ClipPush:
case GuiCommand::Type::ClipPush:
canvas_->PushClip(command.rect);
break;
case services::GuiCommand::Type::ClipPop:
case GuiCommand::Type::ClipPop:
canvas_->PopClip();
break;
case services::GuiCommand::Type::Svg:
case GuiCommand::Type::Svg:
if (command.svgPath.empty()) {
break;
}
@@ -569,4 +564,4 @@ GuiRenderer::GuiRenderer(VkDevice device, VkPhysicalDevice physicalDevice, VkFor
}
}
} // namespace sdl3cpp::gui
} // namespace sdl3cpp::services::impl

View File

@@ -1,5 +1,5 @@
#ifndef SDL3CPP_GUI_GUI_RENDERER_HPP
#define SDL3CPP_GUI_GUI_RENDERER_HPP
#ifndef SDL3CPP_SERVICES_GUI_RENDERER_HPP
#define SDL3CPP_SERVICES_GUI_RENDERER_HPP
#include <filesystem>
#include <memory>
@@ -11,13 +11,13 @@
#include "services/interfaces/gui_types.hpp"
#include "services/interfaces/i_buffer_service.hpp"
namespace sdl3cpp::gui {
namespace sdl3cpp::services::impl {
struct SvgCircle {
float cx = 0.0f;
float cy = 0.0f;
float r = 0.0f;
services::GuiColor color{1.0f, 1.0f, 1.0f, 1.0f};
GuiColor color{1.0f, 1.0f, 1.0f, 1.0f};
};
struct ParsedSvg {
@@ -30,13 +30,13 @@ class GuiRenderer {
public:
GuiRenderer(VkDevice device, VkPhysicalDevice physicalDevice, VkFormat swapchainFormat,
const std::filesystem::path& scriptDirectory,
std::shared_ptr<services::IBufferService> bufferService);
std::shared_ptr<IBufferService> bufferService);
~GuiRenderer();
GuiRenderer(const GuiRenderer&) = delete;
GuiRenderer& operator=(const GuiRenderer&) = delete;
void Prepare(const std::vector<services::GuiCommand>& commands, uint32_t width,
void Prepare(const std::vector<GuiCommand>& commands, uint32_t width,
uint32_t height);
void BlitToSwapchain(VkCommandBuffer commandBuffer, VkImage image);
void Resize(uint32_t width, uint32_t height, VkFormat format);
@@ -65,9 +65,9 @@ private:
uint32_t canvasHeight_ = 0;
std::unique_ptr<Canvas> canvas_;
std::unordered_map<std::string, ParsedSvg> svgCache_;
std::shared_ptr<services::IBufferService> bufferService_;
std::shared_ptr<IBufferService> bufferService_;
};
} // namespace sdl3cpp::gui
} // namespace sdl3cpp::services::impl
#endif // SDL3CPP_GUI_GUI_RENDERER_HPP
#endif // SDL3CPP_SERVICES_GUI_RENDERER_HPP

View File

@@ -18,7 +18,7 @@ void GuiRendererService::Initialize(VkDevice device,
if (logger_) {
logger_->TraceFunction(__func__);
}
renderer_ = std::make_unique<sdl3cpp::gui::GuiRenderer>(
renderer_ = std::make_unique<GuiRenderer>(
device, physicalDevice, format, resourcePath, bufferService_);
}

View File

@@ -4,7 +4,7 @@
#include "../interfaces/i_gui_renderer_service.hpp"
#include "../interfaces/i_logger.hpp"
#include "../../di/lifecycle.hpp"
#include "../../gui/gui_renderer.hpp"
#include "gui_renderer.hpp"
#include <memory>
namespace sdl3cpp::services::impl {
@@ -35,7 +35,7 @@ public:
private:
std::shared_ptr<ILogger> logger_;
std::shared_ptr<IBufferService> bufferService_;
std::unique_ptr<sdl3cpp::gui::GuiRenderer> renderer_;
std::unique_ptr<GuiRenderer> renderer_;
};
} // namespace sdl3cpp::services::impl

View File

@@ -11,7 +11,7 @@ namespace sdl3cpp::services::impl {
/**
* @brief Vulkan GUI service implementation.
*
* Small wrapper service (~60 lines) around GuiRenderer.
* Small wrapper service (~60 lines) around the GUI renderer service.
* Provides 2D GUI overlay rendering for SVG, text, and shapes.
*/
class VulkanGuiService : public IGuiService,

View File

@@ -14,7 +14,7 @@ struct GuiCommand;
* @brief GUI rendering service interface.
*
* Handles 2D GUI overlay rendering using Vulkan.
* Wraps the GuiRenderer class with support for SVG, text, and shapes.
* Delegates low-level draw work to the GUI renderer service.
*/
class IGuiService {
public: