1 /// Vulkan device module
2 module gfx.vulkan.device;
3 
4 package:
5 
6 import core.time : Duration;
7 
8 import gfx.bindings.vulkan;
9 
10 import gfx.core.rc;
11 import gfx.graal;
12 import gfx.graal.cmd;
13 import gfx.graal.device;
14 import gfx.graal.image;
15 import gfx.graal.memory;
16 import gfx.graal.presentation;
17 import gfx.graal.queue;
18 import gfx.graal.pipeline;
19 import gfx.graal.sync;
20 import gfx.vulkan;
21 import gfx.vulkan.buffer;
22 import gfx.vulkan.cmd;
23 import gfx.vulkan.conv;
24 import gfx.vulkan.error;
25 import gfx.vulkan.image;
26 import gfx.vulkan.memory;
27 import gfx.vulkan.pipeline;
28 import gfx.vulkan.queue;
29 import gfx.vulkan.renderpass;
30 import gfx.vulkan.sync;
31 import gfx.vulkan.wsi;
32 
33 import std.typecons : Flag;
34 
35 class VulkanDevObj(VkType, string destroyFn) : Disposable
36 {
37     this (VkType vkObj, VulkanDevice dev)
38     {
39         _vkObj = vkObj;
40         _dev = dev;
41         _dev.retain();
42         _vk = _dev.vk;
43     }
44 
45     override void dispose() {
46         mixin("vk."~destroyFn~"(vkDev, vkObj, null);");
47         _dev.release();
48         _dev = null;
49     }
50 
51     final @property VkType vkObj() {
52         return _vkObj;
53     }
54 
55     final @property VulkanDevice dev() {
56         return _dev;
57     }
58 
59     final @property VkDevice vkDev() {
60         return _dev.vkObj;
61     }
62 
63     final @property VkDeviceCmds vk() {
64         return _vk;
65     }
66 
67     private VkType _vkObj;
68     private VulkanDevice _dev;
69     private VkDeviceCmds _vk;
70 }
71 
72 final class VulkanDevice : VulkanObj!(VkDevice), Device
73 {
74     mixin(atomicRcCode);
75 
76     this (VkDevice vkObj, VulkanPhysicalDevice pd, Instance inst)
77     {
78         super(vkObj);
79         _pd = pd;
80         _inst = inst;
81         _inst.retain();
82         _vk = new VkDeviceCmds(vkObj, pd.vk);
83     }
84 
85     override void dispose() {
86         vk.DestroyDevice(vkObj, null);
87         _pd = null;
88         _inst.release();
89         _inst = null;
90     }
91 
92     override @property Instance instance() {
93         return _inst;
94     }
95 
96     override @property PhysicalDevice physicalDevice() {
97         return _pd;
98     }
99 
100     @property VulkanPhysicalDevice pd() {
101         return _pd;
102     }
103 
104     @property VkDeviceCmds vk() {
105         return _vk;
106     }
107 
108     override void waitIdle() {
109         vulkanEnforce(
110             vk.DeviceWaitIdle(vkObj),
111             "Problem waiting for device"
112         );
113     }
114 
115     override Queue getQueue(uint queueFamilyIndex, uint queueIndex) {
116         VkQueue vkQ;
117         vk.GetDeviceQueue(vkObj, queueFamilyIndex, queueIndex, &vkQ);
118 
119         foreach (q; _queues) {
120             if (q.vkObj is vkQ) {
121                 return q;
122             }
123         }
124 
125         auto q = new VulkanQueue(vkQ, this, queueIndex);
126         _queues ~= q;
127         return q;
128     }
129 
130     override CommandPool createCommandPool(uint queueFamilyIndex) {
131         VkCommandPoolCreateInfo cci;
132         cci.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
133         cci.queueFamilyIndex = queueFamilyIndex;
134         cci.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
135 
136         VkCommandPool vkPool;
137         vulkanEnforce(
138             vk.CreateCommandPool(vkObj, &cci, null, &vkPool),
139             "Could not create vulkan command pool"
140         );
141 
142         return new VulkanCommandPool(vkPool, this);
143     }
144 
145     override DeviceMemory allocateMemory(uint memTypeIndex, size_t size)
146     {
147         VkMemoryAllocateInfo mai;
148         mai.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
149         mai.allocationSize = size;
150         mai.memoryTypeIndex = memTypeIndex;
151 
152         VkDeviceMemory vkMem;
153         vulkanEnforce(vk.AllocateMemory(vkObj, &mai, null, &vkMem), "Could not allocate device memory");
154 
155         const props = pd.memoryProperties.types[memTypeIndex].props;
156 
157         return new VulkanDeviceMemory(vkMem, this, props, size, memTypeIndex);
158     }
159 
160     override void flushMappedMemory(MappedMemorySet set)
161     {
162         import std.algorithm : map;
163         import std.array : array;
164         VkMappedMemoryRange[] mmrs = set.mms.map!((MappedMemorySet.MM mm) {
165             VkMappedMemoryRange mmr;
166             mmr.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
167             mmr.memory = (cast(VulkanDeviceMemory)mm.dm).vkObj;
168             mmr.offset = mm.offset;
169             mmr.size = mm.size;
170             return mmr;
171         }).array;
172 
173         vk.FlushMappedMemoryRanges(vkObj, cast(uint)mmrs.length, mmrs.ptr);
174     }
175 
176     override void invalidateMappedMemory(MappedMemorySet set) {
177         import std.algorithm : map;
178         import std.array : array;
179         VkMappedMemoryRange[] mmrs = set.mms.map!((MappedMemorySet.MM mm) {
180             VkMappedMemoryRange mmr;
181             mmr.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
182             mmr.memory = (cast(VulkanDeviceMemory)mm.dm).vkObj;
183             mmr.offset = mm.offset;
184             mmr.size = mm.size;
185             return mmr;
186         }).array;
187 
188         vk.InvalidateMappedMemoryRanges(vkObj, cast(uint)mmrs.length, mmrs.ptr);
189     }
190 
191     override Buffer createBuffer(BufferUsage usage, size_t size)
192     {
193         VkBufferCreateInfo bci;
194         bci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
195         bci.size = size;
196         bci.usage = bufferUsageToVk(usage);
197 
198         VkBuffer vkBuf;
199         vulkanEnforce(vk.CreateBuffer(vkObj, &bci, null, &vkBuf), "Could not create a Vulkan buffer");
200 
201         return new VulkanBuffer(vkBuf, this, usage, size);
202     }
203 
204     override Image createImage(in ImageInfo info)
205     {
206         import gfx.core.util : transmute;
207 
208         VkImageCreateInfo ici;
209         ici.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
210         if (info.type.isCube) ici.flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
211         ici.imageType = info.type.toVk();
212         ici.format = info.format.toVk();
213         ici.extent = info.dims.transmute!VkExtent3D;
214         ici.mipLevels = info.levels;
215         ici.arrayLayers = info.layers;
216         ici.samples = cast(typeof(ici.samples))info.samples;
217         ici.tiling = info.tiling.toVk();
218         ici.usage = imageUsageToVk(info.usage);
219         ici.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
220 
221         VkImage vkImg;
222         vulkanEnforce(vk.CreateImage(vkObj, &ici, null, &vkImg), "Could not create a Vulkan image");
223 
224         return new VulkanImage(vkImg, this, info);
225     }
226 
227     Sampler createSampler(in SamplerInfo info) {
228         import gfx.core.typecons : ifNone, ifSome;
229         import std.algorithm : each;
230 
231         VkSamplerCreateInfo sci;
232         sci.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
233         sci.minFilter = info.minFilter.toVk();
234         sci.magFilter = info.magFilter.toVk();
235         sci.mipmapMode = info.mipmapFilter.toVkMipmapMode();
236         sci.addressModeU = info.wrapMode[0].toVk();
237         sci.addressModeV = info.wrapMode[1].toVk();
238         sci.addressModeW = info.wrapMode[2].toVk();
239         sci.mipLodBias = info.lodBias;
240         info.anisotropy.save.ifSome!((float max) {
241             sci.anisotropyEnable = VK_TRUE;
242             sci.maxAnisotropy = max;
243         }).ifNone!({
244             sci.anisotropyEnable = VK_FALSE;
245             sci.maxAnisotropy = 1f;
246         });
247         info.compare.save.each!((CompareOp op) {
248             sci.compareEnable = VK_TRUE;
249             sci.compareOp = op.toVk();
250         });
251         sci.minLod = info.lodRange[0];
252         sci.maxLod = info.lodRange[1];
253         sci.borderColor = info.borderColor.toVk();
254         sci.unnormalizedCoordinates = info.unnormalizeCoords ? VK_TRUE : VK_FALSE;
255 
256         VkSampler vkS;
257         vulkanEnforce(
258             vk.CreateSampler(vkObj, &sci, null, &vkS),
259             "Could not create Vulkan sampler"
260         );
261 
262         return new VulkanSampler(vkS, this);
263     }
264 
265     override Semaphore createSemaphore()
266     {
267         VkSemaphoreCreateInfo sci;
268         sci.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
269 
270         VkSemaphore vkSem;
271         vulkanEnforce(vk.CreateSemaphore(vkObj, &sci, null, &vkSem), "Could not create a Vulkan semaphore");
272 
273         return new VulkanSemaphore(vkSem, this);
274     }
275 
276     override Fence createFence(Flag!"signaled" signaled)
277     {
278         VkFenceCreateInfo fci;
279         fci.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
280         if (signaled) {
281             fci.flags = VK_FENCE_CREATE_SIGNALED_BIT;
282         }
283         VkFence vkF;
284         vulkanEnforce(vk.CreateFence(vkObj, &fci, null, &vkF), "Could not create a Vulkan fence");
285 
286         return new VulkanFence(vkF, this);
287     }
288 
289     override void resetFences(Fence[] fences) {
290         import std.algorithm : map;
291         import std.array : array;
292 
293         auto vkFs = fences.map!(
294             f => enforce(cast(VulkanFence)f, "Did not pass a Vulkan fence").vkObj
295         ).array;
296 
297         vulkanEnforce(
298             vk.ResetFences(vkObj, cast(uint)vkFs.length, &vkFs[0]),
299             "Could not reset vulkan fences"
300         );
301     }
302 
303     override void waitForFences(Fence[] fences, Flag!"waitAll" waitAll, Duration timeout)
304     {
305         import std.algorithm : map;
306         import std.array : array;
307 
308         auto vkFs = fences.map!(
309             f => enforce(cast(VulkanFence)f, "Did not pass a Vulkan fence").vkObj
310         ).array;
311 
312         const vkWaitAll = waitAll ? VK_TRUE : VK_FALSE;
313         const nsecs = timeout.total!"nsecs";
314         const vkTimeout = nsecs < 0 ? ulong.max : cast(ulong)nsecs;
315 
316         vulkanEnforce(
317             vk.WaitForFences(vkObj, cast(uint)vkFs.length, &vkFs[0], vkWaitAll, vkTimeout),
318             "could not wait for vulkan fences"
319         );
320     }
321 
322 
323     override Swapchain createSwapchain(Surface graalSurface, PresentMode pm, uint numImages,
324                                        Format format, uint[2] size, ImageUsage usage,
325                                        CompositeAlpha alpha, Swapchain old=null)
326     {
327         auto surf = enforce(
328             cast(VulkanSurface)graalSurface,
329             "Did not pass a Vulkan surface"
330         );
331 
332         auto oldSc = old ? enforce(
333             cast(VulkanSwapchain)old, "Did not pass a vulkan swapchain"
334         ) : null;
335 
336         VkSwapchainCreateInfoKHR sci;
337         sci.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
338         sci.surface = surf.vkObj;
339         sci.minImageCount = numImages;
340         sci.imageFormat = format.toVk;
341         sci.imageExtent = VkExtent2D(size[0], size[1]);
342         sci.imageArrayLayers = 1;
343         sci.imageUsage = imageUsageToVk(usage);
344         sci.imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
345         sci.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
346         sci.clipped = VK_TRUE;
347         sci.presentMode = pm.toVk;
348         sci.compositeAlpha = compositeAlphaToVk(alpha);
349         sci.oldSwapchain = oldSc ? oldSc.vkObj : VK_NULL_ND_HANDLE;
350 
351         VkSwapchainKHR vkSc;
352         vulkanEnforce(
353             vk.CreateSwapchainKHR(vkObj, &sci, null, &vkSc),
354             "Could not create a Vulkan Swap chain"
355         );
356 
357         return new VulkanSwapchain(vkSc, this, graalSurface, size, format, usage);
358     }
359 
360     override RenderPass createRenderPass(in AttachmentDescription[] attachments,
361                                          in SubpassDescription[] subpasses,
362                                          in SubpassDependency[] dependencies)
363     {
364         import std.algorithm : map;
365         import std.array : array;
366 
367         auto vkAttachments = attachments.map!((ref const(AttachmentDescription) ad) {
368             VkAttachmentDescription vkAd;
369             if (ad.mayAlias) {
370                 vkAd.flags = VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT;
371             }
372             vkAd.format = ad.format.toVk();
373             vkAd.loadOp = ad.ops.load.toVk();
374             vkAd.storeOp = ad.ops.store.toVk();
375             vkAd.stencilLoadOp = ad.stencilOps.load.toVk();
376             vkAd.stencilStoreOp = ad.stencilOps.store.toVk();
377             vkAd.initialLayout = ad.layoutTrans.from.toVk();
378             vkAd.finalLayout = ad.layoutTrans.to.toVk();
379             return vkAd;
380         }).array;
381 
382         static VkAttachmentReference mapRef (in AttachmentRef ar) {
383             return VkAttachmentReference(ar.attachment, ar.layout.toVk());
384         }
385         static VkAttachmentReference[] mapRefs(in AttachmentRef[] ars) {
386             return ars.map!mapRef.array;
387         }
388         auto vkSubpasses = subpasses.map!((ref const(SubpassDescription) sd) {
389             auto vkInputs = mapRefs(sd.inputs);
390             auto vkColors = mapRefs(sd.colors);
391             auto vkDepthStencil = sd.depthStencil.save.map!(mapRef).array;
392             VkSubpassDescription vkSd;
393             vkSd.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
394             vkSd.inputAttachmentCount = cast(uint)vkInputs.length;
395             vkSd.pInputAttachments = vkInputs.ptr;
396             vkSd.colorAttachmentCount = cast(uint)vkColors.length;
397             vkSd.pColorAttachments = vkColors.ptr;
398             vkSd.pDepthStencilAttachment = vkDepthStencil.length ?
399                     vkDepthStencil.ptr : null;
400             vkSd.preserveAttachmentCount = cast(uint)sd.preserves.length;
401             vkSd.pPreserveAttachments = sd.preserves.ptr;
402             return vkSd;
403         }).array;
404 
405         auto vkDeps = dependencies.map!((ref const(SubpassDependency) sd) {
406             VkSubpassDependency vkSd;
407             vkSd.srcSubpass = sd.subpass.from;
408             vkSd.dstSubpass = sd.subpass.to;
409             vkSd.srcStageMask = pipelineStageToVk(sd.stageMask.from);
410             vkSd.dstStageMask = pipelineStageToVk(sd.stageMask.to);
411             vkSd.srcAccessMask = accessToVk(sd.accessMask.from);
412             vkSd.dstAccessMask = accessToVk(sd.accessMask.to);
413             return vkSd;
414         }).array;
415 
416         VkRenderPassCreateInfo rpci;
417         rpci.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
418         rpci.attachmentCount = cast(uint)vkAttachments.length;
419         rpci.pAttachments = vkAttachments.ptr;
420         rpci.subpassCount = cast(uint)vkSubpasses.length;
421         rpci.pSubpasses = vkSubpasses.ptr;
422         rpci.dependencyCount = cast(uint)vkDeps.length;
423         rpci.pDependencies = vkDeps.ptr;
424 
425         VkRenderPass vkRp;
426         vulkanEnforce(
427             vk.CreateRenderPass(vkObj, &rpci, null, &vkRp),
428             "Could not create a Vulkan render pass"
429         );
430 
431         return new VulkanRenderPass(vkRp, this);
432     }
433 
434 
435     override Framebuffer createFramebuffer(RenderPass rp, ImageView[] attachments,
436                                            uint width, uint height, uint layers)
437     {
438         import std.algorithm : map;
439         import std.array : array;
440 
441         auto vkRp = enforce(cast(VulkanRenderPass)rp, "Did not pass a Vulkan render pass").vkObj;
442         auto vkAttachments = attachments.map!(
443             iv => enforce(cast(VulkanImageView)iv, "Did not pass a Vulkan image view").vkObj
444         ).array;
445 
446         VkFramebufferCreateInfo fci;
447         fci.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
448         fci.renderPass = vkRp;
449         fci.attachmentCount = cast(uint)vkAttachments.length;
450         fci.pAttachments = vkAttachments.ptr;
451         fci.width = width;
452         fci.height = height;
453         fci.layers = layers;
454 
455         VkFramebuffer vkFb;
456         vulkanEnforce(
457             vk.CreateFramebuffer(vkObj, &fci, null, &vkFb),
458             "Could not create a Vulkan Framebuffer"
459         );
460 
461         return new VulkanFramebuffer(vkFb, this, attachments);
462     }
463 
464     override ShaderModule createShaderModule(const(uint)[] code, string entryPoint)
465     {
466         VkShaderModuleCreateInfo smci;
467         smci.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
468         smci.codeSize = cast(uint)code.length * 4;
469         smci.pCode = code.ptr;
470 
471         VkShaderModule vkSm;
472         vulkanEnforce(
473             vk.CreateShaderModule(vkObj, &smci, null, &vkSm),
474             "Could not create Vulkan shader module"
475         );
476 
477         return new VulkanShaderModule(vkSm, this, entryPoint);
478     }
479 
480     override DescriptorSetLayout createDescriptorSetLayout(in PipelineLayoutBinding[] bindings)
481     {
482         import std.algorithm : map;
483         import std.array : array;
484 
485         auto vkBindings = bindings.map!(b => VkDescriptorSetLayoutBinding(
486             b.binding, b.descriptorType.toVk(), b.descriptorCount, shaderStageToVk(b.stages), null
487         )).array;
488 
489         VkDescriptorSetLayoutCreateInfo ci;
490         ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
491         ci.bindingCount = cast(uint)vkBindings.length;
492         ci.pBindings = vkBindings.ptr;
493 
494         VkDescriptorSetLayout vkL;
495         vulkanEnforce(
496             vk.CreateDescriptorSetLayout(vkObj, &ci, null, &vkL),
497             "Could not create Vulkan descriptor set layout"
498         );
499 
500         return new VulkanDescriptorSetLayout(vkL, this);
501     }
502 
503     override PipelineLayout createPipelineLayout(DescriptorSetLayout[] layouts,
504                                                  in PushConstantRange[] ranges)
505     {
506         import std.algorithm : map;
507         import std.array : array;
508 
509         auto vkLayouts = layouts.map!(
510             l => enforce(
511                 cast(VulkanDescriptorSetLayout)l,
512                 "VulkanDevice.createPipelineLayout: Did not supply a Vulkan DescriptorSetLayout"
513             ).vkObj
514         ).array;
515         auto vkRanges = ranges.map!(
516             r => VkPushConstantRange( shaderStageToVk(r.stages), r.offset, r.size )
517         ).array;
518 
519         VkPipelineLayoutCreateInfo ci;
520         ci.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
521         ci.setLayoutCount = cast(uint)vkLayouts.length;
522         ci.pSetLayouts = vkLayouts.ptr;
523         ci.pushConstantRangeCount = cast(uint)vkRanges.length;
524         ci.pPushConstantRanges = vkRanges.ptr;
525 
526         VkPipelineLayout vkPl;
527         vulkanEnforce(
528             vk.CreatePipelineLayout(vkObj, &ci, null, &vkPl),
529             "Could not create Vulkan pipeline layout"
530         );
531         return new VulkanPipelineLayout(vkPl, this, layouts, ranges);
532     }
533 
534     override DescriptorPool createDescriptorPool(in uint maxSets, in DescriptorPoolSize[] sizes)
535     {
536         import std.algorithm : map;
537         import std.array : array;
538 
539         auto vkSizes = sizes.map!(
540             s => VkDescriptorPoolSize(s.type.toVk(), s.count)
541         ).array;
542 
543         VkDescriptorPoolCreateInfo ci;
544         ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
545         ci.maxSets = maxSets;
546         ci.poolSizeCount = cast(uint)vkSizes.length;
547         ci.pPoolSizes = vkSizes.ptr;
548 
549         VkDescriptorPool vkP;
550         vulkanEnforce(
551             vk.CreateDescriptorPool(vkObj, &ci, null, &vkP),
552             "Could not create Vulkan Descriptor Pool"
553         );
554 
555         return new VulkanDescriptorPool(vkP, this);
556     }
557 
558     override void updateDescriptorSets(WriteDescriptorSet[] writeOps, CopyDescritporSet[] copyOps)
559     {
560         import gfx.core.util : unsafeCast;
561         import std.algorithm : map;
562         import std.array : array;
563 
564         auto vkWrites = writeOps.map!((WriteDescriptorSet wds) {
565             VkWriteDescriptorSet vkWds;
566             vkWds.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
567             vkWds.dstSet = enforce(cast(VulkanDescriptorSet)wds.dstSet).vkObj;
568             vkWds.dstBinding = wds.dstBinding;
569             vkWds.dstArrayElement = wds.dstArrayElem;
570             vkWds.descriptorCount = cast(uint)wds.write.count;
571             vkWds.descriptorType = wds.write.type.toVk();
572 
573             final switch (wds.write.type) {
574             case DescriptorType.sampler:
575                 auto sds = wds.write.samplers;
576                 auto vkArr = sds.map!((SamplerDescriptor sd) {
577                     VkDescriptorImageInfo dii;
578                     dii.sampler = unsafeCast!(VulkanSampler)(sd.sampler).vkObj;
579                     return dii;
580                 }).array;
581                 vkWds.pImageInfo = vkArr.ptr;
582                 break;
583             case DescriptorType.combinedImageSampler:
584                 auto sids = wds.write.imageSamplers;
585                 auto vkArr = sids.map!((ImageSamplerDescriptor sid) {
586                     VkDescriptorImageInfo dii;
587                     dii.sampler = unsafeCast!(VulkanSampler)(sid.sampler).vkObj;
588                     dii.imageView = unsafeCast!(VulkanImageView)(sid.view).vkObj;
589                     dii.imageLayout = sid.layout.toVk();
590                     return dii;
591                 }).array;
592                 vkWds.pImageInfo = vkArr.ptr;
593                 break;
594             case DescriptorType.sampledImage:
595             case DescriptorType.storageImage:
596             case DescriptorType.inputAttachment:
597                 auto ids = wds.write.images;
598                 auto vkArr = ids.map!((ImageDescriptor id) {
599                     VkDescriptorImageInfo dii;
600                     dii.imageView = unsafeCast!(VulkanImageView)(id.view).vkObj;
601                     dii.imageLayout = id.layout.toVk();
602                     return dii;
603                 }).array;
604                 vkWds.pImageInfo = vkArr.ptr;
605                 break;
606             case DescriptorType.uniformBuffer:
607             case DescriptorType.storageBuffer:
608             case DescriptorType.uniformBufferDynamic:
609             case DescriptorType.storageBufferDynamic:
610                 auto bds = wds.write.buffers;
611                 auto vkArr = bds.map!((BufferDescriptor bd) {
612                     VkDescriptorBufferInfo dbi;
613                     dbi.buffer = unsafeCast!(VulkanBuffer)(bd.buffer).vkObj;
614                     dbi.offset = bd.offset;
615                     dbi.range = bd.size;
616                     if (bd.size > 1000000) {
617                         asm { int 0x03; }
618                     }
619                     return dbi;
620                 }).array;
621                 vkWds.pBufferInfo = vkArr.ptr;
622                 break;
623             case DescriptorType.uniformTexelBuffer:
624             case DescriptorType.storageTexelBuffer:
625                 auto tbds = wds.write.texelBuffers;
626                 auto vkArr = tbds.map!((TexelBufferDescriptor tbd) {
627                     return unsafeCast!(VulkanTexelBufferView)(tbd.bufferView).vkObj;
628                 }).array;
629                 vkWds.pTexelBufferView = vkArr.ptr;
630                 break;
631             }
632 
633             return vkWds;
634         }).array;
635 
636         auto vkCopies = copyOps.map!((CopyDescritporSet cds) {
637             VkCopyDescriptorSet vkCds;
638             vkCds.sType = VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET;
639             vkCds.srcSet = enforce(cast(VulkanDescriptorSet)cds.set.from).vkObj;
640             vkCds.srcBinding = cds.binding.from;
641             vkCds.srcArrayElement = cds.arrayElem.from;
642             vkCds.dstSet = enforce(cast(VulkanDescriptorSet)cds.set.to).vkObj;
643             vkCds.dstBinding = cds.binding.to;
644             vkCds.dstArrayElement = cds.arrayElem.to;
645             return vkCds;
646         }).array;
647 
648         vk.UpdateDescriptorSets(vkObj,
649             cast(uint)vkWrites.length, vkWrites.ptr,
650             cast(uint)vkCopies.length, vkCopies.ptr
651         );
652     }
653 
654     override Pipeline[] createPipelines(PipelineInfo[] infos) {
655         import gfx.core.util : transmute;
656         import std.algorithm : map, max;
657         import std.array : array;
658         import std.string : toStringz;
659 
660         auto pcis = new VkGraphicsPipelineCreateInfo[infos.length];
661 
662         foreach (i; 0 .. infos.length) {
663             VkPipelineShaderStageCreateInfo[] sscis;
664             void addShaderStage(ShaderModule sm, ShaderStage ss) {
665                 VkPipelineShaderStageCreateInfo ssci;
666                 ssci.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
667                 ssci.stage = shaderStageToVk(ss);
668                 ssci.module_ = enforce(
669                     cast(VulkanShaderModule)sm,
670                     "did not pass a Vulkan shader module"
671                 ).vkObj;
672                 ssci.pName = toStringz(sm.entryPoint);
673                 sscis ~= ssci;
674             }
675             auto shaders = infos[i].shaders;
676             enforce(shaders.vertex, "Vertex input shader is mandatory");
677             addShaderStage(shaders.vertex, ShaderStage.vertex);
678             if (shaders.tessControl)
679                 addShaderStage(shaders.tessControl, ShaderStage.tessellationControl);
680             if (shaders.tessEval)
681                 addShaderStage(shaders.tessEval, ShaderStage.tessellationEvaluation);
682             if (shaders.geometry)
683                 addShaderStage(shaders.geometry, ShaderStage.geometry);
684             if (shaders.fragment)
685                 addShaderStage(shaders.fragment, ShaderStage.fragment);
686 
687 
688             auto vkInputBindings = infos[i].inputBindings.map!(
689                 ib => VkVertexInputBindingDescription(
690                     ib.binding, cast(uint)ib.stride,
691                     ib.instanced ?
692                             VK_VERTEX_INPUT_RATE_INSTANCE :
693                             VK_VERTEX_INPUT_RATE_VERTEX
694                 )
695             ).array;
696 
697             auto vkInputAttribs = infos[i].inputAttribs.map!(
698                 ia => VkVertexInputAttributeDescription(
699                     ia.location, ia.binding, ia.format.toVk(), cast(uint)ia.offset
700                 )
701             ).array;
702 
703             auto vkVtxInput = new VkPipelineVertexInputStateCreateInfo;
704             vkVtxInput.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
705             vkVtxInput.vertexBindingDescriptionCount = cast(uint)vkInputBindings.length;
706             vkVtxInput.pVertexBindingDescriptions = vkInputBindings.ptr;
707             vkVtxInput.vertexAttributeDescriptionCount = cast(uint)vkInputAttribs.length;
708             vkVtxInput.pVertexAttributeDescriptions = vkInputAttribs.ptr;
709 
710             auto vkAssy = new VkPipelineInputAssemblyStateCreateInfo;
711             vkAssy.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
712             vkAssy.topology = infos[i].assembly.primitive.toVk();
713             vkAssy.primitiveRestartEnable = infos[i].assembly.primitiveRestart ? VK_TRUE : VK_FALSE;
714 
715             auto vkViewport = new VkPipelineViewportStateCreateInfo;
716             vkViewport.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
717             if (infos[i].viewports.length) {
718                 auto vkViewports = infos[i].viewports.map!(vc => vc.viewport).map!(
719                     vp => VkViewport(vp.x, vp.y, vp.width, vp.height, vp.minDepth, vp.maxDepth)
720                 ).array;
721                 auto vkScissors = infos[i].viewports.map!(vc => vc.scissors).map!(
722                     r => VkRect2D(VkOffset2D(r.x, r.y), VkExtent2D(r.width, r.height))
723                 ).array;
724                 vkViewport.viewportCount = cast(uint)infos[i].viewports.length;
725                 vkViewport.pViewports = vkViewports.ptr;
726                 vkViewport.scissorCount = cast(uint)infos[i].viewports.length;
727                 vkViewport.pScissors = vkScissors.ptr;
728             }
729             else {
730                 static const dummyVp = VkViewport(0f, 0f, 1f, 1f, 0f, 1f);
731                 static const dummySc = VkRect2D(VkOffset2D(0, 0), VkExtent2D(1, 1));
732                 vkViewport.viewportCount = 1;
733                 vkViewport.pViewports = &dummyVp;
734                 vkViewport.scissorCount = 1;
735                 vkViewport.pScissors = &dummySc;
736             }
737 
738             auto vkRasterizer = new VkPipelineRasterizationStateCreateInfo;
739             vkRasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
740             vkRasterizer.rasterizerDiscardEnable = shaders.fragment ? VK_FALSE : VK_TRUE;
741             vkRasterizer.polygonMode = infos[i].rasterizer.mode.toVk();
742             vkRasterizer.cullMode = cullModeToVk(infos[i].rasterizer.cull);
743             vkRasterizer.frontFace = infos[i].rasterizer.front.toVk();
744             vkRasterizer.lineWidth = infos[i].rasterizer.lineWidth;
745             vkRasterizer.depthClampEnable = infos[i].rasterizer.depthClamp ? VK_TRUE : VK_FALSE;
746             if (infos[i].rasterizer.depthBias.isSome) {
747                 DepthBias db = infos[i].rasterizer.depthBias.get;
748                 vkRasterizer.depthBiasEnable = VK_TRUE;
749                 vkRasterizer.depthBiasConstantFactor = db.constantFactor;
750                 vkRasterizer.depthBiasClamp = db.clamp;
751                 vkRasterizer.depthBiasSlopeFactor = db.slopeFactor;
752             }
753             else {
754                 vkRasterizer.depthBiasConstantFactor = 0f;
755                 vkRasterizer.depthBiasClamp = 0f;
756                 vkRasterizer.depthBiasSlopeFactor = 0f;
757             }
758 
759             const depthInfo = infos[i].depthInfo;
760             const stencilInfo = infos[i].stencilInfo;
761             auto vkDepthStencil = new VkPipelineDepthStencilStateCreateInfo;
762             vkDepthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
763             vkDepthStencil.depthTestEnable = flagToVk(depthInfo.enabled);
764             vkDepthStencil.depthWriteEnable = flagToVk(depthInfo.write);
765             vkDepthStencil.depthCompareOp = depthInfo.compareOp.toVk();
766             vkDepthStencil.depthBoundsTestEnable = flagToVk(depthInfo.boundsTest);
767             vkDepthStencil.stencilTestEnable = flagToVk(stencilInfo.enabled);
768             vkDepthStencil.front = transmute!VkStencilOpState(stencilInfo.front);
769             vkDepthStencil.back = transmute!VkStencilOpState(stencilInfo.back);
770             vkDepthStencil.minDepthBounds = depthInfo.minBounds;
771             vkDepthStencil.maxDepthBounds = depthInfo.maxBounds;
772 
773             const blendInfo = infos[i].blendInfo;
774             auto vkColorAttachments = blendInfo.attachments.map!(
775                 cba => VkPipelineColorBlendAttachmentState (
776                     cba.enabled ? VK_TRUE : VK_FALSE,
777                     cba.colorBlend.factor.from.toVk(),
778                     cba.colorBlend.factor.to.toVk(),
779                     cba.colorBlend.op.toVk(),
780                     cba.alphaBlend.factor.from.toVk(),
781                     cba.alphaBlend.factor.to.toVk(),
782                     cba.alphaBlend.op.toVk(),
783                     cast(VkColorComponentFlags)cba.colorMask
784                 )
785             ).array;
786             auto vkBlend = new VkPipelineColorBlendStateCreateInfo;
787             vkBlend.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
788             if (blendInfo.logicOp.isSome) {
789                 vkBlend.logicOpEnable = VK_TRUE;
790                 vkBlend.logicOp = blendInfo.logicOp.get.toVk();
791             }
792             vkBlend.attachmentCount = cast(uint)vkColorAttachments.length;
793             vkBlend.pAttachments = vkColorAttachments.ptr;
794             vkBlend.blendConstants = blendInfo.blendConstants;
795 
796             VkPipelineDynamicStateCreateInfo *vkDynStatesInfo;
797             if (infos[i].dynamicStates) {
798                 auto vkDynStates = infos[i].dynamicStates.map!(ds => ds.toVk()).array;
799                 vkDynStatesInfo = new VkPipelineDynamicStateCreateInfo;
800                 vkDynStatesInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
801                 vkDynStatesInfo.dynamicStateCount = cast(uint)vkDynStates.length;
802                 vkDynStatesInfo.pDynamicStates = vkDynStates.ptr;
803             }
804 
805             auto rp = infos[i].renderPass;
806             auto vkRp = rp ? enforce(
807                 cast(VulkanRenderPass)rp,
808                 "did not supply a Vulkan render pass"
809             ).vkObj : VK_NULL_ND_HANDLE;
810 
811             // following bindings are not implemented yet
812             auto vkTess = new VkPipelineTessellationStateCreateInfo;
813             vkTess.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
814             auto vkMs = new VkPipelineMultisampleStateCreateInfo;
815             vkMs.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
816             vkMs.minSampleShading = 1f;
817 
818             pcis[i].sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
819             pcis[i].stageCount = cast(uint)sscis.length;
820             pcis[i].pStages = sscis.ptr;
821             pcis[i].pVertexInputState = vkVtxInput;
822             pcis[i].pInputAssemblyState = vkAssy;
823             pcis[i].pTessellationState = vkTess;
824             pcis[i].pViewportState = vkViewport;
825             pcis[i].pRasterizationState = vkRasterizer;
826             pcis[i].pMultisampleState = vkMs;
827             pcis[i].pDepthStencilState = vkDepthStencil;
828             pcis[i].pColorBlendState = vkBlend;
829             pcis[i].pDynamicState = vkDynStatesInfo;
830             pcis[i].layout = enforce(
831                 cast(VulkanPipelineLayout)infos[i].layout,
832                 "did not pass a valid vulkan pipeline layout"
833             ).vkObj;
834             pcis[i].renderPass = vkRp;
835             pcis[i].subpass = infos[i].subpassIndex;
836             pcis[i].basePipelineIndex = -1;
837         }
838 
839         auto vkPls = new VkPipeline[infos.length];
840         vulkanEnforce(
841             vk.CreateGraphicsPipelines(vkObj, VK_NULL_ND_HANDLE, cast(uint)pcis.length, pcis.ptr, null, vkPls.ptr),
842             "Could not create Vulkan graphics pipeline"
843         );
844 
845         auto pls = new Pipeline[infos.length];
846         foreach (i; 0 .. vkPls.length) {
847             pls[i] = new VulkanPipeline(vkPls[i], this, infos[i].layout);
848         }
849         return pls;
850     }
851 
852     private Instance _inst;
853     private VulkanPhysicalDevice _pd;
854     private VkDeviceCmds _vk;
855     private VulkanQueue[] _queues;
856 }