默认 ClearColor 0.1 → 0(策划阶段结束,无用)。 透明分层折射(玻璃见玻璃):透明段原来单个 ForwardTrans pass,折射 采样源 SceneColor 副本只在不透明段后快照一次(不含任何透明体)→ 前 玻璃折射不出后玻璃。改为按绘制单位远→近拆成至多 kTransparentLayers 层,每层绘制前刷新 SceneColor 副本,使层 L 玻璃采样含所有更远透明体。 RenderGraph pass 编译期定序,故层数固定;超出并入末层。深度 storeOp 改 eStore(多层需 Load 承接有效深度内容)。shader 不改。 透明排序修正:排序键由"相机到实例的欧氏距离"改为视图空间深度 (-(view·p).z)——欧氏距离把侧向偏移计入距离,偏侧物体被误判为更远 (正方体右上的甜甜圈在特定视角被排到后方)。并让透明队列逐实例成批 (不合并):透明本就是每层一个绘制单位,合并成多实例 batch 会让排序 只能用批平均深度、批内实例无法各自定序。零额外 draw 代价。
299 lines
14 KiB
C++
299 lines
14 KiB
C++
//
|
||
// 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
|