mirror of
https://github.com/nunuhara/xsystem4.git
synced 2026-09-22 23:18:01 +03:00
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:
@@ -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
@@ -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
@@ -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++) {
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user