验证错误:必须启用 VK_KHR_portability_subset,因为物理设备 VkPhysicalDevice

问题描述 投票:0回答:1

大家好,有错误

此错误消息表明创建 Vulkan 设备时必须启用 VK_KHR_portability_subset 扩展。 macOS 上的 Vulkan 实现(使用 MoltenVK)似乎需要此扩展才能实现正确的功能。

在我的 Vulkan 应用程序中,我尝试在不启用此扩展的情况下创建逻辑设备,这可能会导致错误。我怀疑在设置 VkDeviceCreateInfo 结构时需要将 VK_KHR_portability_subset 包含在所需设备扩展列表中。

请求帮助:

有人可以帮助我理解为什么会发生此错误以及如何在 macOS 上的 Vulkan 应用程序中正确启用 VK_KHR_portability_subset 扩展吗?任何有关解决此验证错误的指导将不胜感激。

validation layer: Validation Error: \[ VUID-VkDeviceCreateInfo-pProperties-04451 \] Object 0: handle = 0x600002e50360, type = VK_OBJECT_TYPE_PHYSICAL_DEVICE; | MessageID = 0x3a3b6ca0 | vkCreateDevice():  VK_KHR_portability_subset must be enabled because physical device VkPhysicalDevice 0x600002e50360\[

我可以寻求帮助吗 谢谢


#ifndef WINDOW_H
#define WINDOW_H

#define GLFW_INCLUDE_VULKAN

#include <GLFW/glfw3.h>
#include <iostream>
#include<vector>
#include<optional>

#ifdef NDEBUG
const bool enableValidationLayers = false;
#else
const bool enableValidationLayers = true;
#endif


namespace Application {

    struct QueueFamilyIndices {
        std::optional<uint32_t> graphicsFamily;

        std::optional<uint32_t> presentFamily;

        bool isComplete() {
            return graphicsFamily.has_value();
        }
    };


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


    class Window {
    public:
        void run();

    private:
        GLFWwindow *window;
        uint32_t WIDTH = 800;
        uint32_t HEIGHT = 600;
        VkInstance instance;
        VkDebugUtilsMessengerEXT debugMessenger;
        const std::vector<const char *> validationLayers = {
            "VK_LAYER_KHRONOS_validation"
        };


        void initWindow();

        void initVulkan();

        void mainLoop() const;

        void cleanup() const;

        void setupDebugMessenger();


        std::vector<const char *> getRequiredExtensions(bool includePortabilityExtension = true);

        void extensionProperties() const;


        bool checkValidationLayerSupport();


        static void mouse_button_callback(GLFWwindow *window, int button, int action, int mods);


        void createInstance();


        VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;



        QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device);

        void pickPhysicalDevice();



        bool isDeviceSuitable(const VkPhysicalDevice &device, int &score);


        int rateDeviceSuitability(VkPhysicalDevice devices);


        VkDevice device;

        VkQueue graphicsQueue;


        void createLogicalDevice();


        VkSurfaceKHR surface;

        void createSurface();


        VkQueue presentQueue;


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


        bool checkDeviceExtensionSupport(VkPhysicalDevice device);




        SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device);
    };
}

#endif // WINDOW_HPP
//
// Created by Abdulkafi on 03/10/2024 A.
//
#include "Window.hpp"
#include <iostream>
#include <stdexcept>
#include <set>



namespace Application {
    static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
        VkDebugUtilsMessageSeverityFlagBitsEXT meesageSeverity,
        VkDebugUtilsMessageTypeFlagsEXT messageType,
        const VkDebugUtilsMessengerCallbackDataEXT *pCallbackDate,
        void *pUserData) {
        std::cerr << "validation layer: " << pCallbackDate->pMessage << std::endl;
        return VK_FALSE;
    }

    VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT *pCreateInfo,
                                          const VkAllocationCallbacks *pAllocator,
                                          VkDebugUtilsMessengerEXT *pDebugMessenger) {
        auto func = (PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance,
                                                                               "vkCreateDebugUtilsMessengerEXT");
        if (func != nullptr) {
            return func(instance, pCreateInfo, pAllocator, pDebugMessenger);
        } else {
            return VK_ERROR_EXTENSION_NOT_PRESENT;
        }
    }

    void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger,
                                       const VkAllocationCallbacks *pAllocator) {
        auto func = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance,
            "vkDestroyDebugUtilsMessengerEXT");
        if (func != nullptr) {
            func(instance, debugMessenger, pAllocator);
        }
    }

    void populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT &createInfo) {
        createInfo = {};
        createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
        createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
                                     VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
                                     VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
        createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
                                 VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
                                 VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
        createInfo.pfnUserCallback = debugCallback;
    }

    void Window::setupDebugMessenger() {
        if (!enableValidationLayers) return;

        VkDebugUtilsMessengerCreateInfoEXT createInfoExt{};
        populateDebugMessengerCreateInfo(createInfoExt);

        if (CreateDebugUtilsMessengerEXT(instance, &createInfoExt, nullptr, &debugMessenger) != VK_SUCCESS) {
            throw std::runtime_error("Failed to set up debug messenger!");
        }
    }


    void Window::run() {
        initWindow();
        initVulkan();
        mainLoop();
        cleanup();
    }

    void Window::initWindow() {
        if (!glfwInit()) {
            throw std::runtime_error("Failed to initialize GLFW");
        }

        glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
        glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);


        GLFWmonitor *primaryMonitor = glfwGetPrimaryMonitor();
        if (!primaryMonitor) {
            glfwTerminate();
            throw std::runtime_error("Failed to get primary monitor");
        }


        const GLFWvidmode *videoMode = glfwGetVideoMode(primaryMonitor);
        if (videoMode) {
            std::cout << "Screen Width: " << videoMode->width << std::endl;
            std::cout << "Screen Height: " << videoMode->height << std::endl;
            std::cout << "Refresh Rate: " << videoMode->refreshRate << " Hz" << std::endl;
        }


        WIDTH = videoMode->width;
        HEIGHT = videoMode->height;


        window = glfwCreateWindow(WIDTH / 2, HEIGHT / 2, "Abdulkafi", nullptr, nullptr);
        if (!window) {
            glfwTerminate();
            throw std::runtime_error("Failed to create GLFW window");
        }



        glfwMakeContextCurrent(window);


        glfwSetMouseButtonCallback(window, mouse_button_callback);
    }

    void Window::initVulkan() {
        createInstance();
        setupDebugMessenger();
        createSurface();
        pickPhysicalDevice();
        createLogicalDevice();
    }


    void Window::mouse_button_callback(GLFWwindow *window, int button, int action, int mods) {
        if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS) {
            std::cout << "mouse left!" << std::endl;
        } else if (button == GLFW_MOUSE_BUTTON_RIGHT && action == GLFW_PRESS) {
            std::cout << "mouse right!" << std::endl;
        }
    }

    void Window::mainLoop() const {
        while (!glfwWindowShouldClose(window)) {
            glfwPollEvents();
        }
    }

    void Window::cleanup() const {
        vkDestroyDevice(device, nullptr);

        if (enableValidationLayers) {
            DestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);
        }
        vkDestroySurfaceKHR(instance, surface, nullptr);
        vkDestroyInstance(instance, nullptr);
        glfwDestroyWindow(window);
        glfwTerminate();
    }


    void Window::createInstance() {
        if (enableValidationLayers && !checkValidationLayerSupport()) {
            throw std::runtime_error("validation layers requested, but not available!");
        }

        VkApplicationInfo appInfo{};
        appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
        appInfo.pApplicationName = "Abdulkafi BK Game";
        appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
        appInfo.pEngineName = "no Engine";
        appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
        appInfo.apiVersion = VK_MAKE_VERSION(1, 0, 0);


        VkInstanceCreateInfo instanceCreateInfo{};
        instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
        instanceCreateInfo.flags = 0;
        instanceCreateInfo.pApplicationInfo = &appInfo;

        instanceCreateInfo.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
        std::vector<const char *> requiredextensions = getRequiredExtensions();


        instanceCreateInfo.enabledExtensionCount = static_cast<uint32_t>(requiredextensions.size());
        instanceCreateInfo.ppEnabledExtensionNames = requiredextensions.data();

        VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo{};
        if (enableValidationLayers) {
            instanceCreateInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
            instanceCreateInfo.ppEnabledLayerNames = validationLayers.data();
            populateDebugMessengerCreateInfo(debugCreateInfo);
            instanceCreateInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT *) &debugCreateInfo;
        } else {
            instanceCreateInfo.enabledLayerCount = 0;
            instanceCreateInfo.pNext = nullptr;
        }


        if (vkCreateInstance(&instanceCreateInfo, nullptr, &instance) != VK_SUCCESS) {
            throw std::runtime_error("Failed to create instance");
        }
#ifdef DEBUG_MODE
        std::cout << "VkInstance created successfully. Handle value: " << reinterpret_cast<uintptr_t>(instance)
                << std::endl;
        extensionProperties();
#endif
    }


    void Window::extensionProperties() const {

        uint32_t extensionCount = 0;
        VkResult result = vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr);
        if (result != VK_SUCCESS) {
            throw std::runtime_error("Failed to enumerate instance extension properties.");
        }


        std::vector<VkExtensionProperties> extensions(extensionCount);
        result = vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data());
        if (result != VK_SUCCESS) {
            throw std::runtime_error("Failed to enumerate instance extension properties.");
        }


        std::cout << "Available extensions: " << extensionCount << std::endl;
        for (const auto &extension: extensions) {
            std::cout << "  Name: " << extension.extensionName
                    << " | Version: " << extension.specVersion << std::endl;
        }
    }

    std::vector<const char *> Window::getRequiredExtensions(bool includePortabilityExtension) {

        uint32_t glfwExtensionCount = 0;
        const char **glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
        if (!glfwExtensions) {
            throw std::runtime_error("Failed to get required GLFW extensions");
        }


        std::set<const char *> requiredExtensionsSet(glfwExtensions, glfwExtensions + glfwExtensionCount);


        if (includePortabilityExtension) {
            requiredExtensionsSet.insert(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
        }


        std::vector<const char *> requiredExtensions(requiredExtensionsSet.begin(), requiredExtensionsSet.end());
        if (enableValidationLayers) {
            requiredExtensions.emplace_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
        }
#ifdef DEBUG_MODE

        std::cout << "Required extensions count: " << requiredExtensions.size() << std::endl;
        for (const auto &extension: requiredExtensions) {
            std::cout << extension << std::endl;
        }
#endif

        return requiredExtensions;
    }

    bool Window::checkValidationLayerSupport() {
        uint32_t layerCount;
        vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
        std::vector<VkLayerProperties> availableLayers(layerCount);
        vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
        for (const char *layerName: validationLayers) {
            bool layerFound = false;
            for (const auto &layerProperties: availableLayers) {
                if (strcmp(layerName, layerProperties.layerName) == 0) {
                    layerFound = true;
                    break;
                }
            }
            if (!layerFound) {
                return false;
            }
        }
        return true;
    }



    bool Window::isDeviceSuitable(const VkPhysicalDevice &device, int &score) {
        VkPhysicalDeviceProperties deviceProperties;
        VkPhysicalDeviceFeatures deviceFeatures;


        vkGetPhysicalDeviceProperties(device, &deviceProperties);
        vkGetPhysicalDeviceFeatures(device, &deviceFeatures);


        score = 0;


        if (deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) {
            score += 1000; 
        } else if (deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU) {
            score += 500; 
        }


        score += deviceProperties.limits.maxImageDimension2D / 1000;


        if (deviceFeatures.geometryShader) {
            score += 100; 
        }


        return score > 0;
    }


    QueueFamilyIndices Window::findQueueFamilies(VkPhysicalDevice device) {
        QueueFamilyIndices indices;

        uint32_t queueFamilyCount = 0;
        vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);

        if (queueFamilyCount == 0) {
            return indices;
        }

        std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
        vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());

        int i = 0;
        for (const auto &queueFamily: queueFamilies) {
            if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
                indices.graphicsFamily = i;
            }


            VkBool32 presentSupport = false;
            vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);
            if (presentSupport) {
                indices.presentFamily = i;
            }

            if (indices.isComplete()) {
                break;
            }
            i++;
        }

        return indices;
    }

    void Window::pickPhysicalDevice() {
        uint32_t deviceCount = 0;

        vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
        if (deviceCount == 0) {
            throw std::runtime_error("coldn't get physical device");
        }

        std::vector<VkPhysicalDevice> devices(deviceCount);
        vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
        std::cout << "found device " << deviceCount << " physical" << std::endl;

        int highestScore = -1;
        VkPhysicalDevice bestDevice = VK_NULL_HANDLE;


        for (const auto &device: devices) {
            int currentScore = 0;


            if (isDeviceSuitable(device, currentScore)) {
                QueueFamilyIndices indices = findQueueFamilies(device);


                if (indices.isComplete() && currentScore > highestScore) {
                    highestScore = currentScore;
                    bestDevice = device;
                }
            }
        }


        if (bestDevice != VK_NULL_HANDLE) {
            physicalDevice = bestDevice;
            VkPhysicalDeviceProperties bestProperties{};
            vkGetPhysicalDeviceProperties(bestDevice, &bestProperties);
            std::cout << "chose device : " << bestProperties.deviceName << std::endl;
        } else {
            throw std::runtime_error("faild got device physical");
        }
    }

    void Window::createLogicalDevice() {

        QueueFamilyIndices indices = findQueueFamilies(physicalDevice);

        std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
        std::set<uint32_t> uniqueQueueFamilies = {
            indices.graphicsFamily.value(),
            indices.presentFamily.value()
        };

        float queuePriority = 1.0f;
        for (uint32_t queueFamily: uniqueQueueFamilies) {
            VkDeviceQueueCreateInfo queueCreateInfo{};
            queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
            queueCreateInfo.queueFamilyIndex = queueFamily;
            queueCreateInfo.queueCount = 1;
            queueCreateInfo.pQueuePriorities = &queuePriority;
            queueCreateInfos.push_back(queueCreateInfo);
        }


        VkPhysicalDeviceFeatures deviceFeatures{};


        VkDeviceCreateInfo createInfo{};
        createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
        createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());

        createInfo.pQueueCreateInfos = queueCreateInfos.data();
        createInfo.pEnabledFeatures = &deviceFeatures;


        createInfo.enabledExtensionCount = 0;

        if (enableValidationLayers) {
            createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
            createInfo.ppEnabledLayerNames = validationLayers.data();
        } else {
            createInfo.enabledLayerCount = 0;
        }
        if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {
            throw std::runtime_error("failed to create logical device");
        }
        int deviceValue = (device != VK_NULL_HANDLE) ? reinterpret_cast<uintptr_t>(device) : 0;
        std::cout << "number device : " << deviceValue << std::endl;


        vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue);
        vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue);
    }


    void Window::createSurface() {
        if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) {
            throw std::runtime_error("failed to create window surface!");
        }
    }



    bool Window::checkDeviceExtensionSupport(VkPhysicalDevice device) {
        uint32_t extensionCount;
        vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr);

        std::vector<VkExtensionProperties> availableExtensions(extensionCount);
        vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data());
        std::set<std::string> requiredExtensions(deviceExtensions.begin(), deviceExtensions.end());
        for (const auto &extension: availableExtensions) {
            requiredExtensions.erase(extension.extensionName);
        }
        return requiredExtensions.empty();
    }



    SwapChainSupportDetails Window::querySwapChainSupport(VkPhysicalDevice device) {
        SwapChainSupportDetails details;
        vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities);
        uint32_t formatCount;
        vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr);
        if (formatCount != 0) {
            details.formats.reserve(formatCount);
            vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data());
        }

        return details;
    }
};

vulkan 1 Macos 有错误

c++ vulkan
1个回答
0
投票

你确实回答了你自己的问题。

是的,macOS 上的 Vulkan 与 MoltenVk 需要可移植性子集。这是因为完整 Vulkan 所做的一些事情无法(有效)转换为 Metal API 调用。

是的,您需要在创建设备时启用扩展。

Khronos 示例存储库中有一个代码示例展示了如何启用它:

https://docs.vulkan.org/samples/latest/samples/extensions/portability/README.html

© www.soinside.com 2019 - 2024. All rights reserved.