[ui] Use the shaped text cache for passage text

This makes a pretty significant difference (~520us down to 80us) for
simple text passage, and even the first time is fast due to word
repetition.
This commit is contained in:
Bill Currie 2024-01-06 02:59:31 +09:00
parent ce7c2183d3
commit 082d00b6c3
4 changed files with 52 additions and 58 deletions

View file

@ -115,7 +115,8 @@ typedef struct text_system_s {
} text_system_t;
struct view_s Text_PassageView (text_system_t textsys, struct view_s parent,
struct font_s *font, struct passage_s *passage);
struct font_s *font, struct passage_s *passage,
struct text_shaper_s *shaper);
struct view_s Text_StringView (text_system_t textsys, struct view_s parent,
struct font_s *font,
const char *str, uint32_t len,

View file

@ -44,6 +44,7 @@
#include "QF/ui/canvas.h"
#include "QF/ui/font.h"
#include "QF/ui/passage.h"
#include "QF/ui/shaper.h"
#include "QF/ui/text.h"
#include "QF/ui/view.h"
@ -70,6 +71,7 @@ typedef struct {
PR_RESMAP (rua_font_t) font_map;
rua_font_t *fonts;
text_shaper_t *shaper;
ecs_registry_t *reg;
canvas_system_t csys;
text_system_t tsys;
@ -236,6 +238,7 @@ bi_gui_destroy (progs_t *pr, void *_res)
{
qfZoneScoped (true);
gui_resources_t *res = _res;
Shaper_Delete (res->shaper);
ECS_DelRegistry (res->reg);
PR_RESDELMAP (res->passage_map);
PR_RESDELMAP (res->font_map);
@ -331,7 +334,8 @@ bi (Text_PassageView)
rua_font_t *font = get_font (res, P_INT (pr, 1));
rua_passage_t *psg = get_passage (res, P_INT (pr, 2));
view_t view = Text_PassageView (res->tsys, parent,
font->font, psg->passage);
font->font, psg->passage,
res->shaper);
R_INT (pr) = view.id;//FIXME
}
@ -410,6 +414,7 @@ bi (Text_Draw)
}
}
}
Shaper_FlushUnused (res->shaper);
}
bi (View_Delete)
@ -537,6 +542,7 @@ RUA_GUI_Init (progs_t *pr, int secure)
passage_comp_count),
};
ECS_CreateComponentPools (res->reg);
res->shaper = Shaper_New ();
}
canvas_system_t

View file

@ -1209,7 +1209,7 @@ IMUI_Passage (imui_ctx_t *ctx, const char *name, struct passage_s *passage)
set_fill (ctx, anchor_view, ctx->style.background.normal);
auto psg_view = Text_PassageView (ctx->tsys, nullview,
ctx->font, passage);
ctx->font, passage, ctx->shaper);
Canvas_SetReference (ctx->csys, psg_view.id,
Canvas_Entity (ctx->csys,
View_GetRoot (anchor_view).id));

View file

@ -181,29 +181,47 @@ configure_textview (view_t textview, glyphobj_t *glyphs, glyphnode_t *node,
View_Control (textview)->free_y = 1;
}
view_t
Text_PassageView (text_system_t textsys, view_t parent,
font_t *font, passage_t *passage)
static void
set_shaping (shaping_t *shaping, uint32_t ent, text_system_t textsys)
{
auto reg = textsys.reg;
uint32_t c_script = textsys.text_base + text_script;
uint32_t c_features = textsys.text_base + text_features;
if (Ent_HasComponent (ent, c_script, reg)) {
shaping->script = Ent_GetComponent (ent, c_script, reg);
}
if (Ent_HasComponent (ent, c_features, reg)) {
shaping->features = Ent_GetComponent(ent, c_features, reg);
}
}
view_t
Text_PassageView (text_system_t textsys, view_t parent,
font_t *font, passage_t *passage, text_shaper_t *shaper)
{
auto reg = textsys.reg;
uint32_t c_script = textsys.text_base + text_script;
uint32_t c_glyphs = textsys.text_base + text_glyphs;
uint32_t c_passage_glyphs = textsys.text_base + text_passage_glyphs;
hierarchy_t *h = Ent_GetComponent (passage->hierarchy, ecs_hierarchy, reg);
uint32_t psg_ent = passage->hierarchy;
hierarchy_t *h = Ent_GetComponent (psg_ent, ecs_hierarchy, reg);
psg_text_t *text_objects = h->components[passage_type_text_obj];
glyphnode_t *glyph_nodes = 0;
glyphnode_t **head = &glyph_nodes;
hb_font_t *fnt = hb_ft_font_create (font->face, 0);
hb_buffer_t *buffer = hb_buffer_create ();
hb_buffer_allocation_successful (buffer);
hb_script_t psg_script = HB_SCRIPT_LATIN;
text_dir_e psg_direction = text_right_down;
hb_language_t psg_language = hb_language_from_string ("en", 2);
featureset_t passage_features = DARRAY_STATIC_INIT (0);
featureset_t *psg_features = &passage_features;
script_component_t psg_script = {
.script = HB_SCRIPT_LATIN,
.direction = text_right_down,
.language = hb_language_from_string ("en", 2),
};
featureset_t psg_features = DARRAY_STATIC_INIT (0);
shaping_t psg_shaping = {
.script = &psg_script,
.features = &psg_features,
.font = font,
};
set_shaping (&psg_shaping, psg_ent, textsys);
uint32_t glyph_count = 0;
@ -213,20 +231,9 @@ Text_PassageView (text_system_t textsys, view_t parent,
uint32_t paragraph = h->childIndex[0] + i;
uint32_t para_ent = h->ent[paragraph];
hb_script_t para_script = psg_script;
text_dir_e para_direction = psg_direction;
hb_language_t para_language = psg_language;
featureset_t *para_features = psg_features;;
shaping_t para_shaping = psg_shaping;
set_shaping (&para_shaping, para_ent, textsys);
if (Ent_HasComponent (para_ent, c_script, reg)) {
script_component_t *s = Ent_GetComponent (para_ent, c_script, reg);
para_script = s->script;
para_language = s->language;
para_direction = s->direction;
}
if (Ent_HasComponent (para_ent, c_features, reg)) {
para_features = Ent_GetComponent (para_ent, c_features, reg);
}
for (uint32_t j = 0; j < h->childCount[paragraph]; j++) {
uint32_t textind = h->childIndex[paragraph] + j;
uint32_t text_ent = h->ent[textind];
@ -234,32 +241,14 @@ Text_PassageView (text_system_t textsys, view_t parent,
const char *str = passage->text + textobj->text;
uint32_t len = textobj->size;
hb_script_t txt_script = para_script;
text_dir_e txt_direction = para_direction;
hb_language_t txt_language = para_language;
featureset_t *txt_features = para_features;;
shaping_t txt_shaping = para_shaping;
set_shaping (&txt_shaping, text_ent, textsys);
if (Ent_HasComponent (text_ent, c_script, reg)) {
script_component_t *s = Ent_GetComponent (text_ent, c_script,
reg);
txt_script = s->script;
txt_language = s->language;
txt_direction = s->direction;
}
if (Ent_HasComponent (text_ent, c_features, reg)) {
txt_features = Ent_GetComponent (text_ent, c_features, reg);
}
hb_buffer_reset (buffer);
hb_buffer_set_direction (buffer, txt_direction | HB_DIRECTION_LTR);
hb_buffer_set_script (buffer, txt_script);
hb_buffer_set_language (buffer, txt_language);
hb_buffer_add_utf8 (buffer, str, len, 0, len);
hb_shape (fnt, buffer, txt_features->a, txt_features->size);
unsigned c;
auto glyphInfo = hb_buffer_get_glyph_infos (buffer, &c);
auto glyphPos = hb_buffer_get_glyph_positions (buffer, &c);
auto shaped_glyphs = Shaper_ShapeText (shaper, &txt_shaping,
str, len);
unsigned c = shaped_glyphs.count;
auto glyphInfo = shaped_glyphs.glyphInfo;
auto glyphPos = shaped_glyphs.glyphPos;
*head = alloca (sizeof (glyphnode_t));
**head = (glyphnode_t) {
@ -287,7 +276,7 @@ Text_PassageView (text_system_t textsys, view_t parent,
glyphobj_t *glyphs = malloc (glyph_count * sizeof (glyphobj_t));
glyphnode_t *g = glyph_nodes;
// paragraph flow
int psg_vertical = !!(psg_direction & 2);
int psg_vertical = !!(psg_script.direction & 2);
for (uint32_t i = 0; i < h->childCount[0]; i++) {
uint32_t paragraph = h->childIndex[0] + i;
view_t paraview = View_AddToEntity (h->ent[paragraph], viewsys,
@ -308,7 +297,7 @@ Text_PassageView (text_system_t textsys, view_t parent,
Ent_SetComponent (paraview.id, c_glyphs, reg, &pararef);
uint32_t para_ent = h->ent[paragraph];
text_dir_e para_direction = psg_direction;
text_dir_e para_direction = psg_script.direction;
if (Ent_HasComponent (para_ent, c_script, reg)) {
script_component_t *s = Ent_GetComponent (para_ent, c_script,
reg);
@ -333,8 +322,6 @@ Text_PassageView (text_system_t textsys, view_t parent,
free (gs->glyphs);
}
Ent_SetComponent (passage_view.id, c_passage_glyphs, reg, &glyphset);
hb_buffer_destroy (buffer);
hb_font_destroy (fnt);
return passage_view;
}