feat: 引入 RenderGraph、Application 框架、ECS 与资产句柄系统
架构分层(自上而下):
App(SandboxApp) → Application(生命周期+事件) → Scene(ECS) / AssetManager(句柄) → Renderer → RenderGraph → Vulkan 模块
- RenderGraph:声明式帧图。虚拟资源+生成号句柄、死 pass 剔除、
拓扑排序、资源生命周期、barrier 编译期预算(Synchronization2)、
物理镜像池按帧槽位隔离(帧间竞争从根上消除)
- 帧同步修复:per-frame-slot 命令缓冲与 UBO ring(原先单份存在
数据竞争);统一 kMaxFramesInFlight / FrameContext
- Application:生命周期钩子(OnInit/OnUpdate/OnRender/OnShutdown)
+ 事件系统(GLFW 回调 → POD 事件 → 类型化钩子);main 只 new 派生类
- ECS:自研稀疏集 Registry(Entity=(index,generation) 防悬空,
O(1) 增删查,Each 取最小池遍历);Scene 延迟销毁队列;
IdComponent{UUID} 稳定外部身份
- Assets:AssetManager + AssetHandle。组件存句柄不存裸指针/Ref
(单一所有者 + 多使用者),Renderer 删除 LoadModel 改为纯消费
- 修复退出析构:成员析构前 GPU waitIdle(资产层先于 Renderer
析构导致 VUID-00922 资源销毁时仍在使用)
- 引入 entt 子模块备用
validation 全程零错误零警告(RTX 5060 实测)
This commit is contained in:
@@ -8,25 +8,26 @@
|
||||
|
||||
namespace Engine {
|
||||
|
||||
CommandManager::CommandManager(Device& device)
|
||||
CommandManager::CommandManager(Device& device, u32 frameCount)
|
||||
: m_Device(device)
|
||||
{
|
||||
// 创建命令池
|
||||
// eResetCommandBuffer:允许单独 reset 某个 buffer,而不是整池 reset。
|
||||
// 若改为 per-frame pool + 整池 reset,可在帧槽 fence 等待后一次 reset,
|
||||
// 驱动开销更低——当前规模下单池多 buffer 已足够。
|
||||
vk::CommandPoolCreateInfo poolInfo;
|
||||
poolInfo.setQueueFamilyIndex(m_Device.GetGraphicsFamily())
|
||||
.setFlags(vk::CommandPoolCreateFlagBits::eResetCommandBuffer);
|
||||
|
||||
m_Pool = m_Device.GetDevice().createCommandPool(poolInfo);
|
||||
|
||||
// 分配一个命令缓冲
|
||||
vk::CommandBufferAllocateInfo allocInfo;
|
||||
allocInfo.setCommandPool(m_Pool)
|
||||
.setLevel(vk::CommandBufferLevel::ePrimary)
|
||||
.setCommandBufferCount(1);
|
||||
.setCommandBufferCount(frameCount);
|
||||
|
||||
m_Buffer = m_Device.GetDevice().allocateCommandBuffers(allocInfo)[0];
|
||||
m_Buffers = m_Device.GetDevice().allocateCommandBuffers(allocInfo);
|
||||
|
||||
ENGINE_INFO("CommandManager created");
|
||||
ENGINE_INFO("CommandManager created: {} frame buffers", frameCount);
|
||||
}
|
||||
|
||||
CommandManager::~CommandManager()
|
||||
@@ -34,20 +35,21 @@ namespace Engine {
|
||||
m_Device.GetDevice().destroyCommandPool(m_Pool);
|
||||
}
|
||||
|
||||
vk::CommandBuffer CommandManager::BeginFrame()
|
||||
vk::CommandBuffer CommandManager::BeginFrame(u32 frameSlot)
|
||||
{
|
||||
m_Buffer.reset();
|
||||
auto cmd = m_Buffers[frameSlot];
|
||||
cmd.reset();
|
||||
|
||||
vk::CommandBufferBeginInfo beginInfo;
|
||||
beginInfo.setFlags(vk::CommandBufferUsageFlagBits::eOneTimeSubmit);
|
||||
m_Buffer.begin(beginInfo);
|
||||
cmd.begin(beginInfo);
|
||||
|
||||
return m_Buffer;
|
||||
return cmd;
|
||||
}
|
||||
|
||||
void CommandManager::EndFrame()
|
||||
void CommandManager::EndFrame(u32 frameSlot)
|
||||
{
|
||||
m_Buffer.end();
|
||||
m_Buffers[frameSlot].end();
|
||||
}
|
||||
|
||||
} // namespace Engine
|
||||
|
||||
@@ -310,6 +310,26 @@ uint32_t Device::FindMemoryType(uint32_t typeFilter, vk::MemoryPropertyFlags pro
|
||||
return 0;
|
||||
}
|
||||
|
||||
vk::Format Device::FindSupportedFormat(const std::vector<vk::Format>& candidates,
|
||||
vk::ImageTiling tiling,
|
||||
vk::FormatFeatureFlags features) const
|
||||
{
|
||||
for (auto format : candidates) {
|
||||
auto props = m_PhysicalDevice.getFormatProperties(format);
|
||||
if (tiling == vk::ImageTiling::eLinear &&
|
||||
(props.linearTilingFeatures & features) == features) {
|
||||
return format;
|
||||
}
|
||||
if (tiling == vk::ImageTiling::eOptimal &&
|
||||
(props.optimalTilingFeatures & features) == features) {
|
||||
return format;
|
||||
}
|
||||
}
|
||||
|
||||
ENGINE_FATAL("No supported format found in candidate list");
|
||||
return candidates.empty() ? vk::Format::eUndefined : candidates.front();
|
||||
}
|
||||
|
||||
// ---------- 验证层检查 ----------
|
||||
|
||||
bool Device::CheckValidationLayerSupport()
|
||||
|
||||
@@ -99,14 +99,10 @@ namespace Engine
|
||||
m_Extent.width, m_Extent.height,
|
||||
m_Images.size(),
|
||||
vk::to_string(m_Format));
|
||||
|
||||
CreateDepthResources();
|
||||
}
|
||||
|
||||
void Swapchain::Destroy()
|
||||
{
|
||||
DestroyDepthResources();
|
||||
|
||||
const auto& vkDevice = m_Device.GetDevice();
|
||||
|
||||
for (auto view : m_ImageViews)
|
||||
@@ -181,78 +177,4 @@ namespace Engine
|
||||
|
||||
return extent;
|
||||
}
|
||||
|
||||
vk::Format Swapchain::FindDepthFormat()
|
||||
{
|
||||
// 优先 D32_SFLOAT,退回 D32_SFLOAT_S8_UINT,再退回 D24_UNORM_S8_UINT
|
||||
std::array<vk::Format, 3> candidates = {
|
||||
vk::Format::eD32Sfloat,
|
||||
vk::Format::eD32SfloatS8Uint,
|
||||
vk::Format::eD24UnormS8Uint
|
||||
};
|
||||
|
||||
for (auto format : candidates)
|
||||
{
|
||||
auto props = m_Device.GetPhysicalDevice().getFormatProperties(format);
|
||||
if (props.optimalTilingFeatures & vk::FormatFeatureFlagBits::eDepthStencilAttachment)
|
||||
{
|
||||
return format;
|
||||
}
|
||||
}
|
||||
|
||||
ENGINE_FATAL("No depth format supported");
|
||||
return vk::Format::eD32Sfloat;
|
||||
}
|
||||
|
||||
void Swapchain::CreateDepthResources()
|
||||
{
|
||||
m_DepthFormat = FindDepthFormat();
|
||||
|
||||
vk::ImageCreateInfo imageInfo;
|
||||
imageInfo.setImageType(vk::ImageType::e2D)
|
||||
.setFormat(m_DepthFormat)
|
||||
.setExtent({m_Extent.width, m_Extent.height, 1})
|
||||
.setMipLevels(1)
|
||||
.setArrayLayers(1)
|
||||
.setSamples(vk::SampleCountFlagBits::e1)
|
||||
.setTiling(vk::ImageTiling::eOptimal)
|
||||
.setUsage(vk::ImageUsageFlagBits::eDepthStencilAttachment)
|
||||
.setSharingMode(vk::SharingMode::eExclusive)
|
||||
.setInitialLayout(vk::ImageLayout::eUndefined);
|
||||
|
||||
m_DepthImage = m_Device.GetDevice().createImage(imageInfo);
|
||||
|
||||
auto reqs = m_Device.GetDevice().getImageMemoryRequirements(m_DepthImage);
|
||||
auto alloc = m_Allocator.Allocate(reqs, vk::MemoryPropertyFlagBits::eDeviceLocal);
|
||||
|
||||
m_DepthMemory = alloc.Memory;
|
||||
m_Device.GetDevice().bindImageMemory(m_DepthImage, m_DepthMemory, alloc.Offset);
|
||||
|
||||
vk::ImageViewCreateInfo viewInfo;
|
||||
viewInfo.setImage(m_DepthImage)
|
||||
.setViewType(vk::ImageViewType::e2D)
|
||||
.setFormat(m_DepthFormat)
|
||||
.setSubresourceRange({
|
||||
vk::ImageAspectFlagBits::eDepth,
|
||||
0, 1, 0, 1
|
||||
});
|
||||
|
||||
m_DepthImageView = m_Device.GetDevice().createImageView(viewInfo);
|
||||
}
|
||||
|
||||
void Swapchain::DestroyDepthResources()
|
||||
{
|
||||
const auto& vkDevice = m_Device.GetDevice();
|
||||
if (m_DepthImageView)
|
||||
{
|
||||
vkDevice.destroyImageView(m_DepthImageView);
|
||||
m_DepthImageView = nullptr;
|
||||
}
|
||||
if (m_DepthImage)
|
||||
{
|
||||
vkDevice.destroyImage(m_DepthImage);
|
||||
m_DepthImage = nullptr;
|
||||
}
|
||||
// 内存由 MemoryAllocator 统一释放
|
||||
}
|
||||
} // namespace Engine
|
||||
|
||||
@@ -9,28 +9,26 @@
|
||||
|
||||
namespace Engine {
|
||||
|
||||
UniformBuffer::UniformBuffer(Device& device, MemoryAllocator& allocator, u64 size)
|
||||
UniformBuffer::UniformBuffer(Device& device, MemoryAllocator& allocator,
|
||||
u64 size, u32 frameCount)
|
||||
: m_Size(size)
|
||||
{
|
||||
// Uniform Buffer 需要 CPU 每帧更新,所以用 HOST_VISIBLE | HOST_COHERENT
|
||||
m_Buffer = CreateScope<Buffer>(
|
||||
device, allocator,
|
||||
size,
|
||||
vk::BufferUsageFlagBits::eUniformBuffer,
|
||||
vk::MemoryPropertyFlagBits::eHostVisible |
|
||||
vk::MemoryPropertyFlagBits::eHostCoherent);
|
||||
m_Buffers.reserve(frameCount);
|
||||
for (u32 i = 0; i < frameCount; ++i) {
|
||||
// Uniform Buffer 需要 CPU 每帧更新,所以用 HOST_VISIBLE | HOST_COHERENT
|
||||
m_Buffers.push_back(CreateScope<Buffer>(
|
||||
device, allocator, size,
|
||||
vk::BufferUsageFlagBits::eUniformBuffer,
|
||||
vk::MemoryPropertyFlagBits::eHostVisible |
|
||||
vk::MemoryPropertyFlagBits::eHostCoherent));
|
||||
}
|
||||
|
||||
ENGINE_INFO("UniformBuffer created: {} bytes", size);
|
||||
ENGINE_INFO("UniformBuffer created: {} bytes x {} frames", size, frameCount);
|
||||
}
|
||||
|
||||
UniformBuffer::~UniformBuffer()
|
||||
void UniformBuffer::Update(u32 frameSlot, const void* data, u64 size)
|
||||
{
|
||||
// Buffer 析构时自动销毁 vk::Buffer,内存由 MemoryAllocator 统一释放
|
||||
m_Buffers[frameSlot]->Upload(data, size);
|
||||
}
|
||||
|
||||
void UniformBuffer::Update(const void* data, u64 size)
|
||||
{
|
||||
m_Buffer->Upload(data, size);
|
||||
}
|
||||
|
||||
} // namespace Engine
|
||||
} // namespace Engine
|
||||
|
||||
Reference in New Issue
Block a user