Xlib integration of Vulkan

This commit is contained in:
Jack Andersen 2016-02-21 16:47:45 -10:00
parent f9c681bef9
commit ae487b70f3
5 changed files with 733 additions and 121 deletions

View File

@ -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")

View File

@ -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<const char*> m_instanceLayerNames;
std::vector<const char*> m_instanceExtensionNames;
VkInstance m_instance = VK_NULL_HANDLE;
std::vector<const char*> m_deviceLayerNames;
std::vector<const char*> m_deviceExtensionNames;
std::vector<VkPhysicalDevice> m_gpus;
VkPhysicalDeviceProperties m_gpuProps;
VkPhysicalDeviceMemoryProperties m_memoryProperties;
VkDevice m_dev;
uint32_t m_queueCount;
uint32_t m_graphicsQueueFamilyIndex = UINT32_MAX;
std::vector<VkQueueFamilyProperties> 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<Buffer> m_bufs;
uint32_t m_backBuf = 0;
size_t width, height;
};
std::unordered_map<const boo::IWindow*, Window> m_windows;
std::unordered_map<const boo::IWindow*, std::unique_ptr<Window>> 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

View File

@ -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 <vector>
@ -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<presentModeCount ; ++i)
{
if (presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR)
{
swapchainPresentMode = VK_PRESENT_MODE_MAILBOX_KHR;
break;
}
if ((swapchainPresentMode != VK_PRESENT_MODE_MAILBOX_KHR) &&
(presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR))
{
swapchainPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
}
}
// Determine the number of VkImage's to use in the swap chain (we desire to
// own only 1 image at a time, besides the images being displayed and
// queued for display):
uint32_t desiredNumberOfSwapChainImages = surfCapabilities.minImageCount + 1;
if ((surfCapabilities.maxImageCount > 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 ; i<swapchainImageCount ; ++i)
{
VkImageViewCreateInfo colorImageView = {};
colorImageView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
colorImageView.pNext = nullptr;
colorImageView.format = format;
colorImageView.components.r = VK_COMPONENT_SWIZZLE_R;
colorImageView.components.g = VK_COMPONENT_SWIZZLE_G;
colorImageView.components.b = VK_COMPONENT_SWIZZLE_B;
colorImageView.components.a = VK_COMPONENT_SWIZZLE_A;
colorImageView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
colorImageView.subresourceRange.baseMipLevel = 0;
colorImageView.subresourceRange.levelCount = 1;
colorImageView.subresourceRange.baseArrayLayer = 0;
colorImageView.subresourceRange.layerCount = 1;
colorImageView.viewType = VK_IMAGE_VIEW_TYPE_2D;
colorImageView.flags = 0;
windowCtx.m_bufs[i].m_image = swapchainImages[i];
SetImageLayout(m_loadCmdBuf, windowCtx.m_bufs[i].m_image, VK_IMAGE_ASPECT_COLOR_BIT,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
colorImageView.image = windowCtx.m_bufs[i].m_image;
ThrowIfFailed(vkCreateImageView(m_dev, &colorImageView, nullptr, &windowCtx.m_bufs[i].m_view));
}
ThrowIfFailed(vkEndCommandBuffer(m_loadCmdBuf));
VkFenceCreateInfo fenceInfo;
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
ThrowIfFailed(vkCreateFence(m_dev, &fenceInfo, nullptr, &m_loadFence));
VkPipelineStageFlags pipeStageFlags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
VkSubmitInfo submitInfo[1] = {};
submitInfo[0].pNext = nullptr;
submitInfo[0].sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo[0].waitSemaphoreCount = 0;
submitInfo[0].pWaitSemaphores = nullptr;
submitInfo[0].pWaitDstStageMask = &pipeStageFlags;
submitInfo[0].commandBufferCount = 1;
submitInfo[0].pCommandBuffers = &m_loadCmdBuf;
submitInfo[0].signalSemaphoreCount = 0;
submitInfo[0].pSignalSemaphores = nullptr;
ThrowIfFailed(vkQueueSubmit(m_queue, 1, submitInfo, m_loadFence));
ThrowIfFailed(vkWaitForFences(m_dev, 1, &m_loadFence, VK_TRUE, -1));
/* Reset fence and command buffer */
ThrowIfFailed(vkResetFences(m_dev, 1, &m_loadFence));
ThrowIfFailed(vkResetCommandBuffer(m_loadCmdBuf, 0));
ThrowIfFailed(vkBeginCommandBuffer(m_loadCmdBuf, &cmdBufBeginInfo));
}
struct VulkanData : IGraphicsData
{
VulkanContext* m_ctx;
@ -157,80 +501,6 @@ static const VkBufferUsageFlagBits USE_TABLE[] =
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT
};
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 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;

View File

@ -111,7 +111,7 @@ 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);
GLXContext lastCtx, bool useVulkan);
static XIMStyle ChooseBetterStyle(XIMStyle style1, XIMStyle style2)
{
@ -171,6 +171,9 @@ class ApplicationXlib final : public IApplication
int m_xDefaultScreen = 0;
int m_xcbFd, m_dbusFd, m_maxFd;
/* Vulkan enable */
bool m_useVulkan = true;
void _deletedWindow(IWindow* window)
{
m_windows.erase((Window)window->getPlatformHandle());
@ -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;
}

View File

@ -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 <limits.h>
#include <stdlib.h>
#include <stdio.h>
@ -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);
@ -265,12 +273,26 @@ static void genFrameDefault(Screen* screen, int& xOut, int& yOut, int& wOut, int
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,
GraphicsContextXlibGLX(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),
: 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<VulkanContext::Window>())).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 ; i<ctx->m_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 ; i<ctx->m_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<numFBConfigs ; ++i)
{
GLXFBConfig config = fbConfigs[i];
int visualId, depthSize, colorSize, doubleBuffer;
glXGetFBConfigAttrib(display, config, GLX_VISUAL_ID, &visualId);
glXGetFBConfigAttrib(display, config, GLX_DEPTH_SIZE, &depthSize);
glXGetFBConfigAttrib(display, config, GLX_BUFFER_SIZE, &colorSize);
glXGetFBConfigAttrib(display, config, GLX_DOUBLEBUFFER, &doubleBuffer);
/* Double-buffer only */
if (!doubleBuffer)
continue;
if (m_pf == EPixelFormat::RGBA8 && colorSize >= 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<std::mutex> outerLk(initmt);
m_vsyncThread = std::thread([&]()
{
Display* vsyncDisp;
GLXContext vsyncCtx;
{
std::unique_lock<std::mutex> 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<GraphicsContextXlib> 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<std::mutex> lk(m_gfxCtx.m_vsyncmt);
m_gfxCtx.m_vsynccv.wait(lk);
std::unique_lock<std::mutex> 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;
}