Vulkan大闷锅之Swapchain

简述

当前的图形渲染过程并不是画好每帧画面,就立即输出,而是一个Swapchain结构,其主要目的是解决画面撕裂。现在会有多张画面缓存在Swapchan,然后依次输出最上层的一张,每一次的输出操作被称为Present。每个Present动作会将当前帧输出到Surface(上一章已经创建好)。

游戏视效设置中的多重缓冲选项,本质就是在调整Swapchain队列尺寸。

准备工作

获取Surface功能和Present模式:

VkSurfaceCapabilitiesKHR surfCapabilities;
res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(gpus[0], surface,
                                                &surfCapabilities);
assert(res == VK_SUCCESS);
uint32_t presentModeCount;
res = vkGetPhysicalDeviceSurfacePresentModesKHR(gpus[0], surface,
                                                &presentModeCount, nullptr);
assert(res == VK_SUCCESS);

VkPresentModeKHR *presentModes =
    (VkPresentModeKHR *)malloc(presentModeCount * sizeof(VkPresentModeKHR));
res = vkGetPhysicalDeviceSurfacePresentModesKHR(
    gpus[0], surface, &presentModeCount, presentModes);
assert(res == VK_SUCCESS);

有许多配置需要提前准备:

VkExtent2D swapchainExtent;
if (surfCapabilities.currentExtent.width == 0xFFFFFFFF)
{
  swapchainExtent.width = WIDTH;
  swapchainExtent.height = HEIGHT;
  if (swapchainExtent.width < surfCapabilities.minImageExtent.width) 
  { 
    swapchainExtent.width = surfCapabilities.minImageExtent.width; 
  } 
  else if (swapchainExtent.width > surfCapabilities.maxImageExtent.width)
  {
    swapchainExtent.width = surfCapabilities.maxImageExtent.width;
  }

  if (swapchainExtent.height < surfCapabilities.minImageExtent.height) 
  { 
    swapchainExtent.height = surfCapabilities.minImageExtent.height; 
  } else if (swapchainExtent.height >
            surfCapabilities.maxImageExtent.height)
  {
    swapchainExtent.height = surfCapabilities.maxImageExtent.height;
  }
}
else
{
  swapchainExtent = surfCapabilities.currentExtent;
}

VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR;

uint32_t desiredNumberOfSwapChainImages = surfCapabilities.minImageCount;

VkSurfaceTransformFlagBitsKHR preTransform;
if (surfCapabilities.supportedTransforms &
    VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)
{
  preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
}
else
{
  preTransform = surfCapabilities.currentTransform;
}

VkCompositeAlphaFlagBitsKHR compositeAlpha =
    VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
VkCompositeAlphaFlagBitsKHR compositeAlphaFlags[4] = {
    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 (uint32_t i = 0; i < sizeof(compositeAlphaFlags); ++i)
{
  if (surfCapabilities.supportedCompositeAlpha & compositeAlphaFlags[i])
  {
    compositeAlpha = compositeAlphaFlags[i];
    break;
  }
}

创建Swapchain

接下来准备Swapchain的创建信息:

VkSwapchainCreateInfoKHR swapchain_ci = {};
swapchain_ci.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
swapchain_ci.pNext = nullptr;
swapchain_ci.surface = surface;
swapchain_ci.minImageCount = desiredNumberOfSwapChainImages;
swapchain_ci.imageFormat = format;
swapchain_ci.imageExtent = swapchainExtent;
swapchain_ci.preTransform = preTransform;
swapchain_ci.compositeAlpha = compositeAlpha;
swapchain_ci.imageArrayLayers = 1;
swapchain_ci.presentMode = swapchainPresentMode;
swapchain_ci.oldSwapchain = VK_NULL_HANDLE;
swapchain_ci.clipped = VK_TRUE;
swapchain_ci.imageColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
swapchain_ci.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
swapchain_ci.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
swapchain_ci.queueFamilyIndexCount = 0;
swapchain_ci.pQueueFamilyIndices = nullptr

还有几项需要校验成功的配置项:

VkBool32 *pSupportsPresent =
    (VkBool32 *)malloc(queue_family_count * sizeof(VkBool32));

for (uint32_t i = 0; i < queue_family_count; ++i)
{
  vkGetPhysicalDeviceSurfaceSupportKHR(gpus[0], i, surface,
                                        &pSupportsPresent[i]);
  // assert(res);
}

这里诊断会失败,但是不影响程序照常运行。官方文档在这里没有诊断,也没有说明。

uint32_t graphics_queue_family_index = UINT32_MAX;
uint32_t present_queue_family_index = UINT32_MAX;
for (uint32_t i = 0; i < queue_family_count; ++i)
{
  if ((queue_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0)
  {
    if (graphics_queue_family_index == UINT32_MAX)
    {
      graphics_queue_family_index = i;
    }
    if (pSupportsPresent[i] == VK_TRUE)
    {
      graphics_queue_family_index = i;
      present_queue_family_index = i;
      break;
    }
  }
}
if (present_queue_family_index == UINT32_MAX)
{
  for (size_t i = 0; i < queue_family_count; ++i)
  {
    if (pSupportsPresent[i] == VK_TRUE)
    {
      present_queue_family_index = i;
      break;
    }
  }
}
free(pSupportsPresent);

if (graphics_queue_family_index == UINT32_MAX ||
    present_queue_family_index == UINT32_MAX)
{
  std::cout << "could not find a queues for graphics and present"
            << std::endl;
  exit(-1);
}
uint32_t queueFamilyIndices[2] = {
    (uint32_t)graphics_queue_family_index,
    (uint32_t)present_queue_family_index,
};
if (graphics_queue_family_index != present_queue_family_index)
{
  swapchain_ci.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
  swapchain_ci.queueFamilyIndexCount = 2;
  swapchain_ci.pQueueFamilyIndices = queueFamilyIndices;
}

这里代码略显重复,可以在适当的时候优化。下面,终于可以创建Swapchain了:

VkSwapchainKHR swap_chain;
res = vkCreateSwapchainKHR(device, &swapchain_ci, nullptr, &swap_chain);
assert(res == VK_SUCCESS);

Swapchain本质是一系列Image,这里通过创建Image View来定义这些Image的行为:

uint32_t swapchainImageCount;
res = vkGetSwapchainImagesKHR(device, swap_chain, &swapchainImageCount,
                              nullptr);
assert(res == VK_SUCCESS);
VkImage *swapchainImages =
    (VkImage *)malloc(swapchainImageCount * sizeof(VkImage));
assert(swapchainImages);
res = vkGetSwapchainImagesKHR(device, swap_chain, &swapchainImageCount,
                              swapchainImages);
assert(res == VK_SUCCESS);

struct SwapChainBuffer
{
  VkImage image;
  VkImageView view;
};
std::vector<SwapChainBuffer> swapchainBuffers(0);
swapchainBuffers.resize(swapchainImageCount);
for (uint32_t i = 0; i < swapchainImageCount; ++i)
{
  swapchainBuffers[i].image = swapchainImages[i];
}
free(swapchainImages);

for (uint32_t i = 0; i < swapchainImageCount; ++i)
{
  VkImageViewCreateInfo color_image_view = {};
  color_image_view.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
  color_image_view.pNext = nullptr;
  color_image_view.flags = 0;
  color_image_view.image = swapchainBuffers[i].image;
  color_image_view.viewType = VK_IMAGE_VIEW_TYPE_2D;
  color_image_view.format = format;
  color_image_view.components.r = VK_COMPONENT_SWIZZLE_R;
  color_image_view.components.g = VK_COMPONENT_SWIZZLE_G;
  color_image_view.components.b = VK_COMPONENT_SWIZZLE_B;
  color_image_view.components.a = VK_COMPONENT_SWIZZLE_A;
  color_image_view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  color_image_view.subresourceRange.baseMipLevel = 0;
  color_image_view.subresourceRange.levelCount = 1;
  color_image_view.subresourceRange.baseArrayLayer = 0;
  color_image_view.subresourceRange.layerCount = 1;

  res = vkCreateImageView(device, &color_image_view, nullptr,
                          &swapchainBuffers[i].view);
  assert(res == VK_SUCCESS);
}

由于篇幅限制,其余内容将留到后续的博文。如果你了解OpenGL,会发现接下来的概念越加熟悉。

Leave a Reply

Your email address will not be published. Required fields are marked *