summaryrefslogtreecommitdiff
path: root/main.c
diff options
context:
space:
mode:
authorJordan Halase <jordan@halase.me>2019-10-25 16:53:00 -0500
committerJordan Halase <jordan@halase.me>2019-10-25 16:53:00 -0500
commit027a512c762e0ac5e46908f533b94127010de565 (patch)
tree80427017f8cfe03b6bbffc67a6c0b43e3d4ef25c /main.c
Initial commit
Diffstat (limited to 'main.c')
-rw-r--r--main.c1906
1 files changed, 1906 insertions, 0 deletions
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..9f5f10a
--- /dev/null
+++ b/main.c
@@ -0,0 +1,1906 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include <X11/Xlib.h>
+
+#include <vulkan/vulkan.h>
+#include <vulkan/vulkan_xlib.h>
+
+#include "pugl/pugl.h"
+
+#include "vk_mem_alloc.h"
+
+#define ERRFQ(fmt, ...) \
+ do { \
+ fprintf(stderr, fmt __VA_OPT__(,) __VA_ARGS__); \
+ exit(1); \
+ } while(0)
+
+#define ERRQ(str) ERRFQ("%s\n", str)
+
+#define PI 3.14159265359f
+#define TWOPI 6.28318530718f
+#define DEGREES(x) ((x)*PI/180.0f)
+#define MAT4X4_SIZE (16*sizeof(float))
+
+#define MIN(a, b) ((a) <= (b) ? (a) : (b))
+#define MAX(a, b) ((a) >= (b) ? (a) : (b))
+#define CLAMP(x, l, h) ((x) <= (l) ? (l) : (x) >= (h) ? (h) : (x))
+
+#ifdef HAVE_MSAA
+static const unsigned msaaEnabled = 1;
+static const VkSampleCountFlagBits msaaSampleCount = VK_SAMPLE_COUNT_8_BIT;
+#else
+static const unsigned msaaEnabled = 0;
+static const VkSampleCountFlagBits msaaSampleCount = VK_SAMPLE_COUNT_1_BIT;
+#endif
+
+static const VkFormat depthFormat = VK_FORMAT_D24_UNORM_S8_UINT;
+
+#define TIMER_INTERVAL 500
+
+static ssize_t get_file_size(FILE *fp)
+{
+#if defined(__linux__)
+
+#include <sys/stat.h>
+ const int fd = fileno(fp);
+ struct stat st;
+ if (fstat(fd, &st) == -1) {
+ return -1;
+ }
+ return st.st_size;
+
+#else
+#error "Cannot get file sizes on this system"
+#endif
+}
+
+uint8_t *read_file(const char *path, size_t *size)
+{
+#if 0
+ SDL_RWops *rw = SDL_RWFromFile(path, "r");
+ if (!rw) {
+ *size = 0;
+ return NULL;
+ }
+ *size = SDL_RWsize(rw);
+ uint8_t *buf = malloc(*size);
+ SDL_RWread(rw, buf, *size, 1);
+ SDL_RWclose(rw);
+ return buf;
+#else
+ FILE *fp = fopen(path, "rb");
+ if (!fp) {
+ *size = 0;
+ return NULL;
+ }
+ const ssize_t result = get_file_size(fp);
+ if (result == -1) {
+ *size = 0;
+ return NULL;
+ } else {
+ *size = result;
+ }
+ uint8_t *buf = malloc(*size);
+ size_t r = 0;
+ while ((r = fread(buf, *size, 1, fp))) {
+ }
+ fclose(fp);
+ return buf;
+#endif
+}
+
+/* ALL matrices are column major
+ *
+ * Modifications for Vulkan:
+ * Invert y axis
+ * Expect z clip coordinates on range [0, 1]
+ */
+static void load_perspective(float matrix[4][4],
+ const float fov,
+ const float aspect,
+ const float znear,
+ const float zfar)
+{
+ memset(matrix, 0, MAT4X4_SIZE);
+ const float f = 1.0f/tanf(0.5f*fov);
+ matrix[0][0] = f/aspect;
+ matrix[1][1] = -f;
+ matrix[2][2] = -zfar/(zfar - znear);
+ matrix[3][2] = -zfar*znear/(zfar - znear);
+ matrix[2][3] = -1.0f;
+}
+
+static void matmul4(const float A[4][4],
+ const float B[4][4],
+ float C[4][4])
+{
+ memset(C, 0, MAT4X4_SIZE);
+ int i, j, k;
+ for (i = 0; i < 4; ++i) {
+ for (j = 0; j < 4; ++j) {
+ for (k = 0; k < 4; ++k) {
+ C[j][i] += A[k][i] * B[j][k];
+ }
+ }
+ }
+}
+
+static void matvecmul4(const float M[4][4],
+ const float *restrict V,
+ float *restrict C)
+{
+ int i, j;
+ for (i = 0; i < 4; ++i) {
+ C[i] = 0;
+ for (j = 0; j < 4; ++j) {
+ C[i] += M[j][i] * V[j];
+ }
+ }
+}
+
+static void load_identity(float matrix[4][4])
+{
+ memset(matrix, 0, MAT4X4_SIZE);
+ matrix[0][0] = 1.0f;
+ matrix[1][1] = 1.0f;
+ matrix[2][2] = 1.0f;
+ matrix[3][3] = 1.0f;
+}
+
+static void load_rotation_y(float matrix[4][4], const float angle)
+{
+ memset(matrix, 0, MAT4X4_SIZE);
+ const float cosangle = cosf(angle);
+ const float sinangle = sinf(angle);
+ matrix[0][0] = cosangle;
+ matrix[2][0] = sinangle;
+ matrix[0][2] = -sinangle;
+ matrix[2][2] = cosangle;
+ matrix[1][1] = 1.0;
+ matrix[3][3] = 1.0;
+}
+
+static void load_location(float matrix[4][4],
+ const float x,
+ const float y,
+ const float z)
+{
+ load_identity(matrix);
+ matrix[3][0] = x;
+ matrix[3][1] = y;
+ matrix[3][2] = z;
+}
+
+struct Uniform {
+ float model[4][4];
+ float view[4][4];
+ float proj[4][4];
+};
+
+/* Set once when physical device is chosen, do not modify otherwise */
+static VkPhysicalDeviceMemoryProperties memoryProperties;
+static VkPhysicalDeviceProperties deviceProperties;
+
+static VmaAllocator vmaAllocator;
+
+PuglWorld *world;
+PuglView *view;
+static VkDevice device;
+static VkInstance instance;
+static VkDebugReportCallbackEXT callback;
+static VkSurfaceKHR surface;
+static VkPhysicalDevice physicalDevice;
+static uint32_t graphicsIndex;
+static uint32_t transferIndex;
+static VkQueue transferQueue;
+static VkQueue graphicsQueue;
+static VkCommandPool commandPool;
+static VkCommandPool transferPool;
+static VkCommandBuffer *commandBuffers;
+static VkSwapchainKHR rawSwapchain;
+static VkSwapchainKHR oldRawSwapchain;
+static uint32_t nImages;
+static VkSurfaceFormatKHR surfaceFormat;
+static VkImage *swapchainImages;
+static VkImageView *swapchainImageViews;
+static VkExtent2D swapchainExtent;
+static VkImage depthImage;
+static VmaAllocation depthVma;
+static VkImageView depthImageView;
+
+#if 1
+/* TODO: Get rid of this because we're using VMA */
+static uint32_t getMemType(const VkMemoryRequirements *memReq,
+ const VkMemoryPropertyFlags flags)
+{
+ const uint32_t bits = memReq->memoryTypeBits;
+ uint32_t type;
+ for (type = 0; type < memoryProperties.memoryTypeCount; ++type) {
+ if (bits & (1<<type) && (memoryProperties.memoryTypes[type].propertyFlags & flags) == flags) {
+ break;
+ }
+ }
+ if (type >= memoryProperties.memoryTypeCount) {
+ ERRFQ("No suitable memory type found\n");
+ }
+ return type;
+}
+#endif
+
+/* Undefined behavior if `offset` is not a power of two */
+static VkDeviceSize getNearestAlignment(const VkDeviceSize offset, const VkDeviceSize alignment)
+{
+ const VkDeviceSize mask = alignment - 1;
+ if (offset & mask) {
+ return offset + alignment - (offset & mask);
+ }
+ return offset;
+}
+
+static void createInstance()
+{
+ const VkApplicationInfo appInfo = {
+ .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
+ .pApplicationName = "VulkanGame",
+ .applicationVersion = VK_MAKE_VERSION(1, 0, 0),
+ .pEngineName = "VulkanGameEngine",
+ .engineVersion = VK_MAKE_VERSION(1, 0, 0),
+ .apiVersion = VK_MAKE_VERSION(1, 0, 0)
+ };
+
+ unsigned i, nRequired;
+#if 0
+ if (!SDL_Vulkan_GetInstanceExtensions(win, &nRequired, NULL)) {
+ ERRFQ("Could not get number of instance extensions\n");
+ }
+#else
+ nRequired = 0;
+#endif
+ static const char *const additional[] = {
+ VK_KHR_SURFACE_EXTENSION_NAME,
+ VK_KHR_XLIB_SURFACE_EXTENSION_NAME,
+ VK_EXT_DEBUG_REPORT_EXTENSION_NAME
+ };
+ const unsigned nAdditional = sizeof(additional)/sizeof(additional[0]);
+ const uint32_t nExtensions = nRequired + nAdditional;
+ const char **extensions = malloc(sizeof(const char*)*nExtensions);
+#if 0
+ if (!SDL_Vulkan_GetInstanceExtensions(win, &nRequired, extensions)) {
+ /* OS will handle memory freeing */
+ ERRQ(SDL_GetError());
+ }
+#endif
+ for (i = 0; i < nAdditional; ++i) {
+ extensions[nRequired + i] = additional[i];
+ }
+
+ /* Just printing verbosely */
+ for (i = 0; i < nExtensions; ++i) {
+ printf("Using instance extension:\t%s\n", extensions[i]);
+ }
+
+#ifdef VULKAN_VALIDATION
+ static const char *const layers[] = {
+ "VK_LAYER_LUNARG_standard_validation"
+ };
+ const uint32_t nLayers = sizeof(layers)/sizeof(layers[0]);
+#endif
+ VkInstanceCreateInfo createInfo = {
+ .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
+ .pApplicationInfo = &appInfo,
+ .enabledExtensionCount = nExtensions,
+ .ppEnabledExtensionNames = extensions,
+#ifdef VULKAN_VALIDATION
+ .enabledLayerCount = nLayers,
+ .ppEnabledLayerNames = layers
+#endif
+ };
+ VkResult result;
+ if ((result = vkCreateInstance(&createInfo, NULL, &instance))) {
+ /* OS will handle memory freeing */
+ ERRFQ("Could not create Vulkan instance: %d\n", result);
+ }
+ free(extensions);
+}
+
+#ifdef VULKAN_VALIDATION
+static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
+ VkDebugReportFlagsEXT flags,
+ VkDebugReportObjectTypeEXT objType,
+ uint64_t obj,
+ size_t location,
+ int32_t code,
+ const char *layerPrefix,
+ const char *msg,
+ void *userData
+ )
+{
+ fprintf(stderr, "\nValidation layer: %s\n\n", msg);
+ return VK_FALSE;
+}
+
+VkResult createDebugReportCallback(
+ VkInstance instance,
+ const VkDebugReportCallbackCreateInfoEXT *pCreateInfo,
+ const VkAllocationCallbacks *pAllocator,
+ VkDebugReportCallbackEXT *pCallback
+ )
+{
+ printf("Creating debug reporter\n");
+ PFN_vkCreateDebugReportCallbackEXT func = (void*)vkGetInstanceProcAddr(instance,
+ "vkCreateDebugReportCallbackEXT");
+ if (func) {
+ return func(instance, pCreateInfo, pAllocator, pCallback);
+ } else {
+ return VK_ERROR_EXTENSION_NOT_PRESENT;
+ }
+}
+
+void destroyDebugReportCallback(VkInstance instance, VkDebugReportCallbackEXT callback,
+ const VkAllocationCallbacks *pAllocator)
+{
+ PFN_vkDestroyDebugReportCallbackEXT func = (void*)vkGetInstanceProcAddr(instance,
+ "vkDestroyDebugReportCallbackEXT");
+ if (func) {
+ func(instance, callback, pAllocator);
+ }
+}
+static void createDebugReporter()
+{
+ VkDebugReportCallbackCreateInfoEXT createInfo = {
+ .sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT,
+ .flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT,
+ .pfnCallback = debugCallback
+ };
+ if (createDebugReportCallback(instance, &createInfo, NULL, &callback)) {
+ ERRQ("Could not create debug reporter");
+ }
+}
+#endif
+
+static void selectPhysicalDevice(const char *hint)
+{
+ uint32_t nDevices;
+ vkEnumeratePhysicalDevices(instance, &nDevices, NULL);
+ if (!nDevices) {
+ ERRQ("No physical devices found");
+ }
+ VkPhysicalDevice *devices = malloc(nDevices*sizeof(*devices));
+ vkEnumeratePhysicalDevices(instance, &nDevices, devices);
+
+ /* Just printing verbosely */
+ for (uint32_t i = 0; i < nDevices; ++i) {
+ vkGetPhysicalDeviceProperties(devices[i], &deviceProperties);
+ printf("Found physical device:\t`%s`\n", deviceProperties.deviceName);
+ }
+
+ /* FIXME: Blindly choosing the first device */
+ physicalDevice = devices[0];
+
+ uint32_t nQueueFamilies = 0;
+ vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &nQueueFamilies, NULL);
+ VkQueueFamilyProperties *queueProperties = malloc(nQueueFamilies*sizeof(*queueProperties));
+ vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &nQueueFamilies, queueProperties);
+
+ for (uint32_t i = 0; i < nQueueFamilies; ++i) {
+ printf("Queue Family %d queueCount:\t%d\n", i, queueProperties[i].queueCount);
+ }
+
+ for (graphicsIndex = 0; graphicsIndex < nQueueFamilies; ++graphicsIndex) {
+ if (queueProperties[graphicsIndex].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
+ VkBool32 canSurface;
+ vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, graphicsIndex, surface, &canSurface);
+ if (canSurface) break;
+ }
+ }
+ if (graphicsIndex >= nQueueFamilies) {
+ /* Some devices may have separate graphics and present queue families, or none at all.
+ * We only support graphics and present on the same queue family.
+ */
+ ERRQ("No queue families capable of graphics and presentation");
+ }
+
+ /* Look for a queue family with transfer capabilities but no graphics or compute.
+ * Hardcoded literals are for conciseness and come from the `VkQueueFlagBits` definitions.
+ */
+ const VkQueueFlagBits want = 0x4;
+ for (transferIndex = 0; transferIndex < nQueueFamilies; ++transferIndex) {
+ if ((queueProperties[transferIndex].queueFlags & 0x7) == want) {
+ goto found;
+ }
+ }
+ /* Look for a queue family with transfer capabilities that isn't the same as before.
+ * Graphics and compute queue families implicitly support transfer.
+ */
+ for (transferIndex = 0; transferIndex < nQueueFamilies; ++transferIndex) {
+ if (queueProperties[transferIndex].queueFlags & 0x3) {
+ if (transferIndex != graphicsIndex) {
+ goto found;
+ }
+ }
+ }
+ //ERRQ("FIXME: Fallback transfer queue to graphics family");
+ transferIndex = graphicsIndex;
+found:
+ printf("Graphics Queue Family Index:\t%d\nTransfer Queue Family Index:\t%d\n", graphicsIndex, transferIndex);
+
+ vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memoryProperties);
+
+ VkBool32 canSwapchain = 0;
+ uint32_t nExtensions;
+ vkEnumerateDeviceExtensionProperties(physicalDevice, NULL, &nExtensions, NULL);
+ VkExtensionProperties *availableExtensions = malloc(nExtensions*sizeof(*availableExtensions));
+ vkEnumerateDeviceExtensionProperties(physicalDevice, NULL, &nExtensions, availableExtensions);
+
+ for (uint32_t i = 0; i < nExtensions; ++i) {
+ if (!strcmp(availableExtensions[i].extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME)) {
+ canSwapchain = 1;
+ break;
+ }
+ }
+ if (!canSwapchain) {
+ ERRQ("Graphics device not swapchain capable");
+ }
+ free(availableExtensions);
+ free(queueProperties);
+ free(devices);
+}
+
+static void createLogicalDevice()
+{
+ VkPhysicalDeviceFeatures features;
+ vkGetPhysicalDeviceFeatures(physicalDevice, &features);
+ const float queuePriorities[] = { 1.0f, 0.125f };
+ const VkDeviceQueueCreateInfo queueCreateInfos[] = {
+ {
+ .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
+ .queueFamilyIndex = graphicsIndex,
+ .queueCount = 1,
+ .pQueuePriorities = &queuePriorities[0]
+ }, {
+ .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
+ .queueFamilyIndex = transferIndex,
+ .queueCount = 1,
+ .pQueuePriorities = &queuePriorities[1]
+ }
+ };
+
+ const char *const swapchainName = VK_KHR_SWAPCHAIN_EXTENSION_NAME;
+ const VkDeviceCreateInfo createInfo = {
+ .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
+ .pQueueCreateInfos = queueCreateInfos,
+ .queueCreateInfoCount = 2,
+ .pEnabledFeatures = &features,
+ .enabledExtensionCount = 1,
+ .ppEnabledExtensionNames = &swapchainName
+ };
+
+ VkResult result;
+ if ((result = vkCreateDevice(physicalDevice, &createInfo, NULL, &device))) {
+ ERRFQ("Could not create logical device: %d\n", result);
+ }
+
+ const VmaAllocatorCreateInfo allocatorInfo = {
+ //.flags = VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT,
+ .physicalDevice = physicalDevice,
+ .device = device
+ };
+ if ((result = vmaCreateAllocator(&allocatorInfo, &vmaAllocator))) {
+ ERRFQ("Could not create Vulkan Memory Allocator: %d\n", result);
+ }
+}
+
+static void destroyLogicalDevice()
+{
+ vmaDestroyAllocator(vmaAllocator);
+ vkDestroyDevice(device, NULL);
+}
+
+static void createCommandPools()
+{
+ VkCommandPoolCreateInfo createInfo = {
+ .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
+ .queueFamilyIndex = graphicsIndex
+ };
+ VkResult result;
+ if ((result = vkCreateCommandPool(device, &createInfo, NULL, &commandPool))) {
+ ERRFQ("Could not create command pool:\t%d\n", result);
+ }
+ createInfo.queueFamilyIndex = transferIndex;
+ if ((result = vkCreateCommandPool(device, &createInfo, NULL, &transferPool))) {
+ ERRFQ("Could not create transfer pool:\t%d\n", result);
+ }
+}
+
+static void destroyCommandPools()
+{
+ vkDestroyCommandPool(device, transferPool, NULL);
+ vkDestroyCommandPool(device, commandPool, NULL);
+}
+
+static void allocateCommandBuffers()
+{
+ const VkCommandBufferAllocateInfo allocInfo = {
+ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
+ .commandPool = commandPool,
+ .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
+ .commandBufferCount = nImages
+ };
+ commandBuffers = realloc(commandBuffers, nImages*sizeof(VkCommandBuffer));
+ VkResult result;
+ if ((result = vkAllocateCommandBuffers(device, &allocInfo, commandBuffers))) {
+ ERRFQ("Could not create command buffers: %d\n", result);
+ }
+}
+
+static void freeCommandBuffers()
+{
+ if (commandBuffers) {
+ vkFreeCommandBuffers(device, commandPool, nImages, commandBuffers);
+ }
+}
+
+void getFrame(PuglView *view, int *width, int *height)
+{
+ const PuglRect rect = puglGetFrame(view);
+ *width = rect.width;
+ *height = rect.height;
+ //printf("Pugl frame:\t%d, %d\n", *width, *height);
+}
+
+static void createSwapchain()
+{
+ VkSurfaceCapabilitiesKHR capabilities;
+ VkSurfaceFormatKHR *formats;
+ VkPresentModeKHR *presentModes;
+
+ vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &capabilities);
+ uint32_t nFormats;
+ vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &nFormats, NULL);
+ if (!nFormats) {
+ ERRQ("No surface formats available");
+ }
+ formats = malloc(nFormats*sizeof(*formats));
+ vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &nFormats, formats);
+
+ uint32_t nPresentModes;
+ vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &nPresentModes, NULL);
+ if (!nPresentModes) {
+ ERRQ("No surface present modes available");
+ }
+ presentModes = malloc(nPresentModes*sizeof(*presentModes));
+ vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &nPresentModes, presentModes);
+
+ for (uint32_t i = 0; i < nFormats; ++i) {
+ VkSurfaceFormatKHR want = {
+ VK_FORMAT_B8G8R8A8_SRGB, // BGRA sRGB
+ VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
+ };
+ if (formats[i].format == VK_FORMAT_UNDEFINED) {
+ surfaceFormat = want;
+ //SDL_Log("Obtained wanted surface format on first try\n");
+ break;
+ }
+ if (formats[i].format == want.format && formats[i].colorSpace == want.colorSpace) {
+ surfaceFormat = want;
+ //SDL_Log("Obtained wanted format on try %d/%d\n", i+1, nFormats);
+ break;
+ }
+ }
+
+ /* FIXME: Just assuming this is available */
+ //VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR;
+ //VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_RELAXED_KHR;
+ VkPresentModeKHR presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
+ //VkPresentModeKHR presentMode = VK_PRESENT_MODE_MAILBOX_KHR;
+
+ int width, height;
+ //SDL_Vulkan_GetDrawableSize(win, &width, &height);
+ getFrame(view, &width, &height);
+ width = CLAMP(width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width);
+ height = CLAMP(height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height);
+ swapchainExtent.width = width;
+ swapchainExtent.height = height;
+ //swapchainExtent = capabilities.currentExtent;
+
+ //nImages = capabilities.minImageCount + 1;
+ nImages = capabilities.minImageCount;
+ //nImages = capabilities.maxImageCount;
+ if (capabilities.maxImageCount > 0 && nImages > capabilities.maxImageCount) {
+ /* Clamp to max image count if too many requested */
+ nImages = capabilities.maxImageCount;
+ }
+ //printf("Swapchain Images:\t%d\n", nImages);
+
+ const VkSwapchainCreateInfoKHR createInfo = {
+ .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
+ .surface = surface,
+ .minImageCount = nImages,
+ .imageFormat = surfaceFormat.format,
+ .imageExtent = swapchainExtent,
+ .imageArrayLayers = 1,
+ .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
+ .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, // assuming same family
+ .preTransform = capabilities.currentTransform,
+ .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
+ .presentMode = presentMode,
+ .clipped = VK_TRUE,
+ .oldSwapchain = oldRawSwapchain
+ };
+
+ VkResult result;
+ if ((result = vkCreateSwapchainKHR(device, &createInfo, NULL, &rawSwapchain))) {
+ ERRFQ("Could not create swapchain: %d\n", result);
+ }
+ if (oldRawSwapchain != VK_NULL_HANDLE) {
+ vkDestroySwapchainKHR(device, oldRawSwapchain, NULL);
+ oldRawSwapchain = VK_NULL_HANDLE;
+ }
+ free(presentModes);
+ free(formats);
+}
+
+static void destroySwapchain()
+{
+ if (rawSwapchain != VK_NULL_HANDLE) {
+ vkDestroySwapchainKHR(device, rawSwapchain, NULL);
+ }
+ rawSwapchain = VK_NULL_HANDLE;
+}
+
+/* Allocates and reallocates memory */
+static void createSwapchainImageViews()
+{
+ vkGetSwapchainImagesKHR(device, rawSwapchain, &nImages, NULL);
+ swapchainImages = malloc(nImages*sizeof(*swapchainImages));
+ vkGetSwapchainImagesKHR(device, rawSwapchain, &nImages, swapchainImages);
+ swapchainImageViews = malloc(nImages*sizeof(*swapchainImageViews));
+
+ for (uint32_t i = 0; i < nImages; ++i) {
+ const VkImageViewCreateInfo createInfo = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
+ .image = swapchainImages[i],
+ .viewType = VK_IMAGE_VIEW_TYPE_2D,
+ .format = surfaceFormat.format,
+ .components.r = VK_COMPONENT_SWIZZLE_IDENTITY,
+ .components.g = VK_COMPONENT_SWIZZLE_IDENTITY,
+ .components.b = VK_COMPONENT_SWIZZLE_IDENTITY,
+ .components.a = VK_COMPONENT_SWIZZLE_IDENTITY,
+ .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .subresourceRange.baseMipLevel = 0,
+ .subresourceRange.levelCount = 1,
+ .subresourceRange.baseArrayLayer = 0,
+ .subresourceRange.layerCount = 1
+
+ };
+ if (vkCreateImageView(device, &createInfo, NULL, &swapchainImageViews[i])) {
+ ERRQ("Could not create swapchain image views");
+ }
+ }
+}
+
+static void destroySwapchainImageViews()
+{
+ for (uint32_t i = 0; i < nImages; ++i) {
+ vkDestroyImageView(device, swapchainImageViews[i], NULL);
+ }
+ free(swapchainImageViews);
+ free(swapchainImages);
+ swapchainImageViews = NULL;
+ swapchainImages = NULL;
+}
+
+static void createDepthImage()
+{
+ const VkImageCreateInfo createInfo = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
+ .imageType = VK_IMAGE_TYPE_2D,
+ .format = depthFormat,
+ .extent.width = swapchainExtent.width,
+ .extent.height = swapchainExtent.height,
+ .extent.depth = 1,
+ .mipLevels = 1,
+ .arrayLayers = 1,
+ .samples = msaaSampleCount,
+ .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
+ .usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
+ .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
+ };
+ VmaAllocationCreateInfo allocInfo = {
+ .flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
+ .usage = VMA_MEMORY_USAGE_GPU_ONLY
+ };
+ VkResult result;
+ if ((result = vmaCreateImage(vmaAllocator, &createInfo, &allocInfo, &depthImage, &depthVma, NULL))) {
+ fprintf(stderr, "Could not create depth image: %d\n", result);
+ exit(1);
+ }
+ const VkImageViewCreateInfo viewInfo = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
+ .image = depthImage,
+ .format = depthFormat,
+ .components.r = VK_COMPONENT_SWIZZLE_R,
+ .components.g = VK_COMPONENT_SWIZZLE_G,
+ .components.b = VK_COMPONENT_SWIZZLE_B,
+ .components.a = VK_COMPONENT_SWIZZLE_A,
+ .subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT,
+ .subresourceRange.baseMipLevel = 0,
+ .subresourceRange.levelCount = 1,
+ .subresourceRange.baseArrayLayer = 0,
+ .subresourceRange.layerCount = 1,
+ .viewType = VK_IMAGE_VIEW_TYPE_2D,
+ };
+ if ((result = vkCreateImageView(device, &viewInfo, NULL, &depthImageView))) {
+ ERRFQ("Could not create depth buffer image view: %d\n", result);
+ }
+}
+
+static void destroyDepthImage()
+{
+ vkDestroyImageView(device, depthImageView, NULL);
+ vmaDestroyImage(vmaAllocator, depthImage, depthVma);
+ depthImageView = VK_NULL_HANDLE;
+ depthVma = VK_NULL_HANDLE;
+ depthImage = VK_NULL_HANDLE;
+}
+
+static VkImage msaaImage;
+static VkImageView msaaImageView;
+static VmaAllocation msaaVma;
+
+static void createMultisampleImage()
+{
+ const VkImageCreateInfo createInfo = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
+ .imageType = VK_IMAGE_TYPE_2D,
+ .format = surfaceFormat.format,
+ .extent.width = swapchainExtent.width,
+ .extent.height = swapchainExtent.height,
+ .extent.depth = 1,
+ .mipLevels = 1,
+ .arrayLayers = 1,
+ .samples = msaaSampleCount,
+ .usage = VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT |
+ VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
+ .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
+ .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
+ .tiling = VK_IMAGE_TILING_OPTIMAL
+ };
+ const VmaAllocationCreateInfo allocInfo = {
+ .flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
+ .usage = VMA_MEMORY_USAGE_GPU_ONLY
+ };
+ VkResult result;
+ if ((result = vmaCreateImage(vmaAllocator, &createInfo, &allocInfo, &msaaImage, &msaaVma, NULL))) {
+ ERRFQ("Could not create multisample image: %d\n", result);
+ }
+ const VkImageViewCreateInfo viewInfo = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
+ .image = msaaImage,
+ .format = VK_FORMAT_B8G8R8A8_SRGB,
+ .components.r = VK_COMPONENT_SWIZZLE_R,
+ .components.g = VK_COMPONENT_SWIZZLE_G,
+ .components.b = VK_COMPONENT_SWIZZLE_B,
+ .components.a = VK_COMPONENT_SWIZZLE_A,
+ .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .subresourceRange.baseMipLevel = 0,
+ .subresourceRange.levelCount = 1,
+ .subresourceRange.baseArrayLayer = 0,
+ .subresourceRange.layerCount = 1,
+ .viewType = VK_IMAGE_VIEW_TYPE_2D,
+ };
+ if ((result = vkCreateImageView(device, &viewInfo, NULL, &msaaImageView))) {
+ ERRFQ("Could not create multisample image view: %d\n", result);
+ }
+}
+
+static void destroyMultisampleImage()
+{
+ vkDestroyImageView(device, msaaImageView, NULL);
+ vmaDestroyImage(vmaAllocator, msaaImage, msaaVma);
+ msaaImageView = VK_NULL_HANDLE;
+ msaaVma = VK_NULL_HANDLE;
+ msaaImage = VK_NULL_HANDLE;
+}
+
+static VkRenderPass renderPass;
+
+static void createRenderPass()
+{
+ VkAttachmentDescription attachments[] = {{
+ /* Color image */
+ .format = surfaceFormat.format,
+ .samples = msaaSampleCount,
+ .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
+ .storeOp = msaaEnabled ?
+ VK_ATTACHMENT_STORE_OP_DONT_CARE :
+ VK_ATTACHMENT_STORE_OP_STORE,
+ .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
+ .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
+ .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
+ .finalLayout = msaaEnabled ?
+ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL :
+ VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
+ }, {
+ /* Resolve image */
+ .format = surfaceFormat.format,
+ .samples = VK_SAMPLE_COUNT_1_BIT,
+ .loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
+ .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
+ .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
+ .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
+ .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
+ .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
+ }, {
+ /* Depth stencil image */
+ .format = depthFormat,
+ .samples = msaaSampleCount,
+ .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
+ .storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
+ .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
+ .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
+ .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
+ .finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL
+ }};
+
+ if (!msaaEnabled) {
+ attachments[1] = attachments[2];
+ }
+
+ const VkAttachmentReference colorAttachmentRef = {
+ .attachment = 0,
+ .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
+ };
+ const VkAttachmentReference resolveAttachmentRef = {
+ .attachment = 1,
+ .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
+ };
+ const VkAttachmentReference depthStencilAttachmentRef = {
+ .attachment = msaaEnabled ? 2 : 1,
+ .layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL
+ };
+
+ const VkSubpassDescription subpass = {
+ .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
+ .colorAttachmentCount = 1,
+ .pColorAttachments = &colorAttachmentRef,
+ .pResolveAttachments = msaaEnabled ? &resolveAttachmentRef : NULL,
+ .pDepthStencilAttachment = &depthStencilAttachmentRef
+ };
+
+ const VkRenderPassCreateInfo createInfo = {
+ .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
+ .pAttachments = attachments,
+ .subpassCount = 1,
+ .attachmentCount = msaaEnabled ? 3 : 2,
+ .pSubpasses = &subpass
+ };
+
+ VkResult result;
+ if ((result = vkCreateRenderPass(device, &createInfo, NULL, &renderPass))) {
+ ERRFQ("Could not create render pass: %d\n", result);
+ }
+}
+
+static void destroyRenderPass()
+{
+ vkDestroyRenderPass(device, renderPass, NULL);
+ renderPass = VK_NULL_HANDLE;
+}
+
+static VkFramebuffer *swapchainFramebuffers;
+
+static void createSwapchainFramebuffers()
+{
+ swapchainFramebuffers = malloc(nImages*sizeof(*swapchainFramebuffers));
+ for (uint32_t i = 0; i < nImages; ++i) {
+ const VkImageView attachments[] = {
+ msaaImageView,
+ swapchainImageViews[i],
+ depthImageView
+ };
+ const VkFramebufferCreateInfo createInfo = {
+ .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
+ .renderPass = renderPass,
+ .attachmentCount = msaaEnabled ? 3 : 2,
+ .pAttachments = msaaEnabled ? attachments : &attachments[1],
+ .width = swapchainExtent.width,
+ .height = swapchainExtent.height,
+ .layers = 1
+ };
+ VkResult result;
+ if ((result = vkCreateFramebuffer(device, &createInfo, NULL, &swapchainFramebuffers[i]))) {
+ ERRFQ("Could not create swapchainFramebuffers: %d\n", result);
+ }
+ }
+}
+
+static void destroySwapchainFramebuffers()
+{
+ for (uint32_t i = 0; i < nImages; ++i) {
+ vkDestroyFramebuffer(device, swapchainFramebuffers[i], NULL);
+ }
+ free(swapchainFramebuffers);
+ swapchainFramebuffers = NULL;
+}
+
+void initVulkan()
+{
+ createInstance();
+#ifdef VULKAN_VALIDATION
+ createDebugReporter();
+#endif
+#if 0
+ if (!SDL_Vulkan_CreateSurface(win, instance, &surface)) {
+ ERRQ(SDL_GetError());
+ }
+#else
+#include "pugl/detail/x11.h"
+ // HACK: Vulkan needs access to this
+ const VkXlibSurfaceCreateInfoKHR createInfo = {
+ .sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR,
+ .dpy = ((struct PuglWorldImpl*)world)->impl->display,
+ .window = puglGetNativeWindow(view)
+ };
+ VkResult result;
+ if ((result = vkCreateXlibSurfaceKHR(instance, &createInfo, NULL, &surface))) {
+ ERRFQ("Could not create Xlib surface: %d\n", result);
+ }
+#endif
+ selectPhysicalDevice(NULL);
+ createLogicalDevice();
+ vkGetDeviceQueue(device, transferIndex, 0, &transferQueue);
+ vkGetDeviceQueue(device, graphicsIndex, 0, &graphicsQueue);
+ createCommandPools();
+}
+
+void deinitVulkan()
+{
+ destroyCommandPools();
+ destroyLogicalDevice();
+ vkDestroySurfaceKHR(instance, surface, NULL);
+#ifdef VULKAN_VALIDATION
+ destroyDebugReportCallback(instance, callback, NULL);
+#endif
+}
+
+void beginVulkan()
+{
+ createSwapchain();
+ createSwapchainImageViews();
+ allocateCommandBuffers();
+ createDepthImage();
+ if (msaaEnabled) createMultisampleImage();
+ createRenderPass();
+ createSwapchainFramebuffers();
+}
+
+void endVulkan()
+{
+ destroySwapchainFramebuffers();
+ destroyRenderPass();
+ if (msaaEnabled) destroyMultisampleImage();
+ destroyDepthImage();
+ destroySwapchainImageViews();
+ destroySwapchain();
+}
+
+static struct Uniform uniformData;
+static VkBuffer uniformBuffer;
+//static VkDeviceMemory uniformMemory;
+static VmaAllocation uniformVma;
+VkDeviceSize uniformAlignment;
+
+static void createUniformBuffer()
+{
+ const VkDeviceSize alignMask = deviceProperties.limits.minUniformBufferOffsetAlignment - 1;
+ uniformAlignment = (sizeof(uniformData) + alignMask) & ~alignMask;
+ printf("Uniform Alignment:\t%lu\n", uniformAlignment);
+ const VkBufferCreateInfo createInfo = {
+ .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
+ .usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+ .size = nImages*uniformAlignment,
+ .sharingMode = VK_SHARING_MODE_EXCLUSIVE
+ };
+ const VmaAllocationCreateInfo allocInfo = {
+ //.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT,
+ .usage = VMA_MEMORY_USAGE_CPU_TO_GPU
+ };
+ VkResult result;
+ if ((result = vmaCreateBuffer(vmaAllocator, &createInfo, &allocInfo, &uniformBuffer, &uniformVma, NULL))) {
+ ERRFQ("Could not create uniform buffer: %d\n", result);
+ }
+#if 0
+ VkResult result;
+ if ((result = vkCreateBuffer(device, &createInfo, NULL, &uniformBuffer))) {
+ ERRFQ("Could not create uniform buffer: %d\n", result);
+ }
+
+ /* TODO: Figure best memory properties for uniform data */
+ VkMemoryRequirements memReq;
+ vkGetBufferMemoryRequirements(device, uniformBuffer, &memReq);
+ const uint32_t props = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
+ const uint32_t memType = getMemType(&memReq, props);
+
+ const VkMemoryAllocateInfo allocInfo = {
+ .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
+ .allocationSize = memReq.size,
+ .memoryTypeIndex = memType
+ };
+
+ if ((result = vkAllocateMemory(device, &allocInfo, NULL, &uniformMemory))) {
+ ERRFQ("Could not allocate uniform buffer memory: %d\n", result);
+ }
+
+ if ((result = vkBindBufferMemory(device, uniformBuffer, uniformMemory, 0))) {
+ ERRFQ("Could not bind uniform buffer memory: %d\n", result);
+ }
+#endif
+}
+
+static void destroyUniformBuffer()
+{
+ //vkFreeMemory(device, uniformMemory, NULL);
+ //vkDestroyBuffer(device, uniformBuffer, NULL);
+ vmaDestroyBuffer(vmaAllocator, uniformBuffer, uniformVma);
+}
+
+static void populateUniformBuffer()
+{
+ load_identity(uniformData.model);
+ load_identity(uniformData.view);
+ load_perspective(uniformData.proj, DEGREES(45), 16.0f/9.0f, 0.1f, 10.0f);
+ /* Could map and copy into the GPU here but we could also do that per-frame. */
+}
+
+static VkDescriptorPool descriptorPool;
+
+static void createDescriptorPool()
+{
+ const VkDescriptorPoolSize poolSize = {
+ .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
+ .descriptorCount = 1
+ };
+ const VkDescriptorPoolCreateInfo createInfo = {
+ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
+ .poolSizeCount = 1,
+ .pPoolSizes = &poolSize,
+ .maxSets = 1
+ };
+ VkResult result;
+ if ((result = vkCreateDescriptorPool(device, &createInfo, NULL, &descriptorPool))) {
+ ERRFQ("Could not create descriptor pool: %d\n", result);
+ }
+}
+
+static void destroyDescriptorPool()
+{
+ vkDestroyDescriptorPool(device, descriptorPool, NULL);
+}
+
+static VkDescriptorSetLayout descriptorSetLayout;
+
+static void createDescriptorSetLayout()
+{
+ const VkDescriptorSetLayoutBinding layoutBinding = {
+ .binding = 0,
+ .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
+ .descriptorCount = 1,
+ .stageFlags = VK_SHADER_STAGE_VERTEX_BIT
+ };
+ const VkDescriptorSetLayoutCreateInfo createInfo = {
+ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
+ .bindingCount = 1,
+ .pBindings = &layoutBinding
+ };
+ VkResult result;
+ if ((result = vkCreateDescriptorSetLayout(device, &createInfo, NULL, &descriptorSetLayout))) {
+ ERRFQ("Could not create descriptor set layout: %d\n", result);
+ }
+}
+
+static void destroyDescriptorSetLayout()
+{
+ vkDestroyDescriptorSetLayout(device, descriptorSetLayout, NULL);
+}
+
+static VkDescriptorSet descriptorSet;
+
+static void allocateDescriptorSets()
+{
+ const VkDescriptorSetAllocateInfo allocInfo = {
+ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
+ .descriptorPool = descriptorPool,
+ .descriptorSetCount = 1,
+ .pSetLayouts = &descriptorSetLayout
+ };
+ VkResult result;
+ if ((result = vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet))) {
+ ERRFQ("Could not allocate descriptor set: %d\n", result);
+ }
+}
+
+static void updateDescriptorSets()
+{
+ const VkDescriptorBufferInfo bufferInfo = {
+ .buffer = uniformBuffer,
+ .offset = 0,
+ .range = sizeof(uniformData)
+ };
+
+ const VkWriteDescriptorSet descriptorWrite = {
+ .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
+ .dstSet = descriptorSet,
+ .dstBinding = 0,
+ .dstArrayElement = 0,
+ .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
+ .descriptorCount = 1,
+ .pBufferInfo = &bufferInfo
+ };
+
+ VkResult result;
+ vkUpdateDescriptorSets(device, 1, &descriptorWrite, 0, NULL);
+}
+
+static VkPipelineLayout pipelineLayout;
+
+static void createPipelineLayout()
+{
+ const VkPipelineLayoutCreateInfo createInfo = {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
+ .setLayoutCount = 1,
+ .pSetLayouts = &descriptorSetLayout,
+ };
+ VkResult result;
+ if ((result = vkCreatePipelineLayout(device, &createInfo, NULL, &pipelineLayout))) {
+ ERRFQ("Could not create pipeline layout: %d\n", result);
+ }
+}
+
+static void destroyPipelineLayout()
+{
+ vkDestroyPipelineLayout(device, pipelineLayout, NULL);
+}
+
+static VkShaderModule vShaderModule;
+static VkShaderModule fShaderModule;
+
+static void createShaderModules()
+{
+ size_t vertLen, fragLen;
+ uint8_t *vertCode = read_file("vert.spv", &vertLen);
+ if (!vertCode) {
+ ERRQ("Could not read vert.spv");
+ }
+ uint8_t *fragCode = read_file("frag.spv", &fragLen);
+ if (!fragCode) {
+ ERRQ("Could not read frag.spv");
+ }
+
+ VkShaderModuleCreateInfo createInfo = {
+ .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
+ .codeSize = vertLen,
+ .pCode = (void*)vertCode
+ };
+
+ VkResult result;
+ if ((result = vkCreateShaderModule(device, &createInfo, NULL, &vShaderModule))) {
+ ERRFQ("Could not create vertex shader module: %d\n", result);
+ }
+
+ createInfo.codeSize = fragLen;
+ createInfo.pCode = (void*)fragCode;
+ if ((result = vkCreateShaderModule(device, &createInfo, NULL, &fShaderModule))) {
+ ERRFQ("Could not create fragment shader module: %d\n", result);
+ }
+
+ free(vertCode);
+ free(fragCode);
+}
+
+static void destroyShaderModules()
+{
+ vkDestroyShaderModule(device, vShaderModule, NULL);
+ vkDestroyShaderModule(device, fShaderModule, NULL);
+}
+
+// TODO: These should be dynamically allocated as arrays maybe
+static VkSemaphore imageAvailableSemaphore;
+static VkSemaphore renderFinishedSemaphore;
+
+static VkFence *imageFences;
+
+static void createSyncObjects()
+{
+ VkResult result;
+ const VkSemaphoreCreateInfo createInfo = {
+ .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO
+ };
+ if ((result = vkCreateSemaphore(device, &createInfo, NULL, &imageAvailableSemaphore))) {
+ ERRFQ("Could not create image available semaphore: %d\n", result);
+ }
+ if ((result = vkCreateSemaphore(device, &createInfo, NULL, &renderFinishedSemaphore))) {
+ ERRFQ("Could not create render finished semaphore: %d\n", result);
+ }
+ const VkFenceCreateInfo fenceInfo = {
+ .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
+ .flags = VK_FENCE_CREATE_SIGNALED_BIT
+ };
+ imageFences = malloc(nImages*sizeof(VkFence*));
+ for (uint32_t i = 0; i < nImages; ++i) {
+ if ((result = vkCreateFence(device, &fenceInfo, NULL, &imageFences[i]))) {
+ ERRFQ("Could not create render finished fence: %d\n", result);
+ }
+ }
+}
+
+static void destroySyncObjects()
+{
+ vkDestroySemaphore(device, imageAvailableSemaphore, NULL);
+ vkDestroySemaphore(device, renderFinishedSemaphore, NULL);
+ if (imageFences) {
+ for (uint32_t i = 0; i < nImages; ++i) {
+ vkDestroyFence(device, imageFences[i], NULL);
+ }
+ free(imageFences);
+ imageFences = NULL;
+ }
+}
+
+static VkBuffer meshBuffer;
+static VkDeviceMemory meshMemory;
+
+struct VertexLayout {
+ float position[3];
+ float normal[3];
+ float uv[2];
+};
+
+static uint32_t vertexCount;
+static uint32_t indexCount;
+
+VkDeviceSize vertexSize;
+VkDeviceSize indexSize;
+
+VkDeviceSize indexOffset;
+VkIndexType indexType;
+
+static void createVertexBuffer()
+{
+ size_t rawsize;
+ void *rawData = read_file("export.jch", &rawsize);
+ if (!rawData) {
+ ERRFQ("Cannot open model file\n");
+ }
+ const struct {
+ uint32_t signature;
+ uint32_t numVerts;
+ uint32_t numPolys;
+ } *header = rawData;
+ if (header->signature != 0x0048434a) {
+ ERRFQ("File is not model file\n");
+ }
+ printf("Verts:\t%d\nPolys:\t%d\n", header->numVerts, header->numPolys);
+ vertexCount = header->numVerts;
+ indexCount = 3*header->numPolys;
+ vertexSize = vertexCount*sizeof(struct VertexLayout);
+ indexSize = vertexCount > 65536 ? 4*indexCount : 2*indexCount;
+ indexType = vertexCount > 65536 ? VK_INDEX_TYPE_UINT32 : VK_INDEX_TYPE_UINT16;
+ if ((vertexSize + indexSize + sizeof(*header)) != rawsize) {
+ ERRFQ("Model file is corrupt\n");
+ }
+ indexOffset = vertexSize;
+
+ /* Make a simple staging buffer large enough to hold both vertex and index data */
+ const VkDeviceSize stagingSize = vertexSize + indexSize;
+
+ VkBuffer stagingBuffer;
+ VkDeviceMemory stagingMemory;
+
+ VkBufferCreateInfo createInfo = {
+ .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
+ .usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
+ VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
+ VK_BUFFER_USAGE_TRANSFER_DST_BIT,
+ .size = vertexSize + indexSize,
+ .sharingMode = VK_SHARING_MODE_EXCLUSIVE // FIXME: Transfer ownership to graphics queue family?
+ };
+
+ VkResult result;
+ if ((result = vkCreateBuffer(device, &createInfo, NULL, &meshBuffer))) {
+ ERRFQ("Could not create mesh buffer: %d\n", result);
+ }
+
+ createInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
+ createInfo.size = stagingSize;
+
+ if ((result = vkCreateBuffer(device, &createInfo, NULL, &stagingBuffer))) {
+ ERRFQ("Could not create staging buffer: %d\n", result);
+ }
+
+ const uint32_t hostProps = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
+ const uint32_t deviceProps = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
+
+ VkMemoryRequirements meshMemReq;
+ vkGetBufferMemoryRequirements(device, meshBuffer, &meshMemReq);
+ uint32_t memType = getMemType(&meshMemReq, deviceProps);
+
+ VkMemoryAllocateInfo allocInfo = {
+ .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
+ .allocationSize = meshMemReq.size,
+ .memoryTypeIndex = memType
+ };
+
+ if ((result = vkAllocateMemory(device, &allocInfo, NULL, &meshMemory))) {
+ ERRFQ("Could not allocate mesh buffer memory: %d\n", result);
+ }
+
+ VkMemoryRequirements stagingMemReq;
+ vkGetBufferMemoryRequirements(device, stagingBuffer, &stagingMemReq);
+ memType = getMemType(&stagingMemReq, hostProps);
+ allocInfo.allocationSize = stagingMemReq.size;
+ allocInfo.memoryTypeIndex = memType;
+
+ if ((result = vkAllocateMemory(device, &allocInfo, NULL, &stagingMemory))) {
+ ERRFQ("Could not allocate staging buffer memory: %d\n", result);
+ }
+
+ if ((result = vkBindBufferMemory(device, meshBuffer, meshMemory, 0))) {
+ ERRFQ("Could not bind mesh buffer memory: %d\n", result);
+ }
+
+ if ((result = vkBindBufferMemory(device, stagingBuffer, stagingMemory, 0))) {
+ ERRFQ("Could not bind staging buffer memory: %d\n", result);
+ }
+
+ void *data;
+ vkMapMemory(device, stagingMemory, 0, stagingMemReq.size, 0, &data);
+ memcpy(data, rawData + 12, vertexSize + indexSize);
+ vkUnmapMemory(device, stagingMemory);
+
+ free(rawData);
+
+ const VkCommandBufferAllocateInfo copyAllocInfo = {
+ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
+ .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
+ .commandPool = transferPool,
+ .commandBufferCount = 1
+ };
+ VkCommandBuffer copyBuffer;
+ if ((result = vkAllocateCommandBuffers(device, &copyAllocInfo, &copyBuffer))) {
+ ERRFQ("Could not allocate copy command buffer %d\n", result);
+ }
+
+ const VkCommandBufferBeginInfo beginInfo = {
+ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
+ .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT
+ };
+ if ((result = vkBeginCommandBuffer(copyBuffer, &beginInfo))) {
+ ERRFQ("Could not begin recording copy command buffer %d\n", result);
+ }
+ const VkBufferCopy copyRegions[] = {
+ {
+ .srcOffset = 0,
+ .dstOffset = 0,
+ .size = vertexSize
+ }, {
+ .srcOffset = vertexSize,
+ .dstOffset = vertexSize,
+ .size = indexSize
+ }
+ };
+ vkCmdCopyBuffer(copyBuffer, stagingBuffer, meshBuffer, 2, copyRegions);
+ vkEndCommandBuffer(copyBuffer);
+
+ const VkSubmitInfo submitInfo = {
+ .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
+ .commandBufferCount = 1,
+ .pCommandBuffers = &copyBuffer
+ };
+ vkQueueSubmit(transferQueue, 1, &submitInfo, VK_NULL_HANDLE);
+ vkQueueWaitIdle(transferQueue);
+
+ /* Data is guaranteed to be fully written here.
+ * If we didn't choose to synchronize here to free the command buffer
+ * right away, then we would need to wait on a Fence or Semaphore
+ * before touching it later on.
+ */
+ vkFreeCommandBuffers(device, transferPool, 1, &copyBuffer);
+ vkFreeMemory(device, stagingMemory, NULL);
+ vkDestroyBuffer(device, stagingBuffer, NULL);
+}
+
+static void destroyVertexBuffer()
+{
+ vkDestroyBuffer(device, meshBuffer, NULL);
+ vkFreeMemory(device, meshMemory, NULL);
+}
+
+static VkPipeline graphicsPipeline;
+
+static void createGraphicsPipeline()
+{
+ const VkPipelineShaderStageCreateInfo shaderStages[] = {{
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
+ .stage = VK_SHADER_STAGE_VERTEX_BIT,
+ .module = vShaderModule,
+ .pName = "main"
+ }, {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
+ .stage = VK_SHADER_STAGE_FRAGMENT_BIT,
+ .module = fShaderModule,
+ .pName = "main"
+ }};
+
+ const VkVertexInputBindingDescription bindingDescription = {
+ .binding = 0,
+ .stride = sizeof(struct VertexLayout),
+ .inputRate = VK_VERTEX_INPUT_RATE_VERTEX
+ };
+
+ const VkVertexInputAttributeDescription attribDescriptions[3] = {
+ {
+ .binding = 0,
+ .location = 0,
+ .format = VK_FORMAT_R32G32B32_SFLOAT,
+ .offset = offsetof(struct VertexLayout, position)
+ }, {
+ .binding = 0,
+ .location = 1,
+ .format = VK_FORMAT_R32G32B32_SFLOAT,
+ .offset = offsetof(struct VertexLayout, normal)
+ }, {
+ .binding = 0,
+ .location = 2,
+ .format = VK_FORMAT_R32G32_SFLOAT,
+ .offset = offsetof(struct VertexLayout, uv)
+ }};
+
+ const VkPipelineVertexInputStateCreateInfo vertexInputState = {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
+ .vertexBindingDescriptionCount = 1,
+ .pVertexBindingDescriptions = &bindingDescription,
+ .vertexAttributeDescriptionCount = 3,
+ .pVertexAttributeDescriptions = attribDescriptions
+ };
+
+ const VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
+ .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
+ .primitiveRestartEnable = VK_FALSE
+ };
+
+ const VkViewport viewport = {
+ .x = 0.0f,
+ .y = 0.0f,
+ .width = swapchainExtent.width,
+ .height = swapchainExtent.height,
+ .minDepth = 0.0f,
+ .maxDepth = 1.0f
+ };
+
+ const VkRect2D scissor = {
+ .offset = { 0, 0 },
+ .extent = swapchainExtent
+ };
+
+ const VkPipelineViewportStateCreateInfo viewportState = {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
+ .viewportCount = 1,
+ .pViewports = &viewport,
+ .scissorCount = 1,
+ .pScissors = &scissor
+ };
+
+ const VkPipelineRasterizationStateCreateInfo rasterizationState = {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
+ .depthClampEnable = VK_FALSE,
+ .rasterizerDiscardEnable = VK_FALSE,
+ .polygonMode = VK_POLYGON_MODE_FILL,
+ .lineWidth = 1.0f,
+ .cullMode = VK_CULL_MODE_BACK_BIT,
+ .frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE,
+ .depthBiasEnable = VK_FALSE
+ };
+
+ const VkPipelineMultisampleStateCreateInfo multisampleState = {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
+ .rasterizationSamples = msaaSampleCount,
+ .sampleShadingEnable = VK_FALSE
+ };
+
+ VkPipelineDepthStencilStateCreateInfo depthStencilState = {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
+ .depthTestEnable = VK_TRUE,
+ .depthWriteEnable = VK_TRUE,
+ .depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL,
+ .front = {
+ .failOp = VK_STENCIL_OP_KEEP,
+ .passOp = VK_STENCIL_OP_KEEP,
+ .compareOp = VK_COMPARE_OP_ALWAYS,
+ .compareMask = 0,
+ .reference = 0,
+ .depthFailOp = VK_STENCIL_OP_KEEP,
+ .writeMask = 0
+ }
+ };
+ depthStencilState.back = depthStencilState.front;
+
+ const VkPipelineColorBlendAttachmentState colorBlendAttachment = {
+ .colorWriteMask = 0xf,
+ .blendEnable = VK_FALSE,
+ };
+
+ const VkPipelineColorBlendStateCreateInfo colorBlendState = {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
+ .logicOpEnable = VK_FALSE,
+ .logicOp = VK_LOGIC_OP_COPY,
+ .attachmentCount = 1,
+ .pAttachments = &colorBlendAttachment,
+ .blendConstants[0] = 0.0f,
+ .blendConstants[1] = 0.0f,
+ .blendConstants[2] = 0.0f,
+ .blendConstants[3] = 0.0f
+ };
+
+ const VkGraphicsPipelineCreateInfo createInfo = {
+ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
+ .stageCount = 2,
+ .pStages = shaderStages,
+ .pVertexInputState = &vertexInputState,
+ .pInputAssemblyState = &inputAssemblyState,
+ .pTessellationState = NULL,
+ .pViewportState = &viewportState,
+ .pRasterizationState = &rasterizationState,
+ .pMultisampleState = &multisampleState,
+ .pDepthStencilState = &depthStencilState,
+ .pColorBlendState = &colorBlendState,
+ .pDynamicState = NULL,
+ .layout = pipelineLayout,
+ .renderPass = renderPass,
+ .subpass = 0,
+ .basePipelineHandle = VK_NULL_HANDLE,
+ .basePipelineIndex = 0
+ };
+
+ VkResult result;
+ if ((result = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &createInfo, NULL, &graphicsPipeline))) {
+ ERRFQ("Could not create graphics pipeline: %d\n", result);
+ }
+}
+
+static void destroyGraphicsPipeline()
+{
+ vkDestroyPipeline(device, graphicsPipeline, NULL);
+ graphicsPipeline = VK_NULL_HANDLE;
+}
+
+void setupScene()
+{
+ createUniformBuffer();
+ createDescriptorPool();
+ createDescriptorSetLayout();
+ allocateDescriptorSets();
+ updateDescriptorSets();
+ createPipelineLayout();
+ createShaderModules();
+ createSyncObjects();
+ createVertexBuffer();
+ createGraphicsPipeline();
+}
+
+void teardownScene()
+{
+ destroyGraphicsPipeline();
+ destroyVertexBuffer();
+ destroySyncObjects();
+ destroyShaderModules();
+ destroyPipelineLayout();
+ destroyDescriptorSetLayout();
+ destroyDescriptorPool();
+ destroyUniformBuffer();
+}
+
+void recordCommandBuffers()
+{
+ VkClearValue clearValues[3] = {{
+ .color.float32[0] = 0.0f,
+ .color.float32[1] = 0.0f,
+ .color.float32[2] = 0.0f,
+ .color.float32[3] = 1.0f,
+ }, {
+ .color.float32[0] = 0.0f,
+ .color.float32[1] = 0.0f,
+ .color.float32[2] = 0.0f,
+ .color.float32[3] = 1.0f,
+ }, {
+ .depthStencil.depth = 1.0f,
+ .depthStencil.stencil = 0
+ }};
+
+ if (!msaaEnabled) {
+ clearValues[1] = clearValues[2];
+ }
+
+ for (uint32_t i = 0; i < nImages; ++i) {
+ const VkCommandBufferBeginInfo beginInfo = {
+ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
+ .flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT,
+ .pInheritanceInfo = NULL
+ };
+ vkBeginCommandBuffer(commandBuffers[i], &beginInfo);
+
+ const VkRenderPassBeginInfo renderPassBeginInfo = {
+ .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
+ .renderPass = renderPass,
+ .framebuffer = swapchainFramebuffers[i],
+ .renderArea.offset = { 0, 0 },
+ .renderArea.extent = swapchainExtent,
+ .clearValueCount = msaaEnabled ? 3 : 2,
+ .pClearValues = clearValues
+ };
+
+ vkCmdBeginRenderPass(commandBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
+ vkCmdBindPipeline(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline);
+ const uint32_t dynamicOffsets[] = { i*uniformAlignment };
+ vkCmdBindDescriptorSets(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS,
+ pipelineLayout, 0, 1, &descriptorSet, 1, dynamicOffsets);
+ const VkDeviceSize vertexOffsets[] = { 0 };
+ vkCmdBindVertexBuffers(commandBuffers[i], 0, 1, &meshBuffer, vertexOffsets);
+ vkCmdBindIndexBuffer(commandBuffers[i], meshBuffer, indexOffset, indexType);
+ /*vkCmdDraw(commandBuffers[i], vertexCount, 1, 0, 0);*/
+ vkCmdDrawIndexed(commandBuffers[i], indexCount, 1, 0, 0, 0);
+ vkCmdEndRenderPass(commandBuffers[i]);
+
+ VkResult result;
+ if ((result = vkEndCommandBuffer(commandBuffers[i]))) {
+ ERRFQ("Could not record command buffer: %d\n", result);
+ }
+ }
+}
+
+// FIXME: Memory leak
+void recreateSwapchain()
+{
+ oldRawSwapchain = rawSwapchain;
+ destroyGraphicsPipeline();
+
+ // endVulkan();
+ destroySwapchainFramebuffers();
+ destroyRenderPass();
+ if (msaaEnabled) destroyMultisampleImage();
+ destroyDepthImage();
+ destroySwapchainImageViews();
+ //destroySwapchain();
+
+ // beginVulkan();
+ createSwapchain();
+ createSwapchainImageViews();
+ allocateCommandBuffers();
+ createDepthImage();
+ if (msaaEnabled) createMultisampleImage();
+ createRenderPass();
+ createSwapchainFramebuffers();
+
+ createGraphicsPipeline();
+
+ recordCommandBuffers();
+}
+
+float angle = 0.0f;
+uint32_t syncIndex;
+float location[4][4];
+void *data;
+uint32_t imageIndex;
+
+PuglStatus onResize(PuglView *view)
+{
+ //vkDeviceWaitIdle(device);
+ //SDL_Vulkan_GetDrawableSize(win, &width, &height);
+ //int width, height;
+ //getFrame(view, &width, &height);
+ recreateSwapchain();
+ /* Adjust aspect ratio */
+ load_perspective(
+ uniformData.proj,
+ DEGREES(45),
+ (float)swapchainExtent.width/swapchainExtent.height, 0.1f, 10.0f
+ );
+}
+
+PuglStatus onDisplay(PuglView *view)
+{
+ float rotation[4][4];
+ //angle += PI/60.0f;
+ //if (angle > TWOPI) {
+ // angle -= TWOPI;
+ //}
+ load_rotation_y(rotation, angle);
+ matmul4(location, rotation, uniformData.model);
+
+ VkResult result;
+retry: switch(vkAcquireNextImageKHR(
+ device,
+ rawSwapchain,
+ UINT64_MAX,
+ imageAvailableSemaphore,
+ VK_NULL_HANDLE,
+ &imageIndex
+ )) {
+ case VK_SUCCESS:
+ break;
+ case VK_SUBOPTIMAL_KHR:
+ case VK_ERROR_OUT_OF_DATE_KHR:
+ printf("REACTIVE resize\n");
+ onResize(view);
+ goto retry;
+ default:
+ fprintf(stderr, "vkAcquireNextImageKHR: %d\n", result);
+ exit(1);
+ }
+ vkWaitForFences(device, 1, &imageFences[imageIndex], VK_TRUE, UINT64_MAX);
+ memcpy(data + imageIndex*uniformAlignment, &uniformData, sizeof(uniformData));
+ //VkMappedMemoryRange uniformRange = {
+ // .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
+ // .memory = uniformMemory,
+ // .offset = imageIndex*uniformAlignment,
+ // .size = sizeof(uniformData)
+ //};
+ //vkFlushMappedMemoryRanges(device, 1, &uniformRange);
+ vmaFlushAllocation(vmaAllocator, uniformVma, imageIndex*uniformAlignment, sizeof(uniformData));
+ vkResetFences(device, 1, &imageFences[imageIndex]);
+ VkPipelineStageFlags waitStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ const VkSubmitInfo submitInfo = {
+ .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
+ .waitSemaphoreCount = 1,
+ .pWaitSemaphores = &imageAvailableSemaphore,
+ .pWaitDstStageMask = &waitStage,
+ .commandBufferCount = 1,
+ .pCommandBuffers = &commandBuffers[imageIndex],
+ .signalSemaphoreCount = 1,
+ .pSignalSemaphores = &renderFinishedSemaphore
+ };
+ if ((result = vkQueueSubmit(graphicsQueue, 1, &submitInfo, imageFences[imageIndex]))) {
+ ERRFQ("Could not submit draw command buffer: %d\n", result);
+ }
+
+ const VkPresentInfoKHR presentInfo = {
+ .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
+ .waitSemaphoreCount = 1,
+ .pWaitSemaphores = &renderFinishedSemaphore,
+ .swapchainCount = 1,
+ .pSwapchains = &rawSwapchain,
+ .pImageIndices = &imageIndex,
+ .pResults = NULL
+ };
+ vkQueuePresentKHR(graphicsQueue, &presentInfo);
+ //printf("I:\t%d\tS:\t%d\n", imageIndex, syncIndex);
+ syncIndex = (syncIndex+1) >= nImages ? 0 : (syncIndex+1);
+ //vkQueueWaitIdle(graphicsQueue);
+ return PUGL_SUCCESS;
+}
+
+void cleanup()
+{
+ vkDeviceWaitIdle(device);
+ //vkUnmapMemory(device, uniformMemory);
+ vmaUnmapMemory(vmaAllocator, uniformVma);
+
+ teardownScene();
+ endVulkan();
+ deinitVulkan();
+ puglFreeView(view);
+ puglFreeWorld(world);
+
+ /*
+ * Vulkan library MUST be unloaded AFTER call to XCloseDisplay() or it will segfault
+ */
+ vkDestroyInstance(instance, NULL);
+}
+
+int shouldResize(PuglView *view, const PuglEvent *e)
+{
+ if (swapchainExtent.width != e->configure.width ||
+ swapchainExtent.height != e->configure.height) {
+ return 1;
+ }
+ return 0;
+}
+
+void transformCoords(const PuglView *v, const PuglEvent *e, float *x, float *y)
+{
+ const PuglRect rect = puglGetFrame(v);
+ const double nx = e->motion.x/rect.width;
+ const double ny = e->motion.y/rect.height;
+ *x = 2.0*(nx-0.5);
+ *y = 2.0*(ny-0.5);
+}
+
+int running = 1;
+
+PuglStatus onEvent(PuglView *view, const PuglEvent *e)
+{
+ float mx, my, x, y;
+ const float z = -4.0f;
+ switch (e->type) {
+ case PUGL_CONFIGURE:
+ if (shouldResize(view, e)) onResize(view);
+ break;
+ case PUGL_EXPOSE:
+ onDisplay(view);
+ break;
+ case PUGL_CLOSE:
+ running = 0;
+ break;
+ case PUGL_KEY_PRESS:
+ break;
+ case PUGL_MOTION_NOTIFY:
+ transformCoords(view, e, &mx, &my);
+ x = -mx*z/uniformData.proj[0][0];
+ y = -my*z*(uniformData.proj[0][0]/uniformData.proj[1][1]);
+ load_location(location, x, y, z);
+ puglPostRedisplay(view);
+ break;
+ default:
+ break;
+ }
+ return PUGL_SUCCESS;
+}
+
+int main(int argc, char **argv)
+{
+ //if (SDL_Init(SDL_INIT_EVERYTHING) < 0) {
+ // ERRQ(SDL_GetError());
+ //}
+
+ world = puglNewWorld();
+ if (!world) {
+ ERRQ("Could not create world\n");
+ }
+ view = puglNewView(world);
+ if (!view) {
+ ERRQ("Could not create view\n");
+ }
+ puglSetClassName(world, "Pugl Vulkan");
+ const PuglRect frame = { 0, 0, 1280, 720 };
+ puglSetFrame(view, frame);
+ puglSetBackend(view, NULL);
+ puglSetViewHint(view, PUGL_RESIZABLE, 1);
+ PuglStatus status;
+ if ((status = puglCreateWindow(view, "Blank Window"))) {
+ fprintf(stderr, "Could not create window: %d\n", status);
+ exit(1);
+ }
+ puglShowWindow(view);
+ //puglPollEvents(world, -1);
+
+ //SDL_Vulkan_GetDrawableSize(win, &width, &height);
+ int width, height;
+ getFrame(view, &width, &height);
+ initVulkan();
+ beginVulkan(width, height);
+ setupScene();
+ populateUniformBuffer();
+
+ recordCommandBuffers();
+
+ load_location(location, 0.0f, 0.5f, -4.0f);
+
+ //vkMapMemory(device, uniformMemory, 0, nImages*uniformAlignment, 0, &data);
+ vmaMapMemory(vmaAllocator, uniformVma, &data);
+
+ puglSetEventFunc(view, onEvent);
+
+ while (running) {
+ puglPollEvents(world, -1);
+ puglDispatchEvents(world);
+ }
+quit:
+ cleanup();
+ //SDL_Quit();
+ return 0;
+}