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:
@@ -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
|
||||||
@@ -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 编译目标:<name>(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)
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
//
|
||||||
|
// Created by atdunbg on 2026/9/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <Engine/Core/Window.h>
|
||||||
|
#include <Engine/Renderer/Renderer.h>
|
||||||
|
#include <Engine/Core/Log.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
+18
-5
@@ -1,8 +1,21 @@
|
|||||||
cmake_minimum_required(VERSION 4.3)
|
cmake_minimum_required(VERSION 3.20)
|
||||||
project(HelloVulkan)
|
project(HelloVulkan CXX)
|
||||||
|
|
||||||
file(GLOB_RECURSE SOURCE_CODE src/**.cpp)
|
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
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})
|
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
//
|
||||||
|
// Created by atdunbg on 2026/9/20.
|
||||||
|
//
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
//
|
||||||
|
// Created by atdunbg on 2026/9/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
#include <Engine/Core/Window.h>
|
||||||
|
#include <Engine/Core/Log.h>
|
||||||
|
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
|
||||||
|
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<const char*> Window::GetRequiredExtensions()
|
||||||
|
{
|
||||||
|
u32 count = 0;
|
||||||
|
const char** extensions = glfwGetRequiredInstanceExtensions(&count);
|
||||||
|
return std::vector<const char*>(extensions, extensions + count);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Engine
|
||||||
@@ -0,0 +1,464 @@
|
|||||||
|
//
|
||||||
|
// Created by atdunbg on 2026/9/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <Engine/Renderer/Renderer.h>
|
||||||
|
#include <Engine/Vulkan/Device.h>
|
||||||
|
#include <Engine/Vulkan/MemoryAllocator.h>
|
||||||
|
#include <Engine/Vulkan/DescriptorManager.h>
|
||||||
|
#include <Engine/Vulkan/ShaderManager.h>
|
||||||
|
#include <Engine/Vulkan/Swapchain.h>
|
||||||
|
#include <Engine/Vulkan/CommandManager.h>
|
||||||
|
#include <Engine/Vulkan/SyncManager.h>
|
||||||
|
#include <Engine/Vulkan/Buffer.h>
|
||||||
|
#include <Engine/Vulkan/UniformBuffer.h>
|
||||||
|
#include <Engine/Core/Window.h>
|
||||||
|
#include <Engine/Core/Log.h>
|
||||||
|
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <limits>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Engine
|
||||||
|
{
|
||||||
|
// 顶点结构:位置 + 颜色
|
||||||
|
struct Vertex
|
||||||
|
{
|
||||||
|
float position[3];
|
||||||
|
float color[3];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Uniform Buffer 结构
|
||||||
|
struct UniformBufferObject
|
||||||
|
{
|
||||||
|
glm::mat4 mvp;
|
||||||
|
};
|
||||||
|
|
||||||
|
Renderer::Renderer(Window& window)
|
||||||
|
: m_Window(window)
|
||||||
|
{
|
||||||
|
// 1. Device
|
||||||
|
m_Device = CreateScope<Device>(window);
|
||||||
|
|
||||||
|
// 2. MemoryAllocator
|
||||||
|
m_Allocator = CreateScope<MemoryAllocator>(*m_Device);
|
||||||
|
|
||||||
|
// 3. DescriptorManager
|
||||||
|
m_DescriptorManager = CreateScope<DescriptorManager>(*m_Device);
|
||||||
|
|
||||||
|
// 4. DescriptorSetLayout(一个 uniform buffer binding)
|
||||||
|
m_DescriptorSetLayout = m_DescriptorManager->CreateUniformBufferLayout(0);
|
||||||
|
|
||||||
|
// 5. PipelineLayout(shader 绑定描述符时用)
|
||||||
|
m_PipelineLayout = m_DescriptorManager->CreatePipelineLayout(m_DescriptorSetLayout);
|
||||||
|
|
||||||
|
// 6. ShaderManager,编译 shader 时传入描述符布局
|
||||||
|
m_ShaderManager = CreateScope<ShaderManager>(*m_Device);
|
||||||
|
|
||||||
|
std::string shaderDir = "shaders/";
|
||||||
|
auto vert = m_ShaderManager->CreateFromFile(
|
||||||
|
shaderDir + "simple_shader.vert.spv",
|
||||||
|
vk::ShaderStageFlagBits::eVertex,
|
||||||
|
m_DescriptorSetLayout);
|
||||||
|
auto frag = m_ShaderManager->CreateFromFile(
|
||||||
|
shaderDir + "simple_shader.frag.spv",
|
||||||
|
vk::ShaderStageFlagBits::eFragment,
|
||||||
|
m_DescriptorSetLayout);
|
||||||
|
|
||||||
|
m_VertShader = vert.Handle;
|
||||||
|
m_FragShader = frag.Handle;
|
||||||
|
|
||||||
|
if (!m_VertShader || !m_FragShader)
|
||||||
|
{
|
||||||
|
ENGINE_FATAL("Failed to create shader objects");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. Swapchain
|
||||||
|
m_Swapchain = CreateScope<Swapchain>(*m_Device, *m_Allocator, window);
|
||||||
|
m_Extent = m_Swapchain->GetExtent();
|
||||||
|
m_Format = m_Swapchain->GetFormat();
|
||||||
|
|
||||||
|
// 8. SyncManager
|
||||||
|
m_SyncManager = CreateScope<SyncManager>(*m_Device, kMaxFramesInFlight, m_Swapchain->GetImageCount());
|
||||||
|
|
||||||
|
// 9. CommandManager
|
||||||
|
m_CommandManager = CreateScope<CommandManager>(*m_Device);
|
||||||
|
|
||||||
|
// 10. 顶点缓冲
|
||||||
|
std::vector<Vertex> vertices = {
|
||||||
|
// 前面 (-Z)
|
||||||
|
{{-0.5f, -0.5f, 0.5f}, {1.0f, 0.0f, 0.0f}},
|
||||||
|
{{ 0.5f, -0.5f, 0.5f}, {1.0f, 0.0f, 0.0f}},
|
||||||
|
{{ 0.5f, 0.5f, 0.5f}, {1.0f, 0.0f, 0.0f}},
|
||||||
|
{{-0.5f, 0.5f, 0.5f}, {1.0f, 0.0f, 0.0f}},
|
||||||
|
// 后面 (+Z)
|
||||||
|
{{-0.5f, -0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}},
|
||||||
|
{{-0.5f, 0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}},
|
||||||
|
{{ 0.5f, 0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}},
|
||||||
|
{{ 0.5f, -0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}},
|
||||||
|
// 左面 (-X)
|
||||||
|
{{-0.5f, -0.5f, -0.5f}, {0.0f, 0.0f, 1.0f}},
|
||||||
|
{{-0.5f, -0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}},
|
||||||
|
{{-0.5f, 0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}},
|
||||||
|
{{-0.5f, 0.5f, -0.5f}, {0.0f, 0.0f, 1.0f}},
|
||||||
|
// 右面 (+X)
|
||||||
|
{{ 0.5f, -0.5f, 0.5f}, {1.0f, 1.0f, 0.0f}},
|
||||||
|
{{ 0.5f, -0.5f, -0.5f}, {1.0f, 1.0f, 0.0f}},
|
||||||
|
{{ 0.5f, 0.5f, -0.5f}, {1.0f, 1.0f, 0.0f}},
|
||||||
|
{{ 0.5f, 0.5f, 0.5f}, {1.0f, 1.0f, 0.0f}},
|
||||||
|
// 上面 (+Y)
|
||||||
|
{{-0.5f, 0.5f, 0.5f}, {1.0f, 0.0f, 1.0f}},
|
||||||
|
{{ 0.5f, 0.5f, 0.5f}, {1.0f, 0.0f, 1.0f}},
|
||||||
|
{{ 0.5f, 0.5f, -0.5f}, {1.0f, 0.0f, 1.0f}},
|
||||||
|
{{-0.5f, 0.5f, -0.5f}, {1.0f, 0.0f, 1.0f}},
|
||||||
|
// 下面 (-Y)
|
||||||
|
{{-0.5f, -0.5f, -0.5f}, {0.0f, 1.0f, 1.0f}},
|
||||||
|
{{ 0.5f, -0.5f, -0.5f}, {0.0f, 1.0f, 1.0f}},
|
||||||
|
{{ 0.5f, -0.5f, 0.5f}, {0.0f, 1.0f, 1.0f}},
|
||||||
|
{{-0.5f, -0.5f, 0.5f}, {0.0f, 1.0f, 1.0f}},
|
||||||
|
};
|
||||||
|
u64 bufferSize = sizeof(Vertex) * vertices.size();
|
||||||
|
|
||||||
|
|
||||||
|
m_VertexBuffer = CreateScope<Buffer>(
|
||||||
|
*m_Device, *m_Allocator,
|
||||||
|
bufferSize,
|
||||||
|
vk::BufferUsageFlagBits::eVertexBuffer,
|
||||||
|
vk::MemoryPropertyFlagBits::eHostVisible |
|
||||||
|
vk::MemoryPropertyFlagBits::eHostCoherent);
|
||||||
|
m_VertexBuffer->Upload(vertices.data(), bufferSize);
|
||||||
|
|
||||||
|
|
||||||
|
// 索引缓冲:两个三角形组成一个四边形
|
||||||
|
std::vector<u32> indices = {
|
||||||
|
0, 1, 2, 2, 3, 0, // 前面
|
||||||
|
4, 5, 6, 6, 7, 4, // 后面
|
||||||
|
8, 9,10,10,11, 8, // 左面
|
||||||
|
12,13,14,14,15,12, // 右面
|
||||||
|
16,17,18,18,19,16, // 上面
|
||||||
|
20,21,22,22,23,20, // 下面
|
||||||
|
};
|
||||||
|
u64 indexBufferSize = sizeof(u32) * indices.size();
|
||||||
|
|
||||||
|
|
||||||
|
m_IndexBuffer = CreateScope<Buffer>(
|
||||||
|
*m_Device, *m_Allocator,
|
||||||
|
indexBufferSize,
|
||||||
|
vk::BufferUsageFlagBits::eIndexBuffer,
|
||||||
|
vk::MemoryPropertyFlagBits::eHostVisible |
|
||||||
|
vk::MemoryPropertyFlagBits::eHostCoherent);
|
||||||
|
m_IndexBuffer->Upload(indices.data(), indexBufferSize);
|
||||||
|
|
||||||
|
|
||||||
|
// 11. Uniform Buffer
|
||||||
|
m_UniformBuffer = CreateScope<UniformBuffer>(
|
||||||
|
*m_Device, *m_Allocator, sizeof(UniformBufferObject));
|
||||||
|
|
||||||
|
// 12. DescriptorSet + 绑定 UniformBuffer
|
||||||
|
m_DescriptorSet = m_DescriptorManager->AllocateDescriptorSet(m_DescriptorSetLayout);
|
||||||
|
m_DescriptorManager->UpdateUniformBuffer(
|
||||||
|
m_DescriptorSet, 0, m_UniformBuffer->Get(), m_UniformBuffer->GetSize());
|
||||||
|
|
||||||
|
// 13. Debug: 记录开始时间,用于旋转
|
||||||
|
m_StartTime = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
|
ENGINE_INFO("Renderer initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
Renderer::~Renderer()
|
||||||
|
{
|
||||||
|
m_Device->GetDevice().waitIdle();
|
||||||
|
|
||||||
|
if (m_VertShader)
|
||||||
|
{
|
||||||
|
m_Device->GetDevice().destroyShaderEXT(m_VertShader);
|
||||||
|
}
|
||||||
|
if (m_FragShader)
|
||||||
|
{
|
||||||
|
m_Device->GetDevice().destroyShaderEXT(m_FragShader);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_Device->GetDevice().destroyPipelineLayout(m_PipelineLayout);
|
||||||
|
m_Device->GetDevice().destroyDescriptorSetLayout(m_DescriptorSetLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Renderer::Render()
|
||||||
|
{
|
||||||
|
u32 frameIndex = m_CurrentFrame;
|
||||||
|
const auto& vkDevice = m_Device->GetDevice();
|
||||||
|
|
||||||
|
// 1. 更新 MVP 矩阵
|
||||||
|
auto currentTime = std::chrono::steady_clock::now();
|
||||||
|
float time = std::chrono::duration<float, std::chrono::seconds::period>(
|
||||||
|
currentTime - m_StartTime).count();
|
||||||
|
|
||||||
|
auto model = glm::rotate(glm::mat4(1.0f),
|
||||||
|
time * glm::radians(90.0f),
|
||||||
|
glm::vec3(1.0f, 1.0f, 1.0f));
|
||||||
|
|
||||||
|
auto view = glm::lookAt(glm::vec3(0.0f, 0.0f, 2.0f),
|
||||||
|
glm::vec3(0.0f, 0.0f, 0.0f),
|
||||||
|
glm::vec3(0.0f, 1.0f, 0.0f));
|
||||||
|
|
||||||
|
float aspect = static_cast<float>(m_Extent.width) /
|
||||||
|
static_cast<float>(m_Extent.height);
|
||||||
|
|
||||||
|
auto proj = glm::perspective(glm::radians(45.0f), aspect, 0.1f, 10.0f);
|
||||||
|
proj[1][1] *= -1.0f; // Vulkan Y 翻转
|
||||||
|
|
||||||
|
UniformBufferObject ubo{};
|
||||||
|
ubo.mvp = proj * view * model;
|
||||||
|
|
||||||
|
m_UniformBuffer->Update(&ubo, sizeof(ubo));
|
||||||
|
|
||||||
|
// 等待这一帧上一次完成
|
||||||
|
m_SyncManager->WaitFence(frameIndex);
|
||||||
|
|
||||||
|
// 2. 获取交换链 image
|
||||||
|
u32 imageIndex = 0;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const auto result = vkDevice.acquireNextImageKHR(
|
||||||
|
m_Swapchain->Get(),
|
||||||
|
std::numeric_limits<u64>::max(),
|
||||||
|
m_SyncManager->GetImageAvailable(frameIndex));
|
||||||
|
|
||||||
|
if (result.result == vk::Result::eErrorOutOfDateKHR)
|
||||||
|
{
|
||||||
|
m_Swapchain->Recreate(m_Window);
|
||||||
|
return; // ← fence 没被 reset,下帧 wait 立即返回
|
||||||
|
}
|
||||||
|
imageIndex = result.value;
|
||||||
|
}
|
||||||
|
catch (const vk::OutOfDateKHRError&)
|
||||||
|
{
|
||||||
|
m_Swapchain->Recreate(m_Window);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. acquire 成功, reset fence
|
||||||
|
m_SyncManager->ResetFence(frameIndex);
|
||||||
|
|
||||||
|
// 4. 录制命令
|
||||||
|
auto cmd = m_CommandManager->BeginFrame();
|
||||||
|
|
||||||
|
TransitionImageLayout(cmd, m_Swapchain->GetImages()[imageIndex],
|
||||||
|
vk::ImageLayout::eUndefined,
|
||||||
|
vk::ImageLayout::eColorAttachmentOptimal);
|
||||||
|
|
||||||
|
// 深度转换
|
||||||
|
vk::ImageMemoryBarrier2 depthBarrier;
|
||||||
|
depthBarrier.setSrcStageMask(vk::PipelineStageFlagBits2::eEarlyFragmentTests)
|
||||||
|
.setSrcAccessMask(vk::AccessFlagBits2::eDepthStencilAttachmentWrite)
|
||||||
|
.setDstStageMask(vk::PipelineStageFlagBits2::eEarlyFragmentTests)
|
||||||
|
.setDstAccessMask(vk::AccessFlagBits2::eDepthStencilAttachmentWrite)
|
||||||
|
.setOldLayout(vk::ImageLayout::eUndefined)
|
||||||
|
.setNewLayout(vk::ImageLayout::eDepthAttachmentOptimal)
|
||||||
|
.setImage(m_Swapchain->GetDepthImage())
|
||||||
|
.setSubresourceRange({
|
||||||
|
vk::ImageAspectFlagBits::eDepth,
|
||||||
|
0, 1, 0, 1
|
||||||
|
});
|
||||||
|
|
||||||
|
vk::DependencyInfo depthDep;
|
||||||
|
depthDep.setImageMemoryBarriers(depthBarrier);
|
||||||
|
cmd.pipelineBarrier2(depthDep);
|
||||||
|
|
||||||
|
RecordCommands(cmd, imageIndex);
|
||||||
|
|
||||||
|
TransitionImageLayout(cmd, m_Swapchain->GetImages()[imageIndex],
|
||||||
|
vk::ImageLayout::eColorAttachmentOptimal,
|
||||||
|
vk::ImageLayout::ePresentSrcKHR);
|
||||||
|
|
||||||
|
m_CommandManager->EndFrame();
|
||||||
|
|
||||||
|
// 5. 提交
|
||||||
|
std::array<vk::PipelineStageFlags, 1> waitStages = {
|
||||||
|
vk::PipelineStageFlagBits::eColorAttachmentOutput
|
||||||
|
};
|
||||||
|
std::array<vk::Semaphore, 1> waitSemaphores = {
|
||||||
|
m_SyncManager->GetImageAvailable(frameIndex)
|
||||||
|
};
|
||||||
|
std::array<vk::Semaphore, 1> signalSemaphores = {
|
||||||
|
m_SyncManager->GetRenderFinished(imageIndex)
|
||||||
|
};
|
||||||
|
|
||||||
|
vk::SubmitInfo submitInfo;
|
||||||
|
submitInfo.setCommandBuffers(cmd)
|
||||||
|
.setWaitSemaphores(waitSemaphores)
|
||||||
|
.setWaitDstStageMask(waitStages)
|
||||||
|
.setSignalSemaphores(signalSemaphores);
|
||||||
|
|
||||||
|
m_Device->GetGraphicsQueue().submit(submitInfo, m_SyncManager->GetFrameFence(frameIndex));
|
||||||
|
|
||||||
|
// 6. 呈现
|
||||||
|
std::array<vk::SwapchainKHR, 1> swapchains = {m_Swapchain->Get()};
|
||||||
|
std::array<vk::Semaphore, 1> presentWait = {
|
||||||
|
m_SyncManager->GetRenderFinished(imageIndex)
|
||||||
|
};
|
||||||
|
|
||||||
|
vk::PresentInfoKHR presentInfo;
|
||||||
|
presentInfo.setImageIndices(imageIndex)
|
||||||
|
.setSwapchains(swapchains)
|
||||||
|
.setWaitSemaphores(presentWait);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto result = m_Device->GetPresentQueue().presentKHR(presentInfo);
|
||||||
|
if (result == vk::Result::eSuboptimalKHR)
|
||||||
|
{
|
||||||
|
m_Swapchain->Recreate(m_Window);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const vk::OutOfDateKHRError&)
|
||||||
|
{
|
||||||
|
m_Swapchain->Recreate(m_Window);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::RecordCommands(vk::CommandBuffer cmd, u32 imageIndex)
|
||||||
|
{
|
||||||
|
vk::ClearValue clearValue;
|
||||||
|
clearValue.color = vk::ClearColorValue(std::array<float, 4>{0.1f, 0.1f, 0.1f, 1.0f});
|
||||||
|
|
||||||
|
// 颜色附件
|
||||||
|
vk::RenderingAttachmentInfo colorAttachment;
|
||||||
|
colorAttachment.setImageView(m_Swapchain->GetImageViews()[imageIndex])
|
||||||
|
.setImageLayout(vk::ImageLayout::eColorAttachmentOptimal)
|
||||||
|
.setLoadOp(vk::AttachmentLoadOp::eClear)
|
||||||
|
.setStoreOp(vk::AttachmentStoreOp::eStore)
|
||||||
|
.setClearValue(clearValue);
|
||||||
|
|
||||||
|
// 深度附件
|
||||||
|
vk::RenderingAttachmentInfo depthAttachment;
|
||||||
|
depthAttachment.setImageView(m_Swapchain->GetDepthImageView())
|
||||||
|
.setImageLayout(vk::ImageLayout::eDepthAttachmentOptimal)
|
||||||
|
.setLoadOp(vk::AttachmentLoadOp::eClear)
|
||||||
|
.setStoreOp(vk::AttachmentStoreOp::eDontCare)
|
||||||
|
.setClearValue(vk::ClearValue(vk::ClearDepthStencilValue(1.0f, 0)));
|
||||||
|
|
||||||
|
vk::RenderingInfo renderingInfo;
|
||||||
|
renderingInfo.setRenderArea({{0, 0}, m_Extent})
|
||||||
|
.setLayerCount(1)
|
||||||
|
.setColorAttachments(colorAttachment)
|
||||||
|
.setPDepthAttachment(&depthAttachment);
|
||||||
|
|
||||||
|
cmd.beginRendering(renderingInfo);
|
||||||
|
|
||||||
|
// 绑定着色器
|
||||||
|
std::array<vk::ShaderStageFlagBits, 2> stages = {
|
||||||
|
vk::ShaderStageFlagBits::eVertex,
|
||||||
|
vk::ShaderStageFlagBits::eFragment
|
||||||
|
};
|
||||||
|
std::array<vk::ShaderEXT, 2> shaders = {m_VertShader, m_FragShader};
|
||||||
|
cmd.bindShadersEXT(stages, shaders);
|
||||||
|
|
||||||
|
// 绑定描述符集
|
||||||
|
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
|
||||||
|
m_PipelineLayout, 0,
|
||||||
|
m_DescriptorSet, {});
|
||||||
|
|
||||||
|
// ===== 动态状态 =====
|
||||||
|
cmd.setRasterizerDiscardEnable(VK_FALSE);
|
||||||
|
|
||||||
|
cmd.setDepthTestEnable(VK_TRUE);
|
||||||
|
cmd.setDepthWriteEnable(VK_TRUE);
|
||||||
|
cmd.setDepthCompareOp(vk::CompareOp::eLess);
|
||||||
|
cmd.setDepthBoundsTestEnable(VK_FALSE);
|
||||||
|
|
||||||
|
cmd.setStencilTestEnable(VK_FALSE);
|
||||||
|
cmd.setDepthBiasEnable(VK_FALSE);
|
||||||
|
cmd.setPolygonModeEXT(vk::PolygonMode::eFill);
|
||||||
|
cmd.setCullMode(vk::CullModeFlagBits::eNone);
|
||||||
|
cmd.setFrontFace(vk::FrontFace::eCounterClockwise);
|
||||||
|
cmd.setPrimitiveTopology(vk::PrimitiveTopology::eTriangleList);
|
||||||
|
cmd.setPrimitiveRestartEnable(VK_FALSE);
|
||||||
|
|
||||||
|
// 顶点输入
|
||||||
|
vk::VertexInputBindingDescription2EXT binding;
|
||||||
|
binding.setBinding(0)
|
||||||
|
.setStride(sizeof(f32) * 6)
|
||||||
|
.setInputRate(vk::VertexInputRate::eVertex)
|
||||||
|
.setDivisor(1);
|
||||||
|
|
||||||
|
std::array<vk::VertexInputAttributeDescription2EXT, 2> attributes;
|
||||||
|
attributes[0].setLocation(0)
|
||||||
|
.setBinding(0)
|
||||||
|
.setFormat(vk::Format::eR32G32B32Sfloat)
|
||||||
|
.setOffset(0);
|
||||||
|
attributes[1].setLocation(1)
|
||||||
|
.setBinding(0)
|
||||||
|
.setFormat(vk::Format::eR32G32B32Sfloat)
|
||||||
|
.setOffset(sizeof(f32) * 3);
|
||||||
|
|
||||||
|
cmd.setVertexInputEXT(binding, attributes);
|
||||||
|
|
||||||
|
cmd.setRasterizationSamplesEXT(vk::SampleCountFlagBits::e1);
|
||||||
|
vk::SampleMask sampleMask = 0xFFFFFFFF;
|
||||||
|
cmd.setSampleMaskEXT(vk::SampleCountFlagBits::e1, sampleMask);
|
||||||
|
cmd.setAlphaToCoverageEnableEXT(VK_FALSE);
|
||||||
|
cmd.setAlphaToOneEnableEXT(VK_FALSE);
|
||||||
|
|
||||||
|
vk::Viewport viewport(0.0f, 0.0f,
|
||||||
|
static_cast<float>(m_Extent.width),
|
||||||
|
static_cast<float>(m_Extent.height),
|
||||||
|
0.0f, 1.0f);
|
||||||
|
cmd.setViewportWithCount(viewport);
|
||||||
|
|
||||||
|
vk::Rect2D scissor({0, 0}, m_Extent);
|
||||||
|
cmd.setScissorWithCount(scissor);
|
||||||
|
|
||||||
|
vk::Bool32 blendEnable = VK_FALSE;
|
||||||
|
cmd.setColorBlendEnableEXT(0, blendEnable);
|
||||||
|
|
||||||
|
vk::ColorBlendEquationEXT blendEquation;
|
||||||
|
blendEquation.setSrcColorBlendFactor(vk::BlendFactor::eOne)
|
||||||
|
.setDstColorBlendFactor(vk::BlendFactor::eZero)
|
||||||
|
.setColorBlendOp(vk::BlendOp::eAdd)
|
||||||
|
.setSrcAlphaBlendFactor(vk::BlendFactor::eOne)
|
||||||
|
.setDstAlphaBlendFactor(vk::BlendFactor::eZero)
|
||||||
|
.setAlphaBlendOp(vk::BlendOp::eAdd);
|
||||||
|
cmd.setColorBlendEquationEXT(0, blendEquation);
|
||||||
|
|
||||||
|
vk::ColorComponentFlags colorWriteMask =
|
||||||
|
vk::ColorComponentFlagBits::eR |
|
||||||
|
vk::ColorComponentFlagBits::eG |
|
||||||
|
vk::ColorComponentFlagBits::eB |
|
||||||
|
vk::ColorComponentFlagBits::eA;
|
||||||
|
cmd.setColorWriteMaskEXT(0, colorWriteMask);
|
||||||
|
|
||||||
|
// 绑定顶点缓冲并绘制
|
||||||
|
cmd.bindVertexBuffers(0, m_VertexBuffer->Get(), {0});
|
||||||
|
cmd.bindIndexBuffer(m_IndexBuffer->Get(), 0, vk::IndexType::eUint32);
|
||||||
|
// cmd.draw(3, 1, 0, 0);
|
||||||
|
cmd.drawIndexed(36, 1, 0, 0, 0);
|
||||||
|
|
||||||
|
cmd.endRendering();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::TransitionImageLayout(vk::CommandBuffer cmd, vk::Image image,
|
||||||
|
vk::ImageLayout oldLayout, vk::ImageLayout newLayout)
|
||||||
|
{
|
||||||
|
vk::ImageMemoryBarrier2 barrier;
|
||||||
|
barrier.setSrcStageMask(vk::PipelineStageFlagBits2::eColorAttachmentOutput)
|
||||||
|
.setSrcAccessMask(vk::AccessFlagBits2::eColorAttachmentWrite)
|
||||||
|
.setDstStageMask(vk::PipelineStageFlagBits2::eColorAttachmentOutput)
|
||||||
|
.setDstAccessMask(vk::AccessFlagBits2::eColorAttachmentWrite)
|
||||||
|
.setOldLayout(oldLayout)
|
||||||
|
.setNewLayout(newLayout)
|
||||||
|
.setImage(image)
|
||||||
|
.setSubresourceRange({
|
||||||
|
vk::ImageAspectFlagBits::eColor,
|
||||||
|
0, 1, 0, 1
|
||||||
|
});
|
||||||
|
|
||||||
|
vk::DependencyInfo dependencyInfo;
|
||||||
|
dependencyInfo.setImageMemoryBarriers(barrier);
|
||||||
|
|
||||||
|
cmd.pipelineBarrier2(dependencyInfo);
|
||||||
|
}
|
||||||
|
} // namespace Engine
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
//
|
||||||
|
// Created by atdunbg on 2026/9/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <Engine/Vulkan/Buffer.h>
|
||||||
|
#include <Engine/Vulkan/Device.h>
|
||||||
|
#include <Engine/Vulkan/MemoryAllocator.h>
|
||||||
|
#include <Engine/Core/Log.h>
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
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<char*>(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
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
//
|
||||||
|
// Created by atdunbg on 2026/9/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <Engine/Vulkan/CommandManager.h>
|
||||||
|
#include <Engine/Vulkan/Device.h>
|
||||||
|
#include <Engine/Core/Log.h>
|
||||||
|
|
||||||
|
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
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
//
|
||||||
|
// Created by atdunbg on 2026/9/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <Engine/Vulkan/DescriptorManager.h>
|
||||||
|
#include <Engine/Vulkan/Device.h>
|
||||||
|
#include <Engine/Core/Log.h>
|
||||||
|
|
||||||
|
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
|
||||||
@@ -0,0 +1,331 @@
|
|||||||
|
//
|
||||||
|
// Created by atdunbg on 2026/9/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
#include <Engine/Vulkan/Device.h>
|
||||||
|
#include <Engine/Core/Window.h>
|
||||||
|
#include <Engine/Core/Log.h>
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
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<std::string> 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<vk::DeviceQueueCreateInfo> queueInfos;
|
||||||
|
std::set<uint32_t> 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<const char*> 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
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
//
|
||||||
|
// Created by atdunbg on 2026/9/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <Engine/Vulkan/MemoryAllocator.h>
|
||||||
|
#include <Engine/Vulkan/Device.h>
|
||||||
|
#include <Engine/Core/Log.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
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<char*>(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<char*>(block.MappedPtr) + alignedOffset
|
||||||
|
: nullptr;
|
||||||
|
|
||||||
|
blocks.back().Used = alignedOffset + reqs.size;
|
||||||
|
|
||||||
|
ENGINE_INFO("MemoryAllocator: new block of {} bytes (type {})",
|
||||||
|
blockSize, typeIndex);
|
||||||
|
|
||||||
|
return alloc;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Engine
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
//
|
||||||
|
// Created by atdunbg on 2026/9/20.
|
||||||
|
//
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
//
|
||||||
|
// Created by atdunbg on 2026/9/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
#include <Engine/Vulkan/ShaderManager.h>
|
||||||
|
#include <Engine/Vulkan/Device.h>
|
||||||
|
#include <Engine/Core/Log.h>
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
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<u32>& 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<u32> 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<u32> buffer(size / sizeof(u32));
|
||||||
|
file.seekg(0);
|
||||||
|
file.read(reinterpret_cast<char*>(buffer.data()), size);
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Engine
|
||||||
@@ -0,0 +1,255 @@
|
|||||||
|
//
|
||||||
|
// Created by atdunbg on 2026/9/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <Engine/Vulkan/Swapchain.h>
|
||||||
|
#include <Engine/Vulkan/Device.h>
|
||||||
|
#include <Engine/Core/Window.h>
|
||||||
|
#include <Engine/Core/Log.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
#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<vk::SurfaceFormatKHR>& 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<vk::PresentModeKHR>& 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<uint32_t>::max())
|
||||||
|
{
|
||||||
|
return caps.currentExtent;
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::Extent2D extent = {
|
||||||
|
static_cast<uint32_t>(window.GetWidth()),
|
||||||
|
static_cast<uint32_t>(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<vk::Format, 3> candidates = {
|
||||||
|
vk::Format::eD32Sfloat,
|
||||||
|
vk::Format::eD32SfloatS8Uint,
|
||||||
|
vk::Format::eD24UnormS8Uint
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto format : candidates)
|
||||||
|
{
|
||||||
|
auto props = m_Device.GetPhysicalDevice().getFormatProperties(format);
|
||||||
|
if (props.optimalTilingFeatures & vk::FormatFeatureFlagBits::eDepthStencilAttachment)
|
||||||
|
{
|
||||||
|
return format;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ENGINE_FATAL("No depth format supported");
|
||||||
|
return vk::Format::eD32Sfloat;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Swapchain::CreateDepthResources()
|
||||||
|
{
|
||||||
|
m_DepthFormat = FindDepthFormat();
|
||||||
|
|
||||||
|
vk::ImageCreateInfo imageInfo;
|
||||||
|
imageInfo.setImageType(vk::ImageType::e2D)
|
||||||
|
.setFormat(m_DepthFormat)
|
||||||
|
.setExtent({m_Extent.width, m_Extent.height, 1})
|
||||||
|
.setMipLevels(1)
|
||||||
|
.setArrayLayers(1)
|
||||||
|
.setSamples(vk::SampleCountFlagBits::e1)
|
||||||
|
.setTiling(vk::ImageTiling::eOptimal)
|
||||||
|
.setUsage(vk::ImageUsageFlagBits::eDepthStencilAttachment)
|
||||||
|
.setSharingMode(vk::SharingMode::eExclusive)
|
||||||
|
.setInitialLayout(vk::ImageLayout::eUndefined);
|
||||||
|
|
||||||
|
m_DepthImage = m_Device.GetDevice().createImage(imageInfo);
|
||||||
|
|
||||||
|
auto reqs = m_Device.GetDevice().getImageMemoryRequirements(m_DepthImage);
|
||||||
|
auto alloc = m_Allocator.Allocate(reqs, vk::MemoryPropertyFlagBits::eDeviceLocal);
|
||||||
|
|
||||||
|
m_DepthMemory = alloc.Memory;
|
||||||
|
m_Device.GetDevice().bindImageMemory(m_DepthImage, m_DepthMemory, alloc.Offset);
|
||||||
|
|
||||||
|
vk::ImageViewCreateInfo viewInfo;
|
||||||
|
viewInfo.setImage(m_DepthImage)
|
||||||
|
.setViewType(vk::ImageViewType::e2D)
|
||||||
|
.setFormat(m_DepthFormat)
|
||||||
|
.setSubresourceRange({
|
||||||
|
vk::ImageAspectFlagBits::eDepth,
|
||||||
|
0, 1, 0, 1
|
||||||
|
});
|
||||||
|
|
||||||
|
m_DepthImageView = m_Device.GetDevice().createImageView(viewInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Swapchain::DestroyDepthResources()
|
||||||
|
{
|
||||||
|
const auto& vkDevice = m_Device.GetDevice();
|
||||||
|
if (m_DepthImageView)
|
||||||
|
{
|
||||||
|
vkDevice.destroyImageView(m_DepthImageView);
|
||||||
|
m_DepthImageView = nullptr;
|
||||||
|
}
|
||||||
|
if (m_DepthImage)
|
||||||
|
{
|
||||||
|
vkDevice.destroyImage(m_DepthImage);
|
||||||
|
m_DepthImage = nullptr;
|
||||||
|
}
|
||||||
|
// 内存由 MemoryAllocator 统一释放
|
||||||
|
}
|
||||||
|
} // namespace Engine
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
//
|
||||||
|
// Created by atdunbg on 2026/9/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
#include <Engine/Vulkan/SyncManager.h>
|
||||||
|
#include <Engine/Vulkan/Device.h>
|
||||||
|
#include <Engine/Core/Log.h>
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
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<u64>::max());
|
||||||
|
if (result != vk::Result::eSuccess) {
|
||||||
|
ENGINE_ERROR("waitForFences failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SyncManager::ResetFence(u32 frameIndex)
|
||||||
|
{
|
||||||
|
m_Device.GetDevice().resetFences(m_FrameFences[frameIndex]);
|
||||||
|
}
|
||||||
|
} // namespace Engine
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
//
|
||||||
|
// Created by atdunbg on 2026/9/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <Engine/Vulkan/UniformBuffer.h>
|
||||||
|
#include <Engine/Vulkan/Device.h>
|
||||||
|
#include <Engine/Vulkan/MemoryAllocator.h>
|
||||||
|
#include <Engine/Core/Log.h>
|
||||||
|
|
||||||
|
namespace Engine {
|
||||||
|
|
||||||
|
UniformBuffer::UniformBuffer(Device& device, MemoryAllocator& allocator, u64 size)
|
||||||
|
: m_Size(size)
|
||||||
|
{
|
||||||
|
// Uniform Buffer 需要 CPU 每帧更新,所以用 HOST_VISIBLE | HOST_COHERENT
|
||||||
|
m_Buffer = CreateScope<Buffer>(
|
||||||
|
device, allocator,
|
||||||
|
size,
|
||||||
|
vk::BufferUsageFlagBits::eUniformBuffer,
|
||||||
|
vk::MemoryPropertyFlagBits::eHostVisible |
|
||||||
|
vk::MemoryPropertyFlagBits::eHostCoherent);
|
||||||
|
|
||||||
|
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
|
||||||
Submodule
+1
Submodule Lib/glfw added at 92dcf4ce74
Submodule
+1
Submodule Lib/glm added at 6f14f4792a
Submodule
+1
Submodule Lib/spdlog added at 57cb5fb7a8
@@ -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);
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
std::cout << "Hello, World!" << std::endl;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user