Doer fonts fast(er)! (better batching for font glyph rendering)
This commit is contained in:
@@ -268,9 +268,15 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons
|
|||||||
|
|
||||||
const LLFontGlyphInfo* next_glyph = NULL;
|
const LLFontGlyphInfo* next_glyph = NULL;
|
||||||
|
|
||||||
// Remember last-used texture to avoid unnecesssary bind calls.
|
const S32 GLYPH_BATCH_SIZE = 30;
|
||||||
LLImageGL *last_bound_texture = NULL;
|
static LL_ALIGN_16(LLVector4a vertices[GLYPH_BATCH_SIZE * 4]);
|
||||||
|
static LLVector2 uvs[GLYPH_BATCH_SIZE * 4];
|
||||||
|
static LLColor4U colors[GLYPH_BATCH_SIZE * 4];
|
||||||
|
|
||||||
|
LLColor4U text_color(color);
|
||||||
|
|
||||||
|
S32 bitmap_num = -1;
|
||||||
|
S32 glyph_count = 0;
|
||||||
for (i = begin_offset; i < begin_offset + length; i++)
|
for (i = begin_offset; i < begin_offset + length; i++)
|
||||||
{
|
{
|
||||||
llwchar wch = wstr[i];
|
llwchar wch = wstr[i];
|
||||||
@@ -299,11 +305,7 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (last_bound_texture != ext_image)
|
gGL.getTexUnit(0)->bind(ext_image);
|
||||||
{
|
|
||||||
gGL.getTexUnit(0)->bind(ext_image);
|
|
||||||
last_bound_texture = ext_image;
|
|
||||||
}
|
|
||||||
|
|
||||||
// snap origin to whole screen pixel
|
// snap origin to whole screen pixel
|
||||||
const F32 ext_x = (F32)llround(cur_render_x + (EXT_X_BEARING * sScaleX));
|
const F32 ext_x = (F32)llround(cur_render_x + (EXT_X_BEARING * sScaleX));
|
||||||
@@ -311,7 +313,23 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons
|
|||||||
|
|
||||||
LLRectf uv_rect(0.f, 1.f, 1.f, 0.f);
|
LLRectf uv_rect(0.f, 1.f, 1.f, 0.f);
|
||||||
LLRectf screen_rect(ext_x, ext_y + ext_height, ext_x + ext_width, ext_y);
|
LLRectf screen_rect(ext_x, ext_y + ext_height, ext_x + ext_width, ext_y);
|
||||||
drawGlyph(screen_rect, uv_rect, LLColor4::white, style, shadow, drop_shadow_strength);
|
|
||||||
|
if (glyph_count > 0)
|
||||||
|
{
|
||||||
|
gGL.begin(LLRender::QUADS);
|
||||||
|
{
|
||||||
|
gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4);
|
||||||
|
}
|
||||||
|
gGL.end();
|
||||||
|
glyph_count = 0;
|
||||||
|
}
|
||||||
|
renderQuad(vertices, uvs, colors, screen_rect, uv_rect, LLColor4U::white, 0);
|
||||||
|
//No batching here. It will never happen.
|
||||||
|
gGL.begin(LLRender::QUADS);
|
||||||
|
{
|
||||||
|
gGL.vertexBatchPreTransformed(vertices, uvs, colors, 4);
|
||||||
|
}
|
||||||
|
gGL.end();
|
||||||
|
|
||||||
if (!label.empty())
|
if (!label.empty())
|
||||||
{
|
{
|
||||||
@@ -320,13 +338,11 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons
|
|||||||
/*llfloor*/(ext_x / sScaleX) + ext_image->getWidth() + EXT_X_BEARING - sCurOrigin.mX,
|
/*llfloor*/(ext_x / sScaleX) + ext_image->getWidth() + EXT_X_BEARING - sCurOrigin.mX,
|
||||||
/*llfloor*/(cur_render_y / sScaleY) - sCurOrigin.mY,
|
/*llfloor*/(cur_render_y / sScaleY) - sCurOrigin.mY,
|
||||||
color,
|
color,
|
||||||
halign, BASELINE, NORMAL, NO_SHADOW, S32_MAX, S32_MAX, NULL,
|
halign, BASELINE, UNDERLINE, NO_SHADOW, S32_MAX, S32_MAX, NULL,
|
||||||
TRUE );
|
TRUE );
|
||||||
gGL.popMatrix();
|
gGL.popMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
gGL.color4fv(color.mV);
|
|
||||||
|
|
||||||
chars_drawn++;
|
chars_drawn++;
|
||||||
cur_x += ext_advance;
|
cur_x += ext_advance;
|
||||||
if (((i + 1) < length) && wstr[i+1])
|
if (((i + 1) < length) && wstr[i+1])
|
||||||
@@ -349,11 +365,24 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Per-glyph bitmap texture.
|
// Per-glyph bitmap texture.
|
||||||
LLImageGL *image_gl = font_bitmap_cache->getImageGL(fgi->mBitmapNum);
|
S32 next_bitmap_num = fgi->mBitmapNum;
|
||||||
if (last_bound_texture != image_gl)
|
if (next_bitmap_num != bitmap_num)
|
||||||
{
|
{
|
||||||
gGL.getTexUnit(0)->bind(image_gl);
|
// Actually draw the queued glyphs before switching their texture;
|
||||||
last_bound_texture = image_gl;
|
// otherwise the queued glyphs will be taken from wrong textures.
|
||||||
|
if (glyph_count > 0)
|
||||||
|
{
|
||||||
|
gGL.begin(LLRender::QUADS);
|
||||||
|
{
|
||||||
|
gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4);
|
||||||
|
}
|
||||||
|
gGL.end();
|
||||||
|
glyph_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bitmap_num = next_bitmap_num;
|
||||||
|
LLImageGL *font_image = font_bitmap_cache->getImageGL(bitmap_num);
|
||||||
|
gGL.getTexUnit(0)->bind(font_image);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((start_x + scaled_max_pixels) < (cur_x + fgi->mXBearing + fgi->mWidth))
|
if ((start_x + scaled_max_pixels) < (cur_x + fgi->mXBearing + fgi->mWidth))
|
||||||
@@ -368,13 +397,24 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons
|
|||||||
(fgi->mYBitmapOffset + fgi->mHeight + PAD_UVY) * inv_height,
|
(fgi->mYBitmapOffset + fgi->mHeight + PAD_UVY) * inv_height,
|
||||||
(fgi->mXBitmapOffset + fgi->mWidth) * inv_width,
|
(fgi->mXBitmapOffset + fgi->mWidth) * inv_width,
|
||||||
(fgi->mYBitmapOffset - PAD_UVY) * inv_height);
|
(fgi->mYBitmapOffset - PAD_UVY) * inv_height);
|
||||||
// snap glyph origin to whole screen pixel
|
// snap glyph origin to whole screen pixel
|
||||||
LLRectf screen_rect((F32)llround(cur_render_x + (F32)fgi->mXBearing),
|
LLRectf screen_rect((F32)llround(cur_render_x + (F32)fgi->mXBearing),
|
||||||
(F32)llround(cur_render_y + (F32)fgi->mYBearing),
|
(F32)llround(cur_render_y + (F32)fgi->mYBearing),
|
||||||
(F32)llround(cur_render_x + (F32)fgi->mXBearing) + (F32)fgi->mWidth,
|
(F32)llround(cur_render_x + (F32)fgi->mXBearing) + (F32)fgi->mWidth,
|
||||||
(F32)llround(cur_render_y + (F32)fgi->mYBearing) - (F32)fgi->mHeight);
|
(F32)llround(cur_render_y + (F32)fgi->mYBearing) - (F32)fgi->mHeight);
|
||||||
|
|
||||||
drawGlyph(screen_rect, uv_rect, color, style, shadow, drop_shadow_strength);
|
if (glyph_count >= GLYPH_BATCH_SIZE)
|
||||||
|
{
|
||||||
|
gGL.begin(LLRender::QUADS);
|
||||||
|
{
|
||||||
|
gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4);
|
||||||
|
}
|
||||||
|
gGL.end();
|
||||||
|
|
||||||
|
glyph_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
drawGlyph(glyph_count, vertices, uvs, colors, screen_rect, uv_rect, text_color, style, shadow, drop_shadow_strength);
|
||||||
|
|
||||||
chars_drawn++;
|
chars_drawn++;
|
||||||
cur_x += fgi->mXAdvance;
|
cur_x += fgi->mXAdvance;
|
||||||
@@ -400,6 +440,13 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gGL.begin(LLRender::QUADS);
|
||||||
|
{
|
||||||
|
gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4);
|
||||||
|
}
|
||||||
|
gGL.end();
|
||||||
|
|
||||||
|
|
||||||
if (right_x)
|
if (right_x)
|
||||||
{
|
{
|
||||||
*right_x = (cur_x - origin.mV[VX]) / sScaleX;
|
*right_x = (cur_x - origin.mV[VX]) / sScaleX;
|
||||||
@@ -1243,95 +1290,95 @@ LLFontGL &LLFontGL::operator=(const LLFontGL &source)
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLFontGL::renderQuad(const LLRectf& screen_rect, const LLRectf& uv_rect, F32 slant_amt) const
|
void LLFontGL::renderQuad(LLVector4a* vertex_out, LLVector2* uv_out, LLColor4U* colors_out, const LLRectf& screen_rect, const LLRectf& uv_rect, const LLColor4U& color, F32 slant_amt) const
|
||||||
{
|
{
|
||||||
gGL.texCoord2f(uv_rect.mRight, uv_rect.mTop);
|
S32 index = 0;
|
||||||
gGL.vertex2f(llfont_round_x(screen_rect.mRight),
|
|
||||||
llfont_round_y(screen_rect.mTop));
|
|
||||||
|
|
||||||
gGL.texCoord2f(uv_rect.mLeft, uv_rect.mTop);
|
vertex_out[index].set(screen_rect.mRight, screen_rect.mTop, 0.f);
|
||||||
gGL.vertex2f(llfont_round_x(screen_rect.mLeft),
|
uv_out[index] = LLVector2(uv_rect.mRight, uv_rect.mTop);
|
||||||
llfont_round_y(screen_rect.mTop));
|
colors_out[index] = color;
|
||||||
|
index++;
|
||||||
|
|
||||||
gGL.texCoord2f(uv_rect.mLeft, uv_rect.mBottom);
|
vertex_out[index].set(screen_rect.mLeft, screen_rect.mTop, 0.f);
|
||||||
gGL.vertex2f(llfont_round_x(screen_rect.mLeft + slant_amt),
|
uv_out[index] = LLVector2(uv_rect.mLeft, uv_rect.mTop);
|
||||||
llfont_round_y(screen_rect.mBottom));
|
colors_out[index] = color;
|
||||||
|
index++;
|
||||||
|
|
||||||
gGL.texCoord2f(uv_rect.mRight, uv_rect.mBottom);
|
vertex_out[index].set(screen_rect.mLeft, screen_rect.mBottom, 0.f);
|
||||||
gGL.vertex2f(llfont_round_x(screen_rect.mRight + slant_amt),
|
uv_out[index] = LLVector2(uv_rect.mLeft, uv_rect.mBottom);
|
||||||
llfont_round_y(screen_rect.mBottom));
|
colors_out[index] = color;
|
||||||
|
index++;
|
||||||
|
|
||||||
|
vertex_out[index].set(screen_rect.mRight, screen_rect.mBottom, 0.f);
|
||||||
|
uv_out[index] = LLVector2(uv_rect.mRight, uv_rect.mBottom);
|
||||||
|
colors_out[index] = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLFontGL::drawGlyph(const LLRectf& screen_rect, const LLRectf& uv_rect, const LLColor4& color, U8 style, ShadowType shadow, F32 drop_shadow_strength) const
|
void LLFontGL::drawGlyph(S32& glyph_count, LLVector4a* vertex_out, LLVector2* uv_out, LLColor4U* colors_out, const LLRectf& screen_rect, const LLRectf& uv_rect, const LLColor4U& color, U8 style, ShadowType shadow, F32 drop_shadow_strength) const
|
||||||
{
|
{
|
||||||
F32 slant_offset;
|
F32 slant_offset;
|
||||||
slant_offset = ((style & ITALIC) ? ( -mFontFreetype->getAscenderHeight() * 0.2f) : 0.f);
|
slant_offset = ((style & ITALIC) ? ( -mFontFreetype->getAscenderHeight() * 0.2f) : 0.f);
|
||||||
|
|
||||||
gGL.begin(LLRender::QUADS);
|
//FIXME: bold and drop shadow are mutually exclusive only for convenience
|
||||||
|
//Allow both when we need them.
|
||||||
|
if (style & BOLD)
|
||||||
{
|
{
|
||||||
//FIXME: bold and drop shadow are mutually exclusive only for convenience
|
for (S32 pass = 0; pass < 2; pass++)
|
||||||
//Allow both when we need them.
|
|
||||||
if (style & BOLD)
|
|
||||||
{
|
{
|
||||||
gGL.color4fv(color.mV);
|
LLRectf screen_rect_offset = screen_rect;
|
||||||
for (S32 pass = 0; pass < 2; pass++)
|
|
||||||
{
|
|
||||||
LLRectf screen_rect_offset = screen_rect;
|
|
||||||
|
|
||||||
screen_rect_offset.translate((F32)(pass * BOLD_OFFSET), 0.f);
|
screen_rect_offset.translate((F32)(pass * BOLD_OFFSET), 0.f);
|
||||||
renderQuad(screen_rect_offset, uv_rect, slant_offset);
|
renderQuad(&vertex_out[glyph_count * 4], &uv_out[glyph_count * 4], &colors_out[glyph_count * 4], screen_rect_offset, uv_rect, color, slant_offset);
|
||||||
}
|
glyph_count++;
|
||||||
}
|
}
|
||||||
else if (shadow == DROP_SHADOW_SOFT)
|
|
||||||
{
|
|
||||||
LLColor4 shadow_color = LLFontGL::sShadowColor;
|
|
||||||
shadow_color.mV[VALPHA] = color.mV[VALPHA] * drop_shadow_strength * DROP_SHADOW_SOFT_STRENGTH;
|
|
||||||
gGL.color4fv(shadow_color.mV);
|
|
||||||
for (S32 pass = 0; pass < 5; pass++)
|
|
||||||
{
|
|
||||||
LLRectf screen_rect_offset = screen_rect;
|
|
||||||
|
|
||||||
switch(pass)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
screen_rect_offset.translate(-1.f, -1.f);
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
screen_rect_offset.translate(1.f, -1.f);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
screen_rect_offset.translate(1.f, 1.f);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
screen_rect_offset.translate(-1.f, 1.f);
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
screen_rect_offset.translate(0, -2.f);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
renderQuad(screen_rect_offset, uv_rect, slant_offset);
|
|
||||||
}
|
|
||||||
gGL.color4fv(color.mV);
|
|
||||||
renderQuad(screen_rect, uv_rect, slant_offset);
|
|
||||||
}
|
|
||||||
else if (shadow == DROP_SHADOW)
|
|
||||||
{
|
|
||||||
LLColor4 shadow_color = LLFontGL::sShadowColor;
|
|
||||||
shadow_color.mV[VALPHA] = color.mV[VALPHA] * drop_shadow_strength;
|
|
||||||
gGL.color4fv(shadow_color.mV);
|
|
||||||
LLRectf screen_rect_shadow = screen_rect;
|
|
||||||
screen_rect_shadow.translate(1.f, -1.f);
|
|
||||||
renderQuad(screen_rect_shadow, uv_rect, slant_offset);
|
|
||||||
gGL.color4fv(color.mV);
|
|
||||||
renderQuad(screen_rect, uv_rect, slant_offset);
|
|
||||||
}
|
|
||||||
else // normal rendering
|
|
||||||
{
|
|
||||||
gGL.color4fv(color.mV);
|
|
||||||
renderQuad(screen_rect, uv_rect, slant_offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
gGL.end();
|
else if (shadow == DROP_SHADOW_SOFT)
|
||||||
|
{
|
||||||
|
LLColor4U shadow_color = LLFontGL::sShadowColor;
|
||||||
|
shadow_color.mV[VALPHA] = U8(color.mV[VALPHA] * drop_shadow_strength * DROP_SHADOW_SOFT_STRENGTH);
|
||||||
|
for (S32 pass = 0; pass < 5; pass++)
|
||||||
|
{
|
||||||
|
LLRectf screen_rect_offset = screen_rect;
|
||||||
|
|
||||||
|
switch(pass)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
screen_rect_offset.translate(-1.f, -1.f);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
screen_rect_offset.translate(1.f, -1.f);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
screen_rect_offset.translate(1.f, 1.f);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
screen_rect_offset.translate(-1.f, 1.f);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
screen_rect_offset.translate(0, -2.f);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderQuad(&vertex_out[glyph_count * 4], &uv_out[glyph_count * 4], &colors_out[glyph_count * 4], screen_rect_offset, uv_rect, shadow_color, slant_offset);
|
||||||
|
glyph_count++;
|
||||||
|
}
|
||||||
|
renderQuad(&vertex_out[glyph_count * 4], &uv_out[glyph_count * 4], &colors_out[glyph_count * 4], screen_rect, uv_rect, color, slant_offset);
|
||||||
|
glyph_count++;
|
||||||
|
}
|
||||||
|
else if (shadow == DROP_SHADOW)
|
||||||
|
{
|
||||||
|
LLColor4U shadow_color = LLFontGL::sShadowColor;
|
||||||
|
shadow_color.mV[VALPHA] = U8(color.mV[VALPHA] * drop_shadow_strength);
|
||||||
|
LLRectf screen_rect_shadow = screen_rect;
|
||||||
|
screen_rect_shadow.translate(1.f, -1.f);
|
||||||
|
renderQuad(&vertex_out[glyph_count * 4], &uv_out[glyph_count * 4], &colors_out[glyph_count * 4], screen_rect_shadow, uv_rect, shadow_color, slant_offset);
|
||||||
|
glyph_count++;
|
||||||
|
renderQuad(&vertex_out[glyph_count * 4], &uv_out[glyph_count * 4], &colors_out[glyph_count * 4], screen_rect, uv_rect, color, slant_offset);
|
||||||
|
glyph_count++;
|
||||||
|
}
|
||||||
|
else // normal rendering
|
||||||
|
{
|
||||||
|
renderQuad(&vertex_out[glyph_count * 4], &uv_out[glyph_count * 4], &colors_out[glyph_count * 4], screen_rect, uv_rect, color, slant_offset);
|
||||||
|
glyph_count++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -232,8 +232,8 @@ protected:
|
|||||||
LLFontDescriptor mFontDescriptor;
|
LLFontDescriptor mFontDescriptor;
|
||||||
LLPointer<LLFontFreetype> mFontFreetype;
|
LLPointer<LLFontFreetype> mFontFreetype;
|
||||||
|
|
||||||
void renderQuad(const LLRectf& screen_rect, const LLRectf& uv_rect, F32 slant_amt) const;
|
void renderQuad(LLVector4a* vertex_out, LLVector2* uv_out, LLColor4U* colors_out, const LLRectf& screen_rect, const LLRectf& uv_rect, const LLColor4U& color, F32 slant_amt) const;
|
||||||
void drawGlyph(const LLRectf& screen_rect, const LLRectf& uv_rect, const LLColor4& color, U8 style, ShadowType shadow, F32 drop_shadow_fade) const;
|
void drawGlyph(S32& glyph_count, LLVector4a* vertex_out, LLVector2* uv_out, LLColor4U* colors_out, const LLRectf& screen_rect, const LLRectf& uv_rect, const LLColor4U& color, U8 style, ShadowType shadow, F32 drop_shadow_fade) const;
|
||||||
|
|
||||||
// Registry holds all instantiated fonts.
|
// Registry holds all instantiated fonts.
|
||||||
static LLFontRegistry* sFontRegistry;
|
static LLFontRegistry* sFontRegistry;
|
||||||
|
|||||||
Reference in New Issue
Block a user