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:
@@ -124,7 +124,7 @@ namespace Engine {
|
||||
u64 m_FrameCount = 0;
|
||||
|
||||
// 帧率控制
|
||||
bool m_VSync = false; // 垂直同步(present 自带节奏)
|
||||
bool m_VSync = true; // 垂直同步(present 自带节奏)
|
||||
f32 m_TargetFps = 60.0f; // 限帧目标(≥24)
|
||||
|
||||
std::chrono::steady_clock::time_point m_LastFrameTime;
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
//
|
||||
// 已知边界(后续演进点):
|
||||
// - 暂不支持异步 compute queue / 跨队列信号量
|
||||
// - 暂不支持内存别名(需 VMA)与帧历史资源(TAA 等)
|
||||
// - 同一 pass 内对同一资源"读+写"(input attachment / RWM)未建模
|
||||
//
|
||||
|
||||
@@ -98,6 +97,10 @@ namespace Engine::RG {
|
||||
vk::ImageView View(TextureHandle h) const;
|
||||
vk::Image Image(TextureHandle h) const;
|
||||
|
||||
// 帧历史资源:上一帧槽位的物理镜像((slot + framesInFlight - 1) % F)。
|
||||
// 采样描述符经 GetPhysicalView(h, 历史槽位) 预绑(同阴影图模式)。
|
||||
u32 HistorySlot(u32 frameSlot) const;
|
||||
|
||||
class RenderGraph* graph = nullptr;
|
||||
};
|
||||
|
||||
@@ -131,6 +134,10 @@ namespace Engine::RG {
|
||||
};
|
||||
std::vector<SlotState> slots;
|
||||
|
||||
// 是否存在帧历史读(Compile 期校验:历史资源必须有同帧正常使用,
|
||||
// 否则没有物理镜像可读)
|
||||
bool hasHistoryUse = false;
|
||||
|
||||
// Compile 期状态链解算用(执行序上的"当前"状态)
|
||||
vk::ImageLayout chainLayout = vk::ImageLayout::eUndefined;
|
||||
vk::PipelineStageFlags2 chainStage{};
|
||||
@@ -150,6 +157,7 @@ namespace Engine::RG {
|
||||
u32 resIndex;
|
||||
ResourceUse use;
|
||||
vk::ShaderStageFlags readStage; // ShaderRead 时的消费 stage
|
||||
bool history = false; // 帧历史读:读上一帧槽位的物理镜像
|
||||
};
|
||||
std::vector<Use> uses;
|
||||
ExecuteFn exec;
|
||||
@@ -161,10 +169,12 @@ namespace Engine::RG {
|
||||
// steady = 该槽位已被使用过(从 resting 状态出发)
|
||||
// first = 该槽位首次使用(从 Undefined 出发)
|
||||
// imported 资源恒用 first(acquire 后 layout 语义上为 Undefined)
|
||||
// history = 帧历史读(操作上一帧槽位的镜像,steady 变体带跨帧可见性同步)
|
||||
struct BarrierPair {
|
||||
u32 resIndex;
|
||||
vk::ImageMemoryBarrier2 steady;
|
||||
vk::ImageMemoryBarrier2 first;
|
||||
bool history = false;
|
||||
};
|
||||
std::vector<BarrierPair> barriers;
|
||||
};
|
||||
@@ -195,6 +205,11 @@ namespace Engine::RG {
|
||||
// 注意:物理镜像按槽位各一份,槽位变化/重编译后必须重新获取。
|
||||
vk::ImageView GetPhysicalView(TextureHandle h, u32 frameSlot) const;
|
||||
|
||||
// 帧历史槽位 = 上一帧使用的槽位:(slot - 1 + F) mod F
|
||||
u32 HistorySlot(u32 frameSlot) const {
|
||||
return (frameSlot + m_FramesInFlight - 1) % m_FramesInFlight;
|
||||
}
|
||||
|
||||
// ---- Pass 声明 ----
|
||||
|
||||
class PassBuilder {
|
||||
@@ -202,6 +217,12 @@ namespace Engine::RG {
|
||||
PassBuilder& Write(TextureHandle h, ResourceUse use);
|
||||
PassBuilder& Read(TextureHandle h, ResourceUse use,
|
||||
vk::ShaderStageFlags stage = vk::ShaderStageFlagBits::eFragment);
|
||||
// 帧历史读:读上一帧槽位的物理镜像(TAA/运动模糊/时域滤波用)。
|
||||
// - 不产生图内边:跨帧依赖由同队列提交序 + 历史 barrier 保证
|
||||
// - 资源必须有同帧正常使用(否则无物理镜像),Compile 期校验
|
||||
// - 采样描述符在 pass 内用 GetPhysicalView(h, HistorySlot(slot)) 预绑
|
||||
PassBuilder& ReadHistory(TextureHandle h, ResourceUse use,
|
||||
vk::ShaderStageFlags stage = vk::ShaderStageFlagBits::eFragment);
|
||||
PassBuilder& SideEffect();
|
||||
|
||||
private:
|
||||
|
||||
@@ -37,6 +37,7 @@ namespace Engine {
|
||||
bool useACES = true;
|
||||
f32 bloomThreshold = 1.0f; // HDR 域亮度阈值
|
||||
f32 bloomStrength = 0.8f; // 叠加强度
|
||||
bool showTemporalDiff = false; // 调试视图:显示帧历史差异
|
||||
};
|
||||
|
||||
class FullscreenGpu {
|
||||
@@ -48,10 +49,11 @@ namespace Engine {
|
||||
FullscreenGpu(const FullscreenGpu&) = delete;
|
||||
FullscreenGpu& operator=(const FullscreenGpu&) = delete;
|
||||
|
||||
vk::DescriptorSetLayout setLayout; // Set0 = {输入0, 输入1}
|
||||
vk::DescriptorSetLayout setLayout; // Set0 = {输入0, 输入1, 调试输入}
|
||||
vk::PipelineLayout layout; // push 16B(FullscreenPush)
|
||||
vk::Sampler sampler;
|
||||
vk::ShaderEXT vert{}, extractFrag{}, blurFrag{}, tonemapFrag{};
|
||||
vk::ShaderEXT diffFrag{}; // temporal_diff(帧历史调试)
|
||||
|
||||
// 各 pass 自己持有 per-slot set;分配统一走这里
|
||||
vk::DescriptorSet AllocateSet();
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/23.
|
||||
//
|
||||
// TemporalDiffPass:帧历史链路调试视图。
|
||||
// 读 HDRColor 的帧历史镜像(ReadHistory)与当前帧,输出 |prev − cur| × 增益。
|
||||
// 静止画面纯黑、运动边缘发光 = 帧历史机制正确;噪声/残影错位 = 有 bug。
|
||||
// 也是 RenderGraph::ReadHistory 的参考实现(TAA 照此模式接入)。
|
||||
//
|
||||
|
||||
#ifndef HELLOVULKAN_TEMPORALDIFFPASS_H
|
||||
#define HELLOVULKAN_TEMPORALDIFFPASS_H
|
||||
|
||||
#include <Engine/Renderer/Passes/RenderPass.h>
|
||||
#include <Engine/Renderer/Passes/FullscreenGpu.h>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace Engine::Passes {
|
||||
|
||||
class TemporalDiffPass final : public RenderPass {
|
||||
public:
|
||||
TemporalDiffPass(FullscreenGpu& gpu, f32 gain,
|
||||
RG::TextureHandle hdrColor, RG::TextureHandle output);
|
||||
~TemporalDiffPass() override = default;
|
||||
|
||||
void Register(RG::RenderGraph& rg) override;
|
||||
void OnTargetsRebuilt(RG::RenderGraph& rg) override;
|
||||
|
||||
private:
|
||||
FullscreenGpu& m_Gpu;
|
||||
f32 m_Gain;
|
||||
RG::TextureHandle m_HDR;
|
||||
RG::TextureHandle m_Output;
|
||||
|
||||
std::vector<vk::DescriptorSet> m_Sets; // per-slot
|
||||
};
|
||||
|
||||
} // namespace Engine::Passes
|
||||
|
||||
#endif //HELLOVULKAN_TEMPORALDIFFPASS_H
|
||||
@@ -20,7 +20,7 @@ namespace Engine::Passes {
|
||||
public:
|
||||
TonemapPass(FullscreenGpu& gpu, PostProcessParams& params,
|
||||
RG::TextureHandle hdrColor, RG::TextureHandle bloomA,
|
||||
RG::TextureHandle backbuffer);
|
||||
RG::TextureHandle temporalDiff, RG::TextureHandle backbuffer);
|
||||
~TonemapPass() override = default;
|
||||
|
||||
void Register(RG::RenderGraph& rg) override;
|
||||
@@ -31,6 +31,7 @@ namespace Engine::Passes {
|
||||
PostProcessParams& m_Params;
|
||||
RG::TextureHandle m_HDR;
|
||||
RG::TextureHandle m_A;
|
||||
RG::TextureHandle m_TemporalDiff; // 调试视图输入(当前帧槽位)
|
||||
RG::TextureHandle m_Backbuffer;
|
||||
|
||||
std::vector<vk::DescriptorSet> m_Sets; // per-slot
|
||||
|
||||
@@ -79,6 +79,9 @@ namespace Engine {
|
||||
void SetBloomThreshold(f32 v);
|
||||
f32 GetBloomStrength() const;
|
||||
void SetBloomStrength(f32 v);
|
||||
// 调试视图:显示帧历史差异(TemporalDiff pass 产出)
|
||||
bool GetShowTemporalDiff() const;
|
||||
void SetShowTemporalDiff(bool b);
|
||||
|
||||
// pass 级 GPU 耗时(最近完成帧,毫秒;与 GetPassGpuMs 下标对应)
|
||||
const std::vector<std::string>& GetPassNames() const { return m_PassNames; }
|
||||
@@ -125,6 +128,7 @@ namespace Engine {
|
||||
RG::TextureHandle m_HDRColor{}; // Forward 输出(线性 HDR),Tonemap 归一
|
||||
RG::TextureHandle m_BloomA{}; // 半分辨率:提取 + 垂直模糊结果
|
||||
RG::TextureHandle m_BloomB{}; // 半分辨率:水平模糊中间结果
|
||||
RG::TextureHandle m_TemporalDiff{}; // 半分辨率:帧历史差异(调试)
|
||||
|
||||
// pass 共享的可调参数(Renderer 持有,App 层访问器转发)
|
||||
Scope<Passes::PostProcessParams> m_PostParams;
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace Engine
|
||||
|
||||
vk::Format m_Format;
|
||||
vk::Extent2D m_Extent;
|
||||
bool m_VSync = false;
|
||||
bool m_VSync = true;
|
||||
};
|
||||
} // namespace Engine
|
||||
|
||||
|
||||
Reference in New Issue
Block a user