diff --git a/include/nbl/asset/IAsset.h b/include/nbl/asset/IAsset.h index fdb41ed298..0e91b99c36 100644 --- a/include/nbl/asset/IAsset.h +++ b/include/nbl/asset/IAsset.h @@ -94,6 +94,7 @@ class IAsset : virtual public core::IReferenceCounted ET_COMPUTE_PIPELINE = 1ull<<20, //!< asset::ICPUComputePipeline ET_PIPELINE_CACHE = 1ull<<21, //!< asset::ICPUPipelineCache ET_SCENE = 1ull<<22, //!< reserved, to implement later + ET_RAYTRACING_PIPELINE = 1ull << 23, //!< asset::ICPURayTracingPipeline ET_IMPLEMENTATION_SPECIFIC_METADATA = 1ull<<31u, //!< lights, etc. //! Reserved special value used for things like terminating lists of this enum @@ -155,27 +156,21 @@ class IAsset : virtual public core::IReferenceCounted //! inline bool isMutable() const {return m_mutable;} - //! - virtual size_t getDependantCount() const = 0; - inline IAsset* getDependant(const size_t ix) - { - if (ix(this)->getDependant(ix); - return retval; - } + virtual core::unordered_set computeDependants() const = 0; + + virtual core::unordered_set computeDependants() = 0; + + virtual bool valid() const + { + //TODO(kevinyu): Temporary set this to true to make changes compile. Will revisit this later for each asset + return true; + } protected: inline IAsset() = default; //! Pure virtual destructor to ensure no instantiation NBL_API2 virtual ~IAsset() = 0; - virtual IAsset* getDependant_impl(const size_t ix) = 0; - private: friend IAssetManager; bool m_mutable = true; diff --git a/include/nbl/asset/ICPUAccelerationStructure.h b/include/nbl/asset/ICPUAccelerationStructure.h index 9c9af32f7b..73365cbfce 100644 --- a/include/nbl/asset/ICPUAccelerationStructure.h +++ b/include/nbl/asset/ICPUAccelerationStructure.h @@ -136,7 +136,15 @@ class ICPUBottomLevelAccelerationStructure final : public IPreHashed, public IBo } // Do not report anything as a dependant, we'll simply drop the data instead of discarding its contents - inline size_t getDependantCount() const override {return 0;} + inline core::unordered_set computeDependants() const override + { + return {}; + } + + inline core::unordered_set computeDependants() override + { + return {}; + } inline core::blake3_hash_t computeContentHash() const override { @@ -236,8 +244,6 @@ class ICPUBottomLevelAccelerationStructure final : public IPreHashed, public IBo protected: virtual ~ICPUBottomLevelAccelerationStructure() = default; - inline IAsset* getDependant_impl(const size_t ix) override {return nullptr;} - inline void discardContent_impl() override { m_triangleGeoms = nullptr; @@ -263,8 +269,13 @@ class ICPUTopLevelAccelerationStructure final : public IAsset, public ITopLevelA // ICPUTopLevelAccelerationStructure() = default; - // - inline size_t getDependantCount() const override {return m_instances->size();} + inline core::unordered_set computeDependants() const override + { + core::unordered_set dependants; + for (const auto& instance : *m_instances) + dependants.insert(instance.getBase().blas.get()); + return dependants; + } // inline auto& getBuildRangeInfo() @@ -360,11 +371,6 @@ class ICPUTopLevelAccelerationStructure final : public IAsset, public ITopLevelA protected: virtual ~ICPUTopLevelAccelerationStructure() = default; - inline IAsset* getDependant_impl(const size_t ix) override - { - return m_instances->operator[](ix).getBase().blas.get(); - } - private: core::smart_refctd_dynamic_array m_instances = nullptr; hlsl::acceleration_structures::top_level::BuildRangeInfo m_buildRangeInfo; diff --git a/include/nbl/asset/ICPUAnimationLibrary.h b/include/nbl/asset/ICPUAnimationLibrary.h index 1b02787597..8a6cdaf52a 100644 --- a/include/nbl/asset/ICPUAnimationLibrary.h +++ b/include/nbl/asset/ICPUAnimationLibrary.h @@ -96,22 +96,19 @@ class ICPUAnimationLibrary final : public IAnimationLibrary, public constexpr static inline auto AssetType = ET_ANIMATION_LIBRARY; inline E_TYPE getAssetType() const override { return AssetType; } - inline size_t getDependantCount() const override {return 3;} - - protected: - inline IAsset* getDependant_impl(const size_t ix) override + inline core::unordered_set computeDependants() const override { - switch (ix) - { - case 0: - return m_keyframeStorageBinding.buffer.get(); - case 1: - return m_timestampStorageBinding.buffer.get(); - default: - break; - } - return m_animationStorageRange.buffer.get(); + return { m_keyframeStorageBinding.buffer.get(), m_timestampStorageBinding.buffer.get(), m_animationStorageRange.buffer.get() }; } + + private: + + template + requires(std::same_as, ICPUAnimationLibrary>) + static auto computeDependantsImpl(Self* self) { + using asset_ptr_t = std::conditional_t, const IAsset*, IAsset*>; + return core::unordered_set{ self->m_keyframeStorageBinding.buffer.get(), self->m_timestampStorageBinding.buffer.get(), self->m_animationStorageRange.buffer.get() }; + } }; } diff --git a/include/nbl/asset/ICPUBuffer.h b/include/nbl/asset/ICPUBuffer.h index 5bb16bd0ac..0ad1d7bf48 100644 --- a/include/nbl/asset/ICPUBuffer.h +++ b/include/nbl/asset/ICPUBuffer.h @@ -75,7 +75,15 @@ class ICPUBuffer final : public asset::IBuffer, public IPreHashed constexpr static inline auto AssetType = ET_BUFFER; inline IAsset::E_TYPE getAssetType() const override final { return AssetType; } - inline size_t getDependantCount() const override { return 0; } + inline core::unordered_set computeDependants() const override + { + return {}; + } + + inline core::unordered_set computeDependants() override + { + return {}; + } inline core::blake3_hash_t computeContentHash() const override { @@ -113,11 +121,6 @@ class ICPUBuffer final : public asset::IBuffer, public IPreHashed } protected: - inline IAsset* getDependant_impl(const size_t ix) override - { - return nullptr; - } - inline void discardContent_impl() override { if (m_data) diff --git a/include/nbl/asset/ICPUBufferView.h b/include/nbl/asset/ICPUBufferView.h index 3819136c98..55d50356c1 100644 --- a/include/nbl/asset/ICPUBufferView.h +++ b/include/nbl/asset/ICPUBufferView.h @@ -28,7 +28,15 @@ class ICPUBufferView : public IBufferView, public IAsset constexpr static inline auto AssetType = ET_BUFFER_VIEW; inline IAsset::E_TYPE getAssetType() const override { return AssetType; } - inline size_t getDependantCount() const override {return 1;} + inline core::unordered_set computeDependants() const override + { + return computeDependantsImpl(this); + } + + inline core::unordered_set computeDependants() override + { + return computeDependantsImpl(this); + } ICPUBuffer* getUnderlyingBuffer() { @@ -51,10 +59,13 @@ class ICPUBufferView : public IBufferView, public IAsset protected: virtual ~ICPUBufferView() = default; - inline IAsset* getDependant_impl(const size_t ix) override - { - return m_buffer.get(); - } + private: + template + requires(std::same_as, ICPUBufferView>) + static auto computeDependantsImpl(Self* self) { + using asset_ptr_t = std::conditional_t, const IAsset*, IAsset*>; + return core::unordered_set{ self->m_buffer.get() }; + } }; } diff --git a/include/nbl/asset/ICPUComputePipeline.h b/include/nbl/asset/ICPUComputePipeline.h index b9b707d9fc..27d16461a2 100644 --- a/include/nbl/asset/ICPUComputePipeline.h +++ b/include/nbl/asset/ICPUComputePipeline.h @@ -6,62 +6,77 @@ #include "nbl/asset/ICPUPipeline.h" +#include "nbl/asset/IComputePipeline.h" namespace nbl::asset { //! CPU Version of Compute Pipeline -class ICPUComputePipeline : public ICPUPipeline,1> +class ICPUComputePipeline final : public ICPUPipeline> { - using base_t = ICPUPipeline,1>; + using base_t = ICPUPipeline>; public: - struct SCreationParams final : IPipeline::SCreationParams - { - SShaderSpecInfo shader; - }; - static core::smart_refctd_ptr create(const SCreationParams& params) + + static core::smart_refctd_ptr create(const ICPUPipelineLayout* layout) { - if (!params.layout) - return nullptr; - auto retval = new ICPUComputePipeline(core::smart_refctd_ptr(params.layout)); - if (!retval->setSpecInfo(params.shader)) - { - retval->drop(); - return nullptr; - } + auto retval = new ICPUComputePipeline(layout); return core::smart_refctd_ptr(retval,core::dont_grab); } constexpr static inline auto AssetType = ET_COMPUTE_PIPELINE; inline E_TYPE getAssetType() const override { return AssetType; } - //! - inline size_t getDependantCount() const override {return 2;} + //! + inline core::unordered_set computeDependants() const override + { + return computeDependantsImpl(this); + } + + inline core::unordered_set computeDependants() override + { + return computeDependantsImpl(this); + } - // provide default arg - inline IPipelineBase::SShaderSpecInfo getSpecInfo() const {return base_t::getSpecInfo(hlsl::ShaderStage::ESS_COMPUTE);} + inline std::span getSpecInfo(hlsl::ShaderStage stage) const override + { + if (stage==hlsl::ShaderStage::ESS_COMPUTE) + return {&m_specInfo,1}; + return {}; + } + + inline bool valid() const override + { + if (!m_layout) return false; + if (!m_layout->valid()) return false; + return m_specInfo.valid(); + } protected: using base_t::base_t; virtual ~ICPUComputePipeline() = default; - base_t* clone_impl(core::smart_refctd_ptr&& layout) const override - { - return new ICPUComputePipeline(std::move(layout)); - } - - inline IAsset* getDependant_impl(const size_t ix) override + + private: + SShaderSpecInfo m_specInfo; + + inline core::smart_refctd_ptr clone_impl(core::smart_refctd_ptr&& layout, uint32_t depth) const override final { - if (ix!=0) - return m_stages[0].shader.get(); - return const_cast(m_layout.get()); + auto newPipeline = new ICPUComputePipeline(layout.get()); + newPipeline->m_specInfo = m_specInfo.clone(depth); + return core::smart_refctd_ptr(newPipeline, core::dont_grab); } - inline int8_t stageToIndex(const hlsl::ShaderStage stage) const override - { - return stage!=hlsl::ShaderStage::ESS_COMPUTE ? (-1):0; + explicit ICPUComputePipeline(const ICPUPipelineLayout* layout): + base_t(layout, {}) + {} + + template + requires(std::same_as, ICPUComputePipeline>) + static auto computeDependantsImpl(Self* self) { + using asset_ptr_t = std::conditional_t, const IAsset*, IAsset*>; + return core::unordered_set{ self->m_layout.get(), self->m_specInfo.shader.get() }; } }; diff --git a/include/nbl/asset/ICPUDescriptorSet.h b/include/nbl/asset/ICPUDescriptorSet.h index 826c54cc39..c8a6f68d22 100644 --- a/include/nbl/asset/ICPUDescriptorSet.h +++ b/include/nbl/asset/ICPUDescriptorSet.h @@ -47,8 +47,6 @@ class NBL_API2 ICPUDescriptorSet final : public IDescriptorSetgetTotalBindingCount()+1;} - // inline ICPUDescriptorSetLayout* getLayout() { @@ -79,10 +77,12 @@ class NBL_API2 ICPUDescriptorSet final : public IDescriptorSet clone(uint32_t _depth = ~0u) const override; + core::unordered_set computeDependants() const override; + core::unordered_set computeDependants() override; + protected: virtual ~ICPUDescriptorSet() = default; - IAsset* getDependant_impl(size_t ix) override; private: diff --git a/include/nbl/asset/ICPUDescriptorSetLayout.h b/include/nbl/asset/ICPUDescriptorSetLayout.h index 8f45a789ea..aea1520b6f 100644 --- a/include/nbl/asset/ICPUDescriptorSetLayout.h +++ b/include/nbl/asset/ICPUDescriptorSetLayout.h @@ -57,15 +57,34 @@ class ICPUDescriptorSetLayout : public IDescriptorSetLayout, public constexpr static inline auto AssetType = ET_DESCRIPTOR_SET_LAYOUT; inline E_TYPE getAssetType() const override { return AssetType; } - inline size_t getDependantCount() const override {return m_immutableSamplers ? m_immutableSamplers->size():0;} + core::unordered_set computeDependants() const override + { + return computeDependantsImpl(this); + } + + core::unordered_set computeDependants() override + { + return computeDependantsImpl(this); + } protected: virtual ~ICPUDescriptorSetLayout() = default; - inline IAsset* getDependant_impl(const size_t ix) override - { - return m_immutableSamplers->operator[](ix).get(); - } + + private: + template + requires(std::same_as, ICPUDescriptorSetLayout>) + static auto computeDependantsImpl(Self* self) { + using asset_ptr_t = std::conditional_t, const IAsset*, IAsset*>; + core::unordered_set dependants; + if (!self->m_immutableSamplers) return dependants; + for (const auto& sampler: *self->m_immutableSamplers) + { + dependants.insert(sampler.get()); + } + return dependants; + } + }; } diff --git a/include/nbl/asset/ICPUGraphicsPipeline.h b/include/nbl/asset/ICPUGraphicsPipeline.h index 2643db7550..4a7ee3b695 100644 --- a/include/nbl/asset/ICPUGraphicsPipeline.h +++ b/include/nbl/asset/ICPUGraphicsPipeline.h @@ -13,91 +13,114 @@ namespace nbl::asset { -class ICPUGraphicsPipeline final : public ICPUPipeline,5u> +class ICPUGraphicsPipeline final : public ICPUPipeline> { - using pipeline_base_t = IGraphicsPipeline; - using base_t = ICPUPipeline; + using pipeline_base_t = IGraphicsPipeline; + using base_t = ICPUPipeline; public: - struct SCreationParams final : pipeline_base_t::SCreationParams - { - private: - friend class ICPUGraphicsPipeline; - template - inline bool impl_valid(ExtraLambda&& extra) const - { - return pipeline_base_t::SCreationParams::impl_valid(std::move(extra)); - } - }; - static core::smart_refctd_ptr create(const SCreationParams& params) - { - // we'll validate the specialization info later when attempting to set it - if (!params.impl_valid([](const IPipelineBase::SShaderSpecInfo& info)->bool{return true;})) - return nullptr; - auto retval = new ICPUGraphicsPipeline(params); - for (const auto spec : params.shaders) - if (spec.shader) - retval->setSpecInfo(spec); + + static core::smart_refctd_ptr create(const ICPUPipelineLayout* layout) + { + auto retval = new ICPUGraphicsPipeline(layout); return core::smart_refctd_ptr(retval,core::dont_grab); - } - - constexpr static inline auto AssetType = ET_GRAPHICS_PIPELINE; - inline E_TYPE getAssetType() const override { return AssetType; } - - inline size_t getDependantCount() const override - { - auto stageCount = 2; // the layout and renderpass - for (const auto& stage : m_stages) - if (stage.shader) - stageCount++; - return stageCount; - } - - // extras for this class - inline const SCachedCreationParams& getCachedCreationParams() const {return base_t::getCachedCreationParams();} + } + + constexpr static inline auto AssetType = ET_GRAPHICS_PIPELINE; + inline E_TYPE getAssetType() const override { return AssetType; } + + inline core::unordered_set computeDependants() const override + { + return computeDependantsImpl(this); + } + + inline core::unordered_set computeDependants() override + { + return computeDependantsImpl(this); + } + inline SCachedCreationParams& getCachedCreationParams() { assert(isMutable()); return m_params; } + inline virtual std::span getSpecInfo(hlsl::ShaderStage stage) const override final + { + const auto stageIndex = stageToIndex(stage); + if (stageIndex != -1) + return { &m_specInfos[stageIndex], 1 }; + return {}; + } + + + inline virtual bool valid() const override final + { + 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 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, m_params.primitiveAssembly.primitiveType); + } + protected: - using base_t::base_t; - ~ICPUGraphicsPipeline() = default; - - base_t* clone_impl(core::smart_refctd_ptr&& layout) const override - { - std::array _shaders; - for (auto i=0; i(m_layout.get()); - if (ix==1) - return m_renderpass.get(); - size_t stageCount = 0; - for (auto& stage : m_stages) - if (stage.shader) - if ((stageCount++)==ix-2) - return stage.shader.get(); - return nullptr; - } - - inline int8_t stageToIndex(const hlsl::ShaderStage stage) const override - { - const auto stageIx = hlsl::findLSB(stage); - if (stageIx<0 || stageIx>=GRAPHICS_SHADER_STAGE_COUNT || hlsl::bitCount(stage)!=1) - return -1; - return stageIx; - } + using base_t::base_t; + virtual ~ICPUGraphicsPipeline() override = default; + + std::array m_specInfos; + + private: + explicit ICPUGraphicsPipeline(const ICPUPipelineLayout* layout) + : base_t(layout, {}, {}) + {} + + static inline int8_t stageToIndex(const hlsl::ShaderStage stage) + { + const auto stageIx = hlsl::findLSB(stage); + if (stageIx < 0 || stageIx >= GRAPHICS_SHADER_STAGE_COUNT || hlsl::bitCount(stage)!=1) + return -1; + return stageIx; + } + + static inline hlsl::ShaderStage indexToStage(const int8_t index) + { + if (index < 0 || index > GRAPHICS_SHADER_STAGE_COUNT) + return hlsl::ShaderStage::ESS_UNKNOWN; + return static_cast(hlsl::ShaderStage::ESS_VERTEX + index); + } + + template + requires(std::same_as, ICPUGraphicsPipeline>) + static auto computeDependantsImpl(Self* self) { + using asset_ptr_t = std::conditional_t, const IAsset*, IAsset*>; + core::unordered_set dependants = { self->m_layout.get(), self->m_renderpass.get()}; + for (const auto& info : self->m_specInfos) + if (info.shader) dependants.insert(info.shader.get()); + return dependants; + } + + inline core::smart_refctd_ptr clone_impl(core::smart_refctd_ptr&& layout, uint32_t depth) const override final + { + auto* newPipeline = new ICPUGraphicsPipeline(layout.get()); + newPipeline->m_params = m_params; + newPipeline->m_renderpass = m_renderpass; + + 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(newPipeline, core::dont_grab); + } }; } diff --git a/include/nbl/asset/ICPUImage.h b/include/nbl/asset/ICPUImage.h index c27cd21b86..b732e50492 100644 --- a/include/nbl/asset/ICPUImage.h +++ b/include/nbl/asset/ICPUImage.h @@ -46,7 +46,15 @@ class NBL_API2 ICPUImage final : public IImage, public IPreHashed inline IAsset::E_TYPE getAssetType() const override { return AssetType; } // Do not report buffer as dependant, as we will simply drop it instead of discarding its contents! - inline size_t getDependantCount() const override {return 0;} + inline core::unordered_set computeDependants() const override + { + return {}; + } + + inline core::unordered_set computeDependants() override + { + return {}; + } core::blake3_hash_t computeContentHash() const override; @@ -202,8 +210,6 @@ class NBL_API2 ICPUImage final : public IImage, public IPreHashed inline ICPUImage(const SCreationParams& _params) : IImage(_params) {} virtual ~ICPUImage() = default; - inline IAsset* getDependant_impl(const size_t ix) override {return nullptr;} - inline void discardContent_impl() override { buffer = nullptr; diff --git a/include/nbl/asset/ICPUImageView.h b/include/nbl/asset/ICPUImageView.h index 87df463021..9639df6eb9 100644 --- a/include/nbl/asset/ICPUImageView.h +++ b/include/nbl/asset/ICPUImageView.h @@ -49,8 +49,15 @@ class ICPUImageView final : public IImageView, public IAsset constexpr static inline auto AssetType = ET_IMAGE_VIEW; inline IAsset::E_TYPE getAssetType() const override { return AssetType; } - //! - inline size_t getDependantCount() const override {return 1;} + inline core::unordered_set computeDependants() const override + { + return computeDependantsImpl(this); + } + + inline core::unordered_set computeDependants() override + { + return computeDependantsImpl(this); + } //! const SComponentMapping& getComponents() const { return params.components; } @@ -68,10 +75,13 @@ class ICPUImageView final : public IImageView, public IAsset protected: virtual ~ICPUImageView() = default; - inline IAsset* getDependant_impl(const size_t ix) override - { - return params.image.get(); - } + private: + template + requires(std::same_as, ICPUImageView>) + static auto computeDependantsImpl(Self* self) { + using asset_ptr_t = std::conditional_t, const IAsset*, IAsset*>; + return core::unordered_set{ self->params.image.get() }; + } }; } diff --git a/include/nbl/asset/ICPUMesh.h b/include/nbl/asset/ICPUMesh.h index a21f5f3f02..e9aaf53ba4 100644 --- a/include/nbl/asset/ICPUMesh.h +++ b/include/nbl/asset/ICPUMesh.h @@ -82,10 +82,17 @@ class ICPUMesh final : public IMesh, public IAsset } //! CLASS IS DEPRECATED ANYWAY - inline size_t getDependantCount() const override {return 0;} + inline core::unordered_set computeDependants() const override + { + return {}; + } + + inline core::unordered_set computeDependants() override + { + return {}; + } protected: - inline IAsset* getDependant_impl(const size_t ix) override {return nullptr;} private: core::vector> m_meshBuffers; diff --git a/include/nbl/asset/ICPUMeshBuffer.h b/include/nbl/asset/ICPUMeshBuffer.h index 532b622090..c44d055c18 100644 --- a/include/nbl/asset/ICPUMeshBuffer.h +++ b/include/nbl/asset/ICPUMeshBuffer.h @@ -611,11 +611,17 @@ class ICPUMeshBuffer final : public IMeshBuffer(const_cast(this)->getJointAABBs()); } - //! CLASS IS DEPRECATED ANYWAY - inline size_t getDependantCount() const override {return 0;} + //! Class is deprecated anyway. + inline core::unordered_set computeDependants() const override + { + return {}; + } + + inline core::unordered_set computeDependants() override + { + return {}; + } - protected: - inline IAsset* getDependant_impl(const size_t ix) override {return nullptr;} }; } diff --git a/include/nbl/asset/ICPUPipeline.h b/include/nbl/asset/ICPUPipeline.h index d1693f18eb..9674b872e0 100644 --- a/include/nbl/asset/ICPUPipeline.h +++ b/include/nbl/asset/ICPUPipeline.h @@ -13,38 +13,98 @@ namespace nbl::asset { -// Common Base class for pipelines -template -class ICPUPipeline : public IAsset, public PipelineNonAssetBase +class ICPUPipelineBase { - using this_t = ICPUPipeline; - public: - inline core::smart_refctd_ptr clone(uint32_t _depth = ~0u) const override final + struct SShaderSpecInfo { - core::smart_refctd_ptr layout; - if (_depth>0u && PipelineNonAssetBase::m_layout) - layout = core::smart_refctd_ptr_static_cast(PipelineNonAssetBase::m_layout->clone(_depth-1u)); + //! Structure specifying a specialization map entry + /* + Note that if specialization constant ID is used + in a shader, \bsize\b and \boffset'b must match + to \isuch an ID\i accordingly. + + By design the API satisfies: + https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkSpecializationInfo.html#VUID-VkSpecializationInfo-offset-00773 + https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkSpecializationInfo.html#VUID-VkSpecializationInfo-pMapEntries-00774 + */ + //!< The ID of the specialization constant in SPIR-V. If it isn't used in the shader, the map entry does not affect the behavior of the pipeline. + using spec_constant_id_t = uint32_t; + + using SSpecConstantValue = core::vector; + + inline SSpecConstantValue* getSpecializationByteValue(const spec_constant_id_t _specConstID) + { + const auto found = entries.find(_specConstID); + if (found != entries.end() && found->second.size()) return &found->second; + else return nullptr; + } + + static constexpr int32_t INVALID_SPEC_INFO = -1; + inline int32_t valid() const + { + if (!shader) return INVALID_SPEC_INFO; - auto cp = clone_impl(std::move(layout)); - for (auto i=0; i 0x7fffffff) return INVALID_SPEC_INFO; + return static_cast(specData); + } + + core::smart_refctd_ptr shader = nullptr; + std::string entryPoint = ""; + + IPipelineBase::SUBGROUP_SIZE requiredSubgroupSize = IPipelineBase::SUBGROUP_SIZE::UNKNOWN; //!< Default value of 8 means no requirement + + using spec_constant_map_t = core::unordered_map; + // Container choice implicitly satisfies: + // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkSpecializationInfo.html#VUID-VkSpecializationInfo-constantID-04911 + spec_constant_map_t entries; + // By requiring Nabla Core Profile features we implicitly satisfy: + // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipelineShaderStageCreateInfo.html#VUID-VkPipelineShaderStageCreateInfo-flags-02784 + // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipelineShaderStageCreateInfo.html#VUID-VkPipelineShaderStageCreateInfo-flags-02785 + // Also because our API is sane, it satisfies the following by construction: + // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipelineShaderStageCreateInfo.html#VUID-VkPipelineShaderStageCreateInfo-pNext-02754 + + SShaderSpecInfo clone(uint32_t depth) const { - const auto shader = m_stages[i].shader; - if (shader) + auto newSpecInfo = *this; + if (depth > 0u) { - auto stageInfo = m_stages[i].info; - core::smart_refctd_ptr newShader; - if (_depth>0u) - { - newShader = core::smart_refctd_ptr_static_cast(shader->clone(_depth-1u)); - stageInfo.shader = newShader.get(); - } - cp->setSpecInfo(stageInfo); + newSpecInfo.shader = core::smart_refctd_ptr_static_cast(this->shader->clone(depth - 1u)); } + return newSpecInfo; } + }; - return core::smart_refctd_ptr(cp,core::dont_grab); - } + virtual std::span getSpecInfo(hlsl::ShaderStage stage) const = 0; + +}; + +// Common Base class for pipelines +template + requires (std::is_base_of_v, PipelineNonAssetBase> && !std::is_base_of_v) +class ICPUPipeline : public IAsset, public PipelineNonAssetBase, public ICPUPipelineBase +{ + using this_t = ICPUPipeline; + + public: // extras for this class ICPUPipelineLayout* getLayout() @@ -60,82 +120,32 @@ class ICPUPipeline : public IAsset, public PipelineNonAssetBase PipelineNonAssetBase::m_layout = std::move(_layout); } - // The getters are weird because the shader pointer, spec constant map and entry point needs patching - inline IShader* getShader(const hlsl::ShaderStage stage) - { - assert(isMutable()); - return const_cast(getSpecInfo(stage).shader); - } - inline std::string* getEntryPoint(const hlsl::ShaderStage stage) - { - const auto stageIx = stageToIndex(stage); - if (stageIx<0) - return {}; - return &m_stages[stageIx].entryPoint; - } - inline IPipelineBase::SShaderSpecInfo::spec_constant_map_t* getSpecConstantMap(const hlsl::ShaderStage stage) + inline core::smart_refctd_ptr clone(uint32_t _depth = ~0u) const override final { - assert(isMutable()); - return const_cast(getSpecInfo(stage).entries); + if (!getLayout()) return nullptr; + + core::smart_refctd_ptr layout; + if (_depth > 0u) + layout = core::smart_refctd_ptr_static_cast(getLayout()->clone(_depth - 1u)); + + return clone_impl(std::move(layout), _depth); } - // - inline IPipelineBase::SShaderSpecInfo getSpecInfo(const hlsl::ShaderStage stage) const - { - const auto stageIx = stageToIndex(stage); - if (stageIx<0) - return {}; - return m_stages[stageIx].info; - } - inline bool setSpecInfo(const IPipelineBase::SShaderSpecInfo& info) - { - assert(isMutable()); - const int64_t specSize = info.valid(); - if (specSize<0) - return false; - const auto stageIx = stageToIndex(info.stage); - if (stageIx<0) - return false; - auto& outStage = m_stages[stageIx]; - outStage.info = info; - outStage.entryPoint = info.entryPoint; - outStage.shader = core::smart_refctd_ptr(const_cast(info.shader)); - outStage.info.shader = outStage.shader.get(); - auto& outEntries = outStage.entries; - if (specSize>0) - { - outEntries = std::make_unique(); - outEntries->reserve(info.entries->size()); - std::copy(info.entries->begin(),info.entries->end(),std::insert_iterator(*outEntries,outEntries->begin())); - } - else - outEntries = nullptr; - outStage.info.entries = outEntries.get(); - return true; - } - inline bool clearStage(const hlsl::ShaderStage stage) + + // Note(kevinyu): For some reason overload resolution cannot find this function when I name id getSpecInfo. It always use the const variant. Will check on it later. + inline std::span getSpecInfoMut(hlsl::ShaderStage stage) { - assert(isMutable()); - const auto stageIx = stageToIndex(stage); - if (stageIx<0) - return false; - m_stages[stageIx] = {}; - return true; + if (!isMutable()) return {}; + const auto specInfo = const_cast(this)->getSpecInfo(stage); + return { const_cast(specInfo.data()), specInfo.size() }; } protected: + using PipelineNonAssetBase::PipelineNonAssetBase; virtual ~ICPUPipeline() = default; + + virtual core::smart_refctd_ptr clone_impl(core::smart_refctd_ptr&& layout, uint32_t depth) const = 0; - virtual this_t* clone_impl(core::smart_refctd_ptr&& layout) const = 0; - virtual int8_t stageToIndex(const hlsl::ShaderStage stage) const = 0; - - struct ShaderStage - { - std::string entryPoint = {}; - core::smart_refctd_ptr shader = {}; - std::unique_ptr entries = {}; - IPipelineBase::SShaderSpecInfo info = {}; - } m_stages[MaxShaderStageCount] = {}; }; } diff --git a/include/nbl/asset/ICPUPipelineCache.h b/include/nbl/asset/ICPUPipelineCache.h index 0c1d8c17cf..0ff912603d 100644 --- a/include/nbl/asset/ICPUPipelineCache.h +++ b/include/nbl/asset/ICPUPipelineCache.h @@ -60,7 +60,15 @@ class ICPUPipelineCache final : public IPreHashed return core::make_smart_refctd_ptr(std::move(cache_cp)); } - inline size_t getDependantCount() const override {return 0;} + inline core::unordered_set computeDependants() const override + { + return {}; + } + + inline core::unordered_set computeDependants() override + { + return {}; + } // inline core::blake3_hash_t computeContentHash() const override @@ -86,8 +94,6 @@ class ICPUPipelineCache final : public IPreHashed const auto& getEntries() const {return m_cache;} protected: - inline IAsset* getDependant_impl(const size_t ix) override {return nullptr;} - inline void discardContent_impl() override { for (auto& entry : m_cache) diff --git a/include/nbl/asset/ICPUPipelineLayout.h b/include/nbl/asset/ICPUPipelineLayout.h index c4a76fdea9..e755a22f07 100644 --- a/include/nbl/asset/ICPUPipelineLayout.h +++ b/include/nbl/asset/ICPUPipelineLayout.h @@ -30,14 +30,14 @@ class ICPUPipelineLayout : public IAsset, public IPipelineLayout&& _layout2, core::smart_refctd_ptr&& _layout3 ) : IPipelineLayout(_pcRanges,std::move(_layout0),std::move(_layout1),std::move(_layout2),std::move(_layout3)) {} - // - inline size_t getDependantCount() const override + inline core::unordered_set computeDependants() const override { - size_t count = 0; - for (auto i=0; i dependants; + for (auto i = 0; i < m_descSetLayouts.size(); i++) + { + if (m_descSetLayouts[i]) continue; + dependants.insert(m_descSetLayouts[i].get()); + } } // @@ -79,14 +79,19 @@ class ICPUPipelineLayout : public IAsset, public IPipelineLayout + requires(std::same_as, ICPUPipelineLayout>) + static auto computeDependantsImpl(Self* self) { + using asset_ptr_t = std::conditional_t, const IAsset*, IAsset*>; + core::unordered_set dependants; + for (auto i = 0; i < self->m_descSetLayouts.size(); i++) + { + if (self->m_descSetLayouts[i]) continue; + dependants.insert(self->m_descSetLayouts[i].get()); + } + return dependants; + } + }; } diff --git a/include/nbl/asset/ICPURayTracingPipeline.h b/include/nbl/asset/ICPURayTracingPipeline.h new file mode 100644 index 0000000000..618c851883 --- /dev/null +++ b/include/nbl/asset/ICPURayTracingPipeline.h @@ -0,0 +1,150 @@ + +// Copyright (C) 2018-2024 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_I_CPU_RAY_TRACING_PIPELINE_H_INCLUDED_ +#define _NBL_ASSET_I_CPU_RAY_TRACING_PIPELINE_H_INCLUDED_ + +#include "nbl/asset/IRayTracingPipeline.h" +#include "nbl/asset/ICPUPipeline.h" + + +namespace nbl::asset +{ + +//! CPU Version of RayTracing Pipeline +class ICPURayTracingPipeline final : public ICPUPipeline> +{ + using pipeline_base_t = IRayTracingPipeline; + using base_t = ICPUPipeline; + + public: + struct SHitGroupSpecInfos { + core::vector closestHits; + core::vector anyHits; + core::vector intersections; + }; + + static core::smart_refctd_ptr create(const ICPUPipelineLayout* layout) + { + auto retval = new ICPURayTracingPipeline(layout); + return core::smart_refctd_ptr(retval,core::dont_grab); + } + + + + constexpr static inline auto AssetType = ET_RAYTRACING_PIPELINE; + inline E_TYPE getAssetType() const override { return AssetType; } + + virtual core::unordered_set computeDependants() const override final { + return computeDependantsImpl(this); + } + + virtual core::unordered_set computeDependants() override final { + return computeDependantsImpl(this); + } + + inline virtual std::span getSpecInfo(hlsl::ShaderStage stage) const override final + { + switch (stage) + { + case hlsl::ShaderStage::ESS_RAYGEN: + return { &m_raygen, 1 }; + case hlsl::ShaderStage::ESS_MISS: + return m_misses; + case hlsl::ShaderStage::ESS_ANY_HIT: + return m_hitGroups.anyHits; + case hlsl::ShaderStage::ESS_CLOSEST_HIT: + return m_hitGroups.closestHits; + case hlsl::ShaderStage::ESS_INTERSECTION: + return m_hitGroups.intersections; + case hlsl::ShaderStage::ESS_CALLABLE: + return m_callables; + + } + return {}; + } + + inline core::vector* getSpecInfoVec(hlsl::ShaderStage stage) + { + if (!isMutable()) return nullptr; + switch (stage) + { + // raygen is not stored as vector so we can't return it here. Use getSpecInfo + case hlsl::ShaderStage::ESS_MISS: + return &m_misses; + case hlsl::ShaderStage::ESS_ANY_HIT: + return &m_hitGroups.anyHits; + case hlsl::ShaderStage::ESS_CLOSEST_HIT: + return &m_hitGroups.closestHits; + case hlsl::ShaderStage::ESS_INTERSECTION: + return &m_hitGroups.intersections; + case hlsl::ShaderStage::ESS_CALLABLE: + return &m_callables; + + } + return nullptr; + } + + + inline virtual bool valid() const override final + { + if (!m_layout) return false; + if (!m_layout->valid()) return false; + if (m_raygen.valid() == SShaderSpecInfo::INVALID_SPEC_INFO) return false; + return true; + } + + protected: + virtual ~ICPURayTracingPipeline() = default; + + private: + + SShaderSpecInfo m_raygen; + core::vector m_misses; + SHitGroupSpecInfos m_hitGroups; + core::vector m_callables; + + explicit ICPURayTracingPipeline(const ICPUPipelineLayout* layout) + : base_t(layout, {}) + {} + + template + requires(std::same_as, ICPURayTracingPipeline>) + static auto computeDependantsImpl(Self* self) { + using asset_ptr_t = std::conditional_t, const IAsset*, IAsset*>; + core::unordered_set dependants; + dependants.insert(self->m_raygen.shader.get()); + for (const auto& missInfo : self->m_misses) dependants.insert(missInfo.shader.get()); + for (const auto& anyHitInfo : self->m_hitGroups.anyHits) dependants.insert(anyHitInfo.shader.get()); + for (const auto& closestHitInfo : self->m_hitGroups.closestHits) dependants.insert(closestHitInfo.shader.get()); + for (const auto& intersectionInfo : self->m_hitGroups.intersections) dependants.insert(intersectionInfo.shader.get()); + for (const auto& callableInfo : self->m_callables) dependants.insert(callableInfo.shader.get()); + return dependants; + } + + inline core::smart_refctd_ptr clone_impl(core::smart_refctd_ptr&& layout, uint32_t depth) const override final + { + auto newPipeline = new ICPURayTracingPipeline(layout.get()); + newPipeline->m_raygen = m_raygen.clone(depth); + + auto cloneSpecInfos = [depth](const core::vector& specInfos) -> core::vector { + core::vector results; + results.resize(specInfos.size()); + for (auto specInfo_i = 0u; specInfo_i < specInfos.size(); specInfo_i++) + results[specInfo_i] = specInfos[specInfo_i].clone(depth); + return results; + }; + newPipeline->m_misses = cloneSpecInfos(m_misses); + newPipeline->m_hitGroups.anyHits = cloneSpecInfos(m_hitGroups.anyHits); + newPipeline->m_hitGroups.closestHits = cloneSpecInfos(m_hitGroups.closestHits); + newPipeline->m_hitGroups.intersections = cloneSpecInfos(m_hitGroups.intersections); + newPipeline->m_callables = cloneSpecInfos(m_callables); + + newPipeline->m_params = m_params; + return core::smart_refctd_ptr(newPipeline); + } +}; + +} +#endif diff --git a/include/nbl/asset/ICPURenderpass.h b/include/nbl/asset/ICPURenderpass.h index b9cf31d127..9cc73af881 100644 --- a/include/nbl/asset/ICPURenderpass.h +++ b/include/nbl/asset/ICPURenderpass.h @@ -38,13 +38,20 @@ class ICPURenderpass : public IRenderpass, public IAsset return ET_RENDERPASS; } - inline size_t getDependantCount() const override {return 0ull;} + inline core::unordered_set computeDependants() const override + { + return {}; + } + + inline core::unordered_set computeDependants() override + { + return {}; + } protected: inline ICPURenderpass(const SCreationParams& _params, const SCreationParamValidationResult& _validation) : IRenderpass(_params, _validation) {} inline ~ICPURenderpass() = default; - inline IAsset* getDependant_impl(const size_t ix) override {return nullptr;} }; } diff --git a/include/nbl/asset/ICPURenderpassIndependentPipeline.h b/include/nbl/asset/ICPURenderpassIndependentPipeline.h index ed0171d11f..fbff6ee312 100644 --- a/include/nbl/asset/ICPURenderpassIndependentPipeline.h +++ b/include/nbl/asset/ICPURenderpassIndependentPipeline.h @@ -19,6 +19,12 @@ namespace nbl::asset class ICPURenderpassIndependentPipeline : public IRenderpassIndependentPipeline, public IAsset { public: + struct SCreationParams + { + std::span shaders = {}; + SCachedCreationParams cached = {}; + }; + //(TODO) it is true however it causes DSs to not be cached when ECF_DONT_CACHE_TOP_LEVEL is set which isnt really intuitive constexpr static inline uint32_t DESC_SET_HIERARCHYLEVELS_BELOW = 0u; // TODO: @Crisspl HOW ON EARTH DOES THIS MAKE SENSE!? @@ -66,7 +72,15 @@ class ICPURenderpassIndependentPipeline : public IRenderpassIndependentPipeline, _NBL_STATIC_INLINE_CONSTEXPR auto AssetType = ET_RENDERPASS_INDEPENDENT_PIPELINE; inline E_TYPE getAssetType() const override { return AssetType; } - inline size_t getDependantCount() const override {return 0;} + inline core::unordered_set computeDependants() const override + { + return {}; + } + + inline core::unordered_set computeDependants() override + { + return {}; + } // inline const SCachedCreationParams& getCachedCreationParams() const {return IRenderpassIndependentPipeline::getCachedCreationParams();} @@ -137,8 +151,6 @@ class ICPURenderpassIndependentPipeline : public IRenderpassIndependentPipeline, : IRenderpassIndependentPipeline(params), m_layout(std::move(_layout)) {} virtual ~ICPURenderpassIndependentPipeline() = default; - inline IAsset* getDependant_impl(const size_t ix) override {return nullptr;} - core::smart_refctd_ptr m_layout; #if 0 std::array,GRAPHICS_SHADER_STAGE_COUNT> m_shaders = {}; diff --git a/include/nbl/asset/ICPUSampler.h b/include/nbl/asset/ICPUSampler.h index 27a918afaa..ed11e7695d 100644 --- a/include/nbl/asset/ICPUSampler.h +++ b/include/nbl/asset/ICPUSampler.h @@ -17,8 +17,6 @@ class ICPUSampler : public ISampler, public IAsset protected: virtual ~ICPUSampler() = default; - inline IAsset* getDependant_impl(const size_t ix) override {return nullptr;} - public: ICPUSampler(const SParams& _params) : ISampler(_params), IAsset() {} @@ -71,7 +69,15 @@ class ICPUSampler : public ISampler, public IAsset constexpr static inline auto AssetType = ET_SAMPLER; inline IAsset::E_TYPE getAssetType() const override { return AssetType; } - inline size_t getDependantCount() const override {return 0;} + inline core::unordered_set computeDependants() const override + { + return {}; + } + + inline core::unordered_set computeDependants() override + { + return {}; + } }; } diff --git a/include/nbl/asset/ICPUSkeleton.h b/include/nbl/asset/ICPUSkeleton.h index 6f1c576ed8..a29adbabbc 100644 --- a/include/nbl/asset/ICPUSkeleton.h +++ b/include/nbl/asset/ICPUSkeleton.h @@ -79,14 +79,23 @@ class ICPUSkeleton final : public ISkeleton, public IAsset constexpr static inline auto AssetType = ET_SKELETON; inline E_TYPE getAssetType() const override { return AssetType; } - //! - inline size_t getDependantCount() const override {return 2;} + inline core::unordered_set computeDependants() const override + { + return computeDependantsImpl(this); + } - protected: - inline IAsset* getDependant_impl(const size_t ix) override + inline core::unordered_set computeDependants() override { - return (ix!=0 ? m_defaultTransforms:m_parentJointIDs).buffer.get(); + return computeDependantsImpl(this); } + + private: + template + requires(std::same_as, ICPUSkeleton>) + static auto computeDependantsImpl(Self* self) { + using asset_ptr_t = std::conditional_t, const IAsset*, IAsset*>; + return core::unordered_set{ self->m_defaultTransforms.buffer.get(), self->m_parentJointIDs.buffer.get() }; + } }; } diff --git a/include/nbl/asset/IComputePipeline.h b/include/nbl/asset/IComputePipeline.h new file mode 100644 index 0000000000..9ccef877c3 --- /dev/null +++ b/include/nbl/asset/IComputePipeline.h @@ -0,0 +1,40 @@ +#ifndef _NBL_ASSET_I_COMPUTE_PIPELINE_H_INCLUDED_ +#define _NBL_ASSET_I_COMPUTE_PIPELINE_H_INCLUDED_ + +#include "nbl/asset/IPipeline.h" + +namespace nbl::asset +{ + +class IComputePipelineBase : public virtual core::IReferenceCounted +{ + public: + + struct SCachedCreationParams final + { + uint8_t requireFullSubgroups = false; + }; +}; + +template +class IComputePipeline : public IPipeline, public IComputePipelineBase +{ + using base_creation_params_t = IPipeline; + + public: + + inline const SCachedCreationParams& getCachedCreationParams() const { return m_params; } + + protected: + explicit IComputePipeline(const PipelineLayoutType* layout, const SCachedCreationParams& cachedParams) : + IPipeline(core::smart_refctd_ptr(layout)), + m_params(cachedParams) + {} + + SCachedCreationParams m_params; + +}; + +} + +#endif diff --git a/include/nbl/asset/IGraphicsPipeline.h b/include/nbl/asset/IGraphicsPipeline.h index c59ad51ca9..090a368c2f 100644 --- a/include/nbl/asset/IGraphicsPipeline.h +++ b/include/nbl/asset/IGraphicsPipeline.h @@ -88,78 +88,34 @@ class IGraphicsPipeline : public IPipeline, public IGraphics using renderpass_t = RenderpassType; public: - struct SCreationParams : IPipeline::SCreationParams - { - protected: - using SpecInfo = IPipelineBase::SShaderSpecInfo; - template - inline bool impl_valid(ExtraLambda&& extra) const - { - if (!IPipeline::SCreationParams::layout) - return false; - - // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkGraphicsPipelineCreateInfo.html#VUID-VkGraphicsPipelineCreateInfo-dynamicRendering-06576 - if (!renderpass || cached.subpassIx>=renderpass->getSubpassCount()) - return false; - - // TODO: check rasterization samples, etc. - //rp->getCreationParameters().subpasses[i] - - core::bitflag stagePresence = {}; - for (const auto info : shaders) - if (info.shader) - { - if (!extra(info)) - return false; - const auto stage = info.stage; - if (stage>hlsl::ShaderStage::ESS_FRAGMENT) - return false; - if (stagePresence.hasFlags(stage)) - return false; - stagePresence |= stage; - } - // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkGraphicsPipelineCreateInfo.html#VUID-VkGraphicsPipelineCreateInfo-stage-02096 - if (!stagePresence.hasFlags(hlsl::ShaderStage::ESS_VERTEX)) - return false; - // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkGraphicsPipelineCreateInfo.html#VUID-VkGraphicsPipelineCreateInfo-pStages-00729 - // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkGraphicsPipelineCreateInfo.html#VUID-VkGraphicsPipelineCreateInfo-pStages-00730 - if (stagePresence.hasFlags(hlsl::ShaderStage::ESS_TESSELLATION_CONTROL)!=stagePresence.hasFlags(hlsl::ShaderStage::ESS_TESSELLATION_EVALUATION)) - return false; - // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkGraphicsPipelineCreateInfo.html#VUID-VkGraphicsPipelineCreateInfo-pStages-08888 - // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkGraphicsPipelineCreateInfo.html#VUID-VkGraphicsPipelineCreateInfo-topology-08889 - if (stagePresence.hasFlags(hlsl::ShaderStage::ESS_TESSELLATION_EVALUATION)!=(cached.primitiveAssembly.primitiveType==EPT_PATCH_LIST)) - return false; - - return true; - } - - public: - inline bool valid() const - { - return impl_valid([](const SpecInfo& info)->bool - { - if (!info.valid()) - return false; - return false; - }); - } - - std::span shaders = {}; - SCachedCreationParams cached = {}; - renderpass_t* renderpass = nullptr; - }; - 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& stagePresence, E_PRIMITIVE_TOPOLOGY primitiveType) + { + // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkGraphicsPipelineCreateInfo.html#VUID-VkGraphicsPipelineCreateInfo-stage-02096 + if (!stagePresence.hasFlags(hlsl::ShaderStage::ESS_VERTEX)) + return false; + // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkGraphicsPipelineCreateInfo.html#VUID-VkGraphicsPipelineCreateInfo-pStages-00729 + // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkGraphicsPipelineCreateInfo.html#VUID-VkGraphicsPipelineCreateInfo-pStages-00730 + if (stagePresence.hasFlags(hlsl::ShaderStage::ESS_TESSELLATION_CONTROL)!=stagePresence.hasFlags(hlsl::ShaderStage::ESS_TESSELLATION_EVALUATION)) + return false; + // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkGraphicsPipelineCreateInfo.html#VUID-VkGraphicsPipelineCreateInfo-pStages-08888 + // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkGraphicsPipelineCreateInfo.html#VUID-VkGraphicsPipelineCreateInfo-topology-08889 + if (stagePresence.hasFlags(hlsl::ShaderStage::ESS_TESSELLATION_EVALUATION)!=(primitiveType==asset::EPT_PATCH_LIST)) + return false; + return true; + } + protected: - explicit IGraphicsPipeline(const SCreationParams& _params) : - IPipeline(core::smart_refctd_ptr(_params.layout)), - m_params(_params.cached), m_renderpass(core::smart_refctd_ptr(_params.renderpass)) {} + explicit IGraphicsPipeline(const PipelineLayoutType* layout, const SCachedCreationParams& cachedParams, const renderpass_t* renderpass) : + IPipeline(core::smart_refctd_ptr(layout)), + m_params(cachedParams), m_renderpass(core::smart_refctd_ptr(renderpass)) + {} - SCachedCreationParams m_params; - core::smart_refctd_ptr m_renderpass; + SCachedCreationParams m_params = {}; + core::smart_refctd_ptr m_renderpass = nullptr; }; } diff --git a/include/nbl/asset/IPipeline.h b/include/nbl/asset/IPipeline.h index 036a684729..d2a85c42fb 100644 --- a/include/nbl/asset/IPipeline.h +++ b/include/nbl/asset/IPipeline.h @@ -27,249 +27,113 @@ namespace nbl::asset */ class IPipelineBase { - public: - struct SCreationParams - { - protected: - // This is not public to make sure that different pipelines only get the enums they support - enum class FLAGS : uint64_t - { - NONE = 0, // disallowed in maintanance5 - DISABLE_OPTIMIZATIONS = 1<<0, - ALLOW_DERIVATIVES = 1<<1, - - // I can just derive this - //DERIVATIVE = 1<<2, + public: + enum class CreationFlags : uint64_t + { + NONE = 0, // disallowed in maintanance5 + DISABLE_OPTIMIZATIONS = 1 << 0, + ALLOW_DERIVATIVES = 1 << 1, + + // I can just derive this + //DERIVATIVE = 1<<2, + + // Graphics Pipelines only + //VIEW_INDEX_FROM_DEVICE_INDEX = 1<<3, + + // Compute Pipelines only + //DISPATCH_BASE = 1<<4, + + // This is for NV-raytracing extension. Now this is done via IDeferredOperation + //DEFER_COMPILE_NV = 1<<5, + + // We use Renderdoc to take care of this for us, + // we won't be parsing the statistics and internal representation ourselves. + //CAPTURE_STATISTICS = 1<<6, + //CAPTURE_INTERNAL_REPRESENTATIONS = 1<<7, + + // Will soon be deprecated due to + // https://github.com/Devsh-Graphics-Programming/Nabla/issues/854 + FAIL_ON_PIPELINE_COMPILE_REQUIRED = 1 << 8, + EARLY_RETURN_ON_FAILURE = 1 << 9, + + // Will be exposed later with the IPipelineLibrary asset implementation + // https://github.com/Devsh-Graphics-Programming/Nabla/issues/853 + //LINK_TIME_OPTIMIZATION = 1<<10, + + // Won't be exposed because we'll introduce Libraries as a separate object/asset-type + // https://github.com/Devsh-Graphics-Programming/Nabla/issues/853 + //CREATE_LIBRARY = 1<<11, + + // Ray Tracing Pipelines only + //SKIP_BUILT_IN_PRIMITIVES = 1<<12, + //SKIP_AABBS = 1<<13, + //NO_NULL_ANY_HIT_SHADERS = 1<<14, + //NO_NULL_CLOSEST_HIT_SHADERS = 1<<15, + //NO_NULL_MISS_SHADERS = 1<<16, + //NO_NULL_INTERSECTION_SHADERS = 1<<17, + + // There is a new Device Generated Commands extension with its own flag that will deprecate this + //INDIRECT_BINDABLE_NV = 1<<18, + + // Ray Tracing Pipelines only + // For debug tools + //RAY_TRACING_SHADER_GROUP_HANDLE_CAPTURE_REPLAY_BIT_KHR = 1<<19, + + // Ray Tracing Pipelines only + //ALLOW_MOTION = 1<<20, + + // Graphics Pipelineonly (we don't support subpass shading) + //RENDERING_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR = 1<<21, + //RENDERING_FRAGMENT_DENSITY_MAP_ATTACHMENT_BIT_EXT = 1<<22, + + // Will be exposed later with the IPipelineLibrary asset implementation + // https://github.com/Devsh-Graphics-Programming/Nabla/issues/853 + //RETAIN_LINK_TIME_OPTIMIZATION_INFO = 1<<23, + + // Ray Tracing Pipelines only + //RAY_TRACING_OPACITY_MICROMAP_BIT_EXT = 1<<24, + + // Not supported yet, and we will move to dynamic rendering, so this might never be supported + //COLOR_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT = 1<<25, + //DEPTH_STENCIL_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT = 1<<26, + + // Not Supported Yet + //NO_PROTECTED_ACCESS=1<<27, + //RAY_TRACING_DISPLACEMENT_MICROMAP_BIT_NV = 1<<28, + //DESCRIPTOR_VUFFER_BIT=1<<29, + //PROTECTED_ACCESS_ONLY=1<<30, + }; + using FLAGS = CreationFlags; + + // Nabla requires device's reported subgroup size to be between 4 and 128 + enum class SUBGROUP_SIZE : uint8_t + { + // No constraint but probably means `gl_SubgroupSize` is Dynamically Uniform + UNKNOWN = 0, + // Allows the Subgroup Uniform `gl_SubgroupSize` to be non-Dynamically Uniform and vary between Device's min and max + VARYING = 1, + // The rest we encode as log2(x) of the required value + REQUIRE_4 = 2, + REQUIRE_8 = 3, + REQUIRE_16 = 4, + REQUIRE_32 = 5, + REQUIRE_64 = 6, + REQUIRE_128 = 7 + }; - // Graphics Pipelines only - //VIEW_INDEX_FROM_DEVICE_INDEX = 1<<3, - - // Compute Pipelines only - //DISPATCH_BASE = 1<<4, - - // This is for NV-raytracing extension. Now this is done via IDeferredOperation - //DEFER_COMPILE_NV = 1<<5, - - // We use Renderdoc to take care of this for us, - // we won't be parsing the statistics and internal representation ourselves. - //CAPTURE_STATISTICS = 1<<6, - //CAPTURE_INTERNAL_REPRESENTATIONS = 1<<7, - - // Will soon be deprecated due to - // https://github.com/Devsh-Graphics-Programming/Nabla/issues/854 - FAIL_ON_PIPELINE_COMPILE_REQUIRED = 1<<8, - EARLY_RETURN_ON_FAILURE = 1<<9, - - // Will be exposed later with the IPipelineLibrary asset implementation - // https://github.com/Devsh-Graphics-Programming/Nabla/issues/853 - //LINK_TIME_OPTIMIZATION = 1<<10, - - // Won't be exposed because we'll introduce Libraries as a separate object/asset-type - // https://github.com/Devsh-Graphics-Programming/Nabla/issues/853 - //CREATE_LIBRARY = 1<<11, - - // Ray Tracing Pipelines only - //SKIP_BUILT_IN_PRIMITIVES = 1<<12, - //SKIP_AABBS = 1<<13, - //NO_NULL_ANY_HIT_SHADERS = 1<<14, - //NO_NULL_CLOSEST_HIT_SHADERS = 1<<15, - //NO_NULL_MISS_SHADERS = 1<<16, - //NO_NULL_INTERSECTION_SHADERS = 1<<17, - - // There is a new Device Generated Commands extension with its own flag that will deprecate this - //INDIRECT_BINDABLE_NV = 1<<18, - - // Ray Tracing Pipelines only - // For debug tools - //RAY_TRACING_SHADER_GROUP_HANDLE_CAPTURE_REPLAY_BIT_KHR = 1<<19, - - // Ray Tracing Pipelines only - //ALLOW_MOTION = 1<<20, - - // Graphics Pipelineonly (we don't support subpass shading) - //RENDERING_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR = 1<<21, - //RENDERING_FRAGMENT_DENSITY_MAP_ATTACHMENT_BIT_EXT = 1<<22, - - // Will be exposed later with the IPipelineLibrary asset implementation - // https://github.com/Devsh-Graphics-Programming/Nabla/issues/853 - //RETAIN_LINK_TIME_OPTIMIZATION_INFO = 1<<23, - - // Ray Tracing Pipelines only - //RAY_TRACING_OPACITY_MICROMAP_BIT_EXT = 1<<24, - - // Not supported yet, and we will move to dynamic rendering, so this might never be supported - //COLOR_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT = 1<<25, - //DEPTH_STENCIL_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT = 1<<26, - - // Not Supported Yet - //NO_PROTECTED_ACCESS=1<<27, - //RAY_TRACING_DISPLACEMENT_MICROMAP_BIT_NV = 1<<28, - //DESCRIPTOR_VUFFER_BIT=1<<29, - //PROTECTED_ACCESS_ONLY=1<<30, - }; - }; - - /* - Specialization info contains things such as entry point to a shader, - specialization map entry, required subgroup size, etc. for a blob of SPIR-V - - It also handles Specialization Constants. - - In Vulkan, all shaders get halfway-compiled into SPIR-V and - then then lowered (compiled) into the HW ISA by the Vulkan driver. - Normally, the half-way compile folds all constant values - and optimizes the code that uses them. - - But, it would be nice every so often to have your Vulkan - program sneak into the halfway-compiled SPIR-V binary and - manipulate some constants at runtime. This is what - Specialization Constants are for. - - So A Specialization Constant is a way of injecting an integer - constant into a halfway-compiled version of a shader right - before the lowering and linking when creating a pipeline. - - Without Specialization Constants, you would have to commit - to a final value before the SPIR-V compilation - */ - struct SShaderSpecInfo final - { - //! Structure specifying a specialization map entry - /* - Note that if specialization constant ID is used - in a shader, \bsize\b and \boffset'b must match - to \isuch an ID\i accordingly. - - By design the API satisfies: - https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkSpecializationInfo.html#VUID-VkSpecializationInfo-offset-00773 - https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkSpecializationInfo.html#VUID-VkSpecializationInfo-pMapEntries-00774 - */ - //!< The ID of the specialization constant in SPIR-V. If it isn't used in the shader, the map entry does not affect the behavior of the pipeline. - using spec_constant_id_t = uint32_t; - struct SSpecConstantValue - { - const void* data = nullptr; - //!< The byte size of the specialization constant value within the supplied data buffer. - uint32_t size = 0; - - inline operator bool() const {return data&&size;} - - auto operator<=>(const SSpecConstantValue&) const = default; - }; - inline SSpecConstantValue getSpecializationByteValue(const spec_constant_id_t _specConstID) const - { - if (!entries) - return { nullptr,0u }; - - const auto found = entries->find(_specConstID); - if (found != entries->end() && bool(found->second)) - return found->second; - else - return { nullptr,0u }; - } - - // Nabla requires device's reported subgroup size to be between 4 and 128 - enum class SUBGROUP_SIZE : uint8_t - { - // No constraint but probably means `gl_SubgroupSize` is Dynamically Uniform - UNKNOWN = 0, - // Allows the Subgroup Uniform `gl_SubgroupSize` to be non-Dynamically Uniform and vary between Device's min and max - VARYING = 1, - // The rest we encode as log2(x) of the required value - REQUIRE_4 = 2, - REQUIRE_8 = 3, - REQUIRE_16 = 4, - REQUIRE_32 = 5, - REQUIRE_64 = 6, - REQUIRE_128 = 7 - }; - - // - static constexpr int32_t INVALID_SPEC_INFO = -1; - // Returns negative on failure, otherwise the size of the buffer required to reserve for the spec constant data - inline int32_t valid() const - { - if (!shader || hlsl::bitCount(stage)!=1) - return INVALID_SPEC_INFO; - - // Impossible to check: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipelineShaderStageCreateInfo.html#VUID-VkPipelineShaderStageCreateInfo-pName-00707 - if (entryPoint.empty()) - return INVALID_SPEC_INFO; - - // Shader stages already checked for validity w.r.t. features enabled, during unspec shader creation, only check: - // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipelineShaderStageCreateInfo.html#VUID-VkPipelineShaderStageCreateInfo-flags-08988 - if (requireFullSubgroups) - switch (stage) - { - case hlsl::ShaderStage::ESS_COMPUTE: [[fallthrough]]; - case hlsl::ShaderStage::ESS_TASK: [[fallthrough]]; - case hlsl::ShaderStage::ESS_MESH: - break; - default: - return INVALID_SPEC_INFO; - break; - } - // Impossible to efficiently check anything from: - // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipelineShaderStageCreateInfo.html#VUID-VkPipelineShaderStageCreateInfo-maxClipDistances-00708 - // to: - // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipelineShaderStageCreateInfo.html#VUID-VkPipelineShaderStageCreateInfo-stage-06686 - // and from: - // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipelineShaderStageCreateInfo.html#VUID-VkPipelineShaderStageCreateInfo-pNext-02756 - // to: - // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipelineShaderStageCreateInfo.html#VUID-VkPipelineShaderStageCreateInfo-module-08987 - - int64_t specData = 0; - if (entries) - for (const auto& entry : *entries) - { - if (!entry.second) - return INVALID_SPEC_INFO; - specData += entry.second.size; - } - if (specData>0x7fffffff) - return INVALID_SPEC_INFO; - return static_cast(specData); - } - - using spec_constant_map_t = core::unordered_map; - - const IShader* shader = nullptr; - // A name of the function where the entry point of an shader executable begins. It's often "main" function. - std::string_view entryPoint = {}; - // stage must be set - hlsl::ShaderStage stage = hlsl::ShaderStage::ESS_UNKNOWN; - // there's some padding here - SUBGROUP_SIZE requiredSubgroupSize : 3 = SUBGROUP_SIZE::UNKNOWN; //!< Default value of 8 means no requirement - // Valid only for Compute, Mesh and Task shaders - uint8_t requireFullSubgroups : 1 = false; - // Container choice implicitly satisfies: - // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkSpecializationInfo.html#VUID-VkSpecializationInfo-constantID-04911 - const spec_constant_map_t* entries = nullptr; - // By requiring Nabla Core Profile features we implicitly satisfy: - // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipelineShaderStageCreateInfo.html#VUID-VkPipelineShaderStageCreateInfo-flags-02784 - // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipelineShaderStageCreateInfo.html#VUID-VkPipelineShaderStageCreateInfo-flags-02785 - // Also because our API is sane, it satisfies the following by construction: - // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipelineShaderStageCreateInfo.html#VUID-VkPipelineShaderStageCreateInfo-pNext-02754 - }; }; template class IPipeline : public IPipelineBase { - public: - // For now, due to API design we implicitly satisfy a bunch of VUIDs - struct SCreationParams : protected IPipelineBase::SCreationParams - { - public: - const PipelineLayout* layout = nullptr; - }; + public: + inline const PipelineLayout* getLayout() const {return m_layout.get();} - inline const PipelineLayout* getLayout() const {return m_layout.get();} + protected: - protected: - inline IPipeline(core::smart_refctd_ptr&& _layout) - : m_layout(std::move(_layout)) {} + inline IPipeline(core::smart_refctd_ptr&& _layout) + : m_layout(std::move(_layout)) {} - core::smart_refctd_ptr m_layout; + core::smart_refctd_ptr m_layout; }; } diff --git a/include/nbl/asset/IPreHashed.h b/include/nbl/asset/IPreHashed.h index 4bc5ca5dcd..86e1841f61 100644 --- a/include/nbl/asset/IPreHashed.h +++ b/include/nbl/asset/IPreHashed.h @@ -41,36 +41,31 @@ class IPreHashed : public IAsset static inline void discardDependantsContents(const std::span roots) { - struct stack_entry_t - { - IAsset* asset; - size_t childCount = 0; - size_t childrenVisited = 0; - }; - core::stack stack; - core::unordered_set alreadyVisited; + core::stack stack; + core::unordered_set alreadyVisited; // whether we have push the node to the stack + core::unordered_set alreadyDescended; // whether we have push the children to the stack auto push = [&stack,&alreadyVisited](IAsset* node) -> void { if (!node) return; const auto [dummy,inserted] = alreadyVisited.insert(node); if (inserted) - stack.push({.asset=node,.childCount=node->getDependantCount()}); + stack.push(node); }; for (const auto& root : roots) push(root); while (!stack.empty()) { - auto& entry = stack.top(); - if (entry.childrenVisitedgetDependant(entry.childrenVisited++); - push(dep); - } - else + core::unordered_set dependants = entry->computeDependants(); + for (auto* dependant : dependants) push(dependant); + } else { // post order traversal does discard - auto* isPrehashed = dynamic_cast(entry.asset); + auto* isPrehashed = dynamic_cast(entry); if (isPrehashed) isPrehashed->discardContent(); stack.pop(); @@ -79,14 +74,9 @@ class IPreHashed : public IAsset } static inline bool anyDependantDiscardedContents(const IAsset* root) { - struct stack_entry_t - { - const IAsset* asset; - size_t childCount = 0; - size_t childrenVisited = 0; - }; - core::stack stack; - core::unordered_set alreadyVisited; + core::stack stack; + core::unordered_set alreadyVisited; // whether we have push the node to the stack + core::unordered_set alreadyDescended; // whether we have push the children to the stack auto push = [&stack,&alreadyVisited](const IAsset* node) -> bool { if (!node) @@ -97,7 +87,7 @@ class IPreHashed : public IAsset auto* isPrehashed = dynamic_cast(node); if (isPrehashed && isPrehashed->missingContent()) return true; - stack.push({.asset=node,.childCount=node->getDependantCount()}); + stack.push(node); } return false; }; @@ -105,14 +95,13 @@ class IPreHashed : public IAsset return true; while (!stack.empty()) { - auto& entry = stack.top(); - if (entry.childrenVisitedgetDependant(entry.childrenVisited++); - if (push(dep)) - return true; - } - else + core::unordered_set dependants = entry->computeDependants(); + for (auto* dependant : dependants) push(dependant); + } else stack.pop(); } return false; diff --git a/include/nbl/asset/IRayTracingPipeline.h b/include/nbl/asset/IRayTracingPipeline.h index 0bc2d68653..82b47f1fcb 100644 --- a/include/nbl/asset/IRayTracingPipeline.h +++ b/include/nbl/asset/IRayTracingPipeline.h @@ -14,35 +14,6 @@ namespace nbl::asset class IRayTracingPipelineBase : public virtual core::IReferenceCounted { public: - struct SShaderGroupsParams - { - struct SIndex - { - constexpr static inline uint32_t Unused = 0xffFFffFFu; - uint32_t index = Unused; - }; - - struct SHitGroup - { - uint32_t closestHit = SIndex::Unused; - uint32_t anyHit = SIndex::Unused; - uint32_t intersection = SIndex::Unused; - }; - - SIndex raygen; - std::span misses; - std::span hits; - std::span callables; - - inline uint32_t getShaderGroupCount() const - { - return 1 + hits.size() + misses.size() + callables.size(); - } - - }; - using SGeneralShaderGroup = SShaderGroupsParams::SIndex; - using SHitShaderGroup = SShaderGroupsParams::SHitGroup; - struct SCachedCreationParams final { uint32_t maxRecursionDepth : 6 = 0; @@ -53,152 +24,36 @@ class IRayTracingPipelineBase : public virtual core::IReferenceCounted template class IRayTracingPipeline : public IPipeline, public IRayTracingPipelineBase { - using base_creation_params_t = IPipeline::SCreationParams; public: - using SGeneralShaderGroupContainer = core::smart_refctd_dynamic_array; - using SHitShaderGroupContainer = core::smart_refctd_dynamic_array; - - struct SCreationParams : base_creation_params_t + #define base_flag(F) static_cast(IPipelineBase::FLAGS::F) + enum class CreationFlags : uint64_t { - public: - #define base_flag(F) static_cast(base_creation_params_t::FLAGS::F) - enum class FLAGS : uint64_t - { - NONE = base_flag(NONE), - DISABLE_OPTIMIZATIONS = base_flag(DISABLE_OPTIMIZATIONS), - ALLOW_DERIVATIVES = base_flag(ALLOW_DERIVATIVES), - FAIL_ON_PIPELINE_COMPILE_REQUIRED = base_flag(FAIL_ON_PIPELINE_COMPILE_REQUIRED), - EARLY_RETURN_ON_FAILURE = base_flag(EARLY_RETURN_ON_FAILURE), - SKIP_BUILT_IN_PRIMITIVES = 1<<12, - SKIP_AABBS = 1<<13, - NO_NULL_ANY_HIT_SHADERS = 1<<14, - NO_NULL_CLOSEST_HIT_SHADERS = 1<<15, - NO_NULL_MISS_SHADERS = 1<<16, - NO_NULL_INTERSECTION_SHADERS = 1<<17, - ALLOW_MOTION = 1<<20, - }; - #undef base_flag - - protected: - using SpecInfo = IPipelineBase::SShaderSpecInfo; - template - inline bool impl_valid(ExtraLambda&& extra) const - { - if (!IPipeline::SCreationParams::layout) - return false; - - for (const auto info : shaders) - { - if (info.shader) - { - if (!extra(info)) - return false; - const auto stage = info.stage; - if ((stage & ~IShader::E_SHADER_STAGE::ESS_ALL_RAY_TRACING) != 0) - return false; - if (!std::has_single_bit>(stage)) - return false; - } - else - { - // every shader must not be null. use SIndex::Unused to represent unused shader. - return false; - } - } - - auto getShaderStage = [this](size_t index) -> IShader::E_SHADER_STAGE - { - return shaders[index].stage; - }; - - auto isValidShaderIndex = [this, getShaderStage](size_t index, IShader::E_SHADER_STAGE expectedStage, bool is_unused_shader_forbidden) -> bool - { - if (index == SShaderGroupsParams::SIndex::Unused) - return !is_unused_shader_forbidden; - if (index >= shaders.size()) - return false; - if (getShaderStage(index) != expectedStage) - return false; - return true; - }; - - if (!isValidShaderIndex(shaderGroups.raygen.index, IShader::E_SHADER_STAGE::ESS_RAYGEN, true)) - { - return false; - } - - for (const auto& shaderGroup : shaderGroups.hits) - { - // https://docs.vulkan.org/spec/latest/chapters/pipelines.html#VUID-VkRayTracingPipelineCreateInfoKHR-flags-03470 - if (!isValidShaderIndex(shaderGroup.anyHit, - IShader::E_SHADER_STAGE::ESS_ANY_HIT, - bool(flags & FLAGS::NO_NULL_ANY_HIT_SHADERS))) - return false; - - // https://docs.vulkan.org/spec/latest/chapters/pipelines.html#VUID-VkRayTracingPipelineCreateInfoKHR-flags-03471 - if (!isValidShaderIndex(shaderGroup.closestHit, - IShader::E_SHADER_STAGE::ESS_CLOSEST_HIT, - bool(flags & FLAGS::NO_NULL_CLOSEST_HIT_SHADERS))) - return false; - - if (!isValidShaderIndex(shaderGroup.intersection, - IShader::E_SHADER_STAGE::ESS_INTERSECTION, - false)) - return false; - } - - for (const auto& shaderGroup : shaderGroups.misses) - { - if (!isValidShaderIndex(shaderGroup.index, - IShader::E_SHADER_STAGE::ESS_MISS, - false)) - return false; - } - - for (const auto& shaderGroup : shaderGroups.callables) - { - if (!isValidShaderIndex(shaderGroup.index, IShader::E_SHADER_STAGE::ESS_CALLABLE, false)) - return false; - } - return true; - } - - public: - inline bool valid() const - { - return impl_valid([](const SpecInfo& info)->bool - { - if (!info.valid()) - return false; - return false; - }); - } - - std::span shaders = {}; - SShaderGroupsParams shaderGroups; - SCachedCreationParams cached = {}; - // TODO: Could guess the required flags from SPIR-V introspection of declared caps - core::bitflag flags = FLAGS::NONE; + NONE = base_flag(NONE), + DISABLE_OPTIMIZATIONS = base_flag(DISABLE_OPTIMIZATIONS), + ALLOW_DERIVATIVES = base_flag(ALLOW_DERIVATIVES), + FAIL_ON_PIPELINE_COMPILE_REQUIRED = base_flag(FAIL_ON_PIPELINE_COMPILE_REQUIRED), + EARLY_RETURN_ON_FAILURE = base_flag(EARLY_RETURN_ON_FAILURE), + SKIP_BUILT_IN_PRIMITIVES = 1<<12, + SKIP_AABBS = 1<<13, + NO_NULL_ANY_HIT_SHADERS = 1<<14, + NO_NULL_CLOSEST_HIT_SHADERS = 1<<15, + NO_NULL_MISS_SHADERS = 1<<16, + NO_NULL_INTERSECTION_SHADERS = 1<<17, + ALLOW_MOTION = 1<<20, }; + #undef base_flag + using FLAGS = CreationFlags; inline const SCachedCreationParams& getCachedCreationParams() const { return m_params; } protected: - explicit IRayTracingPipeline(const SCreationParams& _params) : - IPipeline(core::smart_refctd_ptr(_params.layout)), - m_params(_params.cached), - m_raygenShaderGroup(_params.shaderGroups.raygen), - m_missShaderGroups(core::make_refctd_dynamic_array(_params.shaderGroups.misses)), - m_hitShaderGroups(core::make_refctd_dynamic_array(_params.shaderGroups.hits)), - m_callableShaderGroups(core::make_refctd_dynamic_array(_params.shaderGroups.callables)) + explicit IRayTracingPipeline(const PipelineLayoutType* layout, const SCachedCreationParams& cachedParams) : + IPipeline(core::smart_refctd_ptr(layout)), + m_params(cachedParams) {} SCachedCreationParams m_params; - SGeneralShaderGroup m_raygenShaderGroup; - SGeneralShaderGroupContainer m_missShaderGroups; - SHitShaderGroupContainer m_hitShaderGroups; - SGeneralShaderGroupContainer m_callableShaderGroups; }; diff --git a/include/nbl/asset/IRenderpassIndependentPipeline.h b/include/nbl/asset/IRenderpassIndependentPipeline.h index 7f33b6abc4..feeaff7c99 100644 --- a/include/nbl/asset/IRenderpassIndependentPipeline.h +++ b/include/nbl/asset/IRenderpassIndependentPipeline.h @@ -28,11 +28,6 @@ class IRenderpassIndependentPipeline SRasterizationParams rasterization = {}; SBlendParams blend = {}; }; - struct SCreationParams - { - std::span shaders = {}; - SCachedCreationParams cached = {}; - }; inline const SCachedCreationParams& getCachedCreationParams() const {return m_cachedParams;} diff --git a/include/nbl/asset/IShader.h b/include/nbl/asset/IShader.h index a6dab09b54..59286e219d 100644 --- a/include/nbl/asset/IShader.h +++ b/include/nbl/asset/IShader.h @@ -50,8 +50,15 @@ class IShader : public IAsset constexpr static inline auto AssetType = ET_SHADER; inline E_TYPE getAssetType() const override { return AssetType; } - // - inline size_t getDependantCount() const override { return 1; } + inline core::unordered_set computeDependants() const override + { + return computeDependantsImpl(this); + } + + inline core::unordered_set computeDependants() override + { + return computeDependantsImpl(this); + } // inline core::smart_refctd_ptr clone(uint32_t _depth=~0u) const override @@ -96,11 +103,17 @@ class IShader : public IAsset protected: virtual ~IShader() = default; - inline IAsset* getDependant_impl(const size_t ix) override {return m_code.get();} - std::string m_filepathHint; core::smart_refctd_ptr m_code; E_CONTENT_TYPE m_contentType; + + private: + template + requires(std::same_as, IShader>) + static auto computeDependantsImpl(Self* self) { + using asset_ptr_t = std::conditional_t, const IAsset*, IAsset*>; + return core::unordered_set{self->m_code.get()}; + } }; } diff --git a/include/nbl/asset/utils/CSPIRVIntrospector.h b/include/nbl/asset/utils/CSPIRVIntrospector.h index 3d6455e020..fa497f08aa 100644 --- a/include/nbl/asset/utils/CSPIRVIntrospector.h +++ b/include/nbl/asset/utils/CSPIRVIntrospector.h @@ -582,7 +582,7 @@ class NBL_API2 CSPIRVIntrospector : public core::Uncopyable } // returns true if successfully added all the info to self, false if incompatible with what's already in our pipeline or incomplete (e.g. missing spec constants) - bool merge(const CStageIntrospectionData* stageData, const IPipelineBase::SShaderSpecInfo::spec_constant_map_t* specConstants=nullptr); + bool merge(const CStageIntrospectionData* stageData, const ICPUPipelineBase::SShaderSpecInfo::spec_constant_map_t* specConstants=nullptr); // core::smart_refctd_dynamic_array createPushConstantRangesFromIntrospection(core::smart_refctd_ptr& introspection); @@ -643,7 +643,7 @@ class NBL_API2 CSPIRVIntrospector : public core::Uncopyable } //! creates pipeline for a single IShader - core::smart_refctd_ptr createApproximateComputePipelineFromIntrospection(const IPipelineBase::SShaderSpecInfo& info, core::smart_refctd_ptr&& layout=nullptr); + core::smart_refctd_ptr createApproximateComputePipelineFromIntrospection(const ICPUPipelineBase::SShaderSpecInfo& info, core::smart_refctd_ptr&& layout=nullptr); #if 0 // wait until Renderpass Indep completely gone and Graphics Pipeline is used in a new way && Graphics Pipeline Libraries struct CShaderStages diff --git a/include/nbl/video/IGPUComputePipeline.h b/include/nbl/video/IGPUComputePipeline.h index 49e44dfcc1..2eb03cf2da 100644 --- a/include/nbl/video/IGPUComputePipeline.h +++ b/include/nbl/video/IGPUComputePipeline.h @@ -6,20 +6,21 @@ #include "nbl/asset/IPipeline.h" +#include "nbl/asset/IComputePipeline.h" -#include "nbl/video/SPipelineCreationParams.h" +#include "nbl/video/IGPUPipeline.h" #include "nbl/video/SPipelineCreationParams.h" namespace nbl::video { -class IGPUComputePipeline : public IBackendObject, public asset::IPipeline +class IGPUComputePipeline : public IGPUPipeline> { - using pipeline_t = asset::IPipeline; + using pipeline_t = asset::IComputePipeline; public: - struct SCreationParams final : pipeline_t::SCreationParams, SPipelineCreationParams + struct SCreationParams final : SPipelineCreationParams { // By construction we satisfy from: // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkComputePipelineCreateInfo.html#VUID-VkComputePipelineCreateInfo-flags-03365 @@ -28,7 +29,7 @@ class IGPUComputePipeline : public IBackendObject, public asset::IPipeline(pipeline_t::SCreationParams::FLAGS::F) + #define base_flag(F) static_cast(pipeline_t::FLAGS::F) enum class FLAGS : uint64_t { NONE = base_flag(NONE), @@ -46,28 +47,25 @@ class IGPUComputePipeline : public IBackendObject, public asset::IPipelinesize()>0x7fffffff) - return {}; - count = static_cast(shader.entries->size()); - } - return {.count=dataSize ? count:0,.dataSize=static_cast(dataSize)}; - } + SSpecializationValidationResult retval = { + .count = 0, + .dataSize = 0, + }; - inline std::span getShaders() const {return {&shader,1}; } + if (!shader.accumulateSpecializationValidationResult(&retval)) + return {}; + + return retval; + } + IGPUPipelineLayout* layout = nullptr; // TODO: Could guess the required flags from SPIR-V introspection of declared caps core::bitflag flags = FLAGS::NONE; - IPipelineBase::SShaderSpecInfo shader = {}; + SShaderSpecInfo shader = {}; }; inline core::bitflag getCreationFlags() const {return m_flags;} @@ -76,10 +74,9 @@ class IGPUComputePipeline : public IBackendObject, public asset::IPipeline&& _layout, const core::bitflag _flags) : - IBackendObject(core::smart_refctd_ptr(_layout->getOriginDevice())), - pipeline_t(std::move(_layout)), - m_flags(_flags) {} + inline IGPUComputePipeline(const SCreationParams& params) : + IGPUPipeline(core::smart_refctd_ptr(params.layout->getOriginDevice()), core::smart_refctd_ptr(params.layout)), m_flags(params.flags) + {} virtual ~IGPUComputePipeline() = default; const core::bitflag m_flags; diff --git a/include/nbl/video/IGPUGraphicsPipeline.h b/include/nbl/video/IGPUGraphicsPipeline.h index 8240bcea94..c44ef5ceb1 100644 --- a/include/nbl/video/IGPUGraphicsPipeline.h +++ b/include/nbl/video/IGPUGraphicsPipeline.h @@ -6,20 +6,21 @@ #include "nbl/video/IGPUPipelineLayout.h" #include "nbl/video/IGPURenderpass.h" +#include "nbl/video/IGPUPipeline.h" namespace nbl::video { -class IGPUGraphicsPipeline : public IBackendObject, public asset::IGraphicsPipeline +class IGPUGraphicsPipeline : public IGPUPipeline> { using pipeline_t = asset::IGraphicsPipeline; public: - struct SCreationParams final : pipeline_t::SCreationParams, SPipelineCreationParams - { + struct SCreationParams final : public SPipelineCreationParams + { public: - #define base_flag(F) static_cast(pipeline_t::SCreationParams::FLAGS::F) + #define base_flag(F) static_cast(pipeline_t::FLAGS::F) enum class FLAGS : uint64_t { NONE = base_flag(NONE), @@ -36,30 +37,48 @@ class IGPUGraphicsPipeline : public IBackendObject, public asset::IGraphicsPipel if (!layout) return {}; SSpecializationValidationResult retval = {.count=0,.dataSize=0}; - const bool valid = pipeline_t::SCreationParams::impl_valid([&retval](const IPipelineBase::SShaderSpecInfo& info)->bool + if (!layout) + return {}; + + // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkGraphicsPipelineCreateInfo.html#VUID-VkGraphicsPipelineCreateInfo-dynamicRendering-06576 + if (!renderpass || cached.subpassIx>=renderpass->getSubpassCount()) + return {}; + + // TODO: check rasterization samples, etc. + //rp->getCreationParameters().subpasses[i] + + core::bitflag stagePresence = {}; + + auto processSpecInfo = [&](const SShaderSpecInfo& specInfo, hlsl::ShaderStage stage) { - const auto dataSize = info.valid(); - if (dataSize<0) - return false; - else if (dataSize==0) - return true; - - const size_t count = info.entries ? info.entries->size():0x80000000ull; - if (count>0x7fffffff) - return {}; - retval += {.count=dataSize ? static_cast(count):0,.dataSize=static_cast(dataSize)}; - return retval; - }); - if (!valid) + if (!specInfo.shader) return true; + if (!specInfo.accumulateSpecializationValidationResult(&retval)) return false; + stagePresence |= stage; + return true; + }; + if (!processSpecInfo(vertexShader, hlsl::ShaderStage::ESS_VERTEX)) return {}; + if (!processSpecInfo(tesselationControlShader, hlsl::ShaderStage::ESS_TESSELLATION_CONTROL)) return {}; + if (!processSpecInfo(tesselationEvaluationShader, hlsl::ShaderStage::ESS_TESSELLATION_EVALUATION)) return {}; + if (!processSpecInfo(geometryShader, hlsl::ShaderStage::ESS_GEOMETRY)) return {}; + if (!processSpecInfo(fragmentShader, hlsl::ShaderStage::ESS_FRAGMENT)) return {}; + + if (!hasRequiredStages(stagePresence, cached.primitiveAssembly.primitiveType)) return {}; return retval; } - inline std::span getShaders() const {return shaders;} + IGPUPipelineLayout* layout = nullptr; + SShaderSpecInfo vertexShader; + SShaderSpecInfo tesselationControlShader; + SShaderSpecInfo tesselationEvaluationShader; + SShaderSpecInfo geometryShader; + SShaderSpecInfo fragmentShader; + SCachedCreationParams cached = {}; + renderpass_t* renderpass = nullptr; // TODO: Could guess the required flags from SPIR-V introspection of declared caps core::bitflag flags = FLAGS::NONE; - }; + }; inline core::bitflag getCreationFlags() const {return m_flags;} @@ -67,9 +86,10 @@ class IGPUGraphicsPipeline : public IBackendObject, public asset::IGraphicsPipel virtual const void* getNativeHandle() const = 0; protected: - IGPUGraphicsPipeline(const SCreationParams& params) : IBackendObject(core::smart_refctd_ptr(params.layout->getOriginDevice())), - pipeline_t(params), m_flags(params.flags) {} - virtual ~IGPUGraphicsPipeline() = default; + IGPUGraphicsPipeline(const SCreationParams& params) : + IGPUPipeline(core::smart_refctd_ptr(params.layout->getOriginDevice()), params.layout, params.cached, params.renderpass), m_flags(params.flags) + {} + virtual ~IGPUGraphicsPipeline() override = default; const core::bitflag m_flags; }; diff --git a/include/nbl/video/IGPUPipeline.h b/include/nbl/video/IGPUPipeline.h new file mode 100644 index 0000000000..f9a32786bf --- /dev/null +++ b/include/nbl/video/IGPUPipeline.h @@ -0,0 +1,127 @@ + + +// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_VIDEO_I_GPU_PIPELINE_H_INCLUDED_ +#define _NBL_VIDEO_I_GPU_PIPELINE_H_INCLUDED_ + +#include "nbl/video/IGPUPipelineLayout.h" +#include "nbl/video/SPipelineCreationParams.h" +#include "nbl/asset/IPipeline.h" + +namespace nbl::video +{ + +class IGPUPipelineBase { + public: + struct SShaderSpecInfo + { + //! Structure specifying a specialization map entry + /* + Note that if specialization constant ID is used + in a shader, \bsize\b and \boffset'b must match + to \isuch an ID\i accordingly. + + By design the API satisfies: + https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkSpecializationInfo.html#VUID-VkSpecializationInfo-offset-00773 + https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkSpecializationInfo.html#VUID-VkSpecializationInfo-pMapEntries-00774 + */ + //!< The ID of the specialization constant in SPIR-V. If it isn't used in the shader, the map entry does not affect the behavior of the pipeline. + using spec_constant_id_t = uint32_t; + + using SSpecConstantValue = std::span; + + inline SSpecConstantValue getSpecializationByteValue(const spec_constant_id_t _specConstID) const + { + if (!entries) return {}; + + const auto found = entries->find(_specConstID); + if (found != entries->end() && found->second.size()) return found->second; + else return {}; + } + + static constexpr int32_t INVALID_SPEC_INFO = -1; + inline int32_t valid() const + { + if (!shader) return INVALID_SPEC_INFO; + + // Impossible to check: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipelineShaderStageCreateInfo.html#VUID-VkPipelineShaderStageCreateInfo-pName-00707 + if (entryPoint.empty()) return INVALID_SPEC_INFO; + + // Impossible to efficiently check anything from: + // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipelineShaderStageCreateInfo.html#VUID-VkPipelineShaderStageCreateInfo-maxClipDistances-00708 + // to: + // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipelineShaderStageCreateInfo.html#VUID-VkPipelineShaderStageCreateInfo-stage-06686 + // and from: + // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipelineShaderStageCreateInfo.html#VUID-VkPipelineShaderStageCreateInfo-pNext-02756 + // to: + // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipelineShaderStageCreateInfo.html#VUID-VkPipelineShaderStageCreateInfo-module-08987 + + int64_t specData = 0; + for (const auto& entry : *entries) + { + if (!entry.second.size()) + return INVALID_SPEC_INFO; + specData += entry.second.size(); + } + if (specData>0x7fffffff) + return INVALID_SPEC_INFO; + return static_cast(specData); + } + + bool accumulateSpecializationValidationResult(SSpecializationValidationResult* retval) const + { + const auto dataSize = valid(); + if (dataSize < 0) + return false; + if (dataSize == 0) + return true; + + const size_t count = entries ? entries->size() : 0x80000000ull; + if (count > 0x7fffffff) + return false; + *retval += { + .count = dataSize ? static_cast(count) : 0, + .dataSize = static_cast(dataSize), + }; + return *retval; + } + + const asset::IShader* shader = nullptr; + std::string_view entryPoint = ""; + + asset::IPipelineBase::SUBGROUP_SIZE requiredSubgroupSize = asset::IPipelineBase::SUBGROUP_SIZE::UNKNOWN; //!< Default value of 8 means no requirement + + + // Container choice implicitly satisfies: + // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkSpecializationInfo.html#VUID-VkSpecializationInfo-constantID-04911 + const core::unordered_map* entries; + // By requiring Nabla Core Profile features we implicitly satisfy: + // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipelineShaderStageCreateInfo.html#VUID-VkPipelineShaderStageCreateInfo-flags-02784 + // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipelineShaderStageCreateInfo.html#VUID-VkPipelineShaderStageCreateInfo-flags-02785 + // Also because our API is sane, it satisfies the following by construction: + // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipelineShaderStageCreateInfo.html#VUID-VkPipelineShaderStageCreateInfo-pNext-02754 + + }; + +}; + +// Common Base class for pipelines +template + requires (std::is_base_of_v, PipelineNonBackendObjectBase> && !std::is_base_of_v) +class IGPUPipeline : public IBackendObject, public PipelineNonBackendObjectBase, public IGPUPipelineBase +{ + protected: + + template + explicit IGPUPipeline(core::smart_refctd_ptr&& device, Args&&... args) : + PipelineNonBackendObjectBase(std::forward(args...)), IBackendObject(std::move(device)) + {} + virtual ~IGPUPipeline() = default; + +}; + +} + +#endif diff --git a/include/nbl/video/IGPURayTracingPipeline.h b/include/nbl/video/IGPURayTracingPipeline.h index fb8c371193..66e3a01072 100644 --- a/include/nbl/video/IGPURayTracingPipeline.h +++ b/include/nbl/video/IGPURayTracingPipeline.h @@ -10,28 +10,70 @@ namespace nbl::video { -class IGPURayTracingPipeline : public IBackendObject, public asset::IRayTracingPipeline +class IGPURayTracingPipeline : public IGPUPipeline> { using pipeline_t = asset::IRayTracingPipeline; public: - - struct SShaderGroupHandle + struct SCreationParams : public SPipelineCreationParams { - private: - uint8_t data[video::SPhysicalDeviceLimits::ShaderGroupHandleSize]; - }; - static_assert(sizeof(SShaderGroupHandle) == video::SPhysicalDeviceLimits::ShaderGroupHandleSize); + using FLAGS = pipeline_t::FLAGS; - struct SHitGroupStackSize - { - uint16_t closestHit; - uint16_t anyHit; - uint16_t intersection; - }; + struct SShaderGroupsParams + { + struct SHitGroup + { + SShaderSpecInfo closestHit; + SShaderSpecInfo anyHit; + SShaderSpecInfo intersection; + }; - struct SCreationParams final : pipeline_t::SCreationParams, SPipelineCreationParams - { + SShaderSpecInfo raygen; + std::span misses; + std::span hits; + std::span callables; + + inline uint32_t getShaderGroupCount() const + { + return 1 + hits.size() + misses.size() + callables.size(); + } + + inline uint32_t getMissShaderCount() const + { + auto count = 0; + for (const auto& miss : misses) + count += (miss.shader != nullptr); + return count; + } + + inline uint32_t getHitShaderCount() const + { + auto count = 0; + for (const auto& hit : hits) + { + count += (hit.closestHit.shader != nullptr); + count += (hit.anyHit.shader != nullptr); + count += (hit.intersection.shader != nullptr); + } + return count; + } + + inline uint32_t getCallableShaderCount() const + { + auto count = 0; + for (const auto& callable : callables) + count += (callable.shader != nullptr ? 1 : 0); + return count; + } + + }; + + IGPUPipelineLayout* layout = nullptr; + SShaderGroupsParams shaderGroups; + + SCachedCreationParams cached = {}; + // TODO: Could guess the required flags from SPIR-V introspection of declared caps + core::bitflag flags = FLAGS::NONE; inline SSpecializationValidationResult valid() const { @@ -39,30 +81,76 @@ class IGPURayTracingPipeline : public IBackendObject, public asset::IRayTracingP return {}; SSpecializationValidationResult retval = { - .count=0, - .dataSize=0, + .count = 0, + .dataSize = 0, }; - const bool valid = pipeline_t::SCreationParams::impl_valid([&retval](const asset::IPipelineBase::SShaderSpecInfo& info)->bool + + if (!shaderGroups.raygen.accumulateSpecializationValidationResult(&retval)) + return {}; + + for (const auto& shaderGroup : shaderGroups.hits) { - const auto dataSize = info.valid(); - if (dataSize<0) - return false; - else if (dataSize==0) - return true; - - const size_t count = info.entries ? info.entries->size():0x80000000ull; - if (count>0x7fffffff) + if (shaderGroup.intersection.shader) + { + if (!shaderGroup.intersection.accumulateSpecializationValidationResult(&retval)) return {}; - retval += {.count=dataSize ? static_cast(count):0,.dataSize=static_cast(dataSize)}; - return retval; - }); - if (!valid) - return {}; + } + + if (shaderGroup.closestHit.shader) + { + if (!shaderGroup.closestHit.accumulateSpecializationValidationResult(&retval)) + return {}; + } + + // https://docs.vulkan.org/spec/latest/chapters/pipelines.html#VUID-VkRayTracingPipelineCreateInfoKHR-flags-03470 + if (flags & FLAGS::NO_NULL_ANY_HIT_SHADERS && !shaderGroup.anyHit.shader) + return {}; + + if (shaderGroup.anyHit.shader) + { + if (!shaderGroup.anyHit.accumulateSpecializationValidationResult(&retval)) + return {}; + } + + // https://docs.vulkan.org/spec/latest/chapters/pipelines.html#VUID-VkRayTracingPipelineCreateInfoKHR-flags-03471 + if (flags & FLAGS::NO_NULL_CLOSEST_HIT_SHADERS && !shaderGroup.intersection.shader) + return {}; + } + + for (const auto& miss : shaderGroups.misses) + { + if (miss.shader) + { + if (!miss.accumulateSpecializationValidationResult(&retval)) + return {}; + } + } + + for (const auto& callable : shaderGroups.callables) + { + if (callable.shader) + { + if (!callable.accumulateSpecializationValidationResult(&retval)) + return {}; + } + } + return retval; } + }; - inline std::span getShaders() const { return shaders; } + struct SShaderGroupHandle + { + private: + uint8_t data[video::SPhysicalDeviceLimits::ShaderGroupHandleSize]; + }; + static_assert(sizeof(SShaderGroupHandle) == video::SPhysicalDeviceLimits::ShaderGroupHandleSize); + struct SHitGroupStackSize + { + uint16_t closestHit; + uint16_t anyHit; + uint16_t intersection; }; inline core::bitflag getCreationFlags() const { return m_flags; } @@ -82,8 +170,7 @@ class IGPURayTracingPipeline : public IBackendObject, public asset::IRayTracingP virtual uint16_t getDefaultStackSize() const = 0; protected: - IGPURayTracingPipeline(const SCreationParams& params) : IBackendObject(core::smart_refctd_ptr(params.layout->getOriginDevice())), - pipeline_t(params), + IGPURayTracingPipeline(const SCreationParams& params) : IGPUPipeline(core::smart_refctd_ptr(params.layout->getOriginDevice()), params), m_flags(params.flags) {} diff --git a/include/nbl/video/ILogicalDevice.h b/include/nbl/video/ILogicalDevice.h index 49364f3a54..ab0d5bea06 100644 --- a/include/nbl/video/ILogicalDevice.h +++ b/include/nbl/video/ILogicalDevice.h @@ -1097,7 +1097,7 @@ class NBL_API2 ILogicalDevice : public core::IReferenceCounted, public IDeviceMe virtual core::smart_refctd_ptr createFramebuffer_impl(IGPUFramebuffer::SCreationParams&& params) = 0; template - inline CreationParams::SSpecializationValidationResult commonCreatePipelines(IGPUPipelineCache* const pipelineCache, const std::span params, ExtraLambda&& extra) + inline SSpecializationValidationResult commonCreatePipelines(IGPUPipelineCache* const pipelineCache, const std::span params, ExtraLambda&& extra) { if (pipelineCache && !pipelineCache->wasCreatedBy(this)) { @@ -1110,7 +1110,7 @@ class NBL_API2 ILogicalDevice : public core::IReferenceCounted, public IDeviceMe return {}; } - typename CreationParams::SSpecializationValidationResult retval = {.count=0,.dataSize=0}; + SSpecializationValidationResult retval = {.count=0,.dataSize=0}; for (auto i=0; i createInfos, core::smart_refctd_ptr* const output, - const IGPUComputePipeline::SCreationParams::SSpecializationValidationResult& validation + const SSpecializationValidationResult& validation ) = 0; virtual void createGraphicsPipelines_impl( IGPUPipelineCache* const pipelineCache, const std::span params, core::smart_refctd_ptr* const output, - const IGPUGraphicsPipeline::SCreationParams::SSpecializationValidationResult& validation + const SSpecializationValidationResult& validation ) = 0; virtual void createRayTracingPipelines_impl( IGPUPipelineCache* const pipelineCache, const std::span createInfos, core::smart_refctd_ptr* const output, - const IGPURayTracingPipeline::SCreationParams::SSpecializationValidationResult& validation + const SSpecializationValidationResult& validation ) = 0; virtual core::smart_refctd_ptr createQueryPool_impl(const IQueryPool::SCreationParams& params) = 0; diff --git a/include/nbl/video/SPipelineCreationParams.h b/include/nbl/video/SPipelineCreationParams.h index 489bff4343..3a25560ae4 100644 --- a/include/nbl/video/SPipelineCreationParams.h +++ b/include/nbl/video/SPipelineCreationParams.h @@ -11,6 +11,31 @@ namespace nbl::video { +struct SSpecializationValidationResult +{ + constexpr static inline uint32_t Invalid = ~0u; + inline operator bool() const + { + return count!=Invalid && dataSize!=Invalid; + } + + inline SSpecializationValidationResult& operator+=(const SSpecializationValidationResult& other) + { + // TODO: check for overflow before adding + if (*this && other) + { + count += other.count; + dataSize += other.dataSize; + } + else + *this = {}; + return *this; + } + + uint32_t count = Invalid; + uint32_t dataSize = Invalid; +}; + // For now, due to API design we implicitly satisfy: // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipelineShaderStageCreateInfo.html#VUID-VkPipelineShaderStageCreateInfo-stage-08771 // to: @@ -18,30 +43,6 @@ namespace nbl::video template struct SPipelineCreationParams { - struct SSpecializationValidationResult - { - constexpr static inline uint32_t Invalid = ~0u; - inline operator bool() const - { - return count!=Invalid && dataSize!=Invalid; - } - - inline SSpecializationValidationResult& operator+=(const SSpecializationValidationResult& other) - { - // TODO: check for overflow before adding - if (*this && other) - { - count += other.count; - dataSize += other.dataSize; - } - else - *this = {}; - return *this; - } - - uint32_t count = Invalid; - uint32_t dataSize = Invalid; - }; constexpr static inline int32_t NotDerivingFromPreviousPipeline = -1; inline bool isDerivative() const diff --git a/src/nbl/asset/ICPUDescriptorSet.cpp b/src/nbl/asset/ICPUDescriptorSet.cpp index 03724be1a2..a95074fdb7 100644 --- a/src/nbl/asset/ICPUDescriptorSet.cpp +++ b/src/nbl/asset/ICPUDescriptorSet.cpp @@ -108,36 +108,47 @@ core::smart_refctd_ptr ICPUDescriptorSet::clone(uint32_t _depth) const return cp; } -IAsset* ICPUDescriptorSet::getDependant_impl(size_t ix) +template + requires(std::same_as, ICPUDescriptorSet>) +static auto computeDependantsImpl(Self* self) { + using asset_ptr_t = std::conditional_t, const IAsset*, IAsset*>; + core::unordered_set dependants = { self->m_layout.get() }; + for (auto i = 0u; i < static_cast(IDescriptor::E_TYPE::ET_COUNT); i++) + { + if (!self->m_descriptorInfos[i]) continue; + const auto size = self->m_descriptorInfos[i]->size(); + for (auto desc_i = 0u; desc_i < size; desc_i++) + { + auto* desc = self->m_descriptorInfos[i]->operator[](desc_i).desc.get(); + if (!desc) continue; + switch (IDescriptor::GetTypeCategory(static_cast(i))) + { + case IDescriptor::EC_BUFFER: + dependants.insert(static_cast(desc)); + case IDescriptor::EC_SAMPLER: + dependants.insert(static_cast(desc)); + case IDescriptor::EC_IMAGE: + dependants.insert(static_cast(desc)); + case IDescriptor::EC_BUFFER_VIEW: + dependants.insert(static_cast(desc)); + case IDescriptor::EC_ACCELERATION_STRUCTURE: + dependants.insert(static_cast(desc)); + default: + break; + } + } + } + return dependants; +} + +core::unordered_set ICPUDescriptorSet::computeDependants() const { - for (auto i=0u; i(IDescriptor::E_TYPE::ET_COUNT); i++) - if (m_descriptorInfos[i]) - { - const auto size = m_descriptorInfos[i]->size(); - if (ixoperator[](ix).desc.get(); - if (desc) - switch (IDescriptor::GetTypeCategory(static_cast(i))) - { - case IDescriptor::EC_BUFFER: - return static_cast(desc); - case IDescriptor::EC_SAMPLER: - return static_cast(desc); - case IDescriptor::EC_IMAGE: - return static_cast(desc); - case IDescriptor::EC_BUFFER_VIEW: - return static_cast(desc); - case IDescriptor::EC_ACCELERATION_STRUCTURE: - return static_cast(desc); - default: - break; - } - return nullptr; - } - else - ix -= size; - } - return nullptr; + return computeDependantsImpl(this); +} + +core::unordered_set ICPUDescriptorSet::computeDependants() +{ + return computeDependantsImpl(this); } + } \ No newline at end of file diff --git a/src/nbl/asset/utils/CSPIRVIntrospector.cpp b/src/nbl/asset/utils/CSPIRVIntrospector.cpp index 8b43c676b7..214ffdddbb 100644 --- a/src/nbl/asset/utils/CSPIRVIntrospector.cpp +++ b/src/nbl/asset/utils/CSPIRVIntrospector.cpp @@ -3,6 +3,8 @@ // For conditions of distribution and use, see copyright notice in nabla.h #include "nbl/asset/utils/CSPIRVIntrospector.h" + +#include "nbl/asset/ICPUPipeline.h" #include "nbl/asset/utils/spvUtils.h" #include "nbl_spirv_cross/spirv_parser.hpp" @@ -106,15 +108,15 @@ static CSPIRVIntrospector::CStageIntrospectionData::VAR_TYPE spvcrossType2E_TYPE } } -core::smart_refctd_ptr CSPIRVIntrospector::createApproximateComputePipelineFromIntrospection(const IPipelineBase::SShaderSpecInfo& info, core::smart_refctd_ptr&& layout/* = nullptr*/) +core::smart_refctd_ptr CSPIRVIntrospector::createApproximateComputePipelineFromIntrospection(const ICPUPipelineBase::SShaderSpecInfo& info, core::smart_refctd_ptr&& layout/* = nullptr*/) { - if (info.stage!=IShader::E_SHADER_STAGE::ESS_COMPUTE || info.valid()==IPipelineBase::SShaderSpecInfo::INVALID_SPEC_INFO) + if (info.valid()==ICPUPipelineBase::SShaderSpecInfo::INVALID_SPEC_INFO) return nullptr; CStageIntrospectionData::SParams params; params.entryPoint = info.entryPoint; params.shader = core::smart_refctd_ptr(info.shader); - params.stage = info.stage; + params.stage = hlsl::ShaderStage::ESS_COMPUTE; auto introspection = introspect(params); @@ -174,15 +176,13 @@ core::smart_refctd_ptr CSPIRVIntrospector::createApproximat layout = pplnIntrospectData->createApproximatePipelineLayoutFromIntrospection(introspection); } - ICPUComputePipeline::SCreationParams pplnCreationParams; - pplnCreationParams.layout = layout.get(); - pplnCreationParams.shader = info; - pplnCreationParams.layout = layout.get(); - return ICPUComputePipeline::create(pplnCreationParams); + auto pipeline = ICPUComputePipeline::create(layout.get()); + pipeline->getSpecInfoMut(hlsl::ShaderStage::ESS_COMPUTE)[0] = info; + return pipeline; } // returns true if successfully added all the info to self, false if incompatible with what's already in our pipeline or incomplete (e.g. missing spec constants) -NBL_API2 bool CSPIRVIntrospector::CPipelineIntrospectionData::merge(const CSPIRVIntrospector::CStageIntrospectionData* stageData, const IPipelineBase::SShaderSpecInfo::spec_constant_map_t* specConstants) +NBL_API2 bool CSPIRVIntrospector::CPipelineIntrospectionData::merge(const CSPIRVIntrospector::CStageIntrospectionData* stageData, const ICPUPipelineBase::SShaderSpecInfo::spec_constant_map_t* specConstants) { if (!stageData) return false; @@ -218,7 +218,7 @@ NBL_API2 bool CSPIRVIntrospector::CPipelineIntrospectionData::merge(const CSPIRV if (specConstantFound == specConstants->end()) return false; - descInfo.count = specConstantFound->second; + descInfo.count = (specConstantFound->second.size() != 0); } else { diff --git a/src/nbl/video/ILogicalDevice.cpp b/src/nbl/video/ILogicalDevice.cpp index 26cfc4c6a8..7714219836 100644 --- a/src/nbl/video/ILogicalDevice.cpp +++ b/src/nbl/video/ILogicalDevice.cpp @@ -7,45 +7,52 @@ using namespace nbl; using namespace nbl::video; -static void debloatShaders(const asset::ISPIRVDebloater& debloater, std::span shaderSpecs, core::vector>& outShaders, asset::IPipelineBase::SShaderSpecInfo* outShaderSpecInfos, system::logger_opt_ptr logger = nullptr) +class SpirvDebloatTask { - using EntryPoints = core::set; - core::map entryPointsMap; - - // collect all entry points first before we debloat - for (const auto& shaderSpec : shaderSpecs) { - const auto* shader = shaderSpec.shader; - auto it = entryPointsMap.find(shader); - if (it == entryPointsMap.end() || it->first != shader) - it = entryPointsMap.emplace_hint(it, shader, EntryPoints()); - it->second.insert({ .name = shaderSpec.entryPoint, .stage = shaderSpec.stage }); - } + public: + using EntryPoints = core::set; - core::map debloatedShaders; - for (const auto& shaderSpec: shaderSpecs) - { - const auto* shader = shaderSpec.shader; - const auto& entryPoints = entryPointsMap[shader]; + SpirvDebloatTask(asset::ISPIRVDebloater* debloater, system::logger_opt_ptr logger) : m_debloater(debloater), m_logger(logger) + { + + } - auto debloatedShaderSpec = shaderSpec; - if (shader != nullptr) + void insertEntryPoint(const IGPUPipelineBase::SShaderSpecInfo& shaderSpec, hlsl::ShaderStage stage) { - if (!debloatedShaders.contains(shader)) - { - const auto outShadersData = outShaders.data(); - outShaders.push_back(debloater.debloat(shader, entryPoints, logger)); - assert(outShadersData == outShaders.data()); - debloatedShaders.emplace(shader, outShaders.back().get()); - } - const auto debloatedShader = debloatedShaders[shader]; - debloatedShaderSpec.shader = debloatedShader; + const auto* shader = shaderSpec.shader; + auto it = m_entryPointsMap.find(shader); + if (it == m_entryPointsMap.end() || it->first != shader) + it = m_entryPointsMap.emplace_hint(it, shader, EntryPoints()); + it->second.insert({ .name = shaderSpec.entryPoint, .stage = stage }); } - *outShaderSpecInfos = debloatedShaderSpec; - outShaderSpecInfos++; - } + IGPUPipelineBase::SShaderSpecInfo debloat(const IGPUPipelineBase::SShaderSpecInfo& shaderSpec, core::vector>& outShaders) + { + const auto* shader = shaderSpec.shader; + const auto& entryPoints = m_entryPointsMap[shader]; -} + auto debloatedShaderSpec = shaderSpec; + if (shader != nullptr) + { + if (!m_debloatedShadersMap.contains(shader)) + { + const auto outShadersData = outShaders.data(); + outShaders.push_back(m_debloater->debloat(shader, entryPoints, m_logger)); + assert(outShadersData == outShaders.data()); + m_debloatedShadersMap.emplace(shader, outShaders.back().get()); + } + const auto debloatedShader = m_debloatedShadersMap[shader]; + debloatedShaderSpec.shader = debloatedShader; + } + return debloatedShaderSpec; + } + + private: + core::map m_entryPointsMap; + core::map m_debloatedShadersMap; + asset::ISPIRVDebloater* m_debloater; + const system::logger_opt_ptr m_logger; +}; ILogicalDevice::ILogicalDevice(core::smart_refctd_ptr&& api, const IPhysicalDevice* const physicalDevice, const SCreationParams& params, const bool runningInRenderdoc) : m_api(api), m_physicalDevice(physicalDevice), m_enabledFeatures(params.featuresToEnable), m_compilerSet(params.compilerSet), @@ -781,10 +788,10 @@ asset::ICPUPipelineCache::SCacheKey ILogicalDevice::getPipelineCacheKey() const bool ILogicalDevice::createComputePipelines(IGPUPipelineCache* const pipelineCache, const std::span params, core::smart_refctd_ptr* const output) { std::fill_n(output,params.size(),nullptr); - IGPUComputePipeline::SCreationParams::SSpecializationValidationResult specConstantValidation = commonCreatePipelines(pipelineCache,params,[this](const asset::IPipelineBase::SShaderSpecInfo& info)->bool + SSpecializationValidationResult specConstantValidation = commonCreatePipelines(pipelineCache,params,[this](const IGPUPipelineBase::SShaderSpecInfo& info)->bool { // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipelineShaderStageCreateInfo.html#VUID-VkPipelineShaderStageCreateInfo-pNext-02755 - if (info.requiredSubgroupSize>=asset::IPipelineBase::SShaderSpecInfo::SUBGROUP_SIZE::REQUIRE_4 && !getPhysicalDeviceLimits().requiredSubgroupSizeStages.hasFlags(info.stage)) + if (info.requiredSubgroupSize>=asset::IPipelineBase::SUBGROUP_SIZE::REQUIRE_4 && !getPhysicalDeviceLimits().requiredSubgroupSizeStages.hasFlags(hlsl::ShaderStage::ESS_COMPUTE)) { NBL_LOG_ERROR("Invalid shader stage"); return false; @@ -808,7 +815,11 @@ bool ILogicalDevice::createComputePipelines(IGPUPipelineCache* const pipelineCac for (auto ix = 0u; ix < params.size(); ix++) { const auto& ci = params[ix]; - debloatShaders(*m_spirvDebloater.get(), ci.getShaders(), debloatedShaders, &newParams[ix].shader, m_logger); + const core::set entryPoints = { asset::ISPIRVDebloater::EntryPoint{.name = ci.shader.entryPoint, .stage = hlsl::ShaderStage::ESS_COMPUTE} }; + debloatedShaders.push_back(m_spirvDebloater->debloat(ci.shader.shader, entryPoints, m_logger)); + auto debloatedShaderSpec = ci.shader; + debloatedShaderSpec.shader = debloatedShaders.back().get(); + newParams[ix].shader = debloatedShaderSpec; } createComputePipelines_impl(pipelineCache,newParams,output,specConstantValidation); @@ -834,12 +845,10 @@ bool ILogicalDevice::createGraphicsPipelines( ) { std::fill_n(output, params.size(), nullptr); - IGPUGraphicsPipeline::SCreationParams::SSpecializationValidationResult specConstantValidation = commonCreatePipelines(nullptr, params, - [this](const asset::IPipelineBase::SShaderSpecInfo& info)->bool + SSpecializationValidationResult specConstantValidation = commonCreatePipelines(nullptr, params, + [this](const IGPUPipelineBase::SShaderSpecInfo& info)->bool { - if (info.stage != hlsl::ShaderStage::ESS_VERTEX) - return true; - return info.shader; + return info.shader != nullptr; } ); if (!specConstantValidation) @@ -858,9 +867,6 @@ bool ILogicalDevice::createGraphicsPipelines( core::vector> debloatedShaders; // vector to hold all the debloated shaders, so the pointer from the new ShaderSpecInfo is not dangling debloatedShaders.reserve(shaderCount); - core::vector debloatedShaderSpecs(shaderCount); - auto outShaderSpecs = debloatedShaderSpecs.data(); - for (auto ix = 0u; ix < params.size(); ix++) { const auto& ci = params[ix]; @@ -953,9 +959,19 @@ bool ILogicalDevice::createGraphicsPipelines( } } } + + SpirvDebloatTask debloatTask(m_spirvDebloater.get(), m_logger); + debloatTask.insertEntryPoint(ci.vertexShader, hlsl::ShaderStage::ESS_VERTEX); + debloatTask.insertEntryPoint(ci.tesselationControlShader, hlsl::ShaderStage::ESS_TESSELLATION_CONTROL); + debloatTask.insertEntryPoint(ci.tesselationEvaluationShader, hlsl::ShaderStage::ESS_TESSELLATION_EVALUATION); + debloatTask.insertEntryPoint(ci.geometryShader, hlsl::ShaderStage::ESS_GEOMETRY); + debloatTask.insertEntryPoint(ci.fragmentShader, hlsl::ShaderStage::ESS_FRAGMENT); - newParams[ix].shaders = std::span(outShaderSpecs, ci.getShaders().size()); - debloatShaders(*m_spirvDebloater.get(), ci.getShaders(), debloatedShaders, outShaderSpecs, m_logger); + newParams[ix].vertexShader = debloatTask.debloat(ci.vertexShader, debloatedShaders); + newParams[ix].tesselationControlShader = debloatTask.debloat(ci.tesselationControlShader, debloatedShaders); + newParams[ix].tesselationEvaluationShader = debloatTask.debloat(ci.tesselationEvaluationShader, debloatedShaders); + newParams[ix].geometryShader = debloatTask.debloat(ci.geometryShader, debloatedShaders); + newParams[ix].fragmentShader = debloatTask.debloat(ci.fragmentShader, debloatedShaders); } createGraphicsPipelines_impl(pipelineCache, newParams, output, specConstantValidation); @@ -980,7 +996,7 @@ bool ILogicalDevice::createRayTracingPipelines(IGPUPipelineCache* const pipeline core::smart_refctd_ptr* const output) { std::fill_n(output,params.size(),nullptr); - IGPURayTracingPipeline::SCreationParams::SSpecializationValidationResult specConstantValidation = commonCreatePipelines(pipelineCache,params,[this](const asset::IPipelineBase::SShaderSpecInfo& info)->bool + SSpecializationValidationResult specConstantValidation = commonCreatePipelines(pipelineCache,params,[this](const IGPUPipelineBase::SShaderSpecInfo& info)->bool { return true; }); @@ -1028,15 +1044,43 @@ bool ILogicalDevice::createRayTracingPipelines(IGPUPipelineCache* const pipeline } core::vector newParams(params.begin(), params.end()); - const auto shaderCount = std::accumulate(params.begin(), params.end(), 0, [](uint32_t sum, auto& param) + const auto raygenCount = params.size(); // assume every param have raygen + const auto missShaderCount = std::accumulate(params.begin(), params.end(), 0, [](uint32_t sum, auto& param) { - return sum + param.getShaders().size(); + return sum + param.shaderGroups.getMissShaderCount(); + }); + const auto hitShaderCount = std::accumulate(params.begin(), params.end(), 0, [](uint32_t sum, auto& param) + { + return sum + param.shaderGroups.getHitShaderCount(); }); + const auto callableShaderCount = std::accumulate(params.begin(), params.end(), 0, [](uint32_t sum, auto& param) + { + return sum + param.shaderGroups.getCallableShaderCount(); + }); + const auto shaderCount = raygenCount + missShaderCount + hitShaderCount + callableShaderCount; core::vector> debloatedShaders; // vector to hold all the debloated shaders, so the pointer from the new ShaderSpecInfo is not dangling debloatedShaders.reserve(shaderCount); - core::vector debloatedShaderSpecs(shaderCount); - auto outShaderSpecs = debloatedShaderSpecs.data(); + const auto missGroupCount = std::accumulate(params.begin(), params.end(), 0, [](uint32_t sum, auto& param) + { + return sum + param.shaderGroups.misses.size(); + }); + const auto hitGroupCount = std::accumulate(params.begin(), params.end(), 0, [](uint32_t sum, auto& param) + { + return sum + param.shaderGroups.hits.size(); + }); + const auto callableGroupCount = std::accumulate(params.begin(), params.end(), 0, [](uint32_t sum, auto& param) + { + return sum + param.shaderGroups.callables.size(); + }); + + + core::vector debloatedMissSpecs(missGroupCount); + auto debloatedMissSpecData = debloatedMissSpecs.data(); + core::vector debloatedHitSpecs(hitGroupCount); + auto debloatedHitSpecData = debloatedHitSpecs.data(); + core::vector debloatedCallableSpecs(callableGroupCount); + auto debloatedCallableSpecData = debloatedCallableSpecs.data(); const auto& limits = getPhysicalDeviceLimits(); for (auto ix = 0u; ix < params.size(); ix++) @@ -1050,14 +1094,47 @@ bool ILogicalDevice::createRayTracingPipelines(IGPUPipelineCache* const pipeline NBL_LOG_ERROR("Invalid maxRecursionDepth. maxRecursionDepth(%u) exceed the limits(%u)", param.cached.maxRecursionDepth, limits.maxRayRecursionDepth); return false; } - if (param.getShaders().empty()) + + SpirvDebloatTask debloatTask(m_spirvDebloater.get(), m_logger); + debloatTask.insertEntryPoint(param.shaderGroups.raygen, hlsl::ShaderStage::ESS_RAYGEN); + for (const auto& miss : param.shaderGroups.misses) + debloatTask.insertEntryPoint(miss, hlsl::ShaderStage::ESS_MISS); + for (const auto& hit : param.shaderGroups.hits) { - NBL_LOG_ERROR("Pipeline must have at least one shader."); - return false; + debloatTask.insertEntryPoint(hit.closestHit, hlsl::ShaderStage::ESS_CLOSEST_HIT); + debloatTask.insertEntryPoint(hit.anyHit, hlsl::ShaderStage::ESS_ANY_HIT); + debloatTask.insertEntryPoint(hit.intersection, hlsl::ShaderStage::ESS_INTERSECTION); + } + for (const auto& callable : param.shaderGroups.callables) + debloatTask.insertEntryPoint(callable, hlsl::ShaderStage::ESS_CALLABLE); + + newParams[ix] = param; + newParams[ix].shaderGroups.raygen = debloatTask.debloat(param.shaderGroups.raygen, debloatedShaders); + + newParams[ix].shaderGroups.misses = { debloatedMissSpecData, param.shaderGroups.misses.size() }; + for (const auto& miss: param.shaderGroups.misses) + { + *debloatedMissSpecData = debloatTask.debloat(miss, debloatedShaders); + debloatedMissSpecData++; } - newParams[ix].shaders = std::span(outShaderSpecs, param.getShaders().size()); - debloatShaders(*m_spirvDebloater.get(), param.getShaders(), debloatedShaders, outShaderSpecs, m_logger); + newParams[ix].shaderGroups.hits = { debloatedHitSpecData, param.shaderGroups.hits.size() }; + for (const auto& hit: param.shaderGroups.hits) + { + *debloatedHitSpecData = { + .closestHit = debloatTask.debloat(hit.closestHit, debloatedShaders), + .intersection = debloatTask.debloat(hit.intersection, debloatedShaders), + .anyHit = debloatTask.debloat(hit.anyHit, debloatedShaders), + }; + debloatedHitSpecData++; + } + + newParams[ix].shaderGroups.callables = { debloatedCallableSpecData, param.shaderGroups.callables.size() }; + for (const auto& callable: param.shaderGroups.callables) + { + *debloatedCallableSpecData = debloatTask.debloat(callable, debloatedShaders); + debloatedCallableSpecData++; + } } createRayTracingPipelines_impl(pipelineCache, newParams,output,specConstantValidation);