Browse Source

Removing exceptions

Panagiotis Christopoulos Charitos 11 years ago
parent
commit
cb178f667c

+ 1 - 4
include/anki/resource/Animation.h

@@ -52,12 +52,9 @@ public:
 	ResourceDArray<Key<F32>> m_scales;
 	ResourceDArray<Key<F32>> m_cameraFovs;
 
-	AnimationChannel(ResourceAllocator<U8>& alloc)
-	:	m_name(alloc)
-	{}
-
 	void destroy(ResourceAllocator<U8>& alloc)
 	{
+		m_name.destroy(alloc);
 		m_positions.destroy(alloc);
 		m_rotations.destroy(alloc);
 		m_scales.destroy(alloc);

+ 7 - 0
include/anki/resource/Common.h

@@ -73,6 +73,13 @@ public:
 		return err; \
 	}
 
+#define ANKI_CHECK_C(x_) \
+	err = x_; \
+	if(ANKI_UNLIKELY(err)) \
+	{ \
+		goto cleanup; \
+	}
+
 /// @}
 
 } // end namespace anki

+ 27 - 12
include/anki/resource/MaterialProgramCreator.h

@@ -31,13 +31,6 @@ public:
 	class Input
 	{
 	public:
-		Input(TempResourceAllocator<char>& alloc)
-		:	m_name(alloc),
-			m_type(alloc),
-			m_value(alloc),
-			m_line(alloc)
-		{}
-
 		MPString m_name;
 		MPString m_type;
 		MPStringList m_value;
@@ -49,6 +42,27 @@ public:
 		GLbitfield m_shaderDefinedMask = 0; ///< Defined in
 		GLbitfield m_shaderReferencedMask = 0; ///< Referenced by
 		Bool8 m_inBlock = true;
+
+		void destroy(TempResourceAllocator<U8> alloc)
+		{
+			m_name.destroy(alloc);
+			m_type.destroy(alloc);
+			m_value.destroy(alloc);
+			m_line.destroy(alloc);
+		}
+
+		void move(Input& b)
+		{
+			m_name = std::move(b.m_name);
+			m_type = std::move(b.m_type);
+			m_value = std::move(b.m_value);
+			m_constant = b.m_constant;
+			m_arraySize = b.m_arraySize;
+			m_line = std::move(m_line);
+			m_shaderDefinedMask = b.m_shaderDefinedMask;
+			m_shaderReferencedMask = b.m_shaderReferencedMask;
+			m_inBlock = b.m_inBlock;
+		}
 	};
 
 	explicit MaterialProgramCreator(TempResourceAllocator<U8>& alloc);
@@ -60,14 +74,14 @@ public:
 	ANKI_USE_RESULT Error parseProgramsTag(const XmlElement& el);
 
 	/// Get the shader program source code
-	MPString getProgramSource(ShaderType shaderType_) const
+	const MPString& getProgramSource(ShaderType shaderType_) const
 	{
 		U shaderType = enumToType(shaderType_);
-		ANKI_ASSERT(m_source[shaderType].size() > 0);
-		return m_source[shaderType].join(CString("\n"));
+		ANKI_ASSERT(!m_sourceBaked[shaderType].isEmpty());
+		return m_sourceBaked[shaderType];
 	}
 
-	const TempResourceVector<Input>& getInputVariables() const
+	const List<Input, TempResourceAllocator<U8>>& getInputVariables() const
 	{
 		return m_inputs;
 	}
@@ -80,7 +94,8 @@ public:
 private:
 	TempResourceAllocator<char> m_alloc; 
 	Array<MPStringList, 5> m_source; ///< Shader program final source
-	TempResourceVector<Input> m_inputs;
+	Array<MPString, 5> m_sourceBaked; ///< Final source baked
+	List<Input, TempResourceAllocator<U8>> m_inputs;
 	MPStringList m_uniformBlock;
 	GLbitfield m_uniformBlockReferencedMask = 0;
 	Bool8 m_instanced = false;

+ 8 - 4
include/anki/resource/ProgramPrePreprocessor.h

@@ -34,13 +34,15 @@ public:
 	/// It loads a file and parses it
 	/// @param[in] filename The file to load
 	ProgramPrePreprocessor(ResourceManager* manager)
-	:	m_shaderSource(manager->_getTempAllocator()),
-		m_sourceLines(manager->_getTempAllocator()),
+	:	m_alloc(manager->_getTempAllocator()),
 		m_manager(manager)
 	{}
 
 	~ProgramPrePreprocessor()
-	{}
+	{
+		m_shaderSource.destroy(m_alloc);
+		m_sourceLines.destroy(m_alloc);
+	}
 
 	/// Parse a PrePreprocessor formated GLSL file. Use the accessors to get 
 	/// the output
@@ -61,6 +63,8 @@ public:
 	}
 
 protected:
+	TempResourceAllocator<U8> m_alloc;
+
 	/// The final program source
 	PPPString m_shaderSource;
 
@@ -80,7 +84,7 @@ protected:
 	/// @param depth The #line in GLSL does not support filename so an
 	///              depth it being used. It also tracks the includance depth
 	ANKI_USE_RESULT Error parseFileForPragmas(
-		const PPPString& filename, U32 depth);
+		CString filename, U32 depth);
 
 	/// Parse the type
 	ANKI_USE_RESULT Error parseType(const PPPString& line, Bool& found);

+ 5 - 4
include/anki/resource/ProgramResource.h

@@ -45,13 +45,14 @@ public:
 	///        of the shader prog
 	/// @param filenamePrefix Add that at the base filename for additional 
 	///        ways to identify the file in the cache
-	/// @return The file pathname of the new shader prog. Its
-	///         $HOME/.anki/cache/ + filenamePrefix + hash + .glsl
-	static String createToCache(
+	/// @param out The file pathname of the new shader prog. Its
+	///            $HOME/.anki/cache/ + filenamePrefix + hash + .glsl
+	static ANKI_USE_RESULT Error createToCache(
 		const CString& filename,
 		const CString& preAppendedSrcCode,
 		const CString& filenamePrefix,
-		ResourceManager& manager);
+		ResourceManager& manager,
+		TempResourceString& out);
 
 private:
 	GlProgramHandle m_prog;

+ 27 - 21
include/anki/resource/ResourceManager.h

@@ -8,7 +8,7 @@
 
 #include "anki/resource/Common.h"
 #include "anki/resource/ResourcePointer.h"
-#include "anki/util/Vector.h"
+#include "anki/util/List.h"
 #include "anki/util/Functions.h"
 #include "anki/util/String.h"
 
@@ -48,15 +48,17 @@ class TypeResourceManager
 public:
 	using ResourcePointerType = ResourcePointer<Type, TResourceManager>;
 	using Container = 
-		Vector<ResourcePointerType, HeapAllocator<ResourcePointerType>>;
+		List<ResourcePointerType, ResourceAllocator<ResourcePointerType>>;
 
 	TypeResourceManager()
 	{}
 
 	~TypeResourceManager()
 	{
-		ANKI_ASSERT(m_ptrs.size() == 0 
+		ANKI_ASSERT(m_ptrs.isEmpty() 
 			&& "Forgot to delete some resource ptrs");
+
+		m_ptrs.destroy(m_alloc);
 	}
 
 	/// @privatesection
@@ -76,12 +78,12 @@ public:
 		}
 	}
 
-	void _registerResource(ResourcePointerType& ptr)
+	ANKI_USE_RESULT Error _registerResource(ResourcePointerType& ptr)
 	{
 		ANKI_ASSERT(ptr.getReferenceCount() == 1);
-		ANKI_ASSERT(find(ptr.getResourceName()) == m_ptrs.end());
+		ANKI_ASSERT(find(ptr.getResourceName()) == m_ptrs.getEnd());
 	
-		m_ptrs.push_back(ptr);
+		return m_ptrs.pushBack(m_alloc, ptr);
 	}
 
 	void _unregisterResource(ResourcePointerType& ptr)
@@ -89,26 +91,25 @@ public:
 		auto it = find(ptr.getResourceName());
 		ANKI_ASSERT(it != m_ptrs.end());
 	
-		m_ptrs.erase(it);
+		m_ptrs.erase(m_alloc, it);
 	}
 	/// @}
 
 protected:
-	void init(HeapAllocator<U8>& alloc)
+	void init(ResourceAllocator<U8> alloc)
 	{
-		HeapAllocator<ResourcePointerType> alloc2 = alloc;
-		Container ptrs(alloc2);
-		m_ptrs = std::move(ptrs);
+		m_alloc = alloc;
 	}
 
 private:
+	ResourceAllocator<U8> m_alloc;
 	Container m_ptrs;
 
-	typename Container::iterator find(const CString& filename)
+	typename Container::Iterator find(const CString& filename)
 	{
-		typename Container::iterator it;
+		typename Container::Iterator it;
 		
-		for(it = m_ptrs.begin(); it != m_ptrs.end(); ++it)
+		for(it = m_ptrs.getBegin(); it != m_ptrs.getEnd(); ++it)
 		{
 			if(it->getResourceName() == filename)
 			{
@@ -145,13 +146,15 @@ public:
 		CString m_cacheDir;
 		AllocAlignedCallback m_allocCallback = 0;
 		void* m_allocCallbackData = nullptr;
-		U32 m_tempAllocatorMemorySize = 2 * 1024 * 1024;
+		U32 m_tempAllocatorMemorySize = 1 * 1024 * 1024;
 	};
 
-	ResourceManager(Initializer& init);
+	ResourceManager();
 
 	~ResourceManager();
 
+	ANKI_USE_RESULT Error create(Initializer& init);
+
 	const ResourceString& getDataDirectory() const
 	{
 		return m_dataDir;
@@ -167,7 +170,9 @@ public:
 		return m_textureAnisotropy;
 	}
 
-	TempResourceString fixResourceFilename(const CString& filename) const;
+	ANKI_USE_RESULT Error fixResourceFilename(
+		const CString& filename,
+		TempResourceString& out) const;
 
 	/// @privatesection
 	/// @{
@@ -192,9 +197,9 @@ public:
 	}
 
 	/// Set it with information from the renderer
-	void _setShadersPrependedSource(const CString& cstr)
+	ANKI_USE_RESULT Error _setShadersPrependedSource(const CString& cstr)
 	{
-		m_shadersPrependedSource = cstr;
+		return m_shadersPrependedSource.create(m_alloc, cstr);
 	}
 
 	const ResourceString& _getShadersPrependedSource() const
@@ -211,9 +216,10 @@ public:
 	}
 
 	template<typename T>
-	void _registerResource(ResourcePointer<T, ResourceManager>& ptr)
+	ANKI_USE_RESULT Error _registerResource(
+		ResourcePointer<T, ResourceManager>& ptr)
 	{
-		TypeResourceManager<T, ResourceManager>::_registerResource(ptr);
+		return TypeResourceManager<T, ResourceManager>::_registerResource(ptr);
 	}
 
 	template<typename T>

+ 2 - 5
include/anki/resource/ResourcePointer.h

@@ -111,11 +111,8 @@ public:
 		const CString& filename, TResourceManager* resources);
 
 	template<typename... TArgs>
-	void loadToCache(TResourceManager* resources, TArgs&&... args)
-	{
-		auto fname = Type::createToCache(args..., *resources);
-		load(fname.toCString(), resources);
-	}
+	ANKI_USE_RESULT Error loadToCache(
+		TResourceManager* resources, TArgs&&... args);
 
 	Bool isLoaded() const
 	{

+ 40 - 4
include/anki/resource/ResourcePointer.inl.h

@@ -33,7 +33,7 @@ Error ResourcePointer<T, TResourceManager>::load(
 
 		if(!m_cb)
 		{
-			ANKI_LOGE("Out of memory when loading resource");
+			ANKI_LOGE("OOM when loading resource");
 			return ErrorCode::OUT_OF_MEMORY;
 		}
 
@@ -46,8 +46,18 @@ Error ResourcePointer<T, TResourceManager>::load(
 		// WARNING: Keep the brackets to force deallocation of newFname before
 		// reseting the mempool
 		{
-			TempResourceString newFname(
-				resources->fixResourceFilename(filename));
+			TempResourceString newFname;
+			TempResourceString::ScopeDestroyer newFnamed(
+				&newFname, resources->_getTempAllocator());
+
+			err = resources->fixResourceFilename(filename, newFname);
+			if(err)
+			{
+				ANKI_LOGE("OOM when loading resource: %s", &newFname[0]);
+				alloc.deleteInstance(m_cb);
+				m_cb = nullptr;
+				return err;
+			}
 
 			ResourceInitializer init(
 				alloc,
@@ -81,7 +91,14 @@ Error ResourcePointer<T, TResourceManager>::load(
 		}
 
 		// Register resource
-		resources->_registerResource(*this);
+		err = resources->_registerResource(*this);
+		if(err)
+		{
+			ANKI_LOGE("OOM when registering resource");
+			alloc.deleteInstance(m_cb);
+			m_cb = nullptr;
+			return err;
+		}
 	}
 	else
 	{
@@ -127,5 +144,24 @@ void ResourcePointer<T, TResourceManager>::copy(const ResourcePointer& b)
 	}
 }
 
+//==============================================================================
+template<typename T, typename TResourceManager>
+template<typename... TArgs>
+Error ResourcePointer<T, TResourceManager>::loadToCache(
+	TResourceManager* resources, TArgs&&... args)
+{
+	TempResourceString fname;
+
+	Error err = T::createToCache(args..., *resources, fname);
+
+	if(!err)
+	{
+		err = load(fname.toCString(), resources);
+	}
+
+	fname.destroy(resources->_getTempAllocator());
+	return err;
+}
+
 } // end namespace anki
 

+ 11 - 3
include/anki/resource/Skeleton.h

@@ -17,9 +17,9 @@ struct Bone
 	friend class Skeleton; ///< For loading
 
 public:
-	Bone(ResourceAllocator<U8>& alloc)
-	:	m_name(alloc)
-	{}
+	Bone() = default;
+
+	~Bone() = default;
 
 	const ResourceString& getName() const
 	{
@@ -31,6 +31,14 @@ public:
 		return m_transform;
 	}
 
+	/// @privatesection
+	/// @{
+	void _destroy(ResourceAllocator<U8> alloc)
+	{
+		m_name.destroy(alloc);
+	}
+	/// @}
+
 private:
 	ResourceString m_name; ///< The name of the bone
 	static const U32 MAX_CHILDS_PER_BONE = 4; ///< Please dont change this

+ 12 - 6
include/anki/scene/SceneComponent.h

@@ -50,18 +50,24 @@ public:
 	{}
 
 	/// Do some updating
-	/// @return true if an update happened
-	virtual Bool update(SceneNode& node, F32 prevTime, F32 crntTime)
+	/// @param[out] updated true if an update happened
+	virtual ANKI_USE_RESULT Error update(
+		SceneNode& node, F32 prevTime, F32 crntTime, Bool& updated)
 	{
-		return false;
+		updated = false;
+		return ErrorCode::NONE;
 	}
 
 	/// Called if SceneComponent::update returned true.
-	virtual void onUpdate(SceneNode& node, F32 prevTime, F32 crntTime)
-	{}
+	virtual ANKI_USE_RESULT Error onUpdate(
+		SceneNode& node, F32 prevTime, F32 crntTime)
+	{
+		return ErrorCode::NONE;
+	}
 
 	/// Called only by the SceneGraph
-	Bool updateReal(SceneNode& node, F32 prevTime, F32 crntTime);
+	ANKI_USE_RESULT Error updateReal(
+		SceneNode& node, F32 prevTime, F32 crntTime, Bool& updated);
 
 	template<typename TComponent>
 	TComponent& downCast()

+ 8 - 4
include/anki/util/File.h

@@ -97,15 +97,19 @@ public:
 	/// Read all the contents of a text file
 	/// If the file is not rewined it will probably fail
 	template<typename TAlloc>
-	ANKI_USE_RESULT Error readAllText(StringBase<TAlloc>& out)
+	ANKI_USE_RESULT Error readAllText(TAlloc alloc, StringBase<TAlloc>& out)
 	{
-		PtrSize size = getSize();
 		Error err = ErrorCode::NONE;
+		PtrSize size = getSize();
 
 		if(size != 0)
 		{
-			out.resize(size, '\0');
-			err = read(&out[0], size);
+			err = out.create(alloc, '?', size);
+
+			if(!err)
+			{
+				err = read(&out[0], size);
+			}
 		}
 		else
 		{

+ 48 - 8
include/anki/util/List.h

@@ -37,12 +37,12 @@ public:
 
 /// List bidirectional iterator.
 template<typename TNodePointer, typename TValuePointer, 
-	typename TValueReference, typename TList>
+	typename TValueReference, typename TListPointer>
 class ListIterator
 {
 public:
 	TNodePointer m_node = nullptr;
-	TList* m_list = nullptr; ///< Used to go back from the end
+	TListPointer m_list = nullptr; ///< Used to go back from the end
 
 	ListIterator() = default;
 
@@ -60,7 +60,7 @@ public:
 		m_list(b.m_list)
 	{}
 
-	ListIterator(TNodePointer node, TList* list)
+	ListIterator(TNodePointer node, TListPointer list)
 	:	m_node(node),
 		m_list(list)
 	{
@@ -144,7 +144,9 @@ public:
 
 	Bool operator==(const ListIterator& b) const
 	{
-		return m_node == b.m_node && m_list == b.m_list;
+		ANKI_ASSERT(m_list == b.m_list 
+			&& "Comparing iterators from different lists");
+		return m_node == b.m_node;
 	}
 
 	Bool operator!=(const ListIterator& b) const
@@ -163,7 +165,7 @@ template<typename T, typename TAlloc = HeapAllocator<T>>
 class List: public NonCopyable
 {
 	template<typename TNodePointer, typename TValuePointer, 
-		typename TValueReference, typename TList>
+		typename TValueReference, typename TListPointer>
 	friend class ListIterator;
 
 public:
@@ -174,9 +176,9 @@ public:
 	using ConstReference = const Value&;
 	using Pointer = Value*;
 	using ConstPointer = const Value*;
-	using Iterator = ListIterator<Node*, Pointer, Reference, List>;
+	using Iterator = ListIterator<Node*, Pointer, Reference, List*>;
 	using ConstIterator = 
-		ListIterator<const Node*, ConstPointer, ConstReference, List>;
+		ListIterator<const Node*, ConstPointer, ConstReference, const List*>;
 
 	List() = default;
 
@@ -201,6 +203,9 @@ public:
 		return *this;
 	}
 
+	/// Compare with another list.
+	Bool operator==(const List& b) const;
+
 	/// Destroy the list.
 	void destroy(Allocator alloc);
 
@@ -258,13 +263,40 @@ public:
 		return it;
 	}
 
+	/// Get begin.
+	Iterator begin()
+	{
+		return getBegin();
+	}
+
+	/// Get begin.
+	ConstIterator begin() const
+	{
+		return getBegin();
+	}
+
+	/// Get end.
+	Iterator end()
+	{
+		return getEnd();
+	}
+
+	/// Get end.
+	ConstIterator end() const
+	{
+		return getEnd();
+	}
+
 	/// Return true if list is empty.
 	Bool isEmpty() const
 	{
 		return m_head == nullptr;
 	}
 
-	/// Construct element at the end of the list.
+	/// Copy an element at the end of the list.
+	ANKI_USE_RESULT Error pushBack(Allocator alloc, const Value& x);
+
+	/// Construct an element at the end of the list.
 	template<typename... TArgs>
 	ANKI_USE_RESULT Error emplaceBack(Allocator alloc, TArgs&&... args);
 
@@ -277,6 +309,9 @@ public:
 	ANKI_USE_RESULT Error emplace(
 		Allocator alloc, Iterator pos, TArgs&&... args);
 
+	/// Pop a value from the back of the list.
+	void popBack(Allocator alloc);
+
 	/// Erase an element.
 	void erase(Allocator alloc, Iterator position);
 
@@ -295,6 +330,9 @@ public:
 	template<typename TCompFunc = std::less<Value>>
 	void sort(TCompFunc compFunc = TCompFunc());
 
+	/// Compute the size of elements in the list.
+	PtrSize getSize() const;
+
 private:
 	Node* m_head = nullptr;
 	Node* m_tail = nullptr;
@@ -315,6 +353,8 @@ private:
 	/// Used in sortInternal.
 	template<typename TCompFunc>
 	Node* partition(TCompFunc compFunc, Node* l, Node* r);
+
+	void pushBackNode(Node* node);
 };
 
 /// @}

+ 88 - 14
include/anki/util/List.inl.h

@@ -34,6 +34,68 @@ ListIterator<TNodePointer, TValuePointer, TValueReference, TList>&
 // List                                                                        =
 //==============================================================================
 
+//==============================================================================
+template<typename T, typename TAlloc>
+Bool List<T, TAlloc>::operator==(const List& b) const
+{
+	Bool same = true;
+	ConstIterator ita = getBegin();
+	ConstIterator itb = b.getBegin();
+
+	while(same && ita != getEnd() && itb != b.getEnd())
+	{
+		same = *ita == *itb;
+		++ita;
+		++itb;
+	}
+
+	if(same && (ita != getEnd() || itb != b.getEnd()))
+	{
+		same = false;
+	}
+
+	return same;
+}
+
+//==============================================================================
+template<typename T, typename TAlloc>
+void List<T, TAlloc>::pushBackNode(Node* node)
+{
+	ANKI_ASSERT(node);
+
+	if(m_tail != nullptr)
+	{
+		ANKI_ASSERT(m_head != nullptr);
+		m_tail->m_next = node;
+		node->m_prev = m_tail;
+		m_tail = node;
+	}
+	else
+	{
+		ANKI_ASSERT(m_head == nullptr);
+		m_tail = m_head = node;
+	}
+}
+
+//==============================================================================
+template<typename T, typename TAlloc>
+Error List<T, TAlloc>::pushBack(Allocator alloc, const Value& x)
+{
+	Error err = ErrorCode::NONE;
+	
+	Node* node = alloc.template newInstance<Node>(x);
+	if(node != nullptr)
+	{
+		pushBackNode(node);
+	}
+	else
+	{
+		err = ErrorCode::OUT_OF_MEMORY;
+	}
+
+	return err;
+}
+
 //==============================================================================
 template<typename T, typename TAlloc>
 template<typename... TArgs>
@@ -41,21 +103,10 @@ Error List<T, TAlloc>::emplaceBack(Allocator alloc, TArgs&&... args)
 {
 	Error err = ErrorCode::NONE;
 	
-	Node* el = alloc.template newInstance<Node>(std::forward<TArgs>(args)...);
-	if(el != nullptr)
+	Node* node = alloc.template newInstance<Node>(std::forward<TArgs>(args)...);
+	if(node != nullptr)
 	{
-		if(m_tail != nullptr)
-		{
-			ANKI_ASSERT(m_head != nullptr);
-			m_tail->m_next = el;
-			el->m_prev = m_tail;
-			m_tail = el;
-		}
-		else
-		{
-			ANKI_ASSERT(m_head == nullptr);
-			m_tail = m_head = el;
-		}
+		pushBackNode(node);
 	}
 	else
 	{
@@ -281,6 +332,14 @@ void List<T, TAlloc>::erase(Allocator alloc, Iterator pos)
 	alloc.deleteInstance(node);
 }
 
+//==============================================================================
+template<typename T, typename TAlloc>
+void List<T, TAlloc>::popBack(Allocator alloc)
+{
+	ANKI_ASSERT(m_tail);
+	erase(alloc, Iterator(m_tail, this));
+}
+
 //==============================================================================
 template<typename T, typename TAlloc>
 typename List<T, TAlloc>::Iterator List<T, TAlloc>::find(const Value& a)
@@ -300,5 +359,20 @@ typename List<T, TAlloc>::Iterator List<T, TAlloc>::find(const Value& a)
 	return it;
 }
 
+//==============================================================================
+template<typename T, typename TAlloc>
+PtrSize List<T, TAlloc>::getSize() const
+{
+	PtrSize size = 0;
+	ConstIterator it = getBegin();
+	ConstIterator end = getEnd();
+	for(; it != end; it++)
+	{
+		++size;
+	}
+
+	return size;
+}
+
 } // end namespace anki
 

+ 37 - 0
include/anki/util/ScopeDestroyer.h

@@ -0,0 +1,37 @@
+// Copyright (C) 2014, Panagiotis Christopoulos Charitos.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#ifndef ANKI_UTIL_SCOPE_DESTROYER_H
+#define ANKI_UTIL_SCOPE_DESTROYER_H
+
+namespace anki {
+
+/// @addtogroup util_containers
+/// @{
+
+/// A class that destroys other instances when it get out of scope.
+template<typename TInstance, typename TAlloc>
+class ScopeDestroyer
+{
+public:
+	TInstance* m_inst;
+	TAlloc m_alloc;
+
+	ScopeDestroyer(TInstance* inst, TAlloc alloc)
+	:	m_inst(inst),
+		m_alloc(alloc)
+	{}
+
+	~ScopeDestroyer()
+	{
+		m_inst->destroy(m_alloc);
+	}
+};
+
+/// @}
+
+} // end namespace anki
+
+#endif

+ 11 - 6
include/anki/util/String.h

@@ -3,12 +3,13 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#ifndef ANKI_STRING_H
-#define ANKI_STRING_H
+#ifndef ANKI_UTIL_STRING_H
+#define ANKI_UTIL_STRING_H
 
 #include "anki/util/DArray.h"
 #include "anki/util/Array.h"
 #include "anki/util/NonCopyable.h"
+#include "anki/util/ScopeDestroyer.h"
 #include <cstring>
 #include <cmath> // For HUGE_VAL
 #include <climits> // For LLONG_MAX
@@ -267,6 +268,9 @@ inline CString operator"" _cstr(const char* str, unsigned long length)
 	return CString(str, length);
 }
 
+template<typename TAlloc>
+using StringScopeDestroyer = ScopeDestroyer<StringBase<TAlloc>, TAlloc>;
+
 /// The base class for strings.
 template<typename TAlloc>
 class StringBase: public NonCopyable
@@ -279,6 +283,8 @@ public:
 	using Iterator = Char*;
 	using ConstIterator = const Char*;
 
+	using ScopeDestroyer = StringScopeDestroyer<Allocator>;
+
 	static const PtrSize NPOS = MAX_PTR_SIZE;
 
 	/// Default constructor.
@@ -359,7 +365,7 @@ public:
 	ConstIterator end() const 
 	{
 		checkInit();
-		return &m_data[m_data.size() - 1];
+		return &m_data[m_data.getSize() - 1];
 	}
 
 	/// Return true if strings are equal
@@ -484,7 +490,7 @@ public:
 		Error err = ErrorCode::NONE;
 		if(!cstr.isEmpty())
 		{
-			err = appendInternal(cstr.get(), cstr.getLength() + 1);
+			err = appendInternal(alloc, &cstr[0], cstr.getLength() + 1);
 		}
 
 		return err;
@@ -525,8 +531,7 @@ public:
 
 	/// Convert a number to a string.
 	template<typename TNumber>
-	static ANKI_USE_RESULT Error toString(
-		Allocator alloc, TNumber number, Self& out);
+	ANKI_USE_RESULT Error toString(Allocator alloc, TNumber number);
 
 	/// Convert to F64.
 	ANKI_USE_RESULT Error toF64(F64& out) const

+ 2 - 2
include/anki/util/String.inl.h

@@ -129,7 +129,7 @@ Error StringBase<TAlloc>::sprintf(Allocator alloc, CString fmt, ...)
 //==============================================================================
 template<typename TAlloc>
 template<typename TNumber>
-Error StringBase<TAlloc>::toString(Allocator alloc, TNumber number, Self& out)
+Error StringBase<TAlloc>::toString(Allocator alloc, TNumber number)
 {
 	Error err = ErrorCode::NONE;
 
@@ -139,7 +139,7 @@ Error StringBase<TAlloc>::toString(Allocator alloc, TNumber number, Self& out)
 	I ret = std::snprintf(
 		&buff[0], buff.size(), detail::toStringFormat<TNumber>(), number);
 
-	if(ret < 0 || ret > static_cast<I>(buff.size()))
+	if(ret < 0 || ret > static_cast<I>(buff.getSize()))
 	{
 		ANKI_LOGE("To small intermediate buffer");
 		err = ErrorCode::FUNCTION_FAILED;

+ 20 - 0
include/anki/util/StringList.h

@@ -12,6 +12,19 @@
 
 namespace anki {
 
+// Forward
+template<typename TAlloc>
+class StringListBase;
+
+/// @addtogroup util_private
+/// @{
+
+template<typename TAlloc>
+using StringListBaseScopeDestroyer = 
+	ScopeDestroyer<StringListBase<TAlloc>, TAlloc>;
+
+/// @}
+
 /// @addtogroup util_containers
 /// @{
 
@@ -26,6 +39,8 @@ public:
 	using String = StringBase<Allocator>; ///< String type
 	using Base = List<String, Allocator>; ///< Base
 
+	using ScopeDestroyer = StringListBaseScopeDestroyer<Allocator>;
+
 	/// Sort method for sortAll().
 	enum class Sort
 	{
@@ -49,6 +64,11 @@ public:
 	/// Sort the string list
 	void sortAll(const Sort method = Sort::ASCENDING);
 
+	/// Push at the end of the list a formated string
+	template<typename... TArgs>
+	ANKI_USE_RESULT Error pushBackSprintf(
+		Allocator alloc, const TArgs&... args);
+
 	/// Split a string using a separator (@a separator) and return these
 	/// strings in a string list
 	static ANKI_USE_RESULT Error splitString(

+ 23 - 1
include/anki/util/StringList.inl.h

@@ -44,7 +44,7 @@ Error StringListBase<TAlloc>::join(
 	for(; it != Base::getEnd(); it++)
 	{
 		const String& from = *it;
-		std::memcpy(out, &from[0], from.getLength() * sizeof(Char));
+		std::memcpy(to, &from[0], from.getLength() * sizeof(Char));
 		to += from.getLength();
 
 		if(it != Base::end() - 1)
@@ -162,4 +162,26 @@ void StringListBase<TAlloc>::sortAll(const Sort method)
 	}
 }
 
+//==============================================================================
+template<typename TAlloc>
+template<typename... TArgs>
+Error StringListBase<TAlloc>::pushBackSprintf(
+	Allocator alloc, const TArgs&... args)
+{
+	String str;
+	Error err = str.sprintf(alloc, args...);
+	
+	if(!err)
+	{
+		err = Base::emplaceBack(alloc);
+	}
+
+	if(!err)
+	{
+		Base::getBack() = std::move(str);
+	}
+
+	return err;
+}
+
 } // end namespace anki

+ 2 - 2
src/resource/Animation.cpp

@@ -75,7 +75,7 @@ Error Animation::load(const CString& filename, ResourceInitializer& init)
 		ANKI_LOGE("Didn't found any channels");
 		return ErrorCode::USER_DATA;
 	}
-	ANKI_CHECK(m_channels.create(m_alloc, channelCount, m_alloc));
+	ANKI_CHECK(m_channels.create(m_alloc, channelCount));
 
 	// For all channels
 	channelCount = 0;
@@ -87,7 +87,7 @@ Error Animation::load(const CString& filename, ResourceInitializer& init)
 		ANKI_CHECK(chEl.getChildElement("name", el));
 		CString strtmp;
 		ANKI_CHECK(el.getText(strtmp));
-		ch.m_name = strtmp;
+		ANKI_CHECK(ch.m_name.create(m_alloc, strtmp));
 
 		XmlElement keysEl, keyEl;
 

+ 7 - 1
src/resource/Image.cpp

@@ -576,7 +576,13 @@ Error Image::load(const CString& filename, U32 maxTextureSize)
 	Error err = ErrorCode::NONE;
 
 	// get the extension
-	String ext = getFileExtension(filename, m_alloc);
+	String ext;
+	String::ScopeDestroyer extd(&ext, m_alloc);
+	err = getFileExtension(filename, m_alloc, ext);
+	if(err)
+	{
+		return err;
+	}
 	
 	if(ext.isEmpty())
 	{

+ 40 - 23
src/resource/Material.cpp

@@ -32,13 +32,13 @@ static MaterialVariable* newMaterialVariable(
 {
 	MaterialVariable* out = nullptr;
 
-	if(in.m_value.size() > 0)
+	if(in.m_value.getSize() > 0)
 	{
 		// Has values
 
 		U floatsNeeded = glvar.getArraySize() * (sizeof(T) / sizeof(F32));
 
-		if(in.m_value.size() != floatsNeeded)
+		if(in.m_value.getSize() != floatsNeeded)
 		{
 			ANKI_LOGE("Incorrect number of values. Variable %s",
 				&glvar.getName()[0]);
@@ -48,16 +48,18 @@ static MaterialVariable* newMaterialVariable(
 
 		TempResourceVector<F32> floatvec(talloc);
 		floatvec.resize(floatsNeeded);
+		auto it = in.m_value.getBegin();
 		for(U i = 0; i < floatsNeeded; ++i)
 		{
 			F64 d;
-			Error err = in.m_value[i].toF64(d);
+			Error err = it->toF64(d);
 			if(err)
 			{
 				return nullptr;
 			}
 
 			floatvec[i] = d;
+			++it;
 		}
 
 		out = alloc.newInstance<MaterialVariableTemplate<T>>(
@@ -104,8 +106,8 @@ static GLenum blendToEnum(const CString& str)
 	TXT_AND_ENUM(GL_SRC_ALPHA_SATURATE)
 	TXT_AND_ENUM(GL_SRC_COLOR)
 	TXT_AND_ENUM(GL_ONE_MINUS_SRC_COLOR);
-	ANKI_ASSERT(0);
-	throw ANKI_EXCEPTION("Incorrect blend enum");
+	ANKI_LOGE("Incorrect blend enum");
+	return 0;
 
 #undef TXT_AND_ENUM
 }
@@ -395,11 +397,19 @@ Error Material::parseMaterialTag(const XmlElement& materialEl,
 		ANKI_CHECK(blendFunctionsEl.getChildElement("sFactor", el));
 		ANKI_CHECK(el.getText(cstr));
 		m_blendingSfactor = blendToEnum(cstr);
+		if(m_blendingSfactor == 0)
+		{
+			return ErrorCode::USER_DATA;
+		}
 
 		// dFactor
 		ANKI_CHECK(blendFunctionsEl.getChildElement("dFactor", el));
 		ANKI_CHECK(el.getText(cstr));
 		m_blendingDfactor = blendToEnum(cstr);
+		if(m_blendingDfactor == 0)
+		{
+			return ErrorCode::USER_DATA;
+		}
 	}
 	else
 	{
@@ -492,18 +502,24 @@ Error Material::parseMaterialTag(const XmlElement& materialEl,
 						continue;
 					}
 
-					TempResourceString src(rinit.m_tempAlloc);
+					TempResourceString src;
+					TempResourceString::ScopeDestroyer srcd(
+						&src, rinit.m_tempAlloc);
 
-					src.sprintf(
+					ANKI_CHECK(src.sprintf(
+						rinit.m_tempAlloc,
 						"%s\n"
 						"#define LOD %u\n"
 						"#define PASS %u\n"
 						"#define TESSELLATION %u\n"
 						"%s\n",
 						&rinit.m_resources._getShadersPrependedSource()[0],
-						level, pid, tess, &loader.getProgramSource(shader)[0]);
+						level, pid, tess, &loader.getProgramSource(shader)[0]));
+
+					TempResourceString filename;
+					TempResourceString::ScopeDestroyer filenamed(
+						&filename, rinit.m_tempAlloc);
 
-					TempResourceString filename(rinit.m_tempAlloc);
 					ANKI_CHECK(createProgramSourceToCache(src, filename));
 
 					RenderingKey key((Pass)pid, level, tess);
@@ -541,27 +557,27 @@ Error Material::createProgramSourceToCache(
 {
 	Error err = ErrorCode::NONE;
 
+	auto alloc = m_resources->_getTempAllocator();
+
 	// Create the hash
 	U64 h = computeHash(&source[0], source.getLength());
-	TempResourceString prefix = 
-		TempResourceString::toString(h, source.getAllocator());
+	TempResourceString prefix;
+	TempResourceString::ScopeDestroyer prefixd(&prefix, alloc);
+
+	ANKI_CHECK(prefix.toString(alloc, h));
 
 	// Create path
-	out.sprintf("%s/mtl_%s.glsl", 
+	ANKI_CHECK(out.sprintf(alloc, "%s/mtl_%s.glsl", 
 		&m_resources->_getCacheDirectory()[0],
-		&prefix[0]);
+		&prefix[0]));
 
 	// If file not exists write it
 	if(!fileExists(out.toCString()))
 	{
 		// If not create it
 		File f;
-		err = f.open(out.toCString(), File::OpenFlag::WRITE);
-
-		if(!err)
-		{
-			err = f.writeText("%s\n", &source[0]);
-		}
+		ANKI_CHECK(f.open(out.toCString(), File::OpenFlag::WRITE));
+		ANKI_CHECK(f.writeText("%s\n", &source[0]));
 	}
 
 	return err;
@@ -573,7 +589,7 @@ Error Material::populateVariables(const MaterialProgramCreator& loader)
 	Error err = ErrorCode::NONE;
 
 	U varCount = 0;
-	for(auto in : loader.getInputVariables())
+	for(const auto& in : loader.getInputVariables())
 	{
 		if(!in.m_constant)
 		{
@@ -584,7 +600,7 @@ Error Material::populateVariables(const MaterialProgramCreator& loader)
 	ANKI_CHECK(m_vars.create(m_resources->_getAllocator(), varCount));
 
 	varCount = 0;
-	for(auto in : loader.getInputVariables())
+	for(const auto& in : loader.getInputVariables())
 	{
 		if(in.m_constant)
 		{
@@ -622,9 +638,10 @@ Error Material::populateVariables(const MaterialProgramCreator& loader)
 			{
 				TextureResourcePointer tp;
 				
-				if(in.m_value.size() > 0)
+				if(in.m_value.getSize() > 0)
 				{
-					ANKI_CHECK(tp.load(in.m_value[0].toCString(), m_resources));
+					ANKI_CHECK(tp.load(
+						in.m_value.getBegin()->toCString(), m_resources));
 				}
 
 				auto alloc = m_resources->_getAllocator();

+ 145 - 117
src/resource/MaterialProgramCreator.cpp

@@ -10,7 +10,6 @@
 #include "anki/util/Logger.h"
 
 #include <algorithm>
-#include <sstream>
 
 namespace anki {
 
@@ -22,17 +21,6 @@ namespace anki {
 /// Define string literal
 #define ANKI_STRL(cstr_) MPString(cstr_, m_alloc)
 
-//==============================================================================
-class InputSortFunctor
-{
-public:
-	Bool operator()(const MaterialProgramCreator::Input& a, 
-		const MaterialProgramCreator::Input& b)
-	{
-		return a.m_name < b.m_name;
-	}
-};
-
 //==============================================================================
 /// Given a string return info about the shader
 static ANKI_USE_RESULT Error getShaderInfo(
@@ -87,16 +75,22 @@ static ANKI_USE_RESULT Error getShaderInfo(
 //==============================================================================
 
 //==============================================================================
-MaterialProgramCreator::MaterialProgramCreator(
-	TempResourceAllocator<U8>& alloc)
-:	m_alloc(alloc),
-	m_inputs(alloc),
-	m_uniformBlock(m_alloc)
+MaterialProgramCreator::MaterialProgramCreator(TempResourceAllocator<U8>& alloc)
+:	m_alloc(alloc)
 {}
 
 //==============================================================================
 MaterialProgramCreator::~MaterialProgramCreator()
-{}
+{
+	for(auto& it : m_inputs)
+	{
+		it.destroy(m_alloc);
+	}
+
+	m_inputs.destroy(m_alloc);
+
+	m_uniformBlock.destroy(m_alloc);
+}
 
 //==============================================================================
 Error MaterialProgramCreator::parseProgramsTag(const XmlElement& el)
@@ -116,7 +110,10 @@ Error MaterialProgramCreator::parseProgramsTag(const XmlElement& el)
 	} while(programEl);
 
 	// Sort them by name to decrease the change of creating unique shaders
-	std::sort(m_inputs.begin(), m_inputs.end(), InputSortFunctor());
+	m_inputs.sort([](const Input& a, const Input& b)
+	{
+		return a.m_name < b.m_name;
+	});
 
 	//
 	// Then parse the includes, operations and other parts of the program
@@ -134,7 +131,7 @@ Error MaterialProgramCreator::parseProgramsTag(const XmlElement& el)
 	//
 
 	// Check that all input is referenced
-	for(Input& in : m_inputs)
+	for(auto& in : m_inputs)
 	{
 		if(in.m_shaderDefinedMask != in.m_shaderReferencedMask)
 		{
@@ -144,6 +141,17 @@ Error MaterialProgramCreator::parseProgramsTag(const XmlElement& el)
 		}
 	}
 
+	//
+	// Merge strings
+	//
+	for(U i = 0; i < m_sourceBaked.getSize(); i++)
+	{
+		if(!m_source[i].isEmpty())
+		{
+			ANKI_CHECK(m_source[i].join(m_alloc, "\n", m_sourceBaked[i]));
+		}
+	}
+
 	return err;
 }
 
@@ -163,9 +171,9 @@ Error MaterialProgramCreator::parseProgramTag(
 	U shaderidx;
 	ANKI_CHECK(getShaderInfo(type, glshader, glshaderbit, shaderidx));
 
-	m_source[shaderidx] = MPStringList(m_alloc);
 	auto& lines = m_source[shaderidx];
-	lines.push_back(ANKI_STRL("#pragma anki type ") + type);
+	ANKI_CHECK(lines.pushBackSprintf(m_alloc, 
+		"#pragma anki type %s", &type[0]));
 
 	if(glshader == GL_TESS_CONTROL_SHADER 
 		|| glshader == GL_TESS_EVALUATION_SHADER)
@@ -183,9 +191,8 @@ Error MaterialProgramCreator::parseProgramTag(
 	{
 		CString tmp;
 		ANKI_CHECK(includeEl.getText(tmp));
-		MPString fname(tmp, m_alloc);
-		lines.push_back(
-			ANKI_STRL("#pragma anki include \"") + fname + "\"");
+		ANKI_CHECK(lines.pushBackSprintf(m_alloc, 
+			"#pragma anki include \"%s\"", &tmp[0]));
 
 		ANKI_CHECK(includeEl.getNextSiblingElement("include", includeEl));
 	} while(includeEl);
@@ -193,17 +200,18 @@ Error MaterialProgramCreator::parseProgramTag(
 	// Inputs
 
 	// Block
-	if(m_uniformBlock.size() > 0 
+	if(!m_uniformBlock.isEmpty()
 		&& (m_uniformBlockReferencedMask & glshaderbit))
 	{
-		// TODO Make block SSB when driver bug is fixed
-		lines.push_back(ANKI_STRL(
+		ANKI_CHECK(lines.pushBackSprintf(m_alloc, 
 			"\nlayout(binding = 0, std140) uniform bDefaultBlock\n{"));
 
-		lines.insert(
-			lines.end(), m_uniformBlock.begin(), m_uniformBlock.end());
+		for(auto& str : m_uniformBlock)
+		{
+			ANKI_CHECK(lines.pushBackSprintf(m_alloc, &str[0]));
+		}
 
-		lines.push_back(ANKI_STRL("};"));
+		ANKI_CHECK(lines.pushBackSprintf(m_alloc, "};"));
 	}
 
 	// Other variables
@@ -211,12 +219,12 @@ Error MaterialProgramCreator::parseProgramTag(
 	{
 		if(!in.m_inBlock && (in.m_shaderDefinedMask & glshaderbit))
 		{
-			lines.push_back(in.m_line);
+			ANKI_ASSERT(lines.pushBackSprintf(m_alloc, &in.m_line[0]));
 		}
 	}
 
 	// <operations></operations>
-	lines.push_back(ANKI_STRL("\nvoid main()\n{"));
+	ANKI_ASSERT(lines.pushBackSprintf(m_alloc, "\nvoid main()\n{"));
 
 	XmlElement opsEl;
 	ANKI_CHECK(programEl.getChildElement("operations", opsEl));
@@ -224,15 +232,16 @@ Error MaterialProgramCreator::parseProgramTag(
 	ANKI_CHECK(opsEl.getChildElement("operation", opEl));
 	do
 	{
-		MPString out(m_alloc);
+		MPString out;
 		ANKI_CHECK(parseOperationTag(opEl, glshader, glshaderbit, out));
-		lines.push_back(out);
+		ANKI_ASSERT(lines.pushBackSprintf(m_alloc, &out[0]));
+		out.destroy(m_alloc);
 
 		// Advance
 		ANKI_CHECK(opEl.getNextSiblingElement("operation", opEl));
 	} while(opEl);
 
-	lines.push_back(ANKI_STRL("}\n"));
+	ANKI_CHECK(lines.pushBackSprintf(m_alloc, "}\n"));
 	return err;
 }
 
@@ -261,17 +270,17 @@ Error MaterialProgramCreator::parseInputsTag(const XmlElement& programEl)
 	ANKI_CHECK(inputsEl.getChildElement("input", inputEl));
 	do
 	{
-		Input inpvar(m_alloc);
+		Input inpvar;
 
 		// <name>
 		ANKI_CHECK(inputEl.getChildElement("name", el));
 		ANKI_CHECK(el.getText(cstr));
-		inpvar.m_name = cstr;
+		ANKI_CHECK(inpvar.m_name.create(m_alloc, cstr));
 
 		// <type>
 		ANKI_CHECK(inputEl.getChildElement("type", el));
 		ANKI_CHECK(el.getText(cstr));
-		inpvar.m_type = cstr;
+		ANKI_CHECK(inpvar.m_type.create(m_alloc, cstr));
 
 		// <value>
 		XmlElement valueEl;
@@ -279,7 +288,8 @@ Error MaterialProgramCreator::parseInputsTag(const XmlElement& programEl)
 		ANKI_CHECK(valueEl.getText(cstr));
 		if(cstr)
 		{
-			inpvar.m_value = MPStringList::splitString(cstr, ' ', m_alloc);
+			ANKI_CHECK(
+				MPStringList::splitString(m_alloc, cstr, ' ', inpvar.m_value));
 		}
 
 		// <const>
@@ -338,14 +348,17 @@ Error MaterialProgramCreator::parseInputsTag(const XmlElement& programEl)
 
 		// Now you have the info to check if duplicate
 		Input* duplicateInp = nullptr;
-		for(Input& in : m_inputs)
+		err = m_inputs.iterateForward(
+			[&duplicateInp, &inpvar](Input& in) -> Error
 		{
 			if(in.m_name == inpvar.m_name)
 			{
 				duplicateInp = &in;
-				break;
+				return ErrorCode::NONE;
 			}
-		}
+
+			return ErrorCode::NONE;
+		});
 
 		if(duplicateInp != nullptr)
 		{
@@ -377,47 +390,61 @@ Error MaterialProgramCreator::parseInputsTag(const XmlElement& programEl)
 		{
 			// Handle NON-consts
 
-			inpvar.m_line = inpvar.m_type + " " + inpvar.m_name;
-				
+			ANKI_CHECK(inpvar.m_line.sprintf(
+				m_alloc, "%s %s", &inpvar.m_type[0], &inpvar.m_name[0]));
+			
+			U arrSize = 0;
 			if(inpvar.m_arraySize > 1)
 			{
-				MPString tmp(MPString::toString(inpvar.m_arraySize, m_alloc));
-				inpvar.m_line += "[" + tmp + "U]";
+				arrSize = inpvar.m_arraySize;
 			}
 
 			if(inpvar.m_instanced)
 			{
-				MPString tmp(
-					MPString::toString(ANKI_GL_MAX_INSTANCES, m_alloc));
-				inpvar.m_line += "[" +  tmp + "U]";
+				inpvar.m_arraySize = ANKI_GL_MAX_INSTANCES;
 			}
 
-			inpvar.m_line += ";";
+			if(arrSize)
+			{
+				MPString tmp;
+				ANKI_CHECK(tmp.sprintf(m_alloc, "[%uU]", arrSize));
+				ANKI_CHECK(inpvar.m_line.append(m_alloc, tmp));
+				tmp.destroy(m_alloc);
+			}
+
+			ANKI_CHECK(inpvar.m_line.append(m_alloc, ";"));
 
 			// Can put it block
 			if(inpvar.m_type == "sampler2D" || inpvar.m_type == "samplerCube")
 			{
-				MPString tmp(
-					MPString::toString(m_texBinding++, m_alloc));
+				MPString tmp;
+
+				ANKI_CHECK(tmp.sprintf(
+					m_alloc, "layout(binding = %u) uniform %s",
+					m_texBinding++, &inpvar.m_line[0]));
+
+				inpvar.m_line.destroy(m_alloc);
+				inpvar.m_line = std::move(tmp);
 
-				inpvar.m_line = ANKI_STRL("layout(binding = ") 
-					+ tmp + ") uniform " + inpvar.m_line;
-		
 				inpvar.m_inBlock = false;
 			}
 			else
 			{
-				inpvar.m_inBlock = true;
+				MPString tmp;
+
+				ANKI_CHECK(tmp.create(m_alloc, inpvar.m_line));
+				ANKI_CHECK(m_uniformBlock.emplaceBack(m_alloc));
+				m_uniformBlock.getBack() = std::move(tmp);
 
-				m_uniformBlock.push_back(inpvar.m_line);
 				m_uniformBlockReferencedMask |= glshaderbit;
+				inpvar.m_inBlock = true;
 			}
 		}
 		else
 		{
 			// Handle consts
 
-			if(inpvar.m_value.size() == 0)
+			if(!inpvar.m_value.isEmpty())
 			{
 				ANKI_LOGE("Empty value and const is illogical");
 				return ErrorCode::USER_DATA;
@@ -431,14 +458,21 @@ Error MaterialProgramCreator::parseInputsTag(const XmlElement& programEl)
 
 			inpvar.m_inBlock = false;
 
-			inpvar.m_line = ANKI_STRL("const ") 
-				+ inpvar.m_type + " " + inpvar.m_name 
-				+ " = " + inpvar.m_type + "(" + inpvar.m_value.join(", ") 
-				+  ");";
+			MPString initList;
+			ANKI_CHECK(inpvar.m_value.join(m_alloc, ", ", initList));
+
+			err = inpvar.m_line.sprintf(m_alloc, "const %s %s = %s(%s);",
+				&inpvar.m_type[0], &inpvar.m_name[0], &inpvar.m_type[0], 
+				&initList[0]);
+			initList.destroy(m_alloc);
+
+			ANKI_CHECK(err);
 		}
 
 		inpvar.m_shaderDefinedMask = glshaderbit;
-		m_inputs.push_back(inpvar);
+
+		ANKI_CHECK(m_inputs.emplaceBack(m_alloc));
+		m_inputs.getBack().move(inpvar);
 
 advance:
 		// Advance
@@ -450,41 +484,39 @@ advance:
 
 //==============================================================================
 Error MaterialProgramCreator::parseOperationTag(
-	const XmlElement& operationTag, GLenum glshader, GLbitfield glshaderbit,
+	const XmlElement& operationTag, 
+	GLenum glshader, 
+	GLbitfield glshaderbit,
 	MPString& out)
 {
 	Error err = ErrorCode::NONE;
 	CString cstr;
-	static const char OUT[] = {"out"};
 	XmlElement el;
 
+	static const char OUT[] = "out";
+
+	CString funcName;
+	MPStringList argsList;
+	MPStringList::ScopeDestroyer argsListd(&argsList, m_alloc);
+
 	// <id></id>
-	I64 tmp;
+	I64 id;
 	ANKI_CHECK(operationTag.getChildElement("id", el));
-	ANKI_CHECK(el.getI64(tmp));
-	I id = tmp;
+	ANKI_CHECK(el.getI64(id));
 	
 	// <returnType></returnType>
 	XmlElement retTypeEl;
 	ANKI_CHECK(operationTag.getChildElement("returnType", retTypeEl));
 	ANKI_CHECK(retTypeEl.getText(cstr));
-	MPString retType(cstr, m_alloc);
-	MPString operationOut(m_alloc);
-	if(retType != "void")
-	{
-		MPString tmp(MPString::toString(id, m_alloc));
-		operationOut = ANKI_STRL(OUT) + tmp;
-	}
+	Bool retTypeVoid = cstr == "void";
 	
 	// <function>functionName</function>
 	ANKI_CHECK(operationTag.getChildElement("function", el));
-	ANKI_CHECK(el.getText(cstr));
-	MPString funcName(cstr, m_alloc);
+	ANKI_CHECK(el.getText(funcName));
 	
 	// <arguments></arguments>
 	XmlElement argsEl;
 	ANKI_CHECK(operationTag.getChildElementOptional("arguments", argsEl));
-	MPStringList argsList(m_alloc);
 	
 	if(argsEl)
 	{
@@ -493,8 +525,8 @@ Error MaterialProgramCreator::parseOperationTag(
 		ANKI_CHECK(argsEl.getChildElement("argument", argEl));
 		do
 		{
-			ANKI_CHECK(argEl.getText(cstr));
-			MPString arg(cstr, m_alloc);
+			CString arg;
+			ANKI_CHECK(argEl.getText(arg));
 
 			// Search for all the inputs and mark the appropriate
 			Input* input = nullptr;
@@ -512,7 +544,7 @@ Error MaterialProgramCreator::parseOperationTag(
 
 			// The argument should be an input variable or an outXX
 			if(!(input != nullptr 
-				|| std::strncmp(&arg[0], OUT, sizeof(OUT) - 1) == 0))
+				|| std::memcmp(&arg[0], "out", 3) == 0))
 			{
 				ANKI_LOGE("Incorrect argument: %s", &arg[0]);
 				return ErrorCode::USER_DATA;
@@ -525,40 +557,28 @@ Error MaterialProgramCreator::parseOperationTag(
 
 				if(glshader == GL_VERTEX_SHADER)
 				{
-					argsList.push_back(ANKI_STRL(cstr) + "[gl_InstanceID]");
+					ANKI_CHECK(argsList.pushBackSprintf(m_alloc, 
+						"%s [gl_InstanceID]", &cstr[0]));
 
 					m_instanceIdMask |= glshaderbit;
 				}
-				else if(glshader == GL_TESS_CONTROL_SHADER)
-				{
-					argsList.push_back(ANKI_STRL(cstr) + "[vInstanceId[0]]");
-
-					m_instanceIdMask |= glshaderbit;
-				}
-				else if(glshader == GL_TESS_EVALUATION_SHADER)
-				{
-					argsList.push_back(ANKI_STRL(cstr) 
-						+ "[commonPatch.instanceId]");
-					
-					m_instanceIdMask |= glshaderbit;
-				}
 				else if(glshader == GL_FRAGMENT_SHADER)
 				{
-					argsList.push_back(ANKI_STRL(cstr) + "[vInstanceId]");
+					ANKI_CHECK(argsList.pushBackSprintf(m_alloc, 
+						"%s [vInstanceId]", &cstr[0]));
 					
 					m_instanceIdMask |= glshaderbit;
 				}
 				else
 				{
-					ANKI_LOGE(
-						"Cannot access the instance ID in all shaders");
+					ANKI_LOGE("Cannot access the instance ID in all shaders");
 					return ErrorCode::USER_DATA;
 				}
 			}
 			else
 			{
 				ANKI_CHECK(argEl.getText(cstr));
-				argsList.push_back(MPString(cstr, m_alloc));
+				ANKI_CHECK(argsList.pushBackSprintf(m_alloc, &cstr[0]));
 			}
 
 			// Advance
@@ -567,39 +587,47 @@ Error MaterialProgramCreator::parseOperationTag(
 	}
 
 	// Now write everything
-	MPString lines(m_alloc);
-	lines.reserve(256);
-	lines += "#if defined(" + funcName + "_DEFINED)";
+	MPStringList lines;
+	MPStringList::ScopeDestroyer linesd(&lines, m_alloc);
+
+	ANKI_CHECK(lines.pushBackSprintf(m_alloc,
+		"#if defined(%s_DEFINED)", &funcName[0]));
 
 	// Write the defines for the operationOuts
 	for(const MPString& arg : argsList)
 	{
 		if(arg.find(OUT) == 0)
 		{
-			lines += " && defined(" + arg + "_DEFINED)";
+			ANKI_CHECK(lines.pushBackSprintf(m_alloc,
+				" && defined(%s_DEFINED)", &arg[0]));
 		}
 	}
-	lines += "\n";
+	ANKI_CHECK(lines.pushBackSprintf(m_alloc, "\n"));
 
-	if(retType != "void")
+	if(!retTypeVoid)
 	{
 		ANKI_CHECK(retTypeEl.getText(cstr));
-		lines += "#\tdefine " + operationOut + "_DEFINED\n\t"
-			+ cstr + " " + operationOut + " = ";
+		ANKI_CHECK(lines.pushBackSprintf(m_alloc,
+			"#\tdefine out%u_DEFINED\n"
+			"\tout%u = ", id));
 	}
 	else
 	{
-		lines += "\t";
+		ANKI_CHECK(lines.pushBackSprintf(m_alloc, "\t"));
 	}
 	
-	// write the blah = func(args...)
-	lines += funcName + "(";
-	lines += argsList.join(", ");
-	lines += ");\n";
-	lines += "#endif";
-
-	// Done
-	out = std::move(lines);
+	// write the "func(args...)" of "blah = func(args...)"
+	MPString argsStr;
+	MPString::ScopeDestroyer argsStrd(&argsStr, m_alloc);
+
+	ANKI_CHECK(argsList.join(m_alloc, ", ", argsStr));
+
+	ANKI_CHECK(lines.pushBackSprintf(m_alloc, "%s(%s);\n#endif",
+		&funcName[0], &argsStr[0]));
+
+	// Bake final string
+	ANKI_CHECK(lines.join(m_alloc, " ", out));
+
 	return err;
 }
 

+ 3 - 4
src/resource/Model.cpp

@@ -266,12 +266,11 @@ Error ModelPatch<MeshResourcePointerType>::create(
 	// Load meshes
 	for(U i = 0; i < meshesCount; i++)
 	{
-		m_meshResources[i].load(meshFNames[i], resources);
+		ANKI_CHECK(m_meshResources[i].load(meshFNames[i], resources));
 		m_meshes[i] = m_meshResources[i].get();
 
 		// Sanity check
-		if(i > 0 
-			&& !m_meshResources[i]->isCompatible(*m_meshResources[i - 1]))
+		if(i > 0 && !m_meshResources[i]->isCompatible(*m_meshResources[i - 1]))
 		{
 			ANKI_LOGE("Meshes not compatible");
 			return ErrorCode::USER_DATA;
@@ -279,7 +278,7 @@ Error ModelPatch<MeshResourcePointerType>::create(
 	}
 
 	// Load material
-	m_mtlResource.load(mtlFName, resources);
+	ANKI_CHECK(m_mtlResource.load(mtlFName, resources));
 	m_mtl = m_mtlResource.get();
 
 	// Create VAOs

+ 1 - 1
src/resource/ParticleEmitterResource.cpp

@@ -174,7 +174,7 @@ Error ParticleEmitterResource::load(
 	CString cstr;
 	ANKI_CHECK(rel.getChildElement("material", el));
 	ANKI_CHECK(el.getText(cstr));
-	m_material.load(cstr, &init.m_resources);
+	ANKI_CHECK(m_material.load(cstr, &init.m_resources));
 
 	// sanity checks
 	//

+ 33 - 20
src/resource/ProgramPrePreprocessor.cpp

@@ -31,23 +31,24 @@ const U32 MAX_DEPTH = 8;
 //==============================================================================
 void ProgramPrePreprocessor::printSourceLines() const
 {
-	for(U i = 0; i < m_sourceLines.size(); ++i)
+	U i = 1;
+	auto it = m_sourceLines.getBegin();
+	auto end = m_sourceLines.getEnd();
+	for(; it != end; ++it)
 	{
-		printf("%4lu %s\n", i + 1, &m_sourceLines[i][0]);
+		printf("%4lu %s\n", i++, &(*it)[0]);
 	}
 }
 
 //==============================================================================
 Error ProgramPrePreprocessor::parseFile(const CString& filename)
 {
-	auto alloc = m_shaderSource.getAllocator();
-
 	// Parse files recursively
-	Error err = parseFileForPragmas(PPPString(filename, alloc), 0);
+	Error err = parseFileForPragmas(filename, 0);
 
 	if(!err)
 	{
-		m_shaderSource = m_sourceLines.join("\n");
+		err = m_sourceLines.join(m_alloc, "\n", m_shaderSource);
 	}
 	
 	return err;
@@ -55,7 +56,7 @@ Error ProgramPrePreprocessor::parseFile(const CString& filename)
 
 //==============================================================================
 Error ProgramPrePreprocessor::parseFileForPragmas(
-	const PPPString& filename, U32 depth)
+	CString filename, U32 depth)
 {
 	// first check the depth
 	if(depth > MAX_DEPTH)
@@ -68,14 +69,18 @@ Error ProgramPrePreprocessor::parseFileForPragmas(
 	Error err = ErrorCode::NONE;
 
 	// load file in lines
-	auto alloc = m_shaderSource.getAllocator();
-	PPPString txt(alloc);
-	PPPStringList lines(alloc);
+	PPPString txt;
+	PPPString::ScopeDestroyer txtd(&txt, m_alloc);
+
+	PPPStringList lines;
+	PPPStringList::ScopeDestroyer linesd(&lines, m_alloc);
+
 	File file;
-	ANKI_CHECK(file.open(filename.toCString(), File::OpenFlag::READ));
-	ANKI_CHECK(file.readAllText(txt));
-	lines = PPPStringList::splitString(txt.toCString(), '\n', alloc);
-	if(lines.size() < 1)
+	ANKI_CHECK(file.open(filename, File::OpenFlag::READ));
+	ANKI_CHECK(file.readAllText(TempResourceAllocator<char>(m_alloc), txt));
+	ANKI_CHECK(PPPStringList::splitString(
+		m_alloc, txt.toCString(), '\n', lines));
+	if(lines.getSize() < 1)
 	{
 		ANKI_LOGE("File is empty: %s", &filename[0]);
 		return ErrorCode::USER_DATA;
@@ -101,14 +106,22 @@ Error ProgramPrePreprocessor::parseFileForPragmas(
 
 				if(line.getLength() >= std::strlen(commands[6]) + 2)
 				{
-					PPPString filen(
+					PPPString filen;
+					PPPString::ScopeDestroyer filend(&filen, m_alloc);
+					
+					ANKI_CHECK(filen.create(
+						m_alloc,
 						line.begin() + std::strlen(commands[6]), 
-						line.end() - 1, 
-						alloc);
+						line.end() - 1));
+
+					PPPString filenFixed;
+					PPPString::ScopeDestroyer filenFixedd(&filenFixed, m_alloc);
 
-					filen = m_manager->fixResourceFilename(filen.toCString());
+					ANKI_CHECK(m_manager->fixResourceFilename(
+						filen.toCString(), filenFixed));
 
-					ANKI_CHECK(parseFileForPragmas(filen, depth + 1));
+					ANKI_CHECK(parseFileForPragmas(
+						filenFixed.toCString(), depth + 1));
 
 					malformed = false; // All OK
 				}
@@ -122,7 +135,7 @@ Error ProgramPrePreprocessor::parseFileForPragmas(
 		}
 		else
 		{
-			m_sourceLines.push_back(line);
+			ANKI_CHECK(m_sourceLines.pushBackSprintf(m_alloc, "%s", &line[0]));
 		}
 	}
 

+ 55 - 20
src/resource/ProgramResource.cpp

@@ -26,11 +26,22 @@ Error ProgramResource::load(const CString& filename, const CString& extraSrc,
 	ResourceManager& manager)
 {
 	Error err = ErrorCode::NONE;
+	auto alloc = manager._getTempAllocator();
 
 	ProgramPrePreprocessor pars(&manager);
 	ANKI_CHECK(pars.parseFile(filename));
-	TempResourceString source(extraSrc + pars.getShaderSource());
+	
+	// Allocate new source
+	TempResourceString source;
+	TempResourceString::ScopeDestroyer sourced(&source, alloc);
+	if(extraSrc.getLength() > 0)
+	{
+		ANKI_CHECK(source.create(alloc, extraSrc));
+	}
+
+	ANKI_CHECK(source.append(alloc, pars.getShaderSource()));
 
+	// Create
 	GlDevice& gl = manager._getGlDevice();
 	GlCommandBufferHandle cmdb;
 	ANKI_CHECK(cmdb.create(&gl));
@@ -49,47 +60,71 @@ Error ProgramResource::load(const CString& filename, const CString& extraSrc,
 }
 
 //==============================================================================
-String ProgramResource::createToCache(
+Error ProgramResource::createToCache(
 	const CString& filename, const CString& preAppendedSrcCode, 
-	const CString& filenamePrefix, ResourceManager& manager)
+	const CString& filenamePrefix, ResourceManager& manager,
+	TempResourceString& out)
 {
-	auto& alloc = manager._getAllocator();
+	Error err = ErrorCode::NONE;
+	auto alloc = manager._getTempAllocator();
 
-	if(preAppendedSrcCode == "")
+	if(preAppendedSrcCode.getLength() < 1)
 	{
-		return String(filename, alloc);
+		return out.create(alloc, filename);
 	}
 
 	// Create suffix
-	String unique(String(filename, alloc) + preAppendedSrcCode);
+	TempResourceString unique;
+	TempResourceString::ScopeDestroyer uniqued(&unique, alloc);
+
+	ANKI_CHECK(unique.create(alloc, filename));
+	ANKI_CHECK(unique.append(alloc, preAppendedSrcCode));
+
 	U64 h = computeHash(&unique[0], unique.getLength());
 
-	String suffix(String::toString(h, alloc));
+	TempResourceString suffix;
+	TempResourceString::ScopeDestroyer suffixd(&suffix, alloc);
+	ANKI_CHECK(suffix.toString(alloc, h));
 
 	// Compose cached filename
-	String newFilename(manager._getCacheDirectory()
-		+ "/" + filenamePrefix + suffix + ".glsl");
+	TempResourceString newFilename;
+	TempResourceString::ScopeDestroyer newFilenamed(&newFilename, alloc);
+
+	ANKI_CHECK(newFilename.sprintf(
+		alloc,
+		"%s/%s%s.glsl", 
+		&manager._getCacheDirectory()[0],
+		&filenamePrefix[0],
+		&suffix[0]));
 
 	if(fileExists(newFilename.toCString()))
 	{
-		return newFilename;
+		out = std::move(newFilename);
+		return err;
 	}
 
 	// Read file and append code
-	String src(alloc);
+	TempResourceString src;
+	TempResourceString::ScopeDestroyer srcd(&src, alloc);
+
+	TempResourceString fixedFname;
+	TempResourceString::ScopeDestroyer fixedFnamed(&fixedFname, alloc);
+	ANKI_CHECK(manager.fixResourceFilename(filename, fixedFname));
+
 	File file;
-	file.open(manager.fixResourceFilename(filename).toCString(), 
-		File::OpenFlag::READ);
-	file.readAllText(src);
-	src = preAppendedSrcCode + src;
+	ANKI_CHECK(file.open(fixedFname.toCString(), File::OpenFlag::READ));
+	ANKI_CHECK(file.readAllText(TempResourceAllocator<char>(alloc), src));
+
+	TempResourceString srcfull;
+	TempResourceString::ScopeDestroyer srcfulld(&srcfull, alloc);
+	ANKI_CHECK(srcfull.sprintf(alloc, "%s%s", &preAppendedSrcCode[0], &src[0]));
 
 	// Write cached file
 	File f;
-	f.open(newFilename.toCString(), File::OpenFlag::WRITE);
-	Error err = f.writeText("%s\n", &src[0]);
-	ANKI_ASSERT(!err && "handle_error");
+	ANKI_CHECK(f.open(newFilename.toCString(), File::OpenFlag::WRITE));
+	ANKI_CHECK(f.writeText("%s\n", &srcfull[0]));
 
-	return newFilename;
+	return err;
 }
 
 } // end namespace anki

+ 40 - 27
src/resource/ResourceManager.cpp

@@ -19,30 +19,46 @@
 namespace anki {
 
 //==============================================================================
-ResourceManager::ResourceManager(Initializer& init)
-:	m_gl(init.m_gl),
-	m_alloc(init.m_allocCallback, init.m_allocCallbackData),
-	m_tmpAlloc(init.m_allocCallback, init.m_allocCallbackData, 
-		init.m_tempAllocatorMemorySize),
-	m_cacheDir(init.m_cacheDir, m_alloc),
-	m_dataDir(m_alloc),
-	m_shadersPrependedSource(m_alloc)
+ResourceManager::ResourceManager()
+{}
+
+//==============================================================================
+ResourceManager::~ResourceManager()
+{
+	m_cacheDir.destroy(m_alloc);
+	m_dataDir.destroy(m_alloc);
+	m_alloc.deleteInstance(m_asyncLoader);
+}
+
+//==============================================================================
+Error ResourceManager::create(Initializer& init)
 {
+	Error err = ErrorCode::NONE;
+
+	m_gl = init.m_gl;
+	m_alloc = ResourceAllocator<U8>(
+		init.m_allocCallback, init.m_allocCallbackData);
+
+	m_tmpAlloc = TempResourceAllocator<U8>(
+		init.m_allocCallback, init.m_allocCallbackData, 
+		init.m_tempAllocatorMemorySize);
+
+	ANKI_CHECK(m_cacheDir.create(m_alloc, init.m_cacheDir));
+
 	// Init the data path
 	//
 	if(getenv("ANKI_DATA_PATH"))
 	{
-		m_dataDir = getenv("ANKI_DATA_PATH");
-		m_dataDir += "/";
+		ANKI_CHECK(m_dataDir.sprintf(m_alloc, "%s/", getenv("ANKI_DATA_PATH")));
 		ANKI_LOGI("Data path: %s", &m_dataDir[0]);
 	}
 	else
 	{
 		// Assume working directory
 #if ANKI_OS == ANKI_OS_ANDROID
-		m_dataDir = "$";
+		ANKI_CHECK(m_dataDir.create(m_alloc, "$"));
 #else
-		m_dataDir = "./";
+		ANKI_CHECK(m_dataDir.create(m_alloc, "./"));
 #endif
 	}
 
@@ -71,35 +87,32 @@ ResourceManager::ResourceManager(Initializer& init)
 
 	// Init the thread
 	m_asyncLoader = m_alloc.newInstance<AsyncLoader>(m_alloc);
-}
-
-//==============================================================================
-ResourceManager::~ResourceManager()
-{
-	m_alloc.deleteInstance(m_asyncLoader);
+	if(m_asyncLoader == nullptr)
+	{
+		return ErrorCode::FUNCTION_FAILED;
+	}
 
-	ANKI_ASSERT(m_tmpAlloc.getMemoryPool().getAllocationsCount() == 0
-		&& "Forgot to deallocate");
+	return err;
 }
 
 //==============================================================================
-TempResourceString ResourceManager::fixResourceFilename(
-	const CString& filename) const
+Error ResourceManager::fixResourceFilename(
+	const CString& filename,
+	TempResourceString& out) const
 {
-	TempResourceString newFname(m_tmpAlloc);
+	Error err = ErrorCode::NONE;
 
 	// If the filename is in cache then dont append the data path
 	if(filename.find(m_cacheDir.toCString()) != TempResourceString::NPOS)
 	{
-		newFname = filename;
+		err = out.create(m_tmpAlloc, filename);
 	}
 	else
 	{
-		newFname = TempResourceString(m_dataDir.toCString(), m_tmpAlloc) 
-			+ filename;
+		err = out.sprintf(m_tmpAlloc, "%s%s", &m_dataDir[0], &filename[0]);
 	}
 
-	return newFname;
+	return err;
 }
 
 } // end namespace anki

+ 7 - 2
src/resource/Skeleton.cpp

@@ -16,6 +16,11 @@ Skeleton::Skeleton(ResourceAllocator<U8>& alloc)
 //==============================================================================
 Skeleton::~Skeleton()
 {
+	for(Bone& b : m_bones)
+	{
+		b._destroy(m_alloc);
+	}
+
 	m_bones.destroy(m_alloc);
 }
 
@@ -42,7 +47,7 @@ Error Skeleton::load(const CString& filename, ResourceInitializer& init)
 	ANKI_CHECK(boneEl.getSiblingElementsCount(bonesCount));
 	++bonesCount;
 
-	ANKI_CHECK(m_bones.create(m_alloc, bonesCount, Bone(init.m_alloc)));
+	ANKI_CHECK(m_bones.create(m_alloc, bonesCount));
 
 	// Load every bone
 	bonesCount = 0;
@@ -55,7 +60,7 @@ Error Skeleton::load(const CString& filename, ResourceInitializer& init)
 		ANKI_CHECK(boneEl.getChildElement("name", nameEl));
 		CString tmp;
 		ANKI_CHECK(nameEl.getText(tmp));
-		bone.m_name = tmp;
+		ANKI_CHECK(bone.m_name.create(m_alloc, tmp));
 
 		// <transform>
 		XmlElement trfEl;

+ 13 - 6
src/scene/SceneComponent.cpp

@@ -9,16 +9,23 @@
 namespace anki {
 
 //==============================================================================
-Bool SceneComponent::updateReal(SceneNode& node, F32 prevTime, F32 crntTime)
+Error SceneComponent::updateReal(SceneNode& node, F32 prevTime, F32 crntTime,
+	Bool& updated)
 {
 	reset();
-	Bool updated = update(node, prevTime, crntTime);
-	if(updated)
+
+	Error err = update(node, prevTime, crntTime, updated);
+	if(!err && updated)
 	{
-		onUpdate(node, prevTime, crntTime);
-		m_timestamp = getGlobTimestamp();
+		err = onUpdate(node, prevTime, crntTime);
+
+		if(!err)
+		{
+			m_timestamp = getGlobTimestamp();
+		}
 	}
-	return updated;
+
+	return err;
 }
 
 } // end namespace anki