diff --git a/src/client/render/vk/Allocator.cpp b/src/client/render/vk/Allocator.cpp index ff540f3..0f3c460 100644 --- a/src/client/render/vk/Allocator.cpp +++ b/src/client/render/vk/Allocator.cpp @@ -12,9 +12,16 @@ constexpr VkDeviceSize MIN_ALLOC_SIZE = 1 << 28; const auto NO_DELETER = Allocator::MemoryDeleter(nullptr); Allocator::memory_ptr Allocator::GetNull() { return Allocator::memory_ptr(nullptr, NO_DELETER); } -Allocator::Allocator(VkDevice device, const PhysicalDeviceInfo &info) : device(device) { - vkGetPhysicalDeviceMemoryProperties(info.device, &properties); +Allocator::Allocator(VkDevice device, const PhysicalDeviceInfo &info): physicalDevice(info.device), device(device) { + if(info.hasMemoryBudget()) { + properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2; + properties2.pNext = &budget; + budget.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT; + } else { + LOG_W("No memory budget. Process may go out of memory."); + } + updateProperties(); { if (!info.queueIndices.transferFamily.has_value()) { LOG_W("No transfer queue family. Using graphics one"); @@ -183,10 +190,26 @@ Allocator::memory_ptr Allocator::createBuffers(const std::vectormemoryType)) && (this->properties.memoryTypes[alloc->memoryType].propertyFlags & properties) == properties && alloc->size > requirements.size) { + if ((requirements.memoryTypeBits & (1 << alloc->memoryType)) && + (getProperties().memoryTypes[alloc->memoryType].propertyFlags & properties) == properties && + alloc->size > requirements.size + ) { VkDeviceSize start = 0; auto aligned = [&](VkDeviceSize offset) { if (offset % requirements.alignment == 0) @@ -222,7 +245,8 @@ Allocator::memory_ptr Allocator::allocate(VkMemoryRequirements requirements, VkM allocInfo.allocationSize = requirements.size; allocInfo.memoryTypeIndex = memIdx.value(); } else { - LOG_E("No suitable memory heap under budget"); + LOG_E("No suitable memory heap within memory budget"); + LOG_D(requirements.memoryTypeBits << ' ' << properties << ' ' << requirements.size); return GetNull(); } @@ -233,7 +257,7 @@ Allocator::memory_ptr Allocator::allocate(VkMemoryRequirements requirements, VkM } void *ptr = nullptr; - if ((this->properties.memoryTypes[allocInfo.memoryTypeIndex].propertyFlags & HOST_EASILY_WRITABLE) == HOST_EASILY_WRITABLE) { + if ((getProperties().memoryTypes[allocInfo.memoryTypeIndex].propertyFlags & HOST_EASILY_WRITABLE) == HOST_EASILY_WRITABLE) { vkMapMemory(device, memory, 0, VK_WHOLE_SIZE, 0, &ptr); } @@ -270,30 +294,35 @@ void Allocator::copyBuffer(buffer_info src, buffer_info dst, VkDeviceSize size) vkResetCommandBuffer(transferBuffer, 0); } -std::optional Allocator::findMemory(uint32_t typeFilter, VkMemoryPropertyFlags requirement, VkDeviceSize size) const { +std::optional Allocator::findMemory(uint32_t typeFilter, VkMemoryPropertyFlags requirement, VkDeviceSize size) { + updateProperties(); #if LOG_TRACE LOG_T("Available memory:"); - for (uint32_t i = 0; i < properties.memoryTypeCount; i++) { - LOG_T('\t' << i << ": " << ((properties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) ? "local " : "") - << ((properties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) ? "visible " : "") - << ((properties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) ? "coherent " : "") - << ((properties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) ? "cached " : "") - << ((properties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) ? "lazy " : "") - << ((properties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_PROTECTED_BIT) ? "protected " : "") - << properties.memoryHeaps[properties.memoryTypes[i].heapIndex].size); + for (uint32_t i = 0; i < getProperties().memoryTypeCount; i++) { + LOG_T('\t' << i << ": " + << getProperties().memoryTypes[i].heapIndex << ' ' + << ((getProperties().memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) ? "local " : "") + << ((getProperties().memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) ? "visible " : "") + << ((getProperties().memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) ? "coherent " : "") + << ((getProperties().memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) ? "cached " : "") + << ((getProperties().memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) ? "lazy " : "") + << ((getProperties().memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_PROTECTED_BIT) ? "protected " : "") + << getProperties().memoryHeaps[getProperties().memoryTypes[i].heapIndex].size); } #endif - for (uint32_t i = 0; i < properties.memoryTypeCount; i++) { - if ((typeFilter & (1 << i)) && (properties.memoryTypes[i].propertyFlags & requirement) == requirement) { + for (uint32_t i = 0; i < getProperties().memoryTypeCount; i++) { + if ((typeFilter & (1 << i)) && (getProperties().memoryTypes[i].propertyFlags & requirement) == requirement) { VkDeviceSize usage = size; for(const auto& alloc: allocations) { if(alloc->memoryType == i) usage += alloc->size; } - VkDeviceSize budget = properties.memoryHeaps[properties.memoryTypes[i].heapIndex].size; - //TODO: use memory budjet extension - if(budget >= usage) { + const auto heapIndex = getProperties().memoryTypes[i].heapIndex; + const VkDeviceSize heapSize = getProperties().memoryHeaps[heapIndex].size; + if (heapSize >= usage && (!hasBudget() || budget.heapBudget[heapIndex] >= budget.heapUsage[heapIndex] + size)) { return i; + } else { + LOG_T("Out of budget " << usage << '/' << heapSize << " : " << budget.heapUsage[heapIndex] + size << '/' << budget.heapBudget[heapIndex]); } } } diff --git a/src/client/render/vk/Allocator.hpp b/src/client/render/vk/Allocator.hpp index 9123730..ec35783 100644 --- a/src/client/render/vk/Allocator.hpp +++ b/src/client/render/vk/Allocator.hpp @@ -62,7 +62,10 @@ public: static memory_ptr GetNull(); private: - std::optional findMemory(uint32_t, VkMemoryPropertyFlags, VkDeviceSize size = 0) const; + std::optional findMemory(uint32_t, VkMemoryPropertyFlags, VkDeviceSize size = 0); + constexpr bool hasBudget() const { return properties2.pNext != nullptr; } + constexpr const VkPhysicalDeviceMemoryProperties &getProperties() const { return hasBudget() ? properties2.memoryProperties : properties; } + void updateProperties(); struct Allocation { Allocation(VkDevice, VkDeviceMemory, VkDeviceSize, uint32_t, void *ptr); @@ -79,8 +82,11 @@ private: std::vector areas; }; - VkDevice device; - VkPhysicalDeviceMemoryProperties properties; + VkPhysicalDevice const physicalDevice; + VkDevice const device; + VkPhysicalDeviceMemoryProperties properties{}; + VkPhysicalDeviceMemoryProperties2 properties2{}; + VkPhysicalDeviceMemoryBudgetPropertiesEXT budget{}; VkQueue transferQueue; VkCommandPool transferPool; diff --git a/src/client/render/vk/PhysicalDeviceInfo.cpp b/src/client/render/vk/PhysicalDeviceInfo.cpp index 48b055a..5ccb4f7 100644 --- a/src/client/render/vk/PhysicalDeviceInfo.cpp +++ b/src/client/render/vk/PhysicalDeviceInfo.cpp @@ -65,4 +65,12 @@ VkSurfaceFormatKHR PhysicalDeviceInfo::getFormat() const { LOG_W("Using suboptimal surface format"); return swapDetails.formats[0]; +} +#include +bool PhysicalDeviceInfo::hasMemoryBudget() const { + for (auto extension: optionalExtensions) { + if (strcmp(extension, VK_EXT_MEMORY_BUDGET_EXTENSION_NAME) == 0) + return true; + } + return false; } \ No newline at end of file diff --git a/src/client/render/vk/PhysicalDeviceInfo.hpp b/src/client/render/vk/PhysicalDeviceInfo.hpp index f144eb9..6c2eb1e 100644 --- a/src/client/render/vk/PhysicalDeviceInfo.hpp +++ b/src/client/render/vk/PhysicalDeviceInfo.hpp @@ -29,11 +29,13 @@ struct PhysicalDeviceInfo { swapDetails(SwapChainSupportDetails::Query(device, surface)), queueIndices(QueueFamilyIndices::Query(device, surface)) { } VkSurfaceFormatKHR getFormat() const; + bool hasMemoryBudget() const; GLFWwindow *window; VkPhysicalDevice device = VK_NULL_HANDLE; VkSurfaceKHR surface; SwapChainSupportDetails swapDetails; QueueFamilyIndices queueIndices; + std::vector optionalExtensions; }; } \ No newline at end of file diff --git a/src/client/render/vk/Renderer.cpp b/src/client/render/vk/Renderer.cpp index 054e764..95300bd 100644 --- a/src/client/render/vk/Renderer.cpp +++ b/src/client/render/vk/Renderer.cpp @@ -15,13 +15,35 @@ using namespace render::vk; constexpr auto LOAD_DEVICE = true; +#if LOG_DEBUG constexpr auto VALIDATION_LAYER = true; +#else +constexpr auto VALIDATION_LAYER = false; +#endif void set_current_extent(VkSurfaceCapabilitiesKHR &capabilities, GLFWwindow *ptr); +VKAPI_ATTR VkBool32 VKAPI_CALL debugValidationCallback( + VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT messageType, + const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData, + void *pUserData); Renderer::Renderer(VkInstance instance, VkDevice device, const PhysicalDeviceInfo& info, const renderOptions& opt): options(opt), instance(instance), surface(info.surface), device(device), physicalInfo(std::make_unique(info)) { + if constexpr(VALIDATION_LAYER) { + VkDebugUtilsMessengerCreateInfoEXT createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; + createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; + createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; + createInfo.pfnUserCallback = debugValidationCallback; + createInfo.pUserData = nullptr; + + if (vkCreateDebugUtilsMessengerEXT(instance, &createInfo, ALLOC, &debugMessenger) != VK_SUCCESS) { + LOG_E("Failed to redirect validation errors"); + } + } + set_current_extent(physicalInfo->swapDetails.capabilities, physicalInfo->window); allocator = std::make_unique(device, *physicalInfo.get()); @@ -67,6 +89,9 @@ Renderer::~Renderer() { vkDestroyDevice(device, ALLOC); vkDestroySurfaceKHR(instance, surface, ALLOC); + if constexpr(VALIDATION_LAYER) { + vkDestroyDebugUtilsMessengerEXT(instance, debugMessenger, ALLOC); + } vkDestroyInstance(instance, ALLOC); } @@ -99,6 +124,37 @@ void set_current_extent(VkSurfaceCapabilitiesKHR &capabilities, GLFWwindow* ptr) std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, windowSize.first)), std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, windowSize.second))}; }; +VKAPI_ATTR VkBool32 VKAPI_CALL debugValidationCallback( + VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT messageType, + const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, + void* pUserData) +{ + switch (messageSeverity) { + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: + LOG_E("[VK] " << pCallbackData->pMessage); + break; + + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: + LOG_W("[VK] " << pCallbackData->pMessage); + break; + + default: + LOG_I("[VK] " << pCallbackData->pMessage); + break; + + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: + LOG_D("[VK] " << pCallbackData->pMessage); + break; + + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: + LOG_T("[VK] " << pCallbackData->pMessage); + break; + + } + + return VK_FALSE; +} bool Renderer::Load(Window& window, const renderOptions& opt) { Window::CreateInfo windowInfo; @@ -136,23 +192,33 @@ bool Renderer::Load(Window& window, const renderOptions& opt) { vkEnumerateInstanceExtensionProperties(nullptr, &availableExtensionCount, availableExtensions.data()); #if LOG_TRACE - LOG_T("Available extensions:"); + LOG_T("Available instance extensions:"); for (const auto &extension : availableExtensions) { LOG_T('\t' << extension.extensionName << " : " << extension.specVersion); } #endif + const auto hasExtension = [&availableExtensions](const char *extension) { + return std::any_of(availableExtensions.begin(), availableExtensions.end(), [&extension](const VkExtensionProperties &ex) { return strcmp(ex.extensionName, extension) == 0; }); + }; + uint32_t glfwExtensionCount = 0; const char **glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); extensions.reserve(glfwExtensionCount); for (uint32_t i = 0; i < glfwExtensionCount; i++) { - if (std::none_of(availableExtensions.begin(), availableExtensions.end(), [&](const VkExtensionProperties &ex) { return strcmp(ex.extensionName, glfwExtensions[i]) == 0; })) { + if (!hasExtension(glfwExtensions[i])) { LOG_E("Missing required glfw extension " << glfwExtensions[i]); return false; } extensions.push_back(glfwExtensions[i]); } - + if constexpr (VALIDATION_LAYER) { + if (hasExtension(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) { + extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + } else { + LOG_W("Debug utils extension unavailable"); + } + } } createInfo.enabledExtensionCount = extensions.size(); createInfo.ppEnabledExtensionNames = extensions.data(); @@ -181,11 +247,6 @@ bool Renderer::Load(Window& window, const renderOptions& opt) { } else { LOG_W("Validation layer unavailable"); } - if (hasLayer(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) { - layers.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); - } else { - LOG_W("Debug utils layer unavailable"); - } } } createInfo.enabledLayerCount = layers.size(); @@ -213,7 +274,8 @@ bool Renderer::Load(Window& window, const renderOptions& opt) { } PhysicalDeviceInfo physicalInfo; - std::vector requiredExtensions = {VK_KHR_SWAPCHAIN_EXTENSION_NAME}; + const std::vector requiredExtensions = {VK_KHR_SWAPCHAIN_EXTENSION_NAME}; + const std::vector optionalExtensions = {VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME, VK_EXT_MEMORY_BUDGET_EXTENSION_NAME}; { uint32_t deviceCount = 0; vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); @@ -232,8 +294,9 @@ bool Renderer::Load(Window& window, const renderOptions& opt) { vkGetPhysicalDeviceProperties(device, &deviceProperties); vkGetPhysicalDeviceFeatures(device, &deviceFeatures); - if (!deviceFeatures.geometryShader) - continue; + auto infos = PhysicalDeviceInfo(window.getPtr(), device, surface); + + //FIXME: if (!deviceFeatures.geometryShader) continue; { uint32_t availableExtensionsCount; @@ -241,12 +304,24 @@ bool Renderer::Load(Window& window, const renderOptions& opt) { std::vector availableExtensions(availableExtensionsCount); vkEnumerateDeviceExtensionProperties(device, nullptr, &availableExtensionsCount, availableExtensions.data()); - if (std::any_of(requiredExtensions.begin(), requiredExtensions.end(), [&](const char *required) { - return std::none_of(availableExtensions.begin(), availableExtensions.end(), [&](const VkExtensionProperties &ex) { - return strcmp(ex.extensionName, required) == 0; - }); - })) +#if LOG_TRACE + LOG_T("Available device extensions:"); + for (const auto &extension : availableExtensions) { + LOG_T('\t' << extension.extensionName << " : " << extension.specVersion); + } +#endif + + const auto hasExtension = [&availableExtensions](const char *extension) { + return std::any_of(availableExtensions.begin(), availableExtensions.end(), [&extension](const VkExtensionProperties &ex) { return strcmp(ex.extensionName, extension) == 0; }); + }; + + if (std::any_of(requiredExtensions.begin(), requiredExtensions.end(), [&](const char *required) { return !hasExtension(required); })) continue; + + for (auto extension: optionalExtensions) { + if (hasExtension(extension)) + infos.optionalExtensions.push_back(extension); + } } if (deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) @@ -255,7 +330,6 @@ bool Renderer::Load(Window& window, const renderOptions& opt) { score += deviceProperties.limits.maxImageDimension2D; //TODO: check others limits - auto infos = PhysicalDeviceInfo(window.getPtr(), device, surface); if (!infos.queueIndices.isComplete()) continue; if (infos.queueIndices.isOptimal()) @@ -299,6 +373,8 @@ bool Renderer::Load(Window& window, const renderOptions& opt) { VkPhysicalDeviceFeatures deviceFeatures{}; //TODO: + std::vector extensions(requiredExtensions); + extensions.insert(extensions.end(), physicalInfo.optionalExtensions.begin(), physicalInfo.optionalExtensions.end()); VkDeviceCreateInfo createInfo{}; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; createInfo.pQueueCreateInfos = queueCreateInfos.data(); @@ -306,8 +382,8 @@ bool Renderer::Load(Window& window, const renderOptions& opt) { createInfo.pEnabledFeatures = &deviceFeatures; createInfo.enabledLayerCount = layers.size(); createInfo.ppEnabledLayerNames = layers.data(); - createInfo.enabledExtensionCount = requiredExtensions.size(); - createInfo.ppEnabledExtensionNames = requiredExtensions.data(); + createInfo.enabledExtensionCount = extensions.size(); + createInfo.ppEnabledExtensionNames = extensions.data(); if (vkCreateDevice(physicalInfo.device, &createInfo, ALLOC, &device) != VK_SUCCESS) { LOG_E("Failed to bind graphic device"); diff --git a/src/client/render/vk/Renderer.hpp b/src/client/render/vk/Renderer.hpp index 18eded7..ce20a1a 100644 --- a/src/client/render/vk/Renderer.hpp +++ b/src/client/render/vk/Renderer.hpp @@ -42,6 +42,7 @@ private: renderOptions options; VkInstance instance; + VkDebugUtilsMessengerEXT debugMessenger; VkSurfaceKHR surface; VkDevice device;