CommandBufferImpl.h 16 KB

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