diff --git a/base/CMakeLists.txt b/base/CMakeLists.txt index 44487c92..c4ccbc01 100644 --- a/base/CMakeLists.txt +++ b/base/CMakeLists.txt @@ -1,5 +1,5 @@ -file(GLOB BASE_SRC "*.cpp" "*.hpp" "../external/imgui/*.cpp") -file(GLOB BASE_HEADERS "*.hpp") +file(GLOB BASE_SRC "*.cpp" "*.hpp" "*.h" "../external/imgui/*.cpp") +file(GLOB BASE_HEADERS "*.hpp" "*.h") set(KTX_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../external/ktx) set(KTX_SOURCES diff --git a/base/VulkanSwapChain.cpp b/base/VulkanSwapChain.cpp new file mode 100644 index 00000000..8af7b3c3 --- /dev/null +++ b/base/VulkanSwapChain.cpp @@ -0,0 +1,589 @@ +/* +* Class wrapping access to the swap chain +* +* A swap chain is a collection of framebuffers used for rendering and presentation to the windowing system +* +* Copyright (C) 2016-2017 by Sascha Willems - www.saschawillems.de +* +* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +*/ + +#include "VulkanSwapChain.h" + +/** @brief Creates the platform specific surface abstraction of the native platform window used for presentation */ +#if defined(VK_USE_PLATFORM_WIN32_KHR) +void VulkanSwapChain::initSurface(void* platformHandle, void* platformWindow) +#elif defined(VK_USE_PLATFORM_ANDROID_KHR) +void VulkanSwapChain::initSurface(ANativeWindow* window) +#elif defined(VK_USE_PLATFORM_WAYLAND_KHR) +void VulkanSwapChain::initSurface(wl_display *display, wl_surface *window) +#elif defined(VK_USE_PLATFORM_XCB_KHR) +void VulkanSwapChain::initSurface(xcb_connection_t* connection, xcb_window_t window) +#elif (defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK)) +void VulkanSwapChain::initSurface(void* view) +#elif defined(_DIRECT2DISPLAY) +void VulkanSwapChain::initSurface(uint32_t width, uint32_t height) +#endif +{ + VkResult err = VK_SUCCESS; + + // Create the os-specific surface +#if defined(VK_USE_PLATFORM_WIN32_KHR) + VkWin32SurfaceCreateInfoKHR surfaceCreateInfo = {}; + surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; + surfaceCreateInfo.hinstance = (HINSTANCE)platformHandle; + surfaceCreateInfo.hwnd = (HWND)platformWindow; + err = vkCreateWin32SurfaceKHR(instance, &surfaceCreateInfo, nullptr, &surface); +#elif defined(VK_USE_PLATFORM_ANDROID_KHR) + VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo = {}; + surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR; + surfaceCreateInfo.window = window; + err = vkCreateAndroidSurfaceKHR(instance, &surfaceCreateInfo, NULL, &surface); +#elif defined(VK_USE_PLATFORM_IOS_MVK) + VkIOSSurfaceCreateInfoMVK surfaceCreateInfo = {}; + surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK; + surfaceCreateInfo.pNext = NULL; + surfaceCreateInfo.flags = 0; + surfaceCreateInfo.pView = view; + err = vkCreateIOSSurfaceMVK(instance, &surfaceCreateInfo, nullptr, &surface); +#elif defined(VK_USE_PLATFORM_MACOS_MVK) + VkMacOSSurfaceCreateInfoMVK surfaceCreateInfo = {}; + surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK; + surfaceCreateInfo.pNext = NULL; + surfaceCreateInfo.flags = 0; + surfaceCreateInfo.pView = view; + err = vkCreateMacOSSurfaceMVK(instance, &surfaceCreateInfo, NULL, &surface); +#elif defined(_DIRECT2DISPLAY) + createDirect2DisplaySurface(width, height); +#elif defined(VK_USE_PLATFORM_WAYLAND_KHR) + VkWaylandSurfaceCreateInfoKHR surfaceCreateInfo = {}; + surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; + surfaceCreateInfo.display = display; + surfaceCreateInfo.surface = window; + err = vkCreateWaylandSurfaceKHR(instance, &surfaceCreateInfo, nullptr, &surface); +#elif defined(VK_USE_PLATFORM_XCB_KHR) + VkXcbSurfaceCreateInfoKHR surfaceCreateInfo = {}; + surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR; + surfaceCreateInfo.connection = connection; + surfaceCreateInfo.window = window; + err = vkCreateXcbSurfaceKHR(instance, &surfaceCreateInfo, nullptr, &surface); +#endif + + if (err != VK_SUCCESS) { + vks::tools::exitFatal("Could not create surface!", err); + } + + // Get available queue family properties + uint32_t queueCount; + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount, NULL); + assert(queueCount >= 1); + + std::vector queueProps(queueCount); + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount, queueProps.data()); + + // Iterate over each queue to learn whether it supports presenting: + // Find a queue with present support + // Will be used to present the swap chain images to the windowing system + std::vector supportsPresent(queueCount); + for (uint32_t i = 0; i < queueCount; i++) + { + fpGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, i, surface, &supportsPresent[i]); + } + + // Search for a graphics and a present queue in the array of queue + // families, try to find one that supports both + uint32_t graphicsQueueNodeIndex = UINT32_MAX; + uint32_t presentQueueNodeIndex = UINT32_MAX; + for (uint32_t i = 0; i < queueCount; i++) + { + if ((queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) + { + if (graphicsQueueNodeIndex == UINT32_MAX) + { + graphicsQueueNodeIndex = i; + } + + if (supportsPresent[i] == VK_TRUE) + { + graphicsQueueNodeIndex = i; + presentQueueNodeIndex = i; + break; + } + } + } + if (presentQueueNodeIndex == UINT32_MAX) + { + // If there's no queue that supports both present and graphics + // try to find a separate present queue + for (uint32_t i = 0; i < queueCount; ++i) + { + if (supportsPresent[i] == VK_TRUE) + { + presentQueueNodeIndex = i; + break; + } + } + } + + // Exit if either a graphics or a presenting queue hasn't been found + if (graphicsQueueNodeIndex == UINT32_MAX || presentQueueNodeIndex == UINT32_MAX) + { + vks::tools::exitFatal("Could not find a graphics and/or presenting queue!", -1); + } + + // todo : Add support for separate graphics and presenting queue + if (graphicsQueueNodeIndex != presentQueueNodeIndex) + { + vks::tools::exitFatal("Separate graphics and presenting queues are not supported yet!", -1); + } + + queueNodeIndex = graphicsQueueNodeIndex; + + // Get list of supported surface formats + uint32_t formatCount; + VK_CHECK_RESULT(fpGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, NULL)); + assert(formatCount > 0); + + std::vector surfaceFormats(formatCount); + VK_CHECK_RESULT(fpGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, surfaceFormats.data())); + + // If the surface format list only includes one entry with VK_FORMAT_UNDEFINED, + // there is no preferred format, so we assume VK_FORMAT_B8G8R8A8_UNORM + if ((formatCount == 1) && (surfaceFormats[0].format == VK_FORMAT_UNDEFINED)) + { + colorFormat = VK_FORMAT_B8G8R8A8_UNORM; + colorSpace = surfaceFormats[0].colorSpace; + } + else + { + // iterate over the list of available surface format and + // check for the presence of VK_FORMAT_B8G8R8A8_UNORM + bool found_B8G8R8A8_UNORM = false; + for (auto&& surfaceFormat : surfaceFormats) + { + if (surfaceFormat.format == VK_FORMAT_B8G8R8A8_UNORM) + { + colorFormat = surfaceFormat.format; + colorSpace = surfaceFormat.colorSpace; + found_B8G8R8A8_UNORM = true; + break; + } + } + + // in case VK_FORMAT_B8G8R8A8_UNORM is not available + // select the first available color format + if (!found_B8G8R8A8_UNORM) + { + colorFormat = surfaceFormats[0].format; + colorSpace = surfaceFormats[0].colorSpace; + } + } + +} + +/** +* Set instance, physical and logical device to use for the swapchain and get all required function pointers +* +* @param instance Vulkan instance to use +* @param physicalDevice Physical device used to query properties and formats relevant to the swapchain +* @param device Logical representation of the device to create the swapchain for +* +*/ +void VulkanSwapChain::connect(VkInstance instance, VkPhysicalDevice physicalDevice, VkDevice device) +{ + this->instance = instance; + this->physicalDevice = physicalDevice; + this->device = device; + fpGetPhysicalDeviceSurfaceSupportKHR = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfaceSupportKHR")); + fpGetPhysicalDeviceSurfaceCapabilitiesKHR = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR")); + fpGetPhysicalDeviceSurfaceFormatsKHR = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfaceFormatsKHR")); + fpGetPhysicalDeviceSurfacePresentModesKHR = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfacePresentModesKHR")); + + fpCreateSwapchainKHR = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCreateSwapchainKHR")); + fpDestroySwapchainKHR = reinterpret_cast(vkGetDeviceProcAddr(device, "vkDestroySwapchainKHR")); + fpGetSwapchainImagesKHR = reinterpret_cast(vkGetDeviceProcAddr(device, "vkGetSwapchainImagesKHR")); + fpAcquireNextImageKHR = reinterpret_cast(vkGetDeviceProcAddr(device, "vkAcquireNextImageKHR")); + fpQueuePresentKHR = reinterpret_cast(vkGetDeviceProcAddr(device, "vkQueuePresentKHR")); +} + +/** +* Create the swapchain and get its images with given width and height +* +* @param width Pointer to the width of the swapchain (may be adjusted to fit the requirements of the swapchain) +* @param height Pointer to the height of the swapchain (may be adjusted to fit the requirements of the swapchain) +* @param vsync (Optional) Can be used to force vsync-ed rendering (by using VK_PRESENT_MODE_FIFO_KHR as presentation mode) +*/ +void VulkanSwapChain::create(uint32_t *width, uint32_t *height, bool vsync) +{ + VkSwapchainKHR oldSwapchain = swapChain; + + // Get physical device surface properties and formats + VkSurfaceCapabilitiesKHR surfCaps; + VK_CHECK_RESULT(fpGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &surfCaps)); + + // Get available present modes + uint32_t presentModeCount; + VK_CHECK_RESULT(fpGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, NULL)); + assert(presentModeCount > 0); + + std::vector presentModes(presentModeCount); + VK_CHECK_RESULT(fpGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, presentModes.data())); + + VkExtent2D swapchainExtent = {}; + // If width (and height) equals the special value 0xFFFFFFFF, the size of the surface will be set by the swapchain + if (surfCaps.currentExtent.width == (uint32_t)-1) + { + // If the surface size is undefined, the size is set to + // the size of the images requested. + swapchainExtent.width = *width; + swapchainExtent.height = *height; + } + else + { + // If the surface size is defined, the swap chain size must match + swapchainExtent = surfCaps.currentExtent; + *width = surfCaps.currentExtent.width; + *height = surfCaps.currentExtent.height; + } + + + // Select a present mode for the swapchain + + // The VK_PRESENT_MODE_FIFO_KHR mode must always be present as per spec + // This mode waits for the vertical blank ("v-sync") + VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR; + + // If v-sync is not requested, try to find a mailbox mode + // It's the lowest latency non-tearing present mode available + if (!vsync) + { + 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 images + uint32_t desiredNumberOfSwapchainImages = surfCaps.minImageCount + 1; + if ((surfCaps.maxImageCount > 0) && (desiredNumberOfSwapchainImages > surfCaps.maxImageCount)) + { + desiredNumberOfSwapchainImages = surfCaps.maxImageCount; + } + + // Find the transformation of the surface + VkSurfaceTransformFlagsKHR preTransform; + if (surfCaps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) + { + // We prefer a non-rotated transform + preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + } + else + { + preTransform = surfCaps.currentTransform; + } + + // Find a supported composite alpha format (not all devices support alpha opaque) + VkCompositeAlphaFlagBitsKHR compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + // Simply select the first composite alpha format available + std::vector compositeAlphaFlags = { + VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, + VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR, + VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR, + VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR, + }; + for (auto& compositeAlphaFlag : compositeAlphaFlags) { + if (surfCaps.supportedCompositeAlpha & compositeAlphaFlag) { + compositeAlpha = compositeAlphaFlag; + break; + }; + } + + VkSwapchainCreateInfoKHR swapchainCI = {}; + swapchainCI.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + swapchainCI.pNext = NULL; + swapchainCI.surface = surface; + swapchainCI.minImageCount = desiredNumberOfSwapchainImages; + swapchainCI.imageFormat = colorFormat; + swapchainCI.imageColorSpace = colorSpace; + swapchainCI.imageExtent = { swapchainExtent.width, swapchainExtent.height }; + swapchainCI.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + swapchainCI.preTransform = (VkSurfaceTransformFlagBitsKHR)preTransform; + swapchainCI.imageArrayLayers = 1; + swapchainCI.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + swapchainCI.queueFamilyIndexCount = 0; + swapchainCI.pQueueFamilyIndices = NULL; + swapchainCI.presentMode = swapchainPresentMode; + swapchainCI.oldSwapchain = oldSwapchain; + // Setting clipped to VK_TRUE allows the implementation to discard rendering outside of the surface area + swapchainCI.clipped = VK_TRUE; + swapchainCI.compositeAlpha = compositeAlpha; + + // Enable transfer source on swap chain images if supported + if (surfCaps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_SRC_BIT) { + swapchainCI.imageUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + } + + // Enable transfer destination on swap chain images if supported + if (surfCaps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT) { + swapchainCI.imageUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; + } + + VK_CHECK_RESULT(fpCreateSwapchainKHR(device, &swapchainCI, nullptr, &swapChain)); + + // If an existing swap chain is re-created, destroy the old swap chain + // This also cleans up all the presentable images + if (oldSwapchain != VK_NULL_HANDLE) + { + for (uint32_t i = 0; i < imageCount; i++) + { + vkDestroyImageView(device, buffers[i].view, nullptr); + } + fpDestroySwapchainKHR(device, oldSwapchain, nullptr); + } + VK_CHECK_RESULT(fpGetSwapchainImagesKHR(device, swapChain, &imageCount, NULL)); + + // Get the swap chain images + images.resize(imageCount); + VK_CHECK_RESULT(fpGetSwapchainImagesKHR(device, swapChain, &imageCount, images.data())); + + // Get the swap chain buffers containing the image and imageview + buffers.resize(imageCount); + for (uint32_t i = 0; i < imageCount; i++) + { + VkImageViewCreateInfo colorAttachmentView = {}; + colorAttachmentView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + colorAttachmentView.pNext = NULL; + colorAttachmentView.format = colorFormat; + colorAttachmentView.components = { + VK_COMPONENT_SWIZZLE_R, + VK_COMPONENT_SWIZZLE_G, + VK_COMPONENT_SWIZZLE_B, + VK_COMPONENT_SWIZZLE_A + }; + colorAttachmentView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + colorAttachmentView.subresourceRange.baseMipLevel = 0; + colorAttachmentView.subresourceRange.levelCount = 1; + colorAttachmentView.subresourceRange.baseArrayLayer = 0; + colorAttachmentView.subresourceRange.layerCount = 1; + colorAttachmentView.viewType = VK_IMAGE_VIEW_TYPE_2D; + colorAttachmentView.flags = 0; + + buffers[i].image = images[i]; + + colorAttachmentView.image = buffers[i].image; + + VK_CHECK_RESULT(vkCreateImageView(device, &colorAttachmentView, nullptr, &buffers[i].view)); + } +} + +/** +* Acquires the next image in the swap chain +* +* @param presentCompleteSemaphore (Optional) Semaphore that is signaled when the image is ready for use +* @param imageIndex Pointer to the image index that will be increased if the next image could be acquired +* +* @note The function will always wait until the next image has been acquired by setting timeout to UINT64_MAX +* +* @return VkResult of the image acquisition +*/ +VkResult VulkanSwapChain::acquireNextImage(VkSemaphore presentCompleteSemaphore, uint32_t *imageIndex) +{ + // By setting timeout to UINT64_MAX we will always wait until the next image has been acquired or an actual error is thrown + // With that we don't have to handle VK_NOT_READY + return fpAcquireNextImageKHR(device, swapChain, UINT64_MAX, presentCompleteSemaphore, (VkFence)nullptr, imageIndex); +} + +/** +* Queue an image for presentation +* +* @param queue Presentation queue for presenting the image +* @param imageIndex Index of the swapchain image to queue for presentation +* @param waitSemaphore (Optional) Semaphore that is waited on before the image is presented (only used if != VK_NULL_HANDLE) +* +* @return VkResult of the queue presentation +*/ +VkResult VulkanSwapChain::queuePresent(VkQueue queue, uint32_t imageIndex, VkSemaphore waitSemaphore) +{ + VkPresentInfoKHR presentInfo = {}; + presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + presentInfo.pNext = NULL; + presentInfo.swapchainCount = 1; + presentInfo.pSwapchains = &swapChain; + presentInfo.pImageIndices = &imageIndex; + // Check if a wait semaphore has been specified to wait for before presenting the image + if (waitSemaphore != VK_NULL_HANDLE) + { + presentInfo.pWaitSemaphores = &waitSemaphore; + presentInfo.waitSemaphoreCount = 1; + } + return fpQueuePresentKHR(queue, &presentInfo); +} + + +/** +* Destroy and free Vulkan resources used for the swapchain +*/ +void VulkanSwapChain::cleanup() +{ + if (swapChain != VK_NULL_HANDLE) + { + for (uint32_t i = 0; i < imageCount; i++) + { + vkDestroyImageView(device, buffers[i].view, nullptr); + } + } + if (surface != VK_NULL_HANDLE) + { + fpDestroySwapchainKHR(device, swapChain, nullptr); + vkDestroySurfaceKHR(instance, surface, nullptr); + } + surface = VK_NULL_HANDLE; + swapChain = VK_NULL_HANDLE; +} + +#if defined(_DIRECT2DISPLAY) +/** +* Create direct to display surface +*/ +void VulkanSwapChain::createDirect2DisplaySurface(uint32_t width, uint32_t height) +{ + uint32_t displayPropertyCount; + + // Get display property + vkGetPhysicalDeviceDisplayPropertiesKHR(physicalDevice, &displayPropertyCount, NULL); + VkDisplayPropertiesKHR* pDisplayProperties = new VkDisplayPropertiesKHR[displayPropertyCount]; + vkGetPhysicalDeviceDisplayPropertiesKHR(physicalDevice, &displayPropertyCount, pDisplayProperties); + + // Get plane property + uint32_t planePropertyCount; + vkGetPhysicalDeviceDisplayPlanePropertiesKHR(physicalDevice, &planePropertyCount, NULL); + VkDisplayPlanePropertiesKHR* pPlaneProperties = new VkDisplayPlanePropertiesKHR[planePropertyCount]; + vkGetPhysicalDeviceDisplayPlanePropertiesKHR(physicalDevice, &planePropertyCount, pPlaneProperties); + + VkDisplayKHR display = VK_NULL_HANDLE; + VkDisplayModeKHR displayMode; + VkDisplayModePropertiesKHR* pModeProperties; + bool foundMode = false; + + for(uint32_t i = 0; i < displayPropertyCount;++i) + { + display = pDisplayProperties[i].display; + uint32_t modeCount; + vkGetDisplayModePropertiesKHR(physicalDevice, display, &modeCount, NULL); + pModeProperties = new VkDisplayModePropertiesKHR[modeCount]; + vkGetDisplayModePropertiesKHR(physicalDevice, display, &modeCount, pModeProperties); + + for (uint32_t j = 0; j < modeCount; ++j) + { + const VkDisplayModePropertiesKHR* mode = &pModeProperties[j]; + + if (mode->parameters.visibleRegion.width == width && mode->parameters.visibleRegion.height == height) + { + displayMode = mode->displayMode; + foundMode = true; + break; + } + } + if (foundMode) + { + break; + } + delete [] pModeProperties; + } + + if(!foundMode) + { + vks::tools::exitFatal("Can't find a display and a display mode!", -1); + return; + } + + // Search for a best plane we can use + uint32_t bestPlaneIndex = UINT32_MAX; + VkDisplayKHR* pDisplays = NULL; + for(uint32_t i = 0; i < planePropertyCount; i++) + { + uint32_t planeIndex=i; + uint32_t displayCount; + vkGetDisplayPlaneSupportedDisplaysKHR(physicalDevice, planeIndex, &displayCount, NULL); + if (pDisplays) + { + delete [] pDisplays; + } + pDisplays = new VkDisplayKHR[displayCount]; + vkGetDisplayPlaneSupportedDisplaysKHR(physicalDevice, planeIndex, &displayCount, pDisplays); + + // Find a display that matches the current plane + bestPlaneIndex = UINT32_MAX; + for(uint32_t j = 0; j < displayCount; j++) + { + if(display == pDisplays[j]) + { + bestPlaneIndex = i; + break; + } + } + if(bestPlaneIndex != UINT32_MAX) + { + break; + } + } + + if(bestPlaneIndex == UINT32_MAX) + { + vks::tools::exitFatal("Can't find a plane for displaying!", -1); + return; + } + + VkDisplayPlaneCapabilitiesKHR planeCap; + vkGetDisplayPlaneCapabilitiesKHR(physicalDevice, displayMode, bestPlaneIndex, &planeCap); + VkDisplayPlaneAlphaFlagBitsKHR alphaMode = (VkDisplayPlaneAlphaFlagBitsKHR)0; + + if (planeCap.supportedAlpha & VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_PREMULTIPLIED_BIT_KHR) + { + alphaMode = VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_PREMULTIPLIED_BIT_KHR; + } + else if (planeCap.supportedAlpha & VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_BIT_KHR) + { + alphaMode = VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_BIT_KHR; + } + else if (planeCap.supportedAlpha & VK_DISPLAY_PLANE_ALPHA_GLOBAL_BIT_KHR) + { + alphaMode = VK_DISPLAY_PLANE_ALPHA_GLOBAL_BIT_KHR; + } + else if (planeCap.supportedAlpha & VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR) + { + alphaMode = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR; + } + + VkDisplaySurfaceCreateInfoKHR surfaceInfo{}; + surfaceInfo.sType = VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR; + surfaceInfo.pNext = NULL; + surfaceInfo.flags = 0; + surfaceInfo.displayMode = displayMode; + surfaceInfo.planeIndex = bestPlaneIndex; + surfaceInfo.planeStackIndex = pPlaneProperties[bestPlaneIndex].currentStackIndex; + surfaceInfo.transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + surfaceInfo.globalAlpha = 1.0; + surfaceInfo.alphaMode = alphaMode; + surfaceInfo.imageExtent.width = width; + surfaceInfo.imageExtent.height = height; + + VkResult result = vkCreateDisplayPlaneSurfaceKHR(instance, &surfaceInfo, NULL, &surface); + if (result !=VK_SUCCESS) { + vks::tools::exitFatal("Failed to create surface!", result); + } + + delete[] pDisplays; + delete[] pModeProperties; + delete[] pDisplayProperties; + delete[] pPlaneProperties; +} +#endif \ No newline at end of file diff --git a/base/VulkanSwapChain.h b/base/VulkanSwapChain.h new file mode 100644 index 00000000..bd2622ad --- /dev/null +++ b/base/VulkanSwapChain.h @@ -0,0 +1,76 @@ +/* +* Class wrapping access to the swap chain +* +* A swap chain is a collection of framebuffers used for rendering and presentation to the windowing system +* +* Copyright (C) 2016-2017 by Sascha Willems - www.saschawillems.de +* +* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +*/ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include "VulkanTools.h" + +#ifdef __ANDROID__ +#include "VulkanAndroid.h" +#endif + +typedef struct _SwapChainBuffers { + VkImage image; + VkImageView view; +} SwapChainBuffer; + +class VulkanSwapChain +{ +private: + VkInstance instance; + VkDevice device; + VkPhysicalDevice physicalDevice; + VkSurfaceKHR surface; + // Function pointers + PFN_vkGetPhysicalDeviceSurfaceSupportKHR fpGetPhysicalDeviceSurfaceSupportKHR; + PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR fpGetPhysicalDeviceSurfaceCapabilitiesKHR; + PFN_vkGetPhysicalDeviceSurfaceFormatsKHR fpGetPhysicalDeviceSurfaceFormatsKHR; + PFN_vkGetPhysicalDeviceSurfacePresentModesKHR fpGetPhysicalDeviceSurfacePresentModesKHR; + PFN_vkCreateSwapchainKHR fpCreateSwapchainKHR; + PFN_vkDestroySwapchainKHR fpDestroySwapchainKHR; + PFN_vkGetSwapchainImagesKHR fpGetSwapchainImagesKHR; + PFN_vkAcquireNextImageKHR fpAcquireNextImageKHR; + PFN_vkQueuePresentKHR fpQueuePresentKHR; +public: + VkFormat colorFormat; + VkColorSpaceKHR colorSpace; + VkSwapchainKHR swapChain = VK_NULL_HANDLE; + uint32_t imageCount; + std::vector images; + std::vector buffers; + uint32_t queueNodeIndex = UINT32_MAX; + +#if defined(VK_USE_PLATFORM_WIN32_KHR) + void initSurface(void* platformHandle, void* platformWindow); +#elif defined(VK_USE_PLATFORM_ANDROID_KHR) + void initSurface(ANativeWindow* window); +#elif defined(VK_USE_PLATFORM_WAYLAND_KHR) + void initSurface(wl_display* display, wl_surface* window); +#elif defined(VK_USE_PLATFORM_XCB_KHR) + void initSurface(xcb_connection_t* connection, xcb_window_t window); +#elif (defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK)) + void initSurface(void* view); +#elif defined(_DIRECT2DISPLAY) + void initSurface(uint32_t width, uint32_t height); + void createDirect2DisplaySurface(uint32_t width, uint32_t height); +#endif + void connect(VkInstance instance, VkPhysicalDevice physicalDevice, VkDevice device); + void create(uint32_t* width, uint32_t* height, bool vsync = false); + VkResult acquireNextImage(VkSemaphore presentCompleteSemaphore, uint32_t* imageIndex); + VkResult queuePresent(VkQueue queue, uint32_t imageIndex, VkSemaphore waitSemaphore = VK_NULL_HANDLE); + void cleanup(); +}; diff --git a/base/VulkanSwapChain.hpp b/base/VulkanSwapChain.hpp deleted file mode 100644 index fc36e6aa..00000000 --- a/base/VulkanSwapChain.hpp +++ /dev/null @@ -1,655 +0,0 @@ -/* -* Class wrapping access to the swap chain -* -* A swap chain is a collection of framebuffers used for rendering and presentation to the windowing system -* -* Copyright (C) 2016-2017 by Sascha Willems - www.saschawillems.de -* -* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) -*/ - -#pragma once - -#include -#include -#include -#include -#include - -#include -#include "VulkanTools.h" - -#ifdef __ANDROID__ -#include "VulkanAndroid.h" -#endif - -// Macro to get a procedure address based on a vulkan instance -#define GET_INSTANCE_PROC_ADDR(inst, entrypoint) \ -{ \ - fp##entrypoint = reinterpret_cast(vkGetInstanceProcAddr(inst, "vk"#entrypoint)); \ - if (fp##entrypoint == NULL) \ - { \ - exit(1); \ - } \ -} - -// Macro to get a procedure address based on a vulkan device -#define GET_DEVICE_PROC_ADDR(dev, entrypoint) \ -{ \ - fp##entrypoint = reinterpret_cast(vkGetDeviceProcAddr(dev, "vk"#entrypoint)); \ - if (fp##entrypoint == NULL) \ - { \ - exit(1); \ - } \ -} - -typedef struct _SwapChainBuffers { - VkImage image; - VkImageView view; -} SwapChainBuffer; - -class VulkanSwapChain -{ -private: - VkInstance instance; - VkDevice device; - VkPhysicalDevice physicalDevice; - VkSurfaceKHR surface; - // Function pointers - PFN_vkGetPhysicalDeviceSurfaceSupportKHR fpGetPhysicalDeviceSurfaceSupportKHR; - PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR fpGetPhysicalDeviceSurfaceCapabilitiesKHR; - PFN_vkGetPhysicalDeviceSurfaceFormatsKHR fpGetPhysicalDeviceSurfaceFormatsKHR; - PFN_vkGetPhysicalDeviceSurfacePresentModesKHR fpGetPhysicalDeviceSurfacePresentModesKHR; - PFN_vkCreateSwapchainKHR fpCreateSwapchainKHR; - PFN_vkDestroySwapchainKHR fpDestroySwapchainKHR; - PFN_vkGetSwapchainImagesKHR fpGetSwapchainImagesKHR; - PFN_vkAcquireNextImageKHR fpAcquireNextImageKHR; - PFN_vkQueuePresentKHR fpQueuePresentKHR; -public: - VkFormat colorFormat; - VkColorSpaceKHR colorSpace; - /** @brief Handle to the current swap chain, required for recreation */ - VkSwapchainKHR swapChain = VK_NULL_HANDLE; - uint32_t imageCount; - std::vector images; - std::vector buffers; - /** @brief Queue family index of the detected graphics and presenting device queue */ - uint32_t queueNodeIndex = UINT32_MAX; - - /** @brief Creates the platform specific surface abstraction of the native platform window used for presentation */ -#if defined(VK_USE_PLATFORM_WIN32_KHR) - void initSurface(void* platformHandle, void* platformWindow) -#elif defined(VK_USE_PLATFORM_ANDROID_KHR) - void initSurface(ANativeWindow* window) -#elif defined(VK_USE_PLATFORM_WAYLAND_KHR) - void initSurface(wl_display *display, wl_surface *window) -#elif defined(VK_USE_PLATFORM_XCB_KHR) - void initSurface(xcb_connection_t* connection, xcb_window_t window) -#elif (defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK)) - void initSurface(void* view) -#elif defined(_DIRECT2DISPLAY) - void initSurface(uint32_t width, uint32_t height) -#endif - { - VkResult err = VK_SUCCESS; - - // Create the os-specific surface -#if defined(VK_USE_PLATFORM_WIN32_KHR) - VkWin32SurfaceCreateInfoKHR surfaceCreateInfo = {}; - surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; - surfaceCreateInfo.hinstance = (HINSTANCE)platformHandle; - surfaceCreateInfo.hwnd = (HWND)platformWindow; - err = vkCreateWin32SurfaceKHR(instance, &surfaceCreateInfo, nullptr, &surface); -#elif defined(VK_USE_PLATFORM_ANDROID_KHR) - VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo = {}; - surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR; - surfaceCreateInfo.window = window; - err = vkCreateAndroidSurfaceKHR(instance, &surfaceCreateInfo, NULL, &surface); -#elif defined(VK_USE_PLATFORM_IOS_MVK) - VkIOSSurfaceCreateInfoMVK surfaceCreateInfo = {}; - surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK; - surfaceCreateInfo.pNext = NULL; - surfaceCreateInfo.flags = 0; - surfaceCreateInfo.pView = view; - err = vkCreateIOSSurfaceMVK(instance, &surfaceCreateInfo, nullptr, &surface); -#elif defined(VK_USE_PLATFORM_MACOS_MVK) - VkMacOSSurfaceCreateInfoMVK surfaceCreateInfo = {}; - surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK; - surfaceCreateInfo.pNext = NULL; - surfaceCreateInfo.flags = 0; - surfaceCreateInfo.pView = view; - err = vkCreateMacOSSurfaceMVK(instance, &surfaceCreateInfo, NULL, &surface); -#elif defined(_DIRECT2DISPLAY) - createDirect2DisplaySurface(width, height); -#elif defined(VK_USE_PLATFORM_WAYLAND_KHR) - VkWaylandSurfaceCreateInfoKHR surfaceCreateInfo = {}; - surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; - surfaceCreateInfo.display = display; - surfaceCreateInfo.surface = window; - err = vkCreateWaylandSurfaceKHR(instance, &surfaceCreateInfo, nullptr, &surface); -#elif defined(VK_USE_PLATFORM_XCB_KHR) - VkXcbSurfaceCreateInfoKHR surfaceCreateInfo = {}; - surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR; - surfaceCreateInfo.connection = connection; - surfaceCreateInfo.window = window; - err = vkCreateXcbSurfaceKHR(instance, &surfaceCreateInfo, nullptr, &surface); -#endif - - if (err != VK_SUCCESS) { - vks::tools::exitFatal("Could not create surface!", err); - } - - // Get available queue family properties - uint32_t queueCount; - vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount, NULL); - assert(queueCount >= 1); - - std::vector queueProps(queueCount); - vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount, queueProps.data()); - - // Iterate over each queue to learn whether it supports presenting: - // Find a queue with present support - // Will be used to present the swap chain images to the windowing system - std::vector supportsPresent(queueCount); - for (uint32_t i = 0; i < queueCount; i++) - { - fpGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, i, surface, &supportsPresent[i]); - } - - // Search for a graphics and a present queue in the array of queue - // families, try to find one that supports both - uint32_t graphicsQueueNodeIndex = UINT32_MAX; - uint32_t presentQueueNodeIndex = UINT32_MAX; - for (uint32_t i = 0; i < queueCount; i++) - { - if ((queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) - { - if (graphicsQueueNodeIndex == UINT32_MAX) - { - graphicsQueueNodeIndex = i; - } - - if (supportsPresent[i] == VK_TRUE) - { - graphicsQueueNodeIndex = i; - presentQueueNodeIndex = i; - break; - } - } - } - if (presentQueueNodeIndex == UINT32_MAX) - { - // If there's no queue that supports both present and graphics - // try to find a separate present queue - for (uint32_t i = 0; i < queueCount; ++i) - { - if (supportsPresent[i] == VK_TRUE) - { - presentQueueNodeIndex = i; - break; - } - } - } - - // Exit if either a graphics or a presenting queue hasn't been found - if (graphicsQueueNodeIndex == UINT32_MAX || presentQueueNodeIndex == UINT32_MAX) - { - vks::tools::exitFatal("Could not find a graphics and/or presenting queue!", -1); - } - - // todo : Add support for separate graphics and presenting queue - if (graphicsQueueNodeIndex != presentQueueNodeIndex) - { - vks::tools::exitFatal("Separate graphics and presenting queues are not supported yet!", -1); - } - - queueNodeIndex = graphicsQueueNodeIndex; - - // Get list of supported surface formats - uint32_t formatCount; - VK_CHECK_RESULT(fpGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, NULL)); - assert(formatCount > 0); - - std::vector surfaceFormats(formatCount); - VK_CHECK_RESULT(fpGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, surfaceFormats.data())); - - // If the surface format list only includes one entry with VK_FORMAT_UNDEFINED, - // there is no preferred format, so we assume VK_FORMAT_B8G8R8A8_UNORM - if ((formatCount == 1) && (surfaceFormats[0].format == VK_FORMAT_UNDEFINED)) - { - colorFormat = VK_FORMAT_B8G8R8A8_UNORM; - colorSpace = surfaceFormats[0].colorSpace; - } - else - { - // iterate over the list of available surface format and - // check for the presence of VK_FORMAT_B8G8R8A8_UNORM - bool found_B8G8R8A8_UNORM = false; - for (auto&& surfaceFormat : surfaceFormats) - { - if (surfaceFormat.format == VK_FORMAT_B8G8R8A8_UNORM) - { - colorFormat = surfaceFormat.format; - colorSpace = surfaceFormat.colorSpace; - found_B8G8R8A8_UNORM = true; - break; - } - } - - // in case VK_FORMAT_B8G8R8A8_UNORM is not available - // select the first available color format - if (!found_B8G8R8A8_UNORM) - { - colorFormat = surfaceFormats[0].format; - colorSpace = surfaceFormats[0].colorSpace; - } - } - - } - - /** - * Set instance, physical and logical device to use for the swapchain and get all required function pointers - * - * @param instance Vulkan instance to use - * @param physicalDevice Physical device used to query properties and formats relevant to the swapchain - * @param device Logical representation of the device to create the swapchain for - * - */ - void connect(VkInstance instance, VkPhysicalDevice physicalDevice, VkDevice device) - { - this->instance = instance; - this->physicalDevice = physicalDevice; - this->device = device; - GET_INSTANCE_PROC_ADDR(instance, GetPhysicalDeviceSurfaceSupportKHR); - GET_INSTANCE_PROC_ADDR(instance, GetPhysicalDeviceSurfaceCapabilitiesKHR); - GET_INSTANCE_PROC_ADDR(instance, GetPhysicalDeviceSurfaceFormatsKHR); - GET_INSTANCE_PROC_ADDR(instance, GetPhysicalDeviceSurfacePresentModesKHR); - GET_DEVICE_PROC_ADDR(device, CreateSwapchainKHR); - GET_DEVICE_PROC_ADDR(device, DestroySwapchainKHR); - GET_DEVICE_PROC_ADDR(device, GetSwapchainImagesKHR); - GET_DEVICE_PROC_ADDR(device, AcquireNextImageKHR); - GET_DEVICE_PROC_ADDR(device, QueuePresentKHR); - } - - /** - * Create the swapchain and get its images with given width and height - * - * @param width Pointer to the width of the swapchain (may be adjusted to fit the requirements of the swapchain) - * @param height Pointer to the height of the swapchain (may be adjusted to fit the requirements of the swapchain) - * @param vsync (Optional) Can be used to force vsync-ed rendering (by using VK_PRESENT_MODE_FIFO_KHR as presentation mode) - */ - void create(uint32_t *width, uint32_t *height, bool vsync = false) - { - VkSwapchainKHR oldSwapchain = swapChain; - - // Get physical device surface properties and formats - VkSurfaceCapabilitiesKHR surfCaps; - VK_CHECK_RESULT(fpGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &surfCaps)); - - // Get available present modes - uint32_t presentModeCount; - VK_CHECK_RESULT(fpGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, NULL)); - assert(presentModeCount > 0); - - std::vector presentModes(presentModeCount); - VK_CHECK_RESULT(fpGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, presentModes.data())); - - VkExtent2D swapchainExtent = {}; - // If width (and height) equals the special value 0xFFFFFFFF, the size of the surface will be set by the swapchain - if (surfCaps.currentExtent.width == (uint32_t)-1) - { - // If the surface size is undefined, the size is set to - // the size of the images requested. - swapchainExtent.width = *width; - swapchainExtent.height = *height; - } - else - { - // If the surface size is defined, the swap chain size must match - swapchainExtent = surfCaps.currentExtent; - *width = surfCaps.currentExtent.width; - *height = surfCaps.currentExtent.height; - } - - - // Select a present mode for the swapchain - - // The VK_PRESENT_MODE_FIFO_KHR mode must always be present as per spec - // This mode waits for the vertical blank ("v-sync") - VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR; - - // If v-sync is not requested, try to find a mailbox mode - // It's the lowest latency non-tearing present mode available - if (!vsync) - { - 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 images - uint32_t desiredNumberOfSwapchainImages = surfCaps.minImageCount + 1; - if ((surfCaps.maxImageCount > 0) && (desiredNumberOfSwapchainImages > surfCaps.maxImageCount)) - { - desiredNumberOfSwapchainImages = surfCaps.maxImageCount; - } - - // Find the transformation of the surface - VkSurfaceTransformFlagsKHR preTransform; - if (surfCaps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) - { - // We prefer a non-rotated transform - preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; - } - else - { - preTransform = surfCaps.currentTransform; - } - - // Find a supported composite alpha format (not all devices support alpha opaque) - VkCompositeAlphaFlagBitsKHR compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; - // Simply select the first composite alpha format available - std::vector compositeAlphaFlags = { - VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, - VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR, - VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR, - VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR, - }; - for (auto& compositeAlphaFlag : compositeAlphaFlags) { - if (surfCaps.supportedCompositeAlpha & compositeAlphaFlag) { - compositeAlpha = compositeAlphaFlag; - break; - }; - } - - VkSwapchainCreateInfoKHR swapchainCI = {}; - swapchainCI.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; - swapchainCI.pNext = NULL; - swapchainCI.surface = surface; - swapchainCI.minImageCount = desiredNumberOfSwapchainImages; - swapchainCI.imageFormat = colorFormat; - swapchainCI.imageColorSpace = colorSpace; - swapchainCI.imageExtent = { swapchainExtent.width, swapchainExtent.height }; - swapchainCI.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - swapchainCI.preTransform = (VkSurfaceTransformFlagBitsKHR)preTransform; - swapchainCI.imageArrayLayers = 1; - swapchainCI.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; - swapchainCI.queueFamilyIndexCount = 0; - swapchainCI.pQueueFamilyIndices = NULL; - swapchainCI.presentMode = swapchainPresentMode; - swapchainCI.oldSwapchain = oldSwapchain; - // Setting clipped to VK_TRUE allows the implementation to discard rendering outside of the surface area - swapchainCI.clipped = VK_TRUE; - swapchainCI.compositeAlpha = compositeAlpha; - - // Enable transfer source on swap chain images if supported - if (surfCaps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_SRC_BIT) { - swapchainCI.imageUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; - } - - // Enable transfer destination on swap chain images if supported - if (surfCaps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT) { - swapchainCI.imageUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; - } - - VK_CHECK_RESULT(fpCreateSwapchainKHR(device, &swapchainCI, nullptr, &swapChain)); - - // If an existing swap chain is re-created, destroy the old swap chain - // This also cleans up all the presentable images - if (oldSwapchain != VK_NULL_HANDLE) - { - for (uint32_t i = 0; i < imageCount; i++) - { - vkDestroyImageView(device, buffers[i].view, nullptr); - } - fpDestroySwapchainKHR(device, oldSwapchain, nullptr); - } - VK_CHECK_RESULT(fpGetSwapchainImagesKHR(device, swapChain, &imageCount, NULL)); - - // Get the swap chain images - images.resize(imageCount); - VK_CHECK_RESULT(fpGetSwapchainImagesKHR(device, swapChain, &imageCount, images.data())); - - // Get the swap chain buffers containing the image and imageview - buffers.resize(imageCount); - for (uint32_t i = 0; i < imageCount; i++) - { - VkImageViewCreateInfo colorAttachmentView = {}; - colorAttachmentView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - colorAttachmentView.pNext = NULL; - colorAttachmentView.format = colorFormat; - colorAttachmentView.components = { - VK_COMPONENT_SWIZZLE_R, - VK_COMPONENT_SWIZZLE_G, - VK_COMPONENT_SWIZZLE_B, - VK_COMPONENT_SWIZZLE_A - }; - colorAttachmentView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - colorAttachmentView.subresourceRange.baseMipLevel = 0; - colorAttachmentView.subresourceRange.levelCount = 1; - colorAttachmentView.subresourceRange.baseArrayLayer = 0; - colorAttachmentView.subresourceRange.layerCount = 1; - colorAttachmentView.viewType = VK_IMAGE_VIEW_TYPE_2D; - colorAttachmentView.flags = 0; - - buffers[i].image = images[i]; - - colorAttachmentView.image = buffers[i].image; - - VK_CHECK_RESULT(vkCreateImageView(device, &colorAttachmentView, nullptr, &buffers[i].view)); - } - } - - /** - * Acquires the next image in the swap chain - * - * @param presentCompleteSemaphore (Optional) Semaphore that is signaled when the image is ready for use - * @param imageIndex Pointer to the image index that will be increased if the next image could be acquired - * - * @note The function will always wait until the next image has been acquired by setting timeout to UINT64_MAX - * - * @return VkResult of the image acquisition - */ - VkResult acquireNextImage(VkSemaphore presentCompleteSemaphore, uint32_t *imageIndex) - { - // By setting timeout to UINT64_MAX we will always wait until the next image has been acquired or an actual error is thrown - // With that we don't have to handle VK_NOT_READY - return fpAcquireNextImageKHR(device, swapChain, UINT64_MAX, presentCompleteSemaphore, (VkFence)nullptr, imageIndex); - } - - /** - * Queue an image for presentation - * - * @param queue Presentation queue for presenting the image - * @param imageIndex Index of the swapchain image to queue for presentation - * @param waitSemaphore (Optional) Semaphore that is waited on before the image is presented (only used if != VK_NULL_HANDLE) - * - * @return VkResult of the queue presentation - */ - VkResult queuePresent(VkQueue queue, uint32_t imageIndex, VkSemaphore waitSemaphore = VK_NULL_HANDLE) - { - VkPresentInfoKHR presentInfo = {}; - presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; - presentInfo.pNext = NULL; - presentInfo.swapchainCount = 1; - presentInfo.pSwapchains = &swapChain; - presentInfo.pImageIndices = &imageIndex; - // Check if a wait semaphore has been specified to wait for before presenting the image - if (waitSemaphore != VK_NULL_HANDLE) - { - presentInfo.pWaitSemaphores = &waitSemaphore; - presentInfo.waitSemaphoreCount = 1; - } - return fpQueuePresentKHR(queue, &presentInfo); - } - - - /** - * Destroy and free Vulkan resources used for the swapchain - */ - void cleanup() - { - if (swapChain != VK_NULL_HANDLE) - { - for (uint32_t i = 0; i < imageCount; i++) - { - vkDestroyImageView(device, buffers[i].view, nullptr); - } - } - if (surface != VK_NULL_HANDLE) - { - fpDestroySwapchainKHR(device, swapChain, nullptr); - vkDestroySurfaceKHR(instance, surface, nullptr); - } - surface = VK_NULL_HANDLE; - swapChain = VK_NULL_HANDLE; - } - -#if defined(_DIRECT2DISPLAY) - /** - * Create direct to display surface - */ - void createDirect2DisplaySurface(uint32_t width, uint32_t height) - { - uint32_t displayPropertyCount; - - // Get display property - vkGetPhysicalDeviceDisplayPropertiesKHR(physicalDevice, &displayPropertyCount, NULL); - VkDisplayPropertiesKHR* pDisplayProperties = new VkDisplayPropertiesKHR[displayPropertyCount]; - vkGetPhysicalDeviceDisplayPropertiesKHR(physicalDevice, &displayPropertyCount, pDisplayProperties); - - // Get plane property - uint32_t planePropertyCount; - vkGetPhysicalDeviceDisplayPlanePropertiesKHR(physicalDevice, &planePropertyCount, NULL); - VkDisplayPlanePropertiesKHR* pPlaneProperties = new VkDisplayPlanePropertiesKHR[planePropertyCount]; - vkGetPhysicalDeviceDisplayPlanePropertiesKHR(physicalDevice, &planePropertyCount, pPlaneProperties); - - VkDisplayKHR display = VK_NULL_HANDLE; - VkDisplayModeKHR displayMode; - VkDisplayModePropertiesKHR* pModeProperties; - bool foundMode = false; - - for(uint32_t i = 0; i < displayPropertyCount;++i) - { - display = pDisplayProperties[i].display; - uint32_t modeCount; - vkGetDisplayModePropertiesKHR(physicalDevice, display, &modeCount, NULL); - pModeProperties = new VkDisplayModePropertiesKHR[modeCount]; - vkGetDisplayModePropertiesKHR(physicalDevice, display, &modeCount, pModeProperties); - - for (uint32_t j = 0; j < modeCount; ++j) - { - const VkDisplayModePropertiesKHR* mode = &pModeProperties[j]; - - if (mode->parameters.visibleRegion.width == width && mode->parameters.visibleRegion.height == height) - { - displayMode = mode->displayMode; - foundMode = true; - break; - } - } - if (foundMode) - { - break; - } - delete [] pModeProperties; - } - - if(!foundMode) - { - vks::tools::exitFatal("Can't find a display and a display mode!", -1); - return; - } - - // Search for a best plane we can use - uint32_t bestPlaneIndex = UINT32_MAX; - VkDisplayKHR* pDisplays = NULL; - for(uint32_t i = 0; i < planePropertyCount; i++) - { - uint32_t planeIndex=i; - uint32_t displayCount; - vkGetDisplayPlaneSupportedDisplaysKHR(physicalDevice, planeIndex, &displayCount, NULL); - if (pDisplays) - { - delete [] pDisplays; - } - pDisplays = new VkDisplayKHR[displayCount]; - vkGetDisplayPlaneSupportedDisplaysKHR(physicalDevice, planeIndex, &displayCount, pDisplays); - - // Find a display that matches the current plane - bestPlaneIndex = UINT32_MAX; - for(uint32_t j = 0; j < displayCount; j++) - { - if(display == pDisplays[j]) - { - bestPlaneIndex = i; - break; - } - } - if(bestPlaneIndex != UINT32_MAX) - { - break; - } - } - - if(bestPlaneIndex == UINT32_MAX) - { - vks::tools::exitFatal("Can't find a plane for displaying!", -1); - return; - } - - VkDisplayPlaneCapabilitiesKHR planeCap; - vkGetDisplayPlaneCapabilitiesKHR(physicalDevice, displayMode, bestPlaneIndex, &planeCap); - VkDisplayPlaneAlphaFlagBitsKHR alphaMode = (VkDisplayPlaneAlphaFlagBitsKHR)0; - - if (planeCap.supportedAlpha & VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_PREMULTIPLIED_BIT_KHR) - { - alphaMode = VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_PREMULTIPLIED_BIT_KHR; - } - else if (planeCap.supportedAlpha & VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_BIT_KHR) - { - alphaMode = VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_BIT_KHR; - } - else if (planeCap.supportedAlpha & VK_DISPLAY_PLANE_ALPHA_GLOBAL_BIT_KHR) - { - alphaMode = VK_DISPLAY_PLANE_ALPHA_GLOBAL_BIT_KHR; - } - else if (planeCap.supportedAlpha & VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR) - { - alphaMode = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR; - } - - VkDisplaySurfaceCreateInfoKHR surfaceInfo{}; - surfaceInfo.sType = VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR; - surfaceInfo.pNext = NULL; - surfaceInfo.flags = 0; - surfaceInfo.displayMode = displayMode; - surfaceInfo.planeIndex = bestPlaneIndex; - surfaceInfo.planeStackIndex = pPlaneProperties[bestPlaneIndex].currentStackIndex; - surfaceInfo.transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; - surfaceInfo.globalAlpha = 1.0; - surfaceInfo.alphaMode = alphaMode; - surfaceInfo.imageExtent.width = width; - surfaceInfo.imageExtent.height = height; - - VkResult result = vkCreateDisplayPlaneSurfaceKHR(instance, &surfaceInfo, NULL, &surface); - if (result !=VK_SUCCESS) { - vks::tools::exitFatal("Failed to create surface!", result); - } - - delete[] pDisplays; - delete[] pModeProperties; - delete[] pDisplayProperties; - delete[] pPlaneProperties; - } -#endif -}; diff --git a/base/vulkanexamplebase.h b/base/vulkanexamplebase.h index 94840abd..66119c66 100644 --- a/base/vulkanexamplebase.h +++ b/base/vulkanexamplebase.h @@ -47,10 +47,10 @@ #include "VulkanTools.h" #include "VulkanDebug.h" #include "VulkanUIOverlay.h" +#include "VulkanSwapChain.h" #include "VulkanInitializers.hpp" #include "VulkanDevice.hpp" -#include "VulkanSwapChain.hpp" #include "camera.hpp" #include "benchmark.hpp"