some device scoring heuristics
[vkray] / src / vk.c
index 101eaa8..2a1db64 100644 (file)
--- a/src/vk.c
+++ b/src/vk.c
@@ -17,6 +17,8 @@ static int create_instance(void);
 static int create_surface(void);
 static int choose_phys_dev(void);
 static int create_device(void);
+static int create_swapchain(void);
+
 static int choose_pixfmt(void);
 static int eval_pdev_score(VkPhysicalDevice dev);
 static int have_inst_layer(const char *name);
@@ -24,10 +26,11 @@ static int have_ext(VkExtensionProperties *ext, int next, const char *name);
 
 static Display *dpy;
 static Window win;
+static int initflags;
 
 static VkInstance vk;
 static VkPhysicalDevice vkpdev;
-static int vkqfam_idx = -1;
+static int vkqfam_idx, vkqfam_maxq;
 static VkDevice vkdev;
 static VkQueue vkq;
 static VkSurfaceKHR vksurf;
@@ -36,6 +39,9 @@ static int vksurf_numfmt, vksurf_selfmt;
 static VkSurfaceFormatKHR *vksurf_fmt;
 static VkSwapchainKHR vksc;
 static int vksc_numimg;
+static VkImage *vksc_img;
+static VkExtent2D vksc_extent;
+static VkImageView *vksc_view;
 
 static VkLayerProperties *inst_layers;
 static VkExtensionProperties *inst_ext, *dev_ext;
@@ -52,23 +58,88 @@ void vk_init_xwin(Display *d, Window w)
        win = w;
 }
 
-int vk_init(void)
+int vk_init(unsigned int flags, unsigned int *usedflags)
 {
+       initflags = flags;
        if(create_instance() == -1)     return -1;
        if(create_surface() == -1) return -1;
        if(choose_phys_dev() == -1) return -1;
        if(create_device() == -1) return -1;
+
+       if(initflags != flags) {
+               if(usedflags) {
+                       *usedflags = initflags;
+               } else {
+                       vk_cleanup();
+                       return -1;
+               }
+       }
        return 0;
 }
 
 void vk_cleanup(void)
 {
-       vkDestroyInstance(vk, 0);
+       int i;
+
+       free(vksc_img);
+       vksc_img = 0;
+       free(vksc_view);
+       vksc_view = 0;
+       if(vksc_view) {
+               for(i=0; i<vksc_numimg; i++) {
+                       vkDestroyImageView(vkdev, vksc_view[i], 0);
+               }
+               vksc_view = 0;
+       }
+       if(vksc) {
+               vkDestroySwapchainKHR(vkdev, vksc, 0);
+               vksc = 0;
+       }
+       if(vkdev) {
+               vkDestroyDevice(vkdev, 0);
+               vkdev = 0;
+       }
+       if(vksurf) {
+               vkDestroySurfaceKHR(vk, vksurf, 0);
+               vksurf = 0;
+       }
+       if(vk) {
+               vkDestroyInstance(vk, 0);
+               vk = 0;
+       }
        free(inst_layers);
+       inst_layers = 0;
        free(inst_ext);
+       inst_ext = 0;
+       free(dev_ext);
+       dev_ext = 0;
        free(pdev_list);
+       pdev_list = 0;
 }
 
+int vk_reshape(int xsz, int ysz)
+{
+       int i;
+
+       if(vksc && vksc_extent.width == xsz && vksc_extent.height == ysz) {
+               return 0;
+       }
+
+       if(vksc_view) {
+               for(i=0; i<vksc_numimg; i++) {
+                       vkDestroyImageView(vkdev, vksc_view[i], 0);
+               }
+       }
+       if(vksc) vkDestroySwapchainKHR(vkdev, vksc, 0);
+
+       vksc_extent.width = xsz;
+       vksc_extent.height = ysz;
+
+       if(create_swapchain() == -1) return -1;
+
+       /* TODO create depth/stencil buffers as needed (initflags) */
+       return 0;
+}
 
 #define ARRSZ(arr)     (sizeof arr / sizeof *arr)
 static const char *known_layer_list[] = {
@@ -95,6 +166,15 @@ static struct {
        {"VK_KHR_debug_report", 0}
 };
 
+static struct {
+       const char *name;
+       int required;
+} known_devext_list[] = {
+       {"VK_KHR_swapchain", 1},
+       {"VK_KHR_acceleration_structure", 0},
+       {"VK_KHR_ray_tracing_pipeline", 0}
+};
+
 static int create_instance(void)
 {
        int i, nlayers = 0, next = 0;
@@ -165,8 +245,6 @@ static int create_instance(void)
 
 static int create_surface(void)
 {
-       int res;
-
        /*
        VkXlibSurfaceCreateInfoKHR xinf = {0};
        xinf.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
@@ -183,8 +261,8 @@ static int create_surface(void)
        xinf.connection = XGetXCBConnection(dpy);
        xinf.window = (xcb_window_t)win;
 
-       if((res = vkCreateXcbSurfaceKHR(vk, &xinf, 0, &vksurf)) != 0) {
-               fprintf(stderr, "failed to create XCB window surface (%d)\n", res);
+       if(vkCreateXcbSurfaceKHR(vk, &xinf, 0, &vksurf) != 0) {
+               fprintf(stderr, "failed to create XCB window surface\n");
                return -1;
        }
        return 0;
@@ -192,9 +270,11 @@ static int create_surface(void)
 
 int choose_phys_dev(void)
 {
-       uint32_t i, num_pdev, score, best_score, best_dev;
+       uint32_t i, num_pdev, num_qfam, score, best_score, best_dev;
        VkPhysicalDevice *pdev;
        VkPhysicalDeviceProperties pdevprop;
+       VkQueueFamilyProperties *qfam;
+       VkBool32 can_pres;
 
        vkEnumeratePhysicalDevices(vk, &num_pdev, 0);
        if(!num_pdev) {
@@ -222,9 +302,23 @@ int choose_phys_dev(void)
                free(pdev);
                return -1;
        }
+       vkpdev = pdev[best_dev];
+
+       vkGetPhysicalDeviceQueueFamilyProperties(vkpdev, &num_qfam, 0);
+       qfam = malloc_nf(num_qfam * sizeof *qfam);
+       vkGetPhysicalDeviceQueueFamilyProperties(vkpdev, &num_qfam, qfam);
 
-       vkpdev = pdev[i];
+       vkqfam_idx = -1;
+       for(i=0; i<num_qfam; i++) {
+               vkGetPhysicalDeviceSurfaceSupportKHR(vkpdev, i, vksurf, &can_pres);
+               if(qfam[i].queueCount && (qfam[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) && can_pres) {
+                       vkqfam_maxq = qfam[i].queueCount;
+                       vkqfam_idx = i;
+                       break;
+               }
+       }
 
+       free(qfam);
        free(pdev);
        choose_pixfmt();
        return 0;
@@ -232,11 +326,127 @@ int choose_phys_dev(void)
 
 static int create_device(void)
 {
+       float prio = 1.0f;
+       VkDeviceQueueCreateInfo qinf = {0};
+       VkPhysicalDeviceFeatures feat = {0};
+       VkDeviceCreateInfo devinf = {0};
+       const char *ext[ARRSZ(known_devext_list) + 16];
+       int i, num_ext;
+
+       vkEnumerateDeviceExtensionProperties(vkpdev, 0, &dev_ext_count, 0);
+       dev_ext = malloc_nf(dev_ext_count * sizeof *dev_ext);
+       vkEnumerateDeviceExtensionProperties(vkpdev, 0, &dev_ext_count, dev_ext);
+
+       num_ext = 0;
+       for(i=0; i<ARRSZ(known_devext_list); i++) {
+               if(have_ext(dev_ext, dev_ext_count, known_devext_list[i].name)) {
+                       ext[num_ext++] = known_devext_list[i].name;
+               } else if(known_devext_list[i].required) {
+                       fprintf(stderr, "Vulkan device lacks required extension: %s\n",
+                                       known_devext_list[i].name);
+                       return -1;
+               }
+       }
+
+       if(initflags & VKINIT_RAY) {
+               if(have_ext(dev_ext, dev_ext_count, "VK_KHR_acceleration_structure") &&
+                               have_ext(dev_ext, dev_ext_count, "VK_KHR_ray_tracing_pipeline")) {
+                       ext[num_ext++] = "VK_KHR_acceleration_structure";
+                       ext[num_ext++] = "VK_KHR_ray_tracing_pipeline";
+               } else {
+                       initflags &= ~VKINIT_RAY;
+               }
+       }
+
+       qinf.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
+       qinf.queueFamilyIndex = vkqfam_idx;
+       qinf.queueCount = 1;
+       qinf.pQueuePriorities = &prio;
+
+       devinf.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
+       devinf.pQueueCreateInfos = &qinf;
+       devinf.queueCreateInfoCount = 1;
+       devinf.pEnabledFeatures = &feat;
+       devinf.enabledExtensionCount = num_ext;
+       devinf.ppEnabledExtensionNames = ext;
+
+       if(vkCreateDevice(vkpdev, &devinf, 0, &vkdev) != 0) {
+               fprintf(stderr, "failed to create vulkan device\n");
+               return -1;
+       }
+
+       vkGetDeviceQueue(vkdev, vkqfam_idx, 0, &vkq);
+       return 0;
+}
+
+static int create_swapchain(void)
+{
+       int i;
+       uint32_t num;
+       VkSwapchainCreateInfoKHR scinf = {0};
+       VkImageViewCreateInfo ivinf;
+
+       if(vksc_extent.width <= 0 || vksc_extent.height <= 0) {
+               return -1;
+       }
+
+       scinf.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
+       scinf.surface = vksurf;
+       scinf.minImageCount = 2;
+       scinf.imageFormat = vksurf_fmt[vksurf_selfmt].format;
+       scinf.imageColorSpace = vksurf_fmt[vksurf_selfmt].colorSpace;
+       scinf.imageExtent = vksc_extent;
+       scinf.imageArrayLayers = 1;
+       scinf.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+       scinf.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
+       scinf.preTransform = vksurf_caps.currentTransform;
+       scinf.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
+       scinf.presentMode = VK_PRESENT_MODE_FIFO_KHR;
+       scinf.clipped = VK_TRUE;
+
+       if(vkCreateSwapchainKHR(vkdev, &scinf, 0, &vksc) != 0) {
+               fprintf(stderr, "failed to create swapchain\n");
+               return -1;
+       }
+
+       if(!vksc_img || vksc_numimg != num) {
+               free(vksc_img);
+               vkGetSwapchainImagesKHR(vkdev, vksc, &num, 0);
+               vksc_img = malloc_nf(num * sizeof *vksc_img);
+               vkGetSwapchainImagesKHR(vkdev, vksc, &num, vksc_img);
+       }
+       if(!vksc_view || vksc_numimg != num) {
+               free(vksc_view);
+               vksc_view = malloc_nf(num * sizeof *vksc_view);
+       }
+       vksc_numimg = num;
+
+       for(i=0; i<num; i++) {
+               memset(&ivinf, 0, sizeof ivinf);
+               ivinf.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+               ivinf.image = vksc_img[i];
+               ivinf.format = vksurf_fmt[vksurf_selfmt].format;
+               ivinf.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
+               ivinf.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
+               ivinf.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
+               ivinf.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
+               ivinf.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+               ivinf.subresourceRange.levelCount = 1;
+               ivinf.subresourceRange.layerCount = 1;
+               ivinf.viewType = VK_IMAGE_VIEW_TYPE_2D;
+
+               if(vkCreateImageView(vkdev, &ivinf, 0, vksc_view + i) != 0) {
+                       fprintf(stderr, "failed to create image view (%d)\n", i);
+                       return -1;
+               }
+       }
        return 0;
 }
 
+
 static int eval_pdev_score(VkPhysicalDevice dev)
 {
+       int score = 0;
        uint32_t i, num_fmt, num_qfam, num_ext;
        VkQueueFamilyProperties *qfam;
        VkExtensionProperties *ext;
@@ -274,22 +484,38 @@ static int eval_pdev_score(VkPhysicalDevice dev)
        qfam = malloc_nf(num_qfam * sizeof *qfam);
        vkGetPhysicalDeviceQueueFamilyProperties(dev, &num_qfam, qfam);
 
-       vkqfam_idx = -1;
        for(i=0; i<num_qfam; i++) {
                vkGetPhysicalDeviceSurfaceSupportKHR(dev, i, vksurf, &can_pres);
                if(qfam[i].queueCount && (qfam[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) && can_pres) {
-                       vkqfam_idx = i;
-                       free(ext);
-                       free(sfmt);
-                       free(qfam);
-                       return 1;
+                       score = 1;
+               }
+       }
+
+       switch(prop.deviceType) {
+       case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
+               score++;
+               break;
+       case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
+               score += 2;
+               break;
+       case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
+               score += 4;
+               break;
+       default:
+               break;
+       }
+
+       if(initflags & VKINIT_RAY) {
+               if(have_ext(ext, num_ext, "VK_KHR_acceleration_structure") &&
+                               have_ext(ext, num_ext, "VK_KHR_ray_tracing_pipeline")) {
+                       score += 100;
                }
        }
 
        free(ext);
        free(sfmt);
        free(qfam);
-       return 0;
+       return score;
 }
 
 static int choose_pixfmt(void)
@@ -302,27 +528,27 @@ static int choose_pixfmt(void)
        };
        int i, j;
        uint32_t num_fmt;
-       VkSurfaceFormatKHR *sfmt;
 
        vkGetPhysicalDeviceSurfaceFormatsKHR(vkpdev, vksurf, &num_fmt, 0);
        if(!num_fmt) return -1;
-       sfmt = malloc_nf(num_fmt * sizeof *sfmt);
-       vkGetPhysicalDeviceSurfaceFormatsKHR(vkpdev, vksurf, &num_fmt, sfmt);
+       vksurf_fmt = malloc_nf(num_fmt * sizeof *vksurf_fmt);
+       vkGetPhysicalDeviceSurfaceFormatsKHR(vkpdev, vksurf, &num_fmt, vksurf_fmt);
 
        vksurf_selfmt = 0;
        for(i=0; i<num_fmt; i++) {
-               if(sfmt[i].colorSpace != VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
+               if(vksurf_fmt[i].colorSpace != VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
                        continue;
                }
                for(j=0; j<sizeof pref / sizeof *pref; j++) {
-                       if(sfmt[i].format == pref[j]) {
+                       if(vksurf_fmt[i].format == pref[j]) {
                                vksurf_selfmt = i;
-                               free(sfmt);
+                               vksurf_numfmt = num_fmt;
                                return i;
                        }
                }
        }
-       free(sfmt);
+       free(vksurf_fmt);
+       vksurf_fmt = 0;
        return -1;
 }