I've been following the Vulkan tutorial at https://vulkan-tutorial.com/ and I've been having issues with the section on setting up the window surface. Upon completing the section and testing nothing had broken, I found I was getting an error on the following GLFW call:
glfwCreateWindowSurface(instance, window, nullptr, &surface);
It was returning -3. A quick bit of research revealed this was the error for VK_ERROR_INITIALIZATION_FAILED
, which I then discovered meant the Vulkan loader was not being found. Upon testing, sure enough, I found that glfwVulkanSupported()
was returning GLFW_FALSE
. However, I cannot figure out why. I am using the LunarG Vulkan SDK 1.0.65.1, from here, and x64 GLFW 3.2.1 from here. As far as I can tell, this should be working; searching the web gives no useful results. Vulkan programs such as cube.exe
, which comes with the SDK, work fine.
Thanks :)
Code:
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#include <iostream>
#include <stdexcept>
#include <functional>
#include <vector>
#include <string.h>
#include <set>
#include <string>
VkResult CreateDebugReportCallbackEXT(
VkInstance instance,
const VkDebugReportCallbackCreateInfoEXT *pCreateInfo,
const VkAllocationCallbacks *pAllocator,
VkDebugReportCallbackEXT *pCallback){
auto func = (PFN_vkCreateDebugReportCallbackEXT) vkGetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT");
if (func != nullptr){
return func(instance, pCreateInfo, pAllocator, pCallback);
}
else{
return VK_ERROR_EXTENSION_NOT_PRESENT;
}
}
void DestroyDebugReportCallbackEXT(
VkInstance instance,
VkDebugReportCallbackEXT callback,
const VkAllocationCallbacks *pAllocator){
auto func = (PFN_vkDestroyDebugReportCallbackEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT");
if (func != nullptr){
func(instance, callback, pAllocator);
}
}
struct QueueFamilyIndices {
int graphicsFamily = -1;
int presentFamily = -1;
bool isComplete() {
return graphicsFamily >= 0 && presentFamily >= 0;
}
};
class CraftmineApplication {
public:
void run(){
initVulkan();
initWindow();
mainLoop();
cleanup();
}
private:
GLFWwindow *window;
VkInstance instance;
VkDebugReportCallbackEXT callback;
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
VkDevice device;
VkQueue graphicsQueue;
VkSurfaceKHR surface;
VkQueue presentQueue;
const int WIDTH = 1920;
const int HEIGHT = 1080;
const std::vector<const char*> validationLayers = {
"VK_LAYER_LUNARG_standard_validation"
};
#ifdef NDEBUG
const bool enableValidationLayers = false;
#else
const bool enableValidationLayers = true;
#endif
static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
VkDebugReportFlagsEXT flags,
VkDebugReportObjectTypeEXT objType,
uint64_t obj,
size_t location,
int32_t code,
const char *layerPrefix,
const char *msg,
void *userData){
std::cerr << "Validation layer: " << msg << std::endl;
return VK_FALSE;
}
QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) {
QueueFamilyIndices indices;
uint32_t queueFamilyCount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());
int i = 0;
for (const auto &queueFamily : queueFamilies) {
VkBool32 presentSupport = false;
vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);
if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
indices.graphicsFamily = i;
}
if (queueFamily.queueCount > 0 && presentSupport) {
indices.presentFamily = i;
}
if (indices.isComplete()) {
break;
}
i++;
}
return indices;
}
void 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 = "Craftmine";
appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.pEngineName = "";
appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.apiVersion = VK_API_VERSION_1_0;
VkInstanceCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
createInfo.pApplicationInfo = &appInfo;
if (enableValidationLayers){
createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
createInfo.ppEnabledLayerNames = validationLayers.data();
}
else{
createInfo.enabledLayerCount = 0;
}
auto extensions = getRequriedExtensions();
createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
createInfo.ppEnabledExtensionNames = extensions.data();
uint32_t extensionCount = 0;
vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr);
std::vector<VkExtensionProperties> vulkan_extensions(extensionCount);
vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, vulkan_extensions.data());
bool extensionsPresent = true;
for (int i = 0; i < extensions.size(); i++) {
const char *extNeeded = extensions[i];
bool found = false;
for (const VkExtensionProperties &extension : vulkan_extensions){
if(!strcmp(extNeeded,extension.extensionName)){
found = true;
break;
}
}
if(!found){
extensionsPresent = false;
break;
}
}
if (!extensionsPresent)
throw std::runtime_error("Not all required window extensions present!");
if(vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS)
throw std::runtime_error("Failed to create instance!");
}
void initWindow(){
glfwInit();
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
window = glfwCreateWindow(WIDTH, HEIGHT, "Craftmine", nullptr, nullptr);
}
void initVulkan(){
createInstance();
setupDebugCallback();
createSurface();
pickPhysicalDevice();
createLogicalDevice();
}
void createSurface() {
if(glfwVulkanSupported() != GLFW_TRUE) throw std::runtime_error("No Vulkan!");
if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS)
throw std::runtime_error("Failed to create window surface!");
}
void createLogicalDevice() {
QueueFamilyIndices indices = findQueueFamilies(physicalDevice);
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
std::set<int> uniqueQueueFamilies = { indices.graphicsFamily, indices.presentFamily };
float queuePriority = 1.0f;
for (int 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.pQueueCreateInfos = queueCreateInfos.data();
createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());
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!");
}
vkGetDeviceQueue(device, indices.graphicsFamily, 0, &graphicsQueue);
vkGetDeviceQueue(device, indices.presentFamily, 0, &presentQueue);
}
void pickPhysicalDevice() {
uint32_t deviceCount = 0;
vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
if (deviceCount == 0)
throw std::runtime_error("Failed to find GPUs with Vulkan support!");
std::vector<VkPhysicalDevice> devices(deviceCount);
vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
int currentMaxScore = 0;
for (const auto &device : devices) {
if (getDeviceScore(device) > currentMaxScore) {
physicalDevice = device;
}
}
if (physicalDevice == VK_NULL_HANDLE) {
throw std::runtime_error("Failed to find a suitable GPU!");
}
}
int getDeviceScore(VkPhysicalDevice device) {
VkPhysicalDeviceProperties deviceProperties;
VkPhysicalDeviceFeatures deviceFeatures;
vkGetPhysicalDeviceProperties(device, &deviceProperties);
vkGetPhysicalDeviceFeatures(device, &deviceFeatures);
QueueFamilyIndices indices = findQueueFamilies(device);
if (!indices.isComplete()) return 0;
int score = 1;
score += deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU;
// If unsuitable return 0
return score;
}
void setupDebugCallback(){
if (!enableValidationLayers) return;
VkDebugReportCallbackCreateInfoEXT createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT;
createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT;
createInfo.pfnCallback = debugCallback;
if(CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, &callback) != VK_SUCCESS){
throw std::runtime_error("Failed to set up debug callback!");
}
}
void mainLoop(){
while (!glfwWindowShouldClose(window)){
glfwPollEvents();
}
}
void cleanup(){
vkDestroyDevice(device, nullptr);
DestroyDebugReportCallbackEXT(instance, callback, nullptr);
vkDestroySurfaceKHR(instance, surface, nullptr);
vkDestroyInstance(instance, nullptr);
glfwDestroyWindow(window);
glfwTerminate();
}
bool 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 VkLayerProperties &layerProperties : availableLayers){
if (!strcmp(layerName, layerProperties.layerName)){
layerFound = true;
break;
}
}
if (!layerFound)
return false;
}
return true;
}
std::vector<const char*> getRequriedExtensions() {
uint32_t glfwExtensionCount = 0;
const char** glfwExtensions;
glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
std::vector<const char*> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount);
if (enableValidationLayers){
extensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
}
return extensions;
}
};
int main(){
CraftmineApplication app;
try{
app.run();
}catch(const std::runtime_error &e){
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Try setting a GLFW error callback and check the error description. Show activity on this post. You should call glfwInit before checking Vulkan compatibility. That is, in void run () call initWindow () before initVulkan (). Thanks for contributing an answer to Stack Overflow!
I am using the LunarG Vulkan SDK 1.0.65.1, from here, and x64 GLFW 3.2.1 from here. As far as I can tell, this should be working; searching the web gives no useful results. Vulkan programs such as cube.exe, which comes with the SDK, work fine.
The Khronos Vulkan Samples also use GLFW, although with a small framework in between. For details on a specific Vulkan support function, see the Vulkan support reference. There are also guides for the other areas of the GLFW API.
However, it also means that if you are using the static library form of the Vulkan loader GLFW will either fail to find it or (worse) use the wrong one. The GLFW_VULKAN_STATIC CMake option makes GLFW call the Vulkan loader directly instead of dynamically loading it at runtime.
You should call glfwInit
before checking Vulkan compatibility. That is, in void run()
call initWindow()
before initVulkan()
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With