refactor: 解耦重构——还清四处架构债(对齐 UE/Unity 分层)
- SceneRenderer:场景→渲染数据翻译层(UE FSceneRenderer / Unity CullingResults
模式),收口相机/灯光/光空间矩阵/双视图剔除,Renderer 只编排
- pass 对象化(Unity ScriptableRenderPass 模式):8 个 pass lambda 拆为
Passes::{Shadow,Forward,Bloom,Tonemap,ImGui,Present},BuildGraph 超级函数消失;
SceneGpu/FullscreenGpu 承接共享装配
- 中性 AssetRef(Core 层):ECS 组件不再依赖 Assets 模块(Unity InstanceID 思路)
- IAssetUploader 接口:AssetManager 不感知 Vulkan 具体类,上传服务由渲染层
VulkanAssetUploader 提供
- Renderer 公共 API 不变(App 层零改动),编译零错误 validation 零警告
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/23.
|
||||
//
|
||||
|
||||
#include <Engine/Renderer/Passes/BloomPass.h>
|
||||
#include <Engine/Core/FrameContext.h>
|
||||
|
||||
namespace Engine::Passes
|
||||
{
|
||||
BloomPass::BloomPass(FullscreenGpu& gpu, PostProcessParams& params,
|
||||
RG::TextureHandle hdrColor,
|
||||
RG::TextureHandle bloomA, RG::TextureHandle bloomB)
|
||||
: m_Gpu(gpu), m_Params(params),
|
||||
m_HDR(hdrColor), m_A(bloomA), m_B(bloomB)
|
||||
{
|
||||
m_ExtractSets.resize(kMaxFramesInFlight);
|
||||
m_BlurHSets.resize(kMaxFramesInFlight);
|
||||
m_BlurVSets.resize(kMaxFramesInFlight);
|
||||
for (u32 slot = 0; slot < kMaxFramesInFlight; ++slot) {
|
||||
m_ExtractSets[slot] = m_Gpu.AllocateSet();
|
||||
m_BlurHSets[slot] = m_Gpu.AllocateSet();
|
||||
m_BlurVSets[slot] = m_Gpu.AllocateSet();
|
||||
}
|
||||
}
|
||||
|
||||
void BloomPass::Register(RG::RenderGraph& rg)
|
||||
{
|
||||
// Extract:HDR → 半分辨率 A(软阈值由 frag 的 param0 传入)
|
||||
rg.AddPass("BloomExtract",
|
||||
[this](RG::RenderGraphContext& ctx) {
|
||||
m_Gpu.RecordFullscreen(ctx, ctx.View(m_A), m_Gpu.extractFrag,
|
||||
m_ExtractSets[ctx.frame.frameSlot],
|
||||
FullscreenPush{0.0f, m_Params.bloomThreshold, 0.0f, 0.0f});
|
||||
})
|
||||
.Read(m_HDR, RG::ResourceUse::ShaderRead,
|
||||
vk::ShaderStageFlagBits::eFragment)
|
||||
.Write(m_A, RG::ResourceUse::ColorAttachment);
|
||||
|
||||
// BlurH:A → B(水平步长经 push 传入)
|
||||
rg.AddPass("BloomBlurH",
|
||||
[this](RG::RenderGraphContext& ctx) {
|
||||
m_Gpu.RecordFullscreen(ctx, ctx.View(m_B), m_Gpu.blurFrag,
|
||||
m_BlurHSets[ctx.frame.frameSlot],
|
||||
FullscreenPush{0.0f,
|
||||
1.0f / ctx.extent.width, 0.0f,
|
||||
0.0f});
|
||||
})
|
||||
.Read(m_A, RG::ResourceUse::ShaderRead,
|
||||
vk::ShaderStageFlagBits::eFragment)
|
||||
.Write(m_B, RG::ResourceUse::ColorAttachment);
|
||||
|
||||
// BlurV:B → A(垂直步长)
|
||||
rg.AddPass("BloomBlurV",
|
||||
[this](RG::RenderGraphContext& ctx) {
|
||||
m_Gpu.RecordFullscreen(ctx, ctx.View(m_A), m_Gpu.blurFrag,
|
||||
m_BlurVSets[ctx.frame.frameSlot],
|
||||
FullscreenPush{0.0f, 0.0f,
|
||||
1.0f / ctx.extent.height,
|
||||
0.0f});
|
||||
})
|
||||
.Read(m_B, RG::ResourceUse::ShaderRead,
|
||||
vk::ShaderStageFlagBits::eFragment)
|
||||
.Write(m_A, RG::ResourceUse::ColorAttachment);
|
||||
}
|
||||
|
||||
void BloomPass::OnTargetsRebuilt(RG::RenderGraph& rg)
|
||||
{
|
||||
for (u32 slot = 0; slot < kMaxFramesInFlight; ++slot) {
|
||||
m_Gpu.UpdateSetInput(m_ExtractSets[slot], 0, rg, m_HDR, slot);
|
||||
m_Gpu.UpdateSetInput(m_BlurHSets[slot], 0, rg, m_A, slot);
|
||||
m_Gpu.UpdateSetInput(m_BlurVSets[slot], 0, rg, m_B, slot);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Engine::Passes
|
||||
@@ -0,0 +1,135 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/23.
|
||||
//
|
||||
|
||||
#include <Engine/Renderer/Passes/ForwardPass.h>
|
||||
#include <Engine/Renderer/Vertex.h>
|
||||
#include <Engine/Renderer/Mesh.h>
|
||||
#include <Engine/Renderer/Material.h>
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace Engine::Passes
|
||||
{
|
||||
void ForwardPass::Register(RG::RenderGraph& rg)
|
||||
{
|
||||
rg.AddPass("Forward", [this](RG::RenderGraphContext& ctx) {
|
||||
const vk::CommandBuffer cmd = ctx.cmd;
|
||||
|
||||
vk::ClearValue clearValue;
|
||||
clearValue.color = vk::ClearColorValue(
|
||||
std::array<float, 4>{0.1f, 0.1f, 0.1f, 0.0f});
|
||||
|
||||
vk::RenderingAttachmentInfo colorAttachment;
|
||||
colorAttachment.setImageView(ctx.View(m_HDRColor))
|
||||
.setImageLayout(vk::ImageLayout::eColorAttachmentOptimal)
|
||||
.setLoadOp(vk::AttachmentLoadOp::eClear)
|
||||
.setStoreOp(vk::AttachmentStoreOp::eStore)
|
||||
.setClearValue(clearValue);
|
||||
|
||||
vk::RenderingAttachmentInfo depthAttachment;
|
||||
depthAttachment.setImageView(ctx.View(m_Depth))
|
||||
.setImageLayout(vk::ImageLayout::eDepthAttachmentOptimal)
|
||||
.setLoadOp(vk::AttachmentLoadOp::eClear)
|
||||
.setStoreOp(vk::AttachmentStoreOp::eDontCare)
|
||||
.setClearValue(vk::ClearValue(
|
||||
vk::ClearDepthStencilValue(1.0f, 0)));
|
||||
|
||||
vk::RenderingInfo renderingInfo;
|
||||
renderingInfo.setRenderArea({{0, 0}, ctx.extent})
|
||||
.setLayerCount(1)
|
||||
.setColorAttachments(colorAttachment)
|
||||
.setPDepthAttachment(&depthAttachment);
|
||||
|
||||
cmd.beginRendering(renderingInfo);
|
||||
|
||||
// 绑定 shader object
|
||||
std::array<vk::ShaderStageFlagBits, 2> stages = {
|
||||
vk::ShaderStageFlagBits::eVertex,
|
||||
vk::ShaderStageFlagBits::eFragment
|
||||
};
|
||||
std::array<vk::ShaderEXT, 2> shaders = {m_Gpu.forwardVert, m_Gpu.forwardFrag};
|
||||
cmd.bindShadersEXT(stages, shaders);
|
||||
|
||||
// Set 0:本帧槽位的 UBO;Set 2:本帧槽位的阴影图
|
||||
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
|
||||
m_Gpu.forwardLayout, 0,
|
||||
m_Gpu.uboSets[ctx.frame.frameSlot], {});
|
||||
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
|
||||
m_Gpu.forwardLayout, 2,
|
||||
m_Gpu.shadowSets[ctx.frame.frameSlot], {});
|
||||
|
||||
// ---- 动态状态 ----
|
||||
cmd.setRasterizerDiscardEnable(VK_FALSE);
|
||||
|
||||
cmd.setDepthTestEnable(VK_TRUE);
|
||||
cmd.setDepthWriteEnable(VK_TRUE);
|
||||
cmd.setDepthCompareOp(vk::CompareOp::eLess);
|
||||
cmd.setDepthBoundsTestEnable(VK_FALSE);
|
||||
|
||||
cmd.setStencilTestEnable(VK_FALSE);
|
||||
cmd.setDepthBiasEnable(VK_FALSE);
|
||||
cmd.setPolygonModeEXT(vk::PolygonMode::eFill);
|
||||
cmd.setCullMode(vk::CullModeFlagBits::eNone);
|
||||
cmd.setFrontFace(vk::FrontFace::eCounterClockwise);
|
||||
cmd.setPrimitiveTopology(vk::PrimitiveTopology::eTriangleList);
|
||||
cmd.setPrimitiveRestartEnable(VK_FALSE);
|
||||
|
||||
auto binding = Vertex::GetBindingDescription();
|
||||
auto attributes = Vertex::GetAttributeDescriptions();
|
||||
cmd.setVertexInputEXT(binding, attributes);
|
||||
|
||||
cmd.setRasterizationSamplesEXT(vk::SampleCountFlagBits::e1);
|
||||
vk::SampleMask sampleMask = 0xFFFFFFFF;
|
||||
cmd.setSampleMaskEXT(vk::SampleCountFlagBits::e1, sampleMask);
|
||||
cmd.setAlphaToCoverageEnableEXT(VK_FALSE);
|
||||
cmd.setAlphaToOneEnableEXT(VK_FALSE);
|
||||
|
||||
vk::Viewport viewport(0.0f, 0.0f,
|
||||
static_cast<float>(ctx.extent.width),
|
||||
static_cast<float>(ctx.extent.height),
|
||||
0.0f, 1.0f);
|
||||
cmd.setViewportWithCount(viewport);
|
||||
|
||||
vk::Rect2D scissor({0, 0}, ctx.extent);
|
||||
cmd.setScissorWithCount(scissor);
|
||||
|
||||
vk::Bool32 blendEnable = VK_FALSE;
|
||||
cmd.setColorBlendEnableEXT(0, blendEnable);
|
||||
|
||||
vk::ColorBlendEquationEXT blendEquation;
|
||||
blendEquation.setSrcColorBlendFactor(vk::BlendFactor::eOne)
|
||||
.setDstColorBlendFactor(vk::BlendFactor::eZero)
|
||||
.setColorBlendOp(vk::BlendOp::eAdd)
|
||||
.setSrcAlphaBlendFactor(vk::BlendFactor::eOne)
|
||||
.setDstAlphaBlendFactor(vk::BlendFactor::eZero)
|
||||
.setAlphaBlendOp(vk::BlendOp::eAdd);
|
||||
cmd.setColorBlendEquationEXT(0, blendEquation);
|
||||
|
||||
vk::ColorComponentFlags colorWriteMask =
|
||||
vk::ColorComponentFlagBits::eR |
|
||||
vk::ColorComponentFlagBits::eG |
|
||||
vk::ColorComponentFlagBits::eB |
|
||||
vk::ColorComponentFlagBits::eA;
|
||||
cmd.setColorWriteMaskEXT(0, colorWriteMask);
|
||||
|
||||
// ---- 绘制:GPUDriven 按 mesh 分 batch(相机视锥剔除后)----
|
||||
// 实例矩阵已由 Renderer 写入 ObjectBuffer(per-slot SSBO);
|
||||
// 同 mesh 的 N 个可见实体合并为一次 drawIndexed(instanceCount=N),
|
||||
// draw 数 = 可见唯一 mesh 数,与实体数量解耦
|
||||
for (const auto& batch : m_Frame.mainView.batches) {
|
||||
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
|
||||
m_Gpu.forwardLayout, 1,
|
||||
batch.mesh->GetMaterial()->GetDescriptorSet(), {});
|
||||
batch.mesh->Draw(cmd, batch.count, batch.first);
|
||||
}
|
||||
|
||||
cmd.endRendering();
|
||||
})
|
||||
.Write(m_HDRColor, RG::ResourceUse::ColorAttachment)
|
||||
.Write(m_Depth, RG::ResourceUse::DepthAttachment)
|
||||
.Read(m_ShadowMap, RG::ResourceUse::ShaderRead,
|
||||
vk::ShaderStageFlagBits::eFragment);
|
||||
}
|
||||
|
||||
} // namespace Engine::Passes
|
||||
@@ -0,0 +1,174 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/23.
|
||||
//
|
||||
|
||||
#include <Engine/Renderer/Passes/FullscreenGpu.h>
|
||||
#include <Engine/Vulkan/Device.h>
|
||||
#include <Engine/Vulkan/DescriptorManager.h>
|
||||
#include <Engine/Vulkan/ShaderManager.h>
|
||||
#include <Engine/Core/Log.h>
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace Engine::Passes
|
||||
{
|
||||
FullscreenGpu::FullscreenGpu(Device& device, DescriptorManager& descriptors,
|
||||
ShaderManager& shaders)
|
||||
: m_Device(device), m_Descriptors(descriptors)
|
||||
{
|
||||
// Set0 = {HDR 颜色图, Bloom 图},push = 16B 通用结构
|
||||
std::vector<DescriptorBinding> bindings = {
|
||||
{0, vk::DescriptorType::eCombinedImageSampler,
|
||||
vk::ShaderStageFlagBits::eFragment},
|
||||
{1, vk::DescriptorType::eCombinedImageSampler,
|
||||
vk::ShaderStageFlagBits::eFragment},
|
||||
};
|
||||
setLayout = descriptors.CreateLayout(bindings);
|
||||
|
||||
layout = descriptors.CreatePipelineLayout(
|
||||
std::vector<vk::DescriptorSetLayout>{setLayout},
|
||||
static_cast<u32>(sizeof(FullscreenPush)));
|
||||
|
||||
sampler = device.GetDevice().createSampler(
|
||||
vk::SamplerCreateInfo{}
|
||||
.setMagFilter(vk::Filter::eLinear)
|
||||
.setMinFilter(vk::Filter::eLinear)
|
||||
.setMipmapMode(vk::SamplerMipmapMode::eNearest)
|
||||
.setAddressModeU(vk::SamplerAddressMode::eClampToEdge)
|
||||
.setAddressModeV(vk::SamplerAddressMode::eClampToEdge)
|
||||
.setAddressModeW(vk::SamplerAddressMode::eClampToEdge));
|
||||
|
||||
const std::string shaderDir = "shaders/";
|
||||
auto vertShader = shaders.CreateFromFile(
|
||||
shaderDir + "tonemap.vert.spv",
|
||||
vk::ShaderStageFlagBits::eVertex,
|
||||
std::vector<vk::DescriptorSetLayout>{setLayout},
|
||||
static_cast<u32>(sizeof(FullscreenPush)));
|
||||
auto tonemapF = shaders.CreateFromFile(
|
||||
shaderDir + "tonemap.frag.spv",
|
||||
vk::ShaderStageFlagBits::eFragment,
|
||||
std::vector<vk::DescriptorSetLayout>{setLayout},
|
||||
static_cast<u32>(sizeof(FullscreenPush)));
|
||||
auto extractF = shaders.CreateFromFile(
|
||||
shaderDir + "bloom_extract.frag.spv",
|
||||
vk::ShaderStageFlagBits::eFragment,
|
||||
std::vector<vk::DescriptorSetLayout>{setLayout},
|
||||
static_cast<u32>(sizeof(FullscreenPush)));
|
||||
auto blurF = shaders.CreateFromFile(
|
||||
shaderDir + "bloom_blur.frag.spv",
|
||||
vk::ShaderStageFlagBits::eFragment,
|
||||
std::vector<vk::DescriptorSetLayout>{setLayout},
|
||||
static_cast<u32>(sizeof(FullscreenPush)));
|
||||
|
||||
vert = vertShader.Handle;
|
||||
tonemapFrag = tonemapF.Handle;
|
||||
extractFrag = extractF.Handle;
|
||||
blurFrag = blurF.Handle;
|
||||
|
||||
if (!vert || !tonemapFrag || !extractFrag || !blurFrag) {
|
||||
ENGINE_FATAL("FullscreenGpu: failed to create shader objects");
|
||||
}
|
||||
}
|
||||
|
||||
FullscreenGpu::~FullscreenGpu()
|
||||
{
|
||||
const auto& vkDevice = m_Device.GetDevice();
|
||||
|
||||
if (vert) vkDevice.destroyShaderEXT(vert);
|
||||
if (tonemapFrag) vkDevice.destroyShaderEXT(tonemapFrag);
|
||||
if (extractFrag) vkDevice.destroyShaderEXT(extractFrag);
|
||||
if (blurFrag) vkDevice.destroyShaderEXT(blurFrag);
|
||||
|
||||
vkDevice.destroySampler(sampler);
|
||||
vkDevice.destroyPipelineLayout(layout);
|
||||
vkDevice.destroyDescriptorSetLayout(setLayout);
|
||||
}
|
||||
|
||||
vk::DescriptorSet FullscreenGpu::AllocateSet()
|
||||
{
|
||||
return m_Descriptors.AllocateDescriptorSet(setLayout);
|
||||
}
|
||||
|
||||
void FullscreenGpu::UpdateSetInput(vk::DescriptorSet set, u32 binding,
|
||||
RG::RenderGraph& rg, RG::TextureHandle input,
|
||||
u32 slot)
|
||||
{
|
||||
// 物理镜像按帧槽位各一份:每个槽位的描述符绑各自的 view
|
||||
m_Descriptors.UpdateTexture(set, binding, sampler,
|
||||
rg.GetPhysicalView(input, slot));
|
||||
}
|
||||
|
||||
void FullscreenGpu::RecordFullscreen(RG::RenderGraphContext& ctx,
|
||||
vk::ImageView view,
|
||||
vk::ShaderEXT frag,
|
||||
const vk::DescriptorSet& set,
|
||||
const FullscreenPush& push)
|
||||
{
|
||||
const vk::CommandBuffer cmd = ctx.cmd;
|
||||
|
||||
vk::ClearValue clearValue;
|
||||
clearValue.color = vk::ClearColorValue(
|
||||
std::array<float, 4>{0.0f, 0.0f, 0.0f, 1.0f});
|
||||
|
||||
vk::RenderingAttachmentInfo colorAttachment;
|
||||
colorAttachment.setImageView(view)
|
||||
.setImageLayout(vk::ImageLayout::eColorAttachmentOptimal)
|
||||
.setLoadOp(vk::AttachmentLoadOp::eClear)
|
||||
.setStoreOp(vk::AttachmentStoreOp::eStore)
|
||||
.setClearValue(clearValue);
|
||||
|
||||
vk::RenderingInfo renderingInfo;
|
||||
renderingInfo.setRenderArea({{0, 0}, ctx.extent})
|
||||
.setLayerCount(1)
|
||||
.setColorAttachments(colorAttachment);
|
||||
|
||||
cmd.beginRendering(renderingInfo);
|
||||
|
||||
std::array<vk::ShaderStageFlagBits, 2> stages = {
|
||||
vk::ShaderStageFlagBits::eVertex,
|
||||
vk::ShaderStageFlagBits::eFragment
|
||||
};
|
||||
std::array<vk::ShaderEXT, 2> shaders = {vert, frag};
|
||||
cmd.bindShadersEXT(stages, shaders);
|
||||
|
||||
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
|
||||
layout, 0, set, {});
|
||||
|
||||
// ---- 动态状态(无深度附件、无顶点输入)----
|
||||
cmd.setRasterizerDiscardEnable(VK_FALSE);
|
||||
cmd.setDepthTestEnable(VK_FALSE);
|
||||
cmd.setDepthWriteEnable(VK_FALSE);
|
||||
cmd.setDepthBoundsTestEnable(VK_FALSE);
|
||||
cmd.setStencilTestEnable(VK_FALSE);
|
||||
cmd.setDepthBiasEnable(VK_FALSE);
|
||||
cmd.setPolygonModeEXT(vk::PolygonMode::eFill);
|
||||
cmd.setCullMode(vk::CullModeFlagBits::eNone);
|
||||
cmd.setFrontFace(vk::FrontFace::eCounterClockwise);
|
||||
cmd.setPrimitiveTopology(vk::PrimitiveTopology::eTriangleList);
|
||||
cmd.setPrimitiveRestartEnable(VK_FALSE);
|
||||
cmd.setVertexInputEXT(
|
||||
std::array<vk::VertexInputBindingDescription2EXT, 0>{},
|
||||
std::array<vk::VertexInputAttributeDescription2EXT, 0>{});
|
||||
cmd.setRasterizationSamplesEXT(vk::SampleCountFlagBits::e1);
|
||||
vk::SampleMask sampleMask = 0xFFFFFFFF;
|
||||
cmd.setSampleMaskEXT(vk::SampleCountFlagBits::e1, sampleMask);
|
||||
cmd.setAlphaToCoverageEnableEXT(VK_FALSE);
|
||||
cmd.setAlphaToOneEnableEXT(VK_FALSE);
|
||||
|
||||
vk::Viewport viewport(0.0f, 0.0f,
|
||||
static_cast<float>(ctx.extent.width),
|
||||
static_cast<float>(ctx.extent.height),
|
||||
0.0f, 1.0f);
|
||||
cmd.setViewportWithCount(viewport);
|
||||
cmd.setScissorWithCount(vk::Rect2D({0, 0}, ctx.extent));
|
||||
|
||||
cmd.pushConstants(layout,
|
||||
vk::ShaderStageFlagBits::eVertex |
|
||||
vk::ShaderStageFlagBits::eFragment,
|
||||
0, sizeof(FullscreenPush), &push);
|
||||
|
||||
cmd.draw(3, 1, 0, 0);
|
||||
cmd.endRendering();
|
||||
}
|
||||
|
||||
} // namespace Engine::Passes
|
||||
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/23.
|
||||
//
|
||||
|
||||
#include <Engine/Renderer/Passes/ImGuiPass.h>
|
||||
#include <Engine/ImGui/ImGuiLayer.h>
|
||||
|
||||
namespace Engine::Passes
|
||||
{
|
||||
void ImGuiPass::Register(RG::RenderGraph& rg)
|
||||
{
|
||||
rg.AddPass("ImGui", [this](RG::RenderGraphContext& ctx) {
|
||||
const vk::CommandBuffer cmd = ctx.cmd;
|
||||
|
||||
vk::RenderingAttachmentInfo colorAttachment;
|
||||
colorAttachment.setImageView(ctx.View(m_Backbuffer))
|
||||
.setImageLayout(vk::ImageLayout::eColorAttachmentOptimal)
|
||||
.setLoadOp(vk::AttachmentLoadOp::eLoad) // 关键:叠加
|
||||
.setStoreOp(vk::AttachmentStoreOp::eStore);
|
||||
|
||||
vk::RenderingInfo renderingInfo;
|
||||
renderingInfo.setRenderArea({{0, 0}, ctx.extent})
|
||||
.setLayerCount(1)
|
||||
.setColorAttachments(colorAttachment);
|
||||
|
||||
cmd.beginRendering(renderingInfo);
|
||||
|
||||
if (*m_Layer) {
|
||||
(*m_Layer)->RenderDrawData(cmd);
|
||||
}
|
||||
|
||||
cmd.endRendering();
|
||||
}).Write(m_Backbuffer, RG::ResourceUse::ColorAttachment);
|
||||
}
|
||||
|
||||
} // namespace Engine::Passes
|
||||
@@ -0,0 +1,139 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/23.
|
||||
//
|
||||
|
||||
#include <Engine/Renderer/Passes/SceneGpu.h>
|
||||
#include <Engine/Vulkan/Device.h>
|
||||
#include <Engine/Vulkan/DescriptorManager.h>
|
||||
#include <Engine/Vulkan/ShaderManager.h>
|
||||
#include <Engine/Vulkan/UniformBuffer.h>
|
||||
#include <Engine/Core/Log.h>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace Engine::Passes
|
||||
{
|
||||
SceneGpu::SceneGpu(Device& device, DescriptorManager& descriptors,
|
||||
ShaderManager& shaders,
|
||||
UniformBuffer& ubo, UniformBuffer& objectBuffer)
|
||||
: m_Device(device), m_Descriptors(descriptors)
|
||||
{
|
||||
// Set 0: UBO + GPUDriven 实例数据(SSBO,vertex 读取)
|
||||
std::vector<DescriptorBinding> uboBindings = {
|
||||
{0, vk::DescriptorType::eUniformBuffer,
|
||||
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment},
|
||||
{1, vk::DescriptorType::eStorageBuffer,
|
||||
vk::ShaderStageFlagBits::eVertex},
|
||||
};
|
||||
uboSetLayout = descriptors.CreateLayout(uboBindings);
|
||||
|
||||
// Set 1: texture
|
||||
std::vector<DescriptorBinding> texBindings = {
|
||||
{0, vk::DescriptorType::eCombinedImageSampler, vk::ShaderStageFlagBits::eFragment},
|
||||
};
|
||||
textureSetLayout = descriptors.CreateLayout(texBindings);
|
||||
|
||||
// Set 2: shadow map(fragment 采样)
|
||||
std::vector<DescriptorBinding> shadowBindings = {
|
||||
{0, vk::DescriptorType::eCombinedImageSampler, vk::ShaderStageFlagBits::eFragment},
|
||||
};
|
||||
shadowSetLayout = descriptors.CreateLayout(shadowBindings);
|
||||
|
||||
// Forward pipeline layout:三个 set;无 push(GPUDriven 经 SSBO)
|
||||
forwardLayout = descriptors.CreatePipelineLayout(
|
||||
{uboSetLayout, textureSetLayout, shadowSetLayout}, 0);
|
||||
|
||||
// Shadow pipeline layout:Set0 = UBO/ObjectBuffer(实例矩阵),
|
||||
// push = lightVP 64B
|
||||
shadowLayout = descriptors.CreatePipelineLayout(
|
||||
std::vector<vk::DescriptorSetLayout>{uboSetLayout},
|
||||
sizeof(glm::mat4));
|
||||
|
||||
const std::string shaderDir = "shaders/";
|
||||
// forward vert/frag 均无 push(range 定义一致:都没有,VUID-08878)
|
||||
auto vert = shaders.CreateFromFile(
|
||||
shaderDir + "simple_shader.vert.spv",
|
||||
vk::ShaderStageFlagBits::eVertex,
|
||||
{uboSetLayout, textureSetLayout, shadowSetLayout}, 0);
|
||||
auto frag = shaders.CreateFromFile(
|
||||
shaderDir + "simple_shader.frag.spv",
|
||||
vk::ShaderStageFlagBits::eFragment,
|
||||
{uboSetLayout, textureSetLayout, shadowSetLayout}, 0);
|
||||
|
||||
// shadow shader:depth-only,push = lightVP
|
||||
auto shadowVertShader = shaders.CreateFromFile(
|
||||
shaderDir + "shadow.vert.spv",
|
||||
vk::ShaderStageFlagBits::eVertex,
|
||||
std::vector<vk::DescriptorSetLayout>{uboSetLayout},
|
||||
sizeof(glm::mat4));
|
||||
auto shadowFragShader = shaders.CreateFromFile(
|
||||
shaderDir + "shadow.frag.spv",
|
||||
vk::ShaderStageFlagBits::eFragment,
|
||||
std::vector<vk::DescriptorSetLayout>{uboSetLayout},
|
||||
sizeof(glm::mat4));
|
||||
|
||||
forwardVert = vert.Handle;
|
||||
forwardFrag = frag.Handle;
|
||||
shadowVert = shadowVertShader.Handle;
|
||||
shadowFrag = shadowFragShader.Handle;
|
||||
|
||||
if (!forwardVert || !forwardFrag || !shadowVert || !shadowFrag) {
|
||||
ENGINE_FATAL("SceneGpu: failed to create shader objects");
|
||||
}
|
||||
|
||||
// 阴影采样器:clamp-to-border 白色 = 光视锥之外无阴影
|
||||
vk::SamplerCreateInfo shadowSamplerInfo;
|
||||
shadowSamplerInfo.setMagFilter(vk::Filter::eLinear)
|
||||
.setMinFilter(vk::Filter::eLinear)
|
||||
.setMipmapMode(vk::SamplerMipmapMode::eNearest)
|
||||
.setAddressModeU(vk::SamplerAddressMode::eClampToBorder)
|
||||
.setAddressModeV(vk::SamplerAddressMode::eClampToBorder)
|
||||
.setAddressModeW(vk::SamplerAddressMode::eClampToBorder)
|
||||
.setBorderColor(vk::BorderColor::eFloatOpaqueWhite)
|
||||
.setUnnormalizedCoordinates(VK_FALSE)
|
||||
.setCompareEnable(VK_FALSE);
|
||||
shadowSampler = device.GetDevice().createSampler(shadowSamplerInfo);
|
||||
|
||||
// per-slot 描述符分配 + Set0 绑定(buffer 句柄全程稳定,不用重绑)
|
||||
uboSets.resize(kMaxFramesInFlight);
|
||||
shadowSets.resize(kMaxFramesInFlight);
|
||||
for (u32 slot = 0; slot < kMaxFramesInFlight; ++slot) {
|
||||
uboSets[slot] = descriptors.AllocateDescriptorSet(uboSetLayout);
|
||||
descriptors.UpdateUniformBuffer(
|
||||
uboSets[slot], 0, ubo.Get(slot), ubo.GetSize());
|
||||
descriptors.UpdateStorageBuffer(
|
||||
uboSets[slot], 1, objectBuffer.Get(slot), objectBuffer.GetSize());
|
||||
|
||||
shadowSets[slot] = descriptors.AllocateDescriptorSet(shadowSetLayout);
|
||||
}
|
||||
// shadowSets 的图片绑定在 Compile 后由 BindShadowMap 完成
|
||||
}
|
||||
|
||||
SceneGpu::~SceneGpu()
|
||||
{
|
||||
const auto& vkDevice = m_Device.GetDevice();
|
||||
|
||||
if (forwardVert) vkDevice.destroyShaderEXT(forwardVert);
|
||||
if (forwardFrag) vkDevice.destroyShaderEXT(forwardFrag);
|
||||
if (shadowVert) vkDevice.destroyShaderEXT(shadowVert);
|
||||
if (shadowFrag) vkDevice.destroyShaderEXT(shadowFrag);
|
||||
|
||||
vkDevice.destroySampler(shadowSampler);
|
||||
vkDevice.destroyPipelineLayout(forwardLayout);
|
||||
vkDevice.destroyPipelineLayout(shadowLayout);
|
||||
vkDevice.destroyDescriptorSetLayout(uboSetLayout);
|
||||
vkDevice.destroyDescriptorSetLayout(textureSetLayout);
|
||||
vkDevice.destroyDescriptorSetLayout(shadowSetLayout);
|
||||
}
|
||||
|
||||
void SceneGpu::BindShadowMap(RG::RenderGraph& rg, RG::TextureHandle shadowMap)
|
||||
{
|
||||
// 物理镜像按帧槽位各一份:每个槽位的描述符绑各自的 view
|
||||
for (u32 slot = 0; slot < kMaxFramesInFlight; ++slot) {
|
||||
m_Descriptors.UpdateTexture(
|
||||
shadowSets[slot], 0, shadowSampler,
|
||||
rg.GetPhysicalView(shadowMap, slot));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Engine::Passes
|
||||
@@ -0,0 +1,104 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/23.
|
||||
//
|
||||
|
||||
#include <Engine/Renderer/Passes/ShadowPass.h>
|
||||
#include <Engine/Renderer/Vertex.h>
|
||||
#include <Engine/Renderer/Mesh.h>
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace Engine::Passes
|
||||
{
|
||||
void ShadowPass::Register(RG::RenderGraph& rg)
|
||||
{
|
||||
// 所有渲染体都写入,不做实体级开关——castShadow 应由材质/实体
|
||||
// 数据驱动剔除,而非 pass 逻辑特判
|
||||
rg.AddPass("Shadow", [this](RG::RenderGraphContext& ctx) {
|
||||
const vk::CommandBuffer cmd = ctx.cmd;
|
||||
|
||||
vk::RenderingAttachmentInfo depthAttachment;
|
||||
depthAttachment.setImageView(ctx.View(m_ShadowMap))
|
||||
.setImageLayout(vk::ImageLayout::eDepthAttachmentOptimal)
|
||||
.setLoadOp(vk::AttachmentLoadOp::eClear)
|
||||
.setStoreOp(vk::AttachmentStoreOp::eStore)
|
||||
.setClearValue(vk::ClearValue(
|
||||
vk::ClearDepthStencilValue(1.0f, 0)));
|
||||
|
||||
vk::RenderingInfo renderingInfo;
|
||||
renderingInfo.setRenderArea({{0, 0}, {kShadowMapSize, kShadowMapSize}})
|
||||
.setLayerCount(1)
|
||||
.setPDepthAttachment(&depthAttachment);
|
||||
|
||||
cmd.beginRendering(renderingInfo);
|
||||
|
||||
std::array<vk::ShaderStageFlagBits, 2> stages = {
|
||||
vk::ShaderStageFlagBits::eVertex,
|
||||
vk::ShaderStageFlagBits::eFragment
|
||||
};
|
||||
std::array<vk::ShaderEXT, 2> shaders = {m_Gpu.shadowVert, m_Gpu.shadowFrag};
|
||||
cmd.bindShadersEXT(stages, shaders);
|
||||
|
||||
// ---- 动态状态(depth-only)----
|
||||
cmd.setRasterizerDiscardEnable(VK_FALSE);
|
||||
|
||||
cmd.setDepthTestEnable(VK_TRUE);
|
||||
cmd.setDepthWriteEnable(VK_TRUE);
|
||||
cmd.setDepthCompareOp(vk::CompareOp::eLess);
|
||||
cmd.setDepthBoundsTestEnable(VK_FALSE);
|
||||
|
||||
cmd.setStencilTestEnable(VK_FALSE);
|
||||
// 深度 bias 压平 shadow acne(NDC 量级)
|
||||
cmd.setDepthBiasEnable(VK_TRUE);
|
||||
cmd.setDepthBias(0.0025f, 0.0f, 0.003f);
|
||||
cmd.setPolygonModeEXT(vk::PolygonMode::eFill);
|
||||
cmd.setCullMode(vk::CullModeFlagBits::eNone);
|
||||
cmd.setFrontFace(vk::FrontFace::eCounterClockwise);
|
||||
cmd.setPrimitiveTopology(vk::PrimitiveTopology::eTriangleList);
|
||||
cmd.setPrimitiveRestartEnable(VK_FALSE);
|
||||
|
||||
auto binding = Vertex::GetBindingDescription();
|
||||
auto attributes = Vertex::GetAttributeDescriptions();
|
||||
cmd.setVertexInputEXT(binding, attributes);
|
||||
|
||||
cmd.setRasterizationSamplesEXT(vk::SampleCountFlagBits::e1);
|
||||
vk::SampleMask sampleMask = 0xFFFFFFFF;
|
||||
cmd.setSampleMaskEXT(vk::SampleCountFlagBits::e1, sampleMask);
|
||||
cmd.setAlphaToCoverageEnableEXT(VK_FALSE);
|
||||
cmd.setAlphaToOneEnableEXT(VK_FALSE);
|
||||
|
||||
vk::Viewport viewport(0.0f, 0.0f,
|
||||
static_cast<float>(kShadowMapSize),
|
||||
static_cast<float>(kShadowMapSize),
|
||||
0.0f, 1.0f);
|
||||
cmd.setViewportWithCount(viewport);
|
||||
|
||||
vk::Rect2D scissor({0, 0}, {kShadowMapSize, kShadowMapSize});
|
||||
cmd.setScissorWithCount(scissor);
|
||||
|
||||
// ---- 绘制:GPUDriven——与 Forward 共用实例数据,但用
|
||||
// 光源视锥剔除后的 batch(相机外实体仍投影子)----
|
||||
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
|
||||
m_Gpu.shadowLayout, 0,
|
||||
m_Gpu.uboSets[ctx.frame.frameSlot], {});
|
||||
|
||||
// stage 必须覆盖 layout range 声明的全部 stage(VUID-01796)
|
||||
cmd.pushConstants(m_Gpu.shadowLayout,
|
||||
vk::ShaderStageFlagBits::eVertex |
|
||||
vk::ShaderStageFlagBits::eFragment,
|
||||
0, sizeof(glm::mat4), &m_Frame.ubo.lightSpace);
|
||||
|
||||
for (const auto& batch : m_Frame.shadowView.batches) {
|
||||
batch.mesh->Draw(cmd, batch.count, batch.first);
|
||||
}
|
||||
|
||||
cmd.endRendering();
|
||||
}).Write(m_ShadowMap, RG::ResourceUse::DepthAttachment);
|
||||
}
|
||||
|
||||
void ShadowPass::OnTargetsRebuilt(RG::RenderGraph& rg)
|
||||
{
|
||||
m_Gpu.BindShadowMap(rg, m_ShadowMap);
|
||||
}
|
||||
|
||||
} // namespace Engine::Passes
|
||||
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/23.
|
||||
//
|
||||
|
||||
#include <Engine/Renderer/Passes/TonemapPass.h>
|
||||
#include <Engine/Core/FrameContext.h>
|
||||
|
||||
namespace Engine::Passes
|
||||
{
|
||||
TonemapPass::TonemapPass(FullscreenGpu& gpu, PostProcessParams& params,
|
||||
RG::TextureHandle hdrColor, RG::TextureHandle bloomA,
|
||||
RG::TextureHandle backbuffer)
|
||||
: m_Gpu(gpu), m_Params(params),
|
||||
m_HDR(hdrColor), m_A(bloomA), m_Backbuffer(backbuffer)
|
||||
{
|
||||
m_Sets.resize(kMaxFramesInFlight);
|
||||
for (u32 slot = 0; slot < kMaxFramesInFlight; ++slot) {
|
||||
m_Sets[slot] = m_Gpu.AllocateSet();
|
||||
}
|
||||
}
|
||||
|
||||
void TonemapPass::Register(RG::RenderGraph& rg)
|
||||
{
|
||||
rg.AddPass("Tonemap",
|
||||
[this](RG::RenderGraphContext& ctx) {
|
||||
m_Gpu.RecordFullscreen(ctx, ctx.View(m_Backbuffer),
|
||||
m_Gpu.tonemapFrag,
|
||||
m_Sets[ctx.frame.frameSlot],
|
||||
FullscreenPush{m_Params.exposure,
|
||||
m_Params.useACES ? 1.0f : 0.0f,
|
||||
m_Params.bloomStrength, 0.0f});
|
||||
})
|
||||
.Write(m_Backbuffer, RG::ResourceUse::ColorAttachment)
|
||||
.Read(m_HDR, RG::ResourceUse::ShaderRead,
|
||||
vk::ShaderStageFlagBits::eFragment)
|
||||
.Read(m_A, RG::ResourceUse::ShaderRead,
|
||||
vk::ShaderStageFlagBits::eFragment);
|
||||
}
|
||||
|
||||
void TonemapPass::OnTargetsRebuilt(RG::RenderGraph& rg)
|
||||
{
|
||||
for (u32 slot = 0; slot < kMaxFramesInFlight; ++slot) {
|
||||
m_Gpu.UpdateSetInput(m_Sets[slot], 0, rg, m_HDR, slot);
|
||||
m_Gpu.UpdateSetInput(m_Sets[slot], 1, rg, m_A, slot);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Engine::Passes
|
||||
Reference in New Issue
Block a user