From b32c5f3c4e710cb0750ef402ed14e4b08fed349b Mon Sep 17 00:00:00 2001 From: niki Date: Thu, 6 Jan 2022 01:12:16 +0100 Subject: [PATCH 001/170] hello world triangle --- CMakeLists.txt | 18 +- src/common/config.h | 2 + src/modules/graphics/Graphics.cpp | 4 + src/modules/graphics/Graphics.h | 1 + src/modules/graphics/ShaderStage.cpp | 2 + src/modules/graphics/vulkan/Graphics.cpp | 848 ++++++++++++++++++++ src/modules/graphics/vulkan/Graphics.h | 141 ++++ src/modules/graphics/vulkan/Shader.cpp | 44 + src/modules/graphics/vulkan/Shader.h | 30 + src/modules/graphics/vulkan/ShaderStage.cpp | 195 +++++ src/modules/graphics/vulkan/ShaderStage.h | 29 + src/modules/graphics/wrap_Graphics.cpp | 4 + src/modules/window/sdl/Window.cpp | 26 +- 13 files changed, 1341 insertions(+), 3 deletions(-) create mode 100644 src/modules/graphics/vulkan/Graphics.cpp create mode 100644 src/modules/graphics/vulkan/Graphics.h create mode 100644 src/modules/graphics/vulkan/Shader.cpp create mode 100644 src/modules/graphics/vulkan/Shader.h create mode 100644 src/modules/graphics/vulkan/ShaderStage.cpp create mode 100644 src/modules/graphics/vulkan/ShaderStage.h diff --git a/CMakeLists.txt b/CMakeLists.txt index beb512dc6..aacdf76c2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,7 +34,7 @@ set(CMAKE_MODULE_PATH "${love_SOURCE_DIR}/extra/cmake" ${CMAKE_MODULE_PATH}) # Needed for shared libs on Linux. (-fPIC). set(CMAKE_POSITION_INDEPENDENT_CODE TRUE) -set (CMAKE_CXX_STANDARD 11) +set (CMAKE_CXX_STANDARD 17) if(MSVC) set(LOVE_CONSOLE_EXE_NAME lovec) @@ -66,6 +66,8 @@ if(POLICY CMP0072) endif() if(MEGA) + find_package(Vulkan REQUIRED) + # LOVE_MSVC_DLLS contains runtime DLLs that should be bundled with the love # binary (in e.g. the installer). Example: msvcp140.dll. set(LOVE_MSVC_DLLS ${MEGA_MSVC_DLLS}) @@ -73,7 +75,7 @@ if(MEGA) # LOVE_INCLUDE_DIRS contains the search directories for #include. It's mostly # not needed for MEGA builds, since almost all the libraries (except LuaJIT) # are CMake targets, causing include paths to be added automatically. - set(LOVE_INCLUDE_DIRS) + set(LOVE_INCLUDE_DIRS ${Vulkan_INCLUDE_DIRS}) if(APPLE) # Some files do #include , but building with megasource @@ -96,6 +98,7 @@ if(MEGA) ${MEGA_SDL2MAIN} ${MEGA_SDL2} ${MEGA_ZLIB} + ${Vulkan_LIBRARIES} ) # These DLLs are moved next to the love binary in a post-build step to @@ -568,13 +571,24 @@ set(LOVE_SRC_MODULE_GRAPHICS_OPENGL src/modules/graphics/opengl/Texture.h ) +set(LOVE_SRC_MODULE_GRAPHICS_VULKAN + src/modules/graphics/vulkan/Graphics.h + src/modules/graphics/vulkan/Graphics.cpp + src/modules/graphics/vulkan/Shader.h + src/modules/graphics/vulkan/Shader.cpp + src/modules/graphics/vulkan/ShaderStage.h + src/modules/graphics/vulkan/ShaderStage.cpp +) + set(LOVE_SRC_MODULE_GRAPHICS ${LOVE_SRC_MODULE_GRAPHICS_ROOT} ${LOVE_SRC_MODULE_GRAPHICS_OPENGL} + ${LOVE_SRC_MODULE_GRAPHICS_VULKAN} ) source_group("modules\\graphics" FILES ${LOVE_SRC_MODULE_GRAPHICS_ROOT}) source_group("modules\\graphics\\opengl" FILES ${LOVE_SRC_MODULE_GRAPHICS_OPENGL}) +source_group("modules\\graphics\\vulkan" FILES ${LOVE_SRC_MODULE_GRAPHICS_VULKAN}) # # love.image diff --git a/src/common/config.h b/src/common/config.h index fe2e7fcf2..1c8771efa 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -124,6 +124,8 @@ # define LOVE_LEGENDARY_ACCELEROMETER_AS_JOYSTICK_HACK #endif +#define LOVE_GRAPHICS_VULKAN + #if defined(LOVE_MACOS) || defined(LOVE_IOS) # define LOVE_GRAPHICS_METAL #endif diff --git a/src/modules/graphics/Graphics.cpp b/src/modules/graphics/Graphics.cpp index 9ca953c49..8b532b42e 100644 --- a/src/modules/graphics/Graphics.cpp +++ b/src/modules/graphics/Graphics.cpp @@ -109,6 +109,7 @@ namespace opengl { extern love::graphics::Graphics *createInstance(); } #if defined(LOVE_MACOS) || defined(LOVE_IOS) namespace metal { extern love::graphics::Graphics *createInstance(); } #endif +namespace vulkan { extern love::graphics::Graphics* createInstance(); } Graphics *Graphics::createInstance(const std::vector &renderers) { @@ -126,6 +127,9 @@ Graphics *Graphics::createInstance(const std::vector &renderers) if (renderer == RENDERER_METAL) instance = metal::createInstance(); #endif + if (renderer == RENDERER_VULKAN) { + instance = vulkan::createInstance(); + } if (instance != nullptr) break; diff --git a/src/modules/graphics/Graphics.h b/src/modules/graphics/Graphics.h index 56119b96c..7ba1d8c0b 100644 --- a/src/modules/graphics/Graphics.h +++ b/src/modules/graphics/Graphics.h @@ -156,6 +156,7 @@ public: RENDERER_NONE, RENDERER_OPENGL, RENDERER_METAL, + RENDERER_VULKAN, RENDERER_MAX_ENUM }; diff --git a/src/modules/graphics/ShaderStage.cpp b/src/modules/graphics/ShaderStage.cpp index 8ebe1acbb..2ff44c0a9 100644 --- a/src/modules/graphics/ShaderStage.cpp +++ b/src/modules/graphics/ShaderStage.cpp @@ -18,6 +18,8 @@ * 3. This notice may not be removed or altered from any source distribution. **/ +#include + #include "ShaderStage.h" #include "common/Exception.h" #include "Graphics.h" diff --git a/src/modules/graphics/vulkan/Graphics.cpp b/src/modules/graphics/vulkan/Graphics.cpp new file mode 100644 index 000000000..7a0d680a4 --- /dev/null +++ b/src/modules/graphics/vulkan/Graphics.cpp @@ -0,0 +1,848 @@ +#include "Graphics.h" +#include "SDL_vulkan.h" +#include "window/Window.h" +#include "common/Exception.h" +#include "Shader.h" + +#include +#include +#include +#include +#include + + +namespace love { + namespace graphics { + namespace vulkan { + const std::vector validationLayers = { + "VK_LAYER_KHRONOS_validation" + }; + + const std::vector deviceExtensions = { + VK_KHR_SWAPCHAIN_EXTENSION_NAME + }; + +#ifdef NDEBUG + const bool enableValidationLayers = false; +#else + const bool enableValidationLayers = true; +#endif + + const int MAX_FRAMES_IN_FLIGHT = 2; + + static std::vector readFile(const std::string& filename) { + std::ifstream file(filename, std::ios::ate | std::ios::binary); + + if (!file.is_open()) { + throw std::runtime_error("failed to open file!"); + } + + size_t fileSize = (size_t)file.tellg(); + std::vector buffer(fileSize); + + file.seekg(0); + file.read(buffer.data(), fileSize); + + file.close(); + + return buffer; + } + + const char* Graphics::getName() const { + return "love.graphics.vulkan"; + } + + Graphics::Graphics() { + } + + void Graphics::initVulkan() { + if (!init) { + init = true; + createVulkanInstance(); + createSurface(); + pickPhysicalDevice(); + createLogicalDevice(); + createSwapChain(); + createImageViews(); + createRenderPass(); + createGraphicsPipeline(); + createFramebuffers(); + createCommandPool(); + createCommandBuffers(); + createSyncObjects(); + } + } + + Graphics::~Graphics() { + if (init) { + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr); + vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr); + vkDestroyFence(device, inFlightFences[i], nullptr); + } + if (vkDeviceWaitIdle(device) != VK_SUCCESS) { + throw love::Exception("vkDeviceWaitIdle failed"); + } + vkDestroyCommandPool(device, commandPool, nullptr); + for (auto framebuffer : swapChainFramBuffers) { + vkDestroyFramebuffer(device, framebuffer, nullptr); + } + vkDestroyPipeline(device, graphicsPipeline, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyRenderPass(device, renderPass, nullptr); + for (auto imageView : swapChainImageViews) { + vkDestroyImageView(device, imageView, nullptr); + } + vkDestroySwapchainKHR(device, swapChain, nullptr); + vkDestroyDevice(device, nullptr); + vkDestroySurfaceKHR(instance, surface, nullptr); + vkDestroyInstance(instance, nullptr); + } + } + + void Graphics::present(void* screenshotCallbackdata) { + vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX); + + uint32_t imageIndex; + vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex); + + if (imagesInFlight[imageIndex] != VK_NULL_HANDLE) { + vkWaitForFences(device, 1, &imagesInFlight[imageIndex], VK_TRUE, UINT64_MAX); + } + imagesInFlight[imageIndex] = inFlightFences[currentFrame]; + + VkSubmitInfo submitInfo{}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + + VkSemaphore waitSemaphores[] = { imageAvailableSemaphores[currentFrame] }; + VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; + submitInfo.waitSemaphoreCount = 1; + submitInfo.pWaitSemaphores = waitSemaphores; + submitInfo.pWaitDstStageMask = waitStages; + + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &commandBuffers[imageIndex]; + + VkSemaphore signalSemaphores[] = { renderFinishedSemaphores[currentFrame] }; + submitInfo.signalSemaphoreCount = 1; + submitInfo.pSignalSemaphores = signalSemaphores; + + vkResetFences(device, 1, &inFlightFences[currentFrame]); + + if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]) != VK_SUCCESS) { + throw love::Exception("failed to submit draw command buffer"); + } + + VkPresentInfoKHR presentInfo{}; + presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + + presentInfo.waitSemaphoreCount = 1; + presentInfo.pWaitSemaphores = signalSemaphores; + + VkSwapchainKHR swapChains[] = { swapChain }; + presentInfo.swapchainCount = 1; + presentInfo.pSwapchains = swapChains; + + presentInfo.pImageIndices = &imageIndex; + + vkQueuePresentKHR(presentQueue, &presentInfo); + + currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; + } + + void Graphics::createVulkanInstance() { + if (enableValidationLayers && !checkValidationSupport()) { + throw love::Exception("validation layers requested, but not available"); + } + + VkApplicationInfo appInfo{}; + appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + appInfo.pApplicationName = "LOVE"; + appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); //todo, get this version from somewhere else? + appInfo.pEngineName = "LOVE Engine"; + appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); //todo, same as above + appInfo.apiVersion = VK_API_VERSION_1_0; + + VkInstanceCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + createInfo.pApplicationInfo = &appInfo; + createInfo.pNext = nullptr; + + auto window = Module::getInstance(M_WINDOW); + const void* handle = window->getHandle(); + + unsigned int count; + if (SDL_Vulkan_GetInstanceExtensions((SDL_Window*)handle, &count, nullptr) != SDL_TRUE) { + throw love::Exception("couldn't retrieve sdl vulkan extensions"); + } + + std::vector extensions = {}; // can add more here + size_t addition_extension_count = extensions.size(); + extensions.resize(addition_extension_count + count); + + if (SDL_Vulkan_GetInstanceExtensions((SDL_Window*)handle, &count, extensions.data() + addition_extension_count) != SDL_TRUE) { + throw love::Exception("couldn't retrieve sdl vulkan extensions"); + } + + createInfo.enabledExtensionCount = static_cast(extensions.size()); + createInfo.ppEnabledExtensionNames = extensions.data(); + + if (enableValidationLayers) { + createInfo.enabledLayerCount = static_cast(validationLayers.size()); + createInfo.ppEnabledLayerNames = validationLayers.data(); + } + else { + createInfo.enabledLayerCount = 0; + createInfo.ppEnabledLayerNames = nullptr; + } + + if (vkCreateInstance( + &createInfo, + nullptr, + &instance) != VK_SUCCESS) { + throw love::Exception("couldn't create vulkan instance"); + } + } + + bool Graphics::checkValidationSupport() { + uint32_t layerCount; + vkEnumerateInstanceLayerProperties(&layerCount, nullptr); + + std::vector availableLayers(layerCount); + vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); + + for (const char* layerName : validationLayers) { + bool layerFound = false; + + for (const auto& layerProperties : availableLayers) { + if (strcmp(layerName, layerProperties.layerName) == 0) { + layerFound = true; + break; + } + } + + if (!layerFound) { + return false; + } + } + + return true; + } + + void Graphics::pickPhysicalDevice() { + uint32_t deviceCount = 0; + vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); + + if (deviceCount == 0) { + throw love::Exception("failed to find GPUs with Vulkan support"); + } + + std::vector devices(deviceCount); + vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); + + std::multimap candidates; + + for (const auto& device : devices) { + int score = rateDeviceSuitability(device); + candidates.insert(std::make_pair(score, device)); + } + + if (candidates.rbegin()->first > 0) { + physicalDevice = candidates.rbegin()->second; + } + else { + throw love::Exception("failed to find a suitable gpu"); + } + } + + bool Graphics::checkDeviceExtensionSupport(VkPhysicalDevice device) { + uint32_t extensionCount; + vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr); + + std::vector availableExtensions(extensionCount); + vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data()); + + std::set requiredExtensions(deviceExtensions.begin(), deviceExtensions.end()); + + for (const auto& extension : availableExtensions) { + requiredExtensions.erase(extension.extensionName); + } + + return requiredExtensions.empty(); + } + + int Graphics::rateDeviceSuitability(VkPhysicalDevice device) { + VkPhysicalDeviceProperties deviceProperties; + VkPhysicalDeviceFeatures deviceFeatures; + vkGetPhysicalDeviceProperties(device, &deviceProperties); + vkGetPhysicalDeviceFeatures(device, &deviceFeatures); + + int score = 1; + + // optional + + if (deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) { + score += 1000; + } + + // definitely needed + + QueueFamilyIndices indices = findQueueFamilies(device); + if (!indices.isComplete()) { + score = 0; + } + + bool extensionsSupported = checkDeviceExtensionSupport(device); + if (!extensionsSupported) { + score = 0; + } + + if (extensionsSupported) { + auto swapChainSupport = querySwapChainSupport(device); + bool swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty(); + if (!swapChainAdequate) { + score = 0; + } + } + + return score; + } + + Graphics::QueueFamilyIndices Graphics::findQueueFamilies(VkPhysicalDevice device) { + QueueFamilyIndices indices; + + uint32_t queueFamilyCount = 0; + vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); + + std::vector queueFamilies(queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); + + int i = 0; + for (const auto& queueFamily : queueFamilies) { + if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) { + indices.graphicsFamily = i; + } + + VkBool32 presentSupport = false; + vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport); + + if (presentSupport) { + indices.presentFamily = i; + } + + if (indices.isComplete()) { + break; + } + + i++; + } + + return indices; + } + + void Graphics::createLogicalDevice() { + QueueFamilyIndices indices = findQueueFamilies(physicalDevice); + + std::vector queueCreateInfos; + std::set uniqueQueueFamilies = { indices.graphicsFamily.value(), indices.presentFamily.value() }; + + float queuePriority = 1.0f; + for (uint32_t queueFamily : uniqueQueueFamilies) { + VkDeviceQueueCreateInfo queueCreateInfo{}; + queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queueCreateInfo.queueFamilyIndex = queueFamily; + queueCreateInfo.queueCount = 1; + queueCreateInfo.pQueuePriorities = &queuePriority; + queueCreateInfos.push_back(queueCreateInfo); + } + + VkPhysicalDeviceFeatures deviceFeatures{}; + + VkDeviceCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); + createInfo.pQueueCreateInfos = queueCreateInfos.data(); + createInfo.pEnabledFeatures = &deviceFeatures; + + createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); + createInfo.ppEnabledExtensionNames = deviceExtensions.data(); + + // can this be removed? + if (enableValidationLayers) { + createInfo.enabledLayerCount = static_cast(validationLayers.size()); + createInfo.ppEnabledLayerNames = validationLayers.data(); + } + else { + createInfo.enabledLayerCount = 0; + } + + if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { + throw love::Exception("failed to create logical device"); + } + + vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue); + vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue); + } + + void Graphics::createSurface() { + auto window = Module::getInstance(M_WINDOW); + const void* handle = window->getHandle(); + if (SDL_Vulkan_CreateSurface((SDL_Window*)handle, instance, &surface) != SDL_TRUE) { + throw love::Exception("failed to create window surface"); + } + } + + Graphics::SwapChainSupportDetails Graphics::querySwapChainSupport(VkPhysicalDevice device) { + SwapChainSupportDetails details; + + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities); + + uint32_t formatCount; + vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr); + + if (formatCount != 0) { + details.formats.resize(formatCount); + vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data()); + } + + uint32_t presentModeCount; + vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr); + + if (presentModeCount != 0) { + details.presentModes.resize(presentModeCount); + vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data()); + } + + return details; + } + + void Graphics::createSwapChain() { + SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice); + + VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats); + VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes); + VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities); + + uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1; + if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) { + imageCount = swapChainSupport.capabilities.maxImageCount; + } + + VkSwapchainCreateInfoKHR createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + createInfo.surface = surface; + + createInfo.minImageCount = imageCount; + createInfo.imageFormat = surfaceFormat.format; + createInfo.imageColorSpace = surfaceFormat.colorSpace; + createInfo.imageExtent = extent; + createInfo.imageArrayLayers = 1; + createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + + QueueFamilyIndices indices = findQueueFamilies(physicalDevice); + uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(), indices.presentFamily.value() }; + + if (indices.graphicsFamily != indices.presentFamily) { + createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + createInfo.queueFamilyIndexCount = 2; + createInfo.pQueueFamilyIndices = queueFamilyIndices; + } + else { + createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + createInfo.queueFamilyIndexCount = 0; + createInfo.pQueueFamilyIndices = nullptr; + } + + createInfo.preTransform = swapChainSupport.capabilities.currentTransform; + createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + createInfo.presentMode = presentMode; + createInfo.clipped = VK_TRUE; + createInfo.oldSwapchain = VK_NULL_HANDLE; + + if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) { + throw love::Exception("failed to create swap chain"); + } + + vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); + swapChainImages.resize(imageCount); + vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data()); + + swapChainImageFormat = surfaceFormat.format; + swapChainExtent = extent; + } + + VkSurfaceFormatKHR Graphics::chooseSwapSurfaceFormat(const std::vector& availableFormats) { + for (const auto& availableFormat : availableFormats) { + if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { + return availableFormat; + } + } + + return availableFormats[0]; + } + + VkPresentModeKHR Graphics::chooseSwapPresentMode(const std::vector& availablePresentModes) { + // needed ? + for (const auto& availablePresentMode : availablePresentModes) { + if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) { + return availablePresentMode; + } + } + + return VK_PRESENT_MODE_FIFO_KHR; + } + + VkExtent2D Graphics::chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) { + if (capabilities.currentExtent.width != UINT32_MAX) { + return capabilities.currentExtent; + } + else { + auto window = Module::getInstance(M_WINDOW); + const void* handle = window->getHandle(); + + int width, height; + // is this the equivalent of glfwGetFramebufferSize ? + SDL_Vulkan_GetDrawableSize((SDL_Window*)handle, &width, &height); + + VkExtent2D actualExtent = { + static_cast(width), + static_cast(height) + }; + + actualExtent.width = std::clamp(actualExtent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width); + actualExtent.height = std::clamp(actualExtent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height); + + return actualExtent; + } + } + + void Graphics::createImageViews() { + swapChainImageViews.resize(swapChainImages.size()); + + for (size_t i = 0; i < swapChainImages.size(); i++) { + VkImageViewCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + createInfo.image = swapChainImages[i]; + createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + createInfo.format = swapChainImageFormat; + createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + createInfo.subresourceRange.baseMipLevel = 0; + createInfo.subresourceRange.levelCount = 1; + createInfo.subresourceRange.baseArrayLayer = 0; + createInfo.subresourceRange.layerCount = 1; + + if (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) { + throw love::Exception("failed to create image views"); + } + } + } + + void Graphics::createRenderPass() { + VkAttachmentDescription colorAttachment{}; + colorAttachment.format = swapChainImageFormat; + colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; + colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + VkAttachmentReference colorAttachmentRef{}; + colorAttachmentRef.attachment = 0; + colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpass{}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &colorAttachmentRef; + + VkSubpassDependency dependency{}; + dependency.srcSubpass = VK_SUBPASS_EXTERNAL; + dependency.dstSubpass = 0; + dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.srcAccessMask = 0; + dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + + VkRenderPassCreateInfo renderPassInfo{}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassInfo.attachmentCount = 1; + renderPassInfo.pAttachments = &colorAttachment; + renderPassInfo.subpassCount = 1; + renderPassInfo.pSubpasses = &subpass; + renderPassInfo.dependencyCount = 1; + renderPassInfo.pDependencies = &dependency; + + if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) { + throw love::Exception("failed to create render pass"); + } + } + + static VkShaderModule createShaderModule(VkDevice device, const std::vector& code) { + VkShaderModuleCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + createInfo.codeSize = code.size(); + createInfo.pCode = reinterpret_cast(code.data()); + + VkShaderModule shaderModule; + if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { + throw love::Exception("failed to create shader module"); + } + + return shaderModule; + } + + void Graphics::createGraphicsPipeline() { + // love::graphics::vulkan::Shader* shader = dynamic_cast(getShader()); + // auto shaderStages = shader->getShaderStages(); + + auto vertShaderCode = readFile("vert.spv"); + auto fragShaderCode = readFile("frag.spv"); + + VkShaderModule vertShaderModule = createShaderModule(device, vertShaderCode); + VkShaderModule fragShaderModule = createShaderModule(device, fragShaderCode); + + VkPipelineShaderStageCreateInfo vertShaderStageInfo{}; + vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; + vertShaderStageInfo.module = vertShaderModule; + vertShaderStageInfo.pName = "main"; + + VkPipelineShaderStageCreateInfo fragShaderStageInfo{}; + fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + fragShaderStageInfo.module = fragShaderModule; + fragShaderStageInfo.pName = "main"; + + VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo, fragShaderStageInfo }; + + VkPipelineVertexInputStateCreateInfo vertexInputInfo{}; + vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + + // todo later + vertexInputInfo.vertexBindingDescriptionCount = 0; + vertexInputInfo.pVertexBindingDescriptions = nullptr; + vertexInputInfo.vertexAttributeDescriptionCount = 0; + vertexInputInfo.pVertexAttributeDescriptions = nullptr; + + VkPipelineInputAssemblyStateCreateInfo inputAssembly{}; + inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + inputAssembly.primitiveRestartEnable = VK_FALSE; + + VkViewport viewport{}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = (float)swapChainExtent.width; + viewport.height = (float)swapChainExtent.height; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + VkRect2D scissor{}; + scissor.offset = { 0, 0 }; + scissor.extent = swapChainExtent; + + VkPipelineViewportStateCreateInfo viewportState{}; + viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportState.viewportCount = 1; + viewportState.pViewports = &viewport; + viewportState.scissorCount = 1; + viewportState.pScissors = &scissor; + + VkPipelineRasterizationStateCreateInfo rasterizer{}; + rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizer.depthClampEnable = VK_FALSE; + rasterizer.rasterizerDiscardEnable = VK_FALSE; + rasterizer.polygonMode = VK_POLYGON_MODE_FILL; + rasterizer.lineWidth = 1.0f; + rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; + rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; + rasterizer.depthBiasEnable = VK_FALSE; + rasterizer.depthBiasConstantFactor = 0.0f; + rasterizer.depthBiasClamp = 0.0f; + rasterizer.depthBiasSlopeFactor = 0.0f; + + VkPipelineMultisampleStateCreateInfo multisampling{}; + multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling.sampleShadingEnable = VK_FALSE; + multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + multisampling.minSampleShading = 1.0f; // Optional + multisampling.pSampleMask = nullptr; // Optional + multisampling.alphaToCoverageEnable = VK_FALSE; // Optional + multisampling.alphaToOneEnable = VK_FALSE; // Optional + + VkPipelineColorBlendAttachmentState colorBlendAttachment{}; + colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + colorBlendAttachment.blendEnable = VK_FALSE; + + VkPipelineColorBlendStateCreateInfo colorBlending{}; + colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlending.logicOpEnable = VK_FALSE; + colorBlending.logicOp = VK_LOGIC_OP_COPY; + colorBlending.attachmentCount = 1; + colorBlending.pAttachments = &colorBlendAttachment; + colorBlending.blendConstants[0] = 0.0f; + colorBlending.blendConstants[1] = 0.0f; + colorBlending.blendConstants[2] = 0.0f; + colorBlending.blendConstants[3] = 0.0f; + + VkPipelineLayoutCreateInfo pipelineLayoutInfo{}; + pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutInfo.setLayoutCount = 0; + pipelineLayoutInfo.pushConstantRangeCount = 0; + + if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) { + throw love::Exception("failed to create pipeline layout"); + } + + VkGraphicsPipelineCreateInfo pipelineInfo{}; + pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + // pipelineInfo.stageCount = static_cast(shaderStages.size()); + // pipelineInfo.pStages = shaderStages.data(); + pipelineInfo.stageCount = 2; + pipelineInfo.pStages = shaderStages; + pipelineInfo.pVertexInputState = &vertexInputInfo; + pipelineInfo.pInputAssemblyState = &inputAssembly; + pipelineInfo.pViewportState = &viewportState; + pipelineInfo.pRasterizationState = &rasterizer; + pipelineInfo.pMultisampleState = &multisampling; + pipelineInfo.pDepthStencilState = nullptr; + pipelineInfo.pColorBlendState = &colorBlending; + pipelineInfo.pDynamicState = nullptr; + pipelineInfo.layout = pipelineLayout; + pipelineInfo.renderPass = renderPass; + pipelineInfo.subpass = 0; + pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; + pipelineInfo.basePipelineIndex = -1; + + if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) { + throw love::Exception("failed to create graphics pipeline"); + } + + vkDestroyShaderModule(device, vertShaderModule, nullptr); + vkDestroyShaderModule(device, fragShaderModule, nullptr); + } + + void Graphics::createFramebuffers() { + swapChainFramBuffers.resize(swapChainImageViews.size()); + for (size_t i = 0; i < swapChainImageViews.size(); i++) { + VkImageView attachments[] = { + swapChainImageViews[i] + }; + + VkFramebufferCreateInfo framebufferInfo{}; + framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebufferInfo.renderPass = renderPass; + framebufferInfo.attachmentCount = 1; + framebufferInfo.pAttachments = attachments; + framebufferInfo.width = swapChainExtent.width; + framebufferInfo.height = swapChainExtent.height; + framebufferInfo.layers = 1; + + if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramBuffers[i]) != VK_SUCCESS) { + throw love::Exception("failed to create framebuffers"); + } + } + } + + void Graphics::createCommandPool() { + QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice); + + VkCommandPoolCreateInfo poolInfo{}; + poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value(); + poolInfo.flags = 0; + + if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) { + throw love::Exception("failed to create command pool"); + } + } + + void Graphics::createCommandBuffers() { + commandBuffers.resize(swapChainFramBuffers.size()); + + VkCommandBufferAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.commandPool = commandPool; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandBufferCount = (uint32_t)commandBuffers.size(); + + if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) { + throw love::Exception("failed to allocate command buffers"); + } + + for (size_t i = 0; i < commandBuffers.size(); i++) { + VkCommandBufferBeginInfo beginInfo{}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = 0; + beginInfo.pInheritanceInfo = nullptr; + + if (vkBeginCommandBuffer(commandBuffers[i], &beginInfo) != VK_SUCCESS) { + throw love::Exception("failed to begin recording command buffer"); + } + + VkRenderPassBeginInfo renderPassInfo{}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + renderPassInfo.renderPass = renderPass; + renderPassInfo.framebuffer = swapChainFramBuffers[i]; + renderPassInfo.renderArea.offset = { 0, 0 }; + renderPassInfo.renderArea.extent = swapChainExtent; + + VkClearValue clearColor = { {{0.0f, 0.0f, 0.0f, 1.0f}} }; + renderPassInfo.clearValueCount = 1; + renderPassInfo.pClearValues = &clearColor; + + // this definitely doesn't belong in here, but leaving here for future reference + vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); + vkCmdBindPipeline(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline); + vkCmdDraw(commandBuffers[i], 3, 1, 0, 0); + + vkCmdEndRenderPass(commandBuffers[i]); + if (vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS) { + throw love::Exception("failed to record command buffer"); + } + } + } + + void Graphics::createSyncObjects() { + imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT); + renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT); + inFlightFences.resize(MAX_FRAMES_IN_FLIGHT); + imagesInFlight.resize(swapChainImages.size(), VK_NULL_HANDLE); + + VkSemaphoreCreateInfo semaphoreInfo{}; + semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + + VkFenceCreateInfo fenceInfo{}; + fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; + + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphores[i]) != VK_SUCCESS || + vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphores[i]) != VK_SUCCESS || + vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences[i]) != VK_SUCCESS) { + throw love::Exception("failed to create synchronization objects for a frame!"); + } + } + } + + love::graphics::Graphics* createInstance() { + love::graphics::Graphics* instance = nullptr; + + try { + instance = new Graphics(); + } + catch (love::Exception& e) { + printf("Cannot create Vulkan renderer: %s\n", e.what()); + } + + return instance; + } + } + } +} diff --git a/src/modules/graphics/vulkan/Graphics.h b/src/modules/graphics/vulkan/Graphics.h new file mode 100644 index 000000000..a7a14dc8a --- /dev/null +++ b/src/modules/graphics/vulkan/Graphics.h @@ -0,0 +1,141 @@ +#ifndef LOVE_GRAPHICS_VULKAN_GRAPHICS_H +#define LOVE_GRAPHICS_VULKAN_GRAPHICS_H + +#include "graphics/Graphics.h" +#include + +#include + +#include +#include + + +namespace love { + namespace graphics { + namespace vulkan { + class Graphics final : public love::graphics::Graphics { + public: + Graphics(); + + void initVulkan(); + + virtual ~Graphics(); + + const char* getName() const override; + + const VkDevice getDevice() const { + return device; + } + + // implementation for virtual functions + Texture* newTexture(const Texture::Settings& settings, const Texture::Slices* data = nullptr) override { return nullptr; } + Buffer* newBuffer(const Buffer::Settings& settings, const std::vector& format, const void* data, size_t size, size_t arraylength) override { return nullptr; } + void clear(OptionalColorD color, OptionalInt stencil, OptionalDouble depth) override {} + void clear(const std::vector& colors, OptionalInt stencil, OptionalDouble depth) override {} + void discard(const std::vector& colorbuffers, bool depthstencil) override {} + void present(void* screenshotCallbackdata) override; + void setViewportSize(int width, int height, int pixelwidth, int pixelheight) override {} + bool setMode(void* context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa) override { return false; } + void unSetMode() override {} + void setActive(bool active) override {} + int getRequestedBackbufferMSAA() const override { return 0; } + int getBackbufferMSAA() const override { return 0; } + void setColor(Colorf c) override {} + void setScissor(const Rect& rect) override {} + void setScissor() override {} + void drawToStencilBuffer(StencilAction action, int value) override {} + void stopDrawToStencilBuffer() override {} + void setStencilTest(CompareMode compare, int value) override {} + void setDepthMode(CompareMode compare, bool write) override {} + void setFrontFaceWinding(Winding winding) override {} + void setColorMask(ColorChannelMask mask) override {} + void setBlendState(const BlendState& blend) override {} + void setPointSize(float size) override {} + void setWireframe(bool enable) override {} + PixelFormat getSizedFormat(PixelFormat format, bool rendertarget, bool readable) const override { return PIXELFORMAT_UNKNOWN; } + bool isPixelFormatSupported(PixelFormat format, PixelFormatUsageFlags usage, bool sRGB = false) override { return false; } + Renderer getRenderer() const override { return RENDERER_VULKAN; } + bool usesGLSLES() const override { return false; } + RendererInfo getRendererInfo() const override { return {}; } + void draw(const DrawCommand& cmd) override {} + void draw(const DrawIndexedCommand& cmd) override {} + void drawQuads(int start, int count, const VertexAttributes& attributes, const BufferBindings& buffers, Texture* texture) override {} + + protected: + ShaderStage* newShaderStageInternal(ShaderStageType stage, const std::string& cachekey, const std::string& source, bool gles) override { return nullptr; } + Shader* newShaderInternal(StrongRef stages[SHADERSTAGE_MAX_ENUM]) override { return nullptr; } + StreamBuffer* newStreamBuffer(BufferUsage type, size_t size) override { return nullptr; } + bool dispatch(int x, int y, int z) override { return false; } + void setRenderTargetsInternal(const RenderTargets& rts, int w, int h, int pixelw, int pixelh, bool hasSRGBtexture) override {} + void initCapabilities() override {} + void getAPIStats(int& shaderswitches) const override {} + + private: + bool init = false; + // vulkan specific member functions and variables + + struct QueueFamilyIndices { + std::optional graphicsFamily; + std::optional presentFamily; + + bool isComplete() { + return graphicsFamily.has_value() && presentFamily.has_value(); + } + }; + + struct SwapChainSupportDetails { + VkSurfaceCapabilitiesKHR capabilities; + std::vector formats; + std::vector presentModes; + }; + + void createVulkanInstance(); + bool checkValidationSupport(); + void pickPhysicalDevice(); + int rateDeviceSuitability(VkPhysicalDevice device); + QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device); + void createLogicalDevice(); + void createSurface(); + bool checkDeviceExtensionSupport(VkPhysicalDevice device); + SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device); + VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats); + VkPresentModeKHR chooseSwapPresentMode(const std::vector& availablePresentModes); + VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities); + void createSwapChain(); + void createImageViews(); + void createRenderPass(); + void createGraphicsPipeline(); + void createFramebuffers(); + void createCommandPool(); + void createCommandBuffers(); + void createSyncObjects(); + + VkInstance instance; + VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; + VkDevice device; + VkQueue graphicsQueue; + VkQueue presentQueue; + VkSurfaceKHR surface; + VkSwapchainKHR swapChain; + std::vector swapChainImages; + VkFormat swapChainImageFormat; + VkExtent2D swapChainExtent; + std::vector swapChainImageViews; + VkPipelineLayout pipelineLayout; + VkRenderPass renderPass; + VkPipeline graphicsPipeline; + std::vector swapChainFramBuffers; + VkCommandPool commandPool; + std::vector commandBuffers; + + std::vector imageAvailableSemaphores; + std::vector renderFinishedSemaphores; + std::vector inFlightFences; + std::vector imagesInFlight; + size_t currentFrame = 0; + }; + } + } +} + +#endif diff --git a/src/modules/graphics/vulkan/Shader.cpp b/src/modules/graphics/vulkan/Shader.cpp new file mode 100644 index 000000000..3e26be7e4 --- /dev/null +++ b/src/modules/graphics/vulkan/Shader.cpp @@ -0,0 +1,44 @@ +#include "Shader.h" + +#include "libraries/glslang/glslang/Public/ShaderLang.h" +#include "libraries/glslang/SPIRV/GlslangToSpv.h" +#include + +namespace love { + namespace graphics { + namespace vulkan { + static VkShaderStageFlagBits getStageBit(ShaderStageType type) { + switch (type) { + case SHADERSTAGE_VERTEX: + return VK_SHADER_STAGE_VERTEX_BIT; + case SHADERSTAGE_PIXEL: + return VK_SHADER_STAGE_FRAGMENT_BIT; + case SHADERSTAGE_COMPUTE: + return VK_SHADER_STAGE_COMPUTE_BIT; + } + throw love::Exception("invalid type"); + } + + Shader::Shader(StrongRef stages[]) + : graphics::Shader(stages) { + + if (false) { + for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++) { + if (!stages[i]) + continue; + + auto stage = dynamic_cast(stages[i].get()); + + VkPipelineShaderStageCreateInfo shaderStageInfo{}; + shaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shaderStageInfo.stage = getStageBit(stage->getStageType()); + shaderStageInfo.module = stage->getShaderModule(); + shaderStageInfo.pName = "main"; + + shaderStages.push_back(shaderStageInfo); + } + } + } + } + } +} \ No newline at end of file diff --git a/src/modules/graphics/vulkan/Shader.h b/src/modules/graphics/vulkan/Shader.h new file mode 100644 index 000000000..1cdedd605 --- /dev/null +++ b/src/modules/graphics/vulkan/Shader.h @@ -0,0 +1,30 @@ +#ifndef LOVE_GRAPHICS_VULKAN_SHADER_H +#define LOVE_GRAPHICS_VULKAN_SHADER_H + +#include +#include +#include "libraries/glslang/glslang/Public/ShaderLang.h" +#include "libraries/glslang/SPIRV/GlslangToSpv.h" +#include + + +namespace love { + namespace graphics { + namespace vulkan { + class Shader final : public graphics::Shader { + public: + Shader(StrongRef stages[]); + virtual ~Shader() = default; + + const std::vector& getShaderStages() const { + return shaderStages; + } + + private: + std::vector shaderStages; + }; + } + } +} + +#endif diff --git a/src/modules/graphics/vulkan/ShaderStage.cpp b/src/modules/graphics/vulkan/ShaderStage.cpp new file mode 100644 index 000000000..3152ecf5c --- /dev/null +++ b/src/modules/graphics/vulkan/ShaderStage.cpp @@ -0,0 +1,195 @@ +#include "ShaderStage.h" + +#include "Graphics.h" + +#include +#include + + +namespace love { + namespace graphics { + namespace vulkan { + // TODO: Use love.graphics to determine actual limits? + static const TBuiltInResource defaultTBuiltInResource = { + /* .MaxLights = */ 32, + /* .MaxClipPlanes = */ 6, + /* .MaxTextureUnits = */ 32, + /* .MaxTextureCoords = */ 32, + /* .MaxVertexAttribs = */ 64, + /* .MaxVertexUniformComponents = */ 16384, + /* .MaxVaryingFloats = */ 128, + /* .MaxVertexTextureImageUnits = */ 32, + /* .MaxCombinedTextureImageUnits = */ 80, + /* .MaxTextureImageUnits = */ 32, + /* .MaxFragmentUniformComponents = */ 16384, + /* .MaxDrawBuffers = */ 8, + /* .MaxVertexUniformVectors = */ 4096, + /* .MaxVaryingVectors = */ 32, + /* .MaxFragmentUniformVectors = */ 4096, + /* .MaxVertexOutputVectors = */ 32, + /* .MaxFragmentInputVectors = */ 31, + /* .MinProgramTexelOffset = */ -8, + /* .MaxProgramTexelOffset = */ 7, + /* .MaxClipDistances = */ 8, + /* .MaxComputeWorkGroupCountX = */ 65535, + /* .MaxComputeWorkGroupCountY = */ 65535, + /* .MaxComputeWorkGroupCountZ = */ 65535, + /* .MaxComputeWorkGroupSizeX = */ 1024, + /* .MaxComputeWorkGroupSizeY = */ 1024, + /* .MaxComputeWorkGroupSizeZ = */ 64, + /* .MaxComputeUniformComponents = */ 1024, + /* .MaxComputeTextureImageUnits = */ 32, + /* .MaxComputeImageUniforms = */ 16, + /* .MaxComputeAtomicCounters = */ 4096, + /* .MaxComputeAtomicCounterBuffers = */ 8, + /* .MaxVaryingComponents = */ 128, + /* .MaxVertexOutputComponents = */ 128, + /* .MaxGeometryInputComponents = */ 128, + /* .MaxGeometryOutputComponents = */ 128, + /* .MaxFragmentInputComponents = */ 128, + /* .MaxImageUnits = */ 192, + /* .MaxCombinedImageUnitsAndFragmentOutputs = */ 144, + /* .MaxCombinedShaderOutputResources = */ 144, + /* .MaxImageSamples = */ 32, + /* .MaxVertexImageUniforms = */ 16, + /* .MaxTessControlImageUniforms = */ 16, + /* .MaxTessEvaluationImageUniforms = */ 16, + /* .MaxGeometryImageUniforms = */ 16, + /* .MaxFragmentImageUniforms = */ 16, + /* .MaxCombinedImageUniforms = */ 80, + /* .MaxGeometryTextureImageUnits = */ 16, + /* .MaxGeometryOutputVertices = */ 256, + /* .MaxGeometryTotalOutputComponents = */ 1024, + /* .MaxGeometryUniformComponents = */ 1024, + /* .MaxGeometryVaryingComponents = */ 64, + /* .MaxTessControlInputComponents = */ 128, + /* .MaxTessControlOutputComponents = */ 128, + /* .MaxTessControlTextureImageUnits = */ 16, + /* .MaxTessControlUniformComponents = */ 1024, + /* .MaxTessControlTotalOutputComponents = */ 4096, + /* .MaxTessEvaluationInputComponents = */ 128, + /* .MaxTessEvaluationOutputComponents = */ 128, + /* .MaxTessEvaluationTextureImageUnits = */ 16, + /* .MaxTessEvaluationUniformComponents = */ 1024, + /* .MaxTessPatchComponents = */ 120, + /* .MaxPatchVertices = */ 32, + /* .MaxTessGenLevel = */ 64, + /* .MaxViewports = */ 16, + /* .MaxVertexAtomicCounters = */ 4096, + /* .MaxTessControlAtomicCounters = */ 4096, + /* .MaxTessEvaluationAtomicCounters = */ 4096, + /* .MaxGeometryAtomicCounters = */ 4096, + /* .MaxFragmentAtomicCounters = */ 4096, + /* .MaxCombinedAtomicCounters = */ 4096, + /* .MaxAtomicCounterBindings = */ 8, + /* .MaxVertexAtomicCounterBuffers = */ 8, + /* .MaxTessControlAtomicCounterBuffers = */ 8, + /* .MaxTessEvaluationAtomicCounterBuffers = */ 8, + /* .MaxGeometryAtomicCounterBuffers = */ 8, + /* .MaxFragmentAtomicCounterBuffers = */ 8, + /* .MaxCombinedAtomicCounterBuffers = */ 8, + /* .MaxAtomicCounterBufferSize = */ 16384, + /* .MaxTransformFeedbackBuffers = */ 4, + /* .MaxTransformFeedbackInterleavedComponents = */ 64, + /* .MaxCullDistances = */ 8, + /* .MaxCombinedClipAndCullDistances = */ 8, + /* .MaxSamples = */ 32, + /* .maxMeshOutputVerticesNV = */ 256, + /* .maxMeshOutputPrimitivesNV = */ 512, + /* .maxMeshWorkGroupSizeX_NV = */ 32, + /* .maxMeshWorkGroupSizeY_NV = */ 1, + /* .maxMeshWorkGroupSizeZ_NV = */ 1, + /* .maxTaskWorkGroupSizeX_NV = */ 32, + /* .maxTaskWorkGroupSizeY_NV = */ 1, + /* .maxTaskWorkGroupSizeZ_NV = */ 1, + /* .maxMeshViewCountNV = */ 4, + /* .maxDualSourceDrawBuffersEXT = */ 1, + /* .limits = */{ + /* .nonInductiveForLoops = */ 1, + /* .whileLoops = */ 1, + /* .doWhileLoops = */ 1, + /* .generalUniformIndexing = */ 1, + /* .generalAttributeMatrixVectorIndexing = */ 1, + /* .generalVaryingIndexing = */ 1, + /* .generalSamplerIndexing = */ 1, + /* .generalVariableIndexing = */ 1, + /* .generalConstantMatrixVectorIndexing = */ 1, + } + }; + + static EShLanguage getShaderStage(ShaderStageType stage) { + switch (stage) { + case SHADERSTAGE_VERTEX: return EShLangVertex; + case SHADERSTAGE_PIXEL: return EShLangFragment; + case SHADERSTAGE_COMPUTE: return EShLangCompute; + case SHADERSTAGE_MAX_ENUM: return EShLangCount; + } + return EShLangCount; + } + + ShaderStage::ShaderStage(love::graphics::Graphics* gfx, ShaderStageType stage, const std::string& glsl, bool gles, const std::string& cachekey) + : love::graphics::ShaderStage(gfx, stage, glsl, gles, cachekey) { + if (false) { + using namespace glslang; + + auto shaderStage = getShaderStage(stage); + + TShader* shader = new TShader(shaderStage); + shader->setEnvInput(EShSourceGlsl, shaderStage, EShClientVulkan, 450); + shader->setEnvClient(EShClientVulkan, EShTargetVulkan_1_2); + shader->setEnvTarget(EShTargetSpv, EShTargetSpv_1_5); + shader->setAutoMapLocations(true); + shader->setAutoMapBindings(true); + shader->setEnvInputVulkanRulesRelaxed(); + shader->setGlobalUniformBinding(0); + shader->setGlobalUniformSet(0); + + const std::string& source = glsl; + const char* csrc = source.c_str(); + int srclen = (int)source.length(); + shader->setStringsWithLengths(&csrc, &srclen, 1); + + int defaultversion = 450; + EProfile defaultprofile = ECoreProfile; + bool forcedefault = false; + bool forwardcompat = true; + + if (!shader->parse(&defaultTBuiltInResource, defaultversion, defaultprofile, forcedefault, forwardcompat, EShMsgSuppressWarnings)) { + const char* stagename = "unknown"; + ShaderStage::getConstant(stage, stagename); + + std::string err = "Error parsing " + std::string(stagename) + " shader:\n\n" + + std::string(shader->getInfoLog()) + "\n" + + std::string(shader->getInfoDebugLog()); + + delete shader; + + throw love::Exception("%s", err.c_str()); + } + + auto intermediate = shader->getIntermediate(); + std::vector code; + GlslangToSpv(*intermediate, code); + + VkShaderModuleCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + createInfo.codeSize = code.size(); + createInfo.pCode = reinterpret_cast(code.data()); + + Graphics* vkGfx = (Graphics*)gfx; + device = vkGfx->getDevice(); + + if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { + throw love::Exception("failed to create shader module"); + } + } + + } + + ShaderStage::~ShaderStage() { + if (false) + vkDestroyShaderModule(device, shaderModule, nullptr); + } + } + } +} diff --git a/src/modules/graphics/vulkan/ShaderStage.h b/src/modules/graphics/vulkan/ShaderStage.h new file mode 100644 index 000000000..49ced1f4f --- /dev/null +++ b/src/modules/graphics/vulkan/ShaderStage.h @@ -0,0 +1,29 @@ +#ifndef LOVE_GRAPHICS_VULKAN_SHADERSTAGE_H +#define LOVE_GRAPHICS_VULKAN_SHADERSTAGE_H + +#include "graphics/ShaderStage.h" +#include "modules/graphics/Graphics.h" +#include + +namespace love { + namespace graphics { + namespace vulkan { + class ShaderStage final : public graphics::ShaderStage { + public: + ShaderStage(love::graphics::Graphics* gfx, ShaderStageType stage, const std::string& glsl, bool gles, const std::string& cachekey); + virtual ~ShaderStage(); + + VkShaderModule getShaderModule() const { + return shaderModule; + } + + private: + VkShaderModule shaderModule; + VkDevice device; + + }; + } + } +} + +#endif diff --git a/src/modules/graphics/wrap_Graphics.cpp b/src/modules/graphics/wrap_Graphics.cpp index fb398de03..a6dcc8055 100644 --- a/src/modules/graphics/wrap_Graphics.cpp +++ b/src/modules/graphics/wrap_Graphics.cpp @@ -3704,7 +3704,11 @@ extern "C" int luaopen_love_graphics(lua_State *L) #if defined(LOVE_MACOS) || defined(LOVE_IOS) renderers.push_back(Graphics::RENDERER_METAL); #endif +#ifdef LOVE_GRAPHICS_VULKAN + renderers.push_back(Graphics::RENDERER_VULKAN); +#else renderers.push_back(Graphics::RENDERER_OPENGL); +#endif instance = Graphics::createInstance(renderers); } diff --git a/src/modules/window/sdl/Window.cpp b/src/modules/window/sdl/Window.cpp index d917f514e..1e150bca6 100644 --- a/src/modules/window/sdl/Window.cpp +++ b/src/modules/window/sdl/Window.cpp @@ -21,6 +21,7 @@ // LOVE #include "common/config.h" #include "graphics/Graphics.h" +#include "graphics/vulkan/Graphics.h" #include "Window.h" #ifdef LOVE_ANDROID @@ -137,6 +138,7 @@ void Window::setGLFramebufferAttributes(bool sRGB) void Window::setGLContextAttributes(const ContextAttribs &attribs) { +#ifndef LOVE_GRAPHICS_VULKAN int profilemask = 0; int contextflags = 0; @@ -154,10 +156,12 @@ void Window::setGLContextAttributes(const ContextAttribs &attribs) SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, attribs.versionMinor); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profilemask); SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, contextflags); +#endif } bool Window::checkGLVersion(const ContextAttribs &attribs, std::string &outversion) { +#ifndef LOVE_GRAPHICS_VULKAN typedef unsigned char GLubyte; typedef unsigned int GLenum; typedef const GLubyte *(APIENTRY *glGetStringPtr)(GLenum name); @@ -202,6 +206,9 @@ bool Window::checkGLVersion(const ContextAttribs &attribs, std::string &outversi return false; return true; +#else + return true; +#endif } std::vector Window::getContextAttribsList() const @@ -314,11 +321,13 @@ bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowfla const auto create = [&](const ContextAttribs *attribs) -> bool { +#ifndef LOVE_GRAPHICS_VULKAN if (glcontext) { SDL_GL_DeleteContext(glcontext); glcontext = nullptr; } +#endif #ifdef LOVE_GRAPHICS_METAL if (metalView) @@ -335,6 +344,7 @@ bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowfla window = nullptr; } +#ifndef LOVE_GRAPHICS_VULKAN window = SDL_CreateWindow(title.c_str(), x, y, w, h, windowflags); if (!window) @@ -366,6 +376,16 @@ bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowfla } return true; + +#else + window = SDL_CreateWindow(title.c_str(), x, y, w, h, SDL_WINDOW_VULKAN); + + love::graphics::Graphics* gfx = graphics.get(); + love::graphics::vulkan::Graphics* vgfx = (love::graphics::vulkan::Graphics*)gfx; + vgfx->initVulkan(); + + return true; +#endif }; if (renderer == graphics::Graphics::RENDERER_OPENGL) @@ -575,14 +595,18 @@ bool Window::setWindow(int width, int height, WindowSettings *settings) } else { - if (renderer == graphics::Graphics::RENDERER_OPENGL) + if (renderer == graphics::Graphics::RENDERER_OPENGL) { sdlflags |= SDL_WINDOW_OPENGL; + } #ifdef LOVE_GRAPHICS_METAL if (renderer == graphics::Graphics::RENDERER_METAL) sdlflags |= SDL_WINDOW_METAL; #endif + if (renderer == graphics::Graphics::RENDERER_VULKAN) + sdlflags |= SDL_WINDOW_VULKAN; + if (f.resizable) sdlflags |= SDL_WINDOW_RESIZABLE; From 81e3bed7785adeee31b0db7d363b70b44b01cf34 Mon Sep 17 00:00:00 2001 From: niki Date: Thu, 6 Jan 2022 01:42:50 +0100 Subject: [PATCH 002/170] handle resizing correctly --- src/modules/graphics/vulkan/Graphics.cpp | 88 +++++++++++++++++------- src/modules/graphics/vulkan/Graphics.h | 6 +- src/modules/window/sdl/Window.cpp | 2 +- 3 files changed, 68 insertions(+), 28 deletions(-) diff --git a/src/modules/graphics/vulkan/Graphics.cpp b/src/modules/graphics/vulkan/Graphics.cpp index 7a0d680a4..f7ed76d97 100644 --- a/src/modules/graphics/vulkan/Graphics.cpp +++ b/src/modules/graphics/vulkan/Graphics.cpp @@ -74,37 +74,22 @@ namespace love { } Graphics::~Graphics() { - if (init) { - for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { - vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr); - vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr); - vkDestroyFence(device, inFlightFences[i], nullptr); - } - if (vkDeviceWaitIdle(device) != VK_SUCCESS) { - throw love::Exception("vkDeviceWaitIdle failed"); - } - vkDestroyCommandPool(device, commandPool, nullptr); - for (auto framebuffer : swapChainFramBuffers) { - vkDestroyFramebuffer(device, framebuffer, nullptr); - } - vkDestroyPipeline(device, graphicsPipeline, nullptr); - vkDestroyPipelineLayout(device, pipelineLayout, nullptr); - vkDestroyRenderPass(device, renderPass, nullptr); - for (auto imageView : swapChainImageViews) { - vkDestroyImageView(device, imageView, nullptr); - } - vkDestroySwapchainKHR(device, swapChain, nullptr); - vkDestroyDevice(device, nullptr); - vkDestroySurfaceKHR(instance, surface, nullptr); - vkDestroyInstance(instance, nullptr); - } + cleanup(); } void Graphics::present(void* screenshotCallbackdata) { vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX); uint32_t imageIndex; - vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex); + VkResult result = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex); + + if (result == VK_ERROR_OUT_OF_DATE_KHR) { + recreateSwapChain(); + return; + } + else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { + throw love::Exception("failed to acquire swap chain image"); + } if (imagesInFlight[imageIndex] != VK_NULL_HANDLE) { vkWaitForFences(device, 1, &imagesInFlight[imageIndex], VK_TRUE, UINT64_MAX); @@ -145,11 +130,23 @@ namespace love { presentInfo.pImageIndices = &imageIndex; - vkQueuePresentKHR(presentQueue, &presentInfo); + result = vkQueuePresentKHR(presentQueue, &presentInfo); + + if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebufferResized) { + framebufferResized = false; + recreateSwapChain(); + } + else if (result != VK_SUCCESS) { + throw love::Exception("failed to present swap chain image"); + } currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; } + void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelheight) { + recreateSwapChain(); + } + void Graphics::createVulkanInstance() { if (enableValidationLayers && !checkValidationSupport()) { throw love::Exception("validation layers requested, but not available"); @@ -831,6 +828,45 @@ namespace love { } } + void Graphics::cleanup() { + cleanupSwapChain(); + + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr); + vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr); + vkDestroyFence(device, inFlightFences[i], nullptr); + } + vkDestroyCommandPool(device, commandPool, nullptr); + vkDestroyDevice(device, nullptr); + vkDestroySurfaceKHR(instance, surface, nullptr); + vkDestroyInstance(instance, nullptr); + } + + void Graphics::cleanupSwapChain() { + for (size_t i = 0; i < swapChainFramBuffers.size(); i++) { + vkDestroyFramebuffer(device, swapChainFramBuffers[i], nullptr); + } + vkFreeCommandBuffers(device, commandPool, static_cast(commandBuffers.size()), commandBuffers.data()); + vkDestroyPipeline(device, graphicsPipeline, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyRenderPass(device, renderPass, nullptr); + for (size_t i = 0; i < swapChainImageViews.size(); i++) { + vkDestroyImageView(device, swapChainImageViews[i], nullptr); + } + vkDestroySwapchainKHR(device, swapChain, nullptr); + } + + void Graphics::recreateSwapChain() { + vkDeviceWaitIdle(device); + + createSwapChain(); + createImageViews(); + createRenderPass(); + createGraphicsPipeline(); + createFramebuffers(); + createCommandBuffers(); + } + love::graphics::Graphics* createInstance() { love::graphics::Graphics* instance = nullptr; diff --git a/src/modules/graphics/vulkan/Graphics.h b/src/modules/graphics/vulkan/Graphics.h index a7a14dc8a..7d8667ec0 100644 --- a/src/modules/graphics/vulkan/Graphics.h +++ b/src/modules/graphics/vulkan/Graphics.h @@ -34,7 +34,7 @@ namespace love { void clear(const std::vector& colors, OptionalInt stencil, OptionalDouble depth) override {} void discard(const std::vector& colorbuffers, bool depthstencil) override {} void present(void* screenshotCallbackdata) override; - void setViewportSize(int width, int height, int pixelwidth, int pixelheight) override {} + void setViewportSize(int width, int height, int pixelwidth, int pixelheight) override; bool setMode(void* context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa) override { return false; } void unSetMode() override {} void setActive(bool active) override {} @@ -109,6 +109,9 @@ namespace love { void createCommandPool(); void createCommandBuffers(); void createSyncObjects(); + void cleanup(); + void cleanupSwapChain(); + void recreateSwapChain(); VkInstance instance; VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; @@ -133,6 +136,7 @@ namespace love { std::vector inFlightFences; std::vector imagesInFlight; size_t currentFrame = 0; + bool framebufferResized = false; }; } } diff --git a/src/modules/window/sdl/Window.cpp b/src/modules/window/sdl/Window.cpp index 1e150bca6..1b8fbbe17 100644 --- a/src/modules/window/sdl/Window.cpp +++ b/src/modules/window/sdl/Window.cpp @@ -378,7 +378,7 @@ bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowfla return true; #else - window = SDL_CreateWindow(title.c_str(), x, y, w, h, SDL_WINDOW_VULKAN); + window = SDL_CreateWindow(title.c_str(), x, y, w, h, windowflags | SDL_WINDOW_VULKAN); love::graphics::Graphics* gfx = graphics.get(); love::graphics::vulkan::Graphics* vgfx = (love::graphics::vulkan::Graphics*)gfx; From aed6595ee63cf5d2900aaa25e9e203bd4689024b Mon Sep 17 00:00:00 2001 From: niki Date: Thu, 6 Jan 2022 14:55:07 +0100 Subject: [PATCH 003/170] first draft of vulkan buffer implementation --- CMakeLists.txt | 2 + src/modules/graphics/vulkan/Buffer.cpp | 78 ++++++++++++++++++++++++ src/modules/graphics/vulkan/Buffer.h | 36 +++++++++++ src/modules/graphics/vulkan/Graphics.cpp | 5 ++ src/modules/graphics/vulkan/Graphics.h | 6 +- 5 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 src/modules/graphics/vulkan/Buffer.cpp create mode 100644 src/modules/graphics/vulkan/Buffer.h diff --git a/CMakeLists.txt b/CMakeLists.txt index aacdf76c2..b4fafef75 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -578,6 +578,8 @@ set(LOVE_SRC_MODULE_GRAPHICS_VULKAN src/modules/graphics/vulkan/Shader.cpp src/modules/graphics/vulkan/ShaderStage.h src/modules/graphics/vulkan/ShaderStage.cpp + src/modules/graphics/vulkan/Buffer.h + src/modules/graphics/vulkan/Buffer.cpp ) set(LOVE_SRC_MODULE_GRAPHICS diff --git a/src/modules/graphics/vulkan/Buffer.cpp b/src/modules/graphics/vulkan/Buffer.cpp new file mode 100644 index 000000000..1f98dea01 --- /dev/null +++ b/src/modules/graphics/vulkan/Buffer.cpp @@ -0,0 +1,78 @@ +#include "Buffer.h" +#include "Graphics.h" + +namespace love { + namespace graphics { + namespace vulkan { + static uint32_t findMemoryType(VkPhysicalDevice physicalDevice, uint32_t typeFtiler, VkMemoryPropertyFlags properties) { + VkPhysicalDeviceMemoryProperties memProperties; + vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties); + + for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) { + if ((typeFtiler & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) { + return i; + } + } + + throw love::Exception("failed to find suitable memory type"); + } + + Buffer::Buffer(love::graphics::Graphics* gfx, const Settings& settings, const std::vector& format, const void* data, size_t size, size_t arraylength) + : love::graphics::Buffer(gfx, settings, format, size, arrayLength) { + auto vgfx = (Graphics*)gfx; + device = vgfx->getDevice(); + auto physicalDevice = vgfx->getPhysicalDevice(); + + VkBufferCreateInfo bufferInfo{}; + bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferInfo.size = getSize(); + bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; // todo: only vertex buffers are allowed for now + bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + if (vkCreateBuffer(device, &bufferInfo, nullptr, &buffer) != VK_SUCCESS) { + throw love::Exception("failed to create buffer"); + } + + VkMemoryRequirements memRequirements; + vkGetBufferMemoryRequirements(device, buffer, &memRequirements); + + VkMemoryAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocInfo.allocationSize = memRequirements.size; + allocInfo.memoryTypeIndex = findMemoryType(physicalDevice, memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + + if (vkAllocateMemory(device, &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS) { + throw love::Exception("failed to allocate vertex buffer memory"); + } + + vkBindBufferMemory(device, buffer, bufferMemory, 0); + + vkMapMemory(device, bufferMemory, 0, getSize(), 0, &mappedMemory); + memcpy(mappedMemory, data, size); + vkUnmapMemory(device, bufferMemory); + } + + Buffer::~Buffer() { + vkDestroyBuffer(device, buffer, nullptr); + vkFreeMemory(device, bufferMemory, nullptr); + } + + void* Buffer::map(MapType map, size_t offset, size_t size) { + vkMapMemory(device, bufferMemory, offset, size, 0, &mappedMemory); + return mappedMemory; + } + + void Buffer::fill(size_t offset, size_t size, const void *data) { + memcpy(mappedMemory, data, size); + } + + void Buffer::unmap(size_t usedoffset, size_t usedsize) { + vkUnmapMemory(device, bufferMemory); + } + + void Buffer::copyTo(love::graphics::Buffer* dest, size_t sourceoffset, size_t destoffset, size_t size) { + throw love::Exception("not implemented yet"); + } + } + } +} \ No newline at end of file diff --git a/src/modules/graphics/vulkan/Buffer.h b/src/modules/graphics/vulkan/Buffer.h new file mode 100644 index 000000000..d80e19890 --- /dev/null +++ b/src/modules/graphics/vulkan/Buffer.h @@ -0,0 +1,36 @@ +#include "graphics/Buffer.h" +#include + + +namespace love { + namespace graphics { + namespace vulkan { + class Buffer : public love::graphics::Buffer { + public: + Buffer(love::graphics::Graphics* gfx, const Settings& settings, const std::vector& format, const void* data, size_t size, size_t arraylength); + virtual ~Buffer(); + + void* map(MapType map, size_t offset, size_t size) override; + void unmap(size_t usedoffset, size_t usedsize) override; + void fill(size_t offset, size_t size, const void* data) override; + void copyTo(love::graphics::Buffer* dest, size_t sourceoffset, size_t destoffset, size_t size) override; + ptrdiff_t getHandle() const override { + return (ptrdiff_t) buffer; // todo ? + } + ptrdiff_t getTexelBufferHandle() const override { + return (ptrdiff_t) nullptr; // todo ? + } + + private: + VkDevice device; + VkPhysicalDevice physicalDevice; + + // todo use a staging buffer for improved performance + VkBuffer buffer; + VkDeviceMemory bufferMemory; + + void* mappedMemory; + }; + } + } +} diff --git a/src/modules/graphics/vulkan/Graphics.cpp b/src/modules/graphics/vulkan/Graphics.cpp index f7ed76d97..454da553d 100644 --- a/src/modules/graphics/vulkan/Graphics.cpp +++ b/src/modules/graphics/vulkan/Graphics.cpp @@ -1,4 +1,5 @@ #include "Graphics.h" +#include "Buffer.h" #include "SDL_vulkan.h" #include "window/Window.h" #include "common/Exception.h" @@ -77,6 +78,10 @@ namespace love { cleanup(); } + love::graphics::Buffer* Graphics::newBuffer(const love::graphics::Buffer::Settings& settings, const std::vector& format, const void* data, size_t size, size_t arraylength) { + return new Buffer(this, settings, format, data, size, arraylength); + } + void Graphics::present(void* screenshotCallbackdata) { vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX); diff --git a/src/modules/graphics/vulkan/Graphics.h b/src/modules/graphics/vulkan/Graphics.h index 7d8667ec0..2cc12003f 100644 --- a/src/modules/graphics/vulkan/Graphics.h +++ b/src/modules/graphics/vulkan/Graphics.h @@ -27,9 +27,13 @@ namespace love { return device; } + const VkPhysicalDevice getPhysicalDevice() const { + return physicalDevice; + } + // implementation for virtual functions Texture* newTexture(const Texture::Settings& settings, const Texture::Slices* data = nullptr) override { return nullptr; } - Buffer* newBuffer(const Buffer::Settings& settings, const std::vector& format, const void* data, size_t size, size_t arraylength) override { return nullptr; } + love::graphics::Buffer* newBuffer(const love::graphics::Buffer::Settings& settings, const std::vector& format, const void* data, size_t size, size_t arraylength) override; void clear(OptionalColorD color, OptionalInt stencil, OptionalDouble depth) override {} void clear(const std::vector& colors, OptionalInt stencil, OptionalDouble depth) override {} void discard(const std::vector& colorbuffers, bool depthstencil) override {} From 73ee691d231a7eaf29995b4e7bae24b006c55311 Mon Sep 17 00:00:00 2001 From: niki Date: Sun, 16 Jan 2022 02:58:11 +0100 Subject: [PATCH 004/170] add vulkan streambuffer implementation --- CMakeLists.txt | 2 + src/modules/graphics/vulkan/StreamBuffer.cpp | 72 ++++++++++++++++++++ src/modules/graphics/vulkan/StreamBuffer.h | 33 +++++++++ 3 files changed, 107 insertions(+) create mode 100644 src/modules/graphics/vulkan/StreamBuffer.cpp create mode 100644 src/modules/graphics/vulkan/StreamBuffer.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b4fafef75..7bbaf254a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -578,6 +578,8 @@ set(LOVE_SRC_MODULE_GRAPHICS_VULKAN src/modules/graphics/vulkan/Shader.cpp src/modules/graphics/vulkan/ShaderStage.h src/modules/graphics/vulkan/ShaderStage.cpp + src/modules/graphics/vulkan/StreamBuffer.h + src/modules/graphics/vulkan/StreamBuffer.cpp src/modules/graphics/vulkan/Buffer.h src/modules/graphics/vulkan/Buffer.cpp ) diff --git a/src/modules/graphics/vulkan/StreamBuffer.cpp b/src/modules/graphics/vulkan/StreamBuffer.cpp new file mode 100644 index 000000000..5c6fd360b --- /dev/null +++ b/src/modules/graphics/vulkan/StreamBuffer.cpp @@ -0,0 +1,72 @@ +#include "StreamBuffer.h" +#include "vulkan/vulkan.h" + + +namespace love { + namespace graphics { + namespace vulkan { + static uint32_t findMemoryType(VkPhysicalDevice physicalDevice, uint32_t typeFtiler, VkMemoryPropertyFlags properties) { + VkPhysicalDeviceMemoryProperties memProperties; + vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties); + + for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) { + if ((typeFtiler & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) { + return i; + } + } + + throw love::Exception("failed to find suitable memory type"); + } + + static VkBufferUsageFlags getUsageFlags(BufferUsage mode) { + switch (mode) { + case BUFFERUSAGE_VERTEX: return VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; + case BUFFERUSAGE_INDEX: return VK_BUFFER_USAGE_INDEX_BUFFER_BIT; + default: + throw love::Exception("unsupported BufferUsage mode"); + } + } + + StreamBuffer::StreamBuffer(VkDevice device, VkPhysicalDevice physicalDevice, BufferUsage mode, size_t size) + : love::graphics::StreamBuffer(mode, size), + device(device) { + VkBufferCreateInfo bufferInfo{}; + bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferInfo.size = getSize(); + bufferInfo.usage = getUsageFlags(mode); + bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + if (vkCreateBuffer(device, &bufferInfo, nullptr, &buffer) != VK_SUCCESS) { + throw love::Exception("failed to create buffer"); + } + + VkMemoryRequirements memRequirements; + vkGetBufferMemoryRequirements(device, buffer, &memRequirements); + + VkMemoryAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocInfo.allocationSize = memRequirements.size; + allocInfo.memoryTypeIndex = findMemoryType(physicalDevice, memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + + if (vkAllocateMemory(device, &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS) { + throw love::Exception("failed to allocate vertex buffer memory"); + } + + vkBindBufferMemory(device, buffer, bufferMemory, 0); + } + + love::graphics::StreamBuffer::MapInfo StreamBuffer::map(size_t minsize) { + vkMapMemory(device, bufferMemory, 0, getSize(), 0, &mappedMemory); + return love::graphics::StreamBuffer::MapInfo((uint8*) mappedMemory, getSize()); + } + + size_t StreamBuffer::unmap(size_t usedSize) { + vkUnmapMemory(device, bufferMemory); + } + + void StreamBuffer::markUsed(size_t usedSize) { + (void)usedSize; + } + } + } +} diff --git a/src/modules/graphics/vulkan/StreamBuffer.h b/src/modules/graphics/vulkan/StreamBuffer.h new file mode 100644 index 000000000..d3a23e1b5 --- /dev/null +++ b/src/modules/graphics/vulkan/StreamBuffer.h @@ -0,0 +1,33 @@ +#ifndef LOVE_GRAPHICS_VULKAN_STREAMBUFFER_H +#define LOVE_GRAPHICS_VULKAN_STREAMBUFFER_H + +#include "modules/graphics/StreamBuffer.h" +#include "vulkan/vulkan.h" + + +namespace love { + namespace graphics { + namespace vulkan { + class StreamBuffer : public love::graphics::StreamBuffer { + public: + StreamBuffer(VkDevice device, VkPhysicalDevice physicalDevice, BufferUsage mode, size_t size); + + MapInfo map(size_t minsize) override; + size_t unmap(size_t usedSize) override; + void markUsed(size_t usedSize) override; + + ptrdiff_t getHandle() const override { + return 0; + } + + private: + VkDevice device; + VkBuffer buffer; + VkDeviceMemory bufferMemory; + void* mappedMemory; + }; + } + } +} + +#endif \ No newline at end of file From 829b33a77e194dac5b1350301e7b66389a6fa4b3 Mon Sep 17 00:00:00 2001 From: niki Date: Fri, 4 Feb 2022 20:01:59 +0100 Subject: [PATCH 005/170] make vulkan::Shder non abstract --- src/modules/graphics/vulkan/Shader.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/modules/graphics/vulkan/Shader.h b/src/modules/graphics/vulkan/Shader.h index 1cdedd605..45f552e2a 100644 --- a/src/modules/graphics/vulkan/Shader.h +++ b/src/modules/graphics/vulkan/Shader.h @@ -20,6 +20,26 @@ namespace love { return shaderStages; } + void attach() override {} + + ptrdiff_t getHandle() const { return 0; } + + std::string getWarnings() const override { return ""; } + + int getVertexAttributeIndex(const std::string& name) override { return 0; } + + const UniformInfo* getUniformInfo(const std::string& name) const override { return nullptr; } + const UniformInfo* getUniformInfo(BuiltinUniform builtin) const override { return nullptr; } + + void updateUniform(const UniformInfo* info, int count) override {} + + void sendTextures(const UniformInfo* info, Texture** textures, int count) override {} + void sendBuffers(const UniformInfo* info, love::graphics::Buffer** buffers, int count) override {} + + bool hasUniform(const std::string& name) const override { return false; } + + void setVideoTextures(Texture* ytexture, Texture* cbtexture, Texture* crtexture) override {} + private: std::vector shaderStages; }; From 9b176cbcde4e1c633a513885c8e51f2dc04fbf7f Mon Sep 17 00:00:00 2001 From: niki Date: Fri, 4 Feb 2022 20:03:23 +0100 Subject: [PATCH 006/170] make vulkan::ShaderStage non abstract --- src/modules/graphics/vulkan/ShaderStage.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/modules/graphics/vulkan/ShaderStage.h b/src/modules/graphics/vulkan/ShaderStage.h index 49ced1f4f..13770960b 100644 --- a/src/modules/graphics/vulkan/ShaderStage.h +++ b/src/modules/graphics/vulkan/ShaderStage.h @@ -17,6 +17,10 @@ namespace love { return shaderModule; } + ptrdiff_t getHandle() const { + return 0; + } + private: VkShaderModule shaderModule; VkDevice device; From 867766dafe2cafc26f5ddcf82044ce6311199b76 Mon Sep 17 00:00:00 2001 From: niki Date: Fri, 4 Feb 2022 20:03:41 +0100 Subject: [PATCH 007/170] fix vulkan::StreamBuffer::unmap --- src/modules/graphics/vulkan/StreamBuffer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/graphics/vulkan/StreamBuffer.cpp b/src/modules/graphics/vulkan/StreamBuffer.cpp index 5c6fd360b..9d1626c19 100644 --- a/src/modules/graphics/vulkan/StreamBuffer.cpp +++ b/src/modules/graphics/vulkan/StreamBuffer.cpp @@ -62,6 +62,7 @@ namespace love { size_t StreamBuffer::unmap(size_t usedSize) { vkUnmapMemory(device, bufferMemory); + return usedSize; } void StreamBuffer::markUsed(size_t usedSize) { From cc56c796060cf565ec46ea3378ffba2d42015f81 Mon Sep 17 00:00:00 2001 From: niki Date: Fri, 4 Feb 2022 20:07:27 +0100 Subject: [PATCH 008/170] use shaderc instead of glslang to compile shader --- src/modules/graphics/vulkan/ShaderStage.cpp | 191 +++----------------- 1 file changed, 23 insertions(+), 168 deletions(-) diff --git a/src/modules/graphics/vulkan/ShaderStage.cpp b/src/modules/graphics/vulkan/ShaderStage.cpp index 3152ecf5c..104dfad23 100644 --- a/src/modules/graphics/vulkan/ShaderStage.cpp +++ b/src/modules/graphics/vulkan/ShaderStage.cpp @@ -2,193 +2,48 @@ #include "Graphics.h" -#include -#include +#include + +#include +#include namespace love { namespace graphics { namespace vulkan { - // TODO: Use love.graphics to determine actual limits? - static const TBuiltInResource defaultTBuiltInResource = { - /* .MaxLights = */ 32, - /* .MaxClipPlanes = */ 6, - /* .MaxTextureUnits = */ 32, - /* .MaxTextureCoords = */ 32, - /* .MaxVertexAttribs = */ 64, - /* .MaxVertexUniformComponents = */ 16384, - /* .MaxVaryingFloats = */ 128, - /* .MaxVertexTextureImageUnits = */ 32, - /* .MaxCombinedTextureImageUnits = */ 80, - /* .MaxTextureImageUnits = */ 32, - /* .MaxFragmentUniformComponents = */ 16384, - /* .MaxDrawBuffers = */ 8, - /* .MaxVertexUniformVectors = */ 4096, - /* .MaxVaryingVectors = */ 32, - /* .MaxFragmentUniformVectors = */ 4096, - /* .MaxVertexOutputVectors = */ 32, - /* .MaxFragmentInputVectors = */ 31, - /* .MinProgramTexelOffset = */ -8, - /* .MaxProgramTexelOffset = */ 7, - /* .MaxClipDistances = */ 8, - /* .MaxComputeWorkGroupCountX = */ 65535, - /* .MaxComputeWorkGroupCountY = */ 65535, - /* .MaxComputeWorkGroupCountZ = */ 65535, - /* .MaxComputeWorkGroupSizeX = */ 1024, - /* .MaxComputeWorkGroupSizeY = */ 1024, - /* .MaxComputeWorkGroupSizeZ = */ 64, - /* .MaxComputeUniformComponents = */ 1024, - /* .MaxComputeTextureImageUnits = */ 32, - /* .MaxComputeImageUniforms = */ 16, - /* .MaxComputeAtomicCounters = */ 4096, - /* .MaxComputeAtomicCounterBuffers = */ 8, - /* .MaxVaryingComponents = */ 128, - /* .MaxVertexOutputComponents = */ 128, - /* .MaxGeometryInputComponents = */ 128, - /* .MaxGeometryOutputComponents = */ 128, - /* .MaxFragmentInputComponents = */ 128, - /* .MaxImageUnits = */ 192, - /* .MaxCombinedImageUnitsAndFragmentOutputs = */ 144, - /* .MaxCombinedShaderOutputResources = */ 144, - /* .MaxImageSamples = */ 32, - /* .MaxVertexImageUniforms = */ 16, - /* .MaxTessControlImageUniforms = */ 16, - /* .MaxTessEvaluationImageUniforms = */ 16, - /* .MaxGeometryImageUniforms = */ 16, - /* .MaxFragmentImageUniforms = */ 16, - /* .MaxCombinedImageUniforms = */ 80, - /* .MaxGeometryTextureImageUnits = */ 16, - /* .MaxGeometryOutputVertices = */ 256, - /* .MaxGeometryTotalOutputComponents = */ 1024, - /* .MaxGeometryUniformComponents = */ 1024, - /* .MaxGeometryVaryingComponents = */ 64, - /* .MaxTessControlInputComponents = */ 128, - /* .MaxTessControlOutputComponents = */ 128, - /* .MaxTessControlTextureImageUnits = */ 16, - /* .MaxTessControlUniformComponents = */ 1024, - /* .MaxTessControlTotalOutputComponents = */ 4096, - /* .MaxTessEvaluationInputComponents = */ 128, - /* .MaxTessEvaluationOutputComponents = */ 128, - /* .MaxTessEvaluationTextureImageUnits = */ 16, - /* .MaxTessEvaluationUniformComponents = */ 1024, - /* .MaxTessPatchComponents = */ 120, - /* .MaxPatchVertices = */ 32, - /* .MaxTessGenLevel = */ 64, - /* .MaxViewports = */ 16, - /* .MaxVertexAtomicCounters = */ 4096, - /* .MaxTessControlAtomicCounters = */ 4096, - /* .MaxTessEvaluationAtomicCounters = */ 4096, - /* .MaxGeometryAtomicCounters = */ 4096, - /* .MaxFragmentAtomicCounters = */ 4096, - /* .MaxCombinedAtomicCounters = */ 4096, - /* .MaxAtomicCounterBindings = */ 8, - /* .MaxVertexAtomicCounterBuffers = */ 8, - /* .MaxTessControlAtomicCounterBuffers = */ 8, - /* .MaxTessEvaluationAtomicCounterBuffers = */ 8, - /* .MaxGeometryAtomicCounterBuffers = */ 8, - /* .MaxFragmentAtomicCounterBuffers = */ 8, - /* .MaxCombinedAtomicCounterBuffers = */ 8, - /* .MaxAtomicCounterBufferSize = */ 16384, - /* .MaxTransformFeedbackBuffers = */ 4, - /* .MaxTransformFeedbackInterleavedComponents = */ 64, - /* .MaxCullDistances = */ 8, - /* .MaxCombinedClipAndCullDistances = */ 8, - /* .MaxSamples = */ 32, - /* .maxMeshOutputVerticesNV = */ 256, - /* .maxMeshOutputPrimitivesNV = */ 512, - /* .maxMeshWorkGroupSizeX_NV = */ 32, - /* .maxMeshWorkGroupSizeY_NV = */ 1, - /* .maxMeshWorkGroupSizeZ_NV = */ 1, - /* .maxTaskWorkGroupSizeX_NV = */ 32, - /* .maxTaskWorkGroupSizeY_NV = */ 1, - /* .maxTaskWorkGroupSizeZ_NV = */ 1, - /* .maxMeshViewCountNV = */ 4, - /* .maxDualSourceDrawBuffersEXT = */ 1, - /* .limits = */{ - /* .nonInductiveForLoops = */ 1, - /* .whileLoops = */ 1, - /* .doWhileLoops = */ 1, - /* .generalUniformIndexing = */ 1, - /* .generalAttributeMatrixVectorIndexing = */ 1, - /* .generalVaryingIndexing = */ 1, - /* .generalSamplerIndexing = */ 1, - /* .generalVariableIndexing = */ 1, - /* .generalConstantMatrixVectorIndexing = */ 1, - } - }; - - static EShLanguage getShaderStage(ShaderStageType stage) { + static shaderc_shader_kind getShaderStage(ShaderStageType stage) { switch (stage) { - case SHADERSTAGE_VERTEX: return EShLangVertex; - case SHADERSTAGE_PIXEL: return EShLangFragment; - case SHADERSTAGE_COMPUTE: return EShLangCompute; - case SHADERSTAGE_MAX_ENUM: return EShLangCount; + case SHADERSTAGE_VERTEX: return shaderc_vertex_shader; + case SHADERSTAGE_PIXEL: return shaderc_fragment_shader; + case SHADERSTAGE_COMPUTE: return shaderc_compute_shader; + default: + throw love::Exception("unknown exception"); } - return EShLangCount; } ShaderStage::ShaderStage(love::graphics::Graphics* gfx, ShaderStageType stage, const std::string& glsl, bool gles, const std::string& cachekey) : love::graphics::ShaderStage(gfx, stage, glsl, gles, cachekey) { - if (false) { - using namespace glslang; + using namespace shaderc; - auto shaderStage = getShaderStage(stage); + Compiler compiler{}; + auto result = compiler.CompileGlslToSpv(glsl, shaderc_vertex_shader, "shader.glsl"); + std::vector code(result.begin(), result.end()); - TShader* shader = new TShader(shaderStage); - shader->setEnvInput(EShSourceGlsl, shaderStage, EShClientVulkan, 450); - shader->setEnvClient(EShClientVulkan, EShTargetVulkan_1_2); - shader->setEnvTarget(EShTargetSpv, EShTargetSpv_1_5); - shader->setAutoMapLocations(true); - shader->setAutoMapBindings(true); - shader->setEnvInputVulkanRulesRelaxed(); - shader->setGlobalUniformBinding(0); - shader->setGlobalUniformSet(0); + VkShaderModuleCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + createInfo.codeSize = code.size() * sizeof(unsigned int); + createInfo.pCode = reinterpret_cast(code.data()); - const std::string& source = glsl; - const char* csrc = source.c_str(); - int srclen = (int)source.length(); - shader->setStringsWithLengths(&csrc, &srclen, 1); + Graphics* vkGfx = (Graphics*)gfx; + device = vkGfx->getDevice(); - int defaultversion = 450; - EProfile defaultprofile = ECoreProfile; - bool forcedefault = false; - bool forwardcompat = true; - - if (!shader->parse(&defaultTBuiltInResource, defaultversion, defaultprofile, forcedefault, forwardcompat, EShMsgSuppressWarnings)) { - const char* stagename = "unknown"; - ShaderStage::getConstant(stage, stagename); - - std::string err = "Error parsing " + std::string(stagename) + " shader:\n\n" - + std::string(shader->getInfoLog()) + "\n" - + std::string(shader->getInfoDebugLog()); - - delete shader; - - throw love::Exception("%s", err.c_str()); - } - - auto intermediate = shader->getIntermediate(); - std::vector code; - GlslangToSpv(*intermediate, code); - - VkShaderModuleCreateInfo createInfo{}; - createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - createInfo.codeSize = code.size(); - createInfo.pCode = reinterpret_cast(code.data()); - - Graphics* vkGfx = (Graphics*)gfx; - device = vkGfx->getDevice(); - - if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { - throw love::Exception("failed to create shader module"); - } + if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { + throw love::Exception("failed to create shader module"); } - } ShaderStage::~ShaderStage() { - if (false) - vkDestroyShaderModule(device, shaderModule, nullptr); + // vkDestroyShaderModule(device, shaderModule, nullptr); } } } From 65074dae155a814be378d1b10975fe2b49197572 Mon Sep 17 00:00:00 2001 From: niki Date: Thu, 6 Jan 2022 01:12:16 +0100 Subject: [PATCH 009/170] hello world triangle --- CMakeLists.txt | 18 +- src/common/config.h | 2 + src/modules/graphics/Graphics.cpp | 5 +- src/modules/graphics/Graphics.h | 1 + src/modules/graphics/ShaderStage.cpp | 2 + src/modules/graphics/vulkan/Graphics.cpp | 861 ++++++++++++++++++++ src/modules/graphics/vulkan/Graphics.h | 140 ++++ src/modules/graphics/vulkan/Shader.cpp | 44 + src/modules/graphics/vulkan/Shader.h | 30 + src/modules/graphics/vulkan/ShaderStage.cpp | 195 +++++ src/modules/graphics/vulkan/ShaderStage.h | 29 + src/modules/window/sdl/Window.cpp | 30 +- 12 files changed, 1350 insertions(+), 7 deletions(-) create mode 100644 src/modules/graphics/vulkan/Graphics.cpp create mode 100644 src/modules/graphics/vulkan/Graphics.h create mode 100644 src/modules/graphics/vulkan/Shader.cpp create mode 100644 src/modules/graphics/vulkan/Shader.h create mode 100644 src/modules/graphics/vulkan/ShaderStage.cpp create mode 100644 src/modules/graphics/vulkan/ShaderStage.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 6688e3bc5..9f1f40296 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,7 +34,7 @@ set(CMAKE_MODULE_PATH "${love_SOURCE_DIR}/extra/cmake" ${CMAKE_MODULE_PATH}) # Needed for shared libs on Linux. (-fPIC). set(CMAKE_POSITION_INDEPENDENT_CODE TRUE) -set (CMAKE_CXX_STANDARD 11) +set (CMAKE_CXX_STANDARD 17) if(MSVC) set(LOVE_CONSOLE_EXE_NAME lovec) @@ -66,6 +66,8 @@ if(POLICY CMP0072) endif() if(MEGA) + find_package(Vulkan REQUIRED) + # LOVE_MSVC_DLLS contains runtime DLLs that should be bundled with the love # binary (in e.g. the installer). Example: msvcp140.dll. set(LOVE_MSVC_DLLS ${MEGA_MSVC_DLLS}) @@ -73,7 +75,7 @@ if(MEGA) # LOVE_INCLUDE_DIRS contains the search directories for #include. It's mostly # not needed for MEGA builds, since almost all the libraries (except LuaJIT) # are CMake targets, causing include paths to be added automatically. - set(LOVE_INCLUDE_DIRS) + set(LOVE_INCLUDE_DIRS ${Vulkan_INCLUDE_DIRS}) if(APPLE) # Some files do #include , but building with megasource @@ -96,6 +98,7 @@ if(MEGA) ${MEGA_SDL2MAIN} ${MEGA_SDL2} ${MEGA_ZLIB} + ${Vulkan_LIBRARIES} ) # These DLLs are moved next to the love binary in a post-build step to @@ -568,13 +571,24 @@ set(LOVE_SRC_MODULE_GRAPHICS_OPENGL src/modules/graphics/opengl/Texture.h ) +set(LOVE_SRC_MODULE_GRAPHICS_VULKAN + src/modules/graphics/vulkan/Graphics.h + src/modules/graphics/vulkan/Graphics.cpp + src/modules/graphics/vulkan/Shader.h + src/modules/graphics/vulkan/Shader.cpp + src/modules/graphics/vulkan/ShaderStage.h + src/modules/graphics/vulkan/ShaderStage.cpp +) + set(LOVE_SRC_MODULE_GRAPHICS ${LOVE_SRC_MODULE_GRAPHICS_ROOT} ${LOVE_SRC_MODULE_GRAPHICS_OPENGL} + ${LOVE_SRC_MODULE_GRAPHICS_VULKAN} ) source_group("modules\\graphics" FILES ${LOVE_SRC_MODULE_GRAPHICS_ROOT}) source_group("modules\\graphics\\opengl" FILES ${LOVE_SRC_MODULE_GRAPHICS_OPENGL}) +source_group("modules\\graphics\\vulkan" FILES ${LOVE_SRC_MODULE_GRAPHICS_VULKAN}) # # love.image diff --git a/src/common/config.h b/src/common/config.h index 34aa3f2ed..f7787d399 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -124,6 +124,8 @@ # define LOVE_LEGENDARY_ACCELEROMETER_AS_JOYSTICK_HACK #endif +#define LOVE_GRAPHICS_VULKAN + #if defined(LOVE_MACOS) || defined(LOVE_IOS) # define LOVE_GRAPHICS_METAL #endif diff --git a/src/modules/graphics/Graphics.cpp b/src/modules/graphics/Graphics.cpp index 92e1b3408..9f8d8f46f 100644 --- a/src/modules/graphics/Graphics.cpp +++ b/src/modules/graphics/Graphics.cpp @@ -109,6 +109,7 @@ namespace opengl { extern love::graphics::Graphics *createInstance(); } #ifdef LOVE_GRAPHICS_METAL namespace metal { extern love::graphics::Graphics *createInstance(); } #endif +namespace vulkan { extern love::graphics::Graphics* createInstance(); } static const Renderer rendererOrder[] = { RENDERER_METAL, @@ -148,6 +149,9 @@ Graphics *Graphics::createInstance() { for (auto r : rendererOrder) { + // FIX ME: proper selection of vulkan backend + instance = vulkan::createInstance(); + if (std::find(_renderers.begin(), _renderers.end(), r) == _renderers.end()) continue; @@ -157,7 +161,6 @@ Graphics *Graphics::createInstance() if (r == RENDERER_METAL) instance = metal::createInstance(); #endif - if (instance != nullptr) break; } diff --git a/src/modules/graphics/Graphics.h b/src/modules/graphics/Graphics.h index d5709f642..253875769 100644 --- a/src/modules/graphics/Graphics.h +++ b/src/modules/graphics/Graphics.h @@ -70,6 +70,7 @@ enum Renderer RENDERER_NONE, RENDERER_OPENGL, RENDERER_METAL, + RENDERER_VULKAN, RENDERER_MAX_ENUM }; diff --git a/src/modules/graphics/ShaderStage.cpp b/src/modules/graphics/ShaderStage.cpp index a035481f8..352604f0c 100644 --- a/src/modules/graphics/ShaderStage.cpp +++ b/src/modules/graphics/ShaderStage.cpp @@ -18,6 +18,8 @@ * 3. This notice may not be removed or altered from any source distribution. **/ +#include + #include "ShaderStage.h" #include "common/Exception.h" #include "Graphics.h" diff --git a/src/modules/graphics/vulkan/Graphics.cpp b/src/modules/graphics/vulkan/Graphics.cpp new file mode 100644 index 000000000..6bf5a278b --- /dev/null +++ b/src/modules/graphics/vulkan/Graphics.cpp @@ -0,0 +1,861 @@ +#include "Graphics.h" +#include "SDL_vulkan.h" +#include "window/Window.h" +#include "common/Exception.h" +#include "Shader.h" + +#include +#include +#include +#include +#include + + +namespace love { + namespace graphics { + namespace vulkan { + const std::vector validationLayers = { + "VK_LAYER_KHRONOS_validation" + }; + + const std::vector deviceExtensions = { + VK_KHR_SWAPCHAIN_EXTENSION_NAME + }; + +#ifdef NDEBUG + const bool enableValidationLayers = false; +#else + const bool enableValidationLayers = true; +#endif + + const int MAX_FRAMES_IN_FLIGHT = 2; + + static std::vector readFile(const std::string& filename) { + std::ifstream file(filename, std::ios::ate | std::ios::binary); + + if (!file.is_open()) { + throw std::runtime_error("failed to open file!"); + } + + size_t fileSize = (size_t)file.tellg(); + std::vector buffer(fileSize); + + file.seekg(0); + file.read(buffer.data(), fileSize); + + file.close(); + + return buffer; + } + + const char* Graphics::getName() const { + return "love.graphics.vulkan"; + } + + Graphics::Graphics() { + } + + void Graphics::initVulkan() { + if (!init) { + std::cout << "initVulkan" << std::endl; + init = true; + createVulkanInstance(); + std::cout << "create vulkan instance" << std::endl; + createSurface(); + std::cout << "create surface" << std::endl; + pickPhysicalDevice(); + std::cout << "create physical device" << std::endl; + createLogicalDevice(); + std::cout << "create logical device" << std::endl; + createSwapChain(); + std::cout << "create swap chain" << std::endl; + createImageViews(); + std::cout << "create image views" << std::endl; + createRenderPass(); + std::cout << "create render pass" << std::endl; + createGraphicsPipeline(); + std::cout << "create graphics pipeline" << std::endl; + createFramebuffers(); + std::cout << "create frame buffers" << std::endl; + createCommandPool(); + std::cout << "create command pool" << std::endl; + createCommandBuffers(); + std::cout << "create command buffers" << std::endl; + createSyncObjects(); + std::cout << "create sync objects" << std::endl; + } + } + + Graphics::~Graphics() { + if (init) { + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + vkDestroySemaphore(device, renderFinishedSemaphores.at(i), nullptr); + vkDestroySemaphore(device, imageAvailableSemaphores.at(i), nullptr); + vkDestroyFence(device, inFlightFences.at(i), nullptr); + } + if (vkDeviceWaitIdle(device) != VK_SUCCESS) { + throw love::Exception("vkDeviceWaitIdle failed"); + } + vkDestroyCommandPool(device, commandPool, nullptr); + for (auto framebuffer : swapChainFramBuffers) { + vkDestroyFramebuffer(device, framebuffer, nullptr); + } + vkDestroyPipeline(device, graphicsPipeline, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyRenderPass(device, renderPass, nullptr); + for (auto imageView : swapChainImageViews) { + vkDestroyImageView(device, imageView, nullptr); + } + vkDestroySwapchainKHR(device, swapChain, nullptr); + vkDestroyDevice(device, nullptr); + vkDestroySurfaceKHR(instance, surface, nullptr); + vkDestroyInstance(instance, nullptr); + } + } + + void Graphics::present(void* screenshotCallbackdata) { + vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX); + + uint32_t imageIndex; + vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex); + + if (imagesInFlight[imageIndex] != VK_NULL_HANDLE) { + vkWaitForFences(device, 1, &imagesInFlight.at(imageIndex), VK_TRUE, UINT64_MAX); + } + imagesInFlight[imageIndex] = inFlightFences[currentFrame]; + + VkSubmitInfo submitInfo{}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + + VkSemaphore waitSemaphores[] = { imageAvailableSemaphores.at(currentFrame) }; + VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; + submitInfo.waitSemaphoreCount = 1; + submitInfo.pWaitSemaphores = waitSemaphores; + submitInfo.pWaitDstStageMask = waitStages; + + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &commandBuffers[imageIndex]; + + VkSemaphore signalSemaphores[] = { renderFinishedSemaphores.at(currentFrame) }; + submitInfo.signalSemaphoreCount = 1; + submitInfo.pSignalSemaphores = signalSemaphores; + + vkResetFences(device, 1, &inFlightFences[currentFrame]); + + if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences.at(currentFrame)) != VK_SUCCESS) { + throw love::Exception("failed to submit draw command buffer"); + } + + VkPresentInfoKHR presentInfo{}; + presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + + presentInfo.waitSemaphoreCount = 1; + presentInfo.pWaitSemaphores = signalSemaphores; + + VkSwapchainKHR swapChains[] = { swapChain }; + presentInfo.swapchainCount = 1; + presentInfo.pSwapchains = swapChains; + + presentInfo.pImageIndices = &imageIndex; + + vkQueuePresentKHR(presentQueue, &presentInfo); + + currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; + } + + void Graphics::createVulkanInstance() { + if (enableValidationLayers && !checkValidationSupport()) { + throw love::Exception("validation layers requested, but not available"); + } + + VkApplicationInfo appInfo{}; + appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + appInfo.pApplicationName = "LOVE"; + appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); //todo, get this version from somewhere else? + appInfo.pEngineName = "LOVE Engine"; + appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); //todo, same as above + appInfo.apiVersion = VK_API_VERSION_1_0; + + VkInstanceCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + createInfo.pApplicationInfo = &appInfo; + createInfo.pNext = nullptr; + + auto window = Module::getInstance(M_WINDOW); + const void* handle = window->getHandle(); + + unsigned int count; + if (SDL_Vulkan_GetInstanceExtensions((SDL_Window*)handle, &count, nullptr) != SDL_TRUE) { + throw love::Exception("couldn't retrieve sdl vulkan extensions"); + } + + std::vector extensions = {}; // can add more here + size_t addition_extension_count = extensions.size(); + extensions.resize(addition_extension_count + count); + + if (SDL_Vulkan_GetInstanceExtensions((SDL_Window*)handle, &count, extensions.data() + addition_extension_count) != SDL_TRUE) { + throw love::Exception("couldn't retrieve sdl vulkan extensions"); + } + + createInfo.enabledExtensionCount = static_cast(extensions.size()); + createInfo.ppEnabledExtensionNames = extensions.data(); + + if (enableValidationLayers) { + createInfo.enabledLayerCount = static_cast(validationLayers.size()); + createInfo.ppEnabledLayerNames = validationLayers.data(); + } + else { + createInfo.enabledLayerCount = 0; + createInfo.ppEnabledLayerNames = nullptr; + } + + if (vkCreateInstance( + &createInfo, + nullptr, + &instance) != VK_SUCCESS) { + throw love::Exception("couldn't create vulkan instance"); + } + } + + bool Graphics::checkValidationSupport() { + uint32_t layerCount; + vkEnumerateInstanceLayerProperties(&layerCount, nullptr); + + std::vector availableLayers(layerCount); + vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); + + for (const char* layerName : validationLayers) { + bool layerFound = false; + + for (const auto& layerProperties : availableLayers) { + if (strcmp(layerName, layerProperties.layerName) == 0) { + layerFound = true; + break; + } + } + + if (!layerFound) { + return false; + } + } + + return true; + } + + void Graphics::pickPhysicalDevice() { + uint32_t deviceCount = 0; + vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); + + if (deviceCount == 0) { + throw love::Exception("failed to find GPUs with Vulkan support"); + } + + std::vector devices(deviceCount); + vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); + + std::multimap candidates; + + for (const auto& device : devices) { + int score = rateDeviceSuitability(device); + candidates.insert(std::make_pair(score, device)); + } + + if (candidates.rbegin()->first > 0) { + physicalDevice = candidates.rbegin()->second; + } + else { + throw love::Exception("failed to find a suitable gpu"); + } + } + + bool Graphics::checkDeviceExtensionSupport(VkPhysicalDevice device) { + uint32_t extensionCount; + vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr); + + std::vector availableExtensions(extensionCount); + vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data()); + + std::set requiredExtensions(deviceExtensions.begin(), deviceExtensions.end()); + + for (const auto& extension : availableExtensions) { + requiredExtensions.erase(extension.extensionName); + } + + return requiredExtensions.empty(); + } + + int Graphics::rateDeviceSuitability(VkPhysicalDevice device) { + VkPhysicalDeviceProperties deviceProperties; + VkPhysicalDeviceFeatures deviceFeatures; + vkGetPhysicalDeviceProperties(device, &deviceProperties); + vkGetPhysicalDeviceFeatures(device, &deviceFeatures); + + int score = 1; + + // optional + + if (deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) { + score += 1000; + } + + // definitely needed + + QueueFamilyIndices indices = findQueueFamilies(device); + if (!indices.isComplete()) { + score = 0; + } + + bool extensionsSupported = checkDeviceExtensionSupport(device); + if (!extensionsSupported) { + score = 0; + } + + if (extensionsSupported) { + auto swapChainSupport = querySwapChainSupport(device); + bool swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty(); + if (!swapChainAdequate) { + score = 0; + } + } + + return score; + } + + Graphics::QueueFamilyIndices Graphics::findQueueFamilies(VkPhysicalDevice device) { + QueueFamilyIndices indices; + + uint32_t queueFamilyCount = 0; + vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); + + std::vector queueFamilies(queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); + + int i = 0; + for (const auto& queueFamily : queueFamilies) { + if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) { + indices.graphicsFamily = i; + } + + VkBool32 presentSupport = false; + vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport); + + if (presentSupport) { + indices.presentFamily = i; + } + + if (indices.isComplete()) { + break; + } + + i++; + } + + return indices; + } + + void Graphics::createLogicalDevice() { + QueueFamilyIndices indices = findQueueFamilies(physicalDevice); + + std::vector queueCreateInfos; + std::set uniqueQueueFamilies = { indices.graphicsFamily.value(), indices.presentFamily.value() }; + + float queuePriority = 1.0f; + for (uint32_t queueFamily : uniqueQueueFamilies) { + VkDeviceQueueCreateInfo queueCreateInfo{}; + queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queueCreateInfo.queueFamilyIndex = queueFamily; + queueCreateInfo.queueCount = 1; + queueCreateInfo.pQueuePriorities = &queuePriority; + queueCreateInfos.push_back(queueCreateInfo); + } + + VkPhysicalDeviceFeatures deviceFeatures{}; + + VkDeviceCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); + createInfo.pQueueCreateInfos = queueCreateInfos.data(); + createInfo.pEnabledFeatures = &deviceFeatures; + + createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); + createInfo.ppEnabledExtensionNames = deviceExtensions.data(); + + // can this be removed? + if (enableValidationLayers) { + createInfo.enabledLayerCount = static_cast(validationLayers.size()); + createInfo.ppEnabledLayerNames = validationLayers.data(); + } + else { + createInfo.enabledLayerCount = 0; + } + + if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { + throw love::Exception("failed to create logical device"); + } + + vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue); + vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue); + } + + void Graphics::createSurface() { + auto window = Module::getInstance(M_WINDOW); + const void* handle = window->getHandle(); + if (SDL_Vulkan_CreateSurface((SDL_Window*)handle, instance, &surface) != SDL_TRUE) { + throw love::Exception("failed to create window surface"); + } + } + + Graphics::SwapChainSupportDetails Graphics::querySwapChainSupport(VkPhysicalDevice device) { + SwapChainSupportDetails details; + + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities); + + uint32_t formatCount; + vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr); + + if (formatCount != 0) { + details.formats.resize(formatCount); + vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data()); + } + + uint32_t presentModeCount; + vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr); + + if (presentModeCount != 0) { + details.presentModes.resize(presentModeCount); + vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data()); + } + + return details; + } + + void Graphics::createSwapChain() { + SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice); + + VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats); + VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes); + VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities); + + uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1; + if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) { + imageCount = swapChainSupport.capabilities.maxImageCount; + } + + VkSwapchainCreateInfoKHR createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + createInfo.surface = surface; + + createInfo.minImageCount = imageCount; + createInfo.imageFormat = surfaceFormat.format; + createInfo.imageColorSpace = surfaceFormat.colorSpace; + createInfo.imageExtent = extent; + createInfo.imageArrayLayers = 1; + createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + + QueueFamilyIndices indices = findQueueFamilies(physicalDevice); + uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(), indices.presentFamily.value() }; + + if (indices.graphicsFamily != indices.presentFamily) { + createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + createInfo.queueFamilyIndexCount = 2; + createInfo.pQueueFamilyIndices = queueFamilyIndices; + } + else { + createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + createInfo.queueFamilyIndexCount = 0; + createInfo.pQueueFamilyIndices = nullptr; + } + + createInfo.preTransform = swapChainSupport.capabilities.currentTransform; + createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + createInfo.presentMode = presentMode; + createInfo.clipped = VK_TRUE; + createInfo.oldSwapchain = VK_NULL_HANDLE; + + if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) { + throw love::Exception("failed to create swap chain"); + } + + vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); + swapChainImages.resize(imageCount); + vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data()); + + swapChainImageFormat = surfaceFormat.format; + swapChainExtent = extent; + } + + VkSurfaceFormatKHR Graphics::chooseSwapSurfaceFormat(const std::vector& availableFormats) { + for (const auto& availableFormat : availableFormats) { + if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { + return availableFormat; + } + } + + return availableFormats[0]; + } + + VkPresentModeKHR Graphics::chooseSwapPresentMode(const std::vector& availablePresentModes) { + // needed ? + for (const auto& availablePresentMode : availablePresentModes) { + if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) { + return availablePresentMode; + } + } + + return VK_PRESENT_MODE_FIFO_KHR; + } + + VkExtent2D Graphics::chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) { + if (capabilities.currentExtent.width != UINT32_MAX) { + return capabilities.currentExtent; + } + else { + auto window = Module::getInstance(M_WINDOW); + const void* handle = window->getHandle(); + + int width, height; + // is this the equivalent of glfwGetFramebufferSize ? + SDL_Vulkan_GetDrawableSize((SDL_Window*)handle, &width, &height); + + VkExtent2D actualExtent = { + static_cast(width), + static_cast(height) + }; + + actualExtent.width = std::clamp(actualExtent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width); + actualExtent.height = std::clamp(actualExtent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height); + + return actualExtent; + } + } + + void Graphics::createImageViews() { + swapChainImageViews.resize(swapChainImages.size()); + + for (size_t i = 0; i < swapChainImages.size(); i++) { + VkImageViewCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + createInfo.image = swapChainImages.at(i); + createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + createInfo.format = swapChainImageFormat; + createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + createInfo.subresourceRange.baseMipLevel = 0; + createInfo.subresourceRange.levelCount = 1; + createInfo.subresourceRange.baseArrayLayer = 0; + createInfo.subresourceRange.layerCount = 1; + + if (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews.at(i)) != VK_SUCCESS) { + throw love::Exception("failed to create image views"); + } + } + } + + void Graphics::createRenderPass() { + VkAttachmentDescription colorAttachment{}; + colorAttachment.format = swapChainImageFormat; + colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; + colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + VkAttachmentReference colorAttachmentRef{}; + colorAttachmentRef.attachment = 0; + colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpass{}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &colorAttachmentRef; + + VkSubpassDependency dependency{}; + dependency.srcSubpass = VK_SUBPASS_EXTERNAL; + dependency.dstSubpass = 0; + dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.srcAccessMask = 0; + dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + + VkRenderPassCreateInfo renderPassInfo{}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassInfo.attachmentCount = 1; + renderPassInfo.pAttachments = &colorAttachment; + renderPassInfo.subpassCount = 1; + renderPassInfo.pSubpasses = &subpass; + renderPassInfo.dependencyCount = 1; + renderPassInfo.pDependencies = &dependency; + + if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) { + throw love::Exception("failed to create render pass"); + } + } + + static VkShaderModule createShaderModule(VkDevice device, const std::vector& code) { + VkShaderModuleCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + createInfo.codeSize = code.size(); + createInfo.pCode = reinterpret_cast(code.data()); + + VkShaderModule shaderModule; + if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { + throw love::Exception("failed to create shader module"); + } + + return shaderModule; + } + + void Graphics::createGraphicsPipeline() { + // love::graphics::vulkan::Shader* shader = dynamic_cast(getShader()); + // auto shaderStages = shader->getShaderStages(); + + auto vertShaderCode = readFile("vert.spv"); + auto fragShaderCode = readFile("frag.spv"); + + VkShaderModule vertShaderModule = createShaderModule(device, vertShaderCode); + VkShaderModule fragShaderModule = createShaderModule(device, fragShaderCode); + + VkPipelineShaderStageCreateInfo vertShaderStageInfo{}; + vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; + vertShaderStageInfo.module = vertShaderModule; + vertShaderStageInfo.pName = "main"; + + VkPipelineShaderStageCreateInfo fragShaderStageInfo{}; + fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + fragShaderStageInfo.module = fragShaderModule; + fragShaderStageInfo.pName = "main"; + + VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo, fragShaderStageInfo }; + + VkPipelineVertexInputStateCreateInfo vertexInputInfo{}; + vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + + // todo later + vertexInputInfo.vertexBindingDescriptionCount = 0; + vertexInputInfo.pVertexBindingDescriptions = nullptr; + vertexInputInfo.vertexAttributeDescriptionCount = 0; + vertexInputInfo.pVertexAttributeDescriptions = nullptr; + + VkPipelineInputAssemblyStateCreateInfo inputAssembly{}; + inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + inputAssembly.primitiveRestartEnable = VK_FALSE; + + VkViewport viewport{}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = (float)swapChainExtent.width; + viewport.height = (float)swapChainExtent.height; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + VkRect2D scissor{}; + scissor.offset = { 0, 0 }; + scissor.extent = swapChainExtent; + + VkPipelineViewportStateCreateInfo viewportState{}; + viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportState.viewportCount = 1; + viewportState.pViewports = &viewport; + viewportState.scissorCount = 1; + viewportState.pScissors = &scissor; + + VkPipelineRasterizationStateCreateInfo rasterizer{}; + rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizer.depthClampEnable = VK_FALSE; + rasterizer.rasterizerDiscardEnable = VK_FALSE; + rasterizer.polygonMode = VK_POLYGON_MODE_FILL; + rasterizer.lineWidth = 1.0f; + rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; + rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; + rasterizer.depthBiasEnable = VK_FALSE; + rasterizer.depthBiasConstantFactor = 0.0f; + rasterizer.depthBiasClamp = 0.0f; + rasterizer.depthBiasSlopeFactor = 0.0f; + + VkPipelineMultisampleStateCreateInfo multisampling{}; + multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling.sampleShadingEnable = VK_FALSE; + multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + multisampling.minSampleShading = 1.0f; // Optional + multisampling.pSampleMask = nullptr; // Optional + multisampling.alphaToCoverageEnable = VK_FALSE; // Optional + multisampling.alphaToOneEnable = VK_FALSE; // Optional + + VkPipelineColorBlendAttachmentState colorBlendAttachment{}; + colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + colorBlendAttachment.blendEnable = VK_FALSE; + + VkPipelineColorBlendStateCreateInfo colorBlending{}; + colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlending.logicOpEnable = VK_FALSE; + colorBlending.logicOp = VK_LOGIC_OP_COPY; + colorBlending.attachmentCount = 1; + colorBlending.pAttachments = &colorBlendAttachment; + colorBlending.blendConstants[0] = 0.0f; + colorBlending.blendConstants[1] = 0.0f; + colorBlending.blendConstants[2] = 0.0f; + colorBlending.blendConstants[3] = 0.0f; + + VkPipelineLayoutCreateInfo pipelineLayoutInfo{}; + pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutInfo.setLayoutCount = 0; + pipelineLayoutInfo.pushConstantRangeCount = 0; + + if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) { + throw love::Exception("failed to create pipeline layout"); + } + + VkGraphicsPipelineCreateInfo pipelineInfo{}; + pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + // pipelineInfo.stageCount = static_cast(shaderStages.size()); + // pipelineInfo.pStages = shaderStages.data(); + pipelineInfo.stageCount = 2; + pipelineInfo.pStages = shaderStages; + pipelineInfo.pVertexInputState = &vertexInputInfo; + pipelineInfo.pInputAssemblyState = &inputAssembly; + pipelineInfo.pViewportState = &viewportState; + pipelineInfo.pRasterizationState = &rasterizer; + pipelineInfo.pMultisampleState = &multisampling; + pipelineInfo.pDepthStencilState = nullptr; + pipelineInfo.pColorBlendState = &colorBlending; + pipelineInfo.pDynamicState = nullptr; + pipelineInfo.layout = pipelineLayout; + pipelineInfo.renderPass = renderPass; + pipelineInfo.subpass = 0; + pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; + pipelineInfo.basePipelineIndex = -1; + + if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) { + throw love::Exception("failed to create graphics pipeline"); + } + + vkDestroyShaderModule(device, vertShaderModule, nullptr); + vkDestroyShaderModule(device, fragShaderModule, nullptr); + } + + void Graphics::createFramebuffers() { + swapChainFramBuffers.resize(swapChainImageViews.size()); + for (size_t i = 0; i < swapChainImageViews.size(); i++) { + VkImageView attachments[] = { + swapChainImageViews.at(i) + }; + + VkFramebufferCreateInfo framebufferInfo{}; + framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebufferInfo.renderPass = renderPass; + framebufferInfo.attachmentCount = 1; + framebufferInfo.pAttachments = attachments; + framebufferInfo.width = swapChainExtent.width; + framebufferInfo.height = swapChainExtent.height; + framebufferInfo.layers = 1; + + if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramBuffers.at(i)) != VK_SUCCESS) { + throw love::Exception("failed to create framebuffers"); + } + } + } + + void Graphics::createCommandPool() { + QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice); + + VkCommandPoolCreateInfo poolInfo{}; + poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value(); + poolInfo.flags = 0; + + if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) { + throw love::Exception("failed to create command pool"); + } + } + + void Graphics::createCommandBuffers() { + commandBuffers.resize(swapChainFramBuffers.size()); + + VkCommandBufferAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.commandPool = commandPool; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandBufferCount = (uint32_t)commandBuffers.size(); + + if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) { + throw love::Exception("failed to allocate command buffers"); + } + + for (size_t i = 0; i < commandBuffers.size(); i++) { + VkCommandBufferBeginInfo beginInfo{}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = 0; + beginInfo.pInheritanceInfo = nullptr; + + if (vkBeginCommandBuffer(commandBuffers[i], &beginInfo) != VK_SUCCESS) { + throw love::Exception("failed to begin recording command buffer"); + } + + VkRenderPassBeginInfo renderPassInfo{}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + renderPassInfo.renderPass = renderPass; + renderPassInfo.framebuffer = swapChainFramBuffers.at(i); + renderPassInfo.renderArea.offset = { 0, 0 }; + renderPassInfo.renderArea.extent = swapChainExtent; + + VkClearValue clearColor = { {{0.0f, 0.0f, 0.0f, 1.0f}} }; + renderPassInfo.clearValueCount = 1; + renderPassInfo.pClearValues = &clearColor; + + // this definitely doesn't belong in here, but leaving here for future reference + vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); + vkCmdBindPipeline(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline); + vkCmdDraw(commandBuffers[i], 3, 1, 0, 0); + + vkCmdEndRenderPass(commandBuffers[i]); + if (vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS) { + throw love::Exception("failed to record command buffer"); + } + } + } + + void Graphics::createSyncObjects() { + imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT); + renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT); + inFlightFences.resize(MAX_FRAMES_IN_FLIGHT); + imagesInFlight.resize(swapChainImages.size(), VK_NULL_HANDLE); + + VkSemaphoreCreateInfo semaphoreInfo{}; + semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + + VkFenceCreateInfo fenceInfo{}; + fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; + + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphores.at(i)) != VK_SUCCESS || + vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphores.at(i)) != VK_SUCCESS || + vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences.at(i)) != VK_SUCCESS) { + throw love::Exception("failed to create synchronization objects for a frame!"); + } + } + } + + love::graphics::Graphics* createInstance() { + love::graphics::Graphics* instance = nullptr; + + try { + instance = new Graphics(); + } + catch (love::Exception& e) { + printf("Cannot create Vulkan renderer: %s\n", e.what()); + } + + return instance; + } + } + } +} diff --git a/src/modules/graphics/vulkan/Graphics.h b/src/modules/graphics/vulkan/Graphics.h new file mode 100644 index 000000000..efa1651ce --- /dev/null +++ b/src/modules/graphics/vulkan/Graphics.h @@ -0,0 +1,140 @@ +#ifndef LOVE_GRAPHICS_VULKAN_GRAPHICS_H +#define LOVE_GRAPHICS_VULKAN_GRAPHICS_H + +#include "graphics/Graphics.h" +#include + +#include + +#include +#include + + +namespace love { + namespace graphics { + namespace vulkan { + class Graphics final : public love::graphics::Graphics { + public: + Graphics(); + + void initVulkan(); + + virtual ~Graphics(); + + const char* getName() const override; + + const VkDevice getDevice() const { + return device; + } + + // implementation for virtual functions + Texture* newTexture(const Texture::Settings& settings, const Texture::Slices* data = nullptr) override { return nullptr; } + Buffer* newBuffer(const Buffer::Settings& settings, const std::vector& format, const void* data, size_t size, size_t arraylength) override { return nullptr; } + void clear(OptionalColorD color, OptionalInt stencil, OptionalDouble depth) override {} + void clear(const std::vector& colors, OptionalInt stencil, OptionalDouble depth) override {} + Matrix4 computeDeviceProjection(const Matrix4& projection, bool rendertotexture) const override { return Matrix4(); } + void discard(const std::vector& colorbuffers, bool depthstencil) override { } + void present(void* screenshotCallbackdata) override; + void setViewportSize(int width, int height, int pixelwidth, int pixelheight) override {} + bool setMode(void* context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa) override { return false; } + void unSetMode() override {} + void setActive(bool active) override {} + int getRequestedBackbufferMSAA() const override { return 0; } + int getBackbufferMSAA() const override { return 0; } + void setColor(Colorf c) override {} + void setScissor(const Rect& rect) override {} + void setScissor() override {} + void setStencilMode(StencilAction action, CompareMode compare, int value, love::uint32 readmask, love::uint32 writemask) override {} + void setDepthMode(CompareMode compare, bool write) override {} + void setFrontFaceWinding(Winding winding) override {} + void setColorMask(ColorChannelMask mask) override {} + void setBlendState(const BlendState& blend) override {} + void setPointSize(float size) override {} + void setWireframe(bool enable) override {} + PixelFormat getSizedFormat(PixelFormat format, bool rendertarget, bool readable) const override { return PIXELFORMAT_UNKNOWN; } + bool isPixelFormatSupported(PixelFormat format, PixelFormatUsageFlags usage, bool sRGB = false) override { return false; } + Renderer getRenderer() const override { return RENDERER_VULKAN; } + bool usesGLSLES() const override { return false; } + RendererInfo getRendererInfo() const override { return {}; } + void draw(const DrawCommand& cmd) override {} + void draw(const DrawIndexedCommand& cmd) override {} + void drawQuads(int start, int count, const VertexAttributes& attributes, const BufferBindings& buffers, Texture* texture) override {} + + protected: + ShaderStage* newShaderStageInternal(ShaderStageType stage, const std::string& cachekey, const std::string& source, bool gles) override { return nullptr; } + Shader* newShaderInternal(StrongRef stages[SHADERSTAGE_MAX_ENUM]) override { return nullptr; } + StreamBuffer* newStreamBuffer(BufferUsage type, size_t size) override { return nullptr; } + bool dispatch(int x, int y, int z) override { return false; } + void initCapabilities() override {} + void getAPIStats(int& shaderswitches) const override {} + void setRenderTargetsInternal(const RenderTargets& rts, int pixelw, int pixelh, bool hasSRGBtexture) override {} + + private: + bool init = false; + // vulkan specific member functions and variables + + struct QueueFamilyIndices { + std::optional graphicsFamily; + std::optional presentFamily; + + bool isComplete() { + return graphicsFamily.has_value() && presentFamily.has_value(); + } + }; + + struct SwapChainSupportDetails { + VkSurfaceCapabilitiesKHR capabilities; + std::vector formats; + std::vector presentModes; + }; + + void createVulkanInstance(); + bool checkValidationSupport(); + void pickPhysicalDevice(); + int rateDeviceSuitability(VkPhysicalDevice device); + QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device); + void createLogicalDevice(); + void createSurface(); + bool checkDeviceExtensionSupport(VkPhysicalDevice device); + SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device); + VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats); + VkPresentModeKHR chooseSwapPresentMode(const std::vector& availablePresentModes); + VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities); + void createSwapChain(); + void createImageViews(); + void createRenderPass(); + void createGraphicsPipeline(); + void createFramebuffers(); + void createCommandPool(); + void createCommandBuffers(); + void createSyncObjects(); + + VkInstance instance; + VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; + VkDevice device; + VkQueue graphicsQueue; + VkQueue presentQueue; + VkSurfaceKHR surface; + VkSwapchainKHR swapChain; + std::vector swapChainImages; + VkFormat swapChainImageFormat; + VkExtent2D swapChainExtent; + std::vector swapChainImageViews; + VkPipelineLayout pipelineLayout; + VkRenderPass renderPass; + VkPipeline graphicsPipeline; + std::vector swapChainFramBuffers; + VkCommandPool commandPool; + std::vector commandBuffers; + + std::vector imageAvailableSemaphores; + std::vector renderFinishedSemaphores; + std::vector inFlightFences; + std::vector imagesInFlight; + size_t currentFrame = 0; + }; + } + } +} + +#endif diff --git a/src/modules/graphics/vulkan/Shader.cpp b/src/modules/graphics/vulkan/Shader.cpp new file mode 100644 index 000000000..3e26be7e4 --- /dev/null +++ b/src/modules/graphics/vulkan/Shader.cpp @@ -0,0 +1,44 @@ +#include "Shader.h" + +#include "libraries/glslang/glslang/Public/ShaderLang.h" +#include "libraries/glslang/SPIRV/GlslangToSpv.h" +#include + +namespace love { + namespace graphics { + namespace vulkan { + static VkShaderStageFlagBits getStageBit(ShaderStageType type) { + switch (type) { + case SHADERSTAGE_VERTEX: + return VK_SHADER_STAGE_VERTEX_BIT; + case SHADERSTAGE_PIXEL: + return VK_SHADER_STAGE_FRAGMENT_BIT; + case SHADERSTAGE_COMPUTE: + return VK_SHADER_STAGE_COMPUTE_BIT; + } + throw love::Exception("invalid type"); + } + + Shader::Shader(StrongRef stages[]) + : graphics::Shader(stages) { + + if (false) { + for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++) { + if (!stages[i]) + continue; + + auto stage = dynamic_cast(stages[i].get()); + + VkPipelineShaderStageCreateInfo shaderStageInfo{}; + shaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shaderStageInfo.stage = getStageBit(stage->getStageType()); + shaderStageInfo.module = stage->getShaderModule(); + shaderStageInfo.pName = "main"; + + shaderStages.push_back(shaderStageInfo); + } + } + } + } + } +} \ No newline at end of file diff --git a/src/modules/graphics/vulkan/Shader.h b/src/modules/graphics/vulkan/Shader.h new file mode 100644 index 000000000..1cdedd605 --- /dev/null +++ b/src/modules/graphics/vulkan/Shader.h @@ -0,0 +1,30 @@ +#ifndef LOVE_GRAPHICS_VULKAN_SHADER_H +#define LOVE_GRAPHICS_VULKAN_SHADER_H + +#include +#include +#include "libraries/glslang/glslang/Public/ShaderLang.h" +#include "libraries/glslang/SPIRV/GlslangToSpv.h" +#include + + +namespace love { + namespace graphics { + namespace vulkan { + class Shader final : public graphics::Shader { + public: + Shader(StrongRef stages[]); + virtual ~Shader() = default; + + const std::vector& getShaderStages() const { + return shaderStages; + } + + private: + std::vector shaderStages; + }; + } + } +} + +#endif diff --git a/src/modules/graphics/vulkan/ShaderStage.cpp b/src/modules/graphics/vulkan/ShaderStage.cpp new file mode 100644 index 000000000..3152ecf5c --- /dev/null +++ b/src/modules/graphics/vulkan/ShaderStage.cpp @@ -0,0 +1,195 @@ +#include "ShaderStage.h" + +#include "Graphics.h" + +#include +#include + + +namespace love { + namespace graphics { + namespace vulkan { + // TODO: Use love.graphics to determine actual limits? + static const TBuiltInResource defaultTBuiltInResource = { + /* .MaxLights = */ 32, + /* .MaxClipPlanes = */ 6, + /* .MaxTextureUnits = */ 32, + /* .MaxTextureCoords = */ 32, + /* .MaxVertexAttribs = */ 64, + /* .MaxVertexUniformComponents = */ 16384, + /* .MaxVaryingFloats = */ 128, + /* .MaxVertexTextureImageUnits = */ 32, + /* .MaxCombinedTextureImageUnits = */ 80, + /* .MaxTextureImageUnits = */ 32, + /* .MaxFragmentUniformComponents = */ 16384, + /* .MaxDrawBuffers = */ 8, + /* .MaxVertexUniformVectors = */ 4096, + /* .MaxVaryingVectors = */ 32, + /* .MaxFragmentUniformVectors = */ 4096, + /* .MaxVertexOutputVectors = */ 32, + /* .MaxFragmentInputVectors = */ 31, + /* .MinProgramTexelOffset = */ -8, + /* .MaxProgramTexelOffset = */ 7, + /* .MaxClipDistances = */ 8, + /* .MaxComputeWorkGroupCountX = */ 65535, + /* .MaxComputeWorkGroupCountY = */ 65535, + /* .MaxComputeWorkGroupCountZ = */ 65535, + /* .MaxComputeWorkGroupSizeX = */ 1024, + /* .MaxComputeWorkGroupSizeY = */ 1024, + /* .MaxComputeWorkGroupSizeZ = */ 64, + /* .MaxComputeUniformComponents = */ 1024, + /* .MaxComputeTextureImageUnits = */ 32, + /* .MaxComputeImageUniforms = */ 16, + /* .MaxComputeAtomicCounters = */ 4096, + /* .MaxComputeAtomicCounterBuffers = */ 8, + /* .MaxVaryingComponents = */ 128, + /* .MaxVertexOutputComponents = */ 128, + /* .MaxGeometryInputComponents = */ 128, + /* .MaxGeometryOutputComponents = */ 128, + /* .MaxFragmentInputComponents = */ 128, + /* .MaxImageUnits = */ 192, + /* .MaxCombinedImageUnitsAndFragmentOutputs = */ 144, + /* .MaxCombinedShaderOutputResources = */ 144, + /* .MaxImageSamples = */ 32, + /* .MaxVertexImageUniforms = */ 16, + /* .MaxTessControlImageUniforms = */ 16, + /* .MaxTessEvaluationImageUniforms = */ 16, + /* .MaxGeometryImageUniforms = */ 16, + /* .MaxFragmentImageUniforms = */ 16, + /* .MaxCombinedImageUniforms = */ 80, + /* .MaxGeometryTextureImageUnits = */ 16, + /* .MaxGeometryOutputVertices = */ 256, + /* .MaxGeometryTotalOutputComponents = */ 1024, + /* .MaxGeometryUniformComponents = */ 1024, + /* .MaxGeometryVaryingComponents = */ 64, + /* .MaxTessControlInputComponents = */ 128, + /* .MaxTessControlOutputComponents = */ 128, + /* .MaxTessControlTextureImageUnits = */ 16, + /* .MaxTessControlUniformComponents = */ 1024, + /* .MaxTessControlTotalOutputComponents = */ 4096, + /* .MaxTessEvaluationInputComponents = */ 128, + /* .MaxTessEvaluationOutputComponents = */ 128, + /* .MaxTessEvaluationTextureImageUnits = */ 16, + /* .MaxTessEvaluationUniformComponents = */ 1024, + /* .MaxTessPatchComponents = */ 120, + /* .MaxPatchVertices = */ 32, + /* .MaxTessGenLevel = */ 64, + /* .MaxViewports = */ 16, + /* .MaxVertexAtomicCounters = */ 4096, + /* .MaxTessControlAtomicCounters = */ 4096, + /* .MaxTessEvaluationAtomicCounters = */ 4096, + /* .MaxGeometryAtomicCounters = */ 4096, + /* .MaxFragmentAtomicCounters = */ 4096, + /* .MaxCombinedAtomicCounters = */ 4096, + /* .MaxAtomicCounterBindings = */ 8, + /* .MaxVertexAtomicCounterBuffers = */ 8, + /* .MaxTessControlAtomicCounterBuffers = */ 8, + /* .MaxTessEvaluationAtomicCounterBuffers = */ 8, + /* .MaxGeometryAtomicCounterBuffers = */ 8, + /* .MaxFragmentAtomicCounterBuffers = */ 8, + /* .MaxCombinedAtomicCounterBuffers = */ 8, + /* .MaxAtomicCounterBufferSize = */ 16384, + /* .MaxTransformFeedbackBuffers = */ 4, + /* .MaxTransformFeedbackInterleavedComponents = */ 64, + /* .MaxCullDistances = */ 8, + /* .MaxCombinedClipAndCullDistances = */ 8, + /* .MaxSamples = */ 32, + /* .maxMeshOutputVerticesNV = */ 256, + /* .maxMeshOutputPrimitivesNV = */ 512, + /* .maxMeshWorkGroupSizeX_NV = */ 32, + /* .maxMeshWorkGroupSizeY_NV = */ 1, + /* .maxMeshWorkGroupSizeZ_NV = */ 1, + /* .maxTaskWorkGroupSizeX_NV = */ 32, + /* .maxTaskWorkGroupSizeY_NV = */ 1, + /* .maxTaskWorkGroupSizeZ_NV = */ 1, + /* .maxMeshViewCountNV = */ 4, + /* .maxDualSourceDrawBuffersEXT = */ 1, + /* .limits = */{ + /* .nonInductiveForLoops = */ 1, + /* .whileLoops = */ 1, + /* .doWhileLoops = */ 1, + /* .generalUniformIndexing = */ 1, + /* .generalAttributeMatrixVectorIndexing = */ 1, + /* .generalVaryingIndexing = */ 1, + /* .generalSamplerIndexing = */ 1, + /* .generalVariableIndexing = */ 1, + /* .generalConstantMatrixVectorIndexing = */ 1, + } + }; + + static EShLanguage getShaderStage(ShaderStageType stage) { + switch (stage) { + case SHADERSTAGE_VERTEX: return EShLangVertex; + case SHADERSTAGE_PIXEL: return EShLangFragment; + case SHADERSTAGE_COMPUTE: return EShLangCompute; + case SHADERSTAGE_MAX_ENUM: return EShLangCount; + } + return EShLangCount; + } + + ShaderStage::ShaderStage(love::graphics::Graphics* gfx, ShaderStageType stage, const std::string& glsl, bool gles, const std::string& cachekey) + : love::graphics::ShaderStage(gfx, stage, glsl, gles, cachekey) { + if (false) { + using namespace glslang; + + auto shaderStage = getShaderStage(stage); + + TShader* shader = new TShader(shaderStage); + shader->setEnvInput(EShSourceGlsl, shaderStage, EShClientVulkan, 450); + shader->setEnvClient(EShClientVulkan, EShTargetVulkan_1_2); + shader->setEnvTarget(EShTargetSpv, EShTargetSpv_1_5); + shader->setAutoMapLocations(true); + shader->setAutoMapBindings(true); + shader->setEnvInputVulkanRulesRelaxed(); + shader->setGlobalUniformBinding(0); + shader->setGlobalUniformSet(0); + + const std::string& source = glsl; + const char* csrc = source.c_str(); + int srclen = (int)source.length(); + shader->setStringsWithLengths(&csrc, &srclen, 1); + + int defaultversion = 450; + EProfile defaultprofile = ECoreProfile; + bool forcedefault = false; + bool forwardcompat = true; + + if (!shader->parse(&defaultTBuiltInResource, defaultversion, defaultprofile, forcedefault, forwardcompat, EShMsgSuppressWarnings)) { + const char* stagename = "unknown"; + ShaderStage::getConstant(stage, stagename); + + std::string err = "Error parsing " + std::string(stagename) + " shader:\n\n" + + std::string(shader->getInfoLog()) + "\n" + + std::string(shader->getInfoDebugLog()); + + delete shader; + + throw love::Exception("%s", err.c_str()); + } + + auto intermediate = shader->getIntermediate(); + std::vector code; + GlslangToSpv(*intermediate, code); + + VkShaderModuleCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + createInfo.codeSize = code.size(); + createInfo.pCode = reinterpret_cast(code.data()); + + Graphics* vkGfx = (Graphics*)gfx; + device = vkGfx->getDevice(); + + if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { + throw love::Exception("failed to create shader module"); + } + } + + } + + ShaderStage::~ShaderStage() { + if (false) + vkDestroyShaderModule(device, shaderModule, nullptr); + } + } + } +} diff --git a/src/modules/graphics/vulkan/ShaderStage.h b/src/modules/graphics/vulkan/ShaderStage.h new file mode 100644 index 000000000..49ced1f4f --- /dev/null +++ b/src/modules/graphics/vulkan/ShaderStage.h @@ -0,0 +1,29 @@ +#ifndef LOVE_GRAPHICS_VULKAN_SHADERSTAGE_H +#define LOVE_GRAPHICS_VULKAN_SHADERSTAGE_H + +#include "graphics/ShaderStage.h" +#include "modules/graphics/Graphics.h" +#include + +namespace love { + namespace graphics { + namespace vulkan { + class ShaderStage final : public graphics::ShaderStage { + public: + ShaderStage(love::graphics::Graphics* gfx, ShaderStageType stage, const std::string& glsl, bool gles, const std::string& cachekey); + virtual ~ShaderStage(); + + VkShaderModule getShaderModule() const { + return shaderModule; + } + + private: + VkShaderModule shaderModule; + VkDevice device; + + }; + } + } +} + +#endif diff --git a/src/modules/window/sdl/Window.cpp b/src/modules/window/sdl/Window.cpp index d05062ae6..0b0b2ed37 100644 --- a/src/modules/window/sdl/Window.cpp +++ b/src/modules/window/sdl/Window.cpp @@ -21,6 +21,7 @@ // LOVE #include "common/config.h" #include "graphics/Graphics.h" +#include "graphics/vulkan/Graphics.h" #include "Window.h" #ifdef LOVE_ANDROID @@ -137,6 +138,7 @@ void Window::setGLFramebufferAttributes(bool sRGB) void Window::setGLContextAttributes(const ContextAttribs &attribs) { +#ifndef LOVE_GRAPHICS_VULKAN int profilemask = 0; int contextflags = 0; @@ -154,10 +156,12 @@ void Window::setGLContextAttributes(const ContextAttribs &attribs) SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, attribs.versionMinor); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profilemask); SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, contextflags); +#endif } bool Window::checkGLVersion(const ContextAttribs &attribs, std::string &outversion) { +#ifndef LOVE_GRAPHICS_VULKAN typedef unsigned char GLubyte; typedef unsigned int GLenum; typedef const GLubyte *(APIENTRY *glGetStringPtr)(GLenum name); @@ -202,6 +206,9 @@ bool Window::checkGLVersion(const ContextAttribs &attribs, std::string &outversi return false; return true; +#else + return true; +#endif } std::vector Window::getContextAttribsList() const @@ -314,11 +321,13 @@ bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowfla const auto create = [&](const ContextAttribs *attribs) -> bool { +#ifndef LOVE_GRAPHICS_VULKAN if (glcontext) { SDL_GL_DeleteContext(glcontext); glcontext = nullptr; } +#endif #ifdef LOVE_GRAPHICS_METAL if (metalView) @@ -335,6 +344,7 @@ bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowfla window = nullptr; } +#ifndef LOVE_GRAPHICS_VULKAN window = SDL_CreateWindow(title.c_str(), x, y, w, h, windowflags); if (!window) @@ -366,6 +376,16 @@ bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowfla } return true; + +#else + window = SDL_CreateWindow(title.c_str(), x, y, w, h, SDL_WINDOW_VULKAN); + + love::graphics::Graphics* gfx = graphics.get(); + love::graphics::vulkan::Graphics* vgfx = (love::graphics::vulkan::Graphics*)gfx; + vgfx->initVulkan(); + + return true; +#endif }; if (renderer == graphics::RENDERER_OPENGL) @@ -577,19 +597,21 @@ bool Window::setWindow(int width, int height, WindowSettings *settings) { if (renderer == graphics::RENDERER_OPENGL) sdlflags |= SDL_WINDOW_OPENGL; - #ifdef LOVE_GRAPHICS_METAL if (renderer == graphics::RENDERER_METAL) sdlflags |= SDL_WINDOW_METAL; #endif - if (f.resizable) + if (renderer == graphics::RENDERER_VULKAN) + sdlflags |= SDL_WINDOW_VULKAN; + + if (f.resizable) sdlflags |= SDL_WINDOW_RESIZABLE; - if (f.borderless) + if (f.borderless) sdlflags |= SDL_WINDOW_BORDERLESS; - if (isHighDPIAllowed()) + if (isHighDPIAllowed()) sdlflags |= SDL_WINDOW_ALLOW_HIGHDPI; if (!createWindowAndContext(x, y, width, height, sdlflags, renderer)) From b80ef89e8deedaf563349de9963d0b1ca8c8ae4d Mon Sep 17 00:00:00 2001 From: niki Date: Thu, 6 Jan 2022 01:42:50 +0100 Subject: [PATCH 010/170] handle resizing correctly --- src/modules/graphics/vulkan/Graphics.cpp | 88 +++++++++++++++++------- src/modules/graphics/vulkan/Graphics.h | 6 +- src/modules/window/sdl/Window.cpp | 2 +- 3 files changed, 68 insertions(+), 28 deletions(-) diff --git a/src/modules/graphics/vulkan/Graphics.cpp b/src/modules/graphics/vulkan/Graphics.cpp index 6bf5a278b..93b2af2ed 100644 --- a/src/modules/graphics/vulkan/Graphics.cpp +++ b/src/modules/graphics/vulkan/Graphics.cpp @@ -87,37 +87,22 @@ namespace love { } Graphics::~Graphics() { - if (init) { - for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { - vkDestroySemaphore(device, renderFinishedSemaphores.at(i), nullptr); - vkDestroySemaphore(device, imageAvailableSemaphores.at(i), nullptr); - vkDestroyFence(device, inFlightFences.at(i), nullptr); - } - if (vkDeviceWaitIdle(device) != VK_SUCCESS) { - throw love::Exception("vkDeviceWaitIdle failed"); - } - vkDestroyCommandPool(device, commandPool, nullptr); - for (auto framebuffer : swapChainFramBuffers) { - vkDestroyFramebuffer(device, framebuffer, nullptr); - } - vkDestroyPipeline(device, graphicsPipeline, nullptr); - vkDestroyPipelineLayout(device, pipelineLayout, nullptr); - vkDestroyRenderPass(device, renderPass, nullptr); - for (auto imageView : swapChainImageViews) { - vkDestroyImageView(device, imageView, nullptr); - } - vkDestroySwapchainKHR(device, swapChain, nullptr); - vkDestroyDevice(device, nullptr); - vkDestroySurfaceKHR(instance, surface, nullptr); - vkDestroyInstance(instance, nullptr); - } + cleanup(); } void Graphics::present(void* screenshotCallbackdata) { vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX); uint32_t imageIndex; - vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex); + VkResult result = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex); + + if (result == VK_ERROR_OUT_OF_DATE_KHR) { + recreateSwapChain(); + return; + } + else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { + throw love::Exception("failed to acquire swap chain image"); + } if (imagesInFlight[imageIndex] != VK_NULL_HANDLE) { vkWaitForFences(device, 1, &imagesInFlight.at(imageIndex), VK_TRUE, UINT64_MAX); @@ -158,11 +143,23 @@ namespace love { presentInfo.pImageIndices = &imageIndex; - vkQueuePresentKHR(presentQueue, &presentInfo); + result = vkQueuePresentKHR(presentQueue, &presentInfo); + + if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebufferResized) { + framebufferResized = false; + recreateSwapChain(); + } + else if (result != VK_SUCCESS) { + throw love::Exception("failed to present swap chain image"); + } currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; } + void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelheight) { + recreateSwapChain(); + } + void Graphics::createVulkanInstance() { if (enableValidationLayers && !checkValidationSupport()) { throw love::Exception("validation layers requested, but not available"); @@ -844,6 +841,45 @@ namespace love { } } + void Graphics::cleanup() { + cleanupSwapChain(); + + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr); + vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr); + vkDestroyFence(device, inFlightFences[i], nullptr); + } + vkDestroyCommandPool(device, commandPool, nullptr); + vkDestroyDevice(device, nullptr); + vkDestroySurfaceKHR(instance, surface, nullptr); + vkDestroyInstance(instance, nullptr); + } + + void Graphics::cleanupSwapChain() { + for (size_t i = 0; i < swapChainFramBuffers.size(); i++) { + vkDestroyFramebuffer(device, swapChainFramBuffers[i], nullptr); + } + vkFreeCommandBuffers(device, commandPool, static_cast(commandBuffers.size()), commandBuffers.data()); + vkDestroyPipeline(device, graphicsPipeline, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyRenderPass(device, renderPass, nullptr); + for (size_t i = 0; i < swapChainImageViews.size(); i++) { + vkDestroyImageView(device, swapChainImageViews[i], nullptr); + } + vkDestroySwapchainKHR(device, swapChain, nullptr); + } + + void Graphics::recreateSwapChain() { + vkDeviceWaitIdle(device); + + createSwapChain(); + createImageViews(); + createRenderPass(); + createGraphicsPipeline(); + createFramebuffers(); + createCommandBuffers(); + } + love::graphics::Graphics* createInstance() { love::graphics::Graphics* instance = nullptr; diff --git a/src/modules/graphics/vulkan/Graphics.h b/src/modules/graphics/vulkan/Graphics.h index efa1651ce..5630732df 100644 --- a/src/modules/graphics/vulkan/Graphics.h +++ b/src/modules/graphics/vulkan/Graphics.h @@ -35,7 +35,7 @@ namespace love { Matrix4 computeDeviceProjection(const Matrix4& projection, bool rendertotexture) const override { return Matrix4(); } void discard(const std::vector& colorbuffers, bool depthstencil) override { } void present(void* screenshotCallbackdata) override; - void setViewportSize(int width, int height, int pixelwidth, int pixelheight) override {} + void setViewportSize(int width, int height, int pixelwidth, int pixelheight) override; bool setMode(void* context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa) override { return false; } void unSetMode() override {} void setActive(bool active) override {} @@ -108,6 +108,9 @@ namespace love { void createCommandPool(); void createCommandBuffers(); void createSyncObjects(); + void cleanup(); + void cleanupSwapChain(); + void recreateSwapChain(); VkInstance instance; VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; @@ -132,6 +135,7 @@ namespace love { std::vector inFlightFences; std::vector imagesInFlight; size_t currentFrame = 0; + bool framebufferResized = false; }; } } diff --git a/src/modules/window/sdl/Window.cpp b/src/modules/window/sdl/Window.cpp index 0b0b2ed37..3324a3ffc 100644 --- a/src/modules/window/sdl/Window.cpp +++ b/src/modules/window/sdl/Window.cpp @@ -378,7 +378,7 @@ bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowfla return true; #else - window = SDL_CreateWindow(title.c_str(), x, y, w, h, SDL_WINDOW_VULKAN); + window = SDL_CreateWindow(title.c_str(), x, y, w, h, windowflags | SDL_WINDOW_VULKAN); love::graphics::Graphics* gfx = graphics.get(); love::graphics::vulkan::Graphics* vgfx = (love::graphics::vulkan::Graphics*)gfx; From d09f834bf390b8824a41193c23387ae91105a64d Mon Sep 17 00:00:00 2001 From: niki Date: Thu, 6 Jan 2022 14:55:07 +0100 Subject: [PATCH 011/170] first draft of vulkan buffer implementation --- CMakeLists.txt | 2 + src/modules/graphics/vulkan/Buffer.cpp | 78 ++++++++++++++++++++++++ src/modules/graphics/vulkan/Buffer.h | 36 +++++++++++ src/modules/graphics/vulkan/Graphics.cpp | 5 ++ src/modules/graphics/vulkan/Graphics.h | 6 +- 5 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 src/modules/graphics/vulkan/Buffer.cpp create mode 100644 src/modules/graphics/vulkan/Buffer.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f1f40296..aeb0d1b99 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -578,6 +578,8 @@ set(LOVE_SRC_MODULE_GRAPHICS_VULKAN src/modules/graphics/vulkan/Shader.cpp src/modules/graphics/vulkan/ShaderStage.h src/modules/graphics/vulkan/ShaderStage.cpp + src/modules/graphics/vulkan/Buffer.h + src/modules/graphics/vulkan/Buffer.cpp ) set(LOVE_SRC_MODULE_GRAPHICS diff --git a/src/modules/graphics/vulkan/Buffer.cpp b/src/modules/graphics/vulkan/Buffer.cpp new file mode 100644 index 000000000..1f98dea01 --- /dev/null +++ b/src/modules/graphics/vulkan/Buffer.cpp @@ -0,0 +1,78 @@ +#include "Buffer.h" +#include "Graphics.h" + +namespace love { + namespace graphics { + namespace vulkan { + static uint32_t findMemoryType(VkPhysicalDevice physicalDevice, uint32_t typeFtiler, VkMemoryPropertyFlags properties) { + VkPhysicalDeviceMemoryProperties memProperties; + vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties); + + for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) { + if ((typeFtiler & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) { + return i; + } + } + + throw love::Exception("failed to find suitable memory type"); + } + + Buffer::Buffer(love::graphics::Graphics* gfx, const Settings& settings, const std::vector& format, const void* data, size_t size, size_t arraylength) + : love::graphics::Buffer(gfx, settings, format, size, arrayLength) { + auto vgfx = (Graphics*)gfx; + device = vgfx->getDevice(); + auto physicalDevice = vgfx->getPhysicalDevice(); + + VkBufferCreateInfo bufferInfo{}; + bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferInfo.size = getSize(); + bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; // todo: only vertex buffers are allowed for now + bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + if (vkCreateBuffer(device, &bufferInfo, nullptr, &buffer) != VK_SUCCESS) { + throw love::Exception("failed to create buffer"); + } + + VkMemoryRequirements memRequirements; + vkGetBufferMemoryRequirements(device, buffer, &memRequirements); + + VkMemoryAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocInfo.allocationSize = memRequirements.size; + allocInfo.memoryTypeIndex = findMemoryType(physicalDevice, memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + + if (vkAllocateMemory(device, &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS) { + throw love::Exception("failed to allocate vertex buffer memory"); + } + + vkBindBufferMemory(device, buffer, bufferMemory, 0); + + vkMapMemory(device, bufferMemory, 0, getSize(), 0, &mappedMemory); + memcpy(mappedMemory, data, size); + vkUnmapMemory(device, bufferMemory); + } + + Buffer::~Buffer() { + vkDestroyBuffer(device, buffer, nullptr); + vkFreeMemory(device, bufferMemory, nullptr); + } + + void* Buffer::map(MapType map, size_t offset, size_t size) { + vkMapMemory(device, bufferMemory, offset, size, 0, &mappedMemory); + return mappedMemory; + } + + void Buffer::fill(size_t offset, size_t size, const void *data) { + memcpy(mappedMemory, data, size); + } + + void Buffer::unmap(size_t usedoffset, size_t usedsize) { + vkUnmapMemory(device, bufferMemory); + } + + void Buffer::copyTo(love::graphics::Buffer* dest, size_t sourceoffset, size_t destoffset, size_t size) { + throw love::Exception("not implemented yet"); + } + } + } +} \ No newline at end of file diff --git a/src/modules/graphics/vulkan/Buffer.h b/src/modules/graphics/vulkan/Buffer.h new file mode 100644 index 000000000..d80e19890 --- /dev/null +++ b/src/modules/graphics/vulkan/Buffer.h @@ -0,0 +1,36 @@ +#include "graphics/Buffer.h" +#include + + +namespace love { + namespace graphics { + namespace vulkan { + class Buffer : public love::graphics::Buffer { + public: + Buffer(love::graphics::Graphics* gfx, const Settings& settings, const std::vector& format, const void* data, size_t size, size_t arraylength); + virtual ~Buffer(); + + void* map(MapType map, size_t offset, size_t size) override; + void unmap(size_t usedoffset, size_t usedsize) override; + void fill(size_t offset, size_t size, const void* data) override; + void copyTo(love::graphics::Buffer* dest, size_t sourceoffset, size_t destoffset, size_t size) override; + ptrdiff_t getHandle() const override { + return (ptrdiff_t) buffer; // todo ? + } + ptrdiff_t getTexelBufferHandle() const override { + return (ptrdiff_t) nullptr; // todo ? + } + + private: + VkDevice device; + VkPhysicalDevice physicalDevice; + + // todo use a staging buffer for improved performance + VkBuffer buffer; + VkDeviceMemory bufferMemory; + + void* mappedMemory; + }; + } + } +} diff --git a/src/modules/graphics/vulkan/Graphics.cpp b/src/modules/graphics/vulkan/Graphics.cpp index 93b2af2ed..0da8f798a 100644 --- a/src/modules/graphics/vulkan/Graphics.cpp +++ b/src/modules/graphics/vulkan/Graphics.cpp @@ -1,4 +1,5 @@ #include "Graphics.h" +#include "Buffer.h" #include "SDL_vulkan.h" #include "window/Window.h" #include "common/Exception.h" @@ -90,6 +91,10 @@ namespace love { cleanup(); } + love::graphics::Buffer* Graphics::newBuffer(const love::graphics::Buffer::Settings& settings, const std::vector& format, const void* data, size_t size, size_t arraylength) { + return new Buffer(this, settings, format, data, size, arraylength); + } + void Graphics::present(void* screenshotCallbackdata) { vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX); diff --git a/src/modules/graphics/vulkan/Graphics.h b/src/modules/graphics/vulkan/Graphics.h index 5630732df..223b61eee 100644 --- a/src/modules/graphics/vulkan/Graphics.h +++ b/src/modules/graphics/vulkan/Graphics.h @@ -27,9 +27,13 @@ namespace love { return device; } + const VkPhysicalDevice getPhysicalDevice() const { + return physicalDevice; + } + // implementation for virtual functions Texture* newTexture(const Texture::Settings& settings, const Texture::Slices* data = nullptr) override { return nullptr; } - Buffer* newBuffer(const Buffer::Settings& settings, const std::vector& format, const void* data, size_t size, size_t arraylength) override { return nullptr; } + love::graphics::Buffer* newBuffer(const love::graphics::Buffer::Settings& settings, const std::vector& format, const void* data, size_t size, size_t arraylength) override; void clear(OptionalColorD color, OptionalInt stencil, OptionalDouble depth) override {} void clear(const std::vector& colors, OptionalInt stencil, OptionalDouble depth) override {} Matrix4 computeDeviceProjection(const Matrix4& projection, bool rendertotexture) const override { return Matrix4(); } From 4d7fe75001b8dbda47507f465c25adcc6849d762 Mon Sep 17 00:00:00 2001 From: niki Date: Sun, 16 Jan 2022 02:58:11 +0100 Subject: [PATCH 012/170] add vulkan streambuffer implementation --- CMakeLists.txt | 2 + src/modules/graphics/vulkan/StreamBuffer.cpp | 72 ++++++++++++++++++++ src/modules/graphics/vulkan/StreamBuffer.h | 33 +++++++++ 3 files changed, 107 insertions(+) create mode 100644 src/modules/graphics/vulkan/StreamBuffer.cpp create mode 100644 src/modules/graphics/vulkan/StreamBuffer.h diff --git a/CMakeLists.txt b/CMakeLists.txt index aeb0d1b99..44cbb6dc6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -578,6 +578,8 @@ set(LOVE_SRC_MODULE_GRAPHICS_VULKAN src/modules/graphics/vulkan/Shader.cpp src/modules/graphics/vulkan/ShaderStage.h src/modules/graphics/vulkan/ShaderStage.cpp + src/modules/graphics/vulkan/StreamBuffer.h + src/modules/graphics/vulkan/StreamBuffer.cpp src/modules/graphics/vulkan/Buffer.h src/modules/graphics/vulkan/Buffer.cpp ) diff --git a/src/modules/graphics/vulkan/StreamBuffer.cpp b/src/modules/graphics/vulkan/StreamBuffer.cpp new file mode 100644 index 000000000..5c6fd360b --- /dev/null +++ b/src/modules/graphics/vulkan/StreamBuffer.cpp @@ -0,0 +1,72 @@ +#include "StreamBuffer.h" +#include "vulkan/vulkan.h" + + +namespace love { + namespace graphics { + namespace vulkan { + static uint32_t findMemoryType(VkPhysicalDevice physicalDevice, uint32_t typeFtiler, VkMemoryPropertyFlags properties) { + VkPhysicalDeviceMemoryProperties memProperties; + vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties); + + for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) { + if ((typeFtiler & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) { + return i; + } + } + + throw love::Exception("failed to find suitable memory type"); + } + + static VkBufferUsageFlags getUsageFlags(BufferUsage mode) { + switch (mode) { + case BUFFERUSAGE_VERTEX: return VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; + case BUFFERUSAGE_INDEX: return VK_BUFFER_USAGE_INDEX_BUFFER_BIT; + default: + throw love::Exception("unsupported BufferUsage mode"); + } + } + + StreamBuffer::StreamBuffer(VkDevice device, VkPhysicalDevice physicalDevice, BufferUsage mode, size_t size) + : love::graphics::StreamBuffer(mode, size), + device(device) { + VkBufferCreateInfo bufferInfo{}; + bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferInfo.size = getSize(); + bufferInfo.usage = getUsageFlags(mode); + bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + if (vkCreateBuffer(device, &bufferInfo, nullptr, &buffer) != VK_SUCCESS) { + throw love::Exception("failed to create buffer"); + } + + VkMemoryRequirements memRequirements; + vkGetBufferMemoryRequirements(device, buffer, &memRequirements); + + VkMemoryAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocInfo.allocationSize = memRequirements.size; + allocInfo.memoryTypeIndex = findMemoryType(physicalDevice, memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + + if (vkAllocateMemory(device, &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS) { + throw love::Exception("failed to allocate vertex buffer memory"); + } + + vkBindBufferMemory(device, buffer, bufferMemory, 0); + } + + love::graphics::StreamBuffer::MapInfo StreamBuffer::map(size_t minsize) { + vkMapMemory(device, bufferMemory, 0, getSize(), 0, &mappedMemory); + return love::graphics::StreamBuffer::MapInfo((uint8*) mappedMemory, getSize()); + } + + size_t StreamBuffer::unmap(size_t usedSize) { + vkUnmapMemory(device, bufferMemory); + } + + void StreamBuffer::markUsed(size_t usedSize) { + (void)usedSize; + } + } + } +} diff --git a/src/modules/graphics/vulkan/StreamBuffer.h b/src/modules/graphics/vulkan/StreamBuffer.h new file mode 100644 index 000000000..d3a23e1b5 --- /dev/null +++ b/src/modules/graphics/vulkan/StreamBuffer.h @@ -0,0 +1,33 @@ +#ifndef LOVE_GRAPHICS_VULKAN_STREAMBUFFER_H +#define LOVE_GRAPHICS_VULKAN_STREAMBUFFER_H + +#include "modules/graphics/StreamBuffer.h" +#include "vulkan/vulkan.h" + + +namespace love { + namespace graphics { + namespace vulkan { + class StreamBuffer : public love::graphics::StreamBuffer { + public: + StreamBuffer(VkDevice device, VkPhysicalDevice physicalDevice, BufferUsage mode, size_t size); + + MapInfo map(size_t minsize) override; + size_t unmap(size_t usedSize) override; + void markUsed(size_t usedSize) override; + + ptrdiff_t getHandle() const override { + return 0; + } + + private: + VkDevice device; + VkBuffer buffer; + VkDeviceMemory bufferMemory; + void* mappedMemory; + }; + } + } +} + +#endif \ No newline at end of file From 1267f2a7bb1d566915e7fa07d20d73279c3cfb66 Mon Sep 17 00:00:00 2001 From: niki Date: Fri, 4 Feb 2022 20:01:59 +0100 Subject: [PATCH 013/170] make vulkan::Shder non abstract --- src/modules/graphics/vulkan/Shader.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/modules/graphics/vulkan/Shader.h b/src/modules/graphics/vulkan/Shader.h index 1cdedd605..45f552e2a 100644 --- a/src/modules/graphics/vulkan/Shader.h +++ b/src/modules/graphics/vulkan/Shader.h @@ -20,6 +20,26 @@ namespace love { return shaderStages; } + void attach() override {} + + ptrdiff_t getHandle() const { return 0; } + + std::string getWarnings() const override { return ""; } + + int getVertexAttributeIndex(const std::string& name) override { return 0; } + + const UniformInfo* getUniformInfo(const std::string& name) const override { return nullptr; } + const UniformInfo* getUniformInfo(BuiltinUniform builtin) const override { return nullptr; } + + void updateUniform(const UniformInfo* info, int count) override {} + + void sendTextures(const UniformInfo* info, Texture** textures, int count) override {} + void sendBuffers(const UniformInfo* info, love::graphics::Buffer** buffers, int count) override {} + + bool hasUniform(const std::string& name) const override { return false; } + + void setVideoTextures(Texture* ytexture, Texture* cbtexture, Texture* crtexture) override {} + private: std::vector shaderStages; }; From 4e583c8cf8f60db4650ad493c4e12ce7ac362278 Mon Sep 17 00:00:00 2001 From: niki Date: Fri, 4 Feb 2022 20:03:23 +0100 Subject: [PATCH 014/170] make vulkan::ShaderStage non abstract --- src/modules/graphics/vulkan/ShaderStage.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/modules/graphics/vulkan/ShaderStage.h b/src/modules/graphics/vulkan/ShaderStage.h index 49ced1f4f..13770960b 100644 --- a/src/modules/graphics/vulkan/ShaderStage.h +++ b/src/modules/graphics/vulkan/ShaderStage.h @@ -17,6 +17,10 @@ namespace love { return shaderModule; } + ptrdiff_t getHandle() const { + return 0; + } + private: VkShaderModule shaderModule; VkDevice device; From 7b8fcfb919f6125ca6f8da46fde9da8680f26acb Mon Sep 17 00:00:00 2001 From: niki Date: Fri, 4 Feb 2022 20:03:41 +0100 Subject: [PATCH 015/170] fix vulkan::StreamBuffer::unmap --- src/modules/graphics/vulkan/StreamBuffer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/graphics/vulkan/StreamBuffer.cpp b/src/modules/graphics/vulkan/StreamBuffer.cpp index 5c6fd360b..9d1626c19 100644 --- a/src/modules/graphics/vulkan/StreamBuffer.cpp +++ b/src/modules/graphics/vulkan/StreamBuffer.cpp @@ -62,6 +62,7 @@ namespace love { size_t StreamBuffer::unmap(size_t usedSize) { vkUnmapMemory(device, bufferMemory); + return usedSize; } void StreamBuffer::markUsed(size_t usedSize) { From 8694655eddc19cd9b8146738ea2ab57f0bcaa829 Mon Sep 17 00:00:00 2001 From: niki Date: Fri, 4 Feb 2022 20:07:27 +0100 Subject: [PATCH 016/170] use shaderc instead of glslang to compile shader --- src/modules/graphics/vulkan/ShaderStage.cpp | 191 +++----------------- 1 file changed, 23 insertions(+), 168 deletions(-) diff --git a/src/modules/graphics/vulkan/ShaderStage.cpp b/src/modules/graphics/vulkan/ShaderStage.cpp index 3152ecf5c..104dfad23 100644 --- a/src/modules/graphics/vulkan/ShaderStage.cpp +++ b/src/modules/graphics/vulkan/ShaderStage.cpp @@ -2,193 +2,48 @@ #include "Graphics.h" -#include -#include +#include + +#include +#include namespace love { namespace graphics { namespace vulkan { - // TODO: Use love.graphics to determine actual limits? - static const TBuiltInResource defaultTBuiltInResource = { - /* .MaxLights = */ 32, - /* .MaxClipPlanes = */ 6, - /* .MaxTextureUnits = */ 32, - /* .MaxTextureCoords = */ 32, - /* .MaxVertexAttribs = */ 64, - /* .MaxVertexUniformComponents = */ 16384, - /* .MaxVaryingFloats = */ 128, - /* .MaxVertexTextureImageUnits = */ 32, - /* .MaxCombinedTextureImageUnits = */ 80, - /* .MaxTextureImageUnits = */ 32, - /* .MaxFragmentUniformComponents = */ 16384, - /* .MaxDrawBuffers = */ 8, - /* .MaxVertexUniformVectors = */ 4096, - /* .MaxVaryingVectors = */ 32, - /* .MaxFragmentUniformVectors = */ 4096, - /* .MaxVertexOutputVectors = */ 32, - /* .MaxFragmentInputVectors = */ 31, - /* .MinProgramTexelOffset = */ -8, - /* .MaxProgramTexelOffset = */ 7, - /* .MaxClipDistances = */ 8, - /* .MaxComputeWorkGroupCountX = */ 65535, - /* .MaxComputeWorkGroupCountY = */ 65535, - /* .MaxComputeWorkGroupCountZ = */ 65535, - /* .MaxComputeWorkGroupSizeX = */ 1024, - /* .MaxComputeWorkGroupSizeY = */ 1024, - /* .MaxComputeWorkGroupSizeZ = */ 64, - /* .MaxComputeUniformComponents = */ 1024, - /* .MaxComputeTextureImageUnits = */ 32, - /* .MaxComputeImageUniforms = */ 16, - /* .MaxComputeAtomicCounters = */ 4096, - /* .MaxComputeAtomicCounterBuffers = */ 8, - /* .MaxVaryingComponents = */ 128, - /* .MaxVertexOutputComponents = */ 128, - /* .MaxGeometryInputComponents = */ 128, - /* .MaxGeometryOutputComponents = */ 128, - /* .MaxFragmentInputComponents = */ 128, - /* .MaxImageUnits = */ 192, - /* .MaxCombinedImageUnitsAndFragmentOutputs = */ 144, - /* .MaxCombinedShaderOutputResources = */ 144, - /* .MaxImageSamples = */ 32, - /* .MaxVertexImageUniforms = */ 16, - /* .MaxTessControlImageUniforms = */ 16, - /* .MaxTessEvaluationImageUniforms = */ 16, - /* .MaxGeometryImageUniforms = */ 16, - /* .MaxFragmentImageUniforms = */ 16, - /* .MaxCombinedImageUniforms = */ 80, - /* .MaxGeometryTextureImageUnits = */ 16, - /* .MaxGeometryOutputVertices = */ 256, - /* .MaxGeometryTotalOutputComponents = */ 1024, - /* .MaxGeometryUniformComponents = */ 1024, - /* .MaxGeometryVaryingComponents = */ 64, - /* .MaxTessControlInputComponents = */ 128, - /* .MaxTessControlOutputComponents = */ 128, - /* .MaxTessControlTextureImageUnits = */ 16, - /* .MaxTessControlUniformComponents = */ 1024, - /* .MaxTessControlTotalOutputComponents = */ 4096, - /* .MaxTessEvaluationInputComponents = */ 128, - /* .MaxTessEvaluationOutputComponents = */ 128, - /* .MaxTessEvaluationTextureImageUnits = */ 16, - /* .MaxTessEvaluationUniformComponents = */ 1024, - /* .MaxTessPatchComponents = */ 120, - /* .MaxPatchVertices = */ 32, - /* .MaxTessGenLevel = */ 64, - /* .MaxViewports = */ 16, - /* .MaxVertexAtomicCounters = */ 4096, - /* .MaxTessControlAtomicCounters = */ 4096, - /* .MaxTessEvaluationAtomicCounters = */ 4096, - /* .MaxGeometryAtomicCounters = */ 4096, - /* .MaxFragmentAtomicCounters = */ 4096, - /* .MaxCombinedAtomicCounters = */ 4096, - /* .MaxAtomicCounterBindings = */ 8, - /* .MaxVertexAtomicCounterBuffers = */ 8, - /* .MaxTessControlAtomicCounterBuffers = */ 8, - /* .MaxTessEvaluationAtomicCounterBuffers = */ 8, - /* .MaxGeometryAtomicCounterBuffers = */ 8, - /* .MaxFragmentAtomicCounterBuffers = */ 8, - /* .MaxCombinedAtomicCounterBuffers = */ 8, - /* .MaxAtomicCounterBufferSize = */ 16384, - /* .MaxTransformFeedbackBuffers = */ 4, - /* .MaxTransformFeedbackInterleavedComponents = */ 64, - /* .MaxCullDistances = */ 8, - /* .MaxCombinedClipAndCullDistances = */ 8, - /* .MaxSamples = */ 32, - /* .maxMeshOutputVerticesNV = */ 256, - /* .maxMeshOutputPrimitivesNV = */ 512, - /* .maxMeshWorkGroupSizeX_NV = */ 32, - /* .maxMeshWorkGroupSizeY_NV = */ 1, - /* .maxMeshWorkGroupSizeZ_NV = */ 1, - /* .maxTaskWorkGroupSizeX_NV = */ 32, - /* .maxTaskWorkGroupSizeY_NV = */ 1, - /* .maxTaskWorkGroupSizeZ_NV = */ 1, - /* .maxMeshViewCountNV = */ 4, - /* .maxDualSourceDrawBuffersEXT = */ 1, - /* .limits = */{ - /* .nonInductiveForLoops = */ 1, - /* .whileLoops = */ 1, - /* .doWhileLoops = */ 1, - /* .generalUniformIndexing = */ 1, - /* .generalAttributeMatrixVectorIndexing = */ 1, - /* .generalVaryingIndexing = */ 1, - /* .generalSamplerIndexing = */ 1, - /* .generalVariableIndexing = */ 1, - /* .generalConstantMatrixVectorIndexing = */ 1, - } - }; - - static EShLanguage getShaderStage(ShaderStageType stage) { + static shaderc_shader_kind getShaderStage(ShaderStageType stage) { switch (stage) { - case SHADERSTAGE_VERTEX: return EShLangVertex; - case SHADERSTAGE_PIXEL: return EShLangFragment; - case SHADERSTAGE_COMPUTE: return EShLangCompute; - case SHADERSTAGE_MAX_ENUM: return EShLangCount; + case SHADERSTAGE_VERTEX: return shaderc_vertex_shader; + case SHADERSTAGE_PIXEL: return shaderc_fragment_shader; + case SHADERSTAGE_COMPUTE: return shaderc_compute_shader; + default: + throw love::Exception("unknown exception"); } - return EShLangCount; } ShaderStage::ShaderStage(love::graphics::Graphics* gfx, ShaderStageType stage, const std::string& glsl, bool gles, const std::string& cachekey) : love::graphics::ShaderStage(gfx, stage, glsl, gles, cachekey) { - if (false) { - using namespace glslang; + using namespace shaderc; - auto shaderStage = getShaderStage(stage); + Compiler compiler{}; + auto result = compiler.CompileGlslToSpv(glsl, shaderc_vertex_shader, "shader.glsl"); + std::vector code(result.begin(), result.end()); - TShader* shader = new TShader(shaderStage); - shader->setEnvInput(EShSourceGlsl, shaderStage, EShClientVulkan, 450); - shader->setEnvClient(EShClientVulkan, EShTargetVulkan_1_2); - shader->setEnvTarget(EShTargetSpv, EShTargetSpv_1_5); - shader->setAutoMapLocations(true); - shader->setAutoMapBindings(true); - shader->setEnvInputVulkanRulesRelaxed(); - shader->setGlobalUniformBinding(0); - shader->setGlobalUniformSet(0); + VkShaderModuleCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + createInfo.codeSize = code.size() * sizeof(unsigned int); + createInfo.pCode = reinterpret_cast(code.data()); - const std::string& source = glsl; - const char* csrc = source.c_str(); - int srclen = (int)source.length(); - shader->setStringsWithLengths(&csrc, &srclen, 1); + Graphics* vkGfx = (Graphics*)gfx; + device = vkGfx->getDevice(); - int defaultversion = 450; - EProfile defaultprofile = ECoreProfile; - bool forcedefault = false; - bool forwardcompat = true; - - if (!shader->parse(&defaultTBuiltInResource, defaultversion, defaultprofile, forcedefault, forwardcompat, EShMsgSuppressWarnings)) { - const char* stagename = "unknown"; - ShaderStage::getConstant(stage, stagename); - - std::string err = "Error parsing " + std::string(stagename) + " shader:\n\n" - + std::string(shader->getInfoLog()) + "\n" - + std::string(shader->getInfoDebugLog()); - - delete shader; - - throw love::Exception("%s", err.c_str()); - } - - auto intermediate = shader->getIntermediate(); - std::vector code; - GlslangToSpv(*intermediate, code); - - VkShaderModuleCreateInfo createInfo{}; - createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - createInfo.codeSize = code.size(); - createInfo.pCode = reinterpret_cast(code.data()); - - Graphics* vkGfx = (Graphics*)gfx; - device = vkGfx->getDevice(); - - if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { - throw love::Exception("failed to create shader module"); - } + if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { + throw love::Exception("failed to create shader module"); } - } ShaderStage::~ShaderStage() { - if (false) - vkDestroyShaderModule(device, shaderModule, nullptr); + // vkDestroyShaderModule(device, shaderModule, nullptr); } } } From 00a5d5b0a0aa7b317eee003a084d9e8ef50d7e4b Mon Sep 17 00:00:00 2001 From: niki Date: Sat, 5 Feb 2022 20:43:41 +0100 Subject: [PATCH 017/170] restructure to not hardcore draw vk commands --- src/modules/graphics/Graphics.cpp | 2 + src/modules/graphics/vulkan/Graphics.cpp | 156 ++++++++++++++++------- src/modules/graphics/vulkan/Graphics.h | 78 +++++++----- 3 files changed, 158 insertions(+), 78 deletions(-) diff --git a/src/modules/graphics/Graphics.cpp b/src/modules/graphics/Graphics.cpp index 9f8d8f46f..a98cc8140 100644 --- a/src/modules/graphics/Graphics.cpp +++ b/src/modules/graphics/Graphics.cpp @@ -149,8 +149,10 @@ Graphics *Graphics::createInstance() { for (auto r : rendererOrder) { +#ifdef LOVE_GRAPHICS_VULKAN // FIX ME: proper selection of vulkan backend instance = vulkan::createInstance(); +#endif if (std::find(_renderers.begin(), _renderers.end(), r) == _renderers.end()) continue; diff --git a/src/modules/graphics/vulkan/Graphics.cpp b/src/modules/graphics/vulkan/Graphics.cpp index 1789ad17f..e881c239c 100644 --- a/src/modules/graphics/vulkan/Graphics.cpp +++ b/src/modules/graphics/vulkan/Graphics.cpp @@ -71,6 +71,7 @@ namespace love { createCommandPool(); createCommandBuffers(); createSyncObjects(); + startRecordingGraphicsCommands(); } } @@ -78,23 +79,68 @@ namespace love { cleanup(); } + // START OVERRIDEN FUNCTIONS + love::graphics::Buffer* Graphics::newBuffer(const love::graphics::Buffer::Settings& settings, const std::vector& format, const void* data, size_t size, size_t arraylength) { + std::cout << "newBuffer "; return nullptr; } - void Graphics::present(void* screenshotCallbackdata) { + void Graphics::startRecordingGraphicsCommands() { vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX); + + while (true) { + VkResult result = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex); + if (result == VK_ERROR_OUT_OF_DATE_KHR) { + recreateSwapChain(); + continue; + } + else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { + throw love::Exception("failed to acquire swap chain image"); + } - uint32_t imageIndex; - VkResult result = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex); + break; + } - if (result == VK_ERROR_OUT_OF_DATE_KHR) { - recreateSwapChain(); - return; + VkCommandBufferBeginInfo beginInfo{}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = 0; + beginInfo.pInheritanceInfo = nullptr; + + std::cout << "beginCommandBuffer(imageIndex=" << imageIndex << ") "; + if (vkBeginCommandBuffer(commandBuffers.at(imageIndex), &beginInfo) != VK_SUCCESS) { + throw love::Exception("failed to begin recording command buffer"); } - else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { - throw love::Exception("failed to acquire swap chain image"); + + VkRenderPassBeginInfo renderPassInfo{}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + renderPassInfo.renderPass = renderPass; + renderPassInfo.framebuffer = swapChainFramBuffers.at(imageIndex); + renderPassInfo.renderArea.offset = { 0, 0 }; + renderPassInfo.renderArea.extent = swapChainExtent; + renderPassInfo.clearValueCount = 1; + renderPassInfo.pClearValues = &clearColor; + + const auto& commandBuffer = commandBuffers.at(imageIndex); + + vkCmdBeginRenderPass(commandBuffers.at(imageIndex), &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); + vkCmdBindPipeline(commandBuffers.at(imageIndex), VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline); + } + + void Graphics::endRecordingGraphicsCommands() { + const auto& commandBuffer = commandBuffers.at(imageIndex); + + std::cout << "endCommandBuffer(imageIndex=" << imageIndex << ") "; + vkCmdEndRenderPass(commandBuffers.at(imageIndex)); + if (vkEndCommandBuffer(commandBuffers.at(imageIndex)) != VK_SUCCESS) { + throw love::Exception("failed to record command buffer"); } + } + + void Graphics::present(void* screenshotCallbackdata) { + flushBatchedDraws(); + + endRecordingGraphicsCommands(); if (imagesInFlight[imageIndex] != VK_NULL_HANDLE) { vkWaitForFences(device, 1, &imagesInFlight.at(imageIndex), VK_TRUE, UINT64_MAX); @@ -135,7 +181,7 @@ namespace love { presentInfo.pImageIndices = &imageIndex; - result = vkQueuePresentKHR(presentQueue, &presentInfo); + VkResult result = vkQueuePresentKHR(presentQueue, &presentInfo); if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebufferResized) { framebufferResized = false; @@ -146,12 +192,52 @@ namespace love { } currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; + + std::cout << "present" << std::endl; + + startRecordingGraphicsCommands(); } void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelheight) { + std::cout << "setViewPortSize"; recreateSwapChain(); } + bool Graphics::setMode(void* context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa) { + std::cout << "setMode "; + + if (batchedDrawState.vb[0] == nullptr) + { + // Initial sizes that should be good enough for most cases. It will + // resize to fit if needed, later. + batchedDrawState.vb[0] = new StreamBuffer(this, device, physicalDevice, BUFFERUSAGE_VERTEX, 1024 * 1024 * 1); + batchedDrawState.vb[1] = new StreamBuffer(this, device, physicalDevice, BUFFERUSAGE_VERTEX, 256 * 1024 * 1); + batchedDrawState.indexBuffer = new StreamBuffer(this, device, physicalDevice, BUFFERUSAGE_INDEX, sizeof(uint16) * LOVE_UINT16_MAX); + } + + return true; + } + + void Graphics::draw(const DrawIndexedCommand& cmd) { + std::cout << "drawIndexed "; + + std::vector buffers; + std::vector offsets; + buffers.push_back((VkBuffer)cmd.buffers->info[0].buffer->getHandle()); + offsets.push_back((VkDeviceSize)cmd.buffers->info[0].offset); + buffers.push_back((VkBuffer)cmd.buffers->info[1].buffer->getHandle()); + offsets.push_back((VkDeviceSize)cmd.buffers->info[1].offset); + + vkCmdDraw(commandBuffers.at(imageIndex), 3, 1, 0, 0); // todo adjust + } + + graphics::StreamBuffer* Graphics::newStreamBuffer(BufferUsage type, size_t size) { + std::cout << "newStreamBuffer "; + return new StreamBuffer(this, device, physicalDevice, type, size); + } + + // END IMPLEMENTATION OVERRIDDEN FUNCTIONS + void Graphics::createVulkanInstance() { if (enableValidationLayers && !checkValidationSupport()) { throw love::Exception("validation layers requested, but not available"); @@ -626,11 +712,22 @@ namespace love { VkPipelineVertexInputStateCreateInfo vertexInputInfo{}; vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + VkVertexInputBindingDescription vertexBindingDescription; + vertexBindingDescription.binding = 0; + vertexBindingDescription.stride = 2 * sizeof(float); // just position for now + vertexBindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + // todo later + VkVertexInputAttributeDescription positionInputAttributeDescription; + positionInputAttributeDescription.binding = 0; + positionInputAttributeDescription.location = 0; + positionInputAttributeDescription.format = VK_FORMAT_R32G32_SFLOAT; + positionInputAttributeDescription.offset = 0; + vertexInputInfo.vertexBindingDescriptionCount = 0; - vertexInputInfo.pVertexBindingDescriptions = nullptr; + vertexInputInfo.pVertexBindingDescriptions = &vertexBindingDescription; vertexInputInfo.vertexAttributeDescriptionCount = 0; - vertexInputInfo.pVertexAttributeDescriptions = nullptr; + vertexInputInfo.pVertexAttributeDescriptions = &positionInputAttributeDescription; VkPipelineInputAssemblyStateCreateInfo inputAssembly{}; inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; @@ -777,38 +874,6 @@ namespace love { if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) { throw love::Exception("failed to allocate command buffers"); } - - for (size_t i = 0; i < commandBuffers.size(); i++) { - VkCommandBufferBeginInfo beginInfo{}; - beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - beginInfo.flags = 0; - beginInfo.pInheritanceInfo = nullptr; - - if (vkBeginCommandBuffer(commandBuffers[i], &beginInfo) != VK_SUCCESS) { - throw love::Exception("failed to begin recording command buffer"); - } - - VkRenderPassBeginInfo renderPassInfo{}; - renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; - renderPassInfo.renderPass = renderPass; - renderPassInfo.framebuffer = swapChainFramBuffers.at(i); - renderPassInfo.renderArea.offset = { 0, 0 }; - renderPassInfo.renderArea.extent = swapChainExtent; - - VkClearValue clearColor = { {{0.0f, 0.0f, 0.0f, 1.0f}} }; - renderPassInfo.clearValueCount = 1; - renderPassInfo.pClearValues = &clearColor; - - // this definitely doesn't belong in here, but leaving here for future reference - vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); - vkCmdBindPipeline(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline); - vkCmdDraw(commandBuffers[i], 3, 1, 0, 0); - - vkCmdEndRenderPass(commandBuffers[i]); - if (vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS) { - throw love::Exception("failed to record command buffer"); - } - } } void Graphics::createSyncObjects() { @@ -834,6 +899,8 @@ namespace love { } void Graphics::cleanup() { + vkDeviceWaitIdle(device); + cleanupSwapChain(); for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { @@ -864,12 +931,15 @@ namespace love { void Graphics::recreateSwapChain() { vkDeviceWaitIdle(device); + cleanupSwapChain(); + createSwapChain(); createImageViews(); createRenderPass(); createGraphicsPipeline(); createFramebuffers(); createCommandBuffers(); + startRecordingGraphicsCommands(); } love::graphics::Graphics* createInstance() { diff --git a/src/modules/graphics/vulkan/Graphics.h b/src/modules/graphics/vulkan/Graphics.h index 223b61eee..935c2171f 100644 --- a/src/modules/graphics/vulkan/Graphics.h +++ b/src/modules/graphics/vulkan/Graphics.h @@ -2,6 +2,7 @@ #define LOVE_GRAPHICS_VULKAN_GRAPHICS_H #include "graphics/Graphics.h" +#include "StreamBuffer.h" #include #include @@ -32,46 +33,46 @@ namespace love { } // implementation for virtual functions - Texture* newTexture(const Texture::Settings& settings, const Texture::Slices* data = nullptr) override { return nullptr; } + Texture* newTexture(const Texture::Settings& settings, const Texture::Slices* data = nullptr) override { std::cout << "newTexture"; return nullptr; } love::graphics::Buffer* newBuffer(const love::graphics::Buffer::Settings& settings, const std::vector& format, const void* data, size_t size, size_t arraylength) override; - void clear(OptionalColorD color, OptionalInt stencil, OptionalDouble depth) override {} - void clear(const std::vector& colors, OptionalInt stencil, OptionalDouble depth) override {} - Matrix4 computeDeviceProjection(const Matrix4& projection, bool rendertotexture) const override { return Matrix4(); } - void discard(const std::vector& colorbuffers, bool depthstencil) override { } + void clear(OptionalColorD color, OptionalInt stencil, OptionalDouble depth) override { std::cout << "clear1 "; } + void clear(const std::vector& colors, OptionalInt stencil, OptionalDouble depth) override { std::cout << "clear2 "; } + Matrix4 computeDeviceProjection(const Matrix4& projection, bool rendertotexture) const override { std::cout << "computeDeviceProjection "; return Matrix4(); } + void discard(const std::vector& colorbuffers, bool depthstencil) override { std::cout << "discard "; } void present(void* screenshotCallbackdata) override; void setViewportSize(int width, int height, int pixelwidth, int pixelheight) override; - bool setMode(void* context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa) override { return false; } - void unSetMode() override {} - void setActive(bool active) override {} - int getRequestedBackbufferMSAA() const override { return 0; } - int getBackbufferMSAA() const override { return 0; } - void setColor(Colorf c) override {} - void setScissor(const Rect& rect) override {} - void setScissor() override {} - void setStencilMode(StencilAction action, CompareMode compare, int value, love::uint32 readmask, love::uint32 writemask) override {} - void setDepthMode(CompareMode compare, bool write) override {} - void setFrontFaceWinding(Winding winding) override {} - void setColorMask(ColorChannelMask mask) override {} - void setBlendState(const BlendState& blend) override {} - void setPointSize(float size) override {} - void setWireframe(bool enable) override {} - PixelFormat getSizedFormat(PixelFormat format, bool rendertarget, bool readable) const override { return PIXELFORMAT_UNKNOWN; } - bool isPixelFormatSupported(PixelFormat format, PixelFormatUsageFlags usage, bool sRGB = false) override { return false; } - Renderer getRenderer() const override { return RENDERER_VULKAN; } - bool usesGLSLES() const override { return false; } - RendererInfo getRendererInfo() const override { return {}; } - void draw(const DrawCommand& cmd) override {} - void draw(const DrawIndexedCommand& cmd) override {} - void drawQuads(int start, int count, const VertexAttributes& attributes, const BufferBindings& buffers, Texture* texture) override {} + bool setMode(void* context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa) override; + void unSetMode() override { std::cout << "unSetMode "; } + void setActive(bool active) override { std::cout << "setActive "; } + int getRequestedBackbufferMSAA() const override { std::cout << "getRequestedBackbufferMSAA "; return 0; } + int getBackbufferMSAA() const override { std::cout << "getBackbufferMSAA "; return 0; } + void setColor(Colorf c) override { std::cout << "setColor "; } + void setScissor(const Rect& rect) override { std::cout << "setScissor "; } + void setScissor() override { std::cout << "setScissor2 "; } + void setStencilMode(StencilAction action, CompareMode compare, int value, love::uint32 readmask, love::uint32 writemask) override { std::cout << "setStencilMode "; } + void setDepthMode(CompareMode compare, bool write) override { std::cout << "setDepthMode "; } + void setFrontFaceWinding(Winding winding) override { std::cout << "setFrontFaceWinding "; } + void setColorMask(ColorChannelMask mask) override { std::cout << "setColorMask "; } + void setBlendState(const BlendState& blend) override { std::cout << "setBlendState "; } + void setPointSize(float size) override { std::cout << "setPointSize "; } + void setWireframe(bool enable) override { std::cout << "setWireframe "; } + PixelFormat getSizedFormat(PixelFormat format, bool rendertarget, bool readable) const override { std::cout << "getSizedFormat "; return PIXELFORMAT_UNKNOWN; } + bool isPixelFormatSupported(PixelFormat format, PixelFormatUsageFlags usage, bool sRGB = false) override { std::cout << "isPixelFormatSupported "; return false; } + Renderer getRenderer() const override { std::cout << "getRenderer "; return RENDERER_VULKAN; } + bool usesGLSLES() const override { std::cout << "usesGLSES "; return false; } + RendererInfo getRendererInfo() const override { std::cout << "getRendererInfo "; return {}; } + void draw(const DrawCommand& cmd) override { std::cout << "draw "; } + void draw(const DrawIndexedCommand& cmd) override; + void drawQuads(int start, int count, const VertexAttributes& attributes, const BufferBindings& buffers, Texture* texture) override { std::cout << "drawQuads "; } protected: - ShaderStage* newShaderStageInternal(ShaderStageType stage, const std::string& cachekey, const std::string& source, bool gles) override { return nullptr; } - Shader* newShaderInternal(StrongRef stages[SHADERSTAGE_MAX_ENUM]) override { return nullptr; } - StreamBuffer* newStreamBuffer(BufferUsage type, size_t size) override { return nullptr; } - bool dispatch(int x, int y, int z) override { return false; } - void initCapabilities() override {} - void getAPIStats(int& shaderswitches) const override {} - void setRenderTargetsInternal(const RenderTargets& rts, int pixelw, int pixelh, bool hasSRGBtexture) override {} + graphics::ShaderStage* newShaderStageInternal(ShaderStageType stage, const std::string& cachekey, const std::string& source, bool gles) override { std::cout << "newShaderStageInternal "; return nullptr; } + graphics::Shader* newShaderInternal(StrongRef stages[SHADERSTAGE_MAX_ENUM]) override { std::cout << "newShaderInternal "; return nullptr; } + graphics::StreamBuffer* newStreamBuffer(BufferUsage type, size_t size) override; + bool dispatch(int x, int y, int z) override { std::cout << "dispatch "; return false; } + void initCapabilities() override { std::cout << "initCapabilities "; } + void getAPIStats(int& shaderswitches) const override { std::cout << "getAPIStats "; } + void setRenderTargetsInternal(const RenderTargets& rts, int pixelw, int pixelh, bool hasSRGBtexture) override { std::cout << "setRenderTargetsInternal "; } private: bool init = false; @@ -116,6 +117,9 @@ namespace love { void cleanupSwapChain(); void recreateSwapChain(); + void startRecordingGraphicsCommands(); + void endRecordingGraphicsCommands(); + VkInstance instance; VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; VkDevice device; @@ -133,13 +137,17 @@ namespace love { std::vector swapChainFramBuffers; VkCommandPool commandPool; std::vector commandBuffers; + VkClearValue clearColor = { {{0.0f, 0.0f, 0.0f, 1.0f}} }; std::vector imageAvailableSemaphores; std::vector renderFinishedSemaphores; std::vector inFlightFences; std::vector imagesInFlight; size_t currentFrame = 0; + uint32_t imageIndex; bool framebufferResized = false; + + friend class StreamBuffer; }; } } From 7d680838b685d440c2b084f9012a5abeacace208 Mon Sep 17 00:00:00 2001 From: niki Date: Sat, 5 Feb 2022 20:43:41 +0100 Subject: [PATCH 018/170] restructure to not hardcore draw vk commands --- src/modules/graphics/Graphics.cpp | 2 + src/modules/graphics/vulkan/Graphics.cpp | 156 ++++++++++++++++------- src/modules/graphics/vulkan/Graphics.h | 78 +++++++----- 3 files changed, 158 insertions(+), 78 deletions(-) diff --git a/src/modules/graphics/Graphics.cpp b/src/modules/graphics/Graphics.cpp index 9f8d8f46f..a98cc8140 100644 --- a/src/modules/graphics/Graphics.cpp +++ b/src/modules/graphics/Graphics.cpp @@ -149,8 +149,10 @@ Graphics *Graphics::createInstance() { for (auto r : rendererOrder) { +#ifdef LOVE_GRAPHICS_VULKAN // FIX ME: proper selection of vulkan backend instance = vulkan::createInstance(); +#endif if (std::find(_renderers.begin(), _renderers.end(), r) == _renderers.end()) continue; diff --git a/src/modules/graphics/vulkan/Graphics.cpp b/src/modules/graphics/vulkan/Graphics.cpp index 1789ad17f..7f3dcfa45 100644 --- a/src/modules/graphics/vulkan/Graphics.cpp +++ b/src/modules/graphics/vulkan/Graphics.cpp @@ -71,6 +71,7 @@ namespace love { createCommandPool(); createCommandBuffers(); createSyncObjects(); + startRecordingGraphicsCommands(); } } @@ -78,23 +79,68 @@ namespace love { cleanup(); } + // START OVERRIDEN FUNCTIONS + love::graphics::Buffer* Graphics::newBuffer(const love::graphics::Buffer::Settings& settings, const std::vector& format, const void* data, size_t size, size_t arraylength) { + std::cout << "newBuffer "; return nullptr; } - void Graphics::present(void* screenshotCallbackdata) { + void Graphics::startRecordingGraphicsCommands() { vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX); + + while (true) { + VkResult result = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex); + if (result == VK_ERROR_OUT_OF_DATE_KHR) { + recreateSwapChain(); + continue; + } + else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { + throw love::Exception("failed to acquire swap chain image"); + } - uint32_t imageIndex; - VkResult result = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex); + break; + } - if (result == VK_ERROR_OUT_OF_DATE_KHR) { - recreateSwapChain(); - return; + VkCommandBufferBeginInfo beginInfo{}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = 0; + beginInfo.pInheritanceInfo = nullptr; + + std::cout << "beginCommandBuffer(imageIndex=" << imageIndex << ") "; + if (vkBeginCommandBuffer(commandBuffers.at(imageIndex), &beginInfo) != VK_SUCCESS) { + throw love::Exception("failed to begin recording command buffer"); } - else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { - throw love::Exception("failed to acquire swap chain image"); + + VkRenderPassBeginInfo renderPassInfo{}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + renderPassInfo.renderPass = renderPass; + renderPassInfo.framebuffer = swapChainFramBuffers.at(imageIndex); + renderPassInfo.renderArea.offset = { 0, 0 }; + renderPassInfo.renderArea.extent = swapChainExtent; + renderPassInfo.clearValueCount = 1; + renderPassInfo.pClearValues = &clearColor; + + const auto& commandBuffer = commandBuffers.at(imageIndex); + + vkCmdBeginRenderPass(commandBuffers.at(imageIndex), &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); + vkCmdBindPipeline(commandBuffers.at(imageIndex), VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline); + } + + void Graphics::endRecordingGraphicsCommands() { + const auto& commandBuffer = commandBuffers.at(imageIndex); + + std::cout << "endCommandBuffer(imageIndex=" << imageIndex << ") "; + vkCmdEndRenderPass(commandBuffers.at(imageIndex)); + if (vkEndCommandBuffer(commandBuffers.at(imageIndex)) != VK_SUCCESS) { + throw love::Exception("failed to record command buffer"); } + } + + void Graphics::present(void* screenshotCallbackdata) { + flushBatchedDraws(); + + endRecordingGraphicsCommands(); if (imagesInFlight[imageIndex] != VK_NULL_HANDLE) { vkWaitForFences(device, 1, &imagesInFlight.at(imageIndex), VK_TRUE, UINT64_MAX); @@ -135,7 +181,7 @@ namespace love { presentInfo.pImageIndices = &imageIndex; - result = vkQueuePresentKHR(presentQueue, &presentInfo); + VkResult result = vkQueuePresentKHR(presentQueue, &presentInfo); if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebufferResized) { framebufferResized = false; @@ -146,12 +192,52 @@ namespace love { } currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; + + std::cout << "present" << std::endl; + + startRecordingGraphicsCommands(); } void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelheight) { + std::cout << "setViewPortSize"; recreateSwapChain(); } + bool Graphics::setMode(void* context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa) { + std::cout << "setMode "; + + if (batchedDrawState.vb[0] == nullptr) + { + // Initial sizes that should be good enough for most cases. It will + // resize to fit if needed, later. + batchedDrawState.vb[0] = new StreamBuffer(device, physicalDevice, BUFFERUSAGE_VERTEX, 1024 * 1024 * 1); + batchedDrawState.vb[1] = new StreamBuffer(device, physicalDevice, BUFFERUSAGE_VERTEX, 256 * 1024 * 1); + batchedDrawState.indexBuffer = new StreamBuffer(device, physicalDevice, BUFFERUSAGE_INDEX, sizeof(uint16) * LOVE_UINT16_MAX); + } + + return true; + } + + void Graphics::draw(const DrawIndexedCommand& cmd) { + std::cout << "drawIndexed "; + + std::vector buffers; + std::vector offsets; + buffers.push_back((VkBuffer)cmd.buffers->info[0].buffer->getHandle()); + offsets.push_back((VkDeviceSize)cmd.buffers->info[0].offset); + buffers.push_back((VkBuffer)cmd.buffers->info[1].buffer->getHandle()); + offsets.push_back((VkDeviceSize)cmd.buffers->info[1].offset); + + vkCmdDraw(commandBuffers.at(imageIndex), 3, 1, 0, 0); // todo adjust + } + + graphics::StreamBuffer* Graphics::newStreamBuffer(BufferUsage type, size_t size) { + std::cout << "newStreamBuffer "; + return new StreamBuffer(device, physicalDevice, type, size); + } + + // END IMPLEMENTATION OVERRIDDEN FUNCTIONS + void Graphics::createVulkanInstance() { if (enableValidationLayers && !checkValidationSupport()) { throw love::Exception("validation layers requested, but not available"); @@ -626,11 +712,22 @@ namespace love { VkPipelineVertexInputStateCreateInfo vertexInputInfo{}; vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + VkVertexInputBindingDescription vertexBindingDescription; + vertexBindingDescription.binding = 0; + vertexBindingDescription.stride = 2 * sizeof(float); // just position for now + vertexBindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + // todo later + VkVertexInputAttributeDescription positionInputAttributeDescription; + positionInputAttributeDescription.binding = 0; + positionInputAttributeDescription.location = 0; + positionInputAttributeDescription.format = VK_FORMAT_R32G32_SFLOAT; + positionInputAttributeDescription.offset = 0; + vertexInputInfo.vertexBindingDescriptionCount = 0; - vertexInputInfo.pVertexBindingDescriptions = nullptr; + vertexInputInfo.pVertexBindingDescriptions = &vertexBindingDescription; vertexInputInfo.vertexAttributeDescriptionCount = 0; - vertexInputInfo.pVertexAttributeDescriptions = nullptr; + vertexInputInfo.pVertexAttributeDescriptions = &positionInputAttributeDescription; VkPipelineInputAssemblyStateCreateInfo inputAssembly{}; inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; @@ -777,38 +874,6 @@ namespace love { if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) { throw love::Exception("failed to allocate command buffers"); } - - for (size_t i = 0; i < commandBuffers.size(); i++) { - VkCommandBufferBeginInfo beginInfo{}; - beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - beginInfo.flags = 0; - beginInfo.pInheritanceInfo = nullptr; - - if (vkBeginCommandBuffer(commandBuffers[i], &beginInfo) != VK_SUCCESS) { - throw love::Exception("failed to begin recording command buffer"); - } - - VkRenderPassBeginInfo renderPassInfo{}; - renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; - renderPassInfo.renderPass = renderPass; - renderPassInfo.framebuffer = swapChainFramBuffers.at(i); - renderPassInfo.renderArea.offset = { 0, 0 }; - renderPassInfo.renderArea.extent = swapChainExtent; - - VkClearValue clearColor = { {{0.0f, 0.0f, 0.0f, 1.0f}} }; - renderPassInfo.clearValueCount = 1; - renderPassInfo.pClearValues = &clearColor; - - // this definitely doesn't belong in here, but leaving here for future reference - vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); - vkCmdBindPipeline(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline); - vkCmdDraw(commandBuffers[i], 3, 1, 0, 0); - - vkCmdEndRenderPass(commandBuffers[i]); - if (vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS) { - throw love::Exception("failed to record command buffer"); - } - } } void Graphics::createSyncObjects() { @@ -834,6 +899,8 @@ namespace love { } void Graphics::cleanup() { + vkDeviceWaitIdle(device); + cleanupSwapChain(); for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { @@ -864,12 +931,15 @@ namespace love { void Graphics::recreateSwapChain() { vkDeviceWaitIdle(device); + cleanupSwapChain(); + createSwapChain(); createImageViews(); createRenderPass(); createGraphicsPipeline(); createFramebuffers(); createCommandBuffers(); + startRecordingGraphicsCommands(); } love::graphics::Graphics* createInstance() { diff --git a/src/modules/graphics/vulkan/Graphics.h b/src/modules/graphics/vulkan/Graphics.h index 223b61eee..935c2171f 100644 --- a/src/modules/graphics/vulkan/Graphics.h +++ b/src/modules/graphics/vulkan/Graphics.h @@ -2,6 +2,7 @@ #define LOVE_GRAPHICS_VULKAN_GRAPHICS_H #include "graphics/Graphics.h" +#include "StreamBuffer.h" #include #include @@ -32,46 +33,46 @@ namespace love { } // implementation for virtual functions - Texture* newTexture(const Texture::Settings& settings, const Texture::Slices* data = nullptr) override { return nullptr; } + Texture* newTexture(const Texture::Settings& settings, const Texture::Slices* data = nullptr) override { std::cout << "newTexture"; return nullptr; } love::graphics::Buffer* newBuffer(const love::graphics::Buffer::Settings& settings, const std::vector& format, const void* data, size_t size, size_t arraylength) override; - void clear(OptionalColorD color, OptionalInt stencil, OptionalDouble depth) override {} - void clear(const std::vector& colors, OptionalInt stencil, OptionalDouble depth) override {} - Matrix4 computeDeviceProjection(const Matrix4& projection, bool rendertotexture) const override { return Matrix4(); } - void discard(const std::vector& colorbuffers, bool depthstencil) override { } + void clear(OptionalColorD color, OptionalInt stencil, OptionalDouble depth) override { std::cout << "clear1 "; } + void clear(const std::vector& colors, OptionalInt stencil, OptionalDouble depth) override { std::cout << "clear2 "; } + Matrix4 computeDeviceProjection(const Matrix4& projection, bool rendertotexture) const override { std::cout << "computeDeviceProjection "; return Matrix4(); } + void discard(const std::vector& colorbuffers, bool depthstencil) override { std::cout << "discard "; } void present(void* screenshotCallbackdata) override; void setViewportSize(int width, int height, int pixelwidth, int pixelheight) override; - bool setMode(void* context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa) override { return false; } - void unSetMode() override {} - void setActive(bool active) override {} - int getRequestedBackbufferMSAA() const override { return 0; } - int getBackbufferMSAA() const override { return 0; } - void setColor(Colorf c) override {} - void setScissor(const Rect& rect) override {} - void setScissor() override {} - void setStencilMode(StencilAction action, CompareMode compare, int value, love::uint32 readmask, love::uint32 writemask) override {} - void setDepthMode(CompareMode compare, bool write) override {} - void setFrontFaceWinding(Winding winding) override {} - void setColorMask(ColorChannelMask mask) override {} - void setBlendState(const BlendState& blend) override {} - void setPointSize(float size) override {} - void setWireframe(bool enable) override {} - PixelFormat getSizedFormat(PixelFormat format, bool rendertarget, bool readable) const override { return PIXELFORMAT_UNKNOWN; } - bool isPixelFormatSupported(PixelFormat format, PixelFormatUsageFlags usage, bool sRGB = false) override { return false; } - Renderer getRenderer() const override { return RENDERER_VULKAN; } - bool usesGLSLES() const override { return false; } - RendererInfo getRendererInfo() const override { return {}; } - void draw(const DrawCommand& cmd) override {} - void draw(const DrawIndexedCommand& cmd) override {} - void drawQuads(int start, int count, const VertexAttributes& attributes, const BufferBindings& buffers, Texture* texture) override {} + bool setMode(void* context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa) override; + void unSetMode() override { std::cout << "unSetMode "; } + void setActive(bool active) override { std::cout << "setActive "; } + int getRequestedBackbufferMSAA() const override { std::cout << "getRequestedBackbufferMSAA "; return 0; } + int getBackbufferMSAA() const override { std::cout << "getBackbufferMSAA "; return 0; } + void setColor(Colorf c) override { std::cout << "setColor "; } + void setScissor(const Rect& rect) override { std::cout << "setScissor "; } + void setScissor() override { std::cout << "setScissor2 "; } + void setStencilMode(StencilAction action, CompareMode compare, int value, love::uint32 readmask, love::uint32 writemask) override { std::cout << "setStencilMode "; } + void setDepthMode(CompareMode compare, bool write) override { std::cout << "setDepthMode "; } + void setFrontFaceWinding(Winding winding) override { std::cout << "setFrontFaceWinding "; } + void setColorMask(ColorChannelMask mask) override { std::cout << "setColorMask "; } + void setBlendState(const BlendState& blend) override { std::cout << "setBlendState "; } + void setPointSize(float size) override { std::cout << "setPointSize "; } + void setWireframe(bool enable) override { std::cout << "setWireframe "; } + PixelFormat getSizedFormat(PixelFormat format, bool rendertarget, bool readable) const override { std::cout << "getSizedFormat "; return PIXELFORMAT_UNKNOWN; } + bool isPixelFormatSupported(PixelFormat format, PixelFormatUsageFlags usage, bool sRGB = false) override { std::cout << "isPixelFormatSupported "; return false; } + Renderer getRenderer() const override { std::cout << "getRenderer "; return RENDERER_VULKAN; } + bool usesGLSLES() const override { std::cout << "usesGLSES "; return false; } + RendererInfo getRendererInfo() const override { std::cout << "getRendererInfo "; return {}; } + void draw(const DrawCommand& cmd) override { std::cout << "draw "; } + void draw(const DrawIndexedCommand& cmd) override; + void drawQuads(int start, int count, const VertexAttributes& attributes, const BufferBindings& buffers, Texture* texture) override { std::cout << "drawQuads "; } protected: - ShaderStage* newShaderStageInternal(ShaderStageType stage, const std::string& cachekey, const std::string& source, bool gles) override { return nullptr; } - Shader* newShaderInternal(StrongRef stages[SHADERSTAGE_MAX_ENUM]) override { return nullptr; } - StreamBuffer* newStreamBuffer(BufferUsage type, size_t size) override { return nullptr; } - bool dispatch(int x, int y, int z) override { return false; } - void initCapabilities() override {} - void getAPIStats(int& shaderswitches) const override {} - void setRenderTargetsInternal(const RenderTargets& rts, int pixelw, int pixelh, bool hasSRGBtexture) override {} + graphics::ShaderStage* newShaderStageInternal(ShaderStageType stage, const std::string& cachekey, const std::string& source, bool gles) override { std::cout << "newShaderStageInternal "; return nullptr; } + graphics::Shader* newShaderInternal(StrongRef stages[SHADERSTAGE_MAX_ENUM]) override { std::cout << "newShaderInternal "; return nullptr; } + graphics::StreamBuffer* newStreamBuffer(BufferUsage type, size_t size) override; + bool dispatch(int x, int y, int z) override { std::cout << "dispatch "; return false; } + void initCapabilities() override { std::cout << "initCapabilities "; } + void getAPIStats(int& shaderswitches) const override { std::cout << "getAPIStats "; } + void setRenderTargetsInternal(const RenderTargets& rts, int pixelw, int pixelh, bool hasSRGBtexture) override { std::cout << "setRenderTargetsInternal "; } private: bool init = false; @@ -116,6 +117,9 @@ namespace love { void cleanupSwapChain(); void recreateSwapChain(); + void startRecordingGraphicsCommands(); + void endRecordingGraphicsCommands(); + VkInstance instance; VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; VkDevice device; @@ -133,13 +137,17 @@ namespace love { std::vector swapChainFramBuffers; VkCommandPool commandPool; std::vector commandBuffers; + VkClearValue clearColor = { {{0.0f, 0.0f, 0.0f, 1.0f}} }; std::vector imageAvailableSemaphores; std::vector renderFinishedSemaphores; std::vector inFlightFences; std::vector imagesInFlight; size_t currentFrame = 0; + uint32_t imageIndex; bool framebufferResized = false; + + friend class StreamBuffer; }; } } From 4692a0490c6e8e7fb7055ad255b42c9c3c6bbe5f Mon Sep 17 00:00:00 2001 From: niki Date: Sun, 6 Feb 2022 02:11:20 +0100 Subject: [PATCH 019/170] basic functionality for indexed draw in vulkan --- src/modules/graphics/vulkan/Graphics.cpp | 25 +++++++++++----------- src/modules/graphics/vulkan/StreamBuffer.h | 2 +- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/modules/graphics/vulkan/Graphics.cpp b/src/modules/graphics/vulkan/Graphics.cpp index 7f3dcfa45..9eb0dd99e 100644 --- a/src/modules/graphics/vulkan/Graphics.cpp +++ b/src/modules/graphics/vulkan/Graphics.cpp @@ -224,11 +224,11 @@ namespace love { std::vector buffers; std::vector offsets; buffers.push_back((VkBuffer)cmd.buffers->info[0].buffer->getHandle()); - offsets.push_back((VkDeviceSize)cmd.buffers->info[0].offset); - buffers.push_back((VkBuffer)cmd.buffers->info[1].buffer->getHandle()); - offsets.push_back((VkDeviceSize)cmd.buffers->info[1].offset); + offsets.push_back((VkDeviceSize) 0); - vkCmdDraw(commandBuffers.at(imageIndex), 3, 1, 0, 0); // todo adjust + vkCmdBindVertexBuffers(commandBuffers.at(imageIndex), 0, 1, buffers.data(), offsets.data()); + vkCmdBindIndexBuffer(commandBuffers.at(imageIndex), (VkBuffer) cmd.indexBuffer->getHandle(), 0, VK_INDEX_TYPE_UINT16); + vkCmdDrawIndexed(commandBuffers.at(imageIndex), static_cast(cmd.indexCount), 1, 0, 0, 0); } graphics::StreamBuffer* Graphics::newStreamBuffer(BufferUsage type, size_t size) { @@ -724,9 +724,9 @@ namespace love { positionInputAttributeDescription.format = VK_FORMAT_R32G32_SFLOAT; positionInputAttributeDescription.offset = 0; - vertexInputInfo.vertexBindingDescriptionCount = 0; + vertexInputInfo.vertexBindingDescriptionCount = 1; vertexInputInfo.pVertexBindingDescriptions = &vertexBindingDescription; - vertexInputInfo.vertexAttributeDescriptionCount = 0; + vertexInputInfo.vertexAttributeDescriptionCount = 1; vertexInputInfo.pVertexAttributeDescriptions = &positionInputAttributeDescription; VkPipelineInputAssemblyStateCreateInfo inputAssembly{}; @@ -855,7 +855,7 @@ namespace love { VkCommandPoolCreateInfo poolInfo{}; poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value(); - poolInfo.flags = 0; + poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) { throw love::Exception("failed to create command pool"); @@ -903,11 +903,6 @@ namespace love { cleanupSwapChain(); - for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { - vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr); - vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr); - vkDestroyFence(device, inFlightFences[i], nullptr); - } vkDestroyCommandPool(device, commandPool, nullptr); vkDestroyDevice(device, nullptr); vkDestroySurfaceKHR(instance, surface, nullptr); @@ -926,6 +921,11 @@ namespace love { vkDestroyImageView(device, swapChainImageViews[i], nullptr); } vkDestroySwapchainKHR(device, swapChain, nullptr); + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr); + vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr); + vkDestroyFence(device, inFlightFences[i], nullptr); + } } void Graphics::recreateSwapChain() { @@ -939,6 +939,7 @@ namespace love { createGraphicsPipeline(); createFramebuffers(); createCommandBuffers(); + createSyncObjects(); startRecordingGraphicsCommands(); } diff --git a/src/modules/graphics/vulkan/StreamBuffer.h b/src/modules/graphics/vulkan/StreamBuffer.h index d3a23e1b5..45ff57460 100644 --- a/src/modules/graphics/vulkan/StreamBuffer.h +++ b/src/modules/graphics/vulkan/StreamBuffer.h @@ -17,7 +17,7 @@ namespace love { void markUsed(size_t usedSize) override; ptrdiff_t getHandle() const override { - return 0; + return (ptrdiff_t) buffer; } private: From 197dfb9738494b4fe278cad41e4f2506e02a0ca7 Mon Sep 17 00:00:00 2001 From: niki Date: Sun, 6 Feb 2022 22:56:24 +0100 Subject: [PATCH 020/170] start implementing vulkan Shader type --- src/modules/graphics/Graphics.cpp | 3 +- src/modules/graphics/Graphics.h | 2 +- src/modules/graphics/Shader.cpp | 56 ++++++++++++++------- src/modules/graphics/Shader.h | 1 + src/modules/graphics/vulkan/Graphics.cpp | 13 ++++- src/modules/graphics/vulkan/Graphics.h | 12 ++++- src/modules/graphics/vulkan/Shader.cpp | 31 ++++++------ src/modules/graphics/vulkan/Shader.h | 28 +++++++++-- src/modules/graphics/vulkan/ShaderStage.cpp | 55 +++++++++++++++++++- 9 files changed, 157 insertions(+), 44 deletions(-) diff --git a/src/modules/graphics/Graphics.cpp b/src/modules/graphics/Graphics.cpp index a98cc8140..87f279029 100644 --- a/src/modules/graphics/Graphics.cpp +++ b/src/modules/graphics/Graphics.cpp @@ -342,7 +342,7 @@ ShaderStage *Graphics::newShaderStage(ShaderStageType stage, const std::string & return s; } -Shader *Graphics::newShader(const std::vector &stagessource) +Shader *Graphics::newShader(const std::vector &stagessource, bool vulkan) { StrongRef stages[SHADERSTAGE_MAX_ENUM] = {}; @@ -353,6 +353,7 @@ Shader *Graphics::newShader(const std::vector &stagessource) for (const std::string &source : stagessource) { Shader::SourceInfo info = Shader::getSourceInfo(source); + info.vulkan = vulkan; bool isanystage = false; for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++) diff --git a/src/modules/graphics/Graphics.h b/src/modules/graphics/Graphics.h index 253875769..95005303f 100644 --- a/src/modules/graphics/Graphics.h +++ b/src/modules/graphics/Graphics.h @@ -449,7 +449,7 @@ public: SpriteBatch *newSpriteBatch(Texture *texture, int size, BufferDataUsage usage); ParticleSystem *newParticleSystem(Texture *texture, int size); - Shader *newShader(const std::vector &stagessource); + Shader *newShader(const std::vector &stagessource, bool vulkan = false); Shader *newComputeShader(const std::string &source); virtual Buffer *newBuffer(const Buffer::Settings &settings, const std::vector &format, const void *data, size_t size, size_t arraylength) = 0; diff --git a/src/modules/graphics/Shader.cpp b/src/modules/graphics/Shader.cpp index 4d9f562bb..6b89b26f7 100644 --- a/src/modules/graphics/Shader.cpp +++ b/src/modules/graphics/Shader.cpp @@ -83,13 +83,27 @@ static const char global_syntax[] = R"( #ifdef GL_OES_standard_derivatives #extension GL_OES_standard_derivatives : enable #endif +#ifdef USE_VULKAN + #define VULKAN_LOCATION(x) layout(location=x) + #define VULKAN_BINDING(x) layout(binding=x) +#else + #define VULKAN_LOCATION(x) + #define VULKAN_BINDING(x) +#endif )"; static const char render_uniforms[] = R"( -// According to the GLSL ES 1.0 spec, uniform precision must match between stages, -// but we can't guarantee that highp is always supported in fragment shaders... -// We *really* don't want to use mediump for these in vertex shaders though. -uniform LOVE_HIGHP_OR_MEDIUMP vec4 love_UniformsPerDraw[13]; +#ifdef USE_VULKAN + VULKAN_BINDING(0) uniform LoveUniformsPerDraw { + vec4 uniformsPerDraw[13]; + } udp; + #define love_UniformsPerDraw udp.uniformsPerDraw +#else + // According to the GLSL ES 1.0 spec, uniform precision must match between stages, + // but we can't guarantee that highp is always supported in fragment shaders... + // We *really* don't want to use mediump for these in vertex shaders though. + uniform LOVE_HIGHP_OR_MEDIUMP vec4 love_UniformsPerDraw[13]; +#endif // These are initialized in love_initializeBuiltinUniforms below. GLSL ES can't // do it as an initializer. @@ -264,12 +278,12 @@ static const char vertex_header[] = R"( static const char vertex_functions[] = R"()"; static const char vertex_main[] = R"( -attribute vec4 VertexPosition; -attribute vec4 VertexTexCoord; -attribute vec4 VertexColor; +VULKAN_LOCATION(0) attribute vec4 VertexPosition; +VULKAN_LOCATION(1) attribute vec4 VertexTexCoord; +VULKAN_LOCATION(2) attribute vec4 VertexColor; -varying vec4 VaryingTexCoord; -varying vec4 VaryingColor; +VULKAN_LOCATION(0) varying vec4 VaryingTexCoord; +VULKAN_LOCATION(1) varying vec4 VaryingColor; vec4 position(mat4 clipSpaceFromLocal, vec4 localPosition); @@ -309,9 +323,9 @@ static const char pixel_header[] = R"( )"; static const char pixel_functions[] = R"( -uniform sampler2D love_VideoYChannel; -uniform sampler2D love_VideoCbChannel; -uniform sampler2D love_VideoCrChannel; +VULKAN_BINDING(1) uniform sampler2D love_VideoYChannel; +VULKAN_BINDING(2) uniform sampler2D love_VideoCbChannel; +VULKAN_BINDING(3) uniform sampler2D love_VideoCrChannel; vec4 VideoTexel(vec2 texcoords) { vec3 yuv; @@ -337,9 +351,9 @@ static const char pixel_main[] = R"( #define love_PixelColor gl_FragColor #endif -uniform sampler2D MainTex; -varying LOVE_HIGHP_OR_MEDIUMP vec4 VaryingTexCoord; -varying mediump vec4 VaryingColor; +VULKAN_BINDING(4) uniform sampler2D MainTex; +VULKAN_LOCATION(0) varying LOVE_HIGHP_OR_MEDIUMP vec4 VaryingTexCoord; +VULKAN_LOCATION(1) varying mediump vec4 VaryingColor; vec4 effect(vec4 vcolor, Image tex, vec2 texcoord, vec2 pixcoord); @@ -374,8 +388,8 @@ static const char pixel_main_custom[] = R"( #define LOVE_MULTI_CANVASES 1 #endif -varying LOVE_HIGHP_OR_MEDIUMP vec4 VaryingTexCoord; -varying mediump vec4 VaryingColor; +VULKAN_LOCATION(0) varying LOVE_HIGHP_OR_MEDIUMP vec4 VaryingTexCoord; +VULKAN_LOCATION(1) varying mediump vec4 VaryingColor; void effect(); @@ -562,6 +576,9 @@ std::string Shader::createShaderStageCode(Graphics *gfx, ShaderStageType stage, if (glsl1on3) lang = LANGUAGE_GLSL3; + if (info.vulkan) + lang = LANGUAGE_GLSL4; + glsl::StageInfo stageinfo = glsl::stageInfo[stage]; std::stringstream ss; @@ -574,6 +591,8 @@ std::string Shader::createShaderStageCode(Graphics *gfx, ShaderStageType stage, ss << "#define LOVE_GAMMA_CORRECT 1\n"; if (info.usesMRT) ss << "#define LOVE_MULTI_RENDER_TARGETS 1\n"; + if (info.vulkan) + ss << "#define USE_VULKAN\n"; ss << glsl::global_syntax; ss << stageinfo.header; ss << stageinfo.uniforms; @@ -893,7 +912,8 @@ bool Shader::validateInternal(StrongRef stages[], std::string &err, { LocalUniform u = {}; auto &values = u.initializerValues; - const glslang::TConstUnionArray *constarray = info.getConstArray(); + // const glslang::TConstUnionArray *constarray = info.getConstArray(); was this function deprecated in a later version? + const glslang::TConstUnionArray* constarray = nullptr; // Store initializer values for local uniforms. Some love graphics // backends strip these out of the shader so we need to be able to diff --git a/src/modules/graphics/Shader.h b/src/modules/graphics/Shader.h index c8d0ed838..ca3278a09 100644 --- a/src/modules/graphics/Shader.h +++ b/src/modules/graphics/Shader.h @@ -112,6 +112,7 @@ public: Language language; EntryPoint stages[SHADERSTAGE_MAX_ENUM]; bool usesMRT; + bool vulkan; }; struct MatrixSize diff --git a/src/modules/graphics/vulkan/Graphics.cpp b/src/modules/graphics/vulkan/Graphics.cpp index 9eb0dd99e..da3767c86 100644 --- a/src/modules/graphics/vulkan/Graphics.cpp +++ b/src/modules/graphics/vulkan/Graphics.cpp @@ -215,6 +215,17 @@ namespace love { batchedDrawState.indexBuffer = new StreamBuffer(device, physicalDevice, BUFFERUSAGE_INDEX, sizeof(uint16) * LOVE_UINT16_MAX); } + for (int i = 0; i < Shader::STANDARD_MAX_ENUM; i++) { + auto stype = (Shader::StandardShader)i; + + if (!Shader::standardShaders[i]) { + std::vector stages; + stages.push_back(Shader::getDefaultCode(stype, SHADERSTAGE_VERTEX)); + stages.push_back(Shader::getDefaultCode(stype, SHADERSTAGE_PIXEL)); + Shader::standardShaders[i] = newShader(stages, true); + } + } + return true; } @@ -759,7 +770,7 @@ namespace love { rasterizer.rasterizerDiscardEnable = VK_FALSE; rasterizer.polygonMode = VK_POLYGON_MODE_FILL; rasterizer.lineWidth = 1.0f; - rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; + rasterizer.cullMode = VK_CULL_MODE_FRONT_BIT; rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; rasterizer.depthBiasEnable = VK_FALSE; rasterizer.depthBiasConstantFactor = 0.0f; diff --git a/src/modules/graphics/vulkan/Graphics.h b/src/modules/graphics/vulkan/Graphics.h index 935c2171f..79f9f2334 100644 --- a/src/modules/graphics/vulkan/Graphics.h +++ b/src/modules/graphics/vulkan/Graphics.h @@ -3,6 +3,8 @@ #include "graphics/Graphics.h" #include "StreamBuffer.h" +#include "ShaderStage.h" +#include "Shader.h" #include #include @@ -66,8 +68,14 @@ namespace love { void drawQuads(int start, int count, const VertexAttributes& attributes, const BufferBindings& buffers, Texture* texture) override { std::cout << "drawQuads "; } protected: - graphics::ShaderStage* newShaderStageInternal(ShaderStageType stage, const std::string& cachekey, const std::string& source, bool gles) override { std::cout << "newShaderStageInternal "; return nullptr; } - graphics::Shader* newShaderInternal(StrongRef stages[SHADERSTAGE_MAX_ENUM]) override { std::cout << "newShaderInternal "; return nullptr; } + graphics::ShaderStage* newShaderStageInternal(ShaderStageType stage, const std::string& cachekey, const std::string& source, bool gles) override { + std::cout << "newShaderStageInternal "; + return new ShaderStage(this, stage, source, gles, cachekey); + } + graphics::Shader* newShaderInternal(StrongRef stages[SHADERSTAGE_MAX_ENUM]) override { + std::cout << "newShaderInternal "; + return new Shader(stages); + } graphics::StreamBuffer* newStreamBuffer(BufferUsage type, size_t size) override; bool dispatch(int x, int y, int z) override { std::cout << "dispatch "; return false; } void initCapabilities() override { std::cout << "initCapabilities "; } diff --git a/src/modules/graphics/vulkan/Shader.cpp b/src/modules/graphics/vulkan/Shader.cpp index 3e26be7e4..c9ab5a8cb 100644 --- a/src/modules/graphics/vulkan/Shader.cpp +++ b/src/modules/graphics/vulkan/Shader.cpp @@ -1,7 +1,5 @@ #include "Shader.h" -#include "libraries/glslang/glslang/Public/ShaderLang.h" -#include "libraries/glslang/SPIRV/GlslangToSpv.h" #include namespace love { @@ -21,24 +19,25 @@ namespace love { Shader::Shader(StrongRef stages[]) : graphics::Shader(stages) { - - if (false) { - for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++) { - if (!stages[i]) - continue; + for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++) { + if (!stages[i]) + continue; - auto stage = dynamic_cast(stages[i].get()); + auto stage = dynamic_cast(stages[i].get()); - VkPipelineShaderStageCreateInfo shaderStageInfo{}; - shaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - shaderStageInfo.stage = getStageBit(stage->getStageType()); - shaderStageInfo.module = stage->getShaderModule(); - shaderStageInfo.pName = "main"; + VkPipelineShaderStageCreateInfo shaderStageInfo{}; + shaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shaderStageInfo.stage = getStageBit(stage->getStageType()); + shaderStageInfo.module = stage->getShaderModule(); + shaderStageInfo.pName = "main"; - shaderStages.push_back(shaderStageInfo); - } + shaderStages.push_back(shaderStageInfo); } } + + int Shader::getVertexAttributeIndex(const std::string& name) { + return vertexAttributeIndices.at(name); + } } } -} \ No newline at end of file +} diff --git a/src/modules/graphics/vulkan/Shader.h b/src/modules/graphics/vulkan/Shader.h index 45f552e2a..0370ee63e 100644 --- a/src/modules/graphics/vulkan/Shader.h +++ b/src/modules/graphics/vulkan/Shader.h @@ -3,10 +3,10 @@ #include #include -#include "libraries/glslang/glslang/Public/ShaderLang.h" -#include "libraries/glslang/SPIRV/GlslangToSpv.h" #include +#include + namespace love { namespace graphics { @@ -26,7 +26,7 @@ namespace love { std::string getWarnings() const override { return ""; } - int getVertexAttributeIndex(const std::string& name) override { return 0; } + int getVertexAttributeIndex(const std::string& name) override; const UniformInfo* getUniformInfo(const std::string& name) const override { return nullptr; } const UniformInfo* getUniformInfo(BuiltinUniform builtin) const override { return nullptr; } @@ -41,7 +41,29 @@ namespace love { void setVideoTextures(Texture* ytexture, Texture* cbtexture, Texture* crtexture) override {} private: + struct Vec4 { + float x, y, z, w; + }; + + struct LoveUniformsPerDraw { + Vec4 uniformsPerDraw[13]; + }; + std::vector shaderStages; + + std::map vertexAttributeIndices = { + { "VertexPosition", 0 }, + { "VertexTexCoord", 1 }, + { "VertexColor", 2 } + }; + + std::map uniformBindings = { + { "love_UniformsPerDraw", 0 }, + { "love_VideoYChannel", 1 }, + { "love_VideoCbChannel", 2 }, + { "love_VideoCrChannel", 3 }, + { "MainTex", 4 } + }; }; } } diff --git a/src/modules/graphics/vulkan/ShaderStage.cpp b/src/modules/graphics/vulkan/ShaderStage.cpp index dfcf0095b..09426df90 100644 --- a/src/modules/graphics/vulkan/ShaderStage.cpp +++ b/src/modules/graphics/vulkan/ShaderStage.cpp @@ -9,12 +9,63 @@ namespace love { namespace graphics { namespace vulkan { + static int someIndex = 0; + + static std::string getFileEnding(ShaderStageType type) { + switch (type) { + case SHADERSTAGE_VERTEX: + return ".vert"; + case SHADERSTAGE_PIXEL: + return ".frag"; + default: + throw love::Exception("unsupported shader stage type"); + } + } + + static std::vector readFile(const std::string& filename) { + std::ifstream file(filename, std::ios::ate | std::ios::binary); + + if (!file.is_open()) { + throw std::runtime_error("failed to open file!"); + } + + size_t fileSize = (size_t)file.tellg(); + std::vector buffer(fileSize); + + file.seekg(0); + file.read(buffer.data(), fileSize); + + file.close(); + + return buffer; + } + + static int shaderSourceId = 0; + + std::vector compileShader(const std::string& glsl, ShaderStageType stage) { + // fixme: use glslang or shaderc for this + + std::string inputFileName = std::string("temp") + std::to_string(shaderSourceId++) + getFileEnding(stage); + std::string outputFileName = std::string("temp.spv"); + + std::ofstream out(inputFileName); + out << glsl; + out.close(); + + std::string command = std::string("glslc -fauto-bind-uniforms ") + inputFileName + " -o " + outputFileName; + system(command.c_str()); + + return readFile(outputFileName); + } + ShaderStage::ShaderStage(love::graphics::Graphics* gfx, ShaderStageType stage, const std::string& glsl, bool gles, const std::string& cachekey) : love::graphics::ShaderStage(gfx, stage, glsl, gles, cachekey) { + auto code = compileShader(glsl, stage); + VkShaderModuleCreateInfo createInfo{}; createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - createInfo.codeSize = 0; - createInfo.pCode = nullptr; + createInfo.codeSize = code.size(); + createInfo.pCode = reinterpret_cast(code.data()); Graphics* vkGfx = (Graphics*)gfx; device = vkGfx->getDevice(); From 157c8f8a2032b43549e3a40bc23a5cce601b3fdb Mon Sep 17 00:00:00 2001 From: niki Date: Mon, 21 Mar 2022 15:26:27 +0100 Subject: [PATCH 021/170] include custom vulkan shader in love source --- src/modules/graphics/Shader.cpp | 41 ++++++++++++++++- src/modules/graphics/vulkan/Graphics.cpp | 58 ++++++++---------------- src/modules/graphics/vulkan/Graphics.h | 1 + 3 files changed, 58 insertions(+), 42 deletions(-) diff --git a/src/modules/graphics/Shader.cpp b/src/modules/graphics/Shader.cpp index 6b89b26f7..d5b4cfaad 100644 --- a/src/modules/graphics/Shader.cpp +++ b/src/modules/graphics/Shader.cpp @@ -432,6 +432,37 @@ void main() { } )"; +static const char vulkan_vert[] = R"( +#version 450 + +layout(location = 0) in vec2 inPosition; + +layout(location = 0) out vec4 fragColor; + +float windowWidth = 800; +float windowHeight = 600; + +void main() { + gl_Position = vec4( + 2 * inPosition.x / windowWidth - 1, + 2 * inPosition.y / windowHeight - 1, + 0.0, 1.0); + fragColor = vec4(1, 1, 1, 1); +} +)"; + +static const char vulkan_pixel[] = R"( +#version 450 + +layout(location = 0) in vec4 fragColor; + +layout(location = 0) out vec4 outColor; + +void main() { + outColor = fragColor; +} +)"; + struct StageInfo { const char *name; @@ -576,8 +607,14 @@ std::string Shader::createShaderStageCode(Graphics *gfx, ShaderStageType stage, if (glsl1on3) lang = LANGUAGE_GLSL3; - if (info.vulkan) - lang = LANGUAGE_GLSL4; + if (info.vulkan) { + if (stage == SHADERSTAGE_VERTEX) { + return love::graphics::glsl::vulkan_vert; + } + if (stage == SHADERSTAGE_PIXEL) { + return love::graphics::glsl::vulkan_pixel; + } + } glsl::StageInfo stageinfo = glsl::stageInfo[stage]; diff --git a/src/modules/graphics/vulkan/Graphics.cpp b/src/modules/graphics/vulkan/Graphics.cpp index da3767c86..4bbc5a2bd 100644 --- a/src/modules/graphics/vulkan/Graphics.cpp +++ b/src/modules/graphics/vulkan/Graphics.cpp @@ -66,6 +66,7 @@ namespace love { createSwapChain(); createImageViews(); createRenderPass(); + createDefaultShaders(); createGraphicsPipeline(); createFramebuffers(); createCommandPool(); @@ -215,17 +216,6 @@ namespace love { batchedDrawState.indexBuffer = new StreamBuffer(device, physicalDevice, BUFFERUSAGE_INDEX, sizeof(uint16) * LOVE_UINT16_MAX); } - for (int i = 0; i < Shader::STANDARD_MAX_ENUM; i++) { - auto stype = (Shader::StandardShader)i; - - if (!Shader::standardShaders[i]) { - std::vector stages; - stages.push_back(Shader::getDefaultCode(stype, SHADERSTAGE_VERTEX)); - stages.push_back(Shader::getDefaultCode(stype, SHADERSTAGE_PIXEL)); - Shader::standardShaders[i] = newShader(stages, true); - } - } - return true; } @@ -696,29 +686,22 @@ namespace love { return shaderModule; } + void Graphics::createDefaultShaders() { + for (int i = 0; i < Shader::STANDARD_MAX_ENUM; i++) { + auto stype = (Shader::StandardShader)i; + + if (!Shader::standardShaders[i]) { + std::vector stages; + stages.push_back(Shader::getDefaultCode(stype, SHADERSTAGE_VERTEX)); + stages.push_back(Shader::getDefaultCode(stype, SHADERSTAGE_PIXEL)); + Shader::standardShaders[i] = newShader(stages, true); + } + } + } + void Graphics::createGraphicsPipeline() { - // love::graphics::vulkan::Shader* shader = dynamic_cast(getShader()); - // auto shaderStages = shader->getShaderStages(); - - auto vertShaderCode = readFile("vert.spv"); - auto fragShaderCode = readFile("frag.spv"); - - VkShaderModule vertShaderModule = createShaderModule(device, vertShaderCode); - VkShaderModule fragShaderModule = createShaderModule(device, fragShaderCode); - - VkPipelineShaderStageCreateInfo vertShaderStageInfo{}; - vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; - vertShaderStageInfo.module = vertShaderModule; - vertShaderStageInfo.pName = "main"; - - VkPipelineShaderStageCreateInfo fragShaderStageInfo{}; - fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; - fragShaderStageInfo.module = fragShaderModule; - fragShaderStageInfo.pName = "main"; - - VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo, fragShaderStageInfo }; + auto shader = reinterpret_cast(love::graphics::vulkan::Shader::standardShaders[Shader::STANDARD_DEFAULT]); + auto shaderStages = shader->getShaderStages(); VkPipelineVertexInputStateCreateInfo vertexInputInfo{}; vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; @@ -812,10 +795,8 @@ namespace love { VkGraphicsPipelineCreateInfo pipelineInfo{}; pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - // pipelineInfo.stageCount = static_cast(shaderStages.size()); - // pipelineInfo.pStages = shaderStages.data(); - pipelineInfo.stageCount = 2; - pipelineInfo.pStages = shaderStages; + pipelineInfo.stageCount = static_cast(shaderStages.size()); + pipelineInfo.pStages = shaderStages.data(); pipelineInfo.pVertexInputState = &vertexInputInfo; pipelineInfo.pInputAssemblyState = &inputAssembly; pipelineInfo.pViewportState = &viewportState; @@ -833,9 +814,6 @@ namespace love { if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) { throw love::Exception("failed to create graphics pipeline"); } - - vkDestroyShaderModule(device, vertShaderModule, nullptr); - vkDestroyShaderModule(device, fragShaderModule, nullptr); } void Graphics::createFramebuffers() { diff --git a/src/modules/graphics/vulkan/Graphics.h b/src/modules/graphics/vulkan/Graphics.h index 79f9f2334..e46d44a96 100644 --- a/src/modules/graphics/vulkan/Graphics.h +++ b/src/modules/graphics/vulkan/Graphics.h @@ -116,6 +116,7 @@ namespace love { void createSwapChain(); void createImageViews(); void createRenderPass(); + void createDefaultShaders(); void createGraphicsPipeline(); void createFramebuffers(); void createCommandPool(); From 158052c52c1a0f2ecf9a053f759b7a787641f74f Mon Sep 17 00:00:00 2001 From: niki Date: Tue, 22 Mar 2022 14:31:50 +0100 Subject: [PATCH 022/170] implement basic per frame uniform variables --- src/modules/graphics/Shader.cpp | 10 +- src/modules/graphics/vertex.h | 1 + src/modules/graphics/vulkan/Graphics.cpp | 102 ++++++++++++++++++- src/modules/graphics/vulkan/Graphics.h | 12 ++- src/modules/graphics/vulkan/Shader.h | 7 ++ src/modules/graphics/vulkan/StreamBuffer.cpp | 6 ++ src/modules/graphics/vulkan/StreamBuffer.h | 1 + 7 files changed, 133 insertions(+), 6 deletions(-) diff --git a/src/modules/graphics/Shader.cpp b/src/modules/graphics/Shader.cpp index d5b4cfaad..148ea4896 100644 --- a/src/modules/graphics/Shader.cpp +++ b/src/modules/graphics/Shader.cpp @@ -439,13 +439,15 @@ layout(location = 0) in vec2 inPosition; layout(location = 0) out vec4 fragColor; -float windowWidth = 800; -float windowHeight = 600; +layout(binding = 0) uniform LoveUniforms { + float windowWidth; + float windowHeight; +} loveUniforms; void main() { gl_Position = vec4( - 2 * inPosition.x / windowWidth - 1, - 2 * inPosition.y / windowHeight - 1, + 2 * inPosition.x / loveUniforms.windowWidth - 1, + 2 * inPosition.y / loveUniforms.windowHeight - 1, 0.0, 1.0); fragColor = vec4(1, 1, 1, 1); } diff --git a/src/modules/graphics/vertex.h b/src/modules/graphics/vertex.h index 73ed163fd..4994b603c 100644 --- a/src/modules/graphics/vertex.h +++ b/src/modules/graphics/vertex.h @@ -59,6 +59,7 @@ enum BufferUsage BUFFERUSAGE_VERTEX = 0, BUFFERUSAGE_INDEX, BUFFERUSAGE_TEXEL, + BUFFERUSAGE_UNIFORM, BUFFERUSAGE_SHADER_STORAGE, BUFFERUSAGE_MAX_ENUM }; diff --git a/src/modules/graphics/vulkan/Graphics.cpp b/src/modules/graphics/vulkan/Graphics.cpp index 4bbc5a2bd..0adf98080 100644 --- a/src/modules/graphics/vulkan/Graphics.cpp +++ b/src/modules/graphics/vulkan/Graphics.cpp @@ -67,10 +67,14 @@ namespace love { createImageViews(); createRenderPass(); createDefaultShaders(); + createDescriptorSetLayout(); createGraphicsPipeline(); createFramebuffers(); createCommandPool(); createCommandBuffers(); + createUniformBuffers(); + createDescriptorPool(); + createDescriptorSets(); createSyncObjects(); startRecordingGraphicsCommands(); } @@ -227,6 +231,9 @@ namespace love { buffers.push_back((VkBuffer)cmd.buffers->info[0].buffer->getHandle()); offsets.push_back((VkDeviceSize) 0); + prepareDraw(currentFrame); + + vkCmdBindDescriptorSets(commandBuffers.at(imageIndex), VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets.at(currentFrame), 0, nullptr); vkCmdBindVertexBuffers(commandBuffers.at(imageIndex), 0, 1, buffers.data(), offsets.data()); vkCmdBindIndexBuffer(commandBuffers.at(imageIndex), (VkBuffer) cmd.indexBuffer->getHandle(), 0, VK_INDEX_TYPE_UINT16); vkCmdDrawIndexed(commandBuffers.at(imageIndex), static_cast(cmd.indexCount), 1, 0, 0, 0); @@ -239,6 +246,18 @@ namespace love { // END IMPLEMENTATION OVERRIDDEN FUNCTIONS + void Graphics::prepareDraw(uint32_t currentImage) { + auto& buffer = uniformBuffers.at(currentImage); + + Shader::UniformBufferObject ubo{}; + ubo.windowWidth = static_cast(swapChainExtent.width); + ubo.windowHeight = static_cast(swapChainExtent.height); + + auto mappedInfo = buffer->map(0); + memcpy(mappedInfo.data, &ubo, sizeof(ubo)); + buffer->unmap(0); + } + void Graphics::createVulkanInstance() { if (enableValidationLayers && !checkValidationSupport()) { throw love::Exception("validation layers requested, but not available"); @@ -699,6 +718,79 @@ namespace love { } } + void Graphics::createDescriptorSetLayout() { + VkDescriptorSetLayoutBinding uboLayoutBinding{}; + uboLayoutBinding.binding = 0; + uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + uboLayoutBinding.descriptorCount = 1; + uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + + VkDescriptorSetLayoutCreateInfo layoutInfo{}; + layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layoutInfo.bindingCount = 1; + layoutInfo.pBindings = &uboLayoutBinding; + + if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &descriptorSetLayout) != VK_SUCCESS) { + throw love::Exception("failed to create descriptor set layout"); + } + } + + void Graphics::createUniformBuffers() { + VkDeviceSize bufferSize = sizeof(Shader::UniformBufferObject); + + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + uniformBuffers.push_back(std::make_unique(device, physicalDevice, BUFFERUSAGE_UNIFORM, bufferSize)); + } + } + + void Graphics::createDescriptorPool() { + VkDescriptorPoolSize poolSize{}; + poolSize.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + poolSize.descriptorCount = static_cast(MAX_FRAMES_IN_FLIGHT); + + VkDescriptorPoolCreateInfo poolInfo{}; + poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + poolInfo.poolSizeCount = 1; + poolInfo.pPoolSizes = &poolSize; + poolInfo.maxSets = static_cast(MAX_FRAMES_IN_FLIGHT); + + if (vkCreateDescriptorPool(device, &poolInfo, nullptr, &descriptorPool) != VK_SUCCESS) { + throw love::Exception("failed to create descriptor pool"); + } + } + + void Graphics::createDescriptorSets() { + std::vector layouts(MAX_FRAMES_IN_FLIGHT, descriptorSetLayout); + VkDescriptorSetAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocInfo.descriptorPool = descriptorPool; + allocInfo.descriptorSetCount = static_cast(MAX_FRAMES_IN_FLIGHT); + allocInfo.pSetLayouts = layouts.data(); + + descriptorSets.resize(MAX_FRAMES_IN_FLIGHT); + if (vkAllocateDescriptorSets(device, &allocInfo, descriptorSets.data()) != VK_SUCCESS) { + throw love::Exception("failed to allocate descriptor sets"); + } + + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + VkDescriptorBufferInfo bufferInfo{}; + bufferInfo.buffer = (VkBuffer) uniformBuffers.at(i)->getHandle(); + bufferInfo.offset = 0; + bufferInfo.range = sizeof(Shader::UniformBufferObject); + + VkWriteDescriptorSet descriptorWrite{}; + descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrite.dstSet = descriptorSets[i]; + descriptorWrite.dstBinding = 0; + descriptorWrite.dstArrayElement = 0; + descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptorWrite.descriptorCount = 1; + descriptorWrite.pBufferInfo = &bufferInfo; + + vkUpdateDescriptorSets(device, 1, &descriptorWrite, 0, nullptr); + } + } + void Graphics::createGraphicsPipeline() { auto shader = reinterpret_cast(love::graphics::vulkan::Shader::standardShaders[Shader::STANDARD_DEFAULT]); auto shaderStages = shader->getShaderStages(); @@ -786,7 +878,8 @@ namespace love { VkPipelineLayoutCreateInfo pipelineLayoutInfo{}; pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - pipelineLayoutInfo.setLayoutCount = 0; + pipelineLayoutInfo.setLayoutCount = 1; + pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout; pipelineLayoutInfo.pushConstantRangeCount = 0; if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) { @@ -892,6 +985,9 @@ namespace love { cleanupSwapChain(); + vkDestroyDescriptorPool(device, descriptorPool, nullptr); + + vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); vkDestroyCommandPool(device, commandPool, nullptr); vkDestroyDevice(device, nullptr); vkDestroySurfaceKHR(instance, surface, nullptr); @@ -915,6 +1011,7 @@ namespace love { vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr); vkDestroyFence(device, inFlightFences[i], nullptr); } + uniformBuffers.clear(); } void Graphics::recreateSwapChain() { @@ -927,6 +1024,9 @@ namespace love { createRenderPass(); createGraphicsPipeline(); createFramebuffers(); + createUniformBuffers(); + createDescriptorPool(); + createDescriptorSets(); createCommandBuffers(); createSyncObjects(); startRecordingGraphicsCommands(); diff --git a/src/modules/graphics/vulkan/Graphics.h b/src/modules/graphics/vulkan/Graphics.h index e46d44a96..0e079e992 100644 --- a/src/modules/graphics/vulkan/Graphics.h +++ b/src/modules/graphics/vulkan/Graphics.h @@ -11,6 +11,7 @@ #include #include +#include namespace love { @@ -117,6 +118,10 @@ namespace love { void createImageViews(); void createRenderPass(); void createDefaultShaders(); + void createDescriptorSetLayout(); + void createUniformBuffers(); + void createDescriptorPool(); + void createDescriptorSets(); void createGraphicsPipeline(); void createFramebuffers(); void createCommandPool(); @@ -128,6 +133,8 @@ namespace love { void startRecordingGraphicsCommands(); void endRecordingGraphicsCommands(); + + void prepareDraw(uint32_t currentImage); VkInstance instance; VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; @@ -140,6 +147,7 @@ namespace love { VkFormat swapChainImageFormat; VkExtent2D swapChainExtent; std::vector swapChainImageViews; + VkDescriptorSetLayout descriptorSetLayout; VkPipelineLayout pipelineLayout; VkRenderPass renderPass; VkPipeline graphicsPipeline; @@ -147,7 +155,9 @@ namespace love { VkCommandPool commandPool; std::vector commandBuffers; VkClearValue clearColor = { {{0.0f, 0.0f, 0.0f, 1.0f}} }; - + std::vector> uniformBuffers; + VkDescriptorPool descriptorPool; + std::vector descriptorSets; std::vector imageAvailableSemaphores; std::vector renderFinishedSemaphores; std::vector inFlightFences; diff --git a/src/modules/graphics/vulkan/Shader.h b/src/modules/graphics/vulkan/Shader.h index 0370ee63e..01e76616a 100644 --- a/src/modules/graphics/vulkan/Shader.h +++ b/src/modules/graphics/vulkan/Shader.h @@ -40,6 +40,11 @@ namespace love { void setVideoTextures(Texture* ytexture, Texture* cbtexture, Texture* crtexture) override {} + struct UniformBufferObject { + float windowWidth; + float windowHeight; + }; + private: struct Vec4 { float x, y, z, w; @@ -64,6 +69,8 @@ namespace love { { "love_VideoCrChannel", 3 }, { "MainTex", 4 } }; + + }; } } diff --git a/src/modules/graphics/vulkan/StreamBuffer.cpp b/src/modules/graphics/vulkan/StreamBuffer.cpp index 9d1626c19..19acf0e73 100644 --- a/src/modules/graphics/vulkan/StreamBuffer.cpp +++ b/src/modules/graphics/vulkan/StreamBuffer.cpp @@ -22,6 +22,7 @@ namespace love { switch (mode) { case BUFFERUSAGE_VERTEX: return VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; case BUFFERUSAGE_INDEX: return VK_BUFFER_USAGE_INDEX_BUFFER_BIT; + case BUFFERUSAGE_UNIFORM: return VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; default: throw love::Exception("unsupported BufferUsage mode"); } @@ -55,6 +56,11 @@ namespace love { vkBindBufferMemory(device, buffer, bufferMemory, 0); } + StreamBuffer::~StreamBuffer() { + //vkDestroyBuffer(device, buffer, nullptr); + //vkFreeMemory(device, bufferMemory, nullptr); + } + love::graphics::StreamBuffer::MapInfo StreamBuffer::map(size_t minsize) { vkMapMemory(device, bufferMemory, 0, getSize(), 0, &mappedMemory); return love::graphics::StreamBuffer::MapInfo((uint8*) mappedMemory, getSize()); diff --git a/src/modules/graphics/vulkan/StreamBuffer.h b/src/modules/graphics/vulkan/StreamBuffer.h index 45ff57460..46af2bf8a 100644 --- a/src/modules/graphics/vulkan/StreamBuffer.h +++ b/src/modules/graphics/vulkan/StreamBuffer.h @@ -11,6 +11,7 @@ namespace love { class StreamBuffer : public love::graphics::StreamBuffer { public: StreamBuffer(VkDevice device, VkPhysicalDevice physicalDevice, BufferUsage mode, size_t size); + virtual ~StreamBuffer(); MapInfo map(size_t minsize) override; size_t unmap(size_t usedSize) override; From 6dc12be45df2a1718074015a9255927b9df6174e Mon Sep 17 00:00:00 2001 From: niki Date: Tue, 22 Mar 2022 14:32:41 +0100 Subject: [PATCH 023/170] fix crash on resizing --- src/modules/graphics/vulkan/Graphics.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/modules/graphics/vulkan/Graphics.cpp b/src/modules/graphics/vulkan/Graphics.cpp index 0adf98080..a35c6ce72 100644 --- a/src/modules/graphics/vulkan/Graphics.cpp +++ b/src/modules/graphics/vulkan/Graphics.cpp @@ -985,6 +985,12 @@ namespace love { cleanupSwapChain(); + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr); + vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr); + vkDestroyFence(device, inFlightFences[i], nullptr); + } + vkDestroyDescriptorPool(device, descriptorPool, nullptr); vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); @@ -1006,11 +1012,6 @@ namespace love { vkDestroyImageView(device, swapChainImageViews[i], nullptr); } vkDestroySwapchainKHR(device, swapChain, nullptr); - for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { - vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr); - vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr); - vkDestroyFence(device, inFlightFences[i], nullptr); - } uniformBuffers.clear(); } @@ -1028,7 +1029,6 @@ namespace love { createDescriptorPool(); createDescriptorSets(); createCommandBuffers(); - createSyncObjects(); startRecordingGraphicsCommands(); } From 0953f3540e598fc2db7cc48b062fb8913d9fbf38 Mon Sep 17 00:00:00 2001 From: niki Date: Tue, 3 May 2022 17:51:18 +0200 Subject: [PATCH 024/170] remove temporary files after compilation --- src/modules/graphics/vulkan/ShaderStage.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/modules/graphics/vulkan/ShaderStage.cpp b/src/modules/graphics/vulkan/ShaderStage.cpp index 09426df90..20d41e945 100644 --- a/src/modules/graphics/vulkan/ShaderStage.cpp +++ b/src/modules/graphics/vulkan/ShaderStage.cpp @@ -5,6 +5,8 @@ #include #include +#include + namespace love { namespace graphics { @@ -55,7 +57,12 @@ namespace love { std::string command = std::string("glslc -fauto-bind-uniforms ") + inputFileName + " -o " + outputFileName; system(command.c_str()); - return readFile(outputFileName); + auto result = readFile(outputFileName); + + std::remove(inputFileName.c_str()); + std::remove(outputFileName.c_str()); + + return result; } ShaderStage::ShaderStage(love::graphics::Graphics* gfx, ShaderStageType stage, const std::string& glsl, bool gles, const std::string& cachekey) From 92992199770591d2f5708b6bb439adc9121dc10a Mon Sep 17 00:00:00 2001 From: niki Date: Tue, 3 May 2022 17:51:35 +0200 Subject: [PATCH 025/170] perform prepareDraw only once per frame --- src/modules/graphics/vulkan/Graphics.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/graphics/vulkan/Graphics.cpp b/src/modules/graphics/vulkan/Graphics.cpp index a35c6ce72..6b85fa47a 100644 --- a/src/modules/graphics/vulkan/Graphics.cpp +++ b/src/modules/graphics/vulkan/Graphics.cpp @@ -147,6 +147,8 @@ namespace love { endRecordingGraphicsCommands(); + prepareDraw(currentFrame); + if (imagesInFlight[imageIndex] != VK_NULL_HANDLE) { vkWaitForFences(device, 1, &imagesInFlight.at(imageIndex), VK_TRUE, UINT64_MAX); } @@ -231,8 +233,6 @@ namespace love { buffers.push_back((VkBuffer)cmd.buffers->info[0].buffer->getHandle()); offsets.push_back((VkDeviceSize) 0); - prepareDraw(currentFrame); - vkCmdBindDescriptorSets(commandBuffers.at(imageIndex), VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets.at(currentFrame), 0, nullptr); vkCmdBindVertexBuffers(commandBuffers.at(imageIndex), 0, 1, buffers.data(), offsets.data()); vkCmdBindIndexBuffer(commandBuffers.at(imageIndex), (VkBuffer) cmd.indexBuffer->getHandle(), 0, VK_INDEX_TYPE_UINT16); From 394956e3202b07d873cc3ebbd74aec0d1ef3b2b2 Mon Sep 17 00:00:00 2001 From: niki Date: Tue, 3 May 2022 17:51:54 +0200 Subject: [PATCH 026/170] ignore minsize in StreamBuffer::map --- src/modules/graphics/vulkan/StreamBuffer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/graphics/vulkan/StreamBuffer.cpp b/src/modules/graphics/vulkan/StreamBuffer.cpp index 19acf0e73..cff2543bb 100644 --- a/src/modules/graphics/vulkan/StreamBuffer.cpp +++ b/src/modules/graphics/vulkan/StreamBuffer.cpp @@ -62,6 +62,7 @@ namespace love { } love::graphics::StreamBuffer::MapInfo StreamBuffer::map(size_t minsize) { + (void)minsize; vkMapMemory(device, bufferMemory, 0, getSize(), 0, &mappedMemory); return love::graphics::StreamBuffer::MapInfo((uint8*) mappedMemory, getSize()); } From 77f1af598df1cb71e04263828ef7f0399b1cd9de Mon Sep 17 00:00:00 2001 From: niki Date: Tue, 3 May 2022 18:11:31 +0200 Subject: [PATCH 027/170] add skeleton for vulkan texture impl --- CMakeLists.txt | 2 ++ src/modules/graphics/vulkan/Texture.cpp | 11 +++++++++++ src/modules/graphics/vulkan/Texture.h | 24 ++++++++++++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 src/modules/graphics/vulkan/Texture.cpp create mode 100644 src/modules/graphics/vulkan/Texture.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 44cbb6dc6..e11b77b5c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -582,6 +582,8 @@ set(LOVE_SRC_MODULE_GRAPHICS_VULKAN src/modules/graphics/vulkan/StreamBuffer.cpp src/modules/graphics/vulkan/Buffer.h src/modules/graphics/vulkan/Buffer.cpp + src/modules/graphics/vulkan/Texture.h + src/modules/graphics/vulkan/Texture.cpp ) set(LOVE_SRC_MODULE_GRAPHICS diff --git a/src/modules/graphics/vulkan/Texture.cpp b/src/modules/graphics/vulkan/Texture.cpp new file mode 100644 index 000000000..e3fad771e --- /dev/null +++ b/src/modules/graphics/vulkan/Texture.cpp @@ -0,0 +1,11 @@ +#include "Texture.h" + +namespace love { + namespace graphics { + namespace vulkan { + Texture::Texture(love::graphics::Graphics* gfx, const Settings& settings, const Slices* data) + : love::graphics::Texture(gfx, settings, data) { + } + } + } +} \ No newline at end of file diff --git a/src/modules/graphics/vulkan/Texture.h b/src/modules/graphics/vulkan/Texture.h new file mode 100644 index 000000000..b05f3b100 --- /dev/null +++ b/src/modules/graphics/vulkan/Texture.h @@ -0,0 +1,24 @@ +#include "graphics/Texture.h" + + +namespace love { + namespace graphics { + namespace vulkan { + class Texture : public graphics::Texture { + public: + Texture(love::graphics::Graphics* gfx, const Settings& settings, const Slices* data); + + void copyFromBuffer(Buffer* source, size_t sourceoffset, int sourcewidth, size_t size, int slice, int mipmap, const Rect& rect) override {}; + void copyToBuffer(Buffer* dest, int slice, int mipmap, const Rect& rect, size_t destoffset, int destwidth, size_t size) override {}; + + ptrdiff_t getRenderTargetHandle() const override {}; + ptrdiff_t getSamplerHandle() const override {}; + + void uploadByteData(PixelFormat pixelformat, const void* data, size_t size, int level, int slice, const Rect& r) override {}; + + void generateMipmapsInternal() override {}; + void readbackImageData(love::image::ImageData* imagedata, int slice, int mipmap, const Rect& rect) override {}; + }; + } + } +} \ No newline at end of file From 74c0c192f7145e22d5f606c6b3d17cb297b9450d Mon Sep 17 00:00:00 2001 From: niki Date: Tue, 3 May 2022 21:09:16 +0200 Subject: [PATCH 028/170] add VMA as library --- CMakeLists.txt | 2 + .../VulkanMemoryAllocator-3.0.0/.gitignore | 4 + .../VulkanMemoryAllocator-3.0.0/.travis.yml | 37 + .../VulkanMemoryAllocator-3.0.0/CHANGELOG.md | 173 + .../CMakeLists.txt | 55 + .../VulkanMemoryAllocator-3.0.0/Doxyfile | 2685 +++ .../VulkanMemoryAllocator-3.0.0/LICENSE.txt | 19 + .../VulkanMemoryAllocator-3.0.0/README.md | 175 + .../bin/Shader.frag.spv | Bin 0 -> 1004 bytes .../bin/Shader.vert.spv | Bin 0 -> 1472 bytes .../bin/VmaSample_Release_vs2019.exe | Bin 0 -> 391680 bytes .../docs/.nojekyll | 0 .../docs/gfx/Aliasing.png | Bin 0 -> 6208 bytes .../gfx/Linear_allocator_1_algo_default.png | Bin 0 -> 1264 bytes .../gfx/Linear_allocator_2_algo_linear.png | Bin 0 -> 1989 bytes .../gfx/Linear_allocator_3_free_at_once.png | Bin 0 -> 5205 bytes .../docs/gfx/Linear_allocator_4_stack.png | Bin 0 -> 3356 bytes .../gfx/Linear_allocator_5_ring_buffer.png | Bin 0 -> 2246 bytes .../gfx/Linear_allocator_7_double_stack.png | Bin 0 -> 1595 bytes .../docs/gfx/Margins_1.png | Bin 0 -> 268 bytes .../docs/gfx/Margins_2.png | Bin 0 -> 6542 bytes .../docs/gfx/VMA_class_diagram.png | Bin 0 -> 36539 bytes .../docs/html/allocation_annotation.html | 117 + .../docs/html/annotated.html | 102 + .../docs/html/bc_s.png | Bin 0 -> 676 bytes .../docs/html/bdwn.png | Bin 0 -> 147 bytes .../docs/html/choosing_memory_type.html | 168 + .../docs/html/classes.html | 80 + .../docs/html/closed.png | Bin 0 -> 132 bytes .../docs/html/configuration.html | 101 + .../docs/html/custom_memory_pools.html | 206 + .../docs/html/debugging_memory_usage.html | 113 + .../docs/html/defragmentation.html | 198 + .../docs/html/deprecated.html | 89 + .../dir_d44c64559bbebec7f509842c48db8b23.html | 84 + .../docs/html/doc.png | Bin 0 -> 746 bytes .../docs/html/doxygen.css | 1841 ++ .../docs/html/doxygen.svg | 26 + .../docs/html/dynsections.js | 121 + .../html/enabling_buffer_device_address.html | 96 + .../docs/html/files.html | 80 + .../docs/html/folderclosed.png | Bin 0 -> 616 bytes .../docs/html/folderopen.png | Bin 0 -> 597 bytes .../docs/html/functions.html | 206 + .../docs/html/functions_vars.html | 206 + .../docs/html/general_considerations.html | 136 + .../docs/html/globals.html | 243 + .../docs/html/globals_defs.html | 78 + .../docs/html/globals_enum.html | 81 + .../docs/html/globals_eval.html | 134 + .../docs/html/globals_func.html | 141 + .../docs/html/globals_type.html | 113 + .../docs/html/group__group__alloc.html | 2706 +++ .../docs/html/group__group__init.html | 577 + .../docs/html/group__group__stats.html | 520 + .../docs/html/group__group__virtual.html | 690 + .../docs/html/index.html | 178 + .../docs/html/jquery.js | 35 + .../docs/html/memory_mapping.html | 141 + .../docs/html/menu.js | 127 + .../docs/html/menudata.js | 75 + .../docs/html/modules.html | 82 + .../docs/html/nav_f.png | Bin 0 -> 153 bytes .../docs/html/nav_g.png | Bin 0 -> 95 bytes .../docs/html/nav_h.png | Bin 0 -> 98 bytes .../docs/html/open.png | Bin 0 -> 123 bytes .../docs/html/opengl_interop.html | 92 + .../docs/html/pages.html | 79 + .../docs/html/quick_start.html | 173 + .../docs/html/resource_aliasing.html | 160 + .../docs/html/search/all_0.html | 37 + .../docs/html/search/all_0.js | 10 + .../docs/html/search/all_1.html | 37 + .../docs/html/search/all_1.js | 9 + .../docs/html/search/all_10.html | 37 + .../docs/html/search/all_10.js | 7 + .../docs/html/search/all_11.html | 37 + .../docs/html/search/all_11.js | 204 + .../docs/html/search/all_2.html | 37 + .../docs/html/search/all_2.js | 6 + .../docs/html/search/all_3.html | 37 + .../docs/html/search/all_3.js | 10 + .../docs/html/search/all_4.html | 37 + .../docs/html/search/all_4.js | 4 + .../docs/html/search/all_5.html | 37 + .../docs/html/search/all_5.js | 4 + .../docs/html/search/all_6.html | 37 + .../docs/html/search/all_6.js | 4 + .../docs/html/search/all_7.html | 37 + .../docs/html/search/all_7.js | 4 + .../docs/html/search/all_8.html | 37 + .../docs/html/search/all_8.js | 4 + .../docs/html/search/all_9.html | 37 + .../docs/html/search/all_9.js | 15 + .../docs/html/search/all_a.html | 37 + .../docs/html/search/all_a.js | 6 + .../docs/html/search/all_b.html | 37 + .../docs/html/search/all_b.js | 22 + .../docs/html/search/all_c.html | 37 + .../docs/html/search/all_c.js | 4 + .../docs/html/search/all_d.html | 37 + .../docs/html/search/all_d.js | 6 + .../docs/html/search/all_e.html | 37 + .../docs/html/search/all_e.js | 9 + .../docs/html/search/all_f.html | 37 + .../docs/html/search/all_f.js | 4 + .../docs/html/search/classes_0.html | 37 + .../docs/html/search/classes_0.js | 27 + .../docs/html/search/close.svg | 31 + .../docs/html/search/defines_0.html | 37 + .../docs/html/search/defines_0.js | 8 + .../docs/html/search/enums_0.html | 37 + .../docs/html/search/enums_0.js | 11 + .../docs/html/search/enumvalues_0.html | 37 + .../docs/html/search/enumvalues_0.js | 62 + .../docs/html/search/files_0.html | 37 + .../docs/html/search/files_0.js | 4 + .../docs/html/search/functions_0.html | 37 + .../docs/html/search/functions_0.js | 69 + .../docs/html/search/groups_0.html | 37 + .../docs/html/search/groups_0.js | 4 + .../docs/html/search/groups_1.html | 37 + .../docs/html/search/groups_1.js | 4 + .../docs/html/search/groups_2.html | 37 + .../docs/html/search/groups_2.js | 4 + .../docs/html/search/groups_3.html | 37 + .../docs/html/search/groups_3.js | 4 + .../docs/html/search/mag_sel.svg | 74 + .../docs/html/search/nomatches.html | 13 + .../docs/html/search/pages_0.html | 37 + .../docs/html/search/pages_0.js | 4 + .../docs/html/search/pages_1.html | 37 + .../docs/html/search/pages_1.js | 6 + .../docs/html/search/pages_2.html | 37 + .../docs/html/search/pages_2.js | 6 + .../docs/html/search/pages_3.html | 37 + .../docs/html/search/pages_3.js | 4 + .../docs/html/search/pages_4.html | 37 + .../docs/html/search/pages_4.js | 4 + .../docs/html/search/pages_5.html | 37 + .../docs/html/search/pages_5.js | 4 + .../docs/html/search/pages_6.html | 37 + .../docs/html/search/pages_6.js | 4 + .../docs/html/search/pages_7.html | 37 + .../docs/html/search/pages_7.js | 4 + .../docs/html/search/pages_8.html | 37 + .../docs/html/search/pages_8.js | 5 + .../docs/html/search/pages_9.html | 37 + .../docs/html/search/pages_9.js | 5 + .../docs/html/search/pages_a.html | 37 + .../docs/html/search/pages_a.js | 8 + .../docs/html/search/search.css | 263 + .../docs/html/search/search.js | 794 + .../docs/html/search/search_l.png | Bin 0 -> 567 bytes .../docs/html/search/search_m.png | Bin 0 -> 158 bytes .../docs/html/search/search_r.png | Bin 0 -> 553 bytes .../docs/html/search/searchdata.js | 45 + .../docs/html/search/typedefs_0.html | 37 + .../docs/html/search/typedefs_0.js | 5 + .../docs/html/search/typedefs_1.html | 37 + .../docs/html/search/typedefs_1.js | 35 + .../docs/html/search/variables_0.html | 37 + .../docs/html/search/variables_0.js | 9 + .../docs/html/search/variables_1.html | 37 + .../docs/html/search/variables_1.js | 9 + .../docs/html/search/variables_2.html | 37 + .../docs/html/search/variables_2.js | 7 + .../docs/html/search/variables_3.html | 37 + .../docs/html/search/variables_3.js | 4 + .../docs/html/search/variables_4.html | 37 + .../docs/html/search/variables_4.js | 4 + .../docs/html/search/variables_5.html | 37 + .../docs/html/search/variables_5.js | 13 + .../docs/html/search/variables_6.html | 37 + .../docs/html/search/variables_6.js | 5 + .../docs/html/search/variables_7.html | 37 + .../docs/html/search/variables_7.js | 20 + .../docs/html/search/variables_8.html | 37 + .../docs/html/search/variables_8.js | 4 + .../docs/html/search/variables_9.html | 37 + .../docs/html/search/variables_9.js | 6 + .../docs/html/search/variables_a.html | 37 + .../docs/html/search/variables_a.js | 4 + .../docs/html/search/variables_b.html | 37 + .../docs/html/search/variables_b.js | 7 + .../docs/html/search/variables_c.html | 37 + .../docs/html/search/variables_c.js | 30 + .../docs/html/splitbar.png | Bin 0 -> 314 bytes .../docs/html/statistics.html | 106 + .../docs/html/staying_within_budget.html | 105 + .../docs/html/struct_vma_allocation.html | 86 + ...ct_vma_allocation_create_info-members.html | 85 + .../struct_vma_allocation_create_info.html | 254 + .../struct_vma_allocation_info-members.html | 84 + .../docs/html/struct_vma_allocation_info.html | 235 + .../docs/html/struct_vma_allocator.html | 84 + ...uct_vma_allocator_create_info-members.html | 88 + .../struct_vma_allocator_create_info.html | 317 + .../struct_vma_allocator_info-members.html | 80 + .../docs/html/struct_vma_allocator_info.html | 150 + .../docs/html/struct_vma_budget-members.html | 80 + .../docs/html/struct_vma_budget.html | 152 + .../struct_vma_defragmentation_context.html | 83 + ...ruct_vma_defragmentation_info-members.html | 81 + .../html/struct_vma_defragmentation_info.html | 170 + ...ruct_vma_defragmentation_move-members.html | 80 + .../html/struct_vma_defragmentation_move.html | 148 + ...efragmentation_pass_move_info-members.html | 79 + ...ct_vma_defragmentation_pass_move_info.html | 145 + ...uct_vma_defragmentation_stats-members.html | 81 + .../struct_vma_defragmentation_stats.html | 166 + ...truct_vma_detailed_statistics-members.html | 83 + .../html/struct_vma_detailed_statistics.html | 209 + ...t_vma_device_memory_callbacks-members.html | 80 + .../struct_vma_device_memory_callbacks.html | 149 + .../docs/html/struct_vma_pool.html | 84 + .../struct_vma_pool_create_info-members.html | 85 + .../html/struct_vma_pool_create_info.html | 251 + .../html/struct_vma_statistics-members.html | 81 + .../docs/html/struct_vma_statistics.html | 170 + .../struct_vma_total_statistics-members.html | 80 + .../html/struct_vma_total_statistics.html | 139 + .../html/struct_vma_virtual_allocation.html | 84 + ...irtual_allocation_create_info-members.html | 81 + ...ct_vma_virtual_allocation_create_info.html | 169 + ...t_vma_virtual_allocation_info-members.html | 80 + .../struct_vma_virtual_allocation_info.html | 150 + .../docs/html/struct_vma_virtual_block.html | 84 + ...vma_virtual_block_create_info-members.html | 80 + .../struct_vma_virtual_block_create_info.html | 149 + .../struct_vma_vulkan_functions-members.html | 103 + .../html/struct_vma_vulkan_functions.html | 531 + .../docs/html/sync_off.png | Bin 0 -> 853 bytes .../docs/html/sync_on.png | Bin 0 -> 845 bytes .../docs/html/tab_a.png | Bin 0 -> 142 bytes .../docs/html/tab_b.png | Bin 0 -> 169 bytes .../docs/html/tab_h.png | Bin 0 -> 177 bytes .../docs/html/tab_s.png | Bin 0 -> 184 bytes .../docs/html/tabs.css | 1 + .../docs/html/usage_patterns.html | 255 + .../docs/html/virtual_allocator.html | 185 + .../docs/html/vk__mem__alloc_8h.html | 634 + .../html/vk_amd_device_coherent_memory.html | 100 + .../docs/html/vk_ext_memory_priority.html | 132 + .../html/vk_khr_dedicated_allocation.html | 103 + .../include/vk_mem_alloc.h | 19564 ++++++++++++++++ .../media/Thumbnail.png | Bin 0 -> 22054 bytes .../src/.editorconfig | 6 + .../src/CMakeLists.txt | 109 + .../src/Common.cpp | 328 + .../VulkanMemoryAllocator-3.0.0/src/Common.h | 339 + .../src/Shaders/CMakeLists.txt | 32 + .../src/Shaders/Shader.frag | 37 + .../src/Shaders/Shader.vert | 42 + .../src/Shaders/SparseBindingTest.comp | 44 + .../src/SparseBindingTest.cpp | 597 + .../src/SparseBindingTest.h | 29 + .../VulkanMemoryAllocator-3.0.0/src/Tests.cpp | 8061 +++++++ .../VulkanMemoryAllocator-3.0.0/src/Tests.h | 32 + .../src/VmaUsage.cpp | 30 + .../src/VmaUsage.h | 101 + .../src/VulkanSample.cpp | 2644 +++ .../src/vk_mem_alloc.natvis | 71 + .../tools/GpuMemDumpVis/GpuMemDumpVis.py | 334 + .../tools/GpuMemDumpVis/README.md | 45 + .../README_files/ExampleOutput.png | Bin 0 -> 98005 bytes .../GpuMemDumpVis/README_files/Legend_Bkg.png | Bin 0 -> 196 bytes .../README_files/Legend_Buffer_1.png | Bin 0 -> 1705 bytes .../README_files/Legend_Buffer_2.png | Bin 0 -> 1590 bytes .../README_files/Legend_Buffer_3.png | Bin 0 -> 1877 bytes .../README_files/Legend_Buffer_4.png | Bin 0 -> 2195 bytes .../README_files/Legend_Details.png | Bin 0 -> 964 bytes .../README_files/Legend_Image_1.png | Bin 0 -> 1739 bytes .../README_files/Legend_Image_2.png | Bin 0 -> 1592 bytes .../README_files/Legend_Image_3.png | Bin 0 -> 1719 bytes .../README_files/Legend_Image_4.png | Bin 0 -> 1925 bytes .../README_files/Legend_Image_Linear.png | Bin 0 -> 1432 bytes .../README_files/Legend_Image_Unknown.png | Bin 0 -> 1449 bytes .../README_files/Legend_Unknown.png | Bin 0 -> 1356 bytes .../tools/GpuMemDumpVis/Sample.json | 426 + 280 files changed, 57650 insertions(+) create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/.gitignore create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/.travis.yml create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/CHANGELOG.md create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/CMakeLists.txt create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/Doxyfile create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/LICENSE.txt create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/README.md create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/bin/Shader.frag.spv create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/bin/Shader.vert.spv create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/bin/VmaSample_Release_vs2019.exe create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/.nojekyll create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/gfx/Aliasing.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/gfx/Linear_allocator_1_algo_default.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/gfx/Linear_allocator_2_algo_linear.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/gfx/Linear_allocator_3_free_at_once.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/gfx/Linear_allocator_4_stack.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/gfx/Linear_allocator_5_ring_buffer.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/gfx/Linear_allocator_7_double_stack.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/gfx/Margins_1.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/gfx/Margins_2.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/gfx/VMA_class_diagram.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/allocation_annotation.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/annotated.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/bc_s.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/bdwn.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/choosing_memory_type.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/classes.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/closed.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/configuration.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/custom_memory_pools.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/debugging_memory_usage.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/defragmentation.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/deprecated.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/dir_d44c64559bbebec7f509842c48db8b23.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/doc.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/doxygen.css create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/doxygen.svg create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/dynsections.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/enabling_buffer_device_address.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/files.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/folderclosed.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/folderopen.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/functions.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/functions_vars.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/general_considerations.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/globals.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/globals_defs.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/globals_enum.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/globals_eval.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/globals_func.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/globals_type.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/group__group__alloc.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/group__group__init.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/group__group__stats.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/group__group__virtual.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/index.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/jquery.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/memory_mapping.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/menu.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/menudata.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/modules.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/nav_f.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/nav_g.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/nav_h.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/open.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/opengl_interop.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/pages.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/quick_start.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/resource_aliasing.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/all_0.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/all_0.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/all_1.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/all_1.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/all_10.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/all_10.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/all_11.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/all_11.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/all_2.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/all_2.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/all_3.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/all_3.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/all_4.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/all_4.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/all_5.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/all_5.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/all_6.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/all_6.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/all_7.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/all_7.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/all_8.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/all_8.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/all_9.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/all_9.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/all_a.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/all_a.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/all_b.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/all_b.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/all_c.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/all_c.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/all_d.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/all_d.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/all_e.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/all_e.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/all_f.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/all_f.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/classes_0.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/classes_0.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/close.svg create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/defines_0.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/defines_0.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/enums_0.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/enums_0.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/enumvalues_0.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/enumvalues_0.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/files_0.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/files_0.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/functions_0.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/functions_0.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/groups_0.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/groups_0.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/groups_1.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/groups_1.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/groups_2.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/groups_2.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/groups_3.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/groups_3.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/mag_sel.svg create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/nomatches.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/pages_0.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/pages_0.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/pages_1.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/pages_1.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/pages_2.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/pages_2.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/pages_3.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/pages_3.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/pages_4.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/pages_4.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/pages_5.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/pages_5.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/pages_6.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/pages_6.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/pages_7.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/pages_7.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/pages_8.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/pages_8.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/pages_9.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/pages_9.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/pages_a.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/pages_a.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/search.css create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/search.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/search_l.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/search_m.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/search_r.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/searchdata.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/typedefs_0.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/typedefs_0.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/typedefs_1.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/typedefs_1.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/variables_0.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/variables_0.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/variables_1.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/variables_1.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/variables_2.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/variables_2.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/variables_3.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/variables_3.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/variables_4.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/variables_4.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/variables_5.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/variables_5.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/variables_6.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/variables_6.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/variables_7.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/variables_7.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/variables_8.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/variables_8.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/variables_9.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/variables_9.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/variables_a.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/variables_a.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/variables_b.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/variables_b.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/variables_c.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/search/variables_c.js create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/splitbar.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/statistics.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/staying_within_budget.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_allocation.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_allocation_create_info-members.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_allocation_create_info.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_allocation_info-members.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_allocation_info.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_allocator.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_allocator_create_info-members.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_allocator_create_info.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_allocator_info-members.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_allocator_info.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_budget-members.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_budget.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_defragmentation_context.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_defragmentation_info-members.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_defragmentation_info.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_defragmentation_move-members.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_defragmentation_move.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_defragmentation_pass_move_info-members.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_defragmentation_pass_move_info.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_defragmentation_stats-members.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_defragmentation_stats.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_detailed_statistics-members.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_detailed_statistics.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_device_memory_callbacks-members.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_device_memory_callbacks.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_pool.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_pool_create_info-members.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_pool_create_info.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_statistics-members.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_statistics.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_total_statistics-members.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_total_statistics.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_virtual_allocation.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_virtual_allocation_create_info-members.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_virtual_allocation_create_info.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_virtual_allocation_info-members.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_virtual_allocation_info.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_virtual_block.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_virtual_block_create_info-members.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_virtual_block_create_info.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_vulkan_functions-members.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/struct_vma_vulkan_functions.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/sync_off.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/sync_on.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/tab_a.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/tab_b.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/tab_h.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/tab_s.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/tabs.css create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/usage_patterns.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/virtual_allocator.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/vk__mem__alloc_8h.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/vk_amd_device_coherent_memory.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/vk_ext_memory_priority.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/docs/html/vk_khr_dedicated_allocation.html create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/include/vk_mem_alloc.h create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/media/Thumbnail.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/src/.editorconfig create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/src/CMakeLists.txt create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/src/Common.cpp create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/src/Common.h create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/src/Shaders/CMakeLists.txt create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/src/Shaders/Shader.frag create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/src/Shaders/Shader.vert create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/src/Shaders/SparseBindingTest.comp create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/src/SparseBindingTest.cpp create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/src/SparseBindingTest.h create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/src/Tests.cpp create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/src/Tests.h create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/src/VmaUsage.cpp create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/src/VmaUsage.h create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/src/VulkanSample.cpp create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/src/vk_mem_alloc.natvis create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/tools/GpuMemDumpVis/GpuMemDumpVis.py create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/tools/GpuMemDumpVis/README.md create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/tools/GpuMemDumpVis/README_files/ExampleOutput.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/tools/GpuMemDumpVis/README_files/Legend_Bkg.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/tools/GpuMemDumpVis/README_files/Legend_Buffer_1.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/tools/GpuMemDumpVis/README_files/Legend_Buffer_2.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/tools/GpuMemDumpVis/README_files/Legend_Buffer_3.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/tools/GpuMemDumpVis/README_files/Legend_Buffer_4.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/tools/GpuMemDumpVis/README_files/Legend_Details.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/tools/GpuMemDumpVis/README_files/Legend_Image_1.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/tools/GpuMemDumpVis/README_files/Legend_Image_2.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/tools/GpuMemDumpVis/README_files/Legend_Image_3.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/tools/GpuMemDumpVis/README_files/Legend_Image_4.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/tools/GpuMemDumpVis/README_files/Legend_Image_Linear.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/tools/GpuMemDumpVis/README_files/Legend_Image_Unknown.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/tools/GpuMemDumpVis/README_files/Legend_Unknown.png create mode 100644 src/libraries/VulkanMemoryAllocator-3.0.0/tools/GpuMemDumpVis/Sample.json diff --git a/CMakeLists.txt b/CMakeLists.txt index e11b77b5c..23c9bad09 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,6 +67,7 @@ endif() if(MEGA) find_package(Vulkan REQUIRED) + add_subdirectory(src/libraries/VulkanMemoryAllocator-3.0.0) # LOVE_MSVC_DLLS contains runtime DLLs that should be bundled with the love # binary (in e.g. the installer). Example: msvcp140.dll. @@ -99,6 +100,7 @@ if(MEGA) ${MEGA_SDL2} ${MEGA_ZLIB} ${Vulkan_LIBRARIES} + VulkanMemoryAllocator ) # These DLLs are moved next to the love binary in a post-build step to diff --git a/src/libraries/VulkanMemoryAllocator-3.0.0/.gitignore b/src/libraries/VulkanMemoryAllocator-3.0.0/.gitignore new file mode 100644 index 000000000..d4501944d --- /dev/null +++ b/src/libraries/VulkanMemoryAllocator-3.0.0/.gitignore @@ -0,0 +1,4 @@ +/bin/* +/build/* +!/bin/VmaSample_Release_vs2019.exe +!/bin/Shader*.spv diff --git a/src/libraries/VulkanMemoryAllocator-3.0.0/.travis.yml b/src/libraries/VulkanMemoryAllocator-3.0.0/.travis.yml new file mode 100644 index 000000000..55871b7a9 --- /dev/null +++ b/src/libraries/VulkanMemoryAllocator-3.0.0/.travis.yml @@ -0,0 +1,37 @@ +language: cpp +sudo: required +os: linux +dist: bionic + +branches: + only: + - master + +compiler: + - clang + - gcc + +before_script: + - sudo apt-get install + - eval "${MATRIX_EVAL}" + +install: + - sudo apt-get -qq update + - sudo apt-get install -y libassimp-dev libglm-dev graphviz libxcb-dri3-0 libxcb-present0 libpciaccess0 cmake libpng-dev libxcb-dri3-dev libx11-dev libx11-xcb-dev libmirclient-dev libwayland-dev libxrandr-dev + - export VK_VERSION=1.2.189.0 + - wget -O vulkansdk-linux-x86_64-$VK_VERSION.tar.gz https://sdk.lunarg.com/sdk/download/$VK_VERSION/linux/vulkansdk-linux-x86_64-$VK_VERSION.tar.gz?Human=true + - tar zxf vulkansdk-linux-x86_64-$VK_VERSION.tar.gz + - export VULKAN_SDK=$TRAVIS_BUILD_DIR/$VK_VERSION/x86_64 + +script: + - mkdir -p build + - cd build + - cmake .. + - make + +notifications: + email: + recipients: + - adam.sawicki@amd.com + on_success: change + on_failure: always diff --git a/src/libraries/VulkanMemoryAllocator-3.0.0/CHANGELOG.md b/src/libraries/VulkanMemoryAllocator-3.0.0/CHANGELOG.md new file mode 100644 index 000000000..5d5498d34 --- /dev/null +++ b/src/libraries/VulkanMemoryAllocator-3.0.0/CHANGELOG.md @@ -0,0 +1,173 @@ +# 3.0.0 (2022-03-25) + +It has been a long time since the previous official release, so hopefully everyone has been using the latest code from "master" branch, which is always maintained in a good state, not the old version. For completeness, here is the list of changes since v2.3.0. The major version number has changed, so there are some compatibility-breaking changes, but the basic API stays the same and is mostly backward-compatible. + +Major features added (some compatibility-breaking): + +- Added new API for selecting preferred memory type: flags `VMA_MEMORY_USAGE_AUTO`, `VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE`, `VMA_MEMORY_USAGE_AUTO_PREFER_HOST`, `VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT`, `VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT`, `VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT`. Old values like `VMA_MEMORY_USAGE_GPU_ONLY` still work as before, for backward compatibility, but are not recommended. +- Added new defragmentation API and algorithm, replacing the old one. See structure `VmaDefragmentationInfo`, `VmaDefragmentationMove`, `VmaDefragmentationPassMoveInfo`, `VmaDefragmentationStats`, function `vmaBeginDefragmentation`, `vmaEndDefragmentation`, `vmaBeginDefragmentationPass`, `vmaEndDefragmentationPass`. +- Redesigned API for statistics, replacing the old one. See structures: `VmaStatistics`, `VmaDetailedStatistics`, `VmaTotalStatistics`. `VmaBudget`, functions: `vmaGetHeapBudgets`, `vmaCalculateStatistics`, `vmaGetPoolStatistics`, `vmaCalculatePoolStatistics`, `vmaGetVirtualBlockStatistics`, `vmaCalculateVirtualBlockStatistics`. +- Added "Virtual allocator" feature - possibility to use core allocation algorithms for allocation of custom memory, not necessarily Vulkan device memory. See functions like `vmaCreateVirtualBlock`, `vmaDestroyVirtualBlock` and many more. +- `VmaAllocation` now keeps both `void* pUserData` and `char* pName`. Added function `vmaSetAllocationName`, member `VmaAllocationInfo::pName`. Flag `VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT` is now deprecated. +- Clarified and cleaned up various ways of importing Vulkan functions. See macros `VMA_STATIC_VULKAN_FUNCTIONS`, `VMA_DYNAMIC_VULKAN_FUNCTIONS`, structure `VmaVulkanFunctions`. Added members `VmaVulkanFunctions::vkGetInstanceProcAddr`, `vkGetDeviceProcAddr`, which are now required when using `VMA_DYNAMIC_VULKAN_FUNCTIONS`. + +Removed (compatibility-breaking): + +- Removed whole "lost allocations" feature. Removed from the interface: `VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT`, `VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT`, `vmaCreateLostAllocation`, `vmaMakePoolAllocationsLost`, `vmaTouchAllocation`, `VmaAllocatorCreateInfo::frameInUseCount`, `VmaPoolCreateInfo::frameInUseCount`. +- Removed whole "record & replay" feature. Removed from the API: `VmaAllocatorCreateInfo::pRecordSettings`, `VmaRecordSettings`, `VmaRecordFlagBits`, `VmaRecordFlags`. Removed VmaReplay application. +- Removed "buddy" algorithm - removed flag `VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT`. + +Minor but compatibility-breaking changes: + +- Changes in `ALLOCATION_CREATE_STRATEGY` flags. Removed flags: `VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT`, `VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT`, `VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT`, which were aliases to other existing flags. +- Added a member `void* pUserData` to `VmaDeviceMemoryCallbacks`. Updated `PFN_vmaAllocateDeviceMemoryFunction`, `PFN_vmaFreeDeviceMemoryFunction` to use the new `pUserData` member. +- Removed function `vmaResizeAllocation` that was already deprecated. + +Other major changes: + +- Added new features to custom pools: support for dedicated allocations, new member `VmaPoolCreateInfo::pMemoryAllocateNext`, `minAllocationAlignment`. +- Added support for Vulkan 1.2, 1.3. +- Added support for VK_KHR_buffer_device_address extension - flag `VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT`. +- Added support for VK_EXT_memory_priority extension - flag `VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT`, members `VmaAllocationCreateInfo::priority`, `VmaPoolCreateInfo::priority`. +- Added support for VK_AMD_device_coherent_memory extension - flag `VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT`. +- Added member `VmaAllocatorCreateInfo::pTypeExternalMemoryHandleTypes`. +- Added function `vmaGetAllocatorInfo`, structure `VmaAllocatorInfo`. +- Added functions `vmaFlushAllocations`, `vmaInvalidateAllocations` for multiple allocations at once. +- Added flag `VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT`. +- Added function `vmaCreateBufferWithAlignment`. +- Added convenience function `vmaGetAllocationMemoryProperties`. +- Added convenience functions: `vmaCreateAliasingBuffer`, `vmaCreateAliasingImage`. + +Other minor changes: + +- Implemented Two-Level Segregated Fit (TLSF) allocation algorithm, replacing previous default one. It is much faster, especially when freeing many allocations at once or when `bufferImageGranularity` is large. +- Renamed debug macro `VMA_DEBUG_ALIGNMENT` to `VMA_MIN_ALIGNMENT`. +- Added CMake support - CMakeLists.txt files. Removed Premake support. +- Changed `vmaInvalidateAllocation` and `vmaFlushAllocation` to return `VkResult`. +- Added nullability annotations for Clang: `VMA_NULLABLE`, `VMA_NOT_NULL`, `VMA_NULLABLE_NON_DISPATCHABLE`, `VMA_NOT_NULL_NON_DISPATCHABLE`, `VMA_LEN_IF_NOT_NULL`. +- JSON dump format has changed. +- Countless fixes and improvements, including performance optimizations, compatibility with various platforms and compilers, documentation. + +# 2.3.0 (2019-12-04) + +Major release after a year of development in "master" branch and feature branches. Notable new features: supporting Vulkan 1.1, supporting query for memory budget. + +Major changes: + +- Added support for Vulkan 1.1. + - Added member `VmaAllocatorCreateInfo::vulkanApiVersion`. + - When Vulkan 1.1 is used, there is no need to enable VK_KHR_dedicated_allocation or VK_KHR_bind_memory2 extensions, as they are promoted to Vulkan itself. +- Added support for query for memory budget and staying within the budget. + - Added function `vmaGetBudget`, structure `VmaBudget`. This can also serve as simple statistics, more efficient than `vmaCalculateStats`. + - By default the budget it is estimated based on memory heap sizes. It may be queried from the system using VK_EXT_memory_budget extension if you use `VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT` flag and `VmaAllocatorCreateInfo::instance` member. + - Added flag `VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT` that fails an allocation if it would exceed the budget. +- Added new memory usage options: + - `VMA_MEMORY_USAGE_CPU_COPY` for memory that is preferably not `DEVICE_LOCAL` but not guaranteed to be `HOST_VISIBLE`. + - `VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED` for memory that is `LAZILY_ALLOCATED`. +- Added support for VK_KHR_bind_memory2 extension: + - Added `VMA_ALLOCATION_CREATE_DONT_BIND_BIT` flag that lets you create both buffer/image and allocation, but don't bind them together. + - Added flag `VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT`, functions `vmaBindBufferMemory2`, `vmaBindImageMemory2` that let you specify additional local offset and `pNext` pointer while binding. +- Added functions `vmaSetPoolName`, `vmaGetPoolName` that let you assign string names to custom pools. JSON dump file format and VmaDumpVis tool is updated to show these names. +- Defragmentation is legal only on buffers and images in `VK_IMAGE_TILING_LINEAR`. This is due to the way it is currently implemented in the library and the restrictions of the Vulkan specification. Clarified documentation in this regard. See discussion in #59. + +Minor changes: + +- Made `vmaResizeAllocation` function deprecated, always returning failure. +- Made changes in the internal algorithm for the choice of memory type. Be careful! You may now get a type that is not `HOST_VISIBLE` or `HOST_COHERENT` if it's not stated as always ensured by some `VMA_MEMORY_USAGE_*` flag. +- Extended VmaReplay application with more detailed statistics printed at the end. +- Added macros `VMA_CALL_PRE`, `VMA_CALL_POST` that let you decorate declarations of all library functions if you want to e.g. export/import them as dynamically linked library. +- Optimized `VmaAllocation` objects to be allocated out of an internal free-list allocator. This makes allocation and deallocation causing 0 dynamic CPU heap allocations on average. +- Updated recording CSV file format version to 1.8, to support new functions. +- Many additions and fixes in documentation. Many compatibility fixes for various compilers and platforms. Other internal bugfixes, optimizations, updates, refactoring... + +# 2.2.0 (2018-12-13) + +Major release after many months of development in "master" branch and feature branches. Notable new features: defragmentation of GPU memory, buddy algorithm, convenience functions for sparse binding. + +Major changes: + +- New, more powerful defragmentation: + - Added structure `VmaDefragmentationInfo2`, functions `vmaDefragmentationBegin`, `vmaDefragmentationEnd`. + - Added support for defragmentation of GPU memory. + - Defragmentation of CPU memory now uses `memmove`, so it can move data to overlapping regions. + - Defragmentation of CPU memory is now available for memory types that are `HOST_VISIBLE` but not `HOST_COHERENT`. + - Added structure member `VmaVulkanFunctions::vkCmdCopyBuffer`. + - Major internal changes in defragmentation algorithm. + - VmaReplay: added parameters: `--DefragmentAfterLine`, `--DefragmentationFlags`. + - Old interface (structure `VmaDefragmentationInfo`, function `vmaDefragment`) is now deprecated. +- Added buddy algorithm, available for custom pools - flag `VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT`. +- Added convenience functions for multiple allocations and deallocations at once, intended for sparse binding resources - functions `vmaAllocateMemoryPages`, `vmaFreeMemoryPages`. +- Added function that tries to resize existing allocation in place: `vmaResizeAllocation`. +- Added flags for allocation strategy: `VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT`, `VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT`, `VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT`, and their aliases: `VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT`, `VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT`, `VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT`. + +Minor changes: + +- Changed behavior of allocation functions to return `VK_ERROR_VALIDATION_FAILED_EXT` when trying to allocate memory of size 0, create buffer with size 0, or image with one of the dimensions 0. +- VmaReplay: Added support for Windows end of lines. +- Updated recording CSV file format version to 1.5, to support new functions. +- Internal optimization: using read-write mutex on some platforms. +- Many additions and fixes in documentation. Many compatibility fixes for various compilers. Other internal bugfixes, optimizations, refactoring, added more internal validation... + +# 2.1.0 (2018-09-10) + +Minor bugfixes. + +# 2.1.0-beta.1 (2018-08-27) + +Major release after many months of development in "development" branch and features branches. Many new features added, some bugs fixed. API stays backward-compatible. + +Major changes: + +- Added linear allocation algorithm, accessible for custom pools, that can be used as free-at-once, stack, double stack, or ring buffer. See "Linear allocation algorithm" documentation chapter. + - Added `VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT`, `VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT`. +- Added feature to record sequence of calls to the library to a file and replay it using dedicated application. See documentation chapter "Record and replay". + - Recording: added `VmaAllocatorCreateInfo::pRecordSettings`. + - Replaying: added VmaReplay project. + - Recording file format: added document "docs/Recording file format.md". +- Improved support for non-coherent memory. + - Added functions: `vmaFlushAllocation`, `vmaInvalidateAllocation`. + - `nonCoherentAtomSize` is now respected automatically. + - Added `VmaVulkanFunctions::vkFlushMappedMemoryRanges`, `vkInvalidateMappedMemoryRanges`. +- Improved debug features related to detecting incorrect mapped memory usage. See documentation chapter "Debugging incorrect memory usage". + - Added debug macro `VMA_DEBUG_DETECT_CORRUPTION`, functions `vmaCheckCorruption`, `vmaCheckPoolCorruption`. + - Added debug macro `VMA_DEBUG_INITIALIZE_ALLOCATIONS` to initialize contents of allocations with a bit pattern. + - Changed behavior of `VMA_DEBUG_MARGIN` macro - it now adds margin also before first and after last allocation in a block. +- Changed format of JSON dump returned by `vmaBuildStatsString` (not backward compatible!). + - Custom pools and memory blocks now have IDs that don't change after sorting. + - Added properties: "CreationFrameIndex", "LastUseFrameIndex", "Usage". + - Changed VmaDumpVis tool to use these new properties for better coloring. + - Changed behavior of `vmaGetAllocationInfo` and `vmaTouchAllocation` to update `allocation.lastUseFrameIndex` even if allocation cannot become lost. + +Minor changes: + +- Changes in custom pools: + - Added new structure member `VmaPoolStats::blockCount`. + - Changed behavior of `VmaPoolCreateInfo::blockSize` = 0 (default) - it now means that pool may use variable block sizes, just like default pools do. +- Improved logic of `vmaFindMemoryTypeIndex` for some cases, especially integrated GPUs. +- VulkanSample application: Removed dependency on external library MathFu. Added own vector and matrix structures. +- Changes that improve compatibility with various platforms, including: Visual Studio 2012, 32-bit code, C compilers. + - Changed usage of "VK_KHR_dedicated_allocation" extension in the code to be optional, driven by macro `VMA_DEDICATED_ALLOCATION`, for compatibility with Android. +- Many additions and fixes in documentation, including description of new features, as well as "Validation layer warnings". +- Other bugfixes. + +# 2.0.0 (2018-03-19) + +A major release with many compatibility-breaking changes. + +Notable new features: + +- Introduction of `VmaAllocation` handle that you must retrieve from allocation functions and pass to deallocation functions next to normal `VkBuffer` and `VkImage`. +- Introduction of `VmaAllocationInfo` structure that you can retrieve from `VmaAllocation` handle to access parameters of the allocation (like `VkDeviceMemory` and offset) instead of retrieving them directly from allocation functions. +- Support for reference-counted mapping and persistently mapped allocations - see `vmaMapMemory`, `VMA_ALLOCATION_CREATE_MAPPED_BIT`. +- Support for custom memory pools - see `VmaPool` handle, `VmaPoolCreateInfo` structure, `vmaCreatePool` function. +- Support for defragmentation (compaction) of allocations - see function `vmaDefragment` and related structures. +- Support for "lost allocations" - see appropriate chapter on documentation Main Page. + +# 1.0.1 (2017-07-04) + +- Fixes for Linux GCC compilation. +- Changed "CONFIGURATION SECTION" to contain #ifndef so you can define these macros before including this header, not necessarily change them in the file. + +# 1.0.0 (2017-06-16) + +First public release. diff --git a/src/libraries/VulkanMemoryAllocator-3.0.0/CMakeLists.txt b/src/libraries/VulkanMemoryAllocator-3.0.0/CMakeLists.txt new file mode 100644 index 000000000..959058cdc --- /dev/null +++ b/src/libraries/VulkanMemoryAllocator-3.0.0/CMakeLists.txt @@ -0,0 +1,55 @@ +cmake_minimum_required(VERSION 3.9) + +project(VulkanMemoryAllocator) + +find_package(Vulkan REQUIRED) +include_directories(${Vulkan_INCLUDE_DIR}) + +# VulkanMemoryAllocator contains an sample application which is not built by default +option(VMA_BUILD_SAMPLE "Build VulkanMemoryAllocator sample application" OFF) +option(VMA_BUILD_SAMPLE_SHADERS "Build VulkanMemoryAllocator sample application's shaders" OFF) + +message(STATUS "VMA_BUILD_SAMPLE = ${VMA_BUILD_SAMPLE}") +message(STATUS "VMA_BUILD_SAMPLE_SHADERS = ${VMA_BUILD_SAMPLE_SHADERS}") + +option(VMA_STATIC_VULKAN_FUNCTIONS "Link statically with Vulkan API" ON) +option(VMA_DYNAMIC_VULKAN_FUNCTIONS "Fetch pointers to Vulkan functions internally (no static linking)" OFF) +option(VMA_DEBUG_ALWAYS_DEDICATED_MEMORY "Every allocation will have its own memory block" OFF) +option(VMA_DEBUG_INITIALIZE_ALLOCATIONS "Automatically fill new allocations and destroyed allocations with some bit pattern" OFF) +option(VMA_DEBUG_GLOBAL_MUTEX "Enable single mutex protecting all entry calls to the library" OFF) +option(VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT "Never exceed VkPhysicalDeviceLimits::maxMemoryAllocationCount and return error" OFF) + +message(STATUS "VMA_STATIC_VULKAN_FUNCTIONS = ${VMA_STATIC_VULKAN_FUNCTIONS}") +message(STATUS "VMA_DYNAMIC_VULKAN_FUNCTIONS = ${VMA_DYNAMIC_VULKAN_FUNCTIONS}") +message(STATUS "VMA_DEBUG_ALWAYS_DEDICATED_MEMORY = ${VMA_DEBUG_ALWAYS_DEDICATED_MEMORY}") +message(STATUS "VMA_DEBUG_INITIALIZE_ALLOCATIONS = ${VMA_DEBUG_INITIALIZE_ALLOCATIONS}") +message(STATUS "VMA_DEBUG_GLOBAL_MUTEX = ${VMA_DEBUG_GLOBAL_MUTEX}") +message(STATUS "VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT = ${VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT}") + +if(VMA_BUILD_SAMPLE) + set(VMA_BUILD_SAMPLE_SHADERS ON) +endif(VMA_BUILD_SAMPLE) + +find_package(Doxygen) +option(BUILD_DOCUMENTATION "Create and install the HTML based API documentation (requires Doxygen)" OFF) + +if(BUILD_DOCUMENTATION) + if(DOXYGEN_FOUND) + # set input and output files + set(DOXYGEN_IN ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile) + set(DOXYGEN_OUT ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) + + # request to configure the file + configure_file(${DOXYGEN_IN} ${DOXYGEN_OUT} @ONLY) + # note the option ALL which allows to build the docs together with the application + add_custom_target( doc_doxygen ALL + COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUT} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating API documentation with Doxygen" + VERBATIM ) + else() + message("Doxygen need to be installed to generate the doxygen documentation") + endif() +endif() + +add_subdirectory(src) diff --git a/src/libraries/VulkanMemoryAllocator-3.0.0/Doxyfile b/src/libraries/VulkanMemoryAllocator-3.0.0/Doxyfile new file mode 100644 index 000000000..d0c4aeff2 --- /dev/null +++ b/src/libraries/VulkanMemoryAllocator-3.0.0/Doxyfile @@ -0,0 +1,2685 @@ +# Doxyfile 1.9.2 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "Vulkan Memory Allocator" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = "@CMAKE_SOURCE_DIR@/docs" + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = NO + +# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line +# such as +# /*************** +# as being the beginning of a Javadoc-style comment "banner". If set to NO, the +# Javadoc-style will behave just like regular comments and it will not be +# interpreted by doxygen. +# The default value is: NO. + +JAVADOC_BANNER = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# By default Python docstrings are displayed as preformatted text and doxygen's +# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the +# doxygen's special commands can be used and the contents of the docstring +# documentation blocks is shown as doxygen documentation. +# The default value is: YES. + +PYTHON_DOCSTRING = YES + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:^^" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". Note that you cannot put \n's in the value part of an alias +# to insert newlines (in the resulting output). You can put ^^ in the value part +# of an alias to insert a newline as if a physical newline was in the original +# file. When you need a literal { or } or , in the value part of an alias you +# have to escape them by means of a backslash (\), this can lead to conflicts +# with the commands \{ and \} for these it is advised to use the version @{ and +# @} or use a double escape (\\{ and \\}) + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, +# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice, +# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files). For instance to make doxygen treat .inc files +# as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. When specifying no_extension you should add +# * to the FILE_PATTERNS. +# +# Note see also the list of default file extension mappings. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See https://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 5. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 0 + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use +# during processing. When set to 0 doxygen will based this on the number of +# cores available in the system. You can set it explicitly to a value larger +# than 0 to get more control over the balance between CPU load and processing +# speed. At this moment only the input processing can be done using multiple +# threads. Since this is still an experimental feature the default is set to 1, +# which effectively disables parallel processing. Please report any issues you +# encounter. Generating dot graphs in parallel is controlled by the +# DOT_NUM_THREADS setting. +# Minimum value: 0, maximum value: 32, default value: 1. + +NUM_PROC_THREADS = 1 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual +# methods of a class will be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIV_VIRTUAL = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If this flag is set to YES, the name of an unnamed parameter in a declaration +# will be determined by the corresponding definition. By default unnamed +# parameters remain unnamed in the output. +# The default value is: YES. + +RESOLVE_UNNAMED_PARAMS = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# declarations. If set to NO, these declarations will be included in the +# documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# With the correct setting of option CASE_SENSE_NAMES doxygen will better be +# able to match the capabilities of the underlying filesystem. In case the +# filesystem is case sensitive (i.e. it supports files in the same directory +# whose names only differ in casing), the option must be set to YES to properly +# deal with such files in case they appear in the input. For filesystems that +# are not case sensitive the option should be be set to NO to properly deal with +# output files written for symbols that only differ in casing, such as for two +# classes, one named CLASS and the other named Class, and to also support +# references to files without having to specify the exact matching casing. On +# Windows (including Cygwin) and MacOS, users should typically set this option +# to NO, whereas on Linux or other Unix flavors it should typically be set to +# YES. +# The default value is: system dependent. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class +# will show which file needs to be included to use the class. +# The default value is: YES. + +SHOW_HEADERFILE = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. See also section "Changing the +# layout of pages" for information. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as documenting some parameters in +# a documented function twice, or documenting parameters that don't exist or +# using markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete +# function parameter documentation. If set to NO, doxygen will accept that some +# parameters have no documentation without warning. +# The default value is: YES. + +WARN_IF_INCOMPLETE_DOC = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong parameter +# documentation, but not about the absence of documentation. If EXTRACT_ALL is +# set to YES then this flag will automatically be disabled. See also +# WARN_IF_INCOMPLETE_DOC +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS +# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the doxygen process doxygen will return with a non-zero status. +# Possible values are: NO, YES and FAIL_ON_WARNINGS. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = "@CMAKE_SOURCE_DIR@/include/vk_mem_alloc.h" + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: +# https://www.gnu.org/software/libiconv/) for the list of possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# Note the list of default checked file patterns might differ from the list of +# default file extension mappings. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, +# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C +# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, +# *.vhdl, *.ucf, *.qsf and *.ice. + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.idl \ + *.ddl \ + *.odl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.cs \ + *.d \ + *.php \ + *.php4 \ + *.php5 \ + *.phtml \ + *.inc \ + *.m \ + *.markdown \ + *.md \ + *.mm \ + *.dox \ + *.py \ + *.pyw \ + *.f90 \ + *.f95 \ + *.f03 \ + *.f08 \ + *.f \ + *.for \ + *.tcl \ + *.vhd \ + *.vhdl \ + *.ucf \ + *.qsf + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# entity all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see https://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = NO + +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: +# http://clang.llvm.org/) for more accurate parsing at the cost of reduced +# performance. This can be particularly helpful with template rich C++ code for +# which doxygen's built-in parser lacks the necessary type information. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS +# tag is set to YES then doxygen will add the directory of each input to the +# include path. +# The default value is: YES. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_ADD_INC_PATHS = YES + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +# If clang assisted parsing is enabled you can provide the clang parser with the +# path to the directory containing a file called compile_commands.json. This +# file is the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the +# options used when the source files were built. This is equivalent to +# specifying the -p option to a clang tool, such as clang-check. These options +# will then be passed to the parser. Any options specified with CLANG_OPTIONS +# will be added as well. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. + +CLANG_DATABASE_PATH = + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a color-wheel, see +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use gray-scales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = NO + +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via JavaScript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have JavaScript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_MENUS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: +# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To +# create a documentation set, doxygen will generate a Makefile in the HTML +# output directory. Running make will produce the docset in that directory and +# running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# on Windows. In the beginning of 2021 Microsoft took the original page, with +# a.o. the download links, offline the HTML help workshop was already many years +# in maintenance mode). You can download the HTML help workshop from the web +# archives at Installation executable (see: +# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo +# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe). +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the main .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location (absolute path +# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to +# run qhelpgenerator on the generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine tune the look of the index (see "Fine-tuning the output"). As an +# example, the default style sheet generated by doxygen has an example that +# shows how to put an image at the root of the tree instead of the PROJECT_NAME. +# Since the tree basically has the same information as the tab index, you could +# consider setting DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = NO + +# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the +# FULL_SIDEBAR option determines if the side bar is limited to only the treeview +# area (value NO) or if it should extend to the full height of the window (value +# YES). Setting this to YES gives a layout similar to +# https://docs.readthedocs.io with more room for contents, but less room for the +# project logo, title, and description. If either GENERATOR_TREEVIEW or +# DISABLE_INDEX is set to NO, this option has no effect. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FULL_SIDEBAR = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg +# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see +# https://inkscape.org) to generate formulas as SVG images instead of PNGs for +# the HTML output. These images will generally look nicer at scaled resolutions. +# Possible values are: png (the default) and svg (looks nicer but requires the +# pdf2svg or inkscape tool). +# The default value is: png. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FORMULA_FORMAT = png + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANSPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. + +FORMULA_MACROFILE = + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# https://www.mathjax.org) which uses client side JavaScript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# With MATHJAX_VERSION it is possible to specify the MathJax version to be used. +# Note that the different versions of MathJax have different requirements with +# regards to the different settings, so it is possible that also other MathJax +# settings have to be changed when switching between the different MathJax +# versions. +# Possible values are: MathJax_2 and MathJax_3. +# The default value is: MathJax_2. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_VERSION = MathJax_2 + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. For more details about the output format see MathJax +# version 2 (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3 +# (see: +# http://docs.mathjax.org/en/latest/web/components/output.html). +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility. This is the name for Mathjax version 2, for MathJax version 3 +# this will be translated into chtml), NativeMML (i.e. MathML. Only supported +# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This +# is the name for Mathjax version 3, for MathJax version 2 this will be +# translated into HTML-CSS) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from https://www.mathjax.org before deployment. The default value is: +# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2 +# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3 +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# for MathJax version 2 (see +# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions): +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# For example for MathJax version 3 (see +# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): +# MATHJAX_EXTENSIONS = ams +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /