Browse Source

Use automatic batching for love.graphics.print/printf.

This doesn’t actually improve performance at all in most cases, but it does remove some OpenGL-specific code from the Font files, and reduces the amount of duplicated rendering code. This makes it slightly easier to add support for Core Profile OpenGL 3.

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

+ 16 - 1
src/modules/graphics/Graphics.cpp

@@ -53,6 +53,13 @@ void gammaCorrectColor(Colorf &c)
 	}
 	}
 }
 }
 
 
+Colorf gammaCorrectColor(const Colorf &c)
+{
+	Colorf r = c;
+	gammaCorrectColor(r);
+	return r;
+}
+
 void unGammaCorrectColor(Colorf &c)
 void unGammaCorrectColor(Colorf &c)
 {
 {
 	if (isGammaCorrect())
 	if (isGammaCorrect())
@@ -63,6 +70,13 @@ void unGammaCorrectColor(Colorf &c)
 	}
 	}
 }
 }
 
 
+Colorf unGammaCorrectColor(const Colorf &c)
+{
+	Colorf r = c;
+	unGammaCorrectColor(r);
+	return r;
+}
+
 love::Type Graphics::type("graphics", &Module::type);
 love::Type Graphics::type("graphics", &Module::type);
 
 
 Graphics::Graphics()
 Graphics::Graphics()
@@ -92,7 +106,7 @@ Graphics::StreamVertexData Graphics::requestStreamDraw(const StreamDrawRequest &
 	if (req.primitiveMode != state.primitiveMode
 	if (req.primitiveMode != state.primitiveMode
 		|| req.formats[0] != state.formats[0] || req.formats[1] != state.formats[1]
 		|| req.formats[0] != state.formats[0] || req.formats[1] != state.formats[1]
 		|| ((req.indexMode != TriangleIndexMode::NONE) != (state.indexCount > 0))
 		|| ((req.indexMode != TriangleIndexMode::NONE) != (state.indexCount > 0))
-		|| req.texture != state.texture)
+		|| req.texture != state.texture || req.textureHandle != state.textureHandle)
 	{
 	{
 		shouldflush = true;
 		shouldflush = true;
 	}
 	}
@@ -156,6 +170,7 @@ Graphics::StreamVertexData Graphics::requestStreamDraw(const StreamDrawRequest &
 		state.formats[0] = req.formats[0];
 		state.formats[0] = req.formats[0];
 		state.formats[1] = req.formats[1];
 		state.formats[1] = req.formats[1];
 		state.texture = req.texture;
 		state.texture = req.texture;
+		state.textureHandle = req.textureHandle;
 	}
 	}
 
 
 	if (shouldresize)
 	if (shouldresize)

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

@@ -66,6 +66,9 @@ void gammaCorrectColor(Colorf &c);
  **/
  **/
 void unGammaCorrectColor(Colorf &c);
 void unGammaCorrectColor(Colorf &c);
 
 
+Colorf gammaCorrectColor(const Colorf &c);
+Colorf unGammaCorrectColor(const Colorf &c);
+
 class Graphics : public Module
 class Graphics : public Module
 {
 {
 public:
 public:
@@ -236,6 +239,10 @@ public:
 		int vertexCount = 0;
 		int vertexCount = 0;
 		Texture *texture = nullptr;
 		Texture *texture = nullptr;
 
 
+		// FIXME: This is only needed for fonts. We should just change fonts to
+		// use love.graphics Images instead of raw OpenGL textures.
+		ptrdiff_t textureHandle = 0;
+
 		StreamDrawRequest()
 		StreamDrawRequest()
 		{
 		{
 			// VS2013 can't initialize arrays in the above manner...
 			// VS2013 can't initialize arrays in the above manner...
@@ -455,6 +462,7 @@ protected:
 		vertex::PrimitiveMode primitiveMode;
 		vertex::PrimitiveMode primitiveMode;
 		vertex::CommonFormat formats[2];
 		vertex::CommonFormat formats[2];
 		StrongRef<Texture> texture;
 		StrongRef<Texture> texture;
+		ptrdiff_t textureHandle = 0;
 		int vertexCount;
 		int vertexCount;
 		int indexCount;
 		int indexCount;
 	};
 	};

+ 35 - 79
src/modules/graphics/opengl/Font.cpp

@@ -141,6 +141,9 @@ GLenum Font::getTextureFormat(FontType fontType, GLenum *internalformat) const
 
 
 void Font::createTexture()
 void Font::createTexture()
 {
 {
+	auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
+	gfx->flushStreamDraws();
+
 	OpenGL::TempDebugGroup debuggroup("Font create texture");
 	OpenGL::TempDebugGroup debuggroup("Font create texture");
 
 
 	size_t bpp = fontType == FONT_TRUETYPE ? 2 : 4;
 	size_t bpp = fontType == FONT_TRUETYPE ? 2 : 4;
@@ -413,7 +416,7 @@ float Font::getHeight() const
 	return (float) height;
 	return (float) height;
 }
 }
 
 
-std::vector<Font::DrawCommand> Font::generateVertices(const ColoredCodepoints &codepoints, std::vector<GlyphVertex> &vertices, float extra_spacing, Vector offset, TextInfo *info)
+std::vector<Font::DrawCommand> Font::generateVertices(const ColoredCodepoints &codepoints, const Colorf &constantcolor, std::vector<GlyphVertex> &vertices, float extra_spacing, Vector offset, TextInfo *info)
 {
 {
 	// Spacing counter and newline handling.
 	// Spacing counter and newline handling.
 	float dx = offset.x;
 	float dx = offset.x;
@@ -431,9 +434,9 @@ std::vector<Font::DrawCommand> Font::generateVertices(const ColoredCodepoints &c
 
 
 	uint32 prevglyph = 0;
 	uint32 prevglyph = 0;
 
 
-	Color initialcolor(255, 255, 255, 255);
+	Colorf linearconstantcolor = gammaCorrectColor(constantcolor);
 
 
-	Color curcolor = initialcolor;
+	Color curcolor = toColor(constantcolor);
 	int curcolori = -1;
 	int curcolori = -1;
 	int ncolors = (int) codepoints.colors.size();
 	int ncolors = (int) codepoints.colors.size();
 
 
@@ -443,7 +446,11 @@ std::vector<Font::DrawCommand> Font::generateVertices(const ColoredCodepoints &c
 
 
 		if (curcolori + 1 < ncolors && codepoints.colors[curcolori + 1].index == i)
 		if (curcolori + 1 < ncolors && codepoints.colors[curcolori + 1].index == i)
 		{
 		{
-			curcolor = toColor(codepoints.colors[++curcolori].color);
+			Colorf color = gammaCorrectColor(codepoints.colors[++curcolori].color);
+			color *= linearconstantcolor;
+			unGammaCorrectColor(color);
+
+			curcolor = toColor(color);
 		}
 		}
 
 
 		if (g == '\n')
 		if (g == '\n')
@@ -476,7 +483,7 @@ std::vector<Font::DrawCommand> Font::generateVertices(const ColoredCodepoints &c
 			vertices.resize(vertstartsize);
 			vertices.resize(vertstartsize);
 			prevglyph = 0;
 			prevglyph = 0;
 			curcolori = -1;
 			curcolori = -1;
-			curcolor = initialcolor;
+			curcolor = toColor(constantcolor);
 			continue;
 			continue;
 		}
 		}
 
 
@@ -516,7 +523,6 @@ std::vector<Font::DrawCommand> Font::generateVertices(const ColoredCodepoints &c
 			dx = floorf(dx + extra_spacing);
 			dx = floorf(dx + extra_spacing);
 
 
 		prevglyph = g;
 		prevglyph = g;
-
 	}
 	}
 
 
 	const auto drawsort = [](const DrawCommand &a, const DrawCommand &b) -> bool
 	const auto drawsort = [](const DrawCommand &a, const DrawCommand &b) -> bool
@@ -535,21 +541,14 @@ std::vector<Font::DrawCommand> Font::generateVertices(const ColoredCodepoints &c
 
 
 	if (info != nullptr)
 	if (info != nullptr)
 	{
 	{
-		info->width = maxwidth - offset.x;;
+		info->width = maxwidth - offset.x;
 		info->height = (int) dy + (dx > 0.0f ? floorf(getHeight() * getLineHeight() + 0.5f) : 0) - offset.y;
 		info->height = (int) dy + (dx > 0.0f ? floorf(getHeight() * getLineHeight() + 0.5f) : 0) - offset.y;
 	}
 	}
 
 
 	return commands;
 	return commands;
 }
 }
 
 
-std::vector<Font::DrawCommand> Font::generateVertices(const std::string &text, std::vector<GlyphVertex> &vertices, float extra_spacing, Vector offset, TextInfo *info)
-{
-	ColoredCodepoints codepoints;
-	getCodepointsFromString(text, codepoints.cps);
-	return generateVertices(codepoints, vertices, extra_spacing, offset, info);
-}
-
-std::vector<Font::DrawCommand> Font::generateVerticesFormatted(const ColoredCodepoints &text, float wrap, AlignMode align, std::vector<GlyphVertex> &vertices, TextInfo *info)
+std::vector<Font::DrawCommand> Font::generateVerticesFormatted(const ColoredCodepoints &text, const Colorf &constantcolor, float wrap, AlignMode align, std::vector<GlyphVertex> &vertices, TextInfo *info)
 {
 {
 	wrap = std::max(wrap, 0.0f);
 	wrap = std::max(wrap, 0.0f);
 
 
@@ -598,7 +597,7 @@ std::vector<Font::DrawCommand> Font::generateVerticesFormatted(const ColoredCode
 			break;
 			break;
 		}
 		}
 
 
-		std::vector<DrawCommand> newcommands = generateVertices(line, vertices, extraspacing, offset);
+		std::vector<DrawCommand> newcommands = generateVertices(line, constantcolor, vertices, extraspacing, offset);
 
 
 		if (!newcommands.empty())
 		if (!newcommands.empty())
 		{
 		{
@@ -633,98 +632,55 @@ std::vector<Font::DrawCommand> Font::generateVerticesFormatted(const ColoredCode
 	if (cacheid != textureCacheID)
 	if (cacheid != textureCacheID)
 	{
 	{
 		vertices.clear();
 		vertices.clear();
-		drawcommands = generateVerticesFormatted(text, wrap, align, vertices);
+		drawcommands = generateVerticesFormatted(text, constantcolor, wrap, align, vertices);
 	}
 	}
 
 
 	return drawcommands;
 	return drawcommands;
 }
 }
 
 
-void Font::drawVertices(const std::vector<DrawCommand> &drawcommands, bool bufferedvertices)
+void Font::printv(graphics::Graphics *gfx, const Matrix4 &t, const std::vector<DrawCommand> &drawcommands, const std::vector<GlyphVertex> &vertices)
 {
 {
-	// Vertex attribute pointers need to be set before calling this function.
-	// This assumes that the attribute pointers are constant for all vertices.
-
-	int totalverts = 0;
-	for (const DrawCommand &cmd : drawcommands)
-		totalverts = std::max(cmd.startvertex + cmd.vertexcount, totalverts);
-
-	if ((size_t) totalverts / 4 > quadIndices.getSize())
-		quadIndices = QuadIndices((size_t) totalverts / 4);
-
-	gl.prepareDraw();
-
-	const GLenum gltype = quadIndices.getType();
-	const size_t elemsize = quadIndices.getElementSize();
+	if (vertices.empty() || drawcommands.empty())
+		return;
 
 
-	// 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();
-	else
-		gl.bindBuffer(BUFFER_INDEX, 0);
+	Matrix4 m(gfx->getTransform(), t);
 
 
-	// We need a separate draw call for every section of the text which uses a
-	// different texture than the previous section.
 	for (const DrawCommand &cmd : drawcommands)
 	for (const DrawCommand &cmd : drawcommands)
 	{
 	{
-		GLsizei count = (cmd.vertexcount / 4) * 6;
-		size_t offset = (cmd.startvertex / 4) * 6 * elemsize;
+		Graphics::StreamDrawRequest req;
+		req.formats[0] = vertex::CommonFormat::XYf_STus_RGBAub;
+		req.indexMode = vertex::TriangleIndexMode::QUADS;
+		req.vertexCount = cmd.vertexcount;
+		req.textureHandle = cmd.texture;
 
 
-		// TODO: Use glDrawElementsBaseVertex when supported?
-		gl.bindTextureToUnit(cmd.texture, 0, false);
+		Graphics::StreamVertexData data = gfx->requestStreamDraw(req);
+		GlyphVertex *vertexdata = (GlyphVertex *) data.stream[0];
 
 
-		if (bufferedvertices)
-			gl.drawElements(GL_TRIANGLES, count, gltype, quadIndices.getPointer(offset));
-		else
-			gl.drawElements(GL_TRIANGLES, count, gltype, quadIndices.getIndices(offset));
+		memcpy(vertexdata, &vertices[cmd.startvertex], sizeof(GlyphVertex) * cmd.vertexcount);
+		m.transform(vertexdata, &vertices[cmd.startvertex], cmd.vertexcount);
 	}
 	}
 }
 }
 
 
-void Font::printv(const Matrix4 &t, const std::vector<DrawCommand> &drawcommands, const std::vector<GlyphVertex> &vertices)
-{
-	if (vertices.empty() || drawcommands.empty())
-		return;
-
-	auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
-
-	gfx->flushStreamDraws();
-
-	OpenGL::TempDebugGroup debuggroup("Font print");
-
-	Graphics::TempTransform transform(gfx, t);
-
-	gl.bindBuffer(BUFFER_VERTEX, 0);
-	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_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(GlyphVertex), &vertices[0].color.r);
-
-	gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD | ATTRIBFLAG_COLOR);
-
-	drawVertices(drawcommands, false);
-}
-
-void Font::print(const std::vector<ColoredString> &text, const Matrix4 &m)
+void Font::print(graphics::Graphics *gfx, const std::vector<ColoredString> &text, const Matrix4 &m, const Colorf &constantcolor)
 {
 {
 	ColoredCodepoints codepoints;
 	ColoredCodepoints codepoints;
 	getCodepointsFromString(text, codepoints);
 	getCodepointsFromString(text, codepoints);
 
 
 	std::vector<GlyphVertex> vertices;
 	std::vector<GlyphVertex> vertices;
-	std::vector<DrawCommand> drawcommands = generateVertices(codepoints, vertices);
+	std::vector<DrawCommand> drawcommands = generateVertices(codepoints, constantcolor, vertices);
 
 
-	printv(m, drawcommands, vertices);
+	printv(gfx, m, drawcommands, vertices);
 }
 }
 
 
-void Font::printf(const std::vector<ColoredString> &text, float wrap, AlignMode align, const Matrix4 &m)
+void Font::printf(graphics::Graphics *gfx, const std::vector<ColoredString> &text, float wrap, AlignMode align, const Matrix4 &m, const Colorf &constantcolor)
 {
 {
 	ColoredCodepoints codepoints;
 	ColoredCodepoints codepoints;
 	getCodepointsFromString(text, codepoints);
 	getCodepointsFromString(text, codepoints);
 
 
 	std::vector<GlyphVertex> vertices;
 	std::vector<GlyphVertex> vertices;
-	std::vector<DrawCommand> drawcommands = generateVerticesFormatted(codepoints, wrap, align, vertices);
+	std::vector<DrawCommand> drawcommands = generateVerticesFormatted(codepoints, constantcolor, wrap, align, vertices);
 
 
-	printv(m, drawcommands, vertices);
+	printv(gfx, m, drawcommands, vertices);
 }
 }
 
 
 int Font::getWidth(const std::string &str)
 int Font::getWidth(const std::string &str)

+ 13 - 16
src/modules/graphics/opengl/Font.h

@@ -35,6 +35,7 @@
 #include "font/Rasterizer.h"
 #include "font/Rasterizer.h"
 #include "graphics/Texture.h"
 #include "graphics/Texture.h"
 #include "graphics/Volatile.h"
 #include "graphics/Volatile.h"
+#include "graphics/vertex.h"
 #include "GLBuffer.h"
 #include "GLBuffer.h"
 
 
 #include "OpenGL.h"
 #include "OpenGL.h"
@@ -43,6 +44,9 @@ namespace love
 {
 {
 namespace graphics
 namespace graphics
 {
 {
+
+class Graphics;
+
 namespace opengl
 namespace opengl
 {
 {
 
 
@@ -53,6 +57,7 @@ public:
 	static love::Type type;
 	static love::Type type;
 
 
 	typedef std::vector<uint32> Codepoints;
 	typedef std::vector<uint32> Codepoints;
+	typedef vertex::XYf_STus_RGBAub GlyphVertex;
 
 
 	enum AlignMode
 	enum AlignMode
 	{
 	{
@@ -81,13 +86,6 @@ public:
 		std::vector<IndexedColor> colors;
 		std::vector<IndexedColor> colors;
 	};
 	};
 
 
-	struct GlyphVertex
-	{
-		float  x, y;
-		uint16 s, t;
-		Color  color;
-	};
-
 	struct TextInfo
 	struct TextInfo
 	{
 	{
 		int width;
 		int width;
@@ -106,21 +104,20 @@ public:
 
 
 	virtual ~Font();
 	virtual ~Font();
 
 
-	std::vector<DrawCommand> generateVertices(const ColoredCodepoints &codepoints, std::vector<GlyphVertex> &vertices, float extra_spacing = 0.0f, Vector offset = {}, TextInfo *info = nullptr);
-	std::vector<DrawCommand> generateVertices(const std::string &text, std::vector<GlyphVertex> &vertices, float extra_spacing = 0.0f, Vector offset = Vector(), TextInfo *info = nullptr);
-
-	std::vector<DrawCommand> generateVerticesFormatted(const ColoredCodepoints &text, float wrap, AlignMode align, std::vector<GlyphVertex> &vertices, TextInfo *info = nullptr);
+	std::vector<DrawCommand> generateVertices(const ColoredCodepoints &codepoints, const Colorf &constantColor, std::vector<GlyphVertex> &vertices,
+	                                          float extra_spacing = 0.0f, Vector offset = {}, TextInfo *info = nullptr);
 
 
-	void drawVertices(const std::vector<DrawCommand> &drawcommands, bool bufferedvertices);
+	std::vector<DrawCommand> generateVerticesFormatted(const ColoredCodepoints &text, const Colorf &constantColor, float wrap, AlignMode align,
+	                                                  std::vector<GlyphVertex> &vertices, TextInfo *info = nullptr);
 
 
 	static void getCodepointsFromString(const std::string &str, Codepoints &codepoints);
 	static void getCodepointsFromString(const std::string &str, Codepoints &codepoints);
 	static void getCodepointsFromString(const std::vector<ColoredString> &strs, ColoredCodepoints &codepoints);
 	static void getCodepointsFromString(const std::vector<ColoredString> &strs, ColoredCodepoints &codepoints);
 
 
 	/**
 	/**
-	 * Draws the text at the designated position with a transformation applied.
+	 * Draws the specified text.
 	 **/
 	 **/
-	void print(const std::vector<ColoredString> &text, const Matrix4 &m);
-	void printf(const std::vector<ColoredString> &text, float wrap, AlignMode align, const Matrix4 &m);
+	void print(graphics::Graphics *gfx, const std::vector<ColoredString> &text, const Matrix4 &m, const Colorf &constantColor);
+	void printf(graphics::Graphics *gfx, const std::vector<ColoredString> &text, float wrap, AlignMode align, const Matrix4 &m, const Colorf &constantColor);
 
 
 	/**
 	/**
 	 * Returns the height of the font.
 	 * Returns the height of the font.
@@ -218,7 +215,7 @@ private:
 	const Glyph &addGlyph(uint32 glyph);
 	const Glyph &addGlyph(uint32 glyph);
 	const Glyph &findGlyph(uint32 glyph);
 	const Glyph &findGlyph(uint32 glyph);
 	float getKerning(uint32 leftglyph, uint32 rightglyph);
 	float getKerning(uint32 leftglyph, uint32 rightglyph);
-	void printv(const Matrix4 &t, const std::vector<DrawCommand> &drawcommands, const std::vector<GlyphVertex> &vertices);
+	void printv(graphics::Graphics *gfx, const Matrix4 &t, const std::vector<DrawCommand> &drawcommands, const std::vector<GlyphVertex> &vertices);
 
 
 	std::vector<StrongRef<love::font::Rasterizer>> rasterizers;
 	std::vector<StrongRef<love::font::Rasterizer>> rasterizers;
 
 

+ 6 - 3
src/modules/graphics/opengl/Graphics.cpp

@@ -468,7 +468,10 @@ void Graphics::flushStreamDraws()
 
 
 	gl.prepareDraw();
 	gl.prepareDraw();
 
 
-	gl.bindTextureToUnit(sbstate.texture, 0, false);
+	if (sbstate.textureHandle != 0)
+		gl.bindTextureToUnit((GLuint) sbstate.textureHandle, 0, false);
+	else
+		gl.bindTextureToUnit(sbstate.texture, 0, false);
 
 
 	gl.useVertexAttribArrays(attribs);
 	gl.useVertexAttribArrays(attribs);
 
 
@@ -1730,7 +1733,7 @@ void Graphics::print(const std::vector<Font::ColoredString> &str, const Matrix4
 	DisplayState &state = states.back();
 	DisplayState &state = states.back();
 
 
 	if (state.font.get() != nullptr)
 	if (state.font.get() != nullptr)
-		state.font->print(str, m);
+		state.font->print(this, str, m, state.color);
 }
 }
 
 
 void Graphics::printf(const std::vector<Font::ColoredString> &str, float wrap, Font::AlignMode align, const Matrix4 &m)
 void Graphics::printf(const std::vector<Font::ColoredString> &str, float wrap, Font::AlignMode align, const Matrix4 &m)
@@ -1740,7 +1743,7 @@ void Graphics::printf(const std::vector<Font::ColoredString> &str, float wrap, F
 	DisplayState &state = states.back();
 	DisplayState &state = states.back();
 
 
 	if (state.font.get() != nullptr)
 	if (state.font.get() != nullptr)
-		state.font->printf(str, wrap, align, m);
+		state.font->printf(this, str, wrap, align, m, state.color);
 }
 }
 
 
 /**
 /**

+ 31 - 4
src/modules/graphics/opengl/Text.cpp

@@ -36,6 +36,7 @@ love::Type Text::type("Text", &Drawable::type);
 Text::Text(Font *font, const std::vector<Font::ColoredString> &text)
 Text::Text(Font *font, const std::vector<Font::ColoredString> &text)
 	: font(font)
 	: font(font)
 	, vbo(nullptr)
 	, vbo(nullptr)
+	, quadIndices(20)
 	, vert_offset(0)
 	, vert_offset(0)
 	, texture_cache_id((uint32) -1)
 	, texture_cache_id((uint32) -1)
 {
 {
@@ -114,11 +115,13 @@ void Text::addTextData(const TextData &t)
 
 
 	Font::TextInfo text_info;
 	Font::TextInfo text_info;
 
 
+	Colorf constantcolor = Colorf(1.0f, 1.0f, 1.0f, 1.0f);
+
 	// We only have formatted text if the align mode is valid.
 	// We only have formatted text if the align mode is valid.
 	if (t.align == Font::ALIGN_MAX_ENUM)
 	if (t.align == Font::ALIGN_MAX_ENUM)
-		new_commands = font->generateVertices(t.codepoints, vertices, 0.0f, Vector(0.0f, 0.0f), &text_info);
+		new_commands = font->generateVertices(t.codepoints, constantcolor, vertices, 0.0f, Vector(0.0f, 0.0f), &text_info);
 	else
 	else
-		new_commands = font->generateVerticesFormatted(t.codepoints, t.wrap, t.align, vertices, &text_info);
+		new_commands = font->generateVerticesFormatted(t.codepoints, constantcolor, t.wrap, t.align, vertices, &text_info);
 
 
 	if (t.use_matrix)
 	if (t.use_matrix)
 		t.matrix.transform(&vertices[0], &vertices[0], (int) vertices.size());
 		t.matrix.transform(&vertices[0], &vertices[0], (int) vertices.size());
@@ -221,24 +224,48 @@ void Text::draw(Graphics *gfx, const Matrix4 &m)
 	if (font->getTextureCacheID() != texture_cache_id)
 	if (font->getTextureCacheID() != texture_cache_id)
 		regenerateVertices();
 		regenerateVertices();
 
 
+	int totalverts = 0;
+	for (const Font::DrawCommand &cmd : draw_commands)
+		totalverts = std::max(cmd.startvertex + cmd.vertexcount, totalverts);
+
+	if ((size_t) totalverts / 4 > quadIndices.getSize())
+		quadIndices = QuadIndices((size_t) totalverts / 4);
+
 	const size_t pos_offset   = offsetof(Font::GlyphVertex, x);
 	const size_t pos_offset   = offsetof(Font::GlyphVertex, x);
 	const size_t tex_offset   = offsetof(Font::GlyphVertex, s);
 	const size_t tex_offset   = offsetof(Font::GlyphVertex, s);
 	const size_t color_offset = offsetof(Font::GlyphVertex, color.r);
 	const size_t color_offset = offsetof(Font::GlyphVertex, color.r);
 	const size_t stride = sizeof(Font::GlyphVertex);
 	const size_t stride = sizeof(Font::GlyphVertex);
 
 
+	const GLenum gltype = quadIndices.getType();
+	const size_t elemsize = quadIndices.getElementSize();
+
 	Graphics::TempTransform transform(gfx, m);
 	Graphics::TempTransform transform(gfx, m);
 
 
+	gl.prepareDraw();
+
 	vbo->bind();
 	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.
 	glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, stride, vbo->getPointer(pos_offset));
 	glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, stride, vbo->getPointer(pos_offset));
 	glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_UNSIGNED_SHORT, GL_TRUE, stride, vbo->getPointer(tex_offset));
 	glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_UNSIGNED_SHORT, GL_TRUE, stride, vbo->getPointer(tex_offset));
 	glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, vbo->getPointer(color_offset));
 	glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, vbo->getPointer(color_offset));
 
 
 	gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD | ATTRIBFLAG_COLOR);
 	gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD | ATTRIBFLAG_COLOR);
 
 
-	font->drawVertices(draw_commands, true);
+	quadIndices.getBuffer()->bind();
+
+	// We need a separate draw call for every section of the text which uses a
+	// different texture than the previous section.
+	for (const Font::DrawCommand &cmd : draw_commands)
+	{
+		GLsizei count = (cmd.vertexcount / 4) * 6;
+		size_t offset = (cmd.startvertex / 4) * 6 * elemsize;
+
+		// TODO: Use glDrawElementsBaseVertex when supported?
+		gl.bindTextureToUnit(cmd.texture, 0, false);
+
+		gl.drawElements(GL_TRIANGLES, count, gltype, quadIndices.getPointer(offset));
+	}
 }
 }
 
 
 void Text::setFont(Font *f)
 void Text::setFont(Font *f)

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

@@ -86,6 +86,7 @@ private:
 
 
 	StrongRef<Font> font;
 	StrongRef<Font> font;
 	GLBuffer *vbo;
 	GLBuffer *vbo;
+	QuadIndices quadIndices;
 
 
 	std::vector<Font::DrawCommand> draw_commands;
 	std::vector<Font::DrawCommand> draw_commands;
 
 

+ 5 - 9
src/modules/graphics/vertex.h

@@ -39,13 +39,6 @@ enum BufferType
 	BUFFER_MAX_ENUM
 	BUFFER_MAX_ENUM
 };
 };
 
 
-struct Vertex
-{
-	float x, y;
-	float s, t;
-	Color color;
-};
-
 namespace vertex
 namespace vertex
 {
 {
 
 
@@ -88,9 +81,9 @@ struct XYf_STf_RGBAub
 
 
 struct XYf_STus_RGBAub
 struct XYf_STus_RGBAub
 {
 {
-	float x, y;
+	float  x, y;
 	uint16 s, t;
 	uint16 s, t;
-	Color color;
+	Color  color;
 };
 };
 
 
 size_t getFormatStride(CommonFormat format);
 size_t getFormatStride(CommonFormat format);
@@ -101,5 +94,8 @@ void fillIndices(TriangleIndexMode mode, uint16 vertexStart, uint16 vertexCount,
 void fillIndices(TriangleIndexMode mode, uint32 vertexStart, uint32 vertexCount, uint32 *indices);
 void fillIndices(TriangleIndexMode mode, uint32 vertexStart, uint32 vertexCount, uint32 *indices);
 
 
 } // vertex
 } // vertex
+
+typedef vertex::XYf_STf_RGBAub Vertex;
+
 } // graphics
 } // graphics
 } // love
 } // love