BLF: Optimize text rendering and caching

The current code allocates and transfers a lot of memory to the GPU,
but only a small portion of this memory is actually used.
In addition, the code calls many costly gl operations during the
caching process.

This commit significantly reduce the amount of memory by allocating
and transferring a flat array without pads to the GPU.
It also calls as little as possible the gl operations during the cache.

This code also simulate a billinear filter `GL_LINEAR` using a 1D texture.

**Average drawing time:**
|before:|0.00003184 sec
|now:|0.00001943 sec
|fac:|1.6385156675048407

**5 worst times:**
|before:|[0.001075, 0.001433, 0.002143, 0.002915, 0.003242]
|now:|[0.00094, 0.000993, 0.001502, 0.002284, 0.002328]

Differential Revision: https://developer.blender.org/D6886
This commit is contained in:
Germano Cavalcante 2020-02-23 17:30:27 -03:00
parent a31bd3f7b5
commit 001f7c92d1
Notes: blender-bot 2023-02-14 06:00:49 +01:00
Referenced by issue #77651, ChromeOS: Black screen/window on startup:
Referenced by issue #74318, Resetting the Theme to default causes most of the texts to disappear
Referenced by issue #74228, strange characters
6 changed files with 240 additions and 229 deletions

View File

@ -584,9 +584,6 @@ static void blf_draw_gl__start(FontBLF *font)
* in BLF_position (old ui_rasterpos_safe).
*/
/* always bind the texture for the first glyph */
font->tex_bind_state = 0;
if ((font->flags & (BLF_ROTATION | BLF_MATRIX | BLF_ASPECT)) == 0) {
return; /* glyphs will be translated individually and batched. */
}

View File

@ -84,16 +84,19 @@ static void blf_batch_draw_init(void)
{
GPUVertFormat format = {0};
g_batch.pos_loc = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
g_batch.tex_loc = GPU_vertformat_attr_add(&format, "tex", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
g_batch.col_loc = GPU_vertformat_attr_add(
&format, "col", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
g_batch.offset_loc = GPU_vertformat_attr_add(&format, "offset", GPU_COMP_I32, 1, GPU_FETCH_INT);
g_batch.glyph_size_loc = GPU_vertformat_attr_add(
&format, "glyph_size", GPU_COMP_I32, 2, GPU_FETCH_INT);
g_batch.verts = GPU_vertbuf_create_with_format_ex(&format, GPU_USAGE_STREAM);
GPU_vertbuf_data_alloc(g_batch.verts, BLF_BATCH_DRAW_LEN_MAX);
GPU_vertbuf_attr_get_raw_data(g_batch.verts, g_batch.pos_loc, &g_batch.pos_step);
GPU_vertbuf_attr_get_raw_data(g_batch.verts, g_batch.tex_loc, &g_batch.tex_step);
GPU_vertbuf_attr_get_raw_data(g_batch.verts, g_batch.col_loc, &g_batch.col_step);
GPU_vertbuf_attr_get_raw_data(g_batch.verts, g_batch.offset_loc, &g_batch.offset_step);
GPU_vertbuf_attr_get_raw_data(g_batch.verts, g_batch.glyph_size_loc, &g_batch.glyph_size_step);
g_batch.glyph_len = 0;
/* A dummy vbo containing 4 points, attribs are not used. */
@ -177,6 +180,46 @@ void blf_batch_draw_begin(FontBLF *font)
}
}
static GPUTexture *blf_batch_cache_texture_load(void)
{
GlyphCacheBLF *gc = g_batch.glyph_cache;
BLI_assert(gc);
BLI_assert(gc->bitmap_len > 0);
if (gc->bitmap_len > gc->bitmap_len_landed) {
const int tex_width = GPU_texture_width(gc->texture);
int bitmap_len_landed = gc->bitmap_len_landed;
int remain = gc->bitmap_len - bitmap_len_landed;
int offset_x = bitmap_len_landed % tex_width;
int offset_y = bitmap_len_landed / tex_width;
/* TODO(germano): Update more than one row in a single call. */
while (remain) {
int remain_row = tex_width - offset_x;
int width = remain > remain_row ? remain_row : remain;
GPU_texture_update_sub(gc->texture,
GPU_DATA_UNSIGNED_BYTE,
&gc->bitmap_result[bitmap_len_landed],
offset_x,
offset_y,
0,
width,
1,
0);
bitmap_len_landed += width;
remain -= width;
offset_x = 0;
offset_y += 1;
}
gc->bitmap_len_landed = bitmap_len_landed;
}
return gc->texture;
}
void blf_batch_draw(void)
{
if (g_batch.glyph_len == 0) {
@ -190,7 +233,8 @@ void blf_batch_draw(void)
/* We need to flush widget base first to ensure correct ordering. */
UI_widgetbase_draw_cache_flush();
GPU_texture_bind(g_batch.tex_bind_state, 0);
GPUTexture *texture = blf_batch_cache_texture_load();
GPU_texture_bind(texture, 0);
GPU_vertbuf_data_len_set(g_batch.verts, g_batch.glyph_len);
GPU_vertbuf_use(g_batch.verts); /* send data */
@ -202,8 +246,9 @@ void blf_batch_draw(void)
/* restart to 1st vertex data pointers */
GPU_vertbuf_attr_get_raw_data(g_batch.verts, g_batch.pos_loc, &g_batch.pos_step);
GPU_vertbuf_attr_get_raw_data(g_batch.verts, g_batch.tex_loc, &g_batch.tex_step);
GPU_vertbuf_attr_get_raw_data(g_batch.verts, g_batch.col_loc, &g_batch.col_step);
GPU_vertbuf_attr_get_raw_data(g_batch.verts, g_batch.offset_loc, &g_batch.offset_step);
GPU_vertbuf_attr_get_raw_data(g_batch.verts, g_batch.glyph_size_loc, &g_batch.glyph_size_step);
g_batch.glyph_len = 0;
}

View File

@ -143,13 +143,6 @@ GlyphCacheBLF *blf_glyph_cache_new(FontBLF *font)
memset(gc->glyph_ascii_table, 0, sizeof(gc->glyph_ascii_table));
memset(gc->bucket, 0, sizeof(gc->bucket));
gc->textures = (GPUTexture **)MEM_callocN(sizeof(GPUTexture *) * 256, __func__);
gc->textures_len = 256;
gc->texture_current = BLF_TEXTURE_UNSET;
gc->offset_x = 3; /* enough padding for blur */
gc->offset_y = 3; /* enough padding for blur */
gc->pad = 6;
gc->glyphs_len_max = (int)font->face->num_glyphs;
gc->glyphs_len_free = (int)font->face->num_glyphs;
gc->ascender = ((float)font->face->size->metrics.ascender) / 64.0f;
@ -173,9 +166,6 @@ GlyphCacheBLF *blf_glyph_cache_new(FontBLF *font)
CLAMP_MIN(gc->glyph_width_max, 1);
CLAMP_MIN(gc->glyph_height_max, 1);
gc->p2_width = 0;
gc->p2_height = 0;
BLI_addhead(&font->cache, gc);
return gc;
}
@ -229,54 +219,15 @@ void blf_glyph_cache_free(GlyphCacheBLF *gc)
blf_glyph_free(g);
}
}
for (i = 0; i < gc->textures_len; i++) {
if (gc->textures[i]) {
GPU_texture_free(gc->textures[i]);
}
if (gc->texture) {
GPU_texture_free(gc->texture);
}
if (gc->bitmap_result) {
MEM_freeN(gc->bitmap_result);
}
MEM_freeN(gc->textures);
MEM_freeN(gc);
}
static void blf_glyph_cache_texture(FontBLF *font, GlyphCacheBLF *gc)
{
int i;
char error[256];
/* move the index. */
gc->texture_current++;
if (UNLIKELY(gc->texture_current >= gc->textures_len)) {
gc->textures_len *= 2;
gc->textures = MEM_recallocN((void *)gc->textures, sizeof(GPUTexture *) * gc->textures_len);
}
gc->p2_width = (int)blf_next_p2(
(unsigned int)((gc->glyphs_len_free * gc->glyph_width_max) + (gc->pad * 2)));
if (gc->p2_width > font->tex_size_max) {
gc->p2_width = font->tex_size_max;
}
i = (int)((gc->p2_width - (gc->pad * 2)) / gc->glyph_width_max);
gc->p2_height = (int)blf_next_p2(
(unsigned int)(((gc->glyphs_len_max / i) + 1) * gc->glyph_height_max + (gc->pad * 2)));
if (gc->p2_height > font->tex_size_max) {
gc->p2_height = font->tex_size_max;
}
GPUTexture *tex = GPU_texture_create_nD(
gc->p2_width, gc->p2_height, 0, 2, NULL, GPU_R8, GPU_DATA_UNSIGNED_BYTE, 0, false, error);
GPU_texture_bind(tex, 0);
GPU_texture_wrap_mode(tex, false);
GPU_texture_filters(tex, GPU_NEAREST, GPU_LINEAR);
GPU_texture_clear(tex, GPU_DATA_UNSIGNED_BYTE, NULL);
GPU_texture_unbind(tex);
gc->textures[gc->texture_current] = tex;
}
GlyphBLF *blf_glyph_search(GlyphCacheBLF *gc, unsigned int c)
{
GlyphBLF *p;
@ -377,8 +328,6 @@ GlyphBLF *blf_glyph_add(FontBLF *font, GlyphCacheBLF *gc, unsigned int index, un
g = (GlyphBLF *)MEM_callocN(sizeof(GlyphBLF), "blf_glyph_add");
g->c = c;
g->idx = (FT_UInt)index;
g->offset_x = -1;
g->offset_y = -1;
bitmap = slot->bitmap;
g->width = (int)bitmap.width;
g->height = (int)bitmap.rows;
@ -418,17 +367,19 @@ GlyphBLF *blf_glyph_add(FontBLF *font, GlyphCacheBLF *gc, unsigned int index, un
void blf_glyph_free(GlyphBLF *g)
{
/* don't need free the texture, the GlyphCache already
* have a list of all the texture and free it.
*/
if (g->bitmap) {
MEM_freeN(g->bitmap);
}
MEM_freeN(g);
}
static void blf_texture_draw(
const unsigned char color[4], const float uv[2][2], float x1, float y1, float x2, float y2)
static void blf_texture_draw(const unsigned char color[4],
const int glyph_size[2],
const int offset,
float x1,
float y1,
float x2,
float y2)
{
/* Only one vertex per glyph, geometry shader expand it into a quad. */
/* TODO Get rid of Geom Shader because it's not optimal AT ALL for the GPU */
@ -437,8 +388,10 @@ static void blf_texture_draw(
y1 + g_batch.ofs[1],
x2 + g_batch.ofs[0],
y2 + g_batch.ofs[1]);
copy_v4_v4(GPU_vertbuf_raw_step(&g_batch.tex_step), (float *)uv);
copy_v4_v4_uchar(GPU_vertbuf_raw_step(&g_batch.col_step), color);
copy_v2_v2_int(GPU_vertbuf_raw_step(&g_batch.glyph_size_step), glyph_size);
*((int *)GPU_vertbuf_raw_step(&g_batch.offset_step)) = offset;
g_batch.glyph_len++;
/* Flush cache if it's full. */
if (g_batch.glyph_len == BLF_BATCH_DRAW_LEN_MAX) {
@ -447,45 +400,35 @@ static void blf_texture_draw(
}
static void blf_texture5_draw(const unsigned char color_in[4],
int tex_w,
int tex_h,
const float uv[2][2],
const int glyph_size[2],
const int offset,
float x1,
float y1,
float x2,
float y2)
{
float ofs[2] = {2 / (float)tex_w, 2 / (float)tex_h};
float uv_flag[2][2];
copy_v4_v4((float *)uv_flag, (float *)uv);
int glyph_size_flag[2];
/* flag the x and y component signs for 5x5 blurring */
uv_flag[0][0] = -(uv_flag[0][0] - ofs[0]);
uv_flag[0][1] = -(uv_flag[0][1] - ofs[1]);
uv_flag[1][0] = -(uv_flag[1][0] + ofs[0]);
uv_flag[1][1] = -(uv_flag[1][1] + ofs[1]);
glyph_size_flag[0] = -glyph_size[0];
glyph_size_flag[1] = -glyph_size[1];
blf_texture_draw(color_in, uv_flag, x1 - 2, y1 + 2, x2 + 2, y2 - 2);
blf_texture_draw(color_in, glyph_size_flag, offset, x1, y1, x2, y2);
}
static void blf_texture3_draw(const unsigned char color_in[4],
int tex_w,
int tex_h,
const float uv[2][2],
const int glyph_size[2],
const int offset,
float x1,
float y1,
float x2,
float y2)
{
float ofs[2] = {1 / (float)tex_w, 1 / (float)tex_h};
float uv_flag[2][2];
copy_v4_v4((float *)uv_flag, (float *)uv);
int glyph_size_flag[2];
/* flag the x component sign for 3x3 blurring */
uv_flag[0][0] = -(uv_flag[0][0] - ofs[0]);
uv_flag[0][1] = (uv_flag[0][1] - ofs[1]);
uv_flag[1][0] = -(uv_flag[1][0] + ofs[0]);
uv_flag[1][1] = (uv_flag[1][1] + ofs[1]);
glyph_size_flag[0] = -glyph_size[0];
glyph_size_flag[1] = glyph_size[1];
blf_texture_draw(color_in, uv_flag, x1 - 1, y1 + 1, x2 + 1, y2 - 1);
blf_texture_draw(color_in, glyph_size_flag, offset, x1, y1, x2, y2);
}
static void blf_glyph_calc_rect(rctf *rect, GlyphBLF *g, float x, float y)
@ -518,63 +461,38 @@ void blf_glyph_render(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, fl
return;
}
if (g->build_tex == 0) {
if (!g->cached) {
if (font->tex_size_max == -1) {
font->tex_size_max = GPU_max_texture_size();
}
if (gc->texture_current == BLF_TEXTURE_UNSET) {
blf_glyph_cache_texture(font, gc);
gc->offset_x = gc->pad;
gc->offset_y = 3; /* enough padding for blur */
}
g->offset = gc->bitmap_len;
if (gc->offset_x > (gc->p2_width - gc->glyph_width_max)) {
gc->offset_x = gc->pad;
gc->offset_y += gc->glyph_height_max;
int buff_size = g->width * g->height;
int bitmap_len = gc->bitmap_len + buff_size;
if (gc->offset_y > (gc->p2_height - gc->glyph_height_max)) {
gc->offset_y = 3; /* enough padding for blur */
blf_glyph_cache_texture(font, gc);
if (bitmap_len > gc->bitmap_len_alloc) {
int w = font->tex_size_max;
int h = bitmap_len / w + 1;
gc->bitmap_len_alloc = w * h;
gc->bitmap_result = MEM_reallocN(gc->bitmap_result, (size_t)gc->bitmap_len_alloc);
/* Keep in sync with the texture. */
if (gc->texture) {
GPU_texture_free(gc->texture);
}
gc->texture = GPU_texture_create_nD(
w, h, 0, 1, NULL, GPU_R8, GPU_DATA_UNSIGNED_BYTE, 0, false, NULL);
gc->bitmap_len_landed = 0;
}
g->tex = gc->textures[gc->texture_current];
g->offset_x = gc->offset_x;
g->offset_y = gc->offset_y;
/* prevent glTexSubImage2D from failing if the character
* asks for pixels out of bounds, this tends only to happen
* with very small sizes (5px high or less) */
if (UNLIKELY((g->offset_x + g->width) > gc->p2_width)) {
g->width -= (g->offset_x + g->width) - gc->p2_width;
BLI_assert(g->width > 0);
}
if (UNLIKELY((g->offset_y + g->height) > gc->p2_height)) {
g->height -= (g->offset_y + g->height) - gc->p2_height;
BLI_assert(g->height > 0);
}
GPU_texture_update_sub(g->tex,
GPU_DATA_UNSIGNED_BYTE,
g->bitmap,
g->offset_x,
g->offset_y,
0,
g->width,
g->height,
0);
g->uv[0][0] = ((float)g->offset_x) / ((float)gc->p2_width);
g->uv[0][1] = ((float)g->offset_y) / ((float)gc->p2_height);
g->uv[1][0] = ((float)(g->offset_x + g->width)) / ((float)gc->p2_width);
g->uv[1][1] = ((float)(g->offset_y + g->height)) / ((float)gc->p2_height);
/* update the x offset for the next glyph. */
gc->offset_x += (int)BLI_rctf_size_x(&g->box) + gc->pad;
memcpy(&gc->bitmap_result[gc->bitmap_len], g->bitmap, (size_t)buff_size);
gc->bitmap_len = bitmap_len;
gc->glyphs_len_free--;
g->build_tex = 1;
g->cached = true;
}
if (font->flags & BLF_CLIPPING) {
@ -587,27 +505,26 @@ void blf_glyph_render(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, fl
}
}
if (font->tex_bind_state != g->tex) {
blf_batch_draw();
font->tex_bind_state = g->tex;
GPU_texture_bind(font->tex_bind_state, 0);
}
g_batch.tex_bind_state = g->tex;
g_batch.glyph_cache = gc;
BLI_assert(g->offset < gc->bitmap_len);
if (font->flags & BLF_SHADOW) {
rctf rect_ofs;
blf_glyph_calc_rect_shadow(&rect_ofs, g, x, y, font);
if (font->shadow == 0) {
blf_texture_draw(
font->shadow_color, g->uv, rect_ofs.xmin, rect_ofs.ymin, rect_ofs.xmax, rect_ofs.ymax);
blf_texture_draw(font->shadow_color,
(int[2]){g->width, g->height},
g->offset,
rect_ofs.xmin,
rect_ofs.ymin,
rect_ofs.xmax,
rect_ofs.ymax);
}
else if (font->shadow <= 4) {
blf_texture3_draw(font->shadow_color,
gc->p2_width,
gc->p2_height,
g->uv,
(int[2]){g->width, g->height},
g->offset,
rect_ofs.xmin,
rect_ofs.ymin,
rect_ofs.xmax,
@ -615,9 +532,8 @@ void blf_glyph_render(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, fl
}
else {
blf_texture5_draw(font->shadow_color,
gc->p2_width,
gc->p2_height,
g->uv,
(int[2]){g->width, g->height},
g->offset,
rect_ofs.xmin,
rect_ofs.ymin,
rect_ofs.xmax,
@ -632,9 +548,8 @@ void blf_glyph_render(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, fl
switch (font->blur) {
case 3:
blf_texture3_draw(font->color,
gc->p2_width,
gc->p2_height,
g->uv,
(int[2]){g->width, g->height},
g->offset,
rect.xmin,
rect.ymin,
rect.xmax,
@ -642,18 +557,29 @@ void blf_glyph_render(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, fl
break;
case 5:
blf_texture5_draw(font->color,
gc->p2_width,
gc->p2_height,
g->uv,
(int[2]){g->width, g->height},
g->offset,
rect.xmin,
rect.ymin,
rect.xmax,
rect.ymax);
break;
default:
blf_texture_draw(font->color, g->uv, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
blf_texture_draw(font->color,
(int[2]){g->width, g->height},
g->offset,
rect.xmin,
rect.ymin,
rect.xmax,
rect.ymax);
}
#else
blf_texture_draw(font->color, g->uv, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
blf_texture_draw(font->color,
(int[2]){g->width, g->height},
g->offset,
rect.xmin,
rect.ymin,
rect.xmax,
rect.ymax);
#endif
}

View File

@ -33,13 +33,13 @@ typedef struct BatchBLF {
struct FontBLF *font; /* can only batch glyph from the same font */
struct GPUBatch *batch;
struct GPUVertBuf *verts;
struct GPUVertBufRaw pos_step, tex_step, col_step;
unsigned int pos_loc, tex_loc, col_loc;
struct GPUVertBufRaw pos_step, col_step, offset_step, glyph_size_step;
unsigned int pos_loc, col_loc, offset_loc, glyph_size_loc;
unsigned int glyph_len;
float ofs[2]; /* copy of font->pos */
float mat[4][4]; /* previous call modelmatrix. */
bool enabled, active, simple_shader;
GPUTexture *tex_bind_state;
struct GlyphCacheBLF *glyph_cache;
} BatchBLF;
extern BatchBLF g_batch;
@ -72,30 +72,16 @@ typedef struct GlyphCacheBLF {
struct GlyphBLF *glyph_ascii_table[256];
/* texture array, to draw the glyphs. */
GPUTexture **textures;
/* size of the array. */
unsigned int textures_len;
/* and the last texture, aka. the current texture. */
unsigned int texture_current;
/* We draw every glyph in a big texture, so this is the
* current position inside the texture. */
int offset_x;
int offset_y;
/* and the space from one to other. */
int pad;
GPUTexture *texture;
char *bitmap_result;
int bitmap_len;
int bitmap_len_landed;
int bitmap_len_alloc;
/* and the bigger glyph in the font. */
int glyph_width_max;
int glyph_height_max;
/* next two integer power of two, to build the texture. */
int p2_width;
int p2_height;
/* number of glyphs in the font. */
int glyphs_len_max;
@ -125,12 +111,8 @@ typedef struct GlyphBLF {
/* avoid conversion to int while drawing */
int advance_i;
/* texture id where this glyph is store. */
GPUTexture *tex;
/* position inside the texture where this glyph is store. */
int offset_x;
int offset_y;
int offset;
/* Bitmap data, from freetype. Take care that this
* can be NULL.
@ -142,9 +124,6 @@ typedef struct GlyphBLF {
int height;
int pitch;
/* uv coords. */
float uv[2][2];
/* X and Y bearing of the glyph.
* The X bearing is from the origin to the glyph left bbox edge.
* The Y bearing is from the baseline to the top of the glyph edge.
@ -152,8 +131,7 @@ typedef struct GlyphBLF {
float pos_x;
float pos_y;
/* with value of zero mean that we need build the texture. */
char build_tex;
bool cached;
} GlyphBLF;
typedef struct FontBufInfoBLF {
@ -239,9 +217,6 @@ typedef struct FontBLF {
/* max texture size. */
int tex_size_max;
/* cache current OpenGL texture to save calls into the API */
GPUTexture *tex_bind_state;
/* font options. */
int flags;
@ -286,6 +261,4 @@ typedef struct DirBLF {
char *path;
} DirBLF;
#define BLF_TEXTURE_UNSET ((unsigned int)-1)
#endif /* __BLF_INTERNAL_TYPES_H__ */

View File

@ -1,9 +1,13 @@
flat in vec4 color_flat;
noperspective in vec2 texCoord_interp;
flat in int glyph_offset;
flat in ivec2 glyph_dim;
flat in int interp_size;
out vec4 fragColor;
uniform sampler2D glyph;
uniform sampler1DArray glyph;
const vec2 offsets4[4] = vec2[4](
vec2(-0.5, 0.5), vec2(0.5, 0.5), vec2(-0.5, -0.5), vec2(-0.5, -0.5));
@ -25,54 +29,107 @@ const vec2 offsets16[16] = vec2[16](vec2(-1.5, 1.5),
vec2(0.5, -1.5),
vec2(1.5, -1.5));
#define sample_glyph_offset(texco, texel, ofs) texture(glyph, texco + ofs * texel).r
//#define GPU_NEAREST
#define sample_glyph_offset(texel, ofs) texture_1D_custom_bilinear_filter(texCoord_interp + ofs * texel)
float texel_fetch(int index)
{
int size_x = textureSize(glyph, 0).r;
if (index >= size_x) {
return texelFetch(glyph, ivec2(index % size_x, index / size_x), 0).r;
}
return texelFetch(glyph, ivec2(index, 0), 0).r;
}
bool is_inside_box(ivec2 v)
{
return all(greaterThanEqual(v, ivec2(0))) && all(lessThan(v, glyph_dim));
}
float texture_1D_custom_bilinear_filter(vec2 uv)
{
vec2 texel_2d = uv * glyph_dim + 0.5;
ivec2 texel_2d_near = ivec2(texel_2d) - 1;
int frag_offset = glyph_offset + texel_2d_near.y * glyph_dim.x + texel_2d_near.x;
float tl = 0.0;
if (is_inside_box(texel_2d_near)) {
tl = texel_fetch(frag_offset);
}
#ifdef GPU_NEAREST
return tl;
#else //GPU_LINEAR
int offset_x = 1;
int offset_y = glyph_dim.x;
float tr = 0.0;
float bl = 0.0;
float br = 0.0;
if (is_inside_box(texel_2d_near + ivec2(1, 0))) {
tr = texel_fetch(frag_offset + offset_x);
}
if (is_inside_box(texel_2d_near + ivec2(0, 1))) {
bl = texel_fetch(frag_offset + offset_y);
}
if (is_inside_box(texel_2d_near + ivec2(1, 1))) {
br = texel_fetch(frag_offset + offset_x + offset_y);
}
vec2 f = fract(texel_2d);
float tA = mix(tl, tr, f.x);
float tB = mix(bl, br, f.x);
return mix(tA, tB, f.y);
#endif
}
void main()
{
// input color replaces texture color
fragColor.rgb = color_flat.rgb;
vec2 texel = 1.0 / vec2(textureSize(glyph, 0));
vec2 texco = abs(texCoord_interp);
// modulate input alpha & texture alpha
if (texCoord_interp.x > 0) {
fragColor.a = texture(glyph, texco).r;
if (interp_size == 0) {
fragColor.a = texture_1D_custom_bilinear_filter(texCoord_interp);
}
else {
vec2 texel = 1.0 / glyph_dim;
fragColor.a = 0.0;
if (texCoord_interp.y > 0) {
if (interp_size == 1) {
/* 3x3 blur */
/* Manual unroll for perf. (stupid glsl compiler) */
fragColor.a += sample_glyph_offset(texco, texel, offsets4[0]);
fragColor.a += sample_glyph_offset(texco, texel, offsets4[1]);
fragColor.a += sample_glyph_offset(texco, texel, offsets4[2]);
fragColor.a += sample_glyph_offset(texco, texel, offsets4[3]);
fragColor.a += sample_glyph_offset(texel, offsets4[0]);
fragColor.a += sample_glyph_offset(texel, offsets4[1]);
fragColor.a += sample_glyph_offset(texel, offsets4[2]);
fragColor.a += sample_glyph_offset(texel, offsets4[3]);
fragColor.a *= (1.0 / 4.0);
}
else {
/* 5x5 blur */
/* Manual unroll for perf. (stupid glsl compiler) */
fragColor.a += sample_glyph_offset(texco, texel, offsets16[0]);
fragColor.a += sample_glyph_offset(texco, texel, offsets16[1]);
fragColor.a += sample_glyph_offset(texco, texel, offsets16[2]);
fragColor.a += sample_glyph_offset(texco, texel, offsets16[3]);
fragColor.a += sample_glyph_offset(texel, offsets16[0]);
fragColor.a += sample_glyph_offset(texel, offsets16[1]);
fragColor.a += sample_glyph_offset(texel, offsets16[2]);
fragColor.a += sample_glyph_offset(texel, offsets16[3]);
fragColor.a += sample_glyph_offset(texco, texel, offsets16[4]);
fragColor.a += sample_glyph_offset(texco, texel, offsets16[5]) * 2.0;
fragColor.a += sample_glyph_offset(texco, texel, offsets16[6]) * 2.0;
fragColor.a += sample_glyph_offset(texco, texel, offsets16[7]);
fragColor.a += sample_glyph_offset(texel, offsets16[4]);
fragColor.a += sample_glyph_offset(texel, offsets16[5]) * 2.0;
fragColor.a += sample_glyph_offset(texel, offsets16[6]) * 2.0;
fragColor.a += sample_glyph_offset(texel, offsets16[7]);
fragColor.a += sample_glyph_offset(texco, texel, offsets16[8]);
fragColor.a += sample_glyph_offset(texco, texel, offsets16[9]) * 2.0;
fragColor.a += sample_glyph_offset(texco, texel, offsets16[10]) * 2.0;
fragColor.a += sample_glyph_offset(texco, texel, offsets16[11]);
fragColor.a += sample_glyph_offset(texel, offsets16[8]);
fragColor.a += sample_glyph_offset(texel, offsets16[9]) * 2.0;
fragColor.a += sample_glyph_offset(texel, offsets16[10]) * 2.0;
fragColor.a += sample_glyph_offset(texel, offsets16[11]);
fragColor.a += sample_glyph_offset(texco, texel, offsets16[12]);
fragColor.a += sample_glyph_offset(texco, texel, offsets16[13]);
fragColor.a += sample_glyph_offset(texco, texel, offsets16[14]);
fragColor.a += sample_glyph_offset(texco, texel, offsets16[15]);
fragColor.a += sample_glyph_offset(texel, offsets16[12]);
fragColor.a += sample_glyph_offset(texel, offsets16[13]);
fragColor.a += sample_glyph_offset(texel, offsets16[14]);
fragColor.a += sample_glyph_offset(texel, offsets16[15]);
fragColor.a *= (1.0 / 20.0);
}
}

View File

@ -2,20 +2,33 @@
uniform mat4 ModelViewProjectionMatrix;
in vec4 pos; /* rect */
in vec4 tex; /* rect */
in vec4 col;
in int offset;
in ivec2 glyph_size;
flat out vec4 color_flat;
noperspective out vec2 texCoord_interp;
flat out int glyph_offset;
flat out ivec2 glyph_dim;
flat out int interp_size;
void main()
{
color_flat = col;
glyph_offset = offset;
glyph_dim = abs(glyph_size);
interp_size = int(glyph_size.x < 0) + int(glyph_size.y < 0);
/* Quad expension using instanced rendering. */
float x = float(gl_VertexID % 2);
float y = float(gl_VertexID / 2);
vec2 quad = vec2(x, y);
gl_Position = ModelViewProjectionMatrix * vec4(mix(pos.xy, pos.zw, quad), 0.0, 1.0);
texCoord_interp = mix(abs(tex.xy), abs(tex.zw), quad) * sign(tex.xw);
color_flat = col;
vec2 interp_offset = float(interp_size) / abs(pos.zw - pos.xy);
texCoord_interp = mix(-interp_offset, 1.0 + interp_offset, quad);
vec2 final_pos = mix(
pos.xy + ivec2(-interp_size, interp_size), pos.zw + ivec2(interp_size, -interp_size), quad);
gl_Position = ModelViewProjectionMatrix * vec4(final_pos, 0.0, 1.0);
}