Home > Enterprise >  Linux GLFW - Vulkan Surface not creating: glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API) is being igno
Linux GLFW - Vulkan Surface not creating: glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API) is being igno

Time:06-13

I'm having an issue with creating a Vulkan surface for a GLFW Window in Linux. Obviously you need to create a window without a client API. But somehow GLFW ignores that window hint?

GLFW spits out the following error message:

GLFW Error 65540: Vulkan: Window surface creation requires the window to have the client API set to GLFW_NO_API

I have no clue why this is not working, it seems like GLFW itself is broken. N.B. the function glfwWindowShouldClose() also seems to ignore close events? Had to make a small workaround.

This is the complete program:

#include <vulkan/vulkan.h>

#include <iostream>
#include <stdexcept>
#include <cstdlib>
#include <vector>
#include <cstring>

#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>


const uint32_t WINDOW_WIDTH = 1024;
const uint32_t WINDOW_HEIGHT = 768;

// NOTE: somehow GLFW's glfwWindowShouldClose() never returns true?!
// So, we create this global for intended behaviour.
bool RUNNING = false;

std::vector<const char*> validation_layers = {
    "VK_LAYER_KHRONOS_validation"
};

#if NDEBUG
    const bool enable_validation_layers = false;
#else
    const bool enable_validation_layers = true;
#endif

struct Application {
    GLFWwindow *window;

    VkInstance vk_instance;
    VkPhysicalDevice vk_physical_device = VK_NULL_HANDLE;
    VkDevice vk_device;
    VkQueue vk_graphics_queue;
    VkSurfaceKHR vk_surface;
};


void glfwErrorCallback(int code, const char* description) {
    std::cerr << "GLFW Error " << code << ": " << description << std::endl;
}

bool checkValidationLayerSupport() {
    uint32_t count;
    vkEnumerateInstanceLayerProperties(&count, nullptr);

    std::vector<VkLayerProperties> available_layers(count);
    vkEnumerateInstanceLayerProperties(&count, available_layers.data());

    for (const auto &layer_name : validation_layers) {
        bool found = false;

        for (const auto &layer_properties : available_layers) {
            if (strcmp(layer_name, layer_properties.layerName)) {
                found = true;
                break;
            }
        }

        if (!found) return false;
    }

    return true;
}


void windowCloseCallback(GLFWwindow *window) {
    RUNNING = false;
}

void keyCallback(GLFWwindow *window, int key, int scancode, int action, int mods) {
    // nothing
}



static void createVKInstance(VkInstance *instance) {

    if ( enable_validation_layers && !checkValidationLayerSupport() ) {
        throw std::runtime_error("Validation layer requested, but not available!");
    }

    VkApplicationInfo app_info = {};
    app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
    app_info.pApplicationName = "Hello Sailor!";
    app_info.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
    app_info.pEngineName = " No engine";
    app_info.engineVersion = VK_MAKE_VERSION(1, 0 ,0);
    app_info.apiVersion = VK_API_VERSION_1_0;

    VkInstanceCreateInfo create_info = {};
    create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
    create_info.pApplicationInfo = &app_info;

    uint32_t glfw_extension_count = 0;
    const char **glfw_extensions;

    glfw_extensions = glfwGetRequiredInstanceExtensions(&glfw_extension_count);

    create_info.enabledExtensionCount = glfw_extension_count;
    create_info.ppEnabledExtensionNames = glfw_extensions;

    if (enable_validation_layers) {
        create_info.enabledLayerCount = (uint32_t) validation_layers.size();
        create_info.ppEnabledLayerNames = validation_layers.data();
    }
    else {
        create_info.enabledLayerCount = 0;
    }

    auto result = vkCreateInstance(&create_info, nullptr, instance);
    if (result != VK_SUCCESS) {
        throw std::runtime_error(" Failed to create Vulkan instance!\n");
    }

    #if 0
        uint32_t p_count = 0;
        vkEnumerateInstanceExtensionProperties(nullptr, &p_count, nullptr);
        std::vector<VkExtensionProperties> props(p_count);
        vkEnumerateInstanceExtensionProperties(nullptr, &p_count, props.data());

        for (auto &prop : props) {
            std::cout << prop.extensionName << std::endl;
        }
    #endif
}


#include <optional>
struct QueueFamilyIndices {
    std::optional<uint32_t> graphics_family;
};

static bool isComplete(QueueFamilyIndices *indices) {
    return indices->graphics_family.has_value();
}

static QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) {
    QueueFamilyIndices indices;

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

    std::vector<VkQueueFamilyProperties> queue_families(count);
    vkGetPhysicalDeviceQueueFamilyProperties(device, &count, queue_families.data());

    int i = 0;
    for (const auto &queue_family : queue_families) {
        if (queue_family.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
            indices.graphics_family = i;
        }

        if (isComplete(&indices)) break;

        i  ;
    }

    return indices;
}


static bool isDeviceSuitable(VkPhysicalDevice device) {
    QueueFamilyIndices indices = findQueueFamilies(device);

    return isComplete(&indices);
}



static void pickPhysicalDevice(VkInstance instance, VkPhysicalDevice *physical_device) {
    uint32_t count = 0;
    vkEnumeratePhysicalDevices(instance, &count, nullptr);

    if (count == 0) {
        throw std::runtime_error("Failed to find GPUs with Vulkan support!");
    }

    std::vector<VkPhysicalDevice> devices(count);
    vkEnumeratePhysicalDevices(instance, &count, devices.data());

    for (const auto &device : devices) {
        if (isDeviceSuitable(device)) {
            *physical_device = device;
            break;
        }
    }

    if (*physical_device == VK_NULL_HANDLE) {
        throw std::runtime_error("Failed to find suitable GPU!");
    }
}

static void createLogicalDevice(VkPhysicalDevice physical_device, VkDevice *device, VkQueue *graphics_queue) {
    QueueFamilyIndices indices = findQueueFamilies(physical_device);

    if (!isComplete(&indices)) {
        throw std::runtime_error("No queue family indices!");
    }

    VkDeviceQueueCreateInfo create_info = {};
    create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
    create_info.queueFamilyIndex = indices.graphics_family.value();
    create_info.queueCount = 1;

    float queue_priority = 1.0f;
    create_info.pQueuePriorities = &queue_priority;


    VkPhysicalDeviceFeatures features = {};

    VkDeviceCreateInfo device_create_info = {};
    device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
    device_create_info.pQueueCreateInfos = &create_info;
    device_create_info.queueCreateInfoCount = 1;

    device_create_info.pEnabledFeatures = &features;

    device_create_info.enabledExtensionCount = 0;

    if (enable_validation_layers) {
        device_create_info.enabledLayerCount = (uint32_t) validation_layers.size();
        device_create_info.ppEnabledLayerNames = validation_layers.data();
    }
    else {
        device_create_info.enabledLayerCount = 0;
    }

    if (vkCreateDevice(physical_device, &device_create_info, nullptr, device) != VK_SUCCESS) {
        throw std::runtime_error("Failed to create logical device!");
    }
    vkGetDeviceQueue(*device, indices.graphics_family.value(), 0, graphics_queue);
}

static void createSurface(GLFWwindow *window, VkInstance instance, VkSurfaceKHR *surface) {
    auto result = glfwCreateWindowSurface(instance, window, nullptr, surface);
    if (result != VK_SUCCESS) {
        throw std::runtime_error("Failed to create window surface!");
    }
}

static void initVulkan(/*GLFWwindow *window, VkInstance *instance, VkPhysicalDevice *physical_device, VkDevice *device, VkQueue *graphics_queue, VkSurfaceKHR *surface */
    Application *app
) {
    createVKInstance(&app->vk_instance);
    createSurface(app->window, app->vk_instance, &app->vk_surface);
    pickPhysicalDevice(app->vk_instance, &app->vk_physical_device);
    createLogicalDevice(app->vk_physical_device, &app->vk_device, &app->vk_graphics_queue);
}


static void initWindow(GLFWwindow *window) {
    glfwInit();
    

    glfwSetErrorCallback(glfwErrorCallback);

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

    window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "Vulkan", nullptr, nullptr);
    if (window == NULL) {
        throw std::runtime_error("Failed to create window!\n");
    }
    else {
        printf("Created window.\n");
    }

    glfwSetWindowCloseCallback(window, windowCloseCallback);
    glfwSetKeyCallback(window, keyCallback);

    RUNNING = true;
}


static void mainLoop(GLFWwindow *window) {
    while (RUNNING) {
        glfwPollEvents();
    }
}

static void cleanUp(GLFWwindow *window, VkInstance instance, VkDevice device, VkSurfaceKHR surface) {
    printf("Cleaning up!\n");

    glfwDestroyWindow(window);
    glfwTerminate();
    
    vkDestroySurfaceKHR(instance, surface, nullptr);
    vkDestroyInstance(instance, nullptr);
    vkDestroyDevice(device, nullptr);
}

static void runApplication(Application *app) {
    initWindow(app->window);
    initVulkan(app);
    mainLoop(app->window);
    cleanUp(app->window, app->vk_instance, app->vk_device, app->vk_surface);
}




int main() {
    Application app;

    try {
        runApplication(&app);
    }
    catch (const std::exception &e) {
        std::cerr << e.what() << std::endl;
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

CodePudding user response:

  • You fail to store the GLFW Window handle in your Application struct, because you pass it by value, not by reference [1]
  • glfwInit returns error code, which must be checked, and\or callback should be setup before the call.
  • glfwVulkanSupported should be called.
  • You have sequencing error with RUNNING variable. It could conceivably be set true even if the window was closed.
  • You enable layers, but no reporting extension, so no Vulkan problems would be reported

1:

struct Application {
    GLFWwindow *window;
};

// passing GLFWwindow* by value (local copy):
static void initWindow(GLFWwindow *window) { 
    // writing to a local GLFWwindow*,
    // which will be destroyed at end of scope
    window = glfwCreateWindow(/*snip*/);
}

// passing GLFWwindow* by value, not reference:
initWindow(app->window);

// reading\dereferencing uninitialized variable app->window:
createSurface(app->window, app->vk_instance, &app->vk_surface);
  • Related