diff --git a/examples_tests b/examples_tests index 301fb402e6..779ce1fe46 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit 301fb402e6d0d3c204b1da67e920283d6f9abca5 +Subproject commit 779ce1fe4630135f82c1bcee1c57e3a37c3fa5cf diff --git a/include/nbl/asset/IAsset.h b/include/nbl/asset/IAsset.h index 7c6a33193d..b7142713bf 100644 --- a/include/nbl/asset/IAsset.h +++ b/include/nbl/asset/IAsset.h @@ -194,4 +194,72 @@ concept Asset = std::is_base_of_v; } +namespace nbl::system::impl +{ +template<> +struct to_string_helper +{ + private: + using enum_t = asset::IAsset::E_TYPE; + + public: + static inline std::string __call(const enum_t value) + { + switch (value) + { + case enum_t::ET_BUFFER: + return "ICPUBuffer"; + case enum_t::ET_BUFFER_VIEW: + return "ICPUBufferView"; + case enum_t::ET_SAMPLER: + return "ICPUSampler"; + case enum_t::ET_IMAGE: + return "ICPUImage"; + case enum_t::ET_IMAGE_VIEW: + return "ICPUImageView"; + case enum_t::ET_DESCRIPTOR_SET: + return "ICPUDescriptorSet"; + case enum_t::ET_DESCRIPTOR_SET_LAYOUT: + return "ICPUDescriptorSetLayout"; + case enum_t::ET_SKELETON: + return "ICPUSkeleton"; + case enum_t::ET_ANIMATION_LIBRARY: + return "ICPUAnimationLibrary"; + case enum_t::ET_PIPELINE_LAYOUT: + return "ICPUPipelineLayout"; + case enum_t::ET_SHADER: + return "IShader"; + case enum_t::ET_GEOMETRY: + return "IGeometry"; + case enum_t::ET_RENDERPASS: + return "ICPURenderpass"; + case enum_t::ET_FRAMEBUFFER: + return "ICPUFramebuffer"; + case enum_t::ET_GRAPHICS_PIPELINE: + return "ICPUGraphicsPipeline"; + case enum_t::ET_BOTOM_LEVEL_ACCELERATION_STRUCTURE: + return "ICPUBottomLevelAccelerationStructure"; + case enum_t::ET_TOP_LEVEL_ACCELERATION_STRUCTURE: + return "ICPUTopLevelAccelerationStructure"; + case enum_t::ET_GEOMETRY_COLLECTION: + return "ICPUGeometryCollection"; + case enum_t::ET_MORPH_TARGETS: + return "ICPUMorphTargets"; + case enum_t::ET_COMPUTE_PIPELINE: + return "ICPUComputePipeline"; + case enum_t::ET_PIPELINE_CACHE: + return "ICPUPipelineCache"; + case enum_t::ET_SCENE: + return "ICPUScene"; + case enum_t::ET_RAYTRACING_PIPELINE: + return "ICPURayTracingPipeline"; + case enum_t::ET_IMPLEMENTATION_SPECIFIC_METADATA: + return ""; + default: + break; + } + return ""; + } +}; +} #endif diff --git a/include/nbl/asset/IBuffer.h b/include/nbl/asset/IBuffer.h index 6f8c1bb35b..3a7cbb5983 100644 --- a/include/nbl/asset/IBuffer.h +++ b/include/nbl/asset/IBuffer.h @@ -120,6 +120,15 @@ struct SBufferRange inline bool operator!=(const SBufferRange& rhs) const { return !operator==(rhs); } }; +template +struct SStridedRange +{ + inline operator bool() const {return range.isValid();} + + SBufferRange range = {}; + uint32_t stride = 0; +}; + } namespace std diff --git a/include/nbl/asset/ICPUBuffer.h b/include/nbl/asset/ICPUBuffer.h index 2d10c2907b..26f45d4ced 100644 --- a/include/nbl/asset/ICPUBuffer.h +++ b/include/nbl/asset/ICPUBuffer.h @@ -119,30 +119,32 @@ class ICPUBuffer final : public asset::IBuffer, public IPreHashed return (m_alignment > 0 && !(m_alignment & (m_alignment - 1))); } -protected: - inline void discardContent_impl() override - { - if (m_data) - m_mem_resource->deallocate(m_data, m_creationParams.size, m_alignment); - m_data = nullptr; - m_mem_resource = nullptr; - m_creationParams.size = 0ull; - } - -private: - ICPUBuffer(SCreationParams&& params) : - asset::IBuffer({ params.size, EUF_TRANSFER_DST_BIT }), m_data(params.data), - m_mem_resource(params.memoryResource), m_alignment(params.alignment) {} - - ~ICPUBuffer() override { - discardContent_impl(); - } - - inline void visitDependents_impl(std::function visit) const override {} - - void* m_data; - core::smart_refctd_ptr m_mem_resource; - size_t m_alignment; + protected: + inline void discardContent_impl() override + { + if (m_data) + m_mem_resource->deallocate(m_data, m_creationParams.size, m_alignment); + m_data = nullptr; + m_mem_resource = nullptr; + m_creationParams.size = 0ull; + } + + private: + // TODO: we should remove the addition of TRANSFER_DST_BIT because its the asset converter patcher that handles that + // But we need LLVM-pipe CI first so I don't have to test 70 examples by hand + inline ICPUBuffer(SCreationParams&& params) : asset::IBuffer({params.size,params.usage|EUF_TRANSFER_DST_BIT}), + m_data(params.data), m_mem_resource(params.memoryResource), m_alignment(params.alignment) {} + + inline ~ICPUBuffer() override + { + discardContent_impl(); + } + + inline void visitDependents_impl(std::function visit) const override {} + + void* m_data; + core::smart_refctd_ptr m_mem_resource; + size_t m_alignment; }; } // end namespace nbl::asset diff --git a/include/nbl/asset/ICPUGeometryCollection.h b/include/nbl/asset/ICPUGeometryCollection.h index 6202b4de12..d231f1df00 100644 --- a/include/nbl/asset/ICPUGeometryCollection.h +++ b/include/nbl/asset/ICPUGeometryCollection.h @@ -24,11 +24,15 @@ class NBL_API2 ICPUGeometryCollection : public IAsset, public IGeometryCollectio inline E_TYPE getAssetType() const override {return AssetType;} // - inline bool valid() const //override + inline bool valid() const override { for (const auto& ref : m_geometries) - if (!ref.geometry->valid()) - return false; + { + if (!ref.operator bool() || !ref.geometry->valid()) + return false; + if (ref.jointRedirectView.src && ref.jointRedirectView.composed.getRange>().maxVx[0]>=getJointCount()) + return false; + } return true; } @@ -61,6 +65,8 @@ class NBL_API2 ICPUGeometryCollection : public IAsset, public IGeometryCollectio return false; } + // + inline const core::vector& getGeometries() const {return base_t::getGeometries();} // inline core::vector* getGeometries() { diff --git a/include/nbl/asset/ICPURayTracingPipeline.h b/include/nbl/asset/ICPURayTracingPipeline.h index 01cf4a3f28..e882b4b4cb 100644 --- a/include/nbl/asset/ICPURayTracingPipeline.h +++ b/include/nbl/asset/ICPURayTracingPipeline.h @@ -13,9 +13,9 @@ namespace nbl::asset { //! CPU Version of RayTracing Pipeline -class ICPURayTracingPipeline final : public ICPUPipeline> +class ICPURayTracingPipeline final : public ICPUPipeline> { - using pipeline_base_t = IRayTracingPipeline; + using pipeline_base_t = IRayTracingPipeline; using base_t = ICPUPipeline; public: diff --git a/include/nbl/asset/ICPUScene.h b/include/nbl/asset/ICPUScene.h index 4ea7a485b4..6205e651fa 100644 --- a/include/nbl/asset/ICPUScene.h +++ b/include/nbl/asset/ICPUScene.h @@ -5,49 +5,237 @@ #define _NBL_ASSET_I_CPU_SCENE_H_INCLUDED_ +#include "nbl/core/containers/CMemoryPool.h" + #include "nbl/asset/IScene.h" -// TODO: change to true IR later -#include "nbl/asset/material_compiler3/CFrontendIR.h" +#include "nbl/asset/ICPUMorphTargets.h" +#include "nbl/asset/material_compiler3/CTrueIR.h" namespace nbl::asset { // -class NBL_API2 ICPUScene : public IAsset, public IScene +class ICPUScene final : public IAsset, public IScene { using base_t = IScene; + using material_table_allocator_t = core::GeneralpurposeAddressAllocatorST; +// using material_table_t = core::CMemoryPool<,core::allocator,false>; public: - inline ICPUScene() = default; + using material_pool_t = material_compiler3::CTrueIR; + // + static inline core::smart_refctd_ptr create(core::smart_refctd_ptr&& ir, const uint8_t maxMorphTargetGeometryCountLog2=16) + { + return core::smart_refctd_ptr(new ICPUScene(std::move(ir),maxMorphTargetGeometryCountLog2),core::dont_grab); + } constexpr static inline auto AssetType = ET_SCENE; inline E_TYPE getAssetType() const override { return AssetType; } inline bool valid() const override { + if (!m_instances) + return false; + auto materialTableOffsetIt = m_instances.materials.begin(); + for (const auto& targets : m_instances.morphTargets) + { + const auto materialTableOffset = *(materialTableOffsetIt++); + // TODO: check if `materialTableOffset` can be contained in `materialTable` + if (!targets || targets->valid()) + return false; + const auto geoCount = targets->getGeometryExclusiveCount({}); + // TODO: check if `materialTableOffset+geoCount` can be contained in `materialTable` + // TODO: check every material is either null or belongs in `m_materialPool` + } return true; } inline core::smart_refctd_ptr clone(uint32_t _depth=~0u) const { const auto nextDepth = _depth ? (_depth-1):0; - auto retval = core::smart_refctd_ptr(); + // TODO: copy the material_table state/contents! + // the True IR isn't an asset (yet), but it probably should be? + auto retval = create(core::smart_refctd_ptr(m_materialPool),m_maxMorphTargetGeometryCountLog2); + if (nextDepth) + { + retval->m_instances.morphTargets.resize(retval->m_instances.size()); + for (auto& targets : m_instances.morphTargets) + retval->m_instances.morphTargets.push_back(core::move_and_static_cast(targets->clone(nextDepth))); + retval->m_envLightTexs.reserve(m_envLightTexs.size()); + for (const auto& tex : m_envLightTexs) + retval->m_envLightTexs.push_back(core::move_and_static_cast(tex->clone(nextDepth))); + } + else + { + retval->m_instances = m_instances; + retval->m_envLightTexs = m_envLightTexs; + } + retval->m_instances.materials = m_instances.materials; + retval->m_instances.initialTransforms = m_instances.initialTransforms; + retval->m_envLightTypes = m_envLightTypes; return retval; } + // TODO: change to CRootNode + using material_t = material_pool_t::TypedHandle; + inline material_compiler3::CTrueIR* getMaterialPool() {return m_materialPool.get();} + inline const material_compiler3::CTrueIR* getMaterialPool() const {return m_materialPool.get();} + + // + using material_table_offset_t = uint32_t; + constexpr static inline material_table_offset_t InvalidMaterialTable = ~0u; + material_table_offset_t allocateMaterialTable(const ICPUMorphTargets* targets) + { + if (!targets) + return material_table_allocator_t::invalid_address; + return allocateMaterialTable(targets->getGeometryExclusiveCount({})); + } + material_table_offset_t allocateMaterialTable(const uint32_t count) + { + // TODO: implement + return material_table_allocator_t::invalid_address; + } + void deallocateMaterialTable(const material_table_offset_t offset, const ICPUMorphTargets* targets) + { + return deallocateMaterialTable(offset,targets->getGeometryExclusiveCount({})); + } + void deallocateMaterialTable(const material_table_offset_t offset, const uint32_t count) + { + // TODO: implement + } + + // TODO: get material table pointer + + // TODO: wrap up in some ECS storage class + struct SInstanceStorage final + { + public: + inline SInstanceStorage(const size_t size=1) : morphTargets(size), materials(size), initialTransforms(size) {} + + inline void clearInitialTransforms() {initialTransforms.clear();} + + inline operator bool() const + { + if (morphTargets.size()!=materials.size()) + return false; + if (initialTransforms.empty()) + return true; + return morphTargets.size()==initialTransforms.size(); + } + + inline void reserve(const size_t newSize) + { + morphTargets.reserve(newSize); + materials.reserve(newSize); + if (!initialTransforms.empty()) + initialTransforms.reserve(newSize); + } + + inline void resize(const size_t newSize) + { + morphTargets.resize(newSize); + materials.resize(newSize,InvalidMaterialTable); + if (!initialTransforms.empty()) + initialTransforms.resize(newSize,ICPUGeometryCollection::SGeometryReference{}.transform); + } + + inline void erase(const size_t first, const size_t last) + { + morphTargets.erase(morphTargets.begin()+first,morphTargets.begin()+last); + materials.erase(materials.begin()+first, materials.begin()+last); + initialTransforms.erase(initialTransforms.begin()+first,initialTransforms.begin()+last); + } + inline void erase(const size_t ix) {return erase(ix,ix+1);} + + inline size_t size() const {return morphTargets.size();} + + inline std::span> getMorphTargets() {return morphTargets;} + inline std::span> getMorphTargets() const {return morphTargets;} + + inline std::span getMaterialTables() {return materials;} + inline std::span getMaterialTables() const {return materials;} + + inline std::span getInitialTransforms() {return initialTransforms;} + inline std::span getInitialTransforms() const {return initialTransforms;} + + private: + friend class ICPUScene; + + core::vector> morphTargets; + // One material table per morph target, + // Within each morph target, one material per geometry + core::vector materials; + core::vector initialTransforms; + // TODO: animations (keyframed transforms, skeleton instance) + }; + + // + inline SInstanceStorage& getInstances() {return m_instances;} + inline const SInstanceStorage& getInstances() const {return m_instances;} + + enum class EEnvLightType : uint8_t + { + Cubemap, + SphereMap, // u=theta, v=phi with (0,0) being top right of image + OctahedralMap, + Count + }; + // + inline bool addEnvLight(const EEnvLightType type, core::smart_refctd_ptr&& tex) + { + if (!tex) + return false; + using view_e = IImageViewBase::E_TYPE; + switch (tex->getCreationParameters().viewType) + { + case view_e::ET_2D: [[fallthrough]]; + case view_e::ET_2D_ARRAY: + m_envLightTypes.push_back(type); + break; + case view_e::ET_CUBE_MAP: [[fallthrough]]; + case view_e::ET_CUBE_MAP_ARRAY: + if (type!=EEnvLightType::Cubemap) + return false; + m_envLightTypes.push_back(EEnvLightType::Cubemap); + break; + default: + return false; + } + m_envLightTexs.push_back(std::move(tex)); + return true; + } + // + inline std::span getEnviornmentLightTypes() const {return m_envLightTypes;} + inline std::span getEnvironmentLightTextures() const {return {&m_envLightTexs.data()->get(),m_envLightTexs.size()}; } + // TODO: add an erase_if and erase with begin/end iterators + inline void clearEnvLights() + { + m_envLightTexs.clear(); + m_envLightTypes.clear(); + } + + // + hlsl::float32_t3 m_ambientLight; + protected: + inline ICPUScene(core::smart_refctd_ptr&& materialPool, const uint32_t maxMorphTargetGeometryCountLog2) : + m_materialPool(std::move(materialPool)), m_maxMorphTargetGeometryCountLog2(maxMorphTargetGeometryCountLog2) {} // inline void visitDependents_impl(std::function visit) const override { + assert(false && "Unimplemented"); // we'd probalby be going over the: morph targets, image views, ... } - - // suggested contents: - // - morph target list - // - material table - // - instance list (morph target, keyframed transforms, material table indexings, FUTURE: reference skeleton) - // - area light list (OBB decompositions, material table indexings) - // - envlight data + // +// TODO material_table_t m_materialTable; + core::smart_refctd_ptr m_materialPool; + // + SInstanceStorage m_instances; + // + core::vector> m_envLightTexs; + core::vector m_envLightTypes; + // + const uint8_t m_maxMorphTargetGeometryCountLog2; }; } diff --git a/include/nbl/asset/IGeometryCollection.h b/include/nbl/asset/IGeometryCollection.h index 29c145c115..1fac15ec17 100644 --- a/include/nbl/asset/IGeometryCollection.h +++ b/include/nbl/asset/IGeometryCollection.h @@ -21,7 +21,7 @@ class NBL_API2 IGeometryCollection : public virtual core::IReferenceCounted inline const auto& getAABB() const {return m_aabb;} // - struct SGeometryReference + struct SGeometryReference final { inline operator bool() const { @@ -29,9 +29,9 @@ class NBL_API2 IGeometryCollection : public virtual core::IReferenceCounted return false; if (jointRedirectView.src) { - if (!jointRedirectView.isFormattedScalarInteger()) + if (!jointRedirectView.composed.isFormattedScalarInteger()) return false; - if (jointRedirectView.getElementCount()getJointCount()*2) + if (jointRedirectView.getElementCount()getJointCount()) return false; } else diff --git a/include/nbl/asset/IMorphTargets.h b/include/nbl/asset/IMorphTargets.h index 14265aa71a..6f208c6f73 100644 --- a/include/nbl/asset/IMorphTargets.h +++ b/include/nbl/asset/IMorphTargets.h @@ -19,6 +19,7 @@ class NBL_API2 IMorphTargets : public virtual core::IReferenceCounted public: struct index_t { + inline index_t() = default; explicit inline index_t(uint32_t _value) : value(_value) {} inline operator bool() const {return value!=(~0u);} @@ -30,6 +31,19 @@ class NBL_API2 IMorphTargets : public virtual core::IReferenceCounted { return static_cast(m_targets.size()); } + // + virtual inline uint32_t getGeometryExclusiveCount(index_t index) const + { + if (const auto targetCount=getTargetCount(); index.value>targetCount) + index.value = targetCount; + uint32_t retval = 0; + for (uint32_t i=0; igetGeometries().size(); + } + return retval; + } template requires std::is_floating_point_v struct SInterpolants @@ -56,7 +70,11 @@ class NBL_API2 IMorphTargets : public virtual core::IReferenceCounted { inline operator bool() const { - return geoCollection && (!jointRedirectView || jointRedirectView.composed.isFormattedScalarInteger()); + if (!geoCollection) + return false; + if (!jointRedirectView) + return true; + return jointRedirectView.composed.isFormattedScalarInteger() && jointRedirectView.getElementCount()>=geoCollection->getJointCount(); } core::smart_refctd_ptr geoCollection = {}; diff --git a/include/nbl/asset/IPipelineLayout.h b/include/nbl/asset/IPipelineLayout.h index 430c812dcb..0244aaaa06 100644 --- a/include/nbl/asset/IPipelineLayout.h +++ b/include/nbl/asset/IPipelineLayout.h @@ -89,6 +89,7 @@ template class IPipelineLayout { public: + using desc_layout_t = DescLayoutType; static inline constexpr uint32_t DESCRIPTOR_SET_COUNT = 4u; std::span getDescriptorSetLayouts() const diff --git a/include/nbl/asset/IRayTracingPipeline.h b/include/nbl/asset/IRayTracingPipeline.h index fe318d271a..23d2fac81f 100644 --- a/include/nbl/asset/IRayTracingPipeline.h +++ b/include/nbl/asset/IRayTracingPipeline.h @@ -13,49 +13,102 @@ namespace nbl::asset class IRayTracingPipelineBase : public virtual core::IReferenceCounted { - public: - #define base_flag(F) static_cast(IPipelineBase::FLAGS::F) - enum class CreationFlags : uint64_t - { - NONE = base_flag(NONE), - // there's a bit of a problem, as the ICPUCompute and Graphics pipelines don't care about flags, because the following 4 flags - 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), - // don't matter for ICPU Pipelines, we'd really need to have these separate from `base_flag` and use the `IRayTracingPipelineBase::CreationFlags` for the ICPU creation params 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, - ALLOW_MOTION = 1<<20, - }; - #undef base_flag - - struct SCachedCreationParams final - { - core::bitflag flags = CreationFlags::NONE; - uint32_t maxRecursionDepth : 6 = 0; - uint32_t dynamicStackSize : 1 = false; - }; + public: + #define base_flag(F) static_cast(IPipelineBase::FLAGS::F) + enum class CreationFlags : uint64_t + { + NONE = base_flag(NONE), + // there's a bit of a problem, as the ICPUCompute and Graphics pipelines don't care about flags, because the following 4 flags + 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), + // don't matter for ICPU Pipelines, we'd really need to have these separate from `base_flag` and use the `IRayTracingPipelineBase::CreationFlags` for the ICPU creation params 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, + ALLOW_MOTION = 1<<20, + }; + #undef base_flag + + struct SCachedCreationParams final + { + core::bitflag flags = CreationFlags::NONE; + uint32_t maxRecursionDepth : 6 = 0; + uint32_t dynamicStackSize : 1 = false; + }; }; -template +template class IRayTracingPipeline : public IPipeline, public IRayTracingPipelineBase { - public: + public: + struct SShaderBindingTable + { + inline bool valid(const core::bitflag flags) const + { + return valid(flags,[](const std::string_view, auto... args)->void{}); + } + template + inline bool valid(const core::bitflag flags, Callback&& cb) const + { + using create_flag_e = IRayTracingPipelineBase::CreationFlags; + // https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdTraceRaysKHR.html#VUID-vkCmdTraceRaysKHR-flags-03696 + // https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdTraceRaysKHR.html#VUID-vkCmdTraceRaysKHR-flags-03697 + // https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdTraceRaysKHR.html#VUID-vkCmdTraceRaysKHR-flags-03512 + const auto shouldHaveHitGroup = flags & (core::bitflag(create_flag_e::NO_NULL_ANY_HIT_SHADERS) | create_flag_e::NO_NULL_CLOSEST_HIT_SHADERS | create_flag_e::NO_NULL_INTERSECTION_SHADERS); + if (shouldHaveHitGroup && !hit.range.buffer) + { + cb("bound pipeline indicates that traceRays command should have hit group, but SRayTracingSBT::hit::range::buffer is null!"); + return false; + } + + // https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdTraceRaysKHR.html#VUID-vkCmdTraceRaysKHR-flags-03511 + const auto shouldHaveMissGroup = flags & create_flag_e::NO_NULL_MISS_SHADERS; + if (shouldHaveMissGroup && !miss.range.buffer) + { + cb("bound pipeline indicates that traceRays command should have miss group, but SRayTracingSBT::hit::range::buffer is null!"); + return false; + } + + auto invalidBufferRegion = [&cb](const SStridedRange& stRange, const char* groupName) -> bool + { + const auto& range = stRange.range; + const auto* const buffer = range.buffer.get(); + if (!buffer) + return false; + + if (!range.isValid()) + { + cb("%s buffer range is not valid!",groupName); + return false; + } + + return false; + }; + + if (invalidBufferRegion({.range=raygen},"Raygen Group")) return false; + if (invalidBufferRegion(miss,"Miss groups")) return false; + if (invalidBufferRegion(hit,"Hit groups")) return false; + if (invalidBufferRegion(callable,"Callable groups")) return false; + + return true; + } + + asset::SBufferRange raygen = {}; + asset::SStridedRange miss = {}, hit = {}, callable = {}; + }; - inline const SCachedCreationParams& getCachedCreationParams() const { return m_params; } + inline const SCachedCreationParams& getCachedCreationParams() const { return m_params; } - protected: - explicit IRayTracingPipeline(PipelineLayoutType* layout, const SCachedCreationParams& cachedParams) : - IPipeline(core::smart_refctd_ptr(layout)), - m_params(cachedParams) - {} + protected: + explicit inline IRayTracingPipeline(PipelineLayoutType* layout, const SCachedCreationParams& cachedParams) : + IPipeline(core::smart_refctd_ptr(layout)), m_params(cachedParams) {} - SCachedCreationParams m_params; + SCachedCreationParams m_params; }; diff --git a/include/nbl/asset/interchange/IAssetLoader.h b/include/nbl/asset/interchange/IAssetLoader.h index 3658f67026..09f842e659 100644 --- a/include/nbl/asset/interchange/IAssetLoader.h +++ b/include/nbl/asset/interchange/IAssetLoader.h @@ -87,24 +87,24 @@ class NBL_API2 IAssetLoader : public virtual core::IReferenceCounted enum E_LOADER_PARAMETER_FLAGS : uint64_t { - ELPF_NONE = 0, //!< default value, it doesn't do anything - /*deprecated*/ELPF_RIGHT_HANDED_MESHES = 0x1, //!< specifies that a mesh will be flipped in such a way that it'll look correctly in right-handed camera system - /*deprecated*/ELPF_DONT_COMPILE_GLSL = 0x2, //!< it states that GLSL won't be compiled to SPIR-V if it is loaded or generated - ELPF_LOAD_METADATA_ONLY = 0x4 //!< it forces the loader to not load the entire scene for performance in special cases to fetch metadata. + ELPF_NONE = 0, //!< default value, it doesn't do anything +//[[deprecated]] ELPF_RIGHT_HANDED_MESHES = 0x1, //!< specifies that a mesh will be flipped in such a way that it'll look correctly in right-handed camera system +//[[deprecated]] ELPF_DONT_COMPILE_GLSL = 0x2, //!< it states that GLSL won't be compiled to SPIR-V if it is loaded or generated + ELPF_LOAD_METADATA_ONLY = 0x4 //!< it forces the loader to not load the entire scene for performance in special cases to fetch metadata. }; struct SAssetLoadParams { - SAssetLoadParams(size_t _decryptionKeyLen = 0u, const uint8_t* _decryptionKey = nullptr, - E_CACHING_FLAGS _cacheFlags = ECF_CACHE_EVERYTHING,const E_LOADER_PARAMETER_FLAGS& _loaderFlags = ELPF_NONE, - system::logger_opt_ptr _logger = nullptr, const std::filesystem::path& cwd = "") : + inline SAssetLoadParams(const size_t _decryptionKeyLen = 0u, const uint8_t* const _decryptionKey = nullptr, + const E_CACHING_FLAGS _cacheFlags = ECF_CACHE_EVERYTHING,const E_LOADER_PARAMETER_FLAGS _loaderFlags = ELPF_NONE, + const system::logger_opt_ptr _logger = nullptr, const std::filesystem::path& cwd = "") : decryptionKeyLen(_decryptionKeyLen), decryptionKey(_decryptionKey), cacheFlags(_cacheFlags), loaderFlags(_loaderFlags), logger(std::move(_logger)), workingDirectory(cwd) { } - SAssetLoadParams(const SAssetLoadParams& rhs, bool _reload = false) : + inline SAssetLoadParams(const SAssetLoadParams& rhs, const bool _reload=false) : decryptionKeyLen(rhs.decryptionKeyLen), decryptionKey(rhs.decryptionKey), cacheFlags(rhs.cacheFlags), diff --git a/include/nbl/asset/material_compiler3/CTrueIR.h b/include/nbl/asset/material_compiler3/CTrueIR.h index a6937f00ab..3d5c89ff58 100644 --- a/include/nbl/asset/material_compiler3/CTrueIR.h +++ b/include/nbl/asset/material_compiler3/CTrueIR.h @@ -17,7 +17,7 @@ class CTrueIR : public CNodePool { public: // constructor - inline core::smart_refctd_ptr create(const uint8_t chunkSizeLog2=19, const uint8_t maxNodeAlignLog2=4, refctd_pmr_t&& _pmr={}) + static inline core::smart_refctd_ptr create(const uint8_t chunkSizeLog2=19, const uint8_t maxNodeAlignLog2=4, refctd_pmr_t&& _pmr={}) { if (chunkSizeLog2<14 || maxNodeAlignLog2<4) return nullptr; diff --git a/include/nbl/asset/metadata/IAssetMetadata.h b/include/nbl/asset/metadata/IAssetMetadata.h index 584fe474f8..d574091dd2 100644 --- a/include/nbl/asset/metadata/IAssetMetadata.h +++ b/include/nbl/asset/metadata/IAssetMetadata.h @@ -10,6 +10,7 @@ #include "nbl/asset/metadata/IImageMetadata.h" #include "nbl/asset/metadata/IImageViewMetadata.h" #include "nbl/asset/metadata/IPolygonGeometryMetadata.h" +#include "nbl/asset/metadata/IGeometryCollectionMetadata.h" namespace nbl::asset @@ -38,6 +39,11 @@ struct IAssetMetadata_base::asset_metadata { using type = IPolygonGeometryMetadata; }; +template<> +struct IAssetMetadata_base::asset_metadata +{ + using type = IGeometryCollectionMetadata; +}; } @@ -79,7 +85,8 @@ class IAssetMetadata : public impl::IAssetMetadata_base std::tuple< asset_metadata_map_t, asset_metadata_map_t, - asset_metadata_map_t + asset_metadata_map_t, + asset_metadata_map_t > m_metaMaps; diff --git a/include/nbl/asset/metadata/IGeometryCollectionMetadata.h b/include/nbl/asset/metadata/IGeometryCollectionMetadata.h new file mode 100644 index 0000000000..02ace03f82 --- /dev/null +++ b/include/nbl/asset/metadata/IGeometryCollectionMetadata.h @@ -0,0 +1,30 @@ +// Copyright (C) 2025-2025 - 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_GEOMETRY_COLLECTION_METADATA_H_INCLUDED_ +#define _NBL_ASSET_I_GEOMETRY_COLLECTION_METADATA_H_INCLUDED_ + + +#include "nbl/asset/ICPUGeometryCollection.h" + + +namespace nbl::asset +{ + +//! +class IGeometryCollectionMetadata : public core::Interface +{ + public: + inline IGeometryCollectionMetadata() = default; + + protected: + virtual ~IGeometryCollectionMetadata() = default; + + inline IGeometryCollectionMetadata& operator=(IGeometryCollectionMetadata&& other) + { + return *this; + } +}; + +} +#endif diff --git a/include/nbl/builtin/hlsl/ext/FullScreenTriangle/default.vert.hlsl b/include/nbl/builtin/hlsl/ext/FullScreenTriangle/default.vert.hlsl index a48d9b4623..abf65296a6 100644 --- a/include/nbl/builtin/hlsl/ext/FullScreenTriangle/default.vert.hlsl +++ b/include/nbl/builtin/hlsl/ext/FullScreenTriangle/default.vert.hlsl @@ -22,7 +22,7 @@ const static float32_t2 tc[3] = { [[vk::constant_id(0)]] const uint32_t SwapchainTransform = 0; [shader("vertex")] -SVertexAttributes main() +SVertexAttributes __nbl__hlsl__ext__FullScreenTriangle__vertex_main() { using namespace ::nbl::hlsl::glsl; @@ -33,4 +33,4 @@ SVertexAttributes main() SVertexAttributes retval; retval.uv = tc[gl_VertexIndex()]; return retval; -} \ No newline at end of file +} diff --git a/include/nbl/builtin/hlsl/indirect_commands.hlsl b/include/nbl/builtin/hlsl/indirect_commands.hlsl index ca8418bde7..db8c7cb1af 100644 --- a/include/nbl/builtin/hlsl/indirect_commands.hlsl +++ b/include/nbl/builtin/hlsl/indirect_commands.hlsl @@ -50,9 +50,9 @@ struct TraceRaysIndirectCommand_t uint64_t callableShaderBindingTableAddress; uint64_t callableShaderBindingTableSize; uint64_t callableShaderBindingTableStride; - uint32_t width; - uint32_t height; - uint32_t depth; + uint32_t width; + uint32_t height; + uint32_t depth; }; } diff --git a/include/nbl/builtin/hlsl/math/linalg/matrix_runtime_traits.hlsl b/include/nbl/builtin/hlsl/math/linalg/matrix_runtime_traits.hlsl index dc74c45ddd..3ed2f549c9 100644 --- a/include/nbl/builtin/hlsl/math/linalg/matrix_runtime_traits.hlsl +++ b/include/nbl/builtin/hlsl/math/linalg/matrix_runtime_traits.hlsl @@ -56,7 +56,7 @@ struct RuntimeTraits bool invertible; bool orthogonal; - scalar_t uniformScaleSq; + scalar_t uniformScaleSq; // TODO: rename to `uniformColumnSqNorm` and move this whole header to `nbl/builtin/hlsl/matrix_utils/` and associated namespace bool orthonormal; }; diff --git a/include/nbl/builtin/hlsl/matrix_utils/transformation_matrix_utils.hlsl b/include/nbl/builtin/hlsl/matrix_utils/transformation_matrix_utils.hlsl index df56d46549..dc2cdf3f02 100644 --- a/include/nbl/builtin/hlsl/matrix_utils/transformation_matrix_utils.hlsl +++ b/include/nbl/builtin/hlsl/matrix_utils/transformation_matrix_utils.hlsl @@ -72,7 +72,7 @@ inline matrix getCastedMatrix(const matrix& in) return out; } -// TODO: use portable_float when merged +// TODO: remove //! multiplies matrices a and b, 3x4 matrices are treated as 4x4 matrices with 4th row set to (0, 0, 0 ,1) template inline matrix concatenateBFollowedByA(const matrix& a, const matrix& b) diff --git a/include/nbl/builtin/hlsl/rwmc/ResolveParameters.hlsl b/include/nbl/builtin/hlsl/rwmc/ResolveParameters.hlsl index 7509eac493..6ef687c506 100644 --- a/include/nbl/builtin/hlsl/rwmc/ResolveParameters.hlsl +++ b/include/nbl/builtin/hlsl/rwmc/ResolveParameters.hlsl @@ -21,7 +21,7 @@ struct ResolveParameters float NOverKappa; }; -ResolveParameters computeResolveParameters(float base, uint32_t sampleCount, float minReliableLuma, float kappa, uint32_t cascadeSize) +inline ResolveParameters computeResolveParameters(float base, uint32_t sampleCount, float minReliableLuma, float kappa, uint32_t cascadeSize) { ResolveParameters retval; retval.lastCascadeIndex = cascadeSize - 1u; diff --git a/include/nbl/builtin/hlsl/surface_transform.h b/include/nbl/builtin/hlsl/surface_transform.h index a681ecf0bb..12e74a098a 100644 --- a/include/nbl/builtin/hlsl/surface_transform.h +++ b/include/nbl/builtin/hlsl/surface_transform.h @@ -4,6 +4,7 @@ #ifndef _NBL_BUILTIN_HLSL_SURFACE_TRANSFORM_INCLUDED_ #define _NBL_BUILTIN_HLSL_SURFACE_TRANSFORM_INCLUDED_ #include +#include #include namespace nbl @@ -174,8 +175,7 @@ inline float32_t2 applyToNDC(const FLAG_BITS transform, const float32_t2 ndc) template TwoColumns applyToDerivatives(const FLAG_BITS transform, TwoColumns dDx_dDy) { - using namespace glsl; // IN HLSL mode, C++ doens't need this to access `inverse` - return mul(inverse(transformMatrix(transform)),dDx_dDy); + return mul(::nbl::hlsl::inverse(transformMatrix(transform)),dDx_dDy); } } diff --git a/include/nbl/core/containers/CMemoryPool.h b/include/nbl/core/containers/CMemoryPool.h index 85488d8e7d..737848ebff 100644 --- a/include/nbl/core/containers/CMemoryPool.h +++ b/include/nbl/core/containers/CMemoryPool.h @@ -1,5 +1,6 @@ -#ifndef __NBL_C_MEMORY_POOL_H_INCLUDED__ -#define __NBL_C_MEMORY_POOL_H_INCLUDED__ +#ifndef _NBL_CORE_C_MEMORY_POOL_H_INCLUDED_ +#define _NBL_CORE_C_MEMORY_POOL_H_INCLUDED_ + #include "nbl/core/decl/compile_config.h" #include "nbl/core/alloc/SimpleBlockBasedAllocator.h" @@ -8,9 +9,11 @@ #include #include + namespace nbl::core { +// TODO: change DataAllocator to PMR template class DataAllocator, bool isThreadSafe, typename... Args> class CMemoryPool : public Uncopyable { diff --git a/include/nbl/ext/FullScreenTriangle/FullScreenTriangle.h b/include/nbl/ext/FullScreenTriangle/FullScreenTriangle.h index 1abebf23ea..597ebdbd4e 100644 --- a/include/nbl/ext/FullScreenTriangle/FullScreenTriangle.h +++ b/include/nbl/ext/FullScreenTriangle/FullScreenTriangle.h @@ -10,97 +10,26 @@ namespace nbl::ext::FullScreenTriangle { struct ProtoPipeline final { - inline core::smart_refctd_ptr createDefaultVertexShader(asset::IAssetManager* assMan, video::ILogicalDevice* device, system::ILogger* logger=nullptr) - { - if (!assMan || !device) - return nullptr; - - using namespace ::nbl::asset; - IAssetLoader::SAssetLoadParams lp = {}; - lp.logger = logger; - lp.workingDirectory = ""; // virtual root - auto assetBundle = assMan->getAsset("nbl/builtin/hlsl/ext/FullScreenTriangle/default.vert.hlsl",lp); - const auto assets = assetBundle.getContents(); - if (assets.empty()) - return nullptr; - - auto source = IAsset::castDown(assets[0]); - if (!source) - return nullptr; - - return device->compileShader({ .source = source.get(), .stage = hlsl::ESS_VERTEX }); - } - public: - inline ProtoPipeline(asset::IAssetManager* assMan, video::ILogicalDevice* device, system::ILogger* logger=nullptr) - { - m_vxShader = createDefaultVertexShader(assMan,device,logger); - } + static core::smart_refctd_ptr createDefaultVertexShader(asset::IAssetManager* assMan, video::ILogicalDevice* device, system::ILogger* logger=nullptr); + static core::smart_refctd_ptr mount(core::smart_refctd_ptr logger, system::ISystem* system, video::ILogicalDevice* device, const std::string_view archiveAlias = "nbl/ext/FullScreenTriangle"); - inline operator bool() const {return m_vxShader.get();} + ProtoPipeline(asset::IAssetManager* assMan, video::ILogicalDevice* device, system::ILogger* logger=nullptr); - inline core::smart_refctd_ptr createPipeline( + operator bool() const; + + core::smart_refctd_ptr createPipeline( const video::IGPUPipelineBase::SShaderSpecInfo& fragShader, video::IGPUPipelineLayout* layout, - video::IGPURenderpass* renderpass, + const video::IGPURenderpass* renderpass, const uint32_t subpassIx=0, asset::SBlendParams blendParams = {}, const hlsl::SurfaceTransform::FLAG_BITS swapchainTransform=hlsl::SurfaceTransform::FLAG_BITS::IDENTITY_BIT - ) - { - if (!renderpass || !bool(*this) || hlsl::bitCount(swapchainTransform)!=1) - return nullptr; - - using namespace ::nbl::video; - auto device = const_cast(renderpass->getOriginDevice()); - - core::smart_refctd_ptr m_retval; - { - const auto orientationAsUint32 = static_cast(swapchainTransform); - - IGPUPipelineBase::SShaderEntryMap specConstants; - specConstants[0] = std::span{ reinterpret_cast(&orientationAsUint32), sizeof(orientationAsUint32)}; - - IGPUGraphicsPipeline::SCreationParams params[1]; - params[0].layout = layout; - params[0].vertexShader = { .shader = m_vxShader.get(), .entryPoint = "main", .entries = &specConstants }; - params[0].fragmentShader = fragShader; - params[0].cached = { - .vertexInput = {}, // The Full Screen Triangle doesn't use any HW vertex input state - .primitiveAssembly = {}, - .rasterization = DefaultRasterParams, - .blend = blendParams, - .subpassIx = subpassIx - }; - params[0].renderpass = renderpass; - - if (!device->createGraphicsPipelines(nullptr,params,&m_retval)) - return nullptr; - } - return m_retval; - } - + ); core::smart_refctd_ptr m_vxShader; - // The default is correct for us - constexpr static inline asset::SRasterizationParams DefaultRasterParams = { - .faceCullingMode = asset::EFCM_NONE, - .depthWriteEnable = false, - .depthCompareOp = asset::ECO_ALWAYS - }; }; - -/* - Helper function for drawing full screen triangle. - It should be called between command buffer render pass - records. -*/ -static inline bool recordDrawCall(video::IGPUCommandBuffer* commandBuffer) -{ - constexpr auto VERTEX_COUNT = 3; - constexpr auto INSTANCE_COUNT = 1; - return commandBuffer->draw(VERTEX_COUNT,INSTANCE_COUNT,0,0); -} +bool recordDrawCall(video::IGPUCommandBuffer* commandBuffer); } -#endif \ No newline at end of file +#endif diff --git a/include/nbl/ext/MitsubaLoader/CElementSampler.h b/include/nbl/ext/MitsubaLoader/CElementSampler.h index 9b9bc2b820..82f586c19e 100644 --- a/include/nbl/ext/MitsubaLoader/CElementSampler.h +++ b/include/nbl/ext/MitsubaLoader/CElementSampler.h @@ -10,8 +10,6 @@ namespace nbl::ext::MitsubaLoader { -class CGlobalMitsubaMetadata; - class CElementSampler : public IElement { public: diff --git a/include/nbl/ext/MitsubaLoader/CElementSensor.h b/include/nbl/ext/MitsubaLoader/CElementSensor.h index fecd248ad0..d240830902 100644 --- a/include/nbl/ext/MitsubaLoader/CElementSensor.h +++ b/include/nbl/ext/MitsubaLoader/CElementSensor.h @@ -263,6 +263,7 @@ class CElementSensor final : public IElement };*/ union { + CameraBase base; PerspectivePinhole perspective; PerspectiveThinLens thinlens; Orthographic orthographic; diff --git a/include/nbl/ext/MitsubaLoader/CElementShape.h b/include/nbl/ext/MitsubaLoader/CElementShape.h index 1ce78a399b..db0ca020e2 100644 --- a/include/nbl/ext/MitsubaLoader/CElementShape.h +++ b/include/nbl/ext/MitsubaLoader/CElementShape.h @@ -10,6 +10,9 @@ #include "nbl/ext/MitsubaLoader/CElementBSDF.h" #include "nbl/ext/MitsubaLoader/CElementEmitter.h" +// awful path +#include "nbl/builtin/hlsl/math/linalg/matrix_utils/transformation_matrix_utils.hlsl" + namespace nbl::ext::MitsubaLoader { @@ -236,15 +239,14 @@ class CElementShape final : public IElement inline std::string getLogName() const override { return "shape"; } - inline hlsl::float32_t3x4 getAbsoluteTransform() const + inline hlsl::float32_t3x4 getTransform() const { // explicit truncation auto local = hlsl::float32_t3x4(transform.matrix); - // TODO restore at some point (and make it actually work??) - // note: INSTANCE can only contain SHAPEGROUP and the latter doesnt have its own transform - //if (type==CElementShape::INSTANCE && instance.parent) - // return mul(instance.parent->getAbsoluteTransform(),local); + // SHAPEGROUP cannot have its own transformation + assert(type!=Type::SHAPEGROUP || hlsl::math::linalg::diagonal(1)==local); + return local; } diff --git a/include/nbl/ext/MitsubaLoader/CMitsubaLoader.h b/include/nbl/ext/MitsubaLoader/CMitsubaLoader.h index 95b2f45c41..f918962b3c 100644 --- a/include/nbl/ext/MitsubaLoader/CMitsubaLoader.h +++ b/include/nbl/ext/MitsubaLoader/CMitsubaLoader.h @@ -9,7 +9,6 @@ #include "nbl/ext/MitsubaLoader/CSerializedLoader.h" #include "nbl/ext/MitsubaLoader/ParserUtil.h" -//#include "nbl/ext/MitsubaLoader/CElementShape.h" #include "nbl/ext/MitsubaLoader/SContext.h" @@ -57,12 +56,8 @@ class CMitsubaLoader final : public asset::ISceneLoader //! Destructor virtual ~CMitsubaLoader() = default; -#if 0 - // - core::vector getMesh(SContext& ctx, uint32_t hierarchyLevel, CElementShape* shape); - core::vector loadShapeGroup(SContext& ctx, uint32_t hierarchyLevel, const CElementShape::ShapeGroup* shapegroup, const core::matrix3x4SIMD& relTform); - SContext::shape_ass_type loadBasicShape(SContext& ctx, uint32_t hierarchyLevel, CElementShape* shape, const core::matrix3x4SIMD& relTform); +#if 0 void cacheTexture(SContext& ctx, uint32_t hierarchyLevel, const CElementTexture* texture, const CMitsubaMaterialCompilerFrontend::E_IMAGE_VIEW_SEMANTIC semantic); void cacheEmissionProfile(SContext& ctx, const CElementEmissionProfile* profile); diff --git a/include/nbl/ext/MitsubaLoader/CMitsubaMetadata.h b/include/nbl/ext/MitsubaLoader/CMitsubaMetadata.h index 6f24951c50..9ffdd8f7fd 100644 --- a/include/nbl/ext/MitsubaLoader/CMitsubaMetadata.h +++ b/include/nbl/ext/MitsubaLoader/CMitsubaMetadata.h @@ -8,17 +8,15 @@ #include "nbl/asset/metadata/IAssetMetadata.h" #include "nbl/asset/ICPUImage.h" -//#include "nbl/ext/MitsubaLoader/SContext.h" -//#include "nbl/ext/MitsubaLoader/CElementEmitter.h" #include "nbl/ext/MitsubaLoader/CElementIntegrator.h" #include "nbl/ext/MitsubaLoader/CElementSensor.h" -//#include "nbl/ext/MitsubaLoader/CElementShape.h" +#include "nbl/ext/MitsubaLoader/CElementShape.h" namespace nbl::ext::MitsubaLoader { -//! A class to derive mitsuba mesh loader metadata objects from +//! A class to derive mitsuba scene loader metadata objects from class CMitsubaMetadata : public asset::IAssetMetadata { public: @@ -27,84 +25,76 @@ class CMitsubaMetadata : public asset::IAssetMetadata public: std::string m_id; }; -#if 0 - class CMesh : public asset::IMeshMetadata, public CID + class IGeometry : public CID { public: - CMesh() : IMeshMetadata(), CID(), type(CElementShape::Type::INVALID) {} - ~CMesh() {} + inline IGeometry() : CID(), type(CElementShape::Type::INVALID) {} + inline ~IGeometry() = default; CElementShape::Type type; }; -#endif + class CPolygonGeometry final : public asset::IPolygonGeometryMetadata, public IGeometry + { + public: + inline CPolygonGeometry() : asset::IPolygonGeometryMetadata(), IGeometry() {} + inline CPolygonGeometry(CPolygonGeometry&& other) : CPolygonGeometry() {operator=(std::move(other));} + inline ~CPolygonGeometry() = default; + + inline CPolygonGeometry& operator=(CPolygonGeometry&& other) + { + asset::IPolygonGeometryMetadata::operator=(std::move(other)); + IGeometry::operator=(std::move(other)); + return *this; + } + }; + class CGeometryCollection final : public asset::IGeometryCollectionMetadata, public CID + { + public: + inline CGeometryCollection() : asset::IGeometryCollectionMetadata(), CID() {} + inline ~CGeometryCollection() = default; + }; + struct SGlobal { public: - inline SGlobal() : m_integrator("invalid") {}// TODO + inline SGlobal() : m_integrator("invalid") {} CElementIntegrator m_integrator; core::vector m_sensors; } m_global; - inline CMitsubaMetadata() : IAssetMetadata()/*, m_metaMeshStorage(), m_metaMeshInstanceStorage(), m_metaMeshInstanceAuxStorage(), - m_meshStorageIt(nullptr), m_instanceStorageIt(nullptr), m_instanceAuxStorageIt(nullptr)*/ - { - } + inline CMitsubaMetadata() : IAssetMetadata(), m_metaPolygonGeometryStorage() {} constexpr static inline const char* LoaderName = "ext::MitsubaLoader::CMitsubaLoader"; const char* getLoaderName() const override {return LoaderName;} -#if 0 - //! - inline const CMesh* getAssetSpecificMetadata(const asset::ICPUMesh* asset) const + + // add more overloads when more asset implementations of IGeometry exist + inline const CPolygonGeometry* getAssetSpecificMetadata(const asset::ICPUPolygonGeometry* asset) const { const auto found = IAssetMetadata::getAssetSpecificMetadata(asset); - return static_cast(found); + return static_cast(found); } -#endif - private: -// friend class CMitsubaLoader; -#if 0 - meta_container_t m_metaMeshStorage; - CMesh* m_meshStorageIt; - inline void reserveMeshStorage(uint32_t meshCount, uint32_t instanceCount) + private: + friend struct SContext; + struct SGeometryMetaPair { - m_metaMeshStorage = IAssetMetadata::createContainer(meshCount); - m_metaMeshInstanceStorage = IAssetMetadata::createContainer(instanceCount); - m_metaMeshInstanceAuxStorage = IAssetMetadata::createContainer(instanceCount); - m_meshStorageIt = m_metaMeshStorage->begin(); - m_instanceStorageIt = m_metaMeshInstanceStorage->begin(); - m_instanceAuxStorageIt = m_metaMeshInstanceAuxStorage->begin(); - } - template - inline uint32_t addMeshMeta(const asset::ICPUMesh* mesh, std::string&& id, const CElementShape::Type type, InstanceIterator instancesBegin, InstanceIterator instancesEnd) + core::smart_refctd_ptr geom; + CMitsubaMetadata::CPolygonGeometry meta; + }; + inline void setPolygonGeometryMeta(core::unordered_map&& container) { - auto instanceStorageBegin = m_instanceStorageIt; - auto instanceAuxStorageBegin = m_instanceAuxStorageIt; - - auto* meta = m_meshStorageIt++; - meta->m_id = std::move(id); + const uint32_t count = container.size(); + m_metaPolygonGeometryStorage = IAssetMetadata::createContainer(count); + auto outIt = m_metaPolygonGeometryStorage->begin(); + for (auto& el : container) { - // copy instance data - for (auto it=instancesBegin; it!=instancesEnd; ++it) - { - auto& inst = it->second; - (m_instanceStorageIt++)->worldTform = inst.tform; - *(m_instanceAuxStorageIt++) = { - inst.emitter.front, - inst.emitter.back, - inst.bsdf - }; - } - meta->m_instances = { instanceStorageBegin,m_instanceStorageIt }; - meta->m_instanceAuxData = { instanceAuxStorageBegin,m_instanceAuxStorageIt }; + *outIt = std::move(el.second.meta); + IAssetMetadata::insertAssetSpecificMetadata(el.second.geom.get(),outIt++); } - meta->type = type; - IAssetMetadata::insertAssetSpecificMetadata(mesh,meta); - - return meta->m_instances.size(); } -#endif + + meta_container_t m_metaPolygonGeometryStorage; }; } diff --git a/include/nbl/ext/MitsubaLoader/ParserUtil.h b/include/nbl/ext/MitsubaLoader/ParserUtil.h index 787c1a534a..5e33951348 100644 --- a/include/nbl/ext/MitsubaLoader/ParserUtil.h +++ b/include/nbl/ext/MitsubaLoader/ParserUtil.h @@ -32,7 +32,7 @@ class IElement; // TODO: replace with common Class for Material Compiler V3 Node Pool template -class ElementPool // similar to : public std::tuple...> +class ElementPool final : public core::Unmovable// similar to : public std::tuple...> { core::SimpleBlockBasedAllocator,core::aligned_allocator> poolAllocator; public: @@ -64,14 +64,32 @@ class ParserManager final system::ISystem* system; asset::IAssetLoader::IAssetLoaderOverride* _override; }; - struct Result + template + struct SNamedElement { - explicit inline operator bool() const {return bool(metadata);} - - // note that its shared between per-file contexts - core::smart_refctd_ptr metadata = nullptr; - // - core::vector > shapegroups = {}; + ElementT* element = nullptr; + core::string name = {}; + }; + struct Result final + { + public: + explicit inline operator bool() const {return bool(metadata);} + + // note that its shared between per-file contexts + core::smart_refctd_ptr metadata = nullptr; + // + using emitter_t = SNamedElement; + core::vector emitters = {}; + // + using shape_group_t = SNamedElement; + core::vector shapegroups = {}; + // + hlsl::float32_t3 ambient = {0,0,0}; + + private: + friend class ParserManager; + // TODO: This leaks memory all over the place because destructors are not ran! + std::unique_ptr> objects = std::make_unique>(); }; Result parse(system::IFile* _file, const Params& _params) const; @@ -97,11 +115,6 @@ class ParserManager final private: const core::tuple_transform_t addPropertyMaps; - struct SNamedElement - { - IElement* element = nullptr; - core::string name = {}; - }; // the XMLs can include each other, so this stores the stuff across files struct SessionContext { @@ -120,13 +133,12 @@ class ParserManager final const ParserManager* const manager; // uint32_t sceneDeclCount = 0; - // TODO: This leaks memory all over the place because destructors are not ran! - ElementPool<> objects = {}; // aliases and names (in Mitsbua XML you can give nodes names and `ref` them) core::unordered_map handles = {}; // stack of currently processed elements, each element of index N is parent of the element of index N+1 // the scene element is a parent of all elements of index 0 - core::stack elements = {}; + using named_element_t = SNamedElement; + core::stack elements = {}; }; // This is for a single XML File struct XMLContext @@ -147,7 +159,7 @@ class ParserManager final { // we still push nullptr (failed creation) onto the stack, we only stop parse on catastrphic failure later on if a use of the element pops up // this is why we don't need XMLCOntext for `killParseWithError` - using func_t = SNamedElement(*)(const char**/*attributes*/,SessionContext*); + using func_t = SessionContext::named_element_t(*)(const char**/*attributes*/,SessionContext*); func_t create; bool retvalGoesOnStack; }; @@ -156,8 +168,8 @@ class ParserManager final template struct CreateElement; // - static SNamedElement processAlias(const char** _atts, SessionContext* ctx); - static SNamedElement processRef(const char** _atts, SessionContext* ctx); + static SessionContext::named_element_t processAlias(const char** _atts, SessionContext* ctx); + static SessionContext::named_element_t processRef(const char** _atts, SessionContext* ctx); }; } diff --git a/include/nbl/ext/MitsubaLoader/SContext.h b/include/nbl/ext/MitsubaLoader/SContext.h index c1d9c6d9b1..a65854bedc 100644 --- a/include/nbl/ext/MitsubaLoader/SContext.h +++ b/include/nbl/ext/MitsubaLoader/SContext.h @@ -9,15 +9,14 @@ //#include "nbl/asset/utils/IGeometryCreator.h" #include "nbl/asset/interchange/CIESProfileLoader.h" +#include "nbl/ext/MitsubaLoader/CMitsubaMetadata.h" //#include "nbl/ext/MitsubaLoader/CMitsubaMaterialCompilerFrontend.h" -//#include "nbl/ext/MitsubaLoader/CElementShape.h" + namespace nbl::ext::MitsubaLoader { -class CMitsubaMetadata; - -struct SContext +struct SContext final { public: SContext( @@ -28,19 +27,29 @@ struct SContext CMitsubaMetadata* _metadata ); + using shape_ass_type = core::smart_refctd_ptr; + shape_ass_type loadBasicShape(const uint32_t hierarchyLevel, const CElementShape* shape); + using group_ass_type = core::smart_refctd_ptr; + group_ass_type loadShapeGroup(const uint32_t hierarchyLevel, const CElementShape::ShapeGroup* shapegroup); + + inline void transferMetadata() + { + meta->setPolygonGeometryMeta(std::move(shapeCache)); + } + // const asset::IGeometryCreator* creator; // const asset::IMeshManipulator* manipulator; const asset::IAssetLoader::SAssetLoadContext inner; asset::IAssetLoader::IAssetLoaderOverride* override_; CMitsubaMetadata* meta; + core::smart_refctd_ptr scene; -#if 0 + private: // - using group_ass_type = core::vector>; - //core::map groupCache; + core::unordered_map groupCache; // - using shape_ass_type = core::smart_refctd_ptr; - core::map shapeCache; + core::unordered_map shapeCache; +#if 0 //image, sampler using tex_ass_type = std::tuple,core::smart_refctd_ptr>; //image, scale @@ -167,37 +176,8 @@ struct SContext using bsdf_type = const CMitsubaMaterialCompilerFrontend::front_and_back_t; //caches instr buffer instr-wise offset (.first) and instruction count (.second) for each bsdf node core::unordered_map instrStreamCache; - - struct SInstanceData - { - SInstanceData(core::matrix3x4SIMD _tform, SContext::bsdf_type _bsdf, const std::string& _id, const CElementEmitter& _emitterFront, const CElementEmitter& _emitterBack) : - tform(_tform), bsdf(_bsdf), -#if defined(_NBL_DEBUG) || defined(_NBL_RELWITHDEBINFO) - bsdf_id(_id), -#endif - emitter{_emitterFront, _emitterBack} - {} - - core::matrix3x4SIMD tform; - SContext::bsdf_type bsdf; -#if defined(_NBL_DEBUG) || defined(_NBL_RELWITHDEBINFO) - std::string bsdf_id; #endif - struct { - // type is invalid if not used - CElementEmitter front; - CElementEmitter back; - } emitter; - }; - core::unordered_multimap mapMesh2instanceData; - - core::unordered_map, SPipelineCacheKey::hash> pipelineCache; -#endif - //material compiler -// core::smart_refctd_ptr ir; -// CMitsubaMaterialCompilerFrontend frontend; - - private: + core::smart_refctd_ptr frontIR; }; } diff --git a/include/nbl/system/declarations.h b/include/nbl/system/declarations.h index ebc5a890ae..fa3dc2c6da 100644 --- a/include/nbl/system/declarations.h +++ b/include/nbl/system/declarations.h @@ -14,6 +14,9 @@ #include "nbl/system/DynamicFunctionCaller.h" #include "nbl/system/SReadWriteSpinLock.h" +// printing and serialization +#include "nbl/system/to_string.h" + // files #include "nbl/system/IFile.h" diff --git a/include/nbl/video/CVulkanCommon.h b/include/nbl/video/CVulkanCommon.h index 4232860baa..e4dfb7e3e9 100644 --- a/include/nbl/video/CVulkanCommon.h +++ b/include/nbl/video/CVulkanCommon.h @@ -1098,14 +1098,15 @@ inline VkPipelineBindPoint getVkPipelineBindPointFrom(asset::E_PIPELINE_BIND_POI } } -inline VkStridedDeviceAddressRegionKHR getVkStridedDeviceAddressRegion(const asset::SBufferRange& range, uint32_t stride) +inline VkStridedDeviceAddressRegionKHR getVkStridedDeviceAddressRegion(const asset::SStridedRange& stRange) { - if (range.buffer.get() == nullptr) + const auto& range = stRange.range; + if (range.buffer.get()==nullptr) return {}; return { .deviceAddress = range.buffer->getDeviceAddress() + range.offset, - .stride = stride, + .stride = stRange.stride, .size = range.size, }; } diff --git a/include/nbl/video/IGPUCommandBuffer.h b/include/nbl/video/IGPUCommandBuffer.h index bb6460754a..6b3bfef18c 100644 --- a/include/nbl/video/IGPUCommandBuffer.h +++ b/include/nbl/video/IGPUCommandBuffer.h @@ -539,12 +539,8 @@ class NBL_API2 IGPUCommandBuffer : public IBackendObject bool resolveImage(const IGPUImage* const srcImage, const IGPUImage::LAYOUT srcImageLayout, IGPUImage* const dstImage, const IGPUImage::LAYOUT dstImageLayout, const uint32_t regionCount, const SImageResolve* const pRegions); bool setRayTracingPipelineStackSize(uint32_t pipelineStackSize); - bool traceRays( - const asset::SBufferRange& raygenGroupRange, - const asset::SBufferRange& missGroupsRange, uint32_t missGroupStride, - const asset::SBufferRange& hitGroupsRange, uint32_t hitGroupStride, - const asset::SBufferRange& callableGroupsRange, uint32_t callableGroupStride, - uint32_t width, uint32_t height, uint32_t depth); + + bool traceRays(const IGPURayTracingPipeline::SShaderBindingTable& sbt, const uint32_t width, const uint32_t height, const uint32_t depth); bool traceRaysIndirect(const asset::SBufferBinding& indirectBinding); //! Secondary CommandBuffer execute @@ -719,14 +715,8 @@ class NBL_API2 IGPUCommandBuffer : public IBackendObject virtual bool resolveImage_impl(const IGPUImage* const srcImage, const IGPUImage::LAYOUT srcImageLayout, IGPUImage* const dstImage, const IGPUImage::LAYOUT dstImageLayout, const uint32_t regionCount, const SImageResolve* pRegions) = 0; virtual bool setRayTracingPipelineStackSize_impl(uint32_t pipelineStackSize) = 0; - virtual bool traceRays_impl( - const asset::SBufferRange& raygenGroupRange, - const asset::SBufferRange& missGroupsRange, uint32_t missGroupStride, - const asset::SBufferRange& hitGroupsRange, uint32_t hitGroupStride, - const asset::SBufferRange& callableGroupsRange, uint32_t callableGroupStride, - uint32_t width, uint32_t height, uint32_t depth) = 0; - virtual bool traceRaysIndirect_impl( - const asset::SBufferBinding& indirectBinding) = 0; + virtual bool traceRays_impl(const IGPURayTracingPipeline::SShaderBindingTable& sbt, const uint32_t width, const uint32_t height, const uint32_t depth) = 0; + virtual bool traceRaysIndirect_impl(const asset::SBufferBinding& indirectBinding) = 0; virtual bool executeCommands_impl(const uint32_t count, IGPUCommandBuffer* const* const cmdbufs) = 0; @@ -881,14 +871,8 @@ class NBL_API2 IGPUCommandBuffer : public IBackendObject } return invalidImage(image,IGPUImage::EUF_TRANSFER_SRC_BIT); } - - bool invalidShaderGroups( - const asset::SBufferRange& raygenGroupRange, - const asset::SBufferRange& missGroupsRange, uint32_t missGroupStride, - const asset::SBufferRange& hitGroupsRange, uint32_t hitGroupStride, - const asset::SBufferRange& callableGroupsRange, uint32_t callableGroupStride, - core::bitflag flags) const; + bool invalidShaderGroups(const IGPURayTracingPipeline::SShaderBindingTable& sbt, const core::bitflag flags) const; // returns total number of Geometries across all AS build infos template uint32_t buildAccelerationStructures_common(const std::span infos, BuildRangeInfos ranges, const IGPUBuffer* const indirectBuffer=nullptr); diff --git a/include/nbl/video/IGPUCommandPool.h b/include/nbl/video/IGPUCommandPool.h index 0424ad83bd..ddc4fcfd5c 100644 --- a/include/nbl/video/IGPUCommandPool.h +++ b/include/nbl/video/IGPUCommandPool.h @@ -71,7 +71,7 @@ class IGPUCommandPool : public IBackendObject m_commandListPool.clear(); return reset_impl(); } - inline uint32_t getResetCounter() { return m_resetCount.load(); } + inline uint64_t getResetCounter() { return m_resetCount.load(); } // recycles unused memory from the command pool back to the system virtual void trim() = 0; // no extra stuff needed for `CCommandSegmentListPool` because it trims unused blocks at runtime diff --git a/include/nbl/video/IGPURayTracingPipeline.h b/include/nbl/video/IGPURayTracingPipeline.h index 816cc68243..7b81ee43e7 100644 --- a/include/nbl/video/IGPURayTracingPipeline.h +++ b/include/nbl/video/IGPURayTracingPipeline.h @@ -10,9 +10,9 @@ namespace nbl::video { -class IGPURayTracingPipeline : public IGPUPipeline> +class IGPURayTracingPipeline : public IGPUPipeline> { - using pipeline_t = asset::IRayTracingPipeline; + using pipeline_t = asset::IRayTracingPipeline; public: struct SHitGroup @@ -172,8 +172,7 @@ class IGPURayTracingPipeline : public IGPUPipeline(params.layout->getOriginDevice()), params.layout, params.cached) - {} + IGPURayTracingPipeline(const SCreationParams& params) : IGPUPipeline(core::smart_refctd_ptr(params.layout->getOriginDevice()), params.layout, params.cached) {} virtual ~IGPURayTracingPipeline() = default; diff --git a/include/nbl/video/utilities/CDefaultSwapchainFramebuffers.h b/include/nbl/video/utilities/CDefaultSwapchainFramebuffers.h index 190fa81e70..285d7f46a7 100644 --- a/include/nbl/video/utilities/CDefaultSwapchainFramebuffers.h +++ b/include/nbl/video/utilities/CDefaultSwapchainFramebuffers.h @@ -17,7 +17,10 @@ namespace nbl::video class CDefaultSwapchainFramebuffers : public ISimpleManagedSurface::ISwapchainResources { public: - inline CDefaultSwapchainFramebuffers(ILogicalDevice* device, const asset::E_FORMAT format, const IGPURenderpass::SCreationParams::SSubpassDependency* dependencies) : m_device(device) + inline CDefaultSwapchainFramebuffers( + ILogicalDevice* device, const asset::E_FORMAT format, const IGPURenderpass::SCreationParams::SSubpassDependency* dependencies, + IGPURenderpass::LOAD_OP loadOp = IGPURenderpass::LOAD_OP::CLEAR + ) : m_device(device) { // If we create the framebuffers by default, we also need to default the renderpass (except dependencies) static const IGPURenderpass::SCreationParams::SColorAttachmentDescription colorAttachments[] = { @@ -27,7 +30,7 @@ class CDefaultSwapchainFramebuffers : public ISimpleManagedSurface::ISwapchainRe .samples = IGPUImage::E_SAMPLE_COUNT_FLAGS::ESCF_1_BIT, .mayAlias = false }, - /*.loadOp = */IGPURenderpass::LOAD_OP::CLEAR, + /*.loadOp = */loadOp, /*.storeOp = */IGPURenderpass::STORE_OP::STORE, /*.initialLayout = */IGPUImage::LAYOUT::UNDEFINED, // because we clear we don't care about contents /*.finalLayout = */ IGPUImage::LAYOUT::PRESENT_SRC // transition to presentation right away so we can skip a barrier @@ -51,6 +54,10 @@ class CDefaultSwapchainFramebuffers : public ISimpleManagedSurface::ISwapchainRe m_renderpass = m_device->createRenderpass(m_params); return m_renderpass.get(); } + inline const IGPURenderpass* getRenderpass() const + { + return m_renderpass.get(); + } inline IGPUFramebuffer* getFramebuffer(const uint8_t imageIx) { diff --git a/include/nbl/video/utilities/CSimpleResizeSurface.h b/include/nbl/video/utilities/CSimpleResizeSurface.h index 3e9abc6e25..126c9d179e 100644 --- a/include/nbl/video/utilities/CSimpleResizeSurface.h +++ b/include/nbl/video/utilities/CSimpleResizeSurface.h @@ -57,6 +57,7 @@ class CSimpleResizeSurface final : public ISimpleManagedSurface // Can be public because we don't need to worry about mutexes unlike the Smooth Resize class inline ISwapchainResources* getSwapchainResources() override {return m_swapchainResources.get();} + inline const ISwapchainResources* getSwapchainResources() const {return m_swapchainResources.get();} // need to see if the swapchain is invalidated (e.g. because we're starting from 0-area old Swapchain) and try to recreate the swapchain inline SAcquireResult acquireNextImage() diff --git a/include/nbl/video/utilities/ISimpleManagedSurface.h b/include/nbl/video/utilities/ISimpleManagedSurface.h index dbde2d5f53..f60aa022dd 100644 --- a/include/nbl/video/utilities/ISimpleManagedSurface.h +++ b/include/nbl/video/utilities/ISimpleManagedSurface.h @@ -186,6 +186,13 @@ class NBL_API2 ISimpleManagedSurface : public core::IReferenceCounted // inline bool irrecoverable() const {return !const_cast(this)->getSwapchainResources();} + // to trigger `becomeIrrecoverable` if window got closwd + inline bool isWindowOpen() + { + if (!m_cb) return true; // native hwnd has no callbacks set -> user's responsibility to not acquire on window close corresponding to the Surface HWND + return m_cb->isWindowOpen(); + } + // inline CThreadSafeQueueAdapter* getAssignedQueue() const {return m_queue;} @@ -339,13 +346,6 @@ class NBL_API2 ISimpleManagedSurface : public core::IReferenceCounted // virtual void deinit_impl() = 0; - // to trigger `becomeIrrecoverable` if window got closwd - inline bool isWindowOpen() - { - if (!m_cb) return true; // native hwnd has no callbacks set -> user's responsibility to not acquire on window close corresponding to the Surface HWND - return m_cb->isWindowOpen(); - } - // ICallback* const m_cb = nullptr; diff --git a/src/nbl/asset/utils/CHLSLCompiler.cpp b/src/nbl/asset/utils/CHLSLCompiler.cpp index 1020fa9446..62166a885c 100644 --- a/src/nbl/asset/utils/CHLSLCompiler.cpp +++ b/src/nbl/asset/utils/CHLSLCompiler.cpp @@ -313,7 +313,7 @@ static DxcCompilationResult dxcCompile(const CHLSLCompiler* compiler, nbl::asset DxcBuffer sourceBuffer; sourceBuffer.Ptr = src->GetBufferPointer(); sourceBuffer.Size = src->GetBufferSize(); - sourceBuffer.Encoding = 0; + sourceBuffer.Encoding = CP_UTF8; ComPtr compileResult; res = dxc->m_dxcCompiler->Compile(&sourceBuffer, args, argCount, nullptr, IID_PPV_ARGS(compileResult.GetAddressOf())); diff --git a/src/nbl/ext/CMakeLists.txt b/src/nbl/ext/CMakeLists.txt index af46b29aab..b4c6cf2b64 100644 --- a/src/nbl/ext/CMakeLists.txt +++ b/src/nbl/ext/CMakeLists.txt @@ -66,6 +66,16 @@ if(NBL_BUILD_DEBUG_DRAW) ) endif() +add_subdirectory(FullScreenTriangle) +set(NBL_EXT_FULL_SCREEN_TRIANGLE_INCLUDE_DIRS + ${NBL_EXT_FULL_SCREEN_TRIANGLE_INCLUDE_DIRS} + PARENT_SCOPE +) +set(NBL_EXT_FULL_SCREEN_TRIANGLE_LIB + ${NBL_EXT_FULL_SCREEN_TRIANGLE_LIB} + PARENT_SCOPE +) + propagate_changed_variables_to_parent_scope() -NBL_ADJUST_FOLDERS(ext) \ No newline at end of file +NBL_ADJUST_FOLDERS(ext) diff --git a/src/nbl/ext/DebugDraw/CMakeLists.txt b/src/nbl/ext/DebugDraw/CMakeLists.txt index dfa4a7624f..812abbce1b 100644 --- a/src/nbl/ext/DebugDraw/CMakeLists.txt +++ b/src/nbl/ext/DebugDraw/CMakeLists.txt @@ -37,7 +37,6 @@ set(JSON [=[ "INPUT": "${NBL_DEBUG_DRAW_HLSL_MOUNT_POINT}/draw_aabb.unified.hlsl", "KEY": "draw_aabb", } - ] ]=]) string(CONFIGURE "${JSON}" JSON) @@ -69,5 +68,4 @@ NBL_CREATE_RESOURCE_ARCHIVE( BUILTINS ${KEYS} ) - -add_library(Nabla::ext::DebugDraw ALIAS ${LIB_NAME}) +add_library(Nabla::ext::DebugDraw ALIAS ${LIB_NAME}) \ No newline at end of file diff --git a/src/nbl/ext/FullScreenTriangle/CFullScreenTriangle.cpp b/src/nbl/ext/FullScreenTriangle/CFullScreenTriangle.cpp new file mode 100644 index 0000000000..fd5411c2ab --- /dev/null +++ b/src/nbl/ext/FullScreenTriangle/CFullScreenTriangle.cpp @@ -0,0 +1,132 @@ +// Copyright (C) 2018-2026 - 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 + +#include "nbl/ext/FullScreenTriangle/FullScreenTriangle.h" + +#ifdef NBL_EMBED_BUILTIN_RESOURCES +#include "nbl/ext/FullScreenTriangle/builtin/build/CArchive.h" +#endif + +#include "nbl/ext/FullScreenTriangle/builtin/build/spirv/keys.hpp" + +using namespace nbl; +using namespace core; +using namespace video; +using namespace system; +using namespace asset; + +namespace nbl::ext::FullScreenTriangle +{ + +constexpr std::string_view NBL_EXT_MOUNT_ENTRY = "nbl/ext/FullScreenTriangle"; +constexpr std::string_view VertexEntryPoint = "__nbl__hlsl__ext__FullScreenTriangle__vertex_main"; + +smart_refctd_ptr ProtoPipeline::mount(smart_refctd_ptr logger, ISystem* system, ILogicalDevice* device, const std::string_view archiveAlias) +{ + assert(system); + if (!system) + return nullptr; + + const auto composed = path(archiveAlias.data()) / builtin::build::get_spirv_key<"full_screen_triangle_vertex">(device); + if (system->exists(composed, {})) + return nullptr; + +#ifdef NBL_EMBED_BUILTIN_RESOURCES + auto archive = make_smart_refctd_ptr(smart_refctd_ptr(logger)); +#else + auto archive = make_smart_refctd_ptr(std::string_view(NBL_FULL_SCREEN_TRIANGLE_HLSL_MOUNT_POINT), smart_refctd_ptr(logger), system); +#endif + + system->mount(smart_refctd_ptr(archive), archiveAlias.data()); + return smart_refctd_ptr(archive); +} + +smart_refctd_ptr ProtoPipeline::createDefaultVertexShader(IAssetManager* assMan, ILogicalDevice* device, ILogger* logger) +{ + if (!assMan || !device) + return nullptr; + + auto system = smart_refctd_ptr(assMan->getSystem()); + if (system) + ProtoPipeline::mount(smart_refctd_ptr(logger), system.get(), device, NBL_EXT_MOUNT_ENTRY); + + IAssetLoader::SAssetLoadParams lp = {}; + lp.logger = logger; + lp.workingDirectory = NBL_EXT_MOUNT_ENTRY.data(); + + const auto key = builtin::build::get_spirv_key<"full_screen_triangle_vertex">(device); + auto bundle = assMan->getAsset(key.c_str(), lp); + const auto assets = bundle.getContents(); + if (assets.empty()) + return nullptr; + + auto source = IAsset::castDown(assets[0]); + if (!source) + return nullptr; + + return device->compileShader({.source = source.get(), .stage = hlsl::ESS_VERTEX}); +} + +ProtoPipeline::ProtoPipeline(IAssetManager* assMan, ILogicalDevice* device, ILogger* logger) +{ + m_vxShader = createDefaultVertexShader(assMan, device, logger); +} + +ProtoPipeline::operator bool() const +{ + return m_vxShader.get(); +} + +smart_refctd_ptr ProtoPipeline::createPipeline( + const IGPUPipelineBase::SShaderSpecInfo& fragShader, + IGPUPipelineLayout* layout, + const IGPURenderpass* renderpass, + const uint32_t subpassIx, + SBlendParams blendParams, + const hlsl::SurfaceTransform::FLAG_BITS swapchainTransform) +{ + if (!renderpass || !bool(*this) || hlsl::bitCount(swapchainTransform) != 1) + return nullptr; + + auto device = const_cast(renderpass->getOriginDevice()); + + smart_refctd_ptr m_retval; + { + constexpr SRasterizationParams defaultRasterParams = { + .faceCullingMode = EFCM_NONE, + .depthWriteEnable = false, + .depthCompareOp = ECO_ALWAYS + }; + const auto orientationAsUint32 = static_cast(swapchainTransform); + + IGPUPipelineBase::SShaderEntryMap specConstants; + specConstants[0] = std::span{ reinterpret_cast(&orientationAsUint32), sizeof(orientationAsUint32) }; + + IGPUGraphicsPipeline::SCreationParams params[1]; + params[0].layout = layout; + params[0].vertexShader = { .shader = m_vxShader.get(), .entryPoint = VertexEntryPoint.data(), .entries = &specConstants }; + params[0].fragmentShader = fragShader; + params[0].cached = { + .vertexInput = {}, // The Full Screen Triangle doesn't use any HW vertex input state + .primitiveAssembly = {}, + .rasterization = defaultRasterParams, + .blend = blendParams, + .subpassIx = subpassIx + }; + params[0].renderpass = renderpass; + + if (!device->createGraphicsPipelines(nullptr, params, &m_retval)) + return nullptr; + } + return m_retval; +} + +bool recordDrawCall(IGPUCommandBuffer* commandBuffer) +{ + constexpr auto VERTEX_COUNT = 3; + constexpr auto INSTANCE_COUNT = 1; + return commandBuffer->draw(VERTEX_COUNT, INSTANCE_COUNT, 0, 0); +} + +} diff --git a/src/nbl/ext/FullScreenTriangle/CMakeLists.txt b/src/nbl/ext/FullScreenTriangle/CMakeLists.txt new file mode 100644 index 0000000000..6d30fd81bc --- /dev/null +++ b/src/nbl/ext/FullScreenTriangle/CMakeLists.txt @@ -0,0 +1,59 @@ +include(common) + +set(HEADERS + ${NBL_EXT_INTERNAL_INCLUDE_DIR}/nbl/ext/FullScreenTriangle/FullScreenTriangle.h +) + +set(SRCS + ${CMAKE_CURRENT_SOURCE_DIR}/CFullScreenTriangle.cpp +) + +nbl_create_ext_library_project(FULL_SCREEN_TRIANGLE + "${HEADERS}" + "${SRCS}" + "" + "" + "" +) + +get_filename_component(DIR "${NBL_ROOT_PATH}/include/nbl/builtin/hlsl/ext/FullScreenTriangle" ABSOLUTE) +set(OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/auto-gen") + +set(SM 6_8) +set(JSON [=[ +[ + { + "INPUT": "${DIR}/default.vert.hlsl", + "KEY": "full_screen_triangle_vertex", + } +] +]=]) +string(CONFIGURE "${JSON}" JSON) + +set(COMPILE_OPTIONS + -T vs_${SM} + -E __nbl__hlsl__ext__FullScreenTriangle__vertex_main +) + +NBL_CREATE_NSC_COMPILE_RULES( + TARGET ${LIB_NAME}SPIRV + LINK_TO ${LIB_NAME} + BINARY_DIR ${OUTPUT_DIRECTORY} + MOUNT_POINT_DEFINE NBL_FULL_SCREEN_TRIANGLE_HLSL_MOUNT_POINT + COMMON_OPTIONS ${COMPILE_OPTIONS} + OUTPUT_VAR KEYS + INCLUDE nbl/ext/FullScreenTriangle/builtin/build/spirv/keys.hpp + NAMESPACE nbl::ext::FullScreenTriangle::builtin::build + INPUTS ${JSON} + GLOB_DIR ${DIR} +) + +NBL_CREATE_RESOURCE_ARCHIVE( + NAMESPACE nbl::ext::FullScreenTriangle::builtin::build + TARGET ${LIB_NAME}_builtinsBuild + LINK_TO ${LIB_NAME} + BIND ${OUTPUT_DIRECTORY} + BUILTINS ${KEYS} +) + +add_library(Nabla::ext::FullScreenTriangle ALIAS ${LIB_NAME}) \ No newline at end of file diff --git a/src/nbl/ext/MitsubaLoader/CElementEmitter.cpp b/src/nbl/ext/MitsubaLoader/CElementEmitter.cpp index 00cf848067..93b77b4c3a 100644 --- a/src/nbl/ext/MitsubaLoader/CElementEmitter.cpp +++ b/src/nbl/ext/MitsubaLoader/CElementEmitter.cpp @@ -197,8 +197,6 @@ bool CElementEmitter::onEndTag(CMitsubaMetadata* globalMetadata, system::logger_ case Type::AREA: break; default: - // TODO: slap into the scene instead! -// globalMetadata->m_global.m_emitters.push_back(*this); break; } diff --git a/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp b/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp index a852978d75..9b63f52d23 100644 --- a/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp +++ b/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp @@ -28,15 +28,6 @@ namespace ext::MitsubaLoader { #if 0 // old material compiler -_NBL_STATIC_INLINE_CONSTEXPR const char* FRAGMENT_SHADER_INPUT_OUTPUT = -R"( -layout (location = 0) in vec3 WorldPos; -layout (location = 1) flat in uint InstanceIndex; -layout (location = 2) in vec3 Normal; -layout (location = 3) in vec2 UV; - -layout (location = 0) out vec4 OutColor; -)"; _NBL_STATIC_INLINE_CONSTEXPR const char* FRAGMENT_SHADER_DEFINITIONS = R"( #include @@ -135,23 +126,6 @@ static core::smart_refctd_ptr createFragmentShader #endif #if 0 -static core::smart_refctd_ptr createImageView(core::smart_refctd_ptr&& _img) // TODO: this should seriously be a utility somewhere -{ - const auto& iparams = _img->getCreationParameters(); - - ICPUImageView::SCreationParams params; - params.format = iparams.format; - params.subresourceRange.baseArrayLayer = 0u; - params.subresourceRange.layerCount = iparams.arrayLayers; - assert(params.subresourceRange.layerCount == 1u); - params.subresourceRange.baseMipLevel = 0u; - params.subresourceRange.levelCount = iparams.mipLevels; - params.viewType = IImageView::ET_2D; - params.flags = static_cast::E_CREATE_FLAGS>(0); - params.image = std::move(_img); - - return ICPUImageView::create(std::move(params)); -} static core::smart_refctd_ptr createDerivMap(SContext& ctx, asset::ICPUImage* _heightMap, const ICPUSampler::SParams& _samplerParams, bool fromNormalMap) { core::smart_refctd_ptr derivmap_img; @@ -302,61 +276,114 @@ SAssetBundle CMitsubaLoader::loadAsset(system::IFile* _file, const IAssetLoader: if (!result) return {}; - auto scene = core::make_smart_refctd_ptr(); if (_params.loaderFlags&IAssetLoader::ELPF_LOAD_METADATA_ONLY) { - return SAssetBundle(std::move(result.metadata),{std::move(scene)}); + return SAssetBundle(std::move(result.metadata),{ICPUScene::create(nullptr)}); } else { -#if 0 SContext ctx( // m_assetMgr->getGeometryCreator(), // m_assetMgr->getMeshManipulator(), IAssetLoader::SAssetLoadContext{ - IAssetLoader::SAssetLoadParams(_params.decryptionKeyLen,_params.decryptionKey,_params.cacheFlags,_params.logger,_file->getFileName().parent_path()), + IAssetLoader::SAssetLoadParams(_params.decryptionKeyLen,_params.decryptionKey,_params.cacheFlags,_params.loaderFlags,_params.logger,_file->getFileName().parent_path()), _file }, _override, - parserManager.m_metadata.get() + result.metadata.get() ); + // + ctx.scene->m_ambientLight = result.ambient; - core::map,std::pair> meshes; - for (auto& shapepair : parserManager.shapegroups) + // TODO: abstract/move away since many loaders will need to do this + core::unordered_map> morphTargetCache; + auto createMorphTargets = [&_params,&morphTargetCache](core::smart_refctd_ptr&& collection)->core::smart_refctd_ptr { - auto* shapedef = shapepair.first; - if (shapedef->type == CElementShape::Type::SHAPEGROUP) - continue; - - auto lowermeshes = getMesh(ctx, _hierarchyLevel, shapedef); - for (auto& mesh : lowermeshes) + auto found = morphTargetCache.find(collection.get()); + if (found!=morphTargetCache.end()) + return found->second; + auto targets = core::make_smart_refctd_ptr(); + if (targets) { - if (!mesh) - continue; + morphTargetCache[collection.get()] = targets; + targets->getTargets()->push_back({.geoCollection=std::move(collection)}); + } + return targets; + }; - auto found = meshes.find(mesh); - if (found == meshes.end()) - meshes.emplace(std::move(mesh),std::pair(std::move(shapepair.second),shapedef->type)); + // + auto& instances = ctx.scene->getInstances(); + instances.reserve(result.shapegroups.size()); + auto addToScene = [&](const CElementShape* shape, core::smart_refctd_ptr&& collection)->void + { + assert(shape && collection); + auto targets = createMorphTargets(std::move(collection)); + if (!targets) + { + _params.logger.log("Failed to create ICPUMorphTargets for Shape with id %s",system::ILogger::ELL_ERROR,shape->id.c_str()); + return; } - } + const auto index = instances.size(); + instances.resize(index+1); + instances.getMorphTargets()[index] = std::move(targets); + // TODO: add materials (incl emission) to the instances + /* + auto emitter = shape->obtainEmitter(); + auto bsdf = getBSDFtreeTraversal(ctx, shape->bsdf, &emitter, getAbsoluteTransform()); + + SContext::SInstanceData instance( + tform, + bsdf, + #if defined(_NBL_DEBUG) || defined(_NBL_RELWITHDEBINFO) + shape->bsdf ? shape->bsdf->id : "", + #endif + emitter, + CElementEmitter{} // no backface emission + ); + */ + instances.getInitialTransforms()[index] = shape->getTransform(); + }; - parserManager.m_metadata->reserveMeshStorage(meshes.size(),ctx.mapMesh2instanceData.size()); - for (auto& mesh : meshes) + // first go over all actually used shapes which are not shapegroups (regular shapes and instances) + for (auto& shapepair : result.shapegroups) { - auto instances_rng = ctx.mapMesh2instanceData.equal_range(mesh.first.get()); - assert(instances_rng.first!=instances_rng.second); + auto* shapedef = shapepair.element; + // this should be filtered out while parsing and we should just assert it + if (shapedef->type==CElementShape::Type::SHAPEGROUP) + continue; - const uint32_t instanceCount = parserManager.m_metadata->addMeshMeta(mesh.first.get(),std::move(mesh.second.first),mesh.second.second,instances_rng.first,instances_rng.second); - for (auto mb : mesh.first.get()->getMeshBuffers()) - mb->setInstanceCount(instanceCount); + if (shapedef->type!=CElementShape::Type::INSTANCE) + { + auto geometry = ctx.loadBasicShape(_hierarchyLevel,shapedef); + if (!geometry) + continue; + auto collection = core::make_smart_refctd_ptr(); + if (!collection) + { + _params.logger.log("Failed to create an ICPUGeometryCollection non-Instanced Shape with id %s",system::ILogger::ELL_ERROR,shapedef->id.c_str()); + continue; + } + // we don't put a transform on the geometry, because we want the transform on the instance + collection->getGeometries()->push_back({.geometry=std::move(geometry)}); + addToScene(shapedef,std::move(collection)); + } + else // mitsuba is weird and lists instances under a shapegroup instead of having instances reference the shapegroup + { + // get group reference + const CElementShape* parent = shapedef->instance.parent; + if (!parent) // we should probably assert this + continue; + assert(parent->type==CElementShape::Type::SHAPEGROUP); + auto collection = ctx.loadShapeGroup(_hierarchyLevel,&parent->shapegroup); + addToScene(shapedef,std::move(collection)); + } } -#endif + result.shapegroups.clear(); #if 0 // TODO: put IR and stuff in metadata so that we can recompile the materials after load auto compResult = ctx.backend.compile(&ctx.backend_ctx, ctx.ir.get(), decltype(ctx.backend)::EGST_PRESENT_WITH_AOV_EXTRACTION); ctx.backend_ctx.vt.commitAll(); - auto pipelineLayout = createPipelineLayout(m_assetMgr, ctx.backend_ctx.vt.getCPUVirtualTexture()); auto fragShader = createFragmentShader(compResult, ctx.backend_ctx.vt.getCPUVirtualTexture()->getFloatViews().size()); auto ds0 = createDS0(ctx, pipelineLayout.get(), compResult, meshes.begin(), meshes.end()); auto basePipeline = createPipeline( @@ -373,186 +400,518 @@ SAssetBundle CMitsubaLoader::loadAsset(system::IFile* _file, const IAssetLoader: { ctx.meta->addDerivMapMeta(derivMap.first.get(), derivMap.second); } - - auto meshSmartPtrArray = core::make_refctd_dynamic_array(meshes.size()); - auto meshSmartPtrArrayIt = meshSmartPtrArray->begin(); - for (const auto& mesh_ : meshes) - { - for (auto mb : mesh_.first.get()->getMeshBuffers()) - { - const auto* prevPipeline = mb->getPipeline(); - SContext::SPipelineCacheKey cacheKey; - cacheKey.vtxParams = prevPipeline->getVertexInputParams(); - cacheKey.primParams = prevPipeline->getPrimitiveAssemblyParams(); - auto found = ctx.pipelineCache.find(cacheKey); - core::smart_refctd_ptr pipeline; - if (found != ctx.pipelineCache.end()) - { - pipeline = found->second; - } - else - { - pipeline = core::smart_refctd_ptr_static_cast(//shallow copy because we're only going to override parameter structs - basePipeline->clone(0u) - ); - pipeline->getVertexInputParams() = cacheKey.vtxParams; - pipeline->getPrimitiveAssemblyParams() = cacheKey.primParams; - ctx.pipelineCache.insert({ cacheKey, pipeline }); - } - - mb->setPipeline(core::smart_refctd_ptr(pipeline)); - } - *(meshSmartPtrArrayIt++) = std::move(mesh_.first); - } - - parserManager.m_metadata->reservePplnStorage(ctx.pipelineCache.size(),core::smart_refctd_ptr(IRenderpassIndependentPipelineLoader::m_basicViewParamsSemantics)); - for (auto& ppln : ctx.pipelineCache) - parserManager.m_metadata->addPplnMeta(ppln.second.get(),core::smart_refctd_ptr(ds0)); - - for (const auto& emitter : parserManager.m_metadata->m_global.m_emitters) +#endif + for (const auto& emitter : result.emitters) { - if(emitter.type == ext::MitsubaLoader::CElementEmitter::Type::ENVMAP) + if(emitter.element->type == ext::MitsubaLoader::CElementEmitter::Type::ENVMAP) { - assert(emitter.envmap.filename.type==ext::MitsubaLoader::SPropertyElementData::Type::STRING); - auto envfilename = emitter.envmap.filename.svalue; - SAssetBundle envmapImageBundle = interm_getAssetInHierarchy(m_assetMgr, envfilename, ctx.inner.params, _hierarchyLevel, ctx.override_); + const auto& envmap = emitter.element->envmap; +#if 0 + SAssetBundle envmapImageBundle = interm_getAssetInHierarchy(m_assetMgr,envmap.filename,ctx.inner.params,_hierarchyLevel,ctx.override_); auto contentRange = envmapImageBundle.getContents(); if (contentRange.empty()) { os::Printer::log(std::string("[ERROR] Could Not Find Envmap Image: ") + envfilename, ELL_ERROR); continue; } - if (envmapImageBundle.getAssetType()!=asset::IAsset::ET_IMAGE) + core::smart_refctd_ptr view = {}; + switch(envmapImageBundle.getAssetType()) { - os::Printer::log("[ERROR] Loaded an Asset for the Envmap but it wasn't an image, was E_ASSET_TYPE " + std::to_string(envmapImageBundle.getAssetType()), ELL_ERROR); - continue; + case asset::IAsset::ET_IMAGE: + { + // TODO: create image view + } + [[fallthrough]]; + case asset::IAsset::ET_IMAGE_VIEW: + view = core::smart_refctd_ptr_static_cast(*contentRange.begin()); + break; + default: + os::Printer::log("[ERROR] Loaded an Asset for the Envmap but it wasn't an image, was E_ASSET_TYPE " + std::to_string(envmapImageBundle.getAssetType()), ELL_ERROR); + break; } - parserManager.m_metadata->m_global.m_envMapImages.push_back(core::smart_refctd_ptr_static_cast(*contentRange.begin())); + ctx.scene->addEnvLight(ICPUScene::EEnvLightType::SphereMap,std::move(view)); +#endif } } -#endif - return asset::SAssetBundle(std::move(result.metadata),{std::move(scene)}); + + ctx.transferMetadata(); + return asset::SAssetBundle(std::move(result.metadata),{std::move(ctx.scene)}); } } #if 0 -core::vector CMitsubaLoader::getMesh(SContext& ctx, uint32_t hierarchyLevel, CElementShape* shape) +void CMitsubaLoader::cacheEmissionProfile(SContext& ctx, const CElementEmissionProfile* profile) { - if (!shape) - return {}; + if (!profile) + return; - if (shape->type!=CElementShape::Type::INSTANCE) - return {loadBasicShape(ctx, hierarchyLevel, shape, core::matrix3x4SIMD())}; - else + auto params = ctx.inner.params; + params.loaderFlags = asset::IAssetLoader::ELPF_LOAD_METADATA_ONLY; + + auto assetLoaded = interm_getAssetInHierarchy(m_assetMgr, profile->filename, params, 0u, ctx.override_); + + if (!assetLoaded.getMetadata()) { - core::matrix3x4SIMD relTform = shape->getAbsoluteTransform(); - // get group reference - const CElementShape* parent = shape->instance.parent; - if (!parent) - return {}; - assert(parent->type==CElementShape::Type::SHAPEGROUP); - const CElementShape::ShapeGroup* shapegroup = &parent->shapegroup; - - return loadShapeGroup(ctx, hierarchyLevel, shapegroup, relTform); + os::Printer::log("[ERROR] Could Not Find Emission Profile: " + profile->filename, ELL_ERROR); + return; } } -core::vector CMitsubaLoader::loadShapeGroup(SContext& ctx, uint32_t hierarchyLevel, const CElementShape::ShapeGroup* shapegroup, const core::matrix3x4SIMD& relTform) +void CMitsubaLoader::cacheTexture(SContext& ctx, uint32_t hierarchyLevel, const CElementTexture* tex, const CMitsubaMaterialCompilerFrontend::E_IMAGE_VIEW_SEMANTIC semantic) { - // @Crisspl why no group cache? - // find group - //auto found = ctx.groupCache.find(shapegroup); - //if (found != ctx.groupCache.end()) - // return found->second; - - const auto children = shapegroup->children; + if (!tex) + return; - core::vector meshes; - for (auto i=0u; ichildCount; i++) + switch (tex->type) { - auto child = children[i]; - if (!child) - continue; - - assert(child->type!=CElementShape::Type::INSTANCE); - if (child->type != CElementShape::Type::SHAPEGROUP) { - auto lowermesh = loadBasicShape(ctx, hierarchyLevel, child, relTform); - meshes.push_back(std::move(lowermesh)); - } - else { - auto lowermeshes = loadShapeGroup(ctx, hierarchyLevel, &child->shapegroup, relTform); - meshes.insert(meshes.begin(), std::make_move_iterator(lowermeshes.begin()), std::make_move_iterator(lowermeshes.end())); - } - } - - //ctx.groupCache.insert({shapegroup,meshes}); - return meshes; -} + case CElementTexture::Type::BITMAP: + { + // get sampler parameters + const auto samplerParams = ctx.computeSamplerParameters(tex->bitmap); -static core::smart_refctd_ptr createMeshFromGeomCreatorReturnType(IGeometryCreator::return_type&& _data, asset::IAssetManager* _manager) -{ - //creating pipeline just to forward vtx and primitive params - auto pipeline = core::make_smart_refctd_ptr( - nullptr, nullptr, nullptr, //no layout nor shaders - _data.inputParams, - asset::SBlendParams(), - _data.assemblyParams, - asset::SRasterizationParams() - ); + // search the cache for the imageview + const auto cacheKey = ctx.imageViewCacheKey(tex->bitmap,semantic); + const asset::IAsset::E_TYPE types[]{asset::IAsset::ET_IMAGE_VIEW,asset::IAsset::ET_TERMINATING_ZERO}; + // could not find view in the cache + if (ctx.override_->findCachedAsset(cacheKey,types,ctx.inner,hierarchyLevel).getContents().empty()) + { + ICPUImageView::SCreationParams viewParams = {}; + // find or restore image from cache + { + auto loadParams = ctx.inner.params; + // always restore, the only reason we haven't found a view is because either the image wasnt loaded yet, or its going to be processed with channel extraction or derivative mapping + const uint32_t restoreLevels = semantic==CMitsubaMaterialCompilerFrontend::EIVS_IDENTITIY&&tex->bitmap.channel==CElementTexture::Bitmap::CHANNEL::INVALID ? 0u:2u; // all the way to the buffer providing the pixels + loadParams.restoreLevels = std::max(loadParams.restoreLevels,hierarchyLevel+restoreLevels); + // load using the actual filename, not the cache key + asset::SAssetBundle bundle = interm_getAssetInHierarchy(m_assetMgr,tex->bitmap.filename.svalue,loadParams,hierarchyLevel,ctx.override_); - auto mb = core::make_smart_refctd_ptr( - nullptr, nullptr, - _data.bindings, std::move(_data.indexBuffer) - ); - mb->setIndexCount(_data.indexCount); - mb->setIndexType(_data.indexType); - mb->setBoundingBox(_data.bbox); - mb->setPipeline(std::move(pipeline)); - constexpr auto NORMAL_ATTRIBUTE = 3; - mb->setNormalAttributeIx(NORMAL_ATTRIBUTE); + // check if found + auto contentRange = bundle.getContents(); + if (contentRange.empty()) + { + os::Printer::log("[ERROR] Could Not Find Texture: "+cacheKey,ELL_ERROR); + return; + } + auto asset = contentRange.begin()[0]; + if (asset->getAssetType()!=asset::IAsset::ET_IMAGE) + { + os::Printer::log("[ERROR] Loaded an Asset but it wasn't a texture, was E_ASSET_TYPE "+std::to_string(asset->getAssetType()),ELL_ERROR); + return; + } - auto mesh = core::make_smart_refctd_ptr(); - mesh->getMeshBufferVector().push_back(std::move(mb)); + viewParams.image = core::smart_refctd_ptr_static_cast(asset); + } + // adjust gamma on pixels (painful and long process) + if (!std::isnan(tex->bitmap.gamma)) + { + _NBL_DEBUG_BREAK_IF(true); // TODO : use an image filter (unify with the below maybe?)! + } + switch (semantic) + { + case CMitsubaMaterialCompilerFrontend::EIVS_IDENTITIY: + case CMitsubaMaterialCompilerFrontend::EIVS_BLEND_WEIGHT: + { + switch (tex->bitmap.channel) + { + // no GL_R8_SRGB support yet + case CElementTexture::Bitmap::CHANNEL::R: + viewParams.image = createSingleChannelImage(viewParams.image.get(),asset::ICPUImageView::SComponentMapping::ES_R); + break; + case CElementTexture::Bitmap::CHANNEL::G: + viewParams.image = createSingleChannelImage(viewParams.image.get(),asset::ICPUImageView::SComponentMapping::ES_G); + break; + case CElementTexture::Bitmap::CHANNEL::B: + viewParams.image = createSingleChannelImage(viewParams.image.get(),asset::ICPUImageView::SComponentMapping::ES_B); + break; + case CElementTexture::Bitmap::CHANNEL::A: + viewParams.image = createSingleChannelImage(viewParams.image.get(),asset::ICPUImageView::SComponentMapping::ES_A); + break; + /* special conversions needed to CIE space + case CElementTexture::Bitmap::CHANNEL::X: + case CElementTexture::Bitmap::CHANNEL::Y: + case CElementTexture::Bitmap::CHANNEL::Z:*/ + case CElementTexture::Bitmap::CHANNEL::INVALID: + [[fallthrough]]; + default: + if (semantic==CMitsubaMaterialCompilerFrontend::EIVS_BLEND_WEIGHT && asset::getFormatChannelCount(viewParams.image->getCreationParameters().format)<3u) + viewParams.image = createSingleChannelImage(viewParams.image.get(),asset::ICPUImageView::SComponentMapping::ES_IDENTITY); + break; + } + } + break; + case CMitsubaMaterialCompilerFrontend::EIVS_NORMAL_MAP: + viewParams.image = createDerivMap(ctx,viewParams.image.get(),samplerParams,true); + break; + case CMitsubaMaterialCompilerFrontend::EIVS_BUMP_MAP: + viewParams.image = createDerivMap(ctx,viewParams.image.get(),samplerParams,false); + break; + default: + _NBL_DEBUG_BREAK_IF(true); + assert(false); + break; + } + // get rest of view params and insert into cache + { + viewParams.flags = static_cast(0); + viewParams.viewType = IImageView::ET_2D; + viewParams.format = viewParams.image->getCreationParameters().format; + viewParams.subresourceRange.aspectMask = static_cast(0); + viewParams.subresourceRange.levelCount = viewParams.image->getCreationParameters().mipLevels; + viewParams.subresourceRange.layerCount = 1u; + //! TODO: this stuff (custom shader sampling code?) + _NBL_DEBUG_BREAK_IF(tex->bitmap.uoffset != 0.f); + _NBL_DEBUG_BREAK_IF(tex->bitmap.voffset != 0.f); + _NBL_DEBUG_BREAK_IF(tex->bitmap.uscale != 1.f); + _NBL_DEBUG_BREAK_IF(tex->bitmap.vscale != 1.f); - return mesh; + asset::SAssetBundle viewBundle(nullptr,{ICPUImageView::create(std::move(viewParams))}); + ctx.override_->insertAssetIntoCache(std::move(viewBundle),cacheKey,ctx.inner,hierarchyLevel); + } + } + } + break; + case CElementTexture::Type::SCALE: + cacheTexture(ctx,hierarchyLevel,tex->scale.texture,semantic); + break; + default: + _NBL_DEBUG_BREAK_IF(true); + break; + } } -SContext::shape_ass_type CMitsubaLoader::loadBasicShape(SContext& ctx, uint32_t hierarchyLevel, CElementShape* shape, const core::matrix3x4SIMD& relTform) +auto CMitsubaLoader::getBSDFtreeTraversal(SContext& ctx, const CElementBSDF* bsdf, const CElementEmitter* emitter, core::matrix4SIMD tform) -> SContext::bsdf_type { - constexpr uint32_t UV_ATTRIB_ID = 2u; + if (!bsdf) + { + static auto blackBSDF = []() -> auto + { + CElementBSDF retval("nullptr BSDF"); + retval.type = CElementBSDF::Type::DIFFUSE, + retval.diffuse.reflectance = 0.f; + retval.diffuse.alpha = 0.f; + return retval; + }(); + bsdf = &blackBSDF; + } - auto addInstance = [shape,&ctx,&relTform,this](SContext::shape_ass_type& mesh) + auto found = ctx.instrStreamCache.find(bsdf); + if (found == ctx.instrStreamCache.end()) + found = ctx.instrStreamCache.insert({ bsdf,genBSDFtreeTraversal(ctx, bsdf) }).first; + auto compiled_bsdf = found->second; + + // TODO cache the IR Node + CMitsubaMaterialCompilerFrontend::EmitterNode* emitterIRNode = nullptr; + if (emitter->type == CElementEmitter::AREA) { - auto emitter = shape->obtainEmitter(); - core::matrix3x4SIMD tform = core::concatenateBFollowedByA(relTform, shape->getAbsoluteTransform()); - auto bsdf = getBSDFtreeTraversal(ctx, shape->bsdf, &emitter, core::matrix4SIMD(tform)); + cacheEmissionProfile(ctx,emitter->area.emissionProfile); + emitterIRNode = ctx.frontend.createEmitterNode(ctx.ir.get(),emitter,tform); + } - SContext::SInstanceData instance( - tform, - bsdf, -#if defined(_NBL_DEBUG) || defined(_NBL_RELWITHDEBINFO) - shape->bsdf ? shape->bsdf->id : "", -#endif - emitter, - CElementEmitter{} - ); - ctx.mapMesh2instanceData.insert({ mesh.get(), instance }); + // A new root node gets made for every {bsdf,emitter} combo + using node_t = asset::material_compiler::IR::INode; + auto createNewRootNode = [&ctx,emitterIRNode](node_t* realBxDFRoot, node_t* emitter=nullptr) -> node_t* + { + // TODO: cache the combo! + auto newRoot = ctx.ir->allocNode(); + if (emitter) + newRoot->children = node_t::createChildrenArray(realBxDFRoot,emitter); + else + newRoot->children = node_t::createChildrenArray(realBxDFRoot); + ctx.ir->addRootNode(newRoot); + return newRoot; }; - auto found = ctx.shapeCache.find(shape); - if (found != ctx.shapeCache.end()) { - addInstance(found->second); - - return found->second; - } + return { createNewRootNode(compiled_bsdf.front,emitterIRNode), createNewRootNode(compiled_bsdf.back)}; +} - auto loadModel = [&](const ext::MitsubaLoader::SPropertyElementData& filename, int64_t index=-1) -> core::smart_refctd_ptr +auto CMitsubaLoader::genBSDFtreeTraversal(SContext& ctx, const CElementBSDF* _bsdf) -> SContext::bsdf_type +{ { - assert(filename.type==ext::MitsubaLoader::SPropertyElementData::Type::STRING); - auto loadParams = ctx.inner.params; - loadParams.loaderFlags = static_cast(loadParams.loaderFlags | IAssetLoader::ELPF_RIGHT_HANDED_MESHES); - auto retval = interm_getAssetInHierarchy(m_assetMgr, filename.svalue, loadParams, hierarchyLevel/*+ICPUScene::MESH_HIERARCHY_LEVELS_BELOW*/, ctx.override_); - if (retval.getContents().empty()) + auto cachePropertyTexture = [&](const auto& const_or_tex, const CMitsubaMaterialCompilerFrontend::E_IMAGE_VIEW_SEMANTIC semantic=CMitsubaMaterialCompilerFrontend::EIVS_IDENTITIY) -> void + { + if (const_or_tex.value.type==SPropertyElementData::INVALID) + cacheTexture(ctx,0u,const_or_tex.texture,semantic); + }; + + core::stack stack; + stack.push(_bsdf); + + while (!stack.empty()) + { + auto* bsdf = stack.top(); + stack.pop(); + // + switch (bsdf->type) + { + case CElementBSDF::COATING: + for (uint32_t i = 0u; i < bsdf->coating.childCount; ++i) + stack.push(bsdf->coating.bsdf[i]); + break; + case CElementBSDF::ROUGHCOATING: + case CElementBSDF::BUMPMAP: + case CElementBSDF::BLEND_BSDF: + case CElementBSDF::MIXTURE_BSDF: + case CElementBSDF::MASK: + case CElementBSDF::TWO_SIDED: + for (uint32_t i = 0u; i < bsdf->meta_common.childCount; ++i) + stack.push(bsdf->meta_common.bsdf[i]); + default: + break; + } + // + switch (bsdf->type) + { + case CElementBSDF::DIFFUSE: + case CElementBSDF::ROUGHDIFFUSE: + cachePropertyTexture(bsdf->diffuse.reflectance); + cachePropertyTexture(bsdf->diffuse.alpha); + break; + case CElementBSDF::DIFFUSE_TRANSMITTER: + cachePropertyTexture(bsdf->difftrans.transmittance); + break; + case CElementBSDF::DIELECTRIC: + case CElementBSDF::THINDIELECTRIC: + case CElementBSDF::ROUGHDIELECTRIC: + cachePropertyTexture(bsdf->dielectric.alphaU); + if (bsdf->dielectric.distribution == CElementBSDF::RoughSpecularBase::ASHIKHMIN_SHIRLEY) + cachePropertyTexture(bsdf->dielectric.alphaV); + break; + case CElementBSDF::CONDUCTOR: + case CElementBSDF::ROUGHCONDUCTOR: + cachePropertyTexture(bsdf->conductor.alphaU); + if (bsdf->conductor.distribution == CElementBSDF::RoughSpecularBase::ASHIKHMIN_SHIRLEY) + cachePropertyTexture(bsdf->conductor.alphaV); + break; + case CElementBSDF::PLASTIC: + case CElementBSDF::ROUGHPLASTIC: + cachePropertyTexture(bsdf->plastic.diffuseReflectance); + cachePropertyTexture(bsdf->plastic.alphaU); + if (bsdf->plastic.distribution == CElementBSDF::RoughSpecularBase::ASHIKHMIN_SHIRLEY) + cachePropertyTexture(bsdf->plastic.alphaV); + break; + case CElementBSDF::BUMPMAP: + cacheTexture(ctx,0u,bsdf->bumpmap.texture,bsdf->bumpmap.wasNormal ? CMitsubaMaterialCompilerFrontend::EIVS_NORMAL_MAP:CMitsubaMaterialCompilerFrontend::EIVS_BUMP_MAP); + break; + case CElementBSDF::BLEND_BSDF: + cachePropertyTexture(bsdf->blendbsdf.weight,CMitsubaMaterialCompilerFrontend::EIVS_BLEND_WEIGHT); + break; + case CElementBSDF::MASK: + cachePropertyTexture(bsdf->mask.opacity,CMitsubaMaterialCompilerFrontend::EIVS_BLEND_WEIGHT); + break; + default: break; + } + } + } + + return ctx.frontend.compileToIRTree(ctx.ir.get(), _bsdf); +} + + +// Also sets instance data buffer offset into meshbuffers' base instance +template +inline core::smart_refctd_ptr CMitsubaLoader::createDS0(const SContext& _ctx, asset::ICPUPipelineLayout* _layout, const asset::material_compiler::CMaterialCompilerGLSLBackendCommon::result_t& _compResult, Iter meshBegin, Iter meshEnd) +{ + +#ifdef DEBUG_MITSUBA_LOADER + std::ofstream ofile("log.txt"); +#endif + core::vector instanceData; + for (auto it=meshBegin; it != meshEnd; ++it) + { + auto mesh = it->first.get(); + + core::vectorSIMDf emissive; + for (auto& mb : mesh->getMeshBuffers()) + mb->setBaseInstance(instanceData.size()); + auto* meshMeta = _ctx.meta->getAssetSpecificMetadata(mesh); + auto baseInstanceDataIt = meshMeta->m_instances.begin(); + for (const auto& inst : meshMeta->m_instanceAuxData) + { + nbl_glsl_ext_Mitsuba_Loader_instance_data_t instData; + + instData.tform = baseInstanceDataIt->worldTform; + instData.tform.getSub3x3InverseTranspose(reinterpret_cast(instData.normalMatrixRow0)); + reinterpret_cast(instData.determinantSignBit) = instData.tform.getPseudoDeterminant().x; + instData.determinantSignBit &= 0x80000000; + + const auto& bsdf = inst.bsdf; + auto bsdf_front = bsdf.front; + auto bsdf_back = bsdf.back; + auto material_it = _compResult.materials.find(bsdf_front); + { + const asset::material_compiler::oriented_material_t* material = + (material_it != _compResult.materials.end()) ? &material_it->second : nullptr; + + if (material) { +#ifdef DEBUG_MITSUBA_LOADER + //os::Printer::log("Debug print front BSDF with id = ", std::to_string(&bsdf), ELL_INFORMATION); + + ofile << "Debug print front BSDF with id = " << &bsdf << std::endl; + _ctx.backend.debugPrint(ofile, *material, _compResult, &_ctx.backend_ctx); + +#endif + instData.material.front = *material; + } + } + material_it = _compResult.materials.find(bsdf_back); + { + const asset::material_compiler::oriented_material_t* material = + (material_it != _compResult.materials.end()) ? &material_it->second : nullptr; + + if (material) + { +#ifdef DEBUG_MITSUBA_LOADER + //os::Printer::log("Debug print back BSDF with id = ", std::to_string(&bsdf), ELL_INFORMATION); + ofile << "Debug print back BSDF with id = " << &bsdf << std::endl; + _ctx.backend.debugPrint(ofile, *material, _compResult, &_ctx.backend_ctx); +#endif + + instData.material.back = *material; + } + } + + instanceData.push_back(instData); + baseInstanceDataIt++; + } + } +#ifdef DEBUG_MITSUBA_LOADER + ofile.close(); +#endif + d = ds0->getDescriptors(INSTANCE_DATA_BINDING).begin(); + { + auto instDataBuf = core::make_smart_refctd_ptr(instanceData.size()*sizeof(nbl_glsl_ext_Mitsuba_Loader_instance_data_t)); + memcpy(instDataBuf->getPointer(), instanceData.data(), instDataBuf->getSize()); + + d->buffer.offset = 0u; + d->buffer.size = instDataBuf->getSize(); + d->desc = std::move(instDataBuf); + } + + return ds0; +} +#endif + +using namespace std::string_literals; + +SContext::SContext( +// const asset::IGeometryCreator* _geomCreator, +// const asset::IMeshManipulator* _manipulator, + const asset::IAssetLoader::SAssetLoadContext& _ctx, + asset::IAssetLoader::IAssetLoaderOverride* _override, + CMitsubaMetadata* _metadata +) : /*creator(_geomCreator), manipulator(_manipulator),*/ inner(_ctx), override_(_override), meta(_metadata) +//,ir(core::make_smart_refctd_ptr()), frontend(this) +{ + auto materialPool = material_compiler3::CTrueIR::create(); + scene = ICPUScene::create(core::smart_refctd_ptr(materialPool)); // TODO: feed it max shapes per group + frontIR = material_compiler3::CFrontendIR::create(); +} + +auto SContext::loadShapeGroup(const uint32_t hierarchyLevel, const CElementShape::ShapeGroup* shapegroup) -> SContext::group_ass_type +{ + auto found = groupCache.find(shapegroup); + if (found!=groupCache.end()) + return found->second; + + auto collection = core::make_smart_refctd_ptr(); + if (!collection) + inner.params.logger.log("Failed to create an ICPUGeometryCollection for Shape Group",system::ILogger::ELL_ERROR); + else + { + auto* geometries = collection->getGeometries(); + const auto children = shapegroup->children; + for (auto i=0u; ichildCount; i++) + { + auto child = children[i]; + if (!child) + continue; + + assert(child->type!=CElementShape::Type::INSTANCE); + if (child->type!=CElementShape::Type::SHAPEGROUP) + { + auto geometry = loadBasicShape(hierarchyLevel,child); + if (geometry) + geometries->push_back({.transform=child->getTransform(),.geometry=std::move(geometry)}); + } + else + { + auto nestedCollection = loadShapeGroup(hierarchyLevel,&child->shapegroup); + if (!nestedCollection) + continue; + auto* nestedGeometries = nestedCollection->getGeometries(); + for (auto& ref : *nestedGeometries) + { + auto& newRef = geometries->emplace_back(std::move(ref)); + // thankfully because SHAPEGROUPS are not allowed to have transforms we don't need to rack them up + //if (newRef.hasTransform()) + // newRef.transform = hlsl::mul(thisTransform,newRef.transform); + //else + // newRef.transform = thisTransform; + } + } + } + groupCache.insert({shapegroup,collection}); + } + return collection; +} + +#if 0 +static core::smart_refctd_ptr createMeshFromGeomCreatorReturnType(IGeometryCreator::return_type&& _data, asset::IAssetManager* _manager) +{ + //creating pipeline just to forward vtx and primitive params + auto pipeline = core::make_smart_refctd_ptr( + nullptr, nullptr, nullptr, //no layout nor shaders + _data.inputParams, + asset::SBlendParams(), + _data.assemblyParams, + asset::SRasterizationParams() + ); + + auto mb = core::make_smart_refctd_ptr( + nullptr, nullptr, + _data.bindings, std::move(_data.indexBuffer) + ); + mb->setIndexCount(_data.indexCount); + mb->setIndexType(_data.indexType); + mb->setBoundingBox(_data.bbox); + mb->setPipeline(std::move(pipeline)); + constexpr auto NORMAL_ATTRIBUTE = 3; + mb->setNormalAttributeIx(NORMAL_ATTRIBUTE); + + auto mesh = core::make_smart_refctd_ptr(); + mesh->getMeshBufferVector().push_back(std::move(mb)); + + return mesh; +} +#endif + +auto SContext::loadBasicShape(const uint32_t hierarchyLevel, const CElementShape* shape) -> SContext::shape_ass_type +{ + auto found = shapeCache.find(shape); + if (found!=shapeCache.end()) + return found->second.geom; + + core::smart_refctd_ptr geo; + auto exiter = core::makeRAIIExiter<>([&]()->void + { + if (geo) + return; + this->inner.params.logger.log("Failed to Load/Create Basic non-Instanced Shape with id %s",system::ILogger::ELL_ERROR,shape->id.c_str()); + } + ); + +#if 0 + constexpr uint32_t UV_ATTRIB_ID = 2u; + + + + auto loadModel = [&](const ext::MitsubaLoader::SPropertyElementData& filename, int64_t index=-1) -> core::smart_refctd_ptr + { + assert(filename.type==ext::MitsubaLoader::SPropertyElementData::Type::STRING); + auto loadParams = ctx.inner.params; + loadParams.loaderFlags = static_cast(loadParams.loaderFlags | IAssetLoader::ELPF_RIGHT_HANDED_MESHES); + auto retval = interm_getAssetInHierarchy(m_assetMgr, filename.svalue, loadParams, hierarchyLevel/*+ICPUScene::MESH_HIERARCHY_LEVELS_BELOW*/, ctx.override_); + if (retval.getContents().empty()) { os::Printer::log(std::string("[ERROR] Could Not Find Mesh: ") + filename.svalue, ELL_ERROR); return nullptr; @@ -586,21 +945,21 @@ SContext::shape_ass_type CMitsubaLoader::loadBasicShape(SContext& ctx, uint32_t else return nullptr; }; - - core::smart_refctd_ptr mesh,newMesh; +#endif bool flipNormals = false; bool faceNormals = false; - float maxSmoothAngle = NAN; + float maxSmoothAngle = hlsl::bit_cast(hlsl::numeric_limits::quiet_NaN); switch (shape->type) { +#if 0 case CElementShape::Type::CUBE: { auto cubeData = ctx.creator->createCubeMesh(core::vector3df(2.f)); mesh = createMeshFromGeomCreatorReturnType(ctx.creator->createCubeMesh(core::vector3df(2.f)), m_assetMgr); flipNormals = flipNormals!=shape->cube.flipNormals; - } break; + } case CElementShape::Type::SPHERE: mesh = createMeshFromGeomCreatorReturnType(ctx.creator->createSphereMesh(1.f,64u,64u), m_assetMgr); flipNormals = flipNormals!=shape->sphere.flipNormals; @@ -642,6 +1001,8 @@ SContext::shape_ass_type CMitsubaLoader::loadBasicShape(SContext& ctx, uint32_t mesh = createMeshFromGeomCreatorReturnType(ctx.creator->createDiskMesh(1.f,64u), m_assetMgr); flipNormals = flipNormals!=shape->disk.flipNormals; break; +#endif +#if 0 case CElementShape::Type::OBJ: mesh = loadModel(shape->obj.filename); flipNormals = flipNormals!=shape->obj.flipNormals; @@ -717,489 +1078,63 @@ SContext::shape_ass_type CMitsubaLoader::loadBasicShape(SContext& ctx, uint32_t faceNormals = shape->serialized.faceNormals; maxSmoothAngle = shape->serialized.maxSmoothAngle; break; +#endif case CElementShape::Type::SHAPEGROUP: [[fallthrough]]; case CElementShape::Type::INSTANCE: assert(false); break; default: - _NBL_DEBUG_BREAK_IF(true); +// _NBL_DEBUG_BREAK_IF(true); break; } // - if (!mesh) - return nullptr; - - // mesh including meshbuffers needs to be cloned because instance counts and base instances will be changed - if (!newMesh) - newMesh = core::smart_refctd_ptr_static_cast(mesh->clone(1u)); - // flip normals if necessary - if (flipNormals) + if (geo) { - for (auto& meshbuffer : mesh->getMeshBufferVector()) +#if 0 + // mesh including meshbuffers needs to be cloned because instance counts and base instances will be changed + if (!newMesh) + newMesh = core::smart_refctd_ptr_static_cast(mesh->clone(1u)); + // flip normals if necessary + if (flipNormals) { - auto binding = meshbuffer->getIndexBufferBinding(); - binding.buffer = core::smart_refctd_ptr_static_cast(binding.buffer->clone(0u)); - meshbuffer->setIndexBufferBinding(std::move(binding)); - ctx.manipulator->flipSurfaces(meshbuffer.get()); - } - } - // recompute normalis if necessary - if (faceNormals || !std::isnan(maxSmoothAngle)) - for (auto& meshbuffer : mesh->getMeshBufferVector()) - { - const float smoothAngleCos = cos(core::radians(maxSmoothAngle)); - - // TODO: make these mesh manipulator functions const-correct - auto newMeshBuffer = ctx.manipulator->createMeshBufferUniquePrimitives(meshbuffer.get()); - ctx.manipulator->filterInvalidTriangles(newMeshBuffer.get()); - ctx.manipulator->calculateSmoothNormals(newMeshBuffer.get(), false, 0.f, newMeshBuffer->getNormalAttributeIx(), - [&](const asset::IMeshManipulator::SSNGVertexData& a, const asset::IMeshManipulator::SSNGVertexData& b, asset::ICPUMeshBuffer* buffer) - { - if (faceNormals) - return a.indexOffset == b.indexOffset; - else - return core::dot(a.parentTriangleFaceNormal, b.parentTriangleFaceNormal).x >= smoothAngleCos; - }); - meshbuffer = std::move(newMeshBuffer); - } - IMeshManipulator::recalculateBoundingBox(newMesh.get()); - mesh = std::move(newMesh); - - addInstance(mesh); - // cache and return - ctx.shapeCache.insert({ shape,mesh }); - return mesh; -} - -void CMitsubaLoader::cacheEmissionProfile(SContext& ctx, const CElementEmissionProfile* profile) -{ - if (!profile) - return; - - auto params = ctx.inner.params; - params.loaderFlags = asset::IAssetLoader::ELPF_LOAD_METADATA_ONLY; - - auto assetLoaded = interm_getAssetInHierarchy(m_assetMgr, profile->filename, params, 0u, ctx.override_); - - if (!assetLoaded.getMetadata()) - { - os::Printer::log("[ERROR] Could Not Find Emission Profile: " + profile->filename, ELL_ERROR); - return; - } -} - -void CMitsubaLoader::cacheTexture(SContext& ctx, uint32_t hierarchyLevel, const CElementTexture* tex, const CMitsubaMaterialCompilerFrontend::E_IMAGE_VIEW_SEMANTIC semantic) -{ - if (!tex) - return; - - switch (tex->type) - { - case CElementTexture::Type::BITMAP: - { - // get sampler parameters - const auto samplerParams = ctx.computeSamplerParameters(tex->bitmap); - - // search the cache for the imageview - const auto cacheKey = ctx.imageViewCacheKey(tex->bitmap,semantic); - const asset::IAsset::E_TYPE types[]{asset::IAsset::ET_IMAGE_VIEW,asset::IAsset::ET_TERMINATING_ZERO}; - // could not find view in the cache - if (ctx.override_->findCachedAsset(cacheKey,types,ctx.inner,hierarchyLevel).getContents().empty()) - { - ICPUImageView::SCreationParams viewParams = {}; - // find or restore image from cache - { - auto loadParams = ctx.inner.params; - // always restore, the only reason we haven't found a view is because either the image wasnt loaded yet, or its going to be processed with channel extraction or derivative mapping - const uint32_t restoreLevels = semantic==CMitsubaMaterialCompilerFrontend::EIVS_IDENTITIY&&tex->bitmap.channel==CElementTexture::Bitmap::CHANNEL::INVALID ? 0u:2u; // all the way to the buffer providing the pixels - loadParams.restoreLevels = std::max(loadParams.restoreLevels,hierarchyLevel+restoreLevels); - // load using the actual filename, not the cache key - asset::SAssetBundle bundle = interm_getAssetInHierarchy(m_assetMgr,tex->bitmap.filename.svalue,loadParams,hierarchyLevel,ctx.override_); - - // check if found - auto contentRange = bundle.getContents(); - if (contentRange.empty()) - { - os::Printer::log("[ERROR] Could Not Find Texture: "+cacheKey,ELL_ERROR); - return; - } - auto asset = contentRange.begin()[0]; - if (asset->getAssetType()!=asset::IAsset::ET_IMAGE) - { - os::Printer::log("[ERROR] Loaded an Asset but it wasn't a texture, was E_ASSET_TYPE "+std::to_string(asset->getAssetType()),ELL_ERROR); - return; - } - - viewParams.image = core::smart_refctd_ptr_static_cast(asset); - } - // adjust gamma on pixels (painful and long process) - if (!std::isnan(tex->bitmap.gamma)) - { - _NBL_DEBUG_BREAK_IF(true); // TODO : use an image filter (unify with the below maybe?)! - } - switch (semantic) - { - case CMitsubaMaterialCompilerFrontend::EIVS_IDENTITIY: - case CMitsubaMaterialCompilerFrontend::EIVS_BLEND_WEIGHT: - { - switch (tex->bitmap.channel) - { - // no GL_R8_SRGB support yet - case CElementTexture::Bitmap::CHANNEL::R: - viewParams.image = createSingleChannelImage(viewParams.image.get(),asset::ICPUImageView::SComponentMapping::ES_R); - break; - case CElementTexture::Bitmap::CHANNEL::G: - viewParams.image = createSingleChannelImage(viewParams.image.get(),asset::ICPUImageView::SComponentMapping::ES_G); - break; - case CElementTexture::Bitmap::CHANNEL::B: - viewParams.image = createSingleChannelImage(viewParams.image.get(),asset::ICPUImageView::SComponentMapping::ES_B); - break; - case CElementTexture::Bitmap::CHANNEL::A: - viewParams.image = createSingleChannelImage(viewParams.image.get(),asset::ICPUImageView::SComponentMapping::ES_A); - break; - /* special conversions needed to CIE space - case CElementTexture::Bitmap::CHANNEL::X: - case CElementTexture::Bitmap::CHANNEL::Y: - case CElementTexture::Bitmap::CHANNEL::Z:*/ - case CElementTexture::Bitmap::CHANNEL::INVALID: - [[fallthrough]]; - default: - if (semantic==CMitsubaMaterialCompilerFrontend::EIVS_BLEND_WEIGHT && asset::getFormatChannelCount(viewParams.image->getCreationParameters().format)<3u) - viewParams.image = createSingleChannelImage(viewParams.image.get(),asset::ICPUImageView::SComponentMapping::ES_IDENTITY); - break; - } - } - break; - case CMitsubaMaterialCompilerFrontend::EIVS_NORMAL_MAP: - viewParams.image = createDerivMap(ctx,viewParams.image.get(),samplerParams,true); - break; - case CMitsubaMaterialCompilerFrontend::EIVS_BUMP_MAP: - viewParams.image = createDerivMap(ctx,viewParams.image.get(),samplerParams,false); - break; - default: - _NBL_DEBUG_BREAK_IF(true); - assert(false); - break; - } - // get rest of view params and insert into cache - { - viewParams.flags = static_cast(0); - viewParams.viewType = IImageView::ET_2D; - viewParams.format = viewParams.image->getCreationParameters().format; - viewParams.subresourceRange.aspectMask = static_cast(0); - viewParams.subresourceRange.levelCount = viewParams.image->getCreationParameters().mipLevels; - viewParams.subresourceRange.layerCount = 1u; - //! TODO: this stuff (custom shader sampling code?) - _NBL_DEBUG_BREAK_IF(tex->bitmap.uoffset != 0.f); - _NBL_DEBUG_BREAK_IF(tex->bitmap.voffset != 0.f); - _NBL_DEBUG_BREAK_IF(tex->bitmap.uscale != 1.f); - _NBL_DEBUG_BREAK_IF(tex->bitmap.vscale != 1.f); - - asset::SAssetBundle viewBundle(nullptr,{ICPUImageView::create(std::move(viewParams))}); - ctx.override_->insertAssetIntoCache(std::move(viewBundle),cacheKey,ctx.inner,hierarchyLevel); - } - } - } - break; - case CElementTexture::Type::SCALE: - cacheTexture(ctx,hierarchyLevel,tex->scale.texture,semantic); - break; - default: - _NBL_DEBUG_BREAK_IF(true); - break; - } -} - -auto CMitsubaLoader::getBSDFtreeTraversal(SContext& ctx, const CElementBSDF* bsdf, const CElementEmitter* emitter, core::matrix4SIMD tform) -> SContext::bsdf_type -{ - if (!bsdf) - { - static auto blackBSDF = []() -> auto - { - CElementBSDF retval("nullptr BSDF"); - retval.type = CElementBSDF::Type::DIFFUSE, - retval.diffuse.reflectance = 0.f; - retval.diffuse.alpha = 0.f; - return retval; - }(); - bsdf = &blackBSDF; - } - - auto found = ctx.instrStreamCache.find(bsdf); - if (found == ctx.instrStreamCache.end()) - found = ctx.instrStreamCache.insert({ bsdf,genBSDFtreeTraversal(ctx, bsdf) }).first; - auto compiled_bsdf = found->second; - - // TODO cache the IR Node - CMitsubaMaterialCompilerFrontend::EmitterNode* emitterIRNode = nullptr; - if (emitter->type == CElementEmitter::AREA) - { - cacheEmissionProfile(ctx,emitter->area.emissionProfile); - emitterIRNode = ctx.frontend.createEmitterNode(ctx.ir.get(),emitter,tform); - } - - // A new root node gets made for every {bsdf,emitter} combo - using node_t = asset::material_compiler::IR::INode; - auto createNewRootNode = [&ctx,emitterIRNode](node_t* realBxDFRoot, node_t* emitter=nullptr) -> node_t* - { - // TODO: cache the combo! - auto newRoot = ctx.ir->allocNode(); - if (emitter) - newRoot->children = node_t::createChildrenArray(realBxDFRoot,emitter); - else - newRoot->children = node_t::createChildrenArray(realBxDFRoot); - ctx.ir->addRootNode(newRoot); - return newRoot; - }; - - return { createNewRootNode(compiled_bsdf.front,emitterIRNode), createNewRootNode(compiled_bsdf.back)}; -} - -auto CMitsubaLoader::genBSDFtreeTraversal(SContext& ctx, const CElementBSDF* _bsdf) -> SContext::bsdf_type -{ - { - auto cachePropertyTexture = [&](const auto& const_or_tex, const CMitsubaMaterialCompilerFrontend::E_IMAGE_VIEW_SEMANTIC semantic=CMitsubaMaterialCompilerFrontend::EIVS_IDENTITIY) -> void - { - if (const_or_tex.value.type==SPropertyElementData::INVALID) - cacheTexture(ctx,0u,const_or_tex.texture,semantic); - }; - - core::stack stack; - stack.push(_bsdf); - - while (!stack.empty()) - { - auto* bsdf = stack.top(); - stack.pop(); - // - switch (bsdf->type) + for (auto& meshbuffer : mesh->getMeshBufferVector()) { - case CElementBSDF::COATING: - for (uint32_t i = 0u; i < bsdf->coating.childCount; ++i) - stack.push(bsdf->coating.bsdf[i]); - break; - case CElementBSDF::ROUGHCOATING: - case CElementBSDF::BUMPMAP: - case CElementBSDF::BLEND_BSDF: - case CElementBSDF::MIXTURE_BSDF: - case CElementBSDF::MASK: - case CElementBSDF::TWO_SIDED: - for (uint32_t i = 0u; i < bsdf->meta_common.childCount; ++i) - stack.push(bsdf->meta_common.bsdf[i]); - default: - break; - } - // - switch (bsdf->type) - { - case CElementBSDF::DIFFUSE: - case CElementBSDF::ROUGHDIFFUSE: - cachePropertyTexture(bsdf->diffuse.reflectance); - cachePropertyTexture(bsdf->diffuse.alpha); - break; - case CElementBSDF::DIFFUSE_TRANSMITTER: - cachePropertyTexture(bsdf->difftrans.transmittance); - break; - case CElementBSDF::DIELECTRIC: - case CElementBSDF::THINDIELECTRIC: - case CElementBSDF::ROUGHDIELECTRIC: - cachePropertyTexture(bsdf->dielectric.alphaU); - if (bsdf->dielectric.distribution == CElementBSDF::RoughSpecularBase::ASHIKHMIN_SHIRLEY) - cachePropertyTexture(bsdf->dielectric.alphaV); - break; - case CElementBSDF::CONDUCTOR: - case CElementBSDF::ROUGHCONDUCTOR: - cachePropertyTexture(bsdf->conductor.alphaU); - if (bsdf->conductor.distribution == CElementBSDF::RoughSpecularBase::ASHIKHMIN_SHIRLEY) - cachePropertyTexture(bsdf->conductor.alphaV); - break; - case CElementBSDF::PLASTIC: - case CElementBSDF::ROUGHPLASTIC: - cachePropertyTexture(bsdf->plastic.diffuseReflectance); - cachePropertyTexture(bsdf->plastic.alphaU); - if (bsdf->plastic.distribution == CElementBSDF::RoughSpecularBase::ASHIKHMIN_SHIRLEY) - cachePropertyTexture(bsdf->plastic.alphaV); - break; - case CElementBSDF::BUMPMAP: - cacheTexture(ctx,0u,bsdf->bumpmap.texture,bsdf->bumpmap.wasNormal ? CMitsubaMaterialCompilerFrontend::EIVS_NORMAL_MAP:CMitsubaMaterialCompilerFrontend::EIVS_BUMP_MAP); - break; - case CElementBSDF::BLEND_BSDF: - cachePropertyTexture(bsdf->blendbsdf.weight,CMitsubaMaterialCompilerFrontend::EIVS_BLEND_WEIGHT); - break; - case CElementBSDF::MASK: - cachePropertyTexture(bsdf->mask.opacity,CMitsubaMaterialCompilerFrontend::EIVS_BLEND_WEIGHT); - break; - default: break; + auto binding = meshbuffer->getIndexBufferBinding(); + binding.buffer = core::smart_refctd_ptr_static_cast(binding.buffer->clone(0u)); + meshbuffer->setIndexBufferBinding(std::move(binding)); + ctx.manipulator->flipSurfaces(meshbuffer.get()); } } - } - - return ctx.frontend.compileToIRTree(ctx.ir.get(), _bsdf); -} - - -// Also sets instance data buffer offset into meshbuffers' base instance -template -inline core::smart_refctd_ptr CMitsubaLoader::createDS0(const SContext& _ctx, asset::ICPUPipelineLayout* _layout, const asset::material_compiler::CMaterialCompilerGLSLBackendCommon::result_t& _compResult, Iter meshBegin, Iter meshEnd) -{ - auto* ds0layout = _layout->getDescriptorSetLayout(0u); - - auto ds0 = core::make_smart_refctd_ptr(core::smart_refctd_ptr(ds0layout)); - { - auto count = _ctx.backend_ctx.vt.getCPUVirtualTexture()->getDescriptorSetWrites(nullptr, nullptr, nullptr); - - auto writes = core::make_refctd_dynamic_array>(count.first); - auto info = core::make_refctd_dynamic_array>(count.second); - - _ctx.backend_ctx.vt.getCPUVirtualTexture()->getDescriptorSetWrites(writes->data(), info->data(), ds0.get()); - - for (const auto& w : (*writes)) - { - auto descRng = ds0->getDescriptors(w.binding); - for (uint32_t i = 0u; i < w.count; ++i) - descRng.begin()[w.arrayElement+i].assign(w.info[i], w.descriptorType); - } - } - auto d = ds0->getDescriptors(PRECOMPUTED_VT_DATA_BINDING).begin(); - { - auto precompDataBuf = core::make_smart_refctd_ptr(sizeof(asset::ICPUVirtualTexture::SPrecomputedData)); - memcpy(precompDataBuf->getPointer(), &_ctx.backend_ctx.vt.getCPUVirtualTexture()->getPrecomputedData(), precompDataBuf->getSize()); - - d->buffer.offset = 0u; - d->buffer.size = precompDataBuf->getSize(); - d->desc = std::move(precompDataBuf); - } - d = ds0->getDescriptors(INSTR_BUF_BINDING).begin(); - { - auto instrbuf = core::make_smart_refctd_ptr(_compResult.instructions.size()*sizeof(decltype(_compResult.instructions)::value_type)); - memcpy(instrbuf->getPointer(), _compResult.instructions.data(), instrbuf->getSize()); - - d->buffer.offset = 0u; - d->buffer.size = instrbuf->getSize(); - d->desc = std::move(instrbuf); - } - d = ds0->getDescriptors(BSDF_BUF_BINDING).begin(); - { - auto bsdfbuf = core::make_smart_refctd_ptr(_compResult.bsdfData.size()*sizeof(decltype(_compResult.bsdfData)::value_type)); - memcpy(bsdfbuf->getPointer(), _compResult.bsdfData.data(), bsdfbuf->getSize()); - - d->buffer.offset = 0u; - d->buffer.size = bsdfbuf->getSize(); - d->desc = std::move(bsdfbuf); - } - d = ds0->getDescriptors(EMITTER_DATA_BUF_BINDING).begin(); - { - auto emitterbuf = core::make_smart_refctd_ptr(_compResult.emitterData.size() * sizeof(decltype(_compResult.emitterData)::value_type)); - memcpy(emitterbuf->getPointer(), _compResult.emitterData.data(), emitterbuf->getSize()); - - d->buffer.offset = 0u; - d->buffer.size = emitterbuf->getSize(); - d->desc = std::move(emitterbuf); - } - d = ds0->getDescriptors(PREFETCH_INSTR_BUF_BINDING).begin(); - { - const size_t sz = _compResult.prefetch_stream.size()*sizeof(decltype(_compResult.prefetch_stream)::value_type); - - constexpr size_t MIN_SSBO_SZ = 128ull; //prefetch stream won't be generated if no textures are used, so make sure we're not creating 0-size buffer - auto prefetch_instr_buf = core::make_smart_refctd_ptr(std::max(MIN_SSBO_SZ, sz)); - memcpy(prefetch_instr_buf->getPointer(), _compResult.prefetch_stream.data(), sz); - - d->buffer.offset = 0u; - d->buffer.size = prefetch_instr_buf->getSize(); - d->desc = std::move(prefetch_instr_buf); - } - -#ifdef DEBUG_MITSUBA_LOADER - std::ofstream ofile("log.txt"); -#endif - core::vector instanceData; - for (auto it=meshBegin; it != meshEnd; ++it) - { - auto mesh = it->first.get(); - - core::vectorSIMDf emissive; - for (auto& mb : mesh->getMeshBuffers()) - mb->setBaseInstance(instanceData.size()); - auto* meshMeta = _ctx.meta->getAssetSpecificMetadata(mesh); - auto baseInstanceDataIt = meshMeta->m_instances.begin(); - for (const auto& inst : meshMeta->m_instanceAuxData) - { - nbl_glsl_ext_Mitsuba_Loader_instance_data_t instData; - - instData.tform = baseInstanceDataIt->worldTform; - instData.tform.getSub3x3InverseTranspose(reinterpret_cast(instData.normalMatrixRow0)); - reinterpret_cast(instData.determinantSignBit) = instData.tform.getPseudoDeterminant().x; - instData.determinantSignBit &= 0x80000000; - - const auto& bsdf = inst.bsdf; - auto bsdf_front = bsdf.front; - auto bsdf_back = bsdf.back; - auto material_it = _compResult.materials.find(bsdf_front); - { - const asset::material_compiler::oriented_material_t* material = - (material_it != _compResult.materials.end()) ? &material_it->second : nullptr; - - if (material) { -#ifdef DEBUG_MITSUBA_LOADER - //os::Printer::log("Debug print front BSDF with id = ", std::to_string(&bsdf), ELL_INFORMATION); - - ofile << "Debug print front BSDF with id = " << &bsdf << std::endl; - _ctx.backend.debugPrint(ofile, *material, _compResult, &_ctx.backend_ctx); - -#endif - instData.material.front = *material; - } - } - material_it = _compResult.materials.find(bsdf_back); + // recompute normalis if necessary + if (faceNormals || !std::isnan(maxSmoothAngle)) + for (auto& meshbuffer : mesh->getMeshBufferVector()) { - const asset::material_compiler::oriented_material_t* material = - (material_it != _compResult.materials.end()) ? &material_it->second : nullptr; + const float smoothAngleCos = cos(core::radians(maxSmoothAngle)); - if (material) - { -#ifdef DEBUG_MITSUBA_LOADER - //os::Printer::log("Debug print back BSDF with id = ", std::to_string(&bsdf), ELL_INFORMATION); - ofile << "Debug print back BSDF with id = " << &bsdf << std::endl; - _ctx.backend.debugPrint(ofile, *material, _compResult, &_ctx.backend_ctx); -#endif - - instData.material.back = *material; - } + // TODO: make these mesh manipulator functions const-correct + auto newMeshBuffer = ctx.manipulator->createMeshBufferUniquePrimitives(meshbuffer.get()); + ctx.manipulator->filterInvalidTriangles(newMeshBuffer.get()); + ctx.manipulator->calculateSmoothNormals(newMeshBuffer.get(), false, 0.f, newMeshBuffer->getNormalAttributeIx(), + [&](const asset::IMeshManipulator::SSNGVertexData& a, const asset::IMeshManipulator::SSNGVertexData& b, asset::ICPUMeshBuffer* buffer) + { + if (faceNormals) + return a.indexOffset == b.indexOffset; + else + return core::dot(a.parentTriangleFaceNormal, b.parentTriangleFaceNormal).x >= smoothAngleCos; + }); + meshbuffer = std::move(newMeshBuffer); } - - instanceData.push_back(instData); - baseInstanceDataIt++; - } - } -#ifdef DEBUG_MITSUBA_LOADER - ofile.close(); + IMeshManipulator::recalculateBoundingBox(newMesh.get()); + mesh = std::move(newMesh); #endif - d = ds0->getDescriptors(INSTANCE_DATA_BINDING).begin(); - { - auto instDataBuf = core::make_smart_refctd_ptr(instanceData.size()*sizeof(nbl_glsl_ext_Mitsuba_Loader_instance_data_t)); - memcpy(instDataBuf->getPointer(), instanceData.data(), instDataBuf->getSize()); - - d->buffer.offset = 0u; - d->buffer.size = instDataBuf->getSize(); - d->desc = std::move(instDataBuf); + // cache and return + CMitsubaMetadata::SGeometryMetaPair geoMeta = {.geom=std::move(geo)}; + geoMeta.meta.m_id = shape->id; + geoMeta.meta.type = shape->type; + shapeCache.insert({shape,std::move(geoMeta)}); } - - return ds0; -} -#endif - -using namespace std::string_literals; - -SContext::SContext( -// const asset::IGeometryCreator* _geomCreator, -// const asset::IMeshManipulator* _manipulator, - const asset::IAssetLoader::SAssetLoadContext& _ctx, - asset::IAssetLoader::IAssetLoaderOverride* _override, - CMitsubaMetadata* _metadata -) : /*creator(_geomCreator), manipulator(_manipulator),*/ inner(_ctx), override_(_override), meta(_metadata) -//,ir(core::make_smart_refctd_ptr()), frontend(this) -{ + return geo; } } diff --git a/src/nbl/ext/MitsubaLoader/ParserUtil.cpp b/src/nbl/ext/MitsubaLoader/ParserUtil.cpp index fb54fab0ef..e8b18d22a8 100644 --- a/src/nbl/ext/MitsubaLoader/ParserUtil.cpp +++ b/src/nbl/ext/MitsubaLoader/ParserUtil.cpp @@ -28,10 +28,8 @@ using namespace nbl::system; auto ParserManager::parse(IFile* _file, const Params& _params) const -> Result { -// CMitsubaMetadata* obj = new CMitsubaMetadata(); - Result result = { - .metadata = core::make_smart_refctd_ptr() - }; + Result result = {}; + result.metadata = core::make_smart_refctd_ptr(); SessionContext ctx = { .result = &result, .params = &_params, @@ -325,11 +323,27 @@ void ParserManager::XMLContext::onEnd(const char* _el) return; } - if (element.element && element.element->getType()==IElement::Type::SHAPE) + // TODO: only allow this for top level elements so we don't load unused shapes + if (element.element) + switch (element.element->getType()) { - auto shape = static_cast(element.element); - if (shape) + case IElement::Type::EMITTER: + { + auto emitter = static_cast(element.element); + if (emitter->type!=CElementEmitter::Type::CONSTANT) + result.emitters.emplace_back(emitter,std::move(element.name)); + else + result.ambient += emitter->constant.radiance; + break; + } + case IElement::Type::SHAPE: + { + auto shape = static_cast(element.element); result.shapegroups.emplace_back(shape,std::move(element.name)); + break; + } + default: + break; } } @@ -337,20 +351,20 @@ void ParserManager::XMLContext::onEnd(const char* _el) template<> struct ParserManager::CreateElement { - static inline SNamedElement __call(const char** _atts, SessionContext* ctx) + static inline SessionContext::named_element_t __call(const char** _atts, SessionContext* ctx) { if (IElement::invalidAttributeCount(_atts,2u)) return {}; if (core::strcmpi(_atts[0],"name")) return {}; - return {ctx->objects.construct(),_atts[1]}; + return {ctx->result->objects->construct(),_atts[1]}; }; }; template<> struct ParserManager::CreateElement { - static inline SNamedElement __call(const char** _atts, SessionContext* ctx) + static inline SessionContext::named_element_t __call(const char** _atts, SessionContext* ctx) { const char* type; const char* id; @@ -358,7 +372,7 @@ struct ParserManager::CreateElement if (!IElement::getTypeIDAndNameStrings(type, id, name, _atts)) return {}; - CElementEmissionProfile* obj = ctx->objects.construct(id); + CElementEmissionProfile* obj = ctx->result->objects->construct(id); if (!obj) return {}; @@ -378,7 +392,7 @@ concept HasVisit = requires() { template requires HasTypeMap struct ParserManager::CreateElement { - static inline SNamedElement __call(const char** _atts, SessionContext* ctx) + static inline SessionContext::named_element_t __call(const char** _atts, SessionContext* ctx) { const char* type; const char* id; @@ -394,7 +408,7 @@ struct ParserManager::CreateElement return {}; } - Element* obj = ctx->objects.construct(id); + Element* obj = ctx->result->objects->construct(id); if (!obj) return {}; @@ -446,7 +460,7 @@ ParserManager::ParserManager() : propertyElements({ CElementEmissionProfile::compAddPropertyMap() }) { } -auto ParserManager::processAlias(const char** _atts, SessionContext* ctx) -> SNamedElement +auto ParserManager::processAlias(const char** _atts, SessionContext* ctx) -> SessionContext::named_element_t { const char* id = nullptr; const char* as = nullptr; @@ -480,7 +494,7 @@ auto ParserManager::processAlias(const char** _atts, SessionContext* ctx) -> SNa return {original,std::move(name)}; } -auto ParserManager::processRef(const char** _atts, SessionContext* ctx) -> SNamedElement +auto ParserManager::processRef(const char** _atts, SessionContext* ctx) -> SessionContext::named_element_t { const char* id; std::string name; diff --git a/src/nbl/video/CVulkanCommandBuffer.cpp b/src/nbl/video/CVulkanCommandBuffer.cpp index a55c3a1e7b..a04b5940ce 100644 --- a/src/nbl/video/CVulkanCommandBuffer.cpp +++ b/src/nbl/video/CVulkanCommandBuffer.cpp @@ -844,17 +844,12 @@ bool CVulkanCommandBuffer::setRayTracingPipelineStackSize_impl(uint32_t pipeline return true; } -bool CVulkanCommandBuffer::traceRays_impl( - const asset::SBufferRange& raygenGroupRange, - const asset::SBufferRange& missGroupsRange, uint32_t missGroupStride, - const asset::SBufferRange& hitGroupsRange, uint32_t hitGroupStride, - const asset::SBufferRange& callableGroupsRange, uint32_t callableGroupStride, - uint32_t width, uint32_t height, uint32_t depth) -{ - const auto vk_raygenGroupRegion = getVkStridedDeviceAddressRegion(raygenGroupRange, raygenGroupRange.size); - const auto vk_missGroupsRegion = getVkStridedDeviceAddressRegion(missGroupsRange, missGroupStride); - const auto vk_hitGroupsRegion = getVkStridedDeviceAddressRegion(hitGroupsRange, hitGroupStride); - const auto vk_callableGroupsRegion = getVkStridedDeviceAddressRegion(callableGroupsRange, callableGroupStride); +bool CVulkanCommandBuffer::traceRays_impl(const IGPURayTracingPipeline::SShaderBindingTable& sbt, const uint32_t width, const uint32_t height, const uint32_t depth) +{ + const auto vk_raygenGroupRegion = getVkStridedDeviceAddressRegion({.range=sbt.raygen,.stride=uint32_t(sbt.raygen.size)}); + const auto vk_missGroupsRegion = getVkStridedDeviceAddressRegion(sbt.miss); + const auto vk_hitGroupsRegion = getVkStridedDeviceAddressRegion(sbt.hit); + const auto vk_callableGroupsRegion = getVkStridedDeviceAddressRegion(sbt.callable); getFunctionTable().vkCmdTraceRaysKHR(m_cmdbuf, &vk_raygenGroupRegion, diff --git a/src/nbl/video/CVulkanCommandBuffer.h b/src/nbl/video/CVulkanCommandBuffer.h index 9383585b23..48d7e9e85c 100644 --- a/src/nbl/video/CVulkanCommandBuffer.h +++ b/src/nbl/video/CVulkanCommandBuffer.h @@ -226,12 +226,7 @@ class CVulkanCommandBuffer final : public IGPUCommandBuffer bool resolveImage_impl(const IGPUImage* const srcImage, const IGPUImage::LAYOUT srcImageLayout, IGPUImage* const dstImage, const IGPUImage::LAYOUT dstImageLayout, const uint32_t regionCount, const SImageResolve* pRegions) override; bool setRayTracingPipelineStackSize_impl(uint32_t pipelineStackSize) override; - bool traceRays_impl( - const asset::SBufferRange& raygenGroupRange, - const asset::SBufferRange& missGroupsRange, uint32_t missGroupStride, - const asset::SBufferRange& hitGroupsRange, uint32_t hitGroupStride, - const asset::SBufferRange& callableGroupsRange, uint32_t callableGroupStride, - uint32_t width, uint32_t height, uint32_t depth) override; + bool traceRays_impl(const IGPURayTracingPipeline::SShaderBindingTable& sbt, const uint32_t width, const uint32_t height, const uint32_t depth) override; bool traceRaysIndirect_impl(const asset::SBufferBinding& indirectBinding) override; bool executeCommands_impl(const uint32_t count, IGPUCommandBuffer* const* const cmdbufs) override; diff --git a/src/nbl/video/CVulkanCommandPool.h b/src/nbl/video/CVulkanCommandPool.h index 8775553b99..d1c3085b4e 100644 --- a/src/nbl/video/CVulkanCommandPool.h +++ b/src/nbl/video/CVulkanCommandPool.h @@ -2,7 +2,6 @@ #define _NBL_VIDEO_C_VULKAN_COMMAND_POOL_H_INCLUDED_ #include "nbl/video/IGPUCommandPool.h" -#include "nbl/core/containers/CMemoryPool.h" #include diff --git a/src/nbl/video/CVulkanLogicalDevice.cpp b/src/nbl/video/CVulkanLogicalDevice.cpp index 5390b4c3fa..46b79a7094 100644 --- a/src/nbl/video/CVulkanLogicalDevice.cpp +++ b/src/nbl/video/CVulkanLogicalDevice.cpp @@ -1551,7 +1551,6 @@ void CVulkanLogicalDevice::createRayTracingPipelines_impl( for (const auto& info : createInfos) { - core::unordered_map shaderIndexes; auto getVkShaderIndex = [&](const IGPUPipelineBase::SShaderSpecInfo& spec) { @@ -1633,6 +1632,7 @@ void CVulkanLogicalDevice::createRayTracingPipelines_impl( { outCreateInfo->pDynamicState = &vk_dynamicStateCreateInfo; } + outCreateInfo++; } auto vk_pipelines = reinterpret_cast(output); diff --git a/src/nbl/video/IGPUCommandBuffer.cpp b/src/nbl/video/IGPUCommandBuffer.cpp index 1f619666ab..f47428aae8 100644 --- a/src/nbl/video/IGPUCommandBuffer.cpp +++ b/src/nbl/video/IGPUCommandBuffer.cpp @@ -50,14 +50,6 @@ bool IGPUCommandBuffer::checkStateBeforeRecording(const core::bitflag flags, const SInheritanceInfo* inheritanceInfo) { - // Using Vulkan 1.2 VUIDs here because we don't want to confuse ourselves with Dynamic Rendering being core - // https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkBeginCommandBuffer-commandBuffer-00049 - if (m_state == STATE::RECORDING || m_state == STATE::PENDING) - { - NBL_LOG_ERROR("command buffer must not be in RECORDING or PENDING state!"); - return false; - } - const bool whollyInsideRenderpass = flags.hasFlags(USAGE::RENDER_PASS_CONTINUE_BIT); const auto physDev = getOriginDevice()->getPhysicalDevice(); if (m_level==IGPUCommandPool::BUFFER_LEVEL::PRIMARY) @@ -126,6 +118,14 @@ bool IGPUCommandBuffer::begin(const core::bitflag flags, const SInheritan } checkForParentPoolReset(); + + // Using Vulkan 1.2 VUIDs here because we don't want to confuse ourselves with Dynamic Rendering being core + // https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkBeginCommandBuffer-commandBuffer-00049 + if (m_state == STATE::RECORDING || m_state == STATE::PENDING) + { + NBL_LOG_ERROR("command buffer must not be in RECORDING or PENDING state!"); + return false; + } // still not initial and pool wasn't reset if (m_state!=STATE::INITIAL) @@ -684,50 +684,21 @@ bool IGPUCommandBuffer::copyImage(const IGPUImage* const srcImage, const IGPUIma return copyImage_impl(srcImage, srcImageLayout, dstImage, dstImageLayout, regionCount, pRegions); } -bool IGPUCommandBuffer::invalidShaderGroups( - const asset::SBufferRange& raygenGroupRange, - const asset::SBufferRange& missGroupsRange, uint32_t missGroupStride, - const asset::SBufferRange& hitGroupsRange, uint32_t hitGroupStride, - const asset::SBufferRange& callableGroupsRange, uint32_t callableGroupStride, - core::bitflag flags) const +bool IGPUCommandBuffer::invalidShaderGroups(const IGPURayTracingPipeline::SShaderBindingTable& sbt, const core::bitflag flags) const { + if (!sbt.valid(flags)) + return true; using PipelineFlag = IGPURayTracingPipeline::SCreationParams::FLAGS; using PipelineFlags = core::bitflag; - // https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdTraceRaysKHR.html#VUID-vkCmdTraceRaysKHR-flags-03696 - // https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdTraceRaysKHR.html#VUID-vkCmdTraceRaysKHR-flags-03697 - // https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdTraceRaysKHR.html#VUID-vkCmdTraceRaysKHR-flags-03512 - const auto shouldHaveHitGroup = flags & - (PipelineFlags(PipelineFlag::NO_NULL_ANY_HIT_SHADERS) | - PipelineFlag::NO_NULL_CLOSEST_HIT_SHADERS | - PipelineFlag::NO_NULL_INTERSECTION_SHADERS); - if (shouldHaveHitGroup && !hitGroupsRange.buffer) - { - NBL_LOG_ERROR("bound pipeline indicates that traceRays command should have hit group, but hitGroupsRange.buffer is null!"); - return true; - } - - // https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdTraceRaysKHR.html#VUID-vkCmdTraceRaysKHR-flags-03511 - const auto shouldHaveMissGroup = flags & PipelineFlag::NO_NULL_MISS_SHADERS; - if (shouldHaveMissGroup && !missGroupsRange.buffer) - { - NBL_LOG_ERROR("bound pipeline indicates that traceRays command should have hit group, but hitGroupsRange.buffer is null!"); - return true; - } - const auto& limits = getOriginDevice()->getPhysicalDevice()->getLimits(); - auto invalidBufferRegion = [this, &limits](const asset::SBufferRange& range, uint32_t stride, const char* groupName) -> bool + auto invalidBufferRegion = [this, &limits](const asset::SStridedRange& stRange, const char* groupName) -> bool { + const auto& range = stRange.range; const IGPUBuffer* const buffer = range.buffer.get(); - - if (!buffer) return false; - - if (!range.isValid()) - { - NBL_LOG_ERROR("%s buffer range is not valid!", groupName); - return true; - } + if (!buffer) + return false; if (!(buffer->getCreationParams().usage & IGPUBuffer::EUF_SHADER_DEVICE_ADDRESS_BIT)) { @@ -743,13 +714,13 @@ bool IGPUCommandBuffer::invalidShaderGroups( } // https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdTraceRaysKHR.html#VUID-vkCmdTraceRaysKHR-pHitShaderBindingTable-03690 - if (stride % limits.shaderGroupHandleAlignment) + if (stRange.stride % limits.shaderGroupHandleAlignment) { NBL_LOG_ERROR("%s buffer offset must be multiple of %u!", groupName, limits.shaderGroupHandleAlignment); return true; } - if (stride > limits.maxShaderGroupStride) + if (stRange.stride > limits.maxShaderGroupStride) { NBL_LOG_ERROR("%s buffer stride must not exceed %u!", groupName, limits.shaderGroupHandleAlignment); return true; @@ -765,10 +736,11 @@ bool IGPUCommandBuffer::invalidShaderGroups( return false; }; - if (invalidBufferRegion(raygenGroupRange, raygenGroupRange.size, "Raygen Group")) return true; - if (invalidBufferRegion(missGroupsRange, missGroupStride, "Miss groups")) return true; - if (invalidBufferRegion(hitGroupsRange, hitGroupStride, "Hit groups")) return true; - if (invalidBufferRegion(callableGroupsRange, callableGroupStride, "Callable groups")) return true; + if (invalidBufferRegion({.range=sbt.raygen,.stride=limits.shaderGroupHandleAlignment},"Raygen Group")) return true; + if (invalidBufferRegion(sbt.miss,"Miss groups")) return true; + if (invalidBufferRegion(sbt.hit,"Hit groups")) return true; + if (invalidBufferRegion(sbt.callable,"Callable groups")) return true; + return false; } @@ -1945,12 +1917,7 @@ bool IGPUCommandBuffer::setRayTracingPipelineStackSize(uint32_t pipelineStackSiz return setRayTracingPipelineStackSize_impl(pipelineStackSize); } -bool IGPUCommandBuffer::traceRays( - const asset::SBufferRange& raygenGroupRange, - const asset::SBufferRange& missGroupsRange, uint32_t missGroupStride, - const asset::SBufferRange& hitGroupsRange, uint32_t hitGroupStride, - const asset::SBufferRange& callableGroupsRange, uint32_t callableGroupStride, - uint32_t width, uint32_t height, uint32_t depth) +bool IGPUCommandBuffer::traceRays(const IGPURayTracingPipeline::SShaderBindingTable& sbt, const uint32_t width, const uint32_t height, const uint32_t depth) { if (!checkStateBeforeRecording(queue_flags_t::COMPUTE_BIT,RENDERPASS_SCOPE::OUTSIDE)) return false; @@ -1983,11 +1950,7 @@ bool IGPUCommandBuffer::traceRays( } const auto flags = m_boundRayTracingPipeline->getCreationFlags(); - if (invalidShaderGroups(raygenGroupRange, - missGroupsRange, missGroupStride, - hitGroupsRange, hitGroupStride, - callableGroupsRange, callableGroupStride, - flags)) + if (invalidShaderGroups(sbt,flags)) { NBL_LOG_ERROR("invalid shader groups for traceRays command!"); return false; @@ -2000,11 +1963,13 @@ bool IGPUCommandBuffer::traceRays( return false; } - if (!m_cmdpool->m_commandListPool.emplace(m_commandList, - core::smart_refctd_ptr(raygenGroupRange.buffer), - core::smart_refctd_ptr(missGroupsRange.buffer), - core::smart_refctd_ptr(hitGroupsRange.buffer), - core::smart_refctd_ptr(callableGroupsRange.buffer))) + if (!m_cmdpool->m_commandListPool.emplace( + m_commandList, + core::smart_refctd_ptr(sbt.raygen.buffer), + core::smart_refctd_ptr(sbt.miss.range.buffer), + core::smart_refctd_ptr(sbt.hit.range.buffer), + core::smart_refctd_ptr(sbt.callable.range.buffer) + )) { NBL_LOG_ERROR("out of host memory!"); return false; @@ -2012,12 +1977,7 @@ bool IGPUCommandBuffer::traceRays( m_noCommands = false; - return traceRays_impl( - raygenGroupRange, - missGroupsRange, missGroupStride, - hitGroupsRange, hitGroupStride, - callableGroupsRange, callableGroupStride, - width, height, depth); + return traceRays_impl(sbt, width, height, depth); } bool IGPUCommandBuffer::traceRaysIndirect(const asset::SBufferBinding& indirectBinding) diff --git a/src/nbl/video/ILogicalDevice.cpp b/src/nbl/video/ILogicalDevice.cpp index 7c3f5dbb81..d5b38f9b69 100644 --- a/src/nbl/video/ILogicalDevice.cpp +++ b/src/nbl/video/ILogicalDevice.cpp @@ -1110,14 +1110,14 @@ bool ILogicalDevice::createRayTracingPipelines(IGPUPipelineCache* const pipeline newParams[ix] = param; newParams[ix].shaderGroups.raygen = trimTask.trim(param.shaderGroups.raygen, trimmedShaders); - newParams[ix].shaderGroups.misses = trimmedMissSpecs; + newParams[ix].shaderGroups.misses = {trimmedMissSpecData,param.shaderGroups.misses.size()}; for (const auto& miss: param.shaderGroups.misses) { *trimmedMissSpecData = trimTask.trim(miss, trimmedShaders); trimmedMissSpecData++; } - newParams[ix].shaderGroups.hits = trimmedHitSpecs; + newParams[ix].shaderGroups.hits = {trimmedHitSpecData,param.shaderGroups.hits.size()}; for (const auto& hit: param.shaderGroups.hits) { *trimmedHitSpecData = { @@ -1128,7 +1128,7 @@ bool ILogicalDevice::createRayTracingPipelines(IGPUPipelineCache* const pipeline trimmedHitSpecData++; } - newParams[ix].shaderGroups.callables = trimmedCallableSpecs; + newParams[ix].shaderGroups.callables = {trimmedCallableSpecData,param.shaderGroups.callables.size()}; for (const auto& callable: param.shaderGroups.callables) { *trimmedCallableSpecData = trimTask.trim(callable, trimmedShaders);