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;
38 
39     // TODO: fall back from wayland to XCB
40     enum surfaceInstanceExtensions = [
41         surfaceExtension, waylandSurfaceExtension, xcbSurfaceExtension
42     ];
43 
44     Surface createVulkanWaylandSurface(Instance graalInst, WlDisplay wlDpy, WlSurface wlSurf)
45     {
46         auto inst = enforce(
47             cast(VulkanInstance)graalInst,
48             "createVulkanWaylandSurface called with non-vulkan instance"
49         );
50 
51         VkWaylandSurfaceCreateInfoKHR sci;
52         sci.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR;
53         sci.display = wlDpy.native;
54         sci.surface = wlSurf.proxy;
55 
56         VkSurfaceKHR vkSurf;
57         vulkanEnforce(
58             inst.cmds.createWaylandSurfaceKHR(inst.vk, &sci, null, &vkSurf),
59             "Could not create Vulkan Wayland Surface"
60         );
61 
62         return new VulkanSurface(vkSurf, inst);
63     }
64 }
65 
66 version(GfxVulkanWin32) {
67     enum surfaceInstanceExtensions = [
68         surfaceExtension, win32SurfaceExtension
69     ];
70 }
71 version(GfxOffscreen) {
72     enum surfaceInstanceExtensions = [];
73 }
74 
75 
76 package:
77 
78 class VulkanSurface : VulkanInstObj!(VkSurfaceKHR), Surface
79 {
80     mixin(atomicRcCode);
81 
82     this(VkSurfaceKHR vk, VulkanInstance inst)
83     {
84         super(vk, inst);
85     }
86 
87     override void dispose() {
88         inst.cmds.destroySurfaceKHR(vkInst, vk, null);
89         super.dispose();
90     }
91 }
92 
93 class VulkanSwapchain : VulkanDevObj!(VkSwapchainKHR, "destroySwapchainKHR"), Swapchain
94 {
95     mixin(atomicRcCode);
96 
97     this(VkSwapchainKHR vk, VulkanDevice dev, uint[2] size, Format format)
98     {
99         super(vk, dev);
100         _size = size;
101         _format = format;
102     }
103 
104     override @property Format format() {
105         return _format;
106     }
107 
108     // not releasing images on purpose, the lifetime is owned by implementation
109 
110     override @property Image[] images() {
111 
112         if (!_images.length) {
113             uint count;
114             vulkanEnforce(
115                 cmds.getSwapchainImagesKHR(vkDev, vk, &count, null),
116                 "Could not get vulkan swap chain images"
117             );
118             auto vkImgs = new VkImage[count];
119             vulkanEnforce(
120                 cmds.getSwapchainImagesKHR(vkDev, vk, &count, &vkImgs[0]),
121                 "Could not get vulkan swap chain images"
122             );
123 
124             import std.algorithm : map;
125             import std.array : array;
126             _images = vkImgs
127                     .map!((VkImage vkImg) {
128                         const dims = ImageDims.d2(_size[0], _size[1]);
129                         auto img = new VulkanImage(vkImg, dev, ImageType.d2, dims, _format);
130                         return cast(Image)img;
131                     })
132                     .array;
133         }
134 
135         return _images;
136     }
137 
138     override uint acquireNextImage(Duration timeout, Semaphore graalSemaphore, out bool suboptimal)
139     {
140         auto sem = enforce(
141             cast(VulkanSemaphore)graalSemaphore,
142             "a non vulkan semaphore was passed"
143         );
144 
145         ulong vkTimeout = timeout.total!"nsecs";
146         import core.time : dur;
147         if (timeout < dur!"nsecs"(0)) {
148             vkTimeout = ulong.max;
149         }
150 
151         uint img;
152         const res = cmds.acquireNextImageKHR(vkDev, vk, vkTimeout, sem.vk, null, &img);
153 
154         if (res == VK_SUBOPTIMAL_KHR) {
155             suboptimal = true;
156         }
157         else {
158             enforce(res == VK_SUCCESS, "Could not acquire next vulkan image");
159         }
160 
161         return img;
162     }
163 
164     private Image[] _images;
165     private uint[2] _size;
166     private Format _format;
167 }