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