Browse Source

Simplify quad drawing code, and use glDrawElementsBaseVertex when available.

Alex Szpakowski 7 years ago
parent
commit
a31b500b03

+ 0 - 103
src/modules/graphics/Buffer.cpp

@@ -18,12 +18,7 @@
  * 3. This notice may not be removed or altered from any source distribution.
  **/
 
-#include "common/config.h"
 #include "Buffer.h"
-#include "Graphics.h"
-#include "common/Exception.h"
-
-#include <algorithm> // std::min
 
 namespace love
 {
@@ -43,103 +38,5 @@ Buffer::~Buffer()
 {
 }
 
-// QuadIndices
-
-static const int MAX_VERTICES_PER_DRAW = LOVE_UINT16_MAX;
-static const int MAX_QUADS_PER_DRAW    = MAX_VERTICES_PER_DRAW / 4;
-
-size_t QuadIndices::objectCount = 0;
-
-Buffer *QuadIndices::indexBuffer = nullptr;
-
-QuadIndices::QuadIndices(Graphics *gfx)
-{
-	if (indexBuffer == nullptr)
-	{
-		size_t buffersize = sizeof(uint16) * MAX_QUADS_PER_DRAW * 6;
-
-		indexBuffer = gfx->newBuffer(buffersize, nullptr, BUFFER_INDEX, vertex::USAGE_STATIC, 0);
-
-		Buffer::Mapper map(*indexBuffer);
-		vertex::fillIndices(vertex::TriangleIndexMode::QUADS, 0, MAX_VERTICES_PER_DRAW, (uint16 *) map.get());
-	}
-
-	objectCount++;
-}
-
-QuadIndices::QuadIndices(const QuadIndices &)
-{
-	objectCount++;
-}
-
-QuadIndices &QuadIndices::operator = (const QuadIndices &)
-{
-	return *this;
-}
-
-QuadIndices::~QuadIndices()
-{
-	--objectCount;
-
-	// Delete the buffer if we were the last living QuadIndices object.
-	if (objectCount <= 0)
-	{
-		delete indexBuffer;
-		indexBuffer = nullptr;
-	}
-}
-
-static inline void advanceVertexOffsets(const vertex::Attributes &attributes, vertex::Buffers &buffers, int vertexcount)
-{
-	// TODO: Figure out a better way to avoid touching the same buffer multiple
-	// times, if multiple attributes share the buffer.
-	uint32 touchedbuffers = 0;
-
-	for (unsigned int i = 0; i < vertex::Attributes::MAX; i++)
-	{
-		if (!attributes.isEnabled(i))
-			continue;
-
-		auto &attrib = attributes.attribs[i];
-
-		uint32 bufferbit = 1u << attrib.bufferindex;
-		if ((touchedbuffers & bufferbit) == 0)
-		{
-			touchedbuffers |= bufferbit;
-			buffers.info[attrib.bufferindex].offset += attrib.stride * vertexcount;
-		}
-	}
-}
-
-void QuadIndices::draw(Graphics *gfx, int quadstart, int quadcount, const vertex::Attributes &attributes, vertex::Buffers buffers, Texture *texture)
-{
-	Graphics::DrawIndexedCommand cmd(&attributes, &buffers, indexBuffer);
-	cmd.primitiveType = PRIMITIVE_TRIANGLES;
-	cmd.indexBufferOffset = 0;
-	cmd.indexType = INDEX_UINT16;
-	cmd.texture = texture;
-
-	// TODO: We can use glDrawElementsBaseVertex when supported, instead of
-	// advancing the vertex offset.
-	if (quadstart > 0)
-		advanceVertexOffsets(attributes, buffers, quadstart * 4);
-
-	for (int quadindex = 0; quadindex < quadcount; quadindex += MAX_QUADS_PER_DRAW)
-	{
-		int quaddrawcount = std::min(MAX_QUADS_PER_DRAW, quadcount - quadindex);
-
-		if (quadindex > 0)
-			advanceVertexOffsets(attributes, buffers, quaddrawcount * 4);
-
-		cmd.indexCount = quaddrawcount * 6;
-		gfx->draw(cmd);
-	}
-}
-
-Buffer *QuadIndices::getBuffer() const
-{
-	return indexBuffer;
-}
-
 } // graphics
 } // love

+ 0 - 55
src/modules/graphics/Buffer.h

@@ -34,9 +34,6 @@ namespace love
 namespace graphics
 {
 
-class Graphics;
-class Texture;
-
 /**
  * A block of GPU-owned memory. Currently meant for internal use.
  **/
@@ -150,57 +147,5 @@ protected:
 	
 }; // Buffer
 
-/**
- * QuadIndices manages one shared Buffer that stores the indices for an element
- * array. Vertex arrays using the vertex structure (or anything else that can
- * use the pattern below) can request a size and use it for the indexed draw
- * call.
- *
- *  indices[i*6 + 0] = i*4 + 0;
- *  indices[i*6 + 1] = i*4 + 1;
- *  indices[i*6 + 2] = i*4 + 2;
- *
- *  indices[i*6 + 3] = i*4 + 2;
- *  indices[i*6 + 4] = i*4 + 1;
- *  indices[i*6 + 5] = i*4 + 3;
- *
- * There will always be a large enough Buffer around until all QuadIndices
- * instances have been deleted.
- *
- * Q: Why have something like QuadIndices?
- * A: The indices for the SpriteBatch do not change, only the array size varies.
- * Using one Buffer for all element arrays removes this duplicated data and
- * saves some memory.
- */
-class QuadIndices
-{
-public:
-
-	/**
-	 * Adds an entry to the list of sizes and resizes the Buffer
-	 * if needed. A size of 1 allocates a group of 6 indices for 4 vertices
-	 * creating 1 face.
-	 */
-	QuadIndices(Graphics *gfx);
-
-	QuadIndices(const QuadIndices &other);
-	QuadIndices &operator = (const QuadIndices &other);
-
-	/**
-	 * Removes an entry from the list of sizes and resizes the GLBuffer
-	 * if needed.
-	 */
-	~QuadIndices();
-
-	void draw(Graphics *gfx, int quadstart, int quadcount, const vertex::Attributes &attributes, vertex::Buffers buffers, Texture *texture);
-	Buffer *getBuffer() const;
-
-private:
-
-	static size_t objectCount;
-	static Buffer *indexBuffer;
-
-}; // QuadIndices
-
 } // graphics
 } // love

+ 15 - 0
src/modules/graphics/Graphics.cpp

@@ -119,6 +119,7 @@ Graphics::Graphics()
 	, canvasSwitchCount(0)
 	, drawCalls(0)
 	, drawCallsBatched(0)
+	, quadIndexBuffer(nullptr)
 	, capabilities()
 	, cachedShaderStages()
 {
@@ -134,6 +135,8 @@ Graphics::Graphics()
 
 Graphics::~Graphics()
 {
+	delete quadIndexBuffer;
+
 	// Clean up standard shaders before the active shader. If we do it after,
 	// the active shader may try to activate a standard shader when deactivating
 	// itself, which will cause problems since it calls Graphics methods in the
@@ -161,6 +164,18 @@ Graphics::~Graphics()
 	Shader::deinitialize();
 }
 
+void Graphics::createQuadIndexBuffer()
+{
+	if (quadIndexBuffer != nullptr)
+		return;
+
+	size_t size = sizeof(uint16) * (LOVE_UINT16_MAX / 4) * 6;
+	quadIndexBuffer = newBuffer(size, nullptr, BUFFER_INDEX, vertex::USAGE_STATIC, 0);
+
+	Buffer::Mapper map(*quadIndexBuffer);
+	vertex::fillIndices(vertex::TriangleIndexMode::QUADS, 0, LOVE_UINT16_MAX, (uint16 *) map.get());
+}
+
 Quad *Graphics::newQuad(Quad::Viewport v, double sw, double sh)
 {
 	return new Quad(v, sw, sh);

+ 5 - 0
src/modules/graphics/Graphics.h

@@ -851,6 +851,7 @@ public:
 
 	virtual void draw(const DrawCommand &cmd) = 0;
 	virtual void draw(const DrawIndexedCommand &cmd) = 0;
+	virtual void drawQuads(int start, int count, const vertex::Attributes &attributes, const vertex::Buffers &buffers, Texture *texture) = 0;
 
 	void flushStreamDraws();
 	StreamVertexData requestStreamDraw(const StreamDrawCommand &command);
@@ -996,6 +997,8 @@ protected:
 	virtual void initCapabilities() = 0;
 	virtual void getAPIStats(int &shaderswitches) const = 0;
 
+	void createQuadIndexBuffer();
+
 	Canvas *getTemporaryCanvas(PixelFormat format, int w, int h, int samples);
 
 	void restoreState(const DisplayState &s);
@@ -1035,6 +1038,8 @@ protected:
 	int drawCalls;
 	int drawCallsBatched;
 
+	Buffer *quadIndexBuffer;
+
 	Capabilities capabilities;
 
 	Deprecations deprecations;

+ 1 - 5
src/modules/graphics/ParticleSystem.cpp

@@ -95,7 +95,6 @@ ParticleSystem::ParticleSystem(Graphics *gfx, Texture *texture, uint32 size)
 	, relativeRotation(false)
 	, vertexAttributes(vertex::CommonFormat::XYf_STf_RGBAub, 0)
 	, buffer(nullptr)
-	, quadIndices(gfx)
 {
 	if (size == 0 || size > MAX_PARTICLES)
 		throw love::Exception("Invalid ParticleSystem size.");
@@ -157,7 +156,6 @@ ParticleSystem::ParticleSystem(const ParticleSystem &p)
 	, relativeRotation(p.relativeRotation)
 	, vertexAttributes(p.vertexAttributes)
 	, buffer(nullptr)
-	, quadIndices(p.quadIndices)
 {
 	setBufferSize(maxParticles);
 }
@@ -194,8 +192,6 @@ void ParticleSystem::createBuffers(size_t size)
 
 		size_t bytes = sizeof(Vertex) * size * 4;
 		buffer = gfx->newBuffer(bytes, nullptr, BUFFER_VERTEX, vertex::USAGE_STREAM, 0);
-
-		quadIndices = QuadIndices(gfx);
 	}
 	catch (std::bad_alloc &)
 	{
@@ -1101,7 +1097,7 @@ void ParticleSystem::draw(Graphics *gfx, const Matrix4 &m)
 	vertex::Buffers vertexbuffers;
 	vertexbuffers.set(0, buffer, 0);
 
-	quadIndices.draw(gfx, 0, pCount, vertexAttributes, vertexbuffers, texture);
+	gfx->drawQuads(0, pCount, vertexAttributes, vertexbuffers, texture);
 }
 
 bool ParticleSystem::getConstant(const char *in, AreaSpreadDistribution &out)

+ 0 - 3
src/modules/graphics/ParticleSystem.h

@@ -702,9 +702,6 @@ private:
 	const vertex::Attributes vertexAttributes;
 	Buffer *buffer;
 
-	// Vertex index buffer.
-	QuadIndices quadIndices;
-
 	static StringMap<AreaSpreadDistribution, DISTRIBUTION_MAX_ENUM>::Entry distributionsEntries[];
 	static StringMap<AreaSpreadDistribution, DISTRIBUTION_MAX_ENUM> distributions;
 

+ 1 - 2
src/modules/graphics/SpriteBatch.cpp

@@ -47,7 +47,6 @@ SpriteBatch::SpriteBatch(Graphics *gfx, Texture *texture, int size, vertex::Usag
 	, color(255, 255, 255, 255)
 	, color_active(false)
 	, array_buf(nullptr)
-	, quad_indices(gfx)
 	, range_start(-1)
 	, range_count(-1)
 {
@@ -394,7 +393,7 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
 	count = std::min(count, next - start);
 
 	if (count > 0)
-		quad_indices.draw(gfx, start, count, attributes, buffers, texture);
+		gfx->drawQuads(start, count, attributes, buffers, texture);
 }
 
 } // graphics

+ 1 - 2
src/modules/graphics/SpriteBatch.h

@@ -140,8 +140,7 @@ private:
 	size_t vertex_stride;
 	
 	love::graphics::Buffer *array_buf;
-	QuadIndices quad_indices;
-	
+
 	std::unordered_map<std::string, AttachedAttribute> attached_attributes;
 	
 	int range_start;

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

@@ -34,7 +34,6 @@ Text::Text(Graphics *gfx, Font *font, const std::vector<Font::ColoredString> &te
 	: font(font)
 	, vertexAttributes(Font::vertexFormat, 0)
 	, vbo(nullptr)
-	, quadIndices(gfx)
 	, vert_offset(0)
 	, texture_cache_id((uint32) -1)
 {
@@ -264,7 +263,7 @@ void Text::draw(Graphics *gfx, const Matrix4 &m)
 	Graphics::TempTransform transform(gfx, m);
 
 	for (const Font::DrawCommand &cmd : draw_commands)
-		quadIndices.draw(gfx, cmd.startvertex / 4, cmd.vertexcount / 4, vertexAttributes, vertexBuffers, cmd.texture);
+		gfx->drawQuads(cmd.startvertex / 4, cmd.vertexcount / 4, vertexAttributes, vertexBuffers, cmd.texture);
 }
 
 } // graphics

+ 0 - 1
src/modules/graphics/Text.h

@@ -89,7 +89,6 @@ private:
 	vertex::Buffers vertexBuffers;
 
 	Buffer *vbo;
-	QuadIndices quadIndices;
 
 	std::vector<Font::DrawCommand> draw_commands;
 

+ 72 - 11
src/modules/graphics/opengl/Graphics.cpp

@@ -55,8 +55,7 @@ namespace opengl
 {
 
 Graphics::Graphics()
-	: quadIndices(nullptr)
-	, windowHasStencil(false)
+	: windowHasStencil(false)
 	, mainVAO(0)
 {
 	gl = OpenGL();
@@ -87,8 +86,6 @@ Graphics::Graphics()
 
 Graphics::~Graphics()
 {
-	if (quadIndices)
-		delete quadIndices;
 }
 
 const char *Graphics::getName() const
@@ -226,13 +223,7 @@ bool Graphics::setMode(int width, int height, int pixelwidth, int pixelheight, b
 	if (!Volatile::loadAll())
 		::printf("Could not reload all volatile objects.\n");
 
-	// Create a quad indices object owned by love.graphics, so at least one
-	// QuadIndices object is alive at all times while love.graphics is alive.
-	// This makes sure there aren't too many expensive destruction/creations of
-	// index buffer objects, since the shared index buffer used by QuadIndices
-	// objects is destroyed when the last object is destroyed.
-	if (quadIndices == nullptr)
-		quadIndices = new QuadIndices(this);
+	createQuadIndexBuffer();
 
 	// Restore the graphics state.
 	restoreState(states.back());
@@ -354,6 +345,76 @@ void Graphics::draw(const DrawIndexedCommand &cmd)
 	++drawCalls;
 }
 
+static inline void advanceVertexOffsets(const vertex::Attributes &attributes, vertex::Buffers &buffers, int vertexcount)
+{
+	// TODO: Figure out a better way to avoid touching the same buffer multiple
+	// times, if multiple attributes share the buffer.
+	uint32 touchedbuffers = 0;
+
+	for (unsigned int i = 0; i < vertex::Attributes::MAX; i++)
+	{
+		if (!attributes.isEnabled(i))
+			continue;
+
+		auto &attrib = attributes.attribs[i];
+
+		uint32 bufferbit = 1u << attrib.bufferindex;
+		if ((touchedbuffers & bufferbit) == 0)
+		{
+			touchedbuffers |= bufferbit;
+			buffers.info[attrib.bufferindex].offset += attrib.stride * vertexcount;
+		}
+	}
+}
+
+void Graphics::drawQuads(int start, int count, const vertex::Attributes &attributes, const vertex::Buffers &buffers, love::graphics::Texture *texture)
+{
+	const int MAX_VERTICES_PER_DRAW = LOVE_UINT16_MAX;
+	const int MAX_QUADS_PER_DRAW    = MAX_VERTICES_PER_DRAW / 4;
+
+	gl.prepareDraw();
+	gl.bindTextureToUnit(texture, 0, false);
+	gl.setCullMode(CULL_NONE);
+
+	gl.bindBuffer(BUFFER_INDEX, quadIndexBuffer->getHandle());
+
+	if (gl.isBaseVertexSupported())
+	{
+		gl.setVertexAttributes(attributes, buffers);
+
+		int basevertex = start;
+
+		for (int quadindex = 0; quadindex < count; quadindex += MAX_QUADS_PER_DRAW)
+		{
+			int quadcount = std::min(MAX_QUADS_PER_DRAW, count - quadindex);
+
+			glDrawElementsBaseVertex(GL_TRIANGLES, quadcount * 6, GL_UNSIGNED_SHORT, BUFFER_OFFSET(0), basevertex);
+			++drawCalls;
+
+			basevertex += quadcount * 4;
+		}
+	}
+	else
+	{
+		vertex::Buffers bufferscopy = buffers;
+		if (start > 0)
+			advanceVertexOffsets(attributes, bufferscopy, start * 4);
+
+		for (int quadindex = 0; quadindex < count; quadindex += MAX_QUADS_PER_DRAW)
+		{
+			gl.setVertexAttributes(attributes, bufferscopy);
+
+			int quadcount = std::min(MAX_QUADS_PER_DRAW, count - quadindex);
+
+			glDrawElements(GL_TRIANGLES, quadcount * 6, GL_UNSIGNED_SHORT, BUFFER_OFFSET(0));
+			++drawCalls;
+
+			if (count > MAX_QUADS_PER_DRAW)
+				advanceVertexOffsets(attributes, bufferscopy, quadcount * 4);
+		}
+	}
+}
+
 static void APIENTRY debugCB(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei /*len*/, const GLchar *msg, const GLvoid* /*usr*/)
 {
 	// Human-readable strings for the debug info.

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

@@ -71,6 +71,7 @@ public:
 
 	void draw(const DrawCommand &cmd) override;
 	void draw(const DrawIndexedCommand &cmd) override;
+	void drawQuads(int start, int count, const vertex::Attributes &attributes, const vertex::Buffers &buffers, Texture *texture) override;
 
 	void clear(OptionalColorf color, OptionalInt stencil, OptionalDouble depth) override;
 	void clear(const std::vector<OptionalColorf> &colors, OptionalInt stencil, OptionalDouble depth) override;
@@ -127,7 +128,6 @@ private:
 	void setDebug(bool enable);
 
 	std::unordered_map<uint32, GLuint> framebufferObjects;
-	QuadIndices *quadIndices;
 	bool windowHasStencil;
 	GLuint mainVAO;
 

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

@@ -95,6 +95,7 @@ OpenGL::OpenGL()
 	: stats()
 	, contextInitialized(false)
 	, pixelShaderHighpSupported(false)
+	, baseVertexSupported(false)
 	, maxAnisotropy(1.0f)
 	, max2DTextureSize(0)
 	, max3DTextureSize(0)
@@ -375,6 +376,32 @@ void OpenGL::initOpenGLFunctions()
 		fp_glCompressedTexSubImage3D = fp_glCompressedTexSubImage3DOES;
 		fp_glFramebufferTexture3D = fp_glFramebufferTexture3DOES;
 	}
+
+	if (!GLAD_VERSION_3_2 && !GLAD_ES_VERSION_3_2 && !GLAD_ARB_draw_elements_base_vertex)
+	{
+		if (GLAD_OES_draw_elements_base_vertex)
+		{
+			fp_glDrawElementsBaseVertex = fp_glDrawElementsBaseVertexOES;
+
+			if (GLAD_ES_VERSION_3_0)
+			{
+				fp_glDrawRangeElementsBaseVertex = fp_glDrawRangeElementsBaseVertexOES;
+				fp_glDrawElementsInstancedBaseVertex = fp_glDrawElementsInstancedBaseVertexOES;
+			}
+
+		}
+		else if (GLAD_EXT_draw_elements_base_vertex)
+		{
+			fp_glDrawElementsBaseVertex = fp_glDrawElementsBaseVertexEXT;
+
+			if (GLAD_ES_VERSION_3_0)
+			{
+				fp_glDrawRangeElementsBaseVertex = fp_glDrawRangeElementsBaseVertexEXT;
+				fp_glDrawElementsInstancedBaseVertex = fp_glDrawElementsInstancedBaseVertexEXT;
+			}
+
+		}
+	}
 }
 
 void OpenGL::initMaxValues()
@@ -389,6 +416,9 @@ void OpenGL::initMaxValues()
 	else
 		pixelShaderHighpSupported = true;
 
+	baseVertexSupported = GLAD_VERSION_3_2 || GLAD_ES_VERSION_3_2 || GLAD_ARB_draw_elements_base_vertex
+		|| GLAD_OES_draw_elements_base_vertex || GLAD_EXT_draw_elements_base_vertex;
+
 	// We'll need this value to clamp anisotropy.
 	if (GLAD_EXT_texture_filter_anisotropic)
 		glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropy);
@@ -1177,6 +1207,11 @@ bool OpenGL::isSamplerLODBiasSupported() const
 	return GLAD_VERSION_1_4;
 }
 
+bool OpenGL::isBaseVertexSupported() const
+{
+	return baseVertexSupported;
+}
+
 int OpenGL::getMax2DTextureSize() const
 {
 	return std::max(max2DTextureSize, 1);

+ 3 - 0
src/modules/graphics/opengl/OpenGL.h

@@ -334,6 +334,7 @@ public:
 	bool isInstancingSupported() const;
 	bool isDepthCompareSampleSupported() const;
 	bool isSamplerLODBiasSupported() const;
+	bool isBaseVertexSupported() const;
 
 	/**
 	 * Returns the maximum supported width or height of a texture.
@@ -413,6 +414,8 @@ private:
 	bool contextInitialized;
 
 	bool pixelShaderHighpSupported;
+	bool baseVertexSupported;
+
 	float maxAnisotropy;
 	float maxLODBias;
 	int max2DTextureSize;