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(cmds.resetCommandPool(vkDev, vk, 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 = vk;
37         cbai.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
38         cbai.commandBufferCount = cast(uint)count;
39 
40         auto vkBufs = new VkCommandBuffer[count];
41         vulkanEnforce(
42             cmds.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").vk
60         ).array;
61         cmds.freeCommandBuffers(vkDev, vk, cast(uint)bufs.length, &vkBufs[0]);
62     }
63 }
64 
65 final class VulkanCommandBuffer : CommandBuffer
66 {
67     this (VkCommandBuffer vk, VulkanCommandPool pool) {
68         _vk = vk;
69         _pool = pool;
70         _cmds = pool.cmds;
71     }
72 
73     @property VkCommandBuffer vk() {
74         return _vk;
75     }
76 
77     override @property CommandPool pool() {
78         return _pool;
79     }
80 
81     @property VkDeviceCmds cmds() {
82         return _cmds;
83     }
84 
85     override void reset() {
86         vulkanEnforce(
87             cmds.resetCommandBuffer(vk, 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             cmds.beginCommandBuffer(vk, &cbbi), "Could not begin vulkan command buffer"
99         );
100     }
101 
102     override void end() {
103         vulkanEnforce(
104             cmds.endCommandBuffer(vk), "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").vk;
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(VulkanImage)imgMb.image, "Did not pass a Vulkan image").vk;
138             vkImgMb.subresourceRange = imgMb.range.toVk();
139             return vkImgMb;
140         }).array;
141 
142         cmds.cmdPipelineBarrier( vk,
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(Image 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(VulkanImage)image, "Did not pass a vulkan image").vk;
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         cmds.cmdClearColorImage(vk, vkImg, vkLayout, vkClear, cast(uint)vkRanges.length, &vkRanges[0]);
162     }
163 
164     override void clearDepthStencilImage(Image 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(VulkanImage)image, "Did not pass a vulkan image").vk;
172         auto vkLayout = layout.toVk();
173         auto vkClear = VkClearDepthStencilValue(clearValues.depth, clearValues.stencil);
174         auto vkRanges = ranges.map!(r => r.toVk()).array;
175 
176         cmds.cmdClearDepthStencilImage(vk, vkImg, vkLayout, &vkClear, cast(uint)vkRanges.length, &vkRanges[0]);
177     }
178 
179     override void beginRenderPass(RenderPass rp, Framebuffer fb,
180                                   Rect area, ClearValues[] clearValues)
181     {
182         import std.algorithm : map;
183         import std.array : array;
184         auto vkCvs = clearValues.map!(
185             (ClearValues cv) {
186                 VkClearValue vkCv;
187                 if (cv.type == ClearValues.Type.color) {
188                     const ccv = cv.values.color;
189                     VkClearColorValue vkCcv;
190                     switch (ccv.type) {
191                     case ClearColorValues.Type.f32:
192                         vkCcv.float32 = ccv.values.f32;
193                         break;
194                     case ClearColorValues.Type.i32:
195                         vkCcv.int32 = ccv.values.i32;
196                         break;
197                     case ClearColorValues.Type.u32:
198                         vkCcv.uint32 = ccv.values.u32;
199                         break;
200                     default:
201                         break;
202                     }
203                     vkCv.color = vkCcv;
204                 }
205                 else if (cv.type == ClearValues.Type.depthStencil) {
206                     const dscv = cv.values.depthStencil;
207                     vkCv.depthStencil = VkClearDepthStencilValue(
208                         dscv.depth, dscv.stencil
209                     );
210                 }
211                 return vkCv;
212             }
213         ).array;
214 
215         VkRenderPassBeginInfo bi;
216         bi.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
217         bi.renderPass = enforce(cast(VulkanRenderPass)rp, "did not supply a valid Vulkan render pass").vk;
218         bi.framebuffer = enforce(cast(VulkanFramebuffer)fb, "did not supply a valid Vulkan frame buffer").vk;
219         bi.renderArea = area.toVk();
220         bi.clearValueCount = cast(uint)vkCvs.length;
221         bi.pClearValues = vkCvs.ptr;
222 
223         cmds.cmdBeginRenderPass(vk, &bi, VK_SUBPASS_CONTENTS_INLINE);
224     }
225 
226     override void nextSubpass() {
227         cmds.cmdNextSubpass(vk, VK_SUBPASS_CONTENTS_INLINE);
228     }
229 
230     override void endRenderPass() {
231         cmds.cmdEndRenderPass(vk);
232     }
233 
234     override void bindPipeline(Pipeline pipeline)
235     {
236         cmds.cmdBindPipeline(vk, VK_PIPELINE_BIND_POINT_GRAPHICS, enforce(
237             cast(VulkanPipeline)pipeline, "did not pass a valid Vulkan pipeline"
238         ).vk);
239     }
240 
241     override void bindVertexBuffers(uint firstBinding, VertexBinding[] bindings) {
242         import std.algorithm : map;
243         import std.array : array;
244         auto vkBufs = bindings
245                 .map!(b => enforce(cast(VulkanBuffer)b.buffer).vk)
246                 .array;
247         auto vkOffsets = bindings
248                 .map!(b => cast(VkDeviceSize)b.offset)
249                 .array;
250         cmds.cmdBindVertexBuffers(vk, firstBinding, cast(uint)bindings.length, vkBufs.ptr, vkOffsets.ptr);
251     }
252 
253 
254     override void draw(uint vertexCount, uint instanceCount, uint firstVertex, uint firstInstance)
255     {
256         cmds.cmdDraw(vk, vertexCount, instanceCount, firstVertex, firstInstance);
257     }
258 
259     private VkCommandBuffer _vk;
260     private VulkanCommandPool _pool;
261     private VkDeviceCmds _cmds;
262 }