Skip to content

Instantly share code, notes, and snippets.

@bl4ckb0ne
Last active May 11, 2021 07:02
Show Gist options
  • Save bl4ckb0ne/92906f3b00531396a61eaf2966676b16 to your computer and use it in GitHub Desktop.
Save bl4ckb0ne/92906f3b00531396a61eaf2966676b16 to your computer and use it in GitHub Desktop.
#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