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 }