From 40e48dce46f1c0009ee57d7e00def483fda6add6 Mon Sep 17 00:00:00 2001 From: Jordan Halase Date: Tue, 29 Oct 2019 13:07:21 -0500 Subject: Overhaul --- main.c | 101 +++++++++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 76 insertions(+), 25 deletions(-) (limited to 'main.c') diff --git a/main.c b/main.c index 30d2933..3aba8ce 100644 --- a/main.c +++ b/main.c @@ -84,11 +84,13 @@ struct VulkanAPI { struct RenderVulkan { struct VulkanAPI *api; char *errMsg; - VkInstance instance; - VkDebugReportCallbackEXT debugCallback; - VkSurfaceKHR surface; - uint32_t graphicsIndex; - VkDevice device; + VkInstance instance; + VkDebugReportCallbackEXT debugCallback; + VkSurfaceKHR surface; + VkPhysicalDeviceProperties deviceProperties; + VkPhysicalDevice physicalDevice; + uint32_t graphicsIndex; + VkDevice device; }; #define RVK_ERRMSG_LEN 4096 @@ -179,6 +181,7 @@ VkResult createVulkanSurface(PuglView *view, #else #define VULKAN_SONAME_LATEST "libvulkan.so.1" #include +// XXX: puglDlopen() void *appDlopen(const char *soname) { return dlopen(soname, RTLD_NOW); @@ -504,7 +507,56 @@ static void rvkCreateSurface(struct RenderVulkan *vk, PuglView *view) } } -int isDeviceSuitable(struct RenderVulkan *vk, VkPhysicalDevice pd) +/** Checks if a particular physical device is suitable for this application. + * + * Choosing a physical device is an *extremely* application-specific procedure. + * + * All rendering in Vulkan is done off-screen by default. To get rendered + * results on-screen they must be "presented" to a surface. + * The Vulkan spec allows devices to have presentation done on a separate queue + * family than GRAPHICS, or no present support at all. However, every graphics + * card today that is capable of connecting to a display has at least one queue + * family capable of both GRAPHICS and present. + * + * This single-threaded application will use only one queue for all operations. + * More specifically, it will make use of GRAPHICS and TRANSFER operations on + * a single queue retrieved from a GRAPHICS queue family that supports present. + * + * In my experience, most cards follow this format: + * + * INTEGRATED: Typically have one queue family for all operations. + * May retrieve only one queue from this single queue family. + * DEDICATED: Older cards typically have one queue family for GRAPHICS and one + * for TRANSFER. Newer cards typically have a separate queue family + * for COMPUTE. Remember that all GRAPHICS queue families + * implicitly allow both COMPUTE and TRANSFER operations on it as + * well. Programmers making use of separate queue families may allow + * devices to work more efficiently. + * + * May typically retrieve up to a few queues from either queue + * 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. + * + * GOTCHA: Some devices may have multiple GRAPHICS queue families + * with only one of them able to present. Do not assume a device is + * unsuitable until ALL queue families have been checked. + * + * 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. + * + * Information for many devices is available online at + * https://vulkan.gpuinfo.org/ + */ +static int isDeviceSuitable(const struct RenderVulkan *const vk, + const VkPhysicalDevice pd, + uint32_t *const graphicsIndex) { uint32_t nQueueFamilies; vk->api->vkGetPhysicalDeviceQueueFamilyProperties(pd, &nQueueFamilies, NULL); @@ -513,22 +565,19 @@ int isDeviceSuitable(struct RenderVulkan *vk, VkPhysicalDevice pd) for (uint32_t i = 0; i < nQueueFamilies; ++i) { printf("Queue Family %d queueCount:\t%d\n", i, queueProperties[i].queueCount); } - uint32_t graphicsIndex; - for (graphicsIndex = 0; graphicsIndex < nQueueFamilies; ++graphicsIndex) { - if (queueProperties[graphicsIndex].queueFlags & VK_QUEUE_GRAPHICS_BIT) { + uint32_t g; + for (g = 0; g < nQueueFamilies; ++g) { + if (queueProperties[g].queueFlags & VK_QUEUE_GRAPHICS_BIT) { VkBool32 canSurface; - vk->api->vkGetPhysicalDeviceSurfaceSupportKHR(pd, graphicsIndex, vk->surface, &canSurface); + vk->api->vkGetPhysicalDeviceSurfaceSupportKHR(pd, g, vk->surface, &canSurface); if (canSurface) break; } } - if (graphicsIndex >= nQueueFamilies) { - /* Some esoteric devices may have separate graphics and present queue families, or none at all. - * An example would be a device without any video output plugs. - * We only support graphics and present on the same queue family. - */ + if (g >= nQueueFamilies) { + /* We only support graphics and present on the same queue family. */ return 0; } - vk->graphicsIndex = graphicsIndex; + *graphicsIndex = g; return 1; } @@ -555,6 +604,7 @@ void rvkSelectPhysicalDevice(struct RenderVulkan *vk) return; } VkPhysicalDevice *devices = malloc(nDevices * sizeof(*devices)); + VkPhysicalDeviceProperties *deviceProperties = malloc(nDevices * sizeof(*deviceProperties)); if ((result = vk->api->vkEnumeratePhysicalDevices(vk->instance, &nDevices, devices))) { if (result != VK_INCOMPLETE) { rvkSetErrMsg(vk, strErrEnum, result); @@ -566,24 +616,25 @@ void rvkSelectPhysicalDevice(struct RenderVulkan *vk) uint32_t i; for (i = 0; i < nDevices; ++i) { - VkPhysicalDeviceProperties deviceProperties; - vk->api->vkGetPhysicalDeviceProperties(devices[i], &deviceProperties); - printf("Found physical device:\t`%s`\n", deviceProperties.deviceName); + vk->api->vkGetPhysicalDeviceProperties(devices[i], &deviceProperties[i]); + printf("Found physical device:\t\t`%s`\n", deviceProperties[i].deviceName); } for (i = 0; i < nDevices; ++i) { - VkPhysicalDeviceProperties deviceProperties; - vk->api->vkGetPhysicalDeviceProperties(devices[i], &deviceProperties); - printf("Checking suitability for `%s`...\n", deviceProperties.deviceName); - if (isDeviceSuitable(vk, devices[i])) { - printf("Using physical device:\t`%s`\n", deviceProperties.deviceName); + printf("Checking suitability for\t`%s`...\n", deviceProperties[i].deviceName); + if (isDeviceSuitable(vk, devices[i], &vk->graphicsIndex)) { + printf("Using physical device:\t\t`%s`\n", deviceProperties[i].deviceName); + vk->deviceProperties = deviceProperties[i]; + vk->physicalDevice = devices[i]; + printf("Graphics Index:\t\t\t%d\n", vk->graphicsIndex); goto done; } - printf("Device `%s` not suitable\n", deviceProperties.deviceName); + printf("Device `%s` not suitable\n", deviceProperties[i].deviceName); } if (i >= nDevices) { rvkSetErrMsg(vk, "No suitable devices found"); } done: + free(deviceProperties); free(devices); } -- cgit v1.2.1