vkCreateDevice fails, VK_ERROR_EXTENSION_NOT_PRESENT

Hi,

I’m having trouble getting error information from Vulkan. As far as I understand I have enabled the validation layer VK_LAYER_KHRONOS_validation and the extension VK_EXT_debug_report. I’m using SDL2 to create a window and a VkSurfaceKHR. All the SDL-calls succeed, but then when I call vkCreateDevice I get a VkResult of -7, which is VK_ERROR_EXTENSION_NOT_PRESENT. However, I don’t know how to find out what extension is in question. The validation layers don’t print anything. Here’s the relevant code:

#include "Rendering.h"

VKAPI_ATTR VkBool32 VKAPI_CALL _VK_debug_fn(
    VkDebugReportFlagsEXT p_flags,
    VkDebugReportObjectTypeEXT p_obj_type,
    uint64 p_obj,
    size_t p_location,
    int32 p_code,
    const char *p_layer_prefix,
    const char *p_msg,
    void *p_data
) {
    print("VK VALIDATION: ");
    println(p_msg);
    return VK_FALSE;
}

InitTerm _Renderer_InitTerm(
    [] () {
        println("_Rendering_Init");
        Memory_Global->Rendering_SDL_window = SDL_CreateWindow(Memory_Global->App_name, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1920, 1080, SDL_WINDOW_VULKAN|SDL_WINDOW_SHOWN);
        if (!Memory_Global->Rendering_SDL_window) {
            println(concat("SDL_CreateWindow error: ", SDL_GetError()));
            abort();
        }

        uint32 ext_count;
        if (!SDL_Vulkan_GetInstanceExtensions(Memory_Global->Rendering_SDL_window, &ext_count, 0)) {
            println(concat("SDL_Vulkan_GetInstanceExtensions error: ", SDL_GetError()));
            abort();
        }

        ListRef<const char*> exts(ext_count);
        auto exts_data = get_data(exts);
        exts_data[0] = VK_EXT_DEBUG_REPORT_EXTENSION_NAME;
        if (!SDL_Vulkan_GetInstanceExtensions(Memory_Global->Rendering_SDL_window, &ext_count, exts_data + 1)) {
            println(concat("SDL_Vulkan_GetInstanceExtensions error: ", SDL_GetError()));
            abort();
        }

        ext_count += 1;
        const char *layers[] = {
            "VK_LAYER_KHRONOS_validation",
            //"VK_LAYER_LUNARG_api_dump",
        };

        uint32 layer_count = sizeof(layers)/sizeof(const char *);
        println("Vulkan instance extensions (enabled):");
        for (uint32 i = 0; i < ext_count; ++i) {
            print("  ");
            println(exts_data[i]);
        }

        println("Vulkan validation layers (enabled):");
        for (uint32 i = 0; i < layer_count; ++i) {
            print("  ");
            println(layers[i]);
        }

        VkApplicationInfo app_info = {};
        app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
        app_info.pApplicationName = Memory_Global->App_name;
        app_info.applicationVersion = VK_MAKE_VERSION(
            Memory_Global->App_version_major,
            Memory_Global->App_version_minor,
            Memory_Global->App_version_patch
        );
        app_info.pEngineName = "Unnamed engine";
        app_info.engineVersion = VK_MAKE_VERSION(
            Memory_Global->App_version_major,
            Memory_Global->App_version_minor,
            Memory_Global->App_version_patch
        );
        app_info.apiVersion = VK_API_VERSION_1_2;

        VkInstanceCreateInfo inst_create_info = {};
        inst_create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
        inst_create_info.pApplicationInfo = &app_info;
        inst_create_info.enabledLayerCount = layer_count;
        inst_create_info.ppEnabledLayerNames = layers;
        inst_create_info.enabledExtensionCount = ext_count;
        inst_create_info.ppEnabledExtensionNames = exts_data;
        println(vkCreateInstance(&inst_create_info, 0, &Memory_Global->Rendering_VK_instance));

        auto SDL2_vkCreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)SDL_Vulkan_GetVkGetInstanceProcAddr();
        VkDebugReportCallbackCreateInfoEXT debug_callback_create_info = {};
        debug_callback_create_info.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT;
        debug_callback_create_info.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT|VK_DEBUG_REPORT_WARNING_BIT_EXT|VK_DEBUG_REPORT_DEBUG_BIT_EXT;
        debug_callback_create_info.pfnCallback = _VK_debug_fn;
        SDL2_vkCreateDebugReportCallbackEXT(Memory_Global->Rendering_VK_instance, &debug_callback_create_info, 0, &Memory_Global->Rendering_VK_debug_callback);

        if (!SDL_Vulkan_CreateSurface(Memory_Global->Rendering_SDL_window, Memory_Global->Rendering_VK_instance, &Memory_Global->Rendering_VK_surface)) {
            println(concat("SDL_Vulkan_CreateSurface error: ", SDL_GetError()));
            abort();
        }

        uint32 dev_count;
        println(vkEnumeratePhysicalDevices(Memory_Global->Rendering_VK_instance, &dev_count, 0));
        ListRef<VkPhysicalDevice> devs(dev_count);
        auto devs_data = get_data(devs);
        println(vkEnumeratePhysicalDevices(Memory_Global->Rendering_VK_instance, &dev_count, devs_data));
        println("Physical devices:");
        for (uint32 i = 0; i < dev_count; ++i) {
            VkPhysicalDeviceProperties dev_props;
            vkGetPhysicalDeviceProperties(devs_data[i], &dev_props);
            print("  ");
            println(dev_props.deviceName);
        }

        Memory_Global->Rendering_VK_physical_device = devs_data[0];
        uint32 queue_family_count;
        vkGetPhysicalDeviceQueueFamilyProperties(Memory_Global->Rendering_VK_physical_device, &queue_family_count, 0);
        ListRef<VkQueueFamilyProperties> queue_families(queue_family_count);
        auto queue_families_data = get_data(queue_families);
        vkGetPhysicalDeviceQueueFamilyProperties(Memory_Global->Rendering_VK_physical_device, &queue_family_count, queue_families_data);
        uint32 graphics_queue_index = 0xffffffff;
        uint32 present_queue_index = 0xffffffff;
        for (uint32 i = 0; i < queue_family_count; ++i) {
            auto queue_family = queue_families_data + i;
            if (queue_family->queueCount > 0 && queue_family->queueFlags & VK_QUEUE_GRAPHICS_BIT) {
                graphics_queue_index = i;
            }
            VkBool32 present_support = VK_FALSE;
            println(vkGetPhysicalDeviceSurfaceSupportKHR(
                Memory_Global->Rendering_VK_physical_device,
                i,
                Memory_Global->Rendering_VK_surface,
                &present_support
            ));
            if (queue_family->queueCount > 0 && present_support) {
                present_queue_index = i;
            }
            if (graphics_queue_index != 0xffffffff && present_queue_index != 0xffffffff) {
                break;
            }
        }

        const char *dev_exts[] = {
            VK_KHR_SWAPCHAIN_EXTENSION_NAME
        };
        float32 queue_prio = 1.0f;
        VkDeviceQueueCreateInfo device_queue_create_infos[2] = {};
        device_queue_create_infos[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
        device_queue_create_infos[0].queueFamilyIndex = graphics_queue_index;
        device_queue_create_infos[0].queueCount = 1;
        device_queue_create_infos[0].pQueuePriorities = &queue_prio;
        device_queue_create_infos[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
        device_queue_create_infos[1].queueFamilyIndex = present_queue_index;
        device_queue_create_infos[1].queueCount = 1;
        device_queue_create_infos[1].pQueuePriorities = &queue_prio;
        uint32 device_queue_create_infos_count = sizeof(device_queue_create_infos)/sizeof(VkDeviceQueueCreateInfo);

        VkPhysicalDeviceFeatures physical_device_features = {};
        VkDeviceCreateInfo device_create_info = {};
        device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
        device_create_info.queueCreateInfoCount = device_queue_create_infos_count;
        device_create_info.pQueueCreateInfos = device_queue_create_infos;
        device_create_info.pEnabledFeatures = &physical_device_features;
        device_create_info.enabledExtensionCount = ext_count;
        device_create_info.ppEnabledExtensionNames = exts_data;
        device_create_info.enabledLayerCount = layer_count;
        device_create_info.ppEnabledLayerNames = layers;

        print("About to create device: ");
        println(vkCreateDevice(Memory_Global->Rendering_VK_physical_device, &device_create_info, 0, &Memory_Global->Rendering_VK_device));
        vkGetDeviceQueue(Memory_Global->Rendering_VK_device, graphics_queue_index, 0, &Memory_Global->Rendering_VK_graphics_queue);
        vkGetDeviceQueue(Memory_Global->Rendering_VK_device, present_queue_index, 0, &Memory_Global->Rendering_VK_present_queue);
        VkSurfaceCapabilitiesKHR capabilities;
        vkGetPhysicalDeviceSurfaceCapabilitiesKHR(Memory_Global->Rendering_VK_physical_device, Memory_Global->Rendering_VK_surface, &capabilities);
    },
    [] () {
        println("_Rendering_Term");
        vkDestroyDevice(Memory_Global->Rendering_VK_device, 0);
        vkDestroySurfaceKHR(Memory_Global->Rendering_VK_instance, Memory_Global->Rendering_VK_surface, 0);
        vkDestroyInstance(Memory_Global->Rendering_VK_instance, 0);
        SDL_DestroyWindow(Memory_Global->Rendering_SDL_window);
        return 0;
    }
);

Memory_Global points to a struct that contains variables for storing the window, surface, device etc.

The output from my prints reads as follows:

_Rendering_Init
Vulkan instance extensions (enabled):
  VK_EXT_debug_report
  VK_KHR_surface
  VK_KHR_win32_surface
Vulkan validation layers (enabled):
  VK_LAYER_KHRONOS_validation
0
0
0
Physical devices:
  NVIDIA GeForce GTX 1060 6GB
0
About to create device: -7

And that’s when the program crashes, when a call to vkGetDeviceQueue gets made. Any help with this would be greatly appreciated. Let me know if you need the output from VK_LAYER_LUNARG_api_dump.

Cheers,
EngineArtist

Rubber-ducking really does help! :slight_smile:

I hadn’t noticed that I was accidentally handing instance extensions to the device instead of device extensions.

I’d still like to ask my original question: How can I get error reporting in this kind of a situation where I’m passing bad data to the API?

Cheers,
EngineArtist

EDIT: And also, what’s the intended way of checking error values, i.e. VkResults?

Passing non-existing extension names is technically valid, so there’s no problem and nothing to report, except you get the error code.

vkEnumerateDeviceExtensionProperties gives you the supported extensions (as does vulkaninfo so you could crosscheck the offending extension there.

Otherwisely, “simply” don’t pass bad data to Vulkan. When you do then from that point on you are in some shadow state, and you might never even find the bug until it blows in somebodys face ten years later. As a last line of defense there are the Validation Layers.

I understand. So basically I’ll just need to be extra careful and build my own checking mechanisms. What about a vkGetError() or such, does something like that exist? In SDL I make use of SDL_GetError() to get a print of whatever blew up.

Thanks,
EngineArtist

No; Vulkan is a low-level API, and it doesn’t include any convenience features of that sort. You get errors by return value, not by out-of-band mechanisms like a “get error” function. And the API normally only gives errors in circumstances that you can’t fix yourself (allocating more memory than is available, etc), not typically for common usability issues.

Vulkan typically does not check user input at all, and actually is discuraged to do any checking. And when you get an error code it typically is not because of bad input, but it is pure and true runtime error. And typically the error code is already self-explanatory without any text, or the driver does not necessarily know any more than you do.

If you feed Vulkan bad input (violate the API contract, aka make a logical error) you get undefined behavior (i.e. anything goes). There are Validation Layers for that when debugging, but they do not necessarily have 100 % coverage (and in some cases cannot have 100 % coverage), and are not necessarily always right.

The VK_ERROR_*_NOT_PRESENT you mention is just rare exception to that convention. But there is no associated textual log, and if you care to know which extension\layer\feature was offensive, you can just find it yourself querying what is actually supported.

That makes sense. Also, I think that such an explicit, low-level API gives vendors less wiggle room, which makes performance easier to drive since you don’t need to guess what’s happening behind each call (compared to eg. OpenGL where you have to do tricks to make sure a certain thing you want to happen, actually happens). From a purely engineering point of view working with something like Vulkan is a joy.

Thank you @Alfonse_Reinheart and @krOoze! I appreciate the help :slight_smile:

1 Like

Right.
PS: Some of the more spammy warning logs were moved to VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT validation sublayer, which is off by default. Not sure if it reports something in this case or not…

Amen to that! (As someone who continues to battle exactly these kind of shenanigans with OpenGL drivers … even this week.)

What I wouldn’t give to just snap my fingers and have this large production system be in Vulkan. Despite its faults, it’s a huge step in the right direction.

I hear ya. Even though OpenGL is easy to approach, getting it to do exactly what you want is a frustrating experience to say the least. If you don’t mind my asking, what kind of system are you working on? As for me, I’m just curious about learning Vulkan along with DX12.

Cheers,
EngineArtist

I don’t want to totally derail your thread. Sorry, I prob should have just liked your post and left it at that :slight_smile:

1 Like