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 : WlDisplay, WlSurface; 38 import xcb.xcb : xcb_connection_t, xcb_window_t; 39 40 // TODO: fall back from wayland to XCB 41 enum surfaceInstanceExtensions = [ 42 surfaceExtension, waylandSurfaceExtension, xcbSurfaceExtension 43 ]; 44 45 Surface createVulkanWaylandSurface(Instance graalInst, WlDisplay wlDpy, WlSurface wlSurf) 46 { 47 auto inst = enforce( 48 cast(VulkanInstance)graalInst, 49 "createVulkanWaylandSurface called with non-vulkan instance" 50 ); 51 52 VkWaylandSurfaceCreateInfoKHR sci; 53 sci.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; 54 sci.display = wlDpy.native; 55 sci.surface = wlSurf.proxy; 56 57 VkSurfaceKHR vkSurf; 58 vulkanEnforce( 59 inst.vk.CreateWaylandSurfaceKHR(inst.vkObj, &sci, null, &vkSurf), 60 "Could not create Vulkan Wayland Surface" 61 ); 62 63 return new VulkanSurface(vkSurf, inst); 64 } 65 66 Surface createVulkanXcbSurface(Instance graalInst, xcb_connection_t* conn, xcb_window_t win) 67 { 68 auto inst = enforce( 69 cast(VulkanInstance)graalInst, 70 "createVulkanXcbSurface called with non-vulkan instance" 71 ); 72 73 VkXcbSurfaceCreateInfoKHR sci; 74 sci.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR; 75 sci.connection = conn; 76 sci.window = win; 77 78 VkSurfaceKHR vkSurf; 79 vulkanEnforce( 80 inst.vk.CreateXcbSurfaceKHR(inst.vkObj, &sci, null, &vkSurf), 81 "Could not create Vulkan Xcb Surface" 82 ); 83 84 return new VulkanSurface(vkSurf, inst); 85 } 86 } 87 88 version(Windows) { 89 90 import core.sys.windows.windef : HINSTANCE, HWND; 91 92 enum surfaceInstanceExtensions = [ 93 surfaceExtension, win32SurfaceExtension 94 ]; 95 96 Surface createVulkanWin32Surface(Instance graalInst, HINSTANCE hinstance, HWND hwnd) { 97 auto inst = enforce( 98 cast(VulkanInstance)graalInst, 99 "createVulkanXcbSurface called with non-vulkan instance" 100 ); 101 102 VkWin32SurfaceCreateInfoKHR sci; 103 sci.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; 104 sci.hinstance = hinstance; 105 sci.hwnd = hwnd; 106 107 VkSurfaceKHR vkSurf; 108 vulkanEnforce( 109 inst.vk.CreateWin32SurfaceKHR(inst.vkObj, &sci, null, &vkSurf), 110 "Could not create Vulkan Xcb Surface" 111 ); 112 113 return new VulkanSurface(vkSurf, inst); 114 } 115 } 116 version(GfxOffscreen) { 117 enum surfaceInstanceExtensions = []; 118 } 119 120 121 package: 122 123 class VulkanSurface : VulkanInstObj!(VkSurfaceKHR), Surface 124 { 125 mixin(atomicRcCode); 126 127 this(VkSurfaceKHR vkObj, VulkanInstance inst) 128 { 129 super(vkObj, inst); 130 } 131 132 override void dispose() { 133 inst.vk.DestroySurfaceKHR(vkInst, vkObj, null); 134 super.dispose(); 135 } 136 } 137 138 class VulkanSwapchain : VulkanDevObj!(VkSwapchainKHR, "DestroySwapchainKHR"), Swapchain 139 { 140 mixin(atomicRcCode); 141 142 this(VkSwapchainKHR vkObj, VulkanDevice dev, uint[2] size, Format format) 143 { 144 super(vkObj, dev); 145 _size = size; 146 _format = format; 147 } 148 149 override @property Format format() { 150 return _format; 151 } 152 153 // not releasing images on purpose, the lifetime is owned by implementation 154 155 override @property ImageBase[] images() { 156 157 if (!_images.length) { 158 uint count; 159 vulkanEnforce( 160 vk.GetSwapchainImagesKHR(vkDev, vkObj, &count, null), 161 "Could not get vulkan swap chain images" 162 ); 163 auto vkImgs = new VkImage[count]; 164 vulkanEnforce( 165 vk.GetSwapchainImagesKHR(vkDev, vkObj, &count, &vkImgs[0]), 166 "Could not get vulkan swap chain images" 167 ); 168 169 import std.algorithm : map; 170 import std.array : array; 171 _images = vkImgs 172 .map!((VkImage vkImg) { 173 const info = ImageInfo.d2(_size[0], _size[1]).withFormat(_format); 174 auto img = new VulkanImageBase(vkImg, dev, info); 175 return cast(ImageBase)img; 176 }) 177 .array; 178 } 179 180 return _images; 181 } 182 183 override uint acquireNextImage(Duration timeout, Semaphore graalSemaphore, out bool suboptimal) 184 { 185 auto sem = enforce( 186 cast(VulkanSemaphore)graalSemaphore, 187 "a non vulkan semaphore was passed" 188 ); 189 190 ulong vkTimeout = timeout.total!"nsecs"; 191 import core.time : dur; 192 if (timeout < dur!"nsecs"(0)) { 193 vkTimeout = ulong.max; 194 } 195 196 uint img; 197 const res = vk.AcquireNextImageKHR(vkDev, vkObj, vkTimeout, sem.vkObj, VK_NULL_ND_HANDLE, &img); 198 199 if (res == VK_SUBOPTIMAL_KHR) { 200 suboptimal = true; 201 } 202 else { 203 enforce(res == VK_SUCCESS, "Could not acquire next vulkan image"); 204 } 205 206 return img; 207 } 208 209 private ImageBase[] _images; 210 private uint[2] _size; 211 private Format _format; 212 }