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 immutable string[] 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 immutable string[] 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 immutable string[] 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, Surface surf, uint[2] size, Format format) 143 { 144 super(vkObj, dev); 145 _surf = surf; 146 _size = size; 147 _format = format; 148 } 149 150 override void dispose() { 151 super.dispose(); 152 _surf.unload(); 153 } 154 155 override @property Device device() { 156 return dev; 157 } 158 159 override @property Surface surface() { 160 return _surf; 161 } 162 163 override @property Format format() { 164 return _format; 165 } 166 167 // not releasing images on purpose, the lifetime is owned by implementation 168 169 override @property ImageBase[] images() { 170 171 if (!_images.length) { 172 uint count; 173 vulkanEnforce( 174 vk.GetSwapchainImagesKHR(vkDev, vkObj, &count, null), 175 "Could not get vulkan swap chain images" 176 ); 177 auto vkImgs = new VkImage[count]; 178 vulkanEnforce( 179 vk.GetSwapchainImagesKHR(vkDev, vkObj, &count, &vkImgs[0]), 180 "Could not get vulkan swap chain images" 181 ); 182 183 import std.algorithm : map; 184 import std.array : array; 185 _images = vkImgs 186 .map!((VkImage vkImg) { 187 const info = ImageInfo.d2(_size[0], _size[1]).withFormat(_format); 188 auto img = new VulkanImageBase(vkImg, dev, info); 189 return cast(ImageBase)img; 190 }) 191 .array; 192 } 193 194 return _images; 195 } 196 197 override uint acquireNextImage(Duration timeout, Semaphore graalSemaphore, out bool suboptimal) 198 { 199 auto sem = enforce( 200 cast(VulkanSemaphore)graalSemaphore, 201 "a non vulkan semaphore was passed acquireNextImage" 202 ); 203 204 ulong vkTimeout = timeout.total!"nsecs"; 205 import core.time : dur; 206 if (timeout < dur!"nsecs"(0)) { 207 vkTimeout = ulong.max; 208 } 209 210 uint img; 211 const res = vk.AcquireNextImageKHR(vkDev, vkObj, vkTimeout, sem.vkObj, VK_NULL_ND_HANDLE, &img); 212 213 if (res == VK_SUBOPTIMAL_KHR) { 214 suboptimal = true; 215 } 216 else { 217 vulkanEnforce(res, "Could not acquire next vulkan image"); 218 } 219 220 return img; 221 } 222 223 private Rc!Surface _surf; 224 private ImageBase[] _images; 225 private uint[2] _size; 226 private Format _format; 227 }