1 /// Vulkan Window System Integration module 2 /// 3 /// The Vulkan backend supports integration with the following windowing systems: 4 /// 5 /// $(UL 6 /// $(LI Linux) 7 /// $(UL 8 /// $(LI <a href="https://code.dlang.org/packages/wayland">Wayland</a>) 9 /// $(LI <a href="https://code.dlang.org/packages/xcb-d">XCB</a>) 10 /// ) 11 /// $(LI Windows) 12 /// $(LI <a href="https://www.glfw.org/docs/3.3/vulkan_guide.html">GLFW</a> via <a href="https://code.dlang.org/packages/bindbc-glfw">bindbc-glfw</a>) 13 /// ) 14 module gfx.vulkan.wsi; 15 16 import core.time : Duration; 17 18 import gfx.bindings.vulkan; 19 20 import gfx.core.rc; 21 import gfx.graal; 22 import gfx.graal.format; 23 import gfx.graal.image; 24 import gfx.graal.sync; 25 import gfx.vulkan; 26 import gfx.vulkan.image; 27 import gfx.vulkan.error; 28 import gfx.vulkan.sync; 29 30 import std.exception : enforce; 31 32 // instance level extensions 33 34 enum surfaceInstanceExtension = "VK_KHR_surface"; 35 36 version(Windows) { 37 enum win32SurfaceInstanceExtension = "VK_KHR_win32_surface"; 38 } 39 version(linux) { 40 enum waylandSurfaceInstanceExtension = "VK_KHR_wayland_surface"; 41 enum xcbSurfaceInstanceExtension = "VK_KHR_xcb_surface"; 42 } 43 44 // device level extensions 45 46 enum swapChainDeviceExtension = "VK_KHR_swapchain"; 47 48 /// Extensions necessary to open Vulkan surfaces on the platform window system 49 @property immutable(string[]) surfaceInstanceExtensions() 50 { 51 version(GfxOffscreen) { 52 return []; 53 } 54 else version (glfw) { 55 return glfwInstanceExtensions; 56 } 57 else version (linux) { 58 import std.process : environment; 59 60 const sessionType = environment.get("XDG_SESSION_TYPE"); 61 if (sessionType == "wayland") { 62 return [ 63 surfaceInstanceExtension, waylandSurfaceInstanceExtension, xcbSurfaceInstanceExtension 64 ]; 65 } 66 else { 67 return [ 68 surfaceInstanceExtension, xcbSurfaceInstanceExtension 69 ]; 70 } 71 } 72 else version(Windows) { 73 return [ 74 surfaceInstanceExtension, win32SurfaceInstanceExtension 75 ]; 76 } 77 } 78 79 80 version(VkWayland) { 81 import wayland.client : WlDisplay, WlSurface; 82 83 /// Extensions necessary to open a Wayland Vulkan surface 84 immutable string[] waylandSurfaceInstanceExtensions = [ 85 surfaceInstanceExtension, waylandSurfaceInstanceExtension 86 ]; 87 88 Surface createVulkanWaylandSurface(Instance graalInst, WlDisplay wlDpy, WlSurface wlSurf) 89 { 90 auto inst = enforce( 91 cast(VulkanInstance)graalInst, 92 "createVulkanWaylandSurface called with non-vulkan instance" 93 ); 94 95 VkWaylandSurfaceCreateInfoKHR sci; 96 sci.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; 97 sci.display = wlDpy.native; 98 sci.surface = wlSurf.proxy; 99 100 VkSurfaceKHR vkSurf; 101 vulkanEnforce( 102 inst.vk.CreateWaylandSurfaceKHR(inst.vkObj, &sci, null, &vkSurf), 103 "Could not create Vulkan Wayland Surface" 104 ); 105 106 return new VulkanSurface(vkSurf, inst); 107 } 108 } 109 110 version(VkXcb) { 111 import xcb.xcb : xcb_connection_t, xcb_window_t; 112 113 /// Extensions necessary to open an XCB Vulkan surface 114 immutable string[] xcbSurfaceInstanceExtensions = [ 115 surfaceInstanceExtension, xcbSurfaceInstanceExtension 116 ]; 117 118 Surface createVulkanXcbSurface(Instance graalInst, xcb_connection_t* conn, xcb_window_t win) 119 { 120 auto inst = enforce( 121 cast(VulkanInstance)graalInst, 122 "createVulkanXcbSurface called with non-vulkan instance" 123 ); 124 125 VkXcbSurfaceCreateInfoKHR sci; 126 sci.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR; 127 sci.connection = conn; 128 sci.window = win; 129 130 VkSurfaceKHR vkSurf; 131 vulkanEnforce( 132 inst.vk.CreateXcbSurfaceKHR(inst.vkObj, &sci, null, &vkSurf), 133 "Could not create Vulkan Xcb Surface" 134 ); 135 136 return new VulkanSurface(vkSurf, inst); 137 } 138 } 139 140 version(Windows) { 141 import core.sys.windows.windef : HINSTANCE, HWND; 142 143 /// Extensions necessary to open a Win32 Vulkan surface 144 immutable string[] win32SurfaceInstanceExtensions = [ 145 surfaceInstanceExtension, win32SurfaceInstanceExtension 146 ]; 147 148 Surface createVulkanWin32Surface(Instance graalInst, HINSTANCE hinstance, HWND hwnd) { 149 auto inst = enforce( 150 cast(VulkanInstance)graalInst, 151 "createVulkanXcbSurface called with non-vulkan instance" 152 ); 153 154 VkWin32SurfaceCreateInfoKHR sci; 155 sci.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; 156 sci.hinstance = hinstance; 157 sci.hwnd = hwnd; 158 159 VkSurfaceKHR vkSurf; 160 vulkanEnforce( 161 inst.vk.CreateWin32SurfaceKHR(inst.vkObj, &sci, null, &vkSurf), 162 "Could not create Vulkan Xcb Surface" 163 ); 164 165 return new VulkanSurface(vkSurf, inst); 166 } 167 } 168 169 version(glfw) { 170 /// Extensions necessary to open a GLFW Vulkan surface 171 @property immutable(string[]) glfwSurfaceInstanceExtensions() { 172 return glfwInstanceExtensions; 173 } 174 175 Surface createVulkanGlfwSurface(Instance graalInst, GLFWwindow* window) { 176 auto inst = enforce( 177 cast(VulkanInstance)graalInst, 178 "createVulkanGlfwSurface called with non-vulkan instance" 179 ); 180 181 VkSurfaceKHR vkSurf; 182 vulkanEnforce( 183 glfwCreateWindowSurface(inst.vkObj, window, null, &vkSurf), 184 "Could not create Vulkan GLFW Surface" 185 ); 186 187 return new VulkanSurface(vkSurf, inst); 188 } 189 190 // TODO: Add createGlfwGlSurface 191 } 192 193 194 package: 195 196 version(glfw) { 197 import bindbc.glfw : GLFWwindow; 198 import gfx.bindings.vulkan : VkInstance; 199 200 extern(C) @nogc nothrow { 201 const(char)** glfwGetRequiredInstanceExtensions(uint*); 202 VkResult glfwCreateWindowSurface( 203 VkInstance, GLFWwindow*, const(VkAllocationCallbacks)*, VkSurfaceKHR* 204 ); 205 } 206 207 @property immutable(string[]) glfwInstanceExtensions() { 208 import std.algorithm.iteration : map; 209 import std.array : array; 210 import std.string : fromStringz; 211 212 uint extensionCount; 213 const glfwRequiredInstanceExtensions = 214 glfwGetRequiredInstanceExtensions(&extensionCount)[0..extensionCount]; 215 immutable extensions = glfwRequiredInstanceExtensions.map!(extension => extension.fromStringz).array; 216 return extensions; 217 } 218 } 219 220 class VulkanSurface : VulkanInstObj!(VkSurfaceKHR), Surface 221 { 222 mixin(atomicRcCode); 223 224 this(VkSurfaceKHR vkObj, VulkanInstance inst) 225 { 226 super(vkObj, inst); 227 } 228 229 override void dispose() { 230 inst.vk.DestroySurfaceKHR(vkInst, vkObj, null); 231 super.dispose(); 232 } 233 } 234 235 class VulkanSwapchain : VulkanDevObj!(VkSwapchainKHR, "DestroySwapchainKHR"), Swapchain 236 { 237 mixin(atomicRcCode); 238 239 this(VkSwapchainKHR vkObj, VulkanDevice dev, Surface surf, uint[2] size, 240 Format format, ImageUsage usage) 241 { 242 super(vkObj, dev); 243 _surf = surf; 244 _size = size; 245 _format = format; 246 _usage = usage; 247 } 248 249 override void dispose() { 250 super.dispose(); 251 _surf.unload(); 252 } 253 254 override @property Device device() { 255 return dev; 256 } 257 258 override @property Surface surface() { 259 return _surf; 260 } 261 262 override @property Format format() { 263 return _format; 264 } 265 266 // not releasing images on purpose, the lifetime is owned by implementation 267 268 override @property ImageBase[] images() { 269 270 if (!_images.length) { 271 uint count; 272 vulkanEnforce( 273 vk.GetSwapchainImagesKHR(vkDev, vkObj, &count, null), 274 "Could not get vulkan swap chain images" 275 ); 276 auto vkImgs = new VkImage[count]; 277 vulkanEnforce( 278 vk.GetSwapchainImagesKHR(vkDev, vkObj, &count, &vkImgs[0]), 279 "Could not get vulkan swap chain images" 280 ); 281 282 import std.algorithm : map; 283 import std.array : array; 284 _images = vkImgs 285 .map!((VkImage vkImg) { 286 const info = ImageInfo.d2(_size[0], _size[1]) 287 .withFormat(_format) 288 .withUsage(_usage); 289 auto img = new VulkanImageBase(vkImg, dev, info); 290 return cast(ImageBase)img; 291 }) 292 .array; 293 } 294 295 return _images; 296 } 297 298 override ImageAcquisition acquireNextImage(Semaphore graalSem, 299 Duration timeout) 300 { 301 auto sem = enforce( 302 cast(VulkanSemaphore)graalSem, 303 "a non vulkan semaphore was passed acquireNextImage" 304 ); 305 306 ulong vkTimeout = timeout.total!"nsecs"; 307 import core.time : dur; 308 if (timeout < dur!"nsecs"(0)) { 309 vkTimeout = ulong.max; 310 } 311 312 uint img; 313 const res = vk.AcquireNextImageKHR(vkDev, vkObj, vkTimeout, sem.vkObj, VK_NULL_ND_HANDLE, &img); 314 315 switch (res) 316 { 317 case VK_SUCCESS: 318 return ImageAcquisition.makeOk(img); 319 case VK_SUBOPTIMAL_KHR: 320 return ImageAcquisition.makeSuboptimal(img); 321 case VK_NOT_READY: 322 case VK_TIMEOUT: 323 return ImageAcquisition.makeNotReady(); 324 case VK_ERROR_OUT_OF_DATE_KHR: 325 return ImageAcquisition.makeOutOfDate(); 326 default: 327 vulkanEnforce(res, "Could not acquire next vulkan image"); 328 assert(false); 329 } 330 } 331 332 private Rc!Surface _surf; 333 private ImageBase[] _images; 334 private uint[2] _size; 335 private Format _format; 336 private ImageUsage _usage; 337 }