Fix performance issue with PartsEngine text

Don't allocate massive textures for each text object. Instead, allocate
a texture for each character and then compose them onto an appropriately
sized texture.

Fixes a performance issue in Rance 01.
This commit is contained in:
Nunuhara Cabbage
2023-04-16 15:44:58 -07:00
parent f7cf2264fa
commit ba81077c2e
6 changed files with 137 additions and 48 deletions
+11
View File
@@ -142,4 +142,15 @@ float gfx_get_actual_font_size_round_down(unsigned face, float size);
void gfx_print_text_style(struct text_style *style, int indent);
static inline float text_style_width(struct text_style *ts, const char *ch)
{
return (gfx_size_char(ts, ch) + (ts->bold_width * 2) + ts->edge_left + ts->edge_right)
* ts->scale_x;
}
static inline float text_style_height(struct text_style *ts)
{
return ts->size + (ts->bold_width * 2) + ts->edge_up + ts->edge_down;
}
#endif /* SYSTEM4_FONT_H */
+8 -5
View File
@@ -28,19 +28,22 @@ static void parts_cg_print(struct parts_cg *cg, int indent)
static void parts_text_print(struct parts_text *text, int indent)
{
indent_printf(indent, "text.lines = {");
indent_printf(indent, "text.lines = {\n");
for (unsigned i = 0; i < text->nr_lines; i++) {
if (i > 0)
putchar(',');
printf("%u", text->lines[i].height);
struct string *s = parts_text_line_get(&text->lines[i]);
indent_printf(indent+1, "contents = \"%s\",\n", display_sjis0(s->text));
indent_printf(indent+1, "width = %u,\n", text->lines[i].width);
indent_printf(indent+1, "height = %u,\n", text->lines[i].height);
free_string(s);
}
printf("},\n");
indent_printf(indent, "},\n");
indent_printf(indent, "text.line_space = %u,\n", text->line_space);
indent_printf(indent, "text.cursor = ");
gfx_print_point(&text->cursor);
printf(",\n");
indent_printf(indent, "text.ts = ");
gfx_print_text_style(&text->ts, indent);
printf("\n");
}
static void parts_animation_print(struct parts_animation *anim, int indent)
+1 -5
View File
@@ -143,11 +143,7 @@ static void parts_state_free(struct parts_state *state)
free_string(state->cg.name);
break;
case PARTS_TEXT:
gfx_delete_texture(&state->common.texture);
for (int i = 0; i < state->text.nr_lines; i++) {
free_string(state->text.lines[i].text);
}
free(state->text.lines);
parts_text_free(&state->text);
break;
case PARTS_ANIMATION:
for (unsigned i = 0; i < state->anim.nr_frames; i++) {
+15 -1
View File
@@ -72,9 +72,18 @@ struct sound_motion {
bool played;
};
struct parts_text_char {
Texture t;
char ch[4];
int advance;
Point off;
};
struct parts_text_line {
struct parts_text_char *chars;
int nr_chars;
unsigned height;
struct string *text;
unsigned width;
};
enum parts_type {
@@ -292,6 +301,11 @@ bool parts_gauge_set_cg_by_index(struct parts *parts, struct parts_gauge *g, int
void parts_hgauge_set_rate(struct parts *parts, struct parts_gauge *g, float rate);
void parts_vgauge_set_rate(struct parts *parts, struct parts_gauge *g, float rate);
// text.c
void parts_text_free(struct parts_text *t);
struct string *parts_text_line_get(struct parts_text_line *line);
struct string *parts_text_get(struct parts_text *t);
// render.c
void parts_render_init(void);
void parts_engine_dirty(void);
+3 -1
View File
@@ -75,7 +75,9 @@ static void save_parts_text(struct iarray_writer *w, struct parts_text *text)
iarray_write_text_style(w, &text->ts);
iarray_write(w, text->nr_lines);
for (unsigned i = 0; i < text->nr_lines; i++) {
iarray_write_string(w, text->lines[i].text);
struct string *s = parts_text_line_get(&text->lines[i]);
iarray_write_string(w, s);
free_string(s);
}
}
+99 -36
View File
@@ -34,63 +34,126 @@ static int extract_sjis_char(const char *src, char *dst)
return 1;
}
static void parts_text_newline(struct parts_text *text)
struct string *parts_text_line_get(struct parts_text_line *line)
{
const unsigned height = text->lines[text->nr_lines-1].height;
text->cursor = POINT(0, text->cursor.y + height + text->line_space);
text->lines = xrealloc_array(text->lines, text->nr_lines, text->nr_lines+1, sizeof(struct parts_text_line));
text->lines[text->nr_lines].text = make_string("", 0);
text->nr_lines++;
struct string *s = make_string("", 0);
for (int i = 0; i < line->nr_chars; i++) {
string_append_cstr(&s, line->chars[i].ch, strlen(line->chars[i].ch));
}
return s;
}
struct string *parts_text_get(struct parts_text *t)
{
struct string *s = make_string("", 0);
for (int i = 0; i < t->nr_lines; i++) {
if (i > 0)
string_push_back(&s, '\n');
struct parts_text_line *line = &t->lines[i];
for (int i = 0; i < line->nr_chars; i++) {
string_append_cstr(&s, line->chars[i].ch, strlen(line->chars[i].ch));
}
}
return s;
}
static Point text_style_offset(struct text_style *ts)
{
int x = max(ts->bold_width, ts->edge_left) * ts->scale_x;
int y = max(ts->bold_width, ts->edge_up);
return (Point){x,y};
}
static const char *parts_text_append_char(struct parts_text *t, const char *str)
{
if (*str == '\n') {
t->lines = xrealloc_array(t->lines, t->nr_lines, t->nr_lines + 1,
sizeof(struct parts_text_line));
t->nr_lines++;
return str + 1;
}
struct parts_text_line *line = &t->lines[t->nr_lines - 1];
line->chars = xrealloc_array(line->chars, line->nr_chars, line->nr_chars + 1,
sizeof(struct parts_text_char));
struct parts_text_char *ch = &line->chars[line->nr_chars++];
ch->off = text_style_offset(&t->ts);
int len = extract_sjis_char(str, ch->ch);
int width = text_style_width(&t->ts, ch->ch);
int height = text_style_height(&t->ts);
gfx_init_texture_rgba(&ch->t, width, height, (SDL_Color){0,0,0,0});
ch->advance = gfx_render_text(&ch->t, 0, 0, ch->ch, &t->ts);
line->width += width;
line->height = max(line->height, height);
return str + len;
}
void parts_text_append(struct parts *parts, struct parts_text *t, struct string *text)
{
if (!t->common.texture.handle) {
gfx_init_texture_rgba(&t->common.texture, config.view_width, config.view_height,
(SDL_Color){ 0, 0, 0, 0 });
}
if (!t->nr_lines) {
t->lines = xcalloc(1, sizeof(struct parts_text_line));
t->lines[0].height = 0;
t->lines[0].text = make_string("", 0);
t->nr_lines = 1;
}
const char *msgp = text->text;
while (*msgp) {
char c[4];
int len = extract_sjis_char(msgp, c);
msgp += len;
if (c[0] == '\n') {
parts_text_newline(t);
continue;
}
t->cursor.x += gfx_render_text(&t->common.texture, t->cursor.x, t->cursor.y, c, &t->ts);
const unsigned old_height = t->lines[t->nr_lines-1].height;
const unsigned new_height = t->ts.size;
struct parts_text_line *line = &t->lines[t->nr_lines-1];
line->height = max(old_height, new_height);
string_append_cstr(&line->text, c, strlen(c));
msgp = parts_text_append_char(t, msgp);
}
parts_set_dims(parts, &t->common, t->cursor.x, t->cursor.y + t->lines[t->nr_lines-1].height);
// calculate required dimensions of texture
int width = 0;
int height = 0;
for (int i = 0; i < t->nr_lines; i++) {
width = max(width, t->lines[i].width);
height += t->lines[i].height;
}
if (!width || !height)
return;
// initialize texture
if (t->common.texture.handle)
gfx_delete_texture(&t->common.texture);
gfx_init_texture_rgba(&t->common.texture, width, height, (SDL_Color){0,0,0,0});
// copy glyph textures to main texture
t->cursor = POINT(0, 0);
for (int i = 0; i < t->nr_lines; i++) {
struct parts_text_line *line = &t->lines[i];
for (int i = 0; i < line->nr_chars; i++) {
struct parts_text_char *ch = &line->chars[i];
gfx_copy_with_alpha_map(&t->common.texture,
t->cursor.x, t->cursor.y,
&ch->t, 0, 0, ch->t.w, ch->t.h);
t->cursor.x += ch->advance;
}
t->cursor = POINT(0, t->cursor.y + line->height);
}
parts_set_dims(parts, &t->common, width, height);
}
void parts_text_free(struct parts_text *t)
{
gfx_delete_texture(&t->common.texture);
for (int i = 0; i < t->nr_lines; i++) {
struct parts_text_line *line = &t->lines[i];
for (int i = 0; i < line->nr_chars; i++) {
gfx_delete_texture(&line->chars[i].t);
}
free(line->chars);
}
free(t->lines);
}
static void parts_text_clear(struct parts *parts, int state)
{
struct parts_text *text = parts_get_text(parts, state);
for (int i = 0; i < text->nr_lines; i++) {
free_string(text->lines[i].text);
}
free(text->lines);
parts_text_free(text);
text->lines = NULL;
text->nr_lines = 0;
text->cursor.x = 0;
text->cursor.y = 0;
gfx_delete_texture(&text->common.texture);
}
bool PE_SetText(int parts_no, struct string *text, int state)
@@ -174,7 +237,7 @@ bool PE_SetPartsFontBoldWeight(int parts_no, float bold_weight, int state)
if (!parts_state_valid(--state))
return false;
parts_get_text(parts_get(parts_no), state)->ts.weight = bold_weight * 1000;
parts_get_text(parts_get(parts_no), state)->ts.bold_width = bold_weight;
return true;
}