feat: Enhance rendering services with pipeline and buffer support, and add shader path resolution

This commit is contained in:
2026-01-05 05:50:41 +00:00
parent e5a41be694
commit e7737c60f9
10 changed files with 288 additions and 15 deletions

View File

@@ -292,6 +292,8 @@ void ServiceBasedApp::RegisterServices() {
registry_.RegisterService<services::IRenderCommandService, services::impl::RenderCommandService>(
registry_.GetService<services::IVulkanDeviceService>(),
registry_.GetService<services::ISwapchainService>(),
registry_.GetService<services::IPipelineService>(),
registry_.GetService<services::IBufferService>(),
registry_.GetService<services::ILogger>());
// Graphics service (facade)
@@ -327,6 +329,7 @@ void ServiceBasedApp::RegisterServices() {
registry_.GetService<services::ILogger>(),
registry_.GetService<services::IGraphicsService>(),
registry_.GetService<services::ISceneScriptService>(),
registry_.GetService<services::IShaderScriptService>(),
registry_.GetService<services::IGuiScriptService>(),
registry_.GetService<services::IGuiService>(),
registry_.GetService<services::ISceneService>());

View File

@@ -7,14 +7,24 @@ namespace sdl3cpp::services::impl {
RenderCommandService::RenderCommandService(std::shared_ptr<IVulkanDeviceService> deviceService,
std::shared_ptr<ISwapchainService> swapchainService,
std::shared_ptr<IPipelineService> pipelineService,
std::shared_ptr<IBufferService> bufferService,
std::shared_ptr<ILogger> logger)
: deviceService_(std::move(deviceService)),
swapchainService_(std::move(swapchainService)),
pipelineService_(std::move(pipelineService)),
bufferService_(std::move(bufferService)),
logger_(logger) {
if (logger_) {
logger_->Trace("RenderCommandService", "RenderCommandService",
"deviceService=" + std::string(deviceService_ ? "set" : "null") +
", swapchainService=" + std::string(swapchainService_ ? "set" : "null"));
", swapchainService=" + std::string(swapchainService_ ? "set" : "null") +
", pipelineService=" + std::string(pipelineService_ ? "set" : "null") +
", bufferService=" + std::string(bufferService_ ? "set" : "null"));
}
if (!deviceService_ || !swapchainService_ || !pipelineService_ || !bufferService_) {
throw std::invalid_argument("All render command dependencies must be provided");
}
}
@@ -117,8 +127,55 @@ void RenderCommandService::RecordCommands(uint32_t imageIndex,
vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
// Record draw commands (placeholder - actual drawing would go here)
// This would bind vertex buffers, index buffers, pipelines, and issue draw calls
VkBuffer vertexBuffer = bufferService_->GetVertexBuffer();
VkBuffer indexBuffer = bufferService_->GetIndexBuffer();
if (vertexBuffer == VK_NULL_HANDLE || indexBuffer == VK_NULL_HANDLE) {
logger_->Error("RenderCommandService: Vertex or index buffer not initialized");
} else if (commands.empty()) {
logger_->Trace("RenderCommandService", "RecordCommands", "No render commands to draw");
} else {
VkDeviceSize offsets[] = {0};
vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vertexBuffer, offsets);
vkCmdBindIndexBuffer(commandBuffer, indexBuffer, 0, VK_INDEX_TYPE_UINT16);
VkPipelineLayout pipelineLayout = pipelineService_->GetPipelineLayout();
if (pipelineLayout == VK_NULL_HANDLE) {
logger_->Error("RenderCommandService: Pipeline layout is not initialized");
} else {
if (logger_) {
logger_->Trace("RenderCommandService", "RecordCommands",
"drawing commands=" + std::to_string(commands.size()));
}
size_t drawIndex = 0;
for (const auto& command : commands) {
if (!pipelineService_->HasShader(command.shaderKey)) {
logger_->Error("RenderCommandService: Missing pipeline for shader key: " + command.shaderKey);
continue;
}
if (logger_) {
logger_->Trace("RenderCommandService", "RecordCommands",
"draw=" + std::to_string(drawIndex) +
", shaderKey=" + command.shaderKey +
", indexOffset=" + std::to_string(command.indexOffset) +
", indexCount=" + std::to_string(command.indexCount) +
", vertexOffset=" + std::to_string(command.vertexOffset));
}
VkPipeline pipeline = pipelineService_->GetPipeline(command.shaderKey);
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
core::PushConstants pushConstants{};
pushConstants.model = command.modelMatrix;
pushConstants.viewProj = viewProj;
vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0,
sizeof(core::PushConstants), &pushConstants);
vkCmdDrawIndexed(commandBuffer, command.indexCount, 1, command.indexOffset, command.vertexOffset, 0);
++drawIndex;
}
}
}
vkCmdEndRenderPass(commandBuffer);
vkEndCommandBuffer(commandBuffer);

View File

@@ -1,6 +1,8 @@
#pragma once
#include "../interfaces/i_render_command_service.hpp"
#include "../interfaces/i_buffer_service.hpp"
#include "../interfaces/i_pipeline_service.hpp"
#include "../interfaces/i_vulkan_device_service.hpp"
#include "../interfaces/i_swapchain_service.hpp"
#include "../interfaces/i_logger.hpp"
@@ -21,6 +23,8 @@ class RenderCommandService : public IRenderCommandService,
public:
explicit RenderCommandService(std::shared_ptr<IVulkanDeviceService> deviceService,
std::shared_ptr<ISwapchainService> swapchainService,
std::shared_ptr<IPipelineService> pipelineService,
std::shared_ptr<IBufferService> bufferService,
std::shared_ptr<ILogger> logger);
~RenderCommandService() override;
@@ -49,6 +53,8 @@ public:
private:
std::shared_ptr<IVulkanDeviceService> deviceService_;
std::shared_ptr<ISwapchainService> swapchainService_;
std::shared_ptr<IPipelineService> pipelineService_;
std::shared_ptr<IBufferService> bufferService_;
std::shared_ptr<ILogger> logger_;
VkCommandPool commandPool_ = VK_NULL_HANDLE;

View File

@@ -5,12 +5,14 @@ namespace sdl3cpp::services::impl {
RenderCoordinatorService::RenderCoordinatorService(std::shared_ptr<ILogger> logger,
std::shared_ptr<IGraphicsService> graphicsService,
std::shared_ptr<ISceneScriptService> sceneScriptService,
std::shared_ptr<IShaderScriptService> shaderScriptService,
std::shared_ptr<IGuiScriptService> guiScriptService,
std::shared_ptr<IGuiService> guiService,
std::shared_ptr<ISceneService> sceneService)
: logger_(std::move(logger)),
graphicsService_(std::move(graphicsService)),
sceneScriptService_(std::move(sceneScriptService)),
shaderScriptService_(std::move(shaderScriptService)),
guiScriptService_(std::move(guiScriptService)),
guiService_(std::move(guiService)),
sceneService_(std::move(sceneService)) {
@@ -18,6 +20,7 @@ RenderCoordinatorService::RenderCoordinatorService(std::shared_ptr<ILogger> logg
logger_->Trace("RenderCoordinatorService", "RenderCoordinatorService",
"graphicsService=" + std::string(graphicsService_ ? "set" : "null") +
", sceneScriptService=" + std::string(sceneScriptService_ ? "set" : "null") +
", shaderScriptService=" + std::string(shaderScriptService_ ? "set" : "null") +
", guiScriptService=" + std::string(guiScriptService_ ? "set" : "null") +
", guiService=" + std::string(guiService_ ? "set" : "null") +
", sceneService=" + std::string(sceneService_ ? "set" : "null"),
@@ -38,15 +41,54 @@ void RenderCoordinatorService::RenderFrame(float time) {
return;
}
if (!shadersLoaded_) {
if (!shaderScriptService_) {
if (logger_) {
logger_->Error("RenderCoordinatorService::RenderFrame: Shader script service not available");
}
return;
}
if (logger_) {
logger_->Trace("RenderCoordinatorService", "RenderFrame", "Loading shaders from Lua");
}
auto shaders = shaderScriptService_->LoadShaderPathsMap();
graphicsService_->LoadShaders(shaders);
shadersLoaded_ = true;
}
graphicsService_->BeginFrame();
if (sceneScriptService_ && sceneService_) {
auto sceneObjects = sceneScriptService_->LoadSceneObjects();
sceneService_->LoadScene(sceneObjects);
const auto& vertices = sceneService_->GetCombinedVertices();
const auto& indices = sceneService_->GetCombinedIndices();
bool geometryChanged = vertices.size() != lastVertexCount_ || indices.size() != lastIndexCount_;
if (!vertices.empty() && !indices.empty() && (!geometryUploaded_ || geometryChanged)) {
if (logger_) {
logger_->Trace("RenderCoordinatorService", "RenderFrame",
"Uploading geometry vertices=" + std::to_string(vertices.size()) +
", indices=" + std::to_string(indices.size()));
}
graphicsService_->UploadVertexData(vertices);
graphicsService_->UploadIndexData(indices);
geometryUploaded_ = true;
lastVertexCount_ = vertices.size();
lastIndexCount_ = indices.size();
} else if (logger_) {
logger_->Trace("RenderCoordinatorService", "RenderFrame",
"Geometry upload skipped (vertices=" + std::to_string(vertices.size()) +
", indices=" + std::to_string(indices.size()) +
", uploaded=" + std::string(geometryUploaded_ ? "true" : "false") +
", changed=" + std::string(geometryChanged ? "true" : "false") + ")");
}
auto renderCommands = sceneService_->GetRenderCommands(time);
float aspect = 1920.0f / 1080.0f;
auto extent = graphicsService_->GetSwapchainExtent();
float aspect = extent.height == 0 ? 1.0f
: static_cast<float>(extent.width) / static_cast<float>(extent.height);
auto viewProj = sceneScriptService_->GetViewProjectionMatrix(aspect);
graphicsService_->RenderScene(renderCommands, viewProj);

View File

@@ -7,6 +7,7 @@
#include "../interfaces/i_logger.hpp"
#include "../interfaces/i_scene_script_service.hpp"
#include "../interfaces/i_scene_service.hpp"
#include "../interfaces/i_shader_script_service.hpp"
#include <memory>
namespace sdl3cpp::services::impl {
@@ -16,6 +17,7 @@ public:
RenderCoordinatorService(std::shared_ptr<ILogger> logger,
std::shared_ptr<IGraphicsService> graphicsService,
std::shared_ptr<ISceneScriptService> sceneScriptService,
std::shared_ptr<IShaderScriptService> shaderScriptService,
std::shared_ptr<IGuiScriptService> guiScriptService,
std::shared_ptr<IGuiService> guiService,
std::shared_ptr<ISceneService> sceneService);
@@ -27,9 +29,14 @@ private:
std::shared_ptr<ILogger> logger_;
std::shared_ptr<IGraphicsService> graphicsService_;
std::shared_ptr<ISceneScriptService> sceneScriptService_;
std::shared_ptr<IShaderScriptService> shaderScriptService_;
std::shared_ptr<IGuiScriptService> guiScriptService_;
std::shared_ptr<IGuiService> guiService_;
std::shared_ptr<ISceneService> sceneService_;
size_t lastVertexCount_ = 0;
size_t lastIndexCount_ = 0;
bool shadersLoaded_ = false;
bool geometryUploaded_ = false;
};
} // namespace sdl3cpp::services::impl

View File

@@ -1,4 +1,5 @@
#include "scene_service.hpp"
#include <limits>
#include <stdexcept>
namespace sdl3cpp::services::impl {
@@ -24,7 +25,79 @@ void SceneService::LoadScene(const std::vector<SceneObject>& objects) {
logger_->Trace("SceneService", "LoadScene",
"objects.size=" + std::to_string(objects.size()));
sceneObjects_ = objects;
combinedVertices_.clear();
combinedIndices_.clear();
drawInfos_.clear();
if (objects.empty()) {
initialized_ = false;
return;
}
size_t totalVertices = 0;
size_t totalIndices = 0;
for (const auto& obj : objects) {
totalVertices += obj.vertices.size();
totalIndices += obj.indices.size();
}
constexpr size_t kMaxIndexValue = std::numeric_limits<uint16_t>::max();
if (totalVertices > kMaxIndexValue) {
if (logger_) {
logger_->Error("Scene vertex count exceeds uint16_t index range");
}
throw std::runtime_error("Scene vertex count exceeds uint16_t index range");
}
combinedVertices_.reserve(totalVertices);
combinedIndices_.reserve(totalIndices);
drawInfos_.reserve(objects.size());
for (const auto& obj : objects) {
if (obj.vertices.empty() || obj.indices.empty()) {
if (logger_) {
logger_->Error("Scene object missing vertex or index data");
}
throw std::runtime_error("Scene object missing vertex or index data");
}
size_t vertexOffset = combinedVertices_.size();
if (vertexOffset + obj.vertices.size() > kMaxIndexValue) {
if (logger_) {
logger_->Error("Scene vertex data exceeds uint16_t index range");
}
throw std::runtime_error("Scene vertex data exceeds uint16_t index range");
}
uint32_t indexOffset = static_cast<uint32_t>(combinedIndices_.size());
combinedVertices_.insert(combinedVertices_.end(), obj.vertices.begin(), obj.vertices.end());
combinedIndices_.reserve(combinedIndices_.size() + obj.indices.size());
for (uint16_t index : obj.indices) {
uint32_t adjusted = static_cast<uint32_t>(index) + static_cast<uint32_t>(vertexOffset);
if (adjusted > kMaxIndexValue) {
if (logger_) {
logger_->Error("Index offset exceeds uint16_t range");
}
throw std::runtime_error("Index offset exceeds uint16_t range");
}
combinedIndices_.push_back(index);
}
SceneDrawInfo drawInfo;
drawInfo.indexOffset = indexOffset;
drawInfo.indexCount = static_cast<uint32_t>(obj.indices.size());
drawInfo.vertexOffset = static_cast<int32_t>(vertexOffset);
drawInfo.computeModelMatrixRef = obj.computeModelMatrixRef;
drawInfo.shaderKey = obj.shaderKey;
drawInfos_.push_back(std::move(drawInfo));
}
if (logger_) {
logger_->Trace("SceneService", "LoadScene",
"combinedVertices=" + std::to_string(combinedVertices_.size()) +
", combinedIndices=" + std::to_string(combinedIndices_.size()) +
", drawCalls=" + std::to_string(drawInfos_.size()));
}
initialized_ = true;
}
@@ -46,32 +119,44 @@ std::vector<RenderCommand> SceneService::GetRenderCommands(float time) const {
}
std::vector<RenderCommand> commands;
commands.reserve(sceneObjects_.size());
commands.reserve(drawInfos_.size());
for (const auto& obj : sceneObjects_) {
for (const auto& drawInfo : drawInfos_) {
RenderCommand cmd;
cmd.indexOffset = 0; // Assume each object has its own index buffer
cmd.indexCount = static_cast<uint32_t>(obj.indices.size());
cmd.vertexOffset = 0; // Assume each object has its own vertex buffer
cmd.shaderKey = obj.shaderKey;
cmd.modelMatrix = scriptService_->ComputeModelMatrix(obj.computeModelMatrixRef, time);
cmd.indexOffset = drawInfo.indexOffset;
cmd.indexCount = drawInfo.indexCount;
cmd.vertexOffset = drawInfo.vertexOffset;
cmd.shaderKey = drawInfo.shaderKey;
cmd.modelMatrix = scriptService_->ComputeModelMatrix(drawInfo.computeModelMatrixRef, time);
commands.push_back(cmd);
}
return commands;
}
const std::vector<core::Vertex>& SceneService::GetCombinedVertices() const {
logger_->Trace("SceneService", "GetCombinedVertices");
return combinedVertices_;
}
const std::vector<uint16_t>& SceneService::GetCombinedIndices() const {
logger_->Trace("SceneService", "GetCombinedIndices");
return combinedIndices_;
}
void SceneService::Clear() {
logger_->Trace("SceneService", "Clear");
sceneObjects_.clear();
combinedVertices_.clear();
combinedIndices_.clear();
drawInfos_.clear();
initialized_ = false;
}
size_t SceneService::GetObjectCount() const {
logger_->Trace("SceneService", "GetObjectCount");
return sceneObjects_.size();
return drawInfos_.size();
}
void SceneService::Shutdown() noexcept {

View File

@@ -25,6 +25,8 @@ public:
void LoadScene(const std::vector<SceneObject>& objects) override;
void UpdateScene(float deltaTime) override;
std::vector<RenderCommand> GetRenderCommands(float time) const override;
const std::vector<core::Vertex>& GetCombinedVertices() const override;
const std::vector<uint16_t>& GetCombinedIndices() const override;
void Clear() override;
size_t GetObjectCount() const override;
@@ -32,9 +34,19 @@ public:
void Shutdown() noexcept override;
private:
struct SceneDrawInfo {
uint32_t indexOffset = 0;
uint32_t indexCount = 0;
int32_t vertexOffset = 0;
int computeModelMatrixRef = -1;
std::string shaderKey;
};
std::shared_ptr<ISceneScriptService> scriptService_;
std::shared_ptr<ILogger> logger_;
std::vector<SceneObject> sceneObjects_;
std::vector<core::Vertex> combinedVertices_;
std::vector<uint16_t> combinedIndices_;
std::vector<SceneDrawInfo> drawInfos_;
bool initialized_ = false;
};

View File

@@ -5,9 +5,11 @@
#include <lua.hpp>
#include <filesystem>
#include <stdexcept>
#include <string>
#include <unordered_map>
#include <vector>
#include <utility>
namespace sdl3cpp::services::impl {
@@ -102,9 +104,53 @@ ShaderPaths ShaderScriptService::ReadShaderPathsTable(lua_State* L, int index) c
paths.fragment = lua_tostring(L, -1);
lua_pop(L, 1);
paths.vertex = ResolveShaderPath(paths.vertex);
paths.fragment = ResolveShaderPath(paths.fragment);
return paths;
}
std::string ShaderScriptService::ResolveShaderPath(const std::string& path) const {
if (path.empty()) {
return path;
}
std::filesystem::path rawPath(path);
if (rawPath.is_absolute()) {
return rawPath.string();
}
std::vector<std::filesystem::path> searchRoots;
if (engineService_) {
auto scriptDir = engineService_->GetScriptDirectory();
if (!scriptDir.empty()) {
auto projectRoot = scriptDir.parent_path();
if (!projectRoot.empty()) {
searchRoots.push_back(projectRoot);
}
searchRoots.push_back(scriptDir);
}
}
searchRoots.push_back(std::filesystem::current_path());
for (const auto& root : searchRoots) {
std::filesystem::path candidate = root / rawPath;
if (std::filesystem::exists(candidate)) {
if (logger_) {
logger_->Trace("ShaderScriptService", "ResolveShaderPath",
"path=" + path + ", resolved=" + candidate.string());
}
return candidate.string();
}
}
if (logger_) {
logger_->Trace("ShaderScriptService", "ResolveShaderPath",
"path=" + path + ", resolved=unmodified");
}
return rawPath.string();
}
lua_State* ShaderScriptService::GetLuaState() const {
if (logger_) {
logger_->Trace("ShaderScriptService", "GetLuaState");

View File

@@ -22,6 +22,7 @@ public:
private:
lua_State* GetLuaState() const;
ShaderPaths ReadShaderPathsTable(lua_State* L, int index) const;
std::string ResolveShaderPath(const std::string& path) const;
std::shared_ptr<IScriptEngineService> engineService_;
std::shared_ptr<ILogger> logger_;

View File

@@ -42,6 +42,20 @@ public:
*/
virtual std::vector<RenderCommand> GetRenderCommands(float time) const = 0;
/**
* @brief Get combined vertex data for the current scene.
*
* @return Reference to the combined vertex array
*/
virtual const std::vector<core::Vertex>& GetCombinedVertices() const = 0;
/**
* @brief Get combined index data for the current scene.
*
* @return Reference to the combined index array
*/
virtual const std::vector<uint16_t>& GetCombinedIndices() const = 0;
/**
* @brief Clear the scene (remove all objects).
*/