feat: 搭建 Vulkan 1.4 渲染器骨架,绘制旋转 3D 立方体

使用 Vulkan 1.4 现代路径初始化引擎骨架:
- 用 Shader Object (VK_EXT_shader_object) 替代传统 VkPipeline
- 用 Dynamic Rendering 替代 RenderPass/Framebuffer
- 用 Synchronization2 管理 barrier

核心模块:
- Window:GLFW 封装,GLFW_NO_API 模式,兼容 Wayland
- Log:spdlog 封装
- Types:u32/u64/f32 类型别名

Vulkan 后端:
- Device:Instance、PhysicalDevice、LogicalDevice、Queue 及 feature 查询
- Swapchain:Image、ImageView、DepthBuffer、Recreate
- ShaderManager:SPIR-V 加载与 ShaderEXT 创建
- CommandManager:CommandPool 与主 CommandBuffer
- SyncManager:per-frame Fence 与 ImageAvailable semaphore,per-image RenderFinished semaphore,2 frames in flight
- MemoryAllocator:内存子分配器,256MB block,persistent mapping
- Buffer:VertexBuffer、IndexBuffer、UniformBuffer 封装
- UniformBuffer:每帧上传 MVP 矩阵
- DescriptorManager:DescriptorSetLayout、DescriptorPool、DescriptorSet 及更新

Renderer:
- 协调所有子系统
- 绘制每面不同颜色的旋转 3D 立方体
- 启用 depth test,MVP 矩阵由 GLM 计算

构建:
- CMake 组织 Engine 静态库与 App 可执行文件
- glfw、glm、spdlog 通过 add_subdirectory 引入
This commit is contained in:
2026-09-20 23:09:40 +08:00
parent 706ee2301e
commit 381b7c0aa6
38 changed files with 2423 additions and 12 deletions
+464
View File
@@ -0,0 +1,464 @@
//
// Created by atdunbg on 2026/9/20.
//
#include <Engine/Renderer/Renderer.h>
#include <Engine/Vulkan/Device.h>
#include <Engine/Vulkan/MemoryAllocator.h>
#include <Engine/Vulkan/DescriptorManager.h>
#include <Engine/Vulkan/ShaderManager.h>
#include <Engine/Vulkan/Swapchain.h>
#include <Engine/Vulkan/CommandManager.h>
#include <Engine/Vulkan/SyncManager.h>
#include <Engine/Vulkan/Buffer.h>
#include <Engine/Vulkan/UniformBuffer.h>
#include <Engine/Core/Window.h>
#include <Engine/Core/Log.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <array>
#include <limits>
#include <vector>
namespace Engine
{
// 顶点结构:位置 + 颜色
struct Vertex
{
float position[3];
float color[3];
};
// Uniform Buffer 结构
struct UniformBufferObject
{
glm::mat4 mvp;
};
Renderer::Renderer(Window& window)
: m_Window(window)
{
// 1. Device
m_Device = CreateScope<Device>(window);
// 2. MemoryAllocator
m_Allocator = CreateScope<MemoryAllocator>(*m_Device);
// 3. DescriptorManager
m_DescriptorManager = CreateScope<DescriptorManager>(*m_Device);
// 4. DescriptorSetLayout(一个 uniform buffer binding)
m_DescriptorSetLayout = m_DescriptorManager->CreateUniformBufferLayout(0);
// 5. PipelineLayout(shader 绑定描述符时用)
m_PipelineLayout = m_DescriptorManager->CreatePipelineLayout(m_DescriptorSetLayout);
// 6. ShaderManager,编译 shader 时传入描述符布局
m_ShaderManager = CreateScope<ShaderManager>(*m_Device);
std::string shaderDir = "shaders/";
auto vert = m_ShaderManager->CreateFromFile(
shaderDir + "simple_shader.vert.spv",
vk::ShaderStageFlagBits::eVertex,
m_DescriptorSetLayout);
auto frag = m_ShaderManager->CreateFromFile(
shaderDir + "simple_shader.frag.spv",
vk::ShaderStageFlagBits::eFragment,
m_DescriptorSetLayout);
m_VertShader = vert.Handle;
m_FragShader = frag.Handle;
if (!m_VertShader || !m_FragShader)
{
ENGINE_FATAL("Failed to create shader objects");
}
// 7. Swapchain
m_Swapchain = CreateScope<Swapchain>(*m_Device, *m_Allocator, window);
m_Extent = m_Swapchain->GetExtent();
m_Format = m_Swapchain->GetFormat();
// 8. SyncManager
m_SyncManager = CreateScope<SyncManager>(*m_Device, kMaxFramesInFlight, m_Swapchain->GetImageCount());
// 9. CommandManager
m_CommandManager = CreateScope<CommandManager>(*m_Device);
// 10. 顶点缓冲
std::vector<Vertex> vertices = {
// 前面 (-Z)
{{-0.5f, -0.5f, 0.5f}, {1.0f, 0.0f, 0.0f}},
{{ 0.5f, -0.5f, 0.5f}, {1.0f, 0.0f, 0.0f}},
{{ 0.5f, 0.5f, 0.5f}, {1.0f, 0.0f, 0.0f}},
{{-0.5f, 0.5f, 0.5f}, {1.0f, 0.0f, 0.0f}},
// 后面 (+Z)
{{-0.5f, -0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}},
{{-0.5f, 0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}},
{{ 0.5f, 0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}},
{{ 0.5f, -0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}},
// 左面 (-X)
{{-0.5f, -0.5f, -0.5f}, {0.0f, 0.0f, 1.0f}},
{{-0.5f, -0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}},
{{-0.5f, 0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}},
{{-0.5f, 0.5f, -0.5f}, {0.0f, 0.0f, 1.0f}},
// 右面 (+X)
{{ 0.5f, -0.5f, 0.5f}, {1.0f, 1.0f, 0.0f}},
{{ 0.5f, -0.5f, -0.5f}, {1.0f, 1.0f, 0.0f}},
{{ 0.5f, 0.5f, -0.5f}, {1.0f, 1.0f, 0.0f}},
{{ 0.5f, 0.5f, 0.5f}, {1.0f, 1.0f, 0.0f}},
// 上面 (+Y)
{{-0.5f, 0.5f, 0.5f}, {1.0f, 0.0f, 1.0f}},
{{ 0.5f, 0.5f, 0.5f}, {1.0f, 0.0f, 1.0f}},
{{ 0.5f, 0.5f, -0.5f}, {1.0f, 0.0f, 1.0f}},
{{-0.5f, 0.5f, -0.5f}, {1.0f, 0.0f, 1.0f}},
// 下面 (-Y)
{{-0.5f, -0.5f, -0.5f}, {0.0f, 1.0f, 1.0f}},
{{ 0.5f, -0.5f, -0.5f}, {0.0f, 1.0f, 1.0f}},
{{ 0.5f, -0.5f, 0.5f}, {0.0f, 1.0f, 1.0f}},
{{-0.5f, -0.5f, 0.5f}, {0.0f, 1.0f, 1.0f}},
};
u64 bufferSize = sizeof(Vertex) * vertices.size();
m_VertexBuffer = CreateScope<Buffer>(
*m_Device, *m_Allocator,
bufferSize,
vk::BufferUsageFlagBits::eVertexBuffer,
vk::MemoryPropertyFlagBits::eHostVisible |
vk::MemoryPropertyFlagBits::eHostCoherent);
m_VertexBuffer->Upload(vertices.data(), bufferSize);
// 索引缓冲:两个三角形组成一个四边形
std::vector<u32> indices = {
0, 1, 2, 2, 3, 0, // 前面
4, 5, 6, 6, 7, 4, // 后面
8, 9,10,10,11, 8, // 左面
12,13,14,14,15,12, // 右面
16,17,18,18,19,16, // 上面
20,21,22,22,23,20, // 下面
};
u64 indexBufferSize = sizeof(u32) * indices.size();
m_IndexBuffer = CreateScope<Buffer>(
*m_Device, *m_Allocator,
indexBufferSize,
vk::BufferUsageFlagBits::eIndexBuffer,
vk::MemoryPropertyFlagBits::eHostVisible |
vk::MemoryPropertyFlagBits::eHostCoherent);
m_IndexBuffer->Upload(indices.data(), indexBufferSize);
// 11. Uniform Buffer
m_UniformBuffer = CreateScope<UniformBuffer>(
*m_Device, *m_Allocator, sizeof(UniformBufferObject));
// 12. DescriptorSet + 绑定 UniformBuffer
m_DescriptorSet = m_DescriptorManager->AllocateDescriptorSet(m_DescriptorSetLayout);
m_DescriptorManager->UpdateUniformBuffer(
m_DescriptorSet, 0, m_UniformBuffer->Get(), m_UniformBuffer->GetSize());
// 13. Debug: 记录开始时间,用于旋转
m_StartTime = std::chrono::steady_clock::now();
ENGINE_INFO("Renderer initialized");
}
Renderer::~Renderer()
{
m_Device->GetDevice().waitIdle();
if (m_VertShader)
{
m_Device->GetDevice().destroyShaderEXT(m_VertShader);
}
if (m_FragShader)
{
m_Device->GetDevice().destroyShaderEXT(m_FragShader);
}
m_Device->GetDevice().destroyPipelineLayout(m_PipelineLayout);
m_Device->GetDevice().destroyDescriptorSetLayout(m_DescriptorSetLayout);
}
void Renderer::Render()
{
u32 frameIndex = m_CurrentFrame;
const auto& vkDevice = m_Device->GetDevice();
// 1. 更新 MVP 矩阵
auto currentTime = std::chrono::steady_clock::now();
float time = std::chrono::duration<float, std::chrono::seconds::period>(
currentTime - m_StartTime).count();
auto model = glm::rotate(glm::mat4(1.0f),
time * glm::radians(90.0f),
glm::vec3(1.0f, 1.0f, 1.0f));
auto view = glm::lookAt(glm::vec3(0.0f, 0.0f, 2.0f),
glm::vec3(0.0f, 0.0f, 0.0f),
glm::vec3(0.0f, 1.0f, 0.0f));
float aspect = static_cast<float>(m_Extent.width) /
static_cast<float>(m_Extent.height);
auto proj = glm::perspective(glm::radians(45.0f), aspect, 0.1f, 10.0f);
proj[1][1] *= -1.0f; // Vulkan Y 翻转
UniformBufferObject ubo{};
ubo.mvp = proj * view * model;
m_UniformBuffer->Update(&ubo, sizeof(ubo));
// 等待这一帧上一次完成
m_SyncManager->WaitFence(frameIndex);
// 2. 获取交换链 image
u32 imageIndex = 0;
try
{
const auto result = vkDevice.acquireNextImageKHR(
m_Swapchain->Get(),
std::numeric_limits<u64>::max(),
m_SyncManager->GetImageAvailable(frameIndex));
if (result.result == vk::Result::eErrorOutOfDateKHR)
{
m_Swapchain->Recreate(m_Window);
return; // ← fence 没被 reset,下帧 wait 立即返回
}
imageIndex = result.value;
}
catch (const vk::OutOfDateKHRError&)
{
m_Swapchain->Recreate(m_Window);
return;
}
// 3. acquire 成功, reset fence
m_SyncManager->ResetFence(frameIndex);
// 4. 录制命令
auto cmd = m_CommandManager->BeginFrame();
TransitionImageLayout(cmd, m_Swapchain->GetImages()[imageIndex],
vk::ImageLayout::eUndefined,
vk::ImageLayout::eColorAttachmentOptimal);
// 深度转换
vk::ImageMemoryBarrier2 depthBarrier;
depthBarrier.setSrcStageMask(vk::PipelineStageFlagBits2::eEarlyFragmentTests)
.setSrcAccessMask(vk::AccessFlagBits2::eDepthStencilAttachmentWrite)
.setDstStageMask(vk::PipelineStageFlagBits2::eEarlyFragmentTests)
.setDstAccessMask(vk::AccessFlagBits2::eDepthStencilAttachmentWrite)
.setOldLayout(vk::ImageLayout::eUndefined)
.setNewLayout(vk::ImageLayout::eDepthAttachmentOptimal)
.setImage(m_Swapchain->GetDepthImage())
.setSubresourceRange({
vk::ImageAspectFlagBits::eDepth,
0, 1, 0, 1
});
vk::DependencyInfo depthDep;
depthDep.setImageMemoryBarriers(depthBarrier);
cmd.pipelineBarrier2(depthDep);
RecordCommands(cmd, imageIndex);
TransitionImageLayout(cmd, m_Swapchain->GetImages()[imageIndex],
vk::ImageLayout::eColorAttachmentOptimal,
vk::ImageLayout::ePresentSrcKHR);
m_CommandManager->EndFrame();
// 5. 提交
std::array<vk::PipelineStageFlags, 1> waitStages = {
vk::PipelineStageFlagBits::eColorAttachmentOutput
};
std::array<vk::Semaphore, 1> waitSemaphores = {
m_SyncManager->GetImageAvailable(frameIndex)
};
std::array<vk::Semaphore, 1> signalSemaphores = {
m_SyncManager->GetRenderFinished(imageIndex)
};
vk::SubmitInfo submitInfo;
submitInfo.setCommandBuffers(cmd)
.setWaitSemaphores(waitSemaphores)
.setWaitDstStageMask(waitStages)
.setSignalSemaphores(signalSemaphores);
m_Device->GetGraphicsQueue().submit(submitInfo, m_SyncManager->GetFrameFence(frameIndex));
// 6. 呈现
std::array<vk::SwapchainKHR, 1> swapchains = {m_Swapchain->Get()};
std::array<vk::Semaphore, 1> presentWait = {
m_SyncManager->GetRenderFinished(imageIndex)
};
vk::PresentInfoKHR presentInfo;
presentInfo.setImageIndices(imageIndex)
.setSwapchains(swapchains)
.setWaitSemaphores(presentWait);
try
{
auto result = m_Device->GetPresentQueue().presentKHR(presentInfo);
if (result == vk::Result::eSuboptimalKHR)
{
m_Swapchain->Recreate(m_Window);
}
}
catch (const vk::OutOfDateKHRError&)
{
m_Swapchain->Recreate(m_Window);
}
}
void Renderer::RecordCommands(vk::CommandBuffer cmd, u32 imageIndex)
{
vk::ClearValue clearValue;
clearValue.color = vk::ClearColorValue(std::array<float, 4>{0.1f, 0.1f, 0.1f, 1.0f});
// 颜色附件
vk::RenderingAttachmentInfo colorAttachment;
colorAttachment.setImageView(m_Swapchain->GetImageViews()[imageIndex])
.setImageLayout(vk::ImageLayout::eColorAttachmentOptimal)
.setLoadOp(vk::AttachmentLoadOp::eClear)
.setStoreOp(vk::AttachmentStoreOp::eStore)
.setClearValue(clearValue);
// 深度附件
vk::RenderingAttachmentInfo depthAttachment;
depthAttachment.setImageView(m_Swapchain->GetDepthImageView())
.setImageLayout(vk::ImageLayout::eDepthAttachmentOptimal)
.setLoadOp(vk::AttachmentLoadOp::eClear)
.setStoreOp(vk::AttachmentStoreOp::eDontCare)
.setClearValue(vk::ClearValue(vk::ClearDepthStencilValue(1.0f, 0)));
vk::RenderingInfo renderingInfo;
renderingInfo.setRenderArea({{0, 0}, m_Extent})
.setLayerCount(1)
.setColorAttachments(colorAttachment)
.setPDepthAttachment(&depthAttachment);
cmd.beginRendering(renderingInfo);
// 绑定着色器
std::array<vk::ShaderStageFlagBits, 2> stages = {
vk::ShaderStageFlagBits::eVertex,
vk::ShaderStageFlagBits::eFragment
};
std::array<vk::ShaderEXT, 2> shaders = {m_VertShader, m_FragShader};
cmd.bindShadersEXT(stages, shaders);
// 绑定描述符集
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
m_PipelineLayout, 0,
m_DescriptorSet, {});
// ===== 动态状态 =====
cmd.setRasterizerDiscardEnable(VK_FALSE);
cmd.setDepthTestEnable(VK_TRUE);
cmd.setDepthWriteEnable(VK_TRUE);
cmd.setDepthCompareOp(vk::CompareOp::eLess);
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.setPrimitiveTopology(vk::PrimitiveTopology::eTriangleList);
cmd.setPrimitiveRestartEnable(VK_FALSE);
// 顶点输入
vk::VertexInputBindingDescription2EXT binding;
binding.setBinding(0)
.setStride(sizeof(f32) * 6)
.setInputRate(vk::VertexInputRate::eVertex)
.setDivisor(1);
std::array<vk::VertexInputAttributeDescription2EXT, 2> attributes;
attributes[0].setLocation(0)
.setBinding(0)
.setFormat(vk::Format::eR32G32B32Sfloat)
.setOffset(0);
attributes[1].setLocation(1)
.setBinding(0)
.setFormat(vk::Format::eR32G32B32Sfloat)
.setOffset(sizeof(f32) * 3);
cmd.setVertexInputEXT(binding, attributes);
cmd.setRasterizationSamplesEXT(vk::SampleCountFlagBits::e1);
vk::SampleMask sampleMask = 0xFFFFFFFF;
cmd.setSampleMaskEXT(vk::SampleCountFlagBits::e1, sampleMask);
cmd.setAlphaToCoverageEnableEXT(VK_FALSE);
cmd.setAlphaToOneEnableEXT(VK_FALSE);
vk::Viewport viewport(0.0f, 0.0f,
static_cast<float>(m_Extent.width),
static_cast<float>(m_Extent.height),
0.0f, 1.0f);
cmd.setViewportWithCount(viewport);
vk::Rect2D scissor({0, 0}, m_Extent);
cmd.setScissorWithCount(scissor);
vk::Bool32 blendEnable = VK_FALSE;
cmd.setColorBlendEnableEXT(0, blendEnable);
vk::ColorBlendEquationEXT blendEquation;
blendEquation.setSrcColorBlendFactor(vk::BlendFactor::eOne)
.setDstColorBlendFactor(vk::BlendFactor::eZero)
.setColorBlendOp(vk::BlendOp::eAdd)
.setSrcAlphaBlendFactor(vk::BlendFactor::eOne)
.setDstAlphaBlendFactor(vk::BlendFactor::eZero)
.setAlphaBlendOp(vk::BlendOp::eAdd);
cmd.setColorBlendEquationEXT(0, blendEquation);
vk::ColorComponentFlags colorWriteMask =
vk::ColorComponentFlagBits::eR |
vk::ColorComponentFlagBits::eG |
vk::ColorComponentFlagBits::eB |
vk::ColorComponentFlagBits::eA;
cmd.setColorWriteMaskEXT(0, colorWriteMask);
// 绑定顶点缓冲并绘制
cmd.bindVertexBuffers(0, m_VertexBuffer->Get(), {0});
cmd.bindIndexBuffer(m_IndexBuffer->Get(), 0, vk::IndexType::eUint32);
// cmd.draw(3, 1, 0, 0);
cmd.drawIndexed(36, 1, 0, 0, 0);
cmd.endRendering();
}
void Renderer::TransitionImageLayout(vk::CommandBuffer cmd, vk::Image image,
vk::ImageLayout oldLayout, vk::ImageLayout newLayout)
{
vk::ImageMemoryBarrier2 barrier;
barrier.setSrcStageMask(vk::PipelineStageFlagBits2::eColorAttachmentOutput)
.setSrcAccessMask(vk::AccessFlagBits2::eColorAttachmentWrite)
.setDstStageMask(vk::PipelineStageFlagBits2::eColorAttachmentOutput)
.setDstAccessMask(vk::AccessFlagBits2::eColorAttachmentWrite)
.setOldLayout(oldLayout)
.setNewLayout(newLayout)
.setImage(image)
.setSubresourceRange({
vk::ImageAspectFlagBits::eColor,
0, 1, 0, 1
});
vk::DependencyInfo dependencyInfo;
dependencyInfo.setImageMemoryBarriers(barrier);
cmd.pipelineBarrier2(dependencyInfo);
}
} // namespace Engine