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