refactor: ShaderManager 引用计数句柄——shader 生命周期自动化

- ShaderObject 值类型 → ShaderRef(shared_ptr 语义,UE TShaderRef
  对标):拷贝共享所有权,最后一个引用析构时自动销毁 VkShaderEXT,
  持有方零清理代码——"新 pass 忘销毁"整类 bug 被机制消灭
- 删除公开 Destroy 与全部持有方手工 destroyShaderEXT(8 处);
  各类析构只清 sampler/layout/set 等其余原生句柄
- RecordFullscreen 参数收窄为 const ShaderRef&(裸 handle 无法
  再被误传);bind 点统一 GetHandle()
- Outline/SelectionMask 撤掉上一轮补的 Device&(不再需要)
- 验证:5s 探针自动退出 → exit 0、无 VUID-05137 泄漏、无 SIGSEGV
This commit is contained in:
2026-10-02 20:20:07 +08:00
parent 47dd879977
commit b0d5ed4550
21 changed files with 223 additions and 217 deletions
@@ -27,6 +27,7 @@
#include <Engine/Core/Types.h>
#include <Engine/Renderer/Cubemap.h> // DecodedEquirect(解码产物数据契约)
#include <Engine/Vulkan/MemoryAllocator.h>
#include <Engine/Vulkan/ShaderManager.h>
#include <glm/glm.hpp>
#include <vulkan/vulkan.hpp>
#include <string>
@@ -36,7 +37,6 @@ namespace Engine {
class Device;
class DescriptorManager;
class ShaderManager;
class Cubemap;
class IblGenerator {
@@ -95,10 +95,11 @@ namespace Engine {
vk::PipelineLayout m_IblLayoutPush;
vk::PipelineLayout m_LutLayout;
vk::ShaderEXT m_ToCubeShader{};
vk::ShaderEXT m_IrrShader{};
vk::ShaderEXT m_PrefShader{};
vk::ShaderEXT m_LutShader{};
// compute shader(引用计数句柄,析构自动销毁)
ShaderManager::ShaderRef m_ToCubeShader;
ShaderManager::ShaderRef m_IrrShader;
ShaderManager::ShaderRef m_PrefShader;
ShaderManager::ShaderRef m_LutShader;
vk::Sampler m_EquirectSampler; // S=repeat(经度环绕)T=clamp
vk::CommandPool m_Pool; // 持久复用,每次生成 reset
@@ -13,13 +13,13 @@
#include <Engine/Core/Types.h>
#include <Engine/RenderGraph/RenderGraph.h>
#include <Engine/Vulkan/ShaderManager.h>
#include <vulkan/vulkan.hpp>
namespace Engine {
class Device;
class DescriptorManager;
class ShaderManager;
namespace Passes {
@@ -64,7 +64,7 @@ namespace Engine {
public:
FullscreenGpu(Device& device, DescriptorManager& descriptors,
ShaderManager& shaders);
~FullscreenGpu();
~FullscreenGpu(); // 只清 sampler/layout(shader 随引用自动销毁)
FullscreenGpu(const FullscreenGpu&) = delete;
FullscreenGpu& operator=(const FullscreenGpu&) = delete;
@@ -72,9 +72,10 @@ namespace Engine {
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(帧历史调试)
vk::ShaderEXT taaFrag{}; // taa(时域抗锯齿 resolve)
// shader 引用计数句柄(析构自动销毁,零手工清理)
ShaderManager::ShaderRef vert, extractFrag, blurFrag, tonemapFrag;
ShaderManager::ShaderRef diffFrag; // temporal_diff(帧历史调试)
ShaderManager::ShaderRef taaFrag; // taa(时域抗锯齿 resolve)
// 各 pass 自己持有 per-slot set;分配统一走这里
vk::DescriptorSet AllocateSet();
@@ -87,7 +88,8 @@ namespace Engine {
// 全屏 pass 公共录制:动态状态/draw 全一致,仅附件 view、
// 输入 set、push 值不同
void RecordFullscreen(RG::RenderGraphContext& ctx, vk::ImageView view,
vk::ShaderEXT frag, const vk::DescriptorSet& set,
const ShaderManager::ShaderRef& frag,
const vk::DescriptorSet& set,
const FullscreenPush& push);
private:
@@ -22,6 +22,7 @@
#include <Engine/Renderer/Passes/RenderPass.h>
#include <Engine/Renderer/SceneTypes.h>
#include <Engine/Vulkan/Buffer.h>
#include <Engine/Vulkan/ShaderManager.h>
#include <vulkan/vulkan.hpp>
namespace Engine {
@@ -61,7 +62,7 @@ namespace Engine {
RG::TextureHandle m_Depth;
vk::PipelineLayout m_Layout; // Set0 = uboSetLayout,无 push
vk::ShaderEXT m_Vert{}, m_Frag{};
ShaderManager::ShaderRef m_Vert, m_Frag; // 析构自动销毁
// 1×1 像素暂存(host visible,持久映射);拷贝录制时置位
Scope<Buffer> m_Staging;
@@ -23,12 +23,11 @@ namespace Engine {
class OutlinePass final : public RenderPass {
public:
// color:描边色(LDR);widthPx:描边宽度(物理像素)
OutlinePass(Device& device, FullscreenGpu& gpu, ShaderManager& shaders,
OutlinePass(FullscreenGpu& gpu, ShaderManager& shaders,
FrameSceneData& frame,
RG::TextureHandle mask, RG::TextureHandle present,
const glm::vec3& color = glm::vec3(1.0f, 0.62f, 0.05f),
f32 widthPx = 6.0f);
~OutlinePass() override;
OutlinePass(const OutlinePass&) = delete;
OutlinePass& operator=(const OutlinePass&) = delete;
@@ -37,14 +36,13 @@ namespace Engine {
void OnTargetsRebuilt(RG::RenderGraph& rg) override;
private:
Device& m_Device;
FullscreenGpu& m_Gpu;
FrameSceneData& m_Frame;
RG::TextureHandle m_Mask;
RG::TextureHandle m_Present;
glm::vec3 m_Color;
f32 m_WidthPx;
ShaderObject m_Frag{};
ShaderManager::ShaderRef m_Frag; // 析构自动销毁
std::vector<vk::DescriptorSet> m_Sets; // per-frame slot
};
@@ -19,6 +19,7 @@
#include <Engine/Renderer/Passes/RenderPass.h>
#include <Engine/Renderer/SceneTypes.h>
#include <Engine/Vulkan/UniformBuffer.h>
#include <Engine/Vulkan/ShaderManager.h>
#include <vulkan/vulkan.hpp>
namespace Engine {
@@ -64,7 +65,7 @@ namespace Engine {
UniformBuffer& m_TriVB;
vk::PipelineLayout m_Layout; // 无描述符,push = viewProj 64B
vk::ShaderEXT m_Vert{}, m_Frag{};
ShaderManager::ShaderRef m_Vert, m_Frag; // 析构自动销毁
std::array<u32, kMaxFramesInFlight> m_LineCounts{};
std::array<u32, kMaxFramesInFlight> m_TriCounts{};
@@ -14,6 +14,7 @@
#include <Engine/Core/FrameContext.h>
#include <Engine/Renderer/SceneTypes.h>
#include <Engine/RenderGraph/RenderGraph.h>
#include <Engine/Vulkan/ShaderManager.h>
#include <vulkan/vulkan.hpp>
#include <array>
#include <vector>
@@ -22,7 +23,6 @@ namespace Engine {
class Device;
class DescriptorManager;
class ShaderManager;
class UniformBuffer;
class Cubemap;
class Buffer;
@@ -40,7 +40,7 @@ namespace Engine {
UniformBuffer& skinOffsetBuffer,
UniformBuffer& skinPaletteBuffer,
UniformBuffer& entityIdBuffer);
~SceneGpu();
~SceneGpu(); // 只清 sampler/layout/set(shader 随引用自动销毁)
SceneGpu(const SceneGpu&) = delete;
SceneGpu& operator=(const SceneGpu&) = delete;
@@ -76,10 +76,11 @@ namespace Engine {
std::array<Scope<Buffer>, kMaxFramesInFlight> indirectCmdBuffers;
std::array<Scope<Buffer>, kMaxFramesInFlight> indirectCountBuffers;
vk::ShaderEXT forwardVert{}, forwardFrag{};
// shader 引用计数句柄(析构自动销毁,零手工清理)
ShaderManager::ShaderRef forwardVert, forwardFrag;
// 透明变体:同 simple_shader + gl_FragDepth=1.0(见 frag 注释)
vk::ShaderEXT translucentFrag{};
vk::ShaderEXT shadowVert{}, shadowFrag{};
ShaderManager::ShaderRef translucentFrag;
ShaderManager::ShaderRef shadowVert, shadowFrag;
// RG 重编译后重绑阴影图(物理镜像按槽位各一份)
void BindShadowMaps(RG::RenderGraph& rg,
@@ -26,10 +26,9 @@ namespace Engine {
class SelectionMaskPass final : public RenderPass {
public:
SelectionMaskPass(Device& device, ShaderManager& shaders,
SelectionMaskPass(ShaderManager& shaders,
SceneGpu& gpu, FrameSceneData& frame,
RG::TextureHandle mask);
~SelectionMaskPass() override;
SelectionMaskPass(const SelectionMaskPass&) = delete;
SelectionMaskPass& operator=(const SelectionMaskPass&) = delete;
@@ -38,11 +37,10 @@ namespace Engine {
void OnTargetsRebuilt(RG::RenderGraph& rg) override {}
private:
Device& m_Device;
SceneGpu& m_Gpu;
FrameSceneData& m_Frame;
RG::TextureHandle m_Mask;
ShaderObject m_Frag{};
ShaderManager::ShaderRef m_Frag; // 析构自动销毁
};
} // namespace Passes
@@ -18,6 +18,7 @@
#include <Engine/Core/FrameContext.h>
#include <Engine/Renderer/Passes/RenderPass.h>
#include <Engine/Renderer/SceneTypes.h>
#include <Engine/Vulkan/ShaderManager.h>
#include <vulkan/vulkan.hpp>
namespace Engine {
@@ -52,7 +53,7 @@ namespace Engine {
vk::DescriptorSetLayout m_SetLayout; // Set0 = {samplerCube}
vk::PipelineLayout m_Layout; // push = invVP 64B
vk::ShaderEXT m_Vert{}, m_Frag{};
ShaderManager::ShaderRef m_Vert, m_Frag; // 析构自动销毁
std::array<vk::DescriptorSet, kMaxFramesInFlight> m_Sets{};
};
+52 -18
View File
@@ -1,6 +1,16 @@
//
// Created by atdunbg on 2026/9/20.
//
// ShaderManager:shader object(VK_EXT_shader_object)创建工厂。
//
// 生命周期模型(UE FShaderResource / TShaderRef 语义):Create* 返回
// 引用计数句柄 ShaderRef,拷贝 = 共享所有权,最后一个引用析构时自动
// 销毁 VkShaderEXT——持有方零清理代码,"新 pass 忘销毁"整类 bug 被
// 机制消灭(曾因手工管理漏销毁退出时报 VUID-05137 泄漏)。
//
// 唯一约束:ShaderRef 不得比 Device 活得久(引擎内所有持有方都在
// Renderer 子树内、先于 Device 析构,天然满足;勿存进静态/长寿命容器)。
//
#ifndef HELLOVULKAN_SHADERMANAGER_H
#define HELLOVULKAN_SHADERMANAGER_H
@@ -10,6 +20,7 @@
#include <Engine/Core/Types.h>
#include <vulkan/vulkan.hpp>
#include <memory>
#include <string>
#include <vector>
@@ -17,31 +28,54 @@ namespace Engine {
class Device;
struct ShaderObject {
vk::ShaderEXT Handle;
vk::ShaderStageFlagBits Stage;
};
class ShaderManager {
public:
ShaderManager(Device& device);
~ShaderManager();
// ---- 引用计数句柄 ----
// 空句柄(创建失败)可用 operator bool 判定,此时 GetHandle()
// 返回空 handle(bind 前由调用方判空跳过)
class ShaderRef {
public:
ShaderRef() = default;
explicit operator bool() const { return m_Entry != nullptr; }
vk::ShaderEXT GetHandle() const {
return m_Entry ? m_Entry->handle : vk::ShaderEXT{};
}
vk::ShaderStageFlagBits GetStage() const { return m_Entry->stage; }
private:
friend class ShaderManager;
struct Entry {
vk::ShaderEXT handle{};
vk::ShaderStageFlagBits stage{};
Device* device = nullptr;
~Entry(); // cpp 定义:销毁 VkShaderEXT(需 Device 完整类型)
};
explicit ShaderRef(std::shared_ptr<Entry> e)
: m_Entry(std::move(e)) {}
std::shared_ptr<Entry> m_Entry;
};
explicit ShaderManager(Device& device);
~ShaderManager() = default; // shader 随引用计数自动释放
ShaderManager(const ShaderManager&) = delete;
ShaderManager& operator=(const ShaderManager&) = delete;
// 改成接受 vector
ShaderObject CreateFromFile(const std::string& spvPath,
vk::ShaderStageFlagBits stage,
const std::vector<vk::DescriptorSetLayout>& layouts = {},
u32 pushConstantSize = 0);
ShaderRef CreateFromFile(const std::string& spvPath,
vk::ShaderStageFlagBits stage,
const std::vector<vk::DescriptorSetLayout>& layouts = {},
u32 pushConstantSize = 0);
ShaderObject CreateFromSPIRV(const std::vector<u32>& spirv,
vk::ShaderStageFlagBits stage,
const std::vector<vk::DescriptorSetLayout>& layouts = {},
u32 pushConstantSize = 0) const;
void Destroy(const ShaderObject& shader) const;
ShaderRef CreateFromSPIRV(const std::vector<u32>& spirv,
vk::ShaderStageFlagBits stage,
const std::vector<vk::DescriptorSetLayout>& layouts = {},
u32 pushConstantSize = 0) const;
private:
static std::vector<u32> ReadSPIRV(const std::string& path);