From b32c5f3c4e710cb0750ef402ed14e4b08fed349b Mon Sep 17 00:00:00 2001 From: niki Date: Thu, 6 Jan 2022 01:12:16 +0100 Subject: [PATCH 1/8] 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 2/8] 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 3/8] 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 4/8] 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 5/8] 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 6/8] 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 7/8] 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 8/8] 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); } } }