feat: ImGui docking Editor + 显示链/帧同步/输入语义重构
- App/editor: Hierarchy/Inspector/Viewport/Stats 四面板 + DockBuilder 默认布局,main.cpp 瘦身为场景搭建与生命周期 - 显示链: Tonemap→PresentTarget 经 ImGui::Image 呈现(零色偏),backbuffer 仅承载 ImGui;Direct/Viewport 双显示模式 + 面板分辨率上报 - 帧首同步 BeginFrameSync: resize/vsync/尺寸/模式意图统一帧首落定(修描述符悬垂);修复 vsync 意图未落 swapchain 导致的每帧重建交换链 - 输入: Editor 每帧判定键盘/鼠标归属(打字让位、Viewport hover/focus 放行、Fullscreen 全放行) - 材质: roughness 独立槽位 + 非 glTF 金属/粗糙贴图映射;透明队列 depthWrite=FALSE + translucent 独立 shader + scratch 清帧 - FBX: 非蒙皮网格烘焙节点链全局变换(Z-up 旋转/单位换算落顶点) - Hierarchy 行 ImGui ID 以 UUID 作用域(重名不冲突、改名不丢选中)
This commit is contained in:
@@ -4,8 +4,10 @@
|
||||
|
||||
#include <Engine/Renderer/Passes/SceneGpu.h>
|
||||
#include <Engine/Renderer/Material.h>
|
||||
#include <Engine/Renderer/Mesh.h> // IndirectGroup 取数(inline 经 Mesh)
|
||||
#include <Engine/Renderer/Cubemap.h>
|
||||
#include <Engine/Vulkan/Device.h>
|
||||
#include <Engine/Vulkan/Buffer.h>
|
||||
#include <Engine/Vulkan/DescriptorManager.h>
|
||||
#include <Engine/Vulkan/ShaderManager.h>
|
||||
#include <Engine/Vulkan/UniformBuffer.h>
|
||||
@@ -15,14 +17,18 @@
|
||||
|
||||
namespace Engine::Passes
|
||||
{
|
||||
SceneGpu::SceneGpu(Device& device, DescriptorManager& descriptors,
|
||||
SceneGpu::SceneGpu(Device& device, MemoryAllocator& allocator,
|
||||
DescriptorManager& descriptors,
|
||||
ShaderManager& shaders,
|
||||
UniformBuffer& ubo, UniformBuffer& objectBuffer,
|
||||
UniformBuffer& prevObjectBuffer)
|
||||
: m_Device(device), m_Descriptors(descriptors)
|
||||
UniformBuffer& prevObjectBuffer,
|
||||
UniformBuffer& skinOffsetBuffer,
|
||||
UniformBuffer& skinPaletteBuffer)
|
||||
: m_Device(device), m_Allocator(allocator), m_Descriptors(descriptors)
|
||||
{
|
||||
// Set 0: UBO + GPUDriven 实例数据(SSBO,vertex 读取)
|
||||
// + 上一帧实例矩阵(TAA velocity,vertex 读取)
|
||||
// + 骨骼蒙皮:SkinOffsets(实例→palette 基址)+ SkinPalettes(骨骼矩阵)
|
||||
std::vector<DescriptorBinding> uboBindings = {
|
||||
{0, vk::DescriptorType::eUniformBuffer,
|
||||
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment},
|
||||
@@ -30,6 +36,10 @@ namespace Engine::Passes
|
||||
vk::ShaderStageFlagBits::eVertex},
|
||||
{2, vk::DescriptorType::eStorageBuffer,
|
||||
vk::ShaderStageFlagBits::eVertex},
|
||||
{3, vk::DescriptorType::eStorageBuffer,
|
||||
vk::ShaderStageFlagBits::eVertex},
|
||||
{4, vk::DescriptorType::eStorageBuffer,
|
||||
vk::ShaderStageFlagBits::eVertex},
|
||||
};
|
||||
uboSetLayout = descriptors.CreateLayout(uboBindings);
|
||||
|
||||
@@ -81,6 +91,11 @@ namespace Engine::Passes
|
||||
shaderDir + "simple_shader.frag.spv",
|
||||
vk::ShaderStageFlagBits::eFragment,
|
||||
{uboSetLayout, textureSetLayout, shadowSetLayout}, 0);
|
||||
// 透明变体:同一套布局,frag 末尾写 gl_FragDepth=1.0
|
||||
auto transFrag = shaders.CreateFromFile(
|
||||
shaderDir + "simple_shader_translucent.frag.spv",
|
||||
vk::ShaderStageFlagBits::eFragment,
|
||||
{uboSetLayout, textureSetLayout, shadowSetLayout}, 0);
|
||||
|
||||
// shadow shader:depth-only,push = lightVP
|
||||
auto shadowVertShader = shaders.CreateFromFile(
|
||||
@@ -96,10 +111,12 @@ namespace Engine::Passes
|
||||
|
||||
forwardVert = vert.Handle;
|
||||
forwardFrag = frag.Handle;
|
||||
translucentFrag = transFrag.Handle;
|
||||
shadowVert = shadowVertShader.Handle;
|
||||
shadowFrag = shadowFragShader.Handle;
|
||||
|
||||
if (!forwardVert || !forwardFrag || !shadowVert || !shadowFrag) {
|
||||
if (!forwardVert || !forwardFrag || !translucentFrag ||
|
||||
!shadowVert || !shadowFrag) {
|
||||
ENGINE_FATAL("SceneGpu: failed to create shader objects");
|
||||
}
|
||||
|
||||
@@ -132,10 +149,34 @@ namespace Engine::Passes
|
||||
descriptors.UpdateStorageBuffer(
|
||||
uboSets[slot], 2, prevObjectBuffer.Get(slot),
|
||||
prevObjectBuffer.GetSize());
|
||||
descriptors.UpdateStorageBuffer(
|
||||
uboSets[slot], 3, skinOffsetBuffer.Get(slot),
|
||||
skinOffsetBuffer.GetSize());
|
||||
descriptors.UpdateStorageBuffer(
|
||||
uboSets[slot], 4, skinPaletteBuffer.Get(slot),
|
||||
skinPaletteBuffer.GetSize());
|
||||
|
||||
shadowSets[slot] = descriptors.AllocateDescriptorSet(shadowSetLayout);
|
||||
}
|
||||
// shadowSets 的图片绑定在 Compile 后由 BindShadowMap 完成
|
||||
|
||||
// ---- MDI per-slot ring:命令 + count(CPU 直写,host visible)----
|
||||
// 布局 [main | casc0 | casc1 | casc2] × kMaxInstances 命令预算;
|
||||
// 两者 usage 都要 eIndirectBuffer(count buffer 的 spec 要求)
|
||||
const vk::DeviceSize cmdBytes = vk::DeviceSize(kIndirectViewMain + 1 + kMaxCascades) *
|
||||
kMaxInstances * sizeof(IndirectDrawCmd);
|
||||
const vk::DeviceSize cntBytes = vk::DeviceSize(kIndirectViewMain + 1 + kMaxCascades) *
|
||||
kMaxInstances * sizeof(u32);
|
||||
constexpr auto memProps = vk::MemoryPropertyFlagBits::eHostVisible |
|
||||
vk::MemoryPropertyFlagBits::eHostCoherent;
|
||||
for (u32 slot = 0; slot < kMaxFramesInFlight; ++slot) {
|
||||
indirectCmdBuffers[slot] = CreateScope<Buffer>(
|
||||
device, allocator, cmdBytes,
|
||||
vk::BufferUsageFlagBits::eIndirectBuffer, memProps);
|
||||
indirectCountBuffers[slot] = CreateScope<Buffer>(
|
||||
device, allocator, cntBytes,
|
||||
vk::BufferUsageFlagBits::eIndirectBuffer, memProps);
|
||||
}
|
||||
}
|
||||
|
||||
SceneGpu::~SceneGpu()
|
||||
@@ -144,6 +185,7 @@ namespace Engine::Passes
|
||||
|
||||
if (forwardVert) vkDevice.destroyShaderEXT(forwardVert);
|
||||
if (forwardFrag) vkDevice.destroyShaderEXT(forwardFrag);
|
||||
if (translucentFrag) vkDevice.destroyShaderEXT(translucentFrag);
|
||||
if (shadowVert) vkDevice.destroyShaderEXT(shadowVert);
|
||||
if (shadowFrag) vkDevice.destroyShaderEXT(shadowFrag);
|
||||
|
||||
@@ -168,6 +210,52 @@ namespace Engine::Passes
|
||||
}
|
||||
}
|
||||
|
||||
// ---- MDI:per-slot 上传 + 组绘制 ----
|
||||
|
||||
void SceneGpu::UploadIndirect(u32 frameSlot, const FrameSceneData& frame)
|
||||
{
|
||||
auto uploadView = [&](u32 viewIdx, const CullResult& v) {
|
||||
if (v.indirectCommands.empty()) return;
|
||||
const vk::DeviceSize base =
|
||||
vk::DeviceSize(viewIdx) * kMaxInstances;
|
||||
indirectCmdBuffers[frameSlot]->Upload(
|
||||
v.indirectCommands.data(),
|
||||
v.indirectCommands.size() * sizeof(IndirectDrawCmd),
|
||||
base * sizeof(IndirectDrawCmd));
|
||||
// 每组一个 count,下标 = 组首命令下标(drawIndexedIndirectCount
|
||||
// 从此读取实际执行条数——GPU 剔除切片接管时只换写入者)
|
||||
for (const auto& g : v.indirectGroups) {
|
||||
indirectCountBuffers[frameSlot]->Upload(
|
||||
&g.commandCount, sizeof(u32),
|
||||
(base + g.firstCommand) * sizeof(u32));
|
||||
}
|
||||
};
|
||||
uploadView(kIndirectViewMain, frame.mainView);
|
||||
for (u32 c = 0; c < kMaxCascades; ++c) {
|
||||
uploadView(kIndirectViewMain + 1 + c, frame.shadowCascades[c]);
|
||||
}
|
||||
}
|
||||
|
||||
void SceneGpu::DrawIndirectGroup(vk::CommandBuffer cmd, u32 frameSlot,
|
||||
u32 viewIndex, const IndirectGroup& g)
|
||||
{
|
||||
// 组内全部命令共享同一 megabuffer:bind 一次,逐 mesh 定位由
|
||||
// 命令里的 firstIndex/vertexOffset 承担
|
||||
cmd.bindVertexBuffers(0, g.mesh->GetVertexBuffer(), {vk::DeviceSize(0)});
|
||||
cmd.bindIndexBuffer(g.mesh->GetIndexBuffer(), vk::DeviceSize(0),
|
||||
vk::IndexType::eUint32);
|
||||
const vk::DeviceSize base =
|
||||
vk::DeviceSize(viewIndex) * kMaxInstances;
|
||||
|
||||
cmd.drawIndexedIndirectCount(
|
||||
indirectCmdBuffers[frameSlot]->Get(),
|
||||
base * sizeof(IndirectDrawCmd),
|
||||
indirectCountBuffers[frameSlot]->Get(),
|
||||
(base + g.firstCommand) * sizeof(u32),
|
||||
g.commandCount, // maxDrawCount = CPU 打包的组大小
|
||||
sizeof(IndirectDrawCmd));
|
||||
}
|
||||
|
||||
void SceneGpu::BindEnvironment(const Cubemap& env)
|
||||
{
|
||||
// IBL 资源 view 全程稳定(不随 RG 重编译变化),绑一次即可。
|
||||
|
||||
Reference in New Issue
Block a user