Просмотр исходного кода

ResourceHandle.isLoaded now requires no allocations
Reduced number of allocations during element sorting in GUIManager::updateMeshes
Fixed a crash in frame alloc

Marko Pintera 10 лет назад
Родитель
Сommit
c544e302c4

+ 2 - 2
BansheeCore/Include/BsFont.h

@@ -87,12 +87,12 @@ namespace BansheeEngine
 		/**
 		 * @copydoc	Resource::getResourceDependencies
 		 */
-		void getResourceDependencies(Vector<HResource>& dependencies) const;
+		void getResourceDependencies(FrameVector<HResource>& dependencies) const override;
 
 		/**
 		 * @copydoc	CoreObject::getCoreDependencies
 		 */
-		void getCoreDependencies(Vector<SPtr<CoreObject>>& dependencies);
+		void getCoreDependencies(Vector<SPtr<CoreObject>>& dependencies) override;
 
 	private:
 		Map<UINT32, FontData> mFontDataPerSize;

+ 12 - 12
BansheeCore/Include/BsGpuParams.h

@@ -301,12 +301,12 @@ namespace BansheeEngine
 		/**
 		 * @copydoc	CoreObject::getThisPtr
 		 */
-		SPtr<GpuParamsCore> _getThisPtr() const;
+		SPtr<GpuParamsCore> _getThisPtr() const override;
 
 		/**
 		 * @copydoc	CoreObjectCore::syncToCore
 		 */
-		void syncToCore(const CoreSyncData& data);
+		void syncToCore(const CoreSyncData& data) override;
 	};
 
 	/**
@@ -329,14 +329,14 @@ namespace BansheeEngine
 		 *
 		 * @note	Internal method.
 		 */
-		void _markCoreDirty();
+		void _markCoreDirty() override;
 
 		/**
 		 * @copydoc	IResourceListener::markResourcesDirty
 		 *
 		 * @note	Internal method.
 		 */
-		void _markResourcesDirty();
+		void _markResourcesDirty() override;
 
 		/**
 		 * @brief	Retrieves a core implementation of a mesh usable only from the
@@ -358,36 +358,36 @@ namespace BansheeEngine
 		/**
 		 * @copydoc	CoreObject::getThisPtr
 		 */
-		SPtr<GpuParams> _getThisPtr() const;
+		SPtr<GpuParams> _getThisPtr() const override;
 
 		/**
 		 * @copydoc	CoreObject::createCore
 		 */
-		SPtr<CoreObjectCore> createCore() const;
+		SPtr<CoreObjectCore> createCore() const override;
 
 		/**
 		 * @copydoc	CoreObject::syncToCore
 		 */
-		CoreSyncData syncToCore(FrameAlloc* allocator);
+		CoreSyncData syncToCore(FrameAlloc* allocator) override;
 
 		/**
-		 * @copydoc	IResourceListener::getResourceDependencies
+		 * @copydoc	IResourceListener::getListenerResources
 		 */
-		void getListenerResources(Vector<HResource>& resources);
+		void getListenerResources(Vector<HResource>& resources) override;
 
 		/**
 		 * @copydoc IResourceListener::notifyResourceLoaded
 		 */
-		void notifyResourceLoaded(const HResource& resource) { markCoreDirty(); }
+		void notifyResourceLoaded(const HResource& resource) override { markCoreDirty(); }
 
 		/**
 		 * @copydoc IResourceListener::notifyResourceDestroyed
 		 */
-		void notifyResourceDestroyed(const HResource& resource) { markCoreDirty(); }
+		void notifyResourceDestroyed(const HResource& resource) override { markCoreDirty(); }
 
 		/**
 		 * @copydoc IResourceListener::notifyResourceChanged
 		 */
-		void notifyResourceChanged(const HResource& resource) { markCoreDirty(); }
+		void notifyResourceChanged(const HResource& resource) override { markCoreDirty(); }
 	};
 }

+ 14 - 14
BansheeCore/Include/BsMaterial.h

@@ -610,7 +610,7 @@ namespace BansheeEngine
 		/**
 		 * @copydoc	CoreObjectCore::syncToCore
 		 */
-		void syncToCore(const CoreSyncData& data);
+		void syncToCore(const CoreSyncData& data) override;
 	};
 
 	/**
@@ -642,7 +642,7 @@ namespace BansheeEngine
 		/**
 		 * @copydoc	CoreObject::initialize
 		 */
-		void initialize();
+		void initialize() override;
 
 		/**
 		 * @brief	Creates a new empty material.
@@ -665,52 +665,52 @@ namespace BansheeEngine
 		/**
 		 * @copydoc	CoreObject::createCore
 		 */
-		SPtr<CoreObjectCore> createCore() const;
+		SPtr<CoreObjectCore> createCore() const override;
 
 		/**
 		 * @copydoc	CoreObject::syncToCore
 		 */
-		CoreSyncData syncToCore(FrameAlloc* allocator);
+		CoreSyncData syncToCore(FrameAlloc* allocator) override;
 
 		/**
 		 * @copydoc	CoreObject::getCoreDependencies
 		 */
-		void getCoreDependencies(Vector<SPtr<CoreObject>>& dependencies);
+		void getCoreDependencies(Vector<SPtr<CoreObject>>& dependencies) override;
 
 		/**
 		 * @copydoc	CoreObject::markCoreDirty
 		 */
-		void _markCoreDirty();
+		void _markCoreDirty() override;
 
 		/**
 		 * @copydoc	IResourceListener::markResourcesDirty
 		 */
-		void _markResourcesDirty();
+		void _markResourcesDirty() override;
 
 		/**
-		 * @copydoc	IResourceListener::getResourceDependencies
+		 * @copydoc	IResourceListener::getListenerResources
 		 */
-		void getListenerResources(Vector<HResource>& resources);
+		void getListenerResources(Vector<HResource>& resources) override;
 
 		/**
 		 * @copydoc IResourceListener::notifyResourceLoaded
 		 */
-		void notifyResourceLoaded(const HResource& resource);
+		void notifyResourceLoaded(const HResource& resource) override;
 
 		/**
 		 * @copydoc IResourceListener::notifyResourceDestroyed
 		 */
-		void notifyResourceDestroyed(const HResource& resource);
+		void notifyResourceDestroyed(const HResource& resource) override;
 
 		/**
 		 * @copydoc IResourceListener::notifyResourceChanged
 		 */
-		void notifyResourceChanged(const HResource& resource);
+		void notifyResourceChanged(const HResource& resource) override;
 
 		/**
 		 * @copydoc	Resource::getResourceDependencies
 		 */
-		void getResourceDependencies(Vector<HResource>& dependencies) const;
+		void getResourceDependencies(FrameVector<HResource>& dependencies) const override;
 
 		/**
 		 * @brief	Performs material initialization when all resources are ready.
@@ -726,6 +726,6 @@ namespace BansheeEngine
 	public:
 		friend class MaterialRTTI;
 		static RTTITypeBase* getRTTIStatic();
-		virtual RTTITypeBase* getRTTI() const;	
+		virtual RTTITypeBase* getRTTI() const override;
 	};
 }

+ 1 - 1
BansheeCore/Include/BsResource.h

@@ -45,7 +45,7 @@ namespace BansheeEngine
 		 * TODO - Consider using a stack-allocated data type since returned data is almost
 		 *		  always transient. Then we can save on memory allocations.
 		 */
-		virtual void getResourceDependencies(Vector<HResource>& dependencies) const { }
+		virtual void getResourceDependencies(FrameVector<HResource>& dependencies) const { }
 
 		/**
 		 * @brief	Checks if all the resources this object is dependent on are fully loaded.

+ 1 - 1
BansheeCore/Source/BsFont.cpp

@@ -83,7 +83,7 @@ namespace BansheeEngine
 		return bestSize;
 	}
 
-	void Font::getResourceDependencies(Vector<HResource>& dependencies) const
+	void Font::getResourceDependencies(FrameVector<HResource>& dependencies) const
 	{
 		for (auto& fontDataEntry : mFontDataPerSize)
 		{

+ 12 - 2
BansheeCore/Source/BsMaterial.cpp

@@ -1176,10 +1176,20 @@ namespace BansheeEngine
 
 	void Material::getListenerResources(Vector<HResource>& resources)
 	{
-		getResourceDependencies(resources);
+		bs_frame_mark();
+
+		{
+			FrameVector<HResource> temp;
+			getResourceDependencies(temp);
+
+			for (auto& entry : temp)
+				resources.push_back(entry);
+		}
+
+		bs_frame_clear();
 	}
 
-	void Material::getResourceDependencies(Vector<HResource>& dependencies) const
+	void Material::getResourceDependencies(FrameVector<HResource>& dependencies) const
 	{
 		if (mShader != nullptr)
 			dependencies.push_back(mShader);

+ 15 - 6
BansheeCore/Source/BsResource.cpp

@@ -25,16 +25,25 @@ namespace BansheeEngine
 
 	bool Resource::areDependenciesLoaded() const
 	{
-		Vector<HResource> dependencies;
-		getResourceDependencies(dependencies);
+		bs_frame_mark();
 
-		for (auto& dependency : dependencies)
+		bool areLoaded = true;
 		{
-			if (dependency != nullptr && !dependency.isLoaded())
-				return false;
+			FrameVector<HResource> dependencies;
+			getResourceDependencies(dependencies);
+
+			for (auto& dependency : dependencies)
+			{
+				if (dependency != nullptr && !dependency.isLoaded())
+				{
+					areLoaded = false;
+					break;
+				}
+			}
 		}
 
-		return true;
+		bs_frame_clear();
+		return areLoaded;
 	}
 
 	RTTITypeBase* Resource::getRTTIStatic()

+ 10 - 4
BansheeCore/Source/BsResourceHandle.cpp

@@ -50,11 +50,17 @@ namespace BansheeEngine
 
 		if (waitForDependencies)
 		{
-			Vector<HResource> dependencies;
-			mData->mPtr->getResourceDependencies(dependencies);
+			bs_frame_mark();
 
-			for (auto& dependency : dependencies)
-				dependency.blockUntilLoaded(waitForDependencies);
+			{
+				FrameVector<HResource> dependencies;
+				mData->mPtr->getResourceDependencies(dependencies);
+
+				for (auto& dependency : dependencies)
+					dependency.blockUntilLoaded(waitForDependencies);
+			}
+
+			bs_frame_clear();
 		}
 	}
 

+ 1 - 1
BansheeEngine/Include/BsRenderableHandler.h

@@ -244,7 +244,7 @@ namespace BansheeEngine
 		void getCoreDependencies(Vector<SPtr<CoreObject>>& dependencies) override;
 
 		/**
-		 * @copydoc	IResourceListener::getResourceDependencies
+		 * @copydoc	IResourceListener::getListenerResources
 		 */
 		void getListenerResources(Vector<HResource>& resources) override;
 

+ 1 - 1
BansheeEngine/Include/BsSpriteTexture.h

@@ -67,7 +67,7 @@ namespace BansheeEngine
 		/**
 		 * @copydoc	Resource::getResourceDependencies
 		 */
-		void getResourceDependencies(Vector<HResource>& dependencies) const override;
+		void getResourceDependencies(FrameVector<HResource>& dependencies) const override;
 
 		/**
 		 * @copydoc	CoreObject::getCoreDependencies

+ 193 - 170
BansheeEngine/Source/BsGUIManager.cpp

@@ -191,10 +191,12 @@ namespace BansheeEngine
 		DragAndDropManager::instance()._update();
 
 		// Update layouts
+		gProfilerCPU().beginSample("UpdateLayout");
 		for(auto& widgetInfo : mWidgets)
 		{
 			widgetInfo.widget->_updateLayout();
 		}
+		gProfilerCPU().endSample("UpdateLayout");
 
 		// Destroy all queued elements (and loop in case any new ones get queued during destruction)
 		do
@@ -368,231 +370,252 @@ namespace BansheeEngine
 			if(!isDirty)
 				continue;
 
-			// Make a list of all GUI elements, sorted from farthest to nearest (highest depth to lowest)
-			auto elemComp = [](const GUIGroupElement& a, const GUIGroupElement& b)
+			bs_frame_mark();
 			{
-				UINT32 aDepth = a.element->_getRenderElementDepth(a.renderElement);
-				UINT32 bDepth = b.element->_getRenderElementDepth(b.renderElement);
+				gProfilerCPU().beginSample("Sort elements");
 
-				// Compare pointers just to differentiate between two elements with the same depth, their order doesn't really matter, but std::set
-				// requires all elements to be unique
-				return (aDepth > bDepth) || 
-					(aDepth == bDepth && a.element > b.element) || 
-					(aDepth == bDepth && a.element == b.element && a.renderElement > b.renderElement); 
-			};
+				// Make a list of all GUI elements, sorted from farthest to nearest (highest depth to lowest)
+				auto elemComp = [](const GUIGroupElement& a, const GUIGroupElement& b)
+				{
+					UINT32 aDepth = a.element->_getRenderElementDepth(a.renderElement);
+					UINT32 bDepth = b.element->_getRenderElementDepth(b.renderElement);
 
-			Set<GUIGroupElement, std::function<bool(const GUIGroupElement&, const GUIGroupElement&)>> allElements(elemComp);
+					// Compare pointers just to differentiate between two elements with the same depth, their order doesn't really matter, but std::set
+					// requires all elements to be unique
+					return (aDepth > bDepth) || 
+						(aDepth == bDepth && a.element > b.element) || 
+						(aDepth == bDepth && a.element == b.element && a.renderElement > b.renderElement); 
+				};
 
-			for(auto& widget : renderData.widgets)
-			{
-				const Vector<GUIElement*>& elements = widget->getElements();
+				FrameSet<GUIGroupElement, std::function<bool(const GUIGroupElement&, const GUIGroupElement&)>> allElements(elemComp);
 
-				for(auto& element : elements)
+				gProfilerCPU().beginSample("InsertAll");
+				for (auto& widget : renderData.widgets)
 				{
-					if(element->_isDisabled())
-						continue;
+					const Vector<GUIElement*>& elements = widget->getElements();
 
-					UINT32 numRenderElems = element->_getNumRenderElements();
-					for(UINT32 i = 0; i < numRenderElems; i++)
+					for (auto& element : elements)
 					{
-						allElements.insert(GUIGroupElement(element, i));
-					}
-				}
-			}
+						if (element->_isDisabled())
+							continue;
 
-			// Group the elements in such a way so that we end up with a smallest amount of
-			// meshes, without breaking back to front rendering order
-			UnorderedMap<UINT64, Vector<GUIMaterialGroup>> materialGroups;
-			for(auto& elem : allElements)
-			{
-				GUIElement* guiElem = elem.element;
-				UINT32 renderElemIdx = elem.renderElement;
-				UINT32 elemDepth = guiElem->_getRenderElementDepth(renderElemIdx);
-
-				Rect2I tfrmedBounds = guiElem->_getClippedBounds();
-				tfrmedBounds.transform(guiElem->_getParentWidget()->SO()->getWorldTfrm());
-
-				const GUIMaterialInfo& matInfo = guiElem->_getMaterial(renderElemIdx);
-
-				UINT64 materialId = matInfo.material->getInternalID(); // TODO - I group based on material ID. So if two widgets used exact copies of the same material
-				// this system won't detect it. Find a better way of determining material similarity?
-
-				// If this is a new material, add a new list of groups
-				auto findIterMaterial = materialGroups.find(materialId);
-				if(findIterMaterial == end(materialGroups))
-					materialGroups[materialId] = Vector<GUIMaterialGroup>();
-
-				// Try to find a group this material will fit in:
-				//  - Group that has a depth value same or one below elements depth will always be a match
-				//  - Otherwise, we search higher depth values as well, but we only use them if no elements in between those depth values
-				//    overlap the current elements bounds.
-				Vector<GUIMaterialGroup>& allGroups = materialGroups[materialId];
-				GUIMaterialGroup* foundGroup = nullptr;
-				for(auto groupIter = allGroups.rbegin(); groupIter != allGroups.rend(); ++groupIter)
-				{
-					// If we separate meshes by widget, ignore any groups with widget parents other than mine
-					if(mSeparateMeshesByWidget)
-					{
-						if(groupIter->elements.size() > 0)
+						UINT32 numRenderElems = element->_getNumRenderElements();
+						for (UINT32 i = 0; i < numRenderElems; i++)
 						{
-							GUIElement* otherElem = groupIter->elements.begin()->element; // We only need to check the first element
-							if(otherElem->_getParentWidget() != guiElem->_getParentWidget())
-								continue;
+							allElements.insert(GUIGroupElement(element, i));
 						}
 					}
+				}
+				gProfilerCPU().endSample("InsertAll");
 
-					GUIMaterialGroup& group = *groupIter;
+				// Group the elements in such a way so that we end up with a smallest amount of
+				// meshes, without breaking back to front rendering order
+				FrameUnorderedMap<UINT64, FrameVector<GUIMaterialGroup>> materialGroups;
+				for (auto& elem : allElements)
+				{
+					GUIElement* guiElem = elem.element;
+					UINT32 renderElemIdx = elem.renderElement;
+					UINT32 elemDepth = guiElem->_getRenderElementDepth(renderElemIdx);
 
-					if(group.depth == elemDepth || group.depth == (elemDepth - 1))
-					{
-						foundGroup = &group;
-						break;
-					}
-					else
-					{
-						UINT32 startDepth = elemDepth;
-						UINT32 endDepth = group.depth;
+					gProfilerCPU().beginSample("Tfrm");
 
-						Rect2I potentialGroupBounds = group.bounds;
-						potentialGroupBounds.encapsulate(tfrmedBounds);
+					Rect2I tfrmedBounds = guiElem->_getClippedBounds();
+					tfrmedBounds.transform(guiElem->_getParentWidget()->SO()->getWorldTfrm());
 
-						bool foundOverlap = false;
-						for(auto& material : materialGroups)
+					gProfilerCPU().endSample("Tfrm");
+
+					const GUIMaterialInfo& matInfo = guiElem->_getMaterial(renderElemIdx);
+
+					UINT64 materialId = matInfo.material->getInternalID(); // TODO - I group based on material ID. So if two widgets used exact copies of the same material
+					// this system won't detect it. Find a better way of determining material similarity?
+
+					// If this is a new material, add a new list of groups
+					auto findIterMaterial = materialGroups.find(materialId);
+					if (findIterMaterial == end(materialGroups))
+						materialGroups[materialId] = FrameVector<GUIMaterialGroup>();
+
+					// Try to find a group this material will fit in:
+					//  - Group that has a depth value same or one below elements depth will always be a match
+					//  - Otherwise, we search higher depth values as well, but we only use them if no elements in between those depth values
+					//    overlap the current elements bounds.
+					FrameVector<GUIMaterialGroup>& allGroups = materialGroups[materialId];
+					GUIMaterialGroup* foundGroup = nullptr;
+					for (auto groupIter = allGroups.rbegin(); groupIter != allGroups.rend(); ++groupIter)
+					{
+						// If we separate meshes by widget, ignore any groups with widget parents other than mine
+						if (mSeparateMeshesByWidget)
 						{
-							for(auto& matGroup : material.second)
+							if (groupIter->elements.size() > 0)
 							{
-								if(&matGroup == &group)
+								GUIElement* otherElem = groupIter->elements.begin()->element; // We only need to check the first element
+								if (otherElem->_getParentWidget() != guiElem->_getParentWidget())
 									continue;
+							}
+						}
+
+						GUIMaterialGroup& group = *groupIter;
+
+						if (group.depth == elemDepth || group.depth == (elemDepth - 1))
+						{
+							foundGroup = &group;
+							break;
+						}
+						else
+						{
+							UINT32 startDepth = elemDepth;
+							UINT32 endDepth = group.depth;
+
+							Rect2I potentialGroupBounds = group.bounds;
+							potentialGroupBounds.encapsulate(tfrmedBounds);
 
-								if(matGroup.depth > startDepth && matGroup.depth < endDepth)
+							bool foundOverlap = false;
+							for (auto& material : materialGroups)
+							{
+								for (auto& matGroup : material.second)
 								{
-									if(matGroup.bounds.overlaps(potentialGroupBounds))
+									if (&matGroup == &group)
+										continue;
+
+									if (matGroup.depth > startDepth && matGroup.depth < endDepth)
 									{
-										foundOverlap = true;
-										break;
+										if (matGroup.bounds.overlaps(potentialGroupBounds))
+										{
+											foundOverlap = true;
+											break;
+										}
 									}
 								}
 							}
-						}
 
-						if(!foundOverlap)
-						{
-							foundGroup = &group;
-							break;
+							if (!foundOverlap)
+							{
+								foundGroup = &group;
+								break;
+							}
 						}
 					}
-				}
 
-				if(foundGroup == nullptr)
-				{
-					allGroups.push_back(GUIMaterialGroup());
-					foundGroup = &allGroups[allGroups.size() - 1];
-
-					foundGroup->depth = elemDepth;
-					foundGroup->bounds = tfrmedBounds;
-					foundGroup->elements.push_back(GUIGroupElement(guiElem, renderElemIdx));
-					foundGroup->matInfo = matInfo;
-					foundGroup->numQuads = guiElem->_getNumQuads(renderElemIdx);
-				}
-				else
-				{
-					foundGroup->bounds.encapsulate(tfrmedBounds);
-					foundGroup->elements.push_back(GUIGroupElement(guiElem, renderElemIdx));
-					foundGroup->depth = std::min(foundGroup->depth, elemDepth);
-					foundGroup->numQuads += guiElem->_getNumQuads(renderElemIdx);
+					gProfilerCPU().beginSample("AddToGroup");
+
+					if (foundGroup == nullptr)
+					{
+						allGroups.push_back(GUIMaterialGroup());
+						foundGroup = &allGroups[allGroups.size() - 1];
+
+						foundGroup->depth = elemDepth;
+						foundGroup->bounds = tfrmedBounds;
+						foundGroup->elements.push_back(GUIGroupElement(guiElem, renderElemIdx));
+						foundGroup->matInfo = matInfo;
+						foundGroup->numQuads = guiElem->_getNumQuads(renderElemIdx);
+					}
+					else
+					{
+						foundGroup->bounds.encapsulate(tfrmedBounds);
+						foundGroup->elements.push_back(GUIGroupElement(guiElem, renderElemIdx));
+						foundGroup->depth = std::min(foundGroup->depth, elemDepth);
+						foundGroup->numQuads += guiElem->_getNumQuads(renderElemIdx);
+					}
+
+					gProfilerCPU().endSample("AddToGroup");
 				}
-			}
 
-			// Make a list of all GUI elements, sorted from farthest to nearest (highest depth to lowest)
-			auto groupComp = [](GUIMaterialGroup* a, GUIMaterialGroup* b)
-			{
-				return (a->depth > b->depth) || (a->depth == b->depth && a > b);
-				// Compare pointers just to differentiate between two elements with the same depth, their order doesn't really matter, but std::set
-				// requires all elements to be unique
-			};
+				// Make a list of all GUI elements, sorted from farthest to nearest (highest depth to lowest)
+				auto groupComp = [](GUIMaterialGroup* a, GUIMaterialGroup* b)
+				{
+					return (a->depth > b->depth) || (a->depth == b->depth && a > b);
+					// Compare pointers just to differentiate between two elements with the same depth, their order doesn't really matter, but std::set
+					// requires all elements to be unique
+				};
 
-			Set<GUIMaterialGroup*, std::function<bool(GUIMaterialGroup*, GUIMaterialGroup*)>> sortedGroups(groupComp);
-			for(auto& material : materialGroups)
-			{
-				for(auto& group : material.second)
+				FrameSet<GUIMaterialGroup*, std::function<bool(GUIMaterialGroup*, GUIMaterialGroup*)>> sortedGroups(groupComp);
+				for(auto& material : materialGroups)
 				{
-					sortedGroups.insert(&group);
+					for(auto& group : material.second)
+					{
+						sortedGroups.insert(&group);
+					}
 				}
-			}
 
-			UINT32 numMeshes = (UINT32)sortedGroups.size();
-			UINT32 oldNumMeshes = (UINT32)renderData.cachedMeshes.size();
+				gProfilerCPU().endSample("Sort elements");
 
-			if(numMeshes < oldNumMeshes)
-			{
-				for (UINT32 i = numMeshes; i < oldNumMeshes; i++)
-					mMeshHeap->dealloc(renderData.cachedMeshes[i]);
+				gProfilerCPU().beginSample("Mesh data");
 
-				renderData.cachedMeshes.resize(numMeshes);
-			}
+				UINT32 numMeshes = (UINT32)sortedGroups.size();
+				UINT32 oldNumMeshes = (UINT32)renderData.cachedMeshes.size();
 
-			renderData.cachedMaterials.resize(numMeshes);
+				if(numMeshes < oldNumMeshes)
+				{
+					for (UINT32 i = numMeshes; i < oldNumMeshes; i++)
+						mMeshHeap->dealloc(renderData.cachedMeshes[i]);
 
-			if(mSeparateMeshesByWidget)
-				renderData.cachedWidgetsPerMesh.resize(numMeshes);
+					renderData.cachedMeshes.resize(numMeshes);
+				}
 
-			// Fill buffers for each group and update their meshes
-			UINT32 groupIdx = 0;
-			for(auto& group : sortedGroups)
-			{
-				renderData.cachedMaterials[groupIdx] = group->matInfo;
+				renderData.cachedMaterials.resize(numMeshes);
 
 				if(mSeparateMeshesByWidget)
+					renderData.cachedWidgetsPerMesh.resize(numMeshes);
+
+				// Fill buffers for each group and update their meshes
+				UINT32 groupIdx = 0;
+				for(auto& group : sortedGroups)
 				{
-					if(group->elements.size() == 0)
-						renderData.cachedWidgetsPerMesh[groupIdx] = nullptr;
-					else
+					renderData.cachedMaterials[groupIdx] = group->matInfo;
+
+					if(mSeparateMeshesByWidget)
 					{
-						GUIElement* elem = group->elements.begin()->element;
-						renderData.cachedWidgetsPerMesh[groupIdx] = elem->_getParentWidget();
+						if(group->elements.size() == 0)
+							renderData.cachedWidgetsPerMesh[groupIdx] = nullptr;
+						else
+						{
+							GUIElement* elem = group->elements.begin()->element;
+							renderData.cachedWidgetsPerMesh[groupIdx] = elem->_getParentWidget();
+						}
 					}
-				}
 
-				MeshDataPtr meshData = bs_shared_ptr<MeshData, PoolAlloc>(group->numQuads * 4, group->numQuads * 6, mVertexDesc);
+					MeshDataPtr meshData = bs_shared_ptr<MeshData, PoolAlloc>(group->numQuads * 4, group->numQuads * 6, mVertexDesc);
 
-				UINT8* vertices = meshData->getElementData(VES_POSITION);
-				UINT8* uvs = meshData->getElementData(VES_TEXCOORD);
-				UINT32* indices = meshData->getIndices32();
-				UINT32 vertexStride = meshData->getVertexDesc()->getVertexStride();
-				UINT32 indexStride = meshData->getIndexElementSize();
+					UINT8* vertices = meshData->getElementData(VES_POSITION);
+					UINT8* uvs = meshData->getElementData(VES_TEXCOORD);
+					UINT32* indices = meshData->getIndices32();
+					UINT32 vertexStride = meshData->getVertexDesc()->getVertexStride();
+					UINT32 indexStride = meshData->getIndexElementSize();
 
-				UINT32 quadOffset = 0;
-				for(auto& matElement : group->elements)
-				{
-					gProfilerCPU().beginSample("_fillBuffer");
-					matElement.element->_fillBuffer(vertices, uvs, indices, quadOffset, group->numQuads, vertexStride, indexStride, matElement.renderElement);
-					gProfilerCPU().endSample("_fillBuffer");
+					UINT32 quadOffset = 0;
+					for(auto& matElement : group->elements)
+					{
+						matElement.element->_fillBuffer(vertices, uvs, indices, quadOffset, group->numQuads, vertexStride, indexStride, matElement.renderElement);
 
-					UINT32 numQuads = matElement.element->_getNumQuads(matElement.renderElement);
-					UINT32 indexStart = quadOffset * 6;
-					UINT32 indexEnd = indexStart + numQuads * 6;
-					UINT32 vertOffset = quadOffset * 4;
+						UINT32 numQuads = matElement.element->_getNumQuads(matElement.renderElement);
+						UINT32 indexStart = quadOffset * 6;
+						UINT32 indexEnd = indexStart + numQuads * 6;
+						UINT32 vertOffset = quadOffset * 4;
 
-					for(UINT32 i = indexStart; i < indexEnd; i++)
-						indices[i] += vertOffset;
+						for(UINT32 i = indexStart; i < indexEnd; i++)
+							indices[i] += vertOffset;
 
-					quadOffset += numQuads;
-				}
+						quadOffset += numQuads;
+					}
 
-				gProfilerCPU().beginSample("alloc/dealloc mesh data");
-				if(groupIdx < (UINT32)renderData.cachedMeshes.size())
-				{
-					mMeshHeap->dealloc(renderData.cachedMeshes[groupIdx]);
-					renderData.cachedMeshes[groupIdx] = mMeshHeap->alloc(meshData);
-				}
-				else
-				{
-					renderData.cachedMeshes.push_back(mMeshHeap->alloc(meshData));
+					gProfilerCPU().beginSample("alloc/dealloc mesh data");
+					if(groupIdx < (UINT32)renderData.cachedMeshes.size())
+					{
+						mMeshHeap->dealloc(renderData.cachedMeshes[groupIdx]);
+						renderData.cachedMeshes[groupIdx] = mMeshHeap->alloc(meshData);
+					}
+					else
+					{
+						renderData.cachedMeshes.push_back(mMeshHeap->alloc(meshData));
+					}
+					gProfilerCPU().endSample("alloc/dealloc mesh data");
+
+					groupIdx++;
 				}
-				gProfilerCPU().endSample("alloc/dealloc mesh data");
 
-				groupIdx++;
+				gProfilerCPU().endSample("Mesh data");
 			}
+
+			bs_frame_clear();			
 		}
 	}
 

+ 1 - 1
BansheeEngine/Source/BsSpriteTexture.cpp

@@ -43,7 +43,7 @@ namespace BansheeEngine
 		return Math::roundToInt(mAtlasTexture->getProperties().getHeight() * mUVScale.y);
 	}
 
-	void SpriteTexture::getResourceDependencies(Vector<HResource>& dependencies) const
+	void SpriteTexture::getResourceDependencies(FrameVector<HResource>& dependencies) const
 	{
 		if (mAtlasTexture != nullptr)
 			dependencies.push_back(mAtlasTexture);

+ 24 - 0
BansheeUtility/Include/BsGlobalFrameAlloc.h

@@ -147,4 +147,28 @@ namespace BansheeEngine
 			bs_frame_free(ptr);
 		}
 	};
+
+	/**
+	 * Implementations of various standard library constructs using the frame allocator.
+	 */
+	typedef std::basic_string<char, std::char_traits<char>, StdAlloc<char, FrameAlloc>> FrameString;
+	typedef std::basic_string<wchar_t, std::char_traits<wchar_t>, StdAlloc<wchar_t, FrameAlloc>> FrameWString;
+
+	template <typename T, typename A = StdAlloc<T, FrameAlloc>>
+	using FrameVector = std::vector < T, A > ;
+
+	template <typename T, typename A = StdAlloc<T, FrameAlloc>>
+	using FrameStack = std::stack < T, std::deque<T, A> > ;
+
+	template <typename T, typename P = std::less<T>, typename A = StdAlloc<T, FrameAlloc>>
+	using FrameSet = std::set < T, P, A > ;
+
+	template <typename K, typename V, typename P = std::less<K>, typename A = StdAlloc<std::pair<const K, V>, FrameAlloc>>
+	using FrameMap = std::map < K, V, P, A >;
+
+	template <typename T, typename H = std::hash<T>, typename C = std::equal_to<T>, typename A = StdAlloc<T, FrameAlloc>>
+	using FrameUnorderedSet = std::unordered_set < T, H, C, A >;
+
+	template <typename K, typename V, typename H = std::hash<K>, typename C = std::equal_to<K>, typename A = StdAlloc<std::pair<const K, V>, FrameAlloc>>
+	using FrameUnorderedMap = std::unordered_map < K, V, H, C, A >;
 }

+ 13 - 6
BansheeUtility/Source/BsFrameAlloc.cpp

@@ -114,7 +114,10 @@ namespace BansheeEngine
 					if (curBlock->mFreePtr == 0)
 					{
 						numFreedBlocks++;
-						mNextBlockIdx = i;
+
+						// Reset block counter if we're gonna reallocate this one
+						if (numFreedBlocks > 1)
+							mNextBlockIdx = i;
 					}
 
 					break;
@@ -127,7 +130,6 @@ namespace BansheeEngine
 				}
 			}
 
-			UINT32 oldNextBlockIdx = mNextBlockIdx;
 			if (numFreedBlocks > 1)
 			{
 				UINT32 totalBytes = 0;
@@ -140,12 +142,17 @@ namespace BansheeEngine
 					mBlocks.erase(mBlocks.begin() + mNextBlockIdx);
 				}
 				
+				UINT32 oldNextBlockIdx = mNextBlockIdx;
 				allocBlock(totalBytes);
+
+				// Point to the first non-full block, or if none available then point the the block we just allocated
+				if (oldNextBlockIdx > 0)
+					mFreeBlock = mBlocks[oldNextBlockIdx - 1];
+			}
+			else
+			{
+				mFreeBlock = mBlocks[mNextBlockIdx - 1];
 			}
-			
-			// Point to the first non-full block, or if none available then point the the block we just allocated
-			if (oldNextBlockIdx > 0)
-				mFreeBlock = mBlocks[oldNextBlockIdx - 1];
 		}
 		else
 		{