#ifndef _VKSETUP_H
#define _VKSETUP_H
/* Start header file */
/**
vulkan setup and basic vector math
Single header file with included implementation in the spirit of
stb_* <https://github.com/nothings/stb>
ASSUMPTIONS:
~~~~~~~~~~~~
- Using SDL2 for the window
- Using cglm for maths
- Using shaderc for compiling glsl
- Using vulkan(!)
This is not meant to be a generic werapper around vulkan. It is actively
paired and chaned by the currently in development application, whatever that
might be. Think of it as the vulkan setup configuratior and helper.
USAGE:
~~~~~~
Do this:
#define VKSETUP_IMPLEMENTATION
before you include this file in *one* C or C++ file to create the implementation.
// i.e. it should look like this:
#include ...
#include ...
#include ...
#define VKSETUP_IMPLEMENTATION
#include "vksetup.h"
*/
#define SDL_MAIN_HANDLED
#define VK_USE_PLATFORM_XCB_KHR
#include <stdarg.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_vulkan.h>
#include <vulkan/vulkan.h>
#include <vulkan/vk_enum_string_helper.h> // for string_VkResult
#include <shaderc/shaderc.h>
#include "../lib/cglm/include/cglm/cglm.h"
#define VK_ARRAY_LEN(arr) sizeof((arr))/sizeof((arr)[0])
#ifdef __clang__
#define __FILENAME__ __FILE_NAME__
#else
#define __FILENAME__ __FILE__
#endif
#define VK_CHECK(x) \
do { \
VkResult err = x; \
if (err < 0) { \
fprintf(stderr, "src/%s:%d:0: vulkan error: %s \n", \
__FILENAME__, __LINE__, string_VkResult(err)); \
abort(); \
} \
} while (0)
typedef enum {
VK_INFO = 0,
VK_WARN,
VK_ERROR,
} log_type;
static inline void
_vk_log(log_type t, const char * f, ...)
{
#ifdef VKDEBUG
va_list args;
va_start(args, f);
switch (t) {
case VK_INFO:
printf("INFO: ");
vprintf(f, args);
break;
case VK_WARN:
fprintf(stderr, "WARN: ");
vfprintf(stderr, f, args);
break;
case VK_ERROR:
fprintf(stderr, "ERROR: ");
vfprintf(stderr, f, args);
break;
}
va_end(args);
#else
return;
#endif
}
#ifdef VKDEBUG
#define vk_log(t, ...) do { \
fprintf(t == VK_INFO ? stdout : stderr, "src/%s:%d:0: ", \
__FILENAME__, __LINE__); \
_vk_log(t, __VA_ARGS__); \
} while(0)
#else
#define vk_log(t, ...)
#endif
/**********/
/* config */
/**********/
const char *const validation_layers[] = {
"VK_LAYER_KHRONOS_validation"
};
const uint32_t validation_layer_count = VK_ARRAY_LEN(validation_layers);
const char *const device_extensions[] = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
};
const uint32_t deviceExtensionCount = VK_ARRAY_LEN(device_extensions);
#ifdef VKDEBUG
const bool enableValidationLayers = true;
#else
const bool enableValidationLayers = false;
#endif
#ifdef __cplusplus
extern "C" {
#endif
#ifndef VKSDEF
#ifdef VKS_STATIC
#define VKSDEF static
#else
#define VKSDEF extern
#endif
#endif
// TODO Create structs for vulkan data
typedef struct {void * a;} vks_vulkan;
typedef struct {void * a;} vks_image;
typedef struct {void * a;} vks_;
/**
Create a VkInstance
*/
VKSDEF VkInstance vks_create_instance(bool validation_layers_toggle, const char * const validation_layers[], uint32_t validation_layer_count, SDL_Window *window);
/* VKSDEF void vulkan_create_surface(); */
/* VKSDEF void vulkan_pick_physical_device(); */
/* VKSDEF void vulkan_create_logical_device(); */
/* VKSDEF void vulkan_create_swap_chain(); */
/* VKSDEF void vulkan_create_image_views(); */
/* VKSDEF void vulkan_create_descriptor_set_layout(); */
/* VKSDEF void vulkan_create_graphics_pipeline(); */
/* VKSDEF void vulkan_create_command_pool(); */
/* VKSDEF void vulkan_create_depth_resources(); */
/* VKSDEF void vulkan_create_texture_image(); */
/* VKSDEF void vulkan_create_texture_image_view(); */
/* VKSDEF void vulkan_create_texture_sampler(); */
/* VKSDEF void vulkan_create_vertex_buffer(); */
/* VKSDEF void vulkan_create_index_buffer(); */
/* VKSDEF void vulkan_create_uniform_buffers(); */
/* VKSDEF void vulkan_create_descriptor_pool(); */
/* VKSDEF void vulkan_create_descriptor_sets(); */
/* VKSDEF void vulkan_create_command_buffer(); */
/* VKSDEF void vulkan_create_sync_objects(); */
#ifdef __cplusplus
}
#endif
/* End header file */
#endif /* _VKSETUP_H */
#ifdef VKSETUP_IMPLEMENTATION
#include <stddef.h>
#include <stdlib.h>
#include <time.h>
/* Vks helpers */
bool
vks_check_validation_layer_support(const char * const validation_layers[], uint32_t validation_layer_count)
{
uint32_t layerCount;
vkEnumerateInstanceLayerProperties(&layerCount, NULL);
VkLayerProperties availableLayers[layerCount];
vkEnumerateInstanceLayerProperties(&layerCount, availableLayers);
bool layerFound = false;
for (uint32_t i = 0; i < validation_layer_count; i++) {
for (uint32_t j = 0; j < layerCount; j++) {
if (strcmp(validation_layers[i], availableLayers[j].layerName) == 0) {
layerFound = true;
break;
}
}
}
return layerFound;
}
/* Vks API implementation */
VKSDEF VkInstance
vks_create_instance(bool validation_layers_toggle, const char * const validation_layers[],
uint32_t validation_layer_count, SDL_Window *window)
{
if (validation_layers_toggle && !vks_check_validation_layer_support(validation_layers, validation_layer_count)) {
vk_log(VK_ERROR, "validation layers requested, but not available!\n");
abort();
}
uint32_t instanceVersion;
VkResult result = vkEnumerateInstanceVersion(&instanceVersion);
if (result == VK_SUCCESS) {
if (instanceVersion < VK_MAKE_API_VERSION(0, 1, 3, 0)) {
vk_log(VK_ERROR, "Vulkan version 1.3 or greater required!\n");
exit(1);
}
vk_log(VK_INFO, "Vulkan version found (%d) %d.%d.%d\n",
VK_API_VERSION_VARIANT(instanceVersion),
VK_API_VERSION_MAJOR(instanceVersion),
VK_API_VERSION_MINOR(instanceVersion),
VK_API_VERSION_PATCH(instanceVersion));
} else {
vk_log(VK_ERROR, "Failed to retrieve vulkan version, is vulkan supported in this system?\n");
exit(1);
}
// Load Vulkan and create instance
VkApplicationInfo appInfo = {
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
.pApplicationName = "Vulkan Application",
.applicationVersion = VK_MAKE_API_VERSION(0, 1, 3, 0),
.pEngineName = NULL,
.engineVersion = VK_MAKE_API_VERSION(0, 1, 3, 0),
.apiVersion = VK_MAKE_API_VERSION(0, 1, 3, 0),
};
uint32_t sdlExtensionCount = 0;
if (SDL_Vulkan_GetInstanceExtensions(window, &sdlExtensionCount, NULL) == SDL_FALSE) {
vk_log(VK_ERROR, "SDL_Vulkan_GetInstanceExtensions failed: %s\n", SDL_GetError());
abort();
}
// make space for debug extenetion
if (validation_layers_toggle) {
sdlExtensionCount++;
}
const char* sdlExtensions[sdlExtensionCount];
if (SDL_Vulkan_GetInstanceExtensions(window, &sdlExtensionCount, sdlExtensions) == SDL_FALSE) {
vk_log(VK_ERROR, "SDL_Vulkan_GetInstanceExtensions failed: %s\n", SDL_GetError());
abort();
}
// add debug extenetion
if (validation_layers_toggle) {
sdlExtensions[sdlExtensionCount] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME;
}
vk_log(VK_INFO, "The sdl extensions:\n");
for (uint32_t i = 0; i < sdlExtensionCount; i++) {
vk_log(VK_INFO, "\t%s\n", sdlExtensions[i]);
}
VkInstanceCreateInfo createInfo = {
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pApplicationInfo = &appInfo,
.enabledExtensionCount = sdlExtensionCount,
.ppEnabledExtensionNames = sdlExtensions,
.enabledLayerCount = 0,
};
if (validation_layers_toggle) {
createInfo.enabledLayerCount = validation_layer_count;
createInfo.ppEnabledLayerNames = validation_layers;
}
VkInstance instance;
if (vkCreateInstance(&createInfo, NULL, &instance) != VK_SUCCESS) {
vk_log(VK_ERROR, "Can't start vulkan instance\n");
}
vk_log(VK_INFO, "Vulkan instance created\n");
return instance;
}
#endif /* VKSETUP_IMPLEMENTATION */