| #include <algorithm> | |
| #include <cassert> | |
| #include <cstring> | |
| #include <iostream> | |
| #include <memory> | |
| #include <vector> | |
| #if defined(VK_USE_PLATFORM_WIN32_KHR) | |
| #elif defined(VK_USE_PLATFORM_ANDROID_KHR) | |
| #elif defined(VK_USE_PLATFORM_WAYLAND_KHR) | |
| #define GLFW_EXPOSE_NATIVE_WAYLAND | |
| #include <wayland-client.h> | |
| #elif defined(VK_USE_PLATFORM_XCB_KHR) | |
| #define GLFW_EXPOSE_NATIVE_X11 | |
| #include <X11/Xlib-xcb.h> | |
| #elif defined(VK_USE_PLATFORM_XLIB_KHR) | |
| #define GLFW_EXPOSE_NATIVE_X11 | |
| #include <X11/Xlib.h> | |
| #elif defined(VK_USE_PLATFORM_IOS_MVK) | |
| #elif defined(VK_USE_PLATFORM_MACOS_MVK) | |
| #endif | |
| #include <vulkan/vulkan.h> | |
| #include <GLFW/glfw3.h> | |
| #include <GLFW/glfw3native.h> | |
| #include <vlk/device.hpp> | |
| #include <vlk/instance.hpp> | |
| #include <vlk/surface.hpp> | |
| #include <vlk/swapchain.hpp> | |
| #include <vlk/utils.hpp> | |
| uint32_t WIDTH = 640; | |
| uint32_t HEIGHT = 480; | |
| const std::vector<const char*> device_extensions | |
| { | |
| VK_KHR_SWAPCHAIN_EXTENSION_NAME | |
| }; | |
| bool is_device_suitable(const vlk::Physical_Device& p) | |
| { | |
| #ifndef NDEBUG | |
| vlk::print_device(p); | |
| #endif | |
| // We need to be sure the vlk::PhysicalDevice has the basics | |
| if (p.device_type() != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU || | |
| !p.has_extension("VK_KHR_swapchain") || | |
| !p.features().geometryShader) | |
| { | |
| return false; | |
| } | |
| return true; | |
| } | |
| #ifndef NDEBUG | |
| static VKAPI_ATTR VkBool32 VKAPI_CALL debug_callback_fun( | |
| VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t obj, size_t location, | |
| int32_t code, const char* layerPrefix, const char* msg, void* userData) | |
| { | |
| std::string prefix(""); | |
| if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) | |
| { | |
| prefix += "ERROR:"; | |
| } | |
| if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT) | |
| { | |
| prefix += "WARNING:"; | |
| } | |
| if (flags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT) | |
| { | |
| prefix += "PERFORMANCE:"; | |
| } | |
| if (flags & VK_DEBUG_REPORT_INFORMATION_BIT_EXT) | |
| { | |
| prefix += "INFO:"; | |
| } | |
| if (flags & VK_DEBUG_REPORT_DEBUG_BIT_EXT) | |
| { | |
| prefix += "DEBUG:"; | |
| } | |
| std::printf("%s [%s] Code %i : %s\n", prefix.c_str(), layerPrefix, code, msg); | |
| return VK_FALSE; | |
| } | |
| #endif | |
| struct Destroy_GLFWwindow | |
| { | |
| void operator()(GLFWwindow* ptr) | |
| { | |
| glfwDestroyWindow(ptr); | |
| } | |
| }; | |
| int main() | |
| { | |
| glfwInit(); | |
| // Prevent glfw to create an OpenGL context | |
| glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); | |
| // Create GLFWwindow | |
| std::unique_ptr<GLFWwindow, Destroy_GLFWwindow> window; | |
| window.reset(glfwCreateWindow(WIDTH, HEIGHT, "Surtr Triangle Example", NULL, NULL)); | |
| if (window == nullptr) | |
| { | |
| std::printf("Surtr Triangle example : Failed to create glfw window\n"); | |
| return -1; | |
| } | |
| // Check for vulkan support | |
| if (GLFW_FALSE == glfwVulkanSupported()) | |
| { | |
| std::printf("Surtr Triangle example : Vulkan not supported\n"); | |
| return -1; | |
| } | |
| // Prepare the extensions for the vlk::Instance creation | |
| std::vector<const char*> extensions; | |
| unsigned int glfw_extension_count = 0; | |
| const char** glfw_extensions; | |
| // glfw will give the appropriate extension for the surface | |
| glfw_extensions = glfwGetRequiredInstanceExtensions(&glfw_extension_count); | |
| for (unsigned int i = 0; i < glfw_extension_count; i++) | |
| { | |
| extensions.push_back(glfw_extensions[i]); | |
| } | |
| //TODO: Xlib surface extension seems to not be loaded by glfw | |
| #if defined(VK_USE_PLATFORM_XLIB_KHR) | |
| extensions.push_back("VK_KHR_xlib_surface"); | |
| #endif | |
| std::vector<const char*> validation_layers; | |
| #ifndef NDEBUG | |
| // Add the debug extensions | |
| extensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); | |
| std::printf("Using extensions :\n"); | |
| for (auto& it : extensions) | |
| { | |
| std::printf("\t%s\n", it); | |
| } | |
| validation_layers.push_back("VK_LAYER_LUNARG_standard_validation"); | |
| std::printf("Using validation layers :\n"); | |
| for (auto& it : validation_layers) | |
| { | |
| std::printf("\t%s\n", it); | |
| } | |
| #endif | |
| // Create the instance and setup the debug callback | |
| std::shared_ptr<vlk::Instance> instance = std::make_shared<vlk::Instance>("vlk Triangle Example", "NO_ENGINE", extensions, validation_layers); | |
| #ifndef NDEBUG | |
| instance->set_debug_callback(&debug_callback_fun); | |
| #endif | |
| // Now that we have the instance, we can | |
| // generate a Surface for the right platform | |
| #if defined(VK_USE_PLATFORM_WIN32_KHR) | |
| #elif defined(VK_USE_PLATFORM_ANDROID_KHR) | |
| #elif defined(VK_USE_PLATFORM_WAYLAND_KHR) | |
| std::printf("Using Wayland interface\n"); | |
| std::shared_ptr<vlk::Surface> surface = std::make_shared<vlk::Surface>(instance, glfwGetWaylandDisplay(), glfwGetWaylandWindow(window.get())); | |
| #elif defined(VK_USE_PLATFORM_XCB_KHR) | |
| // TODO: convert Display to xcb_window_t | |
| std::printf("Using Xcb interface\n"); | |
| std::shared_ptr<vlk::Surface> surface = std::make_shared<vlk::Surface>(instance, XGetXCBConnection(glfwGetX11Display()), glfwGetX11Window(window.get())); | |
| #elif defined(VK_USE_PLATFORM_XLIB_KHR) | |
| // On X11, Vulkan expects a Xlib window to draw on | |
| // Since we are using glfw3, we can extract that from the GLFWwindow | |
| std::printf("Using Xlib interface\n"); | |
| std::shared_ptr<vlk::Surface> surface = std::make_shared<vlk::Surface>(instance, glfwGetX11Display(), glfwGetX11Window(window.get())); | |
| #elif defined(VK_USE_PLATFORM_IOS_MVK) | |
| #elif defined(VK_USE_PLATFORM_MACOS_MVK) | |
| #else | |
| // Empty constructor, will throw :trollface: | |
| std::unique_ptr<vlk::Surface> surface = std::make_unique<vlk::Surface>(); | |
| #endif | |
| // Get the physical devices from the instance | |
| std::vector<vlk::Physical_Device> physical_devices = instance->get_physical_devices(); | |
| #ifndef NDEBUG | |
| std::printf("%zo physical devices found\n", physical_devices.size()); | |
| #endif | |
| // Find a physical device with geometry shader, might not work on integrated graphics | |
| std::vector<vlk::Physical_Device> valid_physical_devices; | |
| std::copy_if(std::begin(physical_devices), std::end(physical_devices), std::back_inserter(valid_physical_devices), is_device_suitable); | |
| assert(valid_physical_devices.size() > 0u && "No suitable physical devices found"); | |
| // Pick the first suitable device | |
| std::unique_ptr<vlk::Physical_Device> physical_device = std::make_unique<vlk::Physical_Device>(valid_physical_devices.at(0)); | |
| #ifndef NDEBUG | |
| std::printf("vlk::Physical_Device in use : %s\n", physical_device->name().c_str()); | |
| std::printf("Available vlk::Physical_Device extensions :\n"); | |
| for (const auto& extension : physical_device->get_extensions()) | |
| { | |
| std::printf("\t%s\n", extension.extensionName); | |
| } | |
| #endif | |
| // Create the logical device for Vulkan to use | |
| std::shared_ptr<vlk::Device> device = std::make_shared<vlk::Device>(instance, std::move(physical_device), static_cast<uint32_t>(physical_device->queue_indice(VK_QUEUE_GRAPHICS_BIT)), device_extensions, validation_layers); | |
| // Now that we have a surface and a logical device, its time to create a vlk::Swapchain | |
| // First, we see if the physical device supports the wanted format. | |
| // https://en.wikipedia.org/wiki/SRGB | |
| VkSurfaceFormatKHR format = { VK_FORMAT_B8G8R8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR }; | |
| { | |
| const vlk::Physical_Device* ptr_p_device = device->get_physical_device(); | |
| std::vector<VkSurfaceFormatKHR> formats = ptr_p_device->get_surface_formats(surface); | |
| assert(formats.size() > 0u && "vlk::Physical_Device : no surface formats"); | |
| // Modern C++ sure is pretty | |
| if (std::find_if( | |
| std::begin(formats), | |
| std::end(formats), | |
| [&format](const VkSurfaceFormatKHR& f) | |
| { | |
| return ((format.format == f.format) && (format.colorSpace == f.colorSpace)); | |
| }) == std::end(formats)) | |
| { | |
| std::printf("vlk Triangle example error : SRGB format not available"); | |
| return 1; | |
| } | |
| } | |
| // VK_PRESENT_MODE_FIFO_KHR is assured to be present. Also avoid screen tearing | |
| VkPresentModeKHR present_mode = VK_PRESENT_MODE_FIFO_KHR; | |
| // Size of the buffer | |
| VkExtent2D extent = { WIDTH, HEIGHT }; | |
| // The minimum number of presentable images that the application needs. | |
| uint32_t img_count = 2u; | |
| vlk::Swapchain swapchain(device, surface, format, present_mode, extent, img_count); | |
| // Terminate glfw at the end | |
| glfwTerminate(); | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment