CPU 逐像素卷积(venice 4k 阻塞 7.36s)→ 4 个 compute shader 一次性 提交:equirect→cube / irradiance / prefilter×5mip / BRDF LUT。 Cubemap 瘦身为纯资源持有类,生成职责移交 IblGenerator; 环境经 AssetManager 缓存(EnvRef),Renderer::SetEnvironment 动态换绑(RenderPass::OnEnvironmentChanged 广播)。 调试视图 +4:EnvCube / Irradiance / Prefilter / BRDF LUT。 性能前后对比(RTX 5060 Laptop,venice_dawn_1_4k.hdr): - 改造前 CPU:7.36 s(主线程阻塞) - 改造后 GPU:HDR decode 127-134ms + 资源 22-35ms + compute 17-19ms ≈ 165-190ms(约 40×);Neutral 兜底 ~6ms - 运行中动态切换环境同量级(ms 级 GPU,decode 占大头) 冒烟:两构建目录 0 警告;运行期 0 validation(含两次环境切换 + 调试视图 9-12 轮播自动化验证)。
112 lines
3.6 KiB
C++
112 lines
3.6 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,
|
||
const vk::ShaderStageFlagBits stage,
|
||
const std::vector<vk::DescriptorSetLayout>& layouts,
|
||
const u32 pushConstantSize)
|
||
{
|
||
const 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,
|
||
const vk::ShaderStageFlagBits stage,
|
||
const std::vector<vk::DescriptorSetLayout>& layouts,
|
||
const u32 pushConstantSize) const
|
||
{
|
||
vk::ShaderStageFlags nextStage = {};
|
||
if (stage == vk::ShaderStageFlagBits::eVertex) {
|
||
nextStage = vk::ShaderStageFlagBits::eFragment;
|
||
}
|
||
// compute 单阶段:无 nextStage(VUID-09183 只约束 graphics 链)
|
||
|
||
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 决定,声明范围只是能力上限。
|
||
// compute 无程序链,单独按 eCompute 声明(layout/push 调用点一致)
|
||
pushRanges[0].setStageFlags(stage == vk::ShaderStageFlagBits::eCompute
|
||
? vk::ShaderStageFlagBits::eCompute
|
||
: vk::ShaderStageFlagBits::eVertex |
|
||
vk::ShaderStageFlagBits::eFragment)
|
||
.setOffset(0)
|
||
.setSize(pushConstantSize);
|
||
createInfo.setPushConstantRanges(pushRanges);
|
||
}
|
||
|
||
vk::ShaderEXT shader;
|
||
try {
|
||
const 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) const
|
||
{
|
||
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 {};
|
||
}
|
||
|
||
const 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
|