Sfoglia il codice sorgente

SceneGraph: Optimize the update

Panagiotis Christopoulos Charitos 2 settimane fa
parent
commit
7441abef3d
6 ha cambiato i file con 148 aggiunte e 178 eliminazioni
  1. 2 0
      AnKi/Gr/Common.h
  2. 3 1
      AnKi/Gr/Texture.h
  3. 65 126
      AnKi/Scene/SceneGraph.cpp
  4. 25 15
      AnKi/Scene/SceneGraph.h
  5. 3 4
      AnKi/Scene/SceneNode.h
  6. 50 32
      AnKi/Util/BlockArray.h

+ 2 - 0
AnKi/Gr/Common.h

@@ -1042,6 +1042,7 @@ public:
 
 	void validate() const
 	{
+#if ANKI_ASSERTIONS_ENABLED
 		m_descriptor.validate();
 		for([[maybe_unused]] VertexAttributeSemantic semantic : EnumIterable<VertexAttributeSemantic>())
 		{
@@ -1054,6 +1055,7 @@ public:
 		{
 			ANKI_ASSERT(m_pixel.m_colorRenderTargetWritemask.get(i) && "Should write to all attachments");
 		}
+#endif
 	}
 
 	/// Combine shader reflection.

+ 3 - 1
AnKi/Gr/Texture.h

@@ -244,8 +244,9 @@ public:
 		return m_allSurfacesOrVolumes || b.m_allSurfacesOrVolumes || (m_mipmap == b.m_mipmap && m_face == b.m_face && m_layer == b.m_layer);
 	}
 
-	void validate(const Texture& tex) const
+	void validate([[maybe_unused]] const Texture& tex) const
 	{
+#if ANKI_ASSERTIONS_ENABLED
 		if(!m_allSurfacesOrVolumes)
 		{
 			ANKI_ASSERT(m_mipmap <= tex.getMipmapCount());
@@ -274,6 +275,7 @@ public:
 		{
 			ANKI_ASSERT(m_depthStencilAspect == DepthStencilAspectBit::kNone);
 		}
+#endif
 	}
 
 private:

+ 65 - 126
AnKi/Scene/SceneGraph.cpp

@@ -45,19 +45,6 @@ ANKI_SVAR(SceneUpdateTime, StatCategory::kTime, "All scene update", StatFlag::kM
 ANKI_SVAR(SceneComponentsUpdated, StatCategory::kScene, "Scene components updated per frame", StatFlag::kZeroEveryFrame)
 ANKI_SVAR(SceneNodesUpdated, StatCategory::kScene, "Scene nodes updated per frame", StatFlag::kZeroEveryFrame)
 
-constexpr U32 kUpdateNodeBatchSize = 1;
-
-static void deleteNodesRecursively(SceneNode* node)
-{
-	ANKI_ASSERT(node);
-	for(SceneNode* child : node->getChildren())
-	{
-		deleteNodesRecursively(child);
-	}
-
-	deleteInstance(SceneMemoryPool::getSingleton(), node);
-}
-
 class SceneGraph::UpdateSceneNodesCtx
 {
 public:
@@ -81,8 +68,8 @@ public:
 		}
 	};
 
-	IntrusiveList<SceneNode>::Iterator m_crntNode;
-	SpinLock m_crntNodeLock;
+	Atomic<U32> m_crntNodeIndex = {0};
+	U32 m_lastNodeIndex = 0;
 
 	Second m_prevUpdateTime = 0.0;
 	Second m_crntTime = 0.0;
@@ -104,14 +91,14 @@ SceneGraph::SceneGraph()
 
 SceneGraph::~SceneGraph()
 {
-	while(!m_deferredOps.m_nodesForRegistration.isEmpty())
+	for(SceneNode* node : m_deferredOps.m_nodesForRegistration)
 	{
-		deleteInstance(SceneMemoryPool::getSingleton(), m_deferredOps.m_nodesForRegistration.popBack());
+		deleteInstance(SceneMemoryPool::getSingleton(), node);
 	}
 
-	while(!m_rootNodes.isEmpty())
+	for(SceneNodeEntry& entry : m_nodes)
 	{
-		deleteNodesRecursively(m_rootNodes.popBack());
+		deleteInstance(SceneMemoryPool::getSingleton(), entry.getNode());
 	}
 
 #define ANKI_CAT_TYPE(arrayName, gpuSceneType, id, cvarName) GpuSceneArrays::arrayName::freeSingleton();
@@ -172,11 +159,11 @@ SceneNode* SceneGraph::tryFindSceneNode(const CString& name)
 
 	// Didn't found it, search those up for registration
 	LockGuard lock(m_deferredOps.m_mtx);
-	for(SceneNode& node : m_deferredOps.m_nodesForRegistration)
+	for(SceneNode* node : m_deferredOps.m_nodesForRegistration)
 	{
-		if(node.getName() == name)
+		if(node->getName() == name)
 		{
-			return &node;
+			return node;
 		}
 	}
 
@@ -218,7 +205,7 @@ void SceneGraph::update(Second prevUpdateTime, Second crntTime)
 	UpdateSceneNodesCtx updateCtx(CoreThreadJobManager::getSingleton().getThreadCount());
 	{
 		ANKI_TRACE_SCOPED_EVENT(SceneNodesUpdate);
-		updateCtx.m_crntNode = m_rootNodes.getBegin();
+		updateCtx.m_lastNodeIndex = (m_nodes.isEmpty()) ? 0 : m_nodes.getBack().getArrayIndex();
 		updateCtx.m_prevUpdateTime = prevUpdateTime;
 		updateCtx.m_crntTime = crntTime;
 		updateCtx.m_forceUpdateSceneBounds = (m_frame % kForceSetSceneBoundsFrameCount) == 0;
@@ -322,13 +309,8 @@ void SceneGraph::update(Second prevUpdateTime, Second crntTime)
 
 		for(SceneNode* node : thread.m_nodesForDeletion)
 		{
-			// Remove from the graph
-			if(node->getParent() == nullptr)
-			{
-				m_rootNodes.erase(node);
-			}
-			ANKI_ASSERT(m_nodeCount > 0);
-			--m_nodeCount;
+			// Remove from the array
+			m_nodes.erase(node->m_nodeArrayIndex);
 
 			if(m_mainCam != m_defaultMainCam && m_mainCam == node)
 			{
@@ -360,6 +342,7 @@ void SceneGraph::update(Second prevUpdateTime, Second crntTime)
 
 void SceneGraph::updateNode(U32 tid, SceneNode& node, UpdateSceneNodesCtx& ctx)
 {
+	ANKI_TRACE_FUNCTION();
 	ANKI_TRACE_INC_COUNTER(SceneNodeUpdated, 1);
 
 	UpdateSceneNodesCtx::PerThread& thread = ctx.m_perThread[tid];
@@ -456,39 +439,29 @@ void SceneGraph::updateNodes(U32 tid, UpdateSceneNodesCtx& ctx)
 {
 	ANKI_TRACE_SCOPED_EVENT(SceneNodeUpdate);
 
-	IntrusiveList<SceneNode>::ConstIterator end = m_rootNodes.getEnd();
-
-	Bool quit = false;
-	while(!quit)
+	while(1)
 	{
-		// Fetch a batch of scene nodes that don't have parent
-		Array<SceneNode*, kUpdateNodeBatchSize> batch;
-		U batchSize = 0;
+		// Fetch a batch of root scene nodes
+		constexpr U32 batchMaxSize = ANKI_CACHE_LINE_SIZE / sizeof(SceneNodeEntry); // Read a cacheline worth of SceneNodeEntry
+		Array<SceneNode*, batchMaxSize> batch;
+		U32 batchSize = 0;
 
+		const U32 entryIndex = ctx.m_crntNodeIndex.fetchAdd(batchMaxSize);
+		if(entryIndex > ctx.m_lastNodeIndex)
 		{
-			LockGuard<SpinLock> lock(ctx.m_crntNodeLock);
+			break;
+		}
 
-			while(1)
+		for(U32 i = entryIndex; i < min(entryIndex + batchMaxSize, ctx.m_lastNodeIndex + 1); ++i)
+		{
+			if(m_nodes.indexExists(i) && m_nodes[i].m_isRoot)
 			{
-				if(batchSize == batch.getSize())
-				{
-					break;
-				}
-
-				if(ctx.m_crntNode == end)
-				{
-					quit = true;
-					break;
-				}
-
-				batch[batchSize++] = &(*ctx.m_crntNode);
-
-				++ctx.m_crntNode;
+				batch[batchSize++] = m_nodes[i].getNode();
 			}
 		}
 
 		// Process nodes
-		for(U i = 0; i < batchSize; ++i)
+		for(U32 i = 0; i < batchSize; ++i)
 		{
 			SceneNode& node = *batch[i];
 			if(node.isMarkedForDeletion()) [[unlikely]]
@@ -574,7 +547,7 @@ Error SceneGraph::saveToFile(CString filename)
 
 	// Scene nodes
 	SceneNode::SerializeCommonArgs serializationArgs;
-	ANKI_ASSERT(serializableNodeCount <= m_nodeCount);
+	ANKI_ASSERT(serializableNodeCount <= m_nodes.getSize());
 	U32 nodeCount = serializableNodeCount;
 	ANKI_SERIALIZE(nodeCount, 1);
 
@@ -742,7 +715,7 @@ Error SceneGraph::loadFromFile(CString filename)
 		ANKI_CHECK(node->serializeCommon(serializer, serializationArgs));
 		ANKI_CHECK(node->serialize(serializer));
 
-		m_deferredOps.m_nodesForRegistration.pushBack(node);
+		m_deferredOps.m_nodesForRegistration.emplaceBack(node);
 	}
 
 	// Components
@@ -787,10 +760,8 @@ Error SceneGraph::loadFromFile(CString filename)
 void SceneGraph::doDeferredOperations()
 {
 	// Register new nodes
-	while(!m_deferredOps.m_nodesForRegistration.isEmpty())
+	for(SceneNode* node : m_deferredOps.m_nodesForRegistration)
 	{
-		SceneNode* node = m_deferredOps.m_nodesForRegistration.popFront();
-
 		// Add to dict if it has a name
 		if(node->getName() != "Unnamed")
 		{
@@ -806,95 +777,63 @@ void SceneGraph::doDeferredOperations()
 
 		// Add to list
 		ANKI_ASSERT(node->getParent() == nullptr && "New nodes can't have a parent yet");
-		m_rootNodes.pushBack(node);
-		++m_nodeCount;
+		SceneNodeEntry entry;
+		entry.setNode(node);
+		entry.m_isRoot = 1;
+		auto it = m_nodes.emplace(entry);
+		node->m_nodeArrayIndex = it.getArrayIndex();
 	}
+	m_deferredOps.m_nodesForRegistration.destroy();
 
 	// Renaming
+	for(auto& pair : m_deferredOps.m_nodesRenamed)
 	{
-		for(auto& pair : m_deferredOps.m_nodesRenamed)
+		SceneNode& node = *pair.first;
+		CString oldName = pair.second;
+
+		auto it = m_nodesDict.find(oldName);
+		if(it != m_nodesDict.getEnd())
+		{
+			m_nodesDict.erase(it);
+		}
+		else
 		{
-			SceneNode& node = *pair.first;
-			CString oldName = pair.second;
+			ANKI_ASSERT(oldName == "Unnamed" && "Didn't found the SceneNode and its name wasn't unnamed");
+		}
 
-			auto it = m_nodesDict.find(oldName);
-			if(it != m_nodesDict.getEnd())
+		if(node.getName() != "Unnamed")
+		{
+			if(m_nodesDict.find(node.getName()) != m_nodesDict.getEnd())
 			{
-				m_nodesDict.erase(it);
+				ANKI_SCENE_LOGE("Node with the same name already exists. New node will not be searchable: %s", node.getName().cstr());
 			}
 			else
 			{
-				ANKI_ASSERT(oldName == "Unnamed" && "Didn't found the SceneNode and its name wasn't unnamed");
-			}
-
-			if(node.getName() != "Unnamed")
-			{
-				if(m_nodesDict.find(node.getName()) != m_nodesDict.getEnd())
-				{
-					ANKI_SCENE_LOGE("Node with the same name already exists. New node will not be searchable: %s", node.getName().cstr());
-				}
-				else
-				{
-					m_nodesDict.emplace(node.getName(), &node);
-				}
+				m_nodesDict.emplace(node.getName(), &node);
 			}
 		}
-
-		m_deferredOps.m_nodesRenamed.destroy();
 	}
+	m_deferredOps.m_nodesRenamed.destroy();
 
 	// Hierarchy changes
+	for(auto& pair : m_deferredOps.m_nodesParentChanged)
 	{
-		for(auto& pair : m_deferredOps.m_nodesParentChanged)
-		{
-			SceneNode* child = pair.first;
-			SceneNode* parent = pair.second;
+		SceneNode* child = pair.first;
+		SceneNode* parent = pair.second;
 
-			const Bool childHasParent = child->getParent() != nullptr;
-			const Bool childIsRootNode = !childHasParent;
+		ANKI_ASSERT((child->getParent() == nullptr && m_nodes[child->m_nodeArrayIndex].m_isRoot) || !m_nodes[child->m_nodeArrayIndex].m_isRoot);
 
-			// Some checks
-			if(childIsRootNode)
-			{
-				[[maybe_unused]] Bool found = false;
-				for(const SceneNode& other : m_rootNodes)
-				{
-					if(other.getUuid() == child->getUuid())
-					{
-						found = true;
-					}
-				}
-				ANKI_ASSERT(found);
-			}
+		child->removeParent();
 
-			child->removeParent();
-
-			if(parent)
-			{
-				// Add new parent
-
-				if(childIsRootNode)
-				{
-					// Child was root node but not anymore, remove it from the list
-					m_rootNodes.erase(child);
-				}
-
-				static_cast<SceneNode::Base*>(child)->setParent(parent);
-			}
-			else
-			{
-				// Remove parent
-
-				if(childHasParent)
-				{
-					// Child wasn't in the rootNodes list, add it
-					m_rootNodes.pushBack(child);
-				}
-			}
+		if(parent)
+		{
+			// Add new parent
+			static_cast<SceneNode::Base*>(child)->setParent(parent);
 		}
 
-		m_deferredOps.m_nodesParentChanged.destroy();
+		m_nodes[child->m_nodeArrayIndex].m_isRoot = parent == nullptr;
 	}
+	m_deferredOps.m_nodesParentChanged.destroy();
 }
 
 } // end namespace anki

+ 25 - 15
AnKi/Scene/SceneGraph.h

@@ -85,11 +85,6 @@ public:
 		return *m_editorUi;
 	}
 
-	U32 getSceneNodesCount() const
-	{
-		return m_nodeCount;
-	}
-
 	EventManager& getEventManager()
 	{
 		return m_events;
@@ -111,13 +106,9 @@ public:
 	template<typename Func>
 	void visitNodes(Func func)
 	{
-		for(SceneNode& node : m_rootNodes)
+		for(SceneNodeEntry& entry : m_nodes)
 		{
-			const FunctorContinue cont = node.visitThisAndChildren([&](SceneNode& n) {
-				return func(n);
-			});
-
-			if(cont == FunctorContinue::kStop)
+			if(func(*entry.getNode()) == FunctorContinue::kStop)
 			{
 				break;
 			}
@@ -132,7 +123,7 @@ public:
 		TNode* node = newInstance<TNode>(SceneMemoryPool::getSingleton(), name);
 		node->m_uuid = getNewUuid();
 		LockGuard lock(m_deferredOps.m_mtx);
-		m_deferredOps.m_nodesForRegistration.pushBack(node);
+		m_deferredOps.m_nodesForRegistration.emplaceBack(node);
 		return node;
 	}
 
@@ -203,6 +194,26 @@ public:
 private:
 	class UpdateSceneNodesCtx;
 
+	class SceneNodeEntry
+	{
+	public:
+		U64 m_nodePtr : 63;
+		U64 m_isRoot : 1;
+
+		SceneNode* getNode() const
+		{
+			return numberToPtr<SceneNode*>(m_nodePtr << 1u);
+		}
+
+		void setNode(SceneNode* node)
+		{
+			U64 ptr = ptrToNumber(node);
+			ptr >>= 1u;
+			m_nodePtr = ptr;
+			ANKI_ASSERT(getNode() == node);
+		}
+	};
+
 	class InitMemPoolDummy
 	{
 	public:
@@ -216,8 +227,7 @@ private:
 
 	mutable StackMemoryPool m_framePool;
 
-	IntrusiveList<SceneNode> m_rootNodes;
-	U32 m_nodeCount = 0;
+	SceneBlockArray<SceneNodeEntry> m_nodes;
 	GrHashMap<CString, SceneNode*> m_nodesDict;
 
 	SceneNode* m_mainCam = nullptr;
@@ -235,7 +245,7 @@ private:
 	class
 	{
 	public:
-		IntrusiveList<SceneNode> m_nodesForRegistration;
+		SceneDynamicArray<SceneNode*> m_nodesForRegistration;
 		SceneDynamicArray<std::pair<SceneNode*, SceneString>> m_nodesRenamed;
 		SceneDynamicArray<std::pair<SceneNode*, SceneNode*>> m_nodesParentChanged;
 		SpinLock m_mtx;

+ 3 - 4
AnKi/Scene/SceneNode.h

@@ -7,9 +7,6 @@
 
 #include <AnKi/Scene/Components/SceneComponent.h>
 #include <AnKi/Util/Hierarchy.h>
-#include <AnKi/Util/BitMask.h>
-#include <AnKi/Util/BitSet.h>
-#include <AnKi/Util/List.h>
 #include <AnKi/Util/Enum.h>
 #include <AnKi/Util/DynamicBitSet.h>
 
@@ -68,7 +65,7 @@ public:
 };
 
 // Base class of the scene
-class SceneNode : private IntrusiveHierarchy<SceneNode>, public IntrusiveListEnabled<SceneNode>
+class SceneNode : private IntrusiveHierarchy<SceneNode>
 {
 	friend class SceneComponent;
 	friend class SceneGraph;
@@ -542,6 +539,8 @@ private:
 	SceneString m_name; // A unique name.
 	U32 m_uuid = 0;
 
+	U32 m_nodeArrayIndex = kMaxU32;
+
 	// Flags
 	Bool m_markedForDeletion : 1 = false;
 	Bool m_localTransformDirty : 1 = true;

+ 50 - 32
AnKi/Util/BlockArray.h

@@ -12,10 +12,7 @@
 
 namespace anki {
 
-/// @addtogroup util_containers
-/// @{
-
-/// Config options for a BlockArray.
+// Config options for a BlockArray.
 template<U32 kElementCountPerBlock>
 class BlockArrayConfig
 {
@@ -26,12 +23,12 @@ public:
 	}
 };
 
-/// Config options for a BlockArray.
+// Config options for a BlockArray.
 class BlockArrayDefaultConfig : public BlockArrayConfig<64>
 {
 };
 
-/// BlockArray iterator.
+// BlockArray iterator.
 template<typename TValuePointer, typename TValueReference, typename TBlockArrayPtr>
 class BlockArrayIterator
 {
@@ -42,21 +39,21 @@ class BlockArrayIterator
 	friend class BlockArrayIterator;
 
 public:
-	/// Default constructor.
+	// Default constructor.
 	BlockArrayIterator()
 		: m_array(nullptr)
 		, m_elementIdx(kMaxU32)
 	{
 	}
 
-	/// Copy.
+	// Copy.
 	BlockArrayIterator(const BlockArrayIterator& b)
 		: m_array(b.m_array)
 		, m_elementIdx(b.m_elementIdx)
 	{
 	}
 
-	/// Allow conversion from iterator to const iterator.
+	// Allow conversion from iterator to const iterator.
 	template<typename YValuePointer, typename YValueReference, typename YBlockArrayPtr>
 	BlockArrayIterator(const BlockArrayIterator<YValuePointer, YValueReference, YBlockArrayPtr>& b)
 		: m_array(b.m_array)
@@ -115,7 +112,7 @@ public:
 		return !(*this == b);
 	}
 
-	/// Returns the imaginary index inside the BlockArray.
+	// Returns the imaginary index inside the BlockArray.
 	U32 getArrayIndex() const
 	{
 		check();
@@ -133,7 +130,7 @@ private:
 	}
 };
 
-/// It's a type of dynamic array that unlike DynamicArray doesn't move elements around when it shrinks or grows the storage.
+// It's a type of dynamic array that unlike DynamicArray doesn't move elements around when it shrinks or grows the storage.
 template<typename T, typename TMemoryPool = SingletonMemoryPoolWrapper<DefaultMemoryPool>, typename TConfig = BlockArrayDefaultConfig>
 class BlockArray
 {
@@ -157,28 +154,28 @@ public:
 	{
 	}
 
-	/// Copy.
+	// Copy.
 	BlockArray(const BlockArray& b)
 	{
 		*this = b;
 	}
 
-	/// Move.
+	// Move.
 	BlockArray(BlockArray&& b)
 	{
 		*this = std::move(b);
 	}
 
-	/// Destroy.
+	// Destroy.
 	~BlockArray()
 	{
 		destroy();
 	}
 
-	/// Copy.
+	// Copy.
 	BlockArray& operator=(const BlockArray& b);
 
-	/// Move operator.
+	// Move operator.
 	BlockArray& operator=(BlockArray&& b)
 	{
 		destroy();
@@ -209,54 +206,76 @@ public:
 		return const_cast<Reference>(constSelf[idx]);
 	}
 
-	/// Get begin.
+	// Get begin.
 	Iterator getBegin()
 	{
 		return Iterator(this, m_firstIndex);
 	}
 
-	/// Get begin.
+	// Get begin.
 	ConstIterator getBegin() const
 	{
 		return ConstIterator(this, m_firstIndex);
 	}
 
-	/// Get end.
+	// Get end.
 	Iterator getEnd()
 	{
 		return Iterator(this, m_endIndex);
 	}
 
-	/// Get end.
+	// Get end.
 	ConstIterator getEnd() const
 	{
 		return ConstIterator(this, m_endIndex);
 	}
 
-	/// Get begin.
+	// Get begin.
 	Iterator begin()
 	{
 		return getBegin();
 	}
 
-	/// Get begin.
+	// Get begin.
 	ConstIterator begin() const
 	{
 		return getBegin();
 	}
 
-	/// Get end.
+	// Get end.
 	Iterator end()
 	{
 		return getEnd();
 	}
 
-	/// Get end.
+	// Get end.
 	ConstIterator end() const
 	{
 		return getEnd();
 	}
 
+	Iterator getFront()
+	{
+		return getBegin();
+	}
+
+	ConstIterator getFront() const
+	{
+		return getBegin();
+	}
+
+	Iterator getBack()
+	{
+		ANKI_ASSERT(m_endIndex > 0);
+		return Iterator(this, m_endIndex - 1);
+	}
+
+	ConstIterator getBack() const
+	{
+		ANKI_ASSERT(m_endIndex > 0);
+		return ConstIterator(this, m_endIndex - 1);
+	}
+
 	U32 getSize() const
 	{
 		return m_elementCount;
@@ -267,19 +286,19 @@ public:
 		return m_elementCount == 0;
 	}
 
-	/// Destroy the array and free its elements.
+	// Destroy the array and free its elements.
 	void destroy();
 
-	/// Emplace somewhere in some block.
+	// Emplace somewhere in some block.
 	template<typename... TArgs>
 	Iterator emplace(TArgs&&... args);
 
-	/// Removes one element.
-	/// @param at Points to the position of the element to remove.
+	// Removes one element.
+	// at: Points to the position of the element to remove.
 	void erase(Iterator idx);
 
-	/// Removes one element.
-	/// @param at Points to the position of the element to remove.
+	// Removes one element.
+	// at: Points to the position of the element to remove.
 	void erase(U32 index)
 	{
 		erase(indexToIterator(index));
@@ -337,7 +356,7 @@ private:
 	DynamicArray<BlockMetadata, TMemoryPool> m_blockMetadatas;
 	U32 m_elementCount = 0;
 	U32 m_firstIndex = 0;
-	U32 m_endIndex = 0; ///< The index after the last.
+	U32 m_endIndex = 0; // The index after the last.
 
 	U32 getFirstElementIndex() const
 	{
@@ -372,7 +391,6 @@ private:
 
 	U32 getNextElementIndex(U32 crnt) const;
 };
-/// @}
 
 } // end namespace anki