feat: RenderGraph 帧历史资源(ReadHistory)+ TemporalDiff 验证 pass
- PassBuilder::ReadHistory:读上一帧槽位 ((slot-1+F) mod F) 的物理镜像, TAA/运动模糊等时域效果的前置能力,RG 最后一个已知边界补齐 - 历史读不建拓扑边(跨帧依赖由同队列提交序保证)、不入本帧状态链、 不改生命周期;剔除照常参与(生产者存活判定) - 历史 barrier:首帧走 first(old=Undefined),之后走 steady 且 src=ALL_COMMANDS|MEMORY_WRITE 显式闭合跨帧可用性+可见性; 有历史读的资源,常规 entry steady 的 src 同样升级(历史读打破了 同槽位 fence 全等保前提) - TemporalDiff 调试 pass:|上一帧−当前帧|×增益,静止=黑/运动边缘发光 即链路正确;Tonemap binding2 经 push 开关切调试视图 - 编译零错误、validation 零警告、8 秒冒烟通过(9 pass 全活跃)
This commit is contained in:
@@ -16,12 +16,16 @@ namespace Engine::Passes
|
||||
ShaderManager& shaders)
|
||||
: m_Device(device), m_Descriptors(descriptors)
|
||||
{
|
||||
// Set0 = {HDR 颜色图, Bloom 图},push = 16B 通用结构
|
||||
// Set0 = {HDR 颜色图, Bloom 图, 调试输入},push = 16B 通用结构
|
||||
//(binding2 只被 tonemap 的调试视图使用;bloom 系 shader 静态
|
||||
// 使用的 binding 是布局的子集,合法)
|
||||
std::vector<DescriptorBinding> bindings = {
|
||||
{0, vk::DescriptorType::eCombinedImageSampler,
|
||||
vk::ShaderStageFlagBits::eFragment},
|
||||
{1, vk::DescriptorType::eCombinedImageSampler,
|
||||
vk::ShaderStageFlagBits::eFragment},
|
||||
{2, vk::DescriptorType::eCombinedImageSampler,
|
||||
vk::ShaderStageFlagBits::eFragment},
|
||||
};
|
||||
setLayout = descriptors.CreateLayout(bindings);
|
||||
|
||||
@@ -59,13 +63,19 @@ namespace Engine::Passes
|
||||
vk::ShaderStageFlagBits::eFragment,
|
||||
std::vector<vk::DescriptorSetLayout>{setLayout},
|
||||
static_cast<u32>(sizeof(FullscreenPush)));
|
||||
auto diffF = shaders.CreateFromFile(
|
||||
shaderDir + "temporal_diff.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;
|
||||
diffFrag = diffF.Handle;
|
||||
|
||||
if (!vert || !tonemapFrag || !extractFrag || !blurFrag) {
|
||||
if (!vert || !tonemapFrag || !extractFrag || !blurFrag || !diffFrag) {
|
||||
ENGINE_FATAL("FullscreenGpu: failed to create shader objects");
|
||||
}
|
||||
}
|
||||
@@ -78,6 +88,7 @@ namespace Engine::Passes
|
||||
if (tonemapFrag) vkDevice.destroyShaderEXT(tonemapFrag);
|
||||
if (extractFrag) vkDevice.destroyShaderEXT(extractFrag);
|
||||
if (blurFrag) vkDevice.destroyShaderEXT(blurFrag);
|
||||
if (diffFrag) vkDevice.destroyShaderEXT(diffFrag);
|
||||
|
||||
vkDevice.destroySampler(sampler);
|
||||
vkDevice.destroyPipelineLayout(layout);
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/23.
|
||||
//
|
||||
|
||||
#include <Engine/Renderer/Passes/TemporalDiffPass.h>
|
||||
#include <Engine/Core/FrameContext.h>
|
||||
|
||||
namespace Engine::Passes
|
||||
{
|
||||
TemporalDiffPass::TemporalDiffPass(FullscreenGpu& gpu, f32 gain,
|
||||
RG::TextureHandle hdrColor,
|
||||
RG::TextureHandle output)
|
||||
: m_Gpu(gpu), m_Gain(gain), m_HDR(hdrColor), m_Output(output)
|
||||
{
|
||||
m_Sets.resize(kMaxFramesInFlight);
|
||||
for (u32 slot = 0; slot < kMaxFramesInFlight; ++slot) {
|
||||
m_Sets[slot] = m_Gpu.AllocateSet();
|
||||
}
|
||||
}
|
||||
|
||||
void TemporalDiffPass::Register(RG::RenderGraph& rg)
|
||||
{
|
||||
// 帧历史读 + 当前帧读:同一虚拟资源的两个槽位镜像在同一个
|
||||
// pass 里采样(TAA 的标准形态)。历史读不建图内边,跨帧同步
|
||||
// 由 RG 的历史 barrier(ALL_COMMANDS 可见性)保证。
|
||||
rg.AddPass("TemporalDiff",
|
||||
[this](RG::RenderGraphContext& ctx) {
|
||||
m_Gpu.RecordFullscreen(ctx, ctx.View(m_Output), m_Gpu.diffFrag,
|
||||
m_Sets[ctx.frame.frameSlot],
|
||||
FullscreenPush{0.0f, m_Gain, 0.0f, 0.0f});
|
||||
})
|
||||
.ReadHistory(m_HDR, RG::ResourceUse::ShaderRead,
|
||||
vk::ShaderStageFlagBits::eFragment)
|
||||
.Read(m_HDR, RG::ResourceUse::ShaderRead,
|
||||
vk::ShaderStageFlagBits::eFragment)
|
||||
.Write(m_Output, RG::ResourceUse::ColorAttachment);
|
||||
}
|
||||
|
||||
void TemporalDiffPass::OnTargetsRebuilt(RG::RenderGraph& rg)
|
||||
{
|
||||
for (u32 slot = 0; slot < kMaxFramesInFlight; ++slot) {
|
||||
// binding0 = 帧历史(上一帧槽位镜像),binding1 = 当前帧
|
||||
m_Gpu.UpdateSetInput(m_Sets[slot], 0, rg, m_HDR,
|
||||
rg.HistorySlot(slot));
|
||||
m_Gpu.UpdateSetInput(m_Sets[slot], 1, rg, m_HDR, slot);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Engine::Passes
|
||||
@@ -9,9 +9,11 @@ namespace Engine::Passes
|
||||
{
|
||||
TonemapPass::TonemapPass(FullscreenGpu& gpu, PostProcessParams& params,
|
||||
RG::TextureHandle hdrColor, RG::TextureHandle bloomA,
|
||||
RG::TextureHandle temporalDiff,
|
||||
RG::TextureHandle backbuffer)
|
||||
: m_Gpu(gpu), m_Params(params),
|
||||
m_HDR(hdrColor), m_A(bloomA), m_Backbuffer(backbuffer)
|
||||
m_HDR(hdrColor), m_A(bloomA), m_TemporalDiff(temporalDiff),
|
||||
m_Backbuffer(backbuffer)
|
||||
{
|
||||
m_Sets.resize(kMaxFramesInFlight);
|
||||
for (u32 slot = 0; slot < kMaxFramesInFlight; ++slot) {
|
||||
@@ -28,12 +30,17 @@ namespace Engine::Passes
|
||||
m_Sets[ctx.frame.frameSlot],
|
||||
FullscreenPush{m_Params.exposure,
|
||||
m_Params.useACES ? 1.0f : 0.0f,
|
||||
m_Params.bloomStrength, 0.0f});
|
||||
m_Params.bloomStrength,
|
||||
m_Params.showTemporalDiff ? 1.0f : 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)
|
||||
// shader 静态采样 binding2(调试视图开关在 push constant 里),
|
||||
// 声明读否则本 pass 的产出无人消费会被剔除、镜像也不会分配
|
||||
.Read(m_TemporalDiff, RG::ResourceUse::ShaderRead,
|
||||
vk::ShaderStageFlagBits::eFragment);
|
||||
}
|
||||
|
||||
@@ -42,6 +49,7 @@ namespace Engine::Passes
|
||||
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);
|
||||
m_Gpu.UpdateSetInput(m_Sets[slot], 2, rg, m_TemporalDiff, slot);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user