Browse Source

Prevent redundant glBindBuffer calls.

--HG--
branch : minor
Alex Szpakowski 8 years ago
parent
commit
d1c5edd792

+ 1 - 0
src/modules/graphics/opengl/Canvas.cpp

@@ -315,6 +315,7 @@ void Canvas::drawv(const Matrix4 &t, const Vertex *v)
 
 
 	gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD);
 	gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD);
 
 
+	gl.bindBuffer(BUFFER_VERTEX, 0);
 	glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &v[0].x);
 	glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &v[0].x);
 	glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &v[0].s);
 	glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &v[0].s);
 
 

+ 3 - 3
src/modules/graphics/opengl/Font.cpp

@@ -663,6 +663,8 @@ void Font::drawVertices(const std::vector<DrawCommand> &drawcommands, bool buffe
 	// with client-side vertex arrays.
 	// with client-side vertex arrays.
 	if (bufferedvertices)
 	if (bufferedvertices)
 		quadIndices.getBuffer()->bind();
 		quadIndices.getBuffer()->bind();
+	else
+		gl.bindBuffer(BUFFER_INDEX, 0);
 
 
 	// We need a separate draw call for every section of the text which uses a
 	// We need a separate draw call for every section of the text which uses a
 	// different texture than the previous section.
 	// different texture than the previous section.
@@ -679,9 +681,6 @@ void Font::drawVertices(const std::vector<DrawCommand> &drawcommands, bool buffe
 		else
 		else
 			gl.drawElements(GL_TRIANGLES, count, gltype, quadIndices.getIndices(offset));
 			gl.drawElements(GL_TRIANGLES, count, gltype, quadIndices.getIndices(offset));
 	}
 	}
-
-	if (bufferedvertices)
-		quadIndices.getBuffer()->unbind();
 }
 }
 
 
 void Font::printv(const Matrix4 &t, const std::vector<DrawCommand> &drawcommands, const std::vector<GlyphVertex> &vertices)
 void Font::printv(const Matrix4 &t, const std::vector<DrawCommand> &drawcommands, const std::vector<GlyphVertex> &vertices)
@@ -694,6 +693,7 @@ void Font::printv(const Matrix4 &t, const std::vector<DrawCommand> &drawcommands
 	OpenGL::TempTransform transform(gl);
 	OpenGL::TempTransform transform(gl);
 	transform.get() *= t;
 	transform.get() *= t;
 
 
+	gl.bindBuffer(BUFFER_VERTEX, 0);
 	glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(GlyphVertex), &vertices[0].x);
 	glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(GlyphVertex), &vertices[0].x);
 	glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_UNSIGNED_SHORT, GL_TRUE, sizeof(GlyphVertex), &vertices[0].s);
 	glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_UNSIGNED_SHORT, GL_TRUE, sizeof(GlyphVertex), &vertices[0].s);
 	glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(GlyphVertex), &vertices[0].color.r);
 	glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(GlyphVertex), &vertices[0].color.r);

+ 19 - 35
src/modules/graphics/opengl/GLBuffer.cpp

@@ -34,11 +34,10 @@ namespace graphics
 namespace opengl
 namespace opengl
 {
 {
 
 
-GLBuffer::GLBuffer(size_t size, const void *data, GLenum target, GLenum usage, uint32 mapflags)
-	: is_bound(false)
-	, is_mapped(false)
+GLBuffer::GLBuffer(size_t size, const void *data, BufferType type, GLenum usage, uint32 mapflags)
+	: is_mapped(false)
 	, size(size)
 	, size(size)
-	, target(target)
+	, type(type)
 	, usage(usage)
 	, usage(usage)
 	, vbo(0)
 	, vbo(0)
 	, memory_map(nullptr)
 	, memory_map(nullptr)
@@ -46,6 +45,8 @@ GLBuffer::GLBuffer(size_t size, const void *data, GLenum target, GLenum usage, u
 	, modified_size(0)
 	, modified_size(0)
 	, map_flags(mapflags)
 	, map_flags(mapflags)
 {
 {
+	target = OpenGL::getGLBufferType(type);
+
 	try
 	try
 	{
 	{
 		memory_map = new char[size];
 		memory_map = new char[size];
@@ -92,15 +93,17 @@ void GLBuffer::unmapStatic(size_t offset, size_t size)
 		return;
 		return;
 
 
 	// Upload the mapped data to the buffer.
 	// Upload the mapped data to the buffer.
-	glBufferSubData(getTarget(), (GLintptr) offset, (GLsizeiptr) size, memory_map + offset);
+	gl.bindBuffer(type, vbo);
+	glBufferSubData(target, (GLintptr) offset, (GLsizeiptr) size, memory_map + offset);
 }
 }
 
 
 void GLBuffer::unmapStream()
 void GLBuffer::unmapStream()
 {
 {
 	// "orphan" current buffer to avoid implicit synchronisation on the GPU:
 	// "orphan" current buffer to avoid implicit synchronisation on the GPU:
 	// http://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-AsynchronousBufferTransfers.pdf
 	// http://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-AsynchronousBufferTransfers.pdf
-	glBufferData(getTarget(), (GLsizeiptr) getSize(), nullptr,    getUsage());
-	glBufferData(getTarget(), (GLsizeiptr) getSize(), memory_map, getUsage());
+	gl.bindBuffer(type, vbo);
+	glBufferData(target, (GLsizeiptr) getSize(), nullptr,    getUsage());
+	glBufferData(target, (GLsizeiptr) getSize(), memory_map, getUsage());
 }
 }
 
 
 void GLBuffer::unmap()
 void GLBuffer::unmap()
@@ -119,14 +122,6 @@ void GLBuffer::unmap()
 		modified_size = getSize();
 		modified_size = getSize();
 	}
 	}
 
 
-	// VBO::bind is a no-op when the VBO is mapped, so we have to make sure it's
-	// bound here.
-	if (!is_bound)
-	{
-		glBindBuffer(getTarget(), vbo);
-		is_bound = true;
-	}
-
 	if (modified_size > 0)
 	if (modified_size > 0)
 	{
 	{
 		switch (getUsage())
 		switch (getUsage())
@@ -173,19 +168,7 @@ void GLBuffer::setMappedRangeModified(size_t offset, size_t modifiedsize)
 
 
 void GLBuffer::bind()
 void GLBuffer::bind()
 {
 {
-	if (!is_mapped)
-	{
-		glBindBuffer(getTarget(), vbo);
-		is_bound = true;
-	}
-}
-
-void GLBuffer::unbind()
-{
-	if (is_bound)
-		glBindBuffer(getTarget(), 0);
-
-	is_bound = false;
+	gl.bindBuffer(getType(), vbo);
 }
 }
 
 
 void GLBuffer::fill(size_t offset, size_t size, const void *data)
 void GLBuffer::fill(size_t offset, size_t size, const void *data)
@@ -195,7 +178,10 @@ void GLBuffer::fill(size_t offset, size_t size, const void *data)
 	if (is_mapped)
 	if (is_mapped)
 		setMappedRangeModified(offset, size);
 		setMappedRangeModified(offset, size);
 	else
 	else
-		glBufferSubData(getTarget(), (GLintptr) offset, (GLsizeiptr) size, data);
+	{
+		gl.bindBuffer(type, vbo);
+		glBufferSubData(target, (GLintptr) offset, (GLsizeiptr) size, data);
+	}
 }
 }
 
 
 const void *GLBuffer::getPointer(size_t offset) const
 const void *GLBuffer::getPointer(size_t offset) const
@@ -217,7 +203,7 @@ bool GLBuffer::load(bool restore)
 {
 {
 	glGenBuffers(1, &vbo);
 	glGenBuffers(1, &vbo);
 
 
-	GLBuffer::Bind bind(*this);
+	bind();
 
 
 	while (glGetError() != GL_NO_ERROR)
 	while (glGetError() != GL_NO_ERROR)
 		/* Clear the error buffer. */;
 		/* Clear the error buffer. */;
@@ -226,7 +212,7 @@ bool GLBuffer::load(bool restore)
 	const GLvoid *src = restore ? memory_map : nullptr;
 	const GLvoid *src = restore ? memory_map : nullptr;
 
 
 	// Note that if 'src' is '0', no data will be copied.
 	// Note that if 'src' is '0', no data will be copied.
-	glBufferData(getTarget(), (GLsizeiptr) getSize(), src, getUsage());
+	glBufferData(target, (GLsizeiptr) getSize(), src, getUsage());
 
 
 	return (glGetError() == GL_NO_ERROR);
 	return (glGetError() == GL_NO_ERROR);
 }
 }
@@ -234,8 +220,7 @@ bool GLBuffer::load(bool restore)
 void GLBuffer::unload()
 void GLBuffer::unload()
 {
 {
 	is_mapped = false;
 	is_mapped = false;
-
-	glDeleteBuffers(1, &vbo);
+	gl.deleteBuffer(vbo);
 	vbo = 0;
 	vbo = 0;
 }
 }
 
 
@@ -274,7 +259,7 @@ QuadIndices::QuadIndices(size_t size)
 		// QuadIndices will propagate the exception and keep the old GLBuffer.
 		// QuadIndices will propagate the exception and keep the old GLBuffer.
 		try
 		try
 		{
 		{
-			newbuffer = new GLBuffer(buffersize, nullptr, GL_ELEMENT_ARRAY_BUFFER, GL_STATIC_DRAW);
+			newbuffer = new GLBuffer(buffersize, nullptr, BUFFER_INDEX, GL_STATIC_DRAW);
 			newindices = new char[buffersize];
 			newindices = new char[buffersize];
 		}
 		}
 		catch (std::bad_alloc &)
 		catch (std::bad_alloc &)
@@ -394,7 +379,6 @@ void QuadIndices::fill()
 		inds[i*6+5] = T(i * 4 + 3);
 		inds[i*6+5] = T(i * 4 + 3);
 	}
 	}
 
 
-	GLBuffer::Bind bind(*indexBuffer);
 	indexBuffer->fill(0, indexBuffer->getSize(), indices);
 	indexBuffer->fill(0, indexBuffer->getSize(), indices);
 }
 }
 
 

+ 7 - 59
src/modules/graphics/opengl/GLBuffer.h

@@ -56,10 +56,10 @@ public:
 	 * Constructor.
 	 * Constructor.
 	 *
 	 *
 	 * @param size The size of the GLBuffer in bytes.
 	 * @param size The size of the GLBuffer in bytes.
-	 * @param target The target GLBuffer object, e.g. GL_ARRAY_BUFFER.
+	 * @param type The type of the buffer object.
 	 * @param usage Usage hint, e.g. GL_DYNAMIC_DRAW.
 	 * @param usage Usage hint, e.g. GL_DYNAMIC_DRAW.
 	 */
 	 */
-	GLBuffer(size_t size, const void *data, GLenum target, GLenum usage, uint32 mapflags = 0);
+	GLBuffer(size_t size, const void *data, BufferType type, GLenum usage, uint32 mapflags = 0);
 
 
 	/**
 	/**
 	 * Destructor.
 	 * Destructor.
@@ -77,13 +77,11 @@ public:
 	}
 	}
 
 
 	/**
 	/**
-	 * Get the target buffer object.
-	 *
-	 * @return The target buffer object, e.g. GL_ARRAY_BUFFER.
+	 * Get the type of the buffer object.
 	 */
 	 */
-	GLenum getTarget() const
+	BufferType getType() const
 	{
 	{
-		return target;
+		return type;
 	}
 	}
 
 
 	/**
 	/**
@@ -96,11 +94,6 @@ public:
 		return usage;
 		return usage;
 	}
 	}
 
 
-	bool isBound() const
-	{
-		return is_bound;
-	}
-
 	bool isMapped() const
 	bool isMapped() const
 	{
 	{
 		return is_mapped;
 		return is_mapped;
@@ -112,8 +105,6 @@ public:
 	 * This can be faster for large changes to the buffer. For smaller
 	 * This can be faster for large changes to the buffer. For smaller
 	 * changes, see fill().
 	 * changes, see fill().
 	 *
 	 *
-	 * The GLBuffer must be bound to use this function.
-	 *
 	 * @return A pointer to memory which represents the buffer.
 	 * @return A pointer to memory which represents the buffer.
 	 */
 	 */
 	void *map();
 	void *map();
@@ -121,8 +112,6 @@ public:
 	/**
 	/**
 	 * Unmap a previously mapped GLBuffer. The buffer must be unmapped
 	 * Unmap a previously mapped GLBuffer. The buffer must be unmapped
 	 * when used to draw elements.
 	 * when used to draw elements.
-	 *
-	 * The GLBuffer must be bound to use this function.
 	 */
 	 */
 	void unmap();
 	void unmap();
 
 
@@ -138,16 +127,9 @@ public:
 	 */
 	 */
 	void bind();
 	void bind();
 
 
-	/**
-	 * Unbind a prevously bound GLBuffer.
-	 */
-	void unbind();
-
 	/**
 	/**
 	 * Fill a portion of the buffer with data and marks the range as modified.
 	 * Fill a portion of the buffer with data and marks the range as modified.
 	 *
 	 *
-	 * The GLBuffer must be bound to use this function.
-	 *
 	 * @param offset The offset in the GLBuffer to store the data.
 	 * @param offset The offset in the GLBuffer to store the data.
 	 * @param size The size of the incoming data.
 	 * @param size The size of the incoming data.
 	 * @param data Pointer to memory to copy data from.
 	 * @param data Pointer to memory to copy data from.
@@ -171,38 +153,6 @@ public:
 	bool loadVolatile() override;
 	bool loadVolatile() override;
 	void unloadVolatile() override;
 	void unloadVolatile() override;
 
 
-	/**
-	 * This helper class can bind a GLBuffer temporarily, and
-	 * automatically un-bind when it's destroyed.
-	 */
-	class Bind
-	{
-	public:
-
-		/**
-		 * Bind a GLBuffer.
-		 */
-		Bind(GLBuffer &buf)
-			: buf(buf)
-		{
-			buf.bind();
-		}
-
-		/**
-		 * Unbinds a GLBuffer.
-		 */
-		~Bind()
-		{
-			buf.unbind();
-		}
-
-	private:
-
-		// GLBuffer to work on.
-		GLBuffer &buf;
-
-	}; // Bind
-
 	class Mapper
 	class Mapper
 	{
 	{
 	public:
 	public:
@@ -253,16 +203,14 @@ private:
 	void unmapStatic(size_t offset, size_t size);
 	void unmapStatic(size_t offset, size_t size);
 	void unmapStream();
 	void unmapStream();
 
 
-	// Whether the buffer is currently bound.
-	bool is_bound;
-
 	// Whether the buffer is currently mapped to main memory.
 	// Whether the buffer is currently mapped to main memory.
 	bool is_mapped;
 	bool is_mapped;
 
 
 	// The size of the buffer, in bytes.
 	// The size of the buffer, in bytes.
 	size_t size;
 	size_t size;
 
 
-	// The target buffer object. (GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER).
+	// The type of the buffer object.
+	BufferType type;
 	GLenum target;
 	GLenum target;
 
 
 	// Usage hint. GL_[DYNAMIC, STATIC, STREAM]_DRAW.
 	// Usage hint. GL_[DYNAMIC, STATIC, STREAM]_DRAW.

+ 2 - 0
src/modules/graphics/opengl/Graphics.cpp

@@ -1288,6 +1288,7 @@ void Graphics::points(const float *coords, const uint8 *colors, size_t numpoints
 
 
 	gl.prepareDraw();
 	gl.prepareDraw();
 	gl.bindTexture(gl.getDefaultTexture());
 	gl.bindTexture(gl.getDefaultTexture());
+	gl.bindBuffer(BUFFER_VERTEX, 0);
 
 
 	uint32 attribflags = ATTRIBFLAG_POS;
 	uint32 attribflags = ATTRIBFLAG_POS;
 	glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, 0, coords);
 	glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, 0, coords);
@@ -1550,6 +1551,7 @@ void Graphics::polygon(DrawMode mode, const float *coords, size_t count)
 
 
 		gl.prepareDraw();
 		gl.prepareDraw();
 		gl.bindTexture(gl.getDefaultTexture());
 		gl.bindTexture(gl.getDefaultTexture());
+		gl.bindBuffer(BUFFER_VERTEX, 0);
 		gl.useVertexAttribArrays(ATTRIBFLAG_POS);
 		gl.useVertexAttribArrays(ATTRIBFLAG_POS);
 		glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, 0, coords);
 		glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, 0, coords);
 		gl.drawArrays(GL_TRIANGLE_FAN, 0, (int)count/2-1); // opengl will close the polygon for us
 		gl.drawArrays(GL_TRIANGLE_FAN, 0, (int)count/2-1); // opengl will close the polygon for us

+ 1 - 0
src/modules/graphics/opengl/Image.cpp

@@ -453,6 +453,7 @@ void Image::drawv(const Matrix4 &t, const Vertex *v)
 
 
 	gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD);
 	gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD);
 
 
+	gl.bindBuffer(BUFFER_VERTEX, 0);
 	glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &v[0].x);
 	glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &v[0].x);
 	glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &v[0].s);
 	glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &v[0].s);
 
 

+ 7 - 27
src/modules/graphics/opengl/Mesh.cpp

@@ -80,7 +80,7 @@ Mesh::Mesh(const std::vector<AttribFormat> &vertexformat, const void *data, size
 	if (vertexCount == 0)
 	if (vertexCount == 0)
 		throw love::Exception("Data size is too small for specified vertex attribute formats.");
 		throw love::Exception("Data size is too small for specified vertex attribute formats.");
 
 
-	vbo = new GLBuffer(datasize, data, GL_ARRAY_BUFFER, getGLBufferUsage(usage), GLBuffer::MAP_EXPLICIT_RANGE_MODIFY);
+	vbo = new GLBuffer(datasize, data, BUFFER_VERTEX, getGLBufferUsage(usage), GLBuffer::MAP_EXPLICIT_RANGE_MODIFY);
 
 
 	vertexScratchBuffer = new char[vertexStride];
 	vertexScratchBuffer = new char[vertexStride];
 }
 }
@@ -106,10 +106,9 @@ Mesh::Mesh(const std::vector<AttribFormat> &vertexformat, int vertexcount, DrawM
 
 
 	size_t buffersize = vertexCount * vertexStride;
 	size_t buffersize = vertexCount * vertexStride;
 
 
-	vbo = new GLBuffer(buffersize, nullptr, GL_ARRAY_BUFFER, getGLBufferUsage(usage), GLBuffer::MAP_EXPLICIT_RANGE_MODIFY);
+	vbo = new GLBuffer(buffersize, nullptr, BUFFER_VERTEX, getGLBufferUsage(usage), GLBuffer::MAP_EXPLICIT_RANGE_MODIFY);
 
 
 	// Initialize the buffer's contents to 0.
 	// Initialize the buffer's contents to 0.
-	GLBuffer::Bind bind(*vbo);
 	memset(vbo->map(), 0, buffersize);
 	memset(vbo->map(), 0, buffersize);
 	vbo->setMappedRangeModified(0, vbo->getSize());
 	vbo->setMappedRangeModified(0, vbo->getSize());
 	vbo->unmap();
 	vbo->unmap();
@@ -192,9 +191,7 @@ void Mesh::setVertex(size_t vertindex, const void *data, size_t datasize)
 	size_t offset = vertindex * vertexStride;
 	size_t offset = vertindex * vertexStride;
 	size_t size = std::min(datasize, vertexStride);
 	size_t size = std::min(datasize, vertexStride);
 
 
-	GLBuffer::Bind bind(*vbo);
 	uint8 *bufferdata = (uint8 *) vbo->map();
 	uint8 *bufferdata = (uint8 *) vbo->map();
-
 	memcpy(bufferdata + offset, data, size);
 	memcpy(bufferdata + offset, data, size);
 
 
 	vbo->setMappedRangeModified(offset, size);
 	vbo->setMappedRangeModified(offset, size);
@@ -209,9 +206,7 @@ size_t Mesh::getVertex(size_t vertindex, void *data, size_t datasize)
 	size_t size = std::min(datasize, vertexStride);
 	size_t size = std::min(datasize, vertexStride);
 
 
 	// We're relying on vbo->map() returning read/write data... ew.
 	// We're relying on vbo->map() returning read/write data... ew.
-	GLBuffer::Bind bind(*vbo);
 	const uint8 *bufferdata = (const uint8 *) vbo->map();
 	const uint8 *bufferdata = (const uint8 *) vbo->map();
-
 	memcpy(data, bufferdata + offset, size);
 	memcpy(data, bufferdata + offset, size);
 
 
 	return size;
 	return size;
@@ -233,9 +228,7 @@ void Mesh::setVertexAttribute(size_t vertindex, int attribindex, const void *dat
 	size_t offset = vertindex * vertexStride + getAttributeOffset(attribindex);
 	size_t offset = vertindex * vertexStride + getAttributeOffset(attribindex);
 	size_t size = std::min(datasize, attributeSizes[attribindex]);
 	size_t size = std::min(datasize, attributeSizes[attribindex]);
 
 
-	GLBuffer::Bind bind(*vbo);
 	uint8 *bufferdata = (uint8 *) vbo->map();
 	uint8 *bufferdata = (uint8 *) vbo->map();
-
 	memcpy(bufferdata + offset, data, size);
 	memcpy(bufferdata + offset, data, size);
 
 
 	vbo->setMappedRangeModified(offset, size);
 	vbo->setMappedRangeModified(offset, size);
@@ -253,9 +246,7 @@ size_t Mesh::getVertexAttribute(size_t vertindex, int attribindex, void *data, s
 	size_t size = std::min(datasize, attributeSizes[attribindex]);
 	size_t size = std::min(datasize, attributeSizes[attribindex]);
 
 
 	// We're relying on vbo->map() returning read/write data... ew.
 	// We're relying on vbo->map() returning read/write data... ew.
-	GLBuffer::Bind bind(*vbo);
 	const uint8 *bufferdata = (const uint8 *) vbo->map();
 	const uint8 *bufferdata = (const uint8 *) vbo->map();
-
 	memcpy(data, bufferdata + offset, size);
 	memcpy(data, bufferdata + offset, size);
 
 
 	return size;
 	return size;
@@ -356,29 +347,21 @@ void Mesh::attachAttribute(const std::string &name, Mesh *mesh)
 
 
 void *Mesh::mapVertexData()
 void *Mesh::mapVertexData()
 {
 {
-	GLBuffer::Bind bind(*vbo);
 	return vbo->map();
 	return vbo->map();
 }
 }
 
 
 void Mesh::unmapVertexData(size_t modifiedoffset, size_t modifiedsize)
 void Mesh::unmapVertexData(size_t modifiedoffset, size_t modifiedsize)
 {
 {
-	GLBuffer::Bind bind(*vbo);
 	vbo->setMappedRangeModified(modifiedoffset, modifiedsize);
 	vbo->setMappedRangeModified(modifiedoffset, modifiedsize);
 	vbo->unmap();
 	vbo->unmap();
 }
 }
 
 
 void Mesh::flush()
 void Mesh::flush()
 {
 {
-	{
-		GLBuffer::Bind vbobind(*vbo);
-		vbo->unmap();
-	}
+	vbo->unmap();
 
 
 	if (ibo != nullptr)
 	if (ibo != nullptr)
-	{
-		GLBuffer::Bind ibobind(*ibo);
 		ibo->unmap();
 		ibo->unmap();
-	}
 }
 }
 
 
 /**
 /**
@@ -414,7 +397,7 @@ void Mesh::setVertexMap(const std::vector<uint32> &map)
 	}
 	}
 
 
 	if (!ibo && size > 0)
 	if (!ibo && size > 0)
-		ibo = new GLBuffer(size, nullptr, GL_ELEMENT_ARRAY_BUFFER, vbo->getUsage());
+		ibo = new GLBuffer(size, nullptr, BUFFER_INDEX, vbo->getUsage());
 
 
 	useIndexBuffer = true;
 	useIndexBuffer = true;
 	elementCount = map.size();
 	elementCount = map.size();
@@ -422,7 +405,6 @@ void Mesh::setVertexMap(const std::vector<uint32> &map)
 	if (!ibo || elementCount == 0)
 	if (!ibo || elementCount == 0)
 		return;
 		return;
 
 
-	GLBuffer::Bind ibobind(*ibo);
 	GLBuffer::Mapper ibomap(*ibo);
 	GLBuffer::Mapper ibomap(*ibo);
 
 
 	// Fill the buffer with the index values from the vector.
 	// Fill the buffer with the index values from the vector.
@@ -467,8 +449,6 @@ bool Mesh::getVertexMap(std::vector<uint32> &map) const
 	if (!ibo || elementCount == 0)
 	if (!ibo || elementCount == 0)
 		return true;
 		return true;
 
 
-	GLBuffer::Bind ibobind(*ibo);
-
 	// We unmap the buffer in Mesh::draw, Mesh::setVertexMap, and Mesh::flush.
 	// We unmap the buffer in Mesh::draw, Mesh::setVertexMap, and Mesh::flush.
 	void *buffer = ibo->map();
 	void *buffer = ibo->map();
 
 
@@ -559,8 +539,8 @@ int Mesh::bindAttributeToShaderInput(int attributeindex, const std::string &inpu
 	if (attriblocation < 0)
 	if (attriblocation < 0)
 		return attriblocation;
 		return attriblocation;
 
 
-	// Needed for unmap and glVertexAttribPointer.
-	GLBuffer::Bind vbobind(*vbo);
+	// Needed for glVertexAttribPointer.
+	vbo->bind();
 
 
 	// Make sure the buffer isn't mapped (sends data to GPU if needed.)
 	// Make sure the buffer isn't mapped (sends data to GPU if needed.)
 	vbo->unmap();
 	vbo->unmap();
@@ -614,7 +594,7 @@ void Mesh::draw(const Matrix4 &m)
 	if (useIndexBuffer && ibo && elementCount > 0)
 	if (useIndexBuffer && ibo && elementCount > 0)
 	{
 	{
 		// Use the custom vertex map (index buffer) to draw the vertices.
 		// Use the custom vertex map (index buffer) to draw the vertices.
-		GLBuffer::Bind ibo_bind(*ibo);
+		ibo->bind();
 
 
 		// Make sure the index buffer isn't mapped (sends data to GPU if needed.)
 		// Make sure the index buffer isn't mapped (sends data to GPU if needed.)
 		ibo->unmap();
 		ibo->unmap();

+ 39 - 0
src/modules/graphics/opengl/OpenGL.cpp

@@ -143,6 +143,12 @@ void OpenGL::setupContext()
 	else
 	else
 		state.framebufferSRGBEnabled = false;
 		state.framebufferSRGBEnabled = false;
 
 
+	for (int i = 0; i < (int) BUFFER_MAX_ENUM; i++)
+	{
+		state.boundBuffers[i] = 0;
+		glBindBuffer(getGLBufferType((BufferType) i), 0);
+	}
+
 	// Initialize multiple texture unit support for shaders.
 	// Initialize multiple texture unit support for shaders.
 	state.boundTextures.clear();
 	state.boundTextures.clear();
 	state.boundTextures.resize(maxTextureUnits, 0);
 	state.boundTextures.resize(maxTextureUnits, 0);
@@ -380,6 +386,39 @@ void OpenGL::prepareDraw()
 	}
 	}
 }
 }
 
 
+GLenum OpenGL::getGLBufferType(BufferType type)
+{
+	switch (type)
+	{
+	case BUFFER_VERTEX:
+		return GL_ARRAY_BUFFER;
+	case BUFFER_INDEX:
+		return GL_ELEMENT_ARRAY_BUFFER;
+	case BUFFER_MAX_ENUM:
+		return GL_ZERO;
+	}
+}
+
+void OpenGL::bindBuffer(BufferType type, GLuint buffer)
+{
+	if (state.boundBuffers[type] != buffer)
+	{
+		glBindBuffer(getGLBufferType(type), buffer);
+		state.boundBuffers[type] = buffer;
+	}
+}
+
+void OpenGL::deleteBuffer(GLuint buffer)
+{
+	glDeleteBuffers(1, &buffer);
+
+	for (int i = 0; i < (int) BUFFER_MAX_ENUM; i++)
+	{
+		if (state.boundBuffers[i] == buffer)
+			state.boundBuffers[i] = 0;
+	}
+}
+
 void OpenGL::drawArrays(GLenum mode, GLint first, GLsizei count)
 void OpenGL::drawArrays(GLenum mode, GLint first, GLsizei count)
 {
 {
 	glDrawArrays(mode, first, count);
 	glDrawArrays(mode, first, count);

+ 24 - 2
src/modules/graphics/opengl/OpenGL.h

@@ -69,6 +69,13 @@ enum VertexAttribFlags
 	ATTRIBFLAG_CONSTANTCOLOR = 1 << ATTRIB_CONSTANTCOLOR
 	ATTRIBFLAG_CONSTANTCOLOR = 1 << ATTRIB_CONSTANTCOLOR
 };
 };
 
 
+enum BufferType
+{
+	BUFFER_VERTEX = 0,
+	BUFFER_INDEX,
+	BUFFER_MAX_ENUM
+};
+
 /**
 /**
  * Thin layer between OpenGL and the rest of the program.
  * Thin layer between OpenGL and the rest of the program.
  * Internally shadows some OpenGL context state for improved efficiency and
  * Internally shadows some OpenGL context state for improved efficiency and
@@ -243,6 +250,18 @@ public:
 	 **/
 	 **/
 	void prepareDraw();
 	void prepareDraw();
 
 
+	/**
+	 * State-tracked glBindBuffer.
+	 * NOTE: This does not account for multiple VAOs being used! Index buffer
+	 * bindings are per-VAO in OpenGL, but this doesn't know about that.
+	 **/
+	void bindBuffer(BufferType type, GLuint buffer);
+
+	/**
+	 * glDeleteBuffers which updates our shadowed state.
+	 **/
+	void deleteBuffer(GLuint buffer);
+
 	/**
 	/**
 	 * glDrawArrays and glDrawElements which increment the draw-call counter by
 	 * glDrawArrays and glDrawElements which increment the draw-call counter by
 	 * themselves.
 	 * themselves.
@@ -396,6 +415,9 @@ public:
 	 **/
 	 **/
 	Vendor getVendor() const;
 	Vendor getVendor() const;
 
 
+	static GLenum getGLBufferType(BufferType type);
+	static GLint getGLWrapMode(Texture::WrapMode wmode);
+
 	static const char *errorString(GLenum errorcode);
 	static const char *errorString(GLenum errorcode);
 
 
 	// Get human-readable strings for debug info.
 	// Get human-readable strings for debug info.
@@ -411,8 +433,6 @@ private:
 	void initMatrices();
 	void initMatrices();
 	void createDefaultTexture();
 	void createDefaultTexture();
 
 
-	GLint getGLWrapMode(Texture::WrapMode wmode);
-
 	bool contextInitialized;
 	bool contextInitialized;
 
 
 	float maxAnisotropy;
 	float maxAnisotropy;
@@ -427,6 +447,8 @@ private:
 	// Tracked OpenGL state.
 	// Tracked OpenGL state.
 	struct
 	struct
 	{
 	{
+		GLuint boundBuffers[BUFFER_MAX_ENUM];
+
 		// Texture unit state (currently bound texture for each texture unit.)
 		// Texture unit state (currently bound texture for each texture unit.)
 		std::vector<GLuint> boundTextures;
 		std::vector<GLuint> boundTextures;
 
 

+ 2 - 0
src/modules/graphics/opengl/ParticleSystem.cpp

@@ -137,6 +137,7 @@ void ParticleSystem::draw(const Matrix4 &m)
 
 
 	gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD | ATTRIBFLAG_COLOR);
 	gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD | ATTRIBFLAG_COLOR);
 
 
+	gl.bindBuffer(BUFFER_VERTEX, 0);
 	glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), &particleVerts[0].r);
 	glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), &particleVerts[0].r);
 	glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &particleVerts[0].x);
 	glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &particleVerts[0].x);
 	glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &particleVerts[0].s);
 	glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &particleVerts[0].s);
@@ -148,6 +149,7 @@ void ParticleSystem::draw(const Matrix4 &m)
 	// at least one graphics driver (the one for Kepler nvidia GPUs in OS X
 	// at least one graphics driver (the one for Kepler nvidia GPUs in OS X
 	// 10.11) fails to render geometry if an index buffer is used with
 	// 10.11) fails to render geometry if an index buffer is used with
 	// client-side vertex arrays.
 	// client-side vertex arrays.
+	gl.bindBuffer(BUFFER_INDEX, 0);
 	gl.drawElements(GL_TRIANGLES, count, gltype, quadIndices.getIndices(0));
 	gl.drawElements(GL_TRIANGLES, count, gltype, quadIndices.getIndices(0));
 }
 }
 
 

+ 4 - 0
src/modules/graphics/opengl/Polyline.cpp

@@ -402,6 +402,7 @@ void Polyline::draw()
 	gl.prepareDraw();
 	gl.prepareDraw();
 
 
 	gl.bindTexture(gl.getDefaultTexture());
 	gl.bindTexture(gl.getDefaultTexture());
+	gl.bindBuffer(BUFFER_VERTEX, 0);
 
 
 	uint32 enabledattribs = ATTRIBFLAG_POS;
 	uint32 enabledattribs = ATTRIBFLAG_POS;
 
 
@@ -426,7 +427,10 @@ void Polyline::draw()
 	// because the vertex array contains both the core line and the overdraw
 	// because the vertex array contains both the core line and the overdraw
 	// vertices.
 	// vertices.
 	if (use_quad_indices)
 	if (use_quad_indices)
+	{
+		gl.bindBuffer(BUFFER_INDEX, 0);
 		gl.drawElements(draw_mode, (int) (total_vertex_count / 4) * 6, GL_UNSIGNED_SHORT, indices);
 		gl.drawElements(draw_mode, (int) (total_vertex_count / 4) * 6, GL_UNSIGNED_SHORT, indices);
+	}
 	else
 	else
 		gl.drawArrays(draw_mode, 0, (int) total_vertex_count);
 		gl.drawArrays(draw_mode, 0, (int) total_vertex_count);
 
 

+ 16 - 29
src/modules/graphics/opengl/SpriteBatch.cpp

@@ -57,7 +57,7 @@ SpriteBatch::SpriteBatch(Texture *texture, int size, Mesh::Usage usage)
 	GLenum gl_usage = Mesh::getGLBufferUsage(usage);
 	GLenum gl_usage = Mesh::getGLBufferUsage(usage);
 	size_t vertex_size = sizeof(Vertex) * 4 * size;
 	size_t vertex_size = sizeof(Vertex) * 4 * size;
 
 
-	array_buf = new GLBuffer(vertex_size, nullptr, GL_ARRAY_BUFFER, gl_usage, GLBuffer::MAP_EXPLICIT_RANGE_MODIFY);
+	array_buf = new GLBuffer(vertex_size, nullptr, BUFFER_VERTEX, gl_usage, GLBuffer::MAP_EXPLICIT_RANGE_MODIFY);
 }
 }
 
 
 SpriteBatch::~SpriteBatch()
 SpriteBatch::~SpriteBatch()
@@ -108,7 +108,6 @@ void SpriteBatch::clear()
 
 
 void SpriteBatch::flush()
 void SpriteBatch::flush()
 {
 {
-	GLBuffer::Bind bind(*array_buf);
 	array_buf->unmap();
 	array_buf->unmap();
 }
 }
 
 
@@ -155,11 +154,7 @@ void SpriteBatch::setBufferSize(int newsize)
 		return;
 		return;
 
 
 	// Map the old GLBuffer to get a pointer to its data.
 	// Map the old GLBuffer to get a pointer to its data.
-	void *old_data = nullptr;
-	{
-		GLBuffer::Bind bind(*array_buf);
-		old_data = array_buf->map();
-	}
+	void *old_data = array_buf->map();
 
 
 	size_t vertex_size = sizeof(Vertex) * 4 * newsize;
 	size_t vertex_size = sizeof(Vertex) * 4 * newsize;
 	GLBuffer *new_array_buf = nullptr;
 	GLBuffer *new_array_buf = nullptr;
@@ -168,9 +163,7 @@ void SpriteBatch::setBufferSize(int newsize)
 
 
 	try
 	try
 	{
 	{
-		new_array_buf = new GLBuffer(vertex_size, nullptr, array_buf->getTarget(), array_buf->getUsage(), array_buf->getMapFlags());
-
-		GLBuffer::Bind bind(*new_array_buf);
+		new_array_buf = new GLBuffer(vertex_size, nullptr, array_buf->getType(), array_buf->getUsage(), array_buf->getMapFlags());
 
 
 		// Copy as much of the old data into the new GLBuffer as can fit.
 		// Copy as much of the old data into the new GLBuffer as can fit.
 		size_t copy_size = sizeof(Vertex) * 4 * new_next;
 		size_t copy_size = sizeof(Vertex) * 4 * new_next;
@@ -263,25 +256,21 @@ void SpriteBatch::draw(const Matrix4 &m)
 
 
 	uint32 enabledattribs = ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD;
 	uint32 enabledattribs = ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD;
 
 
+	// Make sure the VBO isn't mapped when we draw (sends data to GPU if needed.)
+	array_buf->unmap();
+
+	array_buf->bind();
+
+	// Apply per-sprite color, if a color is set.
+	if (color)
 	{
 	{
-		// Scope this bind so it doesn't interfere with the
-		// Mesh::bindAttributeToShaderInput calls below.
-		GLBuffer::Bind array_bind(*array_buf);
-
-		// Make sure the VBO isn't mapped when we draw (sends data to GPU if needed.)
-		array_buf->unmap();
-
-		// Apply per-sprite color, if a color is set.
-		if (color)
-		{
-			enabledattribs |= ATTRIBFLAG_COLOR;
-			glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), array_buf->getPointer(color_offset));
-		}
-
-		glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), array_buf->getPointer(pos_offset));
-		glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), array_buf->getPointer(texel_offset));
+		enabledattribs |= ATTRIBFLAG_COLOR;
+		glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), array_buf->getPointer(color_offset));
 	}
 	}
 
 
+	glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), array_buf->getPointer(pos_offset));
+	glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), array_buf->getPointer(texel_offset));
+
 	for (const auto &it : attached_attributes)
 	for (const auto &it : attached_attributes)
 	{
 	{
 		Mesh *mesh = it.second.mesh.get();
 		Mesh *mesh = it.second.mesh.get();
@@ -309,7 +298,7 @@ void SpriteBatch::draw(const Matrix4 &m)
 
 
 	count = std::min(count, next - start);
 	count = std::min(count, next - start);
 
 
-	GLBuffer::Bind element_bind(*quad_indices.getBuffer());
+	quad_indices.getBuffer()->bind();
 	const void *indices = quad_indices.getPointer(start * quad_indices.getElementSize());
 	const void *indices = quad_indices.getPointer(start * quad_indices.getElementSize());
 
 
 	if (count > 0)
 	if (count > 0)
@@ -327,8 +316,6 @@ void SpriteBatch::addv(const Vertex *v, const Matrix4 &m, int index)
 	if (color)
 	if (color)
 		setColorv(sprite, *color);
 		setColorv(sprite, *color);
 
 
-	GLBuffer::Bind bind(*array_buf);
-
 	// Always keep the VBO mapped when adding data for now (it'll be unmapped
 	// Always keep the VBO mapped when adding data for now (it'll be unmapped
 	// on draw.)
 	// on draw.)
 	array_buf->map();
 	array_buf->map();

+ 2 - 5
src/modules/graphics/opengl/Text.cpp

@@ -58,13 +58,12 @@ void Text::uploadVertices(const std::vector<Font::GlyphVertex> &vertices, size_t
 		if (vbo != nullptr)
 		if (vbo != nullptr)
 			newsize = std::max(size_t(vbo->getSize() * 1.5), newsize);
 			newsize = std::max(size_t(vbo->getSize() * 1.5), newsize);
 
 
-		GLBuffer *new_vbo = new GLBuffer(newsize, nullptr, GL_ARRAY_BUFFER, GL_DYNAMIC_DRAW);
+		GLBuffer *new_vbo = new GLBuffer(newsize, nullptr, BUFFER_VERTEX, GL_DYNAMIC_DRAW);
 
 
 		if (vbo != nullptr)
 		if (vbo != nullptr)
 		{
 		{
 			try
 			try
 			{
 			{
-				GLBuffer::Bind bind(*vbo);
 				vbodata = (uint8 *) vbo->map();
 				vbodata = (uint8 *) vbo->map();
 			}
 			}
 			catch (love::Exception &)
 			catch (love::Exception &)
@@ -73,7 +72,6 @@ void Text::uploadVertices(const std::vector<Font::GlyphVertex> &vertices, size_t
 				throw;
 				throw;
 			}
 			}
 
 
-			GLBuffer::Bind bind(*new_vbo);
 			new_vbo->fill(0, vbo->getSize(), vbodata);
 			new_vbo->fill(0, vbo->getSize(), vbodata);
 		}
 		}
 
 
@@ -83,7 +81,6 @@ void Text::uploadVertices(const std::vector<Font::GlyphVertex> &vertices, size_t
 
 
 	if (vbo != nullptr && datasize > 0)
 	if (vbo != nullptr && datasize > 0)
 	{
 	{
-		GLBuffer::Bind bind(*vbo);
 		vbodata = (uint8 *) vbo->map();
 		vbodata = (uint8 *) vbo->map();
 		memcpy(vbodata + offset, &vertices[0], datasize);
 		memcpy(vbodata + offset, &vertices[0], datasize);
 		// We unmap when we draw, to avoid unnecessary full map()/unmap() calls.
 		// We unmap when we draw, to avoid unnecessary full map()/unmap() calls.
@@ -228,7 +225,7 @@ void Text::draw(const Matrix4 &m)
 	transform.get() *= m;
 	transform.get() *= m;
 
 
 	{
 	{
-		GLBuffer::Bind bind(*vbo);
+		vbo->bind();
 		vbo->unmap(); // Make sure all pending data is flushed to the GPU.
 		vbo->unmap(); // Make sure all pending data is flushed to the GPU.
 
 
 		// Font::drawVertices expects AttribPointer calls to be done already.
 		// Font::drawVertices expects AttribPointer calls to be done already.

+ 1 - 0
src/modules/graphics/opengl/Video.cpp

@@ -134,6 +134,7 @@ void Video::draw(const Matrix4 &m)
 
 
 	gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD);
 	gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD);
 
 
+	gl.bindBuffer(BUFFER_VERTEX, 0);
 	glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &vertices[0].x);
 	glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &vertices[0].x);
 	glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &vertices[0].s);
 	glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &vertices[0].s);