添加多模型渲染,添加贴图渲染,添加平行光, 暂时不上传assets
This commit is contained in:
@@ -3,3 +3,6 @@
|
||||
cmake-build-*/
|
||||
out/
|
||||
build/
|
||||
|
||||
# 目前只上传代码,暂不上传assets
|
||||
assets/
|
||||
@@ -7,3 +7,6 @@
|
||||
[submodule "Lib/spdlog"]
|
||||
path = Lib/spdlog
|
||||
url = https://github.com/gabime/spdlog.git
|
||||
[submodule "Lib/assimp"]
|
||||
path = Lib/assimp
|
||||
url = https://github.com/assimp/assimp.git
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <Engine/Renderer/Renderer.h>
|
||||
#include <Engine/Core/Log.h>
|
||||
|
||||
#include "Engine/Core/Stats.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
try {
|
||||
|
||||
+32
-1
@@ -5,11 +5,41 @@ set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
set(CMAKE_BINARY_DIR ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
|
||||
|
||||
# 第三方库
|
||||
add_subdirectory(Lib/glfw)
|
||||
add_subdirectory(Lib/glm)
|
||||
add_subdirectory(Lib/spdlog)
|
||||
add_subdirectory(Lib/stb)
|
||||
|
||||
# ---- Assimp(vendored submodule,模型导入;只编译需要的 importer) ----
|
||||
set(BUILD_SHARED_LIBS OFF) # 静态链接
|
||||
set(ASSIMP_BUILD_TESTS OFF CACHE BOOL "" FORCE)
|
||||
set(ASSIMP_BUILD_ASSIMP_TOOLS OFF CACHE BOOL "" FORCE) # 不编 assimp_cmd 等工具
|
||||
set(ASSIMP_INSTALL OFF CACHE BOOL "" FORCE)
|
||||
set(ASSIMP_WARNINGS_AS_ERRORS OFF CACHE BOOL "" FORCE) # assimp 源码警告多
|
||||
set(ASSIMP_INJECT_DEBUG_POSTFIX OFF CACHE BOOL "" FORCE)
|
||||
set(ASSIMP_NO_EXPORT ON CACHE BOOL "" FORCE) # 彻底移除导出代码
|
||||
set(ASSIMP_BUILD_ALL_IMPORTERS_BY_DEFAULT OFF CACHE BOOL "" FORCE) # 全关,按需开
|
||||
set(ASSIMP_BUILD_DRACO OFF CACHE BOOL "" FORCE)
|
||||
# 用 assimp 自带 contrib zlib(而非系统 zlib)。原因:系统 zlib 会使 assimp 走
|
||||
# pkg-config 找系统 minizip,而 Arch 的 minizip 头文件在 /usr/include/minizip/
|
||||
# 子目录,与 assimp 的 #include <unzip.h> 不符 → ZipArchiveIOSystem 编译失败。
|
||||
# 自带 zlib 后 ASSIMP_BUILD_MINIZIP=1,跳过 pkg-config,直接用 contrib/unzip。
|
||||
set(ASSIMP_BUILD_ZLIB ON CACHE BOOL "" FORCE)
|
||||
# 清除首次 configure 残留的 UNZIP_FOUND 缓存(INTERNAL=1 会持续生效,
|
||||
# 使 assimp 误判系统 minizip 可用而不把 contrib/unzip 加进 include 路径)。
|
||||
set(UNZIP_FOUND OFF CACHE INTERNAL "" FORCE)
|
||||
# 需要的导入格式(按需增减,每关一个省编译时间):
|
||||
set(ASSIMP_BUILD_OBJ_IMPORTER ON CACHE BOOL "" FORCE)
|
||||
set(ASSIMP_BUILD_GLTF_IMPORTER ON CACHE BOOL "" FORCE) # glTF1+2 同一选项
|
||||
set(ASSIMP_BUILD_FBX_IMPORTER ON CACHE BOOL "" FORCE)
|
||||
set(ASSIMP_BUILD_STL_IMPORTER ON CACHE BOOL "" FORCE)
|
||||
set(ASSIMP_BUILD_PLY_IMPORTER ON CACHE BOOL "" FORCE)
|
||||
add_subdirectory(Lib/assimp)
|
||||
|
||||
|
||||
|
||||
# Vulkan
|
||||
find_package(Vulkan REQUIRED)
|
||||
@@ -18,4 +48,5 @@ find_package(Vulkan REQUIRED)
|
||||
add_subdirectory(Engine)
|
||||
add_subdirectory(App)
|
||||
|
||||
|
||||
file(GLOB ASSETS "assets")
|
||||
file(COPY ${ASSETS} DESTINATION ${CMAKE_BINARY_DIR})
|
||||
|
||||
@@ -15,6 +15,8 @@ target_link_libraries(Engine PUBLIC
|
||||
glfw
|
||||
Vulkan::Vulkan
|
||||
spdlog::spdlog
|
||||
stb
|
||||
assimp::assimp
|
||||
)
|
||||
|
||||
target_compile_definitions(Engine PRIVATE VULKAN_HPP_DISPATCH_LOADER_DYNAMIC=1)
|
||||
@@ -13,6 +13,7 @@ namespace Engine {
|
||||
|
||||
#define ENGINE_TRACE(...) ::spdlog::trace(__VA_ARGS__)
|
||||
#define ENGINE_INFO(...) ::spdlog::info(__VA_ARGS__)
|
||||
#define ENGINE_DEBUG(...) ::spdlog::debug(__VA_ARGS__)
|
||||
#define ENGINE_WARN(...) ::spdlog::warn(__VA_ARGS__)
|
||||
#define ENGINE_ERROR(...) ::spdlog::error(__VA_ARGS__)
|
||||
#define ENGINE_FATAL(...) ::spdlog::critical(__VA_ARGS__)
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/21.
|
||||
//
|
||||
|
||||
#ifndef HELLOVULKAN_STATS_H
|
||||
#define HELLOVULKAN_STATS_H
|
||||
|
||||
|
||||
#include <Engine/Core/Types.h>
|
||||
#include <mutex>
|
||||
|
||||
namespace Engine
|
||||
{
|
||||
class Stats
|
||||
{
|
||||
public:
|
||||
static Stats& Get();
|
||||
|
||||
// ---- Engine 自己管理的 GPU 内存 ----
|
||||
void AddTexture(u64 bytes);
|
||||
void RemoveTexture(u64 bytes);
|
||||
|
||||
void AddMesh(u64 vertexBytes, u64 indexBytes);
|
||||
void RemoveMesh(u64 vertexBytes, u64 indexBytes);
|
||||
|
||||
void AddBuffer(u64 bytes);
|
||||
void RemoveBuffer(u64 bytes);
|
||||
|
||||
void AddMemoryBlock(u64 bytes);
|
||||
void RemoveMemoryBlock(u64 bytes);
|
||||
void AddMemoryUsed(u64 bytes);
|
||||
void RemoveMemoryUsed(u64 bytes);
|
||||
|
||||
// ---- 每帧统计 ----
|
||||
void BeginFrame();
|
||||
void EndFrame();
|
||||
void AddAllocation(u64 bytes);
|
||||
void AddDeallocation(u64 bytes);
|
||||
|
||||
// ---- 采样进程真实内存 ----
|
||||
void SampleProcessMemory();
|
||||
|
||||
// ---- 打印 ----
|
||||
void Print() const;
|
||||
void PrintDetailed() const;
|
||||
void PrintComparison() const;
|
||||
|
||||
private:
|
||||
Stats() = default;
|
||||
|
||||
struct ProcessMemory
|
||||
{
|
||||
u64 VmSize = 0; // /proc/self/status VmSize
|
||||
u64 Rss = 0; // /proc/self/status VmRSS
|
||||
u64 Pss = 0; // smaps_rollup
|
||||
u64 SharedClean = 0;
|
||||
u64 PrivateDirty = 0;
|
||||
u64 PrivateClean = 0;
|
||||
};
|
||||
|
||||
ProcessMemory ReadProcessMemory() const;
|
||||
|
||||
mutable std::mutex m_Mutex;
|
||||
|
||||
// 资源计数
|
||||
u64 m_TextureCount = 0;
|
||||
u64 m_TextureBytes = 0;
|
||||
|
||||
u64 m_MeshCount = 0;
|
||||
u64 m_MeshBytes = 0;
|
||||
|
||||
u64 m_BufferCount = 0;
|
||||
u64 m_BufferBytes = 0;
|
||||
|
||||
// MemoryAllocator
|
||||
u64 m_MemoryBlockBytes = 0;
|
||||
u64 m_MemoryUsedBytes = 0;
|
||||
|
||||
// 每帧
|
||||
u64 m_FrameAllocation = 0;
|
||||
u64 m_FrameDeallocation = 0;
|
||||
u64 m_TotalAllocation = 0;
|
||||
u64 m_TotalDeallocation = 0;
|
||||
|
||||
// 进程快照
|
||||
ProcessMemory m_ProcessMem{};
|
||||
};
|
||||
|
||||
#define STATS Engine::Stats::Get()
|
||||
} // namespace Engine
|
||||
|
||||
#endif //HELLOVULKAN_STATS_H
|
||||
@@ -38,11 +38,26 @@ namespace Engine {
|
||||
// 需要的 Vulkan 扩展(GLFW 提供)
|
||||
static std::vector<const char*> GetRequiredExtensions();
|
||||
|
||||
// 窗口事件
|
||||
void GetFramebufferSize(int& width, int& height) const;
|
||||
bool WasResized() const { return m_FramebufferResized; }
|
||||
void ResetResizedFlag() { m_FramebufferResized = false; }
|
||||
|
||||
// 输入事件系统
|
||||
|
||||
bool IsKeyPressed(int key) const;
|
||||
void GetMouseDelta(float& dx, float& dy);
|
||||
|
||||
|
||||
|
||||
private:
|
||||
GLFWwindow* m_Window = nullptr;
|
||||
u32 m_Width;
|
||||
u32 m_Height;
|
||||
std::string m_Title;
|
||||
|
||||
bool m_FramebufferResized = false;
|
||||
static void FramebufferResizeCallback(GLFWwindow* window, int width, int height);
|
||||
};
|
||||
|
||||
} // namespace Engine
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/21.
|
||||
//
|
||||
|
||||
#ifndef HELLOVULKAN_MATERIAL_H
|
||||
#define HELLOVULKAN_MATERIAL_H
|
||||
|
||||
#include <Engine/Core/Types.h>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
#include <string>
|
||||
|
||||
namespace Engine
|
||||
{
|
||||
class Device;
|
||||
class DescriptorManager;
|
||||
class Texture;
|
||||
|
||||
class Material
|
||||
{
|
||||
public:
|
||||
Material(Device& device,
|
||||
DescriptorManager& descriptorManager,
|
||||
vk::DescriptorSetLayout layout,
|
||||
Texture* texture);
|
||||
~Material();
|
||||
|
||||
Material(const Material&) = delete;
|
||||
Material& operator=(const Material&) = delete;
|
||||
|
||||
vk::DescriptorSet GetDescriptorSet() const { return m_DescriptorSet; }
|
||||
Texture* GetTexture() const { return m_Texture; }
|
||||
|
||||
private:
|
||||
Texture* m_Texture = nullptr;
|
||||
vk::DescriptorSet m_DescriptorSet;
|
||||
};
|
||||
} // namespace Engine
|
||||
|
||||
#endif //HELLOVULKAN_MATERIAL_H
|
||||
@@ -0,0 +1,49 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/21.
|
||||
//
|
||||
|
||||
#ifndef HELLOVULKAN_MESH_H
|
||||
#define HELLOVULKAN_MESH_H
|
||||
|
||||
|
||||
#include <Engine/Core/Types.h>
|
||||
#include <Engine/Renderer/Vertex.h>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace Engine
|
||||
{
|
||||
class Device;
|
||||
class MemoryAllocator;
|
||||
class Buffer;
|
||||
class Material;
|
||||
|
||||
class Mesh
|
||||
{
|
||||
public:
|
||||
Mesh(Device& device, MemoryAllocator& allocator,
|
||||
const std::vector<Vertex>& vertices,
|
||||
const std::vector<u32>& indices,
|
||||
Scope<Material> material);
|
||||
~Mesh();
|
||||
|
||||
Mesh(const Mesh&) = delete;
|
||||
Mesh& operator=(const Mesh&) = delete;
|
||||
|
||||
void Draw(vk::CommandBuffer cmd) const;
|
||||
|
||||
Material* GetMaterial() const { return m_Material.get(); }
|
||||
u32 GetIndexCount() const { return m_IndexCount; }
|
||||
|
||||
private:
|
||||
Scope<Buffer> m_VertexBuffer;
|
||||
Scope<Buffer> m_IndexBuffer;
|
||||
Scope<Material> m_Material;
|
||||
u32 m_IndexCount = 0;
|
||||
|
||||
u64 m_VertexBytes = 0;
|
||||
u64 m_IndexBytes = 0;
|
||||
};
|
||||
} // namespace Engine
|
||||
|
||||
#endif //HELLOVULKAN_MESH_H
|
||||
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/21.
|
||||
//
|
||||
|
||||
#ifndef HELLOVULKAN_MODEL_H
|
||||
#define HELLOVULKAN_MODEL_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Engine/Core/Types.h>
|
||||
#include <Engine/Renderer/Mesh.h>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
struct aiNode;
|
||||
struct aiMesh;
|
||||
struct aiScene;
|
||||
|
||||
namespace Engine
|
||||
{
|
||||
class Device;
|
||||
class MemoryAllocator;
|
||||
class DescriptorManager;
|
||||
class Texture;
|
||||
|
||||
class Model
|
||||
{
|
||||
public:
|
||||
Model(Device& device, MemoryAllocator& allocator,
|
||||
DescriptorManager& descriptorManager,
|
||||
vk::DescriptorSetLayout textureLayout,
|
||||
const std::string& path);
|
||||
~Model();
|
||||
|
||||
Model(const Model&) = delete;
|
||||
Model& operator=(const Model&) = delete;
|
||||
|
||||
const std::vector<Scope<Mesh>>& GetMeshes() const { return m_Meshes; }
|
||||
|
||||
private:
|
||||
void LoadModel(const std::string& path);
|
||||
void ProcessNode(aiNode* node, const aiScene* scene);
|
||||
Scope<Mesh> ProcessMesh(aiMesh* mesh, const aiScene* scene);
|
||||
|
||||
Texture* GetOrLoadTexture(const std::string& path);
|
||||
|
||||
Device& m_Device;
|
||||
MemoryAllocator& m_Allocator;
|
||||
DescriptorManager& m_DescriptorManager;
|
||||
vk::DescriptorSetLayout m_TextureLayout;
|
||||
|
||||
std::vector<Scope<Mesh>> m_Meshes;
|
||||
std::string m_ModelPath;
|
||||
|
||||
// 纹理缓存:同一路径只加载一次
|
||||
std::unordered_map<std::string, Scope<Texture>> m_TextureCache;
|
||||
Scope<Texture> m_DefaultTexture;
|
||||
};
|
||||
} // namespace Engine
|
||||
|
||||
|
||||
#endif //HELLOVULKAN_MODEL_H
|
||||
@@ -11,6 +11,11 @@
|
||||
#include <vulkan/vulkan.hpp>
|
||||
#include <chrono>
|
||||
|
||||
#include "Engine/Vulkan/Texture.h"
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "Model.h"
|
||||
|
||||
namespace Engine {
|
||||
|
||||
class Window;
|
||||
@@ -24,6 +29,11 @@ namespace Engine {
|
||||
class Buffer;
|
||||
class UniformBuffer;
|
||||
|
||||
struct Object {
|
||||
glm::mat4 transform = glm::mat4(1.0f);
|
||||
Model* model = nullptr;
|
||||
};
|
||||
|
||||
class Renderer {
|
||||
public:
|
||||
Renderer(Window& window);
|
||||
@@ -49,13 +59,16 @@ namespace Engine {
|
||||
Scope<SyncManager> m_SyncManager;
|
||||
Scope<CommandManager> m_CommandManager;
|
||||
|
||||
Scope<Buffer> m_VertexBuffer;
|
||||
Scope<Buffer> m_IndexBuffer;
|
||||
Scope<Model> m_Model;
|
||||
std::vector<Object> m_Objects;
|
||||
Scope<UniformBuffer> m_UniformBuffer;
|
||||
|
||||
vk::DescriptorSetLayout m_DescriptorSetLayout;
|
||||
vk::PipelineLayout m_PipelineLayout;
|
||||
vk::DescriptorSet m_DescriptorSet;
|
||||
|
||||
vk::DescriptorSetLayout m_UBOSetLayout;
|
||||
vk::DescriptorSet m_UBODescriptorSet;
|
||||
vk::DescriptorSetLayout m_TextureSetLayout;
|
||||
|
||||
|
||||
vk::ShaderEXT m_VertShader;
|
||||
vk::ShaderEXT m_FragShader;
|
||||
@@ -66,6 +79,16 @@ namespace Engine {
|
||||
static constexpr u32 kMaxFramesInFlight = 2;
|
||||
u32 m_CurrentFrame = 0;
|
||||
|
||||
|
||||
// 相机
|
||||
glm::vec3 m_CameraPos = glm::vec3(0.0f, 0.0f, 7.0f);
|
||||
f32 m_CameraYaw = -90.0f; // 朝向 -Z
|
||||
f32 m_CameraPitch = 0.0f;
|
||||
f32 m_CameraSpeed = 2.5f;
|
||||
f32 m_MouseSensitivity = 0.1f;
|
||||
f32 m_DeltaTime = 0.016f;
|
||||
f32 m_LastFrameTime = 0.0f;
|
||||
|
||||
std::chrono::steady_clock::time_point m_StartTime;
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/21.
|
||||
//
|
||||
|
||||
#ifndef HELLOVULKAN_VERTEX_H
|
||||
#define HELLOVULKAN_VERTEX_H
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
namespace Engine {
|
||||
|
||||
struct Vertex {
|
||||
glm::vec3 position;
|
||||
glm::vec3 normal;
|
||||
glm::vec2 uv;
|
||||
|
||||
static vk::VertexInputBindingDescription2EXT GetBindingDescription() {
|
||||
vk::VertexInputBindingDescription2EXT binding;
|
||||
binding.setBinding(0)
|
||||
.setStride(sizeof(Vertex))
|
||||
.setInputRate(vk::VertexInputRate::eVertex)
|
||||
.setDivisor(1);
|
||||
return binding;
|
||||
}
|
||||
|
||||
static std::array<vk::VertexInputAttributeDescription2EXT, 3> GetAttributeDescriptions() {
|
||||
std::array<vk::VertexInputAttributeDescription2EXT, 3> attrs;
|
||||
|
||||
attrs[0].setLocation(0)
|
||||
.setBinding(0)
|
||||
.setFormat(vk::Format::eR32G32B32Sfloat)
|
||||
.setOffset(offsetof(Vertex, position));
|
||||
|
||||
attrs[1].setLocation(1)
|
||||
.setBinding(0)
|
||||
.setFormat(vk::Format::eR32G32B32Sfloat)
|
||||
.setOffset(offsetof(Vertex, normal));
|
||||
|
||||
attrs[2].setLocation(2)
|
||||
.setBinding(0)
|
||||
.setFormat(vk::Format::eR32G32Sfloat)
|
||||
.setOffset(offsetof(Vertex, uv));
|
||||
|
||||
return attrs;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Engine
|
||||
|
||||
#endif //HELLOVULKAN_VERTEX_H
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
#include "MemoryAllocator.h"
|
||||
|
||||
namespace Engine {
|
||||
|
||||
class Device;
|
||||
@@ -39,6 +41,9 @@ namespace Engine {
|
||||
|
||||
vk::DeviceSize m_BlockSize; // 所属内存块的总大小
|
||||
|
||||
MemoryAllocator::Allocation m_Allocation;
|
||||
MemoryAllocator& m_Allocator;
|
||||
|
||||
void* m_MappedPtr = nullptr;
|
||||
};
|
||||
|
||||
|
||||
@@ -8,11 +8,18 @@
|
||||
|
||||
#include <Engine/Core/Types.h>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace Engine {
|
||||
|
||||
class Device;
|
||||
|
||||
struct DescriptorBinding {
|
||||
u32 Binding;
|
||||
vk::DescriptorType Type;
|
||||
vk::ShaderStageFlags Stages;
|
||||
};
|
||||
|
||||
class DescriptorManager {
|
||||
public:
|
||||
DescriptorManager(Device& device);
|
||||
@@ -21,18 +28,24 @@ namespace Engine {
|
||||
DescriptorManager(const DescriptorManager&) = delete;
|
||||
DescriptorManager& operator=(const DescriptorManager&) = delete;
|
||||
|
||||
// 创建一个描述符布局:一个 binding,类型是 Uniform Buffer
|
||||
vk::DescriptorSetLayout CreateUniformBufferLayout(u32 binding);
|
||||
vk::DescriptorSetLayout CreateLayout(const std::vector<DescriptorBinding>& bindings);
|
||||
|
||||
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);
|
||||
|
||||
void UpdateTexture(vk::DescriptorSet set, u32 binding,
|
||||
vk::Sampler sampler, vk::ImageView view);
|
||||
|
||||
// 单个 set layout 的旧接口(保留)
|
||||
vk::PipelineLayout CreatePipelineLayout(vk::DescriptorSetLayout layout,
|
||||
u32 pushConstantSize);
|
||||
|
||||
// 多 set layout 的新接口
|
||||
vk::PipelineLayout CreatePipelineLayout(
|
||||
const std::vector<vk::DescriptorSetLayout>& layouts,
|
||||
u32 pushConstantSize);
|
||||
|
||||
private:
|
||||
Device& m_Device;
|
||||
@@ -43,4 +56,5 @@ namespace Engine {
|
||||
|
||||
} // namespace Engine
|
||||
|
||||
|
||||
#endif //HELLOVULKAN_DESCRIPTORMANAGER_H
|
||||
|
||||
@@ -6,10 +6,14 @@
|
||||
#define HELLOVULKAN_MEMORYALLOCATOR_H
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan.hpp>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
#include "Engine/Core/Types.h"
|
||||
|
||||
namespace Engine {
|
||||
|
||||
class Device;
|
||||
@@ -26,23 +30,39 @@ namespace Engine {
|
||||
vk::DeviceMemory Memory;
|
||||
vk::DeviceSize Offset;
|
||||
vk::DeviceSize Size;
|
||||
void* MappedPtr; // HOST_VISIBLE 时才有
|
||||
void* MappedPtr = nullptr;
|
||||
|
||||
// 内部用于 Free
|
||||
u32 BlockIndex = 0;
|
||||
u32 TypeIndex = 0;
|
||||
bool Valid = false;
|
||||
};
|
||||
|
||||
Allocation Allocate(vk::MemoryRequirements reqs, vk::MemoryPropertyFlags props);
|
||||
void Free(const Allocation& alloc);
|
||||
|
||||
void PrintStats() const;
|
||||
|
||||
private:
|
||||
struct FreeRange {
|
||||
vk::DeviceSize Offset;
|
||||
vk::DeviceSize Size;
|
||||
};
|
||||
|
||||
struct Block {
|
||||
vk::DeviceMemory Memory;
|
||||
vk::DeviceSize Size;
|
||||
vk::DeviceSize Used;
|
||||
vk::DeviceSize Used = 0; // bump 分配用
|
||||
void* MappedPtr = nullptr;
|
||||
bool IsMapped = false;
|
||||
std::vector<FreeRange> FreeList; // 已释放的区域
|
||||
};
|
||||
|
||||
Device& m_Device;
|
||||
std::array<std::vector<Block>, VK_MAX_MEMORY_TYPES> m_Blocks;
|
||||
vk::DeviceSize m_DefaultBlockSize = 256ull * 1024 * 1024; // 256 MB
|
||||
std::array<vk::DeviceSize, VK_MAX_MEMORY_TYPES> m_TotalAllocated{};
|
||||
|
||||
vk::DeviceSize GetBlockSize(vk::MemoryPropertyFlags props) const;
|
||||
};
|
||||
|
||||
} // namespace Engine
|
||||
|
||||
@@ -30,14 +30,16 @@ namespace Engine {
|
||||
ShaderManager(const ShaderManager&) = delete;
|
||||
ShaderManager& operator=(const ShaderManager&) = delete;
|
||||
|
||||
// layout 可选:为 nullptr 时不使用描述符
|
||||
// 改成接受 vector
|
||||
ShaderObject CreateFromFile(const std::string& spvPath,
|
||||
vk::ShaderStageFlagBits stage,
|
||||
vk::DescriptorSetLayout layout = nullptr);
|
||||
const std::vector<vk::DescriptorSetLayout>& layouts = {},
|
||||
u32 pushConstantSize = 0);
|
||||
|
||||
ShaderObject CreateFromSPIRV(const std::vector<u32>& spirv,
|
||||
vk::ShaderStageFlagBits stage,
|
||||
vk::DescriptorSetLayout layout = nullptr);
|
||||
const std::vector<vk::DescriptorSetLayout>& layouts = {},
|
||||
u32 pushConstantSize = 0);
|
||||
|
||||
void Destroy(const ShaderObject& shader);
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/20.
|
||||
//
|
||||
|
||||
#ifndef HELLOVULKAN_TEXTURE_H
|
||||
#define HELLOVULKAN_TEXTURE_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Engine/Core/Types.h>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
#include <string>
|
||||
|
||||
#include "MemoryAllocator.h"
|
||||
|
||||
namespace Engine {
|
||||
|
||||
class Device;
|
||||
class MemoryAllocator;
|
||||
|
||||
class Texture {
|
||||
public:
|
||||
Texture(Device& device, MemoryAllocator& allocator, const std::string& path);
|
||||
~Texture();
|
||||
|
||||
Texture(const Texture&) = delete;
|
||||
Texture& operator=(const Texture&) = delete;
|
||||
|
||||
vk::Image Get() const { return m_Image; }
|
||||
vk::ImageView GetView() const { return m_View; }
|
||||
u32 GetWidth() const { return m_Width; }
|
||||
u32 GetHeight() const { return m_Height; }
|
||||
|
||||
vk::Sampler GetSampler() const { return m_Sampler; }
|
||||
|
||||
private:
|
||||
Device& m_Device;
|
||||
vk::Image m_Image;
|
||||
vk::ImageView m_View;
|
||||
vk::DeviceMemory m_Memory;
|
||||
|
||||
vk::Sampler m_Sampler;
|
||||
|
||||
|
||||
u32 m_Width = 0;
|
||||
u32 m_Height = 0;
|
||||
|
||||
u64 m_TextureBytes = 0;
|
||||
|
||||
MemoryAllocator::Allocation m_ImageAllocation;
|
||||
MemoryAllocator& m_Allocator;
|
||||
};
|
||||
|
||||
} // namespace Engine
|
||||
|
||||
#endif //HELLOVULKAN_TEXTURE_H
|
||||
@@ -0,0 +1,324 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/21.
|
||||
//
|
||||
#include <Engine/Core/Stats.h>
|
||||
#include <Engine/Core/Log.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
namespace Engine
|
||||
{
|
||||
Stats& Stats::Get()
|
||||
{
|
||||
static Stats instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
// ============ 资源计数 ============
|
||||
|
||||
void Stats::AddTexture(u64 bytes)
|
||||
{
|
||||
std::lock_guard lock(m_Mutex);
|
||||
m_TextureCount++;
|
||||
m_TextureBytes += bytes;
|
||||
m_FrameAllocation += bytes;
|
||||
m_TotalAllocation += bytes;
|
||||
}
|
||||
|
||||
void Stats::RemoveTexture(u64 bytes)
|
||||
{
|
||||
std::lock_guard lock(m_Mutex);
|
||||
if (m_TextureCount > 0) m_TextureCount--;
|
||||
if (m_TextureBytes >= bytes) m_TextureBytes -= bytes;
|
||||
m_FrameDeallocation += bytes;
|
||||
m_TotalDeallocation += bytes;
|
||||
}
|
||||
|
||||
void Stats::AddMesh(u64 vertexBytes, u64 indexBytes)
|
||||
{
|
||||
std::lock_guard lock(m_Mutex);
|
||||
m_MeshCount++;
|
||||
u64 total = vertexBytes + indexBytes;
|
||||
m_MeshBytes += total;
|
||||
m_FrameAllocation += total;
|
||||
m_TotalAllocation += total;
|
||||
}
|
||||
|
||||
void Stats::RemoveMesh(u64 vertexBytes, u64 indexBytes)
|
||||
{
|
||||
std::lock_guard lock(m_Mutex);
|
||||
if (m_MeshCount > 0) m_MeshCount--;
|
||||
u64 total = vertexBytes + indexBytes;
|
||||
if (m_MeshBytes >= total) m_MeshBytes -= total;
|
||||
m_FrameDeallocation += total;
|
||||
m_TotalDeallocation += total;
|
||||
}
|
||||
|
||||
void Stats::AddBuffer(u64 bytes)
|
||||
{
|
||||
std::lock_guard lock(m_Mutex);
|
||||
m_BufferCount++;
|
||||
m_BufferBytes += bytes;
|
||||
m_FrameAllocation += bytes;
|
||||
m_TotalAllocation += bytes;
|
||||
}
|
||||
|
||||
void Stats::RemoveBuffer(u64 bytes)
|
||||
{
|
||||
std::lock_guard lock(m_Mutex);
|
||||
if (m_BufferCount > 0) m_BufferCount--;
|
||||
if (m_BufferBytes >= bytes) m_BufferBytes -= bytes;
|
||||
m_FrameDeallocation += bytes;
|
||||
m_TotalDeallocation += bytes;
|
||||
}
|
||||
|
||||
void Stats::AddMemoryBlock(u64 bytes)
|
||||
{
|
||||
std::lock_guard lock(m_Mutex);
|
||||
m_MemoryBlockBytes += bytes;
|
||||
}
|
||||
|
||||
void Stats::RemoveMemoryBlock(u64 bytes)
|
||||
{
|
||||
std::lock_guard lock(m_Mutex);
|
||||
if (m_MemoryBlockBytes >= bytes) m_MemoryBlockBytes -= bytes;
|
||||
}
|
||||
|
||||
void Stats::AddMemoryUsed(u64 bytes)
|
||||
{
|
||||
std::lock_guard lock(m_Mutex);
|
||||
m_MemoryUsedBytes += bytes;
|
||||
}
|
||||
|
||||
void Stats::RemoveMemoryUsed(u64 bytes)
|
||||
{
|
||||
std::lock_guard lock(m_Mutex);
|
||||
if (m_MemoryUsedBytes >= bytes) m_MemoryUsedBytes -= bytes;
|
||||
}
|
||||
|
||||
// ============ 每帧 ============
|
||||
|
||||
void Stats::BeginFrame()
|
||||
{
|
||||
std::lock_guard lock(m_Mutex);
|
||||
m_FrameAllocation = 0;
|
||||
m_FrameDeallocation = 0;
|
||||
}
|
||||
|
||||
void Stats::EndFrame()
|
||||
{
|
||||
}
|
||||
|
||||
void Stats::AddAllocation(u64 bytes)
|
||||
{
|
||||
std::lock_guard lock(m_Mutex);
|
||||
m_FrameAllocation += bytes;
|
||||
m_TotalAllocation += bytes;
|
||||
}
|
||||
|
||||
void Stats::AddDeallocation(u64 bytes)
|
||||
{
|
||||
std::lock_guard lock(m_Mutex);
|
||||
m_FrameDeallocation += bytes;
|
||||
m_TotalDeallocation += bytes;
|
||||
}
|
||||
|
||||
// ============ 进程内存采样 ============
|
||||
|
||||
Stats::ProcessMemory Stats::ReadProcessMemory() const
|
||||
{
|
||||
ProcessMemory result;
|
||||
|
||||
// ---- 1. 从 /proc/self/status 读 VmRSS 和 VmSize ----
|
||||
// 这是内核报告的权威 RSS。smaps_rollup 会漏掉某些 NVIDIA 显存映射,
|
||||
// 但 status 的 VmRSS 包含所有 VMA。
|
||||
{
|
||||
std::ifstream status("/proc/self/status");
|
||||
std::string line;
|
||||
while (std::getline(status, line))
|
||||
{
|
||||
if (line.rfind("VmRSS:", 0) == 0)
|
||||
{
|
||||
std::istringstream iss(line.substr(6));
|
||||
u64 kb = 0;
|
||||
iss >> kb;
|
||||
result.Rss = kb * 1024;
|
||||
}
|
||||
else if (line.rfind("VmSize:", 0) == 0)
|
||||
{
|
||||
std::istringstream iss(line.substr(7));
|
||||
u64 kb = 0;
|
||||
iss >> kb;
|
||||
result.VmSize = kb * 1024;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 2. 从 /proc/self/smaps_rollup 读 PSS 和分项 ----
|
||||
// 注意:Rss 不从这里读,用上面的。
|
||||
{
|
||||
std::ifstream smaps("/proc/self/smaps_rollup");
|
||||
std::string line;
|
||||
while (std::getline(smaps, line))
|
||||
{
|
||||
std::istringstream iss(line);
|
||||
std::string key;
|
||||
u64 value = 0;
|
||||
std::string unit;
|
||||
|
||||
iss >> key;
|
||||
if (key.empty()) continue;
|
||||
if (key.back() == ':') key.pop_back();
|
||||
|
||||
if (key == "Pss")
|
||||
{
|
||||
iss >> value >> unit;
|
||||
result.Pss = value * 1024;
|
||||
}
|
||||
else if (key == "Shared_Clean")
|
||||
{
|
||||
iss >> value >> unit;
|
||||
result.SharedClean = value * 1024;
|
||||
}
|
||||
else if (key == "Private_Dirty")
|
||||
{
|
||||
iss >> value >> unit;
|
||||
result.PrivateDirty = value * 1024;
|
||||
}
|
||||
else if (key == "Private_Clean")
|
||||
{
|
||||
iss >> value >> unit;
|
||||
result.PrivateClean = value * 1024;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Stats::SampleProcessMemory()
|
||||
{
|
||||
std::lock_guard lock(m_Mutex);
|
||||
m_ProcessMem = ReadProcessMemory();
|
||||
}
|
||||
|
||||
// ============ 打印 ============
|
||||
|
||||
static double MB(u64 bytes) { return bytes / (1024.0 * 1024.0); }
|
||||
|
||||
void Stats::Print() const
|
||||
{
|
||||
std::lock_guard lock(m_Mutex);
|
||||
|
||||
auto proc = ReadProcessMemory();
|
||||
|
||||
ENGINE_INFO("========== Resource Stats ==========");
|
||||
ENGINE_INFO("Textures: {:>4} ({:.2f} MB)", m_TextureCount, MB(m_TextureBytes));
|
||||
ENGINE_INFO("Meshes: {:>4} ({:.2f} MB)", m_MeshCount, MB(m_MeshBytes));
|
||||
ENGINE_INFO("Buffers: {:>4} ({:.2f} MB)", m_BufferCount, MB(m_BufferBytes));
|
||||
ENGINE_INFO("--- GPU MemoryAllocator ---");
|
||||
ENGINE_INFO("Allocated: {:.2f} MB", MB(m_MemoryBlockBytes));
|
||||
ENGINE_INFO("Used: {:.2f} MB", MB(m_MemoryUsedBytes));
|
||||
ENGINE_INFO("Wasted: {:.2f} MB", MB(m_MemoryBlockBytes - m_MemoryUsedBytes));
|
||||
ENGINE_INFO("--- Process Memory ---");
|
||||
ENGINE_INFO("VmSize: {:.2f} MB", MB(proc.VmSize));
|
||||
ENGINE_INFO("RSS (resident): {:.2f} MB", MB(proc.Rss));
|
||||
ENGINE_INFO("PSS: {:.2f} MB", MB(proc.Pss));
|
||||
ENGINE_INFO(" Shared Clean: {:.2f} MB", MB(proc.SharedClean));
|
||||
ENGINE_INFO(" Private Dirty: {:.2f} MB", MB(proc.PrivateDirty));
|
||||
ENGINE_INFO(" Private Clean: {:.2f} MB", MB(proc.PrivateClean));
|
||||
ENGINE_INFO("====================================");
|
||||
}
|
||||
|
||||
void Stats::PrintDetailed() const
|
||||
{
|
||||
std::lock_guard lock(m_Mutex);
|
||||
|
||||
auto proc = ReadProcessMemory();
|
||||
|
||||
ENGINE_INFO("==================== Detailed Memory Stats ====================");
|
||||
|
||||
ENGINE_INFO(" Resources");
|
||||
ENGINE_INFO(" Textures: {:>6} {:>10.2f} MB", m_TextureCount, MB(m_TextureBytes));
|
||||
ENGINE_INFO(" Meshes: {:>6} {:>10.2f} MB", m_MeshCount, MB(m_MeshBytes));
|
||||
ENGINE_INFO(" Buffers: {:>6} {:>10.2f} MB", m_BufferCount, MB(m_BufferBytes));
|
||||
ENGINE_INFO(" Total: {:>10.2f} MB",
|
||||
MB(m_TextureBytes + m_MeshBytes + m_BufferBytes));
|
||||
|
||||
ENGINE_INFO(" GPU MemoryAllocator");
|
||||
ENGINE_INFO(" Allocated: {:>10.2f} MB", MB(m_MemoryBlockBytes));
|
||||
ENGINE_INFO(" Used: {:>10.2f} MB", MB(m_MemoryUsedBytes));
|
||||
ENGINE_INFO(" Wasted: {:>10.2f} MB",
|
||||
MB(m_MemoryBlockBytes - m_MemoryUsedBytes));
|
||||
|
||||
ENGINE_INFO(" Allocation Churn");
|
||||
ENGINE_INFO(" This frame: +{:.2f} MB / -{:.2f} MB",
|
||||
MB(m_FrameAllocation), MB(m_FrameDeallocation));
|
||||
ENGINE_INFO(" Total: +{:.2f} MB / -{:.2f} MB",
|
||||
MB(m_TotalAllocation), MB(m_TotalDeallocation));
|
||||
|
||||
ENGINE_INFO(" Process Memory (/proc/self/status + smaps_rollup)");
|
||||
ENGINE_INFO(" VmSize: {:>10.2f} MB", MB(proc.VmSize));
|
||||
ENGINE_INFO(" RSS: {:>10.2f} MB", MB(proc.Rss));
|
||||
ENGINE_INFO(" PSS: {:>10.2f} MB", MB(proc.Pss));
|
||||
ENGINE_INFO(" Shared Clean: {:>10.2f} MB", MB(proc.SharedClean));
|
||||
ENGINE_INFO(" Private Dirty: {:>10.2f} MB", MB(proc.PrivateDirty));
|
||||
ENGINE_INFO(" Private Clean: {:>10.2f} MB", MB(proc.PrivateClean));
|
||||
|
||||
ENGINE_INFO(" Unaccounted (RSS - GPU Blocks)");
|
||||
u64 engineTotal = m_MemoryBlockBytes;
|
||||
if (proc.Rss > engineTotal)
|
||||
{
|
||||
ENGINE_INFO(" {:.2f} MB (Vulkan driver, libs, stack, heap, ...)",
|
||||
MB(proc.Rss - engineTotal));
|
||||
}
|
||||
else
|
||||
{
|
||||
ENGINE_INFO(" (GPU blocks are the bulk of RSS)");
|
||||
}
|
||||
|
||||
ENGINE_INFO("===============================================================");
|
||||
}
|
||||
|
||||
void Stats::PrintComparison() const
|
||||
{
|
||||
std::lock_guard lock(m_Mutex);
|
||||
|
||||
auto proc = ReadProcessMemory();
|
||||
|
||||
u64 resourceTotal = m_TextureBytes + m_MeshBytes + m_BufferBytes;
|
||||
|
||||
ENGINE_INFO("=============== Memory Comparison ===============");
|
||||
ENGINE_INFO("");
|
||||
ENGINE_INFO("[Engine 自己统计]");
|
||||
ENGINE_INFO(" Textures {:>10.2f} MB", MB(m_TextureBytes));
|
||||
ENGINE_INFO(" Meshes {:>10.2f} MB", MB(m_MeshBytes));
|
||||
ENGINE_INFO(" Buffers {:>10.2f} MB", MB(m_BufferBytes));
|
||||
ENGINE_INFO(" ─────────────────────────────────────");
|
||||
ENGINE_INFO(" 资源合计 {:>10.2f} MB", MB(resourceTotal));
|
||||
ENGINE_INFO("");
|
||||
ENGINE_INFO(" Allocator Used {:>10.2f} MB", MB(m_MemoryUsedBytes));
|
||||
ENGINE_INFO(" Allocator Allocated {:>10.2f} MB", MB(m_MemoryBlockBytes));
|
||||
ENGINE_INFO(" Allocator Wasted {:>10.2f} MB",
|
||||
MB(m_MemoryBlockBytes - m_MemoryUsedBytes));
|
||||
ENGINE_INFO("");
|
||||
ENGINE_INFO("[系统 /proc 读出的]");
|
||||
ENGINE_INFO(" VmRSS {:>10.2f} MB", MB(proc.Rss));
|
||||
ENGINE_INFO(" VmSize {:>10.2f} MB", MB(proc.VmSize));
|
||||
ENGINE_INFO(" PSS {:>10.2f} MB", MB(proc.Pss));
|
||||
ENGINE_INFO(" Shared Clean {:>10.2f} MB", MB(proc.SharedClean));
|
||||
ENGINE_INFO(" Private Dirty {:>10.2f} MB", MB(proc.PrivateDirty));
|
||||
ENGINE_INFO(" Private Clean {:>10.2f} MB", MB(proc.PrivateClean));
|
||||
ENGINE_INFO("");
|
||||
ENGINE_INFO("[差值]");
|
||||
ENGINE_INFO(" RSS - Allocator {:>10.2f} MB (Vulkan 驱动 + libs + 栈堆)",
|
||||
MB(proc.Rss > m_MemoryBlockBytes ? proc.Rss - m_MemoryBlockBytes : 0));
|
||||
ENGINE_INFO(" RSS - 资源合计 {:>10.2f} MB (含分配器碎片)",
|
||||
MB(proc.Rss > resourceTotal ? proc.Rss - resourceTotal : 0));
|
||||
ENGINE_INFO(" Allocated - Used {:>10.2f} MB (分配器空闲块)",
|
||||
MB(m_MemoryBlockBytes - m_MemoryUsedBytes));
|
||||
ENGINE_INFO("================================================");
|
||||
}
|
||||
} // namespace Engine
|
||||
@@ -8,32 +8,46 @@
|
||||
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
namespace Engine {
|
||||
namespace Engine
|
||||
{
|
||||
void Window::FramebufferResizeCallback(GLFWwindow* window, int width, int height)
|
||||
{
|
||||
auto* self = static_cast<Window*>(glfwGetWindowUserPointer(window));
|
||||
self->m_FramebufferResized = true;
|
||||
}
|
||||
|
||||
Window::Window(const std::string& title, u32 width, u32 height)
|
||||
: m_Width(width), m_Height(height), m_Title(title)
|
||||
{
|
||||
if (!glfwInit()) {
|
||||
if (!glfwInit())
|
||||
{
|
||||
ENGINE_FATAL("glfwInit failed");
|
||||
return;
|
||||
}
|
||||
|
||||
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
||||
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
|
||||
// glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
|
||||
|
||||
m_Window = glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr);
|
||||
if (!m_Window) {
|
||||
if (!m_Window)
|
||||
{
|
||||
ENGINE_FATAL("glfwCreateWindow failed");
|
||||
glfwTerminate();
|
||||
return;
|
||||
}
|
||||
|
||||
ENGINE_INFO("Window created: {} ({}x{})", title, width, height);
|
||||
|
||||
// callbacks
|
||||
|
||||
glfwSetWindowUserPointer(m_Window, this);
|
||||
glfwSetFramebufferSizeCallback(m_Window, FramebufferResizeCallback);
|
||||
}
|
||||
|
||||
Window::~Window()
|
||||
{
|
||||
if (m_Window) {
|
||||
if (m_Window)
|
||||
{
|
||||
glfwDestroyWindow(m_Window);
|
||||
}
|
||||
glfwTerminate();
|
||||
@@ -52,7 +66,8 @@ namespace Engine {
|
||||
vk::SurfaceKHR Window::CreateSurface(vk::Instance instance)
|
||||
{
|
||||
VkSurfaceKHR surface;
|
||||
if (glfwCreateWindowSurface(instance, m_Window, nullptr, &surface) != VK_SUCCESS) {
|
||||
if (glfwCreateWindowSurface(instance, m_Window, nullptr, &surface) != VK_SUCCESS)
|
||||
{
|
||||
ENGINE_FATAL("glfwCreateWindowSurface failed");
|
||||
return nullptr;
|
||||
}
|
||||
@@ -71,4 +86,38 @@ namespace Engine {
|
||||
return std::vector<const char*>(extensions, extensions + count);
|
||||
}
|
||||
|
||||
void Window::GetFramebufferSize(int& width, int& height) const
|
||||
{
|
||||
glfwGetFramebufferSize(m_Window, &width, &height);
|
||||
}
|
||||
|
||||
bool Window::IsKeyPressed(int key) const
|
||||
{
|
||||
return glfwGetKey(m_Window, key) == GLFW_PRESS;
|
||||
}
|
||||
|
||||
void Window::GetMouseDelta(float& dx, float& dy)
|
||||
{
|
||||
double mx, my;
|
||||
glfwGetCursorPos(m_Window, &mx, &my);
|
||||
|
||||
static double lastX = mx;
|
||||
static double lastY = my;
|
||||
static bool first = true;
|
||||
|
||||
if (first)
|
||||
{
|
||||
lastX = mx;
|
||||
lastY = my;
|
||||
first = false;
|
||||
dx = dy = 0.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
dx = static_cast<float>(mx - lastX);
|
||||
dy = static_cast<float>(my - lastY);
|
||||
|
||||
lastX = mx;
|
||||
lastY = my;
|
||||
}
|
||||
} // namespace Engine
|
||||
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/21.
|
||||
//
|
||||
|
||||
#include <Engine/Renderer/Material.h>
|
||||
#include <Engine/Vulkan/Device.h>
|
||||
#include <Engine/Vulkan/MemoryAllocator.h>
|
||||
#include <Engine/Vulkan/DescriptorManager.h>
|
||||
#include <Engine/Vulkan/Texture.h>
|
||||
#include <Engine/Core/Log.h>
|
||||
|
||||
namespace Engine
|
||||
{
|
||||
Material::Material(Device& device,
|
||||
DescriptorManager& descriptorManager,
|
||||
vk::DescriptorSetLayout layout,
|
||||
Texture* texture)
|
||||
: m_Texture(texture)
|
||||
{
|
||||
m_DescriptorSet = descriptorManager.AllocateDescriptorSet(layout);
|
||||
|
||||
descriptorManager.UpdateTexture(
|
||||
m_DescriptorSet, 0,
|
||||
m_Texture->GetSampler(),
|
||||
m_Texture->GetView());
|
||||
}
|
||||
|
||||
|
||||
Material::~Material() = default;
|
||||
} // namespace Engine
|
||||
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/21.
|
||||
//
|
||||
|
||||
#include <Engine/Renderer/Mesh.h>
|
||||
#include <Engine/Renderer/Material.h>
|
||||
#include <Engine/Vulkan/Device.h>
|
||||
#include <Engine/Vulkan/MemoryAllocator.h>
|
||||
#include <Engine/Vulkan/Buffer.h>
|
||||
#include <Engine/Core/Log.h>
|
||||
|
||||
#include "Engine/Core/Stats.h"
|
||||
|
||||
namespace Engine
|
||||
{
|
||||
Mesh::Mesh(Device& device, MemoryAllocator& allocator,
|
||||
const std::vector<Vertex>& vertices,
|
||||
const std::vector<u32>& indices,
|
||||
Scope<Material> material)
|
||||
: m_Material(std::move(material))
|
||||
{
|
||||
if (vertices.empty() || indices.empty())
|
||||
{
|
||||
ENGINE_ERROR("Mesh: empty vertices or indices");
|
||||
return;
|
||||
}
|
||||
|
||||
u64 vbSize = sizeof(Vertex) * vertices.size();
|
||||
m_VertexBuffer = CreateScope<Buffer>(
|
||||
device, allocator, vbSize,
|
||||
vk::BufferUsageFlagBits::eVertexBuffer,
|
||||
vk::MemoryPropertyFlagBits::eHostVisible |
|
||||
vk::MemoryPropertyFlagBits::eHostCoherent);
|
||||
m_VertexBuffer->Upload(vertices.data(), vbSize);
|
||||
|
||||
u64 ibSize = sizeof(u32) * indices.size();
|
||||
m_IndexBuffer = CreateScope<Buffer>(
|
||||
device, allocator, ibSize,
|
||||
vk::BufferUsageFlagBits::eIndexBuffer,
|
||||
vk::MemoryPropertyFlagBits::eHostVisible |
|
||||
vk::MemoryPropertyFlagBits::eHostCoherent);
|
||||
m_IndexBuffer->Upload(indices.data(), ibSize);
|
||||
|
||||
m_IndexCount = static_cast<u32>(indices.size());
|
||||
|
||||
ENGINE_INFO("Mesh created: {} vertices, {} indices", vertices.size(), indices.size());
|
||||
|
||||
m_VertexBytes = sizeof(Vertex) * vertices.size();
|
||||
m_IndexBytes = sizeof(u32) * indices.size();
|
||||
STATS.AddMesh(m_VertexBytes, m_IndexBytes);
|
||||
}
|
||||
|
||||
Mesh::~Mesh()
|
||||
{
|
||||
STATS.RemoveMesh(m_VertexBytes, m_IndexBytes);
|
||||
}
|
||||
|
||||
void Mesh::Draw(vk::CommandBuffer cmd) const
|
||||
{
|
||||
cmd.bindVertexBuffers(0, m_VertexBuffer->Get(), {0});
|
||||
cmd.bindIndexBuffer(m_IndexBuffer->Get(), 0, vk::IndexType::eUint32);
|
||||
cmd.drawIndexed(m_IndexCount, 1, 0, 0, 0);
|
||||
}
|
||||
} // namespace Engine
|
||||
@@ -0,0 +1,157 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/21.
|
||||
//
|
||||
|
||||
#include <Engine/Renderer/Model.h>
|
||||
#include <Engine/Renderer/Material.h>
|
||||
#include <Engine/Vulkan/Device.h>
|
||||
#include <Engine/Vulkan/MemoryAllocator.h>
|
||||
#include <Engine/Vulkan/DescriptorManager.h>
|
||||
#include <Engine/Vulkan/Texture.h>
|
||||
#include <Engine/Core/Log.h>
|
||||
|
||||
#include <assimp/Importer.hpp>
|
||||
#include <assimp/scene.h>
|
||||
#include <assimp/postprocess.h>
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
namespace Engine {
|
||||
|
||||
Model::Model(Device& device, MemoryAllocator& allocator,
|
||||
DescriptorManager& descriptorManager,
|
||||
vk::DescriptorSetLayout textureLayout,
|
||||
const std::string& path)
|
||||
: m_Device(device)
|
||||
, m_Allocator(allocator)
|
||||
, m_DescriptorManager(descriptorManager)
|
||||
, m_TextureLayout(textureLayout)
|
||||
{
|
||||
// 兜底纹理
|
||||
m_DefaultTexture = CreateScope<Texture>(
|
||||
m_Device, m_Allocator, "assets/textures/default.tga");
|
||||
|
||||
LoadModel(path);
|
||||
}
|
||||
|
||||
Model::~Model() = default;
|
||||
|
||||
void Model::LoadModel(const std::string& path)
|
||||
{
|
||||
m_ModelPath = path;
|
||||
|
||||
Assimp::Importer importer;
|
||||
const aiScene* scene = importer.ReadFile(path,
|
||||
aiProcess_Triangulate |
|
||||
aiProcess_FlipUVs |
|
||||
aiProcess_GenSmoothNormals |
|
||||
aiProcess_JoinIdenticalVertices);
|
||||
|
||||
if (!scene || (scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE) || !scene->mRootNode) {
|
||||
ENGINE_ERROR("Assimp error: {}", importer.GetErrorString());
|
||||
return;
|
||||
}
|
||||
|
||||
ProcessNode(scene->mRootNode, scene);
|
||||
ENGINE_INFO("Model loaded: {} ({} meshes, {} textures)",
|
||||
path, m_Meshes.size(), m_TextureCache.size());
|
||||
}
|
||||
|
||||
void Model::ProcessNode(aiNode* node, const aiScene* scene)
|
||||
{
|
||||
for (u32 i = 0; i < node->mNumMeshes; ++i) {
|
||||
aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
|
||||
m_Meshes.push_back(ProcessMesh(mesh, scene));
|
||||
}
|
||||
for (u32 i = 0; i < node->mNumChildren; ++i) {
|
||||
ProcessNode(node->mChildren[i], scene);
|
||||
}
|
||||
}
|
||||
|
||||
Texture* Model::GetOrLoadTexture(const std::string& path)
|
||||
{
|
||||
auto it = m_TextureCache.find(path);
|
||||
if (it != m_TextureCache.end()) {
|
||||
return it->second.get();
|
||||
}
|
||||
|
||||
// 文件不存在则用兜底纹理
|
||||
if (!std::filesystem::exists(path)) {
|
||||
ENGINE_WARN("Texture not found, using default: {}", path);
|
||||
return m_DefaultTexture.get();
|
||||
}
|
||||
|
||||
auto tex = CreateScope<Texture>(m_Device, m_Allocator, path);
|
||||
Texture* ptr = tex.get();
|
||||
m_TextureCache.emplace(path, std::move(tex));
|
||||
return ptr;
|
||||
}
|
||||
|
||||
Scope<Mesh> Model::ProcessMesh(aiMesh* mesh, const aiScene* scene)
|
||||
{
|
||||
std::vector<Vertex> vertices;
|
||||
std::vector<u32> indices;
|
||||
|
||||
vertices.reserve(mesh->mNumVertices);
|
||||
|
||||
for (u32 i = 0; i < mesh->mNumVertices; ++i) {
|
||||
Vertex vertex{};
|
||||
|
||||
vertex.position = {
|
||||
mesh->mVertices[i].x,
|
||||
mesh->mVertices[i].y,
|
||||
mesh->mVertices[i].z
|
||||
};
|
||||
|
||||
if (mesh->HasNormals()) {
|
||||
vertex.normal = {
|
||||
mesh->mNormals[i].x,
|
||||
mesh->mNormals[i].y,
|
||||
mesh->mNormals[i].z
|
||||
};
|
||||
}
|
||||
|
||||
if (mesh->mTextureCoords[0]) {
|
||||
vertex.uv = {
|
||||
mesh->mTextureCoords[0][i].x,
|
||||
mesh->mTextureCoords[0][i].y
|
||||
};
|
||||
}
|
||||
|
||||
vertices.push_back(vertex);
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < mesh->mNumFaces; ++i) {
|
||||
aiFace face = mesh->mFaces[i];
|
||||
for (u32 j = 0; j < face.mNumIndices; ++j) {
|
||||
indices.push_back(face.mIndices[j]);
|
||||
}
|
||||
}
|
||||
|
||||
// 读取材质纹理路径
|
||||
std::string texturePath;
|
||||
|
||||
if (mesh->mMaterialIndex >= 0) {
|
||||
aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];
|
||||
|
||||
aiString path;
|
||||
if (material->GetTexture(aiTextureType_DIFFUSE, 0, &path) == AI_SUCCESS) {
|
||||
std::filesystem::path modelDir =
|
||||
std::filesystem::path(m_ModelPath).parent_path();
|
||||
texturePath = (modelDir / path.C_Str()).string();
|
||||
}
|
||||
}
|
||||
|
||||
// 从缓存拿,没有才加载
|
||||
Texture* tex = texturePath.empty()
|
||||
? m_DefaultTexture.get()
|
||||
: GetOrLoadTexture(texturePath);
|
||||
|
||||
auto mat = CreateScope<Material>(
|
||||
m_Device, m_DescriptorManager, m_TextureLayout, tex);
|
||||
|
||||
return CreateScope<Mesh>(
|
||||
m_Device, m_Allocator, vertices, indices, std::move(mat));
|
||||
}
|
||||
|
||||
} // namespace Engine
|
||||
+176
-108
@@ -3,6 +3,7 @@
|
||||
//
|
||||
|
||||
#include <Engine/Renderer/Renderer.h>
|
||||
#include <Engine/Renderer/Material.h>
|
||||
#include <Engine/Vulkan/Device.h>
|
||||
#include <Engine/Vulkan/MemoryAllocator.h>
|
||||
#include <Engine/Vulkan/DescriptorManager.h>
|
||||
@@ -10,7 +11,6 @@
|
||||
#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>
|
||||
@@ -22,21 +22,25 @@
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
#include "Engine/Core/Stats.h"
|
||||
#include "GLFW/glfw3.h"
|
||||
|
||||
namespace Engine
|
||||
{
|
||||
// 顶点结构:位置 + 颜色
|
||||
struct Vertex
|
||||
{
|
||||
float position[3];
|
||||
float color[3];
|
||||
|
||||
struct Light {
|
||||
glm::vec4 direction;
|
||||
glm::vec4 color; // rgb = 颜色,a = 强度
|
||||
};
|
||||
|
||||
// Uniform Buffer 结构
|
||||
struct UniformBufferObject
|
||||
{
|
||||
glm::mat4 mvp;
|
||||
struct UniformBufferObject {
|
||||
glm::mat4 view;
|
||||
glm::mat4 proj;
|
||||
glm::vec4 ambientColor;
|
||||
Light lights[4];
|
||||
};
|
||||
|
||||
|
||||
Renderer::Renderer(Window& window)
|
||||
: m_Window(window)
|
||||
{
|
||||
@@ -49,24 +53,37 @@ namespace Engine
|
||||
// 3. DescriptorManager
|
||||
m_DescriptorManager = CreateScope<DescriptorManager>(*m_Device);
|
||||
|
||||
// 4. DescriptorSetLayout(一个 uniform buffer binding)
|
||||
m_DescriptorSetLayout = m_DescriptorManager->CreateUniformBufferLayout(0);
|
||||
// 4. Set 0: UBO
|
||||
std::vector<DescriptorBinding> uboBindings = {
|
||||
{0, vk::DescriptorType::eUniformBuffer, vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment },
|
||||
};
|
||||
m_UBOSetLayout = m_DescriptorManager->CreateLayout(uboBindings);
|
||||
|
||||
// 5. PipelineLayout(shader 绑定描述符时用)
|
||||
m_PipelineLayout = m_DescriptorManager->CreatePipelineLayout(m_DescriptorSetLayout);
|
||||
// 5. Set 1: texture
|
||||
std::vector<DescriptorBinding> texBindings = {
|
||||
{0, vk::DescriptorType::eCombinedImageSampler, vk::ShaderStageFlagBits::eFragment},
|
||||
};
|
||||
m_TextureSetLayout = m_DescriptorManager->CreateLayout(texBindings);
|
||||
|
||||
// 6. ShaderManager,编译 shader 时传入描述符布局
|
||||
// 6. PipelineLayout:两个 set
|
||||
std::vector<vk::DescriptorSetLayout> layouts = {
|
||||
m_UBOSetLayout, m_TextureSetLayout
|
||||
};
|
||||
m_PipelineLayout = m_DescriptorManager->CreatePipelineLayout(
|
||||
layouts, sizeof(glm::mat4));
|
||||
|
||||
// 7. ShaderManager
|
||||
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);
|
||||
layouts, sizeof(glm::mat4));
|
||||
auto frag = m_ShaderManager->CreateFromFile(
|
||||
shaderDir + "simple_shader.frag.spv",
|
||||
vk::ShaderStageFlagBits::eFragment,
|
||||
m_DescriptorSetLayout);
|
||||
layouts, sizeof(glm::mat4));
|
||||
|
||||
m_VertShader = vert.Handle;
|
||||
m_FragShader = frag.Handle;
|
||||
@@ -76,96 +93,46 @@ namespace Engine
|
||||
ENGINE_FATAL("Failed to create shader objects");
|
||||
}
|
||||
|
||||
// 7. Swapchain
|
||||
// 8. Swapchain
|
||||
m_Swapchain = CreateScope<Swapchain>(*m_Device, *m_Allocator, window);
|
||||
m_Extent = m_Swapchain->GetExtent();
|
||||
m_Format = m_Swapchain->GetFormat();
|
||||
|
||||
// 8. SyncManager
|
||||
// 9. SyncManager
|
||||
m_SyncManager = CreateScope<SyncManager>(*m_Device, kMaxFramesInFlight, m_Swapchain->GetImageCount());
|
||||
|
||||
// 9. CommandManager
|
||||
// 10. 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);
|
||||
// 12. UBO DescriptorSet
|
||||
m_UBODescriptorSet = m_DescriptorManager->AllocateDescriptorSet(m_UBOSetLayout);
|
||||
m_DescriptorManager->UpdateUniformBuffer(
|
||||
m_DescriptorSet, 0, m_UniformBuffer->Get(), m_UniformBuffer->GetSize());
|
||||
m_UBODescriptorSet, 0, m_UniformBuffer->Get(), m_UniformBuffer->GetSize());
|
||||
|
||||
// 13. Model(模型自己加载纹理)
|
||||
m_Model = CreateScope<Model>(
|
||||
*m_Device, *m_Allocator, *m_DescriptorManager,
|
||||
m_TextureSetLayout,
|
||||
"assets/models/backpack/backpack.obj");
|
||||
|
||||
// 14. 创建几个物体,摆一排
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
Object obj;
|
||||
obj.model = m_Model.get();
|
||||
obj.transform = glm::translate(
|
||||
glm::mat4(1.0f),
|
||||
glm::vec3((i - 1) * 1.5f, 0.0f, 0.0f));
|
||||
m_Objects.push_back(obj);
|
||||
}
|
||||
|
||||
// 13. Debug: 记录开始时间,用于旋转
|
||||
m_StartTime = std::chrono::steady_clock::now();
|
||||
|
||||
ENGINE_INFO("Renderer initialized");
|
||||
|
||||
}
|
||||
|
||||
Renderer::~Renderer()
|
||||
@@ -182,36 +149,110 @@ namespace Engine
|
||||
}
|
||||
|
||||
m_Device->GetDevice().destroyPipelineLayout(m_PipelineLayout);
|
||||
m_Device->GetDevice().destroyDescriptorSetLayout(m_DescriptorSetLayout);
|
||||
|
||||
m_Device->GetDevice().destroyDescriptorSetLayout(m_UBOSetLayout);
|
||||
m_Device->GetDevice().destroyDescriptorSetLayout(m_TextureSetLayout);
|
||||
}
|
||||
|
||||
|
||||
void Renderer::Render()
|
||||
{
|
||||
|
||||
if (m_Window.WasResized())
|
||||
{
|
||||
m_Window.ResetResizedFlag();
|
||||
|
||||
m_Device->GetDevice().waitIdle();
|
||||
|
||||
m_Swapchain->Recreate(m_Window);
|
||||
m_Extent = m_Swapchain->GetExtent();
|
||||
m_SyncManager = CreateScope<SyncManager>(
|
||||
*m_Device, kMaxFramesInFlight, m_Swapchain->GetImageCount());
|
||||
}
|
||||
|
||||
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 now = std::chrono::steady_clock::now();
|
||||
float currentTime = std::chrono::duration<float>(now - m_StartTime).count();
|
||||
m_DeltaTime = currentTime - m_LastFrameTime;
|
||||
m_LastFrameTime = currentTime;
|
||||
|
||||
auto model = glm::rotate(glm::mat4(1.0f),
|
||||
time * glm::radians(90.0f),
|
||||
glm::vec3(1.0f, 1.0f, 1.0f));
|
||||
// 更新每个物体的 transform
|
||||
for (size_t i = 0; i < m_Objects.size(); ++i) {
|
||||
float angle = currentTime * glm::radians(60.0f) + i * 0.7f;
|
||||
|
||||
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));
|
||||
glm::mat4 t = glm::translate(
|
||||
glm::mat4(1.0f),
|
||||
glm::vec3((static_cast<float>(i) - 1.0f) * 1.5f, 0.0f, 0.0f));
|
||||
glm::mat4 r = glm::rotate(glm::mat4(1.0f), angle, glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
|
||||
float aspect = static_cast<float>(m_Extent.width) /
|
||||
static_cast<float>(m_Extent.height);
|
||||
m_Objects[i].transform = t * r;
|
||||
}
|
||||
|
||||
auto proj = glm::perspective(glm::radians(45.0f), aspect, 0.1f, 10.0f);
|
||||
// 鼠标控制方向
|
||||
/*
|
||||
float mouseDX = 0, mouseDY = 0;
|
||||
m_Window.GetMouseDelta(mouseDX, mouseDY);
|
||||
if (mouseDX != 0 || mouseDY != 0)
|
||||
{
|
||||
m_CameraYaw += mouseDX * m_MouseSensitivity;
|
||||
m_CameraPitch -= mouseDY * m_MouseSensitivity;
|
||||
m_CameraPitch = glm::clamp(m_CameraPitch, -89.0f, 89.0f);
|
||||
}
|
||||
*/
|
||||
|
||||
// 相机前方向量
|
||||
glm::vec3 forward;
|
||||
forward.x = cosf(glm::radians(m_CameraYaw)) * cos(glm::radians(m_CameraPitch));
|
||||
forward.y = sinf(glm::radians(m_CameraPitch));
|
||||
forward.z = sinf(glm::radians(m_CameraYaw)) * cos(glm::radians(m_CameraPitch));
|
||||
forward = glm::normalize(forward);
|
||||
|
||||
glm::vec3 right = glm::normalize(glm::cross(forward, glm::vec3(0, 1, 0)));
|
||||
glm::vec3 up = glm::normalize(glm::cross(right, forward));
|
||||
|
||||
// WASD 移动
|
||||
float speed = m_CameraSpeed * m_DeltaTime;
|
||||
if (m_Window.IsKeyPressed(GLFW_KEY_W)) m_CameraPos += forward * speed;
|
||||
if (m_Window.IsKeyPressed(GLFW_KEY_S)) m_CameraPos -= forward * speed;
|
||||
if (m_Window.IsKeyPressed(GLFW_KEY_A)) m_CameraPos -= right * speed;
|
||||
if (m_Window.IsKeyPressed(GLFW_KEY_D)) m_CameraPos += right * speed;
|
||||
if (m_Window.IsKeyPressed(GLFW_KEY_SPACE)) m_CameraPos.y += speed;
|
||||
if (m_Window.IsKeyPressed(GLFW_KEY_LEFT_SHIFT)) m_CameraPos.y -= speed;
|
||||
|
||||
|
||||
auto view = glm::lookAt(m_CameraPos,
|
||||
m_CameraPos + forward,
|
||||
up);
|
||||
|
||||
float aspect = static_cast<f32>(m_Extent.width) /
|
||||
static_cast<f32>(m_Extent.height);
|
||||
|
||||
auto proj = glm::perspective(glm::radians(45.0f), aspect, 0.1f, 100.0f);
|
||||
proj[1][1] *= -1.0f; // Vulkan Y 翻转
|
||||
|
||||
UniformBufferObject ubo{};
|
||||
ubo.mvp = proj * view * model;
|
||||
ubo.view = view;
|
||||
ubo.proj = proj;
|
||||
|
||||
ubo.ambientColor = glm::vec4(0.15f, 0.15f, 0.2f, 0.0f);
|
||||
|
||||
// 主光:暖色,从左上方照下
|
||||
ubo.lights[0].direction = glm::vec4(glm::normalize(glm::vec3(-0.5f, -1.0f, -0.3f)), 0.0f);
|
||||
ubo.lights[0].color = glm::vec4(1.0f, 0.95f, 0.9f, 1.0f);
|
||||
|
||||
// 补光:冷色,从右下方照上
|
||||
ubo.lights[1].direction = glm::vec4(glm::normalize(glm::vec3(0.5f, -0.5f, 0.3f)), 0.0f);
|
||||
ubo.lights[1].color = glm::vec4(0.3f, 0.5f, 1.0f, 0.8f);
|
||||
|
||||
// 未使用:强度 0
|
||||
ubo.lights[2].direction = glm::vec4(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
ubo.lights[2].color = glm::vec4(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
|
||||
ubo.lights[3].direction = glm::vec4(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
ubo.lights[3].color = glm::vec4(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
|
||||
m_UniformBuffer->Update(&ubo, sizeof(ubo));
|
||||
|
||||
@@ -318,6 +359,9 @@ namespace Engine
|
||||
{
|
||||
m_Swapchain->Recreate(m_Window);
|
||||
}
|
||||
|
||||
if (m_Window.IsKeyPressed(GLFW_KEY_F1))
|
||||
STATS.PrintComparison();
|
||||
}
|
||||
|
||||
void Renderer::RecordCommands(vk::CommandBuffer cmd, u32 imageIndex)
|
||||
@@ -359,8 +403,8 @@ namespace Engine
|
||||
|
||||
// 绑定描述符集
|
||||
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
|
||||
m_PipelineLayout, 0,
|
||||
m_DescriptorSet, {});
|
||||
m_PipelineLayout, 0,
|
||||
m_UBODescriptorSet, {});
|
||||
|
||||
// ===== 动态状态 =====
|
||||
cmd.setRasterizerDiscardEnable(VK_FALSE);
|
||||
@@ -379,22 +423,30 @@ namespace Engine
|
||||
cmd.setPrimitiveRestartEnable(VK_FALSE);
|
||||
|
||||
// 顶点输入
|
||||
/*
|
||||
vk::VertexInputBindingDescription2EXT binding;
|
||||
binding.setBinding(0)
|
||||
.setStride(sizeof(f32) * 6)
|
||||
.setStride(sizeof(f32) * 5)
|
||||
.setInputRate(vk::VertexInputRate::eVertex)
|
||||
.setDivisor(1);
|
||||
|
||||
std::array<vk::VertexInputAttributeDescription2EXT, 2> attributes;
|
||||
|
||||
// location 0: position
|
||||
attributes[0].setLocation(0)
|
||||
.setBinding(0)
|
||||
.setFormat(vk::Format::eR32G32B32Sfloat)
|
||||
.setOffset(0);
|
||||
|
||||
// location 1: uv
|
||||
attributes[1].setLocation(1)
|
||||
.setBinding(0)
|
||||
.setFormat(vk::Format::eR32G32B32Sfloat)
|
||||
.setFormat(vk::Format::eR32G32Sfloat)
|
||||
.setOffset(sizeof(f32) * 3);
|
||||
*/
|
||||
|
||||
auto binding = Vertex::GetBindingDescription();
|
||||
auto attributes = Vertex::GetAttributeDescriptions();
|
||||
cmd.setVertexInputEXT(binding, attributes);
|
||||
|
||||
cmd.setRasterizationSamplesEXT(vk::SampleCountFlagBits::e1);
|
||||
@@ -431,11 +483,27 @@ namespace Engine
|
||||
vk::ColorComponentFlagBits::eA;
|
||||
cmd.setColorWriteMaskEXT(0, colorWriteMask);
|
||||
|
||||
|
||||
// 绑定顶点缓冲并绘制
|
||||
for (auto& obj : m_Objects) {
|
||||
cmd.pushConstants(m_PipelineLayout,
|
||||
vk::ShaderStageFlagBits::eVertex,
|
||||
0, sizeof(glm::mat4), &obj.transform);
|
||||
|
||||
for (auto& mesh : obj.model->GetMeshes()) {
|
||||
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
|
||||
m_PipelineLayout, 1,
|
||||
mesh->GetMaterial()->GetDescriptorSet(), {});
|
||||
mesh->Draw(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -9,12 +9,14 @@
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "Engine/Core/Stats.h"
|
||||
|
||||
namespace Engine {
|
||||
|
||||
Buffer::Buffer(Device& device, MemoryAllocator& allocator,
|
||||
vk::DeviceSize size, vk::BufferUsageFlags usage,
|
||||
vk::MemoryPropertyFlags memProps)
|
||||
: m_Device(device), m_Size(size)
|
||||
: m_Device(device), m_Allocator(allocator) , m_Size(size)
|
||||
{
|
||||
vk::BufferCreateInfo bufferInfo;
|
||||
bufferInfo.setSize(size)
|
||||
@@ -31,12 +33,17 @@ namespace Engine {
|
||||
m_MappedPtr = alloc.MappedPtr;
|
||||
|
||||
m_Device.GetDevice().bindBufferMemory(m_Buffer, m_Memory, m_Offset);
|
||||
}
|
||||
|
||||
m_Allocation = alloc; // 成员变量,保存起来
|
||||
|
||||
STATS.AddBuffer(m_Size);
|
||||
}
|
||||
|
||||
Buffer::~Buffer()
|
||||
{
|
||||
m_Device.GetDevice().destroyBuffer(m_Buffer);
|
||||
m_Allocator.Free(m_Allocation);
|
||||
STATS.RemoveBuffer(m_Size);
|
||||
}
|
||||
|
||||
void Buffer::Upload(const void* data, vk::DeviceSize size, vk::DeviceSize offset)
|
||||
|
||||
@@ -11,17 +11,17 @@ namespace Engine
|
||||
DescriptorManager::DescriptorManager(Device& device)
|
||||
: m_Device(device)
|
||||
{
|
||||
vk::DescriptorPoolSize poolSize;
|
||||
poolSize.setType(vk::DescriptorType::eUniformBuffer)
|
||||
.setDescriptorCount(kMaxSets);
|
||||
std::array<vk::DescriptorPoolSize, 2> poolSizes = {
|
||||
vk::DescriptorPoolSize(vk::DescriptorType::eUniformBuffer, kMaxSets),
|
||||
vk::DescriptorPoolSize(vk::DescriptorType::eCombinedImageSampler, kMaxSets),
|
||||
};
|
||||
|
||||
vk::DescriptorPoolCreateInfo poolInfo;
|
||||
poolInfo.setMaxSets(kMaxSets)
|
||||
.setPoolSizes(poolSize)
|
||||
.setPoolSizes(poolSizes)
|
||||
.setFlags(vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet);
|
||||
|
||||
m_Pool = m_Device.GetDevice().createDescriptorPool(poolInfo);
|
||||
|
||||
ENGINE_INFO("DescriptorManager created");
|
||||
}
|
||||
|
||||
@@ -30,28 +30,25 @@ namespace Engine
|
||||
m_Device.GetDevice().destroyDescriptorPool(m_Pool);
|
||||
}
|
||||
|
||||
vk::DescriptorSetLayout DescriptorManager::CreateUniformBufferLayout(u32 binding)
|
||||
vk::DescriptorSetLayout DescriptorManager::CreateLayout(const std::vector<DescriptorBinding>& bindings)
|
||||
{
|
||||
vk::DescriptorSetLayoutBinding layoutBinding;
|
||||
layoutBinding.setBinding(binding)
|
||||
.setDescriptorType(vk::DescriptorType::eUniformBuffer)
|
||||
.setDescriptorCount(1)
|
||||
.setStageFlags(vk::ShaderStageFlagBits::eVertex |
|
||||
vk::ShaderStageFlagBits::eFragment);
|
||||
std::vector<vk::DescriptorSetLayoutBinding> vkBindings;
|
||||
for (auto& b : bindings)
|
||||
{
|
||||
vk::DescriptorSetLayoutBinding vkb;
|
||||
vkb.setBinding(b.Binding)
|
||||
.setDescriptorType(b.Type)
|
||||
.setDescriptorCount(1)
|
||||
.setStageFlags(b.Stages);
|
||||
vkBindings.push_back(vkb);
|
||||
}
|
||||
|
||||
vk::DescriptorSetLayoutCreateInfo layoutInfo;
|
||||
layoutInfo.setBindings(layoutBinding);
|
||||
layoutInfo.setBindings(vkBindings);
|
||||
|
||||
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;
|
||||
@@ -66,9 +63,7 @@ namespace Engine
|
||||
vk::Buffer buffer, u64 size)
|
||||
{
|
||||
vk::DescriptorBufferInfo bufferInfo;
|
||||
bufferInfo.setBuffer(buffer)
|
||||
.setOffset(0)
|
||||
.setRange(size);
|
||||
bufferInfo.setBuffer(buffer).setOffset(0).setRange(size);
|
||||
|
||||
vk::WriteDescriptorSet write;
|
||||
write.setDstSet(set)
|
||||
@@ -80,4 +75,60 @@ namespace Engine
|
||||
|
||||
m_Device.GetDevice().updateDescriptorSets(write, {});
|
||||
}
|
||||
|
||||
void DescriptorManager::UpdateTexture(vk::DescriptorSet set, u32 binding,
|
||||
vk::Sampler sampler, vk::ImageView view)
|
||||
{
|
||||
vk::DescriptorImageInfo imageInfo;
|
||||
imageInfo.setSampler(sampler)
|
||||
.setImageView(view)
|
||||
.setImageLayout(vk::ImageLayout::eShaderReadOnlyOptimal);
|
||||
|
||||
vk::WriteDescriptorSet write;
|
||||
write.setDstSet(set)
|
||||
.setDstBinding(binding)
|
||||
.setDstArrayElement(0)
|
||||
.setDescriptorType(vk::DescriptorType::eCombinedImageSampler)
|
||||
.setDescriptorCount(1)
|
||||
.setImageInfo(imageInfo);
|
||||
|
||||
m_Device.GetDevice().updateDescriptorSets(write, {});
|
||||
}
|
||||
|
||||
vk::PipelineLayout DescriptorManager::CreatePipelineLayout(vk::DescriptorSetLayout layout,
|
||||
const u32 pushConstantSize)
|
||||
{
|
||||
vk::PipelineLayoutCreateInfo info;
|
||||
info.setSetLayouts(layout);
|
||||
|
||||
// 关键:数组声明在 if 外面,生命周期覆盖到 createPipelineLayout
|
||||
std::array<vk::PushConstantRange, 1> pushRanges;
|
||||
|
||||
if (pushConstantSize > 0)
|
||||
{
|
||||
pushRanges[0].setStageFlags(vk::ShaderStageFlagBits::eVertex)
|
||||
.setOffset(0)
|
||||
.setSize(pushConstantSize);
|
||||
info.setPushConstantRanges(pushRanges);
|
||||
}
|
||||
|
||||
return m_Device.GetDevice().createPipelineLayout(info);
|
||||
}
|
||||
|
||||
vk::PipelineLayout DescriptorManager::CreatePipelineLayout(const std::vector<vk::DescriptorSetLayout>& layouts,u32 pushConstantSize)
|
||||
{
|
||||
vk::PipelineLayoutCreateInfo info;
|
||||
info.setSetLayouts(layouts);
|
||||
|
||||
std::array<vk::PushConstantRange, 1> pushRanges;
|
||||
if (pushConstantSize > 0) {
|
||||
pushRanges[0].setStageFlags(vk::ShaderStageFlagBits::eVertex)
|
||||
.setOffset(0)
|
||||
.setSize(pushConstantSize);
|
||||
info.setPushConstantRanges(pushRanges);
|
||||
}
|
||||
|
||||
return m_Device.GetDevice().createPipelineLayout(info);
|
||||
}
|
||||
|
||||
} // namespace Engine
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "Engine/Core/Stats.h"
|
||||
|
||||
namespace Engine {
|
||||
|
||||
MemoryAllocator::MemoryAllocator(Device& device)
|
||||
@@ -26,19 +28,76 @@ MemoryAllocator::~MemoryAllocator()
|
||||
vkDevice.unmapMemory(block.Memory);
|
||||
}
|
||||
vkDevice.freeMemory(block.Memory);
|
||||
|
||||
STATS.RemoveMemoryBlock(block.Size);
|
||||
}
|
||||
blocks.clear();
|
||||
}
|
||||
}
|
||||
|
||||
vk::DeviceSize MemoryAllocator::GetBlockSize(vk::MemoryPropertyFlags props) const
|
||||
{
|
||||
if (props & vk::MemoryPropertyFlagBits::eHostVisible) {
|
||||
return 16ull * 1024 * 1024; // 16 MB
|
||||
}
|
||||
return 64ull * 1024 * 1024; // 64 MB
|
||||
}
|
||||
|
||||
MemoryAllocator::Allocation MemoryAllocator::Allocate(
|
||||
vk::MemoryRequirements reqs, vk::MemoryPropertyFlags props)
|
||||
{
|
||||
uint32_t typeIndex = m_Device.FindMemoryType(reqs.memoryTypeBits, props);
|
||||
u32 typeIndex = m_Device.FindMemoryType(reqs.memoryTypeBits, props);
|
||||
auto& blocks = m_Blocks[typeIndex];
|
||||
|
||||
// 先从已有块里找能放下的
|
||||
for (auto& block : blocks) {
|
||||
// 1. 扫描已有块的 FreeList,找能放下的区域(first-fit)
|
||||
for (u32 i = 0; i < blocks.size(); ++i) {
|
||||
auto& block = blocks[i];
|
||||
for (auto it = block.FreeList.begin(); it != block.FreeList.end(); ++it) {
|
||||
vk::DeviceSize alignedOffset =
|
||||
(it->Offset + reqs.alignment - 1) & ~(reqs.alignment - 1);
|
||||
vk::DeviceSize end = alignedOffset + reqs.size;
|
||||
|
||||
if (end <= it->Offset + it->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;
|
||||
alloc.BlockIndex = i;
|
||||
alloc.TypeIndex = typeIndex;
|
||||
alloc.Valid = true;
|
||||
|
||||
// 更新 FreeList
|
||||
if (alignedOffset == it->Offset && reqs.size == it->Size) {
|
||||
// 整个区域被用了,移除
|
||||
block.FreeList.erase(it);
|
||||
} else if (alignedOffset == it->Offset) {
|
||||
// 从头部切
|
||||
it->Offset += reqs.size;
|
||||
it->Size -= reqs.size;
|
||||
} else if (end == it->Offset + it->Size) {
|
||||
// 从尾部切
|
||||
it->Size = alignedOffset - it->Offset;
|
||||
} else {
|
||||
// 从中间切,拆成两段
|
||||
FreeRange tail;
|
||||
tail.Offset = end;
|
||||
tail.Size = (it->Offset + it->Size) - end;
|
||||
it->Size = alignedOffset - it->Offset;
|
||||
block.FreeList.insert(it + 1, tail);
|
||||
}
|
||||
|
||||
return alloc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 在已有块的尾部 bump 分配
|
||||
for (u32 i = 0; i < blocks.size(); ++i) {
|
||||
auto& block = blocks[i];
|
||||
vk::DeviceSize alignedOffset =
|
||||
(block.Used + reqs.alignment - 1) & ~(reqs.alignment - 1);
|
||||
|
||||
@@ -50,14 +109,17 @@ MemoryAllocator::Allocation MemoryAllocator::Allocate(
|
||||
alloc.MappedPtr = block.IsMapped
|
||||
? static_cast<char*>(block.MappedPtr) + alignedOffset
|
||||
: nullptr;
|
||||
alloc.BlockIndex = i;
|
||||
alloc.TypeIndex = typeIndex;
|
||||
alloc.Valid = true;
|
||||
|
||||
block.Used = alignedOffset + reqs.size;
|
||||
return alloc;
|
||||
}
|
||||
}
|
||||
|
||||
// 没有合适的块,创建新块
|
||||
vk::DeviceSize blockSize = std::max(m_DefaultBlockSize, reqs.size);
|
||||
// 3. 创建新块
|
||||
vk::DeviceSize blockSize = std::max(GetBlockSize(props), reqs.size);
|
||||
|
||||
vk::MemoryAllocateInfo allocInfo;
|
||||
allocInfo.setAllocationSize(blockSize)
|
||||
@@ -68,32 +130,96 @@ MemoryAllocator::Allocation MemoryAllocator::Allocate(
|
||||
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);
|
||||
blocks.push_back(std::move(block));
|
||||
m_TotalAllocated[typeIndex] += blockSize;
|
||||
|
||||
u32 newIndex = static_cast<u32>(blocks.size() - 1);
|
||||
auto& newBlock = blocks[newIndex];
|
||||
|
||||
// 从新块分配
|
||||
vk::DeviceSize alignedOffset = (reqs.alignment - 1) & ~(reqs.alignment - 1);
|
||||
|
||||
Allocation alloc;
|
||||
alloc.Memory = block.Memory;
|
||||
alloc.Memory = newBlock.Memory;
|
||||
alloc.Offset = alignedOffset;
|
||||
alloc.Size = reqs.size;
|
||||
alloc.MappedPtr = block.IsMapped
|
||||
? static_cast<char*>(block.MappedPtr) + alignedOffset
|
||||
alloc.MappedPtr = newBlock.IsMapped
|
||||
? static_cast<char*>(newBlock.MappedPtr) + alignedOffset
|
||||
: nullptr;
|
||||
alloc.BlockIndex = newIndex;
|
||||
alloc.TypeIndex = typeIndex;
|
||||
alloc.Valid = true;
|
||||
|
||||
blocks.back().Used = alignedOffset + reqs.size;
|
||||
newBlock.Used = alignedOffset + reqs.size;
|
||||
|
||||
ENGINE_INFO("MemoryAllocator: new block of {} bytes (type {})",
|
||||
blockSize, typeIndex);
|
||||
|
||||
STATS.AddMemoryBlock(blockSize);
|
||||
|
||||
STATS.AddMemoryUsed(reqs.size);
|
||||
return alloc;
|
||||
}
|
||||
|
||||
void MemoryAllocator::Free(const Allocation& alloc)
|
||||
{
|
||||
if (!alloc.Valid) return;
|
||||
STATS.RemoveMemoryUsed(alloc.Size);
|
||||
|
||||
auto& block = m_Blocks[alloc.TypeIndex][alloc.BlockIndex];
|
||||
|
||||
FreeRange range;
|
||||
range.Offset = alloc.Offset;
|
||||
range.Size = alloc.Size;
|
||||
|
||||
// 插入 FreeList,保持按 offset 排序,方便合并
|
||||
auto it = std::lower_bound(
|
||||
block.FreeList.begin(), block.FreeList.end(), range,
|
||||
[](const FreeRange& a, const FreeRange& b) {
|
||||
return a.Offset < b.Offset;
|
||||
});
|
||||
|
||||
it = block.FreeList.insert(it, range);
|
||||
|
||||
// 向前合并
|
||||
if (it != block.FreeList.begin()) {
|
||||
auto prev = it - 1;
|
||||
if (prev->Offset + prev->Size == it->Offset) {
|
||||
prev->Size += it->Size;
|
||||
block.FreeList.erase(it);
|
||||
it = prev;
|
||||
}
|
||||
}
|
||||
|
||||
// 向后合并
|
||||
auto next = it + 1;
|
||||
if (next != block.FreeList.end()) {
|
||||
if (it->Offset + it->Size == next->Offset) {
|
||||
it->Size += next->Size;
|
||||
block.FreeList.erase(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryAllocator::PrintStats() const
|
||||
{
|
||||
for (u32 type = 0; type < VK_MAX_MEMORY_TYPES; ++type) {
|
||||
if (m_Blocks[type].empty()) continue;
|
||||
|
||||
vk::DeviceSize totalUsed = 0;
|
||||
for (auto& b : m_Blocks[type]) totalUsed += b.Used;
|
||||
|
||||
ENGINE_INFO(" Type {}: {} blocks, total allocated {} MB, used {} MB",
|
||||
type,
|
||||
m_Blocks[type].size(),
|
||||
m_TotalAllocated[type] / (1024 * 1024),
|
||||
totalUsed / (1024 * 1024));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Engine
|
||||
@@ -2,11 +2,11 @@
|
||||
// Created by atdunbg on 2026/9/20.
|
||||
//
|
||||
|
||||
|
||||
#include <Engine/Vulkan/ShaderManager.h>
|
||||
#include <Engine/Vulkan/Device.h>
|
||||
#include <Engine/Core/Log.h>
|
||||
|
||||
#include <array>
|
||||
#include <fstream>
|
||||
|
||||
namespace Engine {
|
||||
@@ -22,21 +22,22 @@ ShaderManager::~ShaderManager()
|
||||
|
||||
ShaderObject ShaderManager::CreateFromFile(const std::string& spvPath,
|
||||
vk::ShaderStageFlagBits stage,
|
||||
vk::DescriptorSetLayout layout)
|
||||
const std::vector<vk::DescriptorSetLayout>& layouts,
|
||||
u32 pushConstantSize)
|
||||
{
|
||||
auto spirv = ReadSPIRV(spvPath);
|
||||
if (spirv.empty()) {
|
||||
ENGINE_ERROR("Failed to read SPIR-V: {}", spvPath);
|
||||
return { nullptr, stage };
|
||||
}
|
||||
return CreateFromSPIRV(spirv, stage, layout);
|
||||
return CreateFromSPIRV(spirv, stage, layouts, pushConstantSize);
|
||||
}
|
||||
|
||||
ShaderObject ShaderManager::CreateFromSPIRV(const std::vector<u32>& spirv,
|
||||
vk::ShaderStageFlagBits stage,
|
||||
vk::DescriptorSetLayout layout)
|
||||
const std::vector<vk::DescriptorSetLayout>& layouts,
|
||||
u32 pushConstantSize)
|
||||
{
|
||||
// 下一个阶段:顶点 → 片段
|
||||
vk::ShaderStageFlags nextStage = {};
|
||||
if (stage == vk::ShaderStageFlagBits::eVertex) {
|
||||
nextStage = vk::ShaderStageFlagBits::eFragment;
|
||||
@@ -50,9 +51,16 @@ ShaderObject ShaderManager::CreateFromSPIRV(const std::vector<u32>& spirv,
|
||||
.setPCode(spirv.data())
|
||||
.setPName("main");
|
||||
|
||||
// 有布局就传进去
|
||||
if (layout) {
|
||||
createInfo.setSetLayouts(layout);
|
||||
if (!layouts.empty()) {
|
||||
createInfo.setSetLayouts(layouts);
|
||||
}
|
||||
|
||||
std::array<vk::PushConstantRange, 1> pushRanges;
|
||||
if (pushConstantSize > 0) {
|
||||
pushRanges[0].setStageFlags(vk::ShaderStageFlagBits::eVertex)
|
||||
.setOffset(0)
|
||||
.setSize(pushConstantSize);
|
||||
createInfo.setPushConstantRanges(pushRanges);
|
||||
}
|
||||
|
||||
vk::ShaderEXT shader;
|
||||
|
||||
@@ -168,9 +168,12 @@ namespace Engine
|
||||
return caps.currentExtent;
|
||||
}
|
||||
|
||||
int fbWidth = 0, fbHeight = 0;
|
||||
window.GetFramebufferSize(fbWidth, fbHeight);
|
||||
|
||||
vk::Extent2D extent = {
|
||||
static_cast<uint32_t>(window.GetWidth()),
|
||||
static_cast<uint32_t>(window.GetHeight())
|
||||
static_cast<uint32_t>(fbWidth),
|
||||
static_cast<uint32_t>(fbHeight)
|
||||
};
|
||||
|
||||
extent.width = std::clamp(extent.width, caps.minImageExtent.width, caps.maxImageExtent.width);
|
||||
|
||||
@@ -0,0 +1,213 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/20.
|
||||
//
|
||||
|
||||
#include <Engine/Vulkan/Texture.h>
|
||||
#include <Engine/Vulkan/Device.h>
|
||||
#include <Engine/Vulkan/MemoryAllocator.h>
|
||||
#include <Engine/Core/Log.h>
|
||||
|
||||
#include <stb/stb_image.h>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "Engine/Core/Stats.h"
|
||||
|
||||
namespace Engine
|
||||
{
|
||||
Texture::Texture(Device& device, MemoryAllocator& allocator, const std::string& path)
|
||||
: m_Device(device) , m_Allocator(allocator)
|
||||
{
|
||||
// 1. CPU 加载图片
|
||||
int width = 0, height = 0, channels = 0;
|
||||
stbi_uc* pixels = stbi_load(path.c_str(), &width, &height, &channels, STBI_rgb_alpha);
|
||||
|
||||
if (!pixels)
|
||||
{
|
||||
ENGINE_ERROR("Failed to load texture: {}", path);
|
||||
return;
|
||||
}
|
||||
|
||||
m_Width = static_cast<u32>(width);
|
||||
m_Height = static_cast<u32>(height);
|
||||
vk::DeviceSize imageSize = static_cast<vk::DeviceSize>(width) *
|
||||
static_cast<vk::DeviceSize>(height) * 4; // RGBA8
|
||||
|
||||
// 2. 创建 Staging Buffer(HOST_VISIBLE)
|
||||
vk::BufferCreateInfo stagingInfo;
|
||||
stagingInfo.setSize(imageSize)
|
||||
.setUsage(vk::BufferUsageFlagBits::eTransferSrc)
|
||||
.setSharingMode(vk::SharingMode::eExclusive);
|
||||
|
||||
vk::Buffer stagingBuffer = m_Device.GetDevice().createBuffer(stagingInfo);
|
||||
|
||||
auto stagingReqs = m_Device.GetDevice().getBufferMemoryRequirements(stagingBuffer);
|
||||
auto stagingAlloc = allocator.Allocate(
|
||||
stagingReqs,
|
||||
vk::MemoryPropertyFlagBits::eHostVisible |
|
||||
vk::MemoryPropertyFlagBits::eHostCoherent);
|
||||
|
||||
m_Device.GetDevice().bindBufferMemory(stagingBuffer, stagingAlloc.Memory, stagingAlloc.Offset);
|
||||
|
||||
// 3. 拷贝像素到 Staging Buffer
|
||||
std::memcpy(stagingAlloc.MappedPtr, pixels, static_cast<size_t>(imageSize));
|
||||
|
||||
stbi_image_free(pixels);
|
||||
|
||||
// 4. 创建 Device Local Image
|
||||
vk::ImageCreateInfo imageInfo;
|
||||
imageInfo.setImageType(vk::ImageType::e2D)
|
||||
.setFormat(vk::Format::eR8G8B8A8Srgb)
|
||||
.setExtent({m_Width, m_Height, 1})
|
||||
.setMipLevels(1)
|
||||
.setArrayLayers(1)
|
||||
.setSamples(vk::SampleCountFlagBits::e1)
|
||||
.setTiling(vk::ImageTiling::eOptimal)
|
||||
.setUsage(vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled)
|
||||
.setSharingMode(vk::SharingMode::eExclusive)
|
||||
.setInitialLayout(vk::ImageLayout::eUndefined);
|
||||
|
||||
m_Image = m_Device.GetDevice().createImage(imageInfo);
|
||||
|
||||
auto imageReqs = m_Device.GetDevice().getImageMemoryRequirements(m_Image);
|
||||
auto imageAlloc = allocator.Allocate(imageReqs, vk::MemoryPropertyFlagBits::eDeviceLocal);
|
||||
|
||||
m_ImageAllocation = imageAlloc;
|
||||
|
||||
m_Memory = imageAlloc.Memory;
|
||||
m_Device.GetDevice().bindImageMemory(m_Image, m_Memory, imageAlloc.Offset);
|
||||
|
||||
// 5. 创建 ImageView
|
||||
vk::ImageViewCreateInfo viewInfo;
|
||||
viewInfo.setImage(m_Image)
|
||||
.setViewType(vk::ImageViewType::e2D)
|
||||
.setFormat(vk::Format::eR8G8B8A8Srgb)
|
||||
.setSubresourceRange({
|
||||
vk::ImageAspectFlagBits::eColor,
|
||||
0, 1, 0, 1
|
||||
});
|
||||
|
||||
m_View = m_Device.GetDevice().createImageView(viewInfo);
|
||||
|
||||
|
||||
// 5.5 创建 Sampler
|
||||
vk::SamplerCreateInfo samplerInfo;
|
||||
samplerInfo.setMagFilter(vk::Filter::eLinear)
|
||||
.setMinFilter(vk::Filter::eLinear)
|
||||
.setAddressModeU(vk::SamplerAddressMode::eRepeat)
|
||||
.setAddressModeV(vk::SamplerAddressMode::eRepeat)
|
||||
.setAddressModeW(vk::SamplerAddressMode::eRepeat)
|
||||
.setAnisotropyEnable(VK_FALSE)
|
||||
.setBorderColor(vk::BorderColor::eIntOpaqueBlack)
|
||||
.setUnnormalizedCoordinates(VK_FALSE)
|
||||
.setCompareEnable(VK_FALSE)
|
||||
.setMipmapMode(vk::SamplerMipmapMode::eLinear);
|
||||
|
||||
m_Sampler = m_Device.GetDevice().createSampler(samplerInfo);
|
||||
|
||||
// 6. 一次性命令缓冲:Staging → Image
|
||||
vk::CommandPoolCreateInfo poolInfo;
|
||||
poolInfo.setQueueFamilyIndex(m_Device.GetGraphicsFamily())
|
||||
.setFlags(vk::CommandPoolCreateFlagBits::eTransient);
|
||||
|
||||
vk::CommandPool pool = m_Device.GetDevice().createCommandPool(poolInfo);
|
||||
|
||||
vk::CommandBufferAllocateInfo allocInfo;
|
||||
allocInfo.setCommandPool(pool)
|
||||
.setLevel(vk::CommandBufferLevel::ePrimary)
|
||||
.setCommandBufferCount(1);
|
||||
|
||||
vk::CommandBuffer cmd = m_Device.GetDevice().allocateCommandBuffers(allocInfo)[0];
|
||||
|
||||
vk::CommandBufferBeginInfo beginInfo;
|
||||
beginInfo.setFlags(vk::CommandBufferUsageFlagBits::eOneTimeSubmit);
|
||||
cmd.begin(beginInfo);
|
||||
|
||||
// 6.1 布局:UNDEFINED → TRANSFER_DST
|
||||
vk::ImageMemoryBarrier2 toTransfer;
|
||||
toTransfer.setSrcStageMask(vk::PipelineStageFlagBits2::eNone)
|
||||
.setSrcAccessMask(vk::AccessFlagBits2::eNone)
|
||||
.setDstStageMask(vk::PipelineStageFlagBits2::eTransfer)
|
||||
.setDstAccessMask(vk::AccessFlagBits2::eTransferWrite)
|
||||
.setOldLayout(vk::ImageLayout::eUndefined)
|
||||
.setNewLayout(vk::ImageLayout::eTransferDstOptimal)
|
||||
.setImage(m_Image)
|
||||
.setSubresourceRange({vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1});
|
||||
|
||||
vk::DependencyInfo dep1;
|
||||
dep1.setImageMemoryBarriers(toTransfer);
|
||||
cmd.pipelineBarrier2(dep1);
|
||||
|
||||
// 6.2 拷贝
|
||||
vk::BufferImageCopy region;
|
||||
region.setBufferOffset(0)
|
||||
.setBufferRowLength(0)
|
||||
.setBufferImageHeight(0)
|
||||
.setImageSubresource({vk::ImageAspectFlagBits::eColor, 0, 0, 1})
|
||||
.setImageOffset({0, 0, 0})
|
||||
.setImageExtent({m_Width, m_Height, 1});
|
||||
|
||||
cmd.copyBufferToImage(stagingBuffer, m_Image,
|
||||
vk::ImageLayout::eTransferDstOptimal, region);
|
||||
|
||||
// 6.3 布局:TRANSFER_DST → SHADER_READ_ONLY
|
||||
vk::ImageMemoryBarrier2 toShader;
|
||||
toShader.setSrcStageMask(vk::PipelineStageFlagBits2::eTransfer)
|
||||
.setSrcAccessMask(vk::AccessFlagBits2::eTransferWrite)
|
||||
.setDstStageMask(vk::PipelineStageFlagBits2::eFragmentShader)
|
||||
.setDstAccessMask(vk::AccessFlagBits2::eShaderRead)
|
||||
.setOldLayout(vk::ImageLayout::eTransferDstOptimal)
|
||||
.setNewLayout(vk::ImageLayout::eShaderReadOnlyOptimal)
|
||||
.setImage(m_Image)
|
||||
.setSubresourceRange({vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1});
|
||||
|
||||
vk::DependencyInfo dep2;
|
||||
dep2.setImageMemoryBarriers(toShader);
|
||||
cmd.pipelineBarrier2(dep2);
|
||||
|
||||
cmd.end();
|
||||
|
||||
// 7. 提交并等待
|
||||
vk::SubmitInfo submit;
|
||||
submit.setCommandBuffers(cmd);
|
||||
|
||||
vk::FenceCreateInfo fenceInfo;
|
||||
vk::Fence fence = m_Device.GetDevice().createFence(fenceInfo);
|
||||
|
||||
m_Device.GetGraphicsQueue().submit(submit, fence);
|
||||
auto waitResult = m_Device.GetDevice().waitForFences(fence, VK_TRUE, UINT64_MAX);
|
||||
if (waitResult != vk::Result::eSuccess)
|
||||
{
|
||||
ENGINE_ERROR("Texture upload: waitForFences failed");
|
||||
}
|
||||
|
||||
// 8. 清理临时对象
|
||||
m_Device.GetDevice().destroyFence(fence);
|
||||
m_Device.GetDevice().destroyCommandPool(pool);
|
||||
m_Device.GetDevice().destroyBuffer(stagingBuffer);
|
||||
allocator.Free(stagingAlloc);
|
||||
// staging 内存由 MemoryAllocator 统一释放
|
||||
|
||||
ENGINE_INFO("Texture loaded: {} ({}x{})", path, m_Width, m_Height);
|
||||
|
||||
|
||||
// 显存占用 = 宽 * 高 * 4 字节(RGBA8)
|
||||
m_TextureBytes = static_cast<u64>(m_Width) * m_Height * 4;
|
||||
STATS.AddTexture(m_TextureBytes);
|
||||
}
|
||||
|
||||
Texture::~Texture()
|
||||
{
|
||||
const auto& vkDevice = m_Device.GetDevice();
|
||||
|
||||
vkDevice.destroySampler(m_Sampler);
|
||||
vkDevice.destroyImageView(m_View);
|
||||
vkDevice.destroyImage(m_Image);
|
||||
|
||||
// 归还 image 的显存
|
||||
m_Allocator.Free(m_ImageAllocation);
|
||||
|
||||
STATS.RemoveTexture(m_TextureBytes);
|
||||
}
|
||||
|
||||
} // namespace Engine
|
||||
@@ -0,0 +1,6 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/20.
|
||||
//
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include <stb/stb_image.h>
|
||||
Submodule
+1
Submodule Lib/assimp added at 01cf2b3abf
@@ -0,0 +1,3 @@
|
||||
add_library(stb STATIC ${STB_SOURCE})
|
||||
target_include_directories(stb INTERFACE ./)
|
||||
target_sources(stb PRIVATE ./stb/stb_impl.cpp)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,5 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/20.
|
||||
//
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "stb_image.h"
|
||||
@@ -1,9 +1,38 @@
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec3 fragColor;
|
||||
const int MAX_LIGHTS = 4;
|
||||
|
||||
struct Light {
|
||||
vec4 direction;
|
||||
vec4 color;
|
||||
};
|
||||
|
||||
layout(location = 0) in vec2 fragUV;
|
||||
layout(location = 1) in vec3 fragNormal;
|
||||
layout(location = 2) in vec3 fragWorldPos;
|
||||
|
||||
layout(location = 0) out vec4 outColor;
|
||||
|
||||
layout(set = 0, binding = 0) uniform UniformBufferObject {
|
||||
mat4 view;
|
||||
mat4 proj;
|
||||
vec4 ambientColor;
|
||||
Light lights[MAX_LIGHTS];
|
||||
} ubo;
|
||||
|
||||
layout(set = 1, binding = 0) uniform sampler2D texSampler;
|
||||
|
||||
void main() {
|
||||
outColor = vec4(fragColor, 1.0);
|
||||
vec3 albedo = texture(texSampler, fragUV).rgb;
|
||||
vec3 N = normalize(fragNormal);
|
||||
|
||||
vec3 result = ubo.ambientColor.rgb * albedo;
|
||||
|
||||
for (int i = 0; i < MAX_LIGHTS; ++i) {
|
||||
vec3 L = normalize(-ubo.lights[i].direction.xyz);
|
||||
float diff = max(dot(N, L), 0.0);
|
||||
result += ubo.lights[i].color.rgb * ubo.lights[i].color.a * diff * albedo;
|
||||
}
|
||||
|
||||
outColor = vec4(result, 1.0);
|
||||
}
|
||||
@@ -1,15 +1,39 @@
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec3 inPosition;
|
||||
layout(location = 1) in vec3 inColor;
|
||||
const int MAX_LIGHTS = 4;
|
||||
|
||||
layout(location = 0) out vec3 fragColor;
|
||||
struct Light {
|
||||
vec4 direction;
|
||||
vec4 color;
|
||||
};
|
||||
|
||||
layout(location = 0) in vec3 inPosition;
|
||||
layout(location = 1) in vec3 inNormal;
|
||||
layout(location = 2) in vec2 inUV;
|
||||
|
||||
layout(location = 0) out vec2 fragUV;
|
||||
layout(location = 1) out vec3 fragNormal;
|
||||
layout(location = 2) out vec3 fragWorldPos;
|
||||
|
||||
layout(push_constant) uniform PushConstants {
|
||||
mat4 model;
|
||||
} pc;
|
||||
|
||||
layout(set = 0, binding = 0) uniform UniformBufferObject {
|
||||
mat4 mvp;
|
||||
mat4 view;
|
||||
mat4 proj;
|
||||
vec4 ambientColor;
|
||||
Light lights[MAX_LIGHTS];
|
||||
} ubo;
|
||||
|
||||
void main() {
|
||||
gl_Position = ubo.mvp * vec4(inPosition, 1.0);
|
||||
fragColor = inColor;
|
||||
vec4 worldPos = pc.model * vec4(inPosition, 1.0);
|
||||
gl_Position = ubo.proj * ubo.view * worldPos;
|
||||
|
||||
fragWorldPos = worldPos.xyz;
|
||||
|
||||
mat3 normalMatrix = mat3(transpose(inverse(pc.model)));
|
||||
fragNormal = normalize(normalMatrix * inNormal);
|
||||
|
||||
fragUV = inUV;
|
||||
}
|
||||
Reference in New Issue
Block a user