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,会发现接下来的概念越加熟悉。

One thought on “Vulkan大闷锅之Swapchain”

  1. I have noticed you don’t monetize your website, don’t waste your traffic, you can earn extra bucks every month.
    You can use the best adsense alternative for any type of website
    (they approve all websites), for more info simply search in gooogle: boorfe’s tips
    monetize your website

Leave a Reply

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