Slightly improved performance and cleaned up code a bit for love.graphics.print

This commit is contained in:
Alex Szpakowski
2013-06-26 20:21:08 -03:00
parent b04a2fee87
commit 8b261abfdc
3 changed files with 82 additions and 94 deletions
+5 -1
View File
@@ -41,7 +41,9 @@ namespace opengl
class Canvas : public DrawGable, public Volatile class Canvas : public DrawGable, public Volatile
{ {
public: public:
enum TextureType {
enum TextureType
{
TYPE_NORMAL, TYPE_NORMAL,
TYPE_HDR, TYPE_HDR,
TYPE_MAX_ENUM TYPE_MAX_ENUM
@@ -103,6 +105,7 @@ void setFilter(const Image::Filter &f);
static bool isSupported(); static bool isSupported();
static bool isHDRSupported(); static bool isHDRSupported();
static bool isMultiCanvasSupported(); static bool isMultiCanvasSupported();
static bool getConstant(const char *in, TextureType &out); static bool getConstant(const char *in, TextureType &out);
static bool getConstant(TextureType in, const char *&out); static bool getConstant(TextureType in, const char *&out);
@@ -115,6 +118,7 @@ void setFilter(const Image::Filter &f);
} }
private: private:
friend class Shader; friend class Shader;
GLsizei width; GLsizei width;
+68 -80
View File
@@ -57,8 +57,8 @@ Font::Font(love::font::Rasterizer *r, const Image::Filter &filter)
for (int i = 0; i < NUM_TEXTURE_SIZES; i++) for (int i = 0; i < NUM_TEXTURE_SIZES; i++)
{ {
// Make a rough estimate of the total used texture size, based on glyph // Make a rough estimate of the total used texture size, based on glyph
// height. THe estimated size is likely larger than the actual total // height. The estimated size is likely larger than the actual total
// size, which is good because texture changes are expensive. // size, which is good because texture switching is expensive.
if ((height * 0.8) * height * 95 <= TEXTURE_WIDTHS[i] * TEXTURE_HEIGHTS[i]) if ((height * 0.8) * height * 95 <= TEXTURE_WIDTHS[i] * TEXTURE_HEIGHTS[i])
{ {
textureSizeIndex = i; textureSizeIndex = i;
@@ -133,7 +133,7 @@ void Font::createTexture()
GLint format = (type == FONT_TRUETYPE ? GL_LUMINANCE_ALPHA : GL_RGBA); GLint format = (type == FONT_TRUETYPE ? GL_LUMINANCE_ALPHA : GL_RGBA);
// try to initialize the texture, attempting smaller sizes if initialization fails // Initialize the texture, attempting smaller sizes if initialization fails.
bool initialized = false; bool initialized = false;
while (textureSizeIndex >= 0) while (textureSizeIndex >= 0)
{ {
@@ -150,7 +150,7 @@ void Font::createTexture()
if (!initialized) if (!initialized)
{ {
// cleanup before throwing // Clean up before throwing.
gl.deleteTexture(t); gl.deleteTexture(t);
gl.bindTexture(0); gl.bindTexture(0);
textures.pop_back(); textures.pop_back();
@@ -158,7 +158,7 @@ void Font::createTexture()
throw love::Exception("Could not create font texture!"); throw love::Exception("Could not create font texture!");
} }
// Fill the texture with transparent black // Fill the texture with transparent black.
std::vector<GLubyte> emptyData(textureWidth * textureHeight * (type == FONT_TRUETYPE ? 2 : 4), 0); std::vector<GLubyte> emptyData(textureWidth * textureHeight * (type == FONT_TRUETYPE ? 2 : 4), 0);
glTexSubImage2D(GL_TEXTURE_2D, glTexSubImage2D(GL_TEXTURE_2D,
0, 0,
@@ -196,7 +196,7 @@ Font::Glyph *Font::addGlyph(uint32 glyph)
g->texture = 0; g->texture = 0;
g->spacing = gd->getAdvance(); g->spacing = gd->getAdvance();
memset(&g->quad, 0, sizeof(GlyphQuad)); memset(g->vertices, 0, sizeof(vertex) * 4);
// don't waste space for empty glyphs. also fixes a division by zero bug with ati drivers // don't waste space for empty glyphs. also fixes a division by zero bug with ati drivers
if (w > 0 && h > 0) if (w > 0 && h > 0)
@@ -225,9 +225,9 @@ Font::Glyph *Font::addGlyph(uint32 glyph)
// copy vertex data to the glyph and set proper bearing // copy vertex data to the glyph and set proper bearing
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
{ {
g->quad.vertices[i] = verts[i]; g->vertices[i] = verts[i];
g->quad.vertices[i].x += gd->getBearingX(); g->vertices[i].x += gd->getBearingX();
g->quad.vertices[i].y -= gd->getBearingY(); g->vertices[i].y -= gd->getBearingY();
} }
} }
@@ -245,11 +245,12 @@ Font::Glyph *Font::addGlyph(uint32 glyph)
Font::Glyph *Font::findGlyph(uint32 glyph) Font::Glyph *Font::findGlyph(uint32 glyph)
{ {
Glyph *g = glyphs[glyph]; std::map<uint32, Glyph *>::const_iterator it = glyphs.find(glyph);
if (!g)
g = addGlyph(glyph);
return g; if (it != glyphs.end())
return it->second;
else
return addGlyph(glyph);
} }
float Font::getHeight() const float Font::getHeight() const
@@ -259,22 +260,20 @@ float Font::getHeight() const
void Font::print(const std::string &text, float x, float y, float letter_spacing, float angle, float sx, float sy, float ox, float oy, float kx, float ky) void Font::print(const std::string &text, float x, float y, float letter_spacing, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
{ {
float dx = 0.0f; // spacing counter for newline handling // Spacing counter and newline handling.
float dx = 0.0f;
float dy = 0.0f; float dy = 0.0f;
// keeps track of when we need to switch textures in our vertex array float lineheight = getBaseline();
// Keeps track of when we need to switch textures in our vertex array.
std::vector<GlyphArrayDrawInfo> glyphinfolist; std::vector<GlyphArrayDrawInfo> glyphinfolist;
std::vector<GlyphQuad> glyphquads; // Pre-allocate space for the maximum possible number of vertices.
glyphquads.reserve(text.size()); // pre-allocate space for the maximum possible number of quads std::vector<vertex> glyphverts;
glyphverts.reserve(text.length() * 4);
int quadindex = 0; int vertexcount = 0;
glPushMatrix();
Matrix t;
t.setTransformation(ceil(x), ceil(y), angle, sx, sy, ox, oy, kx, ky);
glMultMatrixf((const GLfloat *)t.getElements());
try try
{ {
@@ -287,90 +286,79 @@ void Font::print(const std::string &text, float x, float y, float letter_spacing
if (g == '\n') if (g == '\n')
{ {
// wrap newline, but do not print it // Wrap newline, but do not print it.
dy += floor(getHeight() * getLineHeight() + 0.5f); dy += floorf(getHeight() * getLineHeight() + 0.5f);
dx = 0.0f; dx = 0.0f;
continue; continue;
} }
Glyph *glyph = findGlyph(g); Glyph *glyph = findGlyph(g);
// we only care about the vertices of glyphs which have a texture
if (glyph->texture != 0) if (glyph->texture != 0)
{ {
// copy glyphquad (4 vertices) from original glyph to our current quad list // Copy the vertices and set their proper relative positions.
glyphquads.push_back(glyph->quad); for (int j = 0; j < 4; j++)
float lineheight = getBaseline();
// set proper relative position
for (int i = 0; i < 4; i++)
{ {
glyphquads[quadindex].vertices[i].x += dx; glyphverts.push_back(glyph->vertices[j]);
glyphquads[quadindex].vertices[i].y += dy + lineheight; glyphverts.back().x += dx;
glyphverts.back().y += dy + lineheight;
} }
size_t listsize = glyphinfolist.size(); // Check if glyph texture has changed since the last iteration.
if (glyphinfolist.size() == 0 || glyphinfolist.back().texture != glyph->texture)
// check if current glyph texture has changed since the previous iteration
if (listsize == 0 || glyphinfolist[listsize-1].texture != glyph->texture)
{ {
// keep track of each sub-section of the string whose glyphs use different textures than the previous section // keep track of each sub-section of the string whose glyphs use different textures than the previous section
GlyphArrayDrawInfo glyphdrawinfo; GlyphArrayDrawInfo gdrawinfo;
glyphdrawinfo.startquad = quadindex; gdrawinfo.startvertex = vertexcount;
glyphdrawinfo.numquads = 0; gdrawinfo.vertexcount = 0;
glyphdrawinfo.texture = glyph->texture; gdrawinfo.texture = glyph->texture;
glyphinfolist.push_back(glyphdrawinfo); glyphinfolist.push_back(gdrawinfo);
} }
++quadindex; vertexcount += 4;
++glyphinfolist[glyphinfolist.size()-1].numquads; glyphinfolist.back().vertexcount += 4;
} }
// advance the x position for the next glyph // Advance the x position for the next glyph.
dx += glyph->spacing + letter_spacing; dx += glyph->spacing + letter_spacing;
} }
} }
catch (love::Exception &)
{
glPopMatrix();
throw;
}
catch (utf8::exception &e) catch (utf8::exception &e)
{ {
glPopMatrix();
throw love::Exception("Decoding error: %s", e.what()); throw love::Exception("Decoding error: %s", e.what());
} }
if (quadindex > 0 && glyphinfolist.size() > 0) if (vertexcount <= 0 || glyphinfolist.size() == 0)
return;
// Sort glyph draw info list by texture first, and quad position in memory
// second (using the struct's < operator).
std::sort(glyphinfolist.begin(), glyphinfolist.end());
glPushMatrix();
Matrix t;
t.setTransformation(ceilf(x), ceilf(y), angle, sx, sy, ox, oy, kx, ky);
glMultMatrixf((const GLfloat *)t.getElements());
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid *)&glyphverts[0].x);
glTexCoordPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid *)&glyphverts[0].s);
// We need to draw a new vertex array for every section of the string which
// uses a different texture than the previous section.
std::vector<GlyphArrayDrawInfo>::const_iterator it;
for (it = glyphinfolist.begin(); it != glyphinfolist.end(); ++it)
{ {
// Sort glyph draw info list by texture first, and quad position in gl.bindTexture(it->texture);
// memory second (using the struct's < operator). glDrawArrays(GL_QUADS, it->startvertex, it->vertexcount);
std::sort(glyphinfolist.begin(), glyphinfolist.end());
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid *)&glyphquads[0].vertices[0].x);
glTexCoordPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid *)&glyphquads[0].vertices[0].s);
// We need to draw a new vertex array for every section of the string
// which uses a different texture than the previous section.
std::vector<GlyphArrayDrawInfo>::const_iterator it;
for (it = glyphinfolist.begin(); it != glyphinfolist.end(); ++it)
{
gl.bindTexture(it->texture);
int startvertex = it->startquad * 4;
int numvertices = it->numquads * 4;
glDrawArrays(GL_QUADS, startvertex, numvertices);
}
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
} }
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glPopMatrix(); glPopMatrix();
} }
+9 -13
View File
@@ -149,24 +149,19 @@ private:
FONT_UNKNOWN FONT_UNKNOWN
}; };
// thin wrapper for an array of 4 vertices
struct GlyphQuad
{
vertex vertices[4];
};
struct Glyph struct Glyph
{ {
GLuint texture; GLuint texture;
int spacing; int spacing;
GlyphQuad quad; vertex vertices[4];
}; };
// used to determine when to change textures in the vertex array generated when printing text // used to determine when to change textures in the vertex array generated when printing text
struct GlyphArrayDrawInfo struct GlyphArrayDrawInfo
{ {
GLuint texture; GLuint texture;
int startquad, numquads; int startvertex;
int vertexcount;
// used when sorting with std::sort // used when sorting with std::sort
// sorts by texture first (binding textures is expensive) and relative position in memory second // sorts by texture first (binding textures is expensive) and relative position in memory second
@@ -175,10 +170,15 @@ private:
if (texture != other.texture) if (texture != other.texture)
return texture < other.texture; return texture < other.texture;
else else
return startquad < other.startquad; return startvertex < other.startvertex;
}; };
}; };
bool initializeTexture(GLint format);
void createTexture();
Glyph *addGlyph(uint32 glyph);
Glyph *findGlyph(uint32 glyph);
love::font::Rasterizer *rasterizer; love::font::Rasterizer *rasterizer;
int height; int height;
@@ -207,10 +207,6 @@ private:
int textureX, textureY; int textureX, textureY;
int rowHeight; int rowHeight;
bool initializeTexture(GLint format);
void createTexture();
Glyph *addGlyph(uint32 glyph);
Glyph *findGlyph(uint32 glyph);
}; // Font }; // Font
} // opengl } // opengl