管线:Shadow → Forward(写 HDRColor R16G16B16A16F) → Tonemap → ImGui(loadOp=Load) → Present。对齐 UE/Unity 结构:场景线性值 进 HDR 离屏图,Tonemap(全屏三角,exposure push + gamma 编码) 显式完成显示变换,Backbuffer 只接收显示空间颜色。 - Swapchain 改 B8G8R8A8Unorm:Srgb 附件会硬件二次提亮,UI 泛白 (#4296FA→#8CCBFC);Unorm 下写什么显示什么,编码责任归 Tonemap - Shader object push range 规则落地:程序级声明——同一 pass 的 全部 shader EXT range 定义必须一致(VUID-08878),layout 同样 按 vertex|fragment 声明,push 调用点覆盖全部声明 stage(VUID-01796) - Tonemap 装配:Set0 = HDR 图(per-frame-slot 描述符 + GetPhysicalView), clamp 占位待 ACES 替换,bloom/FXAA 等后处理在 HDR 域插 pass - CMake 加 imgui_demo.cpp(ShowDemoWindow 链接) - 修复:m_TonemapDescriptorSets 未 resize 即下标写入(启动即崩) 验证:5 pass 全活,validation 0 error 0 warning,UI 原生色彩无泛白
108 lines
3.3 KiB
C++
108 lines
3.3 KiB
C++
//
|
||
// Created by atdunbg on 2026/9/20.
|
||
//
|
||
|
||
#include <Engine/Vulkan/ShaderManager.h>
|
||
#include <Engine/Vulkan/Device.h>
|
||
#include <Engine/Core/Log.h>
|
||
|
||
#include <array>
|
||
#include <fstream>
|
||
|
||
namespace Engine {
|
||
|
||
ShaderManager::ShaderManager(Device& device)
|
||
: m_Device(device)
|
||
{
|
||
}
|
||
|
||
ShaderManager::~ShaderManager()
|
||
{
|
||
}
|
||
|
||
ShaderObject ShaderManager::CreateFromFile(const std::string& spvPath,
|
||
vk::ShaderStageFlagBits stage,
|
||
const std::vector<vk::DescriptorSetLayout>& layouts,
|
||
u32 pushConstantSize)
|
||
{
|
||
auto spirv = ReadSPIRV(spvPath);
|
||
if (spirv.empty()) {
|
||
ENGINE_ERROR("Failed to read SPIR-V: {}", spvPath);
|
||
return { nullptr, stage };
|
||
}
|
||
return CreateFromSPIRV(spirv, stage, layouts, pushConstantSize);
|
||
}
|
||
|
||
ShaderObject ShaderManager::CreateFromSPIRV(const std::vector<u32>& spirv,
|
||
vk::ShaderStageFlagBits stage,
|
||
const std::vector<vk::DescriptorSetLayout>& layouts,
|
||
u32 pushConstantSize)
|
||
{
|
||
vk::ShaderStageFlags nextStage = {};
|
||
if (stage == vk::ShaderStageFlagBits::eVertex) {
|
||
nextStage = vk::ShaderStageFlagBits::eFragment;
|
||
}
|
||
|
||
vk::ShaderCreateInfoEXT createInfo;
|
||
createInfo.setStage(stage)
|
||
.setNextStage(nextStage)
|
||
.setCodeType(vk::ShaderCodeTypeEXT::eSpirv)
|
||
.setCodeSize(spirv.size() * sizeof(u32))
|
||
.setPCode(spirv.data())
|
||
.setPName("main");
|
||
|
||
if (!layouts.empty()) {
|
||
createInfo.setSetLayouts(layouts);
|
||
}
|
||
|
||
std::array<vk::PushConstantRange, 1> pushRanges;
|
||
if (pushConstantSize > 0) {
|
||
// push range 是"程序级"声明:shader object 要求同一程序绑定的
|
||
// 全部 shader(vert+frag)push range 定义完全一致(VUID-08878),
|
||
// 因此统一按 vertex|fragment 声明;shader 实际读取哪些字节由
|
||
// GLSL 的 push_constant block 决定,声明范围只是能力上限
|
||
pushRanges[0].setStageFlags(vk::ShaderStageFlagBits::eVertex |
|
||
vk::ShaderStageFlagBits::eFragment)
|
||
.setOffset(0)
|
||
.setSize(pushConstantSize);
|
||
createInfo.setPushConstantRanges(pushRanges);
|
||
}
|
||
|
||
vk::ShaderEXT shader;
|
||
try {
|
||
auto result = m_Device.GetDevice().createShadersEXT(createInfo);
|
||
shader = result.value[0];
|
||
}
|
||
catch (const vk::SystemError& e) {
|
||
ENGINE_ERROR("Failed to create shader object: {}", e.what());
|
||
return { nullptr, stage };
|
||
}
|
||
|
||
return { shader, stage };
|
||
}
|
||
|
||
void ShaderManager::Destroy(const ShaderObject& shader)
|
||
{
|
||
if (shader.Handle) {
|
||
m_Device.GetDevice().destroyShaderEXT(shader.Handle);
|
||
}
|
||
}
|
||
|
||
std::vector<u32> ShaderManager::ReadSPIRV(const std::string& path)
|
||
{
|
||
std::ifstream file(path, std::ios::binary | std::ios::ate);
|
||
if (!file) {
|
||
ENGINE_ERROR("Failed to open: {}", path);
|
||
return {};
|
||
}
|
||
|
||
size_t size = file.tellg();
|
||
std::vector<u32> buffer(size / sizeof(u32));
|
||
file.seekg(0);
|
||
file.read(reinterpret_cast<char*>(buffer.data()), size);
|
||
file.close();
|
||
|
||
return buffer;
|
||
}
|
||
|
||
} // namespace Engine
|