From 2b6867b4bc49f63b7c5289874041345addc8e374 Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Wed, 9 Jun 2021 18:07:30 +0200 Subject: [PATCH] Move rencache global data into a struct Now we put all the data needed by rencache into a struct and pass a pointer to the struct to all the rencache functions to make it reentrant. A global rencache struct is declared now in main.c so all the operations will work in the same way except we always pass the pointer to rencache struct. This work is meant to transition into a system where the rencache machinery can be used to render and update a part of the window. Currently the rencache performs all the drawing operations into the implicit window's surface. Future work may change this so that rencache can write into an arbitrary surface. --- src/api/renderer.c | 13 ++--- src/api/system.c | 4 +- src/main.c | 4 ++ src/rencache.c | 116 +++++++++++++++++++-------------------------- src/rencache.h | 36 +++++++++++--- 5 files changed, 91 insertions(+), 82 deletions(-) diff --git a/src/api/renderer.c b/src/api/renderer.c index 8dc13ada..401b8578 100644 --- a/src/api/renderer.c +++ b/src/api/renderer.c @@ -2,6 +2,7 @@ #include "renderer.h" #include "rencache.h" +extern RenCache rencache; static RenColor checkcolor(lua_State *L, int idx, int def) { RenColor color; @@ -23,7 +24,7 @@ static RenColor checkcolor(lua_State *L, int idx, int def) { static int f_show_debug(lua_State *L) { luaL_checkany(L, 1); - rencache_show_debug(lua_toboolean(L, 1)); + rencache_show_debug(&rencache, lua_toboolean(L, 1)); return 0; } @@ -38,13 +39,13 @@ static int f_get_size(lua_State *L) { static int f_begin_frame(lua_State *L) { - rencache_begin_frame(L); + rencache_begin_frame(&rencache, L); return 0; } static int f_end_frame(lua_State *L) { - rencache_end_frame(L); + rencache_end_frame(&rencache, L); return 0; } @@ -55,7 +56,7 @@ static int f_set_clip_rect(lua_State *L) { rect.y = luaL_checknumber(L, 2); rect.width = luaL_checknumber(L, 3); rect.height = luaL_checknumber(L, 4); - rencache_set_clip_rect(rect); + rencache_set_clip_rect(&rencache, rect); return 0; } @@ -67,7 +68,7 @@ static int f_draw_rect(lua_State *L) { rect.width = luaL_checknumber(L, 3); rect.height = luaL_checknumber(L, 4); RenColor color = checkcolor(L, 5, 255); - rencache_draw_rect(rect, color); + rencache_draw_rect(&rencache, rect, color); return 0; } @@ -90,7 +91,7 @@ static int draw_text_subpixel_impl(lua_State *L, bool draw_subpixel) { replace_color = (RenColor) {0}; } - x_subpixel = rencache_draw_text(L, font_desc, 1, text, x_subpixel, y, color, draw_subpixel, rep_table, replace_color); + x_subpixel = rencache_draw_text(&rencache, L, font_desc, 1, text, x_subpixel, y, color, draw_subpixel, rep_table, replace_color); lua_pushnumber(L, x_subpixel); return 1; } diff --git a/src/api/system.c b/src/api/system.c index bbe0801b..6f9c194d 100644 --- a/src/api/system.c +++ b/src/api/system.c @@ -13,7 +13,7 @@ #endif extern SDL_Window *window; - +extern RenCache rencache; static const char* button_name(int button) { switch (button) { @@ -116,7 +116,7 @@ top: lua_pushnumber(L, e.window.data2); return 3; } else if (e.window.event == SDL_WINDOWEVENT_EXPOSED) { - rencache_invalidate(); + rencache_invalidate(&rencache); lua_pushstring(L, "exposed"); return 1; } else if (e.window.event == SDL_WINDOWEVENT_MINIMIZED) { diff --git a/src/main.c b/src/main.c index 341f820b..95e641e9 100644 --- a/src/main.c +++ b/src/main.c @@ -19,6 +19,7 @@ SDL_Window *window; +RenCache rencache; static double get_scale(void) { #ifdef _WIN32 @@ -135,6 +136,9 @@ int main(int argc, char **argv) { init_window_icon(); ren_init(window); + rencache.cells_prev = rencache.cells_buf1; + rencache.cells = rencache.cells_buf2; + lua_State *L; init_lua: L = luaL_newstate(); diff --git a/src/rencache.c b/src/rencache.c index 31165e90..541f5cb6 100644 --- a/src/rencache.c +++ b/src/rencache.c @@ -5,15 +5,7 @@ #include #include "rencache.h" -/* a cache over the software renderer -- all drawing operations are stored as -** commands when issued. At the end of the frame we write the commands to a grid -** of hash values, take the cells that have changed since the previous frame, -** merge them into dirty rectangles and redraw only those regions */ - -#define CELLS_X 80 -#define CELLS_Y 50 #define CELL_SIZE 96 -#define COMMAND_BUF_SIZE (1024 * 512) #define COMMAND_BARE_SIZE offsetof(Command, text) enum { SET_CLIP, DRAW_TEXT, DRAW_RECT, DRAW_TEXT_SUBPIXEL }; @@ -42,16 +34,6 @@ FontRef font_refs[FONT_REFS_MAX]; int font_refs_len = 0; -static unsigned cells_buf1[CELLS_X * CELLS_Y]; -static unsigned cells_buf2[CELLS_X * CELLS_Y]; -static unsigned *cells_prev = cells_buf1; -static unsigned *cells = cells_buf2; -static RenRect rect_buf[CELLS_X * CELLS_Y / 2]; -static char command_buf[COMMAND_BUF_SIZE]; -static int command_buf_idx; -static RenRect screen_rect; -static bool show_debug; - static inline int min(int a, int b) { return a < b ? a : b; } static inline int max(int a, int b) { return a > b ? a : b; } @@ -123,14 +105,14 @@ static RenRect merge_rects(RenRect a, RenRect b) { } -static Command* push_command(int type, int size) { - Command *cmd = (Command*) (command_buf + command_buf_idx); - int n = command_buf_idx + size; +static Command* push_command(RenCache *rc, int type, int size) { + Command *cmd = (Command*) (rc->command_buf + rc->command_buf_idx); + int n = rc->command_buf_idx + size; if (n > COMMAND_BUF_SIZE) { fprintf(stderr, "Warning: (" __FILE__ "): exhausted command buffer\n"); return NULL; } - command_buf_idx = n; + rc->command_buf_idx = n; memset(cmd, 0, COMMAND_BARE_SIZE); cmd->type = type; cmd->size = size; @@ -138,37 +120,37 @@ static Command* push_command(int type, int size) { } -static bool next_command(Command **prev) { +static bool next_command(RenCache *rc, Command **prev) { if (*prev == NULL) { - *prev = (Command*) command_buf; + *prev = (Command*) rc->command_buf; } else { *prev = (Command*) (((char*) *prev) + (*prev)->size); } - return *prev != ((Command*) (command_buf + command_buf_idx)); + return *prev != ((Command*) (rc->command_buf + rc->command_buf_idx)); } -void rencache_show_debug(bool enable) { - show_debug = enable; +void rencache_show_debug(RenCache *rc, bool enable) { + rc->show_debug = enable; } -void rencache_set_clip_rect(RenRect rect) { - Command *cmd = push_command(SET_CLIP, COMMAND_BARE_SIZE); - if (cmd) { cmd->rect = intersect_rects(rect, screen_rect); } +void rencache_set_clip_rect(RenCache *rc, RenRect rect) { + Command *cmd = push_command(rc, SET_CLIP, COMMAND_BARE_SIZE); + if (cmd) { cmd->rect = intersect_rects(rect, rc->screen_rect); } } -void rencache_draw_rect(RenRect rect, RenColor color) { - if (!rects_overlap(screen_rect, rect)) { return; } - Command *cmd = push_command(DRAW_RECT, COMMAND_BARE_SIZE); +void rencache_draw_rect(RenCache *rc, RenRect rect, RenColor color) { + if (!rects_overlap(rc->screen_rect, rect)) { return; } + Command *cmd = push_command(rc, DRAW_RECT, COMMAND_BARE_SIZE); if (cmd) { cmd->rect = rect; cmd->color = color; } } -int rencache_draw_text(lua_State *L, FontDesc *font_desc, int font_index, +int rencache_draw_text(RenCache *rc, lua_State *L, FontDesc *font_desc, int font_index, const char *text, int x, int y, RenColor color, bool draw_subpixel, CPReplaceTable *replacements, RenColor replace_color) { @@ -180,9 +162,9 @@ int rencache_draw_text(lua_State *L, FontDesc *font_desc, int font_index, rect.width = ren_font_subpixel_round(w_subpixel, subpixel_scale, 0); rect.height = ren_get_font_height(font_desc); - if (rects_overlap(screen_rect, rect) && font_refs_add(L, font_desc, font_index) >= 0) { + if (rects_overlap(rc->screen_rect, rect) && font_refs_add(L, font_desc, font_index) >= 0) { int sz = strlen(text) + 1; - Command *cmd = push_command(draw_subpixel ? DRAW_TEXT_SUBPIXEL : DRAW_TEXT, COMMAND_BARE_SIZE + sz); + Command *cmd = push_command(rc, draw_subpixel ? DRAW_TEXT_SUBPIXEL : DRAW_TEXT, COMMAND_BARE_SIZE + sz); if (cmd) { memcpy(cmd->text, text, sz); cmd->color = color; @@ -200,25 +182,25 @@ int rencache_draw_text(lua_State *L, FontDesc *font_desc, int font_index, } -void rencache_invalidate(void) { - memset(cells_prev, 0xff, sizeof(cells_buf1)); +void rencache_invalidate(RenCache *rc) { + memset(rc->cells_prev, 0xff, sizeof(rc->cells_buf1)); } -void rencache_begin_frame(lua_State *L) { +void rencache_begin_frame(RenCache *rc, lua_State *L) { /* reset all cells if the screen width/height has changed */ int w, h; ren_get_size(&w, &h); - if (screen_rect.width != w || h != screen_rect.height) { - screen_rect.width = w; - screen_rect.height = h; - rencache_invalidate(); + if (rc->screen_rect.width != w || h != rc->screen_rect.height) { + rc->screen_rect.width = w; + rc->screen_rect.height = h; + rencache_invalidate(rc); } font_refs_clear(L); } -static void update_overlapping_cells(RenRect r, unsigned h) { +static void update_overlapping_cells(RenCache *rc, RenRect r, unsigned h) { int x1 = r.x / CELL_SIZE; int y1 = r.y / CELL_SIZE; int x2 = (r.x + r.width) / CELL_SIZE; @@ -227,72 +209,72 @@ static void update_overlapping_cells(RenRect r, unsigned h) { for (int y = y1; y <= y2; y++) { for (int x = x1; x <= x2; x++) { int idx = cell_idx(x, y); - hash(&cells[idx], &h, sizeof(h)); + hash(&rc->cells[idx], &h, sizeof(h)); } } } -static void push_rect(RenRect r, int *count) { +static void push_rect(RenCache *rc, RenRect r, int *count) { /* try to merge with existing rectangle */ for (int i = *count - 1; i >= 0; i--) { - RenRect *rp = &rect_buf[i]; + RenRect *rp = &rc->rect_buf[i]; if (rects_overlap(*rp, r)) { *rp = merge_rects(*rp, r); return; } } /* couldn't merge with previous rectangle: push */ - rect_buf[(*count)++] = r; + rc->rect_buf[(*count)++] = r; } -void rencache_end_frame(lua_State *L) { +void rencache_end_frame(RenCache *rc, lua_State *L) { /* update cells from commands */ Command *cmd = NULL; - RenRect cr = screen_rect; - while (next_command(&cmd)) { + RenRect cr = rc->screen_rect; + while (next_command(rc, &cmd)) { if (cmd->type == SET_CLIP) { cr = cmd->rect; } RenRect r = intersect_rects(cmd->rect, cr); if (r.width == 0 || r.height == 0) { continue; } unsigned h = HASH_INITIAL; hash(&h, cmd, cmd->size); - update_overlapping_cells(r, h); + update_overlapping_cells(rc, r, h); } /* push rects for all cells changed from last frame, reset cells */ int rect_count = 0; - int max_x = screen_rect.width / CELL_SIZE + 1; - int max_y = screen_rect.height / CELL_SIZE + 1; + int max_x = rc->screen_rect.width / CELL_SIZE + 1; + int max_y = rc->screen_rect.height / CELL_SIZE + 1; for (int y = 0; y < max_y; y++) { for (int x = 0; x < max_x; x++) { /* compare previous and current cell for change */ int idx = cell_idx(x, y); - if (cells[idx] != cells_prev[idx]) { - push_rect((RenRect) { x, y, 1, 1 }, &rect_count); + if (rc->cells[idx] != rc->cells_prev[idx]) { + push_rect(rc, (RenRect) { x, y, 1, 1 }, &rect_count); } - cells_prev[idx] = HASH_INITIAL; + rc->cells_prev[idx] = HASH_INITIAL; } } /* expand rects from cells to pixels */ for (int i = 0; i < rect_count; i++) { - RenRect *r = &rect_buf[i]; + RenRect *r = &rc->rect_buf[i]; r->x *= CELL_SIZE; r->y *= CELL_SIZE; r->width *= CELL_SIZE; r->height *= CELL_SIZE; - *r = intersect_rects(*r, screen_rect); + *r = intersect_rects(*r, rc->screen_rect); } /* redraw updated regions */ for (int i = 0; i < rect_count; i++) { /* draw */ - RenRect r = rect_buf[i]; + RenRect r = rc->rect_buf[i]; ren_set_clip_rect(r); cmd = NULL; - while (next_command(&cmd)) { + while (next_command(rc, &cmd)) { switch (cmd->type) { case SET_CLIP: ren_set_clip_rect(intersect_rects(cmd->rect, r)); @@ -314,7 +296,7 @@ void rencache_end_frame(lua_State *L) { } } - if (show_debug) { + if (rc->show_debug) { RenColor color = { rand(), rand(), rand(), 50 }; ren_draw_rect(r, color); } @@ -322,13 +304,13 @@ void rencache_end_frame(lua_State *L) { /* update dirty rects */ if (rect_count > 0) { - ren_update_rects(rect_buf, rect_count); + ren_update_rects(rc->rect_buf, rect_count); } /* swap cell buffer and reset */ - unsigned *tmp = cells; - cells = cells_prev; - cells_prev = tmp; - command_buf_idx = 0; + unsigned *tmp = rc->cells; + rc->cells = rc->cells_prev; + rc->cells_prev = tmp; + rc->command_buf_idx = 0; } diff --git a/src/rencache.h b/src/rencache.h index 1d0f45a6..b4e761c8 100644 --- a/src/rencache.h +++ b/src/rencache.h @@ -5,13 +5,35 @@ #include #include "renderer.h" -void rencache_show_debug(bool enable); -void rencache_set_clip_rect(RenRect rect); -void rencache_draw_rect(RenRect rect, RenColor color); -int rencache_draw_text(lua_State *L, FontDesc *font_desc, int font_index, const char *text, int x, int y, RenColor color, +/* a cache over the software renderer -- all drawing operations are stored as +** commands when issued. At the end of the frame we write the commands to a grid +** of hash values, take the cells that have changed since the previous frame, +** merge them into dirty rectangles and redraw only those regions */ + +#define CELLS_X 80 +#define CELLS_Y 50 +#define COMMAND_BUF_SIZE (1024 * 512) + +struct RenCache { + unsigned cells_buf1[CELLS_X * CELLS_Y]; + unsigned cells_buf2[CELLS_X * CELLS_Y]; + unsigned *cells_prev; + unsigned *cells; + RenRect rect_buf[CELLS_X * CELLS_Y / 2]; + char command_buf[COMMAND_BUF_SIZE]; + int command_buf_idx; + RenRect screen_rect; + bool show_debug; +}; +typedef struct RenCache RenCache; + +void rencache_show_debug(RenCache *rc, bool enable); +void rencache_set_clip_rect(RenCache *rc, RenRect rect); +void rencache_draw_rect(RenCache *rc, RenRect rect, RenColor color); +int rencache_draw_text(RenCache *rc, lua_State *L, FontDesc *font_desc, int font_index, const char *text, int x, int y, RenColor color, bool draw_subpixel, CPReplaceTable *replacements, RenColor replace_color); -void rencache_invalidate(void); -void rencache_begin_frame(lua_State *L); -void rencache_end_frame(lua_State *L); +void rencache_invalidate(RenCache *rc); +void rencache_begin_frame(RenCache *rc, lua_State *L); +void rencache_end_frame(RenCache *rc, lua_State *L); #endif