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 轮播自动化验证)。
192 lines
7.7 KiB
C++
192 lines
7.7 KiB
C++
//
|
||
// Created by atdunbg on 2026/9/23.
|
||
//
|
||
|
||
#include <Engine/Renderer/Passes/SkyboxPass.h>
|
||
#include <Engine/Renderer/Cubemap.h>
|
||
#include <Engine/Vulkan/Device.h>
|
||
#include <Engine/Vulkan/DescriptorManager.h>
|
||
#include <Engine/Vulkan/ShaderManager.h>
|
||
#include <Engine/Core/Log.h>
|
||
|
||
#include <glm/glm.hpp>
|
||
|
||
#include <tracy/Tracy.hpp>
|
||
|
||
#include <array>
|
||
|
||
namespace Engine::Passes
|
||
{
|
||
SkyboxPass::SkyboxPass(Device& device, ShaderManager& shaders,
|
||
DescriptorManager& descriptors, FrameSceneData& frame,
|
||
RG::TextureHandle hdrColor, const Cubemap* env)
|
||
: m_Device(device), m_Descriptors(descriptors), m_Frame(frame),
|
||
m_HDRColor(hdrColor), m_Env(env)
|
||
{
|
||
// Set0 = {samplerCube}(fragment 采样)
|
||
std::vector<DescriptorBinding> bindings = {
|
||
{0, vk::DescriptorType::eCombinedImageSampler,
|
||
vk::ShaderStageFlagBits::eFragment},
|
||
};
|
||
m_SetLayout = descriptors.CreateLayout(bindings);
|
||
|
||
// push = invVP 64B(skybox.vert/.frag 声明一致,VUID-08878)
|
||
m_Layout = descriptors.CreatePipelineLayout(
|
||
std::vector<vk::DescriptorSetLayout>{m_SetLayout},
|
||
sizeof(glm::mat4));
|
||
|
||
const std::string shaderDir = "shaders/";
|
||
auto vert = shaders.CreateFromFile(
|
||
shaderDir + "skybox.vert.spv",
|
||
vk::ShaderStageFlagBits::eVertex,
|
||
std::vector<vk::DescriptorSetLayout>{m_SetLayout},
|
||
sizeof(glm::mat4));
|
||
auto frag = shaders.CreateFromFile(
|
||
shaderDir + "skybox.frag.spv",
|
||
vk::ShaderStageFlagBits::eFragment,
|
||
std::vector<vk::DescriptorSetLayout>{m_SetLayout},
|
||
sizeof(glm::mat4));
|
||
|
||
m_Vert = vert.Handle;
|
||
m_Frag = frag.Handle;
|
||
if (!m_Vert || !m_Frag) {
|
||
ENGINE_FATAL("SkyboxPass: failed to create shader objects");
|
||
}
|
||
|
||
// per-slot set:cube view 全程稳定,构造期绑定一次即可
|
||
for (u32 slot = 0; slot < kMaxFramesInFlight; ++slot) {
|
||
m_Sets[slot] = descriptors.AllocateDescriptorSet(m_SetLayout);
|
||
if (m_Env) {
|
||
descriptors.UpdateTexture(m_Sets[slot], 0,
|
||
m_Env->GetSampler(),
|
||
m_Env->GetView());
|
||
}
|
||
}
|
||
}
|
||
|
||
SkyboxPass::~SkyboxPass()
|
||
{
|
||
const auto& vkDevice = m_Device.GetDevice();
|
||
|
||
if (m_Vert) vkDevice.destroyShaderEXT(m_Vert);
|
||
if (m_Frag) vkDevice.destroyShaderEXT(m_Frag);
|
||
vkDevice.destroyPipelineLayout(m_Layout);
|
||
vkDevice.destroyDescriptorSetLayout(m_SetLayout);
|
||
}
|
||
|
||
void SkyboxPass::OnEnvironmentChanged(const Cubemap* env)
|
||
{
|
||
m_Env = env;
|
||
if (!m_Env) return;
|
||
|
||
// cube view 全程稳定语义是"单个环境内";切换环境 = 新 view,
|
||
// per-slot set 全部重绑
|
||
for (u32 slot = 0; slot < kMaxFramesInFlight; ++slot) {
|
||
m_Descriptors.UpdateTexture(m_Sets[slot], 0,
|
||
m_Env->GetSampler(),
|
||
m_Env->GetView());
|
||
}
|
||
}
|
||
|
||
void SkyboxPass::Register(RG::RenderGraph& rg)
|
||
{
|
||
rg.AddPass("Skybox", [this](RG::RenderGraphContext& ctx) {
|
||
ZoneScopedN("Skybox");
|
||
const vk::CommandBuffer cmd = ctx.cmd;
|
||
|
||
vk::ClearValue clear;
|
||
clear.color = vk::ClearColorValue(
|
||
std::array<float, 4>{0.1f, 0.1f, 0.1f, 1.0f});
|
||
|
||
vk::RenderingAttachmentInfo color;
|
||
color.setImageView(ctx.View(m_HDRColor))
|
||
.setImageLayout(vk::ImageLayout::eColorAttachmentOptimal)
|
||
.setLoadOp(vk::AttachmentLoadOp::eClear) // 清屏职责在本 pass
|
||
.setStoreOp(vk::AttachmentStoreOp::eStore)
|
||
.setClearValue(clear);
|
||
|
||
vk::RenderingInfo renderingInfo;
|
||
renderingInfo.setRenderArea({{0, 0}, ctx.extent})
|
||
.setLayerCount(1)
|
||
.setColorAttachments(color); // 无深度附件
|
||
|
||
cmd.beginRendering(renderingInfo);
|
||
|
||
if (m_Env) {
|
||
// 无环境贴图:只清屏(Forward loadOp=Load 依赖这里先写)
|
||
std::array<vk::ShaderStageFlagBits, 2> stages = {
|
||
vk::ShaderStageFlagBits::eVertex,
|
||
vk::ShaderStageFlagBits::eFragment
|
||
};
|
||
std::array<vk::ShaderEXT, 2> shaders = {m_Vert, m_Frag};
|
||
cmd.bindShadersEXT(stages, shaders);
|
||
|
||
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
|
||
m_Layout, 0,
|
||
m_Sets[ctx.frame.frameSlot], {});
|
||
cmd.pushConstants<glm::mat4>(
|
||
m_Layout,
|
||
vk::ShaderStageFlagBits::eVertex |
|
||
vk::ShaderStageFlagBits::eFragment,
|
||
0, m_Frame.skyInvVP);
|
||
|
||
// ---- 动态状态 ----
|
||
cmd.setRasterizerDiscardEnable(VK_FALSE);
|
||
|
||
cmd.setDepthTestEnable(VK_FALSE);
|
||
cmd.setDepthWriteEnable(VK_FALSE);
|
||
cmd.setDepthBoundsTestEnable(VK_FALSE);
|
||
cmd.setStencilTestEnable(VK_FALSE);
|
||
cmd.setDepthBiasEnable(VK_FALSE);
|
||
|
||
cmd.setPolygonModeEXT(vk::PolygonMode::eFill);
|
||
cmd.setCullMode(vk::CullModeFlagBits::eNone);
|
||
cmd.setFrontFace(vk::FrontFace::eCounterClockwise);
|
||
cmd.setPrimitiveTopology(vk::PrimitiveTopology::eTriangleList);
|
||
cmd.setPrimitiveRestartEnable(VK_FALSE);
|
||
|
||
// 全屏三角:无顶点输入
|
||
cmd.setVertexInputEXT({}, {});
|
||
|
||
cmd.setRasterizationSamplesEXT(vk::SampleCountFlagBits::e1);
|
||
vk::SampleMask sampleMask = 0xFFFFFFFF;
|
||
cmd.setSampleMaskEXT(vk::SampleCountFlagBits::e1, sampleMask);
|
||
cmd.setAlphaToCoverageEnableEXT(VK_FALSE);
|
||
cmd.setAlphaToOneEnableEXT(VK_FALSE);
|
||
|
||
vk::Viewport viewport(0.0f, 0.0f,
|
||
static_cast<float>(ctx.extent.width),
|
||
static_cast<float>(ctx.extent.height),
|
||
0.0f, 1.0f);
|
||
cmd.setViewportWithCount(viewport);
|
||
cmd.setScissorWithCount(vk::Rect2D({0, 0}, ctx.extent));
|
||
|
||
// 无混合直写(天空不透明)
|
||
vk::ColorBlendEquationEXT blendEquation;
|
||
blendEquation.setSrcColorBlendFactor(vk::BlendFactor::eOne)
|
||
.setDstColorBlendFactor(vk::BlendFactor::eZero)
|
||
.setColorBlendOp(vk::BlendOp::eAdd)
|
||
.setSrcAlphaBlendFactor(vk::BlendFactor::eOne)
|
||
.setDstAlphaBlendFactor(vk::BlendFactor::eZero)
|
||
.setAlphaBlendOp(vk::BlendOp::eAdd);
|
||
// ArrayProxyNoTemporaries 拒绝临时量:具名局部变量传入
|
||
vk::Bool32 blendEnable = VK_FALSE;
|
||
vk::ColorComponentFlags colorWriteMask =
|
||
vk::ColorComponentFlagBits::eR |
|
||
vk::ColorComponentFlagBits::eG |
|
||
vk::ColorComponentFlagBits::eB |
|
||
vk::ColorComponentFlagBits::eA;
|
||
cmd.setColorBlendEnableEXT(0, blendEnable);
|
||
cmd.setColorBlendEquationEXT(0, blendEquation);
|
||
cmd.setColorWriteMaskEXT(0, colorWriteMask);
|
||
|
||
cmd.draw(3, 1, 0, 0);
|
||
}
|
||
|
||
cmd.endRendering();
|
||
})
|
||
.Write(m_HDRColor, RG::ResourceUse::ColorAttachment);
|
||
}
|
||
|
||
} // namespace Engine::Passes
|