4 #include <vulkan/vulkan.h>
10 #include <vulkan/vulkan_win32.h>
12 /*#include <vulkan/vulkan_xlib.h>*/
13 #include <X11/Xlib-xcb.h>
14 #include <vulkan/vulkan_xcb.h>
17 static int create_instance(void);
18 static int create_surface(void);
19 static int choose_phys_dev(void);
20 static int create_device(void);
21 static int create_swapchain(void);
23 static int choose_pixfmt(void);
24 static int eval_pdev_score(VkPhysicalDevice dev);
25 static int have_inst_layer(const char *name);
26 static int have_ext(VkExtensionProperties *ext, int next, const char *name);
33 static VkPhysicalDevice vkpdev;
34 static int vkqfam_idx, vkqfam_maxq;
35 static VkDevice vkdev;
37 static VkSurfaceKHR vksurf;
38 static VkSurfaceCapabilitiesKHR vksurf_caps;
39 static int vksurf_numfmt, vksurf_selfmt;
40 static VkSurfaceFormatKHR *vksurf_fmt;
41 static VkSwapchainKHR vksc;
42 static int vksc_numimg;
43 static VkImage *vksc_img;
44 static VkExtent2D vksc_extent;
45 static VkImageView *vksc_view;
47 static VkLayerProperties *inst_layers;
48 static VkExtensionProperties *inst_ext, *dev_ext;
49 static uint32_t inst_ext_count, dev_ext_count, inst_layers_count;
51 static VkPhysicalDevice *pdev_list;
52 static uint32_t num_pdev;
54 static int have_raytrace, have_debug_report;
56 void vk_init_xwin(Display *d, Window w)
62 int vk_init(unsigned int flags, unsigned int *usedflags)
65 if(create_instance() == -1) return -1;
66 if(create_surface() == -1) return -1;
67 if(choose_phys_dev() == -1) return -1;
68 if(create_device() == -1) return -1;
70 if(initflags != flags) {
72 *usedflags = initflags;
90 for(i=0; i<vksc_numimg; i++) {
91 vkDestroyImageView(vkdev, vksc_view[i], 0);
96 vkDestroySwapchainKHR(vkdev, vksc, 0);
100 vkDestroyDevice(vkdev, 0);
104 vkDestroySurfaceKHR(vk, vksurf, 0);
108 vkDestroyInstance(vk, 0);
121 int vk_reshape(int xsz, int ysz)
125 if(vksc && vksc_extent.width == xsz && vksc_extent.height == ysz) {
130 for(i=0; i<vksc_numimg; i++) {
131 vkDestroyImageView(vkdev, vksc_view[i], 0);
134 if(vksc) vkDestroySwapchainKHR(vkdev, vksc, 0);
136 vksc_extent.width = xsz;
137 vksc_extent.height = ysz;
139 if(create_swapchain() == -1) return -1;
141 /* TODO create depth/stencil buffers as needed (initflags) */
151 /* TODO: stuff about depth-stencil */
158 static struct rpass *rpasses;
160 int vk_create_rpass(void)
163 struct rpass rpass = {0}, *rp = &rpass;
166 rpasses = darr_alloc(0, sizeof *rpasses);
167 darr_push(rpasses, &rpass); /* add dummy rpass */
170 for(i=1; i<darr_size(rpasses); i++) {
171 if(!rpasses[i].used) {
176 /* init renderpass defaults */
178 rp->fmt = vksurf_fmt[vksurf_selfmt].format;
179 rp->zfmt = VK_FORMAT_D24_UNORM_S8_UINT;
187 darr_push(rpasses, rp);
188 return darr_size(rpasses) - 1;
193 void vk_free_rpass(int rp)
195 if(!rpasses || rp < 1 || rp >= darr_size(rpasses)) {
199 if(rpasses[rp].used && rpasses[rp].vkobj) {
200 vkDestroyRenderPass(vkdev, rpasses[rp].vkobj, 0);
202 rpasses[rp].used = 0;
205 void vk_rpass_colorbuf(int rp, int fmt, int n)
207 rpasses[rp].fmt = fmt;
208 rpasses[rp].num_colbuf = n;
209 rpasses[rp].vkobj_valid = 0;
212 void vk_rpass_msaa(int rp, int nsamp)
214 rpasses[rp].num_samples = nsamp;
217 void vk_rpass_clear(int rp, int clear)
219 rpasses[rp].clear = clear;
222 VkRenderPass vk_rpass(int rp)
226 VkAttachmentDescription att[17];
227 VkAttachmentReference catref[16], zatref;
228 VkSubpassDescription subpass;
229 VkRenderPassCreateInfo pinf;
233 if(!r->vkobj_valid) {
234 zidx = r->num_colbuf;
235 memset(att, 0, sizeof att);
236 for(i=0; i<r->num_colbuf; i++) {
237 att[i].format = r->fmt;
238 att[i].samples = r->num_samples;
239 att[i].loadOp = r->clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_DONT_CARE;
240 att[i].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
241 att[i].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
242 att[i].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
243 att[i].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
244 att[i].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
246 att[zidx].format = r->zfmt;
247 att[zidx].samples = 1;
248 att[zidx].loadOp = r->clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_DONT_CARE;
249 att[zidx].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
250 att[zidx].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
251 att[zidx].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
252 att[zidx].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
253 att[zidx].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
255 for(i=0; i<r->num_colbuf; i++) {
256 catref[i].attachment = i;
257 catref[i].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
259 zatref.attachment = zidx;
260 zatref.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
262 memset(&subpass, 0, sizeof subpass);
263 subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
264 subpass.colorAttachmentCount = r->num_colbuf;
265 subpass.pColorAttachments = catref;
266 subpass.pDepthStencilAttachment = &zatref;
268 memset(&pinf, 0, sizeof pinf);
269 pinf.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
270 pinf.attachmentCount = r->num_colbuf + 1;
271 pinf.pAttachments = att;
272 pinf.subpassCount = 1;
273 pinf.pSubpasses = &subpass;
275 if(vkCreateRenderPass(vkdev, &pinf, 0, &r->vkobj) != 0) {
276 fprintf(stderr, "failed to create render pass!\n");
285 #define ARRSZ(arr) (sizeof arr / sizeof *arr)
286 static const char *known_layer_list[] = {
287 "VK_LAYER_GOOGLE_threading",
288 "VK_LAYER_LUNARG_parameter_validation",
289 "VK_LAYER_LUNARG_object_tracker",
290 "VK_LAYER_LUNARG_image",
291 "VK_LAYER_LUNARG_core_validation",
292 "VK_LAYER_LUNARG_swapchain",
293 "VK_LAYER_GOOGLE_unique_objects"
299 } known_instext_list[] = {
300 {"VK_KHR_surface", 1},
302 {"VK_KHR_win32_surface", 1},
304 /*{"VK_KHR_xlib_surface", 1},*/
305 {"VK_KHR_xcb_surface", 1},
307 {"VK_KHR_debug_report", 0}
313 } known_devext_list[] = {
314 {"VK_KHR_swapchain", 1},
315 {"VK_KHR_acceleration_structure", 0},
316 {"VK_KHR_ray_tracing_pipeline", 0}
319 static int create_instance(void)
321 int i, nlayers = 0, next = 0;
322 VkInstanceCreateInfo instinf;
323 VkApplicationInfo appinf;
324 const char *layers[ARRSZ(known_layer_list)];
325 const char *ext[ARRSZ(known_instext_list)];
328 vkEnumerateInstanceVersion(&apiver);
329 printf("Vulkan API version: %d.%d.%d\n", (apiver >> 22) & 0x7f,
330 (apiver >> 12) & 0x3ff, apiver & 0xfff);
332 memset(&appinf, 0, sizeof appinf);
333 appinf.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
334 appinf.pApplicationName = "vkray";
335 appinf.pEngineName = "vkray";
336 appinf.apiVersion = apiver;
338 vkEnumerateInstanceLayerProperties(&inst_layers_count, 0);
339 inst_layers = malloc_nf(inst_layers_count * sizeof *inst_layers);
340 vkEnumerateInstanceLayerProperties(&inst_layers_count, inst_layers);
342 vkEnumerateInstanceExtensionProperties(0, &inst_ext_count, 0);
343 inst_ext = malloc_nf(inst_ext_count * sizeof *inst_ext);
344 vkEnumerateInstanceExtensionProperties(0, &inst_ext_count, inst_ext);
347 for(i=0; i<inst_layers_count; i++) {
348 printf(" - %s: %s\n", inst_layers[i].layerName, inst_layers[i].description);
350 printf("Instance extensions:\n");
351 for(i=0; i<inst_ext_count; i++) {
352 printf(" - %s\n", inst_ext[i].extensionName);
355 have_debug_report = have_ext(inst_ext, inst_ext_count, "VK_KHR_debug_report");
357 for(i=0; i<ARRSZ(known_layer_list); i++) {
358 if(have_inst_layer(known_layer_list[i])) {
359 layers[nlayers++] = known_layer_list[i];
362 for(i=0; i<ARRSZ(known_instext_list); i++) {
363 if(have_ext(inst_ext, inst_ext_count, known_instext_list[i].name)) {
364 ext[next++] = known_instext_list[i].name;
365 } else if(known_instext_list[i].required) {
366 fprintf(stderr, "Vulkan implementation lacks required instance extension: %s\n",
367 known_instext_list[i].name);
372 memset(&instinf, 0, sizeof instinf);
373 instinf.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
374 instinf.pApplicationInfo = &appinf;
375 instinf.enabledLayerCount = nlayers;
376 instinf.ppEnabledLayerNames = layers;
377 instinf.enabledExtensionCount = next;
378 instinf.ppEnabledExtensionNames = ext;
379 if(vkCreateInstance(&instinf, 0, &vk) != 0) {
380 fprintf(stderr, "failed to create vulkan instance\n");
387 static int create_surface(void)
390 VkXlibSurfaceCreateInfoKHR xinf = {0};
391 xinf.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
395 if(vkCreateXlibSurfaceKHR(vk, &xinf, 0, &vksurf) != 0) {
396 fprintf(stderr, "failed to create Xlib window surface\n");
400 VkXcbSurfaceCreateInfoKHR xinf = {0};
401 xinf.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
402 xinf.connection = XGetXCBConnection(dpy);
403 xinf.window = (xcb_window_t)win;
405 if(vkCreateXcbSurfaceKHR(vk, &xinf, 0, &vksurf) != 0) {
406 fprintf(stderr, "failed to create XCB window surface\n");
412 int choose_phys_dev(void)
414 uint32_t i, num_pdev, num_qfam, score, best_score, best_dev;
415 VkPhysicalDevice *pdev;
416 VkPhysicalDeviceProperties pdevprop;
417 VkQueueFamilyProperties *qfam;
420 vkEnumeratePhysicalDevices(vk, &num_pdev, 0);
422 fprintf(stderr, "no vulkan devices found\n");
425 pdev = malloc_nf(num_pdev * sizeof *pdev);
426 vkEnumeratePhysicalDevices(vk, &num_pdev, pdev);
428 printf("Found %d physical devices\n", num_pdev);
432 for(i=0; i<num_pdev; i++) {
433 if((score = eval_pdev_score(pdev[i])) && score > best_score) {
438 vkGetPhysicalDeviceProperties(pdev[i], &pdevprop);
439 printf(" %d: %s (score: %d)\n", i, pdevprop.deviceName, score);
442 fprintf(stderr, "no suitable vulkan device found\n");
446 vkpdev = pdev[best_dev];
448 vkGetPhysicalDeviceQueueFamilyProperties(vkpdev, &num_qfam, 0);
449 qfam = malloc_nf(num_qfam * sizeof *qfam);
450 vkGetPhysicalDeviceQueueFamilyProperties(vkpdev, &num_qfam, qfam);
453 for(i=0; i<num_qfam; i++) {
454 vkGetPhysicalDeviceSurfaceSupportKHR(vkpdev, i, vksurf, &can_pres);
455 if(qfam[i].queueCount && (qfam[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) && can_pres) {
456 vkqfam_maxq = qfam[i].queueCount;
468 static int create_device(void)
471 VkDeviceQueueCreateInfo qinf = {0};
472 VkPhysicalDeviceFeatures feat = {0};
473 VkDeviceCreateInfo devinf = {0};
474 const char *ext[ARRSZ(known_devext_list) + 16];
477 vkEnumerateDeviceExtensionProperties(vkpdev, 0, &dev_ext_count, 0);
478 dev_ext = malloc_nf(dev_ext_count * sizeof *dev_ext);
479 vkEnumerateDeviceExtensionProperties(vkpdev, 0, &dev_ext_count, dev_ext);
482 for(i=0; i<ARRSZ(known_devext_list); i++) {
483 if(have_ext(dev_ext, dev_ext_count, known_devext_list[i].name)) {
484 ext[num_ext++] = known_devext_list[i].name;
485 } else if(known_devext_list[i].required) {
486 fprintf(stderr, "Vulkan device lacks required extension: %s\n",
487 known_devext_list[i].name);
492 if(initflags & VKINIT_RAY) {
493 if(have_ext(dev_ext, dev_ext_count, "VK_KHR_acceleration_structure") &&
494 have_ext(dev_ext, dev_ext_count, "VK_KHR_ray_tracing_pipeline")) {
495 ext[num_ext++] = "VK_KHR_acceleration_structure";
496 ext[num_ext++] = "VK_KHR_ray_tracing_pipeline";
498 initflags &= ~VKINIT_RAY;
502 qinf.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
503 qinf.queueFamilyIndex = vkqfam_idx;
505 qinf.pQueuePriorities = &prio;
507 devinf.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
508 devinf.pQueueCreateInfos = &qinf;
509 devinf.queueCreateInfoCount = 1;
510 devinf.pEnabledFeatures = &feat;
511 devinf.enabledExtensionCount = num_ext;
512 devinf.ppEnabledExtensionNames = ext;
514 if(vkCreateDevice(vkpdev, &devinf, 0, &vkdev) != 0) {
515 fprintf(stderr, "failed to create vulkan device\n");
519 vkGetDeviceQueue(vkdev, vkqfam_idx, 0, &vkq);
523 static int create_swapchain(void)
527 VkSwapchainCreateInfoKHR scinf = {0};
528 VkImageViewCreateInfo ivinf;
530 if(vksc_extent.width <= 0 || vksc_extent.height <= 0) {
534 scinf.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
535 scinf.surface = vksurf;
536 scinf.minImageCount = 2;
537 scinf.imageFormat = vksurf_fmt[vksurf_selfmt].format;
538 scinf.imageColorSpace = vksurf_fmt[vksurf_selfmt].colorSpace;
539 scinf.imageExtent = vksc_extent;
540 scinf.imageArrayLayers = 1;
541 scinf.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
542 scinf.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
543 scinf.preTransform = vksurf_caps.currentTransform;
544 scinf.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
545 scinf.presentMode = VK_PRESENT_MODE_FIFO_KHR;
546 scinf.clipped = VK_TRUE;
548 if(vkCreateSwapchainKHR(vkdev, &scinf, 0, &vksc) != 0) {
549 fprintf(stderr, "failed to create swapchain\n");
553 if(!vksc_img || vksc_numimg != num) {
555 vkGetSwapchainImagesKHR(vkdev, vksc, &num, 0);
556 vksc_img = malloc_nf(num * sizeof *vksc_img);
557 vkGetSwapchainImagesKHR(vkdev, vksc, &num, vksc_img);
559 if(!vksc_view || vksc_numimg != num) {
561 vksc_view = malloc_nf(num * sizeof *vksc_view);
565 for(i=0; i<num; i++) {
566 memset(&ivinf, 0, sizeof ivinf);
567 ivinf.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
568 ivinf.image = vksc_img[i];
569 ivinf.format = vksurf_fmt[vksurf_selfmt].format;
570 ivinf.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
571 ivinf.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
572 ivinf.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
573 ivinf.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
574 ivinf.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
575 ivinf.subresourceRange.levelCount = 1;
576 ivinf.subresourceRange.layerCount = 1;
577 ivinf.viewType = VK_IMAGE_VIEW_TYPE_2D;
579 if(vkCreateImageView(vkdev, &ivinf, 0, vksc_view + i) != 0) {
580 fprintf(stderr, "failed to create image view (%d)\n", i);
588 static int eval_pdev_score(VkPhysicalDevice dev)
591 uint32_t i, num_fmt, num_qfam, num_ext;
592 VkQueueFamilyProperties *qfam;
593 VkExtensionProperties *ext;
594 VkPhysicalDeviceProperties prop;
595 VkPhysicalDeviceFeatures feat;
596 VkSurfaceFormatKHR *sfmt;
599 vkGetPhysicalDeviceProperties(dev, &prop);
600 vkGetPhysicalDeviceFeatures(dev, &feat);
602 /* check if we have the swapchain extension */
603 vkEnumerateDeviceExtensionProperties(dev, 0, &num_ext, 0);
604 ext = malloc_nf(num_ext * sizeof *ext);
605 vkEnumerateDeviceExtensionProperties(dev, 0, &num_ext, ext);
607 if(!have_ext(ext, num_ext, "VK_KHR_swapchain")) {
612 /* populate format and present modes arrays, and make sure we have some of each */
613 vkGetPhysicalDeviceSurfaceFormatsKHR(dev, vksurf, &num_fmt, 0);
618 sfmt = malloc_nf(num_fmt * sizeof *sfmt);
619 vkGetPhysicalDeviceSurfaceFormatsKHR(dev, vksurf, &num_fmt, sfmt);
621 vkGetPhysicalDeviceSurfaceCapabilitiesKHR(dev, vksurf, &vksurf_caps);
623 /* find a queue family which can do graphics and can present */
624 vkGetPhysicalDeviceQueueFamilyProperties(dev, &num_qfam, 0);
625 qfam = malloc_nf(num_qfam * sizeof *qfam);
626 vkGetPhysicalDeviceQueueFamilyProperties(dev, &num_qfam, qfam);
628 for(i=0; i<num_qfam; i++) {
629 vkGetPhysicalDeviceSurfaceSupportKHR(dev, i, vksurf, &can_pres);
630 if(qfam[i].queueCount && (qfam[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) && can_pres) {
635 switch(prop.deviceType) {
636 case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
639 case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
642 case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
649 if(initflags & VKINIT_RAY) {
650 if(have_ext(ext, num_ext, "VK_KHR_acceleration_structure") &&
651 have_ext(ext, num_ext, "VK_KHR_ray_tracing_pipeline")) {
662 static int choose_pixfmt(void)
664 static const VkFormat pref[] = {
665 VK_FORMAT_B8G8R8_UNORM,
666 VK_FORMAT_R8G8B8_UNORM,
667 VK_FORMAT_B8G8R8A8_UNORM,
668 VK_FORMAT_R8G8B8A8_UNORM
673 vkGetPhysicalDeviceSurfaceFormatsKHR(vkpdev, vksurf, &num_fmt, 0);
674 if(!num_fmt) return -1;
675 vksurf_fmt = malloc_nf(num_fmt * sizeof *vksurf_fmt);
676 vkGetPhysicalDeviceSurfaceFormatsKHR(vkpdev, vksurf, &num_fmt, vksurf_fmt);
679 for(i=0; i<num_fmt; i++) {
680 if(vksurf_fmt[i].colorSpace != VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
683 for(j=0; j<sizeof pref / sizeof *pref; j++) {
684 if(vksurf_fmt[i].format == pref[j]) {
686 vksurf_numfmt = num_fmt;
697 static int have_inst_layer(const char *name)
700 for(i=0; i<inst_layers_count; i++) {
701 if(strcmp(inst_layers[i].layerName, name) == 0) {
708 static int have_ext(VkExtensionProperties *ext, int next, const char *name)
711 for(i=0; i<next; i++) {
712 if(strcmp(ext[i].extensionName, name) == 0) {