diff --git a/include/nbl/asset/ICPURayTracingPipeline.h b/include/nbl/asset/ICPURayTracingPipeline.h index 17c53557e1..01cf4a3f28 100644 --- a/include/nbl/asset/ICPURayTracingPipeline.h +++ b/include/nbl/asset/ICPURayTracingPipeline.h @@ -25,14 +25,12 @@ class ICPURayTracingPipeline final : public ICPUPipeline intersections; }; - static core::smart_refctd_ptr create(const ICPUPipelineLayout* layout) + static core::smart_refctd_ptr create(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; } @@ -83,12 +81,13 @@ class ICPURayTracingPipeline final : public ICPUPipelinevalid()) return false; if (m_raygen.valid() == SShaderSpecInfo::INVALID_SPEC_INFO) return false; + if (m_hitGroups.anyHits.size() != m_hitGroups.closestHits.size()) return false; + if (m_hitGroups.anyHits.size() != m_hitGroups.intersections.size()) return false; return true; } @@ -102,7 +101,23 @@ class ICPURayTracingPipeline final : public ICPUPipeline m_callables; - explicit ICPURayTracingPipeline(const ICPUPipelineLayout* layout) + explicit ICPURayTracingPipeline(ICPUPipelineLayout* layout) : base_t(layout, {}) {} inline void visitDependents_impl(std::function visit) const override { - if (!visit(m_raygen.shader.get()) return; - for (const auto& missInfo : self->m_misses) if (!visit(missInfo.shader.get())) return; - for (const auto& anyHitInfo : self->m_hitGroups.anyHits) if (!visit(anyHitInfo.shader.get())) return; - for (const auto& closestHitInfo : self->m_hitGroups.closestHits) if (!visit(closestHitInfo.shader.get())) return; - for (const auto& intersectionInfo : self->m_hitGroups.intersections) if (!visit(intersectionInfo.shader.get())) return; - for (const auto& callableInfo : self->m_callables) if(!visit(callableInfo.shader.get())) return; + if (!visit(m_layout.get())) return; + if (!visit(m_raygen.shader.get())) return; + auto noNullVisit = [&](const IShader* shader) -> bool + { + if (!shader) return true; + return visit(shader); + }; + for (const auto& missInfo : m_misses) if (!noNullVisit(missInfo.shader.get())) return; + for (const auto& anyHitInfo : m_hitGroups.anyHits) if (!noNullVisit(anyHitInfo.shader.get())) return; + for (const auto& closestHitInfo : m_hitGroups.closestHits) if (!noNullVisit(closestHitInfo.shader.get())) return; + for (const auto& intersectionInfo : m_hitGroups.intersections) if (!noNullVisit(intersectionInfo.shader.get())) return; + for (const auto& callableInfo : m_callables) if(!noNullVisit(callableInfo.shader.get())) return; } inline core::smart_refctd_ptr clone_impl(core::smart_refctd_ptr&& layout, uint32_t depth) const override final diff --git a/include/nbl/asset/IRayTracingPipeline.h b/include/nbl/asset/IRayTracingPipeline.h index b97d8d7002..c0d8d98ce5 100644 --- a/include/nbl/asset/IRayTracingPipeline.h +++ b/include/nbl/asset/IRayTracingPipeline.h @@ -14,18 +14,6 @@ namespace nbl::asset class IRayTracingPipelineBase : public virtual core::IReferenceCounted { public: - struct SCachedCreationParams final - { - uint32_t maxRecursionDepth : 6 = 0; - uint32_t dynamicStackSize : 1 = false; - }; -}; - -template -class IRayTracingPipeline : public IPipeline, public IRayTracingPipelineBase -{ - public: - #define base_flag(F) static_cast(IPipelineBase::FLAGS::F) enum class CreationFlags : uint64_t { @@ -43,7 +31,19 @@ class IRayTracingPipeline : public IPipeline, public IRayTra ALLOW_MOTION = 1<<20, }; #undef base_flag - using FLAGS = CreationFlags; + + struct SCachedCreationParams final + { + core::bitflag flags = CreationFlags::NONE; + uint32_t maxRecursionDepth : 6 = 0; + uint32_t dynamicStackSize : 1 = false; + }; +}; + +template +class IRayTracingPipeline : public IPipeline, public IRayTracingPipelineBase +{ + public: inline const SCachedCreationParams& getCachedCreationParams() const { return m_params; } diff --git a/include/nbl/video/IGPUComputePipeline.h b/include/nbl/video/IGPUComputePipeline.h index c7343c131a..9854725cd1 100644 --- a/include/nbl/video/IGPUComputePipeline.h +++ b/include/nbl/video/IGPUComputePipeline.h @@ -74,6 +74,10 @@ class IGPUComputePipeline : public IGPUPipeline& getFlags() { return flags; } + + inline core::bitflag getFlags() const { return flags; } + const IGPUPipelineLayout* layout = nullptr; // TODO: Could guess the required flags from SPIR-V introspection of declared caps core::bitflag flags = FLAGS::NONE; diff --git a/include/nbl/video/IGPUGraphicsPipeline.h b/include/nbl/video/IGPUGraphicsPipeline.h index e5dc7c5d7b..79e1337787 100644 --- a/include/nbl/video/IGPUGraphicsPipeline.h +++ b/include/nbl/video/IGPUGraphicsPipeline.h @@ -87,6 +87,10 @@ class IGPUGraphicsPipeline : public IGPUPipeline& getFlags() { return flags; } + + inline core::bitflag getFlags() const { return flags; } + const IGPUPipelineLayout* layout = nullptr; SShaderSpecInfo vertexShader; SShaderSpecInfo tesselationControlShader; diff --git a/include/nbl/video/IGPURayTracingPipeline.h b/include/nbl/video/IGPURayTracingPipeline.h index ecdc529542..816cc68243 100644 --- a/include/nbl/video/IGPURayTracingPipeline.h +++ b/include/nbl/video/IGPURayTracingPipeline.h @@ -24,7 +24,7 @@ class IGPURayTracingPipeline : public IGPUPipeline { - using FLAGS = pipeline_t::FLAGS; + using FLAGS = IRayTracingPipelineBase::CreationFlags; struct SShaderGroupsParams { @@ -45,8 +45,6 @@ class IGPURayTracingPipeline : public IGPUPipeline flags = FLAGS::NONE; inline SSpecializationValidationResult valid() const { @@ -76,7 +74,7 @@ class IGPURayTracingPipeline : public IGPUPipeline& getFlags() { return cached.flags; } + + inline core::bitflag getFlags() const { return cached.flags; } + }; struct SShaderGroupHandle @@ -153,7 +155,7 @@ class IGPURayTracingPipeline : public IGPUPipeline getCreationFlags() const { return m_flags; } + inline core::bitflag getCreationFlags() const { return getCachedCreationParams().flags; } // Vulkan: const VkPipeline* virtual const void* getNativeHandle() const = 0; @@ -170,13 +172,11 @@ class IGPURayTracingPipeline : public IGPUPipeline(params.layout->getOriginDevice()), params.layout, params.cached), - m_flags(params.flags) + IGPURayTracingPipeline(const SCreationParams& params) : IGPUPipeline(core::smart_refctd_ptr(params.layout->getOriginDevice()), params.layout, params.cached) {} virtual ~IGPURayTracingPipeline() = default; - const core::bitflag m_flags; }; } diff --git a/include/nbl/video/ILogicalDevice.h b/include/nbl/video/ILogicalDevice.h index 6298afeb27..180342e2d4 100644 --- a/include/nbl/video/ILogicalDevice.h +++ b/include/nbl/video/ILogicalDevice.h @@ -1258,7 +1258,7 @@ class NBL_API2 ILogicalDevice : public core::IReferenceCounted, public IDeviceMe } } // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkComputePipelineCreateInfo.html#VUID-VkComputePipelineCreateInfo-flags-07985 - else if (ci.basePipelineIndex < -1 || ci.basePipelineIndex >= i || ci.basePipelineIndex >= 0 && !params[ci.basePipelineIndex].flags.hasFlags(AllowDerivativesFlag)) + else if (ci.basePipelineIndex < -1 || ci.basePipelineIndex >= i || ci.basePipelineIndex >= 0 && !params[ci.basePipelineIndex].getFlags().hasFlags(AllowDerivativesFlag)) { NBL_LOG_ERROR("Invalid basePipeline was specified (params[%d])", i); return {}; diff --git a/include/nbl/video/asset_traits.h b/include/nbl/video/asset_traits.h index faf5322798..c4a6c25ca5 100644 --- a/include/nbl/video/asset_traits.h +++ b/include/nbl/video/asset_traits.h @@ -21,6 +21,8 @@ #include "nbl/video/IGPUAccelerationStructure.h" #include "nbl/asset/ICPUPolygonGeometry.h" #include "nbl/video/IGPUPolygonGeometry.h" +#include "nbl/asset/ICPURayTracingPipeline.h" +#include "nbl/video/IGPURayTracingPipeline.h" namespace nbl::video @@ -244,6 +246,20 @@ struct asset_traits }; +template<> +struct asset_traits +{ + // the asset type + using asset_t = asset::ICPURayTracingPipeline; + // Depends on shader and layout + constexpr static inline bool HasChildren = true; + // the video type + using video_t = IGPURayTracingPipeline; + // lookup type + using lookup_t = const video_t*; +}; + + /* TODO template<> struct asset_traits; diff --git a/include/nbl/video/utilities/CAssetConverter.h b/include/nbl/video/utilities/CAssetConverter.h index 3f0225a78e..a360e3b0f5 100644 --- a/include/nbl/video/utilities/CAssetConverter.h +++ b/include/nbl/video/utilities/CAssetConverter.h @@ -48,6 +48,7 @@ class CAssetConverter : public core::IReferenceCounted asset::ICPUPipelineLayout, asset::ICPUPipelineCache, asset::ICPUComputePipeline, + asset::ICPURayTracingPipeline, asset::ICPURenderpass, asset::ICPUGraphicsPipeline, asset::ICPUDescriptorSet, @@ -690,6 +691,7 @@ class CAssetConverter : public core::IReferenceCounted bool operator()(lookup_t); bool operator()(lookup_t); bool operator()(lookup_t); + bool operator()(lookup_t); bool operator()(lookup_t); bool operator()(lookup_t); bool operator()(lookup_t); diff --git a/src/nbl/video/CVulkanLogicalDevice.cpp b/src/nbl/video/CVulkanLogicalDevice.cpp index 86eaa4fd51..e1c5d89da8 100644 --- a/src/nbl/video/CVulkanLogicalDevice.cpp +++ b/src/nbl/video/CVulkanLogicalDevice.cpp @@ -1132,7 +1132,7 @@ template void initPipelineCreateInfo(VkPipelineCreateInfo_t* vk_info, const SCreationParams& info) { // the new flags type (64bit) is only available with maintenance5 - vk_info->flags = static_cast(info.flags.value); + vk_info->flags = static_cast(info.getFlags().value); vk_info->layout = static_cast(info.layout)->getInternalObject(); if (info.isDerivative()) { diff --git a/src/nbl/video/ILogicalDevice.cpp b/src/nbl/video/ILogicalDevice.cpp index 3f823d0a7d..221ed7844d 100644 --- a/src/nbl/video/ILogicalDevice.cpp +++ b/src/nbl/video/ILogicalDevice.cpp @@ -1025,8 +1025,8 @@ bool ILogicalDevice::createRayTracingPipelines(IGPUPipelineCache* const pipeline for (const auto& param : params) { - const bool skipAABBs = bool(param.flags & IGPURayTracingPipeline::SCreationParams::FLAGS::SKIP_AABBS); - const bool skipBuiltin = bool(param.flags & IGPURayTracingPipeline::SCreationParams::FLAGS::SKIP_BUILT_IN_PRIMITIVES); + const bool skipAABBs = bool(param.getFlags() & IGPURayTracingPipeline::SCreationParams::FLAGS::SKIP_AABBS); + const bool skipBuiltin = bool(param.getFlags() & IGPURayTracingPipeline::SCreationParams::FLAGS::SKIP_BUILT_IN_PRIMITIVES); if (!features.rayTracingPipeline) { diff --git a/src/nbl/video/utilities/CAssetConverter.cpp b/src/nbl/video/utilities/CAssetConverter.cpp index fed2d68cf0..a1c72905af 100644 --- a/src/nbl/video/utilities/CAssetConverter.cpp +++ b/src/nbl/video/utilities/CAssetConverter.cpp @@ -543,6 +543,31 @@ class AssetVisitor : public CRTP return false; return true; } + inline bool impl(const instance_t& instance, const CAssetConverter::patch_t& userPatch) + { + const auto* asset = instance.asset; + const auto* layout = asset->getLayout(); + if (!layout || !descend(layout,{layout})) + return false; + using stage_t = hlsl::ShaderStage; + for (stage_t stage : {hlsl::ShaderStage::ESS_RAYGEN, hlsl::ShaderStage::ESS_MISS, hlsl::ShaderStage::ESS_ANY_HIT, hlsl::ShaderStage::ESS_CLOSEST_HIT, hlsl::ShaderStage::ESS_INTERSECTION, hlsl::ShaderStage::ESS_CALLABLE}) + { + const auto& specInfos = asset->getSpecInfos(stage); + for (auto specInfo_i = 0; specInfo_i < specInfos.size(); specInfo_i++) + { + const auto& specInfo = specInfos[specInfo_i]; + const auto* shader = specInfo.shader.get(); + if (!shader) + { + if (stage == stage_t::ESS_RAYGEN) return false; + CRTP::template nullOptional(); + continue; + } + if (!descend(shader,{shader}, specInfo, stage, specInfo_i)) return false; + } + } + return true; + } inline bool impl(const instance_t& instance, const CAssetConverter::patch_t& userPatch) { const auto* asset = instance.asset; @@ -1104,6 +1129,11 @@ class HashVisit : public CAssetConverter::CHashCache::hash_impl_base assert(hlsl::bitCount(stage) == 1); hasher << stage; hasher << arg0.requiredSubgroupSize; + if constexpr (std::is_same_v) + { + const auto groupIndex = std::get<2>(argTuple); + hasher << groupIndex; + } if (!arg0.entries.empty()) { for (const auto& specConstant : arg0.entries) @@ -1370,6 +1400,25 @@ bool CAssetConverter::CHashCache::hash_impl::operator()(lookup_t lookup) +{ + const auto* asset = lookup.asset; + // + hasher << asset->getMissGroupCount(); + hasher << asset->getHitGroupCount(); + hasher << asset->getCallableGroupCount(); + AssetVisitor> visitor = { + *this, + {asset,static_cast(patchOverride)->uniqueCopyGroupID}, + *lookup.patch + }; + if (!visitor()) + return false; + const auto& params = asset->getCachedCreationParams(); + hasher << params.maxRecursionDepth; + hasher << params.dynamicStackSize; + return true; +} bool CAssetConverter::CHashCache::hash_impl::operator()(lookup_t lookup) { const auto* asset = lookup.asset; @@ -1688,6 +1737,7 @@ void CAssetConverter::CHashCache::eraseStale(const IPatchOverride* patchOverride rehash.operator()(); rehash.operator()(); rehash.operator()(); + rehash.template operator()(); // graphics pipeline needs a renderpass rehash.template operator()(); rehash.template operator()(); @@ -2041,103 +2091,185 @@ class GetDependantVisit : public GetDependantVisitBase) + if constexpr (std::is_same_v) { outInfo.info.image.imageLayout = std::get<0>(argTuple); - if (type==IDescriptor::E_TYPE::ET_COMBINED_IMAGE_SAMPLER) + if (type == IDescriptor::E_TYPE::ET_COMBINED_IMAGE_SAMPLER) { assert(lastCombinedSampler); outInfo.info.combinedImageSampler.sampler = smart_refctd_ptr(lastCombinedSampler); - lastCombinedSampler = nullptr; // for debuggability + lastCombinedSampler = nullptr; } } outInfo.desc = std::move(depObj); return true; - } -}; -template<> -class GetDependantVisit : public GetDependantVisitBase + } + }; + template<> + class GetDependantVisit : public GetDependantVisitBase + { + public: + bool finalize() + { + if (!creationParams.indexing) + return false; + creationParams.jointWeightViews = jointWeightViews; + creationParams.auxAttributeViews = auxAttributeViews; + return true; + } + + IGPUPolygonGeometry::SCreationParams creationParams = {}; + // has to be public because of aggregate init, but its only for internal usage! + core::vector jointWeightViews = {}; + core::vector auxAttributeViews = {}; + + protected: + bool descend_impl( + const instance_t& user, const CAssetConverter::patch_t& userPatch, + const instance_t& dep, const CAssetConverter::patch_t& soloPatch, + const EPolygonGeometryViewType type, const uint32_t index + ) + { + auto depObj = getDependant(dep,soloPatch); + if (!depObj) + return false; + const auto* asset = user.asset; + switch (type) + { + case EPolygonGeometryViewType::Position: + // obligatory attribute, handle basic setup here too + creationParams.indexing = asset->getIndexingCallback(); + creationParams.aabb = asset->getAABBStorage(); + creationParams.jointCount = asset->getJointCount(); + creationParams.positionView = getView(asset->getPositionView(),std::move(depObj)); + break; + case EPolygonGeometryViewType::Index: + creationParams.indexView = getView(asset->getIndexView(),std::move(depObj)); + break; + case EPolygonGeometryViewType::Normal: + creationParams.normalView = getView(asset->getNormalView(),std::move(depObj)); + break; + case EPolygonGeometryViewType::JointOBB: + creationParams.jointOBBView = getView(*asset->getJointOBBView(),std::move(depObj)); + break; + case EPolygonGeometryViewType::JointIndices: + jointWeightViews.resize(index+1); + jointWeightViews[index].indices = getView(asset->getJointWeightViews()[index].indices,std::move(depObj)); + break; + case EPolygonGeometryViewType::JointWeights: + jointWeightViews.resize(index+1); + jointWeightViews[index].weights = getView(asset->getJointWeightViews()[index].weights,std::move(depObj)); + break; + case EPolygonGeometryViewType::Aux: + auxAttributeViews.push_back(getView(asset->getAuxAttributeViews()[index],std::move(depObj))); + break; + default: + return false; + } + // abuse this pointer to signal invalid state + return creationParams.indexing; + } + + private: + IGPUPolygonGeometry::SDataView getView(const ICPUPolygonGeometry::SDataView& orig, core::smart_refctd_ptr&& buff) + { + IGPUPolygonGeometry::SDataView retval = { + .composed = orig.composed, + .src = { + .offset = orig.src.offset, + .size = orig.src.actualSize(), + .buffer = std::move(buff) + } + }; + if (orig && !retval) + creationParams.indexing = nullptr; + return retval; + } + }; + template<> +class GetDependantVisit : public GetDependantVisitBase { - public: - bool finalize() - { - if (!creationParams.indexing) - return false; - creationParams.jointWeightViews = jointWeightViews; - creationParams.auxAttributeViews = auxAttributeViews; - return true; - } +public: - IGPUPolygonGeometry::SCreationParams creationParams = {}; - // has to be public because of aggregate init, but its only for internal usage! - core::vector jointWeightViews = {}; - core::vector auxAttributeViews = {}; - - protected: - bool descend_impl( - const instance_t& user, const CAssetConverter::patch_t& userPatch, - const instance_t& dep, const CAssetConverter::patch_t& soloPatch, - const EPolygonGeometryViewType type, const uint32_t index - ) - { - auto depObj = getDependant(dep,soloPatch); - if (!depObj) - return false; - const auto* asset = user.asset; - switch (type) - { - case EPolygonGeometryViewType::Position: - // obligatory attribute, handle basic setup here too - creationParams.indexing = asset->getIndexingCallback(); - creationParams.aabb = asset->getAABBStorage(); - creationParams.jointCount = asset->getJointCount(); - creationParams.positionView = getView(asset->getPositionView(),std::move(depObj)); - break; - case EPolygonGeometryViewType::Index: - creationParams.indexView = getView(asset->getIndexView(),std::move(depObj)); - break; - case EPolygonGeometryViewType::Normal: - creationParams.normalView = getView(asset->getNormalView(),std::move(depObj)); - break; - case EPolygonGeometryViewType::JointOBB: - creationParams.jointOBBView = getView(*asset->getJointOBBView(),std::move(depObj)); - break; - case EPolygonGeometryViewType::JointIndices: - jointWeightViews.resize(index+1); - jointWeightViews[index].indices = getView(asset->getJointWeightViews()[index].indices,std::move(depObj)); - break; - case EPolygonGeometryViewType::JointWeights: - jointWeightViews.resize(index+1); - jointWeightViews[index].weights = getView(asset->getJointWeightViews()[index].weights,std::move(depObj)); - break; - case EPolygonGeometryViewType::Aux: - auxAttributeViews.push_back(getView(asset->getAuxAttributeViews()[index],std::move(depObj))); - break; - default: - return false; - } - // abuse this pointer to signal invalid state - return creationParams.indexing; - } + inline void allocateShaders(size_t missCount, size_t hitGroupCount, size_t callableGroupCount) + { + misses.resize(missCount); + hitGroups.anyHits.resize(hitGroupCount); + hitGroups.closestHits.resize(hitGroupCount); + hitGroups.intersections.resize(hitGroupCount); + callables.resize(callableGroupCount); + } - private: - IGPUPolygonGeometry::SDataView getView(const ICPUPolygonGeometry::SDataView& orig, core::smart_refctd_ptr&& buff) + inline core::vector* getSpecInfoVector(const hlsl::ShaderStage stage) + { + switch (stage) + { + // raygen is not stored as vector so we can't return it here. Use getSpecInfo + case hlsl::ShaderStage::ESS_MISS: + return &misses; + case hlsl::ShaderStage::ESS_ANY_HIT: + return &hitGroups.anyHits; + case hlsl::ShaderStage::ESS_CLOSEST_HIT: + return &hitGroups.closestHits; + case hlsl::ShaderStage::ESS_INTERSECTION: + return &hitGroups.intersections; + case hlsl::ShaderStage::ESS_CALLABLE: + return &callables; + } + return nullptr; + } + + // ok to do non owning since some cache owns anyway + IGPUPipelineLayout* layout = nullptr; + ICPUPipelineBase::SShaderSpecInfo raygen; + core::vector misses; + ICPURayTracingPipeline::SHitGroupSpecInfos hitGroups; + core::vector callables; + +protected: + bool descend_impl( + const instance_t& user, const CAssetConverter::patch_t& userPatch, + const instance_t& dep, const CAssetConverter::patch_t& soloPatch + ) + { + auto depObj = getDependant(dep, soloPatch); + if (!depObj) + return false; + layout = depObj.get(); + return true; + } + bool descend_impl( + const instance_t& user, const CAssetConverter::patch_t& userPatch, + const instance_t& dep, const CAssetConverter::patch_t& soloPatch, const ICPUPipelineBase::SShaderSpecInfo& inSpecInfo, hlsl::ShaderStage stage, uint32_t groupIndex + ) + { + auto depObj = getDependant(dep, soloPatch); + if (!depObj) + return false; + if (stage == hlsl::ShaderStage::ESS_RAYGEN) { - IGPUPolygonGeometry::SDataView retval = { - .composed = orig.composed, - .src = { - .offset = orig.src.offset, - .size = orig.src.actualSize(), - .buffer = std::move(buff) - } + assert(groupIndex == 0); + raygen = ICPUPipelineBase::SShaderSpecInfo{ + .shader = depObj, + .entryPoint = inSpecInfo.entryPoint, + .requiredSubgroupSize = inSpecInfo.requiredSubgroupSize, + .entries = inSpecInfo.entries, }; - if (orig && !retval) - creationParams.indexing = nullptr; - return retval; + } else + { + auto& shaderGroups = *getSpecInfoVector(stage); + assert(groupIndex < shaderGroups.size()); + shaderGroups[groupIndex] = ICPUPipelineBase::SShaderSpecInfo{ + .shader = depObj, + .entryPoint = inSpecInfo.entryPoint, + .requiredSubgroupSize = inSpecInfo.requiredSubgroupSize, + .entries = inSpecInfo.entries, + }; } + return true; + } }; - // Needed both for reservation and conversion class MetaDeviceMemoryAllocator final { @@ -2774,6 +2906,9 @@ auto CAssetConverter::reserve(const SInputs& inputs) -> SReserveResult case ICPUGraphicsPipeline::AssetType: visit.template operator()(entry); break; + case ICPURayTracingPipeline::AssetType: + visit.template operator()(entry); + break; case ICPUDescriptorSet::AssetType: visit.template operator()(entry); break; @@ -3474,6 +3609,102 @@ auto CAssetConverter::reserve(const SInputs& inputs) -> SReserveResult } } } + if constexpr (std::is_same_v) + { + for (auto& entry : conversionRequests.contentHashToCanonical) + { + const ICPURayTracingPipeline* asset = entry.second.canonicalAsset; + // there is no patching possible for this asset + for (auto i=0ull; i> visitor = { + {visitBase}, + {asset,uniqueCopyGroupID}, + {} + }; + visitor.allocateShaders( + asset->getMissGroupCount(), + asset->getHitGroupCount(), + asset->getCallableGroupCount()); + if (!visitor()) + continue; + // ILogicalDevice::createComputePipelines is rather aggressive on the spec constant validation, so we create one pipeline at a time + core::smart_refctd_ptr ppln; + { + // no derivatives, special flags, etc. + IGPURayTracingPipeline::SCreationParams params = {}; + using SShaderEntryMap = IGPUPipelineBase::SShaderEntryMap; + using stage_t = hlsl::ShaderStage; + using GPUShaderSpecInfo = IGPUPipelineBase::SShaderSpecInfo; + + params.layout = visitor.layout; + + SShaderEntryMap raygenEntryMap; + params.shaderGroups.raygen = GPUShaderSpecInfo::create(visitor.raygen, &raygenEntryMap); + + struct GPUSpecEntryVec + { + core::vector entryMaps; + core::vector specs; + + explicit GPUSpecEntryVec(std::span cpuSpecs) + : entryMaps(cpuSpecs.size()), specs(cpuSpecs.size()) + { + for (auto spec_i = 0u; spec_i < cpuSpecs.size(); spec_i++) + specs[spec_i] = GPUShaderSpecInfo::create(cpuSpecs[spec_i], &entryMaps[spec_i]); + } + }; + + GPUSpecEntryVec missSpecEntry(visitor.misses); + params.shaderGroups.misses = missSpecEntry.specs; + + GPUSpecEntryVec callableSpecEntry(visitor.callables); + params.shaderGroups.callables = callableSpecEntry.specs; + + core::vector hitGroups(visitor.hitGroups.closestHits.size()); + core::vector closestHitEntryMaps(visitor.hitGroups.closestHits.size()); + core::vector anyHitEntryMaps(visitor.hitGroups.anyHits.size()); + core::vector intersectionEntryMaps(visitor.hitGroups.intersections.size()); + assert(anyHitEntryMaps.size() == closestHitEntryMaps.size()); + assert(anyHitEntryMaps.size() == intersectionEntryMaps.size()); + for (auto hitGroup_i = 0u ; hitGroup_i < hitGroups.size(); hitGroup_i++) + { + hitGroups[hitGroup_i].closestHit = GPUShaderSpecInfo::create(visitor.hitGroups.closestHits[hitGroup_i], &closestHitEntryMaps[hitGroup_i]); + hitGroups[hitGroup_i].anyHit = GPUShaderSpecInfo::create(visitor.hitGroups.anyHits[hitGroup_i], &anyHitEntryMaps[hitGroup_i]); + hitGroups[hitGroup_i].intersection = GPUShaderSpecInfo::create(visitor.hitGroups.intersections[hitGroup_i], &intersectionEntryMaps[hitGroup_i]); + } + params.shaderGroups.hits = hitGroups; + params.cached = asset->getCachedCreationParams(); + + using RayTracingFlags = IGPURayTracingPipeline::SCreationParams::FLAGS; + const auto isNullSpecInfo = [](const ICPUPipelineBase::SShaderSpecInfo& specInfo) + { + return specInfo.shader.get() == nullptr; + }; + const auto noNullMiss = std::none_of( + visitor.misses.begin(), + visitor.misses.end(), + isNullSpecInfo); + if (noNullMiss) params.cached.flags |= RayTracingFlags::NO_NULL_MISS_SHADERS; + const auto noNullClosestHit = std::none_of( + visitor.hitGroups.closestHits.begin(), + visitor.hitGroups.closestHits.end(), + isNullSpecInfo); + if (noNullClosestHit) params.cached.flags |= RayTracingFlags::NO_NULL_CLOSEST_HIT_SHADERS; + const auto noNullAnyHit = std::none_of( + visitor.hitGroups.anyHits.begin(), + visitor.hitGroups.anyHits.end(), + isNullSpecInfo); + if (noNullAnyHit) params.cached.flags |= RayTracingFlags::NO_NULL_ANY_HIT_SHADERS; + + device->createRayTracingPipelines(inputs.pipelineCache, {¶ms, 1}, &ppln); + conversionRequests.assign(entry.first, entry.second.firstCopyIx, i, std::move(ppln)); + } + } + } + } if constexpr (std::is_same_v) { // Why we're not grouping multiple descriptor sets into few pools and doing 1 pool per descriptor set. @@ -3675,6 +3906,7 @@ auto CAssetConverter::reserve(const SInputs& inputs) -> SReserveResult dedupCreateProp.template operator()(); dedupCreateProp.template operator()(); dedupCreateProp.template operator()(); + dedupCreateProp.template operator()(); dedupCreateProp.template operator()(); dedupCreateProp.template operator()(); dedupCreateProp.template operator()(); @@ -3758,6 +3990,7 @@ auto CAssetConverter::reserve(const SInputs& inputs) -> SReserveResult pruneStaging.template operator()(); pruneStaging.template operator()(); pruneStaging.template operator()(); + pruneStaging.template operator()(); pruneStaging.template operator()(); pruneStaging.template operator()(); pruneStaging.template operator()();