You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用C++ vulkan-hpp绑定创建Vulkan交换链时触发访问违例错误

问题描述

我正在使用C++ vulkan-hpp绑定学习Vulkan开发,在跟随Vulkan教程学习、参考RAII Samples理解基础逻辑、搭建可渲染三角形的简单应用过程中,创建交换链时遇到如下错误:

0xC0000005: Access violation executing location 0x0000000000000000

调试器显示错误触发于SwapchainKHR类的构造函数执行阶段,具体位于vulkan_raii头文件的第9337行。
同类错误通常由创建交换链所需的扩展缺失导致,但我在代码中已确认添加了对应扩展,且使用这些扩展完成了逻辑设备的初始化,开启验证层也未能捕获到该错误。以下是可复现该问题的完整示例代码:

#include <iostream>
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#include <vulkan/vulkan.hpp>
#include <vulkan/vulkan_raii.hpp>

vk::SurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<vk::SurfaceFormatKHR> &formatsList)
{
    for (vk::SurfaceFormatKHR const &availableFormat : formatsList)
    {
        if (availableFormat.format == vk::Format::eB8G8R8A8Srgb && availableFormat.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear)
            return availableFormat;
    }

    return formatsList.front();
}


vk::Extent2D chooseSwapExtent(uint32_t width, uint32_t height, const vk::SurfaceCapabilitiesKHR &surfaceCapabilities)
{
    vk::Extent2D swapchainExtent;
    if (surfaceCapabilities.currentExtent.width == std::numeric_limits<uint32_t>::max())
    {
        // If the surface size is undefined, the size is set to the size of the images requested.
        swapchainExtent.width = std::clamp(width, surfaceCapabilities.minImageExtent.width, surfaceCapabilities.maxImageExtent.width);
        swapchainExtent.height = std::clamp(height, surfaceCapabilities.minImageExtent.height, surfaceCapabilities.maxImageExtent.height);
    }
    else
    {
        // If the surface size is defined, the swap chain size must match
        swapchainExtent = surfaceCapabilities.currentExtent;
    }

    return swapchainExtent;
}

void findGraphicsAndPresentQueueFamilyIndices(std::vector<vk::QueueFamilyProperties> const &queueFamilyProperties,
                                                uint32_t &graphicsIndex, uint32_t &presentIndex,
                                                const vk::raii::PhysicalDevice &gpu,
                                                const vk::raii::SurfaceKHR &surface)
{
    auto graphicsQueueFamilyProperty =
        std::find_if(queueFamilyProperties.begin(),
                        queueFamilyProperties.end(),
                        [](vk::QueueFamilyProperties const &qfp)
                        { return qfp.queueFlags & vk::QueueFlagBits::eGraphics; });
    assert(graphicsQueueFamilyProperty != queueFamilyProperties.end());
    uint32_t graphicsQueueFamilyIndex = static_cast<uint32_t>(std::distance(queueFamilyProperties.begin(), graphicsQueueFamilyProperty));
    graphicsIndex = graphicsQueueFamilyIndex;
    // get present family index
    uint32_t presentQueueFamilyIndex = gpu.getSurfaceSupportKHR(graphicsIndex, *surface) ? graphicsIndex : static_cast<uint32_t>(queueFamilyProperties.size());

    if (presentQueueFamilyIndex == queueFamilyProperties.size())
    {
        // the graphicsQueueFamilyIndex doesn't support present -> look for an other family index that supports both
        // graphics and present
        for (size_t i = 0; i < queueFamilyProperties.size(); i++)
        {
            if ((queueFamilyProperties[i].queueFlags & vk::QueueFlagBits::eGraphics) &&
                gpu.getSurfaceSupportKHR(static_cast<uint32_t>(i), *surface))
            {
                graphicsQueueFamilyIndex = static_cast<uint32_t>(i);
                presentQueueFamilyIndex = graphicsIndex;
                break;
            }
        }
    }

    if (presentQueueFamilyIndex == queueFamilyProperties.size())
    {
        // there's nothing like a single family index that supports both graphics and present -> look for an other
        // family index that supports present
        for (size_t i = 0; i < queueFamilyProperties.size(); i++)
        {
            if (gpu.getSurfaceSupportKHR(static_cast<uint32_t>(i), *surface))
            {
                presentQueueFamilyIndex = static_cast<uint32_t>(i);
                break;
            }
        }
    }

    if ((graphicsQueueFamilyIndex == queueFamilyProperties.size()) || (presentQueueFamilyIndex == queueFamilyProperties.size()))
    {
        throw std::runtime_error("Could not find a queue for graphics or present -> terminating");
    }

    graphicsIndex = graphicsQueueFamilyIndex;
    presentIndex = presentQueueFamilyIndex;
}

int main()
{
    glfwInit();
    glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
    glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
    auto window = glfwCreateWindow(800, 600, "Vulkan", nullptr, nullptr);

    vk::raii::Context raiContext;

    // get all extensions required by glfw
    uint32_t glfwExtensionCount = 0;
    const char **glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);

    std::vector<char const *> instanceExtensionNames(glfwExtensions, glfwExtensions + glfwExtensionCount);
    instanceExtensionNames.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);

    std::vector<char const *> deviceExtensionNames;
    deviceExtensionNames.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);

    // create instance
    vk::ApplicationInfo appInfo("VulkanTest", 1, "Vulkan", VK_API_VERSION_1_1);
    vk::InstanceCreateInfo instanceCreateInfo({}, &appInfo, {}, instanceExtensionNames);
    
    auto instance = vk::raii::Instance(raiContext, instanceCreateInfo);
    // create window surface
    VkSurfaceKHR _surface;
    if (glfwCreateWindowSurface(static_cast<VkInstance>(*instance), window, nullptr, &_surface) != VK_SUCCESS)
        throw std::runtime_error("Failed to create window surface!");
    
    vk::raii::SurfaceKHR surface (instance, _surface);

    // select physical device
    auto gpu = vk::raii::PhysicalDevice(std::move(instance.enumeratePhysicalDevices().front()));
    
    // get graphics and present index
    auto queueFamilies = gpu.getQueueFamilyProperties();
    uint32_t graphicsIndex = -1;
    uint32_t presentIndex = -1;
    
    findGraphicsAndPresentQueueFamilyIndices(queueFamilies, graphicsIndex, presentIndex, gpu, surface);
    
    if(graphicsIndex < 0 || presentIndex < 0)
        throw std::runtime_error("Cannot find a valid graphics or present queue");

    // create logical device
    float queuePriority = 0.0f;
    auto deviceQueueCreateInfo = vk::DeviceQueueCreateInfo(vk::DeviceQueueCreateFlags(), static_cast<uint32_t>(graphicsIndex), 1, &queuePriority);
    vk::DeviceCreateInfo deviceCreateInfo(vk::DeviceCreateFlags(), deviceQueueCreateInfo, {}, deviceExtensionNames, nullptr);
    auto device = gpu.createDevice(vk::DeviceCreateInfo(vk::DeviceCreateFlags(), deviceQueueCreateInfo));

    // create swap chain
    std::vector<vk::SurfaceFormatKHR> formats = gpu.getSurfaceFormatsKHR(*surface);
    assert(!formats.empty());
    int windowWidth, windowHeight;
    glfwGetFramebufferSize(window, &windowWidth, &windowHeight);
    vk::SurfaceCapabilitiesKHR surfaceCapabilities = gpu.getSurfaceCapabilitiesKHR(*surface);
    vk::Extent2D swapChainExtent = chooseSwapExtent(windowWidth, windowHeight, surfaceCapabilities);
    vk::SurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(formats);
    vk::PresentModeKHR presentMode = vk::PresentModeKHR::eFifo;

    uint32_t imageCount = surfaceCapabilities.minImageCount + 1;

    if (surfaceCapabilities.maxImageCount > 0 && imageCount > surfaceCapabilities.maxImageCount)
    {
        imageCount = surfaceCapabilities.maxImageCount;
    }

    // not using constructor for clarity
    vk::SwapchainCreateInfoKHR swapChainCreateInfo;
    swapChainCreateInfo.setFlags(vk::SwapchainCreateFlagsKHR());
    swapChainCreateInfo.setSurface(*surface);
    swapChainCreateInfo.setMinImageCount(imageCount);
    swapChainCreateInfo.setImageFormat(surfaceFormat.format);
    swapChainCreateInfo.setImageColorSpace(surfaceFormat.colorSpace);
    swapChainCreateInfo.setImageExtent(swapChainExtent);
    swapChainCreateInfo.setImageArrayLayers(1);
    swapChainCreateInfo.setImageUsage(vk::ImageUsageFlagBits::eColorAttachment);
    swapChainCreateInfo.setImageSharingMode(vk::SharingMode::eExclusive);
    swapChainCreateInfo.setPreTransform(surfaceCapabilities.currentTransform);
    swapChainCreateInfo.setCompositeAlpha(vk::CompositeAlphaFlagBitsKHR::eOpaque);
    swapChainCreateInfo.setPresentMode(presentMode);
    swapChainCreateInfo.setClipped(true);

    vk::raii::SwapchainKHR swapChain( device, swapChainCreateInfo );
    
    while (!glfwWindowShouldClose(window))
    {
        glfwPollEvents();
    }
}
错误原因

崩溃的核心原因是逻辑设备创建时未启用交换链扩展:
代码中虽然正确定义了包含VK_KHR_SWAPCHAIN_EXTENSION_NAME的deviceCreateInfo,但实际调用gpu.createDevice时传入的是临时构造的vk::DeviceCreateInfo对象,这个临时对象没有传入设备扩展列表,导致创建出的逻辑设备根本没有加载交换链相关函数,vkCreateSwapchainKHR函数指针为空,调用时直接跳转到0地址触发访问违例,这也是验证层无法捕获错误的原因——崩溃发生在进入Vulkan驱动校验逻辑之前。

代码中另外存在两个隐藏问题:

  • 用uint32_t类型存储队列索引时初始化为-1,后续判断graphicsIndex < 0永远不会成立,因为无符号整数不可能小于0,索引校验逻辑完全失效。
  • 交换链创建时固定使用vk::SharingMode::eExclusive模式,当图形队列和呈现队列不属于同一个队列族时,没有传入对应的队列索引列表,后续会出现呈现错误。
修复方法
  • 修改逻辑设备创建代码,直接使用提前配置好扩展的deviceCreateInfo,替换原有临时构造的创建信息:
    // 原错误代码:auto device = gpu.createDevice(vk::DeviceCreateInfo(vk::DeviceCreateFlags(), deviceQueueCreateInfo));
    auto device = gpu.createDevice(deviceCreateInfo);
    
  • 修正队列索引的初始化和校验逻辑,将索引起始值设为std::numeric_limits<uint32_t>::max(),通过判断索引是否等于该值确认队列是否查找成功。
  • 补充交换链的队列共享模式配置:如果图形队列索引和呈现队列索引不一致,将imageSharingMode改为vk::SharingMode::eConcurrent,并传入两个队列族的索引。

内容的提问来源于stack exchange,提问作者Resident-Rock6028

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 10:18:22