feat: Jolt 物理系统——刚体/碰撞体/物理调试图元 + 引擎时间单源
- PhysicsWorld:Jolt 集成,Dynamic/Kinematic/Static 刚体,姿态写回 Transform(Unity FixedUpdate 语义,吃 scaled dt,暂停自动冻结) - 碰撞体:Box(默认/手动 halfExtents)+ Mesh 碰撞体(Static→精确 MeshShape / Dynamic·Kinematic→ConvexHull,数据源 = Mesh ctor 缓存 的 CPU 位置副本,退化三角剔除,失败回退 Box 并告警) - PhysicsDebugPass:物理调试图元每帧上传绘制(debug_line shader) - Time:引擎时间单源(BeginFrame 落定 FrameTime,scaled/unscaled 分离) - JoltPhysics 以 submodule 接入(官方 url)
This commit is contained in:
@@ -0,0 +1,250 @@
|
||||
//
|
||||
// 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)
|
||||
: m_Device(device), m_Descriptors(descriptors), m_Frame(frame),
|
||||
m_HDRColor(hdrColor), m_Depth(depth), m_LineVB(lineVB), m_TriVB(triVB)
|
||||
{
|
||||
// 无描述符 set:viewProj 走 push constant(shader 侧声明一致)
|
||||
m_Layout = descriptors.CreatePipelineLayout(
|
||||
std::vector<vk::DescriptorSetLayout>{}, sizeof(glm::mat4));
|
||||
|
||||
const std::string shaderDir = "shaders/";
|
||||
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 = vert.Handle;
|
||||
m_Frag = frag.Handle;
|
||||
if (!m_Vert || !m_Frag) {
|
||||
ENGINE_FATAL("PhysicsDebugPass: failed to create shader objects");
|
||||
}
|
||||
}
|
||||
|
||||
PhysicsDebugPass::~PhysicsDebugPass()
|
||||
{
|
||||
const auto& vkDevice = m_Device.GetDevice();
|
||||
|
||||
if (m_Vert) vkDevice.destroyShaderEXT(m_Vert);
|
||||
if (m_Frag) vkDevice.destroyShaderEXT(m_Frag);
|
||||
vkDevice.destroyPipelineLayout(m_Layout);
|
||||
}
|
||||
|
||||
void PhysicsDebugPass::PrepareFrame(u32 frameSlot,
|
||||
const PhysicsDebugFrame& src)
|
||||
{
|
||||
// 预算截断:物理调试不应打满(打满说明该收敛可视化了)
|
||||
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 上传同一窗口语义
|
||||
if (lineVerts > 0) {
|
||||
m_LineVB.Update(frameSlot, src.lines.data(),
|
||||
static_cast<u64>(lineVerts) *
|
||||
sizeof(PhysicsDebugVertex));
|
||||
}
|
||||
if (triVerts > 0) {
|
||||
m_TriVB.Update(frameSlot, src.triangles.data(),
|
||||
static_cast<u64>(triVerts) *
|
||||
sizeof(PhysicsDebugVertex));
|
||||
}
|
||||
m_LineCounts[frameSlot] = lineVerts;
|
||||
m_TriCounts[frameSlot] = triVerts;
|
||||
}
|
||||
|
||||
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];
|
||||
if (lineCount == 0 && triCount == 0) 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, m_Frag};
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user