diff --git a/CMakeLists.txt b/CMakeLists.txt index 302fc00..71bae14 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/src/core/vulkan_utils.cpp b/src/core/vulkan_utils.cpp new file mode 100644 index 0000000..a570204 --- /dev/null +++ b/src/core/vulkan_utils.cpp @@ -0,0 +1,118 @@ +#include "vulkan_utils.hpp" +#include "../logging/logger.hpp" + +#include +#include + +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(std::clamp(width, static_cast(capabilities.minImageExtent.width), + static_cast(capabilities.maxImageExtent.width))), + static_cast(std::clamp(height, static_cast(capabilities.minImageExtent.height), + static_cast(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 \ No newline at end of file diff --git a/src/core/vulkan_utils.hpp b/src/core/vulkan_utils.hpp new file mode 100644 index 0000000..e310f5b --- /dev/null +++ b/src/core/vulkan_utils.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include +#include + +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 \ No newline at end of file diff --git a/src/gui/gui_renderer.cpp b/src/gui/gui_renderer.cpp index e07b536..7e9a083 100644 --- a/src/gui/gui_renderer.cpp +++ b/src/gui/gui_renderer.cpp @@ -1,7 +1,7 @@ #include "gui/gui_renderer.hpp" -#include "app/vulkan_api.hpp" +#include "../core/vulkan_utils.hpp" #include "logging/logger.hpp" #include @@ -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(size), + vulkan_utils::CreateBuffer(device_, physicalDevice_, static_cast(size), VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer_, stagingMemory_); diff --git a/src/services/impl/buffer_service.cpp b/src/services/impl/buffer_service.cpp index 2877cad..9545ebd 100644 --- a/src/services/impl/buffer_service.cpp +++ b/src/services/impl/buffer_service.cpp @@ -1,5 +1,6 @@ #include "buffer_service.hpp" #include "../../logging/logger.hpp" +#include "../../core/vulkan_utils.hpp" #include #include @@ -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() { diff --git a/src/services/impl/buffer_service.hpp b/src/services/impl/buffer_service.hpp index 6e5d972..df6fe0f 100644 --- a/src/services/impl/buffer_service.hpp +++ b/src/services/impl/buffer_service.hpp @@ -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(); }; diff --git a/src/services/interfaces/i_buffer_service.hpp b/src/services/interfaces/i_buffer_service.hpp index 6e0bc4b..83c6be4 100644 --- a/src/services/interfaces/i_buffer_service.hpp +++ b/src/services/interfaces/i_buffer_service.hpp @@ -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