summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile83
-rw-r--r--files/shaders/shader1.frag7
-rw-r--r--files/shaders/shader1.vert10
-rw-r--r--src/common.c33
-rw-r--r--src/common.h34
-rw-r--r--src/device.c395
-rw-r--r--src/device.h33
-rw-r--r--src/main.c88
-rw-r--r--src/window.c36
-rw-r--r--src/window.h23
10 files changed, 742 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..0009173
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,83 @@
+CC := gcc
+GLSLC := glslc
+
+ifeq ($(PROD),1)
+CFLAGS := -O2 # production flags
+else
+CFLAGS := -Wall -Wextra -Wpedantic -g -DDEBUG # debug flags
+endif
+
+CFLAGS += -std=c99 -lm -lvulkan `sdl2-config --cflags --libs`
+
+SRCD := src
+OBJD := obj
+BIND := bin
+FILD := files
+SHDR := shaders
+
+SFILE = $(shell find $(SRCD)/ -type f 2> /dev/null)
+CSRCS = $(filter %.c, $(SFILE))
+COBJS = $(CSRCS:$(SRCD)/%.c=$(OBJD)/%.o)
+
+FILES = $(shell find $(FILD) -type f 2> /dev/null)
+SHDRS = $(filter %.vert %.frag, $(FILES))
+SHOBJ = $(SHDRS:$(FILD)/$(SHDR)/%=$(BIND)/$(SHDR)/%.spv)
+
+CDEPS = $(COBJS:%.o=%.d)
+-include $(CDEPS)
+
+NAME := test
+
+all: clean $(NAME)
+$(NAME): $(BIND)/$(NAME) $(SHOBJ)
+
+clean:
+ rm -rf $(BIND)
+ rm -rf $(OBJD)
+
+$(OBJD)/%.o: $(SRCD)/%.c
+ @mkdir -p $(dir $@)
+ $(CC) $(CFLAGS) -MMD -MF $(@:%.o=%.d) -c $< -o $@
+
+$(BIND)/$(SHDR)/%.spv: $(FILD)/$(SHDR)/%
+ @mkdir -p $(dir $@)
+ $(GLSLC) $< -o $@
+
+$(BIND)/$(NAME): $(COBJS)
+ @mkdir -p $(dir $@)
+ $(CC) $(CFLAGS) $^ -o $@
+
+# Extra Tools
+
+RUN_CMD := $(BIND)/$(NAME)
+UNITY := unity
+
+$(BIND)/$(UNITY): $(CSRCS)
+ @mkdir -p $(dir $@)
+
+ for src in $^; do \
+ cat $${src} >> $(SRCD)/$(UNITY).c; \
+ done
+
+ $(CC) $(CFLAGS) $(SRCD)/$(UNITY).c -o $@
+ @rm $(SRCD)/$(UNITY).c
+
+analyze: clean
+ scan-build \
+ -enable-checker alpha \
+ -enable-checker core \
+ -enable-checker deadcode \
+ -enable-checker security \
+ -enable-checker unix \
+ make $(BIND)/$(UNITY)
+
+sanitize: clean
+ $(MAKE) CC="clang -fsanitize=address" run
+
+run: $(NAME)
+ $(RUN_CMD)
+
+valgrind: all
+ valgrind -s --leak-check=full --show-leak-kinds=all $(RUN_CMD)
+
+
diff --git a/files/shaders/shader1.frag b/files/shaders/shader1.frag
new file mode 100644
index 0000000..f30417b
--- /dev/null
+++ b/files/shaders/shader1.frag
@@ -0,0 +1,7 @@
+#version 450
+
+layout (location = 0) out vec4 outColor;
+
+void main() {
+ outColor = vec4(1.0, 0.0, 0.0, 1.0);
+}
diff --git a/files/shaders/shader1.vert b/files/shaders/shader1.vert
new file mode 100644
index 0000000..c5d117f
--- /dev/null
+++ b/files/shaders/shader1.vert
@@ -0,0 +1,10 @@
+#version 450
+
+vec2 positions[3] = vec2[](
+ vec2(0.0, -0.5),
+ vec2(0.5, 0.5),
+ vec2(-0.5, 0.5));
+
+void main() {
+ gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
+}
diff --git a/src/common.c b/src/common.c
new file mode 100644
index 0000000..084c584
--- /dev/null
+++ b/src/common.c
@@ -0,0 +1,33 @@
+#include <string.h>
+#include <errno.h>
+#include "common.h"
+
+void *xmalloc(size_t size)
+{
+ void *ret;
+ if(!(ret = malloc(size))) {
+ die("malloc: %s", strerror(errno));
+ }
+
+ return ret;
+}
+
+void *xcalloc(size_t nmemb, size_t size)
+{
+ void *ret;
+ if(!(ret = calloc(nmemb, size))) {
+ die("calloc: %s", strerror(errno));
+ }
+
+ return ret;
+}
+
+void *xrealloc(void *ptr, size_t size)
+{
+ void *ret;
+ if(!(ret = realloc(ptr, size))) {
+ die("realloc: %s", strerror(errno));
+ }
+
+ return ret;
+}
diff --git a/src/common.h b/src/common.h
new file mode 100644
index 0000000..e10649a
--- /dev/null
+++ b/src/common.h
@@ -0,0 +1,34 @@
+#ifndef LOG_H
+#define LOG_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#define ARR_SIZE(a) (sizeof(a)/sizeof(*(a)))
+
+#define __RED__ "\033[0;31m"
+#define __GREEN__ "\033[0;32m"
+#define __YELLOW__ "\033[0;33m"
+#define __RESET__ "\033[0m"
+
+#define STR(x) #x
+#define XSTR(x) STR(x)
+
+#define info(...) do { fprintf(stdout, __GREEN__"[INFO]"__RESET__" "__VA_ARGS__); fprintf(stdout, "\n"); } while(0)
+#define err(...) do { fprintf(stderr, __RED__"[ERROR]"__RESET__" "__FILE__":"XSTR(__LINE__)": "__VA_ARGS__); fprintf(stderr, "\n"); } while(0)
+#define warn(...) do { fprintf(stderr, __YELLOW__"[WARN]"__RESET__" "__FILE__":"XSTR(__LINE__)": "__VA_ARGS__); fprintf(stderr, "\n"); } while(0)
+
+#define die(...) do { \
+ err(__VA_ARGS__); \
+ abort(); \
+ } while(0)
+
+#define MAKE_VERSION(major, minor, patch) \
+ ((((uint32_t)(major)) << 22U) | (((uint32_t)(minor)) << 12U) | ((uint32_t)(patch)))
+
+void *xmalloc(size_t size);
+void *xcalloc(size_t nmemb, size_t size);
+void *xrealloc(void *ptr, size_t size);
+
+#endif
diff --git a/src/device.c b/src/device.c
new file mode 100644
index 0000000..fdcc7ad
--- /dev/null
+++ b/src/device.c
@@ -0,0 +1,395 @@
+#include <string.h>
+#include <vulkan/vulkan.h>
+
+#include "device.h"
+#include "common.h"
+
+// TODO: rename device.c/.h
+// TODO: add error checking
+// TODO: add log output
+
+#ifdef DEBUG
+char *validation_layers[] = {
+ "VK_LAYER_KHRONOS_validation"
+};
+#endif
+
+enum {
+ QUEUE_FAMILY_COUNT = 2,
+
+ QUEUE_FAMILY_GRAPHICS_FAMILY = 1 << 0,
+ QUEUE_FAMILY_PRESENT_FAMILY = 1 << 1,
+
+ QUEUE_FAMILY_ALL = QUEUE_FAMILY_GRAPHICS_FAMILY |
+ QUEUE_FAMILY_PRESENT_FAMILY,
+};
+
+struct queue_family_idx {
+ uint32_t graphics_family;
+ uint32_t present_family;
+};
+
+// most function that return an int follow this scheme:
+// 0 - success; 1 - error; (sometimes -1 - fatal error)
+
+// if the function has 'has' or 'is', then
+// 0 - fail; 1 - success (sometimes -1 - fatal error)
+
+static int create_instance(device_t device, struct device_info *info);
+static int create_surface(device_t device, struct device_info *info);
+static int create_physical_device(device_t device, struct device_info *info);
+static int create_logical_device(device_t device, struct device_info *info);
+
+static int is_device_suitable(VkPhysicalDevice phy_device, VkSurfaceKHR surface);
+
+// does not follow the general scheme, see implementation for details
+static int find_queue_families(VkPhysicalDevice phy_device, VkSurfaceKHR surface, struct queue_family_idx *queue_family);
+
+#ifdef DEBUG
+static int create_debug_messenger(device_t device, struct device_info* info);
+static int has_validation_layer_support(const char * const *layers, uint32_t nlayers);
+static void populate_debug_messenger_info(VkDebugUtilsMessengerCreateInfoEXT *info);
+
+VkResult CreateDebugUtilsMessengerEXT(
+ VkInstance instance,
+ const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo,
+ const VkAllocationCallbacks* pAllocator,
+ VkDebugUtilsMessengerEXT* pDebugMessenger);
+void DestroyDebugUtilsMessengerEXT(
+ VkInstance instance,
+ VkDebugUtilsMessengerEXT debugMessenger,
+ const VkAllocationCallbacks* pAllocator);
+static VKAPI_ATTR VkBool32 VKAPI_CALL debug_callback(
+ VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
+ VkDebugUtilsMessageTypeFlagsEXT messageType,
+ const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
+ void* pUserData);
+#endif
+
+#define CHECK(func) if(func(device, info)) { goto fail; }
+
+device_t device_create(struct device_info *info)
+{
+ device_t device = xmalloc(sizeof(struct device));
+
+ CHECK(create_instance);
+ CHECK(create_surface);
+#ifdef DEBUG
+ CHECK(create_debug_messenger);
+#endif
+ CHECK(create_physical_device);
+ CHECK(create_logical_device);
+
+ return device;
+fail:
+ device_destroy(device);
+ return NULL;
+}
+
+void device_destroy(device_t device)
+{
+ if(!device) return;
+
+ vkDestroyDevice(device->logical_device, NULL);
+
+ #ifdef DEBUG
+ DestroyDebugUtilsMessengerEXT(device->instance, device->debug_messenger, NULL);
+ #endif
+
+ vkDestroySurfaceKHR(device->instance, device->surface, NULL);
+
+ vkDestroyInstance(device->instance, NULL);
+
+ free(device);
+}
+
+static int create_instance(device_t device, struct device_info *info)
+{
+ VkApplicationInfo app_info = {0};
+ app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
+ app_info.pEngineName = "Engine";
+ app_info.engineVersion = VK_MAKE_VERSION(1, 0, 0);
+ app_info.apiVersion = VK_API_VERSION_1_0;
+
+ app_info.pApplicationName = info->name;
+ app_info.applicationVersion = info->version;
+
+ VkInstanceCreateInfo create_info = {0};
+ create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
+ create_info.pApplicationInfo = &app_info;
+ create_info.enabledLayerCount = 0;
+
+#ifdef DEBUG
+ // add debug utils extensions for debug callback
+ char **extensions = xcalloc(info->ext_count+1, sizeof(char *));
+ extensions[info->ext_count] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME;
+
+ for(uint32_t i = 0; i < info->ext_count; i++) {
+ extensions[i] = (char *)info->extensions[i];
+ }
+
+ free((void *)info->extensions);
+
+ // we just added one more extension
+ create_info.enabledExtensionCount = info->ext_count+1;
+ create_info.ppEnabledExtensionNames = (const char * const *)extensions;
+
+ // validation layer support
+ char *validation_layers[] = {
+ "VK_LAYER_KHRONOS_validation"
+ };
+
+ uint32_t nlayers = ARR_SIZE(validation_layers);
+
+ if(!has_validation_layer_support((const char * const *)validation_layers, nlayers)) {
+ err("validation_layer_support: failed");
+ goto fail;
+ }
+
+ create_info.ppEnabledLayerNames = (const char * const *)validation_layers;
+ create_info.enabledLayerCount = nlayers;
+
+ // add the debug messenger for instance creation and destruction
+ VkDebugUtilsMessengerCreateInfoEXT msg_info = {0};
+ populate_debug_messenger_info(&msg_info);
+
+ create_info.pNext = &msg_info;
+#endif
+
+ if(vkCreateInstance(&create_info, NULL, &device->instance) != VK_SUCCESS) {
+ err("vkCreateInstance: failed");
+ goto fail;
+ }
+
+#ifdef DEBUG
+ free(extensions);
+#endif
+
+ return 0;
+fail:
+ return 1;
+}
+
+static int create_surface(device_t device, struct device_info *info)
+{
+ if(info->surface_func(device->instance, &device->surface)) {
+ err("Couldn't create a VkSurfaceKHR");
+ return 1;
+ }
+
+ return 0;
+}
+
+static int create_physical_device(device_t device, struct device_info *info)
+{
+ (void)info;
+
+ device->physical_device = VK_NULL_HANDLE;
+
+ uint32_t dev_count = 0;
+ vkEnumeratePhysicalDevices(device->instance, &dev_count, NULL);
+
+ if(dev_count == 0) {
+ err("No physical devices could be found!");
+ goto fail;
+ }
+
+ VkPhysicalDevice *devices = xcalloc(dev_count, sizeof(VkPhysicalDevice));
+ vkEnumeratePhysicalDevices(device->instance, &dev_count, devices);
+
+ for(uint32_t i = 0; i < dev_count; i++) {
+ if(is_device_suitable(devices[i], device->surface)) {
+ device->physical_device = devices[i];
+ break;
+ }
+ }
+
+ free(devices);
+
+ if(device->physical_device == VK_NULL_HANDLE) {
+ err("No suitable physical device could be found");
+ goto fail;
+ }
+
+ return 0;
+fail:
+ return 1;
+}
+
+static int create_logical_device(device_t device, struct device_info *info)
+{
+ (void)info;
+
+ // queue family data
+ struct queue_family_idx indices = {0};
+ find_queue_families(device->physical_device, device->surface, &indices);
+
+ // queue infos
+ VkDeviceQueueCreateInfo *queue_infos = xcalloc(QUEUE_FAMILY_COUNT, sizeof(VkDeviceQueueCreateInfo));
+ memset(queue_infos, 0, QUEUE_FAMILY_COUNT * sizeof(VkDeviceQueueCreateInfo));
+
+ uint32_t *queue_indices = xcalloc(QUEUE_FAMILY_COUNT, sizeof(uint32_t));
+ queue_indices[0] = indices.graphics_family;
+ queue_indices[1] = indices.present_family;
+
+ float queue_priority = 1.0f;
+ for(int i = 0; i < QUEUE_FAMILY_COUNT; i++)
+ {
+ queue_infos[i].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
+ queue_infos[i].queueFamilyIndex = queue_indices[i];
+ queue_infos[i].queueCount = 1;
+ queue_infos[i].pQueuePriorities = &queue_priority;
+ }
+
+ // device features
+ VkPhysicalDeviceFeatures device_features = {0};
+
+ // logical device create info
+ VkDeviceCreateInfo create_info = {0};
+ create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
+
+ create_info.pQueueCreateInfos = queue_infos;
+ create_info.queueCreateInfoCount = QUEUE_FAMILY_COUNT;
+
+ create_info.pEnabledFeatures = &device_features;
+
+ create_info.enabledExtensionCount = 0;
+
+ // validation layers can be set, but
+ // newer implementations will ignore them
+ // so i wont bother adding them
+ create_info.enabledLayerCount = 0;
+
+ if(vkCreateDevice(device->physical_device, &create_info, NULL, &device->logical_device) != VK_SUCCESS) {
+ err("vkCreateDevice: failed");
+ goto fail;
+ }
+
+ // vkGetDeviceQueue(device->logical_device, indices.graphics_family, 0, &device->graphics_queue);
+
+ free(queue_infos);
+ free(queue_indices);
+
+ return 0;
+fail:
+ return 1;
+}
+
+// returns a bitmask if a given family is set, or not
+static int find_queue_families(VkPhysicalDevice phy_device, VkSurfaceKHR surface, struct queue_family_idx *queue_family)
+{
+ int ret = 0;
+
+ uint32_t count = 0;
+ vkGetPhysicalDeviceQueueFamilyProperties(phy_device, &count, NULL);
+
+ VkQueueFamilyProperties *properties = xcalloc(count, sizeof(VkQueueFamilyProperties));
+ vkGetPhysicalDeviceQueueFamilyProperties(phy_device, &count, properties);
+
+ for(uint32_t i = 0; i < count; i++) {
+ if(properties[i].queueFlags & VK_QUEUE_COMPUTE_BIT) {
+ ret |= QUEUE_FAMILY_GRAPHICS_FAMILY;
+ if(queue_family != NULL) {
+ queue_family->graphics_family = i;
+ }
+ }
+
+ VkBool32 present_support = 0;
+ vkGetPhysicalDeviceSurfaceSupportKHR(phy_device, i, surface, &present_support);
+ if(present_support) {
+ ret |= QUEUE_FAMILY_PRESENT_FAMILY;
+ if(queue_family != NULL){
+ queue_family->present_family = i;
+ }
+ }
+ }
+
+ free(properties);
+ return ret;
+}
+
+static int is_device_suitable(VkPhysicalDevice phy_device, VkSurfaceKHR surface)
+{
+ if(find_queue_families(phy_device, surface, NULL) == QUEUE_FAMILY_ALL) {
+ return 1;
+ }
+
+ return 0;
+}
+
+
+#ifdef DEBUG
+
+static int create_debug_messenger(device_t device, struct device_info *info)
+{
+ (void)info;
+
+ VkDebugUtilsMessengerCreateInfoEXT cinfo = {0};
+ populate_debug_messenger_info(&cinfo);
+
+ if(CreateDebugUtilsMessengerEXT(device->instance, &cinfo, NULL, &device->debug_messenger) != VK_SUCCESS) {
+ info("CreateDebugUtilMessengerEXT: failed");
+ return 1;
+ }
+
+ return 0;
+}
+
+static void populate_debug_messenger_info(VkDebugUtilsMessengerCreateInfoEXT *info)
+{
+ info->sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
+ info->messageSeverity =
+ VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
+ VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
+ VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
+ info->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;
+ info->pfnUserCallback = debug_callback;
+}
+
+static int has_validation_layer_support(const char * const *layers, uint32_t nlayers)
+{
+ (void)layers;
+ (void)nlayers;
+
+ // uint32_t navaliable = 0;
+ // vkEnumerateInstanceLayerProperties(&navaliable, NULL);
+
+ // VkLayerProperties *available_layers = xcalloc(navaliable, sizeof(VkLayerProperties));
+ // vkEnumerateInstanceLayerProperties(&navaliable, available_layers);
+ return 1;
+}
+
+static VKAPI_ATTR VkBool32 VKAPI_CALL debug_callback(
+ VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
+ VkDebugUtilsMessageTypeFlagsEXT messageType,
+ const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
+ void* pUserData)
+{
+ (void)messageSeverity;
+ (void)messageType;
+ (void)pUserData;
+
+ warn("Validation Layer: %s", pCallbackData->pMessage);
+ return VK_FALSE;
+}
+
+VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger) {
+ PFN_vkCreateDebugUtilsMessengerEXT f = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT");
+ if (f != NULL) {
+ return f(instance, pCreateInfo, pAllocator, pDebugMessenger);
+ } else {
+ return VK_ERROR_EXTENSION_NOT_PRESENT;
+ }
+}
+
+void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, const VkAllocationCallbacks* pAllocator) {
+ PFN_vkDestroyDebugUtilsMessengerEXT f = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT");
+ if (f != NULL) {
+ f(instance, debugMessenger, pAllocator);
+ }
+}
+
+#endif
diff --git a/src/device.h b/src/device.h
new file mode 100644
index 0000000..d6768e1
--- /dev/null
+++ b/src/device.h
@@ -0,0 +1,33 @@
+#ifndef DEVICE_H
+#define DEVICE_H
+
+#include <vulkan/vulkan.h>
+#include "common.h"
+
+typedef struct device {
+ VkInstance instance;
+ VkDebugUtilsMessengerEXT debug_messenger;
+
+ VkPhysicalDevice physical_device;
+ VkDevice logical_device;
+ VkQueue graphics_queue;
+
+ VkSurfaceKHR surface;
+} * device_t;
+
+typedef int (*surface_func_t)(VkInstance instance, VkSurfaceKHR *surface);
+
+struct device_info {
+ char *name;
+ uint32_t version;
+
+ const char* const* extensions;
+ uint32_t ext_count;
+
+ surface_func_t surface_func;
+};
+
+device_t device_create(struct device_info *info);
+void device_destroy(device_t device);
+
+#endif
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..90985be
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,88 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "device.h"
+#include "window.h"
+#include "common.h"
+
+// TODO: rename device.c/.h
+// TODO: fix fail gotos
+
+window_t window;
+device_t device;
+
+int _create_surface(VkInstance instance, VkSurfaceKHR *surface);
+
+int main(void)
+{
+ int ret = 1;
+
+ if(SDL_Init(0)) {
+ err("SDL_Init: failed");
+ goto sf;
+ }
+
+ // populate window info
+ struct window_info win_info = {0};
+ win_info.title = "Test App";
+ win_info.w = 1200;
+ win_info.h = 800;
+
+ // create window
+ window = window_create(&win_info);
+ if(!window) {
+ err("window_create: failed");
+ goto wf;
+ }
+
+ // get extensions
+ unsigned int ext_count = 0;
+ window_extension_info(window, &ext_count, NULL);
+
+ // populate the device info
+ struct device_info dev_info = {0};
+ dev_info.name = "Test App";
+ dev_info.version = MAKE_VERSION(1, 0, 0);
+
+ if(ext_count == 0){
+ dev_info.ext_count = 0;
+ dev_info.extensions = NULL;
+ } else {
+ char **extensions = xcalloc(ext_count, sizeof(char *));
+ window_extension_info(window, &ext_count, (const char **)extensions);
+
+ dev_info.ext_count = ext_count;
+ dev_info.extensions = (const char * const *)extensions;
+ }
+
+ dev_info.surface_func = _create_surface;
+
+ // create the device
+ device = device_create(&dev_info);
+ if(!device) {
+ err("device_create: failed");
+ goto df;
+ }
+
+ int running = 1;
+ while(running) {
+ SDL_Event windowEvent;
+ while(SDL_PollEvent(&windowEvent))
+ if(windowEvent.type == SDL_QUIT) {
+ running = 0;
+ break;
+ }
+ }
+
+ ret = 0;
+df: device_destroy(device);
+wf: window_destroy(window);
+sf: SDL_Quit();
+ return ret;
+}
+
+int _create_surface(VkInstance instance, VkSurfaceKHR *surface)
+{
+ return window_create_surface(window, instance, surface);
+}
diff --git a/src/window.c b/src/window.c
new file mode 100644
index 0000000..4a498e6
--- /dev/null
+++ b/src/window.c
@@ -0,0 +1,36 @@
+#include "window.h"
+#include "common.h"
+
+window_t window_create(struct window_info *info)
+{
+ info->flags |= SDL_WINDOW_SHOWN | SDL_WINDOW_VULKAN;
+ SDL_Vulkan_LoadLibrary(NULL);
+
+ window_t window = SDL_CreateWindow(info->title, info->x, info->y,
+ info->w, info->h, info->flags);
+ return window;
+}
+
+void window_destroy(window_t window)
+{
+ SDL_DestroyWindow((SDL_Window *)window);
+ SDL_Vulkan_UnloadLibrary();
+}
+
+int window_extension_info(window_t window, unsigned int *ext_count, const char **extensions)
+{
+ if(SDL_Vulkan_GetInstanceExtensions((SDL_Window *)window, ext_count, extensions) == SDL_FALSE) {
+ return 1;
+ }
+
+ return 0;
+}
+
+int window_create_surface(window_t window, VkInstance instance, VkSurfaceKHR *surface)
+{
+ if(SDL_Vulkan_CreateSurface((SDL_Window *)window, instance, surface) == SDL_FALSE) {
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/src/window.h b/src/window.h
new file mode 100644
index 0000000..01ecbb3
--- /dev/null
+++ b/src/window.h
@@ -0,0 +1,23 @@
+#ifndef WINDOW_H
+#define WINDOW_H
+
+#include <SDL2/SDL.h>
+#include <SDL2/SDL_vulkan.h>
+#include <vulkan/vulkan.h>
+
+typedef SDL_Window * window_t;
+
+struct window_info {
+ char *title;
+ int x; int y;
+ int w; int h;
+ Uint32 flags;
+};
+
+window_t window_create(struct window_info *info);
+void window_destroy(window_t window);
+
+int window_extension_info(window_t window, unsigned int *ext_count, const char **extensions);
+int window_create_surface(window_t window, VkInstance instance, VkSurfaceKHR *surface);
+
+#endif