// // PhysicsDebugPass.cpp:pass 装配(shader object + ring VB 上传)与录制。 // #include #include #include #include #include #include #include #include #include #include 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 MakeAttributes() { std::array 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{}, sizeof(glm::mat4)); const std::string shaderDir = "shaders/debug/"; auto vert = shaders.CreateFromFile( shaderDir + "debug_line.vert.spv", vk::ShaderStageFlagBits::eVertex, std::vector{}, sizeof(glm::mat4)); auto frag = shaders.CreateFromFile( shaderDir + "debug_line.frag.spv", vk::ShaderStageFlagBits::eFragment, std::vector{}, 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( static_cast(src.lines.size()), kMaxLineVerts); const u32 triVerts = std::min( static_cast(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(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(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(dbg->verts.size()); const u32 n = std::min(total, Debug::kMaxVerts); m_DebugVB.Update(frameSlot, dbg->verts.data(), static_cast(n) * sizeof(Debug::Vertex)); u32 consumed = 0; for (const Debug::Batch& b : dbg->batches) { if (consumed >= n) break; const u32 take = std::min(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& 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 stages = { vk::ShaderStageFlagBits::eVertex, vk::ShaderStageFlagBits::eFragment }; const std::array 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( 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 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(ctx.extent.width), static_cast(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 bufs{m_LineVB.Get(ctx.frame.frameSlot)}; const std::array offs{0}; cmd.bindVertexBuffers(0, bufs, offs); cmd.draw(lineCount, 1, 0, 0); } if (triCount > 0) { cmd.setPrimitiveTopology(vk::PrimitiveTopology::eTriangleList); const std::array bufs{m_TriVB.Get(ctx.frame.frameSlot)}; const std::array 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 bufs{m_DebugVB.Get(ctx.frame.frameSlot)}; const std::array 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/ForwardTrans 之后、 // TAA(Read HDRColor)之前——调试图元进入 TAA/Bloom/Tonemap 主链路 .Write(m_HDRColor, RG::ResourceUse::ColorAttachment) .Write(m_Depth, RG::ResourceUse::DepthAttachment); } } // namespace Engine::Passes