mirror of
https://github.com/johndoe6345789/SDL3CPlusPlus.git
synced 2026-04-24 21:55:09 +00:00
feat: Implement render graph support in Vulkan backend
- Added RecordRenderGraph method to IRenderCommandService and its implementation in RenderCommandService. - Updated VulkanGraphicsBackend to handle render graph definitions and record commands accordingly. - Introduced GetDepthFormat method in ISwapchainService to retrieve depth buffer format. - Enhanced VulkanGraphicsBackend with methods to set render graph definitions and manage render graph resources. - Added RenderGraphImage structure to manage render targets and depth targets within the render graph. - Updated interfaces and services to accommodate new render graph functionality, including descriptor set layout retrieval in IPipelineService.
This commit is contained in:
@@ -237,6 +237,125 @@ local function resolve_color3(value, fallback)
|
||||
return {fallback[1], fallback[2], fallback[3]}
|
||||
end
|
||||
|
||||
local function resolve_number_optional(value)
|
||||
if type(value) == "number" then
|
||||
return value
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function resolve_color3_optional(value)
|
||||
if type(value) == "table" then
|
||||
local r = tonumber(value[1])
|
||||
local g = tonumber(value[2])
|
||||
local b = tonumber(value[3])
|
||||
if r and g and b then
|
||||
return {r, g, b}
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function format_optional_number(value)
|
||||
if type(value) == "number" then
|
||||
return string_format("%.3f", value)
|
||||
end
|
||||
return "default"
|
||||
end
|
||||
|
||||
local function format_optional_color(value)
|
||||
if type(value) == "table"
|
||||
and type(value[1]) == "number"
|
||||
and type(value[2]) == "number"
|
||||
and type(value[3]) == "number" then
|
||||
return string_format("{%.2f, %.2f, %.2f}", value[1], value[2], value[3])
|
||||
end
|
||||
return "default"
|
||||
end
|
||||
|
||||
local function build_shader_parameter_overrides()
|
||||
if type(config) ~= "table" or type(config.atmospherics) ~= "table" then
|
||||
return {}
|
||||
end
|
||||
|
||||
local atmospherics = config.atmospherics
|
||||
local ambient_strength = resolve_number_optional(atmospherics.ambient_strength)
|
||||
local light_intensity = resolve_number_optional(atmospherics.light_intensity)
|
||||
local key_intensity = resolve_number_optional(atmospherics.key_light_intensity)
|
||||
local fill_intensity = resolve_number_optional(atmospherics.fill_light_intensity)
|
||||
local light_color = resolve_color3_optional(atmospherics.light_color)
|
||||
local pbr_roughness = resolve_number_optional(atmospherics.pbr_roughness)
|
||||
local pbr_metallic = resolve_number_optional(atmospherics.pbr_metallic)
|
||||
|
||||
local parameters = {}
|
||||
|
||||
local function apply_common(key)
|
||||
if ambient_strength == nil and light_intensity == nil and light_color == nil then
|
||||
return
|
||||
end
|
||||
local entry = {}
|
||||
if ambient_strength ~= nil then
|
||||
entry.ambient_strength = ambient_strength
|
||||
end
|
||||
if light_intensity ~= nil then
|
||||
entry.light_intensity = light_intensity
|
||||
end
|
||||
if light_color ~= nil then
|
||||
entry.light_color = light_color
|
||||
end
|
||||
parameters[key] = entry
|
||||
end
|
||||
|
||||
apply_common("solid")
|
||||
apply_common("floor")
|
||||
apply_common("wall")
|
||||
apply_common("ceiling")
|
||||
|
||||
if ambient_strength ~= nil or key_intensity ~= nil or fill_intensity ~= nil or light_intensity ~= nil then
|
||||
local entry = {}
|
||||
if ambient_strength ~= nil then
|
||||
entry.ambient_strength = ambient_strength
|
||||
end
|
||||
if key_intensity ~= nil then
|
||||
entry.key_intensity = key_intensity
|
||||
elseif light_intensity ~= nil then
|
||||
entry.key_intensity = light_intensity
|
||||
end
|
||||
if fill_intensity ~= nil then
|
||||
entry.fill_intensity = fill_intensity
|
||||
elseif light_intensity ~= nil then
|
||||
entry.fill_intensity = light_intensity * 0.45
|
||||
end
|
||||
parameters.default = entry
|
||||
end
|
||||
|
||||
if light_intensity ~= nil or light_color ~= nil or pbr_roughness ~= nil or pbr_metallic ~= nil then
|
||||
local entry = {}
|
||||
if light_intensity ~= nil then
|
||||
entry.light_intensity = light_intensity
|
||||
end
|
||||
if light_color ~= nil then
|
||||
entry.light_color = light_color
|
||||
end
|
||||
if pbr_roughness ~= nil then
|
||||
entry.material_roughness = pbr_roughness
|
||||
end
|
||||
if pbr_metallic ~= nil then
|
||||
entry.material_metallic = pbr_metallic
|
||||
end
|
||||
parameters.pbr = entry
|
||||
end
|
||||
|
||||
if next(parameters) ~= nil then
|
||||
log_debug("Shader lighting overrides: ambient=%s light_intensity=%s light_color=%s",
|
||||
format_optional_number(ambient_strength),
|
||||
format_optional_number(light_intensity),
|
||||
format_optional_color(light_color))
|
||||
end
|
||||
|
||||
return parameters
|
||||
end
|
||||
|
||||
local function apply_skybox_color_from_config()
|
||||
if type(config) ~= "table" then
|
||||
return
|
||||
@@ -335,6 +454,7 @@ end
|
||||
|
||||
local function build_shader_variants()
|
||||
apply_skybox_color_from_config()
|
||||
local shader_parameters = build_shader_parameter_overrides()
|
||||
|
||||
local ok, toolkit = pcall(require, "shader_toolkit")
|
||||
if not ok then
|
||||
@@ -345,7 +465,7 @@ local function build_shader_variants()
|
||||
local output_mode = "source"
|
||||
local compile = false
|
||||
local ok_generate, generated = pcall(toolkit.generate_cube_demo_variants,
|
||||
{compile = compile, output_mode = output_mode})
|
||||
{compile = compile, output_mode = output_mode, parameters = shader_parameters})
|
||||
if not ok_generate then
|
||||
log_debug("Shader generation failed: %s", tostring(generated))
|
||||
return build_static_shader_variants()
|
||||
|
||||
@@ -112,6 +112,9 @@ void GraphicsService::SetRenderGraphDefinition(const RenderGraphDefinition& defi
|
||||
", passes=" + std::to_string(definition.passes.size()));
|
||||
|
||||
renderGraphDefinition_ = definition;
|
||||
if (backend_) {
|
||||
backend_->SetRenderGraphDefinition(definition);
|
||||
}
|
||||
}
|
||||
|
||||
const RenderGraphDefinition& GraphicsService::GetRenderGraphDefinition() const {
|
||||
|
||||
@@ -463,6 +463,10 @@ void GxmGraphicsBackend::SetViewProjection(const std::array<float, 16>& viewProj
|
||||
// Matrix will be set when drawing with specific pipeline
|
||||
}
|
||||
|
||||
void GxmGraphicsBackend::SetRenderGraphDefinition(const RenderGraphDefinition& definition) {
|
||||
(void)definition;
|
||||
}
|
||||
|
||||
void GxmGraphicsBackend::Draw(GraphicsDeviceHandle device, GraphicsPipelineHandle pipeline,
|
||||
GraphicsBufferHandle vertexBuffer, GraphicsBufferHandle indexBuffer,
|
||||
uint32_t indexCount, const std::array<float, 16>& modelMatrix) {
|
||||
|
||||
@@ -37,6 +37,7 @@ public:
|
||||
bool EndFrame(GraphicsDeviceHandle device) override;
|
||||
|
||||
void SetViewProjection(const std::array<float, 16>& viewProj) override;
|
||||
void SetRenderGraphDefinition(const RenderGraphDefinition& definition) override;
|
||||
|
||||
void Draw(GraphicsDeviceHandle device, GraphicsPipelineHandle pipeline,
|
||||
GraphicsBufferHandle vertexBuffer, GraphicsBufferHandle indexBuffer,
|
||||
|
||||
@@ -105,6 +105,10 @@ void PipelineService::Cleanup() {
|
||||
vkDestroyPipelineLayout(device, pipelineLayout_, nullptr);
|
||||
pipelineLayout_ = VK_NULL_HANDLE;
|
||||
}
|
||||
if (descriptorSetLayout_ != VK_NULL_HANDLE) {
|
||||
vkDestroyDescriptorSetLayout(device, descriptorSetLayout_, nullptr);
|
||||
descriptorSetLayout_ = VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
void PipelineService::Shutdown() noexcept {
|
||||
@@ -131,21 +135,42 @@ void PipelineService::CreatePipelineLayout() {
|
||||
|
||||
auto device = deviceService_->GetDevice();
|
||||
|
||||
if (descriptorSetLayout_ != VK_NULL_HANDLE) {
|
||||
vkDestroyDescriptorSetLayout(device, descriptorSetLayout_, nullptr);
|
||||
descriptorSetLayout_ = VK_NULL_HANDLE;
|
||||
}
|
||||
if (pipelineLayout_ != VK_NULL_HANDLE) {
|
||||
vkDestroyPipelineLayout(device, pipelineLayout_, nullptr);
|
||||
pipelineLayout_ = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
VkDescriptorSetLayoutBinding samplerBinding{};
|
||||
samplerBinding.binding = 0;
|
||||
samplerBinding.descriptorCount = 1;
|
||||
samplerBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
samplerBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
|
||||
VkDescriptorSetLayoutCreateInfo setLayoutInfo{};
|
||||
setLayoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
||||
setLayoutInfo.bindingCount = 1;
|
||||
setLayoutInfo.pBindings = &samplerBinding;
|
||||
|
||||
if (vkCreateDescriptorSetLayout(device, &setLayoutInfo, nullptr, &descriptorSetLayout_) != VK_SUCCESS) {
|
||||
throw std::runtime_error("Failed to create descriptor set layout");
|
||||
}
|
||||
|
||||
VkPushConstantRange pushRange{};
|
||||
pushRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
pushRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
pushRange.offset = 0;
|
||||
pushRange.size = sizeof(core::PushConstants);
|
||||
|
||||
VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
|
||||
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
||||
pipelineLayoutInfo.setLayoutCount = 1;
|
||||
pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout_;
|
||||
pipelineLayoutInfo.pushConstantRangeCount = 1;
|
||||
pipelineLayoutInfo.pPushConstantRanges = &pushRange;
|
||||
|
||||
if (pipelineLayout_ != VK_NULL_HANDLE) {
|
||||
vkDestroyPipelineLayout(device, pipelineLayout_, nullptr);
|
||||
pipelineLayout_ = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout_) != VK_SUCCESS) {
|
||||
throw std::runtime_error("Failed to create pipeline layout");
|
||||
}
|
||||
|
||||
@@ -33,6 +33,10 @@ public:
|
||||
logger_->Trace("PipelineService", "GetPipelineLayout");
|
||||
return pipelineLayout_;
|
||||
}
|
||||
VkDescriptorSetLayout GetDescriptorSetLayout() const override {
|
||||
logger_->Trace("PipelineService", "GetDescriptorSetLayout");
|
||||
return descriptorSetLayout_;
|
||||
}
|
||||
bool HasShader(const std::string& key) const override;
|
||||
size_t GetShaderCount() const override {
|
||||
logger_->Trace("PipelineService", "GetShaderCount");
|
||||
@@ -47,6 +51,7 @@ private:
|
||||
std::shared_ptr<ILogger> logger_;
|
||||
|
||||
VkPipelineLayout pipelineLayout_ = VK_NULL_HANDLE;
|
||||
VkDescriptorSetLayout descriptorSetLayout_ = VK_NULL_HANDLE;
|
||||
std::unordered_map<std::string, ShaderPaths> shaderPathMap_;
|
||||
std::unordered_map<std::string, VkPipeline> pipelines_;
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,6 +11,7 @@
|
||||
#include "../interfaces/i_logger.hpp"
|
||||
#include "../../di/lifecycle.hpp"
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace sdl3cpp::services::impl {
|
||||
@@ -40,6 +41,10 @@ public:
|
||||
void RecordCommands(uint32_t imageIndex,
|
||||
const std::vector<RenderCommand>& commands,
|
||||
const std::array<float, 16>& viewProj) override;
|
||||
void RecordRenderGraph(uint32_t imageIndex,
|
||||
const RenderGraphDefinition& graph,
|
||||
const std::vector<RenderCommand>& commands,
|
||||
const std::array<float, 16>& viewProj) override;
|
||||
bool EndFrame(uint32_t imageIndex) override;
|
||||
|
||||
VkCommandBuffer GetCurrentCommandBuffer() const override;
|
||||
@@ -57,6 +62,16 @@ public:
|
||||
void Shutdown() noexcept override;
|
||||
|
||||
private:
|
||||
struct RenderGraphImage {
|
||||
VkImage image = VK_NULL_HANDLE;
|
||||
VkDeviceMemory memory = VK_NULL_HANDLE;
|
||||
VkImageView view = VK_NULL_HANDLE;
|
||||
VkFramebuffer framebuffer = VK_NULL_HANDLE;
|
||||
VkFormat format = VK_FORMAT_UNDEFINED;
|
||||
VkExtent2D extent{};
|
||||
VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
};
|
||||
|
||||
std::shared_ptr<IVulkanDeviceService> deviceService_;
|
||||
std::shared_ptr<ISwapchainService> swapchainService_;
|
||||
std::shared_ptr<IPipelineService> pipelineService_;
|
||||
@@ -73,6 +88,22 @@ private:
|
||||
VkSemaphore renderFinishedSemaphore_ = VK_NULL_HANDLE;
|
||||
VkFence inFlightFence_ = VK_NULL_HANDLE;
|
||||
|
||||
VkDescriptorPool descriptorPool_ = VK_NULL_HANDLE;
|
||||
VkDescriptorSet defaultDescriptorSet_ = VK_NULL_HANDLE;
|
||||
VkDescriptorSet postProcessDescriptorSet_ = VK_NULL_HANDLE;
|
||||
VkSampler sampler_ = VK_NULL_HANDLE;
|
||||
VkImage dummyImage_ = VK_NULL_HANDLE;
|
||||
VkDeviceMemory dummyImageMemory_ = VK_NULL_HANDLE;
|
||||
VkImageView dummyImageView_ = VK_NULL_HANDLE;
|
||||
VkImageLayout dummyImageLayout_ = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
|
||||
VkRenderPass offscreenRenderPass_ = VK_NULL_HANDLE;
|
||||
std::unordered_map<std::string, RenderGraphImage> renderGraphTargets_;
|
||||
std::unordered_map<std::string, RenderGraphImage> renderGraphDepth_;
|
||||
VkExtent2D renderGraphExtent_{};
|
||||
size_t renderGraphResourceCount_ = 0;
|
||||
size_t renderGraphPassCount_ = 0;
|
||||
|
||||
uint32_t currentFrame_ = 0;
|
||||
uint32_t maxFramesInFlight_ = 1; // Single frame in flight for simplicity
|
||||
|
||||
@@ -82,6 +113,33 @@ private:
|
||||
void CreateSyncObjects();
|
||||
void CleanupCommandResources();
|
||||
void CleanupSyncObjects();
|
||||
|
||||
void EnsureDescriptorResources();
|
||||
void CleanupDescriptorResources();
|
||||
void CreateDescriptorPool();
|
||||
void CreateSampler();
|
||||
void CreateDummyImage();
|
||||
void AllocateDescriptorSets();
|
||||
void UpdateDescriptorSet(VkDescriptorSet set, VkImageView view);
|
||||
void EnsureDummyImageLayout(VkCommandBuffer commandBuffer);
|
||||
|
||||
void EnsureRenderGraphResources(const RenderGraphDefinition& graph);
|
||||
void CleanupRenderGraphResources();
|
||||
void RegisterRenderGraphShaders(const RenderGraphDefinition& graph);
|
||||
VkFormat ResolveColorFormat(const std::string& format) const;
|
||||
VkFormat ResolveDepthFormat(const std::string& format) const;
|
||||
VkExtent2D ResolveExtent(const RenderGraphResource& resource) const;
|
||||
RenderGraphImage* FindRenderTarget(const std::string& name);
|
||||
RenderGraphImage* FindDepthTarget(const std::string& name);
|
||||
void CreateRenderGraphImage(RenderGraphImage& image, VkFormat format, VkExtent2D extent,
|
||||
VkImageUsageFlags usage, VkImageAspectFlags aspectMask);
|
||||
void TransitionImageLayout(VkCommandBuffer commandBuffer, RenderGraphImage& image,
|
||||
VkImageLayout newLayout, VkImageAspectFlags aspectMask);
|
||||
bool IsScenePass(const RenderGraphPass& pass) const;
|
||||
bool IsPassEnabled(const RenderGraphPass& pass) const;
|
||||
std::string ResolvePassOutput(const RenderGraphPass& pass) const;
|
||||
std::string ResolvePassInput(const RenderGraphPass& pass) const;
|
||||
core::PushConstants BuildFullscreenConstants(const RenderGraphPass& pass) const;
|
||||
};
|
||||
|
||||
} // namespace sdl3cpp::services::impl
|
||||
|
||||
@@ -54,6 +54,10 @@ public:
|
||||
logger_->Trace("SwapchainService", "GetSwapchainImageFormat");
|
||||
return imageFormat_;
|
||||
}
|
||||
VkFormat GetDepthFormat() const override {
|
||||
logger_->Trace("SwapchainService", "GetDepthFormat");
|
||||
return depthFormat_;
|
||||
}
|
||||
VkExtent2D GetSwapchainExtent() const override {
|
||||
logger_->Trace("SwapchainService", "GetSwapchainExtent");
|
||||
return extent_;
|
||||
|
||||
@@ -220,7 +220,12 @@ bool VulkanGraphicsBackend::EndFrame(GraphicsDeviceHandle device) {
|
||||
logger_->Trace("VulkanGraphicsBackend", "EndFrame");
|
||||
|
||||
// Record all accumulated commands
|
||||
renderCommandService_->RecordCommands(currentImageIndex_, frameCommands_, currentViewProj_);
|
||||
if (renderGraphEnabled_) {
|
||||
renderCommandService_->RecordRenderGraph(currentImageIndex_, renderGraphDefinition_,
|
||||
frameCommands_, currentViewProj_);
|
||||
} else {
|
||||
renderCommandService_->RecordCommands(currentImageIndex_, frameCommands_, currentViewProj_);
|
||||
}
|
||||
|
||||
// End the frame
|
||||
return renderCommandService_->EndFrame(currentImageIndex_);
|
||||
@@ -231,6 +236,14 @@ void VulkanGraphicsBackend::SetViewProjection(const std::array<float, 16>& viewP
|
||||
currentViewProj_ = viewProj;
|
||||
}
|
||||
|
||||
void VulkanGraphicsBackend::SetRenderGraphDefinition(const RenderGraphDefinition& definition) {
|
||||
logger_->Trace("VulkanGraphicsBackend", "SetRenderGraphDefinition",
|
||||
"resources=" + std::to_string(definition.resources.size()) +
|
||||
", passes=" + std::to_string(definition.passes.size()));
|
||||
renderGraphDefinition_ = definition;
|
||||
renderGraphEnabled_ = !definition.passes.empty();
|
||||
}
|
||||
|
||||
void VulkanGraphicsBackend::Draw(GraphicsDeviceHandle device, GraphicsPipelineHandle pipeline,
|
||||
GraphicsBufferHandle vertexBuffer, GraphicsBufferHandle indexBuffer,
|
||||
uint32_t indexCount, const std::array<float, 16>& modelMatrix) {
|
||||
|
||||
@@ -48,6 +48,7 @@ public:
|
||||
bool EndFrame(GraphicsDeviceHandle device) override;
|
||||
|
||||
void SetViewProjection(const std::array<float, 16>& viewProj) override;
|
||||
void SetRenderGraphDefinition(const RenderGraphDefinition& definition) override;
|
||||
|
||||
void Draw(GraphicsDeviceHandle device, GraphicsPipelineHandle pipeline,
|
||||
GraphicsBufferHandle vertexBuffer, GraphicsBufferHandle indexBuffer,
|
||||
@@ -72,6 +73,8 @@ private:
|
||||
uint32_t currentImageIndex_;
|
||||
std::vector<RenderCommand> frameCommands_;
|
||||
std::array<float, 16> currentViewProj_;
|
||||
RenderGraphDefinition renderGraphDefinition_{};
|
||||
bool renderGraphEnabled_ = false;
|
||||
std::unordered_map<GraphicsPipelineHandle, std::string> pipelineToShaderKey_;
|
||||
};
|
||||
|
||||
|
||||
@@ -146,6 +146,13 @@ public:
|
||||
*/
|
||||
virtual void SetViewProjection(const std::array<float, 16>& viewProj) = 0;
|
||||
|
||||
/**
|
||||
* @brief Set the render graph definition (optional).
|
||||
*
|
||||
* @param definition Render graph definition
|
||||
*/
|
||||
virtual void SetRenderGraphDefinition(const RenderGraphDefinition& definition) = 0;
|
||||
|
||||
/**
|
||||
* @brief Draw with a pipeline.
|
||||
*
|
||||
|
||||
@@ -66,6 +66,13 @@ public:
|
||||
*/
|
||||
virtual VkPipelineLayout GetPipelineLayout() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Get the descriptor set layout used by pipelines.
|
||||
*
|
||||
* @return VkDescriptorSetLayout handle
|
||||
*/
|
||||
virtual VkDescriptorSetLayout GetDescriptorSetLayout() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Check if a shader exists.
|
||||
*
|
||||
|
||||
@@ -45,6 +45,19 @@ public:
|
||||
const std::vector<RenderCommand>& commands,
|
||||
const std::array<float, 16>& viewProj) = 0;
|
||||
|
||||
/**
|
||||
* @brief Record rendering commands using a render graph.
|
||||
*
|
||||
* @param imageIndex Swapchain image index
|
||||
* @param graph Render graph definition
|
||||
* @param commands Render commands to execute for scene passes
|
||||
* @param viewProj View-projection matrix
|
||||
*/
|
||||
virtual void RecordRenderGraph(uint32_t imageIndex,
|
||||
const RenderGraphDefinition& graph,
|
||||
const std::vector<RenderCommand>& commands,
|
||||
const std::array<float, 16>& viewProj) = 0;
|
||||
|
||||
/**
|
||||
* @brief End the frame and present.
|
||||
*
|
||||
|
||||
@@ -104,6 +104,13 @@ public:
|
||||
*/
|
||||
virtual VkFormat GetSwapchainImageFormat() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Get the depth buffer format.
|
||||
*
|
||||
* @return VkFormat of depth images
|
||||
*/
|
||||
virtual VkFormat GetDepthFormat() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Get the swapchain extent (size).
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user