Files
HelloVulkan/Engine/src/Renderer/Passes/PhysicsDebugPass.cpp
T
atdunbg 2df6e57b61 透明分层屏幕空间折射 + 透明排序修正 + 默认清屏色归零
默认 ClearColor 0.1 → 0(策划阶段结束,无用)。

透明分层折射(玻璃见玻璃):透明段原来单个 ForwardTrans pass,折射
采样源 SceneColor 副本只在不透明段后快照一次(不含任何透明体)→ 前
玻璃折射不出后玻璃。改为按绘制单位远→近拆成至多 kTransparentLayers
层,每层绘制前刷新 SceneColor 副本,使层 L 玻璃采样含所有更远透明体。
RenderGraph pass 编译期定序,故层数固定;超出并入末层。深度 storeOp
改 eStore(多层需 Load 承接有效深度内容)。shader 不改。

透明排序修正:排序键由"相机到实例的欧氏距离"改为视图空间深度
(-(view·p).z)——欧氏距离把侧向偏移计入距离,偏侧物体被误判为更远
(正方体右上的甜甜圈在特定视角被排到后方)。并让透明队列逐实例成批
(不合并):透明本就是每层一个绘制单位,合并成多实例 batch 会让排序
只能用批平均深度、批内实例无法各自定序。零额外 draw 代价。
2026-10-18 17:09:42 +08:00

299 lines
14 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// PhysicsDebugPass.cpp:pass 装配(shader object + ring VB 上传)与录制。
//
#include <Engine/Renderer/Passes/PhysicsDebugPass.h>
#include <Engine/Physics/PhysicsDebugTypes.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>
#include <cstddef>
namespace Engine::Passes
{
namespace
{
// 顶点布局:location0 = pos(vec3),location1 = color(vec4)
//(setVertexInputEXT 走 2EXT 结构,与 Vertex.h 同一套)
vk::VertexInputBindingDescription2EXT MakeBinding()
{
vk::VertexInputBindingDescription2EXT b;
b.setBinding(0)
.setStride(sizeof(PhysicsDebugVertex))
.setInputRate(vk::VertexInputRate::eVertex)
.setDivisor(1); // 0 需 zeroDivisor feature(VUID-04798)
return b;
}
std::array<vk::VertexInputAttributeDescription2EXT, 2> MakeAttributes()
{
std::array<vk::VertexInputAttributeDescription2EXT, 2> a;
a[0].setLocation(0).setBinding(0)
.setFormat(vk::Format::eR32G32B32Sfloat)
.setOffset(offsetof(PhysicsDebugVertex, pos));
a[1].setLocation(1).setBinding(0)
.setFormat(vk::Format::eR32G32B32A32Sfloat)
.setOffset(offsetof(PhysicsDebugVertex, color));
return a;
}
// 直写方程:(ONE, ZERO) → dst = src,数学上等价于无混合
vk::ColorBlendEquationEXT MakeDirectEquation()
{
vk::ColorBlendEquationEXT eq;
eq.setSrcColorBlendFactor(vk::BlendFactor::eOne)
.setDstColorBlendFactor(vk::BlendFactor::eZero)
.setColorBlendOp(vk::BlendOp::eAdd)
.setSrcAlphaBlendFactor(vk::BlendFactor::eOne)
.setDstAlphaBlendFactor(vk::BlendFactor::eZero)
.setAlphaBlendOp(vk::BlendOp::eAdd);
return eq;
}
} // namespace
PhysicsDebugPass::PhysicsDebugPass(Device& device, MemoryAllocator& /*allocator*/,
ShaderManager& shaders,
DescriptorManager& descriptors,
FrameSceneData& frame,
RG::TextureHandle hdrColor,
RG::TextureHandle depth,
UniformBuffer& lineVB, UniformBuffer& triVB,
UniformBuffer& debugVB)
: m_Device(device), m_Descriptors(descriptors), m_Frame(frame),
m_HDRColor(hdrColor), m_Depth(depth), m_LineVB(lineVB), m_TriVB(triVB),
m_DebugVB(debugVB)
{
// 无描述符 set:viewProj 走 push constant(shader 侧声明一致)
m_Layout = descriptors.CreatePipelineLayout(
std::vector<vk::DescriptorSetLayout>{}, sizeof(glm::mat4));
const std::string shaderDir = "shaders/debug/";
auto vert = shaders.CreateFromFile(
shaderDir + "debug_line.vert.spv",
vk::ShaderStageFlagBits::eVertex,
std::vector<vk::DescriptorSetLayout>{},
sizeof(glm::mat4));
auto frag = shaders.CreateFromFile(
shaderDir + "debug_line.frag.spv",
vk::ShaderStageFlagBits::eFragment,
std::vector<vk::DescriptorSetLayout>{},
sizeof(glm::mat4));
m_Vert = std::move(vert);
m_Frag = std::move(frag);
if (!m_Vert || !m_Frag) {
ENGINE_FATAL("PhysicsDebugPass: failed to create shader objects");
}
}
PhysicsDebugPass::~PhysicsDebugPass()
{
// shader 随 ShaderRef 引用计数自动销毁,此处只清其余原生句柄
m_Device.GetDevice().destroyPipelineLayout(m_Layout);
}
void PhysicsDebugPass::PrepareFrame(u32 frameSlot,
const PhysicsDebugFrame& src,
const Debug::DebugFrame* dbg)
{
// 预算截断:物理调试不应打满(打满说明该收敛可视化了)
const u32 lineVerts = std::min<u32>(
static_cast<u32>(src.lines.size()), kMaxLineVerts);
const u32 triVerts = std::min<u32>(
static_cast<u32>(src.triangles.size()), kMaxTriVerts);
if ((src.lines.size() > kMaxLineVerts ||
src.triangles.size() > kMaxTriVerts) && !m_TruncationWarned) {
m_TruncationWarned = true;
ENGINE_WARN("PhysicsDebugPass: vertex budget exceeded "
"(lines {} / tris {}), clamped", src.lines.size(),
src.triangles.size());
}
// fence 已等(Renderer::Render 的 WaitFence 之后调用),
// 写-读竞争关闭——与 UBO/SSBO 上传同一窗口语义
m_Spans[frameSlot].clear();
if (lineVerts > 0) {
m_LineVB.Update(frameSlot, src.lines.data(),
static_cast<u64>(lineVerts) *
sizeof(PhysicsDebugVertex));
m_Spans[frameSlot].push_back({false, 1.0f, true, 0, lineVerts});
}
if (triVerts > 0) {
m_TriVB.Update(frameSlot, src.triangles.data(),
static_cast<u64>(triVerts) *
sizeof(PhysicsDebugVertex));
m_Spans[frameSlot].push_back({true, 1.0f, true, 0, triVerts});
}
m_LineCounts[frameSlot] = lineVerts;
m_TriCounts[frameSlot] = triVerts;
// 通用调试源(独立 ring VB,与物理源隔离):批次按收集侧的
// (拓扑, 线宽, 深度) 原样展开为绘制段;超预算部分截断
if (dbg != nullptr && !dbg->Empty() && !dbg->verts.empty()) {
const u32 total = static_cast<u32>(dbg->verts.size());
const u32 n = std::min<u32>(total, Debug::kMaxVerts);
m_DebugVB.Update(frameSlot, dbg->verts.data(),
static_cast<u64>(n) * sizeof(Debug::Vertex));
u32 consumed = 0;
for (const Debug::Batch& b : dbg->batches) {
if (consumed >= n) break;
const u32 take = std::min<u32>(b.count, n - consumed);
if (take > 0) {
m_Spans[frameSlot].push_back(
{b.tris, b.width, b.depthTest, consumed, take});
}
consumed += take;
}
}
}
void PhysicsDebugPass::Register(RG::RenderGraph& rg)
{
rg.AddPass("PhysicsDebug", [this](RG::RenderGraphContext& ctx) {
const u32 lineCount = m_LineCounts[ctx.frame.frameSlot];
const u32 triCount = m_TriCounts[ctx.frame.frameSlot];
const std::vector<Span>& spans = m_Spans[ctx.frame.frameSlot];
if (lineCount == 0 && triCount == 0 && spans.empty()) {
return; // 空帧零开销
}
ZoneScopedN("PhysicsDebug");
const vk::CommandBuffer cmd = ctx.cmd;
// HDR:Load 承接 Forward 结果叠加;深度:Load 承接不透明深度
// 做测试(只测不写),DontCare——下帧 Forward 重新 Clear
vk::RenderingAttachmentInfo color;
color.setImageView(ctx.View(m_HDRColor))
.setImageLayout(vk::ImageLayout::eColorAttachmentOptimal)
.setLoadOp(vk::AttachmentLoadOp::eLoad)
.setStoreOp(vk::AttachmentStoreOp::eStore);
vk::RenderingAttachmentInfo depth;
depth.setImageView(ctx.View(m_Depth))
.setImageLayout(vk::ImageLayout::eDepthAttachmentOptimal)
.setLoadOp(vk::AttachmentLoadOp::eLoad)
.setStoreOp(vk::AttachmentStoreOp::eDontCare);
vk::RenderingInfo renderingInfo;
renderingInfo.setRenderArea({{0, 0}, ctx.extent})
.setLayerCount(1)
.setColorAttachments(color)
.setPDepthAttachment(&depth);
cmd.beginRendering(renderingInfo);
const std::array<vk::ShaderStageFlagBits, 2> stages = {
vk::ShaderStageFlagBits::eVertex,
vk::ShaderStageFlagBits::eFragment
};
const std::array<vk::ShaderEXT, 2> shaders = {m_Vert.GetHandle(), m_Frag.GetHandle()};
cmd.bindShadersEXT(stages, shaders);
// layout 的 push range 是 vertex|fragment(CreatePipelineLayout 默认),
// vkCmdPushConstants 的 stageFlags 必须覆盖 range 全部 stage(VUID-01796)
cmd.pushConstants<glm::mat4>(
m_Layout,
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment,
0, m_Frame.ubo.proj * m_Frame.ubo.view);
// ---- 动态状态(shader object 无管线对象,全量显式设置)----
cmd.setRasterizerDiscardEnable(VK_FALSE);
cmd.setDepthTestEnable(VK_TRUE);
cmd.setDepthCompareOp(vk::CompareOp::eLessOrEqual);
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.setPrimitiveRestartEnable(VK_FALSE);
const vk::VertexInputBindingDescription2EXT binding = MakeBinding();
const std::array<vk::VertexInputAttributeDescription2EXT, 2>
attributes = MakeAttributes();
cmd.setVertexInputEXT(binding, attributes);
cmd.setRasterizationSamplesEXT(vk::SampleCountFlagBits::e1);
constexpr vk::SampleMask sampleMask = 0xFFFFFFFF;
cmd.setSampleMaskEXT(vk::SampleCountFlagBits::e1, sampleMask);
cmd.setAlphaToCoverageEnableEXT(VK_FALSE);
cmd.setAlphaToOneEnableEXT(VK_FALSE);
const 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));
// 无混合直写(ArrayProxyNoTemporaries:具名局部变量传入)
constexpr vk::Bool32 blendEnable = VK_FALSE;
const vk::ColorBlendEquationEXT direct = MakeDirectEquation();
constexpr vk::ColorComponentFlags colorWriteMask =
vk::ColorComponentFlagBits::eR |
vk::ColorComponentFlagBits::eG |
vk::ColorComponentFlagBits::eB |
vk::ColorComponentFlagBits::eA;
cmd.setColorBlendEnableEXT(0, blendEnable);
cmd.setColorBlendEquationEXT(0, direct);
cmd.setColorWriteMaskEXT(0, colorWriteMask);
// 线段与三角形各一条流:换流 = 换 VB 绑定 + 拓扑
cmd.setLineWidth(1.0f); // 线拓扑必须显式设置(VUID-08617)
if (lineCount > 0) {
cmd.setPrimitiveTopology(vk::PrimitiveTopology::eLineList);
const std::array<vk::Buffer, 1> bufs{m_LineVB.Get(ctx.frame.frameSlot)};
const std::array<vk::DeviceSize, 1> offs{0};
cmd.bindVertexBuffers(0, bufs, offs);
cmd.draw(lineCount, 1, 0, 0);
}
if (triCount > 0) {
cmd.setPrimitiveTopology(vk::PrimitiveTopology::eTriangleList);
const std::array<vk::Buffer, 1> bufs{m_TriVB.Get(ctx.frame.frameSlot)};
const std::array<vk::DeviceSize, 1> offs{0};
cmd.bindVertexBuffers(0, bufs, offs);
cmd.draw(triCount, 1, 0, 0);
}
// 通用调试源批次:逐批动态状态(拓扑/线宽/深度测试)。
// 线宽 >1 需 wideLines 特性,不支持时钳回 1.0(VUID-07937)
if (!spans.empty()) {
const std::array<vk::Buffer, 1> bufs{m_DebugVB.Get(ctx.frame.frameSlot)};
const std::array<vk::DeviceSize, 1> offs{0};
cmd.bindVertexBuffers(0, bufs, offs);
for (const Span& s : spans) {
cmd.setPrimitiveTopology(
s.tris ? vk::PrimitiveTopology::eTriangleList
: vk::PrimitiveTopology::eLineList);
// 线宽单位 = 像素,设备范围 [1.0, lineWidthRange[1]]:
// 低于下限的值(如 0.18)是校验错误,驱动按 1px 处
// 理——必须钳进范围;wideLines 不支持则恒 1px
cmd.setLineWidth(m_Device.IsWideLinesSupported()
? std::clamp(s.width, 1.0f,
m_Device.MaxLineWidth())
: 1.0f);
cmd.setDepthTestEnable(s.depthTest ? VK_TRUE : VK_FALSE);
cmd.draw(s.count, 1, s.first, 0);
}
cmd.setLineWidth(1.0f); // 恢复默认,防状态外泄
}
cmd.endRendering();
})
// Write use(WAW 链):保证排在 Forward/ForwardTransL* 之后、
// TAA(Read HDRColor)之前——调试图元进入 TAA/Bloom/Tonemap 主链路
.Write(m_HDRColor, RG::ResourceUse::ColorAttachment)
.Write(m_Depth, RG::ResourceUse::DepthAttachment);
}
} // namespace Engine::Passes