vkCreateXlibSurfaceKHR hangs forever
[vkray] / src / vk.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <stdint.h>
4 #include <vulkan/vulkan.h>
5 #include "vk.h"
6 #include "util.h"
7
8 #ifdef __WIN32__
9 #include <vulkan/vulkan_win32.h>
10 #else
11 #include <vulkan/vulkan_xlib.h>
12 #endif
13
14 static int create_instance(void);
15 static int create_surface(void);
16 static int choose_phys_dev(void);
17 static int create_device(void);
18 static int choose_pixfmt(void);
19 static int eval_pdev_score(VkPhysicalDevice dev);
20 static int have_inst_layer(const char *name);
21 static int have_ext(VkExtensionProperties *ext, int next, const char *name);
22
23 static Display *dpy;
24 static Window win;
25
26 static VkInstance vk;
27 static VkPhysicalDevice vkpdev;
28 static int vkqfam_idx = -1;
29 static VkDevice vkdev;
30 static VkQueue vkq;
31 static VkSurfaceKHR vksurf;
32 static VkSurfaceCapabilitiesKHR vksurf_caps;
33 static int vksurf_numfmt, vksurf_selfmt;
34 static VkSurfaceFormatKHR *vksurf_fmt;
35 static VkSwapchainKHR vksc;
36 static int vksc_numimg;
37
38 static VkLayerProperties *inst_layers;
39 static VkExtensionProperties *inst_ext, *dev_ext;
40 static uint32_t inst_ext_count, dev_ext_count, inst_layers_count;
41
42 static VkPhysicalDevice *pdev_list;
43 static uint32_t num_pdev;
44
45 static int have_raytrace, have_debug_report;
46
47 void vk_init_xwin(Display *d, Window w)
48 {
49         dpy = d;
50         win = w;
51 }
52
53 int vk_init(void)
54 {
55         if(create_instance() == -1)     return -1;
56         if(create_surface() == -1) return -1;
57         if(choose_phys_dev() == -1) return -1;
58         if(create_device() == -1) return -1;
59         return 0;
60 }
61
62 void vk_cleanup(void)
63 {
64         vkDestroyInstance(vk, 0);
65         free(inst_layers);
66         free(inst_ext);
67         free(pdev_list);
68 }
69
70
71 #define ARRSZ(arr)      (sizeof arr / sizeof *arr)
72 static const char *known_layer_list[] = {
73         "VK_LAYER_GOOGLE_threading",
74         "VK_LAYER_LUNARG_parameter_validation",
75         "VK_LAYER_LUNARG_object_tracker",
76         "VK_LAYER_LUNARG_image",
77         "VK_LAYER_LUNARG_core_validation",
78         "VK_LAYER_LUNARG_swapchain",
79         "VK_LAYER_GOOGLE_unique_objects"
80 };
81
82 static struct {
83         const char *name;
84         int required;
85 } known_instext_list[] = {
86         {"VK_KHR_surface", 1},
87 #ifdef __WIN32__
88         {"VK_KHR_win32_surface", 1},
89 #else
90         {"VK_KHR_xlib_surface", 1},
91 #endif
92         {"VK_KHR_debug_report", 0}
93 };
94
95 static int create_instance(void)
96 {
97         int i, nlayers = 0, next = 0;
98         VkInstanceCreateInfo instinf;
99         VkApplicationInfo appinf;
100         const char *layers[ARRSZ(known_layer_list)];
101         const char *ext[ARRSZ(known_instext_list)];
102         uint32_t apiver;
103
104         vkEnumerateInstanceVersion(&apiver);
105         printf("Vulkan API version: %d.%d.%d\n", (apiver >> 22) & 0x7f,
106                         (apiver >> 12) & 0x3ff, apiver & 0xfff);
107
108         memset(&appinf, 0, sizeof appinf);
109         appinf.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
110         appinf.pApplicationName = "vkray";
111         appinf.pEngineName = "vkray";
112         appinf.apiVersion = apiver;
113
114         vkEnumerateInstanceLayerProperties(&inst_layers_count, 0);
115         inst_layers = malloc_nf(inst_layers_count * sizeof *inst_layers);
116         vkEnumerateInstanceLayerProperties(&inst_layers_count, inst_layers);
117
118         vkEnumerateInstanceExtensionProperties(0, &inst_ext_count, 0);
119         inst_ext = malloc_nf(inst_ext_count * sizeof *inst_ext);
120         vkEnumerateInstanceExtensionProperties(0, &inst_ext_count, inst_ext);
121
122         printf("Layers:\n");
123         for(i=0; i<inst_layers_count; i++) {
124                 printf(" - %s: %s\n", inst_layers[i].layerName, inst_layers[i].description);
125         }
126         printf("Instance extensions:\n");
127         for(i=0; i<inst_ext_count; i++) {
128                 printf(" - %s\n", inst_ext[i].extensionName);
129         }
130
131         have_debug_report = have_ext(inst_ext, inst_ext_count, "VK_KHR_debug_report");
132
133         for(i=0; i<ARRSZ(known_layer_list); i++) {
134                 if(have_inst_layer(known_layer_list[i])) {
135                         layers[nlayers++] = known_layer_list[i];
136                 }
137         }
138         for(i=0; i<ARRSZ(known_instext_list); i++) {
139                 if(have_ext(inst_ext, inst_ext_count, known_instext_list[i].name)) {
140                         ext[next++] = known_instext_list[i].name;
141                 } else if(known_instext_list[i].required) {
142                         fprintf(stderr, "Vulkan implementation lacks required instance extension: %s\n",
143                                         known_instext_list[i].name);
144                         return -1;
145                 }
146         }
147
148         memset(&instinf, 0, sizeof instinf);
149         instinf.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
150         instinf.pApplicationInfo = &appinf;
151         instinf.enabledLayerCount = nlayers;
152         instinf.ppEnabledLayerNames = layers;
153         instinf.enabledExtensionCount = next;
154         instinf.ppEnabledExtensionNames = ext;
155         if(vkCreateInstance(&instinf, 0, &vk) != 0) {
156                 fprintf(stderr, "failed to create vulkan instance\n");
157                 return -1;
158         }
159
160         return 0;
161 }
162
163 static int create_surface(void)
164 {
165         VkXlibSurfaceCreateInfoKHR xinf = {0};
166         xinf.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
167         xinf.dpy = dpy;
168         xinf.window = win;
169
170         if(vkCreateXlibSurfaceKHR(vk, &xinf, 0, &vksurf) != 0) {
171                 fprintf(stderr, "failed to create Xlib window surface\n");
172                 return -1;
173         }
174         return 0;
175 }
176
177 int choose_phys_dev(void)
178 {
179         uint32_t i, num_pdev, score, best_score, best_dev;
180         VkPhysicalDevice *pdev;
181         VkPhysicalDeviceProperties pdevprop;
182
183         vkEnumeratePhysicalDevices(vk, &num_pdev, 0);
184         if(!num_pdev) {
185                 fprintf(stderr, "no vulkan devices found\n");
186                 return -1;
187         }
188         pdev = malloc_nf(num_pdev * sizeof *pdev);
189         vkEnumeratePhysicalDevices(vk, &num_pdev, pdev);
190
191         printf("Found %d physical devices\n", num_pdev);
192
193         best_score = 0;
194         best_dev = -1;
195         for(i=0; i<num_pdev; i++) {
196                 if((score = eval_pdev_score(pdev[i])) && score > best_score) {
197                         best_score = score;
198                         best_dev = i;
199                 }
200
201                 vkGetPhysicalDeviceProperties(pdev[i], &pdevprop);
202                 printf(" %d: %s (score: %d)\n", i, pdevprop.deviceName, score);
203         }
204         if(best_dev == -1) {
205                 fprintf(stderr, "no suitable vulkan device found\n");
206                 free(pdev);
207                 return -1;
208         }
209
210         vkpdev = pdev[i];
211
212         free(pdev);
213         choose_pixfmt();
214         return 0;
215 }
216
217 static int create_device(void)
218 {
219         return 0;
220 }
221
222 static int eval_pdev_score(VkPhysicalDevice dev)
223 {
224         uint32_t i, num_fmt, num_qfam, num_ext;
225         VkQueueFamilyProperties *qfam;
226         VkExtensionProperties *ext;
227         VkPhysicalDeviceProperties prop;
228         VkPhysicalDeviceFeatures feat;
229         VkSurfaceFormatKHR *sfmt;
230         VkBool32 can_pres;
231
232         vkGetPhysicalDeviceProperties(dev, &prop);
233         vkGetPhysicalDeviceFeatures(dev, &feat);
234
235         /* check if we have the swapchain extension */
236         vkEnumerateDeviceExtensionProperties(dev, 0, &num_ext, 0);
237         ext = malloc_nf(num_ext * sizeof *ext);
238         vkEnumerateDeviceExtensionProperties(dev, 0, &num_ext, ext);
239
240         if(!have_ext(ext, num_ext, "VK_KHR_swapchain")) {
241                 free(ext);
242                 return 0;
243         }
244
245         /* populate format and present modes arrays, and make sure we have some of each */
246         vkGetPhysicalDeviceSurfaceFormatsKHR(dev, vksurf, &num_fmt, 0);
247         if(!num_fmt) {
248                 free(ext);
249                 return 0;
250         }
251         sfmt = malloc_nf(num_fmt * sizeof *sfmt);
252         vkGetPhysicalDeviceSurfaceFormatsKHR(dev, vksurf, &num_fmt, sfmt);
253
254         vkGetPhysicalDeviceSurfaceCapabilitiesKHR(dev, vksurf, &vksurf_caps);
255
256         /* find a queue family which can do graphics and can present */
257         vkGetPhysicalDeviceQueueFamilyProperties(dev, &num_qfam, 0);
258         qfam = malloc_nf(num_qfam * sizeof *qfam);
259         vkGetPhysicalDeviceQueueFamilyProperties(dev, &num_qfam, qfam);
260
261         vkqfam_idx = -1;
262         for(i=0; i<num_qfam; i++) {
263                 vkGetPhysicalDeviceSurfaceSupportKHR(dev, i, vksurf, &can_pres);
264                 if(qfam[i].queueCount && (qfam[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) && can_pres) {
265                         vkqfam_idx = i;
266                         free(ext);
267                         free(sfmt);
268                         free(qfam);
269                         return 1;
270                 }
271         }
272
273         free(ext);
274         free(sfmt);
275         free(qfam);
276         return 0;
277 }
278
279 static int choose_pixfmt(void)
280 {
281         static const VkFormat pref[] = {
282                 VK_FORMAT_B8G8R8_UNORM,
283                 VK_FORMAT_R8G8B8_UNORM,
284                 VK_FORMAT_B8G8R8A8_UNORM,
285                 VK_FORMAT_R8G8B8A8_UNORM
286         };
287         int i, j;
288         uint32_t num_fmt;
289         VkSurfaceFormatKHR *sfmt;
290
291         vkGetPhysicalDeviceSurfaceFormatsKHR(vkpdev, vksurf, &num_fmt, 0);
292         if(!num_fmt) return -1;
293         sfmt = malloc_nf(num_fmt * sizeof *sfmt);
294         vkGetPhysicalDeviceSurfaceFormatsKHR(vkpdev, vksurf, &num_fmt, sfmt);
295
296         vksurf_selfmt = 0;
297         for(i=0; i<num_fmt; i++) {
298                 if(sfmt[i].colorSpace != VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
299                         continue;
300                 }
301                 for(j=0; j<sizeof pref / sizeof *pref; j++) {
302                         if(sfmt[i].format == pref[j]) {
303                                 vksurf_selfmt = i;
304                                 free(sfmt);
305                                 return i;
306                         }
307                 }
308         }
309         free(sfmt);
310         return -1;
311 }
312
313
314 static int have_inst_layer(const char *name)
315 {
316         int i;
317         for(i=0; i<inst_layers_count; i++) {
318                 if(strcmp(inst_layers[i].layerName, name) == 0) {
319                         return 1;
320                 }
321         }
322         return 0;
323 }
324
325 static int have_ext(VkExtensionProperties *ext, int next, const char *name)
326 {
327         int i;
328         for(i=0; i<next; i++) {
329                 if(strcmp(ext[i].extensionName, name) == 0) {
330                         return 1;
331                 }
332         }
333         return 0;
334 }