-
Notifications
You must be signed in to change notification settings - Fork 69
Mesh Pipelines #982
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Mesh Pipelines #982
Changes from 2 commits
882f135
99168b9
32f4d99
5b55cfd
dc845b1
25149d0
8226aac
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,149 @@ | ||
| #ifndef _NBL_I_CPU_MESH_PIPELINE_H_INCLUDED_ | ||
| #define _NBL_I_CPU_MESH_PIPELINE_H_INCLUDED_ | ||
|
|
||
|
|
||
| #include "nbl/asset/IMeshPipeline.h" | ||
| #include "nbl/asset/ICPURenderpass.h" | ||
| #include "nbl/asset/ICPUPipeline.h" | ||
|
|
||
|
|
||
| namespace nbl::asset | ||
| { | ||
|
|
||
| class ICPUMeshPipeline final : public ICPUPipeline<IMeshPipeline<ICPUPipelineLayout,ICPURenderpass>> | ||
| { | ||
| using pipeline_base_t = IMeshPipeline<ICPUPipelineLayout, ICPURenderpass>; | ||
| using base_t = ICPUPipeline<pipeline_base_t>; | ||
|
|
||
| public: | ||
|
|
||
| static core::smart_refctd_ptr<ICPUMeshPipeline> create(ICPUPipelineLayout* layout, ICPURenderpass* renderpass = nullptr) | ||
| { | ||
| auto retval = new ICPUMeshPipeline(layout, renderpass); | ||
| return core::smart_refctd_ptr<ICPUMeshPipeline>(retval,core::dont_grab); | ||
| } | ||
|
|
||
| constexpr static inline auto AssetType = ET_MESH_PIPELINE; | ||
| inline E_TYPE getAssetType() const override { return AssetType; } | ||
|
|
||
| inline const SCachedCreationParams& getCachedCreationParams() const | ||
| { | ||
| return pipeline_base_t::getCachedCreationParams(); | ||
| } | ||
|
|
||
| inline SCachedCreationParams& getCachedCreationParams() | ||
| { | ||
| assert(isMutable()); | ||
| return m_params; | ||
| } | ||
|
Comment on lines
+29
to
+38
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if the creation params are identical for Gfx and MEsh, should go in
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 99168b9#diff-45681900933e70545ee1a41c9ad2419f9cd40ef479ad7afe82dc97f29095c36aR13.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I take that back, I only thought about
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you can use composition ot inheritance to split the SCachedCreationParams. we will support vertex inputs as long as we support graphics pipelines. |
||
|
|
||
| inline std::span<const SShaderSpecInfo> getSpecInfos(const hlsl::ShaderStage stage) const override final | ||
| { | ||
| switch (stage) { | ||
| case hlsl::ShaderStage::ESS_TASK: return { &m_specInfos[0], 1 }; | ||
| case hlsl::ShaderStage::ESS_MESH: return { &m_specInfos[1], 1 }; | ||
| case hlsl::ShaderStage::ESS_FRAGMENT: return { &m_specInfos[2], 1 }; | ||
| } | ||
| return {}; | ||
| } | ||
|
|
||
| inline std::span<SShaderSpecInfo> getSpecInfos(const hlsl::ShaderStage stage) | ||
| { | ||
| return base_t::getSpecInfos(stage); | ||
| } | ||
devshgraphicsprogramming marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| std::span<SShaderSpecInfo> getSpecInfo(const hlsl::ShaderStage stage) | ||
| { | ||
| if (!isMutable()) return {}; | ||
| switch (stage) { | ||
| case hlsl::ShaderStage::ESS_TASK: return { &m_specInfos[0], 1 }; | ||
| case hlsl::ShaderStage::ESS_MESH: return { &m_specInfos[1], 1 }; | ||
| case hlsl::ShaderStage::ESS_FRAGMENT: return { &m_specInfos[2], 1 }; | ||
| } | ||
| return {}; | ||
| } | ||
|
|
||
| std::span<const SShaderSpecInfo> getSpecInfo(const hlsl::ShaderStage stage) const | ||
| { | ||
| switch (stage) { | ||
| case hlsl::ShaderStage::ESS_TASK: return { &m_specInfos[0], 1 }; | ||
| case hlsl::ShaderStage::ESS_MESH: return { &m_specInfos[1], 1 }; | ||
| case hlsl::ShaderStage::ESS_FRAGMENT: return { &m_specInfos[2], 1 }; | ||
| } | ||
| return {}; | ||
| } | ||
|
|
||
| inline bool valid() const override | ||
| { | ||
| if (!m_layout) return false; | ||
| if (!m_layout->valid())return false; | ||
|
|
||
| // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkGraphicsPipelineCreateInfo.html#VUID-VkGraphicsPipelineCreateInfo-dynamicRendering-06576 | ||
| if (!m_renderpass || m_params.subpassIx >= m_renderpass->getSubpassCount()) return false; | ||
|
|
||
| core::bitflag<hlsl::ShaderStage> stagePresence = {}; | ||
| for (auto shader_i = 0u; shader_i < m_specInfos.size(); shader_i++) | ||
| { | ||
| const auto& info = m_specInfos[shader_i]; | ||
| if (info.shader) | ||
| stagePresence |= indexToStage(shader_i); | ||
| } | ||
| return hasRequiredStages(stagePresence); | ||
| } | ||
|
|
||
| protected: | ||
| using base_t::base_t; | ||
| virtual ~ICPUMeshPipeline() override = default; | ||
|
|
||
| std::array<SShaderSpecInfo, MESH_SHADER_STAGE_COUNT> m_specInfos; | ||
|
|
||
| private: | ||
| explicit ICPUMeshPipeline(ICPUPipelineLayout* layout, ICPURenderpass* renderpass) | ||
| : base_t(layout, {}, renderpass) | ||
| {} | ||
|
|
||
| static inline int8_t stageToIndex(const hlsl::ShaderStage stage) | ||
| { | ||
| switch(stage){ | ||
| case hlsl::ShaderStage::ESS_TASK: return 0; | ||
| case hlsl::ShaderStage::ESS_MESH: return 1; | ||
| case hlsl::ShaderStage::ESS_FRAGMENT: return 2; | ||
| } | ||
| return -1; | ||
| } | ||
|
|
||
| static inline hlsl::ShaderStage indexToStage(const int8_t index) | ||
| { | ||
| switch (index) { | ||
| case 0: return hlsl::ShaderStage::ESS_TASK; | ||
| case 1: return hlsl::ShaderStage::ESS_MESH; | ||
| case 2: return hlsl::ShaderStage::ESS_FRAGMENT; | ||
| } | ||
| return hlsl::ShaderStage::ESS_UNKNOWN; | ||
| } | ||
|
|
||
| inline core::smart_refctd_ptr<base_t> clone_impl(core::smart_refctd_ptr<ICPUPipelineLayout>&& layout, uint32_t depth) const override final | ||
| { | ||
| auto* newPipeline = new ICPUMeshPipeline(layout.get(), m_renderpass.get()); | ||
| newPipeline->m_params = m_params; | ||
|
|
||
| for (auto specInfo_i = 0u; specInfo_i < m_specInfos.size(); specInfo_i++) | ||
| { | ||
| newPipeline->m_specInfos[specInfo_i] = m_specInfos[specInfo_i].clone(depth); | ||
| } | ||
|
|
||
| return core::smart_refctd_ptr<base_t>(newPipeline, core::dont_grab); | ||
| } | ||
|
|
||
| inline void visitDependents_impl(std::function<bool(const IAsset*)> visit) const override | ||
| { | ||
| if (!visit(m_layout.get())) return; | ||
| if (!visit(m_renderpass.get())) return; | ||
| for (const auto& info : m_specInfos) | ||
| if (!visit(info.shader.get())) return; | ||
| } | ||
| }; | ||
|
|
||
| } | ||
|
|
||
| #endif | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| #ifndef _NBL_ASSET_I_MESH_PIPELINE_H_INCLUDED_ | ||
| #define _NBL_ASSET_I_MESH_PIPELINE_H_INCLUDED_ | ||
|
|
||
| #include "nbl/asset/IShader.h" | ||
| #include "nbl/asset/RasterizationStates.h" | ||
| #include "nbl/asset/IPipeline.h" | ||
|
|
||
|
|
||
| namespace nbl::asset { | ||
| class IMeshPipelineBase : public virtual core::IReferenceCounted { | ||
| public: | ||
| constexpr static inline uint8_t MESH_SHADER_STAGE_COUNT = 3u; | ||
| struct SCachedCreationParams final { | ||
| SRasterizationParams rasterization = {}; | ||
| SBlendParams blend = {}; | ||
| uint32_t subpassIx = 0u; | ||
| uint8_t requireFullSubgroups = false; | ||
| }; | ||
|
|
||
| }; | ||
|
|
||
| template<typename PipelineLayoutType, typename RenderpassType> | ||
| class IMeshPipeline : public IPipeline<PipelineLayoutType>, public IMeshPipelineBase { | ||
| protected: | ||
| using renderpass_t = RenderpassType; | ||
| public: | ||
| inline const SCachedCreationParams& getCachedCreationParams() const { return m_params; } | ||
| inline const renderpass_t* getRenderpass() const {return m_renderpass.get();} | ||
|
|
||
| static inline bool hasRequiredStages(const core::bitflag<hlsl::ShaderStage>& stagePresence) | ||
| { | ||
| return stagePresence.hasFlags(hlsl::ShaderStage::ESS_MESH); | ||
| } | ||
|
|
||
| protected: | ||
| explicit IMeshPipeline(PipelineLayoutType* layout, const SCachedCreationParams& cachedParams, renderpass_t* renderpass) : | ||
| IPipeline<PipelineLayoutType>(core::smart_refctd_ptr<PipelineLayoutType>(layout)), | ||
| m_params(cachedParams), m_renderpass(core::smart_refctd_ptr<renderpass_t>(renderpass)) | ||
| { | ||
| } | ||
|
|
||
| SCachedCreationParams m_params = {}; | ||
| core::smart_refctd_ptr<renderpass_t> m_renderpass = nullptr; | ||
| }; | ||
|
|
||
| } | ||
|
|
||
|
|
||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -328,8 +328,10 @@ class NBL_API2 IGPUCommandBuffer : public IBackendObject | |
| bool copyAccelerationStructureFromMemory(const AccelerationStructure::DeviceCopyFromMemoryInfo& copyInfo); | ||
|
|
||
| //! state setup | ||
| bool bindComputePipeline(const IGPUComputePipeline* const pipeline); | ||
| bool bindGraphicsPipeline(const IGPUGraphicsPipeline* const pipeline); | ||
| bool bindComputePipeline(const IGPUComputePipeline* const pipeline); | ||
| bool bindMeshPipeline(const IGPUMeshPipeline* const pipeline); | ||
|
|
||
| bool bindRayTracingPipeline(const IGPURayTracingPipeline* const pipeline); | ||
| bool bindDescriptorSets( | ||
| const asset::E_PIPELINE_BIND_POINT pipelineBindPoint, const IGPUPipelineLayout* const layout, | ||
|
|
@@ -434,14 +436,21 @@ class NBL_API2 IGPUCommandBuffer : public IBackendObject | |
| ); | ||
|
|
||
| //! dispatches | ||
| bool dispatch(const uint32_t groupCountX, const uint32_t groupCountY=1, const uint32_t groupCountZ=1); | ||
| template<typename T> requires std::is_integral_v<T> | ||
| bool dispatch(const hlsl::vector<T,3> groupCount) | ||
| { | ||
| return dispatch(groupCount.x,groupCount.y,groupCount.z); | ||
| bool dispatch(const hlsl::vector<uint16_t, 3> groupCount); | ||
| inline bool dispatch(const uint32_t groupCountX, const uint32_t groupCountY=1, const uint32_t groupCountZ=1) | ||
| { | ||
| return dispatch(hlsl::vector<uint16_t, 3>{groupCountX, groupCountY, groupCountZ}); | ||
| } | ||
| bool dispatchIndirect(const asset::SBufferBinding<const IGPUBuffer>& binding); | ||
|
|
||
| bool drawMeshTasks(const hlsl::vector<uint16_t, 3> groupCount); | ||
| inline bool drawMeshTasks(const uint32_t groupCountX, const uint32_t groupCountY = 1, const uint32_t groupCountZ = 1) | ||
| { | ||
| return drawMeshTasks(hlsl::vector<uint16_t, 3>{groupCountX, groupCountY, groupCountZ}); | ||
| } | ||
| bool drawMeshTasksIndirect(const asset::SBufferBinding<const IGPUBuffer>& binding, const uint32_t drawCount, uint32_t stride); | ||
| bool drawMeshTasksIndirectCount(const asset::SBufferBinding<const IGPUBuffer>& indirectBinding, const asset::SBufferBinding<const IGPUBuffer>& countBinding, const uint32_t maxDrawCount, const uint32_t stride); | ||
|
|
||
| //! Begin/End RenderPasses | ||
| struct SRenderpassBeginInfo | ||
| { | ||
|
|
@@ -585,7 +594,7 @@ class NBL_API2 IGPUCommandBuffer : public IBackendObject | |
| virtual const void* getNativeHandle() const = 0; | ||
|
|
||
| inline const core::unordered_map<const IGPUDescriptorSet*, uint64_t>& getBoundDescriptorSetsRecord() const { return m_boundDescriptorSetsRecord; } | ||
| const IGPUGraphicsPipeline* getBoundGraphicsPipeline() const { return m_boundGraphicsPipeline; } | ||
| const IGPUPipelineBase* getBoundGraphicsPipeline() const { return m_boundRasterizationPipeline; } | ||
|
Comment on lines
-588
to
+597
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we have a |
||
| const IGPUComputePipeline* getBoundComputePipeline() const { return m_boundComputePipeline; } | ||
| const IGPURayTracingPipeline* getBoundRayTracingPipeline() const { return m_boundRayTracingPipeline; } | ||
|
|
||
|
|
@@ -670,8 +679,9 @@ class NBL_API2 IGPUCommandBuffer : public IBackendObject | |
| virtual bool copyAccelerationStructureToMemory_impl(const IGPUAccelerationStructure* src, const asset::SBufferBinding<IGPUBuffer>& dst) = 0; | ||
| virtual bool copyAccelerationStructureFromMemory_impl(const asset::SBufferBinding<const IGPUBuffer>& src, IGPUAccelerationStructure* dst) = 0; | ||
|
|
||
| virtual bool bindComputePipeline_impl(const IGPUComputePipeline* const pipeline) = 0; | ||
| virtual bool bindGraphicsPipeline_impl(const IGPUGraphicsPipeline* const pipeline) = 0; | ||
| virtual bool bindComputePipeline_impl(const IGPUComputePipeline* const pipeline) = 0; | ||
| virtual bool bindMeshPipeline_impl(const IGPUMeshPipeline* const pipeline) = 0; | ||
| virtual bool bindRayTracingPipeline_impl(const IGPURayTracingPipeline* const pipeline) = 0; | ||
| virtual bool bindDescriptorSets_impl( | ||
| const asset::E_PIPELINE_BIND_POINT pipelineBindPoint, const IGPUPipelineLayout* const layout, | ||
|
|
@@ -710,10 +720,15 @@ class NBL_API2 IGPUCommandBuffer : public IBackendObject | |
|
|
||
| virtual bool draw_impl(const uint32_t vertexCount, const uint32_t instanceCount, const uint32_t firstVertex, const uint32_t firstInstance) = 0; | ||
| virtual bool drawIndexed_impl(const uint32_t indexCount, const uint32_t instanceCount, const uint32_t firstIndex, const int32_t vertexOffset, const uint32_t firstInstance) = 0; | ||
| virtual bool drawMeshTasks_impl(const uint32_t groupCountX, const uint32_t groupCountY, const uint32_t groupCountZ) = 0; | ||
|
|
||
| virtual bool drawIndirect_impl(const asset::SBufferBinding<const IGPUBuffer>& binding, const uint32_t drawCount, const uint32_t stride) = 0; | ||
| virtual bool drawIndexedIndirect_impl(const asset::SBufferBinding<const IGPUBuffer>& binding, const uint32_t drawCount, const uint32_t stride) = 0; | ||
| virtual bool drawMeshTasksIndirect_impl(const asset::SBufferBinding<const IGPUBuffer>& binding, const uint32_t drawCount, const uint32_t stride) = 0; | ||
|
|
||
| virtual bool drawIndirectCount_impl(const asset::SBufferBinding<const IGPUBuffer>& indirectBinding, const asset::SBufferBinding<const IGPUBuffer>& countBinding, const uint32_t maxDrawCount, const uint32_t stride) = 0; | ||
| virtual bool drawIndexedIndirectCount_impl(const asset::SBufferBinding<const IGPUBuffer>& indirectBinding, const asset::SBufferBinding<const IGPUBuffer>& countBinding, const uint32_t maxDrawCount, const uint32_t stride) = 0; | ||
| virtual bool drawMeshTasksIndirectCount_impl(const asset::SBufferBinding<const IGPUBuffer>& indirectBinding, const asset::SBufferBinding<const IGPUBuffer>& countBinding, const uint32_t maxDrawCount, const uint32_t stride) = 0; | ||
|
|
||
| virtual bool blitImage_impl(const IGPUImage* const srcImage, const IGPUImage::LAYOUT srcImageLayout, IGPUImage* const dstImage, const IGPUImage::LAYOUT dstImageLayout, const std::span<const SImageBlit> regions, const IGPUSampler::E_TEXTURE_FILTER filter) = 0; | ||
| virtual bool resolveImage_impl(const IGPUImage* const srcImage, const IGPUImage::LAYOUT srcImageLayout, IGPUImage* const dstImage, const IGPUImage::LAYOUT dstImageLayout, const uint32_t regionCount, const SImageResolve* pRegions) = 0; | ||
|
|
@@ -750,7 +765,7 @@ class NBL_API2 IGPUCommandBuffer : public IBackendObject | |
|
|
||
| m_boundDescriptorSetsRecord.clear(); | ||
| m_TLASTrackingOps.clear(); | ||
| m_boundGraphicsPipeline= nullptr; | ||
| m_boundRasterizationPipeline= nullptr; | ||
| m_boundComputePipeline= nullptr; | ||
| m_boundRayTracingPipeline= nullptr; | ||
| m_haveRtPipelineStackSize = false; | ||
|
|
@@ -768,7 +783,7 @@ class NBL_API2 IGPUCommandBuffer : public IBackendObject | |
| deleteCommandList(); | ||
| m_boundDescriptorSetsRecord.clear(); | ||
| m_TLASTrackingOps.clear(); | ||
| m_boundGraphicsPipeline= nullptr; | ||
| m_boundRasterizationPipeline= nullptr; | ||
| m_boundComputePipeline= nullptr; | ||
| m_boundRayTracingPipeline= nullptr; | ||
| m_haveRtPipelineStackSize = false; | ||
|
|
@@ -895,10 +910,10 @@ class NBL_API2 IGPUCommandBuffer : public IBackendObject | |
|
|
||
| bool invalidDynamic(const uint32_t first, const uint32_t count); | ||
|
|
||
| template<typename IndirectCommand> requires nbl::is_any_of_v<IndirectCommand,hlsl::DrawArraysIndirectCommand_t,hlsl::DrawElementsIndirectCommand_t> | ||
| template<typename IndirectCommand> requires nbl::is_any_of_v<IndirectCommand,hlsl::DrawArraysIndirectCommand_t,hlsl::DrawElementsIndirectCommand_t, hlsl::DrawMeshTasksIndirectCommand_t> | ||
| bool invalidDrawIndirect(const asset::SBufferBinding<const IGPUBuffer>& binding, const uint32_t drawCount, uint32_t stride); | ||
|
|
||
| template<typename IndirectCommand> requires nbl::is_any_of_v<IndirectCommand, hlsl::DrawArraysIndirectCommand_t, hlsl::DrawElementsIndirectCommand_t> | ||
| template<typename IndirectCommand> requires nbl::is_any_of_v<IndirectCommand, hlsl::DrawArraysIndirectCommand_t, hlsl::DrawElementsIndirectCommand_t, hlsl::DrawMeshTasksIndirectCommand_t> | ||
| bool invalidDrawIndirectCount(const asset::SBufferBinding<const IGPUBuffer>& indirectBinding, const asset::SBufferBinding<const IGPUBuffer>& countBinding, const uint32_t maxDrawCount, const uint32_t stride); | ||
|
|
||
| core::smart_refctd_ptr<const core::IReferenceCounted>* reserveReferences(const uint32_t size); | ||
|
|
@@ -929,7 +944,7 @@ class NBL_API2 IGPUCommandBuffer : public IBackendObject | |
| // operations as they'll be performed in order | ||
| core::vector<std::variant<TLASTrackingWrite,TLASTrackingCopy,TLASTrackingRead>> m_TLASTrackingOps; | ||
|
|
||
| const IGPUGraphicsPipeline* m_boundGraphicsPipeline; | ||
| const IGPUPipelineBase* m_boundRasterizationPipeline; | ||
| const IGPUComputePipeline* m_boundComputePipeline; | ||
| const IGPURayTracingPipeline* m_boundRayTracingPipeline; | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make a
ICPURasterizationPipeline : IRasterizationPipeline<ICPUPipelineLayout,ICPUrenderpass>There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried IRasterization earlier.


I took another look. I don't use polymorphism too much, so maybe there's an easy answer staring me in the face. Most of the issue is that IGPURasterization is an incomplete type unless it's specialized with IMesh or IGraphics, at which point it's not an effective abstraction.
Here is perhaps the simplest solution.
Another potential solution I have is removing IGraphics and IMesh and replacing with IRasterization. However, that introduces a new problem, the construction parameters would be above the IGPU/ICPU abstraction level, unless some virtual functions are used.

If none of these solutions are acceptable, my recommendation would be a larger rewrite of the Pipeline system as whole. I have ideas here, but I'll hold until further direction is given.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i bookmarked this and will look into it as soon as #1000 is merged.
I actually had the plan to rewrite the Pipeline stuff and introduce PipelineLibraries and binaries and deprecate Pipeline Caches, but I'm out of Nabla improvement time/budget and it would disturb some other workflows,
that would be fine.
I'll take a look first thing after PR 1000 and decide on one of your prosals.