فهرست منبع

Thread safety in OpenGL containers

Panagiotis Christopoulos Charitos 13 سال پیش
والد
کامیت
3e4e4fadfa
17فایلهای تغییر یافته به همراه346 افزوده شده و 365 حذف شده
  1. 5 0
      anki/gl/BufferObject.h
  2. 40 30
      anki/gl/Fbo.cpp
  3. 40 29
      anki/gl/Fbo.h
  4. 0 3
      anki/gl/GlException.cpp
  5. 6 7
      anki/gl/GlException.h
  6. 0 8
      anki/gl/Query.cpp
  7. 10 10
      anki/gl/Query.h
  8. 16 10
      anki/gl/ShaderProgram.cpp
  9. 28 9
      anki/gl/ShaderProgram.h
  10. 96 64
      anki/gl/Texture.cpp
  11. 99 65
      anki/gl/Texture.h
  12. 0 63
      anki/gl/TimeQuery.cpp
  13. 0 46
      anki/gl/TimeQuery.h
  14. 1 7
      anki/gl/Vao.cpp
  15. 5 10
      anki/gl/Vao.h
  16. 0 3
      anki/util/Assert.cpp
  17. 0 1
      anki/util/Assert.h

+ 5 - 0
anki/gl/BufferObject.h

@@ -7,6 +7,9 @@
 
 namespace anki {
 
+/// @addtogroup gl
+/// @{
+	
 /// A wrapper for OpenGL buffer objects (vertex arrays, texture buffers etc)
 /// to prevent us from making idiotic errors
 class BufferObject
@@ -92,6 +95,7 @@ public:
 	void destroy()
 	{
 		ANKI_ASSERT(isCreated());
+		unbind();
 		glDeleteBuffers(1, &glId);
 		glId = 0;
 	}
@@ -129,6 +133,7 @@ private:
 	GLenum usage; ///< GL_STREAM_DRAW or GL_STATIC_DRAW or GL_DYNAMIC_DRAW
 	size_t sizeInBytes; ///< The size of the buffer
 };
+/// @}
 
 } // end namespace anki
 

+ 40 - 30
anki/gl/Fbo.cpp

@@ -1,19 +1,17 @@
 #include "anki/gl/Fbo.h"
 #include "anki/gl/Texture.h"
-#include <boost/lexical_cast.hpp>
 #include <array>
 
-
 namespace anki {
 
-
 //==============================================================================
 
-static std::array<GLenum, 8> colorAttachments = {{
+static const std::array<GLenum, 8> colorAttachments = {{
 	GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2,
 	GL_COLOR_ATTACHMENT3, GL_COLOR_ATTACHMENT4, GL_COLOR_ATTACHMENT5,
 	GL_COLOR_ATTACHMENT6, GL_COLOR_ATTACHMENT7}};
 
+thread_local const Fbo* Fbo::currentFbo = nullptr;
 
 //==============================================================================
 Fbo::~Fbo()
@@ -24,56 +22,67 @@ Fbo::~Fbo()
 	}
 }
 
-
 //==============================================================================
-void Fbo::checkIfGood() const
+void Fbo::bind() const
 {
 	ANKI_ASSERT(isCreated());
-	ANKI_ASSERT(getCurrentFbo() == glId); // another FBO is binded
 
-	GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
-
-	if(status != GL_FRAMEBUFFER_COMPLETE)
+	if(currentFbo != this)
 	{
-		throw ANKI_EXCEPTION("FBO is incomplete: " 
-			+ boost::lexical_cast<std::string>(status));
+		glBindFramebuffer(GL_FRAMEBUFFER, glId);
+		currentFbo = this;
 	}
 }
 
-
 //==============================================================================
-void Fbo::setNumOfColorAttachements(uint num) const
+void Fbo::unbind() const
 {
 	ANKI_ASSERT(isCreated());
-	ANKI_ASSERT(getCurrentFbo() == glId); // another FBO is binded
 
-	if(num == 0)
+	if(currentFbo == this)
 	{
-		glDrawBuffer(GL_NONE);
-		glReadBuffer(GL_NONE);
+		glBindFramebuffer(GL_FRAMEBUFFER, 0);
+		currentFbo = nullptr;
 	}
-	else
+}
+
+//==============================================================================
+void Fbo::unbindAll()
+{
+	if(currentFbo != nullptr)
 	{
-		ANKI_ASSERT(num <= colorAttachments.size());
-		glDrawBuffers(num, &colorAttachments[0]);
+		glBindFramebuffer(GL_FRAMEBUFFER, 0);
+		currentFbo = nullptr;
 	}
 }
 
-
 //==============================================================================
-uint Fbo::getCurrentFbo()
+void Fbo::checkIfGood() const
 {
-	int fboGlId;
-	glGetIntegerv(GL_FRAMEBUFFER_BINDING, &fboGlId);
-	return (uint)fboGlId;
-}
+	ANKI_ASSERT(isCreated());
+	ANKI_ASSERT(glId == getCurrentDrawFboGlId() && "Not binded");
 
+	GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+	if(status != GL_FRAMEBUFFER_COMPLETE)
+	{
+		throw ANKI_EXCEPTION("FBO is incomplete");
+	}
+}
 
 //==============================================================================
 void Fbo::setColorAttachments(const std::initializer_list<const Texture*>& 
 	textures)
 {
-	setNumOfColorAttachements(textures.size());
+	ANKI_ASSERT(isCreated());
+	ANKI_ASSERT(glId == getCurrentDrawFboGlId() && "Not binded");
+	ANKI_ASSERT(textures.size() > 0);
+
+	// Set number of color attachments
+	//
+	glDrawBuffers(textures.size(), &colorAttachments[0]);
+
+	// Set attachments
+	//
 	int i = 0;
 	for(const Texture* tex : textures)
 	{
@@ -83,13 +92,14 @@ void Fbo::setColorAttachments(const std::initializer_list<const Texture*>&
 	}
 }
 
-
 //==============================================================================
 void Fbo::setOtherAttachment(GLenum attachment, const Texture& tex)
 {
+	ANKI_ASSERT(isCreated());
+	ANKI_ASSERT(glId == getCurrentDrawFboGlId() && "Not binded");
+
 	glFramebufferTexture2D(GL_FRAMEBUFFER, attachment,
 		GL_TEXTURE_2D, tex.getGlId(), 0);
 }
 
-
 } // end namespace

+ 40 - 29
anki/gl/Fbo.h

@@ -8,14 +8,17 @@
 #include <array>
 #include <initializer_list>
 
-
 namespace anki {
 
-
 class Texture;
 
+/// @addtogroup gl
+/// @{
 
-/// The class is actually a wrapper to avoid common mistakes
+/// Frame buffer object
+///
+/// The class is actually a wrapper to avoid common mistakes. It only supports
+/// binding at both draw and read targets
 class Fbo
 {
 public:
@@ -28,30 +31,27 @@ public:
 	~Fbo();
 	/// @}
 
-	uint getGlId() const
+	/// @name Accessors
+	/// @{
+	GLuint getGlId() const
 	{
-		ANKI_ASSERT(!isCreated());
+		ANKI_ASSERT(isCreated());
 		return glId;
 	}
+	/// @}
 
 	/// Binds FBO
-	void bind(GLenum target_ = GL_FRAMEBUFFER)
-	{
-		ANKI_ASSERT(isCreated());
-		target = target_;
-		ANKI_ASSERT(target == GL_DRAW_FRAMEBUFFER
-			|| target == GL_READ_FRAMEBUFFER
-			|| target == GL_FRAMEBUFFER);
-		glBindFramebuffer(target, glId);
-	}
+	void bind() const;
 
 	/// Unbinds the FBO
-	void unbind()
-	{
-		glBindFramebuffer(target, 0);
-	}
+	void unbind() const;
 
-	/// Checks the status of an initialized FBO and if fails throw exception
+	/// Unbind all
+	///
+	/// Unbinds both draw and read FBOs so the active is the default FBO
+	static void unbindAll();
+
+	/// Checks the status of an initialized FBO and if fails throws exception
 	/// @exception Exception
 	void checkIfGood() const;
 
@@ -62,39 +62,50 @@ public:
 	/// Set other attachment
 	void setOtherAttachment(GLenum attachment, const Texture& tex);
 
-	/// Returns the GL id of the current attached FBO
-	/// @return Returns the GL id of the current attached FBO
-	static uint getCurrentFbo();
-
 	/// Creates a new FBO
 	void create()
 	{
 		ANKI_ASSERT(!isCreated());
 		glGenFramebuffers(1, &glId);
+		ANKI_ASSERT(glId != 0);
 	}
 
 	/// Destroy it
 	void destroy()
 	{
 		ANKI_ASSERT(isCreated());
+		unbind();
 		glDeleteFramebuffers(1, &glId);
+		glId = 0;
 	}
 
 private:
-	uint glId; ///< OpenGL identification
-	GLenum target; ///< How the buffer is bind
+	/// Current binded FBOs for draw/read
+	static thread_local const Fbo* currentFbo;
+
+	GLuint glId; ///< OpenGL identification
 
 	bool isCreated() const
 	{
 		return glId != 0;
 	}
 
-	/// Set the number of color attachments of the FBO
-	void setNumOfColorAttachements(uint num) const;
-};
+	static GLuint getCurrentDrawFboGlId()
+	{
+		GLint i;
+		glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &i);
+		return i;
+	}
 
+	static GLuint getCurrentReadFboGlId()
+	{
+		GLint i;
+		glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &i);
+		return i;
+	}
+};
+/// @}
 
 } // end namespace
 
-
 #endif

+ 0 - 3
anki/gl/GlException.cpp

@@ -3,10 +3,8 @@
 #include <string>
 #include <GL/glew.h>
 
-
 namespace anki {
 
-
 void glConditionalThrowException(const char* file, int line, const char* func)
 {
 	GLenum errId = glGetError();
@@ -20,5 +18,4 @@ void glConditionalThrowException(const char* file, int line, const char* func)
 	throw Exception(err.c_str(), file, line, func);
 }
 
-
 } // end namespace

+ 6 - 7
anki/gl/GlException.h

@@ -1,20 +1,19 @@
 #ifndef ANKI_GL_GL_EXCEPTION_H
 #define ANKI_GL_GL_EXCEPTION_H
 
-
 namespace anki {
 
-
 /// The function throws an exception if there is an OpenGL error. Use it with
 /// the ANKI_CHECK_GL_ERROR macro
 void glConditionalThrowException(const char* file, int line, const char* func);
 
-
-#define ANKI_CHECK_GL_ERROR() \
-	glConditionalThrowException(__FILE__, __LINE__, __func__)
-
+#if !defined(NDEBUG)
+#	define ANKI_CHECK_GL_ERROR() \
+		glConditionalThrowException(__FILE__, __LINE__, __func__)
+#else
+#	define ANKI_CHECK_GL_ERROR() ((void)0)
+#endif
 
 } // end namespace
 
-
 #endif

+ 0 - 8
anki/gl/Query.cpp

@@ -2,10 +2,8 @@
 #include "anki/util/Exception.h"
 #include "anki/util/Assert.h"
 
-
 namespace anki {
 
-
 //==============================================================================
 Query::Query(GLenum q)
 {
@@ -19,28 +17,24 @@ Query::Query(GLenum q)
 	}
 }
 
-
 //==============================================================================
 Query::~Query()
 {
 	glDeleteQueries(1, &glId);
 }
 
-
 //==============================================================================
 void Query::beginQuery()
 {
 	glBeginQuery(question, glId);
 }
 
-
 //==============================================================================
 void Query::endQuery()
 {
 	glEndQuery(question);
 }
 
-
 //==============================================================================
 uint64_t Query::getResult()
 {
@@ -49,7 +43,6 @@ uint64_t Query::getResult()
 	return result;
 }
 
-
 //==============================================================================
 uint64_t Query::getResultNoWait(bool& finished)
 {
@@ -71,5 +64,4 @@ uint64_t Query::getResultNoWait(bool& finished)
 	return result;
 }
 
-
 } // end namespace anki

+ 10 - 10
anki/gl/Query.h

@@ -4,14 +4,12 @@
 #include <cstdint>
 #include <GL/glew.h>
 
-
 namespace anki {
 
+/// @addtogroup gl
+///
 
-class QueryImpl;
-
-
-/// XXX
+/// Query object
 class Query
 {
 public:
@@ -32,20 +30,22 @@ public:
 	void endQuery();
 
 	/// Get results
-	/// @note Waits for operations to finish
+	///
+	/// Waits for operations to finish
 	uint64_t getResult();
 
-	/// Get results. Doesn't Wait for operations to finish. If @a finished is 
-	/// false then the return value is irrelevant
+	/// Get results
+	///
+	/// Doesn't Wait for operations to finish. If @a finished is false then 
+	/// the return value is irrelevant
 	uint64_t getResultNoWait(bool& finished);
 
 private:
 	GLuint glId;
 	GLenum question;
 };
-
+/// @}
 
 } // end namespace anki
 
-
 #endif

+ 16 - 10
anki/gl/ShaderProgram.cpp

@@ -14,8 +14,8 @@ namespace anki {
 void ShaderProgramUniformVariable::doSanityChecks() const
 {
 	ANKI_ASSERT(getLocation() != -1);
-	ANKI_ASSERT(ShaderProgram::getCurrentProgram() == 
-		&getFatherShaderProgram());
+	ANKI_ASSERT(ShaderProgram::getCurrentProgramGlId() == 
+		getFatherShaderProgram().getGlId());
 	ANKI_ASSERT(glGetUniformLocation(getFatherShaderProgram().getGlId(),
 		getName().c_str()) == getLocation());
 }
@@ -128,14 +128,14 @@ const char* ShaderProgram::stdSourceCode =
 	"#pragma debug(on)\n";
 #endif
 
-const ShaderProgram* ShaderProgram::currentProgram = nullptr;
+const thread_local ShaderProgram* ShaderProgram::currentProgram = nullptr;
 
 //==============================================================================
 void ShaderProgram::create(const char* vertSource, const char* tcSource, 
 	const char* teSource, const char* geomSource, const char* fragSource,
 	const char* transformFeedbackVaryings[])
 {
-	ANKI_ASSERT(!isInitialized());
+	ANKI_ASSERT(!isCreated());
 
 	// 1) create and compile the shaders
 	//
@@ -202,7 +202,8 @@ void ShaderProgram::create(const char* vertSource, const char* tcSource,
 
 	if(count)
 	{
-		glTransformFeedbackVaryings(glId,
+		glTransformFeedbackVaryings(
+			glId,
 			count, 
 			transformFeedbackVaryings,
 			GL_SEPARATE_ATTRIBS);
@@ -218,6 +219,8 @@ void ShaderProgram::create(const char* vertSource, const char* tcSource,
 //==============================================================================
 void ShaderProgram::destroy()
 {
+	unbind();
+
 	if(vertShaderGlId != 0)
 	{
 		glDeleteShader(vertShaderGlId);
@@ -243,7 +246,12 @@ void ShaderProgram::destroy()
 		glDeleteShader(fragShaderGlId);
 	}
 
-	glDeleteProgram(glId);
+	if(glId != 0)
+	{
+		glDeleteProgram(glId);
+	}
+
+	init();
 }
 
 //==============================================================================
@@ -332,8 +340,7 @@ void ShaderProgram::getUniAndAttribVars()
 		int loc = glGetAttribLocation(glId, &name_[0]);
 		if(loc == -1) // if -1 it means that its an FFP var
 		{
-			throw ANKI_EXCEPTION("You are using FFP vertex attributes (\"" 
-				+ std::string(&name_[0]) + "\")");
+			throw ANKI_EXCEPTION("You are using FFP vertex attributes");
 		}
 
 		ShaderProgramAttributeVariable* var =
@@ -358,8 +365,7 @@ void ShaderProgram::getUniAndAttribVars()
 		int loc = glGetUniformLocation(glId, &name_[0]);
 		if(loc == -1) // if -1 it means that its an FFP var
 		{
-			throw ANKI_EXCEPTION("You are using FFP vertex uniforms (\"" 
-				+ std::string(&name_[0]) + "\")");
+			throw ANKI_EXCEPTION("You are using FFP vertex uniforms");
 		}
 
 		ShaderProgramUniformVariable* var =

+ 28 - 9
anki/gl/ShaderProgram.h

@@ -186,6 +186,8 @@ public:
 	ShaderProgram(const ShaderProgram&) = delete;
 	ShaderProgram& operator=(const ShaderProgram&) = delete;
 
+	/// @name Constructors/Destructor
+	/// @{
 	ShaderProgram()
 	{
 		init();
@@ -202,14 +204,18 @@ public:
 
 	~ShaderProgram()
 	{
-		destroy();
+		if(isCreated())
+		{
+			destroy();
+		}
 	}
+	/// @}
 
 	/// @name Accessors
 	/// @{
 	GLuint getGlId() const
 	{
-		ANKI_ASSERT(isInitialized());
+		ANKI_ASSERT(isCreated());
 		return glId;
 	}
 
@@ -236,7 +242,7 @@ public:
 	/// @param teSource Tessellation evaluation shader source. Can be nullptr
 	/// @param geomSource Geometry shader source. Can be nullptr
 	/// @param fragSource Fragment shader source. Can be nullptr
-	/// @param transformFeedbackVaryings A list of varyings names. Eg 
+	/// @param transformFeedbackVaryings An array of varyings names. Eg 
 	///                                  {"var0", "var1", nullptr} or {nullptr}
 	void create(const char* vertSource, const char* tcSource, 
 		const char* teSource, const char* geomSource, const char* fragSource,
@@ -245,14 +251,24 @@ public:
 	/// Bind the shader program
 	void bind() const
 	{
-		ANKI_ASSERT(isInitialized());
-		if(currentProgram == nullptr || currentProgram != this)
+		ANKI_ASSERT(isCreated());
+		if(currentProgram != this)
 		{
 			glUseProgram(glId);
 			currentProgram = this;
 		}
 	}
 
+	// Unbinds only @a this if its binded
+	void unbind() const
+	{
+		if(currentProgram == this)
+		{
+			glUseProgram(0);
+			currentProgram = nullptr;
+		}
+	}
+
 	/// @name Variable finders
 	/// Used to find and return the variable. They return nullptr if the 
 	/// variable is not found
@@ -264,9 +280,11 @@ public:
 		const char* varName) const;
 	/// @}
 
-	static const ShaderProgram* getCurrentProgram()
+	static GLuint getCurrentProgramGlId()
 	{
-		return currentProgram;
+		int i;
+		glGetIntegerv(GL_CURRENT_PROGRAM, &i);
+		return i;
 	}
 
 	/// For debugging
@@ -284,7 +302,7 @@ private:
 		NameToAttribVarHashMap;
 
 	/// Is an optimization. it keeps the program that is now binded 
-	static const ShaderProgram* currentProgram;
+	static const thread_local ShaderProgram* currentProgram;
 
 	/// Shader source that is used in ALL shader programs
 	static const char* stdSourceCode;
@@ -322,11 +340,12 @@ private:
 	void link() const;
 
 	/// Returns true if the class points to a valid GL ID
-	bool isInitialized() const
+	bool isCreated() const
 	{
 		return glId != 0;
 	}
 
+	/// Common construction code
 	void init()
 	{
 		glId = vertShaderGlId = tcShaderGlId = teShaderGlId =

+ 96 - 64
anki/gl/Texture.cpp

@@ -1,12 +1,9 @@
 #include "anki/gl/Texture.h"
 #include "anki/gl/GlException.h"
 #include "anki/util/Exception.h"
-#include <boost/lexical_cast.hpp>
-
 
 namespace anki {
 
-
 //==============================================================================
 // TextureManager                                                              =
 //==============================================================================
@@ -16,94 +13,138 @@ TextureManager::TextureManager()
 {
 	GLint tmp;
 	glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &tmp);
-	units.resize(tmp, Unit{nullptr, 0});
+	unitsCount = tmp;
 	
-	activeUnit = -1;
 	mipmapping = true;
 	anisotropyLevel = 8;
 	compression = true;
 }
 
+//==============================================================================
+// TextureUnits                                                                =
+//==============================================================================
 
 //==============================================================================
-void TextureManager::activateUnit(uint unit)
+TextureUnits::TextureUnits()
 {
-	ANKI_ASSERT(unit < units.size());
-	if(activeUnit == unit)
-	{
-		return;
-	}
+	units.resize(TextureManagerSingleton::get().getMaxUnitsCount(), 
+		Unit{0, 0});
+	ANKI_ASSERT(units.size() > 7);
 
-	activeUnit = unit;
-	glActiveTexture(GL_TEXTURE0 + activeUnit);
+	activeUnit = -1;
+	choseUnitTimes = 0;
 }
 
+//==============================================================================
+int TextureUnits::whichUnit(const Texture& tex)
+{
+	GLuint glid = tex.getGlId();
+	int i = units.size();
+
+	do
+	{
+		--i;
+	} while(i >= 0 && glid != units[i].tex);
+
+	return i;
+}
 
 //==============================================================================
-uint TextureManager::choseUnit(Texture* tex)
+void TextureUnits::activateUnit(uint32_t unit)
 {
-	// Increase life for all
-	//
-	for(Unit& unit : units)
+	ANKI_ASSERT(unit < units.size());
+	if(activeUnit != (int)unit)
 	{
-		++unit.life;
+		activeUnit = (int)unit;
+		glActiveTexture(GL_TEXTURE0 + activeUnit);
 	}
+}
+
+//==============================================================================
+uint32_t TextureUnits::choseUnit(const Texture& tex, bool& allreadyBinded)
+{
+	++choseUnitTimes;
+	int myTexUnit = whichUnit(tex);
+	allreadyBinded = false;
 
-	// Already binded
+	// Already binded => renew it
 	//
-	if(tex->unit != -1)
+	if(myTexUnit != -1)
 	{
-		ANKI_ASSERT(units[tex->unit].tex == tex);
-		units[tex->unit].life = 0;
-		return tex->unit;
+		ANKI_ASSERT(units[myTexUnit].tex == tex.getGlId());
+		units[myTexUnit].born = choseUnitTimes;
+		allreadyBinded = true;
+		return myTexUnit;
 	}
 
 	// Find an empty slot for it
 	//
-	for(uint i = 0; i < units.size(); i++)
+	for(uint32_t i = 0; i < units.size(); i++)
 	{
-		if(units[i].tex == nullptr)
+		if(units[i].tex == 0)
 		{
-			units[i].tex = tex;
-			units[i].life = 0;
+			units[i].tex = tex.getGlId();
+			units[i].born = choseUnitTimes;
 			return i;
 		}
 	}
 
-	// If all units occupied chose the older
+	// Find the older unit and replace the texture
 	//
-
-	// Find the unit with the max life
-	uint umaxlife = 0;
-	for(uint i = 1; i < units.size(); ++i)
+	uint64_t older = 0;
+	for(uint32_t i = 1; i < units.size(); ++i)
 	{
-		if(units[umaxlife].life < units[i].life)
+		if(units[i].born < units[older].born)
 		{
-			umaxlife = i;
+			older = i;
 		}
 	}
 
-	units[umaxlife].tex->unit = -1;
-	units[umaxlife].tex = tex;
-	units[umaxlife].life = 0;
-	return umaxlife;
+	units[older].tex = tex.getGlId();
+	units[older].born = choseUnitTimes;
+	return older;
 }
 
+//==============================================================================
+void TextureUnits::bindTexture(const Texture& tex)
+{
+	bool allreadyBinded;
+	uint32_t unit = choseUnit(tex, allreadyBinded);
+
+	if(!allreadyBinded)
+	{
+		activateUnit(unit);
+		glBindTexture(tex.getTarget(), tex.getGlId());
+	}
+}
 
 //==============================================================================
-void TextureManager::textureDeleted(Texture* tex)
+void TextureUnits::bindTextureAndActivateUnit(const Texture& tex)
 {
-	for(auto it = units.begin(); it != units.end(); ++it)
+	bool allreadyBinded;
+	uint32_t unit = choseUnit(tex, allreadyBinded);
+
+	activateUnit(unit);
+
+	if(!allreadyBinded)
 	{
-		if(it->tex == tex)
-		{
-			it->tex = nullptr;
-			return;
-		}
+		glBindTexture(tex.getTarget(), tex.getGlId());
 	}
-	ANKI_ASSERT(0 && "Pointer not found");
 }
 
+//==============================================================================
+void TextureUnits::unbindTexture(const Texture& tex)
+{
+	int unit = whichUnit(tex);
+	if(unit == -1)
+	{
+		return;
+	}
+
+	units[unit].tex = 0;
+	units[unit].born = 0;
+	// There is no need to use GL to unbind
+}
 
 //==============================================================================
 // Texture                                                                     =
@@ -111,33 +152,33 @@ void TextureManager::textureDeleted(Texture* tex)
 
 //==============================================================================
 Texture::Texture()
-	: glId(std::numeric_limits<uint>::max()), target(GL_TEXTURE_2D), unit(-1)
+	: glId(0), target(GL_TEXTURE_2D)
 {}
 
-
 //==============================================================================
 Texture::~Texture()
 {
-	if(isLoaded())
+	TextureUnitsSingleton::get().unbindTexture(*this);
+	if(isCreated())
 	{
 		glDeleteTextures(1, &glId);
 	}
 
-	TextureManagerSingleton::get().textureDeleted(this);
+	TextureUnitsSingleton::get().unbindTexture(*this);
 }
 
-
 //==============================================================================
 void Texture::create(const Initializer& init)
 {
 	// Sanity checks
 	//
-	ANKI_ASSERT(!isLoaded());
+	ANKI_ASSERT(!isCreated());
 	ANKI_ASSERT(init.internalFormat <= 4 && "Deprecated internal format");
 
 	// Create
 	//
 	glGenTextures(1, &glId);
+	ANKI_ASSERT(glId != 0);
 	target = GL_TEXTURE_2D;
 	internalFormat = init.internalFormat;
 	format = init.format;
@@ -146,8 +187,7 @@ void Texture::create(const Initializer& init)
 	height = init.height;
 
 	// Bind
-	glActiveTexture(GL_TEXTURE0);
-	glBindTexture(target, glId);
+	TextureUnitsSingleton::get().bindTextureAndActivateUnit(*this);
 
 	switch(internalFormat)
 	{
@@ -198,26 +238,19 @@ void Texture::create(const Initializer& init)
 	ANKI_CHECK_GL_ERROR();
 }
 
-
 //==============================================================================
 void Texture::bind() const
 {
-	unit = TextureManagerSingleton::get().choseUnit(
-		const_cast<Texture*>(this));
-
-	TextureManagerSingleton::get().activateUnit(unit);
-	glBindTexture(target, glId);
+	TextureUnitsSingleton::get().bindTexture(*this);
 }
 
-
 //==============================================================================
 void Texture::genMipmap()
 {
-	bind();
+	TextureUnitsSingleton::get().bindTextureAndActivateUnit(*this);
 	glGenerateMipmap(target);
 }
 
-
 //==============================================================================
 void Texture::setFilteringNoBind(TextureFilteringType filterType) const
 {
@@ -237,5 +270,4 @@ void Texture::setFilteringNoBind(TextureFilteringType filterType) const
 	}
 }
 
-
 } // end namespace

+ 99 - 65
anki/gl/Texture.h

@@ -10,14 +10,14 @@
 #include <limits>
 #include <thread>
 
-
 namespace anki {
 
-
 class Texture;
 
+/// @addtogroup gl
+/// @{
 
-/// XXX
+/// Contains a few hints on how to crate textures
 class TextureManager
 {
 public:
@@ -25,11 +25,11 @@ public:
 
 	/// @name Accessors
 	/// @{
-	uint getAnisotropyLevel() const
+	uint32_t getAnisotropyLevel() const
 	{
 		return anisotropyLevel;
 	}
-	void setAnisotropyLevel(uint x) 
+	void setAnisotropyLevel(uint32_t x) 
 	{
 		anisotropyLevel = x;
 	}
@@ -52,61 +52,92 @@ public:
 		compression = x;
 	}
 
-	size_t getMaxUnitsCount() const
+	uint32_t getMaxUnitsCount() const
 	{
-		return units.size() + 1;
+		return unitsCount;
 	}
 	/// @}
 
-	/// The manager returns the texture unit where the @a tex can be binded
-	uint choseUnit(Texture* tex);
-
-	/// Alias for glActiveTexture
-	void activateUnit(uint unit);
-
-	/// This is called by the texture destructor in order to invalidate the 
-	/// texture
-	void textureDeleted(Texture* tex);
-
 private:
-	/// Texture unit representation
-	struct Unit
-	{
-		Texture* tex;
-		/// The bigger the life is the longer the @a tex has stayed witout bing 
-		/// binded
-		uint life; 
-	};
-
-	/// Texture units. The last is reserved and only used in Texture::create
-	std::vector<Unit> units;
-	uint activeUnit;
-
 	/// @name Hints
 	/// Hints when creating new textures. The implementation may try to ignore 
 	/// them
 	/// @{
-	uint anisotropyLevel;
+	uint32_t anisotropyLevel;
 	bool mipmapping;
 	/// If true the new textures will be compressed if not already
 	bool compression;
 	/// @}
-};
 
+	uint32_t unitsCount;
+};
 
 /// The global texture manager
 typedef Singleton<TextureManager> TextureManagerSingleton;
 
+/// Class for effective binding
+class TextureUnits
+{
+public:
+	TextureUnits();
+
+	/// Alias for glActiveTexture
+	void activateUnit(uint32_t unit);
+
+	/// Bind the texture to a unit. It's not sure that it will activate the unit
+	void bindTexture(const Texture& tex);
+
+	/// XXX
+	void bindTextureAndActivateUnit(const Texture& tex);
 
-/// @brief Texture class
-/// Generally its not thread safe and a few methods can be called by other 
-/// threads. See the docs for each method
+	/// Unbind a texture from it's unit
+	void unbindTexture(const Texture& tex);
+
+	/// Get the number of the texture unit the @a tex is binded. Returns -1 if
+	/// not binded to any unit
+	int whichUnit(const Texture& tex);
+
+private:
+	/// Texture unit representation
+	struct Unit
+	{
+		/// Have the GL ID to save memory. -1 if no tex is binded to that unit
+		GLuint tex;
+		
+		/// Born time
+		///
+		/// The bigger the value the latter the unit has been accessed. This 
+		/// practicaly means that if the @a born is low the unit is a canditate
+		/// for replacement 
+		uint64_t born; 
+	};
+
+	/// Texture units
+	std::vector<Unit> units;
+	/// The active texture unit
+	int activeUnit;
+	/// How many times the @a choseUnit has been called. Used to set the 
+	/// Unit::born
+	uint64_t choseUnitTimes; 
+
+	/// Helper method 
+	///
+	/// It returns the texture unit where the @a tex can be binded
+	uint32_t choseUnit(const Texture& tex, bool& allreadyBinded);
+};
+
+/// The global texture units manager. Its per thread
+typedef SingletonThreadSafe<TextureUnits> TextureUnitsSingleton;
+
+/// Texture class.
+/// Generally its thread safe as long as you have a shared context and the 
+/// driver supports it
 class Texture
 {
 	friend class TextureManager;
 
 public:
-	/// @brief Texture filtering type
+	/// Texture filtering type
 	enum TextureFilteringType
 	{
 		TFT_NEAREST,
@@ -114,11 +145,11 @@ public:
 		TFT_TRILINEAR
 	};
 
-	/// @brief Texture initializer struct
+	/// Texture initializer struct
 	struct Initializer
 	{
-		uint width;
-		uint height;
+		uint32_t width;
+		uint32_t height;
 		GLint internalFormat;
 		GLenum format;
 		GLenum type;
@@ -130,44 +161,56 @@ public:
 		size_t dataSize; ///< For compressed textures
 	};
 
-	/// @brief Default constructor
-	/// Thread safe
+	/// @name Constructors/Destructor
+	/// @{
+
+	/// Default constructor
 	Texture();
 
-	/// @brief Desrcuctor
-	/// Thread unsafe
+	Texture(const Initializer& init)
+	{
+		create(init);
+	}
+
+	/// Desrcuctor
 	~Texture();
+	/// @}
 
 	/// @name Accessors
 	/// Thread safe
 	/// @{
 	GLuint getGlId() const
 	{
-		ANKI_ASSERT(isLoaded());
+		ANKI_ASSERT(isCreated());
 		return glId;
 	}
 
 	GLenum getInternalFormat() const
 	{
-		ANKI_ASSERT(isLoaded());
+		ANKI_ASSERT(isCreated());
 		return internalFormat;
 	}
 
 	GLenum getFormat() const
 	{
-		ANKI_ASSERT(isLoaded());
+		ANKI_ASSERT(isCreated());
 		return format;
 	}
 
+	GLenum getTarget() const
+	{
+		return target;
+	}
+
 	GLenum getType() const
 	{
-		ANKI_ASSERT(isLoaded());
+		ANKI_ASSERT(isCreated());
 		return type;
 	}
 
 	int getUnit() const
 	{
-		return unit;
+		return TextureUnitsSingleton::get().whichUnit(*this);
 	}
 
 	GLuint getWidth() const
@@ -181,24 +224,20 @@ public:
 	}
 	/// @}
 
-	/// @brief Create a texture
-	/// Thread safe
+	/// Create a texture
 	void create(const Initializer& init);
 
-	/// @brief Bind the texture to a unit that the texture manager will decide
-	/// Thread unsafe
+	/// Bind the texture to a unit that the texture unit system will decide
 	void bind() const;
 
-	/// @brief Change the filtering type
-	/// Thread unsafe
+	/// Change the filtering type
 	void setFiltering(TextureFilteringType filterType)
 	{
-		bind();
+		TextureUnitsSingleton::get().bindTextureAndActivateUnit(*this);
 		setFilteringNoBind(filterType);
 	}
 
-	/// @brief Generate new mipmaps
-	/// Thread unsafe
+	/// Generate new mipmaps
 	void genMipmap();
 
 private:
@@ -209,20 +248,15 @@ private:
 	GLuint type; ///< GL_UNSIGNED_BYTE, GL_BYTE etc
 	GLuint width, height;
 
-	/// The texure unit that its binded to. It's -1 if it's not binded to any
-	/// texture unit
-	mutable int unit; 
-
-	bool isLoaded() const
+	bool isCreated() const
 	{
-		return glId != std::numeric_limits<uint>::max();
+		return glId != 0;
 	}
 
 	void setFilteringNoBind(TextureFilteringType filterType) const;
 };
-
+/// @}
 
 } // end namespace
 
-
 #endif

+ 0 - 63
anki/gl/TimeQuery.cpp

@@ -1,63 +0,0 @@
-#include "anki/gl/TimeQuery.h"
-#include "anki/util/Assert.h"
-#include "anki/gl/GlException.h"
-
-
-namespace anki
-{
-
-
-//==============================================================================
-TimeQuery::TimeQuery()
-{
-	glGenQueries(2, &glIds[0]);
-	state = S_CREATED;
-}
-
-
-//==============================================================================
-TimeQuery::~TimeQuery()
-{
-	glDeleteQueries(2, &glIds[0]);
-}
-
-
-//==============================================================================
-void TimeQuery::begin()
-{
-	ANKI_ASSERT(state == S_CREATED || state == S_ENDED);
-
-	glQueryCounter(glIds[0], GL_TIMESTAMP);
-
-	state = S_STARTED;
-}
-
-
-//==============================================================================
-double TimeQuery::end()
-{
-	ANKI_ASSERT(state == S_STARTED);
-
-	glQueryCounter(glIds[1], GL_TIMESTAMP);
-
-	// Wait
-	GLint done = 0;
-	while(!done)
-	{
-		/// XXX BAD BAD BAD BAD!!!!!
-		glGetQueryObjectiv(glIds[1], GL_QUERY_RESULT_AVAILABLE, &done);
-	}
-
-	// Get elapsed time
-	GLuint64 timerStart, timerEnd;
-
-	glGetQueryObjectui64v(glIds[0], GL_QUERY_RESULT, &timerStart);
-	glGetQueryObjectui64v(glIds[1], GL_QUERY_RESULT, &timerEnd);
-
-	state = S_ENDED;
-
-	return (timerEnd - timerStart) / 1000000000.0;
-}
-
-
-} // end namespace

+ 0 - 46
anki/gl/TimeQuery.h

@@ -1,46 +0,0 @@
-#ifndef ANKI_GL_TIME_QUERY_H
-#define ANKI_GL_TIME_QUERY_H
-
-#include <GL/glew.h>
-#include <boost/array.hpp>
-
-
-namespace anki
-{
-
-
-/// Used to profile the GPU. It gets the time elapsed from when the begin() is
-/// called until the endAndGetTimeElapsed() method. The query causes
-/// synchronization issues because it waits for the GL commands to finish. For
-/// that it is slow and its not recommended for use in production code.
-class TimeQuery
-{
-public:
-	TimeQuery();
-	~TimeQuery();
-
-	/// Begin
-	void begin();
-
-	/// End and get elapsed time. In seconds
-	double end();
-
-private:
-	/// The query state
-	enum State
-	{
-		S_CREATED,
-		S_STARTED,
-		S_ENDED
-	};
-
-	boost::array<GLuint, 2> glIds; ///< GL IDs
-	/// The query state. It saves us from improper use of the the class
-	State state;
-};
-
-
-} // end namespace
-
-
-#endif

+ 1 - 7
anki/gl/Vao.cpp

@@ -3,10 +3,8 @@
 #include "anki/util/Exception.h"
 #include "anki/gl/ShaderProgram.h"
 
-
 namespace anki {
 
-
 //==============================================================================
 Vao::~Vao()
 {
@@ -16,9 +14,8 @@ Vao::~Vao()
 	}
 }
 
-
 //==============================================================================
-void Vao::attachArrayBufferVbo(const Vbo& vbo, uint attribVarLocation,
+void Vao::attachArrayBufferVbo(const Vbo& vbo, GLuint attribVarLocation,
 	GLint size, GLenum type, GLboolean normalized, GLsizei stride,
 	const GLvoid* pointer)
 {
@@ -41,7 +38,6 @@ void Vao::attachArrayBufferVbo(const Vbo& vbo, uint attribVarLocation,
 	ANKI_CHECK_GL_ERROR();
 }
 
-
 //==============================================================================
 void Vao::attachArrayBufferVbo(const Vbo& vbo,
 	const ShaderProgramAttributeVariable& attribVar,
@@ -52,7 +48,6 @@ void Vao::attachArrayBufferVbo(const Vbo& vbo,
 		stride, pointer);
 }
 
-
 //==============================================================================
 void Vao::attachElementArrayBufferVbo(const Vbo& vbo)
 {
@@ -68,5 +63,4 @@ void Vao::attachElementArrayBufferVbo(const Vbo& vbo)
 	ANKI_CHECK_GL_ERROR();
 }
 
-
 } // end namespace

+ 5 - 10
anki/gl/Vao.h

@@ -6,14 +6,11 @@
 #include "anki/gl/GlException.h"
 #include <GL/glew.h>
 
-
 namespace anki {
 
-
 class ShaderProgramAttributeVariable;
 class Vbo;
 
-
 /// Vertex array object. Non-copyable to avoid instantiating it in the stack
 class Vao
 {
@@ -22,21 +19,21 @@ public:
 	Vao(const Vao&) = delete;
 	Vao& operator=(const Vao&) = delete;
 
-	/// @name Constructors
+	/// @name Constructors/Destructor
 	/// @{
 
 	/// Default
 	Vao()
 		: glId(0)
 	{}
-	/// @}
 
 	/// Destroy VAO from the OpenGL context
 	~Vao();
+	/// @}
 
 	/// @name Accessors
 	/// @{
-	uint getGlId() const
+	GLuint getGlId() const
 	{
 		ANKI_ASSERT(isCreated());
 		return glId;
@@ -97,7 +94,7 @@ public:
 	///        first generic vertex attribute in the array
 	void attachArrayBufferVbo(
 	    const Vbo& vbo,
-	    uint attribVarLocation,
+	    GLuint attribVarLocation,
 	    GLint size,
 	    GLenum type,
 	    GLboolean normalized,
@@ -120,7 +117,7 @@ public:
 	}
 
 private:
-	uint glId; ///< The OpenGL id
+	GLuint glId; ///< The OpenGL id
 
 	bool isCreated() const
 	{
@@ -128,8 +125,6 @@ private:
 	}
 };
 
-
 } // end namespace
 
-
 #endif

+ 0 - 3
anki/util/Assert.cpp

@@ -1,10 +1,8 @@
 #include <cstdlib>
 #include <iostream>
 
-
 namespace anki {
 
-
 //==============================================================================
 void akassert(bool expr, const char* exprTxt, const char* file, int line,
 	const char* func)
@@ -19,5 +17,4 @@ void akassert(bool expr, const char* exprTxt, const char* file, int line,
 	}
 }
 
-
 } // end namespace

+ 0 - 1
anki/util/Assert.h

@@ -1,7 +1,6 @@
 #ifndef ANKI_UTIL_ASSERT_H
 #define ANKI_UTIL_ASSERT_H
 
-
 /// Assertion. Print an error and stop the debugger (if it runs through a
 /// debugger) and then abort
 #if defined(NDEBUG)