BLF: Use Batch API instead of IMM.

This is not a perfect win just yet. It's now calling glBufferSubData for
every call (instead of using glMapBufferRange which is almost faster), but
with this system we will be able to batch drawcalls together.

See next commit.
This commit is contained in:
Clément Foucault 2018-03-30 19:16:14 +02:00
parent 8568d38f1b
commit ab9adf9cdc
8 changed files with 154 additions and 64 deletions

View File

@ -47,6 +47,7 @@ void BLF_exit(void);
void BLF_default_dpi(int dpi);
void BLF_default_set(int fontid);
int BLF_default(void); /* get default font ID so we can pass it to other functions */
void BLF_batch_reset(void); /* call when changing opengl context. */
void BLF_antialias_set(bool enabled);
bool BLF_antialias_get(void);

View File

@ -132,6 +132,11 @@ void BLF_exit(void)
blf_font_exit();
}
void BLF_batch_reset(void)
{
blf_batching_vao_clear();
}
void BLF_cache_clear(void)
{
FontBLF *font;
@ -561,8 +566,11 @@ static void blf_draw_gl__start(FontBLF *font)
* in BLF_position (old ui_rasterpos_safe).
*/
glEnable(GL_BLEND);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
/* 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. */
gpuPushMatrix();
@ -576,35 +584,12 @@ static void blf_draw_gl__start(FontBLF *font)
if (font->flags & BLF_ROTATION)
gpuRotate2D(RAD2DEG(font->angle));
#ifndef BLF_STANDALONE
Gwn_VertFormat *format = immVertexFormat();
unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 4, GWN_FETCH_FLOAT);
unsigned int texCoord = GWN_vertformat_attr_add(format, "tex", GWN_COMP_F32, 4, GWN_FETCH_FLOAT);
unsigned int color = GWN_vertformat_attr_add(format, "col", GWN_COMP_U8, 4, GWN_FETCH_INT_TO_FLOAT_UNIT);
BLI_assert(pos == BLF_POS_ID);
BLI_assert(texCoord == BLF_COORD_ID);
BLI_assert(color == BLF_COLOR_ID);
UNUSED_VARS_NDEBUG(pos, texCoord, color);
immBindBuiltinProgram(GPU_SHADER_TEXT);
#endif
/* always bind the texture for the first glyph */
font->tex_bind_state = -1;
}
static void blf_draw_gl__end(void)
static void blf_draw_gl__end(FontBLF *font)
{
gpuPopMatrix();
#ifndef BLF_STANDALONE
immUnbindProgram();
#endif
glDisable(GL_BLEND);
if ((font->flags & (BLF_ROTATION | BLF_MATRIX | BLF_ASPECT)) != 0)
gpuPopMatrix();
}
void BLF_draw_ex(
@ -623,7 +608,7 @@ void BLF_draw_ex(
else {
blf_font_draw(font, str, len, r_info);
}
blf_draw_gl__end();
blf_draw_gl__end(font);
}
}
void BLF_draw(int fontid, const char *str, size_t len)
@ -652,7 +637,7 @@ void BLF_draw_ascii_ex(
else {
blf_font_draw_ascii(font, str, len, r_info);
}
blf_draw_gl__end();
blf_draw_gl__end(font);
}
}
void BLF_draw_ascii(int fontid, const char *str, size_t len)
@ -676,7 +661,7 @@ int BLF_draw_mono(int fontid, const char *str, size_t len, int cwidth)
if (font && font->glyph_cache) {
blf_draw_gl__start(font);
columns = blf_font_draw_mono(font, str, len, cwidth);
blf_draw_gl__end();
blf_draw_gl__end(font);
}
return columns;

View File

@ -59,6 +59,8 @@
#include "BLF_api.h"
#include "GPU_immediate.h"
#include "GPU_matrix.h"
#include "GPU_batch.h"
#include "blf_internal_types.h"
#include "blf_internal.h"
@ -69,12 +71,104 @@
# define FT_New_Face FT_New_Face__win32_compat
#endif
/* Batching buffer for drawing. */
BatchBLF g_batch;
/* freetype2 handle ONLY for this file!. */
static FT_Library ft_lib;
static SpinLock ft_lib_mutex;
/* -------------------------------------------------------------------- */
/** \name Glyph Batching
* \{ */
/**
* Drawcalls are precious! make them count!
* Since most of the Text elems are not covered by other UI elements, we can
* group some strings together and render them in one drawcall. This behaviour
* is on demand only, between BLF_batch_start() and BLF_batch_end().
**/
static void blf_batching_init(void)
{
Gwn_VertFormat format = {0};
g_batch.pos_loc = GWN_vertformat_attr_add(&format, "pos", GWN_COMP_F32, 4, GWN_FETCH_FLOAT);
g_batch.tex_loc = GWN_vertformat_attr_add(&format, "tex", GWN_COMP_F32, 4, GWN_FETCH_FLOAT);
g_batch.col_loc = GWN_vertformat_attr_add(&format, "col", GWN_COMP_U8, 4, GWN_FETCH_INT_TO_FLOAT_UNIT);
g_batch.verts = GWN_vertbuf_create_with_format_ex(&format, GWN_USAGE_STREAM);
GWN_vertbuf_data_alloc(g_batch.verts, BLF_BATCHING_SIZE);
g_batch.batch = GWN_batch_create_ex(GWN_PRIM_POINTS, g_batch.verts, NULL, GWN_BATCH_OWNS_VBO);
}
static void blf_batching_exit(void)
{
GWN_BATCH_DISCARD_SAFE(g_batch.batch);
}
void blf_batching_vao_clear(void)
{
if (g_batch.batch) {
gwn_batch_vao_cache_clear(g_batch.batch);
}
}
void blf_batching_start(FontBLF *font)
{
if (g_batch.batch == NULL) {
blf_batching_init();
}
zero_v2(g_batch.ofs);
if ((font->flags & (BLF_ROTATION | BLF_MATRIX | BLF_ASPECT)) == 0) {
copy_v2_v2(g_batch.ofs, font->pos);
}
/* restart to 1st vertex data pointers */
GWN_vertbuf_attr_get_raw_data(g_batch.verts, g_batch.pos_loc, &g_batch.pos_step);
GWN_vertbuf_attr_get_raw_data(g_batch.verts, g_batch.tex_loc, &g_batch.tex_step);
GWN_vertbuf_attr_get_raw_data(g_batch.verts, g_batch.col_loc, &g_batch.col_step);
g_batch.glyph_ct = 0;
g_batch.font = font;
}
void blf_batching_draw(void)
{
if (g_batch.glyph_ct == 0)
return;
glEnable(GL_BLEND);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
BLI_assert(g_batch.font->tex_bind_state != 0); /* must still be valid */
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, g_batch.font->tex_bind_state);
GWN_vertbuf_vertex_count_set(g_batch.verts, g_batch.glyph_ct);
GWN_vertbuf_use(g_batch.verts); /* send data */
GWN_batch_program_set_builtin(g_batch.batch, GPU_SHADER_TEXT);
GWN_batch_uniform_1i(g_batch.batch, "glyph", 0);
GWN_batch_draw(g_batch.batch);
glDisable(GL_BLEND);
g_batch.glyph_ct = 0;
}
static void blf_batching_end(void)
{
if (!g_batch.enabled) {
blf_batching_draw();
}
}
/** \} */
/* -------------------------------------------------------------------- */
int blf_font_init(void)
{
memset(&g_batch, 0, sizeof(g_batch));
BLI_spin_init(&ft_lib_mutex);
return FT_Init_FreeType(&ft_lib);
}
@ -83,6 +177,7 @@ void blf_font_exit(void)
{
FT_Done_FreeType(ft_lib);
BLI_spin_end(&ft_lib_mutex);
blf_batching_exit();
}
void blf_font_size(FontBLF *font, unsigned int size, unsigned int dpi)
@ -174,24 +269,6 @@ static void blf_font_ensure_ascii_table(FontBLF *font)
} \
} (void)0
static unsigned int verts_needed(const FontBLF *font, const char *str, size_t len)
{
size_t str_len = (len > 50) ? strlen(str) : INT_MAX; /* Arbitrary. */
unsigned int length = (unsigned int)MIN2(str_len, len);
unsigned int quad_ct = 1;
if (font->flags & BLF_SHADOW) {
if (font->shadow == 0)
quad_ct += 1;
if (font->shadow <= 4)
quad_ct += 9; /* 3x3 kernel */
else
quad_ct += 25; /* 5x5 kernel */
}
return length * quad_ct; /* Only one vert per quad */
}
static void blf_font_draw_ex(
FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info,
int pen_y)
@ -212,8 +289,7 @@ static void blf_font_draw_ex(
blf_font_ensure_ascii_table(font);
immBeginAtMost(GWN_PRIM_POINTS, verts_needed(font, str, len));
/* at most because some glyphs might be clipped & not drawn */
blf_batching_start(font);
while ((i < len) && str[i]) {
BLF_UTF8_NEXT_FAST(font, g, str, i, c, glyph_ascii_table);
@ -232,7 +308,7 @@ static void blf_font_draw_ex(
g_prev = g;
}
immEnd();
blf_batching_end();
if (r_info) {
r_info->lines = 1;
@ -259,7 +335,7 @@ static void blf_font_draw_ascii_ex(
blf_font_ensure_ascii_table(font);
immBeginAtMost(GWN_PRIM_POINTS, verts_needed(font, str, len));
blf_batching_start(font);
while ((c = *(str++)) && len--) {
BLI_assert(c < 128);
@ -275,7 +351,7 @@ static void blf_font_draw_ascii_ex(
g_prev = g;
}
immEnd();
blf_batching_end();
if (r_info) {
r_info->lines = 1;
@ -299,7 +375,7 @@ int blf_font_draw_mono(FontBLF *font, const char *str, size_t len, int cwidth)
blf_font_ensure_ascii_table(font);
immBeginAtMost(GWN_PRIM_POINTS, verts_needed(font, str, len));
blf_batching_start(font);
while ((i < len) && str[i]) {
BLF_UTF8_NEXT_FAST(font, g, str, i, c, glyph_ascii_table);
@ -320,7 +396,7 @@ int blf_font_draw_mono(FontBLF *font, const char *str, size_t len, int cwidth)
pen_x += cwidth * col;
}
immEnd();
blf_batching_end();
return columns;
}

View File

@ -63,6 +63,7 @@
#include "blf_internal.h"
#include "BLI_strict_flags.h"
#include "BLI_math_vector.h"
GlyphCacheBLF *blf_glyph_cache_find(FontBLF *font, unsigned int size, unsigned int dpi)
{
@ -316,13 +317,19 @@ void blf_glyph_free(GlyphBLF *g)
MEM_freeN(g);
}
static void blf_texture_draw(const unsigned char color[4], float uv[2][2], float dx, float y1, float dx1, float y2)
static void blf_texture_draw(const unsigned char color[4], float uv[2][2], float x1, float y1, float x2, float y2)
{
if (g_batch.glyph_ct == BLF_BATCHING_SIZE) {
blf_batching_draw();
blf_batching_start(g_batch.font);
}
g_batch.glyph_ct++;
/* 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 */
immAttrib4ubv(BLF_COLOR_ID, color);
immAttrib4fv(BLF_COORD_ID, (float *)uv);
immVertex4f(BLF_POS_ID, dx, y1, dx1, y2);
copy_v4_fl4(GWN_vertbuf_raw_step(&g_batch.pos_step), x1 + g_batch.ofs[0], y1 + g_batch.ofs[1],
x2 + g_batch.ofs[0], y2 + g_batch.ofs[1]);
copy_v4_v4(GWN_vertbuf_raw_step(&g_batch.tex_step), (float *)uv);
copy_v4_v4_uchar(GWN_vertbuf_raw_step(&g_batch.col_step), color);
}
static void blf_texture5_draw(const unsigned char color_in[4], float uv[2][2], float x1, float y1, float x2, float y2)

View File

@ -37,10 +37,9 @@ struct GlyphBLF;
struct GlyphCacheBLF;
struct rctf;
/* vertex attribute IDs (fixed IDs so we don't have to pass them around) */
#define BLF_POS_ID 0
#define BLF_COORD_ID 1
#define BLF_COLOR_ID 2
void blf_batching_vao_clear(void);
void blf_batching_start(struct FontBLF *font);
void blf_batching_draw(void);
unsigned int blf_next_p2(unsigned int x);
unsigned int blf_hash(unsigned int val);

View File

@ -31,6 +31,24 @@
#ifndef __BLF_INTERNAL_TYPES_H__
#define __BLF_INTERNAL_TYPES_H__
#include "../../../intern/gawain/gawain/gwn_vertex_buffer.h"
#define BLF_BATCHING_SIZE 2048 /* in glyph */
typedef struct BatchBLF{
struct FontBLF *font; /* can only batch glyph from the same font */
struct Gwn_Batch *batch;
struct Gwn_VertBuf *verts;
struct Gwn_VertBufRaw pos_step, tex_step, col_step;
unsigned int pos_loc, tex_loc, col_loc;
unsigned int glyph_ct;
float ofs[2]; /* copy of font->pos */
float mat[4][4]; /* to catch bad usage */
bool enabled;
} BatchBLF;
extern BatchBLF g_batch;
typedef struct GlyphCacheBLF {
struct GlyphCacheBLF *next;
struct GlyphCacheBLF *prev;

View File

@ -40,6 +40,7 @@ struct rctf;
// #include "gawain/batch.h"
#include "BLI_compiler_attrs.h"
#include "BLI_sys_types.h"
#include "GPU_shader.h"

View File

@ -519,6 +519,7 @@ void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win)
}
if (tmpwin) {
BLF_batch_reset();
gpu_batch_presets_reset();
immDeactivate();
}
@ -1168,6 +1169,7 @@ void wm_window_make_drawable(wmWindowManager *wm, wmWindow *win)
printf("%s: set drawable %d\n", __func__, win->winid);
}
BLF_batch_reset();
gpu_batch_presets_reset();
immDeactivate();
GHOST_ActivateWindowDrawingContext(win->ghostwin);
@ -1192,6 +1194,7 @@ void wm_window_reset_drawable(void)
wmWindow *win = wm->windrawable;
if (win && win->ghostwin) {
BLF_batch_reset();
gpu_batch_presets_reset();
immDeactivate();
GHOST_ActivateWindowDrawingContext(win->ghostwin);