Vulkan笔记(六)--- 交换链 - 教程

交换链(swapChain)

           Vulkan 没有“默认帧缓冲区”的概念,因此它需要一个基础设施来拥有我们将渲染到的缓冲区,然后再将它们可视化到屏幕上。此基础结构是称为交换链,必须在 Vulkan 中显式创建。交换链本质上是等待呈现给屏幕。我们的应用程序将获取这样的图像以绘制到它,然后将其返回到队列中。队列到底是如何工作的,以及从队列中呈现图像取决于交换链的设置方式,但是交换链的一般目的是同步具有屏幕刷新率的图像。

          并非所有显卡都能够将图像直接呈现到屏幕上。例如为服务器设计的不具有任何显示输出功能。由于图像与呈现是紧密相关的,所以要想显示必须启用设备扩展VK_KHR_swapchain。先检查设备是否支持交换链的扩展。

const std::vector deviceExtensionNames = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME
};
bool CVulkanApp::checkDeviceExtensionSupport(VkPhysicalDevice dev)
{
uint32_t deviceExtensionCount;
vkEnumerateDeviceExtensionProperties(dev, nullptr, &deviceExtensionCount, nullptr);
std::vector deviceExtensions(deviceExtensionCount);
vkEnumerateDeviceExtensionProperties(dev, nullptr, &deviceExtensionCount, deviceExtensions.data());
std::set requiredExtensions(deviceExtensionNames.begin(), deviceExtensionNames.end());
for ( const auto& extension : deviceExtensions)
{
requiredExtensions.erase(extension.extensionName);
}
return requiredExtensions.empty();
}

枚举设备扩展的个数,然后再获取具体扩展属性。这里的做了一个清空需要扩展属性的名字的处理。我们要设置的属性名存放在临时容器requiredExtensions中,如果全被删除,说明枚举设备扩展中包含了我们所设置的扩展属性,以此方式来判断是否支持此扩展。

bool CVulkanApp::isDeviceSuitable(VkPhysicalDevice dev)
{
VkPhysicalDeviceProperties deviceProperties;
VkPhysicalDeviceFeatures deviceFeatures;
vkGetPhysicalDeviceProperties(dev, &deviceProperties);
vkGetPhysicalDeviceFeatures(dev, &deviceFeatures);
bool deviceSupport =  checkDeviceExtensionSupport(dev);
QueueFamilyIndices indices = findQueueFamilies(dev);
return  deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU
&& deviceFeatures.geometryShader
&& indices.isComplete()
&& deviceSupport;
}

在设备判断是否支持的函数中增加物理设备是否支持交换链的判断。

VkDeviceCreateInfo createInfo{};
createInfo.enabledExtensionCount = static_cast(deviceExtensionNames.size());
createInfo.ppEnabledExtensionNames = deviceExtensionNames.data();

在创建逻辑设备的变量中,设置我们增加索要扩展的属性。仅检查交换链是否可用是不够的,因为它可能不可用。

我们基本上需要检查三种属性:

  • 基本表面功能(交换链中的最小/最大图像数,最小/最大值 图像的宽度和高度)

  • 表面格式(像素格式、色彩空间)

  • 可用的演示模式

    struct SwapChainSupportDetails
    {
    VkSurfaceCapabilitiesKHR capabilities;
    std::vector formats;
    std::vector presentModes;
    };
    SwapChainSupportDetails CVulkanApp::querySwapChainSupport(VkPhysicalDevice dev)
    {
    SwapChainSupportDetails details;
    vkGetPhysicalDeviceSurfaceCapabilitiesKHR(dev, surface_, &details.capabilities);
    uint32_t formatCount;
    vkGetPhysicalDeviceSurfaceFormatsKHR(dev, surface_, &formatCount, nullptr);
    if (formatCount != 0)
    {
    details.formats.resize(formatCount);
    vkGetPhysicalDeviceSurfaceFormatsKHR(dev, surface_, &formatCount, details.formats.data());
    }
    uint32_t presentModeCount;
    vkGetPhysicalDeviceSurfacePresentModesKHR(dev, surface_, &presentModeCount, nullptr);
    if (presentModeCount != 0)
    {
    details.presentModes.resize(presentModeCount);
    vkGetPhysicalDeviceSurfacePresentModesKHR(dev, surface_, &presentModeCount, details.presentModes.data());
    }
    return details;
    }

    以上代码就是获取三个属性信息用于Vulkan与窗口界面的支持。仅支持还不够,我们要从中选择合适的属性支持。

    VkSurfaceFormatKHR CVulkanApp::chooseSwapSurfaceFormat(const std::vector& formats)
    {
    for (const auto& format : formats)
    {
    if (format.format == VK_FORMAT_B8G8R8_SRGB &&
    format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
    {
    return format;
    }
    }
    return formats[0];
    }

    选取一种符合的标准颜色深度以及色彩空间SRGB非线性设置。

    VkPresentModeKHR CVulkanApp::chooseSwapPresentMode(const std::vector& presentModes)
    {
    for (const auto& mode : presentModes)
    {
    if (mode == VK_PRESENT_MODE_MAILBOX_KHR)
    {
    return mode;
    }
    }
    return VK_PRESENT_MODE_FIFO_KHR;
    }

    选择一种图片绘制到界面的模式 三缓冲 或 双缓冲处理。

    VkExtent2D  CVulkanApp::chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities)
    {
    if (capabilities.currentExtent.width != UINT32_MAX)
    {
    return capabilities.currentExtent;
    }
    int width, height;
    glfwGetFramebufferSize(window_, &width, &height);
    VkExtent2D actualExtent = {
    static_cast(width),
    static_cast(height)
    };
    actualExtent.width = glm::clamp(actualExtent.width,
    capabilities.minImageExtent.width,
    capabilities.maxImageExtent.width);
    actualExtent.height = glm::clamp(actualExtent.height,
    capabilities.minImageExtent.height,
    capabilities.maxImageExtent.height);
    return actualExtent;
    }

这个就是选择设备支持的与屏幕设置的尺寸大小的分辨率。比如(800 * 600)。高分屏就要设置设备相匹配的分辨率。

void CVulkanApp::createSwapChain()
{
assert(logicDevice_ != VK_NULL_HANDLE);
assert(surface_ != VK_NULL_HANDLE);
SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice_);
VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats);
VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes);
VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities);
uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;
if (swapChainSupport.capabilities.maxImageCount > 0
&& imageCount > swapChainSupport.capabilities.maxImageCount)
{
imageCount = swapChainSupport.capabilities.maxImageCount;
}
VkSwapchainCreateInfoKHR createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
createInfo.surface = surface_;
createInfo.minImageCount = imageCount;
createInfo.imageFormat = surfaceFormat.format;
createInfo.imageColorSpace = surfaceFormat.colorSpace;
createInfo.imageExtent = extent;
createInfo.imageArrayLayers = 1;
createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
QueueFamilyIndices indices = findQueueFamilies(physicalDevice_);
qFamily_.graphicsFamily = indices.graphicsFamily.value();
qFamily_.presentFamily = indices.presentFamily.value();
std::vector uniqueQueueFamilies = {
indices.graphicsFamily.value(),
indices.presentFamily.value()
};
if (indices.graphicsFamily != indices.presentFamily)
{//并发模式
createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
createInfo.queueFamilyIndexCount = static_cast(uniqueQueueFamilies.size());
createInfo.pQueueFamilyIndices = uniqueQueueFamilies.data();
}
else
{//独占模式
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
createInfo.queueFamilyIndexCount = 0;
createInfo.pQueueFamilyIndices = nullptr;
}
createInfo.preTransform = swapChainSupport.capabilities.currentTransform;
createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
createInfo.presentMode = presentMode;
createInfo.clipped = VK_TRUE;
createInfo.oldSwapchain = VK_NULL_HANDLE;
if (vkCreateSwapchainKHR(logicDevice_, &createInfo, nullptr, &swapChain_) != VK_SUCCESS)
{
throw std::runtime_error("failed to crate swap chain!");
}
vkGetSwapchainImagesKHR(logicDevice_, swapChain_, &imageCount, nullptr);
swapChainImages_.resize(imageCount);
vkGetSwapchainImagesKHR(logicDevice_, swapChain_, &imageCount, swapChainImages_.data());
swapChainImageFormat_ = surfaceFormat.format;
swapChainExtent_ = extent;
}

交换链设置的完整流程。

posted @ 2025-08-19 13:06  yjbjingcha  阅读(22)  评论(0)    收藏  举报