From 073ff1de55b0d380eedd31c61bb9eaae6551ba7c Mon Sep 17 00:00:00 2001 From: Jordan Halase Date: Sun, 3 Nov 2019 15:01:30 -0600 Subject: Overhaul --- main.c | 334 +++++++++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 201 insertions(+), 133 deletions(-) diff --git a/main.c b/main.c index 117d665..1cf5001 100644 --- a/main.c +++ b/main.c @@ -39,11 +39,10 @@ PERFORMANCE OF THIS SOFTWARE. #if defined(_WIN32) #include -#include // to implement puglCreateVulkanSurface() on Win32 +#include // XXX Pugl: to implement puglCreateVulkanSurface() on Win32 #else -#include -#include // to implement puglCreateVulkanSurface() on X11 -#include "pugl/detail/x11.h" // to implement puglCreateVulkanSurface() on X11 +#include // XXX Pugl: to implement puglCreateVulkanSurface() on X11 +#include // XXX Pugl: to implement puglCreateVulkanSurface() on X11 #endif #include "test/test_utils.h" // for printEvent() @@ -120,6 +119,7 @@ struct VulkanDev { PFN_vkQueuePresentKHR vkQueuePresentKHR; }; +/** Application-specific swapchain behavior */ struct SwapchainVulkan { VkSwapchainKHR rawSwapchain; VkSurfaceFormatKHR surfaceFormat; @@ -129,23 +129,34 @@ struct SwapchainVulkan { VkImageView *imageViews; }; +struct SemaphoreVulkan { + VkSemaphore imageAvailable; + VkSemaphore renderFinished; +}; + +struct FenceVulkan { + VkFence *swapchain; // Fences for each swapchain image +}; + +struct SyncVulkan { + struct SemaphoreVulkan semaphore; + struct FenceVulkan fence; +}; + /** Vulkan application that uses Pugl for windows and events * * TODO: Separate Instance from rest of application. - * All Pugl stuff must be destroyed AFTER freeing the surface but BEFORE - * freeing the instance. Right now we are forced to destroy them inside of - * `rvkDestroy` even though the user creates them. */ struct RenderVulkan { struct VulkanAPI *api; struct VulkanDev *dev; + char *errMsg; PuglWorld *world; PuglView *view; - char *errMsg; VkInstance instance; VkDebugReportCallbackEXT debugCallback; VkSurfaceKHR surface; - VkPhysicalDeviceProperties deviceProperties; // TODO: Put this on the heap + VkPhysicalDeviceProperties deviceProperties; // TODO: Make this a pointer. It's really big. VkPhysicalDevice physicalDevice; uint32_t graphicsIndex; VkDevice device; @@ -153,15 +164,7 @@ struct RenderVulkan { VkCommandPool commandPool; struct SwapchainVulkan swapchain; VkCommandBuffer *commandBuffers; - struct SyncVulkan { - struct SemaphoreVulkan { - VkSemaphore imageAvailable; - VkSemaphore renderFinished; - } semaphore; - struct FenceVulkan { - VkFence *swapchain; - } fence; - } sync; + struct SyncVulkan sync; }; #define RVK_ERRMSG_LEN 4096 @@ -216,15 +219,15 @@ static inline int appDlclose(void *handle) return FreeLibrary(handle); } -PuglStatus getRequiredInstanceExtensions(PuglWorld *world, +/* XXX: puglGetRequiredInstanceExtensions() implementation for Win32 + * "Get the platform-specific names of the required instance-level + * extensions if you want to display your images to the screen." + * Reminder that Vulkan is off-screen by default. + */ +void getRequiredInstanceExtensions( uint32_t *pExtensionCount, const char **const ppExtensionNames) { - if (!world) { - if (pExtensionCount) *pExtensionCount = 0; - if (ppExtensionNames) *ppExtensionNames = NULL; - return PUGL_FAILURE; - } static const char *const required[] = { VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_WIN32_SURFACE_EXTENSION_NAME @@ -237,33 +240,40 @@ PuglStatus getRequiredInstanceExtensions(PuglWorld *world, } else { *pExtensionCount = num; } - return PUGL_SUCCESS; } +/* XXX: puglCreateVulkanSurface() implementation for Win32 + * + * Creating a surface is platform-specific and should be handled by Pugl + * with this function exposed publicly. + * + * No need to wrap vkFreeSurfaceKHR(). Creating a surface is platform + * specific, but freeing it is not. + */ VkResult createVulkanSurface(PuglView *view, VkInstance instance, PFN_vkGetInstanceProcAddr getInstanceProcAddrFunc, const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface) { - PuglWorld *world = ((struct PuglViewImpl*)view)->world; - VkWin32SurfaceCreateInfoKHR createInfo = { 0 }; createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; - createInfo.hinstance = GetModuleHandle(0); // FIXME (?) + //createInfo.hinstance = GetModuleHandle(0); // FIXME (?) + createInfo.hinstance = puglGetNativeWorld(view->world); createInfo.hwnd = puglGetNativeWindow(view); PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR = (PFN_vkCreateWin32SurfaceKHR)getInstanceProcAddrFunc( instance, "vkCreateWin32SurfaceKHR"); - return vkCreateWin32SurfaceKHR( - instance, &createInfo, pAllocator, pSurface); + return vkCreateWin32SurfaceKHR(instance, + &createInfo, + pAllocator, + pSurface); } #else #define VULKAN_SONAME_LATEST "libvulkan.so.1" #include -// XXX: puglDlopen() and friends static inline void *appDlopen(const char *soname) { return dlopen(soname, RTLD_NOW); @@ -284,30 +294,15 @@ static inline int appDlclose(void *handle) return dlclose(handle); } -/* XXX: puglGetRequiredInstanceExtensions() - * "Get the platform-specific names of the required instance-level +/* XXX: puglGetRequiredInstanceExtensions() implementation for Xlib + * "Get the platform-specific names of the required instance-level * extensions if you want to display your images to the screen." * Reminder that Vulkan is off-screen by default. - * - * TODO: Linux actually has three possible surfaces: Xlib, XCB, and Wayland. - * This should be figured out at runtime (without using #ifdefs). - * In which case, `world` will be used to determine which to use. - * As of now, Pugl (and LV2! suil, etc.) only supports Xlib, but the - * variable should be left in the function signature for forward - * compatibility. */ -PuglStatus getRequiredInstanceExtensions(PuglWorld *world, +void getRequiredInstanceExtensions( uint32_t *pExtensionCount, const char **const ppExtensionNames) { - if (!world) { - if (pExtensionCount) *pExtensionCount = 0; - if (ppExtensionNames) *ppExtensionNames = NULL; - return PUGL_FAILURE; - } - // TODO: if (world->system == X11) { ... } - // else if (world->system == Wayland) { ... } - // else ... static const char *const required[] = { VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_XLIB_SURFACE_EXTENSION_NAME @@ -320,10 +315,9 @@ PuglStatus getRequiredInstanceExtensions(PuglWorld *world, } else { *pExtensionCount = num; } - return PUGL_SUCCESS; } -/* XXX: puglCreateVulkanSurface() +/* XXX: puglCreateVulkanSurface() implementation for Xlib * * Creating a surface is platform-specific and should be handled by Pugl * with this function exposed publicly. @@ -337,11 +331,10 @@ VkResult createVulkanSurface(PuglView *view, const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface) { - PuglWorld *world = ((struct PuglViewImpl*)view)->world; - VkXlibSurfaceCreateInfoKHR createInfo = { 0 }; createInfo.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; - createInfo.dpy = ((struct PuglWorldImpl*)world)->impl->display; + //createInfo.dpy = ((struct PuglWorldImpl*)world)->impl->display; + createInfo.dpy = puglGetNativeWorld(view->world); createInfo.window = puglGetNativeWindow(view); PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR = @@ -392,7 +385,7 @@ static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback( return VK_FALSE; } -static VkResult rvkCreateInstance( +static VkResult rvkCreateInternalInstance( struct RenderVulkan *vk, const uint32_t nLayers, const char *const *const layers, @@ -409,12 +402,12 @@ static VkResult rvkCreateInstance( appInfo.apiVersion = VK_MAKE_VERSION(1, 0, 0); uint32_t i, j, nRequired; - getRequiredInstanceExtensions(vk->world, &nRequired, NULL); + getRequiredInstanceExtensions(&nRequired, NULL); const uint32_t nExtensions = nRequired + nAdditional; const char **const extensions = malloc(sizeof(const char*) * nExtensions); - getRequiredInstanceExtensions(vk->world, NULL, extensions); + getRequiredInstanceExtensions(NULL, extensions); for (i = nRequired, j = 0; i < nExtensions; ++i, ++j) { extensions[i] = additionalExtensions[j]; } @@ -443,35 +436,18 @@ static VkResult rvkCreateInstance( return result; } -/** Must not be called until all derivative objects are destroyed first */ -static void rvkDestroyInstance(struct RenderVulkan *vk) +static void rvkDestroyInternalInstance(struct RenderVulkan *vk) { vk->api->vkDestroyInstance(vk->instance, ALLOC_VK); vk->instance = VK_NULL_HANDLE; } -void rvkDestroy(struct RenderVulkan *vk); - /** Create a self-contained Vulkan instance and set up a debug reporter * - * XXX - * - * At the moment `world` is not used for anything, but if/when Pugl supports - * dynamically choosing a backend (X11, XCB, Wayland), it is anticipated that - * `world` will contain information regarding which is in use, which will then - * be used by `createVulkanSurface`. - * - * If creating a surface is handled by Pugl (`puglCreateVulkanSurface`), then - * `PuglWorld` can remain completely opaque to the application. - * - * `world` is NOT internally managed by the application. It MUST be destroyed - * separately. - * - * If errors occurred, the struct will be returned in an unusable state, - * and MUST be checked via `rvkGetErrMsg`. It MUST then be destroyed via - * `rvkDestroy`. + * If errors occurred, the struct will be returned in an unusable state. + * It MUST then be destroyed via `rvkDestroyWorld`. * */ -int rvkCreate(PuglWorld *world, struct RenderVulkan **vkOut) +int rvkCreateWorld(struct RenderVulkan **vkOut) { static const char *const instanceLayers[] = { "VK_LAYER_KHRONOS_validation" @@ -484,13 +460,13 @@ int rvkCreate(PuglWorld *world, struct RenderVulkan **vkOut) struct RenderVulkan *vk = calloc(1, sizeof(*vk)); *vkOut = vk; - if (!world) { - rvkSetErrMsg(vk, "No PuglWorld provided"); + vk->world = puglNewWorld(); + if (!vk->world) { + rvkSetErrMsg(vk, "Could not create Pugl world"); return -1; } - vk->world = world; - vk->api = calloc(1, sizeof(*vk->api)); + vk->api = calloc(1, sizeof(*vk->api)); vk->api->handle = loadVulkanLibrary(); if (!vk->api->handle) { rvkSetErrMsg(vk, "Error loading Vulkan shared library:\n%s\n", appDlerror()); @@ -511,8 +487,8 @@ int rvkCreate(PuglWorld *world, struct RenderVulkan **vkOut) } VkResult result; - //if ((result = rvkCreateInstance(vk, 0, NULL, 0, NULL))) { - if ((result = rvkCreateInstance(vk, nInstanceLayers, instanceLayers, nInstanceExtensions, instanceExtensions))) { + //if ((result = rvkCreateInternalInstance(vk, 0, NULL, 0, NULL))) { + if ((result = rvkCreateInternalInstance(vk, nInstanceLayers, instanceLayers, nInstanceExtensions, instanceExtensions))) { return -1; } @@ -542,8 +518,6 @@ int rvkCreate(PuglWorld *world, struct RenderVulkan **vkOut) /* Dynamically load instance-level Vulkan function pointers via `vkGetInstanceProcAddr` * * TODO: This could perhaps be generated by a script and put into a separate "loader" file - * - * Casts to `uintptr_t` as an intermediary to suppress warnings when compiling with `-Wpedantic` */ static const char *const strDestroySurfaceKHR = "vkDestroySurfaceKHR"; uintptr_t *ulDestroySurfaceKHR = (uintptr_t*)&vk->api->vkDestroySurfaceKHR; @@ -656,19 +630,62 @@ int rvkCreate(PuglWorld *world, struct RenderVulkan **vkOut) return 0; } -static int rvkCreateSurface(struct RenderVulkan *vk, PuglView *view) +/** Create the view, window, and surface for this application */ +int rvkCreateSurface(struct RenderVulkan *vk, const PuglRect frame) { + vk->view = puglNewView(vk->world); + if (!vk->view) { + rvkSetErrMsg(vk, "Could not create Pugl view"); + return -1; + } + puglSetFrame(vk->view, frame); + puglSetHandle(vk->view, vk); + puglSetBackend(vk->view, puglStubBackend()); + + PuglStatus status; + if ((status = puglCreateWindow(vk->view, "Vulkan Application Using Pugl"))) { + rvkSetErrMsg(vk, "Could not create window: %d\n", status); + return -1; + } + VkResult result; - if ((result = createVulkanSurface(view, vk->instance, vk->api->vkGetInstanceProcAddr, ALLOC_VK, &vk->surface))) { + if ((result = createVulkanSurface(vk->view, vk->instance, vk->api->vkGetInstanceProcAddr, ALLOC_VK, &vk->surface))) { rvkSetErrMsg(vk, "Could not create window surface: %d\n", result); return -1; } return 0; } +static void printQueueFamilyCapabilities(const VkQueueFlagBits queueFlags) +{ + const char *maybeOr = ""; + const char *const or = " | "; + if (queueFlags & VK_QUEUE_GRAPHICS_BIT) { + printf("%sGRAPHICS", maybeOr); + maybeOr = or; + } + if (queueFlags & VK_QUEUE_COMPUTE_BIT) { + printf("%sCOMPUTE", maybeOr); + maybeOr = or; + } + if (queueFlags & VK_QUEUE_TRANSFER_BIT) { + printf("%sTRANSFER", maybeOr); + maybeOr = or; + } + if (queueFlags & VK_QUEUE_SPARSE_BINDING_BIT) { + printf("%sSPARSE_BINDING", maybeOr); + maybeOr = or; + } + if (queueFlags & VK_QUEUE_PROTECTED_BIT) { + printf("%sPROTECTED", maybeOr); + maybeOr = or; + } + printf("\n"); +} + /** Checks if a particular physical device is suitable for this application. * - * This function is referentially transparent. + * This function is referentially transparent (aside from printing to stdout). * * Choosing a physical device is an *extremely* application-specific procedure. * @@ -698,9 +715,9 @@ static int rvkCreateSurface(struct RenderVulkan *vk, PuglView *view) * family. The maximum number of available queues is specific to * that device's queue families. Programmers making use of separate * queues for highly independent workloads may allow devices to work - * more efficiently. Queues must be used from a single thread, so - * programmers wanting to make use of multithreaded queue submission - * must retrieve queues per-thread. + * more efficiently. Queues must be submitted to from a single + * thread at a time, so programmers wanting to make use of + * multithreaded queue submission must retrieve queues per-thread. * * GOTCHA: Some devices may have multiple GRAPHICS queue families * with only one of them able to present. Do not assume a @@ -725,6 +742,10 @@ static bool rvkIsDeviceSuitable(const struct RenderVulkan *const vk, for (uint32_t i = 0; i < nQueueFamilies; ++i) { printf("Queue Family %d queueCount:\t%d\n", i, queueProperties[i].queueCount); } + for (uint32_t i = 0; i < nQueueFamilies; ++i) { + printf("Queue family %d queueFlags:\t", i); + printQueueFamilyCapabilities(queueProperties[i].queueFlags); + } uint32_t g; for (g = 0; g < nQueueFamilies; ++g) { if (queueProperties[g].queueFlags & VK_QUEUE_GRAPHICS_BIT) { @@ -812,7 +833,7 @@ int rvkSelectPhysicalDevice(struct RenderVulkan *vk) vk->deviceProperties = deviceProperties[i]; vk->physicalDevice = devices[i]; vk->graphicsIndex = graphicsIndex; - printf("Graphics Index:\t\t\t%d\n", vk->graphicsIndex); + printf("Using GRAPHICS family index:\t%d\n", vk->graphicsIndex); goto done; } printf("Device `%s` not suitable\n", deviceProperties[i].deviceName); @@ -1075,11 +1096,38 @@ int rvkOpenDevice(struct RenderVulkan *vk) return 0; } +static char *strPresentMode(const VkPresentModeKHR presentMode) +{ + switch (presentMode) { + case VK_PRESENT_MODE_IMMEDIATE_KHR: + return "Immediate"; + case VK_PRESENT_MODE_MAILBOX_KHR: + return "Mailbox"; + case VK_PRESENT_MODE_FIFO_KHR: + return "FIFO"; + case VK_PRESENT_MODE_FIFO_RELAXED_KHR: + return "FIFO relaxed"; + default: + return "Other"; + } +} + +static bool isPresentModeSupported(const VkPresentModeKHR *const presentModes, + const uint32_t nPresentModes, const VkPresentModeKHR want) +{ + for (uint32_t i = 0; i < nPresentModes; ++i) { + if (presentModes[i] == want) { + return true; + } + } + return false; +} + static int rvkCreateRawSwapchain(struct RenderVulkan *vk, int width, int height) { VkSurfaceCapabilitiesKHR surfaceCapabilities; VkSurfaceFormatKHR *surfaceFormats; - VkPresentModeKHR *presentModes; // TODO + VkPresentModeKHR *presentModes; VkResult result; if ((result = vk->api->vkGetPhysicalDeviceSurfaceCapabilitiesKHR( vk->physicalDevice, vk->surface, &surfaceCapabilities))) { @@ -1126,8 +1174,31 @@ static int rvkCreateRawSwapchain(struct RenderVulkan *vk, int width, int height) return -1; } - // TODO - VkPresentModeKHR presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; + uint32_t nPresentModes; + vk->api->vkGetPhysicalDeviceSurfacePresentModesKHR(vk->physicalDevice, vk->surface, &nPresentModes, NULL); + if (!nPresentModes) { + rvkSetErrMsg(vk, "No present modes available"); + return -1; + } + presentModes = malloc(nPresentModes * sizeof(*presentModes)); + vk->api->vkGetPhysicalDeviceSurfacePresentModesKHR(vk->physicalDevice, + vk->surface, &nPresentModes, presentModes); + + VkPresentModeKHR presentMode; + for (i = 0; i < nPresentModes; ++i) { + presentMode = presentModes[i]; + printf("Found present mode:\t\t%s\t(%d)\n", strPresentMode(presentMode), presentMode); + } + + if (isPresentModeSupported(presentModes, nPresentModes, VK_PRESENT_MODE_MAILBOX_KHR)) { + presentMode = VK_PRESENT_MODE_MAILBOX_KHR; + } else if (isPresentModeSupported(presentModes, nPresentModes, VK_PRESENT_MODE_IMMEDIATE_KHR)) { + presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; + } else { + presentMode = VK_PRESENT_MODE_FIFO_KHR; + } + free(presentModes); + printf("Using present mode:\t\t%s\t(%d)\n", strPresentMode(presentMode), presentMode); // TODO: Clamp vk->swapchain.extent.width = width; @@ -1405,7 +1476,7 @@ static void rvkCloseDevice(struct RenderVulkan *vk) vk->dev->vkDestroyDevice(vk->device, ALLOC_VK); } else { /* This is only theoretical, because we must load the "destroy" - * function *after* we create the device + * function AFTER we create the device */ fprintf(stderr, "Fatal: Unable to destroy logical device (missing function pointer)\n"); } @@ -1416,7 +1487,7 @@ static void rvkCloseDevice(struct RenderVulkan *vk) } /** This must work no matter the current state of `vk` */ -void rvkDestroy(struct RenderVulkan *vk) +void rvkDestroyApplication(struct RenderVulkan *vk) { if (vk) { rvkCloseDevice(vk); @@ -1424,21 +1495,30 @@ void rvkDestroy(struct RenderVulkan *vk) vk->api->vkDestroySurfaceKHR(vk->instance, vk->surface, ALLOC_VK); vk->surface = VK_NULL_HANDLE; } - /* PuglView must be freed AFTER surface but BEFORE instance destroy */ if (vk->view) { + /* PuglView must be freed AFTER surface but BEFORE instance destroy */ puglFreeView(vk->view); vk->view = NULL; } + } +} + +/** This must be called AFTER `rvkDestroyApplication` if it was created */ +void rvkDestroyWorld(struct RenderVulkan *vk) +{ + if (vk) { + if (vk->world) { + /* PuglWorld must be unloaded AFTER PuglView but BEFORE instance destroy */ + puglFreeWorld(vk->world); + vk->world = NULL; + } if (vk->debugCallback) { /* `vk->debugCallback` implies `vk->api` and all instance functions loaded */ vk->api->vkDestroyDebugReportCallbackEXT(vk->instance, vk->debugCallback, ALLOC_VK); vk->debugCallback = VK_NULL_HANDLE; } - /* PuglWorld must be unloaded AFTER PuglView but BEFORE instance destroy */ - puglFreeWorld(vk->world); - vk->world = NULL; if (vk->instance) { - rvkDestroyInstance(vk); + rvkDestroyInternalInstance(vk); vk->instance = VK_NULL_HANDLE; } if (vk->api) { @@ -1462,10 +1542,8 @@ void rvkDestroy(struct RenderVulkan *vk) static void rvkFatal(struct RenderVulkan *vk) { fprintf(stderr, "%s\n", rvkGetErrMsg(vk)); - rvkDestroy(vk); - if (vk->world) { - puglFreeWorld(vk->world); - } + rvkDestroyApplication(vk); + rvkDestroyWorld(vk); printf("Aborting gracefully\n"); exit(1); } @@ -1482,6 +1560,7 @@ PuglStatus onDisplay(PuglView *view) uint32_t imageIndex; VkResult result; + if ((result = vk->dev->vkAcquireNextImageKHR( vk->device, vk->swapchain.rawSwapchain, @@ -1493,11 +1572,12 @@ PuglStatus onDisplay(PuglView *view) return PUGL_FAILURE; } - /* If running continuously, Vulkan can blast the GPU with rendering work - * faster than it can get around to presenting it, causing RAM usage to - * grow indefinitely. We use fences to limit the number of frames to the - * number of swapchain images. This will also be required later when - * flushing persistently mapped uniform buffer ranges anyway. + /* If running continuously, Vulkan can blast the queue with rendering + * work faster than the GPU can get around to doing it, causing RAM + * usage to grow indefinitely. We use fences to limit the number of + * submitted frames to the number of swapchain images. These fences will + * be required later anyway when flushing persistently mapped uniform + * buffer ranges. */ vk->dev->vkWaitForFences(vk->device, 1, &vk->sync.fence.swapchain[imageIndex], @@ -1561,27 +1641,14 @@ int main() /* The Mesa Vulkan drivers require this to be called */ XInitThreads(); #endif - PuglWorld *world = puglNewWorld(); - /* Vulkan application that uses Pugl for windows and events */ struct RenderVulkan *vk; - CHECK_RVK(vk, rvkCreate(world, &vk)); + CHECK_RVK(vk, rvkCreateWorld(&vk)); printf("Created Vulkan Instance Successfully\n"); - PuglView *view = puglNewView(world); - const PuglRect frame = { 0, 0, 800, 600 }; - puglSetFrame(view, frame); - puglSetHandle(view, vk); - puglSetBackend(view, puglStubBackend()); - PuglStatus status; - if ((status = puglCreateWindow(view, "Vulkan Application Using Pugl"))) { - fprintf(stderr, "Could not create window: %d\n", status); - rvkFatal(vk); - } - vk->view = view; - - CHECK_RVK(vk, rvkCreateSurface(vk, view)); + const PuglRect frame = { 0, 0, 800, 600 }; + CHECK_RVK(vk, rvkCreateSurface(vk, frame)); CHECK_RVK(vk, rvkSelectPhysicalDevice(vk)); CHECK_RVK(vk, rvkOpenDevice(vk)); CHECK_RVK(vk, rvkCreateSwapchain(vk, frame.width, frame.height)); @@ -1589,17 +1656,18 @@ int main() printf("Opened Vulkan Device Successfully\n"); - puglSetEventFunc(view, onEvent); + puglSetEventFunc(vk->view, onEvent); //PuglFpsPrinter fpsPrinter = { puglGetTime(world) }; - puglShowWindow(view); + puglShowWindow(vk->view); while (running) { - puglPollEvents(world, -1); + puglPollEvents(vk->world, -1); //puglPostRedisplay(view); - puglDispatchEvents(world); + puglDispatchEvents(vk->world); //puglPrintFps(world, &fpsPrinter, &framesDrawn); } - rvkDestroy(vk); + rvkDestroyApplication(vk); + rvkDestroyWorld(vk); printf("Exiting gracefully\n"); return 0; } -- cgit v1.2.1