1 /// Vulkan command module
2 module gfx.vulkan.cmd;
3 
4 package:
5 
6 import gfx.bindings.vulkan;
7 
8 import gfx.core.rc;
9 import gfx.core.typecons;
10 import gfx.graal.cmd;
11 import gfx.graal.renderpass;
12 import gfx.vulkan.buffer;
13 import gfx.vulkan.conv;
14 import gfx.vulkan.device;
15 import gfx.vulkan.error;
16 import gfx.vulkan.image;
17 import gfx.vulkan.renderpass;
18 
19 import std.typecons : Flag;
20 
21 class VulkanCommandPool : VulkanDevObj!(VkCommandPool, "DestroyCommandPool"), CommandPool
22 {
23     mixin(atomicRcCode);
24 
25     this (VkCommandPool pool, VulkanDevice dev) {
26         super(pool, dev);
27     }
28 
29     override void reset() {
30         vulkanEnforce(vk.ResetCommandPool(vkDev, vkObj, 0), "Could not reset command buffer");
31     }
32 
33     override CommandBuffer[] allocate(size_t count) {
34         VkCommandBufferAllocateInfo cbai;
35         cbai.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
36         cbai.commandPool = vkObj;
37         cbai.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
38         cbai.commandBufferCount = cast(uint)count;
39 
40         auto vkBufs = new VkCommandBuffer[count];
41         vulkanEnforce(
42             vk.AllocateCommandBuffers(vkDev, &cbai, &vkBufs[0]),
43             "Could not allocate command buffers"
44         );
45 
46         import std.algorithm : map;
47         import std.array : array;
48 
49         return vkBufs
50                 .map!(vkBuf => cast(CommandBuffer)new VulkanCommandBuffer(vkBuf, this))
51                 .array;
52     }
53 
54     override void free(CommandBuffer[] bufs) {
55         import std.algorithm : map;
56         import std.array : array;
57 
58         auto vkBufs = bufs.map!(
59             b => enforce(cast(VulkanCommandBuffer)b, "Did not pass a Vulkan command buffer").vkObj
60         ).array;
61         vk.FreeCommandBuffers(vkDev, vkObj, cast(uint)bufs.length, &vkBufs[0]);
62     }
63 }
64 
65 final class VulkanCommandBuffer : CommandBuffer
66 {
67     this (VkCommandBuffer vkObj, VulkanCommandPool pool) {
68         _vkObj = vkObj;
69         _pool = pool;
70         _vk = pool.vk;
71     }
72 
73     @property VkCommandBuffer vkObj() {
74         return _vkObj;
75     }
76 
77     override @property CommandPool pool() {
78         return _pool;
79     }
80 
81     @property VkDeviceCmds vk() {
82         return _vk;
83     }
84 
85     override void reset() {
86         vulkanEnforce(
87             vk.ResetCommandBuffer(vkObj, 0), "Could not reset vulkan command buffer"
88         );
89     }
90 
91     override void begin(Flag!"persistent" persistent) {
92         VkCommandBufferBeginInfo cbbi;
93         cbbi.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
94         cbbi.flags = persistent ?
95             VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT :
96             VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
97         vulkanEnforce(
98             vk.BeginCommandBuffer(vkObj, &cbbi), "Could not begin vulkan command buffer"
99         );
100     }
101 
102     override void end() {
103         vulkanEnforce(
104             vk.EndCommandBuffer(vkObj), "Could not end vulkan command buffer"
105         );
106     }
107 
108     override void pipelineBarrier(Trans!PipelineStage stageTrans,
109                                   BufferMemoryBarrier[] bufMbs,
110                                   ImageMemoryBarrier[] imgMbs)
111     {
112         import std.algorithm : map;
113         import std.array : array;
114 
115         auto vkBufMbs = bufMbs.map!((BufferMemoryBarrier bufMb) {
116             VkBufferMemoryBarrier vkBufMb;
117             vkBufMb.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
118             vkBufMb.srcAccessMask = accessToVk(bufMb.accessMaskTrans.from);
119             vkBufMb.dstAccessMask = accessToVk(bufMb.accessMaskTrans.to);
120             vkBufMb.srcQueueFamilyIndex = bufMb.queueFamIndexTrans.from;
121             vkBufMb.dstQueueFamilyIndex = bufMb.queueFamIndexTrans.to;
122             vkBufMb.buffer = enforce(cast(VulkanBuffer)bufMb.buffer, "Did not pass a Vulkan buffer").vkObj;
123             vkBufMb.offset = bufMb.offset;
124             vkBufMb.size = bufMb.size;
125             return vkBufMb;
126         }).array;
127 
128         auto vkImgMbs = imgMbs.map!((ImageMemoryBarrier imgMb) {
129             VkImageMemoryBarrier vkImgMb;
130             vkImgMb.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
131             vkImgMb.srcAccessMask = accessToVk(imgMb.accessMaskTrans.from);
132             vkImgMb.dstAccessMask = accessToVk(imgMb.accessMaskTrans.to);
133             vkImgMb.oldLayout = imgMb.layoutTrans.from.toVk();
134             vkImgMb.newLayout = imgMb.layoutTrans.to.toVk();
135             vkImgMb.srcQueueFamilyIndex = imgMb.queueFamIndexTrans.from;
136             vkImgMb.dstQueueFamilyIndex = imgMb.queueFamIndexTrans.to;
137             vkImgMb.image = enforce(cast(VulkanImageBase)imgMb.image, "Did not pass a Vulkan image").vkObj;
138             vkImgMb.subresourceRange = imgMb.range.toVk();
139             return vkImgMb;
140         }).array;
141 
142         vk.CmdPipelineBarrier( vkObj,
143             pipelineStageToVk(stageTrans.from), pipelineStageToVk(stageTrans.to),
144             0, 0, null,
145             cast(uint)vkBufMbs.length, vkBufMbs.ptr,
146             cast(uint)vkImgMbs.length, vkImgMbs.ptr
147         );
148     }
149 
150     override void clearColorImage(ImageBase image, ImageLayout layout,
151                          in ClearColorValues clearValues, ImageSubresourceRange[] ranges)
152     {
153         import std.algorithm : map;
154         import std.array : array;
155 
156         auto vkImg = enforce(cast(VulkanImageBase)image, "Did not pass a vulkan image").vkObj;
157         auto vkLayout = layout.toVk();
158         auto vkClear = cast(const(VkClearColorValue)*) cast(const(void)*) &clearValues.values;
159         auto vkRanges = ranges.map!(r => r.toVk()).array;
160 
161         vk.CmdClearColorImage(vkObj, vkImg, vkLayout, vkClear, cast(uint)vkRanges.length, &vkRanges[0]);
162     }
163 
164     override void clearDepthStencilImage(ImageBase image, ImageLayout layout,
165                                          in ClearDepthStencilValues clearValues,
166                                          ImageSubresourceRange[] ranges)
167     {
168         import std.algorithm : map;
169         import std.array : array;
170 
171         auto vkImg = enforce(cast(VulkanImageBase)image, "Did not pass a vulkan image").vkObj;
172         auto vkLayout = layout.toVk();
173         auto vkClear = VkClearDepthStencilValue(clearValues.depth, clearValues.stencil);
174         auto vkRanges = ranges.map!(r => r.toVk()).array;
175 
176         vk.CmdClearDepthStencilImage(vkObj, vkImg, vkLayout, &vkClear, cast(uint)vkRanges.length, &vkRanges[0]);
177     }
178 
179     override void fillBuffer(Buffer dst, in size_t offset, in size_t size, uint value)
180     {
181         auto vkBuf = enforce(cast(VulkanBuffer)dst, "Did not pass a valid vulkan buffer to fillBuffer").vkObj;
182         vk.CmdFillBuffer(vkObj, vkBuf, offset, size, value);
183     }
184 
185     override void updateBuffer(Buffer dst, in size_t offset, in uint[] data)
186     {
187         auto vkBuf = enforce(cast(VulkanBuffer)dst, "Did not pass a valid vulkan buffer to updateBuffer").vkObj;
188         vk.CmdUpdateBuffer(vkObj, vkBuf, offset, 4*data.length, cast(void*)data.ptr);
189     }
190 
191     override void copyBuffer(Trans!Buffer buffers, in CopyRegion[] regions)
192     {
193         import std.algorithm : map;
194         import std.array : array;
195 
196         auto vkRegions = regions.map!(
197             r => VkBufferCopy(r.offset.from, r.offset.to, r.size)
198         ).array;
199 
200         vk.CmdCopyBuffer(vkObj,
201             enforce(cast(VulkanBuffer)buffers.from).vkObj,
202             enforce(cast(VulkanBuffer)buffers.to).vkObj,
203             cast(uint)vkRegions.length, vkRegions.ptr
204         );
205     }
206 
207     void copyBufferToImage(Buffer srcBuffer, ImageBase dstImage,
208                            in ImageLayout dstLayout, in BufferImageCopy[] regions)
209     {
210         import gfx.core.util : transmute;
211         import std.algorithm : map;
212         import std.array : array;
213 
214         auto vkRegions = regions.map!(
215             bic => transmute!VkBufferImageCopy(bic)
216         ).array;
217 
218         vk.CmdCopyBufferToImage(
219             vkObj,
220             enforce(cast(VulkanBuffer)srcBuffer).vkObj,
221             enforce(cast(VulkanImageBase)dstImage).vkObj,
222             dstLayout.toVk(),
223             cast(uint)vkRegions.length, vkRegions.ptr
224         );
225     }
226 
227     override void setViewport(in uint firstViewport, in Viewport[] viewports)
228     {
229         import gfx.core.util : transmute;
230         const vkVp = transmute!(const(VkViewport)[])(viewports);
231         vk.CmdSetViewport(vkObj, firstViewport, cast(uint)vkVp.length, vkVp.ptr);
232     }
233 
234     override void setScissor(in uint firstScissor, in Rect[] scissors)
235     {
236         import gfx.core.util : transmute;
237         const vkSc = transmute!(const(VkRect2D)[])(scissors);
238         vk.CmdSetScissor(vkObj, firstScissor, cast(uint)vkSc.length, vkSc.ptr);
239     }
240 
241     override void setDepthBounds(in float minDepth, in float maxDepth)
242     {
243         vk.CmdSetDepthBounds(vkObj, minDepth, maxDepth);
244     }
245 
246     override void setLineWidth(in float lineWidth)
247     {
248         vk.CmdSetLineWidth(vkObj, lineWidth);
249     }
250 
251     override void setDepthBias(in float constFactor, in float clamp, in float slopeFactor)
252     {
253         vk.CmdSetDepthBias(vkObj, constFactor, clamp, slopeFactor);
254     }
255 
256     override void setStencilCompareMask(in StencilFace faceMask, in uint compareMask)
257     {
258         vk.CmdSetStencilCompareMask(vkObj, cast(VkStencilFaceFlags)faceMask, compareMask);
259     }
260 
261     override void setStencilWriteMask(in StencilFace faceMask, in uint writeMask)
262     {
263         vk.CmdSetStencilWriteMask(vkObj, cast(VkStencilFaceFlags)faceMask, writeMask);
264     }
265 
266     override void setStencilReference(in StencilFace faceMask, in uint reference)
267     {
268         vk.CmdSetStencilReference(vkObj, cast(VkStencilFaceFlags)faceMask, reference);
269     }
270 
271     override void setBlendConstants(in float[4] blendConstants)
272     {
273         vk.CmdSetBlendConstants(vkObj, blendConstants);
274     }
275 
276     override void beginRenderPass(RenderPass rp, Framebuffer fb,
277                                   Rect area, ClearValues[] clearValues)
278     {
279         import std.algorithm : map;
280         import std.array : array;
281         auto vkCvs = clearValues.map!(
282             (ClearValues cv) {
283                 VkClearValue vkCv;
284                 if (cv.type == ClearValues.Type.color) {
285                     const ccv = cv.values.color;
286                     VkClearColorValue vkCcv;
287                     switch (ccv.type) {
288                     case ClearColorValues.Type.f32:
289                         vkCcv.float32 = ccv.values.f32;
290                         break;
291                     case ClearColorValues.Type.i32:
292                         vkCcv.int32 = ccv.values.i32;
293                         break;
294                     case ClearColorValues.Type.u32:
295                         vkCcv.uint32 = ccv.values.u32;
296                         break;
297                     default:
298                         break;
299                     }
300                     vkCv.color = vkCcv;
301                 }
302                 else if (cv.type == ClearValues.Type.depthStencil) {
303                     const dscv = cv.values.depthStencil;
304                     vkCv.depthStencil = VkClearDepthStencilValue(
305                         dscv.depth, dscv.stencil
306                     );
307                 }
308                 return vkCv;
309             }
310         ).array;
311 
312         VkRenderPassBeginInfo bi;
313         bi.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
314         bi.renderPass = enforce(cast(VulkanRenderPass)rp, "did not supply a valid Vulkan render pass").vkObj;
315         bi.framebuffer = enforce(cast(VulkanFramebuffer)fb, "did not supply a valid Vulkan frame buffer").vkObj;
316         bi.renderArea = area.toVk();
317         bi.clearValueCount = cast(uint)vkCvs.length;
318         bi.pClearValues = vkCvs.ptr;
319 
320         vk.CmdBeginRenderPass(vkObj, &bi, VK_SUBPASS_CONTENTS_INLINE);
321     }
322 
323     override void nextSubpass() {
324         vk.CmdNextSubpass(vkObj, VK_SUBPASS_CONTENTS_INLINE);
325     }
326 
327     override void endRenderPass() {
328         vk.CmdEndRenderPass(vkObj);
329     }
330 
331     override void bindPipeline(Pipeline pipeline)
332     {
333         vk.CmdBindPipeline(vkObj, VK_PIPELINE_BIND_POINT_GRAPHICS, enforce(
334             cast(VulkanPipeline)pipeline, "did not pass a valid Vulkan pipeline"
335         ).vkObj);
336     }
337 
338     override void bindVertexBuffers(uint firstBinding, VertexBinding[] bindings) {
339         import std.algorithm : map;
340         import std.array : array;
341         auto vkBufs = bindings
342                 .map!(b => enforce(cast(VulkanBuffer)b.buffer).vkObj)
343                 .array;
344         auto vkOffsets = bindings
345                 .map!(b => cast(VkDeviceSize)b.offset)
346                 .array;
347         vk.CmdBindVertexBuffers(vkObj, firstBinding, cast(uint)bindings.length, vkBufs.ptr, vkOffsets.ptr);
348     }
349 
350     override void bindIndexBuffer(Buffer indexBuf, size_t offset, IndexType type) {
351         auto vkBuf = enforce(cast(VulkanBuffer)indexBuf).vkObj;
352         vk.CmdBindIndexBuffer(vkObj, vkBuf, offset, type.toVk());
353     }
354 
355     override void bindDescriptorSets(PipelineBindPoint bindPoint, PipelineLayout layout,
356                                      uint firstSet, DescriptorSet[] sets,
357                                      in size_t[] dynamicOffsets)
358     {
359         import std.algorithm : map;
360         import std.array : array;
361 
362         auto vkSets = sets.map!(s => enforce(cast(VulkanDescriptorSet)s).vkObj).array;
363         static if (size_t.sizeof == uint.sizeof) {
364             const vkOffsets = dynamicOffsets;
365         }
366         else {
367             const vkOffsets = dynamicOffsets.map!(o => cast(uint)o).array;
368         }
369 
370         vk.CmdBindDescriptorSets( vkObj, bindPoint.toVk(),
371             enforce(cast(VulkanPipelineLayout)layout).vkObj,
372             firstSet, cast(uint)vkSets.length, vkSets.ptr,
373             cast(uint)vkOffsets.length, vkOffsets.ptr);
374     }
375 
376     override void pushConstants(PipelineLayout layout, ShaderStage stages,
377                                 size_t offset, size_t size, const(void)* data)
378     {
379         auto vkPl = enforce(cast(VulkanPipelineLayout)layout).vkObj;
380         vk.CmdPushConstants(vkObj, vkPl, shaderStageToVk(stages), cast(uint)offset, cast(uint)size, data);
381     }
382 
383     override void draw(uint vertexCount, uint instanceCount, uint firstVertex, uint firstInstance)
384     {
385         vk.CmdDraw(vkObj, vertexCount, instanceCount, firstVertex, firstInstance);
386     }
387 
388     override void drawIndexed(uint indexCount, uint instanceCount, uint firstVertex, int vertexOffset, uint firstInstance)
389     {
390         vk.CmdDrawIndexed(vkObj, indexCount, instanceCount, firstVertex, vertexOffset, firstInstance);
391     }
392 
393     private VkCommandBuffer _vkObj;
394     private VulkanCommandPool _pool;
395     private VkDeviceCmds _vk;
396 }