添加多模型渲染,添加贴图渲染,添加平行光, 暂时不上传assets

This commit is contained in:
2026-09-21 14:07:20 +08:00
parent 381b7c0aa6
commit 7d628f8d8a
37 changed files with 9817 additions and 189 deletions
+2
View File
@@ -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)
+1
View File
@@ -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__)
+92
View File
@@ -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
+15
View File
@@ -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
+39
View File
@@ -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
+49
View File
@@ -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
+64
View File
@@ -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
+27 -4
View File
@@ -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;
};
+51
View File
@@ -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
+5
View File
@@ -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
+23 -3
View File
@@ -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
+5 -3
View File
@@ -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);
+56
View File
@@ -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
+324
View File
@@ -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
+56 -7
View File
@@ -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);
}
} // namespace Engine
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
+30
View File
@@ -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
+64
View File
@@ -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
+157
View File
@@ -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
View File
@@ -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 -2
View File
@@ -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)
+74 -23
View File
@@ -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
+139 -13
View File
@@ -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;
}
} // namespace Engine
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
+16 -8
View File
@@ -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;
+5 -2
View File
@@ -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);
+213
View File
@@ -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
+6
View File
@@ -0,0 +1,6 @@
//
// Created by atdunbg on 2026/9/20.
//
#define STB_IMAGE_IMPLEMENTATION
#include <stb/stb_image.h>