From 1ba02c6336d20ec104f1922fb7549ae0c7369a40 Mon Sep 17 00:00:00 2001 From: kj16609 Date: Sat, 25 Jan 2025 01:50:21 -0500 Subject: [PATCH] Adds optimization to octtree navigation --- src/editor/src/main.cpp | 2 - src/engine/EngineContext.cpp | 4 +- src/engine/FrameInfo.hpp | 3 +- .../descriptors/DescriptorSetLayout.cpp | 8 +-- .../descriptors/DescriptorSetLayout.hpp | 6 +- src/engine/tree/bvh/BVHTree.cpp | 9 +++ src/engine/tree/bvh/BVHTree.hpp | 43 +++++++++++ src/engine/tree/octtree/OctTreeNode.cpp | 71 ++++++++++++++++++- src/engine/tree/octtree/OctTreeNode.hpp | 6 ++ 9 files changed, 138 insertions(+), 14 deletions(-) create mode 100644 src/engine/tree/bvh/BVHTree.cpp create mode 100644 src/engine/tree/bvh/BVHTree.hpp diff --git a/src/editor/src/main.cpp b/src/editor/src/main.cpp index 15a5b4b..fced099 100644 --- a/src/editor/src/main.cpp +++ b/src/editor/src/main.cpp @@ -42,7 +42,6 @@ int main() { EngineContext engine_ctx {}; - /* EditorGuiContext editor_ctx { engine_ctx.getWindow() }; // We start by hooking into the imgui rendering. @@ -90,7 +89,6 @@ int main() } engine_ctx.waitIdle(); - */ } catch ( const vk::LayerNotPresentError& e ) { diff --git a/src/engine/EngineContext.cpp b/src/engine/EngineContext.cpp index 1017e7e..f2891f1 100644 --- a/src/engine/EngineContext.cpp +++ b/src/engine/EngineContext.cpp @@ -87,8 +87,8 @@ namespace fgl::engine } obj.getTransform().translation = WorldCoordinate( - -10.0f + ( static_cast< float >( x ) * offset ), - -10.0f + ( static_cast< float >( y ) * offset ), + 10.0f + ( static_cast< float >( x ) * offset ), + 10.0f + ( static_cast< float >( y ) * offset ), 0.0f ); m_game_objects_root.addGameObject( std::move( obj ) ); diff --git a/src/engine/FrameInfo.hpp b/src/engine/FrameInfo.hpp index 5bcb459..d2e51d8 100644 --- a/src/engine/FrameInfo.hpp +++ b/src/engine/FrameInfo.hpp @@ -5,8 +5,8 @@ #pragma once //clang-format: off -#include #include +#include //clang-format: on #include "descriptors/Descriptor.hpp" @@ -58,6 +58,7 @@ namespace fgl::engine constexpr descriptors::AttachmentDescriptor metallic_descriptor { 3, FRAG_STAGE }; constexpr descriptors::AttachmentDescriptor emissive_descriptor { 4, FRAG_STAGE }; + //TODO: Move this from being static, It being here prevents safe cleanup inline static descriptors::DescriptorSetLayout gbuffer_set { 0, color_descriptor, position_descriptor, normal_descriptor, metallic_descriptor, emissive_descriptor }; diff --git a/src/engine/descriptors/DescriptorSetLayout.cpp b/src/engine/descriptors/DescriptorSetLayout.cpp index 5b9ea8a..f61ac40 100644 --- a/src/engine/descriptors/DescriptorSetLayout.cpp +++ b/src/engine/descriptors/DescriptorSetLayout.cpp @@ -27,9 +27,9 @@ namespace fgl::engine::descriptors binding.stageFlags = descriptor.m_stage_flags; binding.pImmutableSamplers = VK_NULL_HANDLE; - bindings.emplace_back( binding ); + m_bindings.emplace_back( binding ); - flags.emplace_back( descriptor.m_binding_flags ); + m_flags.emplace_back( descriptor.m_binding_flags ); } } @@ -47,11 +47,11 @@ namespace fgl::engine::descriptors vk::raii::DescriptorSetLayout DescriptorSetLayout::createLayout() const { vk::DescriptorSetLayoutBindingFlagsCreateInfo flags_info {}; - flags_info.setBindingFlags( flags ); + flags_info.setBindingFlags( m_flags ); vk::DescriptorSetLayoutCreateInfo info {}; info.setFlags( vk::DescriptorSetLayoutCreateFlagBits::eUpdateAfterBindPool ); - info.setBindings( bindings ); + info.setBindings( m_bindings ); info.setPNext( &flags_info ); return Device::getInstance()->createDescriptorSetLayout( info ); diff --git a/src/engine/descriptors/DescriptorSetLayout.hpp b/src/engine/descriptors/DescriptorSetLayout.hpp index 3fec1a8..74a287d 100644 --- a/src/engine/descriptors/DescriptorSetLayout.hpp +++ b/src/engine/descriptors/DescriptorSetLayout.hpp @@ -19,8 +19,8 @@ namespace fgl::engine::descriptors class DescriptorSetLayout { - std::vector< vk::DescriptorSetLayoutBinding > bindings {}; - std::vector< vk::DescriptorBindingFlags > flags {}; + std::vector< vk::DescriptorSetLayoutBinding > m_bindings {}; + std::vector< vk::DescriptorBindingFlags > m_flags {}; std::optional< vk::raii::DescriptorSetLayout > m_layout { std::nullopt }; @@ -45,7 +45,7 @@ namespace fgl::engine::descriptors DescriptorSetLayout( set_idx, std::vector< std::reference_wrapper< const Descriptor > > { descriptors... } ) {} - [[nodiscard]] std::size_t count() const { return bindings.size(); } + [[nodiscard]] std::size_t count() const { return m_bindings.size(); } std::unique_ptr< DescriptorSet > create(); [[nodiscard]] vk::raii::DescriptorSetLayout createLayout() const; diff --git a/src/engine/tree/bvh/BVHTree.cpp b/src/engine/tree/bvh/BVHTree.cpp new file mode 100644 index 0000000..3963d2b --- /dev/null +++ b/src/engine/tree/bvh/BVHTree.cpp @@ -0,0 +1,9 @@ +// +// Created by kj16609 on 1/24/25. +// +#include "BVHTree.hpp" + +namespace fgl::engine +{ + +} \ No newline at end of file diff --git a/src/engine/tree/bvh/BVHTree.hpp b/src/engine/tree/bvh/BVHTree.hpp new file mode 100644 index 0000000..3d70ab8 --- /dev/null +++ b/src/engine/tree/bvh/BVHTree.hpp @@ -0,0 +1,43 @@ +// +// Created by kj16609 on 1/24/25. +// +#pragma once +#include +#include + +#include "glm/vec3.hpp" + +namespace fgl::engine +{ + using BVHIndex = std::uint32_t; + + class BVHTree + { + struct GameObjectInfo + {}; + + struct BVHNode + { + using FlagType = std::uint8_t; + + enum Masks : FlagType + { + Flag_None = 0, + //! This node was visible during the last test + Flag_Previously_Visible = 1 << 0, + //! This node was not visible during the last test + Flag_Previously_Invisible = 1 << 1, + }; + + FlagType m_flags { Masks::Flag_None }; + glm::vec3 m_centerpoint; + + //! Index to try next if we hit this index. + BVHIndex m_hit; + //! Index t otry if we fail this index. + BVHIndex m_miss; + }; + + public: + }; +} // namespace fgl::engine \ No newline at end of file diff --git a/src/engine/tree/octtree/OctTreeNode.cpp b/src/engine/tree/octtree/OctTreeNode.cpp index caa9876..9d79d03 100644 --- a/src/engine/tree/octtree/OctTreeNode.cpp +++ b/src/engine/tree/octtree/OctTreeNode.cpp @@ -34,7 +34,6 @@ namespace fgl::engine void imGuiOctTreeSettings( const FrameInfo& info ) { -#if ENABLE_IMGUI if ( ImGui::CollapsingHeader( "OctTree debug settings" ) ) { ImGui::Checkbox( "Draw leaf fitted bounding boxes", &draw_leaf_fit_bounds ); @@ -61,13 +60,17 @@ namespace fgl::engine time = std::chrono::duration_cast< std::chrono::microseconds >( time_diff ); } + if ( ImGui::Button( "Optimize Octree Travel" ) ) + { + info.game_objects.optimizePath(); + } + if ( time.has_value() ) { ImGui::Text( "Time spent reorganizing: %.2ldus", time.value().count() ); ImGui::Text( "Moved %ld objects", number_moved ); } } -#endif } void OctTreeNode::getAllLeafsInFrustum( const Frustum& frustum, std::vector< OctTreeNodeLeaf* >& out_leafs ) @@ -87,6 +90,12 @@ namespace fgl::engine const OctTreeNodeArray& node_array { std::get< OctTreeNodeArray >( m_node_data ) }; //Search deeper + if ( m_skip != nullptr ) + { + m_skip->getAllLeafsInFrustum( frustum, out_leafs ); + return; + } + node_array[ LEFT ][ FORWARD ][ TOP ]->getAllLeafsInFrustum( frustum, out_leafs ); node_array[ LEFT ][ FORWARD ][ BOTTOM ]->getAllLeafsInFrustum( frustum, out_leafs ); @@ -464,6 +473,8 @@ namespace fgl::engine if ( std::holds_alternative< OctTreeNodeArray >( this->m_node_data ) ) { + if ( m_skip != nullptr ) return m_skip->findID( id ); + const auto& node_array { std::get< OctTreeNodeArray >( this->m_node_data ) }; FOR_EACH_OCTTREE_NODE @@ -532,8 +543,16 @@ namespace fgl::engine } else { + if ( m_skip != nullptr ) + { + m_skip->getAllLeafs( out_leafs ); + return; + } + const auto& nodes { std::get< OctTreeNodeArray >( m_node_data ) }; + // If we have a node to skip to, Skip to it. + FOR_EACH_OCTTREE_NODE { auto ret { nodes[ x ][ y ][ z ]->getAllLeafs() }; @@ -542,8 +561,56 @@ namespace fgl::engine } } + OctTreeNode* OctTreeNode::optimizePath() + { + ZoneScoped; + + // The node returned here will be the optimal node to jump to. If we return nullptr then that means this node has multiple paths it can take. + + if ( std::holds_alternative< NodeDataT >( m_node_data ) ) + { + const auto& nodes { std::get< NodeDataT >( m_node_data ) }; + + OctTreeNode* optimal { nullptr }; + + FOR_EACH_OCTTREE_NODE + { + // Why did I skip the 0,0,0 node previously? + // if ( x == 0 && y == 0 && z == 0 ) continue; + const auto& node { nodes[ x ][ y ][ z ] }; + + if ( auto* optimal_ret = node->optimizePath(); optimal_ret != nullptr ) + { + // If the node returns nullptr, Then it means that this node shouldn't jump to any node directly. + if ( optimal != nullptr ) + { + m_skip = nullptr; + return this; + } + + optimal = optimal_ret; + } + } + + m_skip = optimal; + return optimal; + } + + if ( std::holds_alternative< LeafDataT >( m_node_data ) ) + { + const auto& leaf_data { std::get< LeafDataT >( m_node_data ) }; + + if ( leaf_data.empty() ) return nullptr; + + return this; + } + + FGL_UNREACHABLE(); + } + std::size_t OctTreeNode::reorganize() { + ZoneScoped; std::size_t counter { 0 }; if ( std::holds_alternative< NodeDataT >( m_node_data ) ) { diff --git a/src/engine/tree/octtree/OctTreeNode.hpp b/src/engine/tree/octtree/OctTreeNode.hpp index 68000b6..906abb8 100644 --- a/src/engine/tree/octtree/OctTreeNode.hpp +++ b/src/engine/tree/octtree/OctTreeNode.hpp @@ -52,6 +52,11 @@ namespace fgl::engine OctTreeNode* m_parent; + //! Node to skip too when attempting to parse this node. + //! This is done in order to prevent navigating through an empty set of nodes. + //! This is only set if there is only one leaf filled with data. + OctTreeNode* m_skip { nullptr }; + public: OctTreeNode() = delete; @@ -106,6 +111,7 @@ namespace fgl::engine //! Rebuilds the tree checking if nodes have moved. std::size_t reorganize(); + OctTreeNode* optimizePath(); //! Returns true if the fixed bounding box is larger then the inital bounding box bool isBoundsExpanded() const;