Added VMA

This commit is contained in:
2026-09-30 01:31:59 -07:00
parent fc70475538
commit 97ff85ea8a
9 changed files with 118 additions and 119 deletions
+1
View File
@@ -24,6 +24,7 @@ target_compile_definitions(${ENGINE_NAME} PUBLIC
target_link_libraries(${ENGINE_NAME} PUBLIC
vulkan
vma
shaderc
glfw
glm
+18 -18
View File
@@ -28,26 +28,32 @@ Buffer::Buffer(Device& device, vk::DeviceSize instanceSize, u32 instanceCount,
alignmentSize = getAlignment(instanceSize, minOffsetAlignment);
bufferSize = alignmentSize * instanceCount;
device.createBuffer(bufferSize, usageFlags, memoryPropertyFlags, buffer,
memory);
allocation);
}
Buffer::~Buffer() {
unmap();
this->device.get()->destroyBuffer(this->buffer);
this->device.get()->freeMemory(this->memory);
vmaDestroyBuffer(this->device.getAllocator(), this->buffer, this->allocation);
}
// VMA always maps the whole allocation, so `size` only exists to keep the
// interface; `offset` is applied to the returned pointer.
vk::Result Buffer::map(vk::DeviceSize size, vk::DeviceSize offset) {
SOPHIA_ASSERT(buffer && memory, "Called map on buffer before create");
SOPHIA_ASSERT(buffer && allocation, "Called map on buffer before create");
(void)size;
vk::MemoryMapFlags emptyFlag{};
return this->device.get()->mapMemory(this->memory, offset, size, emptyFlag,
&this->mapped);
void* base = nullptr;
vk::Result result = static_cast<vk::Result>(
vmaMapMemory(this->device.getAllocator(), this->allocation, &base));
if (result == vk::Result::eSuccess) {
this->mapped = static_cast<char*>(base) + offset;
}
return result;
}
void Buffer::unmap() {
if (mapped) {
this->device.get()->unmapMemory(this->memory);
vmaUnmapMemory(this->device.getAllocator(), this->allocation);
mapped = nullptr;
}
}
@@ -66,19 +72,13 @@ void Buffer::writeToBuffer(void* data, vk::DeviceSize size,
}
vk::Result Buffer::flush(vk::DeviceSize size, vk::DeviceSize offset) {
vk::MappedMemoryRange mappedRange = {};
mappedRange.memory = memory;
mappedRange.offset = offset;
mappedRange.size = size;
return this->device.get()->flushMappedMemoryRanges(1, &mappedRange);
return static_cast<vk::Result>(vmaFlushAllocation(
this->device.getAllocator(), this->allocation, offset, size));
}
vk::Result Buffer::invalidate(vk::DeviceSize size, vk::DeviceSize offset) {
vk::MappedMemoryRange mappedRange = {};
mappedRange.memory = memory;
mappedRange.offset = offset;
mappedRange.size = size;
return this->device.get()->invalidateMappedMemoryRanges(1, &mappedRange);
return static_cast<vk::Result>(vmaInvalidateAllocation(
this->device.getAllocator(), this->allocation, offset, size));
}
vk::DescriptorBufferInfo Buffer::descriptorInfo(vk::DeviceSize size,
+1 -1
View File
@@ -55,7 +55,7 @@ class Buffer {
Device& device;
void* mapped = nullptr;
vk::Buffer buffer = VK_NULL_HANDLE;
vk::DeviceMemory memory = VK_NULL_HANDLE;
VmaAllocation allocation = VK_NULL_HANDLE;
vk::DeviceSize bufferSize;
uint32_t instanceCount;
+43 -72
View File
@@ -69,10 +69,14 @@ Device::Device(Window* window)
}
pickPhysicalDevice();
createLogicalDevice();
createAllocator();
createCommandPool();
}
Device::~Device() {
vmaDestroyAllocator(this->allocator);
log::trace("destroyed VmaAllocator");
if (this->enableValidationLayers) {
destroyDebugUtilsMessengerEXT(this->instance.get(), this->debugMessenger,
nullptr);
@@ -88,22 +92,6 @@ Device::~Device() {
}
}
u32 Device::findMemoryType(u32 typeFilter, vk::MemoryPropertyFlags properties) {
vk::PhysicalDeviceMemoryProperties memoryProperties =
this->physicalDevice.getMemoryProperties();
for (u32 i = 0; i < memoryProperties.memoryTypeCount; i++) {
if ((typeFilter & (1 << i)) &&
(memoryProperties.memoryTypes[i].propertyFlags & properties) ==
properties) {
return i;
}
}
log::fatal("failed to find suitable memory type");
throw std::runtime_error("failed to find suitable memory type");
}
vk::Format Device::findSupportedFormat(
const std::vector<vk::Format>& candidates, vk::ImageTiling tiling,
vk::FormatFeatureFlags features) {
@@ -125,35 +113,26 @@ vk::Format Device::findSupportedFormat(
void Device::createBuffer(vk::DeviceSize size, vk::BufferUsageFlags usage,
vk::MemoryPropertyFlags properties,
vk::Buffer& buffer, vk::DeviceMemory& bufferMemory) {
vk::Buffer& buffer, VmaAllocation& allocation) {
vk::BufferCreateInfo bufferInfo = {};
bufferInfo.size = size;
bufferInfo.usage = usage;
bufferInfo.sharingMode = vk::SharingMode::eExclusive;
try {
buffer = this->device->createBuffer(bufferInfo);
} catch (const vk::SystemError& error) {
log::fatal("failed to create vertex buffer");
throw std::runtime_error("failed to create vertex buffer");
VmaAllocationCreateInfo allocInfo = {};
allocInfo.requiredFlags = static_cast<VkMemoryPropertyFlags>(properties);
VkBuffer rawBuffer = VK_NULL_HANDLE;
VkResult result = vmaCreateBuffer(
this->allocator, &static_cast<const VkBufferCreateInfo&>(bufferInfo),
&allocInfo, &rawBuffer, &allocation, nullptr);
if (result != VK_SUCCESS) {
log::fatal("failed to create buffer. Error: ",
vk::to_string(static_cast<vk::Result>(result)));
throw std::runtime_error("failed to create buffer");
}
vk::MemoryRequirements memoryRequirements =
this->device->getBufferMemoryRequirements(buffer);
vk::MemoryAllocateInfo allocInfo = {};
allocInfo.allocationSize = memoryRequirements.size;
allocInfo.memoryTypeIndex =
findMemoryType(memoryRequirements.memoryTypeBits, properties);
try {
bufferMemory = this->device->allocateMemory(allocInfo);
} catch (const vk::SystemError& error) {
log::fatal("failed to allocate vertex buffer memory");
throw std::runtime_error("failed to allocate vertex buffer memory");
}
this->device->bindBufferMemory(buffer, bufferMemory, 0);
buffer = rawBuffer;
}
vk::CommandBuffer Device::beginSingleTimeCommands() {
@@ -220,43 +199,21 @@ void Device::copyBuffer(vk::Buffer sourceBuffer, vk::Buffer destinationBuffer,
void Device::createImageWithInfo(const vk::ImageCreateInfo& imageInfo,
vk::MemoryPropertyFlags properties,
vk::Image& image,
vk::DeviceMemory& imageMemory) {
try {
vk::Result result = this->device->createImage(&imageInfo, nullptr, &image);
if (result != vk::Result::eSuccess) {
throw vk::SystemError(result);
}
} catch (const vk::SystemError& error) {
log::fatal("failed to create image. Error: ", error.what());
vk::Image& image, VmaAllocation& allocation) {
VmaAllocationCreateInfo allocInfo = {};
allocInfo.requiredFlags = static_cast<VkMemoryPropertyFlags>(properties);
VkImage rawImage = VK_NULL_HANDLE;
VkResult result = vmaCreateImage(
this->allocator, &static_cast<const VkImageCreateInfo&>(imageInfo),
&allocInfo, &rawImage, &allocation, nullptr);
if (result != VK_SUCCESS) {
log::fatal("failed to create image. Error: ",
vk::to_string(static_cast<vk::Result>(result)));
throw std::runtime_error("failed to create image");
}
vk::MemoryRequirements memoryRequirements =
this->device->getImageMemoryRequirements(image);
vk::MemoryAllocateInfo allocateInfo{};
allocateInfo.allocationSize = memoryRequirements.size;
allocateInfo.memoryTypeIndex =
findMemoryType(memoryRequirements.memoryTypeBits, properties);
try {
vk::Result result =
this->device->allocateMemory(&allocateInfo, nullptr, &imageMemory);
if (result != vk::Result::eSuccess) {
throw vk::SystemError(result);
}
} catch (const vk::SystemError& error) {
log::fatal("failed to allocate image memory. Error: ", error.what());
throw std::runtime_error("failed to allocate image memory");
}
try {
this->device->bindImageMemory(image, imageMemory, 0);
} catch (const vk::SystemError& error) {
log::fatal("failed to bind image memory. Error: ", error.what());
throw std::runtime_error("failed to bind image memory");
}
image = rawImage;
}
void Device::populateImGuiInitInfo(ImGui_ImplVulkan_InitInfo& initInfo) {
@@ -514,6 +471,20 @@ void Device::createLogicalDevice() {
}
}
void Device::createAllocator() {
VmaAllocatorCreateInfo createInfo = {};
createInfo.vulkanApiVersion = SOPHIA_VULKAN_API_VERSION;
createInfo.instance = this->instance.get();
createInfo.physicalDevice = this->physicalDevice;
createInfo.device = this->device.get();
if (vmaCreateAllocator(&createInfo, &this->allocator) != VK_SUCCESS) {
log::fatal("failed to create VmaAllocator");
throw std::runtime_error("failed to create VmaAllocator");
}
log::trace("created VmaAllocator");
}
SwapchainSupportDetails Device::querySwapchainSupport(
vk::PhysicalDevice device) {
SwapchainSupportDetails details;
+7 -4
View File
@@ -1,6 +1,7 @@
#pragma once
#include <imgui_impl_vulkan.h>
#include <vk_mem_alloc.h>
#include <memory>
#include <optional>
@@ -47,6 +48,7 @@ class Device {
void waitIdle() { this->device->waitIdle(); }
VmaAllocator getAllocator() const { return this->allocator; }
vk::CommandPool getCommandPool() const { return this->commandPool; }
vk::SurfaceKHR getSurface() const { return surface; }
vk::Queue getGraphicsQueue() const { return graphicsQueue; }
@@ -60,15 +62,13 @@ class Device {
return querySwapchainSupport(this->physicalDevice);
}
u32 findMemoryType(u32 typeFilter, vk::MemoryPropertyFlags properties);
vk::Format findSupportedFormat(const std::vector<vk::Format>& candidates,
vk::ImageTiling tiling,
vk::FormatFeatureFlags features);
void createBuffer(vk::DeviceSize size, vk::BufferUsageFlags usage,
vk::MemoryPropertyFlags properties, vk::Buffer& buffer,
vk::DeviceMemory& bufferMemory);
VmaAllocation& allocation);
vk::CommandBuffer beginSingleTimeCommands();
@@ -79,7 +79,7 @@ class Device {
void createImageWithInfo(const vk::ImageCreateInfo& imageInfo,
vk::MemoryPropertyFlags properties, vk::Image& image,
vk::DeviceMemory& imageMemory);
VmaAllocation& allocation);
void populateImGuiInitInfo(ImGui_ImplVulkan_InitInfo& initInfo);
@@ -100,6 +100,7 @@ class Device {
SwapchainSupportDetails querySwapchainSupport(vk::PhysicalDevice device);
void createLogicalDevice();
void createAllocator();
void createCommandPool();
vk::UniqueInstance instance;
@@ -111,6 +112,8 @@ class Device {
vk::PhysicalDevice physicalDevice = VK_NULL_HANDLE;
vk::UniqueDevice device;
VmaAllocator allocator = VK_NULL_HANDLE;
vk::Queue graphicsQueue;
vk::Queue presentQueue;
+34 -20
View File
@@ -3,11 +3,35 @@
#include <cstring>
#include <sophia/util/logger.hpp>
#include <stdexcept>
#include <utility>
#include "vulkan/device.hpp"
namespace sophia {
Image::Image(Image&& other) noexcept
: device{other.device},
externalImage{other.externalImage},
image{std::exchange(other.image, nullptr)},
allocation{std::exchange(other.allocation, VK_NULL_HANDLE)},
view{std::move(other.view)},
sampler{std::move(other.sampler)},
format{other.format},
extent{other.extent},
aspectMask{other.aspectMask},
mipLevels{other.mipLevels},
arrayLayers{other.arrayLayers},
layout{other.layout} {}
Image::~Image() {
this->sampler.reset();
this->view.reset();
if (this->image) {
vmaDestroyImage(this->device.getAllocator(), this->image, this->allocation);
}
}
vk::ImageSubresourceRange Image::getSubresourceRange() const {
return vk::ImageSubresourceRange{this->aspectMask, 0, this->mipLevels, 0,
this->arrayLayers};
@@ -194,35 +218,26 @@ std::unique_ptr<Image> Image::Builder::build() {
imageInfo.sharingMode = vk::SharingMode::eExclusive;
imageInfo.samples = this->samples;
result->image = this->device.get()->createImageUnique(imageInfo);
vk::MemoryRequirements memoryRequirements =
this->device.get()->getImageMemoryRequirements(result->image.get());
vk::MemoryAllocateInfo allocInfo{};
allocInfo.allocationSize = memoryRequirements.size;
allocInfo.memoryTypeIndex = this->device.findMemoryType(
memoryRequirements.memoryTypeBits, this->memoryProperties);
result->memory = this->device.get()->allocateMemoryUnique(allocInfo);
this->device.get()->bindImageMemory(result->image.get(),
result->memory.get(), 0);
this->device.createImageWithInfo(imageInfo, this->memoryProperties,
result->image, result->allocation);
}
if (!this->isExternalImage && this->initialData != nullptr) {
VmaAllocator allocator = this->device.getAllocator();
vk::Buffer stagingBuffer;
vk::DeviceMemory stagingMemory;
VmaAllocation stagingAllocation;
this->device.createBuffer(this->initialDataSize,
vk::BufferUsageFlagBits::eTransferSrc,
vk::MemoryPropertyFlagBits::eHostVisible |
vk::MemoryPropertyFlagBits::eHostCoherent,
stagingBuffer, stagingMemory);
stagingBuffer, stagingAllocation);
void* mapped =
this->device.get()->mapMemory(stagingMemory, 0, this->initialDataSize);
void* mapped = nullptr;
vmaMapMemory(allocator, stagingAllocation, &mapped);
memcpy(mapped, this->initialData,
static_cast<size_t>(this->initialDataSize));
this->device.get()->unmapMemory(stagingMemory);
vmaUnmapMemory(allocator, stagingAllocation);
vk::BufferImageCopy region{};
region.bufferOffset = 0;
@@ -247,8 +262,7 @@ std::unique_ptr<Image> Image::Builder::build() {
}
this->device.endSingleTimeCommands(commandBuffer);
this->device.get()->destroyBuffer(stagingBuffer);
this->device.get()->freeMemory(stagingMemory);
vmaDestroyBuffer(allocator, stagingBuffer, stagingAllocation);
}
if (this->createView) {
+7 -4
View File
@@ -1,5 +1,7 @@
#pragma once
#include <vk_mem_alloc.h>
#include <memory>
#include <sophia/types.hpp>
#include <vulkan/vulkan.hpp>
@@ -16,11 +18,12 @@ class Image {
Image(const Image&) = delete;
Image& operator=(const Image&) = delete;
Image(Image&&) = default;
Image(Image&& other) noexcept;
Image& operator=(Image&&) = delete;
~Image();
vk::Image get() const {
return this->image ? this->image.get() : this->externalImage;
return this->image ? this->image : this->externalImage;
}
vk::ImageView getView() const { return this->view.get(); }
vk::Sampler getSampler() const { return this->sampler.get(); }
@@ -56,8 +59,8 @@ class Image {
Device& device;
vk::Image externalImage;
vk::UniqueImage image;
vk::UniqueDeviceMemory memory;
vk::Image image;
VmaAllocation allocation = VK_NULL_HANDLE;
vk::UniqueImageView view;
vk::UniqueSampler sampler;
+2
View File
@@ -0,0 +1,2 @@
#define VMA_IMPLEMENTATION
#include <vk_mem_alloc.h>
+5
View File
@@ -17,6 +17,11 @@ target_link_libraries(vulkan INTERFACE
Vulkan::shaderc_combined
)
# 2. VMA (header-only, ships with the Vulkan SDK)
add_library(vma INTERFACE)
target_include_directories(vma INTERFACE ${Vulkan_INCLUDE_DIRS}/vma)
target_link_libraries(vma INTERFACE vulkan)
# 3. GLFW (static)
option(GLFW_BUILD_EXAMPLES "Build the GLFW example programs" OFF)
option(GLFW_BUILD_TESTS "Build the GLFW test programs" OFF)