1 /// Vulkan Window System Integration module 2 module gfx.vulkan.wsi; 3 4 import core.time : Duration; 5 6 import gfx.bindings.vulkan; 7 8 import gfx.core.rc; 9 import gfx.graal; 10 import gfx.graal.format; 11 import gfx.graal.image; 12 import gfx.graal.sync; 13 import gfx.vulkan; 14 import gfx.vulkan.image; 15 import gfx.vulkan.error; 16 import gfx.vulkan.sync; 17 18 import std.exception : enforce; 19 20 // instance level extensions 21 22 enum surfaceExtension = "VK_KHR_surface"; 23 24 version(Windows) { 25 enum win32SurfaceExtension = "VK_KHR_win32_surface"; 26 } 27 version(linux) { 28 enum waylandSurfaceExtension = "VK_KHR_wayland_surface"; 29 enum xcbSurfaceExtension = "VK_KHR_xcb_surface"; 30 } 31 32 // device level extensions 33 34 enum swapChainExtension = "VK_KHR_swapchain"; 35 36 version(linux) { 37 import wayland.client; 38 39 // TODO: fall back from wayland to XCB 40 enum surfaceInstanceExtensions = [ 41 surfaceExtension, waylandSurfaceExtension, xcbSurfaceExtension 42 ]; 43 44 Surface createVulkanWaylandSurface(Instance graalInst, WlDisplay wlDpy, WlSurface wlSurf) 45 { 46 auto inst = enforce( 47 cast(VulkanInstance)graalInst, 48 "createVulkanWaylandSurface called with non-vulkan instance" 49 ); 50 51 VkWaylandSurfaceCreateInfoKHR sci; 52 sci.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; 53 sci.display = wlDpy.native; 54 sci.surface = wlSurf.proxy; 55 56 VkSurfaceKHR vkSurf; 57 vulkanEnforce( 58 inst.cmds.createWaylandSurfaceKHR(inst.vk, &sci, null, &vkSurf), 59 "Could not create Vulkan Wayland Surface" 60 ); 61 62 return new VulkanSurface(vkSurf, inst); 63 } 64 } 65 66 version(GfxVulkanWin32) { 67 enum surfaceInstanceExtensions = [ 68 surfaceExtension, win32SurfaceExtension 69 ]; 70 } 71 version(GfxOffscreen) { 72 enum surfaceInstanceExtensions = []; 73 } 74 75 76 package: 77 78 class VulkanSurface : VulkanInstObj!(VkSurfaceKHR), Surface 79 { 80 mixin(atomicRcCode); 81 82 this(VkSurfaceKHR vk, VulkanInstance inst) 83 { 84 super(vk, inst); 85 } 86 87 override void dispose() { 88 inst.cmds.destroySurfaceKHR(vkInst, vk, null); 89 super.dispose(); 90 } 91 } 92 93 class VulkanSwapchain : VulkanDevObj!(VkSwapchainKHR, "destroySwapchainKHR"), Swapchain 94 { 95 mixin(atomicRcCode); 96 97 this(VkSwapchainKHR vk, VulkanDevice dev, uint[2] size, Format format) 98 { 99 super(vk, dev); 100 _size = size; 101 _format = format; 102 } 103 104 override @property Format format() { 105 return _format; 106 } 107 108 // not releasing images on purpose, the lifetime is owned by implementation 109 110 override @property Image[] images() { 111 112 if (!_images.length) { 113 uint count; 114 vulkanEnforce( 115 cmds.getSwapchainImagesKHR(vkDev, vk, &count, null), 116 "Could not get vulkan swap chain images" 117 ); 118 auto vkImgs = new VkImage[count]; 119 vulkanEnforce( 120 cmds.getSwapchainImagesKHR(vkDev, vk, &count, &vkImgs[0]), 121 "Could not get vulkan swap chain images" 122 ); 123 124 import std.algorithm : map; 125 import std.array : array; 126 _images = vkImgs 127 .map!((VkImage vkImg) { 128 const dims = ImageDims.d2(_size[0], _size[1]); 129 auto img = new VulkanImage(vkImg, dev, ImageType.d2, dims, _format); 130 return cast(Image)img; 131 }) 132 .array; 133 } 134 135 return _images; 136 } 137 138 override uint acquireNextImage(Duration timeout, Semaphore graalSemaphore, out bool suboptimal) 139 { 140 auto sem = enforce( 141 cast(VulkanSemaphore)graalSemaphore, 142 "a non vulkan semaphore was passed" 143 ); 144 145 ulong vkTimeout = timeout.total!"nsecs"; 146 import core.time : dur; 147 if (timeout < dur!"nsecs"(0)) { 148 vkTimeout = ulong.max; 149 } 150 151 uint img; 152 const res = cmds.acquireNextImageKHR(vkDev, vk, vkTimeout, sem.vk, null, &img); 153 154 if (res == VK_SUBOPTIMAL_KHR) { 155 suboptimal = true; 156 } 157 else { 158 enforce(res == VK_SUCCESS, "Could not acquire next vulkan image"); 159 } 160 161 return img; 162 } 163 164 private Image[] _images; 165 private uint[2] _size; 166 private Format _format; 167 }