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 }