int main(int argc, char** argv) { g_validationLayers = std::vector<const char*> { "VK_LAYER_LUNARG_mem_tracker", "VK_LAYER_GOOGLE_unique_objects", }; // Init GLFW { // Handle GLFW errors glfwSetErrorCallback([](int error, const char* description) { std::cout << "GLFW error: " << error << " - " << description << std::endl; }); // Initialize GLFW if (!glfwInit()) error("Cannot initialize GLFW."); // Check Vulkan support if (!glfwVulkanSupported()) error("Cannot find compatible Vulkan client driver."); } // Get Validation layers { uint32_t numInstanceLayers = 0; // Get numInstanceLayers if (vkEnumerateInstanceLayerProperties(&numInstanceLayers, nullptr)) error("Vulkan: Could not enumerate instance layer properties."); if (numInstanceLayers > 0) { std::vector<VkLayerProperties> instanceLayers(numInstanceLayers); if (vkEnumerateInstanceLayerProperties(&numInstanceLayers, instanceLayers.data())) error("Vulkan: Could not enumerate instance layer properties."); // Print layers: std::cout << "Validation layers: " << std::endl; for (int i = 0; i < numInstanceLayers; ++i) { std::cout << "\t" << instanceLayers[i].layerName << std::endl; std::cout << "\t\t" << instanceLayers[i].description << std::endl; std::cout << std::endl; } std::cout << std::endl; } else std::cout << "No validation layers found!" << std::endl; // TODO: Check Layers } // Check instance extensions { int numRequiredExtensions; const char** requiredExtensions; // Get required extensions from GLFW { requiredExtensions = glfwGetRequiredInstanceExtensions((int*)&numRequiredExtensions); if (numRequiredExtensions > 0) { // Write to global g_extensions for (int i = 0; i < numRequiredExtensions; ++i) g_extensions.push_back(requiredExtensions[i]); // Print std::cout << "Required Instance Extensions(GLFW):" << std::endl; for (int i = 0; i < numRequiredExtensions; ++i) { std::cout << "\t" << requiredExtensions[i] << std::endl; } std::cout << std::endl; } // TODO: Check extensions } // Get Instance extensions { VkResult err; uint32_t numInstanceExtensions; err = vkEnumerateInstanceExtensionProperties(nullptr, &numInstanceExtensions, nullptr); if (numInstanceExtensions > 0) { std::vector<VkExtensionProperties> instanceExtensions(numInstanceExtensions); err = vkEnumerateInstanceExtensionProperties(NULL, &numInstanceExtensions, instanceExtensions.data()); // Print std::cout << "Instance Extensions: " << std::endl; for (int i = 0; i < numInstanceExtensions; ++i) { std::cout << "\t" <<instanceExtensions[i].extensionName << std::endl; std::cout << "\t\t" << instanceExtensions[i].specVersion << std::endl; std::cout << std::endl; } std::cout << std::endl; } // TODO: Check instance extensions(with required instance extensions) } } // Create Vulkan Instance { VkApplicationInfo app; { app.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; app.pNext = nullptr; app.pApplicationName = "Vulkan test 1"; app.applicationVersion = 0; app.pEngineName = "Vulkan test 1"; app.engineVersion = 0; app.apiVersion = VK_API_VERSION; } VkInstanceCreateInfo instanceInfo; { instanceInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; instanceInfo.pNext = nullptr; instanceInfo.pApplicationInfo = &app; instanceInfo.enabledLayerCount = g_validationLayers.size(); instanceInfo.ppEnabledLayerNames = g_validationLayers.data(); instanceInfo.enabledExtensionCount = g_extensions.size(); instanceInfo.ppEnabledExtensionNames = g_extensions.data(); } // TODO: Aligned allocators VkAllocationCallbacks allocator; { allocator.pUserData = nullptr; allocator.pfnAllocation = [](void* pUserData, size_t size, size_t alignment, VkSystemAllocationScope allocationScope)->void* { return malloc(size); }; allocator.pfnFree = [](void* pUserData, void* pMemory) { free(pMemory); }; allocator.pfnReallocation = [](void* pUserData, void *pOriginal, size_t size, size_t alignment, VkSystemAllocationScope allocationScope) { free(pOriginal); return malloc(size); }; allocator.pfnInternalAllocation = nullptr; allocator.pfnInternalFree = nullptr; allocator.pfnReallocation = nullptr; } // Create vulkan instance VkResult vkError = vkCreateInstance(&instanceInfo, &allocator, &g_vkInstance); // Handle errors switch (vkError) { case VK_ERROR_INCOMPATIBLE_DRIVER: error("Drivers do not support vulkan. Drivers could be outdated."); break; case VK_ERROR_EXTENSION_NOT_PRESENT: error("Cannot find specified extension."); break; case VK_SUCCESS: // Succes! (prevent default from catching success as error) std::cout << "Vulkan instance created!" << std::endl; break; default: error("Could not create vulkan Instance. Drivers could be outdated."); break; } } // Look for GPU device { uint32_t numGPUs; VkResult vkError = vkEnumeratePhysicalDevices(g_vkInstance, &numGPUs, nullptr); if (numGPUs < 0) error("vkEnumeratePhysicalDevices could not find any GPU devices."); if (vkError) error("vkEnumeratePhysicalDevices could not enumerate GPU devices."); if (numGPUs > 0) { std::vector<VkPhysicalDevice> physicalDevices(numGPUs); if (vkEnumeratePhysicalDevices(g_vkInstance, &numGPUs, physicalDevices.data())) error("vkEnumeratePhysicalDevices could not enumerate GPU devices."); g_vkGPU = physicalDevices[0]; std::cout << numGPUs << " GPUs found!" << std::endl; } } // Get queue properties { uint32_t numQueues; vkGetPhysicalDeviceProperties(g_vkGPU, &g_vkGPUProperties); vkGetPhysicalDeviceQueueFamilyProperties(g_vkGPU, &numQueues, nullptr); if (numQueues == 0) error("vkGetPhysicalDeviceQueueFamilyProperties could not find any queues."); g_vkQueueProperties = std::vector<VkQueueFamilyProperties>(numQueues); vkGetPhysicalDeviceQueueFamilyProperties(g_vkGPU, &numQueues, g_vkQueueProperties.data()); } // Look for device layers (Unecessary code that does nothing) { uint32_t numDeviceLayers; if (vkEnumerateDeviceLayerProperties(g_vkGPU, &numDeviceLayers, nullptr)) error("vkEnumerateDeviceLayerProperties failed!"); if (numDeviceLayers > 0) { std::vector<VkLayerProperties> deviceLayers(numDeviceLayers); if (vkEnumerateDeviceLayerProperties(g_vkGPU, &numDeviceLayers, deviceLayers.data())) error("vkEnumerateDeviceLayerProperties failed!"); // TODO: Check device layers. } } // Look for device extensions (swapchain extension) { uint32_t numDeviceExtensions; bool extensionSwapChainFound = false; if (vkEnumerateDeviceExtensionProperties(g_vkGPU, nullptr, &numDeviceExtensions, nullptr)) error("vkEnumerateDeviceExtensionProperties failed!"); if (numDeviceExtensions > 0) { std::vector<VkExtensionProperties> deviceExtensions(numDeviceExtensions); if (vkEnumerateDeviceExtensionProperties(g_vkGPU, nullptr, &numDeviceExtensions, deviceExtensions.data())) error("vkEnumerateDeviceExtensionProperties failed!"); // Search for swapchain extension for (VkExtensionProperties extension : deviceExtensions) { if (!strcmp(extension.extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME)) extensionSwapChainFound = true; } // Print std::cout << std::endl << "Extensions:" << std::endl; for (VkExtensionProperties extension : deviceExtensions) { std::cout << extension.extensionName << "(" << extension.specVersion << ")" << std::endl; } std::cout << std::endl; } if (!extensionSwapChainFound) error("Failed to find the " VK_KHR_SWAPCHAIN_EXTENSION_NAME " extension!"); } // TODO: Validate // Get instance function adresses { GET_INSTANCE_PROC_ADDR(g_vkInstance, GetPhysicalDeviceSurfaceCapabilitiesKHR); GET_INSTANCE_PROC_ADDR(g_vkInstance, GetPhysicalDeviceSurfaceFormatsKHR); GET_INSTANCE_PROC_ADDR(g_vkInstance, GetPhysicalDeviceSurfacePresentModesKHR); GET_INSTANCE_PROC_ADDR(g_vkInstance, GetPhysicalDeviceSurfaceSupportKHR); GET_INSTANCE_PROC_ADDR(g_vkInstance, CreateSwapchainKHR); GET_INSTANCE_PROC_ADDR(g_vkInstance, DestroySwapchainKHR); GET_INSTANCE_PROC_ADDR(g_vkInstance, GetSwapchainImagesKHR); GET_INSTANCE_PROC_ADDR(g_vkInstance, AcquireNextImageKHR); GET_INSTANCE_PROC_ADDR(g_vkInstance, QueuePresentKHR); } // Create window { glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); g_window = glfwCreateWindow(g_width, g_height, "Vulkan test", NULL, NULL); if (!g_window) error("Could not create window!"); glfwSetWindowRefreshCallback(g_window, [](GLFWwindow* window) { // TODO: draw(); }); glfwSetFramebufferSizeCallback(g_window, [](GLFWwindow* window, int width, int height) { g_width = width; g_height = height; // TODO: resize(); }); } // Init swapchain { glfwCreateWindowSurface(g_vkInstance, g_window, nullptr, &g_vkSurface); std::vector<VkBool32> supportsPresent(g_vkQueueProperties.size()); for (uint32_t i = 0; i < g_vkQueueProperties.size(); ++i) g_vkFPGetPhysicalDeviceSurfaceSupportKHR(g_vkGPU, i, g_vkSurface, &supportsPresent[i]); uint32_t graphicsQueueNodeIndex = UINT32_MAX; uint32_t presentQueueNodeIndex = UINT32_MAX; for (uint32_t i = 0; i < g_vkQueueProperties.size(); ++i) { if (graphicsQueueNodeIndex == UINT32_MAX) { graphicsQueueNodeIndex = i; } if (supportsPresent[i] == VK_TRUE) { graphicsQueueNodeIndex = i; presentQueueNodeIndex = i; break; } } //if (presentQueueNodeIndex == UINT32_MAX) { // for (uint32_t i = 0; i < g_vkQueueProperties.size(); ++i) { // if (supportsPresent[i] == VK_TRUE) { // presentQueueNodeIndex = i; // } // } //} if (graphicsQueueNodeIndex == UINT32_MAX || presentQueueNodeIndex == UINT32_MAX) error("Could not find a graphics and a present queue."); if (graphicsQueueNodeIndex != presentQueueNodeIndex) error("Could not find a common graphics and present queue."); g_vkGraphicsQueueNodeIndex = graphicsQueueNodeIndex; //TODO: init device { float queuePriotities = 0.f; VkDeviceQueueCreateInfo queue; { queue.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; queue.pNext = NULL; queue.queueFamilyIndex = g_vkGraphicsQueueNodeIndex; queue.queueCount = 1; queue.pQueuePriorities = &queuePriotities; } } //vkGetDeviceQueue(g_vkDevice, g_vkGraphicsQueueNodeIndex, 0, g_vkQueue); } std::cin.get(); return 0; }
int XdevLSwapChainVulkan::createSurface(IPXdevLWindow window) { Display* display = static_cast<Display*>(window->getInternal(XdevLInternalName("X11_DISPLAY"))); if(nullptr == display) { XDEVL_MODULEX_ERROR(XdevLSwapChainVulkan, "Could not get native X11 display information.\n"); return RET_FAILED; } Window x11window = (Window)(window->getInternal(XdevLInternalName("X11_WINDOW"))); if(None == x11window) { XDEVL_MODULEX_ERROR(XdevLSwapChainVulkan, "Could not get native X11 window information.\n"); return RET_FAILED; } // // Get the Surface extensions. // VkResult result; #if defined(_WIN32) VkWin32SurfaceCreateInfoKHR surfaceCreateInfo; surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; surfaceCreateInfo.hinstance = (HINSTANCE)platformHandle; // provided by the platform code surfaceCreateInfo.hwnd = (HWND)platformWindow; // provided by the platform code result = vkCreateWin32SurfaceKHR(instance, &surfaceCreateInfo, nullptr, &m_surface); #elif defined(__ANDROID__) VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo; surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR; surfaceCreateInfo.window = window; // provided by the platform code result = vkCreateAndroidSurfaceKHR(instance, &surfaceCreateInfo, nullptr, &m_surface); #else VkXcbSurfaceCreateInfoKHR surfaceCreateInfo = { VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR, nullptr, 0, XGetXCBConnection(display), (xcb_window_t)x11window }; result = vkCreateXcbSurfaceKHR(m_instance, &surfaceCreateInfo, nullptr, &m_surface); #endif if(result != VK_SUCCESS) { std::cerr << "Failed to create Vulkan surface: " << vkVkResultToString(result) << std::endl; return 1; } uint32_t queueCount; vkGetPhysicalDeviceQueueFamilyProperties(m_physicalDevice, &queueCount, nullptr); std::vector<VkQueueFamilyProperties> queueProps(queueCount); vkGetPhysicalDeviceQueueFamilyProperties(m_physicalDevice, &queueCount, queueProps.data()); // Will be used to present the swap chain images to the windowing system std::vector<VkBool32> supportsPresent(queueCount); for(uint32_t i = 0; i < queueCount; i++) { fpGetPhysicalDeviceSurfaceSupportKHR(m_physicalDevice, i, m_surface, &supportsPresent[i]); } // Search for a graphics and a present queue in the array of queue // families, try to find one that supports both uint32_t graphicsQueueNodeIndex = UINT32_MAX; uint32_t presentQueueNodeIndex = UINT32_MAX; for(uint32_t i = 0; i < queueCount; i++) { if((queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) { if(graphicsQueueNodeIndex == UINT32_MAX) { graphicsQueueNodeIndex = i; } if(supportsPresent[i] == VK_TRUE) { graphicsQueueNodeIndex = i; presentQueueNodeIndex = i; break; } } } if(presentQueueNodeIndex == UINT32_MAX) { // If there's no queue that supports both present and graphics // try to find a separate present queue for(uint32_t i = 0; i < queueCount; ++i) { if(supportsPresent[i] == VK_TRUE) { presentQueueNodeIndex = i; break; } } } // Exit if either a graphics or a presenting queue hasn't been found if(graphicsQueueNodeIndex == UINT32_MAX || presentQueueNodeIndex == UINT32_MAX) { return 1; } // todo : Add support for separate graphics and presenting queue if(graphicsQueueNodeIndex != presentQueueNodeIndex) { return 1; } m_queueNodeIndex = graphicsQueueNodeIndex; // Get list of supported surface formats uint32_t formatCount; result = vkGetPhysicalDeviceSurfaceFormatsKHR(m_physicalDevice, m_surface, &formatCount, nullptr); if(VK_SUCCESS != result) { std::cerr << "vkGetPhysicalDeviceSurfaceFormatsKHR failed: " << vkVkResultToString(result) << std::endl; return 1; } assert(formatCount > 0); std::vector<VkSurfaceFormatKHR> surfaceFormats(formatCount); result = vkGetPhysicalDeviceSurfaceFormatsKHR(m_physicalDevice, m_surface, &formatCount, surfaceFormats.data()); assert(!result); // If the surface format list only includes one entry with VK_FORMAT_UNDEFINED, // there is no preferered format, so we assume VK_FORMAT_B8G8R8A8_UNORM if((formatCount == 1) && (surfaceFormats[0].format == VK_FORMAT_UNDEFINED)) { m_colorFormat = VK_FORMAT_B8G8R8A8_UNORM; } else { // Always select the first available color format // If you need a specific format (e.g. SRGB) you'd need to // iterate over the list of available surface format and // check for it's presence m_colorFormat = surfaceFormats[0].format; } m_colorSpace = surfaceFormats[0].colorSpace; return 0; }
// Creates an os specific surface // Tries to find a graphics and a present queue void initSurface( xcb_connection_t* connection, xcb_window_t window ) { VkResult err; // Create surface depending on OS VkXcbSurfaceCreateInfoKHR surfaceCreateInfo = {}; surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR; surfaceCreateInfo.connection = connection; surfaceCreateInfo.window = window; err = vkCreateXcbSurfaceKHR(instance, &surfaceCreateInfo, nullptr, &surface); // Get available queue family properties uint32_t queueCount; vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount, NULL); assert(queueCount >= 1); std::vector<VkQueueFamilyProperties> queueProps(queueCount); vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount, queueProps.data()); // Iterate over each queue to learn whether it supports presenting: // Find a queue with present support // Will be used to present the swap chain images to the windowing system std::vector<VkBool32> supportsPresent(queueCount); for (uint32_t i = 0; i < queueCount; i++) { fpGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, i, surface, &supportsPresent[i]); } // Search for a graphics and a present queue in the array of queue // families, try to find one that supports both uint32_t graphicsQueueNodeIndex = UINT32_MAX; uint32_t presentQueueNodeIndex = UINT32_MAX; for (uint32_t i = 0; i < queueCount; i++) { if ((queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) { if (graphicsQueueNodeIndex == UINT32_MAX) { graphicsQueueNodeIndex = i; } if (supportsPresent[i] == VK_TRUE) { graphicsQueueNodeIndex = i; presentQueueNodeIndex = i; break; } } } if (presentQueueNodeIndex == UINT32_MAX) { // If there's no queue that supports both present and graphics // try to find a separate present queue for (uint32_t i = 0; i < queueCount; ++i) { if (supportsPresent[i] == VK_TRUE) { presentQueueNodeIndex = i; break; } } } // Exit if either a graphics or a presenting queue hasn't been found if (graphicsQueueNodeIndex == UINT32_MAX || presentQueueNodeIndex == UINT32_MAX) { feather::vulkan::tools::exitFatal("Could not find a graphics and/or presenting queue!", "Fatal error"); } // todo : Add support for separate graphics and presenting queue if (graphicsQueueNodeIndex != presentQueueNodeIndex) { feather::vulkan::tools::exitFatal("Separate graphics and presenting queues are not supported yet!", "Fatal error"); } queueNodeIndex = graphicsQueueNodeIndex; // Get list of supported surface formats uint32_t formatCount; err = fpGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, NULL); assert(!err); assert(formatCount > 0); std::vector<VkSurfaceFormatKHR> surfaceFormats(formatCount); err = fpGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, surfaceFormats.data()); assert(!err); // If the surface format list only includes one entry with VK_FORMAT_UNDEFINED, // there is no preferered format, so we assume VK_FORMAT_B8G8R8A8_UNORM if ((formatCount == 1) && (surfaceFormats[0].format == VK_FORMAT_UNDEFINED)) { colorFormat = VK_FORMAT_B8G8R8A8_UNORM; } else { // Always select the first available color format // If you need a specific format (e.g. SRGB) you'd need to // iterate over the list of available surface format and // check for it's presence colorFormat = surfaceFormats[0].format; } colorSpace = surfaceFormats[0].colorSpace; }