From 8700e9a9bcab89de998d9e9b43372bc574c86cc0 Mon Sep 17 00:00:00 2001 From: Jordan Halase Date: Wed, 30 Oct 2019 16:54:54 -0500 Subject: Refactor --- CMakeLists.txt | 2 + main.c | 117 +++++++++++++++++++++++++++++++++++++-------------------- 2 files changed, 79 insertions(+), 40 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3192c6a..e63816d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,8 @@ cmake_minimum_required(VERSION 3.2) project(vkpugltest LANGUAGES C) +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g") + set(pugldir "../../pugl") set(PUGL_INCLUDE "${pugldir}") set(PUGL_LIBDIR "${pugldir}/build") diff --git a/main.c b/main.c index fb81e89..e0d2451 100644 --- a/main.c +++ b/main.c @@ -83,8 +83,10 @@ struct VulkanAPI { PFN_vkEnumerateDeviceExtensionProperties vkEnumerateDeviceExtensionProperties; }; +/** Vulkan application that uses Pugl for windows and events */ struct RenderVulkan { struct VulkanAPI *api; + PuglWorld *world; char *errMsg; VkInstance instance; VkDebugReportCallbackEXT debugCallback; @@ -121,12 +123,12 @@ const char *rvkGetErrMsg(struct RenderVulkan *vk) #if defined(_WIN32) #define VULKAN_SONAME_LATEST "vulkan-1.dll" -void *appDlopen(const char *soname) +static inline void *appDlopen(const char *soname) { return LoadLibraryA(soname); } -char *appDlerror() +static inline char *appDlerror() { // TODO: Print a more informative string. Error codes are annoying. DWORD errCode = GetLastError(); @@ -135,34 +137,39 @@ char *appDlerror() return errStr; } -void *appDlsym(void *handle, const char *symbol) +static inline void *appDlsym(void *handle, const char *symbol) { const uintptr_t ulAddr = (uintptr_t)GetProcAddress(handle, symbol); return (void*)ulAddr; } -int appDlclose(void *handle) +static inline int appDlclose(void *handle) { return FreeLibrary(handle); } -void getRequiredInstanceExtensions(PuglWorld *world, - unsigned *pExtensionCount, +PuglStatus getRequiredInstanceExtensions(PuglWorld *world, + uint32_t *pExtensionCount, const char **const ppExtensionNames) { - (void)world; + 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 }; - static const unsigned num = sizeof(required) / sizeof(required[0]); + static const uint32_t num = sizeof(required) / sizeof(required[0]); if (ppExtensionNames) { - for (int i = 0; i < num; ++i) { + for (uint32_t i = 0; i < num; ++i) { ppExtensionNames[i] = required[i]; } } else { *pExtensionCount = num; } + return PUGL_SUCCESS; } VkResult createVulkanSurface(PuglView *view, @@ -184,36 +191,31 @@ VkResult createVulkanSurface(PuglView *view, #else #define VULKAN_SONAME_LATEST "libvulkan.so.1" #include -// XXX: puglDlopen() -void *appDlopen(const char *soname) +// XXX: puglDlopen() and friends +static inline void *appDlopen(const char *soname) { return dlopen(soname, RTLD_NOW); } -char *appDlerror() +static inline char *appDlerror() { return dlerror(); } -void *appDlsym(void *handle, const char *symbol) +static inline void *appDlsym(void *handle, const char *symbol) { return dlsym(handle, symbol); } -int appDlclose(void *handle) +static inline int appDlclose(void *handle) { return dlclose(handle); } /* XXX: puglGetRequiredInstanceExtensions() - * This will return the platform-specific names of the instance level - * extensions required to display images to the screen. - * Vulkan is off-screen by default and can be used without presenting - * at all, so off-screen applications will not need to call this - * function. In other words: - * - * "Get the names of the required instance extensions if you want to - * display your images to the screen." + * "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). @@ -222,27 +224,38 @@ int appDlclose(void *handle) * variable should be left in the function signature for forward * compatibility. */ -void getRequiredInstanceExtensions(PuglWorld *world, - unsigned *pExtensionCount, +PuglStatus getRequiredInstanceExtensions(PuglWorld *world, + uint32_t *pExtensionCount, const char **const ppExtensionNames) { - (void)world; + 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 }; - static const unsigned num = sizeof(required) / sizeof(required[0]); + static const uint32_t num = sizeof(required) / sizeof(required[0]); if (ppExtensionNames) { - for (int i = 0; i < num; ++i) { + for (uint32_t i = 0; i < num; ++i) { ppExtensionNames[i] = required[i]; } } else { *pExtensionCount = num; } + return PUGL_SUCCESS; } /* XXX: puglCreateVulkanSurface() - * No need to wrap VkFreeSurfaceKHR() + * + * 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, @@ -312,13 +325,13 @@ static VkResult createInstance(struct RenderVulkan *vk, /* MoltenVK for macOS currently only supports Vulkan 1.0 */ appInfo.apiVersion = VK_MAKE_VERSION(1, 0, 0); - unsigned i, j, nRequired; - getRequiredInstanceExtensions(NULL, &nRequired, NULL); + uint32_t i, j, nRequired; + getRequiredInstanceExtensions(vk->world, &nRequired, NULL); const uint32_t nExtensions = nRequired + nAdditional; const char **const extensions = malloc(sizeof(const char*) * nExtensions); - getRequiredInstanceExtensions(NULL, NULL, extensions); + getRequiredInstanceExtensions(vk->world, NULL, extensions); for (i = nRequired, j = 0; i < nExtensions; ++i, ++j) { extensions[i] = additionalExtensions[j]; } @@ -374,12 +387,25 @@ 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`. * */ -struct RenderVulkan *rvkCreate() +struct RenderVulkan *rvkCreate(PuglWorld *world) { static const char *const instanceLayers[] = { "VK_LAYER_LUNARG_standard_validation" @@ -391,6 +417,11 @@ struct RenderVulkan *rvkCreate() const uint32_t nInstanceExtensions = sizeof(instanceExtensions) / sizeof(instanceExtensions[0]); struct RenderVulkan *vk = calloc(1, sizeof(*vk)); + if (!world) { + rvkSetErrMsg(vk, "No PuglWorld provided"); + return vk; + } + vk->world = world; vk->api = calloc(1, sizeof(*vk->api)); vk->api->handle = loadVulkanLibrary(NULL); @@ -570,14 +601,12 @@ static void rvkCreateSurface(struct RenderVulkan *vk, PuglView *view) * * Because this application will load all resources before any rendering begins, * and the amount of resources are small enough to be loaded almost instantly, - * it will not make use of a separate TRANSFER queue family. This application - * will also not attempt to find the "most powerful" device on the system, and - * will choose the first suitable device it finds. + * it will not make use of a separate TRANSFER queue family. * * Information for many devices is available online at * https://vulkan.gpuinfo.org/ */ -static int isDeviceSuitable(const struct RenderVulkan *const vk, +static int rvkIsDeviceSuitable(const struct RenderVulkan *const vk, const VkPhysicalDevice physicalDevice, uint32_t *const graphicsIndex) { @@ -622,6 +651,11 @@ static int isDeviceSuitable(const struct RenderVulkan *const vk, return 1; } +/** Selects a physical device + * + * This application will not attempt to find the "most powerful" device on the + * system, and will choose the first suitable device it finds. + */ void rvkSelectPhysicalDevice(struct RenderVulkan *vk) { if (!vk->surface) { @@ -663,7 +697,7 @@ void rvkSelectPhysicalDevice(struct RenderVulkan *vk) for (i = 0; i < nDevices; ++i) { printf("Checking suitability for\t`%s`...\n", deviceProperties[i].deviceName); uint32_t graphicsIndex; - if (isDeviceSuitable(vk, devices[i], &graphicsIndex)) { + if (rvkIsDeviceSuitable(vk, devices[i], &graphicsIndex)) { printf("Using physical device:\t\t`%s`\n", deviceProperties[i].deviceName); vk->deviceProperties = deviceProperties[i]; vk->physicalDevice = devices[i]; @@ -712,19 +746,21 @@ int main() /* The Mesa Vulkan drivers require this to be called */ XInitThreads(); #endif + PuglWorld *world = puglNewWorld(); const char *errMsg = NULL; - struct RenderVulkan *vk = rvkCreate(); + + /** Vulkan application that uses Pugl for windows and events */ + struct RenderVulkan *vk = rvkCreate(world); rvkCheckFatal(vk); printf("Created Vulkan Instance Successfully\n"); - PuglWorld *world = puglNewWorld(); PuglView *view = puglNewView(world); const PuglRect frame = { 0, 0, 800, 600 }; puglSetBackend(view, puglStubBackend()); PuglStatus status; - if ((status = puglCreateWindow(view, "Pugl Vulkan Test"))) { + if ((status = puglCreateWindow(view, "Vulkan Application Using Pugl"))) { fprintf(stderr, "Could not create window: %d\n", status); rvkDestroy(vk); puglFreeWorld(world); @@ -746,6 +782,7 @@ int main() puglFreeView(view); puglFreeWorld(world); + /* Vulkan library MUST be unloaded AFTER call to XCloseDisplay() or it will segfault */ rvkDestroy(vk); return 0; } -- cgit v1.2.1