1 /// Vulkan implementation of GrAAL
2 module gfx.vulkan;
3 
4 import gfx.bindings.vulkan;
5 import gfx.graal;
6 
7 
8 // some standard layers
9 
10 enum lunarGValidationLayers = [
11     "VK_LAYER_LUNARG_core_validation",
12     "VK_LAYER_LUNARG_standard_validation",
13     "VK_LAYER_LUNARG_parameter_validation",
14 ];
15 
16 @property ApiProps vulkanApiProps() {
17     return ApiProps(
18         "vulkan", CoordSystem.rightHanded
19     );
20 }
21 
22 /// Load global level vulkan functions, and instance level layers and extensions
23 /// This function must be called before any other in this module
24 void vulkanInit()
25 {
26     synchronized {
27         _globCmds = loadVulkanGlobalCmds();
28         _instanceLayers = loadInstanceLayers();
29         _instanceExtensions = loadInstanceExtensions();
30     }
31 }
32 
33 struct VulkanVersion
34 {
35     import std.bitmanip : bitfields;
36     mixin(bitfields!(
37         uint, "patch", 12,
38         uint, "minor", 10,
39         uint, "major", 10,
40     ));
41 
42     this (in uint major, in uint minor, in uint patch) {
43         this.major = major; this.minor = minor; this.patch = patch;
44     }
45 
46     this (in uint vkVer) {
47         this(VK_VERSION_MAJOR(vkVer), VK_VERSION_MINOR(vkVer), VK_VERSION_PATCH(vkVer));
48     }
49 
50     static VulkanVersion fromUint(in uint vkVer) {
51         return *cast(VulkanVersion*)(cast(void*)&vkVer);
52     }
53 
54     uint toUint() const {
55         return *cast(uint*)(cast(void*)&this);
56     }
57 
58     string toString() {
59         import std.format : format;
60         return format("VulkanVersion(%s, %s, %s)", this.major, this.minor, this.patch);
61     }
62 }
63 
64 unittest {
65     const vkVer = VK_MAKE_VERSION(12, 7, 38);
66     auto vv = VulkanVersion.fromUint(vkVer);
67     assert(vv.major == 12);
68     assert(vv.minor == 7);
69     assert(vv.patch == 38);
70     assert(vv.toUint() == vkVer);
71 }
72 
73 struct VulkanLayerProperties
74 {
75     string layerName;
76     VulkanVersion specVer;
77     VulkanVersion implVer;
78     string description;
79 
80     @property VulkanExtensionProperties[] instanceExtensions()
81     {
82         return loadInstanceExtensions(layerName);
83     }
84 }
85 
86 struct VulkanExtensionProperties
87 {
88     string extensionName;
89     VulkanVersion specVer;
90 }
91 
92 /// Retrieve available instance level layer properties
93 @property VulkanLayerProperties[] vulkanInstanceLayers() {
94     return _instanceLayers;
95 }
96 /// Retrieve available instance level extensions properties
97 @property VulkanExtensionProperties[] vulkanInstanceExtensions()
98 {
99     return _instanceExtensions;
100 }
101 
102 /// Creates a vulkan instance with default layers and extensions
103 VulkanInstance createVulkanInstance(in string appName=null,
104                                     in VulkanVersion appVersion=VulkanVersion(0, 0, 0))
105 {
106     debug {
107         const wantedLayers = lunarGValidationLayers;
108         const wantedExts = [ "VK_KHR_debug_report", "VK_EXT_debug_report" ];
109     }
110     else {
111         const string[] wantedLayers = [];
112         const string[] wantedExts = [];
113     }
114 
115     import gfx.vulkan.wsi : surfaceInstanceExtensions;
116 
117     import std.algorithm : canFind, filter, map;
118     import std.array : array;
119     import std.range : chain;
120 
121     const layers = wantedLayers
122             .filter!(l => _instanceLayers.map!(il => il.layerName).canFind(l))
123             .array;
124     const exts = wantedExts
125             .filter!(e => _instanceExtensions.map!(ie => ie.extensionName).canFind(e))
126             .array
127         ~ surfaceInstanceExtensions;
128 
129     return createVulkanInstance(layers, exts, appName, appVersion);
130 }
131 
132 /// Creates an Instance object with Vulkan backend with user specified layers and extensions
133 VulkanInstance createVulkanInstance(in string[] layers, in string[] extensions,
134                                     in string appName=null,
135                                     in VulkanVersion appVersion=VulkanVersion(0, 0, 0))
136 {
137     import gfx : gfxVersionMaj, gfxVersionMin, gfxVersionMic;
138     import std.algorithm : all, canFind, map;
139     import std.array : array;
140     import std.exception : enforce;
141     import std.string : toStringz;
142 
143     // throw if some requested layers or extensions are not available
144     // TODO: specific exception
145     foreach (l; layers) {
146         enforce(
147             _instanceLayers.map!(il => il.layerName).canFind(l),
148             "Could not find layer " ~ l ~ " when creating Vulkan instance"
149         );
150     }
151     foreach (e; extensions) {
152         enforce(
153             _instanceExtensions.map!(ie => ie.extensionName).canFind(e),
154             "Could not find extension " ~ e ~ " when creating Vulkan instance"
155         );
156     }
157 
158     VkApplicationInfo ai;
159     ai.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
160     if (appName.length) {
161         ai.pApplicationName = toStringz(appName);
162     }
163     ai.applicationVersion = appVersion.toUint();
164     ai.pEngineName = "gfx-d\n".ptr;
165     ai.engineVersion = VK_MAKE_VERSION(gfxVersionMaj, gfxVersionMin, gfxVersionMic);
166 
167     auto vkLayers = layers.map!toStringz.array;
168     auto vkExts = extensions.map!toStringz.array;
169 
170     VkInstanceCreateInfo ici;
171     ici.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
172     ici.pApplicationInfo = &ai;
173     ici.enabledLayerCount = cast(uint)vkLayers.length;
174     ici.ppEnabledLayerNames = &vkLayers[0];
175     ici.enabledExtensionCount = cast(uint)vkExts.length;
176     ici.ppEnabledExtensionNames = &vkExts[0];
177 
178     auto vk = _globCmds;
179     VkInstance vkInst;
180     vulkanEnforce(vk.CreateInstance(&ici, null, &vkInst), "Could not create Vulkan instance");
181 
182     return new VulkanInstance(vkInst);
183 }
184 
185 /// Retrieve available device level layers
186 @property VulkanLayerProperties[] vulkanDeviceLayers(PhysicalDevice device) {
187     auto pd = cast(VulkanPhysicalDevice)device;
188     if (!pd) return [];
189 
190     return pd._availableLayers;
191 }
192 /// Retrieve available instance level extensions properties
193 VulkanExtensionProperties[] vulkanDeviceExtensions(PhysicalDevice device, in string layerName=null)
194 {
195     auto pd = cast(VulkanPhysicalDevice)device;
196     if (!pd) return [];
197 
198     if (!layerName) {
199         return pd._availableExtensions;
200     }
201     else {
202         return pd.loadDeviceExtensions(layerName);
203     }
204 }
205 
206 void overrideDeviceOpenVulkanLayers(PhysicalDevice device, string[] layers)
207 {
208     auto pd = cast(VulkanPhysicalDevice)device;
209     if (!pd) return;
210 
211     pd._openLayers = layers;
212 }
213 
214 void overrideDeviceOpenVulkanExtensions(PhysicalDevice device, string[] extensions)
215 {
216     auto pd = cast(VulkanPhysicalDevice)device;
217     if (!pd) return;
218 
219     pd._openExtensions = extensions;
220 }
221 
222 
223 package:
224 
225 import gfx.core.rc;
226 import gfx.graal.device;
227 import gfx.graal.format;
228 import gfx.graal.memory;
229 import gfx.graal.presentation;
230 import gfx.graal.queue;
231 import gfx.vulkan.conv;
232 import gfx.vulkan.device;
233 import gfx.vulkan.error;
234 import gfx.vulkan.wsi : VulkanSurface;
235 
236 import std.exception : enforce;
237 
238 __gshared VkGlobalCmds _globCmds;
239 __gshared VulkanLayerProperties[] _instanceLayers;
240 __gshared VulkanExtensionProperties[] _instanceExtensions;
241 
242 VulkanLayerProperties[] loadInstanceLayers()
243 {
244     auto vk = _globCmds;
245     uint count;
246     vulkanEnforce(
247         vk.EnumerateInstanceLayerProperties(&count, null),
248         "Could not retrieve Vulkan instance layers"
249     );
250     if (!count) return[];
251 
252     auto vkLayers = new VkLayerProperties[count];
253     vulkanEnforce(
254         vk.EnumerateInstanceLayerProperties(&count, &vkLayers[0]),
255         "Could not retrieve Vulkan instance layers"
256     );
257 
258     import std.algorithm : map;
259     import std.array : array;
260     import std.string : fromStringz;
261 
262     return vkLayers
263             .map!((ref VkLayerProperties vkLp) {
264                 return VulkanLayerProperties(
265                     fromStringz(&vkLp.layerName[0]).idup,
266                     VulkanVersion.fromUint(vkLp.specVersion),
267                     VulkanVersion.fromUint(vkLp.implementationVersion),
268                     fromStringz(&vkLp.description[0]).idup,
269                 );
270             })
271             .array;
272 }
273 
274 VulkanExtensionProperties[] loadInstanceExtensions(in string layerName=null)
275 {
276     import std.string : toStringz;
277 
278     const(char)* layer;
279     if (layerName.length) {
280         layer = toStringz(layerName);
281     }
282     auto vk = _globCmds;
283     uint count;
284     vulkanEnforce(
285         vk.EnumerateInstanceExtensionProperties(layer, &count, null),
286         "Could not retrieve Vulkan instance extensions"
287     );
288     if (!count) return[];
289 
290     auto vkExts = new VkExtensionProperties[count];
291     vulkanEnforce(
292         vk.EnumerateInstanceExtensionProperties(layer, &count, &vkExts[0]),
293         "Could not retrieve Vulkan instance extensions"
294     );
295 
296     import std.algorithm : map;
297     import std.array : array;
298     import std.string : fromStringz;
299 
300     return vkExts
301             .map!((ref VkExtensionProperties vkExt) {
302                 return VulkanExtensionProperties(
303                     fromStringz(&vkExt.extensionName[0]).idup,
304                     VulkanVersion.fromUint(vkExt.specVersion)
305                 );
306             })
307             .array;
308 }
309 
310 VulkanLayerProperties[] loadDeviceLayers(VulkanPhysicalDevice pd)
311 {
312     auto vk = pd.vk;
313     uint count;
314     vulkanEnforce(
315         vk.EnumerateDeviceLayerProperties(pd.vkObj, &count, null),
316         "Could not retrieve Vulkan device layers"
317     );
318     if (!count) return[];
319 
320     auto vkLayers = new VkLayerProperties[count];
321     vulkanEnforce(
322         vk.EnumerateDeviceLayerProperties(pd.vkObj, &count, &vkLayers[0]),
323         "Could not retrieve Vulkan device layers"
324     );
325 
326     import std.algorithm : map;
327     import std.array : array;
328     import std.string : fromStringz;
329 
330     return vkLayers
331             .map!((ref VkLayerProperties vkLp) {
332                 return VulkanLayerProperties(
333                     fromStringz(&vkLp.layerName[0]).idup,
334                     VulkanVersion.fromUint(vkLp.specVersion),
335                     VulkanVersion.fromUint(vkLp.implementationVersion),
336                     fromStringz(&vkLp.description[0]).idup,
337                 );
338             })
339             .array;
340 }
341 
342 VulkanExtensionProperties[] loadDeviceExtensions(VulkanPhysicalDevice pd, in string layerName=null)
343 {
344     import std.string : toStringz;
345 
346     const(char)* layer;
347     if (layerName.length) {
348         layer = toStringz(layerName);
349     }
350 
351     auto vk = pd.vk;
352     uint count;
353     vulkanEnforce(
354         vk.EnumerateDeviceExtensionProperties(pd.vkObj, layer, &count, null),
355         "Could not retrieve Vulkan device extensions"
356     );
357     if (!count) return[];
358 
359     auto vkExts = new VkExtensionProperties[count];
360     vulkanEnforce(
361         vk.EnumerateDeviceExtensionProperties(pd.vkObj, layer, &count, &vkExts[0]),
362         "Could not retrieve Vulkan device extensions"
363     );
364 
365     import std.algorithm : map;
366     import std.array : array;
367     import std.string : fromStringz;
368 
369     return vkExts
370             .map!((ref VkExtensionProperties vkExt) {
371                 return VulkanExtensionProperties(
372                     fromStringz(&vkExt.extensionName[0]).idup,
373                     VulkanVersion.fromUint(vkExt.specVersion)
374                 );
375             })
376             .array;
377 }
378 
379 class VulkanObj(VkType)
380 {
381     this (VkType vkObj) {
382         _vkObj = vkObj;
383     }
384 
385     final @property VkType vkObj() {
386         return _vkObj;
387     }
388 
389     private VkType _vkObj;
390 }
391 
392 class VulkanInstObj(VkType) : Disposable
393 {
394     this (VkType vkObj, VulkanInstance inst)
395     {
396         _vkObj = vkObj;
397         _inst = inst;
398         _inst.retain();
399     }
400 
401     override void dispose() {
402         _inst.release();
403         _inst = null;
404     }
405 
406     final @property VkType vkObj() {
407         return _vkObj;
408     }
409 
410     final @property VulkanInstance inst() {
411         return _inst;
412     }
413 
414     final @property VkInstance vkInst() {
415         return _inst.vkObj;
416     }
417 
418     private VkType _vkObj;
419     private VulkanInstance _inst;
420 }
421 
422 final class VulkanInstance : VulkanObj!(VkInstance), Instance
423 {
424     mixin(atomicRcCode);
425 
426     this(VkInstance vkObj) {
427         super(vkObj);
428         _vk = new VkInstanceCmds(vkObj, _globCmds);
429     }
430 
431     override void dispose() {
432         if (_vkCb) {
433             vk.DestroyDebugReportCallbackEXT(vkObj, _vkCb, null);
434             _vkCb = null;
435             _callback = null;
436         }
437         vk.DestroyInstance(vkObj, null);
438     }
439 
440     override @property Backend backend() {
441         return Backend.vulkan;
442     }
443 
444     override @property ApiProps apiProps() {
445         return vulkanApiProps;
446     }
447 
448     @property VkInstanceCmds vk() {
449         return _vk;
450     }
451 
452     override PhysicalDevice[] devices()
453     {
454         import std.array : array, uninitializedArray;
455         uint count;
456         vulkanEnforce(vk.EnumeratePhysicalDevices(vkObj, &count, null),
457                 "Could not enumerate Vulkan devices");
458         auto devices = uninitializedArray!(VkPhysicalDevice[])(count);
459         vulkanEnforce(vk.EnumeratePhysicalDevices(vkObj, &count, devices.ptr),
460                 "Could not enumerate Vulkan devices");
461 
462         import std.algorithm : map;
463         return devices
464             .map!(d => cast(PhysicalDevice)(new VulkanPhysicalDevice(d, this)))
465             .array;
466     }
467 
468     override void setDebugCallback(DebugCallback callback) {
469         VkDebugReportCallbackCreateInfoEXT ci;
470         ci.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT;
471         ci.flags = 0x1f;
472         ci.pfnCallback = &gfxd_vk_DebugReportCallback;
473         ci.pUserData = cast(void*)this;
474 
475         vk.CreateDebugReportCallbackEXT(vkObj, &ci, null, &_vkCb);
476         _callback = callback;
477     }
478 
479     VkInstanceCmds _vk;
480     VkDebugReportCallbackEXT _vkCb;
481     DebugCallback _callback;
482 }
483 
484 extern(C) nothrow {
485     VkBool32 gfxd_vk_DebugReportCallback(VkDebugReportFlagsEXT flags,
486                                          VkDebugReportObjectTypeEXT objectType,
487                                          ulong object,
488                                          size_t location,
489                                          int messageCode,
490                                          const(char)* pLayerPrefix,
491                                          const(char)* pMessage,
492                                          void* pUserData)
493     {
494         auto vkInst = cast(VulkanInstance)pUserData;
495         if (vkInst && vkInst._callback) {
496             import gfx.vulkan.conv : debugReportFlagsToGfx;
497             import std.string : fromStringz;
498             try {
499                 vkInst._callback(debugReportFlagsToGfx(flags), fromStringz(pMessage).idup);
500             }
501             catch(Exception ex) {
502                 import std.exception : collectException;
503                 import std.stdio : stderr;
504                 collectException(
505                     stderr.writefln("Exception thrown in debug callback: %s", ex.msg)
506                 );
507             }
508         }
509 
510         return VK_FALSE;
511     }
512 }
513 
514 final class VulkanPhysicalDevice : PhysicalDevice
515 {
516     mixin(atomicRcCode);
517 
518     this(VkPhysicalDevice vkObj, VulkanInstance inst) {
519         _vkObj = vkObj;
520         _inst = inst;
521         _inst.retain();
522         _vk = _inst.vk;
523 
524         vk.GetPhysicalDeviceProperties(_vkObj, &_vkProps);
525 
526         _availableLayers = loadDeviceLayers(this);
527         _availableExtensions = loadDeviceExtensions(this);
528 
529         import std.algorithm : canFind, map;
530         import std.exception : enforce;
531         debug {
532             foreach (l; lunarGValidationLayers) {
533                 if (_availableLayers.map!"a.layerName".canFind(l)) {
534                     _openLayers ~= l;
535                 }
536             }
537         }
538         version(GfxOffscreen) {}
539         else {
540             import gfx.vulkan.wsi : swapChainExtension;
541             enforce(_availableExtensions.map!"a.extensionName".canFind(swapChainExtension));
542             _openExtensions ~= swapChainExtension;
543         }
544     }
545 
546     override void dispose() {
547         _inst.release();
548         _inst = null;
549     }
550 
551 
552     @property VkPhysicalDevice vkObj() {
553         return _vkObj;
554     }
555 
556     @property VkInstanceCmds vk() {
557         return _vk;
558     }
559 
560     override @property string name() {
561         import std.string : fromStringz;
562         return fromStringz(_vkProps.deviceName.ptr).idup;
563     }
564     override @property DeviceType type() {
565         return devTypeToGfx(_vkProps.deviceType);
566     }
567     override @property DeviceFeatures features() {
568         import std.algorithm : canFind, map;
569         import gfx.vulkan.wsi : swapChainExtension;
570 
571         VkPhysicalDeviceFeatures vkFeats;
572         vk.GetPhysicalDeviceFeatures(vkObj, &vkFeats);
573 
574         DeviceFeatures features;
575         features.anisotropy = vkFeats.samplerAnisotropy == VK_TRUE;
576         features.presentation = vulkanDeviceExtensions(this)
577                 .map!(e => e.extensionName)
578                 .canFind(swapChainExtension);
579         return features;
580     }
581     override @property DeviceLimits limits() {
582         return DeviceLimits.init;
583     }
584 
585     override @property MemoryProperties memoryProperties()
586     {
587         VkPhysicalDeviceMemoryProperties vkProps=void;
588         vk.GetPhysicalDeviceMemoryProperties(_vkObj, &vkProps);
589 
590         MemoryProperties props;
591 
592         foreach(i; 0 .. vkProps.memoryHeapCount) {
593             const vkHeap = vkProps.memoryHeaps[i];
594             props.heaps ~= MemoryHeap(
595                 cast(size_t)vkHeap.size, (vkHeap.flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0
596             );
597         }
598         foreach(i; 0 .. vkProps.memoryTypeCount) {
599             const vkMemType = vkProps.memoryTypes[i];
600             props.types ~= MemoryType(
601                 memPropsToGfx(vkMemType.propertyFlags),
602                 vkMemType.heapIndex,
603             );
604         }
605 
606         return props;
607     }
608 
609     override @property QueueFamily[] queueFamilies()
610     {
611         import std.array : array, uninitializedArray;
612         uint count;
613         vk.GetPhysicalDeviceQueueFamilyProperties(_vkObj, &count, null);
614 
615         auto vkQueueFams = uninitializedArray!(VkQueueFamilyProperties[])(count);
616         vk.GetPhysicalDeviceQueueFamilyProperties(_vkObj, &count, vkQueueFams.ptr);
617 
618         import std.algorithm : map;
619         return vkQueueFams.map!(vkObj => QueueFamily(
620             queueCapToGfx(vkObj.queueFlags), vkObj.queueCount
621         )).array;
622     }
623 
624     override FormatProperties formatProperties(in Format format)
625     {
626         VkFormatProperties vkFp;
627         vk.GetPhysicalDeviceFormatProperties(_vkObj, format.toVk(), &vkFp);
628 
629         return FormatProperties(
630             vkFp.linearTilingFeatures.toGfx(),
631             vkFp.optimalTilingFeatures.toGfx(),
632             vkFp.bufferFeatures.toGfx(),
633         );
634     }
635 
636     override bool supportsSurface(uint queueFamilyIndex, Surface graalSurface) {
637         auto surf = enforce(
638             cast(VulkanSurface)graalSurface,
639             "Did not pass a Vulkan surface"
640         );
641         VkBool32 supported;
642         vulkanEnforce(
643             vk.GetPhysicalDeviceSurfaceSupportKHR(vkObj, queueFamilyIndex, surf.vkObj, &supported),
644             "Could not query vulkan surface support"
645         );
646         return supported != VK_FALSE;
647     }
648 
649     override SurfaceCaps surfaceCaps(Surface graalSurface) {
650         auto surf = enforce(
651             cast(VulkanSurface)graalSurface,
652             "Did not pass a Vulkan surface"
653         );
654         VkSurfaceCapabilitiesKHR vkSc;
655         vulkanEnforce(
656             vk.GetPhysicalDeviceSurfaceCapabilitiesKHR(vkObj, surf.vkObj, &vkSc),
657             "Could not query vulkan surface capabilities"
658         );
659         return vkSc.toGfx();
660     }
661 
662     override Format[] surfaceFormats(Surface graalSurface) {
663         auto surf = enforce(
664             cast(VulkanSurface)graalSurface,
665             "Did not pass a Vulkan surface"
666         );
667 
668         uint count;
669         vulkanEnforce(
670             vk.GetPhysicalDeviceSurfaceFormatsKHR(vkObj, surf.vkObj, &count, null),
671             "Could not query vulkan surface formats"
672         );
673         auto vkSf = new VkSurfaceFormatKHR[count];
674         vulkanEnforce(
675             vk.GetPhysicalDeviceSurfaceFormatsKHR(vkObj, surf.vkObj, &count, &vkSf[0]),
676             "Could not query vulkan surface formats"
677         );
678 
679         import std.algorithm : filter, map;
680         import std.array : array;
681         return vkSf
682                 .filter!(sf => sf.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
683                 .map!(sf => sf.format.toGfx())
684                 .array;
685     }
686 
687     override PresentMode[] surfacePresentModes(Surface graalSurface) {
688         auto surf = enforce(
689             cast(VulkanSurface)graalSurface,
690             "Did not pass a Vulkan surface"
691         );
692 
693         uint count;
694         vulkanEnforce(
695             vk.GetPhysicalDeviceSurfacePresentModesKHR(vkObj, surf.vkObj, &count, null),
696             "Could not query vulkan surface present modes"
697         );
698         auto vkPms = new VkPresentModeKHR[count];
699         vulkanEnforce(
700             vk.GetPhysicalDeviceSurfacePresentModesKHR(vkObj, surf.vkObj, &count, &vkPms[0]),
701             "Could not query vulkan surface present modes"
702         );
703 
704         import std.algorithm : filter, map;
705         import std.array : array;
706         return vkPms
707                 .filter!(pm => pm.hasGfxSupport)
708                 .map!(pm => pm.toGfx())
709                 .array;
710     }
711 
712     override Device open(in QueueRequest[] queues, in DeviceFeatures features=DeviceFeatures.all)
713     {
714         import std.algorithm : filter, map, sort;
715         import std.array : array;
716         import std.exception : enforce;
717         import std.string : toStringz;
718         import gfx.vulkan.wsi : swapChainExtension;
719 
720         if (!queues.length) {
721             return null;
722         }
723 
724         const qcis = queues.map!((const(QueueRequest) r) {
725             VkDeviceQueueCreateInfo qci;
726             qci.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
727             qci.queueFamilyIndex = r.familyIndex;
728             qci.queueCount = cast(uint)r.priorities.length;
729             qci.pQueuePriorities = r.priorities.ptr;
730             return qci;
731         }).array;
732 
733         const layers = _openLayers.map!toStringz.array;
734         const extensions = _openExtensions
735                 .filter!(e => e != swapChainExtension || features.presentation)
736                 .map!toStringz.array;
737         VkPhysicalDeviceFeatures vkFeats;
738         vkFeats.samplerAnisotropy = features.anisotropy ? VK_TRUE : VK_FALSE;
739 
740         VkDeviceCreateInfo ci;
741         ci.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
742         ci.queueCreateInfoCount = cast(uint)qcis.length;
743         ci.pQueueCreateInfos = qcis.ptr;
744         ci.enabledLayerCount = cast(uint)layers.length;
745         ci.ppEnabledLayerNames = &layers[0];
746         ci.enabledExtensionCount = cast(uint)extensions.length;
747         ci.ppEnabledExtensionNames = &extensions[0];
748         ci.pEnabledFeatures = &vkFeats;
749 
750         VkDevice vkDev;
751         vulkanEnforce(vk.CreateDevice(_vkObj, &ci, null, &vkDev),
752                 "Vulkan device creation failed");
753 
754         return new VulkanDevice(vkDev, this);
755     }
756 
757     private VkPhysicalDevice _vkObj;
758     private VkPhysicalDeviceProperties _vkProps;
759     private VulkanInstance _inst;
760 
761     private VkInstanceCmds _vk;
762 
763     private VulkanLayerProperties[] _availableLayers;
764     private VulkanExtensionProperties[] _availableExtensions;
765 
766     private string[] _openLayers;
767     private string[] _openExtensions;
768 }
769 
770 DeviceType devTypeToGfx(in VkPhysicalDeviceType vkType)
771 {
772     switch (vkType) {
773     case VK_PHYSICAL_DEVICE_TYPE_OTHER:
774         return DeviceType.other;
775     case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
776         return DeviceType.integratedGpu;
777     case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
778         return DeviceType.discreteGpu;
779     case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
780         return DeviceType.virtualGpu;
781     case VK_PHYSICAL_DEVICE_TYPE_CPU:
782         return DeviceType.cpu;
783     default:
784         assert(false, "unexpected vulkan device type constant");
785     }
786 }