为什么vkCreateSwapchainKHR会在0地址处导致访问冲突?

11
我正试着通过遵循vulkan-tutorial.com的优秀教程学习Vulkan,但是在必须创建交换链的时候,我遇到了一些麻烦。如标题所述,vkCreateSwapchainKHR创建了以下错误:Access violation executing location 0x0000000000000000
该教程暗示这可能与Steam浮层发生冲突。但这对我不适用,因为从教程中复制整个代码可以工作。
我正在努力找出我的代码出了什么问题,并学习如何调试此类问题,因为将来我将没有参考代码。涉嫌的代码行如下:
if (vkCreateSwapchainKHR(device, &swapChainCreateInfo, nullptr, &swapChain) != VK_SUCCESS) {
    throw std::runtime_error("Could not create swap chain");
}

我在这行设置了一个断点,以比较我的代码中参数的值与参考代码中的值是否相同。就我所知,没有区别。(当然地址是不同的)。

我应该在哪里查找代码问题?变量swapChain如预期一样为空。错误形式的swapChainCreateInfo不应导致vkCreateSwapchainKHR崩溃。它只会返回一个不是VK_SUCCESS的东西。而设备也已经成功创建:

if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {
    throw std::runtime_error("Failed to create logical device");
}

编辑 - 我正在使用验证层VK_LAYER_LUNARG_standard_validation,我的createInfo设置如下。

// Useful functions and structures
VkPhysicalDevice physicalDevice;
VkSurfaceKHR surface;
VkSwapchainKHR swapChain;

struct QueueFamilyIndices {
    std::optional<uint32_t> graphicsFamily;
    std::optional<uint32_t> presentationFamily;
    bool isComplete() {
        return graphicsFamily.has_value() && presentationFamily.has_value();
    }
};

struct SwapChainSupportDetails {
    VkSurfaceCapabilitiesKHR surfaceCapabilities;
    std::vector<VkSurfaceFormatKHR> formats;
    std::vector<VkPresentModeKHR> presentModes;
};

SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice physicalDevice) {
    SwapChainSupportDetails swapChainSupportDetails;

    vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &swapChainSupportDetails.surfaceCapabilities);

    uint32_t formatCount = 0;
    vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, nullptr);
    if (formatCount != 0) {
        swapChainSupportDetails.formats.resize(formatCount);
        vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, swapChainSupportDetails.formats.data());
    }

    uint32_t presentModeCount = 0;
    vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, nullptr);
    if (presentModeCount != 0) {
        swapChainSupportDetails.presentModes.resize(presentModeCount);
        vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, swapChainSupportDetails.presentModes.data());
    }

    return swapChainSupportDetails;
}

VkSurfaceFormatKHR chooseSwapChainSurfaceFormat(const std::vector<VkSurfaceFormatKHR> & availableFormats) {
    if (availableFormats.size() == 1 && availableFormats[0].format == VK_FORMAT_UNDEFINED) {
        return { VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR };
    }

    for (const auto & availableFormat : availableFormats) {
        if (availableFormat.format == VK_FORMAT_B8G8R8A8_UNORM && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
            return availableFormat;
        }
    }

    return availableFormats[0];
}

VkPresentModeKHR chooseSwapChainPresentMode(const std::vector<VkPresentModeKHR> & availablePresentModes) {

    VkPresentModeKHR bestMode = VK_PRESENT_MODE_FIFO_KHR;
    for (const auto & availablePresentMode : availablePresentModes) {
        if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) {
            return availablePresentMode;
        }
        else if (availablePresentMode == VK_PRESENT_MODE_IMMEDIATE_KHR) {
            bestMode = availablePresentMode;
        }
    }

    return bestMode;
}

VkExtent2D chooseSwapChainExtent2D(const VkSurfaceCapabilitiesKHR & surfaceCapabilities) {
    if (surfaceCapabilities.currentExtent.width != std::numeric_limits<uint32_t>::max()) {
        return surfaceCapabilities.currentExtent;
    }
    else {
        VkExtent2D actualExtent = { WIDTH, HEIGHT };
        actualExtent.width = std::max(std::min(surfaceCapabilities.maxImageExtent.width, actualExtent.width), surfaceCapabilities.minImageExtent.width);
        actualExtent.height = std::max(std::min(surfaceCapabilities.maxImageExtent.height, actualExtent.height), surfaceCapabilities.minImageExtent.height);
        return actualExtent;
    }
}

// Swap Chain creation code

SwapChainSupportDetails swapChainSupportDetails = querySwapChainSupport(physicalDevice);

VkSurfaceFormatKHR surfaceFormat = chooseSwapChainSurfaceFormat(swapChainSupportDetails.formats);
VkPresentModeKHR presentMode = chooseSwapChainPresentMode(swapChainSupportDetails.presentModes);
VkExtent2D extent = chooseSwapChainExtent2D(swapChainSupportDetails.surfaceCapabilities);
uint32_t imageCount = swapChainSupportDetails.surfaceCapabilities.minImageCount + 1;
if (swapChainSupportDetails.surfaceCapabilities.maxImageCount > 0 && imageCount > swapChainSupportDetails.surfaceCapabilities.maxImageCount) {
    imageCount = swapChainSupportDetails.surfaceCapabilities.minImageCount;
}

VkSwapchainCreateInfoKHR swapChainCreateInfo = {};
swapChainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
swapChainCreateInfo.surface = surface;
swapChainCreateInfo.minImageCount = imageCount;
swapChainCreateInfo.imageFormat = surfaceFormat.format;
swapChainCreateInfo.imageColorSpace = surfaceFormat.colorSpace;
swapChainCreateInfo.imageExtent = extent;
swapChainCreateInfo.imageArrayLayers = 1;
swapChainCreateInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;

QueueFamilyIndices familyIndices = findQueueFamilies(physicalDevice);
uint32_t queueFamilyIndices[] = { familyIndices.graphicsFamily.value(), familyIndices.presentationFamily.value() };
if (familyIndices.graphicsFamily != familyIndices.presentationFamily) {
    swapChainCreateInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
    swapChainCreateInfo.queueFamilyIndexCount = 2;
    swapChainCreateInfo.pQueueFamilyIndices = queueFamilyIndices;
}
else {
    swapChainCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
    swapChainCreateInfo.queueFamilyIndexCount = 0;
    swapChainCreateInfo.pQueueFamilyIndices = nullptr;
}

swapChainCreateInfo.preTransform = swapChainSupportDetails.surfaceCapabilities.currentTransform;
swapChainCreateInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
swapChainCreateInfo.presentMode = presentMode;
swapChainCreateInfo.clipped = VK_TRUE;
swapChainCreateInfo.oldSwapchain = VK_NULL_HANDLE;

if (vkCreateSwapchainKHR(device, &swapChainCreateInfo, nullptr, &swapChain) != VK_SUCCESS) {
    throw std::runtime_error("Could not create swap chain");
}
我得到了以下结构:enter image description here

你验证过你的加载器成功加载vkCreateSwapchainKHR函数指针而不是nullptr了吗? - genpfault
不,我没有在代码中检查过那个。不过我用了我的断点,地址不是 nullptr。谢谢你的回答! - François Guthmann
2
一个错误的swapChainCreateInfo不应该使vkCreateSwapchainKHR崩溃。它只会返回一个不是VK_SUCCESS的东西。但这并不是真的。在Vulkan中,大多数情况下,如果您提供无效的输入(规范定义了什么是有效的),行为是未定义的,未定义的行为可能包括崩溃。您应该使用验证层,以便在崩溃之前获得解释错误的消息。如果您在此处发布VkSwapchainCreateInfo内容,我们可能能够为您找到问题。 - Jesse Hall
感谢您关于“swapChainCreateInfo”的更正!我正在使用VK_LAYER_LUNARG_standard_validation验证层,但我还不确定它如何操作。此外,我已更新帖子,并进行了我的VkSwapchainCreateInfo设置。如果您需要更多信息,请告诉我,谢谢 :) - François Guthmann
3个回答

11

创建逻辑设备时,需要将enabledExtensionCount设置为所需扩展的实际数量,而不是0,如果希望扩展正常工作。在我的情况下,这只是一个简单的编辑错误。这是我代码中的亮点:

createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
createInfo.ppEnabledExtensionNames = deviceExtensions.data();

createInfo.enabledExtensionCount = 0;

我通过将代码中的每个函数替换为参考代码中的函数,直到代码可以工作,才解决了这个问题。我有点失望验证层没有发现这个问题。是我的设置出了问题吗?这是他们应该捕捉到的吗?

编辑:如LIANG LIU所指出,这是deviceExtensions的初始化:

const std::vector<const char*> deviceExtensions = {
    VK_KHR_SWAPCHAIN_EXTENSION_NAME
};

1
vkCreateSwapchainKHR()函数是由扩展引入的。如果在逻辑设备创建期间未启用该扩展,则无法使用此函数。因此,这绝对应该被验证层捕获。我相信它们确实会捕获这样的问题。检查验证层是否正常工作的最简单方法是启用“api dump”层,并查看Vulkan函数调用是否正确列出。 - Ekzuzy
我之前不知道这个,谢谢!正如Cerulean Quasar在下面的回答中指出的那样,验证层本身需要一个扩展。因此,通过将计数设置为0,我实际上禁用了验证层。 - François Guthmann
这就是为什么我不会通过代码启用验证层,而是通过环境变量来进行操作,这样就避免了对代码的更改。可能并非所有的验证层都可以通过这种方式启用,这种解决方案也许不够灵活,但在大多数情况下是足够的,而且可以轻松地捕获此类错误。 - Ekzuzy
情节反转,VK_EXT_DEBUG_UTILS_EXTENSION_NAME是一个实例级扩展,而不是逻辑设备扩展。这意味着它已经为实例启用,并且验证层应该捕获到了我的错误:/。 我尝试启用VK_LAYER_LUNARG_api_dump验证层,确实在控制台上打印了很多信息。这必须意味着我的验证层起作用了。只是它们似乎没有捕捉到这个特定的错误! - François Guthmann
2
目前至少有8个人似乎遇到了完全相同的问题,因此看起来他们应该着手解决这个问题。 - geometrian
1
感谢您发布这个问题和解决方案。我刚好遇到了完全相同的问题,根本原因也是一样的。验证层没有提供任何有用的信息。 - Tim Kane

4

在创建VkDevice时启用VK_KHR_SWAPCHAIN_EXTENSION_NAME

void VKRenderer::createVkLogicalDevice()
{
    // device extensions
    vector<const char*>::type deviceExtensionNames = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };

    // priorities
    float queuePrioritys[2] = { 1.f, 1.f};

    // graphics queue
    VkDeviceQueueCreateInfo queueCreateInfos;
    queueCreateInfos.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
    queueCreateInfos.pNext = nullptr;
    queueCreateInfos.queueFamilyIndex = getGraphicsQueueFamilyIndex();
    queueCreateInfos.queueCount = 1;
    queueCreateInfos.pQueuePriorities = &queuePrioritys[0];

    // device features
    VkPhysicalDeviceFeatures deviceFeatures = {};

    VkDeviceCreateInfo createInfo = {};
    createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
    createInfo.pNext = nullptr;
    createInfo.pQueueCreateInfos = &queueCreateInfos;
    createInfo.queueCreateInfoCount = 1;
    createInfo.pEnabledFeatures = &deviceFeatures;
    createInfo.enabledExtensionCount = deviceExtensionNames.size();
    createInfo.ppEnabledExtensionNames = deviceExtensionNames.data();

    // create logical device and retrieve graphics queue
    if (VK_SUCCESS == vkCreateDevice(m_vkPhysicalDevice, &createInfo, nullptr, &m_vkDevice))
    {
        vkGetDeviceQueue(m_vkDevice, getGraphicsQueueFamilyIndex(), 0, &m_vkGraphicsQueue);
        vkGetDeviceQueue(m_vkDevice, getPresentQueueFamilyIndex(), 0, &m_vkPresentQueue);
    }
    else
    {
        EchoLogError("Failed to create vulkan logical device!");
    }
}

1
是的,正如您在我的上面答案中所看到的,我已经做到了这一点。不幸的是,我还将扩展计数设置为0,从而禁用了此扩展。后来有人告诉我,这应该被验证层捕获,但没有人添加该特定检查。 - François Guthmann
我知道,我回答这个问题只是因为我在你的回答中找不到“ppEnabledExtensionNames”的值。 - LIANG LIU
哦,没错,好主意!我也编辑了我的答案并给了你信用。 - François Guthmann

0

看起来你在代码段末尾调用了vkCreateDevice来创建交换链,并将VkSwapchainCreateInfo传递给它。也许你想调用vkCreateSwapchainKHR,像这样:

if (vkCreateSwapchainKHR(device, &swapChainCreateInfo, nullptr, &swapChain) !=
    VK_SUCCESS) {
    throw std::runtime_error("failed to create swap chain");
}

如果你真的在调用vkCreateSwapchainKHR,请编辑你的问题以指明这一点。


是的,我实际上正在调用 vkCreateSwapchainKHR,对此感到抱歉。我已经编辑了问题中的代码 :) - François Guthmann
1
我没有收到任何消息,只是崩溃了。 我知道验证层是有效的。在尝试设置交换链之前,我有一个正常运行的程序。在这个程序中,我试图“忘记”删除设备,然后我从验证层得到了一条消息,指出存在内存泄漏。这有意义吗?还是我漏掉了什么? - François Guthmann
正如您所看到的,我解决了我的问题,但我一定会尝试这个! - François Guthmann
不好,验证层没有被触发 :/。我看到的唯一消息是“添加了信使”。我尝试在参考代码中重新引入我的错误,结果相同。崩溃而没有任何错误消息,这不太好 :/。 - François Guthmann
情节反转,VK_EXT_DEBUG_UTILS_EXTENSION_NAME是一个实例级扩展,而不是逻辑设备扩展。这意味着它已经为实例启用,并且验证层应该捕获到我的错误 :/. - François Guthmann
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接