CommandBufferImpl.h 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581
  1. // Copyright (C) 2009-2021, Panagiotis Christopoulos Charitos and contributors.
  2. // All rights reserved.
  3. // Code licensed under the BSD License.
  4. // http://www.anki3d.org/LICENSE
  5. #pragma once
  6. #include <AnKi/Gr/CommandBuffer.h>
  7. #include <AnKi/Gr/Vulkan/VulkanObject.h>
  8. #include <AnKi/Gr/Vulkan/CommandBufferFactory.h>
  9. #include <AnKi/Gr/CommandBuffer.h>
  10. #include <AnKi/Gr/Texture.h>
  11. #include <AnKi/Gr/Buffer.h>
  12. #include <AnKi/Gr/Shader.h>
  13. #include <AnKi/Gr/Vulkan/BufferImpl.h>
  14. #include <AnKi/Gr/Vulkan/TextureImpl.h>
  15. #include <AnKi/Gr/Vulkan/Pipeline.h>
  16. #include <AnKi/Gr/Vulkan/GrManagerImpl.h>
  17. #include <AnKi/Util/List.h>
  18. namespace anki
  19. {
  20. #define ANKI_BATCH_COMMANDS 1
  21. // Forward
  22. class CommandBufferInitInfo;
  23. /// @addtogroup vulkan
  24. /// @{
  25. #define ANKI_CMD(x_, t_) \
  26. flushBatches(CommandBufferCommandType::t_); \
  27. x_;
  28. /// List the commands that can be batched.
  29. enum class CommandBufferCommandType : U8
  30. {
  31. SET_BARRIER,
  32. RESET_QUERY,
  33. WRITE_QUERY_RESULT,
  34. PUSH_SECOND_LEVEL,
  35. ANY_OTHER_COMMAND
  36. };
  37. /// Command buffer implementation.
  38. class CommandBufferImpl final : public CommandBuffer, public VulkanObject<CommandBuffer, CommandBufferImpl>
  39. {
  40. public:
  41. /// Default constructor
  42. CommandBufferImpl(GrManager* manager, CString name)
  43. : CommandBuffer(manager, name)
  44. , m_renderedToDefaultFb(false)
  45. , m_finalized(false)
  46. , m_empty(false)
  47. , m_beganRecording(false)
  48. {
  49. }
  50. ~CommandBufferImpl();
  51. ANKI_USE_RESULT Error init(const CommandBufferInitInfo& init);
  52. void setFence(MicroFencePtr& fence)
  53. {
  54. m_microCmdb->setFence(fence);
  55. }
  56. const MicroCommandBufferPtr& getMicroCommandBuffer()
  57. {
  58. return m_microCmdb;
  59. }
  60. VkCommandBuffer getHandle() const
  61. {
  62. ANKI_ASSERT(m_handle);
  63. return m_handle;
  64. }
  65. Bool renderedToDefaultFramebuffer() const
  66. {
  67. return m_renderedToDefaultFb;
  68. }
  69. Bool isEmpty() const
  70. {
  71. return m_empty;
  72. }
  73. Bool isSecondLevel() const
  74. {
  75. return !!(m_flags & CommandBufferFlag::SECOND_LEVEL);
  76. }
  77. void bindVertexBuffer(U32 binding, BufferPtr buff, PtrSize offset, PtrSize stride, VertexStepRate stepRate)
  78. {
  79. commandCommon();
  80. m_state.bindVertexBuffer(binding, stride, stepRate);
  81. VkBuffer vkbuff = static_cast<const BufferImpl&>(*buff).getHandle();
  82. ANKI_CMD(vkCmdBindVertexBuffers(m_handle, binding, 1, &vkbuff, &offset), ANY_OTHER_COMMAND);
  83. m_microCmdb->pushObjectRef(buff);
  84. }
  85. void setVertexAttribute(U32 location, U32 buffBinding, const Format fmt, PtrSize relativeOffset)
  86. {
  87. commandCommon();
  88. m_state.setVertexAttribute(location, buffBinding, fmt, relativeOffset);
  89. }
  90. void bindIndexBuffer(BufferPtr buff, PtrSize offset, IndexType type)
  91. {
  92. commandCommon();
  93. ANKI_CMD(vkCmdBindIndexBuffer(m_handle, static_cast<const BufferImpl&>(*buff).getHandle(), offset,
  94. convertIndexType(type)),
  95. ANY_OTHER_COMMAND);
  96. m_microCmdb->pushObjectRef(buff);
  97. }
  98. void setPrimitiveRestart(Bool enable)
  99. {
  100. commandCommon();
  101. m_state.setPrimitiveRestart(enable);
  102. }
  103. void setFillMode(FillMode mode)
  104. {
  105. commandCommon();
  106. m_state.setFillMode(mode);
  107. }
  108. void setCullMode(FaceSelectionBit mode)
  109. {
  110. commandCommon();
  111. m_state.setCullMode(mode);
  112. }
  113. void setViewport(U32 minx, U32 miny, U32 width, U32 height)
  114. {
  115. ANKI_ASSERT(width > 0 && height > 0);
  116. commandCommon();
  117. if(m_viewport[0] != minx || m_viewport[1] != miny || m_viewport[2] != width || m_viewport[3] != height)
  118. {
  119. m_viewportDirty = true;
  120. m_viewport[0] = minx;
  121. m_viewport[1] = miny;
  122. m_viewport[2] = width;
  123. m_viewport[3] = height;
  124. }
  125. }
  126. void setScissor(U32 minx, U32 miny, U32 width, U32 height)
  127. {
  128. ANKI_ASSERT(width > 0 && height > 0);
  129. commandCommon();
  130. if(m_scissor[0] != minx || m_scissor[1] != miny || m_scissor[2] != width || m_scissor[3] != height)
  131. {
  132. m_scissorDirty = true;
  133. m_scissor[0] = minx;
  134. m_scissor[1] = miny;
  135. m_scissor[2] = width;
  136. m_scissor[3] = height;
  137. }
  138. }
  139. void setPolygonOffset(F32 factor, F32 units)
  140. {
  141. commandCommon();
  142. m_state.setPolygonOffset(factor, units);
  143. }
  144. void setStencilOperations(FaceSelectionBit face, StencilOperation stencilFail,
  145. StencilOperation stencilPassDepthFail, StencilOperation stencilPassDepthPass)
  146. {
  147. commandCommon();
  148. m_state.setStencilOperations(face, stencilFail, stencilPassDepthFail, stencilPassDepthPass);
  149. }
  150. void setStencilCompareOperation(FaceSelectionBit face, CompareOperation comp)
  151. {
  152. commandCommon();
  153. m_state.setStencilCompareOperation(face, comp);
  154. }
  155. void setStencilCompareMask(FaceSelectionBit face, U32 mask);
  156. void setStencilWriteMask(FaceSelectionBit face, U32 mask);
  157. void setStencilReference(FaceSelectionBit face, U32 ref);
  158. void setDepthWrite(Bool enable)
  159. {
  160. commandCommon();
  161. m_state.setDepthWrite(enable);
  162. }
  163. void setDepthCompareOperation(CompareOperation op)
  164. {
  165. commandCommon();
  166. m_state.setDepthCompareOperation(op);
  167. }
  168. void setAlphaToCoverage(Bool enable)
  169. {
  170. commandCommon();
  171. m_state.setAlphaToCoverage(enable);
  172. }
  173. void setColorChannelWriteMask(U32 attachment, ColorBit mask)
  174. {
  175. commandCommon();
  176. m_state.setColorChannelWriteMask(attachment, mask);
  177. }
  178. void setBlendFactors(U32 attachment, BlendFactor srcRgb, BlendFactor dstRgb, BlendFactor srcA, BlendFactor dstA)
  179. {
  180. commandCommon();
  181. m_state.setBlendFactors(attachment, srcRgb, dstRgb, srcA, dstA);
  182. }
  183. void setBlendOperation(U32 attachment, BlendOperation funcRgb, BlendOperation funcA)
  184. {
  185. commandCommon();
  186. m_state.setBlendOperation(attachment, funcRgb, funcA);
  187. }
  188. void bindTextureAndSamplerInternal(U32 set, U32 binding, TextureViewPtr& texView, SamplerPtr sampler, U32 arrayIdx)
  189. {
  190. commandCommon();
  191. const TextureViewImpl& view = static_cast<const TextureViewImpl&>(*texView);
  192. const TextureImpl& tex = view.getTextureImpl();
  193. ANKI_ASSERT(tex.isSubresourceGoodForSampling(view.getSubresource()));
  194. const VkImageLayout lay = tex.computeLayout(TextureUsageBit::ALL_SAMPLED & tex.getTextureUsage(), 0);
  195. m_dsetState[set].bindTextureAndSampler(binding, arrayIdx, &view, sampler.get(), lay);
  196. m_microCmdb->pushObjectRef(texView);
  197. m_microCmdb->pushObjectRef(sampler);
  198. }
  199. void bindTextureInternal(U32 set, U32 binding, TextureViewPtr& texView, U32 arrayIdx)
  200. {
  201. commandCommon();
  202. const TextureViewImpl& view = static_cast<const TextureViewImpl&>(*texView);
  203. const TextureImpl& tex = view.getTextureImpl();
  204. ANKI_ASSERT(tex.isSubresourceGoodForSampling(view.getSubresource()));
  205. const VkImageLayout lay = tex.computeLayout(TextureUsageBit::ALL_SAMPLED & tex.getTextureUsage(), 0);
  206. m_dsetState[set].bindTexture(binding, arrayIdx, &view, lay);
  207. m_microCmdb->pushObjectRef(texView);
  208. }
  209. void bindSamplerInternal(U32 set, U32 binding, SamplerPtr& sampler, U32 arrayIdx)
  210. {
  211. commandCommon();
  212. m_dsetState[set].bindSampler(binding, arrayIdx, sampler.get());
  213. m_microCmdb->pushObjectRef(sampler);
  214. }
  215. void bindImageInternal(U32 set, U32 binding, TextureViewPtr& img, U32 arrayIdx)
  216. {
  217. commandCommon();
  218. m_dsetState[set].bindImage(binding, arrayIdx, img.get());
  219. const Bool isPresentable =
  220. !!(static_cast<const TextureViewImpl&>(*img).getTextureImpl().getTextureUsage() & TextureUsageBit::PRESENT);
  221. if(isPresentable)
  222. {
  223. m_renderedToDefaultFb = true;
  224. }
  225. m_microCmdb->pushObjectRef(img);
  226. }
  227. void bindAccelerationStructureInternal(U32 set, U32 binding, AccelerationStructurePtr& as, U32 arrayIdx)
  228. {
  229. commandCommon();
  230. m_dsetState[set].bindAccelerationStructure(binding, arrayIdx, as.get());
  231. m_microCmdb->pushObjectRef(as);
  232. }
  233. void bindAllBindlessInternal(U32 set)
  234. {
  235. commandCommon();
  236. m_dsetState[set].bindBindlessDescriptorSet();
  237. }
  238. void beginRenderPass(FramebufferPtr fb, const Array<TextureUsageBit, MAX_COLOR_ATTACHMENTS>& colorAttachmentUsages,
  239. TextureUsageBit depthStencilAttachmentUsage, U32 minx, U32 miny, U32 width, U32 height);
  240. void endRenderPass();
  241. void drawArrays(PrimitiveTopology topology, U32 count, U32 instanceCount, U32 first, U32 baseInstance);
  242. void drawElements(PrimitiveTopology topology, U32 count, U32 instanceCount, U32 firstIndex, U32 baseVertex,
  243. U32 baseInstance);
  244. void drawArraysIndirect(PrimitiveTopology topology, U32 drawCount, PtrSize offset, BufferPtr& buff);
  245. void drawElementsIndirect(PrimitiveTopology topology, U32 drawCount, PtrSize offset, BufferPtr& buff);
  246. void dispatchCompute(U32 groupCountX, U32 groupCountY, U32 groupCountZ);
  247. void traceRaysInternal(BufferPtr& sbtBuffer, PtrSize sbtBufferOffset, U32 sbtRecordSize, U32 hitGroupSbtRecordCount,
  248. U32 rayTypeCount, U32 width, U32 height, U32 depth);
  249. void resetOcclusionQuery(OcclusionQueryPtr query);
  250. void beginOcclusionQuery(OcclusionQueryPtr query);
  251. void endOcclusionQuery(OcclusionQueryPtr query);
  252. void resetTimestampQueryInternal(TimestampQueryPtr& query);
  253. void writeTimestampInternal(TimestampQueryPtr& query);
  254. void generateMipmaps2d(TextureViewPtr texView);
  255. void clearTextureView(TextureViewPtr texView, const ClearValue& clearValue);
  256. void pushSecondLevelCommandBuffer(CommandBufferPtr cmdb);
  257. void endRecording();
  258. void setTextureBarrier(TexturePtr tex, TextureUsageBit prevUsage, TextureUsageBit nextUsage,
  259. const TextureSubresourceInfo& subresource);
  260. void setTextureSurfaceBarrier(TexturePtr tex, TextureUsageBit prevUsage, TextureUsageBit nextUsage,
  261. const TextureSurfaceInfo& surf);
  262. void setTextureVolumeBarrier(TexturePtr tex, TextureUsageBit prevUsage, TextureUsageBit nextUsage,
  263. const TextureVolumeInfo& vol);
  264. void setTextureBarrierRange(TexturePtr tex, TextureUsageBit prevUsage, TextureUsageBit nextUsage,
  265. const VkImageSubresourceRange& range);
  266. void setBufferBarrier(VkPipelineStageFlags srcStage, VkAccessFlags srcAccess, VkPipelineStageFlags dstStage,
  267. VkAccessFlags dstAccess, PtrSize offset, PtrSize size, VkBuffer buff);
  268. void setBufferBarrier(BufferPtr& buff, BufferUsageBit before, BufferUsageBit after, PtrSize offset, PtrSize size);
  269. void setAccelerationStructureBarrierInternal(AccelerationStructurePtr& as, AccelerationStructureUsageBit prevUsage,
  270. AccelerationStructureUsageBit nextUsage);
  271. void fillBuffer(BufferPtr buff, PtrSize offset, PtrSize size, U32 value);
  272. void writeOcclusionQueryResultToBuffer(OcclusionQueryPtr query, PtrSize offset, BufferPtr buff);
  273. void bindShaderProgram(ShaderProgramPtr& prog);
  274. void bindUniformBufferInternal(U32 set, U32 binding, BufferPtr& buff, PtrSize offset, PtrSize range, U32 arrayIdx)
  275. {
  276. commandCommon();
  277. m_dsetState[set].bindUniformBuffer(binding, arrayIdx, buff.get(), offset, range);
  278. m_microCmdb->pushObjectRef(buff);
  279. }
  280. void bindStorageBufferInternal(U32 set, U32 binding, BufferPtr& buff, PtrSize offset, PtrSize range, U32 arrayIdx)
  281. {
  282. commandCommon();
  283. m_dsetState[set].bindStorageBuffer(binding, arrayIdx, buff.get(), offset, range);
  284. m_microCmdb->pushObjectRef(buff);
  285. }
  286. void copyBufferToTextureViewInternal(BufferPtr buff, PtrSize offset, PtrSize range, TextureViewPtr texView);
  287. void copyBufferToBuffer(BufferPtr& src, PtrSize srcOffset, BufferPtr& dst, PtrSize dstOffset, PtrSize range);
  288. void buildAccelerationStructureInternal(AccelerationStructurePtr& as);
  289. void setPushConstants(const void* data, U32 dataSize);
  290. void setRasterizationOrder(RasterizationOrder order);
  291. void setLineWidth(F32 width);
  292. void addReference(GrObjectPtr& ptr)
  293. {
  294. m_microCmdb->pushObjectRef(ptr);
  295. }
  296. private:
  297. StackAllocator<U8> m_alloc;
  298. MicroCommandBufferPtr m_microCmdb;
  299. VkCommandBuffer m_handle = VK_NULL_HANDLE;
  300. ThreadId m_tid = ~ThreadId(0);
  301. CommandBufferFlag m_flags = CommandBufferFlag::NONE;
  302. Bool m_renderedToDefaultFb : 1;
  303. Bool m_finalized : 1;
  304. Bool m_empty : 1;
  305. Bool m_beganRecording : 1;
  306. #if ANKI_EXTRA_CHECKS
  307. U32 m_commandCount = 0;
  308. U32 m_setPushConstantsSize = 0;
  309. #endif
  310. FramebufferPtr m_activeFb;
  311. Array<U32, 4> m_renderArea = {0, 0, MAX_U32, MAX_U32};
  312. Array<U32, 2> m_fbSize = {0, 0};
  313. U32 m_rpCommandCount = 0; ///< Number of drawcalls or pushed cmdbs in rp.
  314. Array<TextureUsageBit, MAX_COLOR_ATTACHMENTS> m_colorAttachmentUsages = {};
  315. TextureUsageBit m_depthStencilAttachmentUsage = TextureUsageBit::NONE;
  316. PipelineStateTracker m_state;
  317. Array<DescriptorSetState, MAX_DESCRIPTOR_SETS> m_dsetState;
  318. ShaderProgramImpl* m_graphicsProg ANKI_DEBUG_CODE(= nullptr); ///< Last bound graphics program
  319. ShaderProgramImpl* m_computeProg ANKI_DEBUG_CODE(= nullptr);
  320. ShaderProgramImpl* m_rtProg ANKI_DEBUG_CODE(= nullptr);
  321. VkSubpassContents m_subpassContents = VK_SUBPASS_CONTENTS_MAX_ENUM;
  322. CommandBufferCommandType m_lastCmdType = CommandBufferCommandType::ANY_OTHER_COMMAND;
  323. /// @name state_opts
  324. /// @{
  325. Array<U32, 4> m_viewport = {0, 0, 0, 0};
  326. Array<U32, 4> m_scissor = {0, 0, MAX_U32, MAX_U32};
  327. Bool m_viewportDirty = true;
  328. VkViewport m_lastViewport = {};
  329. Bool m_scissorDirty = true;
  330. VkRect2D m_lastScissor = {{-1, -1}, {MAX_U32, MAX_U32}};
  331. Array<U32, 2> m_stencilCompareMasks = {0x5A5A5A5A, 0x5A5A5A5A}; ///< Use a stupid number to initialize.
  332. Array<U32, 2> m_stencilWriteMasks = {0x5A5A5A5A, 0x5A5A5A5A};
  333. Array<U32, 2> m_stencilReferenceMasks = {0x5A5A5A5A, 0x5A5A5A5A};
  334. #if ANKI_ENABLE_ASSERTIONS
  335. Bool m_lineWidthSet = false;
  336. #endif
  337. /// Rebind the above dynamic state. Needed after pushing secondary command buffers (they dirty the state).
  338. void rebindDynamicState();
  339. /// @}
  340. /// @name barrier_batch
  341. /// @{
  342. DynamicArray<VkImageMemoryBarrier> m_imgBarriers;
  343. DynamicArray<VkBufferMemoryBarrier> m_buffBarriers;
  344. DynamicArray<VkMemoryBarrier> m_memBarriers;
  345. U16 m_imgBarrierCount = 0;
  346. U16 m_buffBarrierCount = 0;
  347. U16 m_memBarrierCount = 0;
  348. VkPipelineStageFlags m_srcStageMask = 0;
  349. VkPipelineStageFlags m_dstStageMask = 0;
  350. /// @}
  351. /// @name reset_query_batch
  352. /// @{
  353. class QueryResetAtom
  354. {
  355. public:
  356. VkQueryPool m_pool;
  357. U32 m_queryIdx;
  358. };
  359. DynamicArray<QueryResetAtom> m_queryResetAtoms;
  360. /// @}
  361. /// @name write_query_result_batch
  362. /// @{
  363. class WriteQueryAtom
  364. {
  365. public:
  366. VkQueryPool m_pool;
  367. U32 m_queryIdx;
  368. VkBuffer m_buffer;
  369. PtrSize m_offset;
  370. };
  371. DynamicArray<WriteQueryAtom> m_writeQueryAtoms;
  372. /// @}
  373. /// @name push_second_level_batch
  374. /// @{
  375. DynamicArray<VkCommandBuffer> m_secondLevelAtoms;
  376. U16 m_secondLevelAtomCount = 0;
  377. /// @}
  378. /// Some common operations per command.
  379. void commandCommon();
  380. /// Flush batches. Use ANKI_CMD on every vkCmdXXX to do that automatically and call it manually before adding to a
  381. /// batch.
  382. void flushBatches(CommandBufferCommandType type);
  383. void drawcallCommon();
  384. Bool insideRenderPass() const
  385. {
  386. return m_activeFb.isCreated();
  387. }
  388. void beginRenderPassInternal();
  389. Bool secondLevel() const
  390. {
  391. return !!(m_flags & CommandBufferFlag::SECOND_LEVEL);
  392. }
  393. /// Flush batched image and buffer barriers.
  394. void flushBarriers();
  395. void flushQueryResets();
  396. void flushWriteQueryResults();
  397. void setImageBarrier(VkPipelineStageFlags srcStage, VkAccessFlags srcAccess, VkImageLayout prevLayout,
  398. VkPipelineStageFlags dstStage, VkAccessFlags dstAccess, VkImageLayout newLayout, VkImage img,
  399. const VkImageSubresourceRange& range);
  400. void beginRecording();
  401. Bool flipViewport() const;
  402. static VkViewport computeViewport(U32* viewport, U32 fbWidth, U32 fbHeight, Bool flipvp)
  403. {
  404. const U32 minx = viewport[0];
  405. const U32 miny = viewport[1];
  406. const U32 width = min<U32>(fbWidth, viewport[2]);
  407. const U32 height = min<U32>(fbHeight, viewport[3]);
  408. ANKI_ASSERT(width > 0 && height > 0);
  409. ANKI_ASSERT(minx + width <= fbWidth);
  410. ANKI_ASSERT(miny + height <= fbHeight);
  411. VkViewport s = {};
  412. s.x = F32(minx);
  413. s.y = (flipvp) ? F32(fbHeight - miny) : F32(miny); // Move to the bottom;
  414. s.width = F32(width);
  415. s.height = (flipvp) ? -F32(height) : F32(height);
  416. s.minDepth = 0.0f;
  417. s.maxDepth = 1.0f;
  418. return s;
  419. }
  420. static VkRect2D computeScissor(U32* scissor, U32 fbWidth, U32 fbHeight, Bool flipvp)
  421. {
  422. const U32 minx = scissor[0];
  423. const U32 miny = scissor[1];
  424. const U32 width = min<U32>(fbWidth, scissor[2]);
  425. const U32 height = min<U32>(fbHeight, scissor[3]);
  426. ANKI_ASSERT(minx + width <= fbWidth);
  427. ANKI_ASSERT(miny + height <= fbHeight);
  428. VkRect2D out = {};
  429. out.extent.width = width;
  430. out.extent.height = height;
  431. out.offset.x = minx;
  432. out.offset.y = (flipvp) ? (fbHeight - (miny + height)) : miny;
  433. return out;
  434. }
  435. const ShaderProgramImpl& getBoundProgram()
  436. {
  437. if(m_graphicsProg)
  438. {
  439. ANKI_ASSERT(m_computeProg == nullptr && m_rtProg == nullptr);
  440. return *m_graphicsProg;
  441. }
  442. else if(m_computeProg)
  443. {
  444. ANKI_ASSERT(m_graphicsProg == nullptr && m_rtProg == nullptr);
  445. return *m_computeProg;
  446. }
  447. else
  448. {
  449. ANKI_ASSERT(m_graphicsProg == nullptr && m_computeProg == nullptr && m_rtProg != nullptr);
  450. return *m_rtProg;
  451. }
  452. }
  453. };
  454. /// @}
  455. } // end namespace anki
  456. #include <AnKi/Gr/Vulkan/CommandBufferImpl.inl.h>