diff --git a/CMakeLists.txt b/CMakeLists.txt index cc033a0..eeee59a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -125,12 +125,13 @@ else(NOT GEKKO) include_directories(${DBUS_INCLUDE_DIR} ${DBUS_ARCH_INCLUDE_DIR}) list(APPEND _BOO_SYS_LIBS X11 Xi GL ${DBUS_LIBRARY} pthread) + unset(VULKAN_LIBRARY CACHE) find_library(VULKAN_LIBRARY vulkan) - if(NOT VULKAN_LIBRARY-NOTFOUND) + if(NOT (VULKAN_LIBRARY STREQUAL VULKAN_LIBRARY-NOTFOUND)) message(STATUS "Vulkan loader found; enabling Vulkan support") + list(APPEND _BOO_SYS_DEFINES -DBOO_HAS_VULKAN=1) list(APPEND _BOO_SYS_LIBS ${VULKAN_LIBRARY}) - list(APPEND PLAT_SRCS - lib/graphicsdev/Vulkan.cpp) + list(APPEND PLAT_SRCS lib/graphicsdev/Vulkan.cpp) endif() if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") diff --git a/include/boo/graphicsdev/Vulkan.hpp b/include/boo/graphicsdev/Vulkan.hpp index f98f7cc..cfd7f98 100644 --- a/include/boo/graphicsdev/Vulkan.hpp +++ b/include/boo/graphicsdev/Vulkan.hpp @@ -1,5 +1,6 @@ #ifndef GDEV_VULKAN_HPP #define GDEV_VULKAN_HPP +#if BOO_HAS_VULKAN #include "IGraphicsDataFactory.hpp" #include "IGraphicsCommandQueue.hpp" @@ -15,11 +16,18 @@ namespace boo struct VulkanContext { - VkInstance m_instance; - VkPhysicalDevice m_adapter; - VkPhysicalDeviceProperties m_devProps; + std::vector m_instanceLayerNames; + std::vector m_instanceExtensionNames; + VkInstance m_instance = VK_NULL_HANDLE; + std::vector m_deviceLayerNames; + std::vector m_deviceExtensionNames; + std::vector m_gpus; + VkPhysicalDeviceProperties m_gpuProps; VkPhysicalDeviceMemoryProperties m_memoryProperties; VkDevice m_dev; + uint32_t m_queueCount; + uint32_t m_graphicsQueueFamilyIndex = UINT32_MAX; + std::vector m_queueProps; VkQueue m_queue; VkDescriptorSetLayout m_descSetLayout; VkPipelineLayout m_layout; @@ -30,18 +38,28 @@ struct VulkanContext VkSampler m_linearSampler; struct Window { - VkSwapchainKHR m_swapChain; + VkSwapchainKHR m_swapChain = VK_NULL_HANDLE; struct Buffer { VkImage m_image; VkImageView m_view; + void destroy(VkDevice dev) + { + vkDestroyImageView(dev, m_view, nullptr); + vkDestroyImage(dev, m_image, nullptr); + } }; std::vector m_bufs; uint32_t m_backBuf = 0; size_t width, height; }; - std::unordered_map m_windows; + std::unordered_map> m_windows; + + void initVulkan(const char* appName); + void initDevice(); + void initSwapChain(Window& windowCtx, VkSurfaceKHR surface, VkFormat format); }; +extern VulkanContext g_VulkanContext; class VulkanDataFactory : public IGraphicsDataFactory { @@ -106,4 +124,5 @@ public: } +#endif #endif // GDEV_VULKAN_HPP diff --git a/lib/graphicsdev/Vulkan.cpp b/lib/graphicsdev/Vulkan.cpp index a6e3082..1bc9ea3 100644 --- a/lib/graphicsdev/Vulkan.cpp +++ b/lib/graphicsdev/Vulkan.cpp @@ -1,3 +1,9 @@ +#ifdef _WIN32 +#define VK_USE_PLATFORM_WIN32_KHR +#else +#define VK_USE_PLATFORM_XLIB_KHR +#endif + #include "boo/graphicsdev/Vulkan.hpp" #include "boo/IGraphicsContext.hpp" #include @@ -8,6 +14,7 @@ #undef min #undef max +#undef None #define MAX_UNIFORM_COUNT 8 #define MAX_TEXTURE_COUNT 8 @@ -114,6 +121,7 @@ static const TBuiltInResource DefaultBuiltInResource = namespace boo { static LogVisor::LogModule Log("boo::Vulkan"); +VulkanContext g_VulkanContext; static inline void ThrowIfFailed(VkResult res) { @@ -127,6 +135,342 @@ static inline void ThrowIfFalse(bool res) Log.report(LogVisor::FatalError, "operation failed\n", res); } +static bool MemoryTypeFromProperties(VulkanContext* ctx, uint32_t typeBits, + VkFlags requirementsMask, + uint32_t *typeIndex) +{ + /* Search memtypes to find first index with those properties */ + for (uint32_t i = 0; i < 32; i++) + { + if ((typeBits & 1) == 1) + { + /* Type is available, does it match user properties? */ + if ((ctx->m_memoryProperties.memoryTypes[i].propertyFlags & + requirementsMask) == requirementsMask) { + *typeIndex = i; + return true; + } + } + typeBits >>= 1; + } + /* No memory types matched, return failure */ + return false; +} + +static void SetImageLayout(VkCommandBuffer cmd, VkImage image, + VkImageAspectFlags aspectMask, + VkImageLayout old_image_layout, + VkImageLayout new_image_layout) +{ + VkImageMemoryBarrier imageMemoryBarrier = {}; + imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + imageMemoryBarrier.pNext = NULL; + imageMemoryBarrier.srcAccessMask = 0; + imageMemoryBarrier.dstAccessMask = 0; + imageMemoryBarrier.oldLayout = old_image_layout; + imageMemoryBarrier.newLayout = new_image_layout; + imageMemoryBarrier.image = image; + imageMemoryBarrier.subresourceRange.aspectMask = aspectMask; + imageMemoryBarrier.subresourceRange.baseMipLevel = 0; + imageMemoryBarrier.subresourceRange.levelCount = 1; + imageMemoryBarrier.subresourceRange.layerCount = 1; + + if (old_image_layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { + imageMemoryBarrier.srcAccessMask = + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + } + + if (new_image_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + /* Make sure anything that was copying from this image has completed */ + imageMemoryBarrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; + } + + if (new_image_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { + /* Make sure any Copy or CPU writes to image are flushed */ + imageMemoryBarrier.srcAccessMask = + VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT; + imageMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + } + + if (new_image_layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { + imageMemoryBarrier.dstAccessMask = + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; + } + + if (new_image_layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { + imageMemoryBarrier.dstAccessMask = + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT; + } + + VkPipelineStageFlags src_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + VkPipelineStageFlags dest_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + + vkCmdPipelineBarrier(cmd, src_stages, dest_stages, 0, 0, NULL, 0, NULL, + 1, &imageMemoryBarrier); +} + +void VulkanContext::initVulkan(const char* appName) +{ + /* need platform surface extensions */ + m_instanceExtensionNames.push_back(VK_KHR_SURFACE_EXTENSION_NAME); +#ifdef _WIN32 + m_instanceExtensionNames.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); +#else + m_instanceExtensionNames.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME); +#endif + + /* need swapchain device extension */ + m_deviceExtensionNames.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); + + /* create the instance */ + VkApplicationInfo appInfo = {}; + appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + appInfo.pNext = nullptr; + appInfo.pApplicationName = appName; + appInfo.applicationVersion = 1; + appInfo.pEngineName = "libBoo"; + appInfo.engineVersion = 1; + appInfo.apiVersion = VK_API_VERSION; + + VkInstanceCreateInfo instInfo = {}; + instInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + instInfo.pNext = nullptr; + instInfo.flags = 0; + instInfo.pApplicationInfo = &appInfo; + instInfo.enabledLayerCount = m_instanceLayerNames.size(); + instInfo.ppEnabledLayerNames = m_instanceLayerNames.size() + ? m_instanceLayerNames.data() + : nullptr; + instInfo.enabledExtensionCount = m_instanceExtensionNames.size(); + instInfo.ppEnabledExtensionNames = m_instanceExtensionNames.data(); + + ThrowIfFailed(vkCreateInstance(&instInfo, nullptr, &m_instance)); + + uint32_t gpuCount = 1; + ThrowIfFailed(vkEnumeratePhysicalDevices(m_instance, &gpuCount, nullptr)); + assert(gpuCount); + m_gpus.resize(gpuCount); + + ThrowIfFailed(vkEnumeratePhysicalDevices(m_instance, &gpuCount, m_gpus.data())); + assert(gpuCount >= 1); + + vkGetPhysicalDeviceQueueFamilyProperties(m_gpus[0], &m_queueCount, nullptr); + assert(m_queueCount >= 1); + + m_queueProps.resize(m_queueCount); + vkGetPhysicalDeviceQueueFamilyProperties(m_gpus[0], &m_queueCount, m_queueProps.data()); + assert(m_queueCount >= 1); + + /* This is as good a place as any to do this */ + vkGetPhysicalDeviceMemoryProperties(m_gpus[0], &m_memoryProperties); + vkGetPhysicalDeviceProperties(m_gpus[0], &m_gpuProps); +} + +void VulkanContext::initDevice() +{ + if (m_graphicsQueueFamilyIndex == UINT32_MAX) + Log.report(LogVisor::FatalError, + "VulkanContext::m_graphicsQueueFamilyIndex hasn't been initialized"); + + /* create the device */ + VkDeviceQueueCreateInfo queueInfo = {}; + float queuePriorities[1] = {0.0}; + queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queueInfo.pNext = nullptr; + queueInfo.queueCount = 1; + queueInfo.pQueuePriorities = queuePriorities; + queueInfo.queueFamilyIndex = m_graphicsQueueFamilyIndex; + + VkDeviceCreateInfo deviceInfo = {}; + deviceInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + deviceInfo.pNext = nullptr; + deviceInfo.queueCreateInfoCount = 1; + deviceInfo.pQueueCreateInfos = &queueInfo; + deviceInfo.enabledLayerCount = m_deviceLayerNames.size(); + deviceInfo.ppEnabledLayerNames = + deviceInfo.enabledLayerCount ? m_deviceLayerNames.data() : nullptr; + deviceInfo.enabledExtensionCount = m_deviceExtensionNames.size(); + deviceInfo.ppEnabledExtensionNames = + deviceInfo.enabledExtensionCount ? m_deviceExtensionNames.data() : nullptr; + deviceInfo.pEnabledFeatures = nullptr; + + ThrowIfFailed(vkCreateDevice(m_gpus[0], &deviceInfo, nullptr, &m_dev)); +} + +void VulkanContext::initSwapChain(VulkanContext::Window& windowCtx, VkSurfaceKHR surface, VkFormat format) +{ + VkSurfaceCapabilitiesKHR surfCapabilities; + ThrowIfFailed(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(m_gpus[0], surface, &surfCapabilities)); + + uint32_t presentModeCount; + ThrowIfFailed(vkGetPhysicalDeviceSurfacePresentModesKHR(m_gpus[0], surface, &presentModeCount, nullptr)); + VkPresentModeKHR* presentModes = (VkPresentModeKHR*)malloc(presentModeCount * sizeof(VkPresentModeKHR)); + + ThrowIfFailed(vkGetPhysicalDeviceSurfacePresentModesKHR(m_gpus[0], surface, &presentModeCount, presentModes)); + + VkExtent2D swapChainExtent; + // width and height are either both -1, or both not -1. + if (surfCapabilities.currentExtent.width == (uint32_t)-1) + { + // If the surface size is undefined, the size is set to + // the size of the images requested. + swapChainExtent.width = 50; + swapChainExtent.height = 50; + } + else + { + // If the surface size is defined, the swap chain size must match + swapChainExtent = surfCapabilities.currentExtent; + } + + // If mailbox mode is available, use it, as is the lowest-latency non- + // tearing mode. If not, try IMMEDIATE which will usually be available, + // and is fastest (though it tears). If not, fall back to FIFO which is + // always available. + VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR; + for (size_t i=0 ; i 0) && + (desiredNumberOfSwapChainImages > surfCapabilities.maxImageCount)) + { + // Application must settle for fewer images than desired: + desiredNumberOfSwapChainImages = surfCapabilities.maxImageCount; + } + + VkSurfaceTransformFlagBitsKHR preTransform; + if (surfCapabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) + preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + else + preTransform = surfCapabilities.currentTransform; + + VkSwapchainCreateInfoKHR swapChainInfo = {}; + swapChainInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + swapChainInfo.pNext = nullptr; + swapChainInfo.surface = surface; + swapChainInfo.minImageCount = desiredNumberOfSwapChainImages; + swapChainInfo.imageFormat = format; + swapChainInfo.imageExtent.width = swapChainExtent.width; + swapChainInfo.imageExtent.height = swapChainExtent.height; + swapChainInfo.preTransform = preTransform; + swapChainInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + swapChainInfo.imageArrayLayers = 1; + swapChainInfo.presentMode = swapchainPresentMode; + swapChainInfo.oldSwapchain = nullptr; + swapChainInfo.clipped = true; + swapChainInfo.imageColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; + swapChainInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + swapChainInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + swapChainInfo.queueFamilyIndexCount = 0; + swapChainInfo.pQueueFamilyIndices = nullptr; + + ThrowIfFailed(vkCreateSwapchainKHR(m_dev, &swapChainInfo, nullptr, &windowCtx.m_swapChain)); + + uint32_t swapchainImageCount; + ThrowIfFailed(vkGetSwapchainImagesKHR(m_dev, windowCtx.m_swapChain, &swapchainImageCount, nullptr)); + + VkImage* swapchainImages = (VkImage*)malloc(swapchainImageCount * sizeof(VkImage)); + ThrowIfFailed(vkGetSwapchainImagesKHR(m_dev, windowCtx.m_swapChain, &swapchainImageCount, swapchainImages)); + + windowCtx.m_bufs.resize(swapchainImageCount); + + // Going to need a command buffer to send the memory barriers in + // set_image_layout but we couldn't have created one before we knew + // what our graphics_queue_family_index is, but now that we have it, + // create the command buffer + + VkCommandPoolCreateInfo cmdPoolInfo = {}; + cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + cmdPoolInfo.pNext = nullptr; + cmdPoolInfo.queueFamilyIndex = m_graphicsQueueFamilyIndex; + cmdPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + ThrowIfFailed(vkCreateCommandPool(m_dev, &cmdPoolInfo, nullptr, &m_loadPool)); + + VkCommandBufferAllocateInfo cmd = {}; + cmd.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + cmd.pNext = nullptr; + cmd.commandPool = m_loadPool; + cmd.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + cmd.commandBufferCount = 1; + ThrowIfFailed(vkAllocateCommandBuffers(m_dev, &cmd, &m_loadCmdBuf)); + + VkCommandBufferBeginInfo cmdBufBeginInfo = {}; + cmdBufBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + cmdBufBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + ThrowIfFailed(vkBeginCommandBuffer(m_loadCmdBuf, &cmdBufBeginInfo)); + + vkGetDeviceQueue(m_dev, m_graphicsQueueFamilyIndex, 0, &m_queue); + + for (uint32_t i=0 ; im_memoryProperties.memoryTypes[i].propertyFlags & - requirementsMask) == requirementsMask) { - *typeIndex = i; - return true; - } - } - typeBits >>= 1; - } - /* No memory types matched, return failure */ - return false; -} - -static void SetImageLayout(VkCommandBuffer cmd, VkImage image, - VkImageAspectFlags aspectMask, - VkImageLayout old_image_layout, - VkImageLayout new_image_layout) -{ - VkImageMemoryBarrier image_memory_barrier = {}; - image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - image_memory_barrier.pNext = NULL; - image_memory_barrier.srcAccessMask = 0; - image_memory_barrier.dstAccessMask = 0; - image_memory_barrier.oldLayout = old_image_layout; - image_memory_barrier.newLayout = new_image_layout; - image_memory_barrier.image = image; - image_memory_barrier.subresourceRange.aspectMask = aspectMask; - image_memory_barrier.subresourceRange.baseMipLevel = 0; - image_memory_barrier.subresourceRange.levelCount = 1; - image_memory_barrier.subresourceRange.layerCount = 1; - - if (old_image_layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { - image_memory_barrier.srcAccessMask = - VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - } - - if (new_image_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - /* Make sure anything that was copying from this image has completed */ - image_memory_barrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; - } - - if (new_image_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { - /* Make sure any Copy or CPU writes to image are flushed */ - image_memory_barrier.srcAccessMask = - VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT; - image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - } - - if (new_image_layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { - image_memory_barrier.dstAccessMask = - VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; - } - - if (new_image_layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { - image_memory_barrier.dstAccessMask = - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT; - } - - VkPipelineStageFlags src_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; - VkPipelineStageFlags dest_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; - - vkCmdPipelineBarrier(cmd, src_stages, dest_stages, 0, 0, NULL, 0, NULL, - 1, &image_memory_barrier); -} - class VulkanGraphicsBufferS : public IGraphicsBufferS { friend class VulkanDataFactory; @@ -267,11 +537,11 @@ public: VkDeviceSize sizeForGPU(VulkanContext* ctx, uint32_t& memTypeBits, VkDeviceSize offset) { - if (m_uniform && ctx->m_devProps.limits.minUniformBufferOffsetAlignment) + if (m_uniform && ctx->m_gpuProps.limits.minUniformBufferOffsetAlignment) { offset = (offset + - ctx->m_devProps.limits.minUniformBufferOffsetAlignment - 1) & - ~(ctx->m_devProps.limits.minUniformBufferOffsetAlignment - 1); + ctx->m_gpuProps.limits.minUniformBufferOffsetAlignment - 1) & + ~(ctx->m_gpuProps.limits.minUniformBufferOffsetAlignment - 1); } VkMemoryRequirements memReqs; @@ -334,11 +604,11 @@ public: { for (int i=0 ; i<2 ; ++i) { - if (m_uniform && ctx->m_devProps.limits.minUniformBufferOffsetAlignment) + if (m_uniform && ctx->m_gpuProps.limits.minUniformBufferOffsetAlignment) { offset = (offset + - ctx->m_devProps.limits.minUniformBufferOffsetAlignment - 1) & - ~(ctx->m_devProps.limits.minUniformBufferOffsetAlignment - 1); + ctx->m_gpuProps.limits.minUniformBufferOffsetAlignment - 1) & + ~(ctx->m_gpuProps.limits.minUniformBufferOffsetAlignment - 1); } VkMemoryRequirements memReqs; diff --git a/lib/x11/ApplicationXlib.hpp b/lib/x11/ApplicationXlib.hpp index a649154..6c5c823 100644 --- a/lib/x11/ApplicationXlib.hpp +++ b/lib/x11/ApplicationXlib.hpp @@ -110,8 +110,8 @@ static Window GetWindowOfEvent(XEvent* event, bool& windowEvent) } IWindow* _WindowXlibNew(const std::string& title, - Display* display, int defaultScreen, XIM xIM, XIMStyle bestInputStyle, XFontSet fontset, - GLXContext lastCtx); + Display* display, int defaultScreen, XIM xIM, XIMStyle bestInputStyle, XFontSet fontset, + GLXContext lastCtx, bool useVulkan); static XIMStyle ChooseBetterStyle(XIMStyle style1, XIMStyle style2) { @@ -170,6 +170,9 @@ class ApplicationXlib final : public IApplication XIMStyle m_bestStyle = 0; int m_xDefaultScreen = 0; int m_xcbFd, m_dbusFd, m_maxFd; + + /* Vulkan enable */ + bool m_useVulkan = true; void _deletedWindow(IWindow* window) { @@ -455,7 +458,8 @@ public: IWindow* newWindow(const std::string& title) { - IWindow* newWindow = _WindowXlibNew(title, m_xDisp, m_xDefaultScreen, m_xIM, m_bestStyle, m_fontset, m_lastGlxCtx); + IWindow* newWindow = _WindowXlibNew(title, m_xDisp, m_xDefaultScreen, m_xIM, + m_bestStyle, m_fontset, m_lastGlxCtx, m_useVulkan); m_windows[(Window)newWindow->getPlatformHandle()] = newWindow; return newWindow; } diff --git a/lib/x11/WindowXlib.cpp b/lib/x11/WindowXlib.cpp index 00639a9..f47d627 100644 --- a/lib/x11/WindowXlib.cpp +++ b/lib/x11/WindowXlib.cpp @@ -3,6 +3,9 @@ #include "boo/IApplication.hpp" #include "boo/graphicsdev/GL.hpp" +#define VK_USE_PLATFORM_XLIB_KHR +#include "boo/graphicsdev/Vulkan.hpp" + #include #include #include @@ -103,8 +106,13 @@ const size_t MAINICON_NETWM_SZ __attribute__ ((weak)) = 0; namespace boo { -static LogVisor::LogModule Log("boo::WindowXCB"); +static LogVisor::LogModule Log("boo::WindowXlib"); IGraphicsCommandQueue* _NewGLCommandQueue(IGraphicsContext* parent); +#if BOO_HAS_VULKAN +IGraphicsCommandQueue* _NewVulkanCommandQueue(VulkanContext* ctx, + VulkanContext::Window* windowCtx, + IGraphicsContext* parent); +#endif void _XlibUpdateLastGlxCtx(GLXContext lastGlxCtx); void GLXExtensionCheck(); void GLXEnableVSync(Display* disp, GLXWindow drawable); @@ -264,13 +272,27 @@ static void genFrameDefault(Screen* screen, int& xOut, int& yOut, int& wOut, int wOut = width; hOut = height; } - -struct GraphicsContextGLX : IGraphicsContext + +struct GraphicsContextXlib : IGraphicsContext { EGraphicsAPI m_api; EPixelFormat m_pf; IWindow* m_parentWindow; - Display* m_xDisp = nullptr; + Display* m_xDisp; + + std::mutex m_vsyncmt; + std::condition_variable m_vsynccv; + + GraphicsContextXlib(EGraphicsAPI api, EPixelFormat pf, IWindow* parentWindow, Display* disp) + : m_api(api), + m_pf(pf), + m_parentWindow(parentWindow), + m_xDisp(disp) {} + virtual void destroy()=0; +}; + +struct GraphicsContextXlibGLX : GraphicsContextXlib +{ GLXContext m_lastCtx = 0; GLXFBConfig m_fbconfig = 0; @@ -289,13 +311,10 @@ struct GraphicsContextGLX : IGraphicsContext public: IWindowCallback* m_callback; - GraphicsContextGLX(EGraphicsAPI api, IWindow* parentWindow, - Display* display, int defaultScreen, - GLXContext lastCtx, uint32_t& visualIdOut) - : m_api(api), - m_pf(EPixelFormat::RGBA8_Z24), - m_parentWindow(parentWindow), - m_xDisp(display), + GraphicsContextXlibGLX(EGraphicsAPI api, IWindow* parentWindow, + Display* display, int defaultScreen, + GLXContext lastCtx, uint32_t& visualIdOut) + : GraphicsContextXlib(api, EPixelFormat::RGBA8, parentWindow, display), m_lastCtx(lastCtx) { m_dataFactory = new class GLDataFactory(this); @@ -383,7 +402,7 @@ public: } } - ~GraphicsContextGLX() {destroy();} + ~GraphicsContextXlibGLX() {destroy();} void _setCallback(IWindowCallback* cb) { @@ -407,9 +426,6 @@ public: m_pf = pf; } - std::mutex m_vsyncmt; - std::condition_variable m_vsynccv; - void initializeContext() { if (!glXCreateContextAttribsARB) @@ -570,6 +586,302 @@ public: }; +#if BOO_HAS_VULKAN +struct GraphicsContextXlibVulkan : GraphicsContextXlib +{ + VulkanContext* m_ctx; + VkSurfaceKHR m_surface; + VkFormat m_format; + + GLXFBConfig m_fbconfig = 0; + int m_visualid = 0; + + IGraphicsCommandQueue* m_commandQueue = nullptr; + IGraphicsDataFactory* m_dataFactory = nullptr; + + std::thread m_vsyncThread; + bool m_vsyncRunning; + + static void ThrowIfFailed(VkResult res) + { + if (res != VK_SUCCESS) + Log.report(LogVisor::FatalError, "%d\n", res); + } + +public: + IWindowCallback* m_callback; + + GraphicsContextXlibVulkan(IWindow* parentWindow, + Display* display, int defaultScreen, + VulkanContext* ctx, uint32_t& visualIdOut) + : GraphicsContextXlib(EGraphicsAPI::Vulkan, EPixelFormat::RGBA8, parentWindow, display), + m_ctx(ctx) + { + if (ctx->m_instance == VK_NULL_HANDLE) + ctx->initVulkan(APP->getProcessName().c_str()); + + VulkanContext::Window& m_windowCtx = + *ctx->m_windows.emplace(std::make_pair(parentWindow, + std::make_unique())).first->second; + m_dataFactory = new class VulkanDataFactory(this, ctx); + + VkXlibSurfaceCreateInfoKHR surfaceInfo = {}; + surfaceInfo.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; + surfaceInfo.dpy = display; + surfaceInfo.window = parentWindow->getPlatformHandle(); + ThrowIfFailed(vkCreateXlibSurfaceKHR(ctx->m_instance, &surfaceInfo, nullptr, &m_surface)); + + /* Iterate over each queue to learn whether it supports presenting */ + VkBool32 *supportsPresent = (VkBool32*)malloc(ctx->m_queueCount * sizeof(VkBool32)); + for (uint32_t i=0 ; im_queueCount ; ++i) + vkGetPhysicalDeviceSurfaceSupportKHR(ctx->m_gpus[0], i, m_surface, &supportsPresent[i]); + + /* Search for a graphics queue and a present queue in the array of queue + * families, try to find one that supports both */ + if (m_ctx->m_graphicsQueueFamilyIndex == UINT32_MAX) + { + /* First window, init device */ + for (uint32_t i=0 ; im_queueCount; ++i) + { + if ((ctx->m_queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) + { + if (supportsPresent[i] == VK_TRUE) + { + m_ctx->m_graphicsQueueFamilyIndex = i; + break; + } + } + } + + /* Generate error if could not find a queue that supports both a graphics + * and present */ + if (m_ctx->m_graphicsQueueFamilyIndex == UINT32_MAX) + Log.report(LogVisor::FatalError, + "Could not find a queue that supports both graphics and present"); + + m_ctx->initDevice(); + } + else + { + /* Subsequent window, verify present */ + if (supportsPresent[m_ctx->m_graphicsQueueFamilyIndex] == VK_FALSE) + Log.report(LogVisor::FatalError, "subsequent surface doesn't support present"); + } + free(supportsPresent); + + + /* Get the list of VkFormats that are supported */ + uint32_t formatCount; + ThrowIfFailed(vkGetPhysicalDeviceSurfaceFormatsKHR(ctx->m_gpus[0], m_surface, &formatCount, nullptr)); + VkSurfaceFormatKHR* surfFormats = (VkSurfaceFormatKHR*)malloc(formatCount * sizeof(VkSurfaceFormatKHR)); + ThrowIfFailed(vkGetPhysicalDeviceSurfaceFormatsKHR(ctx->m_gpus[0], m_surface, &formatCount, surfFormats)); + + /* If the format list includes just one entry of VK_FORMAT_UNDEFINED, + * the surface has no preferred format. Otherwise, at least one + * supported format will be returned. */ + if (formatCount >= 1) + { + if (surfFormats[0].format == VK_FORMAT_UNDEFINED) + m_format = VK_FORMAT_B8G8R8A8_UNORM; + else + m_format = surfFormats[0].format; + } + else + Log.report(LogVisor::FatalError, "no surface formats available for Vulkan swapchain"); + + + /* Query framebuffer configurations */ + GLXFBConfig* fbConfigs = nullptr; + int numFBConfigs = 0; + fbConfigs = glXGetFBConfigs(display, defaultScreen, &numFBConfigs); + if (!fbConfigs || numFBConfigs == 0) + { + Log.report(LogVisor::FatalError, "glXGetFBConfigs failed"); + return; + } + + for (int i=0 ; i= 32) + { + m_fbconfig = config; + m_visualid = visualId; + break; + } + else if (m_pf == EPixelFormat::RGBA8_Z24 && colorSize >= 32 && depthSize >= 24) + { + m_fbconfig = config; + m_visualid = visualId; + break; + } + else if (m_pf == EPixelFormat::RGBAF32 && colorSize >= 128) + { + m_fbconfig = config; + m_visualid = visualId; + break; + } + else if (m_pf == EPixelFormat::RGBAF32_Z24 && colorSize >= 128 && depthSize >= 24) + { + m_fbconfig = config; + m_visualid = visualId; + break; + } + } + XFree(fbConfigs); + + if (!m_fbconfig) + { + Log.report(LogVisor::FatalError, "unable to find suitable pixel format"); + return; + } + + if (!vkGetPhysicalDeviceXlibPresentationSupportKHR(ctx->m_gpus[0], ctx->m_graphicsQueueFamilyIndex, display, m_visualid)) + { + Log.report(LogVisor::FatalError, "unable to find vulkan-compatible pixel format"); + return; + } + + visualIdOut = m_visualid; + } + + void destroy() + { + VulkanContext::Window& m_windowCtx = *m_ctx->m_windows[m_parentWindow]; + vkDestroySwapchainKHR(m_ctx->m_dev, m_windowCtx.m_swapChain, nullptr); + vkDestroySurfaceKHR(m_ctx->m_instance, m_surface, nullptr); + for (VulkanContext::Window::Buffer& buf : m_windowCtx.m_bufs) + buf.destroy(m_ctx->m_dev); + m_windowCtx.m_bufs.clear(); + + if (m_vsyncRunning) + { + m_vsyncRunning = false; + m_vsyncThread.join(); + } + } + + ~GraphicsContextXlibVulkan() {destroy();} + + void _setCallback(IWindowCallback* cb) + { + m_callback = cb; + } + + EGraphicsAPI getAPI() const + { + return m_api; + } + + EPixelFormat getPixelFormat() const + { + return m_pf; + } + + void setPixelFormat(EPixelFormat pf) + { + if (pf > EPixelFormat::RGBAF32_Z24) + return; + m_pf = pf; + } + + void initializeContext() + { + if (!glXWaitVideoSyncSGI) + { + glXWaitVideoSyncSGI = (glXWaitVideoSyncSGIProc) + glXGetProcAddressARB((const GLubyte*)"glXWaitVideoSyncSGI"); + if (!glXWaitVideoSyncSGI) + Log.report(LogVisor::FatalError, "unable to resolve glXWaitVideoSyncSGI"); + } + + /* Spawn vsync thread */ + m_vsyncRunning = true; + std::mutex initmt; + std::condition_variable initcv; + std::unique_lock outerLk(initmt); + m_vsyncThread = std::thread([&]() + { + Display* vsyncDisp; + GLXContext vsyncCtx; + { + std::unique_lock innerLk(initmt); + + vsyncDisp = XOpenDisplay(0); + if (!vsyncDisp) + Log.report(LogVisor::FatalError, "unable to open new vsync display"); + XLockDisplay(vsyncDisp); + + static int attributeList[] = { GLX_RGBA, GLX_DOUBLEBUFFER, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, 0 }; + XVisualInfo *vi = glXChooseVisual(vsyncDisp, DefaultScreen(vsyncDisp), attributeList); + + vsyncCtx = glXCreateContext(vsyncDisp, vi, nullptr, True); + if (!vsyncCtx) + Log.report(LogVisor::FatalError, "unable to make new vsync GLX context"); + + if (!glXMakeCurrent(vsyncDisp, DefaultRootWindow(vsyncDisp), vsyncCtx)) + Log.report(LogVisor::FatalError, "unable to make vsync context current"); + } + initcv.notify_one(); + + while (m_vsyncRunning) + { + unsigned int sync; + int err = glXWaitVideoSyncSGI(1, 0, &sync); + if (err) + Log.report(LogVisor::FatalError, "wait err"); + m_vsynccv.notify_one(); + } + + glXMakeCurrent(vsyncDisp, 0, nullptr); + glXDestroyContext(vsyncDisp, vsyncCtx); + XUnlockDisplay(vsyncDisp); + XCloseDisplay(vsyncDisp); + }); + initcv.wait(outerLk); + + m_commandQueue = _NewVulkanCommandQueue(m_ctx, m_ctx->m_windows[m_parentWindow].get(), this); + } + + void makeCurrent() {} + + void postInit() {} + + IGraphicsCommandQueue* getCommandQueue() + { + return m_commandQueue; + } + + IGraphicsDataFactory* getDataFactory() + { + return m_dataFactory; + } + + IGraphicsDataFactory* getMainContextDataFactory() + { + return getDataFactory(); + } + + IGraphicsDataFactory* getLoadContextDataFactory() + { + return getDataFactory(); + } + + void present() {} + +}; +#endif + class WindowXlib : public IWindow { Display* m_xDisp; @@ -578,7 +890,7 @@ class WindowXlib : public IWindow Window m_windowId; XIMStyle m_bestStyle; XIC m_xIC = nullptr; - GraphicsContextGLX m_gfxCtx; + std::unique_ptr m_gfxCtx; uint32_t m_visualId; /* Last known input device id (0xffff if not yet set) */ @@ -624,16 +936,22 @@ class WindowXlib : public IWindow public: WindowXlib(const std::string& title, Display* display, int defaultScreen, XIM xIM, XIMStyle bestInputStyle, XFontSet fontset, - GLXContext lastCtx) + GLXContext lastCtx, bool useVulkan) : m_xDisp(display), m_callback(nullptr), - m_gfxCtx(IGraphicsContext::EGraphicsAPI::OpenGL3_3, - this, display, defaultScreen, - lastCtx, m_visualId), m_bestStyle(bestInputStyle) { if (!S_ATOMS) S_ATOMS = new XlibAtoms(display); +#if BOO_HAS_VULKAN + if (useVulkan) + m_gfxCtx.reset(new GraphicsContextXlibVulkan(this, display, defaultScreen, + &g_VulkanContext, m_visualId)); + else +#endif + m_gfxCtx.reset(new GraphicsContextXlibGLX(IGraphicsContext::EGraphicsAPI::OpenGL3_3, + this, display, defaultScreen, lastCtx, m_visualId)); + /* Default screen */ Screen* screen = ScreenOfDisplay(display, defaultScreen); m_pixelFactor = screen->width / (float)screen->mwidth / REF_DPMM; @@ -726,13 +1044,13 @@ public: setCursor(EMouseCursor::Pointer); XFlush(m_xDisp); - m_gfxCtx.initializeContext(); + m_gfxCtx->initializeContext(); } ~WindowXlib() { XLockDisplay(m_xDisp); - m_gfxCtx.destroy(); + m_gfxCtx->destroy(); XUnmapWindow(m_xDisp, m_windowId); XDestroyWindow(m_xDisp, m_windowId); XFreeColormap(m_xDisp, m_colormapId); @@ -1109,8 +1427,8 @@ public: void waitForRetrace() { - std::unique_lock lk(m_gfxCtx.m_vsyncmt); - m_gfxCtx.m_vsynccv.wait(lk); + std::unique_lock lk(m_gfxCtx->m_vsyncmt); + m_gfxCtx->m_vsynccv.wait(lk); } uintptr_t getPlatformHandle() const @@ -1594,22 +1912,22 @@ public: IGraphicsCommandQueue* getCommandQueue() { - return m_gfxCtx.getCommandQueue(); + return m_gfxCtx->getCommandQueue(); } IGraphicsDataFactory* getDataFactory() { - return m_gfxCtx.getDataFactory(); + return m_gfxCtx->getDataFactory(); } IGraphicsDataFactory* getMainContextDataFactory() { - return m_gfxCtx.getMainContextDataFactory(); + return m_gfxCtx->getMainContextDataFactory(); } IGraphicsDataFactory* getLoadContextDataFactory() { - return m_gfxCtx.getLoadContextDataFactory(); + return m_gfxCtx->getLoadContextDataFactory(); } bool _isWindowMapped() @@ -1624,10 +1942,10 @@ public: IWindow* _WindowXlibNew(const std::string& title, Display* display, int defaultScreen, XIM xIM, XIMStyle bestInputStyle, XFontSet fontset, - GLXContext lastCtx) + GLXContext lastCtx, bool useVulkan) { XLockDisplay(display); - IWindow* ret = new WindowXlib(title, display, defaultScreen, xIM, bestInputStyle, fontset, lastCtx); + IWindow* ret = new WindowXlib(title, display, defaultScreen, xIM, bestInputStyle, fontset, lastCtx, useVulkan); XUnlockDisplay(display); return ret; }