From 381b7c0aa60dcd866083668a87550be9d6f9f647 Mon Sep 17 00:00:00 2001 From: atdunbg Date: Sun, 20 Sep 2026 23:09:22 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=90=AD=E5=BB=BA=20Vulkan=201.4=20?= =?UTF-8?q?=E6=B8=B2=E6=9F=93=E5=99=A8=E9=AA=A8=E6=9E=B6=EF=BC=8C=E7=BB=98?= =?UTF-8?q?=E5=88=B6=E6=97=8B=E8=BD=AC=203D=20=E7=AB=8B=E6=96=B9=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 使用 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 引入 --- .gitmodules | 9 + App/CMakeLists.txt | 27 + App/main.cpp | 26 + CMakeLists.txt | 23 +- Engine/CMakeLists.txt | 20 + Engine/include/Engine/Core/Log.h | 22 + Engine/include/Engine/Core/Types.h | 45 ++ Engine/include/Engine/Core/Window.h | 51 ++ Engine/include/Engine/Renderer/Renderer.h | 74 +++ Engine/include/Engine/Vulkan/Buffer.h | 47 ++ Engine/include/Engine/Vulkan/CommandManager.h | 38 ++ .../include/Engine/Vulkan/DescriptorManager.h | 46 ++ Engine/include/Engine/Vulkan/Device.h | 83 ++++ .../include/Engine/Vulkan/MemoryAllocator.h | 50 ++ Engine/include/Engine/Vulkan/Pipeline.h | 8 + Engine/include/Engine/Vulkan/ShaderManager.h | 52 ++ Engine/include/Engine/Vulkan/Swapchain.h | 73 +++ Engine/include/Engine/Vulkan/SyncManager.h | 50 ++ Engine/include/Engine/Vulkan/UniformBuffer.h | 39 ++ Engine/src/Core/Log.cpp | 3 + Engine/src/Core/Window.cpp | 74 +++ Engine/src/Renderer/Renderer.cpp | 464 ++++++++++++++++++ Engine/src/Vulkan/Buffer.cpp | 58 +++ Engine/src/Vulkan/CommandManager.cpp | 53 ++ Engine/src/Vulkan/DescriptorManager.cpp | 83 ++++ Engine/src/Vulkan/Device.cpp | 331 +++++++++++++ Engine/src/Vulkan/MemoryAllocator.cpp | 99 ++++ Engine/src/Vulkan/Pipeline.cpp | 3 + Engine/src/Vulkan/ShaderManager.cpp | 95 ++++ Engine/src/Vulkan/Swapchain.cpp | 255 ++++++++++ Engine/src/Vulkan/SyncManager.cpp | 64 +++ Engine/src/Vulkan/UniformBuffer.cpp | 36 ++ Lib/glfw | 1 + Lib/glm | 1 + Lib/spdlog | 1 + shaders/simple_shader.frag | 9 + shaders/simple_shader.vert | 15 + src/main.cpp | 7 - 38 files changed, 2423 insertions(+), 12 deletions(-) create mode 100644 .gitmodules create mode 100644 App/CMakeLists.txt create mode 100644 App/main.cpp create mode 100644 Engine/CMakeLists.txt create mode 100644 Engine/include/Engine/Core/Log.h create mode 100644 Engine/include/Engine/Core/Types.h create mode 100644 Engine/include/Engine/Core/Window.h create mode 100644 Engine/include/Engine/Renderer/Renderer.h create mode 100644 Engine/include/Engine/Vulkan/Buffer.h create mode 100644 Engine/include/Engine/Vulkan/CommandManager.h create mode 100644 Engine/include/Engine/Vulkan/DescriptorManager.h create mode 100644 Engine/include/Engine/Vulkan/Device.h create mode 100644 Engine/include/Engine/Vulkan/MemoryAllocator.h create mode 100644 Engine/include/Engine/Vulkan/Pipeline.h create mode 100644 Engine/include/Engine/Vulkan/ShaderManager.h create mode 100644 Engine/include/Engine/Vulkan/Swapchain.h create mode 100644 Engine/include/Engine/Vulkan/SyncManager.h create mode 100644 Engine/include/Engine/Vulkan/UniformBuffer.h create mode 100644 Engine/src/Core/Log.cpp create mode 100644 Engine/src/Core/Window.cpp create mode 100644 Engine/src/Renderer/Renderer.cpp create mode 100644 Engine/src/Vulkan/Buffer.cpp create mode 100644 Engine/src/Vulkan/CommandManager.cpp create mode 100644 Engine/src/Vulkan/DescriptorManager.cpp create mode 100644 Engine/src/Vulkan/Device.cpp create mode 100644 Engine/src/Vulkan/MemoryAllocator.cpp create mode 100644 Engine/src/Vulkan/Pipeline.cpp create mode 100644 Engine/src/Vulkan/ShaderManager.cpp create mode 100644 Engine/src/Vulkan/Swapchain.cpp create mode 100644 Engine/src/Vulkan/SyncManager.cpp create mode 100644 Engine/src/Vulkan/UniformBuffer.cpp create mode 160000 Lib/glfw create mode 160000 Lib/glm create mode 160000 Lib/spdlog create mode 100644 shaders/simple_shader.frag create mode 100644 shaders/simple_shader.vert delete mode 100644 src/main.cpp diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..440c05c --- /dev/null +++ b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "Lib/glfw"] + path = Lib/glfw + url = https://github.com/glfw/glfw.git +[submodule "Lib/glm"] + path = Lib/glm + url = https://github.com/g-truc/glm.git +[submodule "Lib/spdlog"] + path = Lib/spdlog + url = https://github.com/gabime/spdlog.git diff --git a/App/CMakeLists.txt b/App/CMakeLists.txt new file mode 100644 index 0000000..53ec873 --- /dev/null +++ b/App/CMakeLists.txt @@ -0,0 +1,27 @@ +add_executable(HelloVulkan main.cpp) + +target_link_libraries(HelloVulkan PRIVATE Engine) + + +###### 自动构建shader ###### +find_program(GLSLC glslc REQUIRED) + +set(SHADER_SRC_DIR ${CMAKE_SOURCE_DIR}/shaders) +set(SHADER_OUT_DIR ${CMAKE_BINARY_DIR}/bin/shaders) + +# 定义一个 shader 编译目标:(glsl_file stage) +function(auto_compile_shader glsl_file stage) + # string(REGEX REPLACE "\\.glsl$" "" base "${glsl_file}") + set(spv ${SHADER_OUT_DIR}/${glsl_file}.spv) + add_custom_command( + OUTPUT ${spv} + COMMAND ${GLSLC} -fshader-stage=${stage} -o ${spv} ${SHADER_SRC_DIR}/${glsl_file} + DEPENDS ${SHADER_SRC_DIR}/${glsl_file} + VERBATIM) + set_source_files_properties(${spv} PROPERTIES GENERATED TRUE) + target_sources(${PROJECT_NAME} PRIVATE ${spv}) + set_property(DIRECTORY APPEND PROPERTY HZ_SHADER_SPV_FILES ${spv}) +endfunction() + +auto_compile_shader(simple_shader.vert vert) +auto_compile_shader(simple_shader.frag frag) diff --git a/App/main.cpp b/App/main.cpp new file mode 100644 index 0000000..d751331 --- /dev/null +++ b/App/main.cpp @@ -0,0 +1,26 @@ +// +// Created by atdunbg on 2026/9/20. +// + +#include +#include +#include + +int main() +{ + try { + Engine::Window window("HelloVulkan", 1280, 720); + Engine::Renderer renderer(window); + + while (!window.ShouldClose()) { + window.PollEvents(); + renderer.Render(); + } + } + catch (const std::exception& e) { + ENGINE_FATAL("Exception: {}", e.what()); + return -1; + } + + return 0; +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index bc3df16..5dd4bc9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,21 @@ -cmake_minimum_required(VERSION 4.3) -project(HelloVulkan) - -file(GLOB_RECURSE SOURCE_CODE src/**.cpp) +cmake_minimum_required(VERSION 3.20) +project(HelloVulkan CXX) set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + +# 第三方库 +add_subdirectory(Lib/glfw) +add_subdirectory(Lib/glm) +add_subdirectory(Lib/spdlog) + +# Vulkan +find_package(Vulkan REQUIRED) + +# Engine 和 App +add_subdirectory(Engine) +add_subdirectory(App) + -add_executable(${PROJECT_NAME} ${SOURCE_CODE}) diff --git a/Engine/CMakeLists.txt b/Engine/CMakeLists.txt new file mode 100644 index 0000000..5880b1f --- /dev/null +++ b/Engine/CMakeLists.txt @@ -0,0 +1,20 @@ +file(GLOB_RECURSE ENGINE_SOURCES + src/*.cpp +) + +# 编译成静态库 +add_library(Engine STATIC ${ENGINE_SOURCES}) + + +target_include_directories(Engine PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include +) + +# 链接第三方库 +target_link_libraries(Engine PUBLIC + glfw + Vulkan::Vulkan + spdlog::spdlog +) + +target_compile_definitions(Engine PRIVATE VULKAN_HPP_DISPATCH_LOADER_DYNAMIC=1) \ No newline at end of file diff --git a/Engine/include/Engine/Core/Log.h b/Engine/include/Engine/Core/Log.h new file mode 100644 index 0000000..283a418 --- /dev/null +++ b/Engine/include/Engine/Core/Log.h @@ -0,0 +1,22 @@ +// +// Created by atdunbg on 2026/9/20. +// + +#ifndef HELLOVULKAN_LOG_H +#define HELLOVULKAN_LOG_H + +#pragma once + +#include + +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 diff --git a/Engine/include/Engine/Core/Types.h b/Engine/include/Engine/Core/Types.h new file mode 100644 index 0000000..3dc12bf --- /dev/null +++ b/Engine/include/Engine/Core/Types.h @@ -0,0 +1,45 @@ +// +// Created by atdunbg on 2026/9/20. +// + +#ifndef HELLOVULKAN_TYPES_H +#define HELLOVULKAN_TYPES_H + +#include +#include +#include + +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 + using Scope = std::unique_ptr; + + template + using Ref = std::shared_ptr; + + template + Scope CreateScope(Args&&... args) { + return std::make_unique(std::forward(args)...); + } + + template + Ref CreateRef(Args&&... args) { + return std::make_shared(std::forward(args)...); + } + +} // namespace Engine + +#endif //HELLOVULKAN_TYPES_H diff --git a/Engine/include/Engine/Core/Window.h b/Engine/include/Engine/Core/Window.h new file mode 100644 index 0000000..daea20e --- /dev/null +++ b/Engine/include/Engine/Core/Window.h @@ -0,0 +1,51 @@ +// +// Created by atdunbg on 2026/9/20. +// + +#ifndef HELLOVULKAN_WINDOW_H +#define HELLOVULKAN_WINDOW_H + + +#include +#include +#include + +#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 GetRequiredExtensions(); + + private: + GLFWwindow* m_Window = nullptr; + u32 m_Width; + u32 m_Height; + std::string m_Title; + }; + +} // namespace Engine + + +#endif //HELLOVULKAN_WINDOW_H diff --git a/Engine/include/Engine/Renderer/Renderer.h b/Engine/include/Engine/Renderer/Renderer.h new file mode 100644 index 0000000..9406ab4 --- /dev/null +++ b/Engine/include/Engine/Renderer/Renderer.h @@ -0,0 +1,74 @@ +// +// Created by atdunbg on 2026/9/20. +// + +#ifndef HELLOVULKAN_RENDERER_H +#define HELLOVULKAN_RENDERER_H + +#pragma once + +#include +#include +#include + +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 m_Device; + Scope m_Allocator; + Scope m_DescriptorManager; + Scope m_ShaderManager; + Scope m_Swapchain; + Scope m_SyncManager; + Scope m_CommandManager; + + Scope m_VertexBuffer; + Scope m_IndexBuffer; + Scope 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 diff --git a/Engine/include/Engine/Vulkan/Buffer.h b/Engine/include/Engine/Vulkan/Buffer.h new file mode 100644 index 0000000..388c4bb --- /dev/null +++ b/Engine/include/Engine/Vulkan/Buffer.h @@ -0,0 +1,47 @@ +// +// Created by atdunbg on 2026/9/20. +// + +#ifndef HELLOVULKAN_BUFFER_H +#define HELLOVULKAN_BUFFER_H + + +#include + +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 diff --git a/Engine/include/Engine/Vulkan/CommandManager.h b/Engine/include/Engine/Vulkan/CommandManager.h new file mode 100644 index 0000000..794bec1 --- /dev/null +++ b/Engine/include/Engine/Vulkan/CommandManager.h @@ -0,0 +1,38 @@ +// +// Created by atdunbg on 2026/9/20. +// + +#ifndef HELLOVULKAN_COMMANDMANAGER_H +#define HELLOVULKAN_COMMANDMANAGER_H + +#include + +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 diff --git a/Engine/include/Engine/Vulkan/DescriptorManager.h b/Engine/include/Engine/Vulkan/DescriptorManager.h new file mode 100644 index 0000000..9e4d80e --- /dev/null +++ b/Engine/include/Engine/Vulkan/DescriptorManager.h @@ -0,0 +1,46 @@ +// +// Created by atdunbg on 2026/9/20. +// + +#ifndef HELLOVULKAN_DESCRIPTORMANAGER_H +#define HELLOVULKAN_DESCRIPTORMANAGER_H + + +#include +#include + +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 diff --git a/Engine/include/Engine/Vulkan/Device.h b/Engine/include/Engine/Vulkan/Device.h new file mode 100644 index 0000000..cd2c3cc --- /dev/null +++ b/Engine/include/Engine/Vulkan/Device.h @@ -0,0 +1,83 @@ +// +// Created by atdunbg on 2026/9/20. +// + +#ifndef HELLOVULKAN_DEVICE_H +#define HELLOVULKAN_DEVICE_H + + +#include +#include + +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 m_ValidationLayers = { "VK_LAYER_KHRONOS_validation" }; + std::vector 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 diff --git a/Engine/include/Engine/Vulkan/MemoryAllocator.h b/Engine/include/Engine/Vulkan/MemoryAllocator.h new file mode 100644 index 0000000..267aa40 --- /dev/null +++ b/Engine/include/Engine/Vulkan/MemoryAllocator.h @@ -0,0 +1,50 @@ +// +// Created by atdunbg on 2026/9/20. +// + +#ifndef HELLOVULKAN_MEMORYALLOCATOR_H +#define HELLOVULKAN_MEMORYALLOCATOR_H + + +#include +#include +#include + +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, VK_MAX_MEMORY_TYPES> m_Blocks; + vk::DeviceSize m_DefaultBlockSize = 256ull * 1024 * 1024; // 256 MB + }; + +} // namespace Engine + +#endif //HELLOVULKAN_MEMORYALLOCATOR_H diff --git a/Engine/include/Engine/Vulkan/Pipeline.h b/Engine/include/Engine/Vulkan/Pipeline.h new file mode 100644 index 0000000..2f2da02 --- /dev/null +++ b/Engine/include/Engine/Vulkan/Pipeline.h @@ -0,0 +1,8 @@ +// +// Created by atdunbg on 2026/9/20. +// + +#ifndef HELLOVULKAN_PIPELINE_H +#define HELLOVULKAN_PIPELINE_H + +#endif //HELLOVULKAN_PIPELINE_H diff --git a/Engine/include/Engine/Vulkan/ShaderManager.h b/Engine/include/Engine/Vulkan/ShaderManager.h new file mode 100644 index 0000000..e353d9e --- /dev/null +++ b/Engine/include/Engine/Vulkan/ShaderManager.h @@ -0,0 +1,52 @@ +// +// Created by atdunbg on 2026/9/20. +// + +#ifndef HELLOVULKAN_SHADERMANAGER_H +#define HELLOVULKAN_SHADERMANAGER_H + + +#pragma once + +#include +#include +#include +#include + +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& spirv, + vk::ShaderStageFlagBits stage, + vk::DescriptorSetLayout layout = nullptr); + + void Destroy(const ShaderObject& shader); + + private: + std::vector ReadSPIRV(const std::string& path); + + Device& m_Device; + }; + +} // namespace Engine + +#endif //HELLOVULKAN_SHADERMANAGER_H diff --git a/Engine/include/Engine/Vulkan/Swapchain.h b/Engine/include/Engine/Vulkan/Swapchain.h new file mode 100644 index 0000000..87b7461 --- /dev/null +++ b/Engine/include/Engine/Vulkan/Swapchain.h @@ -0,0 +1,73 @@ +// +// Created by atdunbg on 2026/9/20. +// + +#ifndef HELLOVULKAN_SWAPCHAIN_H +#define HELLOVULKAN_SWAPCHAIN_H + +#include +#include + +#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(m_Images.size()); } + + const std::vector& GetImages() const { return m_Images; } + const std::vector& 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& formats); + vk::PresentModeKHR ChoosePresentMode(const std::vector& 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 m_Images; + std::vector 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 diff --git a/Engine/include/Engine/Vulkan/SyncManager.h b/Engine/include/Engine/Vulkan/SyncManager.h new file mode 100644 index 0000000..356502d --- /dev/null +++ b/Engine/include/Engine/Vulkan/SyncManager.h @@ -0,0 +1,50 @@ +// +// Created by atdunbg on 2026/9/20. +// + +#ifndef HELLOVULKAN_SYNCMANAGER_H +#define HELLOVULKAN_SYNCMANAGER_H + + +#include +#include + +#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 m_ImageAvailable; + std::vector m_RenderFinished; + std::vector m_FrameFences; + }; + +} // namespace Engine + +#endif //HELLOVULKAN_SYNCMANAGER_H diff --git a/Engine/include/Engine/Vulkan/UniformBuffer.h b/Engine/include/Engine/Vulkan/UniformBuffer.h new file mode 100644 index 0000000..bccbf8b --- /dev/null +++ b/Engine/include/Engine/Vulkan/UniformBuffer.h @@ -0,0 +1,39 @@ +// +// Created by atdunbg on 2026/9/20. +// + +#ifndef HELLOVULKAN_UNIFORMBUFFER_H +#define HELLOVULKAN_UNIFORMBUFFER_H + + +#include +#include +#include + +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 m_Buffer; + u64 m_Size; + }; + +} // namespace Engine + +#endif //HELLOVULKAN_UNIFORMBUFFER_H diff --git a/Engine/src/Core/Log.cpp b/Engine/src/Core/Log.cpp new file mode 100644 index 0000000..bb80704 --- /dev/null +++ b/Engine/src/Core/Log.cpp @@ -0,0 +1,3 @@ +// +// Created by atdunbg on 2026/9/20. +// diff --git a/Engine/src/Core/Window.cpp b/Engine/src/Core/Window.cpp new file mode 100644 index 0000000..17f95a4 --- /dev/null +++ b/Engine/src/Core/Window.cpp @@ -0,0 +1,74 @@ +// +// Created by atdunbg on 2026/9/20. +// + + +#include +#include + +#include + +namespace Engine { + + Window::Window(const std::string& title, u32 width, u32 height) + : m_Width(width), m_Height(height), m_Title(title) + { + if (!glfwInit()) { + ENGINE_FATAL("glfwInit failed"); + return; + } + + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); + + m_Window = glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr); + if (!m_Window) { + ENGINE_FATAL("glfwCreateWindow failed"); + glfwTerminate(); + return; + } + + ENGINE_INFO("Window created: {} ({}x{})", title, width, height); + } + + Window::~Window() + { + if (m_Window) { + glfwDestroyWindow(m_Window); + } + glfwTerminate(); + } + + bool Window::ShouldClose() const + { + return glfwWindowShouldClose(m_Window); + } + + void Window::PollEvents() + { + glfwPollEvents(); + } + + vk::SurfaceKHR Window::CreateSurface(vk::Instance instance) + { + VkSurfaceKHR surface; + if (glfwCreateWindowSurface(instance, m_Window, nullptr, &surface) != VK_SUCCESS) { + ENGINE_FATAL("glfwCreateWindowSurface failed"); + return nullptr; + } + return surface; + } + + void Window::DestroySurface(vk::Instance instance, vk::SurfaceKHR surface) + { + instance.destroySurfaceKHR(surface); + } + + std::vector Window::GetRequiredExtensions() + { + u32 count = 0; + const char** extensions = glfwGetRequiredInstanceExtensions(&count); + return std::vector(extensions, extensions + count); + } + +} // namespace Engine \ No newline at end of file diff --git a/Engine/src/Renderer/Renderer.cpp b/Engine/src/Renderer/Renderer.cpp new file mode 100644 index 0000000..65c5f33 --- /dev/null +++ b/Engine/src/Renderer/Renderer.cpp @@ -0,0 +1,464 @@ +// +// Created by atdunbg on 2026/9/20. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +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(window); + + // 2. MemoryAllocator + m_Allocator = CreateScope(*m_Device); + + // 3. DescriptorManager + m_DescriptorManager = CreateScope(*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(*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(*m_Device, *m_Allocator, window); + m_Extent = m_Swapchain->GetExtent(); + m_Format = m_Swapchain->GetFormat(); + + // 8. SyncManager + m_SyncManager = CreateScope(*m_Device, kMaxFramesInFlight, m_Swapchain->GetImageCount()); + + // 9. CommandManager + m_CommandManager = CreateScope(*m_Device); + + // 10. 顶点缓冲 + std::vector 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( + *m_Device, *m_Allocator, + bufferSize, + vk::BufferUsageFlagBits::eVertexBuffer, + vk::MemoryPropertyFlagBits::eHostVisible | + vk::MemoryPropertyFlagBits::eHostCoherent); + m_VertexBuffer->Upload(vertices.data(), bufferSize); + + + // 索引缓冲:两个三角形组成一个四边形 + std::vector 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( + *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( + *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( + 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(m_Extent.width) / + static_cast(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::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 waitStages = { + vk::PipelineStageFlagBits::eColorAttachmentOutput + }; + std::array waitSemaphores = { + m_SyncManager->GetImageAvailable(frameIndex) + }; + std::array 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 swapchains = {m_Swapchain->Get()}; + std::array 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{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 stages = { + vk::ShaderStageFlagBits::eVertex, + vk::ShaderStageFlagBits::eFragment + }; + std::array 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 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(m_Extent.width), + static_cast(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 diff --git a/Engine/src/Vulkan/Buffer.cpp b/Engine/src/Vulkan/Buffer.cpp new file mode 100644 index 0000000..ac248e4 --- /dev/null +++ b/Engine/src/Vulkan/Buffer.cpp @@ -0,0 +1,58 @@ +// +// Created by atdunbg on 2026/9/20. +// + +#include +#include +#include +#include + +#include + +namespace Engine { + + Buffer::Buffer(Device& device, MemoryAllocator& allocator, + vk::DeviceSize size, vk::BufferUsageFlags usage, + vk::MemoryPropertyFlags memProps) + : m_Device(device), m_Size(size) + { + vk::BufferCreateInfo bufferInfo; + bufferInfo.setSize(size) + .setUsage(usage) + .setSharingMode(vk::SharingMode::eExclusive); + + m_Buffer = m_Device.GetDevice().createBuffer(bufferInfo); + + auto reqs = m_Device.GetDevice().getBufferMemoryRequirements(m_Buffer); + auto alloc = allocator.Allocate(reqs, memProps); + + m_Memory = alloc.Memory; + m_Offset = alloc.Offset; + m_MappedPtr = alloc.MappedPtr; + + m_Device.GetDevice().bindBufferMemory(m_Buffer, m_Memory, m_Offset); + } + + + Buffer::~Buffer() + { + m_Device.GetDevice().destroyBuffer(m_Buffer); + } + + void Buffer::Upload(const void* data, vk::DeviceSize size, vk::DeviceSize offset) + { + if (!m_MappedPtr) { + ENGINE_ERROR("Buffer::Upload: buffer is not host visible"); + return; + } + memcpy(static_cast(m_MappedPtr) + offset, data, size); + + // flush 整个内存块,避免对齐问题 + vk::MappedMemoryRange range; + range.setMemory(m_Memory) + .setOffset(0) + .setSize(VK_WHOLE_SIZE); + + m_Device.GetDevice().flushMappedMemoryRanges(range); + } +} // namespace Engine \ No newline at end of file diff --git a/Engine/src/Vulkan/CommandManager.cpp b/Engine/src/Vulkan/CommandManager.cpp new file mode 100644 index 0000000..259f603 --- /dev/null +++ b/Engine/src/Vulkan/CommandManager.cpp @@ -0,0 +1,53 @@ +// +// Created by atdunbg on 2026/9/20. +// + +#include +#include +#include + +namespace Engine { + + CommandManager::CommandManager(Device& device) + : m_Device(device) + { + // 创建命令池 + 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); + + m_Buffer = m_Device.GetDevice().allocateCommandBuffers(allocInfo)[0]; + + ENGINE_INFO("CommandManager created"); + } + + CommandManager::~CommandManager() + { + m_Device.GetDevice().destroyCommandPool(m_Pool); + } + + vk::CommandBuffer CommandManager::BeginFrame() + { + m_Buffer.reset(); + + vk::CommandBufferBeginInfo beginInfo; + beginInfo.setFlags(vk::CommandBufferUsageFlagBits::eOneTimeSubmit); + m_Buffer.begin(beginInfo); + + return m_Buffer; + } + + void CommandManager::EndFrame() + { + m_Buffer.end(); + } + +} // namespace Engine diff --git a/Engine/src/Vulkan/DescriptorManager.cpp b/Engine/src/Vulkan/DescriptorManager.cpp new file mode 100644 index 0000000..a853005 --- /dev/null +++ b/Engine/src/Vulkan/DescriptorManager.cpp @@ -0,0 +1,83 @@ +// +// Created by atdunbg on 2026/9/20. +// + +#include +#include +#include + +namespace Engine +{ + DescriptorManager::DescriptorManager(Device& device) + : m_Device(device) + { + vk::DescriptorPoolSize poolSize; + poolSize.setType(vk::DescriptorType::eUniformBuffer) + .setDescriptorCount(kMaxSets); + + vk::DescriptorPoolCreateInfo poolInfo; + poolInfo.setMaxSets(kMaxSets) + .setPoolSizes(poolSize) + .setFlags(vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet); + + m_Pool = m_Device.GetDevice().createDescriptorPool(poolInfo); + + ENGINE_INFO("DescriptorManager created"); + } + + DescriptorManager::~DescriptorManager() + { + m_Device.GetDevice().destroyDescriptorPool(m_Pool); + } + + vk::DescriptorSetLayout DescriptorManager::CreateUniformBufferLayout(u32 binding) + { + vk::DescriptorSetLayoutBinding layoutBinding; + layoutBinding.setBinding(binding) + .setDescriptorType(vk::DescriptorType::eUniformBuffer) + .setDescriptorCount(1) + .setStageFlags(vk::ShaderStageFlagBits::eVertex | + vk::ShaderStageFlagBits::eFragment); + + vk::DescriptorSetLayoutCreateInfo layoutInfo; + layoutInfo.setBindings(layoutBinding); + + return m_Device.GetDevice().createDescriptorSetLayout(layoutInfo); + } + + vk::PipelineLayout DescriptorManager::CreatePipelineLayout(vk::DescriptorSetLayout layout) + { + vk::PipelineLayoutCreateInfo info; + info.setSetLayouts(layout); + return m_Device.GetDevice().createPipelineLayout(info); + } + + vk::DescriptorSet DescriptorManager::AllocateDescriptorSet(vk::DescriptorSetLayout layout) + { + vk::DescriptorSetAllocateInfo allocInfo; + allocInfo.setDescriptorPool(m_Pool) + .setSetLayouts(layout); + + auto sets = m_Device.GetDevice().allocateDescriptorSets(allocInfo); + return sets[0]; + } + + void DescriptorManager::UpdateUniformBuffer(vk::DescriptorSet set, u32 binding, + vk::Buffer buffer, u64 size) + { + vk::DescriptorBufferInfo bufferInfo; + bufferInfo.setBuffer(buffer) + .setOffset(0) + .setRange(size); + + vk::WriteDescriptorSet write; + write.setDstSet(set) + .setDstBinding(binding) + .setDstArrayElement(0) + .setDescriptorType(vk::DescriptorType::eUniformBuffer) + .setDescriptorCount(1) + .setBufferInfo(bufferInfo); + + m_Device.GetDevice().updateDescriptorSets(write, {}); + } +} // namespace Engine diff --git a/Engine/src/Vulkan/Device.cpp b/Engine/src/Vulkan/Device.cpp new file mode 100644 index 0000000..66dc51d --- /dev/null +++ b/Engine/src/Vulkan/Device.cpp @@ -0,0 +1,331 @@ +// +// Created by atdunbg on 2026/9/20. +// + + +#include +#include +#include + +#include +#include + +VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE + +namespace Engine { + +// ---------- Debug Callback ---------- +static VKAPI_ATTR vk::Bool32 VKAPI_CALL DebugCallback( + vk::DebugUtilsMessageSeverityFlagBitsEXT severity, + vk::DebugUtilsMessageTypeFlagsEXT type, + const vk::DebugUtilsMessengerCallbackDataEXT* data, + void* userData) +{ + if (severity >= vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning) { + ENGINE_WARN("[Vulkan] {}", data->pMessage); + } else { + ENGINE_TRACE("[Vulkan] {}", data->pMessage); + } + return vk::False; +} + +// ---------- 构造 / 析构 ---------- + +Device::Device(Window& window) +{ + CreateInstance(); + SetupDebugMessenger(); + CreateSurface(window); + PickPhysicalDevice(); + CreateLogicalDevice(); +} + +Device::~Device() +{ + m_Device.destroy(); + + if (m_DebugMessenger) { + m_Instance.destroyDebugUtilsMessengerEXT(m_DebugMessenger); + } + + m_Instance.destroySurfaceKHR(m_Surface); + m_Instance.destroy(); +} + +// ---------- Instance ---------- + +void Device::CreateInstance() +{ + VULKAN_HPP_DEFAULT_DISPATCHER.init(); + + if (kEnableValidation && !CheckValidationLayerSupport()) { + ENGINE_FATAL("Validation layers requested, but not available"); + } + + vk::ApplicationInfo appInfo; + appInfo.setPApplicationName("HelloVulkan") + .setApplicationVersion(VK_MAKE_VERSION(1, 0, 0)) + .setPEngineName("Engine") + .setEngineVersion(VK_MAKE_VERSION(1, 0, 0)) + .setApiVersion(VK_API_VERSION_1_4); // ← 1.4 + + auto extensions = Window::GetRequiredExtensions(); + if (kEnableValidation) { + extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + } + + vk::InstanceCreateInfo createInfo; + createInfo.setPApplicationInfo(&appInfo) + .setPEnabledExtensionNames(extensions); + + if (kEnableValidation) { + createInfo.setPEnabledLayerNames(m_ValidationLayers); + } + + m_Instance = vk::createInstance(createInfo); + + VULKAN_HPP_DEFAULT_DISPATCHER.init(m_Instance); + + ENGINE_INFO("Vulkan Instance created (API 1.4)"); + +} + +void Device::SetupDebugMessenger() +{ + if (!kEnableValidation) return; + + vk::DebugUtilsMessengerCreateInfoEXT createInfo; + createInfo.setMessageSeverity( + vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning | + vk::DebugUtilsMessageSeverityFlagBitsEXT::eError) + .setMessageType( + vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral | + vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation | + vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance) + .setPfnUserCallback(&DebugCallback); + + m_DebugMessenger = m_Instance.createDebugUtilsMessengerEXT(createInfo); +} + +// ---------- Surface ---------- + +void Device::CreateSurface(Window& window) +{ + m_Surface = window.CreateSurface(m_Instance); + ENGINE_INFO("Vulkan Surface created"); +} + +// ---------- PhysicalDevice ---------- + +void Device::PickPhysicalDevice() +{ + auto devices = m_Instance.enumeratePhysicalDevices(); + if (devices.empty()) { + ENGINE_FATAL("No Vulkan-capable GPU found"); + } + + for (auto& device : devices) { + if (IsDeviceSuitable(device)) { + m_PhysicalDevice = device; + break; + } + } + + if (!m_PhysicalDevice) { + ENGINE_FATAL("No suitable GPU found"); + } + + m_MemoryProperties = m_PhysicalDevice.getMemoryProperties(); + auto props = m_PhysicalDevice.getProperties(); + ENGINE_INFO("Selected GPU: {}", props.deviceName.data()); + + // ======================================================== + // 查询 1.4 特性 + // + // 关键点:dynamicRendering 和 synchronization2 是 1.3 的特性, + // 它们就定义在 VkPhysicalDeviceVulkan13Features 里[reference:2]。 + // Vulkan 1.4 没有把它们“搬走”,而是在 1.4 的结构体里新增了 + // dynamicRenderingLocalRead、pushDescriptor 等 1.4 独有的特性[reference:3]。 + // + // 所以查询时,两个结构体都要有,用 pNext 串起来。 + // ======================================================== + + vk::PhysicalDeviceVulkan14Features features14{}; + vk::PhysicalDeviceVulkan13Features features13{}; + vk::PhysicalDeviceVulkan12Features features12{}; + + // 链接:1.4 → 1.3 → 1.2 + features14.pNext = &features13; + features13.pNext = &features12; + + vk::PhysicalDeviceFeatures2 features2{}; + features2.pNext = &features14; + m_PhysicalDevice.getFeatures2(&features2); + + // 1.3 的特性 + m_DynamicRenderingSupported = features13.dynamicRendering; + m_Synchronization2Supported = features13.synchronization2; + + // 1.4 的新特性 + bool localReadSupported = features14.dynamicRenderingLocalRead; + bool pushDescriptorSupported = features14.pushDescriptor; + + // 查询 Shader Object 扩展 + auto exts = m_PhysicalDevice.enumerateDeviceExtensionProperties(); + for (auto& ext : exts) { + if (strcmp(ext.extensionName, "VK_EXT_shader_object") == 0) { + m_ShaderObjectSupported = true; + } + } + + ENGINE_INFO(" Dynamic Rendering (1.3): {}", m_DynamicRenderingSupported ? "Yes" : "No"); + ENGINE_INFO(" Synchronization2 (1.3): {}", m_Synchronization2Supported ? "Yes" : "No"); + ENGINE_INFO(" Dynamic Rendering Local Read (1.4): {}", localReadSupported ? "Yes" : "No"); + ENGINE_INFO(" Push Descriptor (1.4): {}", pushDescriptorSupported ? "Yes" : "No"); + ENGINE_INFO(" Shader Object (EXT): {}", m_ShaderObjectSupported ? "Yes" : "No"); +} + +bool Device::IsDeviceSuitable(vk::PhysicalDevice device) +{ + auto families = device.getQueueFamilyProperties(); + bool hasGraphics = false; + bool hasPresent = false; + + for (uint32_t i = 0; i < families.size(); ++i) { + if (families[i].queueFlags & vk::QueueFlagBits::eGraphics) { + hasGraphics = true; + } + if (device.getSurfaceSupportKHR(i, m_Surface)) { + hasPresent = true; + } + } + + if (!hasGraphics || !hasPresent) return false; + + auto exts = device.enumerateDeviceExtensionProperties(); + std::set required(m_DeviceExtensions.begin(), m_DeviceExtensions.end()); + for (auto& ext : exts) { + required.erase(ext.extensionName); + } + if (!required.empty()) return false; + + auto formats = device.getSurfaceFormatsKHR(m_Surface); + auto modes = device.getSurfacePresentModesKHR(m_Surface); + if (formats.empty() || modes.empty()) return false; + + return true; +} + +// ---------- LogicalDevice ---------- + +void Device::CreateLogicalDevice() +{ + auto families = m_PhysicalDevice.getQueueFamilyProperties(); + + for (uint32_t i = 0; i < families.size(); ++i) { + if ((families[i].queueFlags & vk::QueueFlagBits::eGraphics) && m_GraphicsFamily == 0) { + m_GraphicsFamily = i; + } + if (m_PhysicalDevice.getSurfaceSupportKHR(i, m_Surface) && m_PresentFamily == 0) { + m_PresentFamily = i; + } + } + + std::vector queueInfos; + std::set uniqueFamilies = { m_GraphicsFamily, m_PresentFamily }; + float priority = 1.0f; + + for (uint32_t family : uniqueFamilies) { + vk::DeviceQueueCreateInfo info; + info.setQueueFamilyIndex(family) + .setQueueCount(1) + .setPQueuePriorities(&priority); + queueInfos.push_back(info); + } + + vk::PhysicalDeviceFeatures features; + + // ======================================================== + // 启用 1.4 特性链 + // + // 同样的:1.3 的特性放 features13,1.4 的新特性放 features14。 + // 用 pNext 串联,最后挂到 DeviceCreateInfo 的 pNext 上。 + // ======================================================== + + vk::PhysicalDeviceVulkan14Features features14{}; + vk::PhysicalDeviceVulkan13Features features13{}; + + // 启用 1.3 的特性 + features13.dynamicRendering = VK_TRUE; + features13.synchronization2 = VK_TRUE; + + // 启用 1.4 的新特性 + features14.dynamicRenderingLocalRead = VK_TRUE; + features14.pushDescriptor = VK_TRUE; + + // 链接:1.4 → 1.3 → (shader object) + features14.pNext = &features13; + + // Shader Object 特性(如果支持) + vk::PhysicalDeviceShaderObjectFeaturesEXT shaderObjFeatures{}; + if (m_ShaderObjectSupported) { + shaderObjFeatures.shaderObject = VK_TRUE; + features13.pNext = &shaderObjFeatures; + } + + // 扩展列表 + std::vector deviceExtensions = m_DeviceExtensions; + if (m_ShaderObjectSupported) { + deviceExtensions.push_back(VK_EXT_SHADER_OBJECT_EXTENSION_NAME); + } + + vk::DeviceCreateInfo createInfo; + createInfo.setQueueCreateInfos(queueInfos) + .setPEnabledExtensionNames(deviceExtensions) + .setPEnabledFeatures(&features) + .setPNext(&features14); // ← 特性链从这里开始 + + m_Device = m_PhysicalDevice.createDevice(createInfo); + + m_GraphicsQueue = m_Device.getQueue(m_GraphicsFamily, 0); + m_PresentQueue = m_Device.getQueue(m_PresentFamily, 0); + + VULKAN_HPP_DEFAULT_DISPATCHER.init(m_Device); + + ENGINE_INFO("Logical Device created (graphics={}, present={})", + m_GraphicsFamily, m_PresentFamily); +} + +// ---------- 内存类型 ---------- + +uint32_t Device::FindMemoryType(uint32_t typeFilter, vk::MemoryPropertyFlags props) const +{ + for (uint32_t i = 0; i < m_MemoryProperties.memoryTypeCount; ++i) { + if ((typeFilter & (1 << i)) && + (m_MemoryProperties.memoryTypes[i].propertyFlags & props) == props) { + return i; + } + } + ENGINE_FATAL("No suitable memory type found"); + return 0; +} + +// ---------- 验证层检查 ---------- + +bool Device::CheckValidationLayerSupport() +{ + auto layers = vk::enumerateInstanceLayerProperties(); + for (const char* layerName : m_ValidationLayers) { + bool found = false; + for (auto& layer : layers) { + if (std::strcmp(layer.layerName, layerName) == 0) { + found = true; + break; + } + } + if (!found) return false; + } + return true; +} + +} // namespace Engine \ No newline at end of file diff --git a/Engine/src/Vulkan/MemoryAllocator.cpp b/Engine/src/Vulkan/MemoryAllocator.cpp new file mode 100644 index 0000000..a896270 --- /dev/null +++ b/Engine/src/Vulkan/MemoryAllocator.cpp @@ -0,0 +1,99 @@ +// +// Created by atdunbg on 2026/9/20. +// + +#include +#include +#include + +#include + +namespace Engine { + +MemoryAllocator::MemoryAllocator(Device& device) + : m_Device(device) +{ + ENGINE_INFO("MemoryAllocator created"); +} + +MemoryAllocator::~MemoryAllocator() +{ + const auto& vkDevice = m_Device.GetDevice(); + + for (auto& blocks : m_Blocks) { + for (auto& block : blocks) { + if (block.IsMapped) { + vkDevice.unmapMemory(block.Memory); + } + vkDevice.freeMemory(block.Memory); + } + blocks.clear(); + } +} + +MemoryAllocator::Allocation MemoryAllocator::Allocate( + vk::MemoryRequirements reqs, vk::MemoryPropertyFlags props) +{ + uint32_t typeIndex = m_Device.FindMemoryType(reqs.memoryTypeBits, props); + auto& blocks = m_Blocks[typeIndex]; + + // 先从已有块里找能放下的 + for (auto& block : blocks) { + vk::DeviceSize alignedOffset = + (block.Used + reqs.alignment - 1) & ~(reqs.alignment - 1); + + if (alignedOffset + reqs.size <= block.Size) { + Allocation alloc; + alloc.Memory = block.Memory; + alloc.Offset = alignedOffset; + alloc.Size = reqs.size; + alloc.MappedPtr = block.IsMapped + ? static_cast(block.MappedPtr) + alignedOffset + : nullptr; + + block.Used = alignedOffset + reqs.size; + return alloc; + } + } + + // 没有合适的块,创建新块 + vk::DeviceSize blockSize = std::max(m_DefaultBlockSize, reqs.size); + + vk::MemoryAllocateInfo allocInfo; + allocInfo.setAllocationSize(blockSize) + .setMemoryTypeIndex(typeIndex); + + Block block; + block.Memory = m_Device.GetDevice().allocateMemory(allocInfo); + block.Size = blockSize; + block.Used = 0; + + // HOST_VISIBLE 的块持久映射 + if (props & vk::MemoryPropertyFlagBits::eHostVisible) { + block.MappedPtr = m_Device.GetDevice().mapMemory( + block.Memory, 0, VK_WHOLE_SIZE); + block.IsMapped = true; + } + + blocks.push_back(block); + + // 从新块分配 + vk::DeviceSize alignedOffset = (reqs.alignment - 1) & ~(reqs.alignment - 1); + + Allocation alloc; + alloc.Memory = block.Memory; + alloc.Offset = alignedOffset; + alloc.Size = reqs.size; + alloc.MappedPtr = block.IsMapped + ? static_cast(block.MappedPtr) + alignedOffset + : nullptr; + + blocks.back().Used = alignedOffset + reqs.size; + + ENGINE_INFO("MemoryAllocator: new block of {} bytes (type {})", + blockSize, typeIndex); + + return alloc; +} + +} // namespace Engine diff --git a/Engine/src/Vulkan/Pipeline.cpp b/Engine/src/Vulkan/Pipeline.cpp new file mode 100644 index 0000000..bb80704 --- /dev/null +++ b/Engine/src/Vulkan/Pipeline.cpp @@ -0,0 +1,3 @@ +// +// Created by atdunbg on 2026/9/20. +// diff --git a/Engine/src/Vulkan/ShaderManager.cpp b/Engine/src/Vulkan/ShaderManager.cpp new file mode 100644 index 0000000..c804433 --- /dev/null +++ b/Engine/src/Vulkan/ShaderManager.cpp @@ -0,0 +1,95 @@ +// +// Created by atdunbg on 2026/9/20. +// + + +#include +#include +#include + +#include + +namespace Engine { + +ShaderManager::ShaderManager(Device& device) + : m_Device(device) +{ +} + +ShaderManager::~ShaderManager() +{ +} + +ShaderObject ShaderManager::CreateFromFile(const std::string& spvPath, + vk::ShaderStageFlagBits stage, + vk::DescriptorSetLayout layout) +{ + auto spirv = ReadSPIRV(spvPath); + if (spirv.empty()) { + ENGINE_ERROR("Failed to read SPIR-V: {}", spvPath); + return { nullptr, stage }; + } + return CreateFromSPIRV(spirv, stage, layout); +} + +ShaderObject ShaderManager::CreateFromSPIRV(const std::vector& spirv, + vk::ShaderStageFlagBits stage, + vk::DescriptorSetLayout layout) +{ + // 下一个阶段:顶点 → 片段 + vk::ShaderStageFlags nextStage = {}; + if (stage == vk::ShaderStageFlagBits::eVertex) { + nextStage = vk::ShaderStageFlagBits::eFragment; + } + + vk::ShaderCreateInfoEXT createInfo; + createInfo.setStage(stage) + .setNextStage(nextStage) + .setCodeType(vk::ShaderCodeTypeEXT::eSpirv) + .setCodeSize(spirv.size() * sizeof(u32)) + .setPCode(spirv.data()) + .setPName("main"); + + // 有布局就传进去 + if (layout) { + createInfo.setSetLayouts(layout); + } + + vk::ShaderEXT shader; + try { + auto result = m_Device.GetDevice().createShadersEXT(createInfo); + shader = result.value[0]; + } + catch (const vk::SystemError& e) { + ENGINE_ERROR("Failed to create shader object: {}", e.what()); + return { nullptr, stage }; + } + + return { shader, stage }; +} + +void ShaderManager::Destroy(const ShaderObject& shader) +{ + if (shader.Handle) { + m_Device.GetDevice().destroyShaderEXT(shader.Handle); + } +} + +std::vector ShaderManager::ReadSPIRV(const std::string& path) +{ + std::ifstream file(path, std::ios::binary | std::ios::ate); + if (!file) { + ENGINE_ERROR("Failed to open: {}", path); + return {}; + } + + size_t size = file.tellg(); + std::vector buffer(size / sizeof(u32)); + file.seekg(0); + file.read(reinterpret_cast(buffer.data()), size); + file.close(); + + return buffer; +} + +} // namespace Engine \ No newline at end of file diff --git a/Engine/src/Vulkan/Swapchain.cpp b/Engine/src/Vulkan/Swapchain.cpp new file mode 100644 index 0000000..8ccd7c5 --- /dev/null +++ b/Engine/src/Vulkan/Swapchain.cpp @@ -0,0 +1,255 @@ +// +// Created by atdunbg on 2026/9/20. +// + +#include +#include +#include +#include + +#include +#include + +#include "Engine/Vulkan/MemoryAllocator.h" + +namespace Engine +{ + Swapchain::Swapchain(Device& device, MemoryAllocator& allocator, Window& window) + : m_Device(device), m_Allocator(allocator) + { + Create(window); + } + + Swapchain::~Swapchain() + { + Destroy(); + } + + void Swapchain::Create(Window& window) + { + const auto& vkDevice = m_Device.GetDevice(); + auto surface = m_Device.GetSurface(); + auto physicalDevice = m_Device.GetPhysicalDevice(); + + // 查询支持 + auto caps = physicalDevice.getSurfaceCapabilitiesKHR(surface); + auto formats = physicalDevice.getSurfaceFormatsKHR(surface); + auto modes = physicalDevice.getSurfacePresentModesKHR(surface); + + auto surfaceFormat = ChooseSurfaceFormat(formats); + auto presentMode = ChoosePresentMode(modes); + auto extent = ChooseExtent(caps, window); + + m_Format = surfaceFormat.format; + m_Extent = extent; + + // 交换链 image 数量 + uint32_t imageCount = caps.minImageCount + 1; + if (caps.maxImageCount > 0 && imageCount > caps.maxImageCount) + { + imageCount = caps.maxImageCount; + } + + vk::SwapchainCreateInfoKHR createInfo; + createInfo.setSurface(surface) + .setMinImageCount(imageCount) + .setImageFormat(surfaceFormat.format) + .setImageColorSpace(surfaceFormat.colorSpace) + .setImageExtent(extent) + .setImageArrayLayers(1) + .setImageUsage(vk::ImageUsageFlagBits::eColorAttachment) + .setImageSharingMode(vk::SharingMode::eExclusive) + .setPreTransform(caps.currentTransform) + .setCompositeAlpha(vk::CompositeAlphaFlagBitsKHR::eOpaque) + .setPresentMode(presentMode) + .setClipped(VK_TRUE) + .setOldSwapchain(nullptr); + + // graphics 和 present 队列族不同时,用 concurrent + uint32_t indices[] = {m_Device.GetGraphicsFamily(), m_Device.GetPresentFamily()}; + if (indices[0] != indices[1]) + { + createInfo.setImageSharingMode(vk::SharingMode::eConcurrent) + .setQueueFamilyIndices(indices); + } + + m_Swapchain = vkDevice.createSwapchainKHR(createInfo); + + // 拿 image + m_Images = vkDevice.getSwapchainImagesKHR(m_Swapchain); + + // 创建 image view + m_ImageViews.resize(m_Images.size()); + for (size_t i = 0; i < m_Images.size(); ++i) + { + vk::ImageViewCreateInfo viewInfo; + viewInfo.setImage(m_Images[i]) + .setViewType(vk::ImageViewType::e2D) + .setFormat(m_Format) + .setSubresourceRange({ + vk::ImageAspectFlagBits::eColor, + 0, 1, + 0, 1 + }); + + m_ImageViews[i] = vkDevice.createImageView(viewInfo); + } + + ENGINE_INFO("Swapchain created: {}x{}, {} images, format={}", + 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) + { + vkDevice.destroyImageView(view); + } + m_ImageViews.clear(); + m_Images.clear(); + + if (m_Swapchain) + { + vkDevice.destroySwapchainKHR(m_Swapchain); + m_Swapchain = nullptr; + } + } + + void Swapchain::Recreate(Window& window) + { + const auto& vkDevice = m_Device.GetDevice(); + vkDevice.waitIdle(); + + Destroy(); + Create(window); + + ENGINE_INFO("Swapchain recreated: {}x{}", m_Extent.width, m_Extent.height); + } + + // ---------- 选择函数 ---------- + + vk::SurfaceFormatKHR Swapchain::ChooseSurfaceFormat(const std::vector& formats) + { + for (const auto& f : formats) + { + if (f.format == vk::Format::eB8G8R8A8Srgb && + f.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear) + { + return f; + } + } + return formats[0]; + } + + vk::PresentModeKHR Swapchain::ChoosePresentMode(const std::vector& modes) + { + for (const auto& m : modes) + { + if (m == vk::PresentModeKHR::eMailbox) + { + return m; + } + } + return vk::PresentModeKHR::eFifo; + } + + vk::Extent2D Swapchain::ChooseExtent(const vk::SurfaceCapabilitiesKHR& caps, Window& window) + { + if (caps.currentExtent.width != std::numeric_limits::max()) + { + return caps.currentExtent; + } + + vk::Extent2D extent = { + static_cast(window.GetWidth()), + static_cast(window.GetHeight()) + }; + + extent.width = std::clamp(extent.width, caps.minImageExtent.width, caps.maxImageExtent.width); + extent.height = std::clamp(extent.height, caps.minImageExtent.height, caps.maxImageExtent.height); + + return extent; + } + + vk::Format Swapchain::FindDepthFormat() + { + // 优先 D32_SFLOAT,退回 D32_SFLOAT_S8_UINT,再退回 D24_UNORM_S8_UINT + std::array 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 diff --git a/Engine/src/Vulkan/SyncManager.cpp b/Engine/src/Vulkan/SyncManager.cpp new file mode 100644 index 0000000..a0c22af --- /dev/null +++ b/Engine/src/Vulkan/SyncManager.cpp @@ -0,0 +1,64 @@ +// +// Created by atdunbg on 2026/9/20. +// + + +#include +#include +#include + +#include + +namespace Engine { + + SyncManager::SyncManager(Device& device, u32 maxFramesInFlight, u32 imageCount) + : m_Device(device) + { + vk::SemaphoreCreateInfo semInfo; + + // 每帧一个 ImageAvailable + m_ImageAvailable.resize(maxFramesInFlight); + for (auto& sem : m_ImageAvailable) { + sem = m_Device.GetDevice().createSemaphore(semInfo); + } + + // 每张交换链 image 一个 RenderFinished + m_RenderFinished.resize(imageCount); + for (auto& sem : m_RenderFinished) { + sem = m_Device.GetDevice().createSemaphore(semInfo); + } + + // 每帧一个 Fence,初始 signaled + vk::FenceCreateInfo fenceInfo; + fenceInfo.setFlags(vk::FenceCreateFlagBits::eSignaled); + m_FrameFences.resize(maxFramesInFlight); + for (auto& fence : m_FrameFences) { + fence = m_Device.GetDevice().createFence(fenceInfo); + } + + ENGINE_INFO("SyncManager created ({} frames, {} images)", + maxFramesInFlight, imageCount); + } + + SyncManager::~SyncManager() + { + const auto& vkDevice = m_Device.GetDevice(); + for (auto f : m_FrameFences) vkDevice.destroyFence(f); + for (auto s : m_ImageAvailable) vkDevice.destroySemaphore(s); + for (auto s : m_RenderFinished) vkDevice.destroySemaphore(s); + } + + void SyncManager::WaitFence(u32 frameIndex) + { + auto result = m_Device.GetDevice().waitForFences( + m_FrameFences[frameIndex], VK_TRUE, std::numeric_limits::max()); + if (result != vk::Result::eSuccess) { + ENGINE_ERROR("waitForFences failed"); + } + } + + void SyncManager::ResetFence(u32 frameIndex) + { + m_Device.GetDevice().resetFences(m_FrameFences[frameIndex]); + } +} // namespace Engine \ No newline at end of file diff --git a/Engine/src/Vulkan/UniformBuffer.cpp b/Engine/src/Vulkan/UniformBuffer.cpp new file mode 100644 index 0000000..5324a96 --- /dev/null +++ b/Engine/src/Vulkan/UniformBuffer.cpp @@ -0,0 +1,36 @@ +// +// Created by atdunbg on 2026/9/20. +// + +#include +#include +#include +#include + +namespace Engine { + + UniformBuffer::UniformBuffer(Device& device, MemoryAllocator& allocator, u64 size) + : m_Size(size) + { + // Uniform Buffer 需要 CPU 每帧更新,所以用 HOST_VISIBLE | HOST_COHERENT + m_Buffer = CreateScope( + device, allocator, + size, + vk::BufferUsageFlagBits::eUniformBuffer, + vk::MemoryPropertyFlagBits::eHostVisible | + vk::MemoryPropertyFlagBits::eHostCoherent); + + ENGINE_INFO("UniformBuffer created: {} bytes", size); + } + + UniformBuffer::~UniformBuffer() + { + // Buffer 析构时自动销毁 vk::Buffer,内存由 MemoryAllocator 统一释放 + } + + void UniformBuffer::Update(const void* data, u64 size) + { + m_Buffer->Upload(data, size); + } + +} // namespace Engine \ No newline at end of file diff --git a/Lib/glfw b/Lib/glfw new file mode 160000 index 0000000..92dcf4c --- /dev/null +++ b/Lib/glfw @@ -0,0 +1 @@ +Subproject commit 92dcf4ce74f2e2554a98fea09be7c705c17daa5a diff --git a/Lib/glm b/Lib/glm new file mode 160000 index 0000000..6f14f47 --- /dev/null +++ b/Lib/glm @@ -0,0 +1 @@ +Subproject commit 6f14f4792a0cde5d0cf2c910506724d61cb95834 diff --git a/Lib/spdlog b/Lib/spdlog new file mode 160000 index 0000000..57cb5fb --- /dev/null +++ b/Lib/spdlog @@ -0,0 +1 @@ +Subproject commit 57cb5fb7a8ff30079751728234623230535a5c92 diff --git a/shaders/simple_shader.frag b/shaders/simple_shader.frag new file mode 100644 index 0000000..13009da --- /dev/null +++ b/shaders/simple_shader.frag @@ -0,0 +1,9 @@ +#version 450 + +layout(location = 0) in vec3 fragColor; + +layout(location = 0) out vec4 outColor; + +void main() { + outColor = vec4(fragColor, 1.0); +} \ No newline at end of file diff --git a/shaders/simple_shader.vert b/shaders/simple_shader.vert new file mode 100644 index 0000000..3a216d1 --- /dev/null +++ b/shaders/simple_shader.vert @@ -0,0 +1,15 @@ +#version 450 + +layout(location = 0) in vec3 inPosition; +layout(location = 1) in vec3 inColor; + +layout(location = 0) out vec3 fragColor; + +layout(set = 0, binding = 0) uniform UniformBufferObject { + mat4 mvp; +} ubo; + +void main() { + gl_Position = ubo.mvp * vec4(inPosition, 1.0); + fragColor = inColor; +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp deleted file mode 100644 index 4f9de56..0000000 --- a/src/main.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include - -int main() -{ - std::cout << "Hello, World!" << std::endl; - return 0; -}