You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
452 lines
14 KiB
452 lines
14 KiB
/*
|
|
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
|
|
#include "swapchain_vk.hpp"
|
|
#include "error_vk.hpp"
|
|
|
|
#include <assert.h>
|
|
|
|
#include <nvvk/debug_util_vk.hpp>
|
|
namespace nvvk {
|
|
bool SwapChain::init(VkDevice device,
|
|
VkPhysicalDevice physicalDevice,
|
|
VkQueue queue,
|
|
uint32_t queueFamilyIndex,
|
|
VkSurfaceKHR surface,
|
|
VkFormat format,
|
|
VkImageUsageFlags imageUsage)
|
|
{
|
|
assert(!m_device);
|
|
m_device = device;
|
|
m_physicalDevice = physicalDevice;
|
|
m_swapchain = VK_NULL_HANDLE;
|
|
m_queue = queue;
|
|
m_queueFamilyIndex = queueFamilyIndex;
|
|
m_changeID = 0;
|
|
m_currentSemaphore = 0;
|
|
m_surface = surface;
|
|
m_imageUsage = imageUsage;
|
|
|
|
VkResult result;
|
|
|
|
// Get the list of VkFormat's that are supported:
|
|
uint32_t formatCount;
|
|
result = vkGetPhysicalDeviceSurfaceFormatsKHR(m_physicalDevice, m_surface, &formatCount, nullptr);
|
|
assert(!result);
|
|
|
|
std::vector<VkSurfaceFormatKHR> surfFormats(formatCount);
|
|
result = vkGetPhysicalDeviceSurfaceFormatsKHR(m_physicalDevice, m_surface, &formatCount, surfFormats.data());
|
|
assert(!result);
|
|
// 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.
|
|
|
|
m_surfaceFormat = VK_FORMAT_B8G8R8A8_UNORM;
|
|
m_surfaceColor = surfFormats[0].colorSpace;
|
|
|
|
for(uint32_t i = 0; i < formatCount; i++)
|
|
{
|
|
if(surfFormats[i].format == format)
|
|
{
|
|
m_surfaceFormat = format;
|
|
m_surfaceColor = surfFormats[i].colorSpace;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
VkExtent2D SwapChain::update(int width, int height, bool vsync)
|
|
{
|
|
m_changeID++;
|
|
|
|
VkResult err;
|
|
VkSwapchainKHR oldSwapchain = m_swapchain;
|
|
|
|
err = waitIdle();
|
|
if(nvvk::checkResult(err, __FILE__, __LINE__))
|
|
{
|
|
exit(-1);
|
|
}
|
|
// Check the surface capabilities and formats
|
|
VkSurfaceCapabilitiesKHR surfCapabilities;
|
|
err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(m_physicalDevice, m_surface, &surfCapabilities);
|
|
assert(!err);
|
|
|
|
uint32_t presentModeCount;
|
|
err = vkGetPhysicalDeviceSurfacePresentModesKHR(m_physicalDevice, m_surface, &presentModeCount, nullptr);
|
|
assert(!err);
|
|
std::vector<VkPresentModeKHR> presentModes(presentModeCount);
|
|
err = vkGetPhysicalDeviceSurfacePresentModesKHR(m_physicalDevice, m_surface, &presentModeCount, presentModes.data());
|
|
assert(!err);
|
|
|
|
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 = width;
|
|
swapchainExtent.height = height;
|
|
}
|
|
else
|
|
{
|
|
// If the surface size is defined, the swap chain size must match
|
|
swapchainExtent = surfCapabilities.currentExtent;
|
|
}
|
|
|
|
// test against valid size, typically hit when windows are minimized, the app must
|
|
// prevent triggering this code accordingly
|
|
assert(swapchainExtent.width && swapchainExtent.height);
|
|
|
|
// everyone must support FIFO mode
|
|
VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR;
|
|
// no vsync try to find a faster alternative to FIFO
|
|
if(!vsync)
|
|
{
|
|
for(uint32_t i = 0; i < presentModeCount; i++)
|
|
{
|
|
if(presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR)
|
|
{
|
|
swapchainPresentMode = VK_PRESENT_MODE_MAILBOX_KHR;
|
|
}
|
|
if(presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR)
|
|
{
|
|
swapchainPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
|
|
}
|
|
if (swapchainPresentMode == m_preferredVsyncOffMode)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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 swapchain = {VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR};
|
|
swapchain.surface = m_surface;
|
|
swapchain.minImageCount = desiredNumberOfSwapchainImages;
|
|
swapchain.imageFormat = m_surfaceFormat;
|
|
swapchain.imageColorSpace = m_surfaceColor;
|
|
swapchain.imageExtent = swapchainExtent;
|
|
swapchain.imageUsage = m_imageUsage;
|
|
swapchain.preTransform = preTransform;
|
|
swapchain.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
|
swapchain.imageArrayLayers = 1;
|
|
swapchain.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
swapchain.queueFamilyIndexCount = 1;
|
|
swapchain.pQueueFamilyIndices = &m_queueFamilyIndex;
|
|
swapchain.presentMode = swapchainPresentMode;
|
|
swapchain.oldSwapchain = oldSwapchain;
|
|
swapchain.clipped = true;
|
|
|
|
err = vkCreateSwapchainKHR(m_device, &swapchain, nullptr, &m_swapchain);
|
|
assert(!err);
|
|
|
|
nvvk::DebugUtil debugUtil(m_device);
|
|
|
|
debugUtil.setObjectName(m_swapchain, "SwapChain::m_swapchain");
|
|
|
|
// If we just re-created an existing swapchain, we should destroy the old
|
|
// swapchain at this point.
|
|
// Note: destroying the swapchain also cleans up all its associated
|
|
// presentable images once the platform is done with them.
|
|
if(oldSwapchain != VK_NULL_HANDLE)
|
|
{
|
|
for(auto it : m_entries)
|
|
{
|
|
vkDestroyImageView(m_device, it.imageView, nullptr);
|
|
vkDestroySemaphore(m_device, it.readSemaphore, nullptr);
|
|
vkDestroySemaphore(m_device, it.writtenSemaphore, nullptr);
|
|
}
|
|
vkDestroySwapchainKHR(m_device, oldSwapchain, nullptr);
|
|
}
|
|
|
|
err = vkGetSwapchainImagesKHR(m_device, m_swapchain, &m_imageCount, nullptr);
|
|
assert(!err);
|
|
|
|
m_entries.resize(m_imageCount);
|
|
m_barriers.resize(m_imageCount);
|
|
|
|
std::vector<VkImage> images(m_imageCount);
|
|
|
|
err = vkGetSwapchainImagesKHR(m_device, m_swapchain, &m_imageCount, images.data());
|
|
assert(!err);
|
|
//
|
|
// Image views
|
|
//
|
|
for(uint32_t i = 0; i < m_imageCount; i++)
|
|
{
|
|
Entry& entry = m_entries[i];
|
|
|
|
// image
|
|
entry.image = images[i];
|
|
|
|
// imageview
|
|
VkImageViewCreateInfo viewCreateInfo = {VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
|
nullptr,
|
|
0,
|
|
entry.image,
|
|
VK_IMAGE_VIEW_TYPE_2D,
|
|
m_surfaceFormat,
|
|
{VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A},
|
|
{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}};
|
|
|
|
err = vkCreateImageView(m_device, &viewCreateInfo, nullptr, &entry.imageView);
|
|
assert(!err);
|
|
|
|
// semaphore
|
|
VkSemaphoreCreateInfo semCreateInfo = {VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO};
|
|
|
|
err = vkCreateSemaphore(m_device, &semCreateInfo, nullptr, &entry.readSemaphore);
|
|
assert(!err);
|
|
err = vkCreateSemaphore(m_device, &semCreateInfo, nullptr, &entry.writtenSemaphore);
|
|
assert(!err);
|
|
|
|
// initial barriers
|
|
VkImageSubresourceRange range = {0};
|
|
range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
range.baseMipLevel = 0;
|
|
range.levelCount = VK_REMAINING_MIP_LEVELS;
|
|
range.baseArrayLayer = 0;
|
|
range.layerCount = VK_REMAINING_ARRAY_LAYERS;
|
|
|
|
VkImageMemoryBarrier memBarrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER};
|
|
memBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
|
memBarrier.dstAccessMask = 0;
|
|
memBarrier.srcAccessMask = 0;
|
|
memBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
memBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
|
memBarrier.image = entry.image;
|
|
memBarrier.subresourceRange = range;
|
|
|
|
m_barriers[i] = memBarrier;
|
|
|
|
debugUtil.setObjectName(entry.image, "swapchainImage:" + std::to_string(i));
|
|
debugUtil.setObjectName(entry.imageView, "swapchainImageView:" + std::to_string(i));
|
|
debugUtil.setObjectName(entry.readSemaphore, "swapchainReadSemaphore:" + std::to_string(i));
|
|
debugUtil.setObjectName(entry.writtenSemaphore, "swapchainWrittenSemaphore:" + std::to_string(i));
|
|
}
|
|
|
|
|
|
m_updateWidth = width;
|
|
m_updateHeight = height;
|
|
m_vsync = vsync;
|
|
m_extent = swapchainExtent;
|
|
|
|
m_currentSemaphore = 0;
|
|
m_currentImage = 0;
|
|
|
|
return swapchainExtent;
|
|
}
|
|
|
|
void SwapChain::deinitResources()
|
|
{
|
|
if(!m_device)
|
|
return;
|
|
|
|
VkResult result = waitIdle();
|
|
if(nvvk::checkResult(result, __FILE__, __LINE__))
|
|
{
|
|
exit(-1);
|
|
}
|
|
|
|
for(auto it : m_entries)
|
|
{
|
|
vkDestroyImageView(m_device, it.imageView, nullptr);
|
|
vkDestroySemaphore(m_device, it.readSemaphore, nullptr);
|
|
vkDestroySemaphore(m_device, it.writtenSemaphore, nullptr);
|
|
}
|
|
|
|
if(m_swapchain)
|
|
{
|
|
vkDestroySwapchainKHR(m_device, m_swapchain, nullptr);
|
|
m_swapchain = VK_NULL_HANDLE;
|
|
}
|
|
|
|
m_entries.clear();
|
|
m_barriers.clear();
|
|
}
|
|
|
|
void SwapChain::deinit()
|
|
{
|
|
deinitResources();
|
|
|
|
m_physicalDevice = VK_NULL_HANDLE;
|
|
m_device = VK_NULL_HANDLE;
|
|
m_surface = VK_NULL_HANDLE;
|
|
m_changeID = 0;
|
|
}
|
|
|
|
bool SwapChain::acquire(bool* pRecreated, SwapChainAcquireState* pOut)
|
|
{
|
|
return acquireCustom(VK_NULL_HANDLE, m_updateWidth, m_updateHeight, pRecreated, pOut);
|
|
}
|
|
|
|
bool SwapChain::acquireAutoResize(int width, int height, bool* pRecreated, SwapChainAcquireState* pOut)
|
|
{
|
|
return acquireCustom(VK_NULL_HANDLE, width, height, pRecreated, pOut);
|
|
}
|
|
|
|
bool SwapChain::acquireCustom(VkSemaphore argSemaphore, bool* pRecreated, SwapChainAcquireState* pOut)
|
|
{
|
|
return acquireCustom(argSemaphore, m_updateWidth, m_updateHeight, pRecreated, pOut);
|
|
}
|
|
|
|
bool SwapChain::acquireCustom(VkSemaphore argSemaphore, int width, int height, bool* pRecreated, SwapChainAcquireState* pOut)
|
|
{
|
|
bool didRecreate = false;
|
|
|
|
if(width != m_updateWidth || height != m_updateHeight)
|
|
{
|
|
deinitResources();
|
|
update(width, height);
|
|
m_updateWidth = width;
|
|
m_updateHeight = height;
|
|
didRecreate = true;
|
|
}
|
|
if(pRecreated != nullptr)
|
|
{
|
|
*pRecreated = didRecreate;
|
|
}
|
|
|
|
// try recreation a few times
|
|
for(int i = 0; i < 2; i++)
|
|
{
|
|
VkSemaphore semaphore = argSemaphore ? argSemaphore : getActiveReadSemaphore();
|
|
VkResult result;
|
|
result = vkAcquireNextImageKHR(m_device, m_swapchain, UINT64_MAX, semaphore, (VkFence)VK_NULL_HANDLE, &m_currentImage);
|
|
|
|
if(result == VK_SUCCESS)
|
|
{
|
|
if(pOut != nullptr)
|
|
{
|
|
pOut->image = getActiveImage();
|
|
pOut->view = getActiveImageView();
|
|
pOut->index = getActiveImageIndex();
|
|
pOut->waitSem = getActiveReadSemaphore();
|
|
pOut->signalSem = getActiveWrittenSemaphore();
|
|
}
|
|
return true;
|
|
}
|
|
else if(result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR)
|
|
{
|
|
deinitResources();
|
|
update(width, height, m_vsync);
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
VkSemaphore SwapChain::getActiveWrittenSemaphore() const
|
|
{
|
|
return m_entries[(m_currentSemaphore % m_imageCount)].writtenSemaphore;
|
|
}
|
|
|
|
VkSemaphore SwapChain::getActiveReadSemaphore() const
|
|
{
|
|
return m_entries[(m_currentSemaphore % m_imageCount)].readSemaphore;
|
|
}
|
|
|
|
VkImage SwapChain::getActiveImage() const
|
|
{
|
|
return m_entries[m_currentImage].image;
|
|
}
|
|
|
|
VkImageView SwapChain::getActiveImageView() const
|
|
{
|
|
return m_entries[m_currentImage].imageView;
|
|
}
|
|
|
|
VkImage SwapChain::getImage(uint32_t i) const
|
|
{
|
|
if(i >= m_imageCount)
|
|
return nullptr;
|
|
return m_entries[i].image;
|
|
}
|
|
|
|
void SwapChain::present(VkQueue queue)
|
|
{
|
|
VkResult result;
|
|
VkPresentInfoKHR presentInfo;
|
|
|
|
presentCustom(presentInfo);
|
|
|
|
result = vkQueuePresentKHR(queue, &presentInfo);
|
|
//assert(result == VK_SUCCESS); // can fail on application exit
|
|
}
|
|
|
|
void SwapChain::presentCustom(VkPresentInfoKHR& presentInfo)
|
|
{
|
|
VkSemaphore& written = m_entries[(m_currentSemaphore % m_imageCount)].writtenSemaphore;
|
|
|
|
presentInfo = {VK_STRUCTURE_TYPE_PRESENT_INFO_KHR};
|
|
presentInfo.swapchainCount = 1;
|
|
presentInfo.waitSemaphoreCount = 1;
|
|
presentInfo.pWaitSemaphores = &written;
|
|
presentInfo.pSwapchains = &m_swapchain;
|
|
presentInfo.pImageIndices = &m_currentImage;
|
|
|
|
m_currentSemaphore++;
|
|
}
|
|
|
|
void SwapChain::cmdUpdateBarriers(VkCommandBuffer cmd) const
|
|
{
|
|
vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0,
|
|
nullptr, m_imageCount, m_barriers.data());
|
|
}
|
|
|
|
uint32_t SwapChain::getChangeID() const
|
|
{
|
|
return m_changeID;
|
|
}
|
|
|
|
VkImageView SwapChain::getImageView(uint32_t i) const
|
|
{
|
|
if(i >= m_imageCount)
|
|
return nullptr;
|
|
return m_entries[i].imageView;
|
|
}
|
|
|
|
} // namespace nvvk
|
|
|