diff --git a/src/app/service_based_app.cpp b/src/app/service_based_app.cpp index 9ebe458..5b8c1dc 100644 --- a/src/app/service_based_app.cpp +++ b/src/app/service_based_app.cpp @@ -292,6 +292,8 @@ void ServiceBasedApp::RegisterServices() { registry_.RegisterService( registry_.GetService(), registry_.GetService(), + registry_.GetService(), + registry_.GetService(), registry_.GetService()); // Graphics service (facade) @@ -327,6 +329,7 @@ void ServiceBasedApp::RegisterServices() { registry_.GetService(), registry_.GetService(), registry_.GetService(), + registry_.GetService(), registry_.GetService(), registry_.GetService(), registry_.GetService()); diff --git a/src/services/impl/render_command_service.cpp b/src/services/impl/render_command_service.cpp index aca5e68..093d481 100644 --- a/src/services/impl/render_command_service.cpp +++ b/src/services/impl/render_command_service.cpp @@ -7,14 +7,24 @@ namespace sdl3cpp::services::impl { RenderCommandService::RenderCommandService(std::shared_ptr deviceService, std::shared_ptr swapchainService, + std::shared_ptr pipelineService, + std::shared_ptr bufferService, std::shared_ptr 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); diff --git a/src/services/impl/render_command_service.hpp b/src/services/impl/render_command_service.hpp index c682ff5..06759d7 100644 --- a/src/services/impl/render_command_service.hpp +++ b/src/services/impl/render_command_service.hpp @@ -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 deviceService, std::shared_ptr swapchainService, + std::shared_ptr pipelineService, + std::shared_ptr bufferService, std::shared_ptr logger); ~RenderCommandService() override; @@ -49,6 +53,8 @@ public: private: std::shared_ptr deviceService_; std::shared_ptr swapchainService_; + std::shared_ptr pipelineService_; + std::shared_ptr bufferService_; std::shared_ptr logger_; VkCommandPool commandPool_ = VK_NULL_HANDLE; diff --git a/src/services/impl/render_coordinator_service.cpp b/src/services/impl/render_coordinator_service.cpp index 1dad955..2b3312f 100644 --- a/src/services/impl/render_coordinator_service.cpp +++ b/src/services/impl/render_coordinator_service.cpp @@ -5,12 +5,14 @@ namespace sdl3cpp::services::impl { RenderCoordinatorService::RenderCoordinatorService(std::shared_ptr logger, std::shared_ptr graphicsService, std::shared_ptr sceneScriptService, + std::shared_ptr shaderScriptService, std::shared_ptr guiScriptService, std::shared_ptr guiService, std::shared_ptr 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 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(extent.width) / static_cast(extent.height); auto viewProj = sceneScriptService_->GetViewProjectionMatrix(aspect); graphicsService_->RenderScene(renderCommands, viewProj); diff --git a/src/services/impl/render_coordinator_service.hpp b/src/services/impl/render_coordinator_service.hpp index b35f0b5..d85eab8 100644 --- a/src/services/impl/render_coordinator_service.hpp +++ b/src/services/impl/render_coordinator_service.hpp @@ -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 namespace sdl3cpp::services::impl { @@ -16,6 +17,7 @@ public: RenderCoordinatorService(std::shared_ptr logger, std::shared_ptr graphicsService, std::shared_ptr sceneScriptService, + std::shared_ptr shaderScriptService, std::shared_ptr guiScriptService, std::shared_ptr guiService, std::shared_ptr sceneService); @@ -27,9 +29,14 @@ private: std::shared_ptr logger_; std::shared_ptr graphicsService_; std::shared_ptr sceneScriptService_; + std::shared_ptr shaderScriptService_; std::shared_ptr guiScriptService_; std::shared_ptr guiService_; std::shared_ptr sceneService_; + size_t lastVertexCount_ = 0; + size_t lastIndexCount_ = 0; + bool shadersLoaded_ = false; + bool geometryUploaded_ = false; }; } // namespace sdl3cpp::services::impl diff --git a/src/services/impl/scene_service.cpp b/src/services/impl/scene_service.cpp index 2f53f81..65c679e 100644 --- a/src/services/impl/scene_service.cpp +++ b/src/services/impl/scene_service.cpp @@ -1,4 +1,5 @@ #include "scene_service.hpp" +#include #include namespace sdl3cpp::services::impl { @@ -24,7 +25,79 @@ void SceneService::LoadScene(const std::vector& 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::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(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(index) + static_cast(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(obj.indices.size()); + drawInfo.vertexOffset = static_cast(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 SceneService::GetRenderCommands(float time) const { } std::vector 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(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& SceneService::GetCombinedVertices() const { + logger_->Trace("SceneService", "GetCombinedVertices"); + return combinedVertices_; +} + +const std::vector& 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 { diff --git a/src/services/impl/scene_service.hpp b/src/services/impl/scene_service.hpp index 109d3fa..9e81786 100644 --- a/src/services/impl/scene_service.hpp +++ b/src/services/impl/scene_service.hpp @@ -25,6 +25,8 @@ public: void LoadScene(const std::vector& objects) override; void UpdateScene(float deltaTime) override; std::vector GetRenderCommands(float time) const override; + const std::vector& GetCombinedVertices() const override; + const std::vector& 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 scriptService_; std::shared_ptr logger_; - std::vector sceneObjects_; + std::vector combinedVertices_; + std::vector combinedIndices_; + std::vector drawInfos_; bool initialized_ = false; }; diff --git a/src/services/impl/shader_script_service.cpp b/src/services/impl/shader_script_service.cpp index 16b8cee..2617a18 100644 --- a/src/services/impl/shader_script_service.cpp +++ b/src/services/impl/shader_script_service.cpp @@ -5,9 +5,11 @@ #include +#include #include #include #include +#include #include 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 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"); diff --git a/src/services/impl/shader_script_service.hpp b/src/services/impl/shader_script_service.hpp index d40887e..07c255c 100644 --- a/src/services/impl/shader_script_service.hpp +++ b/src/services/impl/shader_script_service.hpp @@ -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 engineService_; std::shared_ptr logger_; diff --git a/src/services/interfaces/i_scene_service.hpp b/src/services/interfaces/i_scene_service.hpp index b3ecaf2..c9b31d9 100644 --- a/src/services/interfaces/i_scene_service.hpp +++ b/src/services/interfaces/i_scene_service.hpp @@ -42,6 +42,20 @@ public: */ virtual std::vector 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& GetCombinedVertices() const = 0; + + /** + * @brief Get combined index data for the current scene. + * + * @return Reference to the combined index array + */ + virtual const std::vector& GetCombinedIndices() const = 0; + /** * @brief Clear the scene (remove all objects). */