Browse Source

Hopefully work around an nvidia graphics driver bug in OS X.

Some nvidia GPUs in OS X fail to render geometry that uses an OpenGL index buffer and client-side vertex arrays. This affected ParticleSystems and love.graphics.print. The code for those has been changed to use a client-side index array instead of an OpenGL index buffer.
Alex Szpakowski 9 years ago
parent
commit
5460a5b872

+ 16 - 4
src/modules/graphics/opengl/Font.cpp

@@ -617,7 +617,7 @@ std::vector<Font::DrawCommand> Font::generateVerticesFormatted(const ColoredCode
 	return drawcommands;
 }
 
-void Font::drawVertices(const std::vector<DrawCommand> &drawcommands)
+void Font::drawVertices(const std::vector<DrawCommand> &drawcommands, bool bufferedvertices)
 {
 	// Vertex attribute pointers need to be set before calling this function.
 	// This assumes that the attribute pointers are constant for all vertices.
@@ -634,7 +634,12 @@ void Font::drawVertices(const std::vector<DrawCommand> &drawcommands)
 	const GLenum gltype = quadIndices.getType();
 	const size_t elemsize = quadIndices.getElementSize();
 
-	GLBuffer::Bind bind(*quadIndices.getBuffer());
+	// We only get indices from the index buffer if we're also using vertex
+	// buffers, because 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 client-side vertex arrays.
+	if (bufferedvertices)
+		quadIndices.getBuffer()->bind();
 
 	// We need a separate draw call for every section of the text which uses a
 	// different texture than the previous section.
@@ -645,8 +650,15 @@ void Font::drawVertices(const std::vector<DrawCommand> &drawcommands)
 
 		// TODO: Use glDrawElementsBaseVertex when supported?
 		gl.bindTexture(cmd.texture);
-		gl.drawElements(GL_TRIANGLES, count, gltype, quadIndices.getPointer(offset));
+
+		if (bufferedvertices)
+			gl.drawElements(GL_TRIANGLES, count, gltype, quadIndices.getPointer(offset));
+		else
+			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)
@@ -665,7 +677,7 @@ void Font::printv(const Matrix4 &t, const std::vector<DrawCommand> &drawcommands
 
 	gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD | ATTRIBFLAG_COLOR);
 
-	drawVertices(drawcommands);
+	drawVertices(drawcommands, false);
 }
 
 void Font::print(const std::vector<ColoredString> &text, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)

+ 1 - 1
src/modules/graphics/opengl/Font.h

@@ -109,7 +109,7 @@ public:
 
 	std::vector<DrawCommand> generateVerticesFormatted(const ColoredCodepoints &text, float wrap, AlignMode align, std::vector<GlyphVertex> &vertices, TextInfo *info = nullptr);
 
-	void drawVertices(const std::vector<DrawCommand> &drawcommands);
+	void drawVertices(const std::vector<DrawCommand> &drawcommands, bool bufferedvertices);
 
 	static void getCodepointsFromString(const std::string &str, Codepoints &codepoints);
 	static void getCodepointsFromString(const std::vector<ColoredString> &strs, ColoredCodepoints &codepoints);

+ 25 - 10
src/modules/graphics/opengl/GLBuffer.cpp

@@ -244,7 +244,9 @@ void GLBuffer::unload()
 size_t QuadIndices::maxSize = 0;
 size_t QuadIndices::elementSize = 0;
 size_t QuadIndices::objectCount = 0;
+
 GLBuffer *QuadIndices::indexBuffer = nullptr;
+char *QuadIndices::indices = nullptr;
 
 QuadIndices::QuadIndices(size_t size)
 	: size(size)
@@ -259,6 +261,7 @@ QuadIndices::QuadIndices(size_t size)
 	if (indexBuffer == nullptr || size > maxSize)
 	{
 		GLBuffer *newbuffer = nullptr;
+		char *newindices = nullptr;
 
 		// Depending on the size, a switch to int and more memory is needed.
 		GLenum targettype = getType(size);
@@ -271,9 +274,12 @@ QuadIndices::QuadIndices(size_t size)
 		try
 		{
 			newbuffer = new GLBuffer(buffersize, nullptr, GL_ELEMENT_ARRAY_BUFFER, GL_STATIC_DRAW);
+			newindices = new char[buffersize];
 		}
 		catch (std::bad_alloc &)
 		{
+			delete[] newbuffer;
+			delete[] newindices;
 			throw love::Exception("Out of memory.");
 		}
 
@@ -281,6 +287,10 @@ QuadIndices::QuadIndices(size_t size)
 		// The old GLBuffer can now be deleted.
 		delete indexBuffer;
 		indexBuffer = newbuffer;
+
+		delete[] indices;
+		indices = newindices;
+
 		maxSize = size;
 		elementSize = elemsize;
 
@@ -355,13 +365,15 @@ const void *QuadIndices::getPointer(size_t offset) const
 	return indexBuffer->getPointer(offset);
 }
 
+const void *QuadIndices::getIndices(size_t offset) const
+{
+	return indices + offset;
+}
+
 template <typename T>
 void QuadIndices::fill()
 {
-	GLBuffer::Bind bind(*indexBuffer);
-	GLBuffer::Mapper mapper(*indexBuffer);
-
-	T *indices = (T *) mapper.get();
+	T *inds = (T *) indices;
 
 	// 0----2
 	// |  / |
@@ -369,14 +381,17 @@ void QuadIndices::fill()
 	// 1----3
 	for (size_t i = 0; i < maxSize; ++i)
 	{
-		indices[i*6+0] = T(i * 4 + 0);
-		indices[i*6+1] = T(i * 4 + 1);
-		indices[i*6+2] = T(i * 4 + 2);
+		inds[i*6+0] = T(i * 4 + 0);
+		inds[i*6+1] = T(i * 4 + 1);
+		inds[i*6+2] = T(i * 4 + 2);
 
-		indices[i*6+3] = T(i * 4 + 2);
-		indices[i*6+4] = T(i * 4 + 1);
-		indices[i*6+5] = T(i * 4 + 3);
+		inds[i*6+3] = T(i * 4 + 2);
+		inds[i*6+4] = T(i * 4 + 1);
+		inds[i*6+5] = T(i * 4 + 3);
 	}
+
+	GLBuffer::Bind bind(*indexBuffer);
+	indexBuffer->fill(0, indexBuffer->getSize(), indices);
 }
 
 } // opengl

+ 17 - 0
src/modules/graphics/opengl/GLBuffer.h

@@ -384,6 +384,20 @@ public:
 	 */
 	const void *getPointer(size_t offset) const;
 
+	/**
+	 * Returns a direct pointer to the index data.
+	 *
+	 * At least one graphics driver (the one for Kepler nvidia GPUs in OS X)
+	 * fails to render geometry if the vertex data was a direct CPU pointer but
+	 * the index data came from an Index Buffer.
+	 * So the direct pointer to the index buffer should be used instead of the
+	 * index buffer when rendering using client-side vertex arrays.
+	 *
+	 * @param offset An offset in bytes into the index data.
+	 * @return A direct pointer to the index data at the specified offset.
+	 **/
+	const void *getIndices(size_t offset) const;
+
 private:
 
 	/**
@@ -406,6 +420,9 @@ private:
 
 	// The GLBuffer for the element array. Can be null.
 	static GLBuffer *indexBuffer;
+
+	// The array of indices that will also be stored in the index buffer.
+	static char *indices;
 };
 
 } // opengl

+ 8 - 5
src/modules/graphics/opengl/ParticleSystem.cpp

@@ -141,11 +141,14 @@ void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, flo
 	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);
 
-	{
-		GLsizei count = (GLsizei) quadIndices.getIndexCount(pCount);
-		GLBuffer::Bind ibo_bind(*quadIndices.getBuffer());
-		gl.drawElements(GL_TRIANGLES, count, quadIndices.getType(), quadIndices.getPointer(0));
-	}
+	GLsizei count = (GLsizei) quadIndices.getIndexCount(pCount);
+	GLenum gltype = quadIndices.getType();
+
+	// We use a client-side index array instead of an Index Buffers, because
+	// 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
+	// client-side vertex arrays.
+	gl.drawElements(GL_TRIANGLES, count, gltype, quadIndices.getIndices(0));
 }
 
 } // opengl

+ 1 - 1
src/modules/graphics/opengl/Text.cpp

@@ -245,7 +245,7 @@ void Text::draw(float x, float y, float angle, float sx, float sy, float ox, flo
 
 	gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD | ATTRIBFLAG_COLOR);
 
-	font->drawVertices(draw_commands);
+	font->drawVertices(draw_commands, true);
 }
 
 void Text::setFont(Font *f)