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 settrue
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);