summaryrefslogtreecommitdiff
path: root/tools/render-test/vk-swap-chain.cpp
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2018-05-03 14:25:13 -0400
committerGitHub <noreply@github.com>2018-05-03 14:25:13 -0400
commit367f3a78a40731da45ee12b9a18c94707f1d1429 (patch)
tree5993ef627e1a094ea1d401c31e6b00e3c63c308a /tools/render-test/vk-swap-chain.cpp
parent78935493587ec65a199d844327613021667acc1b (diff)
Feature/vulkan first render (#545)
* First pass at InputLayout for Vulkan Add support for RGBA_Float32 * Use VulkanModule and VulkanApi to handle accessing Vulkan types. * First pass at Vulkan swap chain/Device queue. * Added VulkanUtil for generic function functions. * Move more functionality to VulkanApi and VulkanUtil. Make Buffer able to initialize itself. * More tidy up around VulkanDeviceQueue * First pass use of VulkanDeviceQueue in VkRenderer * First pass use of VulkanSwapChain on VkRenderer * Added depth formats. Binding for constant and vertex buffers for Vulkan. * Setting up VkImageView on backbuffers. * First pass support for setting up vkRenderPass. * Fixes to work around Vulkan swap chain/verification issues. * Added support for Pipeline and a pipeline cache. * Working without waiting - because use of pipeline cache. * Added support for VkFramebuffer in Vulkan. * First pass at creating Vulkan graphics pipeline. * More efforts to get Vulkan to render. * Small improvement for checking of Binding flags. * Removed setConstantBuffers from the Renderer interface - so that all resource binding takes place through the BindingState. To make this work required a 'hack' in render-test main.cpp - so that the constant buffer binding that is needed in some tests is only added when it doesn't clash. * RendererID -> unified into RendererType. Added getRendererType to Renderer interface. Added ProjectionStyle, and function to get from RendererType. Added getIdentityProjection to RendererUtil - to get projection that is the 'identity' - but hits the same pixels for all projection styles. * Fix build problem on Win32 on Vulkan where should use VK_NULL_HANDLE. * Improve naming, comments. Remove dead code. * Remove unwanted comment.
Diffstat (limited to 'tools/render-test/vk-swap-chain.cpp')
-rw-r--r--tools/render-test/vk-swap-chain.cpp421
1 files changed, 421 insertions, 0 deletions
diff --git a/tools/render-test/vk-swap-chain.cpp b/tools/render-test/vk-swap-chain.cpp
new file mode 100644
index 000000000..7b8b6221c
--- /dev/null
+++ b/tools/render-test/vk-swap-chain.cpp
@@ -0,0 +1,421 @@
+// vk-swap-chain.cpp
+#include "vk-swap-chain.h"
+
+#include "vk-util.h"
+
+#include "../../source/core/list.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+namespace renderer_test {
+using namespace Slang;
+
+static int _indexOf(List<VkSurfaceFormatKHR>& formatsIn, VkFormat format)
+{
+ const int numFormats = int(formatsIn.Count());
+ const VkSurfaceFormatKHR* formats = formatsIn.Buffer();
+
+ for (int i = 0; i < numFormats; ++i)
+ {
+ if (formats[i].format == format)
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+SlangResult VulkanSwapChain::init(VulkanDeviceQueue* deviceQueue, const Desc& descIn, const PlatformDesc* platformDescIn)
+{
+ assert(platformDescIn);
+
+ m_deviceQueue = deviceQueue;
+ m_api = deviceQueue->getApi();
+
+ // Make sure it's not set initially
+ m_format = VK_FORMAT_UNDEFINED;
+
+ Desc desc(descIn);
+
+#if SLANG_WINDOWS_FAMILY
+ const WinPlatformDesc* platformDesc = static_cast<const WinPlatformDesc*>(platformDescIn);
+ _setPlatformDesc(*platformDesc);
+
+ VkWin32SurfaceCreateInfoKHR surfaceCreateInfo = {};
+ surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
+ surfaceCreateInfo.hinstance = platformDesc->m_hinstance;
+ surfaceCreateInfo.hwnd = platformDesc->m_hwnd;
+
+ SLANG_VK_RETURN_ON_FAIL(m_api->vkCreateWin32SurfaceKHR(m_api->m_instance, &surfaceCreateInfo, nullptr, &m_surface));
+#else
+ const XPlatformDesc* platformDesc = static_cast<const XPlatformDesc*>(platformDescIn);
+ _setPlatformDesc(*platformDesc);
+
+ VkXlibSurfaceCreateInfoKHR surfaceCreateInfo = {};
+ surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
+ surfaceCreateInfo.dpy = platformDesc->m_display;
+ surfaceCreateInfo.window = platformDesc->m_window;
+
+ 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, deviceQueue->getQueueIndex(), m_surface, &supported);
+
+ uint32_t numSurfaceFormats = 0;
+ List<VkSurfaceFormatKHR> surfaceFormats;
+ m_api->vkGetPhysicalDeviceSurfaceFormatsKHR(m_api->m_physicalDevice, m_surface, &numSurfaceFormats, nullptr);
+ surfaceFormats.SetSize(int(numSurfaceFormats));
+ m_api->vkGetPhysicalDeviceSurfaceFormatsKHR(m_api->m_physicalDevice, m_surface, &numSurfaceFormats, surfaceFormats.Buffer());
+
+ // Look for a suitable format
+ List<VkFormat> formats;
+ formats.Add(VulkanUtil::getVkFormat(desc.m_format));
+ // HACK! To check for a different format if couldn't be found
+ if (descIn.m_format == Format::RGBA_Unorm_UInt8)
+ {
+ formats.Add(VK_FORMAT_B8G8R8A8_UNORM);
+ }
+
+ for(int i = 0; i < int(formats.Count()); ++i)
+ {
+ VkFormat format = formats[i];
+ if (_indexOf(surfaceFormats, format) >= 0)
+ {
+ m_format = format;
+ }
+ }
+
+ if (m_format == VK_FORMAT_UNDEFINED)
+ {
+ return SLANG_FAIL;
+ }
+
+ // Save the desc
+ m_desc = desc;
+
+ SLANG_RETURN_ON_FAIL(_createSwapChain());
+
+ m_desc = desc;
+ return SLANG_OK;
+}
+
+void VulkanSwapChain::getWindowSize(int* widthOut, int* heightOut) const
+{
+#if SLANG_WINDOWS_FAMILY
+ auto platformDesc = _getPlatformDesc<WinPlatformDesc>();
+
+ RECT rc;
+ ::GetClientRect(platformDesc->m_hwnd, &rc);
+ *widthOut = rc.right - rc.left;
+ *heightOut = rc.bottom - rc.top;
+#else
+ auto platformDesc = _getPlatformDesc<XPlatformDesc>();
+
+ XWindowAttributes winAttr = {};
+ XGetWindowAttributes(platformDesc->m_display, platformDesc->m_window, &winAttr);
+
+ *widthOut = winAttr.width;
+ *heightOut = winAttr.height;
+#endif
+}
+
+SlangResult VulkanSwapChain::_createFrameBuffers(VkRenderPass renderPass)
+{
+ assert(renderPass != VK_NULL_HANDLE);
+
+ for (int i = 0; i < int(m_images.Count()); ++i)
+ {
+ Image& image = m_images[i];
+ VkImageView attachments[] =
+ {
+ image.m_imageView
+ };
+
+ VkFramebufferCreateInfo framebufferInfo = {};
+ framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
+ framebufferInfo.renderPass = renderPass;
+ framebufferInfo.attachmentCount = 1;
+ framebufferInfo.pAttachments = attachments;
+ framebufferInfo.width = m_width;
+ framebufferInfo.height = m_height;
+ framebufferInfo.layers = 1;
+
+ SLANG_VK_RETURN_ON_FAIL(m_api->vkCreateFramebuffer(m_api->m_device, &framebufferInfo, nullptr, &image.m_frameBuffer));
+ }
+
+ return SLANG_OK;
+}
+
+void VulkanSwapChain::_destroyFrameBuffers()
+{
+ for (int i = 0; i < int(m_images.Count()); ++i)
+ {
+ Image& image = m_images[i];
+ if (image.m_frameBuffer != VK_NULL_HANDLE)
+ {
+ m_api->vkDestroyFramebuffer(m_api->m_device, image.m_frameBuffer, nullptr);
+ image.m_frameBuffer = VK_NULL_HANDLE;
+ }
+ }
+}
+
+SlangResult VulkanSwapChain::createFrameBuffers(VkRenderPass renderPass)
+{
+ if (m_renderPass != VK_NULL_HANDLE)
+ {
+ _destroyFrameBuffers();
+ m_renderPass = VK_NULL_HANDLE;
+ }
+ if (renderPass != VK_NULL_HANDLE)
+ {
+ SLANG_RETURN_ON_FAIL(_createFrameBuffers(renderPass));
+ }
+ m_renderPass = renderPass;
+ return SLANG_OK;
+}
+
+SlangResult VulkanSwapChain::_createSwapChain()
+{
+ if (hasValidSwapChain())
+ {
+ return SLANG_OK;
+ }
+
+ int width, height;
+ getWindowSize(&width, &height);
+
+ VkExtent2D imageExtent = {};
+ imageExtent.width = width;
+ imageExtent.height = height;
+
+ m_width = width;
+ m_height = height;
+
+ // catch this before throwing error
+ if (m_width == 0 || m_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));
+ }
+
+ List<VkPresentModeKHR> presentModes;
+ uint32_t numPresentModes = 0;
+ m_api->vkGetPhysicalDeviceSurfacePresentModesKHR(m_api->m_physicalDevice, m_surface, &numPresentModes, nullptr);
+ presentModes.SetSize(numPresentModes);
+ m_api->vkGetPhysicalDeviceSurfacePresentModesKHR(m_api->m_physicalDevice, m_surface, &numPresentModes, presentModes.Buffer());
+
+ {
+ int numCheckPresentOptions = 3;
+ VkPresentModeKHR presentOptions[] = { VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_FIFO_KHR };
+ if (m_vsync)
+ {
+ presentOptions[0] = VK_PRESENT_MODE_FIFO_KHR;
+ presentOptions[1] = VK_PRESENT_MODE_IMMEDIATE_KHR;
+ presentOptions[2] = VK_PRESENT_MODE_MAILBOX_KHR;
+ }
+
+ m_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]) != UInt(-1))
+ {
+ m_presentMode = presentOptions[j];
+ break;
+ }
+ }
+
+ if (m_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 = 3;
+ swapchainDesc.imageFormat = m_format;
+ 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 = m_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> images;
+ images.SetSize(numSwapChainImages);
+
+ m_api->vkGetSwapchainImagesKHR(m_api->m_device, m_swapChain, &numSwapChainImages, images.Buffer());
+
+ m_images.SetSize(numSwapChainImages);
+ for (int i = 0; i < int(numSwapChainImages); ++i)
+ {
+ Image& dstImage = m_images[i];
+ dstImage.m_image = images[i];
+
+ }
+ }
+
+ {
+ VkImageViewCreateInfo createInfo = {};
+ createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+
+ createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
+ createInfo.format = m_format;
+
+ createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
+ createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
+ createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
+ createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
+
+ createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ createInfo.subresourceRange.baseMipLevel = 0;
+ createInfo.subresourceRange.levelCount = 1;
+ createInfo.subresourceRange.baseArrayLayer = 0;
+ createInfo.subresourceRange.layerCount = 1;
+
+ for (int i = 0; i < int(numSwapChainImages); ++i)
+ {
+ Image& image = m_images[i];
+
+ createInfo.image = image.m_image;
+
+ SLANG_VK_RETURN_ON_FAIL(m_api->vkCreateImageView(m_api->m_device, &createInfo, nullptr, &image.m_imageView));
+ }
+ }
+
+ if (m_renderPass != VK_NULL_HANDLE)
+ {
+ _createFrameBuffers(m_renderPass);
+ }
+
+ return SLANG_OK;
+}
+
+void VulkanSwapChain::_destroySwapChain()
+{
+ if (!hasValidSwapChain())
+ {
+ return;
+ }
+
+ m_deviceQueue->waitForIdle();
+
+ if (m_renderPass != VK_NULL_HANDLE)
+ {
+ _destroyFrameBuffers();
+ }
+
+ for (int i = 0; i < int(m_images.Count()); ++i)
+ {
+ Image& image = m_images[i];
+
+ if (image.m_imageView != VK_NULL_HANDLE)
+ {
+ m_api->vkDestroyImageView(m_api->m_device, image.m_imageView, nullptr);
+ }
+ }
+
+ 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();
+}
+
+VulkanSwapChain::~VulkanSwapChain()
+{
+ _destroySwapChain();
+
+ if (m_surface)
+ {
+ m_api->vkDestroySurfaceKHR(m_api->m_instance, m_surface, nullptr);
+ m_surface = VK_NULL_HANDLE;
+ }
+}
+
+int VulkanSwapChain::nextFrontImageIndex()
+{
+ if (!hasValidSwapChain())
+ {
+ if (SLANG_FAILED(_createSwapChain()))
+ {
+ return -1;
+ }
+ }
+
+ VkSemaphore beginFrameSemaphore = m_deviceQueue->makeCurrent(VulkanDeviceQueue::EventType::BeginFrame);
+
+ uint32_t swapChainIndex = 0;
+ VkResult result = m_api->vkAcquireNextImageKHR(m_api->m_device, m_swapChain, UINT64_MAX, beginFrameSemaphore, VK_NULL_HANDLE, &swapChainIndex);
+
+ if (result != VK_SUCCESS)
+ {
+ _destroySwapChain();
+ return -1;
+ }
+ m_currentSwapChainIndex = int(swapChainIndex);
+ return swapChainIndex;
+}
+
+void VulkanSwapChain::present(bool vsync)
+{
+ if (!hasValidSwapChain())
+ {
+ m_deviceQueue->flush();
+ return;
+ }
+
+ VkSemaphore endFrameSemaphore = m_deviceQueue->makeCurrent(VulkanDeviceQueue::EventType::EndFrame);
+
+ m_deviceQueue->flushStepA();
+
+ uint32_t swapChainIndices[] = { uint32_t(m_currentSwapChainIndex) };
+
+ VkPresentInfoKHR presentInfo = {};
+ presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
+ presentInfo.swapchainCount = 1;
+ presentInfo.pSwapchains = &m_swapChain;
+ presentInfo.pImageIndices = swapChainIndices;
+ presentInfo.waitSemaphoreCount = 1;
+ presentInfo.pWaitSemaphores = &endFrameSemaphore;
+
+ VkResult result = m_api->vkQueuePresentKHR(m_deviceQueue->getQueue(), &presentInfo);
+
+ m_deviceQueue->makeCompleted(VulkanDeviceQueue::EventType::EndFrame);
+
+ m_deviceQueue->flushStepB();
+
+ if (result != VK_SUCCESS || m_vsync != vsync)
+ {
+ m_vsync = vsync;
+ _destroySwapChain();
+ }
+}
+
+} // renderer_test