diff --git a/demo/sdl_vulkan/.gitignore b/demo/sdl_vulkan/.gitignore new file mode 100644 index 0000000..668fb19 --- /dev/null +++ b/demo/sdl_vulkan/.gitignore @@ -0,0 +1,3 @@ +src/nuklearshaders/*.spv +src/nuklear_sdl_vulkan.h +shaders/*.spv diff --git a/demo/sdl_vulkan/Makefile b/demo/sdl_vulkan/Makefile new file mode 100644 index 0000000..2f64928 --- /dev/null +++ b/demo/sdl_vulkan/Makefile @@ -0,0 +1,29 @@ +# Install +BIN = demo + +# Flags +CFLAGS += -std=c89 -Wall -Wextra -pedantic -fsanitize=address -O2 + +SRC = main.c +OBJ = $(SRC:.c=.o) + +ifeq ($(OS),Windows_NT) +BIN := $(BIN).exe +LIBS = -lsdl2 -lvulkan -lm +else + UNAME_S := $(shell uname -s) + SDL2 := $(shell pkg-config --libs sdl2) + LIBS = $(SDL2) -lvulkan -lm +endif + + +$(BIN): shaders/demo.vert.spv shaders/demo.frag.spv + @mkdir -p bin + rm -f bin/$(BIN) $(OBJS) + $(CC) $(SRC) $(CFLAGS) -o bin/$(BIN) $(LIBS) + +shaders/demo.vert.spv: shaders/demo.vert + glslc --target-env=vulkan shaders/demo.vert -o shaders/demo.vert.spv + +shaders/demo.frag.spv: shaders/demo.frag + glslc --target-env=vulkan shaders/demo.frag -o shaders/demo.frag.spv diff --git a/demo/sdl_vulkan/README.md b/demo/sdl_vulkan/README.md new file mode 100644 index 0000000..fc7dc8a --- /dev/null +++ b/demo/sdl_vulkan/README.md @@ -0,0 +1,52 @@ +# nuklear sdl vulkan + +## Theory of operation + +The nuklear SDL vulkan integration creates an independent graphics pipeline that will render the nuklear UI to separate render targets. +The application is responsible to fully manage these render targets. So it must ensure they are properly sized (and resized when requested). + +Furthermore it is assumed that you will have a swap chain in place and the number of nuklear overlay images and number of swap chain images match. + +This is how you can integrate it in your application: + +``` +/* +Setup: overlay_images have been created and their number match with the number +of the swap_chain_images of your application. The overlay_images in this +example have the same format as your swap_chain images (optional) +*/ + struct nk_context *ctx = nk_sdl_init( + demo.win, demo.device, demo.physical_device, demo.indices.graphics, + demo.overlay_image_views, demo.swap_chain_images_len, + demo.swap_chain_image_format, 0, + MAX_VERTEX_BUFFER, MAX_ELEMENT_BUFFER); +[...] +/* +in your draw loop draw you can then render to the overlay image at +`image_index` +your own application can then wait for the semaphore and produce +the swap_chain_image at `image_index` +this should simply sample from the overlay_image (see example) +*/ +nk_semaphore semaphore = + nk_sdl_render(demo.graphics_queue, image_index, + demo.image_available, NK_ANTI_ALIASING_ON); + if (!render(&demo, &bg, nk_semaphore, image_index)) { + fprintf(stderr, "render failed\n"); + return false; + } +``` + +You must call `nk_sdl_resize` whenever the size of the overlay_images resize. + +## Using images + +Images can be used by providing a VkImageView as an nk_image_ptr to nuklear: + +``` +img = nk_image_ptr(demo.demo_texture_image_view); +``` + +Note that they must have SHADER_READ_OPTIMAL layout + +It is currently not possible to specify how they are being sampled. The nuklear SDL vulkan integration uses a fixed sampler that does linear filtering. diff --git a/demo/sdl_vulkan/main.c b/demo/sdl_vulkan/main.c new file mode 100644 index 0000000..b768421 --- /dev/null +++ b/demo/sdl_vulkan/main.c @@ -0,0 +1,2251 @@ +/* nuklear - 1.32.0 - public domain */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define NK_INCLUDE_FIXED_TYPES +#define NK_INCLUDE_STANDARD_IO +#define NK_INCLUDE_STANDARD_VARARGS +#define NK_INCLUDE_DEFAULT_ALLOCATOR +#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT +#define NK_INCLUDE_FONT_BAKING +#define NK_INCLUDE_DEFAULT_FONT +#define NK_IMPLEMENTATION +#define NK_SDL_VULKAN_IMPLEMENTATION +#define NK_KEYSTATE_BASED_INPUT +#include "../../nuklear.h" +#include "nuklear_sdl_vulkan.h" + +#define WINDOW_WIDTH 1200 +#define WINDOW_HEIGHT 800 + +#define MAX_VERTEX_BUFFER 512 * 1024 +#define MAX_ELEMENT_BUFFER 128 * 1024 + +/* =============================================================== + * + * EXAMPLE + * + * ===============================================================*/ +/* This are some code examples to provide a small overview of what can be + * done with this library. To try out an example uncomment the defines */ +/* #define INCLUDE_ALL */ +/* #define INCLUDE_STYLE */ +/* #define INCLUDE_CALCULATOR */ +/* #define INCLUDE_CANVAS */ +/* #define INCLUDE_OVERVIEW */ +/* #define INCLUDE_NODE_EDITOR */ + +#ifdef INCLUDE_ALL +#define INCLUDE_STYLE +#define INCLUDE_CALCULATOR +#define INCLUDE_CANVAS +#define INCLUDE_OVERVIEW +#define INCLUDE_NODE_EDITOR +#endif + +#ifdef INCLUDE_STYLE +#include "../../demo/common/style.c" +#endif +#ifdef INCLUDE_CALCULATOR +#include "../../demo/common/calculator.c" +#endif +#ifdef INCLUDE_CANVAS +#include "../../demo/common/canvas.c" +#endif +#ifdef INCLUDE_OVERVIEW +#include "../../demo/common/overview.c" +#endif +#ifdef INCLUDE_NODE_EDITOR +#include "../../demo/common/node_editor.c" +#endif + +/* =============================================================== + * + * DEMO + * + * ===============================================================*/ + +static const char *validation_layer_name = "VK_LAYER_KHRONOS_validation"; + +struct queue_family_indices { + int graphics; + int present; +}; + +struct swap_chain_support_details { + VkSurfaceCapabilitiesKHR capabilities; + VkSurfaceFormatKHR *formats; + uint32_t formats_len; + VkPresentModeKHR *present_modes; + uint32_t present_modes_len; +}; + +void swap_chain_support_details_free( + struct swap_chain_support_details *swap_chain_support) { + if (swap_chain_support->formats_len > 0) { + free(swap_chain_support->formats); + swap_chain_support->formats = NULL; + } + if (swap_chain_support->present_modes_len > 0) { + free(swap_chain_support->present_modes); + swap_chain_support->present_modes = NULL; + } +} + +struct vulkan_demo { + SDL_Window *win; + VkInstance instance; + VkDebugUtilsMessengerEXT debug_messenger; + VkSurfaceKHR surface; + VkPhysicalDevice physical_device; + struct queue_family_indices indices; + VkDevice device; + VkQueue graphics_queue; + VkQueue present_queue; + VkSampler sampler; + + VkSwapchainKHR swap_chain; + VkImage *swap_chain_images; + uint32_t swap_chain_images_len; + VkImageView *swap_chain_image_views; + VkFormat swap_chain_image_format; + VkExtent2D swap_chain_image_extent; + + VkImage *overlay_images; + VkImageView *overlay_image_views; + VkDeviceMemory *overlay_image_memories; + + VkRenderPass render_pass; + VkFramebuffer *framebuffers; + VkDescriptorSetLayout descriptor_set_layout; + VkDescriptorPool descriptor_pool; + VkDescriptorSet *descriptor_sets; + VkPipelineLayout pipeline_layout; + VkPipeline pipeline; + VkCommandPool command_pool; + VkCommandBuffer *command_buffers; + VkSemaphore image_available; + VkSemaphore render_finished; + + VkImage demo_texture_image; + VkImageView demo_texture_image_view; + VkDeviceMemory demo_texture_memory; + + VkFence render_fence; +}; + +VKAPI_ATTR VkBool32 VKAPI_CALL +vulkan_debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity, + VkDebugUtilsMessageTypeFlagsEXT message_type, + const VkDebugUtilsMessengerCallbackDataEXT *callback_data, + void *user_data) { + (void)message_severity; + (void)message_type; + (void)user_data; + fprintf(stderr, "validation layer: %s\n", callback_data->pMessage); + + return VK_FALSE; +} + +bool check_validation_layer_support() { + uint32_t layer_count; + bool ret = false; + VkResult result; + uint32_t i; + VkLayerProperties *available_layers = NULL; + + result = vkEnumerateInstanceLayerProperties(&layer_count, NULL); + if (result != VK_SUCCESS) { + fprintf(stderr, "vkEnumerateInstanceLayerProperties failed: %d\n", + result); + return ret; + } + + available_layers = malloc(layer_count * sizeof(VkLayerProperties)); + result = vkEnumerateInstanceLayerProperties(&layer_count, available_layers); + if (result != VK_SUCCESS) { + fprintf(stderr, "vkEnumerateInstanceLayerProperties failed: %d\n", + result); + goto cleanup; + } + + printf("Available vulkan layers:\n"); + for (i = 0; i < layer_count; i++) { + printf(" %s\n", available_layers[i].layerName); + if (strcmp(validation_layer_name, available_layers[i].layerName) == 0) { + ret = true; + break; + } + } +cleanup: + free(available_layers); + return ret; +} + +VkResult create_debug_utils_messenger_ext( + VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT *pCreateInfo, + const VkAllocationCallbacks *pAllocator, + VkDebugUtilsMessengerEXT *pDebugMessenger) { + PFN_vkCreateDebugUtilsMessengerEXT func = + (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr( + instance, "vkCreateDebugUtilsMessengerEXT"); + if (func != NULL) { + return func(instance, pCreateInfo, pAllocator, pDebugMessenger); + } else { + return VK_ERROR_EXTENSION_NOT_PRESENT; + } +} + +bool create_debug_callback(struct vulkan_demo *demo) { + VkResult result; + + VkDebugUtilsMessengerCreateInfoEXT create_info; + memset(&create_info, 0, sizeof(VkDebugUtilsMessengerCreateInfoEXT)); + create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; + create_info.messageSeverity = + VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; + create_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; + create_info.pfnUserCallback = vulkan_debug_callback; + + result = create_debug_utils_messenger_ext(demo->instance, &create_info, + NULL, &demo->debug_messenger); + if (result != VK_SUCCESS) { + fprintf(stderr, "create_debug_utils_messenger_ext failed %d\n", result); + return false; + } + return true; +} + +bool create_instance(struct vulkan_demo *demo) { + uint32_t i; + uint32_t available_instance_extension_count; + VkResult result; + VkExtensionProperties *available_instance_extensions = NULL; + bool ret = false; + VkApplicationInfo app_info; + VkInstanceCreateInfo create_info; + uint32_t sdl_extension_count; + uint32_t enabled_extension_count; + const char **enabled_extensions = NULL; + bool validation_layers_installed; + + validation_layers_installed = check_validation_layer_support(); + + if (!validation_layers_installed) { + fprintf(stdout, + "Couldn't find validation layer %s. Continuing without " + "validation layers.\n", + validation_layer_name); + } + result = vkEnumerateInstanceExtensionProperties( + NULL, &available_instance_extension_count, NULL); + if (result != VK_SUCCESS) { + fprintf(stderr, "vkEnumerateInstanceExtensionProperties failed %d\n", + result); + return ret; + } + + available_instance_extensions = malloc(available_instance_extension_count * + sizeof(VkExtensionProperties)); + + result = vkEnumerateInstanceExtensionProperties( + NULL, &available_instance_extension_count, + available_instance_extensions); + + if (result != VK_SUCCESS) { + fprintf(stderr, "vkEnumerateInstanceExtensionProperties failed %d\n", + result); + goto cleanup; + } + + printf("available instance extensions:\n"); + for (i = 0; i < available_instance_extension_count; i++) { + printf(" %s\n", available_instance_extensions[i].extensionName); + } + + if (SDL_Vulkan_GetInstanceExtensions(demo->win, &sdl_extension_count, + NULL) != SDL_TRUE) { + fprintf(stderr, "SDL_Vulkan_GetInstanceExtensions failed: %s\n", + SDL_GetError()); + goto cleanup; + } + + enabled_extension_count = + sdl_extension_count + (validation_layers_installed ? 1 : 0); + + enabled_extensions = malloc(enabled_extension_count * sizeof(char *)); + if (SDL_Vulkan_GetInstanceExtensions(demo->win, &sdl_extension_count, + enabled_extensions) != SDL_TRUE) { + fprintf(stderr, "SDL_Vulkan_GetInstanceExtensions failed: %s\n", + SDL_GetError()); + goto cleanup; + } + + if (validation_layers_installed) { + enabled_extensions[sdl_extension_count] = + VK_EXT_DEBUG_UTILS_EXTENSION_NAME; + } + + printf("Trying to enable the following instance extensions: "); + for (i = 0; i < enabled_extension_count; i++) { + if (i > 0) { + printf(", "); + } + printf("%s\n", enabled_extensions[i]); + } + printf("\n"); + for (i = 0; i < enabled_extension_count; i++) { + int extension_missing = 1; + uint32_t j; + for (j = 0; j < available_instance_extension_count; j++) { + if (strcmp(enabled_extensions[i], + available_instance_extensions[j].extensionName) == 0) { + extension_missing = 0; + break; + } + } + if (extension_missing) { + fprintf(stderr, "Extension %s is missing\n", enabled_extensions[i]); + return ret; + } + } + + memset(&app_info, 0, sizeof(VkApplicationInfo)); + app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + app_info.pApplicationName = "Demo"; + app_info.applicationVersion = VK_MAKE_VERSION(1, 0, 0); + app_info.pEngineName = "No Engine"; + app_info.engineVersion = VK_MAKE_VERSION(1, 0, 0); + app_info.apiVersion = VK_API_VERSION_1_0; + + memset(&create_info, 0, sizeof(VkInstanceCreateInfo)); + create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + create_info.pApplicationInfo = &app_info; + create_info.enabledExtensionCount = enabled_extension_count; + create_info.ppEnabledExtensionNames = enabled_extensions; + if (validation_layers_installed) { + create_info.enabledLayerCount = 1; + create_info.ppEnabledLayerNames = &validation_layer_name; + } + result = vkCreateInstance(&create_info, NULL, &demo->instance); + if (result != VK_SUCCESS) { + fprintf(stderr, "vkCreateInstance result %d\n", result); + return ret; + } + if (validation_layers_installed) { + ret = create_debug_callback(demo); + } else { + ret = true; + } +cleanup: + if (available_instance_extensions) { + free(available_instance_extensions); + } + if (enabled_extensions) { + free(enabled_extensions); + } + + return ret; +} + +bool create_surface(struct vulkan_demo *demo) { + SDL_bool result; + result = + SDL_Vulkan_CreateSurface(demo->win, demo->instance, &demo->surface); + if (result != SDL_TRUE) { + fprintf(stderr, "creating vulkan surface failed: %s\n", SDL_GetError()); + return false; + } + return true; +} + +bool find_queue_families(VkPhysicalDevice physical_device, VkSurfaceKHR surface, + struct queue_family_indices *indices) { + VkResult result; + uint32_t queue_family_count = 0; + uint32_t i = 0; + bool ret = false; + VkQueueFamilyProperties *queue_family_properties; + VkBool32 present_support; + + vkGetPhysicalDeviceQueueFamilyProperties(physical_device, + &queue_family_count, NULL); + + queue_family_properties = + malloc(queue_family_count * sizeof(VkQueueFamilyProperties)); + vkGetPhysicalDeviceQueueFamilyProperties( + physical_device, &queue_family_count, queue_family_properties); + + for (i = 0; i < queue_family_count; i++) { + if (queue_family_properties[i].queueCount == 0) { + continue; + } + if (queue_family_properties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { + indices->graphics = i; + } + + result = vkGetPhysicalDeviceSurfaceSupportKHR( + physical_device, i, surface, &present_support); + if (result != VK_SUCCESS) { + fprintf(stderr, + "vkGetPhysicalDeviceSurfaceSupportKHR failed with %d\n", + result); + goto cleanup; + } + if (present_support == VK_TRUE) { + indices->present = i; + } + if (indices->graphics >= 0 && indices->present >= 0) { + break; + } + } + ret = true; +cleanup: + free(queue_family_properties); + return ret; +} + +bool query_swap_chain_support( + VkPhysicalDevice device, VkSurfaceKHR surface, + struct swap_chain_support_details *swap_chain_support) { + VkResult result; + + result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR( + device, surface, &swap_chain_support->capabilities); + if (result != VK_SUCCESS) { + fprintf(stderr, + "vkGetPhysicalDeviceSurfaceCapabilitiesKHR failed: %d\n", + result); + return false; + } + + result = vkGetPhysicalDeviceSurfaceFormatsKHR( + device, surface, &swap_chain_support->formats_len, NULL); + + if (result != VK_SUCCESS) { + fprintf(stderr, "vkGetPhysicalDeviceSurfaceFormatsKHR failed: %d\n", + result); + return false; + } + + if (swap_chain_support->formats_len != 0) { + swap_chain_support->formats = malloc(swap_chain_support->formats_len * + sizeof(VkSurfaceFormatKHR)); + result = vkGetPhysicalDeviceSurfaceFormatsKHR( + device, surface, &swap_chain_support->formats_len, + swap_chain_support->formats); + + if (result != VK_SUCCESS) { + fprintf(stderr, "vkGetPhysicalDeviceSurfaceFormatsKHR failed: %d\n", + result); + return false; + } + } + + result = vkGetPhysicalDeviceSurfacePresentModesKHR( + device, surface, &swap_chain_support->present_modes_len, NULL); + + if (result != VK_SUCCESS) { + fprintf(stderr, + "vkGetPhysicalDeviceSurfacePresentModesKHR failed: %d\n", + result); + return false; + } + + if (swap_chain_support->present_modes_len != 0) { + swap_chain_support->present_modes = malloc( + swap_chain_support->present_modes_len * sizeof(VkPresentModeKHR)); + result = vkGetPhysicalDeviceSurfacePresentModesKHR( + device, surface, &swap_chain_support->present_modes_len, + swap_chain_support->present_modes); + + if (result != VK_SUCCESS) { + fprintf(stderr, + "vkGetPhysicalDeviceSurfacePresentModesKHR failed: %d\n", + result); + return false; + } + } + + return true; +} + +bool is_suitable_physical_device(VkPhysicalDevice physical_device, + VkSurfaceKHR surface, + struct queue_family_indices *indices) { + VkResult result; + uint32_t device_extension_count; + uint32_t i; + VkExtensionProperties *device_extensions; + bool ret = false; + struct swap_chain_support_details swap_chain_support; + int found_khr_surface = 0; + + VkPhysicalDeviceProperties device_properties; + vkGetPhysicalDeviceProperties(physical_device, &device_properties); + + printf("Probing physical device %s\n", device_properties.deviceName); + + result = vkEnumerateDeviceExtensionProperties( + physical_device, NULL, &device_extension_count, NULL); + if (result != VK_SUCCESS) { + fprintf(stderr, "vkEnumerateDeviceExtensionProperties failed: %d\n", + result); + return false; + } + + device_extensions = + malloc(device_extension_count * sizeof(VkExtensionProperties)); + + result = vkEnumerateDeviceExtensionProperties( + physical_device, NULL, &device_extension_count, device_extensions); + + if (result != VK_SUCCESS) { + fprintf(stderr, "vkEnumerateDeviceExtensionProperties failed: %d\n", + result); + goto cleanup; + } + + printf(" Supported device extensions:\n"); + + for (i = 0; i < device_extension_count; i++) { + printf(" %s\n", device_extensions[i].extensionName); + if (strcmp(VK_KHR_SWAPCHAIN_EXTENSION_NAME, + device_extensions[i].extensionName) == 0) { + found_khr_surface = 1; + break; + } + } + if (!found_khr_surface) { + printf(" Device doesnt support %s\n", VK_KHR_SWAPCHAIN_EXTENSION_NAME); + goto cleanup; + } + if (!find_queue_families(physical_device, surface, indices)) { + goto cleanup; + } + if (indices->graphics < 0 || indices->present < 0) { + printf(" Device is missing graphics and/or present support. graphics: " + "%d, present: %d\n", + indices->graphics, indices->present); + goto cleanup; + } + + if (!query_swap_chain_support(physical_device, surface, + &swap_chain_support)) { + goto cleanup; + } + + if (swap_chain_support.formats_len == 0) { + printf(" Device doesn't support any swap chain formats\n"); + goto cleanup; + } + + if (swap_chain_support.present_modes_len == 0) { + printf(" Device doesn't support any swap chain present modes\n"); + goto cleanup; + } + ret = true; + +cleanup: + free(device_extensions); + swap_chain_support_details_free(&swap_chain_support); + + return ret; +} + +bool create_physical_device(struct vulkan_demo *demo) { + uint32_t device_count = 0; + VkPhysicalDevice *physical_devices; + VkResult result; + uint32_t i; + bool ret = false; + + result = vkEnumeratePhysicalDevices(demo->instance, &device_count, NULL); + if (result != VK_SUCCESS) { + fprintf(stderr, "vkEnumeratePhysicalDevices failed: %d\n", result); + return ret; + } + if (device_count == 0) { + fprintf(stderr, "no vulkan capable GPU found!"); + return ret; + } + + physical_devices = malloc(device_count * sizeof(VkPhysicalDevice)); + result = vkEnumeratePhysicalDevices(demo->instance, &device_count, + physical_devices); + if (result != VK_SUCCESS) { + fprintf(stderr, "vkEnumeratePhysicalDevices failed: %d\n", result); + goto cleanup; + } + + for (i = 0; i < device_count; i++) { + struct queue_family_indices indices = {-1, -1}; + if (is_suitable_physical_device(physical_devices[i], demo->surface, + &indices)) { + printf(" Selecting this device for rendering. Queue families: " + "graphics: %d, present: %d!\n", + indices.graphics, indices.present); + demo->physical_device = physical_devices[i]; + demo->indices = indices; + break; + } + } + if (demo->physical_device == NULL) { + fprintf(stderr, "failed to find a suitable GPU!\n"); + } else { + ret = true; + } +cleanup: + free(physical_devices); + return ret; +} + +bool create_logical_device(struct vulkan_demo *demo) { + VkResult result; + bool ret = false; + float queuePriority = 1.0f; + uint32_t num_queues = 1; + VkDeviceQueueCreateInfo *queue_create_infos; + VkDeviceCreateInfo create_info; + const char *swap_chain_extension_name = VK_KHR_SWAPCHAIN_EXTENSION_NAME; + + queue_create_infos = calloc(2, sizeof(VkDeviceQueueCreateInfo)); + queue_create_infos[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queue_create_infos[0].queueFamilyIndex = demo->indices.graphics; + queue_create_infos[0].queueCount = 1; + queue_create_infos[0].pQueuePriorities = &queuePriority; + + if (demo->indices.present != demo->indices.graphics) { + queue_create_infos[1].sType = + VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queue_create_infos[1].queueFamilyIndex = demo->indices.present; + queue_create_infos[1].queueCount = 1; + queue_create_infos[1].pQueuePriorities = &queuePriority; + num_queues = 2; + } + + memset(&create_info, 0, sizeof(VkDeviceCreateInfo)); + create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + create_info.queueCreateInfoCount = num_queues; + create_info.pQueueCreateInfos = queue_create_infos; + create_info.enabledExtensionCount = 1; + create_info.ppEnabledExtensionNames = &swap_chain_extension_name; + + result = vkCreateDevice(demo->physical_device, &create_info, NULL, + &demo->device); + + if (result != VK_SUCCESS) { + fprintf(stderr, "vkCreateDevice failed: %d\n", result); + goto cleanup; + } + + vkGetDeviceQueue(demo->device, demo->indices.graphics, 0, + &demo->graphics_queue); + vkGetDeviceQueue(demo->device, demo->indices.present, 0, + &demo->present_queue); + ret = true; +cleanup: + free(queue_create_infos); + return ret; +} + +bool create_sampler(struct vulkan_demo *demo) { + VkResult result; + VkSamplerCreateInfo sampler_info; + + memset(&sampler_info, 0, sizeof(VkSamplerCreateInfo)); + sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + sampler_info.pNext = NULL; + sampler_info.maxAnisotropy = 1.0; + sampler_info.magFilter = VK_FILTER_LINEAR; + sampler_info.minFilter = VK_FILTER_LINEAR; + sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + sampler_info.mipLodBias = 0.0f; + sampler_info.compareEnable = VK_FALSE; + sampler_info.compareOp = VK_COMPARE_OP_ALWAYS; + sampler_info.minLod = 0.0f; + sampler_info.maxLod = 0.0f; + sampler_info.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK; + + result = vkCreateSampler(demo->device, &sampler_info, NULL, &demo->sampler); + if (result != VK_SUCCESS) { + fprintf(stderr, "vkCreateSampler failed: %d\n", result); + return false; + } + return true; +} + +VkSurfaceFormatKHR +choose_swap_surface_format(VkSurfaceFormatKHR *available_formats, + uint32_t available_formats_len) { + VkSurfaceFormatKHR undefined_format = {VK_FORMAT_B8G8R8A8_UNORM, + VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}; + uint32_t i; + if (available_formats_len == 1 && + available_formats[0].format == VK_FORMAT_UNDEFINED) { + return undefined_format; + } + + for (i = 0; i < available_formats_len; i++) { + if (available_formats[i].format == VK_FORMAT_B8G8R8A8_UNORM && + available_formats[i].colorSpace == + VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { + return available_formats[i]; + } + } + + return available_formats[0]; +} + +VkPresentModeKHR +choose_swap_present_mode(VkPresentModeKHR *available_present_modes, + uint32_t available_present_modes_len) { + uint32_t i; + for (i = 0; i < available_present_modes_len; i++) { + /* + best mode to ensure good input latency while ensuring we are not + producing tearing + */ + if (available_present_modes[i] == VK_PRESENT_MODE_MAILBOX_KHR) { + return available_present_modes[i]; + } + } + + /* must be supported */ + return VK_PRESENT_MODE_FIFO_KHR; +} + +VkExtent2D choose_swap_extent(struct vulkan_demo *demo, + VkSurfaceCapabilitiesKHR *capabilities) { + int width, height; + VkExtent2D actual_extent; + if (capabilities->currentExtent.width != 0xFFFFFFFF) { + return capabilities->currentExtent; + } else { + /* not window size! */ + SDL_Vulkan_GetDrawableSize(demo->win, &width, &height); + + actual_extent.width = (uint32_t)width; + actual_extent.height = (uint32_t)height; + + actual_extent.width = NK_MAX( + capabilities->minImageExtent.width, + NK_MIN(capabilities->maxImageExtent.width, actual_extent.width)); + actual_extent.height = NK_MAX( + capabilities->minImageExtent.height, + NK_MIN(capabilities->maxImageExtent.height, actual_extent.height)); + + return actual_extent; + } +} + +bool create_swap_chain(struct vulkan_demo *demo) { + struct swap_chain_support_details swap_chain_support; + VkSurfaceFormatKHR surface_format; + VkPresentModeKHR present_mode; + VkExtent2D extent; + VkResult result; + VkSwapchainCreateInfoKHR create_info; + uint32_t queue_family_indices[2]; + bool ret = false; + + queue_family_indices[0] = (uint32_t)demo->indices.graphics; + queue_family_indices[1] = (uint32_t)demo->indices.present; + + if (!query_swap_chain_support(demo->physical_device, demo->surface, + &swap_chain_support)) { + goto cleanup; + } + surface_format = choose_swap_surface_format(swap_chain_support.formats, + swap_chain_support.formats_len); + present_mode = choose_swap_present_mode( + swap_chain_support.present_modes, swap_chain_support.present_modes_len); + extent = choose_swap_extent(demo, &swap_chain_support.capabilities); + + demo->swap_chain_images_len = + swap_chain_support.capabilities.minImageCount + 1; + if (swap_chain_support.capabilities.maxImageCount > 0 && + demo->swap_chain_images_len > + swap_chain_support.capabilities.maxImageCount) { + demo->swap_chain_images_len = + swap_chain_support.capabilities.maxImageCount; + } + + memset(&create_info, 0, sizeof(VkSwapchainCreateInfoKHR)); + create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + create_info.surface = demo->surface; + + create_info.minImageCount = demo->swap_chain_images_len; + create_info.imageFormat = surface_format.format; + create_info.imageColorSpace = surface_format.colorSpace; + create_info.imageExtent = extent; + create_info.imageArrayLayers = 1; + create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + + if (demo->indices.graphics != demo->indices.present) { + create_info.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + create_info.queueFamilyIndexCount = 2; + create_info.pQueueFamilyIndices = queue_family_indices; + } else { + create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + } + + create_info.preTransform = swap_chain_support.capabilities.currentTransform; + create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + create_info.presentMode = present_mode; + create_info.clipped = VK_TRUE; + + result = vkCreateSwapchainKHR(demo->device, &create_info, NULL, + &demo->swap_chain); + + if (result != VK_SUCCESS) { + fprintf(stderr, "vkCreateSwapchainKHR failed: %d\n", result); + goto cleanup; + } + + result = vkGetSwapchainImagesKHR(demo->device, demo->swap_chain, + &demo->swap_chain_images_len, NULL); + if (result != VK_SUCCESS) { + fprintf(stderr, "vkGetSwapchainImagesKHR failed: %d\n", result); + goto cleanup; + } + if (demo->swap_chain_images == NULL) { + demo->swap_chain_images = + malloc(demo->swap_chain_images_len * sizeof(VkImage)); + } + result = vkGetSwapchainImagesKHR(demo->device, demo->swap_chain, + &demo->swap_chain_images_len, + demo->swap_chain_images); + + if (result != VK_SUCCESS) { + fprintf(stderr, "vkGetSwapchainImagesKHR failed: %d\n", result); + return false; + } + + demo->swap_chain_image_format = surface_format.format; + demo->swap_chain_image_extent = extent; + + ret = true; +cleanup: + swap_chain_support_details_free(&swap_chain_support); + + return ret; +} + +bool create_swap_chain_image_views(struct vulkan_demo *demo) { + uint32_t i; + VkResult result; + VkImageViewCreateInfo create_info; + + memset(&create_info, 0, sizeof(VkImageViewCreateInfo)); + create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + create_info.format = demo->swap_chain_image_format; + create_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + create_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + create_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + create_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + create_info.subresourceRange.baseMipLevel = 0; + create_info.subresourceRange.levelCount = 1; + create_info.subresourceRange.baseArrayLayer = 0; + create_info.subresourceRange.layerCount = 1; + + if (!demo->swap_chain_image_views) { + demo->swap_chain_image_views = + malloc(demo->swap_chain_images_len * sizeof(VkImageView)); + } + + for (i = 0; i < demo->swap_chain_images_len; i++) { + create_info.image = demo->swap_chain_images[i]; + result = vkCreateImageView(demo->device, &create_info, NULL, + &demo->swap_chain_image_views[i]); + + if (result != VK_SUCCESS) { + fprintf(stderr, "vkCreateImageView failed: %d\n", result); + return false; + } + } + return true; +} + +bool create_overlay_images(struct vulkan_demo *demo) { + uint32_t i, j; + VkResult result; + VkMemoryRequirements mem_requirements; + VkPhysicalDeviceMemoryProperties mem_properties; + int found; + VkImageCreateInfo image_info; + VkMemoryAllocateInfo alloc_info; + VkImageViewCreateInfo image_view_info; + + memset(&image_info, 0, sizeof(VkImageCreateInfo)); + image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + image_info.imageType = VK_IMAGE_TYPE_2D; + image_info.extent.width = demo->swap_chain_image_extent.width; + image_info.extent.height = demo->swap_chain_image_extent.height; + image_info.extent.depth = 1; + image_info.mipLevels = 1; + image_info.arrayLayers = 1; + image_info.format = demo->swap_chain_image_format; + image_info.tiling = VK_IMAGE_TILING_OPTIMAL; + image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + image_info.usage = + VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + image_info.samples = VK_SAMPLE_COUNT_1_BIT; + image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + memset(&alloc_info, 0, sizeof(VkMemoryAllocateInfo)); + alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + + memset(&image_view_info, 0, sizeof(VkImageViewCreateInfo)); + image_view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + image_view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + image_view_info.format = demo->swap_chain_image_format; + image_view_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + image_view_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + image_view_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + image_view_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + image_view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + image_view_info.subresourceRange.baseMipLevel = 0; + image_view_info.subresourceRange.levelCount = 1; + image_view_info.subresourceRange.baseArrayLayer = 0; + image_view_info.subresourceRange.layerCount = 1; + + if (!demo->overlay_images) { + demo->overlay_images = + malloc(demo->swap_chain_images_len * sizeof(VkImage)); + } + + if (!demo->overlay_image_memories) { + demo->overlay_image_memories = + malloc(demo->swap_chain_images_len * sizeof(VkDeviceMemory)); + } + if (!demo->overlay_image_views) { + demo->overlay_image_views = + malloc(demo->swap_chain_images_len * sizeof(VkImageView)); + } + + for (i = 0; i < demo->swap_chain_images_len; i++) { + result = vkCreateImage(demo->device, &image_info, NULL, + &demo->overlay_images[i]); + + if (result != VK_SUCCESS) { + fprintf(stderr, "vkCreateImage failed for index %lu: %d\n", + (unsigned long)i, result); + return false; + } + + vkGetImageMemoryRequirements(demo->device, demo->overlay_images[i], + &mem_requirements); + + alloc_info.allocationSize = mem_requirements.size; + + vkGetPhysicalDeviceMemoryProperties(demo->physical_device, + &mem_properties); + found = 0; + for (j = 0; j < mem_properties.memoryTypeCount; j++) { + if ((mem_requirements.memoryTypeBits & (1 << j)) && + (mem_properties.memoryTypes[j].propertyFlags & + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) == + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) { + found = 1; + break; + } + } + if (!found) { + fprintf(stderr, + "failed to find suitable memory type for index %lu!\n", + (unsigned long)i); + return false; + } + alloc_info.memoryTypeIndex = j; + result = vkAllocateMemory(demo->device, &alloc_info, NULL, + &demo->overlay_image_memories[i]); + if (result != VK_SUCCESS) { + fprintf(stderr, + "failed to allocate vulkan memory for index %lu: %d!\n", + (unsigned long)i, result); + return false; + } + result = vkBindImageMemory(demo->device, demo->overlay_images[i], + demo->overlay_image_memories[i], 0); + if (result != VK_SUCCESS) { + fprintf(stderr, "Couldn't bind image memory for index %lu: %d\n", + (unsigned long)i, result); + return false; + } + + image_view_info.image = demo->overlay_images[i]; + result = vkCreateImageView(demo->device, &image_view_info, NULL, + &demo->overlay_image_views[i]); + + if (result != VK_SUCCESS) { + fprintf(stderr, "vkCreateImageView failed for index %lu: %d\n", + (unsigned long)i, result); + return false; + } + } + return true; +} + +bool create_render_pass(struct vulkan_demo *demo) { + VkAttachmentDescription attachment; + VkAttachmentReference color_attachment_ref; + VkSubpassDescription subpass; + VkSubpassDependency dependency; + VkRenderPassCreateInfo render_pass_info; + VkResult result; + + memset(&attachment, 0, sizeof(VkAttachmentDescription)); + attachment.format = demo->swap_chain_image_format; + attachment.samples = VK_SAMPLE_COUNT_1_BIT; + attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + memset(&color_attachment_ref, 0, sizeof(VkAttachmentReference)); + color_attachment_ref.attachment = 0; + color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + memset(&subpass, 0, sizeof(VkSubpassDescription)); + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &color_attachment_ref; + + memset(&dependency, 0, sizeof(VkSubpassDependency)); + dependency.srcSubpass = VK_SUBPASS_EXTERNAL; + dependency.dstSubpass = 0; + dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.srcAccessMask = 0; + dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + + memset(&render_pass_info, 0, sizeof(VkRenderPassCreateInfo)); + render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + render_pass_info.attachmentCount = 1; + render_pass_info.pAttachments = &attachment; + render_pass_info.subpassCount = 1; + render_pass_info.pSubpasses = &subpass; + render_pass_info.dependencyCount = 1; + render_pass_info.pDependencies = &dependency; + + result = vkCreateRenderPass(demo->device, &render_pass_info, NULL, + &demo->render_pass); + if (result != VK_SUCCESS) { + fprintf(stderr, "vkCreateRenderPass failed: %d\n", result); + return false; + } + return true; +} + +bool create_framebuffers(struct vulkan_demo *demo) { + uint32_t i; + VkResult result; + VkFramebufferCreateInfo framebuffer_info; + + if (!demo->framebuffers) { + demo->framebuffers = + malloc(demo->swap_chain_images_len * sizeof(VkFramebuffer)); + } + + memset(&framebuffer_info, 0, sizeof(VkFramebufferCreateInfo)); + framebuffer_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebuffer_info.renderPass = demo->render_pass; + framebuffer_info.attachmentCount = 1; + framebuffer_info.width = demo->swap_chain_image_extent.width; + framebuffer_info.height = demo->swap_chain_image_extent.height; + framebuffer_info.layers = 1; + + for (i = 0; i < demo->swap_chain_images_len; i++) { + framebuffer_info.pAttachments = &demo->swap_chain_image_views[i]; + + result = vkCreateFramebuffer(demo->device, &framebuffer_info, NULL, + &demo->framebuffers[i]); + if (result != VK_SUCCESS) { + fprintf(stderr, "vkCreateFramebuffer failed from index %lu: %d\n", + (unsigned long)i, result); + return false; + } + } + return true; +} + +bool create_descriptor_set_layout(struct vulkan_demo *demo) { + VkDescriptorSetLayoutBinding overlay_layout_binding; + VkDescriptorSetLayoutCreateInfo descriptor_set_layout_create_nfo; + VkResult result; + + memset(&overlay_layout_binding, 0, sizeof(VkDescriptorSetLayoutBinding)); + overlay_layout_binding.binding = 0; + overlay_layout_binding.descriptorCount = 1; + overlay_layout_binding.descriptorType = + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + overlay_layout_binding.pImmutableSamplers = NULL; + overlay_layout_binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + + memset(&descriptor_set_layout_create_nfo, 0, + sizeof(VkDescriptorSetLayoutCreateInfo)); + descriptor_set_layout_create_nfo.sType = + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + descriptor_set_layout_create_nfo.bindingCount = 1; + descriptor_set_layout_create_nfo.pBindings = &overlay_layout_binding; + + result = vkCreateDescriptorSetLayout(demo->device, + &descriptor_set_layout_create_nfo, + NULL, &demo->descriptor_set_layout); + if (result != VK_SUCCESS) { + fprintf(stderr, "vkCreateDescriptorSetLayout failed: %d\n", result); + return false; + } + return true; +} + +bool create_descriptor_pool(struct vulkan_demo *demo) { + VkDescriptorPoolSize pool_size; + VkDescriptorPoolCreateInfo pool_info; + VkResult result; + + memset(&pool_size, 0, sizeof(VkDescriptorPoolSize)); + pool_size.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + pool_size.descriptorCount = demo->swap_chain_images_len; + + memset(&pool_info, 0, sizeof(VkDescriptorPoolCreateInfo)); + pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + pool_info.poolSizeCount = 1; + pool_info.pPoolSizes = &pool_size; + pool_info.maxSets = demo->swap_chain_images_len; + result = vkCreateDescriptorPool(demo->device, &pool_info, NULL, + &demo->descriptor_pool); + + if (result != VK_SUCCESS) { + fprintf(stderr, "vkCreateDescriptorPool failed: %d\n", result); + return false; + } + return true; +} + +void update_descriptor_sets(struct vulkan_demo *demo) { + uint32_t i; + VkDescriptorImageInfo descriptor_image_info; + VkWriteDescriptorSet descriptor_write; + + memset(&descriptor_image_info, 0, sizeof(VkDescriptorImageInfo)); + descriptor_image_info.imageLayout = + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + descriptor_image_info.sampler = demo->sampler; + + memset(&descriptor_write, 0, sizeof(VkWriteDescriptorSet)); + descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptor_write.dstBinding = 0; + descriptor_write.dstArrayElement = 0; + descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + descriptor_write.descriptorCount = 1; + descriptor_write.pImageInfo = &descriptor_image_info; + + for (i = 0; i < demo->swap_chain_images_len; i++) { + descriptor_write.dstSet = demo->descriptor_sets[i]; + descriptor_image_info.imageView = demo->overlay_image_views[i]; + + vkUpdateDescriptorSets(demo->device, 1, &descriptor_write, 0, NULL); + } +} + +bool create_descriptor_sets(struct vulkan_demo *demo) { + bool ret = false; + VkDescriptorSetLayout *descriptor_set_layouts; + VkDescriptorSetAllocateInfo alloc_info; + uint32_t i; + VkResult result; + + demo->descriptor_sets = + malloc(demo->swap_chain_images_len * sizeof(VkDescriptorSet)); + descriptor_set_layouts = + malloc(demo->swap_chain_images_len * sizeof(VkDescriptorSetLayout)); + + for (i = 0; i < demo->swap_chain_images_len; i++) { + descriptor_set_layouts[i] = demo->descriptor_set_layout; + } + + memset(&alloc_info, 0, sizeof(VkDescriptorSetAllocateInfo)); + alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + alloc_info.descriptorPool = demo->descriptor_pool; + alloc_info.descriptorSetCount = demo->swap_chain_images_len; + alloc_info.pSetLayouts = descriptor_set_layouts; + result = vkAllocateDescriptorSets(demo->device, &alloc_info, + demo->descriptor_sets); + if (result != VK_SUCCESS) { + fprintf(stderr, "vkAllocateDescriptorSets failed: %d\n", result); + goto cleanup; + } + + update_descriptor_sets(demo); + + ret = true; +cleanup: + free(descriptor_set_layouts); + + return ret; +} + +bool create_shader_module(VkDevice device, char *shader_buffer, + size_t shader_buffer_len, + VkShaderModule *shader_module) { + VkShaderModuleCreateInfo create_info; + VkResult result; + + memset(&create_info, 0, sizeof(VkShaderModuleCreateInfo)); + create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + create_info.codeSize = shader_buffer_len; + create_info.pCode = (const uint32_t *)shader_buffer; + + result = vkCreateShaderModule(device, &create_info, NULL, shader_module); + if (result != VK_SUCCESS) { + fprintf(stderr, "vkCreateShaderModule failed: %d\n", result); + return false; + } + + return true; +} + +bool create_graphics_pipeline(struct vulkan_demo *demo) { + bool ret = false; + char *vert_shader_code = NULL; + char *frag_shader_code = NULL; + VkShaderModule vert_shader_module = VK_NULL_HANDLE; + VkShaderModule frag_shader_module = VK_NULL_HANDLE; + FILE *fp; + size_t file_len; + VkPipelineShaderStageCreateInfo vert_shader_stage_info; + VkPipelineShaderStageCreateInfo frag_shader_stage_info; + VkPipelineShaderStageCreateInfo shader_stages[2]; + VkPipelineVertexInputStateCreateInfo vertex_input_info; + VkPipelineInputAssemblyStateCreateInfo input_assembly; + VkViewport viewport; + VkRect2D scissor; + VkPipelineViewportStateCreateInfo viewport_state; + VkPipelineRasterizationStateCreateInfo rasterizer; + VkPipelineMultisampleStateCreateInfo multisampling; + VkPipelineColorBlendAttachmentState color_blend_attachment; + VkPipelineColorBlendStateCreateInfo color_blending; + VkPipelineLayoutCreateInfo pipeline_layout_info; + VkResult result; + VkGraphicsPipelineCreateInfo pipeline_info; + size_t read_result; + + fp = fopen("shaders/demo.vert.spv", "r"); + if (!fp) { + fprintf(stderr, "Couldn't open shaders/demo.vert.spv\n"); + return false; + } + fseek(fp, 0, SEEK_END); + file_len = ftell(fp); + vert_shader_code = malloc(file_len); + fseek(fp, 0, 0); + read_result = fread(vert_shader_code, file_len, 1, fp); + fclose(fp); + if (read_result != 1) { + fprintf(stderr, "Could not read vertex shader\n"); + goto cleanup; + } + + if (!create_shader_module(demo->device, vert_shader_code, file_len, + &vert_shader_module)) { + goto cleanup; + } + + fp = fopen("shaders/demo.frag.spv", "r"); + if (!fp) { + fprintf(stderr, "Couldn't open shaders/demo.frag.spv\n"); + return false; + } + fseek(fp, 0, SEEK_END); + file_len = ftell(fp); + frag_shader_code = malloc(file_len); + fseek(fp, 0, 0); + read_result = fread(frag_shader_code, file_len, 1, fp); + fclose(fp); + if (read_result != 1) { + fprintf(stderr, "Could not read fragment shader\n"); + goto cleanup; + } + + if (!create_shader_module(demo->device, frag_shader_code, file_len, + &frag_shader_module)) { + goto cleanup; + } + + memset(&vert_shader_stage_info, 0, sizeof(VkPipelineShaderStageCreateInfo)); + vert_shader_stage_info.sType = + VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + vert_shader_stage_info.stage = VK_SHADER_STAGE_VERTEX_BIT; + vert_shader_stage_info.module = vert_shader_module; + vert_shader_stage_info.pName = "main"; + + memset(&frag_shader_stage_info, 0, sizeof(VkPipelineShaderStageCreateInfo)); + frag_shader_stage_info.sType = + VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + frag_shader_stage_info.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + frag_shader_stage_info.module = frag_shader_module; + frag_shader_stage_info.pName = "main"; + + shader_stages[0] = vert_shader_stage_info; + shader_stages[1] = frag_shader_stage_info; + + memset(&vertex_input_info, 0, sizeof(VkPipelineVertexInputStateCreateInfo)); + vertex_input_info.sType = + VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + + memset(&input_assembly, 0, sizeof(VkPipelineInputAssemblyStateCreateInfo)); + input_assembly.sType = + VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + input_assembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + input_assembly.primitiveRestartEnable = VK_FALSE; + + memset(&viewport, 0, sizeof(VkViewport)); + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = (float)demo->swap_chain_image_extent.width; + viewport.height = (float)demo->swap_chain_image_extent.height; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + memset(&scissor, 0, sizeof(VkRect2D)); + scissor.extent.width = demo->swap_chain_image_extent.width; + scissor.extent.height = demo->swap_chain_image_extent.height; + + memset(&viewport_state, 0, sizeof(VkPipelineViewportStateCreateInfo)); + viewport_state.sType = + VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewport_state.viewportCount = 1; + viewport_state.pViewports = &viewport; + viewport_state.scissorCount = 1; + viewport_state.pScissors = &scissor; + + memset(&rasterizer, 0, sizeof(VkPipelineRasterizationStateCreateInfo)); + rasterizer.sType = + VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizer.depthClampEnable = VK_FALSE; + rasterizer.rasterizerDiscardEnable = VK_FALSE; + rasterizer.polygonMode = VK_POLYGON_MODE_FILL; + rasterizer.lineWidth = 1.0f; + rasterizer.cullMode = VK_CULL_MODE_FRONT_BIT; + rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + rasterizer.depthBiasEnable = VK_FALSE; + + memset(&multisampling, 0, sizeof(VkPipelineMultisampleStateCreateInfo)); + multisampling.sType = + VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling.sampleShadingEnable = VK_FALSE; + multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + + memset(&color_blend_attachment, 0, + sizeof(VkPipelineColorBlendAttachmentState)); + color_blend_attachment.colorWriteMask = + VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | + VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + color_blend_attachment.blendEnable = VK_TRUE; + color_blend_attachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; + color_blend_attachment.dstColorBlendFactor = + VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + color_blend_attachment.colorBlendOp = VK_BLEND_OP_ADD; + color_blend_attachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + color_blend_attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; + color_blend_attachment.alphaBlendOp = VK_BLEND_OP_ADD; + + memset(&color_blending, 0, sizeof(VkPipelineColorBlendStateCreateInfo)); + color_blending.sType = + VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + color_blending.logicOpEnable = VK_FALSE; + color_blending.logicOp = VK_LOGIC_OP_COPY; + color_blending.attachmentCount = 1; + color_blending.pAttachments = &color_blend_attachment; + color_blending.blendConstants[0] = 1.0f; + color_blending.blendConstants[1] = 1.0f; + color_blending.blendConstants[2] = 1.0f; + color_blending.blendConstants[3] = 1.0f; + + memset(&pipeline_layout_info, 0, sizeof(VkPipelineLayoutCreateInfo)); + pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipeline_layout_info.setLayoutCount = 0; + pipeline_layout_info.pushConstantRangeCount = 0; + pipeline_layout_info.setLayoutCount = 1; + pipeline_layout_info.pSetLayouts = &demo->descriptor_set_layout; + + result = vkCreatePipelineLayout(demo->device, &pipeline_layout_info, NULL, + &demo->pipeline_layout); + + if (result != VK_SUCCESS) { + fprintf(stderr, "vkCreatePipelineLayout failed: %d\n", result); + goto cleanup; + } + + memset(&pipeline_info, 0, sizeof(VkGraphicsPipelineCreateInfo)); + pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipeline_info.stageCount = 2; + pipeline_info.pStages = shader_stages; + pipeline_info.pVertexInputState = &vertex_input_info; + pipeline_info.pInputAssemblyState = &input_assembly; + pipeline_info.pViewportState = &viewport_state; + pipeline_info.pRasterizationState = &rasterizer; + pipeline_info.pMultisampleState = &multisampling; + pipeline_info.pColorBlendState = &color_blending; + pipeline_info.layout = demo->pipeline_layout; + pipeline_info.renderPass = demo->render_pass; + pipeline_info.basePipelineHandle = NULL; + + result = vkCreateGraphicsPipelines(demo->device, NULL, 1, &pipeline_info, + NULL, &demo->pipeline); + if (result != VK_SUCCESS) { + fprintf(stderr, "vkCreateGraphicsPipelines failed: %d\n", result); + goto cleanup; + } + ret = true; +cleanup: + if (frag_shader_module) { + vkDestroyShaderModule(demo->device, frag_shader_module, NULL); + } + if (frag_shader_code) { + free(frag_shader_code); + } + if (vert_shader_module) { + vkDestroyShaderModule(demo->device, vert_shader_module, NULL); + } + if (vert_shader_code) { + free(vert_shader_code); + } + + return ret; +} + +bool create_command_pool(struct vulkan_demo *demo) { + VkCommandPoolCreateInfo pool_info; + VkResult result; + + memset(&pool_info, 0, sizeof(VkCommandPoolCreateInfo)); + pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + pool_info.queueFamilyIndex = demo->indices.graphics; + + result = vkCreateCommandPool(demo->device, &pool_info, NULL, + &demo->command_pool); + if (result != VK_SUCCESS) { + fprintf(stderr, "vkCreateCommandPool failed: %d\n", result); + return false; + } + return true; +} + +bool create_command_buffers(struct vulkan_demo *demo) { + VkCommandBufferAllocateInfo alloc_info; + VkResult result; + + demo->command_buffers = + malloc(demo->swap_chain_images_len * sizeof(VkCommandBuffer)); + + memset(&alloc_info, 0, sizeof(VkCommandBufferAllocateInfo)); + alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + alloc_info.commandPool = demo->command_pool; + alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + alloc_info.commandBufferCount = demo->swap_chain_images_len; + + result = vkAllocateCommandBuffers(demo->device, &alloc_info, + demo->command_buffers); + if (result != VK_SUCCESS) { + fprintf(stderr, "vkAllocateCommandBuffers failed: %d\n", result); + return false; + } + + return true; +} + +bool create_semaphores(struct vulkan_demo *demo) { + VkSemaphoreCreateInfo semaphore_info; + VkResult result; + + memset(&semaphore_info, 0, sizeof(VkSemaphoreCreateInfo)); + semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + result = vkCreateSemaphore(demo->device, &semaphore_info, NULL, + &demo->image_available); + if (result != VK_SUCCESS) { + fprintf(stderr, "vkCreateSemaphore failed: %d\n", result); + return false; + } + result = vkCreateSemaphore(demo->device, &semaphore_info, NULL, + &demo->render_finished); + if (result != VK_SUCCESS) { + fprintf(stderr, "vkCreateSemaphore failed: %d\n", result); + return false; + } + return true; +} + +bool create_fence(struct vulkan_demo *demo) { + VkResult result; + VkFenceCreateInfo fence_create_info; + + memset(&fence_create_info, 0, sizeof(VkFenceCreateInfo)); + fence_create_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fence_create_info.flags = VK_FENCE_CREATE_SIGNALED_BIT; + + result = vkCreateFence(demo->device, &fence_create_info, NULL, + &demo->render_fence); + + if (result != VK_SUCCESS) { + fprintf(stderr, "vkCreateFence failed: %d\n", result); + return false; + } + return true; +} + +bool create_swap_chain_related_resources(struct vulkan_demo *demo) { + if (!create_swap_chain(demo)) { + return false; + } + if (!create_swap_chain_image_views(demo)) { + return false; + } + if (!create_overlay_images(demo)) { + return false; + } + if (!create_render_pass(demo)) { + return false; + } + if (!create_framebuffers(demo)) { + return false; + } + if (!create_graphics_pipeline(demo)) { + return false; + } + return true; +} + +bool destroy_swap_chain_related_resources(struct vulkan_demo *demo) { + uint32_t i; + VkResult result; + + result = vkQueueWaitIdle(demo->graphics_queue); + if (result != VK_SUCCESS) { + fprintf(stderr, "vkQueueWaitIdle failed: %d\n", result); + return false; + } + + for (i = 0; i < demo->swap_chain_images_len; i++) { + vkDestroyFramebuffer(demo->device, demo->framebuffers[i], NULL); + vkDestroyImageView(demo->device, demo->overlay_image_views[i], NULL); + vkDestroyImage(demo->device, demo->overlay_images[i], NULL); + vkFreeMemory(demo->device, demo->overlay_image_memories[i], NULL); + vkDestroyImageView(demo->device, demo->swap_chain_image_views[i], NULL); + } + vkDestroySwapchainKHR(demo->device, demo->swap_chain, NULL); + vkDestroyRenderPass(demo->device, demo->render_pass, NULL); + vkDestroyPipeline(demo->device, demo->pipeline, NULL); + vkDestroyPipelineLayout(demo->device, demo->pipeline_layout, NULL); + return true; +} + +bool create_demo_texture(struct vulkan_demo *demo) { + VkResult result; + VkMemoryRequirements mem_requirements; + VkPhysicalDeviceMemoryProperties mem_properties; + int found; + uint32_t i; + VkImageCreateInfo image_info; + VkMemoryAllocateInfo alloc_info; + VkImageViewCreateInfo image_view_info; + VkBufferCreateInfo buffer_info; + struct { + VkDeviceMemory memory; + VkBuffer buffer; + } staging_buffer; + void *data; + VkCommandBuffer command_buffer; + VkCommandBufferBeginInfo begin_info; + VkImageMemoryBarrier image_transfer_dst_memory_barrier; + VkBufferImageCopy buffer_copy_region; + VkImageMemoryBarrier image_shader_memory_barrier; + VkFence fence; + VkFenceCreateInfo fence_create; + VkSubmitInfo submit_info; + + memset(&image_info, 0, sizeof(VkImageCreateInfo)); + image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + image_info.imageType = VK_IMAGE_TYPE_2D; + image_info.extent.width = 2; + image_info.extent.height = 2; + image_info.extent.depth = 1; + image_info.mipLevels = 1; + image_info.arrayLayers = 1; + image_info.format = VK_FORMAT_R8_UNORM; + image_info.tiling = VK_IMAGE_TILING_LINEAR; + image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + image_info.usage = + VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + image_info.samples = VK_SAMPLE_COUNT_1_BIT; + image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + memset(&alloc_info, 0, sizeof(VkMemoryAllocateInfo)); + alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + + memset(&image_view_info, 0, sizeof(VkImageViewCreateInfo)); + image_view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + image_view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + image_view_info.format = VK_FORMAT_R8_UNORM; + image_view_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + image_view_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + image_view_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + image_view_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + image_view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + image_view_info.subresourceRange.baseMipLevel = 0; + image_view_info.subresourceRange.levelCount = 1; + image_view_info.subresourceRange.baseArrayLayer = 0; + image_view_info.subresourceRange.layerCount = 1; + + result = vkCreateImage(demo->device, &image_info, NULL, + &demo->demo_texture_image); + + if (result != VK_SUCCESS) { + fprintf(stderr, "vkCreateImage failed: %d\n", result); + return false; + } + + vkGetImageMemoryRequirements(demo->device, demo->demo_texture_image, + &mem_requirements); + + alloc_info.allocationSize = mem_requirements.size; + + vkGetPhysicalDeviceMemoryProperties(demo->physical_device, &mem_properties); + found = 0; + for (i = 0; i < mem_properties.memoryTypeCount; i++) { + if ((mem_requirements.memoryTypeBits & (1 << i)) && + (mem_properties.memoryTypes[i].propertyFlags & + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) == + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) { + found = 1; + break; + } + } + if (!found) { + fprintf(stderr, "failed to find suitable memory for demo texture!\n"); + return false; + } + alloc_info.memoryTypeIndex = i; + result = vkAllocateMemory(demo->device, &alloc_info, NULL, + &demo->demo_texture_memory); + if (result != VK_SUCCESS) { + fprintf(stderr, + "failed to allocate vulkan memory for demo texture: %d!\n", + result); + return false; + } + result = vkBindImageMemory(demo->device, demo->demo_texture_image, + demo->demo_texture_memory, 0); + if (result != VK_SUCCESS) { + fprintf(stderr, "Couldn't bind image memory for demo texture: %d\n", + result); + return false; + } + + image_view_info.image = demo->demo_texture_image; + result = vkCreateImageView(demo->device, &image_view_info, NULL, + &demo->demo_texture_image_view); + + if (result != VK_SUCCESS) { + fprintf(stderr, "vkCreateImageView failed for demo texture: %d\n", + result); + return false; + } + + memset(&buffer_info, 0, sizeof(VkBufferCreateInfo)); + buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buffer_info.size = alloc_info.allocationSize; + buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + result = vkCreateBuffer(demo->device, &buffer_info, NULL, + &staging_buffer.buffer); + if (result != VK_SUCCESS) { + fprintf(stderr, "vkCreateBuffer failed for demo texture: %d\n", result); + return false; + } + vkGetBufferMemoryRequirements(demo->device, staging_buffer.buffer, + &mem_requirements); + + alloc_info.allocationSize = mem_requirements.size; + found = 0; + for (i = 0; i < mem_properties.memoryTypeCount; i++) { + if ((mem_requirements.memoryTypeBits & (1 << i)) && + (mem_properties.memoryTypes[i].propertyFlags & + (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) == + (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) { + found = 1; + break; + } + } + if (!found) { + fprintf(stderr, "failed to find suitable staging buffer memory for " + "demo texture!\n"); + return false; + } + alloc_info.memoryTypeIndex = i; + result = vkAllocateMemory(demo->device, &alloc_info, NULL, + &staging_buffer.memory); + if (!found) { + fprintf(stderr, "vkAllocateMemory failed for demo texture: %d\n", + result); + return false; + } + result = vkBindBufferMemory(demo->device, staging_buffer.buffer, + staging_buffer.memory, 0); + if (!found) { + fprintf(stderr, "vkBindBufferMemory failed for demo texture: %d\n", + result); + return false; + } + + result = vkMapMemory(demo->device, staging_buffer.memory, 0, + sizeof(uint32_t), 0, &data); + if (result != VK_SUCCESS) { + fprintf(stderr, "vkMapMemory failed for demo texture: %d\n", result); + return false; + } + *((uint32_t *)data) = 0x00FFFF00; + vkUnmapMemory(demo->device, staging_buffer.memory); + + memset(&begin_info, 0, sizeof(VkCommandBufferBeginInfo)); + begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + + command_buffer = demo->command_buffers[0]; + result = vkBeginCommandBuffer(command_buffer, &begin_info); + + memset(&image_transfer_dst_memory_barrier, 0, sizeof(VkImageMemoryBarrier)); + image_transfer_dst_memory_barrier.sType = + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + image_transfer_dst_memory_barrier.image = demo->demo_texture_image; + image_transfer_dst_memory_barrier.srcQueueFamilyIndex = + VK_QUEUE_FAMILY_IGNORED; + image_transfer_dst_memory_barrier.dstQueueFamilyIndex = + VK_QUEUE_FAMILY_IGNORED; + image_transfer_dst_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + image_transfer_dst_memory_barrier.newLayout = + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + image_transfer_dst_memory_barrier.subresourceRange.aspectMask = + VK_IMAGE_ASPECT_COLOR_BIT; + image_transfer_dst_memory_barrier.subresourceRange.levelCount = 1; + image_transfer_dst_memory_barrier.subresourceRange.layerCount = 1; + image_transfer_dst_memory_barrier.dstAccessMask = + VK_ACCESS_TRANSFER_WRITE_BIT; + + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1, + &image_transfer_dst_memory_barrier); + + memset(&buffer_copy_region, 0, sizeof(VkBufferImageCopy)); + buffer_copy_region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + buffer_copy_region.imageSubresource.layerCount = 1; + buffer_copy_region.imageExtent.width = 2; + buffer_copy_region.imageExtent.height = 2; + buffer_copy_region.imageExtent.depth = 1; + + vkCmdCopyBufferToImage( + command_buffer, staging_buffer.buffer, demo->demo_texture_image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &buffer_copy_region); + + memset(&image_shader_memory_barrier, 0, sizeof(VkImageMemoryBarrier)); + image_shader_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + image_shader_memory_barrier.image = demo->demo_texture_image; + image_shader_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_shader_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_shader_memory_barrier.oldLayout = + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + image_shader_memory_barrier.newLayout = + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + image_shader_memory_barrier.subresourceRange.aspectMask = + VK_IMAGE_ASPECT_COLOR_BIT; + image_shader_memory_barrier.subresourceRange.levelCount = 1; + image_shader_memory_barrier.subresourceRange.layerCount = 1; + image_shader_memory_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + image_shader_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT, + + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, NULL, 0, + NULL, 1, &image_shader_memory_barrier); + + result = vkEndCommandBuffer(command_buffer); + if (result != VK_SUCCESS) { + fprintf(stderr, "vkEndCommandBuffer failed for demo texture: %d\n", + result); + return false; + } + + memset(&fence_create, 0, sizeof(VkFenceCreateInfo)); + fence_create.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + result = vkCreateFence(demo->device, &fence_create, NULL, &fence); + if (result != VK_SUCCESS) { + fprintf(stderr, "vkCreateFence failed for demo texture: %d\n", result); + return false; + } + + memset(&submit_info, 0, sizeof(VkSubmitInfo)); + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &command_buffer; + + result = vkQueueSubmit(demo->graphics_queue, 1, &submit_info, fence); + if (result != VK_SUCCESS) { + fprintf(stderr, "vkQueueSubmit failed for demo texture: %d\n", result); + return false; + } + result = vkWaitForFences(demo->device, 1, &fence, VK_TRUE, UINT64_MAX); + if (result != VK_SUCCESS) { + fprintf(stderr, "vkWaitForFences failed for demo texture: %d\n", + result); + return false; + } + + vkDestroyBuffer(demo->device, staging_buffer.buffer, NULL); + vkFreeMemory(demo->device, staging_buffer.memory, NULL); + vkDestroyFence(demo->device, fence, NULL); + + return true; +} + +bool create_vulkan_demo(struct vulkan_demo *demo) { + if (!create_instance(demo)) { + return false; + } + if (!create_surface(demo)) { + return false; + } + if (!create_physical_device(demo)) { + return false; + } + if (!create_logical_device(demo)) { + return false; + } + if (!create_sampler(demo)) { + return false; + } + if (!create_descriptor_set_layout(demo)) { + return false; + } + if (!create_swap_chain_related_resources(demo)) { + return false; + } + if (!create_descriptor_pool(demo)) { + return false; + } + if (!create_descriptor_sets(demo)) { + return false; + } + if (!create_command_pool(demo)) { + return false; + } + if (!create_command_buffers(demo)) { + return false; + } + if (!create_semaphores(demo)) { + return false; + } + if (!create_fence(demo)) { + return false; + } + if (!create_demo_texture(demo)) { + return false; + } + + return true; +} + +bool recreate_swap_chain(struct vulkan_demo *demo) { + printf("recreating swapchain\n"); + if (!destroy_swap_chain_related_resources(demo)) { + return false; + } + if (!create_swap_chain_related_resources(demo)) { + return false; + } + + update_descriptor_sets(demo); + + nk_sdl_resize(demo->swap_chain_image_extent.width, + demo->swap_chain_image_extent.height); + + return true; +} + +bool render(struct vulkan_demo *demo, struct nk_colorf *bg, + VkSemaphore wait_semaphore, uint32_t image_index) { + VkCommandBufferBeginInfo command_buffer_begin_info; + VkCommandBuffer command_buffer; + VkRenderPassBeginInfo render_pass_info; + VkSubmitInfo submit_info; + VkPipelineStageFlags wait_stage = + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + VkResult result; + VkPresentInfoKHR present_info; + VkClearValue clear_color; + + memcpy(&clear_color.color, bg, sizeof(VkClearColorValue)); + + memset(&command_buffer_begin_info, 0, sizeof(VkCommandBufferBeginInfo)); + command_buffer_begin_info.sType = + VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + + command_buffer = demo->command_buffers[image_index]; + result = vkBeginCommandBuffer(command_buffer, &command_buffer_begin_info); + + if (result != VK_SUCCESS) { + fprintf(stderr, "vkBeginCommandBuffer failed: %d\n", result); + return false; + } + + memset(&render_pass_info, 0, sizeof(VkRenderPassBeginInfo)); + render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + render_pass_info.renderPass = demo->render_pass; + render_pass_info.framebuffer = demo->framebuffers[image_index]; + render_pass_info.renderArea.offset.x = 0; + render_pass_info.renderArea.offset.y = 0; + render_pass_info.renderArea.extent = demo->swap_chain_image_extent; + render_pass_info.clearValueCount = 1; + render_pass_info.pClearValues = &clear_color; + + vkCmdBeginRenderPass(command_buffer, &render_pass_info, + VK_SUBPASS_CONTENTS_INLINE); + + vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + demo->pipeline); + vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + demo->pipeline_layout, 0, 1, + &demo->descriptor_sets[image_index], 0, NULL); + vkCmdDraw(command_buffer, 3, 1, 0, 0); + + vkCmdEndRenderPass(command_buffer); + + result = vkEndCommandBuffer(command_buffer); + if (result != VK_SUCCESS) { + fprintf(stderr, "vkEndCommandBuffer failed: %d\n", result); + return false; + } + + memset(&submit_info, 0, sizeof(VkSubmitInfo)); + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.waitSemaphoreCount = 1; + submit_info.pWaitSemaphores = &wait_semaphore; + submit_info.pWaitDstStageMask = &wait_stage; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &demo->command_buffers[image_index]; + submit_info.signalSemaphoreCount = 1; + submit_info.pSignalSemaphores = &demo->render_finished; + + result = vkQueueSubmit(demo->graphics_queue, 1, &submit_info, + demo->render_fence); + + if (result != VK_SUCCESS) { + fprintf(stderr, "vkQueueSubmit failed: %d\n", result); + return false; + } + + memset(&present_info, 0, sizeof(VkPresentInfoKHR)); + present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + present_info.waitSemaphoreCount = 1; + present_info.pWaitSemaphores = &demo->render_finished; + present_info.swapchainCount = 1; + present_info.pSwapchains = &demo->swap_chain; + present_info.pImageIndices = &image_index; + + result = vkQueuePresentKHR(demo->present_queue, &present_info); + + if (result == VK_ERROR_OUT_OF_DATE_KHR) { + if (!recreate_swap_chain(demo)) { + fprintf(stderr, "failed to recreate swap chain!\n"); + } + } else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { + fprintf(stderr, "vkQueuePresentKHR failed: %d\n", result); + return false; + } + return true; +} + +VkResult +destroy_debug_utils_messenger_ext(VkInstance instance, + VkDebugUtilsMessengerEXT debugMessenger, + const VkAllocationCallbacks *pAllocator) { + PFN_vkDestroyDebugUtilsMessengerEXT func = + (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr( + instance, "vkDestroyDebugUtilsMessengerEXT"); + if (func != NULL) { + func(instance, debugMessenger, pAllocator); + return VK_SUCCESS; + } else { + return VK_ERROR_EXTENSION_NOT_PRESENT; + } +} + +bool cleanup(struct vulkan_demo *demo) { + VkResult result; + + printf("cleaning up\n"); + result = vkDeviceWaitIdle(demo->device); + if (result != VK_SUCCESS) { + fprintf(stderr, "vkDeviceWaitIdle failed: %d\n", result); + return false; + } + + destroy_swap_chain_related_resources(demo); + + vkFreeCommandBuffers(demo->device, demo->command_pool, + demo->swap_chain_images_len, demo->command_buffers); + vkDestroyCommandPool(demo->device, demo->command_pool, NULL); + vkDestroySampler(demo->device, demo->sampler, NULL); + vkDestroySemaphore(demo->device, demo->render_finished, NULL); + vkDestroySemaphore(demo->device, demo->image_available, NULL); + vkDestroyFence(demo->device, demo->render_fence, NULL); + + vkDestroyImage(demo->device, demo->demo_texture_image, NULL); + vkDestroyImageView(demo->device, demo->demo_texture_image_view, NULL); + vkFreeMemory(demo->device, demo->demo_texture_memory, NULL); + + vkDestroyDescriptorSetLayout(demo->device, demo->descriptor_set_layout, + NULL); + vkDestroyDescriptorPool(demo->device, demo->descriptor_pool, NULL); + + vkDestroyDevice(demo->device, NULL); + vkDestroySurfaceKHR(demo->instance, demo->surface, NULL); + + if (demo->debug_messenger) { + result = destroy_debug_utils_messenger_ext(demo->instance, + demo->debug_messenger, NULL); + if (result != VK_SUCCESS) { + fprintf(stderr, "Couldn't destroy debug messenger: %d\n", result); + return false; + } + } + vkDestroyInstance(demo->instance, NULL); + if (demo->swap_chain_images) { + free(demo->swap_chain_images); + } + if (demo->swap_chain_image_views) { + free(demo->swap_chain_image_views); + } + + if (demo->overlay_images) { + free(demo->overlay_images); + } + if (demo->overlay_image_views) { + free(demo->overlay_image_views); + } + if (demo->overlay_image_memories) { + free(demo->overlay_image_memories); + } + + if (demo->descriptor_sets) { + free(demo->descriptor_sets); + } + if (demo->framebuffers) { + free(demo->framebuffers); + } + if (demo->command_buffers) { + free(demo->command_buffers); + } + + SDL_DestroyWindow(demo->win); + + return true; +} + +int main(void) { + struct vulkan_demo demo; + struct nk_context *ctx; + struct nk_colorf bg; + struct nk_image img; + uint32_t image_index; + VkResult result; + VkSemaphore nk_semaphore; + + SDL_SetHint(SDL_HINT_VIDEO_HIGHDPI_DISABLED, "0"); + + if (SDL_Init(SDL_INIT_VIDEO) < 0) { + fprintf(stderr, "[SDL] failed to init!\n"); + exit(1); + } + + memset(&demo, 0, sizeof(struct vulkan_demo)); + demo.win = SDL_CreateWindow( + "Demo", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, WINDOW_WIDTH, + WINDOW_HEIGHT, + SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); + + if (!create_vulkan_demo(&demo)) { + fprintf(stderr, "failed to create vulkan demo!\n"); + exit(1); + } + ctx = nk_sdl_init(demo.win, demo.device, demo.physical_device, + demo.indices.graphics, demo.overlay_image_views, + demo.swap_chain_images_len, demo.swap_chain_image_format, + 0, MAX_VERTEX_BUFFER, MAX_ELEMENT_BUFFER); + /* Load Fonts: if none of these are loaded a default font will be used */ + /* Load Cursor: if you uncomment cursor loading please hide the cursor */ + { + struct nk_font_atlas *atlas; + nk_sdl_font_stash_begin(&atlas); + /*struct nk_font *droid = nk_font_atlas_add_from_file(atlas, + * "../../../extra_font/DroidSans.ttf", 14, 0);*/ + /*struct nk_font *roboto = nk_font_atlas_add_from_file(atlas, + * "../../../extra_font/Roboto-Regular.ttf", 14, 0);*/ + /*struct nk_font *future = nk_font_atlas_add_from_file(atlas, + * "../../../extra_font/kenvector_future_thin.ttf", 13, 0);*/ + /*struct nk_font *clean = nk_font_atlas_add_from_file(atlas, + * "../../../extra_font/ProggyClean.ttf", 12, 0);*/ + /*struct nk_font *tiny = nk_font_atlas_add_from_file(atlas, + * "../../../extra_font/ProggyTiny.ttf", 10, 0);*/ + /*struct nk_font *cousine = nk_font_atlas_add_from_file(atlas, + * "../../../extra_font/Cousine-Regular.ttf", 13, 0);*/ + nk_sdl_font_stash_end(demo.graphics_queue); + /*nk_style_load_all_cursors(ctx, atlas->cursors);*/ + /*nk_style_set_font(ctx, &droid->handle);*/ + } + + img = nk_image_ptr(demo.demo_texture_image_view); + bg.r = 0.10f, bg.g = 0.18f, bg.b = 0.24f, bg.a = 1.0f; + while (true) { + SDL_Event evt; + + nk_input_begin(ctx); + while (SDL_PollEvent(&evt)) { + if (evt.type == SDL_QUIT) + goto cleanup; + if (evt.type == SDL_WINDOWEVENT && + evt.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) + recreate_swap_chain(&demo); + nk_sdl_handle_event(&evt); + } + nk_sdl_handle_grab(); /* optional grabbing behavior */ + nk_input_end(ctx); + + /* GUI */ + if (nk_begin(ctx, "Demo", nk_rect(50, 50, 230, 250), + NK_WINDOW_BORDER | NK_WINDOW_MOVABLE | NK_WINDOW_SCALABLE | + NK_WINDOW_MINIMIZABLE | NK_WINDOW_TITLE)) { + enum { EASY, HARD }; + static int op = EASY; + static int property = 20; + nk_layout_row_static(ctx, 30, 80, 1); + if (nk_button_label(ctx, "button")) + fprintf(stdout, "button pressed\n"); + + nk_layout_row_dynamic(ctx, 30, 2); + if (nk_option_label(ctx, "easy", op == EASY)) + op = EASY; + if (nk_option_label(ctx, "hard", op == HARD)) + op = HARD; + + nk_layout_row_dynamic(ctx, 25, 1); + nk_property_int(ctx, "Compression:", 0, &property, 100, 10, 1); + + nk_layout_row_dynamic(ctx, 20, 1); + nk_label(ctx, "background:", NK_TEXT_LEFT); + nk_layout_row_dynamic(ctx, 25, 1); + if (nk_combo_begin_color(ctx, nk_rgb_cf(bg), + nk_vec2(nk_widget_width(ctx), 400))) { + nk_layout_row_dynamic(ctx, 120, 1); + bg = nk_color_picker(ctx, bg, NK_RGBA); + nk_layout_row_dynamic(ctx, 25, 1); + bg.r = nk_propertyf(ctx, "#R:", 0, bg.r, 1.0f, 0.01f, 0.005f); + bg.g = nk_propertyf(ctx, "#G:", 0, bg.g, 1.0f, 0.01f, 0.005f); + bg.b = nk_propertyf(ctx, "#B:", 0, bg.b, 1.0f, 0.01f, 0.005f); + bg.a = nk_propertyf(ctx, "#A:", 0, bg.a, 1.0f, 0.01f, 0.005f); + nk_combo_end(ctx); + } + } + nk_end(ctx); + + /* Bindless Texture */ + if (nk_begin(ctx, "Texture", nk_rect(500, 300, 200, 200), + NK_WINDOW_BORDER | NK_WINDOW_MOVABLE | NK_WINDOW_SCALABLE | + NK_WINDOW_MINIMIZABLE | NK_WINDOW_TITLE)) { + struct nk_command_buffer *canvas = nk_window_get_canvas(ctx); + struct nk_rect total_space = nk_window_get_content_region(ctx); + nk_draw_image(canvas, total_space, &img, nk_white); + } + nk_end(ctx); + + /* -------------- EXAMPLES ---------------- */ +#ifdef INCLUDE_CALCULATOR + calculator(ctx); +#endif +#ifdef INCLUDE_CANVAS + canvas(ctx); +#endif +#ifdef INCLUDE_OVERVIEW + overview(ctx); +#endif +#ifdef INCLUDE_NODE_EDITOR + node_editor(ctx); +#endif + /* ----------------------------------------- */ + + result = vkWaitForFences(demo.device, 1, &demo.render_fence, VK_TRUE, + UINT64_MAX); + + if (result != VK_SUCCESS) { + fprintf(stderr, "vkWaitForFences failed: %d\n", result); + return false; + } + + result = vkResetFences(demo.device, 1, &demo.render_fence); + if (result != VK_SUCCESS) { + fprintf(stderr, "vkResetFences failed: %d\n", result); + return false; + } + + result = + vkAcquireNextImageKHR(demo.device, demo.swap_chain, UINT64_MAX, + demo.image_available, NULL, &image_index); + + if (result == VK_ERROR_OUT_OF_DATE_KHR) { + recreate_swap_chain(&demo); + + /* If vkAcquireNextImageKHR does not successfully acquire an image, + * semaphore and fence are unaffected. */ + continue; + } + if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { + fprintf(stderr, "vkAcquireNextImageKHR failed: %d\n", result); + return false; + } + + /* Draw */ + nk_semaphore = nk_sdl_render(demo.graphics_queue, image_index, + demo.image_available, NK_ANTI_ALIASING_ON); + if (!render(&demo, &bg, nk_semaphore, image_index)) { + fprintf(stderr, "render failed\n"); + return false; + } + } +cleanup: + nk_sdl_shutdown(); + cleanup(&demo); + SDL_Quit(); + return 0; +} diff --git a/demo/sdl_vulkan/nuklear_sdl_vulkan.h b/demo/sdl_vulkan/nuklear_sdl_vulkan.h new file mode 100644 index 0000000..af66add --- /dev/null +++ b/demo/sdl_vulkan/nuklear_sdl_vulkan.h @@ -0,0 +1,1648 @@ +/* + * Nuklear - 1.32.0 - public domain + * no warranty implied; use at your own risk. + * authored from 2015-2016 by Micha Mettke + */ +/* + * ============================================================== + * + * API + * + * =============================================================== + */ +#ifndef NK_SDL_VULKAN_H_ +#define NK_SDL_VULKAN_H_ + +unsigned char nuklearshaders_nuklear_vert_spv[] = { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x0d, 0x00, + 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, + 0x2a, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xc2, 0x01, 0x00, 0x00, + 0x04, 0x00, 0x09, 0x00, 0x47, 0x4c, 0x5f, 0x41, 0x52, 0x42, 0x5f, 0x73, + 0x65, 0x70, 0x61, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x73, 0x68, 0x61, 0x64, + 0x65, 0x72, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x00, 0x00, + 0x04, 0x00, 0x0a, 0x00, 0x47, 0x4c, 0x5f, 0x47, 0x4f, 0x4f, 0x47, 0x4c, + 0x45, 0x5f, 0x63, 0x70, 0x70, 0x5f, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x5f, + 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, + 0x76, 0x65, 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x47, 0x4c, 0x5f, 0x47, + 0x4f, 0x4f, 0x47, 0x4c, 0x45, 0x5f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, + 0x65, 0x5f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x67, 0x6c, 0x5f, 0x50, 0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x50, 0x6f, 0x73, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x00, 0x05, 0x00, 0x03, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x42, 0x75, 0x66, 0x66, 0x65, + 0x72, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x00, 0x06, 0x00, 0x06, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x75, 0x62, 0x6f, 0x00, 0x05, 0x00, 0x05, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x27, 0x00, 0x00, 0x00, + 0x66, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x63, 0x6f, 0x6c, 0x6f, + 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x42, 0x00, 0x00, 0x00, + 0x66, 0x72, 0x61, 0x67, 0x55, 0x76, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x43, 0x00, 0x00, 0x00, 0x75, 0x76, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x48, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x27, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x2a, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x42, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x43, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x04, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x03, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x20, 0x00, 0x04, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x29, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x29, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0x43, 0x2b, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x36, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x41, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x41, 0x00, 0x00, 0x00, + 0x42, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1b, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x50, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x1a, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x91, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x1d, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x1f, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x7f, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x26, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x26, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x2c, 0x00, 0x00, 0x00, + 0x2d, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, + 0x2d, 0x00, 0x00, 0x00, 0x70, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x2f, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x2c, 0x00, 0x00, 0x00, + 0x32, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, + 0x32, 0x00, 0x00, 0x00, 0x70, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x2c, 0x00, 0x00, 0x00, + 0x37, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, + 0x37, 0x00, 0x00, 0x00, 0x70, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x39, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x2c, 0x00, 0x00, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x70, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, + 0x3a, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x27, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x42, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, + 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00 +}; +unsigned int nuklearshaders_nuklear_vert_spv_len = 1856; +unsigned char nuklearshaders_nuklear_frag_spv[] = { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x0d, 0x00, + 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xc2, 0x01, 0x00, 0x00, + 0x04, 0x00, 0x09, 0x00, 0x47, 0x4c, 0x5f, 0x41, 0x52, 0x42, 0x5f, 0x73, + 0x65, 0x70, 0x61, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x73, 0x68, 0x61, 0x64, + 0x65, 0x72, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x00, 0x00, + 0x04, 0x00, 0x0a, 0x00, 0x47, 0x4c, 0x5f, 0x47, 0x4f, 0x4f, 0x47, 0x4c, + 0x45, 0x5f, 0x63, 0x70, 0x70, 0x5f, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x5f, + 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, + 0x76, 0x65, 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x47, 0x4c, 0x5f, 0x47, + 0x4f, 0x4f, 0x47, 0x4c, 0x45, 0x5f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, + 0x65, 0x5f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x74, 0x65, 0x78, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x06, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x63, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x74, 0x54, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x66, 0x72, 0x61, 0x67, + 0x55, 0x76, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x6f, 0x75, 0x74, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, 0x66, 0x72, 0x61, 0x67, + 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x03, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x57, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x1a, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x15, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, + 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00 +}; +unsigned int nuklearshaders_nuklear_frag_spv_len = 860; + +#include +#include +#include +#include + +enum nk_sdl_init_state { NK_SDL_DEFAULT = 0 }; + +NK_API struct nk_context * +nk_sdl_init(SDL_Window *win, VkDevice logical_device, + VkPhysicalDevice physical_device, + uint32_t graphics_queue_family_index, VkImageView *image_views, + uint32_t image_views_len, VkFormat color_format, + enum nk_sdl_init_state init_state, VkDeviceSize max_vertex_buffer, + VkDeviceSize max_element_buffer); +NK_API void nk_sdl_shutdown(void); +NK_API void nk_sdl_font_stash_begin(struct nk_font_atlas **atlas); +NK_API void nk_sdl_font_stash_end(VkQueue graphics_queue); +NK_API int nk_sdl_handle_event(SDL_Event *evt); +NK_API VkSemaphore nk_sdl_render(VkQueue graphics_queue, uint32_t buffer_index, + VkSemaphore wait_semaphore, + enum nk_anti_aliasing AA); +NK_API void nk_sdl_resize(uint32_t framebuffer_width, + uint32_t framebuffer_height); +NK_API void nk_sdl_device_destroy(void); +NK_API void +nk_sdl_device_create(VkDevice logical_device, VkPhysicalDevice physical_device, + uint32_t graphics_queue_family_index, + VkImageView *image_views, uint32_t image_views_len, + VkFormat color_format, VkDeviceSize max_vertex_buffer, + VkDeviceSize max_element_buffer, + uint32_t framebuffer_width, uint32_t framebuffer_height); +NK_API void nk_sdl_handle_grab(void); + +#endif +/* + * ============================================================== + * + * IMPLEMENTATION + * + * =============================================================== + */ +#ifdef NK_SDL_VULKAN_IMPLEMENTATION +#undef NK_SDL_VULKAN_IMPLEMENTATION +#include + +#ifndef NK_SDL_TEXT_MAX +#define NK_SDL_TEXT_MAX 256 +#endif +#ifndef NK_SDL_MAX_TEXTURES +#define NK_SDL_MAX_TEXTURES 256 +#endif +#ifndef NK_SDL_MAX_KEYS +#define NK_SDL_MAX_KEYS 32 +#endif + +#define VK_COLOR_COMPONENT_MASK_RGBA \ + VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | \ + VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT + +struct nk_sdl_vertex { + float position[2]; + float uv[2]; + nk_byte col[4]; +}; + +struct nk_vulkan_texture_descriptor_set { + VkImageView image_view; + VkDescriptorSet descriptor_set; +}; + +struct nk_sdl_device { + struct nk_buffer cmds; + struct nk_draw_null_texture tex_null; + int max_vertex_buffer; + int max_element_buffer; + VkDevice logical_device; + VkPhysicalDevice physical_device; + VkImageView *image_views; + uint32_t image_views_len; + VkFormat color_format; + VkFramebuffer *framebuffers; + uint32_t framebuffers_len; + VkCommandBuffer *command_buffers; + uint32_t command_buffers_len; + VkSampler sampler; + VkCommandPool command_pool; + VkSemaphore render_completed; + VkBuffer vertex_buffer; + VkDeviceMemory vertex_memory; + void *mapped_vertex; + VkBuffer index_buffer; + VkDeviceMemory index_memory; + void *mapped_index; + VkBuffer uniform_buffer; + VkDeviceMemory uniform_memory; + void *mapped_uniform; + VkRenderPass render_pass; + VkDescriptorPool descriptor_pool; + VkDescriptorSetLayout uniform_descriptor_set_layout; + VkDescriptorSet uniform_descriptor_set; + VkDescriptorSetLayout texture_descriptor_set_layout; + struct nk_vulkan_texture_descriptor_set *texture_descriptor_sets; + uint32_t texture_descriptor_sets_len; + VkPipelineLayout pipeline_layout; + VkPipeline pipeline; + VkImage font_image; + VkImageView font_image_view; + VkDeviceMemory font_memory; +}; + +static struct nk_sdl { + SDL_Window *win; + int width, height; + int display_width, display_height; + struct nk_sdl_device vulkan; + struct nk_context ctx; + struct nk_font_atlas atlas; + unsigned int text[NK_SDL_TEXT_MAX]; + int text_len; + struct nk_vec2 scroll; + uint64_t delta_time_milliseconds_last; +} sdl; + +struct Mat4f { + float m[16]; +}; + +NK_INTERN uint32_t nk_sdl_find_memory_index(VkPhysicalDevice physical_device, + uint32_t type_filter, + VkMemoryPropertyFlags properties) { + VkPhysicalDeviceMemoryProperties mem_properties; + uint32_t i; + + vkGetPhysicalDeviceMemoryProperties(physical_device, &mem_properties); + for (i = 0; i < mem_properties.memoryTypeCount; i++) { + if ((type_filter & (1 << i)) && + (mem_properties.memoryTypes[i].propertyFlags & properties) == + properties) { + return i; + } + } + + assert(0); + return 0; +} + +NK_INTERN void nk_sdl_create_sampler(struct nk_sdl_device *dev) { + VkResult result; + VkSamplerCreateInfo sampler_info; + memset(&sampler_info, 0, sizeof(VkSamplerCreateInfo)); + + sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + sampler_info.pNext = NULL; + sampler_info.maxAnisotropy = 1.0; + sampler_info.magFilter = VK_FILTER_LINEAR; + sampler_info.minFilter = VK_FILTER_LINEAR; + sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + sampler_info.mipLodBias = 0.0f; + sampler_info.compareEnable = VK_FALSE; + sampler_info.compareOp = VK_COMPARE_OP_ALWAYS; + sampler_info.minLod = 0.0f; + sampler_info.maxLod = 0.0f; + sampler_info.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK; + + result = vkCreateSampler(dev->logical_device, &sampler_info, NULL, + &dev->sampler); + NK_ASSERT(result == VK_SUCCESS); +} + +NK_INTERN void +nk_sdl_create_command_pool(struct nk_sdl_device *dev, + uint32_t graphics_queue_family_index) { + VkResult result; + VkCommandPoolCreateInfo pool_info; + memset(&pool_info, 0, sizeof(VkCommandPoolCreateInfo)); + + pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + pool_info.queueFamilyIndex = graphics_queue_family_index; + pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + result = vkCreateCommandPool(dev->logical_device, &pool_info, NULL, + &dev->command_pool); + NK_ASSERT(result == VK_SUCCESS); +} + +NK_INTERN void nk_sdl_create_command_buffers(struct nk_sdl_device *dev) { + VkResult result; + VkCommandBufferAllocateInfo allocate_info; + memset(&allocate_info, 0, sizeof(VkCommandBufferAllocateInfo)); + + dev->command_buffers = (VkCommandBuffer *)malloc(dev->image_views_len * + sizeof(VkCommandBuffer)); + dev->command_buffers_len = dev->image_views_len; + + allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocate_info.commandPool = dev->command_pool; + allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocate_info.commandBufferCount = dev->command_buffers_len; + + result = vkAllocateCommandBuffers(dev->logical_device, &allocate_info, + dev->command_buffers); + NK_ASSERT(result == VK_SUCCESS); +} + +NK_INTERN void nk_sdl_create_semaphore(struct nk_sdl_device *dev) { + VkResult result; + VkSemaphoreCreateInfo semaphore_info; + memset(&semaphore_info, 0, sizeof(VkSemaphoreCreateInfo)); + + semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + result = (vkCreateSemaphore(dev->logical_device, &semaphore_info, NULL, + &dev->render_completed)); + NK_ASSERT(result == VK_SUCCESS); +} + +NK_INTERN void nk_sdl_create_buffer_and_memory(struct nk_sdl_device *dev, + VkBuffer *buffer, + VkBufferUsageFlags usage, + VkDeviceMemory *memory, + VkDeviceSize size) { + VkMemoryRequirements mem_reqs; + VkResult result; + VkBufferCreateInfo buffer_info; + VkMemoryAllocateInfo alloc_info; + + memset(&buffer_info, 0, sizeof(VkBufferCreateInfo)); + buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buffer_info.size = size; + buffer_info.usage = usage; + buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + result = vkCreateBuffer(dev->logical_device, &buffer_info, NULL, buffer); + NK_ASSERT(result == VK_SUCCESS); + + vkGetBufferMemoryRequirements(dev->logical_device, *buffer, &mem_reqs); + + memset(&alloc_info, 0, sizeof(VkMemoryAllocateInfo)); + alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.allocationSize = mem_reqs.size; + alloc_info.memoryTypeIndex = + nk_sdl_find_memory_index(dev->physical_device, mem_reqs.memoryTypeBits, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + + result = vkAllocateMemory(dev->logical_device, &alloc_info, NULL, memory); + NK_ASSERT(result == VK_SUCCESS); + result = vkBindBufferMemory(dev->logical_device, *buffer, *memory, 0); + NK_ASSERT(result == VK_SUCCESS); +} + +NK_INTERN void nk_sdl_create_render_pass(struct nk_sdl_device *dev) { + VkAttachmentDescription attachment; + VkAttachmentReference color_reference; + VkSubpassDependency subpass_dependency; + VkSubpassDescription subpass_description; + VkRenderPassCreateInfo render_pass_info; + VkResult result; + + memset(&attachment, 0, sizeof(VkAttachmentDescription)); + attachment.format = dev->color_format; + attachment.samples = VK_SAMPLE_COUNT_1_BIT; + attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachment.finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + memset(&color_reference, 0, sizeof(VkAttachmentReference)); + color_reference.attachment = 0; + color_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + memset(&subpass_dependency, 0, sizeof(VkSubpassDependency)); + subpass_dependency.srcSubpass = VK_SUBPASS_EXTERNAL; + subpass_dependency.srcAccessMask = 0; + subpass_dependency.srcStageMask = + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + subpass_dependency.dstSubpass = 0; + subpass_dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + subpass_dependency.dstStageMask = + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + + memset(&subpass_description, 0, sizeof(VkSubpassDescription)); + subpass_description.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass_description.colorAttachmentCount = 1; + subpass_description.pColorAttachments = &color_reference; + + memset(&render_pass_info, 0, sizeof(VkRenderPassCreateInfo)); + render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + render_pass_info.attachmentCount = 1; + render_pass_info.pAttachments = &attachment; + render_pass_info.subpassCount = 1; + render_pass_info.pSubpasses = &subpass_description; + render_pass_info.dependencyCount = 1; + render_pass_info.pDependencies = &subpass_dependency; + + result = vkCreateRenderPass(dev->logical_device, &render_pass_info, NULL, + &dev->render_pass); + NK_ASSERT(result == VK_SUCCESS); +} + +NK_INTERN void nk_sdl_create_framebuffers(struct nk_sdl_device *dev, + uint32_t framebuffer_width, + uint32_t framebuffer_height) { + + VkFramebufferCreateInfo framebuffer_create_info; + uint32_t i; + VkResult result; + + dev->framebuffers = + (VkFramebuffer *)malloc(dev->image_views_len * sizeof(VkFramebuffer)); + + memset(&framebuffer_create_info, 0, sizeof(VkFramebufferCreateInfo)); + framebuffer_create_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebuffer_create_info.renderPass = dev->render_pass; + framebuffer_create_info.attachmentCount = 1; + framebuffer_create_info.width = framebuffer_width; + framebuffer_create_info.height = framebuffer_height; + framebuffer_create_info.layers = 1; + for (i = 0; i < dev->image_views_len; i++) { + framebuffer_create_info.pAttachments = &dev->image_views[i]; + result = + vkCreateFramebuffer(dev->logical_device, &framebuffer_create_info, + NULL, &dev->framebuffers[i]); + NK_ASSERT(result == VK_SUCCESS); + } + dev->framebuffers_len = dev->image_views_len; +} + +NK_INTERN void nk_sdl_create_descriptor_pool(struct nk_sdl_device *dev) { + VkDescriptorPoolSize pool_sizes[2]; + VkDescriptorPoolCreateInfo pool_info; + VkResult result; + + memset(&pool_sizes, 0, sizeof(VkDescriptorPoolSize) * 2); + pool_sizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + pool_sizes[0].descriptorCount = 1; + pool_sizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + pool_sizes[1].descriptorCount = NK_SDL_MAX_TEXTURES; + + memset(&pool_info, 0, sizeof(VkDescriptorPoolCreateInfo)); + pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + pool_info.poolSizeCount = 2; + pool_info.pPoolSizes = pool_sizes; + pool_info.maxSets = 1 + NK_SDL_MAX_TEXTURES; + + result = vkCreateDescriptorPool(dev->logical_device, &pool_info, NULL, + &dev->descriptor_pool); + NK_ASSERT(result == VK_SUCCESS); +} + +NK_INTERN void +nk_sdl_create_uniform_descriptor_set_layout(struct nk_sdl_device *dev) { + VkDescriptorSetLayoutBinding binding; + VkDescriptorSetLayoutCreateInfo descriptor_set_info; + VkResult result; + + memset(&binding, 0, sizeof(VkDescriptorSetLayoutBinding)); + binding.binding = 0; + binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + binding.descriptorCount = 1; + binding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + + memset(&descriptor_set_info, 0, sizeof(VkDescriptorSetLayoutCreateInfo)); + descriptor_set_info.sType = + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + descriptor_set_info.bindingCount = 1; + descriptor_set_info.pBindings = &binding; + + result = + vkCreateDescriptorSetLayout(dev->logical_device, &descriptor_set_info, + NULL, &dev->uniform_descriptor_set_layout); + + NK_ASSERT(result == VK_SUCCESS); +} + +NK_INTERN void +nk_sdl_create_and_update_uniform_descriptor_set(struct nk_sdl_device *dev) { + VkDescriptorSetAllocateInfo allocate_info; + VkDescriptorBufferInfo buffer_info; + VkWriteDescriptorSet descriptor_write; + VkResult result; + + memset(&allocate_info, 0, sizeof(VkDescriptorSetAllocateInfo)); + allocate_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocate_info.descriptorPool = dev->descriptor_pool; + allocate_info.descriptorSetCount = 1; + allocate_info.pSetLayouts = &dev->uniform_descriptor_set_layout; + + result = vkAllocateDescriptorSets(dev->logical_device, &allocate_info, + &dev->uniform_descriptor_set); + NK_ASSERT(result == VK_SUCCESS); + + memset(&buffer_info, 0, sizeof(VkDescriptorBufferInfo)); + buffer_info.buffer = dev->uniform_buffer; + buffer_info.offset = 0; + buffer_info.range = sizeof(struct Mat4f); + + memset(&descriptor_write, 0, sizeof(VkWriteDescriptorSet)); + descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptor_write.dstSet = dev->uniform_descriptor_set; + descriptor_write.dstBinding = 0; + descriptor_write.dstArrayElement = 0; + descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptor_write.descriptorCount = 1; + descriptor_write.pBufferInfo = &buffer_info; + + vkUpdateDescriptorSets(dev->logical_device, 1, &descriptor_write, 0, NULL); +} + +NK_INTERN void +nk_sdl_create_texture_descriptor_set_layout(struct nk_sdl_device *dev) { + VkDescriptorSetLayoutBinding binding; + VkDescriptorSetLayoutCreateInfo descriptor_set_info; + VkResult result; + + memset(&binding, 0, sizeof(VkDescriptorSetLayoutBinding)); + binding.binding = 0; + binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + binding.descriptorCount = 1; + binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + + memset(&descriptor_set_info, 0, sizeof(VkDescriptorSetLayoutCreateInfo)); + descriptor_set_info.sType = + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + descriptor_set_info.bindingCount = 1; + descriptor_set_info.pBindings = &binding; + + result = + vkCreateDescriptorSetLayout(dev->logical_device, &descriptor_set_info, + NULL, &dev->texture_descriptor_set_layout); + + NK_ASSERT(result == VK_SUCCESS); +} + +NK_INTERN void +nk_sdl_create_texture_descriptor_sets(struct nk_sdl_device *dev) { + VkDescriptorSetLayout *descriptor_set_layouts; + VkDescriptorSet *descriptor_sets; + VkDescriptorSetAllocateInfo allocate_info; + VkResult result; + int i; + + descriptor_set_layouts = (VkDescriptorSetLayout *)malloc( + NK_SDL_MAX_TEXTURES * sizeof(VkDescriptorSetLayout)); + descriptor_sets = (VkDescriptorSet *)malloc(NK_SDL_MAX_TEXTURES * + sizeof(VkDescriptorSet)); + + dev->texture_descriptor_sets = + (struct nk_vulkan_texture_descriptor_set *)malloc( + NK_SDL_MAX_TEXTURES * + sizeof(struct nk_vulkan_texture_descriptor_set)); + dev->texture_descriptor_sets_len = 0; + + for (i = 0; i < NK_SDL_MAX_TEXTURES; i++) { + descriptor_set_layouts[i] = dev->texture_descriptor_set_layout; + descriptor_sets[i] = dev->texture_descriptor_sets[i].descriptor_set; + } + + memset(&allocate_info, 0, sizeof(VkDescriptorSetAllocateInfo)); + allocate_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocate_info.descriptorPool = dev->descriptor_pool; + allocate_info.descriptorSetCount = NK_SDL_MAX_TEXTURES; + allocate_info.pSetLayouts = descriptor_set_layouts; + + result = vkAllocateDescriptorSets(dev->logical_device, &allocate_info, + descriptor_sets); + NK_ASSERT(result == VK_SUCCESS); + + for (i = 0; i < NK_SDL_MAX_TEXTURES; i++) { + dev->texture_descriptor_sets[i].descriptor_set = descriptor_sets[i]; + } + free(descriptor_set_layouts); + free(descriptor_sets); +} + +NK_INTERN void nk_sdl_create_pipeline_layout(struct nk_sdl_device *dev) { + VkPipelineLayoutCreateInfo pipeline_layout_info; + VkDescriptorSetLayout descriptor_set_layouts[2]; + VkResult result; + + descriptor_set_layouts[0] = dev->uniform_descriptor_set_layout; + descriptor_set_layouts[1] = dev->texture_descriptor_set_layout; + + memset(&pipeline_layout_info, 0, sizeof(VkPipelineLayoutCreateInfo)); + pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipeline_layout_info.setLayoutCount = 2; + pipeline_layout_info.pSetLayouts = descriptor_set_layouts; + + result = (vkCreatePipelineLayout(dev->logical_device, &pipeline_layout_info, + NULL, &dev->pipeline_layout)); + NK_ASSERT(result == VK_SUCCESS); +} + +NK_INTERN VkPipelineShaderStageCreateInfo +nk_sdl_create_shader(struct nk_sdl_device *dev, unsigned char *spv_shader, + uint32_t size, VkShaderStageFlagBits stage_bit) { + VkShaderModuleCreateInfo create_info; + VkPipelineShaderStageCreateInfo shader_info; + VkShaderModule module = NULL; + VkResult result; + + memset(&create_info, 0, sizeof(VkShaderModuleCreateInfo)); + create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + create_info.codeSize = size; + create_info.pCode = (const uint32_t *)spv_shader; + result = + vkCreateShaderModule(dev->logical_device, &create_info, NULL, &module); + NK_ASSERT(result == VK_SUCCESS); + + memset(&shader_info, 0, sizeof(VkPipelineShaderStageCreateInfo)); + shader_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shader_info.stage = stage_bit; + shader_info.module = module; + shader_info.pName = "main"; + return shader_info; +} + +NK_INTERN void nk_sdl_create_pipeline(struct nk_sdl_device *dev) { + VkPipelineInputAssemblyStateCreateInfo input_assembly_state; + VkPipelineRasterizationStateCreateInfo rasterization_state; + VkPipelineColorBlendAttachmentState attachment_state = { + VK_TRUE, + VK_BLEND_FACTOR_SRC_ALPHA, + VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, + VK_BLEND_OP_ADD, + VK_BLEND_FACTOR_SRC_ALPHA, + VK_BLEND_FACTOR_ONE, + VK_BLEND_OP_ADD, + VK_COLOR_COMPONENT_MASK_RGBA, + }; + VkPipelineColorBlendStateCreateInfo color_blend_state; + VkPipelineViewportStateCreateInfo viewport_state; + VkPipelineMultisampleStateCreateInfo multisample_state; + VkDynamicState dynamic_states[2] = {VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR}; + VkPipelineDynamicStateCreateInfo dynamic_state; + VkPipelineShaderStageCreateInfo shader_stages[2]; + VkVertexInputBindingDescription vertex_input_info; + VkVertexInputAttributeDescription vertex_attribute_description[3]; + VkPipelineVertexInputStateCreateInfo vertex_input; + VkGraphicsPipelineCreateInfo pipeline_info; + VkResult result; + + memset(&input_assembly_state, 0, + sizeof(VkPipelineInputAssemblyStateCreateInfo)); + input_assembly_state.sType = + VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + input_assembly_state.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + input_assembly_state.primitiveRestartEnable = VK_FALSE; + + memset(&rasterization_state, 0, + sizeof(VkPipelineRasterizationStateCreateInfo)); + rasterization_state.sType = + VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterization_state.polygonMode = VK_POLYGON_MODE_FILL; + rasterization_state.cullMode = VK_CULL_MODE_NONE; + rasterization_state.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + rasterization_state.lineWidth = 1.0f; + + memset(&color_blend_state, 0, sizeof(VkPipelineColorBlendStateCreateInfo)); + color_blend_state.sType = + VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + color_blend_state.attachmentCount = 1; + color_blend_state.pAttachments = &attachment_state; + + memset(&viewport_state, 0, sizeof(VkPipelineViewportStateCreateInfo)); + viewport_state.sType = + VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewport_state.viewportCount = 1; + viewport_state.scissorCount = 1; + + memset(&multisample_state, 0, sizeof(VkPipelineMultisampleStateCreateInfo)); + multisample_state.sType = + VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisample_state.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + + memset(&dynamic_state, 0, sizeof(VkPipelineDynamicStateCreateInfo)); + dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamic_state.pDynamicStates = dynamic_states; + dynamic_state.dynamicStateCount = 2; + + shader_stages[0] = nk_sdl_create_shader( + dev, nuklearshaders_nuklear_vert_spv, + nuklearshaders_nuklear_vert_spv_len, VK_SHADER_STAGE_VERTEX_BIT); + shader_stages[1] = nk_sdl_create_shader( + dev, nuklearshaders_nuklear_frag_spv, + nuklearshaders_nuklear_frag_spv_len, VK_SHADER_STAGE_FRAGMENT_BIT); + + memset(&vertex_input_info, 0, sizeof(VkVertexInputBindingDescription)); + vertex_input_info.binding = 0; + vertex_input_info.stride = sizeof(struct nk_sdl_vertex); + vertex_input_info.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + memset(&vertex_attribute_description, 0, + sizeof(VkVertexInputAttributeDescription) * 3); + vertex_attribute_description[0].location = 0; + vertex_attribute_description[0].format = VK_FORMAT_R32G32_SFLOAT; + vertex_attribute_description[0].offset = + NK_OFFSETOF(struct nk_sdl_vertex, position); + vertex_attribute_description[1].location = 1; + vertex_attribute_description[1].format = VK_FORMAT_R32G32_SFLOAT; + vertex_attribute_description[1].offset = + NK_OFFSETOF(struct nk_sdl_vertex, uv); + vertex_attribute_description[2].location = 2; + vertex_attribute_description[2].format = VK_FORMAT_R8G8B8A8_UINT; + vertex_attribute_description[2].offset = + NK_OFFSETOF(struct nk_sdl_vertex, col); + + memset(&vertex_input, 0, sizeof(VkPipelineVertexInputStateCreateInfo)); + vertex_input.sType = + VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertex_input.vertexBindingDescriptionCount = 1; + vertex_input.pVertexBindingDescriptions = &vertex_input_info; + vertex_input.vertexAttributeDescriptionCount = 3; + vertex_input.pVertexAttributeDescriptions = vertex_attribute_description; + + memset(&pipeline_info, 0, sizeof(VkGraphicsPipelineCreateInfo)); + pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipeline_info.flags = 0; + pipeline_info.stageCount = 2; + pipeline_info.pStages = shader_stages; + pipeline_info.pVertexInputState = &vertex_input; + pipeline_info.pInputAssemblyState = &input_assembly_state; + pipeline_info.pViewportState = &viewport_state; + pipeline_info.pRasterizationState = &rasterization_state; + pipeline_info.pMultisampleState = &multisample_state; + pipeline_info.pColorBlendState = &color_blend_state; + pipeline_info.pDynamicState = &dynamic_state; + pipeline_info.layout = dev->pipeline_layout; + pipeline_info.renderPass = dev->render_pass; + pipeline_info.basePipelineIndex = -1; + pipeline_info.basePipelineHandle = NULL; + + result = vkCreateGraphicsPipelines(dev->logical_device, NULL, 1, + &pipeline_info, NULL, &dev->pipeline); + NK_ASSERT(result == VK_SUCCESS); + + vkDestroyShaderModule(dev->logical_device, shader_stages[0].module, NULL); + vkDestroyShaderModule(dev->logical_device, shader_stages[1].module, NULL); +} + +NK_INTERN void nk_sdl_create_render_resources(struct nk_sdl_device *dev, + uint32_t framebuffer_width, + uint32_t framebuffer_height) { + nk_sdl_create_render_pass(dev); + nk_sdl_create_framebuffers(dev, framebuffer_width, framebuffer_height); + nk_sdl_create_descriptor_pool(dev); + nk_sdl_create_uniform_descriptor_set_layout(dev); + nk_sdl_create_and_update_uniform_descriptor_set(dev); + nk_sdl_create_texture_descriptor_set_layout(dev); + nk_sdl_create_texture_descriptor_sets(dev); + nk_sdl_create_pipeline_layout(dev); + nk_sdl_create_pipeline(dev); +} + +NK_API void +nk_sdl_device_create(VkDevice logical_device, VkPhysicalDevice physical_device, + uint32_t graphics_queue_family_index, + VkImageView *image_views, uint32_t image_views_len, + VkFormat color_format, VkDeviceSize max_vertex_buffer, + VkDeviceSize max_element_buffer, + uint32_t framebuffer_width, uint32_t framebuffer_height) { + struct nk_sdl_device *dev = &sdl.vulkan; + dev->max_vertex_buffer = max_vertex_buffer; + dev->max_element_buffer = max_element_buffer; + nk_buffer_init_default(&dev->cmds); + dev->logical_device = logical_device; + dev->physical_device = physical_device; + dev->image_views = image_views; + dev->image_views_len = image_views_len; + dev->color_format = color_format; + dev->framebuffers = NULL; + dev->framebuffers_len = 0; + + nk_sdl_create_sampler(dev); + nk_sdl_create_command_pool(dev, graphics_queue_family_index); + nk_sdl_create_command_buffers(dev); + nk_sdl_create_semaphore(dev); + + nk_sdl_create_buffer_and_memory(dev, &dev->vertex_buffer, + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + &dev->vertex_memory, max_vertex_buffer); + nk_sdl_create_buffer_and_memory(dev, &dev->index_buffer, + VK_BUFFER_USAGE_INDEX_BUFFER_BIT, + &dev->index_memory, max_element_buffer); + nk_sdl_create_buffer_and_memory(dev, &dev->uniform_buffer, + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + &dev->uniform_memory, sizeof(struct Mat4f)); + + vkMapMemory(dev->logical_device, dev->vertex_memory, 0, max_vertex_buffer, + 0, &dev->mapped_vertex); + vkMapMemory(dev->logical_device, dev->index_memory, 0, max_element_buffer, + 0, &dev->mapped_index); + vkMapMemory(dev->logical_device, dev->uniform_memory, 0, + sizeof(struct Mat4f), 0, &dev->mapped_uniform); + + nk_sdl_create_render_resources(dev, framebuffer_width, framebuffer_height); +} + +NK_INTERN void nk_sdl_device_upload_atlas(VkQueue graphics_queue, + const void *image, int width, + int height) { + struct nk_sdl_device *dev = &sdl.vulkan; + + VkImageCreateInfo image_info; + VkResult result; + VkMemoryRequirements mem_reqs; + VkMemoryAllocateInfo alloc_info; + VkBufferCreateInfo buffer_info; + uint8_t *data = 0; + VkCommandBufferBeginInfo begin_info; + VkCommandBuffer command_buffer; + VkImageMemoryBarrier image_memory_barrier; + VkBufferImageCopy buffer_copy_region; + VkImageMemoryBarrier image_shader_memory_barrier; + VkFence fence; + VkFenceCreateInfo fence_create; + VkSubmitInfo submit_info; + VkImageViewCreateInfo image_view_info; + struct { + VkDeviceMemory memory; + VkBuffer buffer; + } staging_buffer; + + memset(&image_info, 0, sizeof(VkImageCreateInfo)); + image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + image_info.imageType = VK_IMAGE_TYPE_2D; + image_info.format = VK_FORMAT_R8G8B8A8_UNORM; + image_info.extent.width = (uint32_t)width; + image_info.extent.height = (uint32_t)height; + image_info.extent.depth = 1; + image_info.mipLevels = 1; + image_info.arrayLayers = 1; + image_info.samples = VK_SAMPLE_COUNT_1_BIT; + image_info.tiling = VK_IMAGE_TILING_OPTIMAL; + image_info.usage = + VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + + result = + vkCreateImage(dev->logical_device, &image_info, NULL, &dev->font_image); + NK_ASSERT(result == VK_SUCCESS); + + vkGetImageMemoryRequirements(dev->logical_device, dev->font_image, + &mem_reqs); + + memset(&alloc_info, 0, sizeof(VkMemoryAllocateInfo)); + alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.allocationSize = mem_reqs.size; + alloc_info.memoryTypeIndex = + nk_sdl_find_memory_index(dev->physical_device, mem_reqs.memoryTypeBits, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + + result = vkAllocateMemory(dev->logical_device, &alloc_info, NULL, + &dev->font_memory); + NK_ASSERT(result == VK_SUCCESS); + result = vkBindImageMemory(dev->logical_device, dev->font_image, + dev->font_memory, 0); + NK_ASSERT(result == VK_SUCCESS); + + memset(&buffer_info, 0, sizeof(VkBufferCreateInfo)); + buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buffer_info.size = alloc_info.allocationSize; + buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + result = vkCreateBuffer(dev->logical_device, &buffer_info, NULL, + &staging_buffer.buffer); + NK_ASSERT(result == VK_SUCCESS); + vkGetBufferMemoryRequirements(dev->logical_device, staging_buffer.buffer, + &mem_reqs); + + alloc_info.allocationSize = mem_reqs.size; + alloc_info.memoryTypeIndex = + nk_sdl_find_memory_index(dev->physical_device, mem_reqs.memoryTypeBits, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + + result = vkAllocateMemory(dev->logical_device, &alloc_info, NULL, + &staging_buffer.memory); + NK_ASSERT(result == VK_SUCCESS); + result = vkBindBufferMemory(dev->logical_device, staging_buffer.buffer, + staging_buffer.memory, 0); + NK_ASSERT(result == VK_SUCCESS); + + result = vkMapMemory(dev->logical_device, staging_buffer.memory, 0, + alloc_info.allocationSize, 0, (void **)&data); + NK_ASSERT(result == VK_SUCCESS); + memcpy(data, image, width * height * 4); + vkUnmapMemory(dev->logical_device, staging_buffer.memory); + + memset(&begin_info, 0, sizeof(VkCommandBufferBeginInfo)); + begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + + NK_ASSERT(dev->command_buffers_len > 0); + /* + use the same command buffer as for render as we are regenerating the + buffer during render anyway + */ + command_buffer = dev->command_buffers[0]; + result = vkBeginCommandBuffer(command_buffer, &begin_info); + NK_ASSERT(result == VK_SUCCESS); + + memset(&image_memory_barrier, 0, sizeof(VkImageMemoryBarrier)); + image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + image_memory_barrier.image = dev->font_image; + image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + image_memory_barrier.subresourceRange.aspectMask = + VK_IMAGE_ASPECT_COLOR_BIT; + image_memory_barrier.subresourceRange.levelCount = 1; + image_memory_barrier.subresourceRange.layerCount = 1; + image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1, + &image_memory_barrier); + + memset(&buffer_copy_region, 0, sizeof(VkBufferImageCopy)); + buffer_copy_region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + buffer_copy_region.imageSubresource.layerCount = 1; + buffer_copy_region.imageExtent.width = (uint32_t)width; + buffer_copy_region.imageExtent.height = (uint32_t)height; + buffer_copy_region.imageExtent.depth = 1; + + vkCmdCopyBufferToImage( + command_buffer, staging_buffer.buffer, dev->font_image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &buffer_copy_region); + + memset(&image_shader_memory_barrier, 0, sizeof(VkImageMemoryBarrier)); + image_shader_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + image_shader_memory_barrier.image = dev->font_image; + image_shader_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_shader_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_shader_memory_barrier.oldLayout = + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + image_shader_memory_barrier.newLayout = + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + image_shader_memory_barrier.subresourceRange.aspectMask = + VK_IMAGE_ASPECT_COLOR_BIT; + image_shader_memory_barrier.subresourceRange.levelCount = 1; + image_shader_memory_barrier.subresourceRange.layerCount = 1; + image_shader_memory_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + image_shader_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT, + + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, NULL, 0, + NULL, 1, &image_shader_memory_barrier); + + result = vkEndCommandBuffer(command_buffer); + NK_ASSERT(result == VK_SUCCESS); + + memset(&fence_create, 0, sizeof(VkFenceCreateInfo)); + fence_create.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + + result = vkCreateFence(dev->logical_device, &fence_create, NULL, &fence); + NK_ASSERT(result == VK_SUCCESS); + + memset(&submit_info, 0, sizeof(VkSubmitInfo)); + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &command_buffer; + + result = vkQueueSubmit(graphics_queue, 1, &submit_info, fence); + NK_ASSERT(result == VK_SUCCESS); + result = + vkWaitForFences(dev->logical_device, 1, &fence, VK_TRUE, UINT64_MAX); + NK_ASSERT(result == VK_SUCCESS); + + vkDestroyFence(dev->logical_device, fence, NULL); + + vkFreeMemory(dev->logical_device, staging_buffer.memory, NULL); + vkDestroyBuffer(dev->logical_device, staging_buffer.buffer, NULL); + + memset(&image_view_info, 0, sizeof(VkImageViewCreateInfo)); + image_view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + image_view_info.image = dev->font_image; + image_view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + image_view_info.format = image_info.format; + image_view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + image_view_info.subresourceRange.layerCount = 1; + image_view_info.subresourceRange.levelCount = 1; + + result = vkCreateImageView(dev->logical_device, &image_view_info, NULL, + &dev->font_image_view); + NK_ASSERT(result == VK_SUCCESS); +} + +NK_INTERN void nk_sdl_destroy_render_resources(struct nk_sdl_device *dev) { + uint32_t i; + + vkDestroyPipeline(dev->logical_device, dev->pipeline, NULL); + vkDestroyPipelineLayout(dev->logical_device, dev->pipeline_layout, NULL); + vkDestroyDescriptorSetLayout(dev->logical_device, + dev->texture_descriptor_set_layout, NULL); + vkDestroyDescriptorSetLayout(dev->logical_device, + dev->uniform_descriptor_set_layout, NULL); + vkDestroyDescriptorPool(dev->logical_device, dev->descriptor_pool, NULL); + for (i = 0; i < dev->framebuffers_len; i++) { + vkDestroyFramebuffer(dev->logical_device, dev->framebuffers[i], NULL); + } + free(dev->framebuffers); + dev->framebuffers_len = 0; + free(dev->texture_descriptor_sets); + dev->texture_descriptor_sets_len = 0; + vkDestroyRenderPass(dev->logical_device, dev->render_pass, NULL); +} + +NK_API void nk_sdl_resize(uint32_t framebuffer_width, + uint32_t framebuffer_height) { + struct nk_sdl_device *dev = &sdl.vulkan; + + SDL_GetWindowSize(sdl.win, &sdl.width, &sdl.height); + sdl.display_width = framebuffer_width; + sdl.display_height = framebuffer_height; + + nk_sdl_destroy_render_resources(dev); + nk_sdl_create_render_resources(dev, sdl.display_width, sdl.display_height); +} + +NK_API void nk_sdl_device_destroy(void) { + struct nk_sdl_device *dev = &sdl.vulkan; + + vkDeviceWaitIdle(dev->logical_device); + + nk_sdl_destroy_render_resources(dev); + + vkFreeCommandBuffers(dev->logical_device, dev->command_pool, + dev->command_buffers_len, dev->command_buffers); + vkDestroyCommandPool(dev->logical_device, dev->command_pool, NULL); + vkDestroySemaphore(dev->logical_device, dev->render_completed, NULL); + + vkUnmapMemory(dev->logical_device, dev->vertex_memory); + vkUnmapMemory(dev->logical_device, dev->index_memory); + vkUnmapMemory(dev->logical_device, dev->uniform_memory); + + vkFreeMemory(dev->logical_device, dev->vertex_memory, NULL); + vkFreeMemory(dev->logical_device, dev->index_memory, NULL); + vkFreeMemory(dev->logical_device, dev->uniform_memory, NULL); + + vkDestroyBuffer(dev->logical_device, dev->vertex_buffer, NULL); + vkDestroyBuffer(dev->logical_device, dev->index_buffer, NULL); + vkDestroyBuffer(dev->logical_device, dev->uniform_buffer, NULL); + + vkDestroySampler(dev->logical_device, dev->sampler, NULL); + + vkFreeMemory(dev->logical_device, dev->font_memory, NULL); + vkDestroyImage(dev->logical_device, dev->font_image, NULL); + vkDestroyImageView(dev->logical_device, dev->font_image_view, NULL); + + free(dev->command_buffers); + nk_buffer_free(&dev->cmds); +} + +NK_API +void nk_sdl_shutdown(void) { + nk_font_atlas_clear(&sdl.atlas); + nk_free(&sdl.ctx); + nk_sdl_device_destroy(); + memset(&sdl, 0, sizeof(sdl)); +} + +NK_API void nk_sdl_font_stash_begin(struct nk_font_atlas **atlas) { + nk_font_atlas_init_default(&sdl.atlas); + nk_font_atlas_begin(&sdl.atlas); + *atlas = &sdl.atlas; +} + +NK_API void nk_sdl_font_stash_end(VkQueue graphics_queue) { + struct nk_sdl_device *dev = &sdl.vulkan; + + const void *image; + int w, h; + image = nk_font_atlas_bake(&sdl.atlas, &w, &h, NK_FONT_ATLAS_RGBA32); + nk_sdl_device_upload_atlas(graphics_queue, image, w, h); + nk_font_atlas_end(&sdl.atlas, nk_handle_ptr(dev->font_image_view), + &dev->tex_null); + if (sdl.atlas.default_font) { + nk_style_set_font(&sdl.ctx, &sdl.atlas.default_font->handle); + } +} + +NK_API void nk_sdl_handle_grab(void) { + struct nk_context *ctx = &sdl.ctx; + if (ctx->input.mouse.grab) { + SDL_SetRelativeMouseMode(SDL_TRUE); + } else if (ctx->input.mouse.ungrab) { + /* better support for older SDL by setting mode first; causes an extra + * mouse motion event */ + SDL_SetRelativeMouseMode(SDL_FALSE); + SDL_WarpMouseInWindow(sdl.win, (int)ctx->input.mouse.prev.x, + (int)ctx->input.mouse.prev.y); + } else if (ctx->input.mouse.grabbed) { + ctx->input.mouse.pos.x = ctx->input.mouse.prev.x; + ctx->input.mouse.pos.y = ctx->input.mouse.prev.y; + } +} + +NK_API int nk_sdl_handle_event(SDL_Event *evt) { + struct nk_context *ctx = &sdl.ctx; + + switch (evt->type) { + case SDL_KEYUP: /* KEYUP & KEYDOWN share same routine */ + case SDL_KEYDOWN: { + int down = evt->type == SDL_KEYDOWN; + const Uint8 *state = SDL_GetKeyboardState(0); + switch (evt->key.keysym.sym) { + case SDLK_RSHIFT: /* RSHIFT & LSHIFT share same routine */ + case SDLK_LSHIFT: + nk_input_key(ctx, NK_KEY_SHIFT, down); + break; + case SDLK_DELETE: + nk_input_key(ctx, NK_KEY_DEL, down); + break; + case SDLK_RETURN: + nk_input_key(ctx, NK_KEY_ENTER, down); + break; + case SDLK_TAB: + nk_input_key(ctx, NK_KEY_TAB, down); + break; + case SDLK_BACKSPACE: + nk_input_key(ctx, NK_KEY_BACKSPACE, down); + break; + case SDLK_HOME: + nk_input_key(ctx, NK_KEY_TEXT_START, down); + nk_input_key(ctx, NK_KEY_SCROLL_START, down); + break; + case SDLK_END: + nk_input_key(ctx, NK_KEY_TEXT_END, down); + nk_input_key(ctx, NK_KEY_SCROLL_END, down); + break; + case SDLK_PAGEDOWN: + nk_input_key(ctx, NK_KEY_SCROLL_DOWN, down); + break; + case SDLK_PAGEUP: + nk_input_key(ctx, NK_KEY_SCROLL_UP, down); + break; + case SDLK_z: + nk_input_key(ctx, NK_KEY_TEXT_UNDO, + down && state[SDL_SCANCODE_LCTRL]); + break; + case SDLK_r: + nk_input_key(ctx, NK_KEY_TEXT_REDO, + down && state[SDL_SCANCODE_LCTRL]); + break; + case SDLK_c: + nk_input_key(ctx, NK_KEY_COPY, down && state[SDL_SCANCODE_LCTRL]); + break; + case SDLK_v: + nk_input_key(ctx, NK_KEY_PASTE, down && state[SDL_SCANCODE_LCTRL]); + break; + case SDLK_x: + nk_input_key(ctx, NK_KEY_CUT, down && state[SDL_SCANCODE_LCTRL]); + break; + case SDLK_b: + nk_input_key(ctx, NK_KEY_TEXT_LINE_START, + down && state[SDL_SCANCODE_LCTRL]); + break; + case SDLK_e: + nk_input_key(ctx, NK_KEY_TEXT_LINE_END, + down && state[SDL_SCANCODE_LCTRL]); + break; + case SDLK_UP: + nk_input_key(ctx, NK_KEY_UP, down); + break; + case SDLK_DOWN: + nk_input_key(ctx, NK_KEY_DOWN, down); + break; + case SDLK_LEFT: + if (state[SDL_SCANCODE_LCTRL]) + nk_input_key(ctx, NK_KEY_TEXT_WORD_LEFT, down); + else + nk_input_key(ctx, NK_KEY_LEFT, down); + break; + case SDLK_RIGHT: + if (state[SDL_SCANCODE_LCTRL]) + nk_input_key(ctx, NK_KEY_TEXT_WORD_RIGHT, down); + else + nk_input_key(ctx, NK_KEY_RIGHT, down); + break; + } + return 1; + } + + case SDL_MOUSEBUTTONUP: /* MOUSEBUTTONUP & MOUSEBUTTONDOWN share same + routine */ + case SDL_MOUSEBUTTONDOWN: { + int down = evt->type == SDL_MOUSEBUTTONDOWN; + const int x = evt->button.x, y = evt->button.y; + switch (evt->button.button) { + case SDL_BUTTON_LEFT: + if (evt->button.clicks > 1) + nk_input_button(ctx, NK_BUTTON_DOUBLE, x, y, down); + nk_input_button(ctx, NK_BUTTON_LEFT, x, y, down); + break; + case SDL_BUTTON_MIDDLE: + nk_input_button(ctx, NK_BUTTON_MIDDLE, x, y, down); + break; + case SDL_BUTTON_RIGHT: + nk_input_button(ctx, NK_BUTTON_RIGHT, x, y, down); + break; + } + return 1; + } + + case SDL_MOUSEMOTION: { + if (ctx->input.mouse.grabbed) { + int x = (int)ctx->input.mouse.prev.x, + y = (int)ctx->input.mouse.prev.y; + nk_input_motion(ctx, x + evt->motion.xrel, y + evt->motion.yrel); + } else + nk_input_motion(ctx, evt->motion.x, evt->motion.y); + return 1; + } + + case SDL_TEXTINPUT: { + nk_glyph glyph; + memcpy(glyph, evt->text.text, NK_UTF_SIZE); + nk_input_glyph(ctx, glyph); + return 1; + } + + case SDL_MOUSEWHEEL: + nk_input_scroll(ctx, nk_vec2((float)evt->wheel.x, (float)evt->wheel.y)); + return 1; + } + return 0; +} + +NK_INTERN void update_texture_descriptor_set( + struct nk_sdl_device *dev, + struct nk_vulkan_texture_descriptor_set *texture_descriptor_set, + VkImageView image_view) { + VkDescriptorImageInfo descriptor_image_info; + VkWriteDescriptorSet descriptor_write; + + texture_descriptor_set->image_view = image_view; + + memset(&descriptor_image_info, 0, sizeof(VkDescriptorImageInfo)); + descriptor_image_info.sampler = dev->sampler; + descriptor_image_info.imageView = texture_descriptor_set->image_view; + descriptor_image_info.imageLayout = + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + memset(&descriptor_write, 0, sizeof(VkWriteDescriptorSet)); + descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptor_write.dstSet = texture_descriptor_set->descriptor_set; + descriptor_write.dstBinding = 0; + descriptor_write.dstArrayElement = 0; + descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + descriptor_write.descriptorCount = 1; + descriptor_write.pImageInfo = &descriptor_image_info; + + vkUpdateDescriptorSets(dev->logical_device, 1, &descriptor_write, 0, NULL); +} + +NK_API +VkSemaphore nk_sdl_render(VkQueue graphics_queue, uint32_t buffer_index, + VkSemaphore wait_semaphore, + enum nk_anti_aliasing AA) { + struct nk_sdl_device *dev = &sdl.vulkan; + struct nk_buffer vbuf, ebuf; + + struct Mat4f projection = { + {2.0f, 0.0f, 0.0f, 0.0f, 0.0f, -2.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, + 0.0f, -1.0f, 1.0f, 0.0f, 1.0f}, + }; + + VkCommandBufferBeginInfo begin_info; + VkClearValue clear_value = {{{0.0f, 0.0f, 0.0f, 0.0f}}}; + VkRenderPassBeginInfo render_pass_begin_nfo; + VkCommandBuffer command_buffer; + VkResult result; + VkViewport viewport; + + VkDeviceSize doffset = 0; + VkImageView current_texture = NULL; + uint32_t index_offset = 0; + VkRect2D scissor; + uint32_t wait_semaphore_count; + VkSemaphore *wait_semaphores; + VkPipelineStageFlags wait_stage = + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + VkSubmitInfo submit_info; + uint64_t time_now; + + time_now = SDL_GetTicks64(); + sdl.ctx.delta_time_seconds = + (float)(time_now - sdl.delta_time_milliseconds_last) / 1000.0f; + sdl.delta_time_milliseconds_last = time_now; + + projection.m[0] /= sdl.display_width; + projection.m[5] /= sdl.display_height; + + memcpy(dev->mapped_uniform, &projection, sizeof(projection)); + + memset(&begin_info, 0, sizeof(VkCommandBufferBeginInfo)); + begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + + memset(&render_pass_begin_nfo, 0, sizeof(VkRenderPassBeginInfo)); + render_pass_begin_nfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + render_pass_begin_nfo.renderPass = dev->render_pass; + render_pass_begin_nfo.renderArea.extent.width = (uint32_t)sdl.display_width; + render_pass_begin_nfo.renderArea.extent.height = + (uint32_t)sdl.display_height; + render_pass_begin_nfo.clearValueCount = 1; + render_pass_begin_nfo.pClearValues = &clear_value; + render_pass_begin_nfo.framebuffer = dev->framebuffers[buffer_index]; + + command_buffer = dev->command_buffers[buffer_index]; + + result = vkBeginCommandBuffer(command_buffer, &begin_info); + NK_ASSERT(result == VK_SUCCESS); + vkCmdBeginRenderPass(command_buffer, &render_pass_begin_nfo, + VK_SUBPASS_CONTENTS_INLINE); + + memset(&viewport, 0, sizeof(VkViewport)); + viewport.width = (float)sdl.width; + viewport.height = (float)sdl.height; + viewport.maxDepth = 1.0f; + vkCmdSetViewport(command_buffer, 0, 1, &viewport); + + vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + dev->pipeline); + vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + dev->pipeline_layout, 0, 1, + &dev->uniform_descriptor_set, 0, NULL); + { + /* convert from command queue into draw list and draw to screen */ + const struct nk_draw_command *cmd; + /* load draw vertices & elements directly into vertex + element buffer + */ + { + /* fill convert configuration */ + struct nk_convert_config config; + static const struct nk_draw_vertex_layout_element vertex_layout[] = + {{NK_VERTEX_POSITION, NK_FORMAT_FLOAT, + NK_OFFSETOF(struct nk_sdl_vertex, position)}, + {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, + NK_OFFSETOF(struct nk_sdl_vertex, uv)}, + {NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, + NK_OFFSETOF(struct nk_sdl_vertex, col)}, + {NK_VERTEX_LAYOUT_END}}; + NK_MEMSET(&config, 0, sizeof(config)); + config.vertex_layout = vertex_layout; + config.vertex_size = sizeof(struct nk_sdl_vertex); + config.vertex_alignment = NK_ALIGNOF(struct nk_sdl_vertex); + config.tex_null = dev->tex_null; + config.circle_segment_count = 22; + config.curve_segment_count = 22; + config.arc_segment_count = 22; + config.global_alpha = 1.0f; + config.shape_AA = AA; + config.line_AA = AA; + + /* setup buffers to load vertices and elements */ + nk_buffer_init_fixed(&vbuf, dev->mapped_vertex, + (size_t)dev->max_vertex_buffer); + nk_buffer_init_fixed(&ebuf, dev->mapped_index, + (size_t)dev->max_element_buffer); + nk_convert(&sdl.ctx, &dev->cmds, &vbuf, &ebuf, &config); + } + + /* iterate over and execute each draw command */ + + vkCmdBindVertexBuffers(command_buffer, 0, 1, &dev->vertex_buffer, + &doffset); + vkCmdBindIndexBuffer(command_buffer, dev->index_buffer, 0, + VK_INDEX_TYPE_UINT16); + + nk_draw_foreach(cmd, &sdl.ctx, &dev->cmds) { + if (!cmd->texture.ptr) { + continue; + } + if (cmd->texture.ptr && cmd->texture.ptr != current_texture) { + int found = 0; + uint32_t i; + for (i = 0; i < dev->texture_descriptor_sets_len; i++) { + if (dev->texture_descriptor_sets[i].image_view == + cmd->texture.ptr) { + found = 1; + break; + } + } + + if (!found) { + update_texture_descriptor_set( + dev, &dev->texture_descriptor_sets[i], + (VkImageView)cmd->texture.ptr); + dev->texture_descriptor_sets_len++; + } + vkCmdBindDescriptorSets( + command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + dev->pipeline_layout, 1, 1, + &dev->texture_descriptor_sets[i].descriptor_set, 0, NULL); + } + + if (!cmd->elem_count) + continue; + + scissor.offset.x = (int32_t)(NK_MAX(cmd->clip_rect.x, 0.f)); + scissor.offset.y = (int32_t)(NK_MAX(cmd->clip_rect.y, 0.f)); + scissor.extent.width = (uint32_t)(cmd->clip_rect.w); + scissor.extent.height = (uint32_t)(cmd->clip_rect.h); + vkCmdSetScissor(command_buffer, 0, 1, &scissor); + vkCmdDrawIndexed(command_buffer, cmd->elem_count, 1, index_offset, + 0, 0); + index_offset += cmd->elem_count; + } + nk_clear(&sdl.ctx); + } + + vkCmdEndRenderPass(command_buffer); + result = vkEndCommandBuffer(command_buffer); + NK_ASSERT(result == VK_SUCCESS); + + if (wait_semaphore) { + wait_semaphore_count = 1; + wait_semaphores = &wait_semaphore; + } else { + wait_semaphore_count = 0; + wait_semaphores = NULL; + } + + memset(&submit_info, 0, sizeof(VkSubmitInfo)); + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &command_buffer; + submit_info.pWaitDstStageMask = &wait_stage; + submit_info.waitSemaphoreCount = wait_semaphore_count; + submit_info.pWaitSemaphores = wait_semaphores; + submit_info.signalSemaphoreCount = 1; + submit_info.pSignalSemaphores = &dev->render_completed; + + result = vkQueueSubmit(graphics_queue, 1, &submit_info, NULL); + NK_ASSERT(result == VK_SUCCESS); + + return dev->render_completed; +} + +NK_INTERN void nk_sdl_clipboard_paste(nk_handle usr, + struct nk_text_edit *edit) { + const char *text = SDL_GetClipboardText(); + if (text) + nk_textedit_paste(edit, text, nk_strlen(text)); + SDL_free((void *)text); + (void)usr; +} + +NK_INTERN void nk_sdl_clipboard_copy(nk_handle usr, const char *text, int len) { + char *str = 0; + (void)usr; + if (!len) + return; + str = (char *)malloc((size_t)len + 1); + if (!str) + return; + memcpy(str, text, (size_t)len); + str[len] = '\0'; + SDL_SetClipboardText(str); + free(str); +} + +NK_API struct nk_context * +nk_sdl_init(SDL_Window *win, VkDevice logical_device, + VkPhysicalDevice physical_device, + uint32_t graphics_queue_family_index, VkImageView *image_views, + uint32_t image_views_len, VkFormat color_format, + enum nk_sdl_init_state init_state, VkDeviceSize max_vertex_buffer, + VkDeviceSize max_element_buffer) { + (void)init_state; + + memset(&sdl, 0, sizeof(struct nk_sdl)); + sdl.win = win; + + nk_init_default(&sdl.ctx, 0); + sdl.ctx.clip.copy = nk_sdl_clipboard_copy; + sdl.ctx.clip.paste = nk_sdl_clipboard_paste; + sdl.ctx.clip.userdata = nk_handle_ptr(0); + + SDL_GetWindowSize(win, &sdl.width, &sdl.height); + SDL_Vulkan_GetDrawableSize(win, &sdl.display_width, &sdl.display_height); + nk_sdl_device_create(logical_device, physical_device, + graphics_queue_family_index, image_views, + image_views_len, color_format, max_vertex_buffer, + max_element_buffer, (uint32_t)sdl.display_width, + (uint32_t)sdl.display_height); + + return &sdl.ctx; +} + +#endif diff --git a/demo/sdl_vulkan/shaders/demo.frag b/demo/sdl_vulkan/shaders/demo.frag new file mode 100644 index 0000000..2034597 --- /dev/null +++ b/demo/sdl_vulkan/shaders/demo.frag @@ -0,0 +1,12 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(binding = 0) uniform sampler2D overlay; + +layout(location = 0) in vec2 inUV; + +layout(location = 0) out vec4 outColor; + +void main() { + outColor = texture(overlay, inUV); +} diff --git a/demo/sdl_vulkan/shaders/demo.vert b/demo/sdl_vulkan/shaders/demo.vert new file mode 100644 index 0000000..c39196a --- /dev/null +++ b/demo/sdl_vulkan/shaders/demo.vert @@ -0,0 +1,10 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout (location = 0) out vec2 outUV; + +void main() +{ + outUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2); + gl_Position = vec4(outUV * 2.0f + -1.0f, 0.0f, 1.0f); +} diff --git a/demo/sdl_vulkan/src/Makefile b/demo/sdl_vulkan/src/Makefile new file mode 100644 index 0000000..85c8e44 --- /dev/null +++ b/demo/sdl_vulkan/src/Makefile @@ -0,0 +1,11 @@ +create_shader_inlined_header: nuklearshaders/nuklear.vert.spv nuklearshaders/nuklear.frag.spv + awk -v st='// NUKLEAR_SHADERS_START' -v et='// NUKLEAR_SHADERS_END' -v repl="$$(xxd -i nuklearshaders/nuklear.vert.spv && xxd -i nuklearshaders/nuklear.frag.spv)" '$$0 == st{del=1} $$0 == et{$$0 = repl; del=0} !del' nuklear_sdl_vulkan.in.h > nuklear_sdl_vulkan.h + +nuklearshaders/nuklear.vert.spv: nuklearshaders/nuklear.vert + glslc --target-env=vulkan nuklearshaders/nuklear.vert -o nuklearshaders/nuklear.vert.spv + +nuklearshaders/nuklear.frag.spv: nuklearshaders/nuklear.frag + glslc --target-env=vulkan nuklearshaders/nuklear.frag -o nuklearshaders/nuklear.frag.spv + +clean: + rm nuklearshaders/nuklear.vert.spv nuklearshaders/nuklear.frag.spv nuklear_sdl_vulkan.h diff --git a/demo/sdl_vulkan/src/README.md b/demo/sdl_vulkan/src/README.md new file mode 100644 index 0000000..f73c81d --- /dev/null +++ b/demo/sdl_vulkan/src/README.md @@ -0,0 +1,5 @@ +Contrary to OpenGL Vulkan needs precompiled shaders in the SPIR-V format which makes it a bit more difficult to inline the shadercode. + +After executing `make` the result should be a self contained `nuklear_sdl_vulkan.h`. Copy the result file to the parent directory and the "release" should be done. + +You will need to have `xxd`, `glslc` and `awk` installed for this. diff --git a/demo/sdl_vulkan/src/nuklear_sdl_vulkan.in.h b/demo/sdl_vulkan/src/nuklear_sdl_vulkan.in.h new file mode 100644 index 0000000..329b49d --- /dev/null +++ b/demo/sdl_vulkan/src/nuklear_sdl_vulkan.in.h @@ -0,0 +1,1425 @@ +/* + * Nuklear - 1.32.0 - public domain + * no warranty implied; use at your own risk. + * authored from 2015-2016 by Micha Mettke + */ +/* + * ============================================================== + * + * API + * + * =============================================================== + */ +#ifndef NK_SDL_VULKAN_H_ +#define NK_SDL_VULKAN_H_ + +// NUKLEAR_SHADERS_START +// will be replaced with the real shader code +// so we can have some ide support while editing the .in file +#include "nuklear.h" + +unsigned char nuklearshaders_nuklear_vert_spv[] = {}; +unsigned int nuklearshaders_nuklear_vert_spv_len = 0; +unsigned char nuklearshaders_nuklear_frag_spv[] = {}; +unsigned int nuklearshaders_nuklear_frag_spv_len = 0; +// NUKLEAR_SHADERS_END + +#include +#include +#include +#include + +enum nk_sdl_init_state { NK_SDL_DEFAULT = 0 }; + +NK_API struct nk_context * +nk_sdl_init(SDL_Window *win, VkDevice logical_device, + VkPhysicalDevice physical_device, + uint32_t graphics_queue_family_index, VkImageView *image_views, + uint32_t image_views_len, VkFormat color_format, + enum nk_sdl_init_state init_state, VkDeviceSize max_vertex_buffer, + VkDeviceSize max_element_buffer); +NK_API void nk_sdl_shutdown(void); +NK_API void nk_sdl_font_stash_begin(struct nk_font_atlas **atlas); +NK_API void nk_sdl_font_stash_end(VkQueue graphics_queue); +NK_API int nk_sdl_handle_event(SDL_Event *evt); +NK_API VkSemaphore nk_sdl_render(VkQueue graphics_queue, uint32_t buffer_index, + VkSemaphore wait_semaphore, + enum nk_anti_aliasing AA); +NK_API void nk_sdl_resize(uint32_t framebuffer_width, + uint32_t framebuffer_height); +NK_API void nk_sdl_device_destroy(void); +NK_API void +nk_sdl_device_create(VkDevice logical_device, VkPhysicalDevice physical_device, + uint32_t graphics_queue_family_index, + VkImageView *image_views, uint32_t image_views_len, + VkFormat color_format, VkDeviceSize max_vertex_buffer, + VkDeviceSize max_element_buffer, + uint32_t framebuffer_width, uint32_t framebuffer_height); +NK_API void nk_sdl_handle_grab(void); + +#endif +/* + * ============================================================== + * + * IMPLEMENTATION + * + * =============================================================== + */ +#ifdef NK_SDL_VULKAN_IMPLEMENTATION +#undef NK_SDL_VULKAN_IMPLEMENTATION +#include + +#ifndef NK_SDL_TEXT_MAX +#define NK_SDL_TEXT_MAX 256 +#endif +#ifndef NK_SDL_MAX_TEXTURES +#define NK_SDL_MAX_TEXTURES 256 +#endif +#ifndef NK_SDL_MAX_KEYS +#define NK_SDL_MAX_KEYS 32 +#endif + +#define VK_COLOR_COMPONENT_MASK_RGBA \ + VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | \ + VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT + +struct nk_sdl_vertex { + float position[2]; + float uv[2]; + nk_byte col[4]; +}; + +struct nk_vulkan_texture_descriptor_set { + VkImageView image_view; + VkDescriptorSet descriptor_set; +}; + +struct nk_sdl_device { + struct nk_buffer cmds; + struct nk_draw_null_texture tex_null; + int max_vertex_buffer; + int max_element_buffer; + VkDevice logical_device; + VkPhysicalDevice physical_device; + VkImageView *image_views; + uint32_t image_views_len; + VkFormat color_format; + VkFramebuffer *framebuffers; + uint32_t framebuffers_len; + VkCommandBuffer *command_buffers; + uint32_t command_buffers_len; + VkSampler sampler; + VkCommandPool command_pool; + VkSemaphore render_completed; + VkBuffer vertex_buffer; + VkDeviceMemory vertex_memory; + void *mapped_vertex; + VkBuffer index_buffer; + VkDeviceMemory index_memory; + void *mapped_index; + VkBuffer uniform_buffer; + VkDeviceMemory uniform_memory; + void *mapped_uniform; + VkRenderPass render_pass; + VkDescriptorPool descriptor_pool; + VkDescriptorSetLayout uniform_descriptor_set_layout; + VkDescriptorSet uniform_descriptor_set; + VkDescriptorSetLayout texture_descriptor_set_layout; + struct nk_vulkan_texture_descriptor_set *texture_descriptor_sets; + uint32_t texture_descriptor_sets_len; + VkPipelineLayout pipeline_layout; + VkPipeline pipeline; + VkImage font_image; + VkImageView font_image_view; + VkDeviceMemory font_memory; +}; + +static struct nk_sdl { + SDL_Window *win; + int width, height; + int display_width, display_height; + struct nk_sdl_device vulkan; + struct nk_context ctx; + struct nk_font_atlas atlas; + unsigned int text[NK_SDL_TEXT_MAX]; + int text_len; + struct nk_vec2 scroll; + uint64_t delta_time_milliseconds_last; +} sdl; + +struct Mat4f { + float m[16]; +}; + +NK_INTERN uint32_t nk_sdl_find_memory_index(VkPhysicalDevice physical_device, + uint32_t type_filter, + VkMemoryPropertyFlags properties) { + VkPhysicalDeviceMemoryProperties mem_properties; + uint32_t i; + + vkGetPhysicalDeviceMemoryProperties(physical_device, &mem_properties); + for (i = 0; i < mem_properties.memoryTypeCount; i++) { + if ((type_filter & (1 << i)) && + (mem_properties.memoryTypes[i].propertyFlags & properties) == + properties) { + return i; + } + } + + assert(0); + return 0; +} + +NK_INTERN void nk_sdl_create_sampler(struct nk_sdl_device *dev) { + VkResult result; + VkSamplerCreateInfo sampler_info; + memset(&sampler_info, 0, sizeof(VkSamplerCreateInfo)); + + sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + sampler_info.pNext = NULL; + sampler_info.maxAnisotropy = 1.0; + sampler_info.magFilter = VK_FILTER_LINEAR; + sampler_info.minFilter = VK_FILTER_LINEAR; + sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + sampler_info.mipLodBias = 0.0f; + sampler_info.compareEnable = VK_FALSE; + sampler_info.compareOp = VK_COMPARE_OP_ALWAYS; + sampler_info.minLod = 0.0f; + sampler_info.maxLod = 0.0f; + sampler_info.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK; + + result = vkCreateSampler(dev->logical_device, &sampler_info, NULL, + &dev->sampler); + NK_ASSERT(result == VK_SUCCESS); +} + +NK_INTERN void +nk_sdl_create_command_pool(struct nk_sdl_device *dev, + uint32_t graphics_queue_family_index) { + VkResult result; + VkCommandPoolCreateInfo pool_info; + memset(&pool_info, 0, sizeof(VkCommandPoolCreateInfo)); + + pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + pool_info.queueFamilyIndex = graphics_queue_family_index; + pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + result = vkCreateCommandPool(dev->logical_device, &pool_info, NULL, + &dev->command_pool); + NK_ASSERT(result == VK_SUCCESS); +} + +NK_INTERN void nk_sdl_create_command_buffers(struct nk_sdl_device *dev) { + VkResult result; + VkCommandBufferAllocateInfo allocate_info; + memset(&allocate_info, 0, sizeof(VkCommandBufferAllocateInfo)); + + dev->command_buffers = (VkCommandBuffer *)malloc(dev->image_views_len * + sizeof(VkCommandBuffer)); + dev->command_buffers_len = dev->image_views_len; + + allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocate_info.commandPool = dev->command_pool; + allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocate_info.commandBufferCount = dev->command_buffers_len; + + result = vkAllocateCommandBuffers(dev->logical_device, &allocate_info, + dev->command_buffers); + NK_ASSERT(result == VK_SUCCESS); +} + +NK_INTERN void nk_sdl_create_semaphore(struct nk_sdl_device *dev) { + VkResult result; + VkSemaphoreCreateInfo semaphore_info; + memset(&semaphore_info, 0, sizeof(VkSemaphoreCreateInfo)); + + semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + result = (vkCreateSemaphore(dev->logical_device, &semaphore_info, NULL, + &dev->render_completed)); + NK_ASSERT(result == VK_SUCCESS); +} + +NK_INTERN void nk_sdl_create_buffer_and_memory(struct nk_sdl_device *dev, + VkBuffer *buffer, + VkBufferUsageFlags usage, + VkDeviceMemory *memory, + VkDeviceSize size) { + VkMemoryRequirements mem_reqs; + VkResult result; + VkBufferCreateInfo buffer_info; + VkMemoryAllocateInfo alloc_info; + + memset(&buffer_info, 0, sizeof(VkBufferCreateInfo)); + buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buffer_info.size = size; + buffer_info.usage = usage; + buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + result = vkCreateBuffer(dev->logical_device, &buffer_info, NULL, buffer); + NK_ASSERT(result == VK_SUCCESS); + + vkGetBufferMemoryRequirements(dev->logical_device, *buffer, &mem_reqs); + + memset(&alloc_info, 0, sizeof(VkMemoryAllocateInfo)); + alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.allocationSize = mem_reqs.size; + alloc_info.memoryTypeIndex = + nk_sdl_find_memory_index(dev->physical_device, mem_reqs.memoryTypeBits, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + + result = vkAllocateMemory(dev->logical_device, &alloc_info, NULL, memory); + NK_ASSERT(result == VK_SUCCESS); + result = vkBindBufferMemory(dev->logical_device, *buffer, *memory, 0); + NK_ASSERT(result == VK_SUCCESS); +} + +NK_INTERN void nk_sdl_create_render_pass(struct nk_sdl_device *dev) { + VkAttachmentDescription attachment; + VkAttachmentReference color_reference; + VkSubpassDependency subpass_dependency; + VkSubpassDescription subpass_description; + VkRenderPassCreateInfo render_pass_info; + VkResult result; + + memset(&attachment, 0, sizeof(VkAttachmentDescription)); + attachment.format = dev->color_format; + attachment.samples = VK_SAMPLE_COUNT_1_BIT; + attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachment.finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + memset(&color_reference, 0, sizeof(VkAttachmentReference)); + color_reference.attachment = 0; + color_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + memset(&subpass_dependency, 0, sizeof(VkSubpassDependency)); + subpass_dependency.srcSubpass = VK_SUBPASS_EXTERNAL; + subpass_dependency.srcAccessMask = 0; + subpass_dependency.srcStageMask = + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + subpass_dependency.dstSubpass = 0; + subpass_dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + subpass_dependency.dstStageMask = + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + + memset(&subpass_description, 0, sizeof(VkSubpassDescription)); + subpass_description.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass_description.colorAttachmentCount = 1; + subpass_description.pColorAttachments = &color_reference; + + memset(&render_pass_info, 0, sizeof(VkRenderPassCreateInfo)); + render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + render_pass_info.attachmentCount = 1; + render_pass_info.pAttachments = &attachment; + render_pass_info.subpassCount = 1; + render_pass_info.pSubpasses = &subpass_description; + render_pass_info.dependencyCount = 1; + render_pass_info.pDependencies = &subpass_dependency; + + result = vkCreateRenderPass(dev->logical_device, &render_pass_info, NULL, + &dev->render_pass); + NK_ASSERT(result == VK_SUCCESS); +} + +NK_INTERN void nk_sdl_create_framebuffers(struct nk_sdl_device *dev, + uint32_t framebuffer_width, + uint32_t framebuffer_height) { + + VkFramebufferCreateInfo framebuffer_create_info; + uint32_t i; + VkResult result; + + dev->framebuffers = + (VkFramebuffer *)malloc(dev->image_views_len * sizeof(VkFramebuffer)); + + memset(&framebuffer_create_info, 0, sizeof(VkFramebufferCreateInfo)); + framebuffer_create_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebuffer_create_info.renderPass = dev->render_pass; + framebuffer_create_info.attachmentCount = 1; + framebuffer_create_info.width = framebuffer_width; + framebuffer_create_info.height = framebuffer_height; + framebuffer_create_info.layers = 1; + for (i = 0; i < dev->image_views_len; i++) { + framebuffer_create_info.pAttachments = &dev->image_views[i]; + result = + vkCreateFramebuffer(dev->logical_device, &framebuffer_create_info, + NULL, &dev->framebuffers[i]); + NK_ASSERT(result == VK_SUCCESS); + } + dev->framebuffers_len = dev->image_views_len; +} + +NK_INTERN void nk_sdl_create_descriptor_pool(struct nk_sdl_device *dev) { + VkDescriptorPoolSize pool_sizes[2]; + VkDescriptorPoolCreateInfo pool_info; + VkResult result; + + memset(&pool_sizes, 0, sizeof(VkDescriptorPoolSize) * 2); + pool_sizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + pool_sizes[0].descriptorCount = 1; + pool_sizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + pool_sizes[1].descriptorCount = NK_SDL_MAX_TEXTURES; + + memset(&pool_info, 0, sizeof(VkDescriptorPoolCreateInfo)); + pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + pool_info.poolSizeCount = 2; + pool_info.pPoolSizes = pool_sizes; + pool_info.maxSets = 1 + NK_SDL_MAX_TEXTURES; + + result = vkCreateDescriptorPool(dev->logical_device, &pool_info, NULL, + &dev->descriptor_pool); + NK_ASSERT(result == VK_SUCCESS); +} + +NK_INTERN void +nk_sdl_create_uniform_descriptor_set_layout(struct nk_sdl_device *dev) { + VkDescriptorSetLayoutBinding binding; + VkDescriptorSetLayoutCreateInfo descriptor_set_info; + VkResult result; + + memset(&binding, 0, sizeof(VkDescriptorSetLayoutBinding)); + binding.binding = 0; + binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + binding.descriptorCount = 1; + binding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + + memset(&descriptor_set_info, 0, sizeof(VkDescriptorSetLayoutCreateInfo)); + descriptor_set_info.sType = + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + descriptor_set_info.bindingCount = 1; + descriptor_set_info.pBindings = &binding; + + result = + vkCreateDescriptorSetLayout(dev->logical_device, &descriptor_set_info, + NULL, &dev->uniform_descriptor_set_layout); + + NK_ASSERT(result == VK_SUCCESS); +} + +NK_INTERN void +nk_sdl_create_and_update_uniform_descriptor_set(struct nk_sdl_device *dev) { + VkDescriptorSetAllocateInfo allocate_info; + VkDescriptorBufferInfo buffer_info; + VkWriteDescriptorSet descriptor_write; + VkResult result; + + memset(&allocate_info, 0, sizeof(VkDescriptorSetAllocateInfo)); + allocate_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocate_info.descriptorPool = dev->descriptor_pool; + allocate_info.descriptorSetCount = 1; + allocate_info.pSetLayouts = &dev->uniform_descriptor_set_layout; + + result = vkAllocateDescriptorSets(dev->logical_device, &allocate_info, + &dev->uniform_descriptor_set); + NK_ASSERT(result == VK_SUCCESS); + + memset(&buffer_info, 0, sizeof(VkDescriptorBufferInfo)); + buffer_info.buffer = dev->uniform_buffer; + buffer_info.offset = 0; + buffer_info.range = sizeof(struct Mat4f); + + memset(&descriptor_write, 0, sizeof(VkWriteDescriptorSet)); + descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptor_write.dstSet = dev->uniform_descriptor_set; + descriptor_write.dstBinding = 0; + descriptor_write.dstArrayElement = 0; + descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptor_write.descriptorCount = 1; + descriptor_write.pBufferInfo = &buffer_info; + + vkUpdateDescriptorSets(dev->logical_device, 1, &descriptor_write, 0, NULL); +} + +NK_INTERN void +nk_sdl_create_texture_descriptor_set_layout(struct nk_sdl_device *dev) { + VkDescriptorSetLayoutBinding binding; + VkDescriptorSetLayoutCreateInfo descriptor_set_info; + VkResult result; + + memset(&binding, 0, sizeof(VkDescriptorSetLayoutBinding)); + binding.binding = 0; + binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + binding.descriptorCount = 1; + binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + + memset(&descriptor_set_info, 0, sizeof(VkDescriptorSetLayoutCreateInfo)); + descriptor_set_info.sType = + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + descriptor_set_info.bindingCount = 1; + descriptor_set_info.pBindings = &binding; + + result = + vkCreateDescriptorSetLayout(dev->logical_device, &descriptor_set_info, + NULL, &dev->texture_descriptor_set_layout); + + NK_ASSERT(result == VK_SUCCESS); +} + +NK_INTERN void +nk_sdl_create_texture_descriptor_sets(struct nk_sdl_device *dev) { + VkDescriptorSetLayout *descriptor_set_layouts; + VkDescriptorSet *descriptor_sets; + VkDescriptorSetAllocateInfo allocate_info; + VkResult result; + int i; + + descriptor_set_layouts = (VkDescriptorSetLayout *)malloc( + NK_SDL_MAX_TEXTURES * sizeof(VkDescriptorSetLayout)); + descriptor_sets = (VkDescriptorSet *)malloc(NK_SDL_MAX_TEXTURES * + sizeof(VkDescriptorSet)); + + dev->texture_descriptor_sets = + (struct nk_vulkan_texture_descriptor_set *)malloc( + NK_SDL_MAX_TEXTURES * + sizeof(struct nk_vulkan_texture_descriptor_set)); + dev->texture_descriptor_sets_len = 0; + + for (i = 0; i < NK_SDL_MAX_TEXTURES; i++) { + descriptor_set_layouts[i] = dev->texture_descriptor_set_layout; + descriptor_sets[i] = dev->texture_descriptor_sets[i].descriptor_set; + } + + memset(&allocate_info, 0, sizeof(VkDescriptorSetAllocateInfo)); + allocate_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocate_info.descriptorPool = dev->descriptor_pool; + allocate_info.descriptorSetCount = NK_SDL_MAX_TEXTURES; + allocate_info.pSetLayouts = descriptor_set_layouts; + + result = vkAllocateDescriptorSets(dev->logical_device, &allocate_info, + descriptor_sets); + NK_ASSERT(result == VK_SUCCESS); + + for (i = 0; i < NK_SDL_MAX_TEXTURES; i++) { + dev->texture_descriptor_sets[i].descriptor_set = descriptor_sets[i]; + } + free(descriptor_set_layouts); + free(descriptor_sets); +} + +NK_INTERN void nk_sdl_create_pipeline_layout(struct nk_sdl_device *dev) { + VkPipelineLayoutCreateInfo pipeline_layout_info; + VkDescriptorSetLayout descriptor_set_layouts[2]; + VkResult result; + + descriptor_set_layouts[0] = dev->uniform_descriptor_set_layout; + descriptor_set_layouts[1] = dev->texture_descriptor_set_layout; + + memset(&pipeline_layout_info, 0, sizeof(VkPipelineLayoutCreateInfo)); + pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipeline_layout_info.setLayoutCount = 2; + pipeline_layout_info.pSetLayouts = descriptor_set_layouts; + + result = (vkCreatePipelineLayout(dev->logical_device, &pipeline_layout_info, + NULL, &dev->pipeline_layout)); + NK_ASSERT(result == VK_SUCCESS); +} + +NK_INTERN VkPipelineShaderStageCreateInfo +nk_sdl_create_shader(struct nk_sdl_device *dev, unsigned char *spv_shader, + uint32_t size, VkShaderStageFlagBits stage_bit) { + VkShaderModuleCreateInfo create_info; + VkPipelineShaderStageCreateInfo shader_info; + VkShaderModule module = NULL; + VkResult result; + + memset(&create_info, 0, sizeof(VkShaderModuleCreateInfo)); + create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + create_info.codeSize = size; + create_info.pCode = (const uint32_t *)spv_shader; + result = + vkCreateShaderModule(dev->logical_device, &create_info, NULL, &module); + NK_ASSERT(result == VK_SUCCESS); + + memset(&shader_info, 0, sizeof(VkPipelineShaderStageCreateInfo)); + shader_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shader_info.stage = stage_bit; + shader_info.module = module; + shader_info.pName = "main"; + return shader_info; +} + +NK_INTERN void nk_sdl_create_pipeline(struct nk_sdl_device *dev) { + VkPipelineInputAssemblyStateCreateInfo input_assembly_state; + VkPipelineRasterizationStateCreateInfo rasterization_state; + VkPipelineColorBlendAttachmentState attachment_state = { + VK_TRUE, + VK_BLEND_FACTOR_SRC_ALPHA, + VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, + VK_BLEND_OP_ADD, + VK_BLEND_FACTOR_SRC_ALPHA, + VK_BLEND_FACTOR_ONE, + VK_BLEND_OP_ADD, + VK_COLOR_COMPONENT_MASK_RGBA, + }; + VkPipelineColorBlendStateCreateInfo color_blend_state; + VkPipelineViewportStateCreateInfo viewport_state; + VkPipelineMultisampleStateCreateInfo multisample_state; + VkDynamicState dynamic_states[2] = {VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR}; + VkPipelineDynamicStateCreateInfo dynamic_state; + VkPipelineShaderStageCreateInfo shader_stages[2]; + VkVertexInputBindingDescription vertex_input_info; + VkVertexInputAttributeDescription vertex_attribute_description[3]; + VkPipelineVertexInputStateCreateInfo vertex_input; + VkGraphicsPipelineCreateInfo pipeline_info; + VkResult result; + + memset(&input_assembly_state, 0, + sizeof(VkPipelineInputAssemblyStateCreateInfo)); + input_assembly_state.sType = + VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + input_assembly_state.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + input_assembly_state.primitiveRestartEnable = VK_FALSE; + + memset(&rasterization_state, 0, + sizeof(VkPipelineRasterizationStateCreateInfo)); + rasterization_state.sType = + VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterization_state.polygonMode = VK_POLYGON_MODE_FILL; + rasterization_state.cullMode = VK_CULL_MODE_NONE; + rasterization_state.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + rasterization_state.lineWidth = 1.0f; + + memset(&color_blend_state, 0, sizeof(VkPipelineColorBlendStateCreateInfo)); + color_blend_state.sType = + VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + color_blend_state.attachmentCount = 1; + color_blend_state.pAttachments = &attachment_state; + + memset(&viewport_state, 0, sizeof(VkPipelineViewportStateCreateInfo)); + viewport_state.sType = + VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewport_state.viewportCount = 1; + viewport_state.scissorCount = 1; + + memset(&multisample_state, 0, sizeof(VkPipelineMultisampleStateCreateInfo)); + multisample_state.sType = + VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisample_state.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + + memset(&dynamic_state, 0, sizeof(VkPipelineDynamicStateCreateInfo)); + dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamic_state.pDynamicStates = dynamic_states; + dynamic_state.dynamicStateCount = 2; + + shader_stages[0] = nk_sdl_create_shader( + dev, nuklearshaders_nuklear_vert_spv, + nuklearshaders_nuklear_vert_spv_len, VK_SHADER_STAGE_VERTEX_BIT); + shader_stages[1] = nk_sdl_create_shader( + dev, nuklearshaders_nuklear_frag_spv, + nuklearshaders_nuklear_frag_spv_len, VK_SHADER_STAGE_FRAGMENT_BIT); + + memset(&vertex_input_info, 0, sizeof(VkVertexInputBindingDescription)); + vertex_input_info.binding = 0; + vertex_input_info.stride = sizeof(struct nk_sdl_vertex); + vertex_input_info.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + memset(&vertex_attribute_description, 0, + sizeof(VkVertexInputAttributeDescription) * 3); + vertex_attribute_description[0].location = 0; + vertex_attribute_description[0].format = VK_FORMAT_R32G32_SFLOAT; + vertex_attribute_description[0].offset = + NK_OFFSETOF(struct nk_sdl_vertex, position); + vertex_attribute_description[1].location = 1; + vertex_attribute_description[1].format = VK_FORMAT_R32G32_SFLOAT; + vertex_attribute_description[1].offset = + NK_OFFSETOF(struct nk_sdl_vertex, uv); + vertex_attribute_description[2].location = 2; + vertex_attribute_description[2].format = VK_FORMAT_R8G8B8A8_UINT; + vertex_attribute_description[2].offset = + NK_OFFSETOF(struct nk_sdl_vertex, col); + + memset(&vertex_input, 0, sizeof(VkPipelineVertexInputStateCreateInfo)); + vertex_input.sType = + VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertex_input.vertexBindingDescriptionCount = 1; + vertex_input.pVertexBindingDescriptions = &vertex_input_info; + vertex_input.vertexAttributeDescriptionCount = 3; + vertex_input.pVertexAttributeDescriptions = vertex_attribute_description; + + memset(&pipeline_info, 0, sizeof(VkGraphicsPipelineCreateInfo)); + pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipeline_info.flags = 0; + pipeline_info.stageCount = 2; + pipeline_info.pStages = shader_stages; + pipeline_info.pVertexInputState = &vertex_input; + pipeline_info.pInputAssemblyState = &input_assembly_state; + pipeline_info.pViewportState = &viewport_state; + pipeline_info.pRasterizationState = &rasterization_state; + pipeline_info.pMultisampleState = &multisample_state; + pipeline_info.pColorBlendState = &color_blend_state; + pipeline_info.pDynamicState = &dynamic_state; + pipeline_info.layout = dev->pipeline_layout; + pipeline_info.renderPass = dev->render_pass; + pipeline_info.basePipelineIndex = -1; + pipeline_info.basePipelineHandle = NULL; + + result = vkCreateGraphicsPipelines(dev->logical_device, NULL, 1, + &pipeline_info, NULL, &dev->pipeline); + NK_ASSERT(result == VK_SUCCESS); + + vkDestroyShaderModule(dev->logical_device, shader_stages[0].module, NULL); + vkDestroyShaderModule(dev->logical_device, shader_stages[1].module, NULL); +} + +NK_INTERN void nk_sdl_create_render_resources(struct nk_sdl_device *dev, + uint32_t framebuffer_width, + uint32_t framebuffer_height) { + nk_sdl_create_render_pass(dev); + nk_sdl_create_framebuffers(dev, framebuffer_width, framebuffer_height); + nk_sdl_create_descriptor_pool(dev); + nk_sdl_create_uniform_descriptor_set_layout(dev); + nk_sdl_create_and_update_uniform_descriptor_set(dev); + nk_sdl_create_texture_descriptor_set_layout(dev); + nk_sdl_create_texture_descriptor_sets(dev); + nk_sdl_create_pipeline_layout(dev); + nk_sdl_create_pipeline(dev); +} + +NK_API void +nk_sdl_device_create(VkDevice logical_device, VkPhysicalDevice physical_device, + uint32_t graphics_queue_family_index, + VkImageView *image_views, uint32_t image_views_len, + VkFormat color_format, VkDeviceSize max_vertex_buffer, + VkDeviceSize max_element_buffer, + uint32_t framebuffer_width, uint32_t framebuffer_height) { + struct nk_sdl_device *dev = &sdl.vulkan; + dev->max_vertex_buffer = max_vertex_buffer; + dev->max_element_buffer = max_element_buffer; + nk_buffer_init_default(&dev->cmds); + dev->logical_device = logical_device; + dev->physical_device = physical_device; + dev->image_views = image_views; + dev->image_views_len = image_views_len; + dev->color_format = color_format; + dev->framebuffers = NULL; + dev->framebuffers_len = 0; + + nk_sdl_create_sampler(dev); + nk_sdl_create_command_pool(dev, graphics_queue_family_index); + nk_sdl_create_command_buffers(dev); + nk_sdl_create_semaphore(dev); + + nk_sdl_create_buffer_and_memory(dev, &dev->vertex_buffer, + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + &dev->vertex_memory, max_vertex_buffer); + nk_sdl_create_buffer_and_memory(dev, &dev->index_buffer, + VK_BUFFER_USAGE_INDEX_BUFFER_BIT, + &dev->index_memory, max_element_buffer); + nk_sdl_create_buffer_and_memory(dev, &dev->uniform_buffer, + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + &dev->uniform_memory, sizeof(struct Mat4f)); + + vkMapMemory(dev->logical_device, dev->vertex_memory, 0, max_vertex_buffer, + 0, &dev->mapped_vertex); + vkMapMemory(dev->logical_device, dev->index_memory, 0, max_element_buffer, + 0, &dev->mapped_index); + vkMapMemory(dev->logical_device, dev->uniform_memory, 0, + sizeof(struct Mat4f), 0, &dev->mapped_uniform); + + nk_sdl_create_render_resources(dev, framebuffer_width, framebuffer_height); +} + +NK_INTERN void nk_sdl_device_upload_atlas(VkQueue graphics_queue, + const void *image, int width, + int height) { + struct nk_sdl_device *dev = &sdl.vulkan; + + VkImageCreateInfo image_info; + VkResult result; + VkMemoryRequirements mem_reqs; + VkMemoryAllocateInfo alloc_info; + VkBufferCreateInfo buffer_info; + uint8_t *data = 0; + VkCommandBufferBeginInfo begin_info; + VkCommandBuffer command_buffer; + VkImageMemoryBarrier image_memory_barrier; + VkBufferImageCopy buffer_copy_region; + VkImageMemoryBarrier image_shader_memory_barrier; + VkFence fence; + VkFenceCreateInfo fence_create; + VkSubmitInfo submit_info; + VkImageViewCreateInfo image_view_info; + struct { + VkDeviceMemory memory; + VkBuffer buffer; + } staging_buffer; + + memset(&image_info, 0, sizeof(VkImageCreateInfo)); + image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + image_info.imageType = VK_IMAGE_TYPE_2D; + image_info.format = VK_FORMAT_R8G8B8A8_UNORM; + image_info.extent.width = (uint32_t)width; + image_info.extent.height = (uint32_t)height; + image_info.extent.depth = 1; + image_info.mipLevels = 1; + image_info.arrayLayers = 1; + image_info.samples = VK_SAMPLE_COUNT_1_BIT; + image_info.tiling = VK_IMAGE_TILING_OPTIMAL; + image_info.usage = + VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + + result = + vkCreateImage(dev->logical_device, &image_info, NULL, &dev->font_image); + NK_ASSERT(result == VK_SUCCESS); + + vkGetImageMemoryRequirements(dev->logical_device, dev->font_image, + &mem_reqs); + + memset(&alloc_info, 0, sizeof(VkMemoryAllocateInfo)); + alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.allocationSize = mem_reqs.size; + alloc_info.memoryTypeIndex = + nk_sdl_find_memory_index(dev->physical_device, mem_reqs.memoryTypeBits, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + + result = vkAllocateMemory(dev->logical_device, &alloc_info, NULL, + &dev->font_memory); + NK_ASSERT(result == VK_SUCCESS); + result = vkBindImageMemory(dev->logical_device, dev->font_image, + dev->font_memory, 0); + NK_ASSERT(result == VK_SUCCESS); + + memset(&buffer_info, 0, sizeof(VkBufferCreateInfo)); + buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buffer_info.size = alloc_info.allocationSize; + buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + result = vkCreateBuffer(dev->logical_device, &buffer_info, NULL, + &staging_buffer.buffer); + NK_ASSERT(result == VK_SUCCESS); + vkGetBufferMemoryRequirements(dev->logical_device, staging_buffer.buffer, + &mem_reqs); + + alloc_info.allocationSize = mem_reqs.size; + alloc_info.memoryTypeIndex = + nk_sdl_find_memory_index(dev->physical_device, mem_reqs.memoryTypeBits, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + + result = vkAllocateMemory(dev->logical_device, &alloc_info, NULL, + &staging_buffer.memory); + NK_ASSERT(result == VK_SUCCESS); + result = vkBindBufferMemory(dev->logical_device, staging_buffer.buffer, + staging_buffer.memory, 0); + NK_ASSERT(result == VK_SUCCESS); + + result = vkMapMemory(dev->logical_device, staging_buffer.memory, 0, + alloc_info.allocationSize, 0, (void **)&data); + NK_ASSERT(result == VK_SUCCESS); + memcpy(data, image, width * height * 4); + vkUnmapMemory(dev->logical_device, staging_buffer.memory); + + memset(&begin_info, 0, sizeof(VkCommandBufferBeginInfo)); + begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + + NK_ASSERT(dev->command_buffers_len > 0); + /* + use the same command buffer as for render as we are regenerating the + buffer during render anyway + */ + command_buffer = dev->command_buffers[0]; + result = vkBeginCommandBuffer(command_buffer, &begin_info); + NK_ASSERT(result == VK_SUCCESS); + + memset(&image_memory_barrier, 0, sizeof(VkImageMemoryBarrier)); + image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + image_memory_barrier.image = dev->font_image; + image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + image_memory_barrier.subresourceRange.aspectMask = + VK_IMAGE_ASPECT_COLOR_BIT; + image_memory_barrier.subresourceRange.levelCount = 1; + image_memory_barrier.subresourceRange.layerCount = 1; + image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1, + &image_memory_barrier); + + memset(&buffer_copy_region, 0, sizeof(VkBufferImageCopy)); + buffer_copy_region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + buffer_copy_region.imageSubresource.layerCount = 1; + buffer_copy_region.imageExtent.width = (uint32_t)width; + buffer_copy_region.imageExtent.height = (uint32_t)height; + buffer_copy_region.imageExtent.depth = 1; + + vkCmdCopyBufferToImage( + command_buffer, staging_buffer.buffer, dev->font_image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &buffer_copy_region); + + memset(&image_shader_memory_barrier, 0, sizeof(VkImageMemoryBarrier)); + image_shader_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + image_shader_memory_barrier.image = dev->font_image; + image_shader_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_shader_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_shader_memory_barrier.oldLayout = + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + image_shader_memory_barrier.newLayout = + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + image_shader_memory_barrier.subresourceRange.aspectMask = + VK_IMAGE_ASPECT_COLOR_BIT; + image_shader_memory_barrier.subresourceRange.levelCount = 1; + image_shader_memory_barrier.subresourceRange.layerCount = 1; + image_shader_memory_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + image_shader_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT, + + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, NULL, 0, + NULL, 1, &image_shader_memory_barrier); + + result = vkEndCommandBuffer(command_buffer); + NK_ASSERT(result == VK_SUCCESS); + + memset(&fence_create, 0, sizeof(VkFenceCreateInfo)); + fence_create.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + + result = vkCreateFence(dev->logical_device, &fence_create, NULL, &fence); + NK_ASSERT(result == VK_SUCCESS); + + memset(&submit_info, 0, sizeof(VkSubmitInfo)); + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &command_buffer; + + result = vkQueueSubmit(graphics_queue, 1, &submit_info, fence); + NK_ASSERT(result == VK_SUCCESS); + result = + vkWaitForFences(dev->logical_device, 1, &fence, VK_TRUE, UINT64_MAX); + NK_ASSERT(result == VK_SUCCESS); + + vkDestroyFence(dev->logical_device, fence, NULL); + + vkFreeMemory(dev->logical_device, staging_buffer.memory, NULL); + vkDestroyBuffer(dev->logical_device, staging_buffer.buffer, NULL); + + memset(&image_view_info, 0, sizeof(VkImageViewCreateInfo)); + image_view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + image_view_info.image = dev->font_image; + image_view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + image_view_info.format = image_info.format; + image_view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + image_view_info.subresourceRange.layerCount = 1; + image_view_info.subresourceRange.levelCount = 1; + + result = vkCreateImageView(dev->logical_device, &image_view_info, NULL, + &dev->font_image_view); + NK_ASSERT(result == VK_SUCCESS); +} + +NK_INTERN void nk_sdl_destroy_render_resources(struct nk_sdl_device *dev) { + uint32_t i; + + vkDestroyPipeline(dev->logical_device, dev->pipeline, NULL); + vkDestroyPipelineLayout(dev->logical_device, dev->pipeline_layout, NULL); + vkDestroyDescriptorSetLayout(dev->logical_device, + dev->texture_descriptor_set_layout, NULL); + vkDestroyDescriptorSetLayout(dev->logical_device, + dev->uniform_descriptor_set_layout, NULL); + vkDestroyDescriptorPool(dev->logical_device, dev->descriptor_pool, NULL); + for (i = 0; i < dev->framebuffers_len; i++) { + vkDestroyFramebuffer(dev->logical_device, dev->framebuffers[i], NULL); + } + free(dev->framebuffers); + dev->framebuffers_len = 0; + free(dev->texture_descriptor_sets); + dev->texture_descriptor_sets_len = 0; + vkDestroyRenderPass(dev->logical_device, dev->render_pass, NULL); +} + +NK_API void nk_sdl_resize(uint32_t framebuffer_width, + uint32_t framebuffer_height) { + struct nk_sdl_device *dev = &sdl.vulkan; + + SDL_GetWindowSize(sdl.win, &sdl.width, &sdl.height); + sdl.display_width = framebuffer_width; + sdl.display_height = framebuffer_height; + + nk_sdl_destroy_render_resources(dev); + nk_sdl_create_render_resources(dev, sdl.display_width, sdl.display_height); +} + +NK_API void nk_sdl_device_destroy(void) { + struct nk_sdl_device *dev = &sdl.vulkan; + + vkDeviceWaitIdle(dev->logical_device); + + nk_sdl_destroy_render_resources(dev); + + vkFreeCommandBuffers(dev->logical_device, dev->command_pool, + dev->command_buffers_len, dev->command_buffers); + vkDestroyCommandPool(dev->logical_device, dev->command_pool, NULL); + vkDestroySemaphore(dev->logical_device, dev->render_completed, NULL); + + vkUnmapMemory(dev->logical_device, dev->vertex_memory); + vkUnmapMemory(dev->logical_device, dev->index_memory); + vkUnmapMemory(dev->logical_device, dev->uniform_memory); + + vkFreeMemory(dev->logical_device, dev->vertex_memory, NULL); + vkFreeMemory(dev->logical_device, dev->index_memory, NULL); + vkFreeMemory(dev->logical_device, dev->uniform_memory, NULL); + + vkDestroyBuffer(dev->logical_device, dev->vertex_buffer, NULL); + vkDestroyBuffer(dev->logical_device, dev->index_buffer, NULL); + vkDestroyBuffer(dev->logical_device, dev->uniform_buffer, NULL); + + vkDestroySampler(dev->logical_device, dev->sampler, NULL); + + vkFreeMemory(dev->logical_device, dev->font_memory, NULL); + vkDestroyImage(dev->logical_device, dev->font_image, NULL); + vkDestroyImageView(dev->logical_device, dev->font_image_view, NULL); + + free(dev->command_buffers); + nk_buffer_free(&dev->cmds); +} + +NK_API +void nk_sdl_shutdown(void) { + nk_font_atlas_clear(&sdl.atlas); + nk_free(&sdl.ctx); + nk_sdl_device_destroy(); + memset(&sdl, 0, sizeof(sdl)); +} + +NK_API void nk_sdl_font_stash_begin(struct nk_font_atlas **atlas) { + nk_font_atlas_init_default(&sdl.atlas); + nk_font_atlas_begin(&sdl.atlas); + *atlas = &sdl.atlas; +} + +NK_API void nk_sdl_font_stash_end(VkQueue graphics_queue) { + struct nk_sdl_device *dev = &sdl.vulkan; + + const void *image; + int w, h; + image = nk_font_atlas_bake(&sdl.atlas, &w, &h, NK_FONT_ATLAS_RGBA32); + nk_sdl_device_upload_atlas(graphics_queue, image, w, h); + nk_font_atlas_end(&sdl.atlas, nk_handle_ptr(dev->font_image_view), + &dev->tex_null); + if (sdl.atlas.default_font) { + nk_style_set_font(&sdl.ctx, &sdl.atlas.default_font->handle); + } +} + +NK_API void nk_sdl_handle_grab(void) { + struct nk_context *ctx = &sdl.ctx; + if (ctx->input.mouse.grab) { + SDL_SetRelativeMouseMode(SDL_TRUE); + } else if (ctx->input.mouse.ungrab) { + /* better support for older SDL by setting mode first; causes an extra + * mouse motion event */ + SDL_SetRelativeMouseMode(SDL_FALSE); + SDL_WarpMouseInWindow(sdl.win, (int)ctx->input.mouse.prev.x, + (int)ctx->input.mouse.prev.y); + } else if (ctx->input.mouse.grabbed) { + ctx->input.mouse.pos.x = ctx->input.mouse.prev.x; + ctx->input.mouse.pos.y = ctx->input.mouse.prev.y; + } +} + +NK_API int nk_sdl_handle_event(SDL_Event *evt) { + struct nk_context *ctx = &sdl.ctx; + + switch (evt->type) { + case SDL_KEYUP: /* KEYUP & KEYDOWN share same routine */ + case SDL_KEYDOWN: { + int down = evt->type == SDL_KEYDOWN; + const Uint8 *state = SDL_GetKeyboardState(0); + switch (evt->key.keysym.sym) { + case SDLK_RSHIFT: /* RSHIFT & LSHIFT share same routine */ + case SDLK_LSHIFT: + nk_input_key(ctx, NK_KEY_SHIFT, down); + break; + case SDLK_DELETE: + nk_input_key(ctx, NK_KEY_DEL, down); + break; + case SDLK_RETURN: + nk_input_key(ctx, NK_KEY_ENTER, down); + break; + case SDLK_TAB: + nk_input_key(ctx, NK_KEY_TAB, down); + break; + case SDLK_BACKSPACE: + nk_input_key(ctx, NK_KEY_BACKSPACE, down); + break; + case SDLK_HOME: + nk_input_key(ctx, NK_KEY_TEXT_START, down); + nk_input_key(ctx, NK_KEY_SCROLL_START, down); + break; + case SDLK_END: + nk_input_key(ctx, NK_KEY_TEXT_END, down); + nk_input_key(ctx, NK_KEY_SCROLL_END, down); + break; + case SDLK_PAGEDOWN: + nk_input_key(ctx, NK_KEY_SCROLL_DOWN, down); + break; + case SDLK_PAGEUP: + nk_input_key(ctx, NK_KEY_SCROLL_UP, down); + break; + case SDLK_z: + nk_input_key(ctx, NK_KEY_TEXT_UNDO, + down && state[SDL_SCANCODE_LCTRL]); + break; + case SDLK_r: + nk_input_key(ctx, NK_KEY_TEXT_REDO, + down && state[SDL_SCANCODE_LCTRL]); + break; + case SDLK_c: + nk_input_key(ctx, NK_KEY_COPY, down && state[SDL_SCANCODE_LCTRL]); + break; + case SDLK_v: + nk_input_key(ctx, NK_KEY_PASTE, down && state[SDL_SCANCODE_LCTRL]); + break; + case SDLK_x: + nk_input_key(ctx, NK_KEY_CUT, down && state[SDL_SCANCODE_LCTRL]); + break; + case SDLK_b: + nk_input_key(ctx, NK_KEY_TEXT_LINE_START, + down && state[SDL_SCANCODE_LCTRL]); + break; + case SDLK_e: + nk_input_key(ctx, NK_KEY_TEXT_LINE_END, + down && state[SDL_SCANCODE_LCTRL]); + break; + case SDLK_UP: + nk_input_key(ctx, NK_KEY_UP, down); + break; + case SDLK_DOWN: + nk_input_key(ctx, NK_KEY_DOWN, down); + break; + case SDLK_LEFT: + if (state[SDL_SCANCODE_LCTRL]) + nk_input_key(ctx, NK_KEY_TEXT_WORD_LEFT, down); + else + nk_input_key(ctx, NK_KEY_LEFT, down); + break; + case SDLK_RIGHT: + if (state[SDL_SCANCODE_LCTRL]) + nk_input_key(ctx, NK_KEY_TEXT_WORD_RIGHT, down); + else + nk_input_key(ctx, NK_KEY_RIGHT, down); + break; + } + return 1; + } + + case SDL_MOUSEBUTTONUP: /* MOUSEBUTTONUP & MOUSEBUTTONDOWN share same + routine */ + case SDL_MOUSEBUTTONDOWN: { + int down = evt->type == SDL_MOUSEBUTTONDOWN; + const int x = evt->button.x, y = evt->button.y; + switch (evt->button.button) { + case SDL_BUTTON_LEFT: + if (evt->button.clicks > 1) + nk_input_button(ctx, NK_BUTTON_DOUBLE, x, y, down); + nk_input_button(ctx, NK_BUTTON_LEFT, x, y, down); + break; + case SDL_BUTTON_MIDDLE: + nk_input_button(ctx, NK_BUTTON_MIDDLE, x, y, down); + break; + case SDL_BUTTON_RIGHT: + nk_input_button(ctx, NK_BUTTON_RIGHT, x, y, down); + break; + } + return 1; + } + + case SDL_MOUSEMOTION: { + if (ctx->input.mouse.grabbed) { + int x = (int)ctx->input.mouse.prev.x, + y = (int)ctx->input.mouse.prev.y; + nk_input_motion(ctx, x + evt->motion.xrel, y + evt->motion.yrel); + } else + nk_input_motion(ctx, evt->motion.x, evt->motion.y); + return 1; + } + + case SDL_TEXTINPUT: { + nk_glyph glyph; + memcpy(glyph, evt->text.text, NK_UTF_SIZE); + nk_input_glyph(ctx, glyph); + return 1; + } + + case SDL_MOUSEWHEEL: + nk_input_scroll(ctx, nk_vec2((float)evt->wheel.x, (float)evt->wheel.y)); + return 1; + } + return 0; +} + +NK_INTERN void update_texture_descriptor_set( + struct nk_sdl_device *dev, + struct nk_vulkan_texture_descriptor_set *texture_descriptor_set, + VkImageView image_view) { + VkDescriptorImageInfo descriptor_image_info; + VkWriteDescriptorSet descriptor_write; + + texture_descriptor_set->image_view = image_view; + + memset(&descriptor_image_info, 0, sizeof(VkDescriptorImageInfo)); + descriptor_image_info.sampler = dev->sampler; + descriptor_image_info.imageView = texture_descriptor_set->image_view; + descriptor_image_info.imageLayout = + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + memset(&descriptor_write, 0, sizeof(VkWriteDescriptorSet)); + descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptor_write.dstSet = texture_descriptor_set->descriptor_set; + descriptor_write.dstBinding = 0; + descriptor_write.dstArrayElement = 0; + descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + descriptor_write.descriptorCount = 1; + descriptor_write.pImageInfo = &descriptor_image_info; + + vkUpdateDescriptorSets(dev->logical_device, 1, &descriptor_write, 0, NULL); +} + +NK_API +VkSemaphore nk_sdl_render(VkQueue graphics_queue, uint32_t buffer_index, + VkSemaphore wait_semaphore, + enum nk_anti_aliasing AA) { + struct nk_sdl_device *dev = &sdl.vulkan; + struct nk_buffer vbuf, ebuf; + + struct Mat4f projection = { + {2.0f, 0.0f, 0.0f, 0.0f, 0.0f, -2.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, + 0.0f, -1.0f, 1.0f, 0.0f, 1.0f}, + }; + + VkCommandBufferBeginInfo begin_info; + VkClearValue clear_value = {{{0.0f, 0.0f, 0.0f, 0.0f}}}; + VkRenderPassBeginInfo render_pass_begin_nfo; + VkCommandBuffer command_buffer; + VkResult result; + VkViewport viewport; + + VkDeviceSize doffset = 0; + VkImageView current_texture = NULL; + uint32_t index_offset = 0; + VkRect2D scissor; + uint32_t wait_semaphore_count; + VkSemaphore *wait_semaphores; + VkPipelineStageFlags wait_stage = + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + VkSubmitInfo submit_info; + uint64_t time_now; + + time_now = SDL_GetTicks64(); + sdl.ctx.delta_time_seconds = + (float)(time_now - sdl.delta_time_milliseconds_last) / 1000.0f; + sdl.delta_time_milliseconds_last = time_now; + + projection.m[0] /= sdl.display_width; + projection.m[5] /= sdl.display_height; + + memcpy(dev->mapped_uniform, &projection, sizeof(projection)); + + memset(&begin_info, 0, sizeof(VkCommandBufferBeginInfo)); + begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + + memset(&render_pass_begin_nfo, 0, sizeof(VkRenderPassBeginInfo)); + render_pass_begin_nfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + render_pass_begin_nfo.renderPass = dev->render_pass; + render_pass_begin_nfo.renderArea.extent.width = (uint32_t)sdl.display_width; + render_pass_begin_nfo.renderArea.extent.height = + (uint32_t)sdl.display_height; + render_pass_begin_nfo.clearValueCount = 1; + render_pass_begin_nfo.pClearValues = &clear_value; + render_pass_begin_nfo.framebuffer = dev->framebuffers[buffer_index]; + + command_buffer = dev->command_buffers[buffer_index]; + + result = vkBeginCommandBuffer(command_buffer, &begin_info); + NK_ASSERT(result == VK_SUCCESS); + vkCmdBeginRenderPass(command_buffer, &render_pass_begin_nfo, + VK_SUBPASS_CONTENTS_INLINE); + + memset(&viewport, 0, sizeof(VkViewport)); + viewport.width = (float)sdl.width; + viewport.height = (float)sdl.height; + viewport.maxDepth = 1.0f; + vkCmdSetViewport(command_buffer, 0, 1, &viewport); + + vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + dev->pipeline); + vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + dev->pipeline_layout, 0, 1, + &dev->uniform_descriptor_set, 0, NULL); + { + /* convert from command queue into draw list and draw to screen */ + const struct nk_draw_command *cmd; + /* load draw vertices & elements directly into vertex + element buffer + */ + { + /* fill convert configuration */ + struct nk_convert_config config; + static const struct nk_draw_vertex_layout_element vertex_layout[] = + {{NK_VERTEX_POSITION, NK_FORMAT_FLOAT, + NK_OFFSETOF(struct nk_sdl_vertex, position)}, + {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, + NK_OFFSETOF(struct nk_sdl_vertex, uv)}, + {NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, + NK_OFFSETOF(struct nk_sdl_vertex, col)}, + {NK_VERTEX_LAYOUT_END}}; + NK_MEMSET(&config, 0, sizeof(config)); + config.vertex_layout = vertex_layout; + config.vertex_size = sizeof(struct nk_sdl_vertex); + config.vertex_alignment = NK_ALIGNOF(struct nk_sdl_vertex); + config.tex_null = dev->tex_null; + config.circle_segment_count = 22; + config.curve_segment_count = 22; + config.arc_segment_count = 22; + config.global_alpha = 1.0f; + config.shape_AA = AA; + config.line_AA = AA; + + /* setup buffers to load vertices and elements */ + nk_buffer_init_fixed(&vbuf, dev->mapped_vertex, + (size_t)dev->max_vertex_buffer); + nk_buffer_init_fixed(&ebuf, dev->mapped_index, + (size_t)dev->max_element_buffer); + nk_convert(&sdl.ctx, &dev->cmds, &vbuf, &ebuf, &config); + } + + /* iterate over and execute each draw command */ + + vkCmdBindVertexBuffers(command_buffer, 0, 1, &dev->vertex_buffer, + &doffset); + vkCmdBindIndexBuffer(command_buffer, dev->index_buffer, 0, + VK_INDEX_TYPE_UINT16); + + nk_draw_foreach(cmd, &sdl.ctx, &dev->cmds) { + if (!cmd->texture.ptr) { + continue; + } + if (cmd->texture.ptr && cmd->texture.ptr != current_texture) { + int found = 0; + uint32_t i; + for (i = 0; i < dev->texture_descriptor_sets_len; i++) { + if (dev->texture_descriptor_sets[i].image_view == + cmd->texture.ptr) { + found = 1; + break; + } + } + + if (!found) { + update_texture_descriptor_set( + dev, &dev->texture_descriptor_sets[i], + (VkImageView)cmd->texture.ptr); + dev->texture_descriptor_sets_len++; + } + vkCmdBindDescriptorSets( + command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + dev->pipeline_layout, 1, 1, + &dev->texture_descriptor_sets[i].descriptor_set, 0, NULL); + } + + if (!cmd->elem_count) + continue; + + scissor.offset.x = (int32_t)(NK_MAX(cmd->clip_rect.x, 0.f)); + scissor.offset.y = (int32_t)(NK_MAX(cmd->clip_rect.y, 0.f)); + scissor.extent.width = (uint32_t)(cmd->clip_rect.w); + scissor.extent.height = (uint32_t)(cmd->clip_rect.h); + vkCmdSetScissor(command_buffer, 0, 1, &scissor); + vkCmdDrawIndexed(command_buffer, cmd->elem_count, 1, index_offset, + 0, 0); + index_offset += cmd->elem_count; + } + nk_clear(&sdl.ctx); + } + + vkCmdEndRenderPass(command_buffer); + result = vkEndCommandBuffer(command_buffer); + NK_ASSERT(result == VK_SUCCESS); + + if (wait_semaphore) { + wait_semaphore_count = 1; + wait_semaphores = &wait_semaphore; + } else { + wait_semaphore_count = 0; + wait_semaphores = NULL; + } + + memset(&submit_info, 0, sizeof(VkSubmitInfo)); + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &command_buffer; + submit_info.pWaitDstStageMask = &wait_stage; + submit_info.waitSemaphoreCount = wait_semaphore_count; + submit_info.pWaitSemaphores = wait_semaphores; + submit_info.signalSemaphoreCount = 1; + submit_info.pSignalSemaphores = &dev->render_completed; + + result = vkQueueSubmit(graphics_queue, 1, &submit_info, NULL); + NK_ASSERT(result == VK_SUCCESS); + + return dev->render_completed; +} + +NK_INTERN void nk_sdl_clipboard_paste(nk_handle usr, + struct nk_text_edit *edit) { + const char *text = SDL_GetClipboardText(); + if (text) + nk_textedit_paste(edit, text, nk_strlen(text)); + SDL_free((void *)text); + (void)usr; +} + +NK_INTERN void nk_sdl_clipboard_copy(nk_handle usr, const char *text, int len) { + char *str = 0; + (void)usr; + if (!len) + return; + str = (char *)malloc((size_t)len + 1); + if (!str) + return; + memcpy(str, text, (size_t)len); + str[len] = '\0'; + SDL_SetClipboardText(str); + free(str); +} + +NK_API struct nk_context * +nk_sdl_init(SDL_Window *win, VkDevice logical_device, + VkPhysicalDevice physical_device, + uint32_t graphics_queue_family_index, VkImageView *image_views, + uint32_t image_views_len, VkFormat color_format, + enum nk_sdl_init_state init_state, VkDeviceSize max_vertex_buffer, + VkDeviceSize max_element_buffer) { + (void)init_state; + + memset(&sdl, 0, sizeof(struct nk_sdl)); + sdl.win = win; + + nk_init_default(&sdl.ctx, 0); + sdl.ctx.clip.copy = nk_sdl_clipboard_copy; + sdl.ctx.clip.paste = nk_sdl_clipboard_paste; + sdl.ctx.clip.userdata = nk_handle_ptr(0); + + SDL_GetWindowSize(win, &sdl.width, &sdl.height); + SDL_Vulkan_GetDrawableSize(win, &sdl.display_width, &sdl.display_height); + nk_sdl_device_create(logical_device, physical_device, + graphics_queue_family_index, image_views, + image_views_len, color_format, max_vertex_buffer, + max_element_buffer, (uint32_t)sdl.display_width, + (uint32_t)sdl.display_height); + + return &sdl.ctx; +} + +#endif diff --git a/demo/sdl_vulkan/src/nuklearshaders/nuklear.frag b/demo/sdl_vulkan/src/nuklearshaders/nuklear.frag new file mode 100644 index 0000000..376ad16 --- /dev/null +++ b/demo/sdl_vulkan/src/nuklearshaders/nuklear.frag @@ -0,0 +1,13 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(binding = 0, set = 1) uniform sampler2D currentTexture; + +layout(location = 0) in vec4 fragColor; +layout(location = 1) in vec2 fragUv; +layout(location = 0) out vec4 outColor; + +void main() { + vec4 texColor = texture(currentTexture, fragUv); + outColor = fragColor * texColor; +} diff --git a/demo/sdl_vulkan/src/nuklearshaders/nuklear.vert b/demo/sdl_vulkan/src/nuklearshaders/nuklear.vert new file mode 100644 index 0000000..3eee82d --- /dev/null +++ b/demo/sdl_vulkan/src/nuklearshaders/nuklear.vert @@ -0,0 +1,23 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +out gl_PerVertex { + vec4 gl_Position; +}; + +layout(binding = 0) uniform UniformBufferObject { + mat4 projection; +} ubo; + +layout(location = 0) in vec2 position; +layout(location = 1) in vec2 uv; +layout(location = 2) in uvec4 color; +layout(location = 0) out vec4 fragColor; +layout(location = 1) out vec2 fragUv; + +void main() { + gl_Position = ubo.projection * vec4(position, 0.0, 1.0); + gl_Position.y = -gl_Position.y; + fragColor = vec4(color[0]/255.0, color[1]/255.0, color[2]/255.0, color[3]/255.0); + fragUv = uv; +}