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
+47
View File
@@ -0,0 +1,47 @@
//
// Created by atdunbg on 2026/9/20.
//
#ifndef HELLOVULKAN_BUFFER_H
#define HELLOVULKAN_BUFFER_H
#include <vulkan/vulkan.hpp>
namespace Engine {
class Device;
class MemoryAllocator;
class Buffer {
public:
Buffer(Device& device, MemoryAllocator& allocator,
vk::DeviceSize size, vk::BufferUsageFlags usage,
vk::MemoryPropertyFlags memProps);
~Buffer();
Buffer(const Buffer&) = delete;
Buffer& operator=(const Buffer&) = delete;
vk::Buffer Get() const { return m_Buffer; }
vk::DeviceSize GetSize() const { return m_Size; }
// 只有 HOST_VISIBLE 的 Buffer 能调
void Upload(const void* data, vk::DeviceSize size, vk::DeviceSize offset = 0);
private:
Device& m_Device;
vk::Buffer m_Buffer;
vk::DeviceSize m_Size;
vk::DeviceMemory m_Memory;
vk::DeviceSize m_Offset;
vk::DeviceSize m_BlockSize; // 所属内存块的总大小
void* m_MappedPtr = nullptr;
};
} // namespace Engine
#endif //HELLOVULKAN_BUFFER_H
@@ -0,0 +1,38 @@
//
// Created by atdunbg on 2026/9/20.
//
#ifndef HELLOVULKAN_COMMANDMANAGER_H
#define HELLOVULKAN_COMMANDMANAGER_H
#include <vulkan/vulkan.hpp>
namespace Engine {
class Device;
class CommandManager {
public:
CommandManager(Device& device);
~CommandManager();
CommandManager(const CommandManager&) = delete;
CommandManager& operator=(const CommandManager&) = delete;
// 开始录制
vk::CommandBuffer BeginFrame();
// 结束录制
void EndFrame();
vk::CommandBuffer GetBuffer() const { return m_Buffer; }
private:
Device& m_Device;
vk::CommandPool m_Pool;
vk::CommandBuffer m_Buffer;
};
} // namespace Engine
#endif //HELLOVULKAN_COMMANDMANAGER_H
@@ -0,0 +1,46 @@
//
// Created by atdunbg on 2026/9/20.
//
#ifndef HELLOVULKAN_DESCRIPTORMANAGER_H
#define HELLOVULKAN_DESCRIPTORMANAGER_H
#include <Engine/Core/Types.h>
#include <vulkan/vulkan.hpp>
namespace Engine {
class Device;
class DescriptorManager {
public:
DescriptorManager(Device& device);
~DescriptorManager();
DescriptorManager(const DescriptorManager&) = delete;
DescriptorManager& operator=(const DescriptorManager&) = delete;
// 创建一个描述符布局:一个 binding,类型是 Uniform Buffer
vk::DescriptorSetLayout CreateUniformBufferLayout(u32 binding);
vk::PipelineLayout CreatePipelineLayout(vk::DescriptorSetLayout layout);
// 根据布局分配一个描述符集
vk::DescriptorSet AllocateDescriptorSet(vk::DescriptorSetLayout layout);
// 把 Buffer 绑到描述符集的某个 binding
void UpdateUniformBuffer(vk::DescriptorSet set, u32 binding,
vk::Buffer buffer, u64 size);
private:
Device& m_Device;
vk::DescriptorPool m_Pool;
static constexpr u32 kMaxSets = 1000;
};
} // namespace Engine
#endif //HELLOVULKAN_DESCRIPTORMANAGER_H
+83
View File
@@ -0,0 +1,83 @@
//
// Created by atdunbg on 2026/9/20.
//
#ifndef HELLOVULKAN_DEVICE_H
#define HELLOVULKAN_DEVICE_H
#include <vulkan/vulkan.hpp>
#include <vector>
namespace Engine {
class Window;
class Device {
public:
Device(Window& window);
~Device();
Device(const Device&) = delete;
Device& operator=(const Device&) = delete;
vk::Instance GetInstance() const { return m_Instance; }
vk::PhysicalDevice GetPhysicalDevice() const { return m_PhysicalDevice; }
vk::Device GetDevice() const { return m_Device; }
vk::SurfaceKHR GetSurface() const { return m_Surface; }
vk::Queue GetGraphicsQueue() const { return m_GraphicsQueue; }
vk::Queue GetPresentQueue() const { return m_PresentQueue; }
uint32_t GetGraphicsFamily() const { return m_GraphicsFamily; }
uint32_t GetPresentFamily() const { return m_PresentFamily; }
uint32_t FindMemoryType(uint32_t typeFilter, vk::MemoryPropertyFlags props) const;
// 1.4 特性查询
bool IsDynamicRenderingSupported() const { return m_DynamicRenderingSupported; }
bool IsShaderObjectSupported() const { return m_ShaderObjectSupported; }
bool IsSynchronization2Supported() const { return m_Synchronization2Supported; }
private:
void CreateInstance();
void SetupDebugMessenger();
void CreateSurface(Window& window);
void PickPhysicalDevice();
void CreateLogicalDevice();
bool IsDeviceSuitable(vk::PhysicalDevice device);
bool CheckValidationLayerSupport();
vk::Instance m_Instance;
vk::DebugUtilsMessengerEXT m_DebugMessenger;
vk::SurfaceKHR m_Surface;
vk::PhysicalDevice m_PhysicalDevice;
vk::Device m_Device;
vk::Queue m_GraphicsQueue;
vk::Queue m_PresentQueue;
uint32_t m_GraphicsFamily = 0;
uint32_t m_PresentFamily = 0;
vk::PhysicalDeviceMemoryProperties m_MemoryProperties;
// 1.4 特性标志
bool m_DynamicRenderingSupported = false;
bool m_ShaderObjectSupported = false;
bool m_Synchronization2Supported = false;
std::vector<const char*> m_ValidationLayers = { "VK_LAYER_KHRONOS_validation" };
std::vector<const char*> m_DeviceExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };
#ifdef NDEBUG
static constexpr bool kEnableValidation = false;
#else
static constexpr bool kEnableValidation = true;
#endif
};
} // namespace Engine
#endif //HELLOVULKAN_DEVICE_H
@@ -0,0 +1,50 @@
//
// Created by atdunbg on 2026/9/20.
//
#ifndef HELLOVULKAN_MEMORYALLOCATOR_H
#define HELLOVULKAN_MEMORYALLOCATOR_H
#include <vulkan/vulkan.hpp>
#include <array>
#include <vector>
namespace Engine {
class Device;
class MemoryAllocator {
public:
MemoryAllocator(Device& device);
~MemoryAllocator();
MemoryAllocator(const MemoryAllocator&) = delete;
MemoryAllocator& operator=(const MemoryAllocator&) = delete;
struct Allocation {
vk::DeviceMemory Memory;
vk::DeviceSize Offset;
vk::DeviceSize Size;
void* MappedPtr; // HOST_VISIBLE 时才有
};
Allocation Allocate(vk::MemoryRequirements reqs, vk::MemoryPropertyFlags props);
private:
struct Block {
vk::DeviceMemory Memory;
vk::DeviceSize Size;
vk::DeviceSize Used;
void* MappedPtr = nullptr;
bool IsMapped = false;
};
Device& m_Device;
std::array<std::vector<Block>, VK_MAX_MEMORY_TYPES> m_Blocks;
vk::DeviceSize m_DefaultBlockSize = 256ull * 1024 * 1024; // 256 MB
};
} // namespace Engine
#endif //HELLOVULKAN_MEMORYALLOCATOR_H
+8
View File
@@ -0,0 +1,8 @@
//
// Created by atdunbg on 2026/9/20.
//
#ifndef HELLOVULKAN_PIPELINE_H
#define HELLOVULKAN_PIPELINE_H
#endif //HELLOVULKAN_PIPELINE_H
@@ -0,0 +1,52 @@
//
// Created by atdunbg on 2026/9/20.
//
#ifndef HELLOVULKAN_SHADERMANAGER_H
#define HELLOVULKAN_SHADERMANAGER_H
#pragma once
#include <Engine/Core/Types.h>
#include <vulkan/vulkan.hpp>
#include <string>
#include <vector>
namespace Engine {
class Device;
struct ShaderObject {
vk::ShaderEXT Handle;
vk::ShaderStageFlagBits Stage;
};
class ShaderManager {
public:
ShaderManager(Device& device);
~ShaderManager();
ShaderManager(const ShaderManager&) = delete;
ShaderManager& operator=(const ShaderManager&) = delete;
// layout 可选:为 nullptr 时不使用描述符
ShaderObject CreateFromFile(const std::string& spvPath,
vk::ShaderStageFlagBits stage,
vk::DescriptorSetLayout layout = nullptr);
ShaderObject CreateFromSPIRV(const std::vector<u32>& spirv,
vk::ShaderStageFlagBits stage,
vk::DescriptorSetLayout layout = nullptr);
void Destroy(const ShaderObject& shader);
private:
std::vector<u32> ReadSPIRV(const std::string& path);
Device& m_Device;
};
} // namespace Engine
#endif //HELLOVULKAN_SHADERMANAGER_H
+73
View File
@@ -0,0 +1,73 @@
//
// Created by atdunbg on 2026/9/20.
//
#ifndef HELLOVULKAN_SWAPCHAIN_H
#define HELLOVULKAN_SWAPCHAIN_H
#include <vulkan/vulkan.hpp>
#include <vector>
#include "MemoryAllocator.h"
#include "Engine/Core/Types.h"
namespace Engine
{
class Device;
class Window;
class Swapchain
{
public:
Swapchain(Device& device, MemoryAllocator& allocator, Window& window);
~Swapchain();
Swapchain(const Swapchain&) = delete;
Swapchain& operator=(const Swapchain&) = delete;
vk::SwapchainKHR Get() const { return m_Swapchain; }
vk::Format GetFormat() const { return m_Format; }
vk::Extent2D GetExtent() const { return m_Extent; }
uint32_t GetImageCount() const { return static_cast<u32>(m_Images.size()); }
const std::vector<vk::Image>& GetImages() const { return m_Images; }
const std::vector<vk::ImageView>& GetImageViews() const { return m_ImageViews; }
vk::ImageView GetDepthImageView() const { return m_DepthImageView; }
vk::Format GetDepthFormat() const { return m_DepthFormat; }
vk::Image GetDepthImage() const { return m_DepthImage; }
void Recreate(Window& window);
private:
void Create(Window& window);
void Destroy();
vk::SurfaceFormatKHR ChooseSurfaceFormat(const std::vector<vk::SurfaceFormatKHR>& formats);
vk::PresentModeKHR ChoosePresentMode(const std::vector<vk::PresentModeKHR>& modes);
vk::Extent2D ChooseExtent(const vk::SurfaceCapabilitiesKHR& caps, Window& window);
void CreateDepthResources();
void DestroyDepthResources();
vk::Format FindDepthFormat();
Device& m_Device;
vk::SwapchainKHR m_Swapchain;
std::vector<vk::Image> m_Images;
std::vector<vk::ImageView> m_ImageViews;
vk::Image m_DepthImage;
vk::DeviceMemory m_DepthMemory;
vk::ImageView m_DepthImageView;
vk::Format m_DepthFormat;
MemoryAllocator& m_Allocator;
vk::Format m_Format;
vk::Extent2D m_Extent;
};
} // namespace Engine
#endif //HELLOVULKAN_SWAPCHAIN_H
@@ -0,0 +1,50 @@
//
// Created by atdunbg on 2026/9/20.
//
#ifndef HELLOVULKAN_SYNCMANAGER_H
#define HELLOVULKAN_SYNCMANAGER_H
#include <vulkan/vulkan.hpp>
#include <vector>
#include "Engine/Core/Types.h"
namespace Engine {
class Device;
class SyncManager {
public:
SyncManager(Device& device, u32 maxFramesInFlight, u32 imageCount);
~SyncManager();
SyncManager(const SyncManager&) = delete;
SyncManager& operator=(const SyncManager&) = delete;
// 每帧一个:acquire 完成时 signal
vk::Semaphore GetImageAvailable(u32 imageIndex) const { return m_ImageAvailable[imageIndex]; }
// 每张交换链 image 一个:渲染完成时 signal
vk::Semaphore GetRenderFinished(u32 imageIndex) const {
return m_RenderFinished[imageIndex];
}
// 每帧一个:GPU 渲染完成时 signal
vk::Fence GetFrameFence(u32 frameIndex) const { return m_FrameFences[frameIndex]; }
// 等待并重置这一帧的 Fence
void WaitFence(u32 frameIndex); // 只等,不 reset
void ResetFence(u32 frameIndex); // 只 reset
private:
Device& m_Device;
std::vector<vk::Semaphore> m_ImageAvailable;
std::vector<vk::Semaphore> m_RenderFinished;
std::vector<vk::Fence> m_FrameFences;
};
} // namespace Engine
#endif //HELLOVULKAN_SYNCMANAGER_H
@@ -0,0 +1,39 @@
//
// Created by atdunbg on 2026/9/20.
//
#ifndef HELLOVULKAN_UNIFORMBUFFER_H
#define HELLOVULKAN_UNIFORMBUFFER_H
#include <Engine/Core/Types.h>
#include <Engine/Vulkan/Buffer.h>
#include <vulkan/vulkan.hpp>
namespace Engine {
class Device;
class MemoryAllocator;
class UniformBuffer {
public:
UniformBuffer(Device& device, MemoryAllocator& allocator, u64 size);
~UniformBuffer();
UniformBuffer(const UniformBuffer&) = delete;
UniformBuffer& operator=(const UniformBuffer&) = delete;
vk::Buffer Get() const { return m_Buffer->Get(); }
u64 GetSize() const { return m_Size; }
// 每帧更新数据
void Update(const void* data, u64 size);
private:
Scope<Buffer> m_Buffer;
u64 m_Size;
};
} // namespace Engine
#endif //HELLOVULKAN_UNIFORMBUFFER_H