mirror of
https://github.com/johndoe6345789/SDL3CPlusPlus.git
synced 2026-04-24 13:44:58 +00:00
feat: Add Vulkan utility functions for buffer creation and memory management
This commit is contained in:
@@ -143,6 +143,7 @@ if(BUILD_SDL3_APP)
|
||||
src/app/audio_player.cpp
|
||||
src/app/service_based_app.cpp
|
||||
src/gui/gui_renderer.cpp
|
||||
src/core/vulkan_utils.cpp
|
||||
src/script/script_engine.cpp
|
||||
src/script/physics_bridge.cpp
|
||||
src/script/lua_helpers.cpp
|
||||
|
||||
118
src/core/vulkan_utils.cpp
Normal file
118
src/core/vulkan_utils.cpp
Normal file
@@ -0,0 +1,118 @@
|
||||
#include "vulkan_utils.hpp"
|
||||
#include "../logging/logger.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace sdl3cpp::vulkan::utils {
|
||||
|
||||
namespace {
|
||||
|
||||
uint32_t FindMemoryType(VkPhysicalDevice physicalDevice, uint32_t typeFilter, VkMemoryPropertyFlags properties) {
|
||||
VkPhysicalDeviceMemoryProperties memProperties;
|
||||
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties);
|
||||
|
||||
for (uint32_t i = 0; i < memProperties.memoryTypeCount; ++i) {
|
||||
if ((typeFilter & (1 << i)) &&
|
||||
(memProperties.memoryTypes[i].propertyFlags & properties) == properties) {
|
||||
sdl3cpp::logging::Logger::GetInstance().Debug("Found suitable memory type: " + std::to_string(i));
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
sdl3cpp::logging::Logger::GetInstance().Error("Failed to find suitable memory type");
|
||||
throw std::runtime_error("Failed to find suitable memory type");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
VkExtent2D ChooseSwapExtent(VkSurfaceCapabilitiesKHR capabilities, SDL_Window* window) {
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
SDL_GetWindowSize(window, &width, &height);
|
||||
|
||||
return VkExtent2D{
|
||||
static_cast<uint32_t>(std::clamp(width, static_cast<int>(capabilities.minImageExtent.width),
|
||||
static_cast<int>(capabilities.maxImageExtent.width))),
|
||||
static_cast<uint32_t>(std::clamp(height, static_cast<int>(capabilities.minImageExtent.height),
|
||||
static_cast<int>(capabilities.maxImageExtent.height)))
|
||||
};
|
||||
}
|
||||
|
||||
void CreateBuffer(VkDevice device, VkPhysicalDevice physicalDevice, VkDeviceSize size, VkBufferUsageFlags usage,
|
||||
VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory) {
|
||||
sdl3cpp::logging::Logger::GetInstance().Debug("Creating buffer with size " + std::to_string(size) + " bytes");
|
||||
// Validate buffer size
|
||||
if (size == 0) {
|
||||
sdl3cpp::logging::Logger::GetInstance().Error("Cannot create buffer with size 0");
|
||||
throw std::runtime_error("Cannot create buffer with size 0");
|
||||
}
|
||||
|
||||
// Check available memory before allocating
|
||||
VkPhysicalDeviceMemoryProperties memProps;
|
||||
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProps);
|
||||
|
||||
uint64_t totalAvailable = 0;
|
||||
for (uint32_t i = 0; i < memProps.memoryHeapCount; ++i) {
|
||||
if (memProps.memoryHeaps[i].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) {
|
||||
totalAvailable += memProps.memoryHeaps[i].size;
|
||||
}
|
||||
}
|
||||
|
||||
if (size > totalAvailable) {
|
||||
throw std::runtime_error("Requested buffer size (" +
|
||||
std::to_string(size / (1024 * 1024)) + " MB) exceeds available GPU memory (" +
|
||||
std::to_string(totalAvailable / (1024 * 1024)) + " MB)");
|
||||
}
|
||||
|
||||
VkBufferCreateInfo bufferInfo{};
|
||||
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
||||
bufferInfo.size = size;
|
||||
bufferInfo.usage = usage;
|
||||
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
|
||||
VkResult createResult = vkCreateBuffer(device, &bufferInfo, nullptr, &buffer);
|
||||
if (createResult != VK_SUCCESS) {
|
||||
throw std::runtime_error("Failed to create buffer (error code: " +
|
||||
std::to_string(createResult) + ", size: " +
|
||||
std::to_string(size / 1024) + " KB)");
|
||||
}
|
||||
|
||||
VkMemoryRequirements memRequirements;
|
||||
vkGetBufferMemoryRequirements(device, buffer, &memRequirements);
|
||||
|
||||
VkMemoryAllocateInfo allocInfo{};
|
||||
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||
allocInfo.allocationSize = memRequirements.size;
|
||||
|
||||
try {
|
||||
allocInfo.memoryTypeIndex =
|
||||
FindMemoryType(physicalDevice, memRequirements.memoryTypeBits, properties);
|
||||
} catch (const std::exception& e) {
|
||||
vkDestroyBuffer(device, buffer, nullptr);
|
||||
throw std::runtime_error(std::string("Failed to find suitable memory type: ") + e.what());
|
||||
}
|
||||
|
||||
VkResult allocResult = vkAllocateMemory(device, &allocInfo, nullptr, &bufferMemory);
|
||||
if (allocResult != VK_SUCCESS) {
|
||||
vkDestroyBuffer(device, buffer, nullptr);
|
||||
std::string errorMsg = "Failed to allocate buffer memory.\n";
|
||||
errorMsg += "Requested: " + std::to_string(memRequirements.size / (1024 * 1024)) + " MB\n";
|
||||
errorMsg += "Error code: " + std::to_string(allocResult) + "\n";
|
||||
if (allocResult == VK_ERROR_OUT_OF_DEVICE_MEMORY) {
|
||||
errorMsg += "\nOut of GPU memory. Try:\n";
|
||||
errorMsg += "- Closing other GPU-intensive applications\n";
|
||||
errorMsg += "- Reducing window resolution\n";
|
||||
errorMsg += "- Upgrading GPU or system memory";
|
||||
} else if (allocResult == VK_ERROR_OUT_OF_HOST_MEMORY) {
|
||||
errorMsg += "\nOut of system memory. Try:\n";
|
||||
errorMsg += "- Closing other applications\n";
|
||||
errorMsg += "- Adding more RAM to your system";
|
||||
}
|
||||
throw std::runtime_error(errorMsg);
|
||||
}
|
||||
|
||||
vkBindBufferMemory(device, buffer, bufferMemory, 0);
|
||||
}
|
||||
|
||||
} // namespace sdl3cpp::vulkan::utils
|
||||
13
src/core/vulkan_utils.hpp
Normal file
13
src/core/vulkan_utils.hpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
namespace sdl3cpp::vulkan::utils {
|
||||
|
||||
VkExtent2D ChooseSwapExtent(VkSurfaceCapabilitiesKHR capabilities, SDL_Window* window);
|
||||
|
||||
void CreateBuffer(VkDevice device, VkPhysicalDevice physicalDevice, VkDeviceSize size, VkBufferUsageFlags usage,
|
||||
VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory);
|
||||
|
||||
} // namespace sdl3cpp::vulkan::utils
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
#include "gui/gui_renderer.hpp"
|
||||
|
||||
#include "app/vulkan_api.hpp"
|
||||
#include "../core/vulkan_utils.hpp"
|
||||
#include "logging/logger.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -19,7 +19,7 @@
|
||||
#include "../../third_party/font8x8_basic.h"
|
||||
|
||||
namespace script = sdl3cpp::script;
|
||||
namespace vulkan = sdl3cpp::app::vulkan;
|
||||
namespace vulkan_utils = sdl3cpp::vulkan::utils;
|
||||
|
||||
namespace sdl3cpp::gui {
|
||||
namespace {
|
||||
@@ -519,7 +519,7 @@ GuiRenderer::GuiRenderer(VkDevice device, VkPhysicalDevice physicalDevice, VkFor
|
||||
if (size == 0) {
|
||||
return;
|
||||
}
|
||||
vulkan::CreateBuffer(device_, physicalDevice_, static_cast<VkDeviceSize>(size),
|
||||
vulkan_utils::CreateBuffer(device_, physicalDevice_, static_cast<VkDeviceSize>(size),
|
||||
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
||||
stagingBuffer_, stagingMemory_);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "buffer_service.hpp"
|
||||
#include "../../logging/logger.hpp"
|
||||
#include "../../core/vulkan_utils.hpp"
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
|
||||
@@ -96,79 +97,7 @@ void BufferService::CreateBuffer(VkDeviceSize size, VkBufferUsageFlags usage,
|
||||
auto device = deviceService_->GetDevice();
|
||||
auto physicalDevice = deviceService_->GetPhysicalDevice();
|
||||
|
||||
logging::Logger::GetInstance().Debug("Creating buffer with size " + std::to_string(size) + " bytes");
|
||||
|
||||
// Validate buffer size
|
||||
if (size == 0) {
|
||||
logging::Logger::GetInstance().Error("Cannot create buffer with size 0");
|
||||
throw std::runtime_error("Cannot create buffer with size 0");
|
||||
}
|
||||
|
||||
// Check available memory before allocating
|
||||
VkPhysicalDeviceMemoryProperties memProps;
|
||||
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProps);
|
||||
|
||||
uint64_t totalAvailable = 0;
|
||||
for (uint32_t i = 0; i < memProps.memoryHeapCount; ++i) {
|
||||
if (memProps.memoryHeaps[i].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) {
|
||||
totalAvailable += memProps.memoryHeaps[i].size;
|
||||
}
|
||||
}
|
||||
|
||||
if (size > totalAvailable) {
|
||||
throw std::runtime_error("Requested buffer size (" +
|
||||
std::to_string(size / (1024 * 1024)) + " MB) exceeds available GPU memory (" +
|
||||
std::to_string(totalAvailable / (1024 * 1024)) + " MB)");
|
||||
}
|
||||
|
||||
VkBufferCreateInfo bufferInfo{};
|
||||
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
||||
bufferInfo.size = size;
|
||||
bufferInfo.usage = usage;
|
||||
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
|
||||
VkResult createResult = vkCreateBuffer(device, &bufferInfo, nullptr, &buffer);
|
||||
if (createResult != VK_SUCCESS) {
|
||||
throw std::runtime_error("Failed to create buffer (error code: " +
|
||||
std::to_string(createResult) + ", size: " +
|
||||
std::to_string(size / 1024) + " KB)");
|
||||
}
|
||||
|
||||
VkMemoryRequirements memRequirements;
|
||||
vkGetBufferMemoryRequirements(device, buffer, &memRequirements);
|
||||
|
||||
VkMemoryAllocateInfo allocInfo{};
|
||||
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||
allocInfo.allocationSize = memRequirements.size;
|
||||
|
||||
try {
|
||||
allocInfo.memoryTypeIndex =
|
||||
deviceService_->FindMemoryType(memRequirements.memoryTypeBits, properties);
|
||||
} catch (const std::exception& e) {
|
||||
vkDestroyBuffer(device, buffer, nullptr);
|
||||
throw std::runtime_error(std::string("Failed to find suitable memory type: ") + e.what());
|
||||
}
|
||||
|
||||
VkResult allocResult = vkAllocateMemory(device, &allocInfo, nullptr, &bufferMemory);
|
||||
if (allocResult != VK_SUCCESS) {
|
||||
vkDestroyBuffer(device, buffer, nullptr);
|
||||
std::string errorMsg = "Failed to allocate buffer memory.\n";
|
||||
errorMsg += "Requested: " + std::to_string(memRequirements.size / (1024 * 1024)) + " MB\n";
|
||||
errorMsg += "Error code: " + std::to_string(allocResult) + "\n";
|
||||
if (allocResult == VK_ERROR_OUT_OF_DEVICE_MEMORY) {
|
||||
errorMsg += "\nOut of GPU memory. Try:\n";
|
||||
errorMsg += "- Closing other GPU-intensive applications\n";
|
||||
errorMsg += "- Reducing window resolution\n";
|
||||
errorMsg += "- Upgrading GPU or system memory";
|
||||
} else if (allocResult == VK_ERROR_OUT_OF_HOST_MEMORY) {
|
||||
errorMsg += "\nOut of system memory. Try:\n";
|
||||
errorMsg += "- Closing other applications\n";
|
||||
errorMsg += "- Adding more RAM to your system";
|
||||
}
|
||||
throw std::runtime_error(errorMsg);
|
||||
}
|
||||
|
||||
vkBindBufferMemory(device, buffer, bufferMemory, 0);
|
||||
vulkan::utils::CreateBuffer(device, physicalDevice, size, usage, properties, buffer, bufferMemory);
|
||||
}
|
||||
|
||||
void BufferService::CleanupBuffers() {
|
||||
|
||||
@@ -29,6 +29,10 @@ public:
|
||||
size_t GetVertexCount() const override { return vertexCount_; }
|
||||
size_t GetIndexCount() const override { return indexCount_; }
|
||||
|
||||
// Public buffer creation utility
|
||||
void CreateBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties,
|
||||
VkBuffer& buffer, VkDeviceMemory& bufferMemory) override;
|
||||
|
||||
// IShutdownable interface
|
||||
void Shutdown() noexcept override;
|
||||
|
||||
@@ -44,9 +48,6 @@ private:
|
||||
size_t indexCount_ = 0;
|
||||
|
||||
// Helper methods
|
||||
void CreateBuffer(VkDeviceSize size, VkBufferUsageFlags usage,
|
||||
VkMemoryPropertyFlags properties,
|
||||
VkBuffer& buffer, VkDeviceMemory& bufferMemory);
|
||||
void CleanupBuffers();
|
||||
};
|
||||
|
||||
|
||||
@@ -65,6 +65,19 @@ public:
|
||||
* @return Index count
|
||||
*/
|
||||
virtual size_t GetIndexCount() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Create a Vulkan buffer with memory allocation.
|
||||
*
|
||||
* @param size Buffer size in bytes
|
||||
* @param usage Buffer usage flags
|
||||
* @param properties Memory property flags
|
||||
* @param[out] buffer Created buffer handle
|
||||
* @param[out] bufferMemory Allocated memory handle
|
||||
* @throws std::runtime_error if creation fails
|
||||
*/
|
||||
virtual void CreateBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties,
|
||||
VkBuffer& buffer, VkDeviceMemory& bufferMemory) = 0;
|
||||
};
|
||||
|
||||
} // namespace sdl3cpp::services
|
||||
|
||||
Reference in New Issue
Block a user