Quellcode durchsuchen

Adding double and triple buffering to buffers. Minor refactoring in GL

Panagiotis Christopoulos Charitos vor 12 Jahren
Ursprung
Commit
2356b7d54f

+ 68 - 50
include/anki/gl/BufferObject.h

@@ -10,10 +10,10 @@ namespace anki {
 	
 /// A wrapper for OpenGL buffer objects (vertex arrays, texture buffers etc)
 /// to prevent us from making idiotic errors
-class BufferObject: public GlObject
+class BufferObject: public GlMultiObject
 {
 public:
-	typedef GlObject Base;
+	typedef GlMultiObject Base;
 
 	/// @name Constructors/Destructor
 	/// @{
@@ -28,13 +28,6 @@ public:
 		*this = std::move(b);
 	}
 
-	/// @see create
-	BufferObject(GLenum target, U32 sizeInBytes,
-		const void* dataPtr, GLenum usage)
-	{
-		create(target, sizeInBytes, dataPtr, usage);
-	}
-
 	/// It deletes the BO
 	~BufferObject()
 	{
@@ -76,15 +69,7 @@ public:
 	void bind() const
 	{
 		ANKI_ASSERT(isCreated());
-		glBindBuffer(target, glId);
-	}
-
-	/// Bind and change target
-	void bind(GLenum target_)
-	{
-		ANKI_ASSERT(isCreated());
-		setTarget(target_);
-		glBindBuffer(target, glId);
+		glBindBuffer(target, glId[getGlobTimestamp() % objectsCount]);
 	}
 
 	/// Unbind BO
@@ -102,9 +87,9 @@ public:
 	///		   Put NULL if you want just to allocate memory
 	/// @param usage It should be: GL_STREAM_DRAW or GL_STATIC_DRAW or
 	///		   GL_DYNAMIC_DRAW only!!!!!!!!!
-	/// @exception Exception
+	/// @param objectCount The number of objects
 	void create(GLenum target, U32 sizeInBytes, const void* dataPtr,
-		GLenum usage);
+		GLenum usage, U objectCount = SINGLE_OBJECT);
 
 	/// Delete the BO
 	void destroy();
@@ -151,38 +136,10 @@ public:
 	void unmap();
 
 	/// Set the binding for this buffer
-	void setBinding(GLuint binding) const
-	{
-		Bool correctTarget = target == GL_TRANSFORM_FEEDBACK_BUFFER 
-#if ANKI_GL == ANKI_GL_DESKTOP
-			|| target == GL_SHADER_STORAGE_BUFFER
-#endif
-			|| target == GL_UNIFORM_BUFFER;
-
-		ANKI_ASSERT(correctTarget);
-		(void)correctTarget;
-		
-		glBindBufferBase(target, binding, glId);
-	}
+	void setBinding(GLuint binding) const;
 
 	/// Set the binding point of this buffer with range
-	void setBindingRange(GLuint binding, PtrSize offset, PtrSize size) const
-	{
-		Bool correctTarget = target == GL_TRANSFORM_FEEDBACK_BUFFER 
-#if ANKI_GL == ANKI_GL_DESKTOP
-			|| target == GL_SHADER_STORAGE_BUFFER
-#endif
-			|| target == GL_UNIFORM_BUFFER;
-
-		ANKI_ASSERT(correctTarget);
-		(void)correctTarget;
-
-		ANKI_ASSERT(offset + size <= sizeInBytes);
-		ANKI_ASSERT(size > 0);
-
-		glBindBufferRange(target, binding, glId, offset, size);
-		ANKI_CHECK_GL_ERROR();
-	}
+	void setBindingRange(GLuint binding, PtrSize offset, PtrSize size) const;
 
 	/// Return GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT
 	static PtrSize getUniformBufferOffsetAlignment()
@@ -205,6 +162,67 @@ private:
 	Bool mapped = false; ///< Only in debug
 #endif
 };
+
+/// This is a wrapper for Vertex Buffer Objects to prevent us from making
+/// idiotic errors
+class Vbo: public BufferObject
+{
+public:
+	/// The same as BufferObject::create but it only accepts
+	/// GL_ARRAY_BUFFER or GL_ELEMENT_ARRAY_BUFFER in target
+	/// @see BufferObject::create
+	void create(GLenum target, PtrSize sizeInBytes, const void* dataPtr,
+		GLenum usage, U objectCount = SINGLE_OBJECT)
+	{
+		// unacceptable target_
+		ANKI_ASSERT(target == GL_ARRAY_BUFFER
+			|| target == GL_ELEMENT_ARRAY_BUFFER);
+		BufferObject::create(target, sizeInBytes, dataPtr, usage, objectCount);
+	}
+
+	/// Unbinds all VBOs, meaning both GL_ARRAY_BUFFER and
+	/// GL_ELEMENT_ARRAY_BUFFER targets
+	static void unbindAllTargets()
+	{
+		glBindBuffer(GL_ARRAY_BUFFER, 0);
+		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+	}
+};
+
+/// Uniform buffer object
+class Ubo: public BufferObject
+{
+public:
+	/// Create a UBO
+	void create(PtrSize size, const void* data, U objectCount = SINGLE_OBJECT)
+	{
+		GLint64 maxBufferSize;
+		glGetInteger64v(GL_MAX_UNIFORM_BLOCK_SIZE, &maxBufferSize);
+		ANKI_ASSERT(size <= (PtrSize)maxBufferSize);
+
+		BufferObject::create(GL_UNIFORM_BUFFER, size, data, GL_DYNAMIC_DRAW,
+			objectCount);
+	}
+};
+
+/// Pixel buffer object
+class Pbo: public BufferObject
+{
+public:
+	/// Create a PBO
+	void create(GLenum target, PtrSize size, const void* data, 
+		U objectCount = SINGLE_OBJECT)
+	{
+		ANKI_ASSERT(target == GL_PIXEL_PACK_BUFFER 
+			|| target == GL_PIXEL_UNPACK_BUFFER);
+
+		GLenum pboUsage = (target == GL_PIXEL_PACK_BUFFER) 
+			? GL_DYNAMIC_READ : GL_DYNAMIC_DRAW;
+
+		BufferObject::create(target, size, data, pboUsage, objectCount);
+	}
+};
+
 /// @}
 
 } // end namespace anki

+ 0 - 3
include/anki/gl/Gl.h

@@ -5,8 +5,6 @@
 #define ANKI_GL_GL_H
 
 #include "anki/gl/BufferObject.h"
-#include "anki/gl/Ubo.h"
-#include "anki/gl/Pbo.h"
 #include "anki/gl/Fbo.h"
 #include "anki/gl/GlException.h"
 #include "anki/gl/GlState.h"
@@ -14,7 +12,6 @@
 #include "anki/gl/ShaderProgram.h"
 #include "anki/gl/Texture.h"
 #include "anki/gl/Vao.h"
-#include "anki/gl/Vbo.h"
 #include "anki/gl/Drawcall.h"
 
 #include "anki/gl/Ogl.h"

+ 81 - 1
include/anki/gl/GlObject.h

@@ -3,10 +3,12 @@
 
 #include "anki/gl/Ogl.h"
 #include "anki/gl/GlException.h"
+#include "anki/Config.h"
 #include "anki/util/NonCopyable.h"
 #include "anki/util/Assert.h"
+#include "anki/util/Array.h"
 #include "anki/util/StdTypes.h"
-#include "anki/Config.h"
+#include "anki/core/Timestamp.h"
 #include <thread>
 
 namespace anki {
@@ -61,6 +63,84 @@ protected:
 	GLuint glId = 0;
 };
 
+/// A compound GL object that supports buffering depending on the frame
+class GlMultiObject: public NonCopyable
+{
+public:
+	/// Buffering technique
+	enum
+	{
+		SINGLE_OBJECT = 1,
+		DOUBLE_OBJECT = 2,
+		TRIPLE_OBJECT = 3,
+		MAX_OBJECTS = 3
+	};
+
+	/// Default
+	GlMultiObject()
+	{}
+
+	/// Move
+	GlMultiObject(GlMultiObject&& b)
+	{
+		*this = std::move(b);
+	}
+
+	~GlMultiObject()
+	{
+		// The destructor of the derived GL object should pass 0 name
+		ANKI_ASSERT(!isCreated());
+	}
+
+	/// Move
+	GlMultiObject& operator=(GlMultiObject&& b)
+	{
+		ANKI_ASSERT(!isCreated());
+		
+		for(U i = 0; i < MAX_OBJECTS; i++)
+		{
+			glId[i] = b.glId[i];
+			b.glId[i] = 0;
+		}
+
+		objectsCount = b.objectsCount;
+		b.objectsCount = 0;
+		return *this;
+	}
+
+	/// Get the GL name
+	GLuint getGlId() const
+	{
+		ANKI_ASSERT(isCreated());
+		return glId[getGlobTimestamp() % MAX_OBJECTS];
+	}
+
+	/// GL object is created
+	Bool isCreated() const
+	{
+#if ANKI_DEBUG
+		U mask = 0;
+		for(U i = 0; i < MAX_OBJECTS; i++)
+		{
+			mask <<= 1;
+			mask |= (glId[i] != 0);
+		}
+
+		// If the mask is not zero then make sure that objectsCount is sane
+		ANKI_ASSERT(!(mask != 0 && __builtin_popcount(mask) != objectsCount));
+#endif
+
+		return objectsCount > 0;
+	}
+
+protected:
+	/// OpenGL names
+	Array<GLuint, MAX_OBJECTS> glId = {{0, 0, 0}};
+
+	/// The size of the glId array
+	U8 objectsCount = 0;
+};
+
 /// Defines an non sharable GL object. Used to avoid idiotic mistakes and more
 /// specifically using the object from other than contexts
 class GlObjectContextNonSharable: protected GlObject

+ 0 - 30
include/anki/gl/Pbo.h

@@ -1,30 +0,0 @@
-#ifndef ANKI_GL_PBO_H
-#define ANKI_GL_PBO_H
-
-#include "anki/gl/BufferObject.h"
-
-namespace anki {
-
-/// @addtogroup OpenGL
-/// @{
-
-/// Pixel buffer object
-class Pbo: public BufferObject
-{
-public:
-	void create(GLenum target, PtrSize size, void* data)
-	{
-		ANKI_ASSERT(target == GL_PIXEL_PACK_BUFFER 
-			|| target == GL_PIXEL_UNPACK_BUFFER);
-
-		GLenum pboUsage = (target == GL_PIXEL_PACK_BUFFER) 
-			? GL_STREAM_READ : GL_STREAM_DRAW;
-
-		BufferObject::create(target, size, data, pboUsage);
-	}
-};
-/// @}
-
-} // end namespace anki
-
-#endif

+ 0 - 28
include/anki/gl/Ubo.h

@@ -1,28 +0,0 @@
-#ifndef ANKI_GL_UBO_H
-#define ANKI_GL_UBO_H
-
-#include "anki/gl/BufferObject.h"
-#include "anki/util/StdTypes.h"
-
-namespace anki {
-
-/// @addtogroup OpenGL
-/// @{
-	
-/// Uniform buffer object
-class Ubo: public BufferObject
-{
-public:
-	void create(PtrSize size, void* data)
-	{
-		// XXX GL_MAX_UNIFORM_BLOCK_SIZE
-		GLint64 maxBufferSize;
-		glGetInteger64v(GL_MAX_UNIFORM_BLOCK_SIZE, &maxBufferSize);
-		BufferObject::create(GL_UNIFORM_BUFFER, size, data, GL_DYNAMIC_DRAW);
-	}
-};
-/// @}
-
-} // end namespace anki
-
-#endif

+ 0 - 50
include/anki/gl/Vbo.h

@@ -1,50 +0,0 @@
-#ifndef ANKI_GL_VBO_H
-#define ANKI_GL_VBO_H
-
-#include "anki/gl/BufferObject.h"
-
-namespace anki {
-
-/// @addtogroup OpenGL
-/// @{
-
-/// This is a wrapper for Vertex Buffer Objects to prevent us from making
-/// idiotic errors
-class Vbo: public BufferObject
-{
-public:
-	Vbo()
-	{}
-
-	/// Adds an extra check in target_ @see BufferObject::BufferObject
-	Vbo(GLenum target_, PtrSize sizeInBytes_, const void* dataPtr_,
-		GLenum usage_)
-	{
-		create(target_, sizeInBytes_, dataPtr_, usage_);
-	}
-
-	/// Unbinds all VBOs, meaning both GL_ARRAY_BUFFER and
-	/// GL_ELEMENT_ARRAY_BUFFER targets
-	static void unbindAllTargets()
-	{
-		glBindBuffer(GL_ARRAY_BUFFER, 0);
-		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
-	}
-
-	/// The same as BufferObject::create but it only accepts
-	/// GL_ARRAY_BUFFER or GL_ELEMENT_ARRAY_BUFFER in target
-	/// @see BufferObject::create
-	void create(GLenum target, PtrSize sizeInBytes, const void* dataPtr,
-		GLenum usage)
-	{
-		// unacceptable target_
-		ANKI_ASSERT(target == GL_ARRAY_BUFFER
-			|| target == GL_ELEMENT_ARRAY_BUFFER);
-		BufferObject::create(target, sizeInBytes, dataPtr, usage);
-	}
-};
-/// @}
-
-} // end namespace
-
-#endif

+ 1 - 1
include/anki/renderer/DebugDrawer.h

@@ -2,7 +2,7 @@
 #define ANKI_RENDERER_DEBUG_DRAWER_H
 
 #include "anki/Math.h"
-#include "anki/gl/Vbo.h"
+#include "anki/gl/BufferObject.h"
 #include "anki/gl/Vao.h"
 #include "anki/resource/Resource.h"
 #include "anki/collision/CollisionShape.h"

+ 1 - 1
include/anki/renderer/Hdr.h

@@ -3,11 +3,11 @@
 
 #include "anki/renderer/RenderingPass.h"
 #include "anki/gl/Fbo.h"
+#include "anki/gl/BufferObject.h"
 #include "anki/resource/TextureResource.h"
 #include "anki/resource/ShaderProgramResource.h"
 #include "anki/resource/Resource.h"
 #include "anki/core/Timestamp.h"
-#include "anki/gl/Ubo.h"
 
 namespace anki {
 

+ 1 - 1
include/anki/renderer/Ssao.h

@@ -7,7 +7,7 @@
 #include "anki/resource/Resource.h"
 #include "anki/gl/Fbo.h"
 #include "anki/gl/Texture.h"
-#include "anki/gl/Ubo.h"
+#include "anki/gl/BufferObject.h"
 #include "anki/core/Timestamp.h"
 
 namespace anki {

+ 1 - 1
include/anki/resource/Mesh.h

@@ -2,7 +2,7 @@
 #define ANKI_RESOURCE_MESH_H
 
 #include "anki/Math.h"
-#include "anki/gl/Vbo.h"
+#include "anki/gl/BufferObject.h"
 #include "anki/collision/Obb.h"
 #include "anki/util/Vector.h"
 

+ 1 - 1
include/anki/scene/Renderable.h

@@ -3,7 +3,7 @@
 
 #include "anki/scene/Property.h"
 #include "anki/scene/Common.h"
-#include "anki/gl/Ubo.h"
+#include "anki/gl/BufferObject.h"
 #include "anki/resource/Material.h"
 #include "anki/resource/Model.h"
 #include <mutex>

+ 55 - 15
src/gl/BufferObject.cpp

@@ -4,10 +4,22 @@
 #include "anki/util/Exception.h"
 #include "anki/core/Logger.h"
 
+namespace anki {
+
+//==============================================================================
+// Misc                                                                        =
+//==============================================================================
+
+//==============================================================================
+
 /// Instead of map/unmap use glBufferSubData() when writing to the whole buffer
 #define USE_BUFFER_DATA_ON_WRITE 1
 
-namespace anki {
+#define GL_ID glId[getGlobTimestamp() % objectsCount]
+
+//==============================================================================
+// BufferObject                                                                =
+//==============================================================================
 
 //==============================================================================
 BufferObject& BufferObject::operator=(BufferObject&& b)
@@ -29,41 +41,49 @@ void BufferObject::destroy()
 	if(isCreated())
 	{
 		unbind();
-		glDeleteBuffers(1, &glId);
-		glId = 0;
+		glDeleteBuffers(objectsCount, &glId[0]);
+		memset(&glId[0], sizeof(Base::glId), 0);
+		objectsCount = 0;
 	}
 }
 
 //==============================================================================
 void BufferObject::create(GLenum target_, U32 sizeInBytes_,
-	const void* dataPtr, GLenum usage_)
+	const void* dataPtr, GLenum usage_, U objectsCount_)
 {
 	ANKI_ASSERT(!isCreated());
 
 	usage = usage_;
 	target = target_;
 	sizeInBytes = sizeInBytes_;
+	objectsCount = objectsCount_;
 
 	ANKI_ASSERT(sizeInBytes > 0 && "Unacceptable sizeInBytes");
 	ANKI_ASSERT(!(target == GL_UNIFORM_BUFFER && usage != GL_DYNAMIC_DRAW)
 		&& "Don't use UBOs like that");
+	ANKI_ASSERT(objectsCount > 0 && objectsCount < MAX_OBJECTS);
+	ANKI_ASSERT(!(objectsCount > 1 && dataPtr != nullptr)
+		&& "Multibuffering with data is not making sence");
 
 	// Create
-	glGenBuffers(1, &glId);
-	ANKI_ASSERT(glId != 0);
-	bind();
-	glBufferData(target, sizeInBytes, dataPtr, usage);
+	glGenBuffers(objectsCount, &glId[0]);
 
-	// make a check
-	GLint bufferSize = 0;
-	glGetBufferParameteriv(target, GL_BUFFER_SIZE, &bufferSize);
-	if(sizeInBytes != (U32)bufferSize)
+	for(U i = 0; i < objectsCount; i++)
 	{
-		destroy();
-		throw ANKI_EXCEPTION("Data size mismatch");
+		glBindBuffer(target, glId[i]);
+		glBufferData(target, sizeInBytes, dataPtr, usage);
+
+		// make a check
+		GLint bufferSize = 0;
+		glGetBufferParameteriv(target, GL_BUFFER_SIZE, &bufferSize);
+		if(sizeInBytes != (U32)bufferSize)
+		{
+			destroy();
+			throw ANKI_EXCEPTION("Data size mismatch");
+		}
 	}
 
-	unbind();
+	glBindBuffer(target, 0);
 	ANKI_CHECK_GL_ERROR();
 }
 
@@ -140,4 +160,24 @@ void BufferObject::read(void* outBuff, U32 offset, U32 size)
 	unmap();
 }
 
+//==============================================================================
+void BufferObject::setBinding(GLuint binding) const
+{
+	ANKI_ASSERT(isCreated());
+	glBindBufferBase(target, binding, GL_ID);
+	ANKI_CHECK_GL_ERROR();
+}
+
+//==============================================================================
+void BufferObject::setBindingRange(
+	GLuint binding, PtrSize offset, PtrSize size) const
+{
+	ANKI_ASSERT(isCreated());
+	ANKI_ASSERT(offset + size <= sizeInBytes);
+	ANKI_ASSERT(size > 0);
+
+	glBindBufferRange(target, binding, GL_ID, offset, size);
+	ANKI_CHECK_GL_ERROR();
+}
+
 } // end namespace anki

+ 1 - 1
src/gl/Vao.cpp

@@ -1,5 +1,5 @@
 #include "anki/gl/Vao.h"
-#include "anki/gl/Vbo.h"
+#include "anki/gl/BufferObject.h"
 #include "anki/util/Exception.h"
 #include "anki/gl/ShaderProgram.h"
 #include "anki/core/Logger.h"

+ 1 - 1
src/renderer/Drawer.cpp

@@ -30,7 +30,7 @@ static U64 countVerts(U32* indicesCount, I primCount)
 //==============================================================================
 /// Visitor that sets a uniform
 /// Align it because the clientBlock will store SIMD data
-ANKI_ATTRIBUTE_ALIGN(struct, 16) SetupRenderableVariableVisitor
+ANKI_ATTRIBUTE_ALIGNED(struct, 16) SetupRenderableVariableVisitor
 {
 	Array<U8, UNIFORM_BLOCK_MAX_SIZE> clientBlock;
 	const Frustumable* fr = nullptr;

+ 0 - 1
src/resource/Mesh.cpp

@@ -1,7 +1,6 @@
 #include "anki/resource/Mesh.h"
 #include "anki/resource/Material.h"
 #include "anki/resource/MeshLoader.h"
-#include "anki/gl/Vbo.h"
 #include "anki/util/Functions.h"
 #include "anki/misc/Xml.h"
 

+ 1 - 1
src/scene/Renderable.cpp

@@ -118,7 +118,7 @@ void Renderable::init(PropertyMap& pmap)
 
 	if(block)
 	{
-		ubo.create(block->getSize(), nullptr);
+		ubo.create(block->getSize(), nullptr, GlMultiObject::DOUBLE_OBJECT);
 	}
 
 	// Instancing sanity checks

+ 1 - 1
testapp/Main.cpp

@@ -474,7 +474,7 @@ void mainLoop()
 
 		// Sleep
 		//
-#if 1
+#if 0
 		timer.stop();
 		if(timer.getElapsedTime() < AppSingleton::get().getTimerTick())
 		{