Commit 3ea83d40 authored by Shu's avatar Shu 🍠
Browse files

Memory budget and validation logs

parent d5f1bba4
......@@ -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::vector<buffer_requirem
return memory;
}
void Allocator::updateProperties() {
if (hasBudget()) {
vkGetPhysicalDeviceMemoryProperties2(physicalDevice, &properties2);
#if LOG_TRACE
LOG_T("Available heaps:")
for (size_t i = 0; i < getProperties().memoryHeapCount; i++) {
LOG_T('\t' << i << ": " << budget.heapUsage[i] << '/' << budget.heapBudget[i]);
}
#endif
} else {
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &properties);
}
}
Allocator::memory_ptr Allocator::allocate(VkMemoryRequirements requirements, VkMemoryPropertyFlags properties) {
// Search in existing allocations
for (auto& alloc: allocations) {
if ((requirements.memoryTypeBits & (1 << alloc->memoryType)) && (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<uint32_t> Allocator::findMemory(uint32_t typeFilter, VkMemoryPropertyFlags requirement, VkDeviceSize size) const {
std::optional<uint32_t> 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]);
}
}
}
......
......@@ -62,7 +62,10 @@ public:
static memory_ptr GetNull();
private:
std::optional<uint32_t> findMemory(uint32_t, VkMemoryPropertyFlags, VkDeviceSize size = 0) const;
std::optional<uint32_t> 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<area> areas;
};
VkDevice device;
VkPhysicalDeviceMemoryProperties properties;
VkPhysicalDevice const physicalDevice;
VkDevice const device;
VkPhysicalDeviceMemoryProperties properties{};
VkPhysicalDeviceMemoryProperties2 properties2{};
VkPhysicalDeviceMemoryBudgetPropertiesEXT budget{};
VkQueue transferQueue;
VkCommandPool transferPool;
......
......@@ -65,4 +65,12 @@ VkSurfaceFormatKHR PhysicalDeviceInfo::getFormat() const {
LOG_W("Using suboptimal surface format");
return swapDetails.formats[0];
}
#include <string.h>
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
......@@ -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<const char *> optionalExtensions;
};
}
\ No newline at end of file
......@@ -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<PhysicalDeviceInfo>(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<Allocator>(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<uint32_t>(capabilities.maxImageExtent.width, windowSize.first)),
std::max(capabilities.minImageExtent.height, std::min<uint32_t>(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<const char *> requiredExtensions = {VK_KHR_SWAPCHAIN_EXTENSION_NAME};
const std::vector<const char *> requiredExtensions = {VK_KHR_SWAPCHAIN_EXTENSION_NAME};
const std::vector<const char *> 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<VkExtensionProperties> 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<const char*> 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");
......
......@@ -42,6 +42,7 @@ private:
renderOptions options;
VkInstance instance;
VkDebugUtilsMessengerEXT debugMessenger;
VkSurfaceKHR surface;
VkDevice device;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment