Last active
August 3, 2016 03:56
-
-
Save v3c70r/b208168e823535b791e8252b1989a6cb to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define GLFW_INCLUDE_VULKAN | |
#include <GLFW/glfw3.h> | |
#include <iostream> | |
#include <vector> | |
#include <stdexcept> | |
#include <functional> | |
#include <cstring> | |
#include <set> | |
const int WIDTH = 800; | |
const int HEIGHT = 600; | |
const std::vector<const char*> validationLayers = { | |
"VK_LAYER_LUNARG_standard_validation" | |
}; | |
const std::vector<const char*> deviceExtensions = { | |
VK_KHR_SWAPCHAIN_EXTENSION_NAME | |
}; | |
#ifdef NDEBUG | |
const bool enableValidationLayers = false; | |
#else | |
const bool enableValidationLayers = true; | |
#endif | |
/////////////////// Callbacks //////////////////// | |
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); | |
} | |
} | |
/////////////////Main bodies/////////////////// | |
// | |
struct QueueFamilyIndices { | |
int graphicsFamily = -1; | |
int presentFamily = -1; | |
bool isComplete() { | |
return graphicsFamily >= 0 && | |
presentFamily >=0; | |
} | |
}; | |
struct SwapChainSupportDetails { | |
VkSurfaceCapabilitiesKHR capabilities; | |
std::vector<VkSurfaceFormatKHR> formats; | |
std::vector<VkPresentModeKHR> presentModes; | |
}; | |
template <typename T> | |
class VDeleter { | |
public: | |
VDeleter() : VDeleter([](T _) {}) {} | |
VDeleter(std::function<void(T, VkAllocationCallbacks*)> deletef) { | |
object = VK_NULL_HANDLE; | |
this->deleter = [=](T obj) { deletef(obj, nullptr); }; | |
} | |
VDeleter(const VDeleter<VkInstance>& instance, std::function<void(VkInstance, T, VkAllocationCallbacks*)> deletef) { | |
object = VK_NULL_HANDLE; | |
this->deleter = [&instance, deletef](T obj) { deletef(instance, obj, nullptr); }; | |
} | |
VDeleter(const VDeleter<VkDevice>& device, std::function<void(VkDevice, T, VkAllocationCallbacks*)> deletef) { | |
object = VK_NULL_HANDLE; | |
this->deleter = [&device, deletef](T obj) { deletef(device, obj, nullptr); }; | |
} | |
~VDeleter() { | |
cleanup(); | |
} | |
T* operator &() { | |
cleanup(); | |
return &object; | |
} | |
operator T() const { | |
return object; | |
} | |
private: | |
T object; | |
std::function<void(T)> deleter; | |
void cleanup() { | |
if (object != VK_NULL_HANDLE) { | |
deleter(object); | |
} | |
object = VK_NULL_HANDLE; | |
} | |
}; | |
class HelloTriangleApplication { | |
public: | |
void run() { | |
initWindow(); | |
initVulkan(); | |
mainLoop(); | |
} | |
private: | |
VDeleter<VkInstance> instance {vkDestroyInstance}; | |
VDeleter<VkDevice> device{vkDestroyDevice}; | |
VDeleter<VkDebugReportCallbackEXT> callback{instance, DestroyDebugReportCallbackEXT}; | |
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; | |
VDeleter<VkSurfaceKHR> surface{instance, vkDestroySurfaceKHR}; | |
VkQueue presentQueue; | |
VkQueue graphicsQueue; | |
GLFWwindow* window; | |
std::vector<const char*> getRequiredExtensions() { | |
std::vector<const char*> extensions; | |
unsigned int glfwExtensionCount = 0; | |
const char** glfwExtensions; | |
glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); | |
for (unsigned int i = 0; i < glfwExtensionCount; i++) { | |
extensions.push_back(glfwExtensions[i]); | |
} | |
if (enableValidationLayers) { | |
extensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); | |
} | |
return extensions; | |
} | |
void initWindow() { | |
glfwInit(); | |
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); | |
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); | |
window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); | |
} | |
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 = (PFN_vkDebugReportCallbackEXT) debugCallback; | |
if (CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, &callback) != | |
VK_SUCCESS) | |
throw std::runtime_error("Failed to set up debug callback!"); | |
} | |
void initVulkan() { | |
createInstance(); | |
setupDebugCallback(); | |
createSurface(); | |
pickPhysicalDevice(); | |
createLogicalDevice(); | |
} | |
VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats) { | |
if (availableFormats.size() == 1 && availableFormats[0].format == VK_FORMAT_UNDEFINED) { | |
return {VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}; | |
} | |
for (const auto& availableFormat : availableFormats) { | |
if (availableFormat.format == VK_FORMAT_B8G8R8A8_UNORM && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { | |
return availableFormat; | |
} | |
} | |
return availableFormats[0]; | |
} | |
VkPresentModeKHR chooseSwapPresentMode(const std::vector<VkPresentModeKHR> availablePresentModes) { | |
for (const auto& availablePresentMode : availablePresentModes) { | |
if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) { | |
return availablePresentMode; | |
} | |
} | |
return VK_PRESENT_MODE_FIFO_KHR; | |
} | |
SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device) | |
{ | |
SwapChainSupportDetails details; | |
// Query capabilities | |
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities); | |
// Query formats | |
uint32_t formatCount; | |
vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr); | |
if (formatCount !=0 ) { | |
details.formats.resize(formatCount); | |
vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data()); | |
} | |
uint32_t presentModeCount; | |
vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr); | |
if (presentModeCount != 0) { | |
details.presentModes.resize(presentModeCount); | |
vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data()); | |
} | |
return details; | |
} | |
void createSurface(){ | |
if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS){ | |
throw std::runtime_error("failed to create window surface!"); | |
} | |
} | |
void createLogicalDevice(){ | |
// Create a queue | |
QueueFamilyIndices indices = findQueueFamilies(physicalDevice); | |
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos; | |
std::set<int> uniqueQueueFamilise = {indices.graphicsFamily, indices.presentFamily}; | |
for (int queueFamily: uniqueQueueFamilise) { | |
VkDeviceQueueCreateInfo queueCreateInfo = {}; | |
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; | |
queueCreateInfo.queueFamilyIndex = indices.graphicsFamily; | |
queueCreateInfo.queueCount = 1; | |
float queuePriority = 1.0f; | |
queueCreateInfo.pQueuePriorities = &queuePriority; | |
queueCreateInfos.push_back(queueCreateInfo); | |
} | |
VkPhysicalDeviceFeatures deviceFeatures = {}; | |
// Create logical device with a queue | |
VkDeviceCreateInfo createInfo = {}; | |
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; | |
createInfo.pQueueCreateInfos = queueCreateInfos.data(); | |
createInfo.queueCreateInfoCount = (uint32_t)queueCreateInfos.size(); | |
createInfo.pEnabledFeatures = &deviceFeatures; | |
// Add extensiosns | |
createInfo.enabledExtensionCount = deviceExtensions.size(); | |
createInfo.ppEnabledExtensionNames = deviceExtensions.data(); | |
// enable validation layer | |
if (enableValidationLayers) { | |
createInfo.enabledLayerCount = validationLayers.size(); | |
createInfo.ppEnabledLayerNames = validationLayers.data(); | |
} else { | |
createInfo.enabledLayerCount = 0; | |
} | |
// Instantiate logical device | |
if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { | |
throw std::runtime_error("failed to create logical device!"); | |
} | |
// Get queue handle | |
vkGetDeviceQueue(device, indices.graphicsFamily, 0, &graphicsQueue); | |
vkGetDeviceQueue(device, indices.presentFamily, 0, &presentQueue); | |
} | |
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) { | |
if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) { | |
indices.graphicsFamily = i; | |
} | |
VkBool32 presentSupport = false; | |
vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport); | |
if (queueFamily.queueCount > 0 && presentSupport) | |
indices.presentFamily = i; | |
if (indices.isComplete()) { | |
break; | |
} | |
i++; | |
} | |
return indices; | |
} | |
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()); | |
// Use the first device | |
for (const auto& device: devices){ | |
if (isDeviceSuitable(device)){ | |
physicalDevice = device; | |
break; | |
} | |
} | |
if (physicalDevice == VK_NULL_HANDLE){ | |
throw std::runtime_error("failed to find a suitable GPU!"); | |
} | |
} | |
/* | |
* Check if a physical device suitable for this application | |
*/ | |
bool isDeviceSuitable(VkPhysicalDevice device) | |
{ | |
QueueFamilyIndices indices = findQueueFamilies(device); | |
bool extensionSupported = checkDeviceExtensionSupport(device); | |
bool swapChainAdequate = false; | |
if (extensionSupported) { | |
SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device); | |
swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty(); | |
} | |
return indices.isComplete() && extensionSupported && swapChainAdequate; | |
} | |
bool checkDeviceExtensionSupport(VkPhysicalDevice device) { | |
uint32_t extensionCount; | |
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr); | |
std::vector<VkExtensionProperties> availableExtensions(extensionCount); | |
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data()); | |
std::set<std::string> requiredExtensions(deviceExtensions.begin(), deviceExtensions.end()); | |
for (const auto &extension: availableExtensions){ | |
requiredExtensions.erase(extension.extensionName); | |
} | |
return requiredExtensions.empty(); | |
} | |
bool checkValidationLayerSupport() { | |
/* | |
* Check validation layer support | |
*/ | |
uint32_t layerCount; | |
// Get number of layers | |
vkEnumerateInstanceLayerProperties(&layerCount, nullptr); | |
std::vector<VkLayerProperties> availableLayers(layerCount); | |
// Write validation stuff to vector | |
vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); | |
std::cout<< "Found "<<layerCount <<" layers\n"; | |
for (const auto & layerProperty: availableLayers) | |
std::cout<<"Available layer: "<<layerProperty.layerName<<std::endl; | |
// Check if validation layer is in available layers | |
for (const char* layerName : validationLayers) { | |
bool layerFound = false; | |
std::cout<<"Checking layer: "<<layerName<<std::endl; | |
for (const auto& layerProperties : availableLayers) { | |
if (strcmp(layerName, layerProperties.layerName) == 0) { | |
layerFound = true; | |
break; | |
} | |
} | |
if (!layerFound) { | |
return false; | |
} | |
} | |
return true; | |
} | |
void createInstance() | |
{ | |
if (enableValidationLayers && !checkValidationLayerSupport()) { | |
throw std::runtime_error("validation layers requested, but not available!"); | |
} | |
// Construct VkApplicationInfo struct | |
VkApplicationInfo appInfo = {}; | |
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; | |
appInfo.pApplicationName = "Hello Triangle"; | |
appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); | |
appInfo.pEngineName = "No Engine"; | |
appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); | |
appInfo.apiVersion = VK_API_VERSION_1_0; | |
// Construct VkInstanceCreateInfo struct with VkApplicationInfo | |
VkInstanceCreateInfo createInfo = {}; | |
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; | |
createInfo.pApplicationInfo = &appInfo; | |
// Get extensions | |
//unsigned int glfwExtensionCount = 0; | |
//const char** glfwExtensions; | |
//glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); | |
//// Query vkEumerateInstanceExtensionProperties | |
//uint32_t extensionCount = 0; | |
//vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr); | |
//std::vector<VkExtensionProperties> extensions(extensionCount); | |
//vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data()); | |
//std::cout<<"Aavailable extensions\n"; | |
//for (const auto &extension: extensions) | |
// std::cout<<"\t"<<extension.extensionName<<std::endl; | |
//// Assign extensions to createInfo | |
//createInfo.enabledExtensionCount = glfwExtensionCount; | |
//createInfo.ppEnabledExtensionNames = glfwExtensions; | |
auto extensions = getRequiredExtensions(); | |
createInfo.enabledExtensionCount = extensions.size(); | |
createInfo.ppEnabledExtensionNames = extensions.data(); | |
std::cout<<"Required glfw extensions:\n"; | |
for (const auto & extension: extensions) | |
std::cout<<extension<<std::endl; | |
// Validation layer? | |
if (enableValidationLayers){ | |
createInfo.enabledLayerCount = validationLayers.size(); | |
createInfo.ppEnabledLayerNames = validationLayers.data(); | |
} | |
else | |
createInfo.enabledLayerCount = 0; | |
// Finally, create the instance | |
if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { | |
throw std::runtime_error("failed to create instance!"); | |
} | |
} | |
void mainLoop() { | |
while (!glfwWindowShouldClose(window)) { | |
glfwPollEvents(); | |
} | |
} | |
////////////////// Callback functions | |
static VkBool32 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; | |
} | |
}; | |
int main() { | |
HelloTriangleApplication app; | |
try { | |
app.run(); | |
} catch (const std::runtime_error& e) { | |
std::cerr << e.what() << std::endl; | |
return EXIT_FAILURE; | |
} | |
return EXIT_SUCCESS; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment