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 }