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