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 surfaceInstanceExtension = "VK_KHR_surface"; 23 24 version(Windows) { 25 enum win32SurfaceInstanceExtension = "VK_KHR_win32_surface"; 26 } 27 version(linux) { 28 enum waylandSurfaceInstanceExtension = "VK_KHR_wayland_surface"; 29 enum xcbSurfaceInstanceExtension = "VK_KHR_xcb_surface"; 30 } 31 32 // device level extensions 33 34 enum swapChainDeviceExtension = "VK_KHR_swapchain"; 35 36 version(GfxOffscreen) { 37 /// Extensions to open Vulkan surfaces on the platform window system 38 immutable string[] surfaceInstanceExtensions = []; 39 } 40 else { 41 version(linux) { 42 import wayland.client : WlDisplay, WlSurface; 43 import xcb.xcb : xcb_connection_t, xcb_window_t; 44 45 /// Extensions to open Vulkan surfaces on the platform window system 46 immutable string[] surfaceInstanceExtensions = [ 47 surfaceInstanceExtension, waylandSurfaceInstanceExtension, xcbSurfaceInstanceExtension 48 ]; 49 /// Extensions necessary to open a Wayland Vulkan surface 50 immutable string[] waylandSurfaceInstanceExtensions = [ 51 surfaceInstanceExtension, waylandSurfaceInstanceExtension 52 ]; 53 /// Extensions necessary to open an XCB Vulkan surface 54 immutable string[] xcbSurfaceInstanceExtensions = [ 55 surfaceInstanceExtension, xcbSurfaceInstanceExtension 56 ]; 57 58 Surface createVulkanWaylandSurface(Instance graalInst, WlDisplay wlDpy, WlSurface wlSurf) 59 { 60 auto inst = enforce( 61 cast(VulkanInstance)graalInst, 62 "createVulkanWaylandSurface called with non-vulkan instance" 63 ); 64 65 VkWaylandSurfaceCreateInfoKHR sci; 66 sci.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; 67 sci.display = wlDpy.native; 68 sci.surface = wlSurf.proxy; 69 70 VkSurfaceKHR vkSurf; 71 vulkanEnforce( 72 inst.vk.CreateWaylandSurfaceKHR(inst.vkObj, &sci, null, &vkSurf), 73 "Could not create Vulkan Wayland Surface" 74 ); 75 76 return new VulkanSurface(vkSurf, inst); 77 } 78 79 Surface createVulkanXcbSurface(Instance graalInst, xcb_connection_t* conn, xcb_window_t win) 80 { 81 auto inst = enforce( 82 cast(VulkanInstance)graalInst, 83 "createVulkanXcbSurface called with non-vulkan instance" 84 ); 85 86 VkXcbSurfaceCreateInfoKHR sci; 87 sci.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR; 88 sci.connection = conn; 89 sci.window = win; 90 91 VkSurfaceKHR vkSurf; 92 vulkanEnforce( 93 inst.vk.CreateXcbSurfaceKHR(inst.vkObj, &sci, null, &vkSurf), 94 "Could not create Vulkan Xcb Surface" 95 ); 96 97 return new VulkanSurface(vkSurf, inst); 98 } 99 } 100 101 version(Windows) { 102 103 import core.sys.windows.windef : HINSTANCE, HWND; 104 105 /// Extensions to open Vulkan surfaces on the platform window system 106 immutable string[] surfaceInstanceExtensions = [ 107 surfaceInstanceExtension, win32SurfaceInstanceExtension 108 ]; 109 /// Extensions necessary to open an Win32 Vulkan surface 110 immutable string[] win32SurfaceInstanceExtensions = [ 111 surfaceInstanceExtension, win32SurfaceInstanceExtension 112 ]; 113 114 Surface createVulkanWin32Surface(Instance graalInst, HINSTANCE hinstance, HWND hwnd) { 115 auto inst = enforce( 116 cast(VulkanInstance)graalInst, 117 "createVulkanXcbSurface called with non-vulkan instance" 118 ); 119 120 VkWin32SurfaceCreateInfoKHR sci; 121 sci.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; 122 sci.hinstance = hinstance; 123 sci.hwnd = hwnd; 124 125 VkSurfaceKHR vkSurf; 126 vulkanEnforce( 127 inst.vk.CreateWin32SurfaceKHR(inst.vkObj, &sci, null, &vkSurf), 128 "Could not create Vulkan Xcb Surface" 129 ); 130 131 return new VulkanSurface(vkSurf, inst); 132 } 133 } 134 } 135 136 137 package: 138 139 class VulkanSurface : VulkanInstObj!(VkSurfaceKHR), Surface 140 { 141 mixin(atomicRcCode); 142 143 this(VkSurfaceKHR vkObj, VulkanInstance inst) 144 { 145 super(vkObj, inst); 146 } 147 148 override void dispose() { 149 inst.vk.DestroySurfaceKHR(vkInst, vkObj, null); 150 super.dispose(); 151 } 152 } 153 154 class VulkanSwapchain : VulkanDevObj!(VkSwapchainKHR, "DestroySwapchainKHR"), Swapchain 155 { 156 mixin(atomicRcCode); 157 158 this(VkSwapchainKHR vkObj, VulkanDevice dev, Surface surf, uint[2] size, 159 Format format, ImageUsage usage) 160 { 161 super(vkObj, dev); 162 _surf = surf; 163 _size = size; 164 _format = format; 165 _usage = usage; 166 } 167 168 override void dispose() { 169 super.dispose(); 170 _surf.unload(); 171 } 172 173 override @property Device device() { 174 return dev; 175 } 176 177 override @property Surface surface() { 178 return _surf; 179 } 180 181 override @property Format format() { 182 return _format; 183 } 184 185 // not releasing images on purpose, the lifetime is owned by implementation 186 187 override @property ImageBase[] images() { 188 189 if (!_images.length) { 190 uint count; 191 vulkanEnforce( 192 vk.GetSwapchainImagesKHR(vkDev, vkObj, &count, null), 193 "Could not get vulkan swap chain images" 194 ); 195 auto vkImgs = new VkImage[count]; 196 vulkanEnforce( 197 vk.GetSwapchainImagesKHR(vkDev, vkObj, &count, &vkImgs[0]), 198 "Could not get vulkan swap chain images" 199 ); 200 201 import std.algorithm : map; 202 import std.array : array; 203 _images = vkImgs 204 .map!((VkImage vkImg) { 205 const info = ImageInfo.d2(_size[0], _size[1]) 206 .withFormat(_format) 207 .withUsage(_usage); 208 auto img = new VulkanImageBase(vkImg, dev, info); 209 return cast(ImageBase)img; 210 }) 211 .array; 212 } 213 214 return _images; 215 } 216 217 override ImageAcquisition acquireNextImage(Semaphore graalSem, 218 Duration timeout) 219 { 220 auto sem = enforce( 221 cast(VulkanSemaphore)graalSem, 222 "a non vulkan semaphore was passed acquireNextImage" 223 ); 224 225 ulong vkTimeout = timeout.total!"nsecs"; 226 import core.time : dur; 227 if (timeout < dur!"nsecs"(0)) { 228 vkTimeout = ulong.max; 229 } 230 231 uint img; 232 const res = vk.AcquireNextImageKHR(vkDev, vkObj, vkTimeout, sem.vkObj, VK_NULL_ND_HANDLE, &img); 233 234 switch (res) 235 { 236 case VK_SUCCESS: 237 return ImageAcquisition.makeOk(img); 238 case VK_SUBOPTIMAL_KHR: 239 return ImageAcquisition.makeSuboptimal(img); 240 case VK_NOT_READY: 241 case VK_TIMEOUT: 242 return ImageAcquisition.makeNotReady(); 243 case VK_ERROR_OUT_OF_DATE_KHR: 244 return ImageAcquisition.makeOutOfDate(); 245 default: 246 vulkanEnforce(res, "Could not acquire next vulkan image"); 247 assert(false); 248 } 249 } 250 251 private Rc!Surface _surf; 252 private ImageBase[] _images; 253 private uint[2] _size; 254 private Format _format; 255 private ImageUsage _usage; 256 }