summaryrefslogtreecommitdiff
path: root/tools/gfx/vulkan/vk-swap-chain.cpp
diff options
context:
space:
mode:
authorlucy96chen <47800040+lucy96chen@users.noreply.github.com>2022-05-26 10:54:35 -0700
committerGitHub <noreply@github.com>2022-05-26 10:54:35 -0700
commit43e1b7cdc70b2fcac8a3e8ee72f5bc91726f4ec5 (patch)
tree1e4701b4ab324a199b81e1f6c671f6660f1050c5 /tools/gfx/vulkan/vk-swap-chain.cpp
parent5ff4f42c636a67724523e4fe60697cfac64908cd (diff)
Split render-vk.h/.cpp into a set of smaller files (#2244)
* Some preliminary work on splitting render-vk * render-vk split, tests currently crash on null reference * fixed circular include
Diffstat (limited to 'tools/gfx/vulkan/vk-swap-chain.cpp')
-rw-r--r--tools/gfx/vulkan/vk-swap-chain.cpp362
1 files changed, 362 insertions, 0 deletions
diff --git a/tools/gfx/vulkan/vk-swap-chain.cpp b/tools/gfx/vulkan/vk-swap-chain.cpp
new file mode 100644
index 000000000..158919197
--- /dev/null
+++ b/tools/gfx/vulkan/vk-swap-chain.cpp
@@ -0,0 +1,362 @@
+// vk-swap-chain.cpp
+#include "vk-swap-chain.h"
+
+#include "vk-util.h"
+
+namespace gfx
+{
+
+using namespace Slang;
+
+namespace vk
+{
+
+ISwapchain* SwapchainImpl::getInterface(const Guid& guid)
+{
+ if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_ISwapchain)
+ return static_cast<ISwapchain*>(this);
+ return nullptr;
+}
+
+void SwapchainImpl::destroySwapchainAndImages()
+{
+ m_api->vkQueueWaitIdle(m_queue->m_queue);
+ if (m_swapChain != VK_NULL_HANDLE)
+ {
+ m_api->vkDestroySwapchainKHR(m_api->m_device, m_swapChain, nullptr);
+ m_swapChain = VK_NULL_HANDLE;
+ }
+
+ // Mark that it is no longer used
+ m_images.clear();
+}
+
+void SwapchainImpl::getWindowSize(int* widthOut, int* heightOut) const
+{
+#if SLANG_WINDOWS_FAMILY
+ RECT rc;
+ ::GetClientRect((HWND)m_windowHandle.handleValues[0], &rc);
+ *widthOut = rc.right - rc.left;
+ *heightOut = rc.bottom - rc.top;
+#elif defined(SLANG_ENABLE_XLIB)
+ XWindowAttributes winAttr = {};
+ XGetWindowAttributes(
+ (Display*)m_windowHandle.handleValues[0], (Window)m_windowHandle.handleValues[1], &winAttr);
+
+ *widthOut = winAttr.width;
+ *heightOut = winAttr.height;
+#else
+ *widthOut = 0;
+ *heightOut = 0;
+#endif
+}
+
+Result SwapchainImpl::createSwapchainAndImages()
+{
+ int width, height;
+ getWindowSize(&width, &height);
+
+ VkExtent2D imageExtent = {};
+ imageExtent.width = width;
+ imageExtent.height = height;
+
+ m_desc.width = width;
+ m_desc.height = height;
+
+ // catch this before throwing error
+ if (width == 0 || height == 0)
+ {
+ return SLANG_FAIL;
+ }
+
+ // It is necessary to query the caps -> otherwise the LunarG verification layer will
+ // issue an error
+ {
+ VkSurfaceCapabilitiesKHR surfaceCaps;
+
+ SLANG_VK_RETURN_ON_FAIL(m_api->vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
+ m_api->m_physicalDevice, m_surface, &surfaceCaps));
+ }
+
+ VkPresentModeKHR presentMode;
+ List<VkPresentModeKHR> presentModes;
+ uint32_t numPresentModes = 0;
+ m_api->vkGetPhysicalDeviceSurfacePresentModesKHR(
+ m_api->m_physicalDevice, m_surface, &numPresentModes, nullptr);
+ presentModes.setCount(numPresentModes);
+ m_api->vkGetPhysicalDeviceSurfacePresentModesKHR(
+ m_api->m_physicalDevice, m_surface, &numPresentModes, presentModes.getBuffer());
+
+ {
+ int numCheckPresentOptions = 3;
+ VkPresentModeKHR presentOptions[] = {
+ VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_FIFO_KHR};
+ if (m_desc.enableVSync)
+ {
+ presentOptions[0] = VK_PRESENT_MODE_FIFO_KHR;
+ presentOptions[1] = VK_PRESENT_MODE_IMMEDIATE_KHR;
+ presentOptions[2] = VK_PRESENT_MODE_MAILBOX_KHR;
+ }
+
+ presentMode = VK_PRESENT_MODE_MAX_ENUM_KHR; // Invalid
+
+ // Find the first option that's available on the device
+ for (int j = 0; j < numCheckPresentOptions; j++)
+ {
+ if (presentModes.indexOf(presentOptions[j]) != Index(-1))
+ {
+ presentMode = presentOptions[j];
+ break;
+ }
+ }
+
+ if (presentMode == VK_PRESENT_MODE_MAX_ENUM_KHR)
+ {
+ return SLANG_FAIL;
+ }
+ }
+
+ VkSwapchainKHR oldSwapchain = VK_NULL_HANDLE;
+
+ VkSwapchainCreateInfoKHR swapchainDesc = {};
+ swapchainDesc.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
+ swapchainDesc.surface = m_surface;
+ swapchainDesc.minImageCount = m_desc.imageCount;
+ swapchainDesc.imageFormat = m_vkformat;
+ swapchainDesc.imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
+ swapchainDesc.imageExtent = imageExtent;
+ swapchainDesc.imageArrayLayers = 1;
+ swapchainDesc.imageUsage =
+ VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
+ swapchainDesc.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
+ swapchainDesc.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
+ swapchainDesc.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
+ swapchainDesc.presentMode = presentMode;
+ swapchainDesc.clipped = VK_TRUE;
+ swapchainDesc.oldSwapchain = oldSwapchain;
+
+ SLANG_VK_RETURN_ON_FAIL(
+ m_api->vkCreateSwapchainKHR(m_api->m_device, &swapchainDesc, nullptr, &m_swapChain));
+
+ uint32_t numSwapChainImages = 0;
+ m_api->vkGetSwapchainImagesKHR(m_api->m_device, m_swapChain, &numSwapChainImages, nullptr);
+ List<VkImage> vkImages;
+ {
+ vkImages.setCount(numSwapChainImages);
+ m_api->vkGetSwapchainImagesKHR(
+ m_api->m_device, m_swapChain, &numSwapChainImages, vkImages.getBuffer());
+ }
+
+ for (GfxIndex i = 0; i < m_desc.imageCount; i++)
+ {
+ ITextureResource::Desc imageDesc = {};
+ imageDesc.allowedStates = ResourceStateSet(
+ ResourceState::Present, ResourceState::RenderTarget, ResourceState::CopyDestination);
+ imageDesc.type = IResource::Type::Texture2D;
+ imageDesc.arraySize = 0;
+ imageDesc.format = m_desc.format;
+ imageDesc.size.width = m_desc.width;
+ imageDesc.size.height = m_desc.height;
+ imageDesc.size.depth = 1;
+ imageDesc.numMipLevels = 1;
+ imageDesc.defaultState = ResourceState::Present;
+ RefPtr<TextureResourceImpl> image = new TextureResourceImpl(imageDesc, m_renderer);
+ image->m_image = vkImages[i];
+ image->m_imageMemory = 0;
+ image->m_vkformat = m_vkformat;
+ image->m_isWeakImageReference = true;
+ m_images.add(image);
+ }
+ return SLANG_OK;
+}
+
+SwapchainImpl::~SwapchainImpl()
+{
+ destroySwapchainAndImages();
+ if (m_surface)
+ {
+ m_api->vkDestroySurfaceKHR(m_api->m_instance, m_surface, nullptr);
+ m_surface = VK_NULL_HANDLE;
+ }
+ m_renderer->m_api.vkDestroySemaphore(m_renderer->m_api.m_device, m_nextImageSemaphore, nullptr);
+}
+
+Index SwapchainImpl::_indexOfFormat(List<VkSurfaceFormatKHR>& formatsIn, VkFormat format)
+{
+ const Index numFormats = formatsIn.getCount();
+ const VkSurfaceFormatKHR* formats = formatsIn.getBuffer();
+
+ for (Index i = 0; i < numFormats; ++i)
+ {
+ if (formats[i].format == format)
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+Result SwapchainImpl::init(DeviceImpl* renderer, const ISwapchain::Desc& desc, WindowHandle window)
+{
+ m_desc = desc;
+ m_renderer = renderer;
+ m_api = &renderer->m_api;
+ m_queue = static_cast<CommandQueueImpl*>(desc.queue);
+ m_windowHandle = window;
+
+ VkSemaphoreCreateInfo semaphoreCreateInfo = {};
+ semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+ SLANG_VK_RETURN_ON_FAIL(renderer->m_api.vkCreateSemaphore(
+ renderer->m_api.m_device, &semaphoreCreateInfo, nullptr, &m_nextImageSemaphore));
+
+ m_queue = static_cast<CommandQueueImpl*>(desc.queue);
+
+ // Make sure it's not set initially
+ m_vkformat = VK_FORMAT_UNDEFINED;
+
+#if SLANG_WINDOWS_FAMILY
+ VkWin32SurfaceCreateInfoKHR surfaceCreateInfo = {};
+ surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
+ surfaceCreateInfo.hinstance = ::GetModuleHandle(nullptr);
+ surfaceCreateInfo.hwnd = (HWND)window.handleValues[0];
+ SLANG_VK_RETURN_ON_FAIL(
+ m_api->vkCreateWin32SurfaceKHR(m_api->m_instance, &surfaceCreateInfo, nullptr, &m_surface));
+#else
+ VkXlibSurfaceCreateInfoKHR surfaceCreateInfo = {};
+ surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
+ surfaceCreateInfo.dpy = (Display*)window.handleValues[0];
+ surfaceCreateInfo.window = (Window)window.handleValues[1];
+ SLANG_VK_RETURN_ON_FAIL(
+ m_api->vkCreateXlibSurfaceKHR(m_api->m_instance, &surfaceCreateInfo, nullptr, &m_surface));
+#endif
+
+ VkBool32 supported = false;
+ m_api->vkGetPhysicalDeviceSurfaceSupportKHR(
+ m_api->m_physicalDevice, renderer->m_queueFamilyIndex, m_surface, &supported);
+
+ uint32_t numSurfaceFormats = 0;
+ List<VkSurfaceFormatKHR> surfaceFormats;
+ m_api->vkGetPhysicalDeviceSurfaceFormatsKHR(
+ m_api->m_physicalDevice, m_surface, &numSurfaceFormats, nullptr);
+ surfaceFormats.setCount(int(numSurfaceFormats));
+ m_api->vkGetPhysicalDeviceSurfaceFormatsKHR(
+ m_api->m_physicalDevice, m_surface, &numSurfaceFormats, surfaceFormats.getBuffer());
+
+ // Look for a suitable format
+ List<VkFormat> formats;
+ formats.add(VulkanUtil::getVkFormat(desc.format));
+ // HACK! To check for a different format if couldn't be found
+ if (desc.format == Format::R8G8B8A8_UNORM)
+ {
+ formats.add(VK_FORMAT_B8G8R8A8_UNORM);
+ }
+
+ for (Index i = 0; i < formats.getCount(); ++i)
+ {
+ VkFormat format = formats[i];
+ if (_indexOfFormat(surfaceFormats, format) >= 0)
+ {
+ m_vkformat = format;
+ }
+ }
+
+ if (m_vkformat == VK_FORMAT_UNDEFINED)
+ {
+ return SLANG_FAIL;
+ }
+
+ // Save the desc
+ m_desc = desc;
+ if (m_desc.format == Format::R8G8B8A8_UNORM && m_vkformat == VK_FORMAT_B8G8R8A8_UNORM)
+ {
+ m_desc.format = Format::B8G8R8A8_UNORM;
+ }
+
+ createSwapchainAndImages();
+ return SLANG_OK;
+}
+
+Result SwapchainImpl::getImage(GfxIndex index, ITextureResource** outResource)
+{
+ if (m_images.getCount() <= (Index)index)
+ return SLANG_FAIL;
+ returnComPtr(outResource, m_images[index]);
+ return SLANG_OK;
+}
+
+Result SwapchainImpl::resize(GfxCount width, GfxCount height)
+{
+ SLANG_UNUSED(width);
+ SLANG_UNUSED(height);
+ destroySwapchainAndImages();
+ return createSwapchainAndImages();
+}
+
+Result SwapchainImpl::present()
+{
+ // If there are pending fence wait operations, flush them as an
+ // empty vkQueueSubmit.
+ if (m_queue->m_pendingWaitFences.getCount() != 0)
+ {
+ m_queue->queueSubmitImpl(0, nullptr, nullptr, 0);
+ }
+
+ uint32_t swapChainIndices[] = {uint32_t(m_currentImageIndex)};
+
+ VkPresentInfoKHR presentInfo = {};
+ presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
+ presentInfo.swapchainCount = 1;
+ presentInfo.pSwapchains = &m_swapChain;
+ presentInfo.pImageIndices = swapChainIndices;
+ Array<VkSemaphore, 2> waitSemaphores;
+ for (auto s : m_queue->m_pendingWaitSemaphores)
+ {
+ if (s != VK_NULL_HANDLE)
+ {
+ waitSemaphores.add(s);
+ }
+ }
+ m_queue->m_pendingWaitSemaphores[0] = VK_NULL_HANDLE;
+ m_queue->m_pendingWaitSemaphores[1] = VK_NULL_HANDLE;
+ presentInfo.waitSemaphoreCount = (uint32_t)waitSemaphores.getCount();
+ if (presentInfo.waitSemaphoreCount)
+ {
+ presentInfo.pWaitSemaphores = waitSemaphores.getBuffer();
+ }
+ if (m_currentImageIndex != -1)
+ m_api->vkQueuePresentKHR(m_queue->m_queue, &presentInfo);
+ return SLANG_OK;
+}
+
+int SwapchainImpl::acquireNextImage()
+{
+ if (!m_images.getCount())
+ {
+ m_queue->m_pendingWaitSemaphores[1] = VK_NULL_HANDLE;
+ return -1;
+ }
+
+ m_currentImageIndex = -1;
+ VkResult result = m_api->vkAcquireNextImageKHR(
+ m_api->m_device,
+ m_swapChain,
+ UINT64_MAX,
+ m_nextImageSemaphore,
+ VK_NULL_HANDLE,
+ (uint32_t*)&m_currentImageIndex);
+
+ if (result != VK_SUCCESS)
+ {
+ m_currentImageIndex = -1;
+ destroySwapchainAndImages();
+ return m_currentImageIndex;
+ }
+ // Make the queue's next submit wait on `m_nextImageSemaphore`.
+ m_queue->m_pendingWaitSemaphores[1] = m_nextImageSemaphore;
+ return m_currentImageIndex;
+}
+
+Result SwapchainImpl::setFullScreenMode(bool mode) { return SLANG_FAIL; }
+
+} // namespace vk
+} // namespace gfx