Skip to content

Instantly share code, notes, and snippets.

@nickelpro
Last active April 18, 2019 23:55
Show Gist options
  • Save nickelpro/28e86c67daf88807ea0e40c49cdf18d5 to your computer and use it in GitHub Desktop.
Save nickelpro/28e86c67daf88807ea0e40c49cdf18d5 to your computer and use it in GitHub Desktop.
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#include <stdio.h>
#include <string.h>
#include <graphene-1.0/graphene.h>
#include "vgfx.h"
#include "logc/log.h"
#include "par/par_shapes.h"
#include "sds/sds.h"
//Utility stuff
enum vgfx_errors {
VGFX_SUCCESS = 0,
VGFX_FAIL,
VGFX_MEM_FAIL,
VGFX_NO_LUNARG,
};
char *pmode2str(VkPresentModeKHR pmode) {
switch(pmode) {
case VK_PRESENT_MODE_IMMEDIATE_KHR:
return "Immediate";
case VK_PRESENT_MODE_MAILBOX_KHR:
return "Mailbox";
case VK_PRESENT_MODE_FIFO_KHR:
return "FIFO";
case VK_PRESENT_MODE_FIFO_RELAXED_KHR:
return "FIFO Relaxed";
default:
return "Unkown";
}
}
char *vkr2str(VkResult err) {
switch(err) {
case VK_SUCCESS:
return "Success";
case VK_NOT_READY:
return "Not Ready";
case VK_TIMEOUT:
return "Timeout";
case VK_EVENT_SET:
return "Event Set";
case VK_EVENT_RESET:
return "Event Reset";
case VK_INCOMPLETE:
return "Incomplete";
case VK_ERROR_OUT_OF_HOST_MEMORY:
return "Out of Host Memory";
case VK_ERROR_OUT_OF_DEVICE_MEMORY:
return "Out of Device Memory";
case VK_ERROR_INITIALIZATION_FAILED:
return "Initialization Failed";
case VK_ERROR_DEVICE_LOST:
return "Device Lost";
case VK_ERROR_MEMORY_MAP_FAILED:
return "Memory Map Failed";
case VK_ERROR_LAYER_NOT_PRESENT:
return "Layer Not Present";
case VK_ERROR_EXTENSION_NOT_PRESENT:
return "Extension Not Present";
case VK_ERROR_FEATURE_NOT_PRESENT:
return "Feature Not Present";
case VK_ERROR_INCOMPATIBLE_DRIVER:
return "Incompatible Driver";
case VK_ERROR_TOO_MANY_OBJECTS:
return "Too Many Objects";
case VK_ERROR_FORMAT_NOT_SUPPORTED:
return "Format Not Supported";
case VK_ERROR_FRAGMENTED_POOL:
return "Fragmented Pool";
case VK_ERROR_OUT_OF_POOL_MEMORY:
return "Out of Pool Memory";
case VK_ERROR_INVALID_EXTERNAL_HANDLE:
return "Invalid External Handle";
case VK_ERROR_SURFACE_LOST_KHR:
return "Surface Lost";
case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR:
return "Native Window in Use";
case VK_SUBOPTIMAL_KHR:
return "Suboptimal";
case VK_ERROR_OUT_OF_DATE_KHR:
return "Out of Date";
case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR:
return "Incompatible Display";
case VK_ERROR_VALIDATION_FAILED_EXT:
return "Validation Failed";
case VK_ERROR_INVALID_SHADER_NV:
return "Invalid Shader";
case VK_ERROR_FRAGMENTATION_EXT:
return "Fragmentation";
case VK_ERROR_NOT_PERMITTED_EXT:
return "Not Permitted";
default:
return "Unknown";
}
}
int read_file(const char *path, sds *out) {
*out = NULL;
FILE *f = fopen(path, "rb");
if(!f) goto failed_open;
if(fseek(f, 0, SEEK_END)) goto borked;
long length = ftell(f);
if(length < 0) goto borked;
if(!(*out = sdsnewlen(NULL, (size_t) length))) goto borked;
if(fseek(f, 0, SEEK_SET)) goto borked;
if(fread(*out, 1, length, f) != (size_t) length) goto borked;
fclose(f);
return VGFX_SUCCESS;
borked:
sdsfree(*out);
fclose(f);
failed_open:
log_error("Failed to read: %s", path);
return VGFX_FAIL;
}
int read_spirv(vk_spirvbuf_t *buf, const char *path) {
buf->base = NULL;
FILE *f = fopen(path, "rb");
if(!f) goto failed_open;
if(fseek(f, 0, SEEK_END)) goto borked;
long length = ftell(f);
if(length < 0 || (length % 4)) goto borked;
if(!(buf->base = malloc(length))) goto borked;
if(fseek(f, 0, SEEK_SET)) goto borked;
if(fread((char *) buf->base, 1, length, f) != (size_t) length) goto borked;
fclose(f);
buf->len = (size_t) length;
return VGFX_SUCCESS;
borked:
fclose(f);
free(buf->base);
failed_open:
log_error("Failed to read/invalid spirv: %s", path);
return VGFX_FAIL;
}
void destroy_spirv(vk_spirvbuf_t buf) {
free(buf.base);
}
//Debugging Callbacks
VKAPI_ATTR VkBool32 VKAPI_CALL vk_debug_cb(
VkDebugUtilsMessageSeverityFlagBitsEXT sev,
VkDebugUtilsMessageTypeFlagsEXT type,
const VkDebugUtilsMessengerCallbackDataEXT *cb_data,
void *user_data
) {
int is_simple = log_is_simple();
log_set_simple(1);
static const char *const s = "Vulkan Debug: %s";
switch(sev) {
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT:
log_trace(s, cb_data->pMessage);
break;
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT:
log_info(s, cb_data->pMessage);
break;
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT:
log_warn(s, cb_data->pMessage);
break;
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:
log_error(s, cb_data->pMessage);
break;
default:
log_error("Invalid Vulkan Error Level: %s", cb_data->pMessage);
break;
};
log_set_simple(is_simple);
return VK_FALSE;
}
VkResult create_vk_debug_msg(
VkInstance instance,
const VkDebugUtilsMessengerCreateInfoEXT *create_info,
const VkAllocationCallbacks *allocator,
VkDebugUtilsMessengerEXT *cb
) {
PFN_vkCreateDebugUtilsMessengerEXT f = (
PFN_vkCreateDebugUtilsMessengerEXT
) vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT");
if(!f) {
log_error("Failed to get proc addr for vkCreateDebugUtilsMessengerEXT");
return VK_ERROR_EXTENSION_NOT_PRESENT;
}
return f(instance, create_info, allocator, cb);
}
void destroy_vk_debug_msg(
VkInstance instance,
VkDebugUtilsMessengerEXT cb,
const VkAllocationCallbacks* allocator
) {
PFN_vkDestroyDebugUtilsMessengerEXT f = (
PFN_vkDestroyDebugUtilsMessengerEXT
) vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT");
if(f) f(instance, cb, allocator);
}
void glfw_error_cb(int err, const char *desc) {
log_error("GLFW Error: %s", desc);
}
void glfw_size_cb(GLFWwindow *window, int width, int height) {
log_trace("Resize Width: %d Height: %d", width, height);
}
int get_vk_validation(uint32_t *layer_count, VkLayerProperties *layer_props) {
log_debug("Enabling vulkan validation layers");
VkResult err = vkEnumerateInstanceLayerProperties(layer_count, NULL);
if(err != VK_SUCCESS) goto borked;
bool found_lunarg_validation = false;
if(*layer_count) {
layer_props = malloc(*layer_count * sizeof(*layer_props));
if(!layer_props) {
log_error("Failed to allocate memory for layer_props");
return VGFX_MEM_FAIL;
}
err = vkEnumerateInstanceLayerProperties(layer_count, layer_props);
if(err != VK_SUCCESS) goto borked;
sds s = sdsnew("Discovered the following layers:");
for(unsigned int i = 0; i < *layer_count; i++) {
s = sdscatfmt(s, "\n\t%s", layer_props[i].layerName);
if(!strcmp(
layer_props[i].layerName,
"VK_LAYER_LUNARG_standard_validation"
)) found_lunarg_validation = true;
}
log_debug(s);
sdsfree(s);
} else {
log_debug("No vulkan validation layers found");
}
if(!found_lunarg_validation) return VGFX_NO_LUNARG;
return VGFX_SUCCESS;
borked:
log_error("Vulcan layer enumeration failed on: %s", vkr2str(err));
return VGFX_FAIL;
}
int init_vk_instance(vk_instance_t *instance, int use_validation) {
log_debug("Creating vulkan instance");
struct {
uint32_t count;
const char** names;
} req_exts, tot_exts, layers;
VkResult err;
req_exts.names = glfwGetRequiredInstanceExtensions(&req_exts.count);
tot_exts = req_exts;
layers.count = 0;
layers.names = NULL;
if(use_validation) {
//This is dumb, we're only looking for the return value, the function
//parameters are ignored in all cases. Eventually this should test for more
//than just the standard validation layer, in which case that free is gonna
//have to move.
VkLayerProperties *layer_props = NULL;
int ret = get_vk_validation(&layers.count, layer_props);
free(layer_props);
if(ret == VGFX_SUCCESS) {
layers.count = 1;
layers.names = &(const char *) {"VK_LAYER_LUNARG_standard_validation"};
tot_exts.names = malloc(++tot_exts.count * sizeof(*tot_exts.names));
if(!tot_exts.names) {
log_error("Failed to allocate memory for tot_exts.names");
return VGFX_MEM_FAIL;
}
memcpy(
tot_exts.names,
req_exts.names,
req_exts.count * sizeof(*req_exts.names)
);
tot_exts.names[req_exts.count] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME;
} else {
layers.count = 0;
log_error("No lunarg validation layer found");
}
}
err = vkCreateInstance(
&(VkInstanceCreateInfo) {
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pNext = NULL,
.flags = 0,
.enabledLayerCount = layers.count,
.ppEnabledLayerNames = layers.names,
.enabledExtensionCount = tot_exts.count,
.ppEnabledExtensionNames = tot_exts.names,
.pApplicationInfo = &(VkApplicationInfo) {
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
.pNext = NULL,
.pApplicationName = NULL,
.applicationVersion = 0,
.pEngineName = NULL,
.engineVersion = 0,
.apiVersion = VK_API_VERSION_1_1
}
},
NULL,
&instance->handle
);
if(tot_exts.names != req_exts.names) free(tot_exts.names);
if(err != VK_SUCCESS) goto borked;
if(use_validation) {
err = create_vk_debug_msg(
instance->handle,
&(const VkDebugUtilsMessengerCreateInfoEXT) {
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
.pNext = NULL,
.flags = 0,
.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT \
| VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT \
| VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT \
| VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT \
| VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT \
| VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT,
.pfnUserCallback = vk_debug_cb,
.pUserData = NULL
},
NULL,
&instance->debug_cb
);
if(err != VK_SUCCESS) goto borked;
}
return VGFX_SUCCESS;
borked:
log_error("Vulkan instancing failed on: %s", vkr2str(err));
return VGFX_FAIL;
}
void destroy_vk_instance(vk_instance_t instance) {
destroy_vk_debug_msg(instance.handle, instance.debug_cb, NULL);
vkDestroyInstance(instance.handle, NULL);
}
int init_vk_surface(
vk_surface_t *surface,
vk_instance_t instance,
GLFWwindow *window
) {
VkResult err = glfwCreateWindowSurface(
instance.handle,
window,
NULL,
&surface->handle
);
if(err != VK_SUCCESS) {
log_error("Surface creation failed on: %s", vkr2str(err));
return VGFX_FAIL;
}
surface->window = window;
return VGFX_SUCCESS;
}
void destroy_vk_surface(vk_surface_t surface, vk_instance_t instance) {
vkDestroySurfaceKHR(instance.handle, surface.handle, NULL);
}
int init_vk_devices(vk_devices_t *devices, vk_instance_t instance) {
*devices = (const vk_devices_t) { 0 };
sds s = sdsnew("Discovered the following devices:");
VkResult err = vkEnumeratePhysicalDevices(
instance.handle,
&devices->count,
NULL
);
if(err != VK_SUCCESS) goto borked;
if(!devices->count) {
log_error("Vulkan reports no available physical devices");
return VGFX_FAIL;
}
devices->handles = malloc(devices->count * sizeof(*devices->handles));
if(!devices->handles) goto borked_malloc;
devices->props = malloc(devices->count * sizeof(*devices->props));
if(!devices->props) goto borked_malloc;
devices->feats = malloc(devices->count * sizeof(*devices->feats));
if(!devices->feats) goto borked_malloc;
if((devices->qfams = malloc(devices->count * sizeof(*devices->qfams))))
for(unsigned int i = 0; i < devices->count; i++)
devices->qfams[i].props = NULL;
else goto borked_malloc;
if((devices->exts = malloc(devices->count * sizeof(*devices->exts))))
for(unsigned int i = 0; i < devices->count; i++)
devices->exts[i].props = NULL;
else goto borked_malloc;
err = vkEnumeratePhysicalDevices(
instance.handle,
&devices->count,
devices->handles
);
if(err != VK_SUCCESS) goto borked;
for(unsigned int i = 0; i < devices->count; i++) {
vkGetPhysicalDeviceProperties(devices->handles[i], &devices->props[i]);
vkGetPhysicalDeviceFeatures(devices->handles[i], &devices->feats[i]);
vkGetPhysicalDeviceQueueFamilyProperties(
devices->handles[i],
&devices->qfams[i].count,
NULL
);
devices->qfams[i].props = malloc(
devices->qfams[i].count * sizeof(*devices->qfams[i].props)
);
if(!devices->qfams[i].props) goto borked_malloc;
vkGetPhysicalDeviceQueueFamilyProperties(
devices->handles[i],
&devices->qfams[i].count,
devices->qfams[i].props
);
s = sdscatfmt(s, "\n\t%s", devices->props[i].deviceName);
err = vkEnumerateDeviceExtensionProperties(
devices->handles[i],
NULL,
&devices->exts[i].count,
NULL
);
if(err != VK_SUCCESS) goto borked;
devices->exts[i].props = malloc(
devices->exts[i].count * sizeof(*devices->exts[i].props)
);
if(!devices->exts[i].props) goto borked_malloc;
err = vkEnumerateDeviceExtensionProperties(
devices->handles[i],
NULL,
&devices->exts[i].count,
devices->exts[i].props
);
if(err != VK_SUCCESS) goto borked;
}
log_debug(s);
sdsfree(s);
return VGFX_SUCCESS;
borked_malloc:
log_error("Failed to allocate memory for devices");
destroy_vk_devices(*devices);
sdsfree(s);
return VGFX_MEM_FAIL;
borked:
log_error("Vulkan device enumeration failed on: %s", vkr2str(err));
destroy_vk_devices(*devices);
sdsfree(s);
return VGFX_FAIL;
}
void destroy_vk_devices(vk_devices_t devices) {
free(devices.handles);
free(devices.props);
free(devices.feats);
for(unsigned int i = 0; i < devices.count; i++) {
free(devices.qfams[i].props);
free(devices.exts[i].props);
}
free(devices.qfams);
free(devices.exts);
}
//Select a device and queue family for a given surface
int init_vk_seldev(
vk_seldev_t *seldev,
vk_devices_t devices,
vk_surface_t surface
) {
VkResult err;
int success;
*seldev = (const vk_seldev_t) {0};
for(unsigned int i = 0, j; i < devices.count; i++) {
for(j = 0, success = 0; j < devices.qfams[i].count; j++) {
VkBool32 surface_support;
err = vkGetPhysicalDeviceSurfaceSupportKHR(
devices.handles[i],
j,
surface.handle,
&surface_support
);
if(err != VK_SUCCESS) goto borked;
if(
devices.qfams[i].props[j].queueFlags & VK_QUEUE_GRAPHICS_BIT &&
surface_support == VK_TRUE //Supposedly this implies swapchain support
) {
seldev->qfam_idx = j;
seldev->qfam_props = devices.qfams[i].props[j];
success = 1;
break;
}
}
if(!success) continue;
//Explicitly verify swapchain support
for(j = 0, success = 0; j < devices.exts[i].count; j++) {
if(!strcmp(
devices.exts[i].props[j].extensionName,
VK_KHR_SWAPCHAIN_EXTENSION_NAME
)) {
success = 1;
break;
}
}
if(!success) continue;
success = 0;
err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
devices.handles[i],
surface.handle,
&seldev->caps
);
if(err != VK_SUCCESS) goto borked;
//Verify at least one surface format
err = vkGetPhysicalDeviceSurfaceFormatsKHR(
devices.handles[i],
surface.handle,
&seldev->formats_count,
NULL
);
if(err != VK_SUCCESS) goto borked;
if(!seldev->formats_count) continue;
seldev->formats = malloc(seldev->formats_count * sizeof(*seldev->formats));
if(!seldev->formats) goto borked_malloc;
err = vkGetPhysicalDeviceSurfaceFormatsKHR(
devices.handles[i],
surface.handle,
&seldev->formats_count,
seldev->formats
);
if(err != VK_SUCCESS) goto borked;
//Verify at least one present mode
err = vkGetPhysicalDeviceSurfacePresentModesKHR(
devices.handles[i],
surface.handle,
&seldev->pmodes_count,
NULL
);
if(err != VK_SUCCESS) goto borked;
if(!seldev->pmodes_count) {
free(seldev->formats);
seldev->formats = NULL;
continue;
}
seldev->pmodes = malloc(seldev->pmodes_count * sizeof(*seldev->pmodes));
if(!seldev->pmodes) goto borked_malloc;
err = vkGetPhysicalDeviceSurfacePresentModesKHR(
devices.handles[i],
surface.handle,
&seldev->pmodes_count,
seldev->pmodes
);
if(err != VK_SUCCESS) goto borked;
seldev->handle = devices.handles[i];
seldev->dev_props = devices.props[i];
seldev->feats = devices.feats[i];
success = 1;
break;
}
if(success) log_debug(
"Selected device: %s qfam: %d",
seldev->dev_props.deviceName,
seldev->qfam_idx
);
return success ? VGFX_SUCCESS : VGFX_FAIL;
borked_malloc:
log_error("Failed to allocate memory for seldev");
destroy_vk_seldev(*seldev);
return VGFX_MEM_FAIL;
borked:
log_error("Vulkan device selection failed on: %s", vkr2str(err));
destroy_vk_seldev(*seldev);
return VGFX_FAIL;
}
void destroy_vk_seldev(vk_seldev_t seldev) {
free(seldev.formats);
free(seldev.pmodes);
}
int init_vk_logicdev(
vk_logicdev_t *logicdev,
vk_seldev_t seldev
) {
VkResult err = vkCreateDevice(
seldev.handle,
&(const VkDeviceCreateInfo) {
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.pNext = NULL,
.flags = 0,
.queueCreateInfoCount = 1,
.pQueueCreateInfos = &(const VkDeviceQueueCreateInfo) {
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
.pNext = NULL,
.flags = 0,
.queueFamilyIndex = seldev.qfam_idx,
.queueCount = 1,
.pQueuePriorities = &(const float) {1.0}
},
.enabledLayerCount = 0,
.ppEnabledLayerNames = NULL,
.enabledExtensionCount = 1,
.ppEnabledExtensionNames = &(const char *const) {
VK_KHR_SWAPCHAIN_EXTENSION_NAME
},
.pEnabledFeatures = &seldev.feats
},
NULL,
&logicdev->handle
);
if(err != VK_SUCCESS) goto borked;
vkGetDeviceQueue(logicdev->handle, seldev.qfam_idx, 0, &logicdev->q);
return VGFX_SUCCESS;
borked:
log_error("Vulkan logical device creation failed on: %s", vkr2str(err));
return VGFX_FAIL;
}
void destroy_vk_logicdev(vk_logicdev_t logicdev) {
vkDestroyDevice(logicdev.handle, NULL);
}
int init_vk_swapchain(
vk_swapchain_t *swapchain,
vk_surface_t surface,
vk_seldev_t seldev,
vk_logicdev_t logicdev
) {
swapchain->image_handles = NULL;
swapchain->logicdev = logicdev;
if(
seldev.formats_count == 1 &&
seldev.formats[0].format == VK_FORMAT_UNDEFINED
) {
swapchain->info.imageFormat = VK_FORMAT_B8G8R8A8_UNORM;
swapchain->info.imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
} else {
swapchain->info.imageFormat = seldev.formats[0].format;
swapchain->info.imageColorSpace = seldev.formats[0].colorSpace;
for(unsigned int i = 0; i < seldev.formats_count; i++) {
if(
seldev.formats[i].format == VK_FORMAT_B8G8R8A8_UNORM &&
seldev.formats[i].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
) {
swapchain->info.imageFormat = VK_FORMAT_B8G8R8A8_UNORM;
swapchain->info.imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
break;
}
}
}
swapchain->info.presentMode = VK_PRESENT_MODE_FIFO_KHR;
for(unsigned int i = 0; i < seldev.pmodes_count; i++) {
log_debug(
"Discovered the following Present Mode: %s",
pmode2str(seldev.pmodes[i])
);
if(seldev.pmodes[i] == VK_PRESENT_MODE_MAILBOX_KHR) {
swapchain->info.presentMode = VK_PRESENT_MODE_MAILBOX_KHR;
break;
} else if(seldev.pmodes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR)
swapchain->info.presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
}
log_debug(
"Selected the %s Present Mode",
pmode2str(swapchain->info.presentMode)
);
if(seldev.caps.currentExtent.width != UINT32_MAX) {
swapchain->info.imageExtent = seldev.caps.currentExtent;
} else {
uint32_t w, h;
glfwGetFramebufferSize(surface.window, (int *) &w, (int *) &h);
w = w < seldev.caps.maxImageExtent.width ?
w : seldev.caps.maxImageExtent.width;
w = w > seldev.caps.minImageExtent.width ?
w : seldev.caps.maxImageExtent.width;
h = h < seldev.caps.maxImageExtent.height ?
h : seldev.caps.maxImageExtent.height;
h = h > seldev.caps.minImageExtent.height ?
h : seldev.caps.maxImageExtent.height;
swapchain->info.imageExtent.width = w;
swapchain->info.imageExtent.height = h;
}
if(
swapchain->info.presentMode == VK_PRESENT_MODE_MAILBOX_KHR &&
seldev.caps.minImageCount != seldev.caps.maxImageCount
) swapchain->info.minImageCount = seldev.caps.minImageCount + 1;
else swapchain->info.minImageCount = seldev.caps.minImageCount;
swapchain->info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
swapchain->info.pNext = NULL;
swapchain->info.flags = 0;
swapchain->info.surface = surface.handle;
swapchain->info.imageArrayLayers = 1;
swapchain->info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
swapchain->info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
swapchain->info.queueFamilyIndexCount = 0;
swapchain->info.pQueueFamilyIndices = NULL;
swapchain->info.preTransform = seldev.caps.currentTransform;
swapchain->info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
swapchain->info.clipped = VK_TRUE;
swapchain->info.oldSwapchain = VK_NULL_HANDLE;
VkResult err = vkCreateSwapchainKHR(
logicdev.handle,
&swapchain->info,
NULL,
&swapchain->handle
);
if(err != VK_SUCCESS) goto borked;
err = vkGetSwapchainImagesKHR(
logicdev.handle,
swapchain->handle,
&swapchain->image_count,
NULL
);
if(err != VK_SUCCESS) goto borked2;
if(!(swapchain->image_handles = malloc(
swapchain->image_count * sizeof(*swapchain->image_handles)
))) {
log_error("Failed to allocate memory for swapchain images");
destroy_vk_swapchain(*swapchain);
return VGFX_MEM_FAIL;
}
err = vkGetSwapchainImagesKHR(
logicdev.handle,
swapchain->handle,
&swapchain->image_count,
swapchain->image_handles
);
if(err != VK_SUCCESS) goto borked2;
return VGFX_SUCCESS;
borked2:
destroy_vk_swapchain(*swapchain);
borked:
log_error("Vulkan swapchain creation failed on: %s", vkr2str(err));
return VGFX_FAIL;
}
void destroy_vk_swapchain(vk_swapchain_t swapchain) {
free(swapchain.image_handles);
vkDestroySwapchainKHR(swapchain.logicdev.handle, swapchain.handle, NULL);
}
int init_vk_imageviews(vk_imageviews_t *imageviews, vk_swapchain_t swapchain) {
imageviews->handles = NULL;
imageviews->count = swapchain.image_count;
imageviews->logicdev = swapchain.logicdev;
if(!(imageviews->handles = malloc(
imageviews->count * sizeof(*imageviews->handles)
))) {
log_error("Failed to allocate memory for image views");
return VGFX_MEM_FAIL;
}
for(unsigned int i = 0; i < imageviews->count; i++) {
VkResult err = vkCreateImageView(
imageviews->logicdev.handle,
&(const VkImageViewCreateInfo) {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.pNext = NULL,
.flags = 0,
.image = swapchain.image_handles[i],
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = swapchain.info.imageFormat,
.components = (const VkComponentMapping) {
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
.g = VK_COMPONENT_SWIZZLE_IDENTITY,
.b = VK_COMPONENT_SWIZZLE_IDENTITY,
.a = VK_COMPONENT_SWIZZLE_IDENTITY
},
.subresourceRange = (const VkImageSubresourceRange) {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1
}
},
NULL,
&imageviews->handles[i]
);
if(err != VK_SUCCESS) {
imageviews->count = i;
destroy_vk_imageviews(*imageviews);
log_error("Vulkan imageview creation failed on: %s", vkr2str(err));
return VGFX_FAIL;
}
}
return VGFX_SUCCESS;
}
void destroy_vk_imageviews(vk_imageviews_t imageviews) {
if(imageviews.handles) {
for(unsigned int i = 0; i < imageviews.count; i++) {
vkDestroyImageView(
imageviews.logicdev.handle,
imageviews.handles[i],
NULL
);
}
free(imageviews.handles);
}
}
int init_vk_renderpass(vk_renderpass_t *renderpass, vk_swapchain_t swapchain) {
renderpass->swapchain = swapchain;
VkResult err = vkCreateRenderPass(
swapchain.logicdev.handle,
&(const VkRenderPassCreateInfo) {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
.pNext = NULL,
.flags = 0,
.attachmentCount = 1,
.pAttachments = &(const VkAttachmentDescription) {
.flags = 0,
.format = swapchain.info.imageFormat,
.samples = VK_SAMPLE_COUNT_1_BIT,
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
},
.subpassCount = 1,
.pSubpasses = &(const VkSubpassDescription) {
.flags = 0,
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
.inputAttachmentCount = 0,
.pInputAttachments = NULL,
.colorAttachmentCount = 1,
.pColorAttachments = &(const VkAttachmentReference) {
.attachment = 0,
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
},
.pResolveAttachments = NULL,
.pDepthStencilAttachment = NULL,
.preserveAttachmentCount = 0,
.pPreserveAttachments = NULL,
},
.dependencyCount = 0,
.pDependencies = NULL
},
NULL,
&renderpass->handle
);
if(err != VK_SUCCESS) return VGFX_FAIL;
return VGFX_SUCCESS;
}
void destroy_vk_renderpass(vk_renderpass_t renderpass) {
vkDestroyRenderPass(
renderpass.swapchain.logicdev.handle,
renderpass.handle,
NULL
);
}
int init_vk_gpipe(
vk_gpipe_t *gpipe,
vk_swapchain_t swapchain,
vk_renderpass_t renderpass
) {
gpipe->swapchain = swapchain;
gpipe->renderpass = renderpass;
vk_spirvbuf_t vert_code, frag_code;
if(read_spirv(&vert_code, "shaders/shader.vert.spv")) goto borked_noerr;
if(read_spirv(&frag_code, "shaders/shader.frag.spv")) {
destroy_spirv(vert_code);
goto borked_noerr;
}
VkShaderModule vert, frag;
VkResult err = vkCreateShaderModule(
swapchain.logicdev.handle,
&(const VkShaderModuleCreateInfo) {
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
.pNext = NULL,
.flags = 0,
.codeSize = vert_code.len,
.pCode = vert_code.base
},
NULL,
&vert
);
destroy_spirv(vert_code);
if(err != VK_SUCCESS) {
destroy_spirv(frag_code);
goto borked_noclean;
}
err = vkCreateShaderModule(
swapchain.logicdev.handle,
&(const VkShaderModuleCreateInfo) {
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
.pNext = NULL,
.flags = 0,
.codeSize = frag_code.len,
.pCode = frag_code.base
},
NULL,
&frag
);
destroy_spirv(frag_code);
if(err != VK_SUCCESS) {
vkDestroyShaderModule(swapchain.logicdev.handle, vert, NULL);
goto borked_noclean;
}
VkPipelineShaderStageCreateInfo shader_stages[] = {
//Vertex
(const VkPipelineShaderStageCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.pNext = NULL,
.flags = 0,
.stage = VK_SHADER_STAGE_VERTEX_BIT,
.module = vert,
.pName = "main",
.pSpecializationInfo = NULL
},
//Fragment
(const VkPipelineShaderStageCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.pNext = NULL,
.flags = 0,
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
.module = frag,
.pName = "main",
.pSpecializationInfo = NULL
}
};
err = vkCreatePipelineLayout(
swapchain.logicdev.handle,
&(const VkPipelineLayoutCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.pNext = NULL,
.flags = 0,
.setLayoutCount = 0,
.pSetLayouts = NULL,
.pushConstantRangeCount = 0,
.pPushConstantRanges = NULL
},
NULL,
&gpipe->layout_handle
);
if(err != VK_SUCCESS) goto borked_layout;
err = vkCreateGraphicsPipelines(
swapchain.logicdev.handle,
VK_NULL_HANDLE,
1,
&(const VkGraphicsPipelineCreateInfo) {
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = NULL,
.flags = 0,
.stageCount = 2,
.pStages = shader_stages,
.pVertexInputState = &(const VkPipelineVertexInputStateCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.pNext = NULL,
.flags = 0,
.vertexBindingDescriptionCount = 0,
.pVertexBindingDescriptions = NULL,
.vertexAttributeDescriptionCount = 0,
.pVertexAttributeDescriptions = NULL
},
.pInputAssemblyState = &(const VkPipelineInputAssemblyStateCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.pNext = NULL,
.flags = 0,
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
.primitiveRestartEnable = VK_FALSE
},
.pTessellationState = NULL,
.pViewportState = &(const VkPipelineViewportStateCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pNext = NULL,
.flags = 0,
.viewportCount = 1,
.pViewports = &(const VkViewport) {
.x = 0.0f,
.y = 0.0f,
.width = (float) swapchain.info.imageExtent.width,
.height = (float) swapchain.info.imageExtent.height,
.minDepth = 0.0f,
.maxDepth = 1.0f
},
.scissorCount = 1,
.pScissors = &(const VkRect2D) {
.offset = {0, 0},
.extent = swapchain.info.imageExtent
},
},
.pRasterizationState = &(const VkPipelineRasterizationStateCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.pNext = NULL,
.flags = 0,
.depthClampEnable = VK_FALSE,
.rasterizerDiscardEnable = VK_FALSE,
.polygonMode = VK_POLYGON_MODE_FILL,
.cullMode = VK_CULL_MODE_BACK_BIT,
.frontFace = VK_FRONT_FACE_CLOCKWISE,
.depthBiasEnable = VK_FALSE,
.depthBiasConstantFactor = 0.0f,
.depthBiasClamp = 0.0f,
.depthBiasSlopeFactor = 0.0f,
.lineWidth = 1.0f
},
.pMultisampleState = &(const VkPipelineMultisampleStateCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.pNext = NULL,
.flags = 0,
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
.sampleShadingEnable = VK_FALSE,
.minSampleShading = 1.0f,
.pSampleMask = NULL,
.alphaToCoverageEnable = VK_FALSE,
.alphaToOneEnable = VK_FALSE
},
.pDepthStencilState = NULL,
.pColorBlendState = &(const VkPipelineColorBlendStateCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.pNext = NULL,
.flags = 0,
.logicOpEnable = VK_FALSE,
.logicOp = VK_LOGIC_OP_COPY,
.attachmentCount = 1,
.pAttachments = &(const VkPipelineColorBlendAttachmentState) {
.blendEnable = VK_FALSE,
.srcColorBlendFactor = VK_BLEND_FACTOR_ONE,
.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO,
.colorBlendOp = VK_BLEND_OP_ADD,
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
.alphaBlendOp = VK_BLEND_OP_ADD,
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT |
VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT |
VK_COLOR_COMPONENT_A_BIT
},
.blendConstants = {0.0f, 0.0f, 0.0f, 0.0f},
},
.pDynamicState = NULL,
.layout = gpipe->layout_handle,
.renderPass = renderpass.handle,
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = -1
},
NULL,
&gpipe->handle
);
if(err != VK_SUCCESS) goto borked_pipeline;
vkDestroyShaderModule(swapchain.logicdev.handle, vert, NULL);
vkDestroyShaderModule(swapchain.logicdev.handle, frag, NULL);
return VGFX_SUCCESS;
borked_pipeline:
vkDestroyPipelineLayout(
swapchain.logicdev.handle,
gpipe->layout_handle,
NULL
);
borked_layout:
vkDestroyShaderModule(swapchain.logicdev.handle, vert, NULL);
vkDestroyShaderModule(swapchain.logicdev.handle, frag, NULL);
borked_noclean:
log_error("Graphics Pipeline initiation failed on %s", vkr2str(err));
borked_noerr:
return VGFX_FAIL;
}
void destroy_vk_gpipe(vk_gpipe_t gpipe) {
vkDestroyPipeline(gpipe.swapchain.logicdev.handle, gpipe.handle, NULL);
vkDestroyPipelineLayout(
gpipe.swapchain.logicdev.handle,
gpipe.layout_handle,
NULL
);
}
int init_vk(vgfx_vk_t *vk, GLFWwindow *window) {
if(init_vk_instance(&vk->instance, 1)) {
log_error("Failed to init instance");
return VGFX_FAIL;
}
if(init_vk_surface(&vk->surface, vk->instance, window)) {
log_error("Failed to init surface");
goto cleanup_instance;
}
if(init_vk_devices(&vk->devices, vk->instance)) {
log_error("Failed to init devices");
goto cleanup_surface;
}
if(init_vk_seldev(&vk->seldev, vk->devices, vk->surface)) {
log_error("Failed to init seldev");
goto cleanup_devices;
}
if(init_vk_logicdev(&vk->logicdev, vk->seldev)) {
log_error("Failed to init logicdev");
goto cleanup_seldev;
}
if(init_vk_swapchain(
&vk->swapchain,
vk->surface,
vk->seldev,
vk->logicdev
)) {
log_error("Failed to init swapchain");
goto cleanup_logicdev;
}
if(init_vk_imageviews(&vk->imageviews, vk->swapchain)) {
log_error("Failed to init imageviews");
goto cleanup_swapchain;
}
if(init_vk_renderpass(&vk->renderpass, vk->swapchain)) {
log_error("Failed to init renderpass");
goto cleanup_imageviews;
}
if(init_vk_gpipe(&vk->gpipe, vk->swapchain, vk->renderpass)) {
log_error("Failed to init graphics pipeline");
goto cleanup_renderpass;
}
return VGFX_SUCCESS;
cleanup_renderpass:
destroy_vk_renderpass(vk->renderpass);
cleanup_imageviews:
destroy_vk_imageviews(vk->imageviews);
cleanup_swapchain:
destroy_vk_swapchain(vk->swapchain);
cleanup_logicdev:
destroy_vk_logicdev(vk->logicdev);
cleanup_seldev:
destroy_vk_seldev(vk->seldev);
cleanup_devices:
destroy_vk_devices(vk->devices);
cleanup_surface:
destroy_vk_surface(vk->surface, vk->instance);
cleanup_instance:
destroy_vk_instance(vk->instance);
return VGFX_FAIL;
}
void destroy_vk(vgfx_vk_t vk) {
destroy_vk_gpipe(vk.gpipe);
destroy_vk_renderpass(vk.renderpass);
destroy_vk_imageviews(vk.imageviews);
destroy_vk_swapchain(vk.swapchain);
destroy_vk_logicdev(vk.logicdev);
destroy_vk_seldev(vk.seldev);
destroy_vk_devices(vk.devices);
destroy_vk_surface(vk.surface, vk.instance);
destroy_vk_instance(vk.instance);
}
int main(int argc, char const *argv[]) {
log_set_level(LOG_DEBUG);
log_set_simple(0);
//Setup glfw
glfwSetErrorCallback(glfw_error_cb);
if(!glfwInit()) {
log_error("Failed to init glfw");
return VGFX_FAIL;
}
log_debug("Successful init of glfw");
log_debug("Creating a window");
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
GLFWwindow *window = glfwCreateWindow(
800, 600, "Test Window", NULL, NULL
);
if(!window) {
log_error("Window creation failed");
return VGFX_FAIL;
}
glfwMakeContextCurrent(window);
glfwSetWindowSizeCallback(window, glfw_size_cb);
vgfx_vk_t vk;
if(init_vk(&vk, window)) {
log_error("init_vk failed");
return VGFX_FAIL;
}
while(!glfwWindowShouldClose(window)) {
//glfwSwapBuffers(window);
glfwPollEvents();
}
destroy_vk(vk);
log_debug("Terminating glfw");
glfwDestroyWindow(window);
glfwTerminate();
log_debug("Shutting Down");
return VGFX_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment