添加多模型渲染,添加贴图渲染,添加平行光, 暂时不上传assets
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
} // 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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
} // 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
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user