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
+22
View File
@@ -0,0 +1,22 @@
//
// Created by atdunbg on 2026/9/20.
//
#ifndef HELLOVULKAN_LOG_H
#define HELLOVULKAN_LOG_H
#pragma once
#include <spdlog/spdlog.h>
namespace Engine {
#define ENGINE_TRACE(...) ::spdlog::trace(__VA_ARGS__)
#define ENGINE_INFO(...) ::spdlog::info(__VA_ARGS__)
#define ENGINE_WARN(...) ::spdlog::warn(__VA_ARGS__)
#define ENGINE_ERROR(...) ::spdlog::error(__VA_ARGS__)
#define ENGINE_FATAL(...) ::spdlog::critical(__VA_ARGS__)
} // namespace Engine
#endif //HELLOVULKAN_LOG_H
+45
View File
@@ -0,0 +1,45 @@
//
// Created by atdunbg on 2026/9/20.
//
#ifndef HELLOVULKAN_TYPES_H
#define HELLOVULKAN_TYPES_H
#include <cstdint>
#include <memory>
#include <string>
namespace Engine {
using u8 = uint8_t;
using u16 = uint16_t;
using u32 = uint32_t;
using u64 = uint64_t;
using i8 = int8_t;
using i16 = int16_t;
using i32 = int32_t;
using i64 = int64_t;
using f32 = float;
using f64 = double;
template<typename T>
using Scope = std::unique_ptr<T>;
template<typename T>
using Ref = std::shared_ptr<T>;
template<typename T, typename... Args>
Scope<T> CreateScope(Args&&... args) {
return std::make_unique<T>(std::forward<Args>(args)...);
}
template<typename T, typename... Args>
Ref<T> CreateRef(Args&&... args) {
return std::make_shared<T>(std::forward<Args>(args)...);
}
} // namespace Engine
#endif //HELLOVULKAN_TYPES_H
+51
View File
@@ -0,0 +1,51 @@
//
// Created by atdunbg on 2026/9/20.
//
#ifndef HELLOVULKAN_WINDOW_H
#define HELLOVULKAN_WINDOW_H
#include <vulkan/vulkan.hpp>
#include <string>
#include <functional>
#include "Types.h"
struct GLFWwindow;
namespace Engine {
class Window {
public:
Window(const std::string& title, u32 width, u32 height);
~Window();
Window(const Window&) = delete;
Window& operator=(const Window&) = delete;
bool ShouldClose() const;
void PollEvents();
vk::SurfaceKHR CreateSurface(vk::Instance instance);
void DestroySurface(vk::Instance instance, vk::SurfaceKHR surface);
u32 GetWidth() const { return m_Width; }
u32 GetHeight() const { return m_Height; }
GLFWwindow* GetHandle() const { return m_Window; }
// 需要的 Vulkan 扩展(GLFW 提供)
static std::vector<const char*> GetRequiredExtensions();
private:
GLFWwindow* m_Window = nullptr;
u32 m_Width;
u32 m_Height;
std::string m_Title;
};
} // namespace Engine
#endif //HELLOVULKAN_WINDOW_H
+74
View File
@@ -0,0 +1,74 @@
//
// Created by atdunbg on 2026/9/20.
//
#ifndef HELLOVULKAN_RENDERER_H
#define HELLOVULKAN_RENDERER_H
#pragma once
#include <Engine/Core/Types.h>
#include <vulkan/vulkan.hpp>
#include <chrono>
namespace Engine {
class Window;
class Device;
class MemoryAllocator;
class DescriptorManager;
class ShaderManager;
class Swapchain;
class CommandManager;
class SyncManager;
class Buffer;
class UniformBuffer;
class Renderer {
public:
Renderer(Window& window);
~Renderer();
Renderer(const Renderer&) = delete;
Renderer& operator=(const Renderer&) = delete;
void Render();
private:
void RecordCommands(vk::CommandBuffer cmd, u32 imageIndex);
void TransitionImageLayout(vk::CommandBuffer cmd, vk::Image image,
vk::ImageLayout oldLayout, vk::ImageLayout newLayout);
Window& m_Window;
Scope<Device> m_Device;
Scope<MemoryAllocator> m_Allocator;
Scope<DescriptorManager> m_DescriptorManager;
Scope<ShaderManager> m_ShaderManager;
Scope<Swapchain> m_Swapchain;
Scope<SyncManager> m_SyncManager;
Scope<CommandManager> m_CommandManager;
Scope<Buffer> m_VertexBuffer;
Scope<Buffer> m_IndexBuffer;
Scope<UniformBuffer> m_UniformBuffer;
vk::DescriptorSetLayout m_DescriptorSetLayout;
vk::PipelineLayout m_PipelineLayout;
vk::DescriptorSet m_DescriptorSet;
vk::ShaderEXT m_VertShader;
vk::ShaderEXT m_FragShader;
vk::Extent2D m_Extent;
vk::Format m_Format;
static constexpr u32 kMaxFramesInFlight = 2;
u32 m_CurrentFrame = 0;
std::chrono::steady_clock::time_point m_StartTime;
};
} // namespace Engine
#endif //HELLOVULKAN_RENDERER_H
+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