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.
This commit is contained in:
Francesco Abbate 2021-06-09 18:07:30 +02:00
parent 7de1fde9cf
commit 2b6867b4bc
5 changed files with 91 additions and 82 deletions

View File

@ -2,6 +2,7 @@
#include "renderer.h" #include "renderer.h"
#include "rencache.h" #include "rencache.h"
extern RenCache rencache;
static RenColor checkcolor(lua_State *L, int idx, int def) { static RenColor checkcolor(lua_State *L, int idx, int def) {
RenColor color; RenColor color;
@ -23,7 +24,7 @@ static RenColor checkcolor(lua_State *L, int idx, int def) {
static int f_show_debug(lua_State *L) { static int f_show_debug(lua_State *L) {
luaL_checkany(L, 1); luaL_checkany(L, 1);
rencache_show_debug(lua_toboolean(L, 1)); rencache_show_debug(&rencache, lua_toboolean(L, 1));
return 0; return 0;
} }
@ -38,13 +39,13 @@ static int f_get_size(lua_State *L) {
static int f_begin_frame(lua_State *L) { static int f_begin_frame(lua_State *L) {
rencache_begin_frame(L); rencache_begin_frame(&rencache, L);
return 0; return 0;
} }
static int f_end_frame(lua_State *L) { static int f_end_frame(lua_State *L) {
rencache_end_frame(L); rencache_end_frame(&rencache, L);
return 0; return 0;
} }
@ -55,7 +56,7 @@ static int f_set_clip_rect(lua_State *L) {
rect.y = luaL_checknumber(L, 2); rect.y = luaL_checknumber(L, 2);
rect.width = luaL_checknumber(L, 3); rect.width = luaL_checknumber(L, 3);
rect.height = luaL_checknumber(L, 4); rect.height = luaL_checknumber(L, 4);
rencache_set_clip_rect(rect); rencache_set_clip_rect(&rencache, rect);
return 0; return 0;
} }
@ -67,7 +68,7 @@ static int f_draw_rect(lua_State *L) {
rect.width = luaL_checknumber(L, 3); rect.width = luaL_checknumber(L, 3);
rect.height = luaL_checknumber(L, 4); rect.height = luaL_checknumber(L, 4);
RenColor color = checkcolor(L, 5, 255); RenColor color = checkcolor(L, 5, 255);
rencache_draw_rect(rect, color); rencache_draw_rect(&rencache, rect, color);
return 0; return 0;
} }
@ -90,7 +91,7 @@ static int draw_text_subpixel_impl(lua_State *L, bool draw_subpixel) {
replace_color = (RenColor) {0}; 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); lua_pushnumber(L, x_subpixel);
return 1; return 1;
} }

View File

@ -13,7 +13,7 @@
#endif #endif
extern SDL_Window *window; extern SDL_Window *window;
extern RenCache rencache;
static const char* button_name(int button) { static const char* button_name(int button) {
switch (button) { switch (button) {
@ -116,7 +116,7 @@ top:
lua_pushnumber(L, e.window.data2); lua_pushnumber(L, e.window.data2);
return 3; return 3;
} else if (e.window.event == SDL_WINDOWEVENT_EXPOSED) { } else if (e.window.event == SDL_WINDOWEVENT_EXPOSED) {
rencache_invalidate(); rencache_invalidate(&rencache);
lua_pushstring(L, "exposed"); lua_pushstring(L, "exposed");
return 1; return 1;
} else if (e.window.event == SDL_WINDOWEVENT_MINIMIZED) { } else if (e.window.event == SDL_WINDOWEVENT_MINIMIZED) {

View File

@ -19,6 +19,7 @@
SDL_Window *window; SDL_Window *window;
RenCache rencache;
static double get_scale(void) { static double get_scale(void) {
#ifdef _WIN32 #ifdef _WIN32
@ -135,6 +136,9 @@ int main(int argc, char **argv) {
init_window_icon(); init_window_icon();
ren_init(window); ren_init(window);
rencache.cells_prev = rencache.cells_buf1;
rencache.cells = rencache.cells_buf2;
lua_State *L; lua_State *L;
init_lua: init_lua:
L = luaL_newstate(); L = luaL_newstate();

View File

@ -5,15 +5,7 @@
#include <lauxlib.h> #include <lauxlib.h>
#include "rencache.h" #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 CELL_SIZE 96
#define COMMAND_BUF_SIZE (1024 * 512)
#define COMMAND_BARE_SIZE offsetof(Command, text) #define COMMAND_BARE_SIZE offsetof(Command, text)
enum { SET_CLIP, DRAW_TEXT, DRAW_RECT, DRAW_TEXT_SUBPIXEL }; 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; 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 min(int a, int b) { return a < b ? a : b; }
static inline int max(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) { static Command* push_command(RenCache *rc, int type, int size) {
Command *cmd = (Command*) (command_buf + command_buf_idx); Command *cmd = (Command*) (rc->command_buf + rc->command_buf_idx);
int n = command_buf_idx + size; int n = rc->command_buf_idx + size;
if (n > COMMAND_BUF_SIZE) { if (n > COMMAND_BUF_SIZE) {
fprintf(stderr, "Warning: (" __FILE__ "): exhausted command buffer\n"); fprintf(stderr, "Warning: (" __FILE__ "): exhausted command buffer\n");
return NULL; return NULL;
} }
command_buf_idx = n; rc->command_buf_idx = n;
memset(cmd, 0, COMMAND_BARE_SIZE); memset(cmd, 0, COMMAND_BARE_SIZE);
cmd->type = type; cmd->type = type;
cmd->size = size; 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) { if (*prev == NULL) {
*prev = (Command*) command_buf; *prev = (Command*) rc->command_buf;
} else { } else {
*prev = (Command*) (((char*) *prev) + (*prev)->size); *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) { void rencache_show_debug(RenCache *rc, bool enable) {
show_debug = enable; rc->show_debug = enable;
} }
void rencache_set_clip_rect(RenRect rect) { void rencache_set_clip_rect(RenCache *rc, RenRect rect) {
Command *cmd = push_command(SET_CLIP, COMMAND_BARE_SIZE); Command *cmd = push_command(rc, SET_CLIP, COMMAND_BARE_SIZE);
if (cmd) { cmd->rect = intersect_rects(rect, screen_rect); } if (cmd) { cmd->rect = intersect_rects(rect, rc->screen_rect); }
} }
void rencache_draw_rect(RenRect rect, RenColor color) { void rencache_draw_rect(RenCache *rc, RenRect rect, RenColor color) {
if (!rects_overlap(screen_rect, rect)) { return; } if (!rects_overlap(rc->screen_rect, rect)) { return; }
Command *cmd = push_command(DRAW_RECT, COMMAND_BARE_SIZE); Command *cmd = push_command(rc, DRAW_RECT, COMMAND_BARE_SIZE);
if (cmd) { if (cmd) {
cmd->rect = rect; cmd->rect = rect;
cmd->color = color; 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, const char *text, int x, int y, RenColor color, bool draw_subpixel,
CPReplaceTable *replacements, RenColor replace_color) 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.width = ren_font_subpixel_round(w_subpixel, subpixel_scale, 0);
rect.height = ren_get_font_height(font_desc); 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; 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) { if (cmd) {
memcpy(cmd->text, text, sz); memcpy(cmd->text, text, sz);
cmd->color = color; cmd->color = color;
@ -200,25 +182,25 @@ int rencache_draw_text(lua_State *L, FontDesc *font_desc, int font_index,
} }
void rencache_invalidate(void) { void rencache_invalidate(RenCache *rc) {
memset(cells_prev, 0xff, sizeof(cells_buf1)); 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 */ /* reset all cells if the screen width/height has changed */
int w, h; int w, h;
ren_get_size(&w, &h); ren_get_size(&w, &h);
if (screen_rect.width != w || h != screen_rect.height) { if (rc->screen_rect.width != w || h != rc->screen_rect.height) {
screen_rect.width = w; rc->screen_rect.width = w;
screen_rect.height = h; rc->screen_rect.height = h;
rencache_invalidate(); rencache_invalidate(rc);
} }
font_refs_clear(L); 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 x1 = r.x / CELL_SIZE;
int y1 = r.y / CELL_SIZE; int y1 = r.y / CELL_SIZE;
int x2 = (r.x + r.width) / 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 y = y1; y <= y2; y++) {
for (int x = x1; x <= x2; x++) { for (int x = x1; x <= x2; x++) {
int idx = cell_idx(x, y); 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 */ /* try to merge with existing rectangle */
for (int i = *count - 1; i >= 0; i--) { for (int i = *count - 1; i >= 0; i--) {
RenRect *rp = &rect_buf[i]; RenRect *rp = &rc->rect_buf[i];
if (rects_overlap(*rp, r)) { if (rects_overlap(*rp, r)) {
*rp = merge_rects(*rp, r); *rp = merge_rects(*rp, r);
return; return;
} }
} }
/* couldn't merge with previous rectangle: push */ /* 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 */ /* update cells from commands */
Command *cmd = NULL; Command *cmd = NULL;
RenRect cr = screen_rect; RenRect cr = rc->screen_rect;
while (next_command(&cmd)) { while (next_command(rc, &cmd)) {
if (cmd->type == SET_CLIP) { cr = cmd->rect; } if (cmd->type == SET_CLIP) { cr = cmd->rect; }
RenRect r = intersect_rects(cmd->rect, cr); RenRect r = intersect_rects(cmd->rect, cr);
if (r.width == 0 || r.height == 0) { continue; } if (r.width == 0 || r.height == 0) { continue; }
unsigned h = HASH_INITIAL; unsigned h = HASH_INITIAL;
hash(&h, cmd, cmd->size); 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 */ /* push rects for all cells changed from last frame, reset cells */
int rect_count = 0; int rect_count = 0;
int max_x = screen_rect.width / CELL_SIZE + 1; int max_x = rc->screen_rect.width / CELL_SIZE + 1;
int max_y = screen_rect.height / CELL_SIZE + 1; int max_y = rc->screen_rect.height / CELL_SIZE + 1;
for (int y = 0; y < max_y; y++) { for (int y = 0; y < max_y; y++) {
for (int x = 0; x < max_x; x++) { for (int x = 0; x < max_x; x++) {
/* compare previous and current cell for change */ /* compare previous and current cell for change */
int idx = cell_idx(x, y); int idx = cell_idx(x, y);
if (cells[idx] != cells_prev[idx]) { if (rc->cells[idx] != rc->cells_prev[idx]) {
push_rect((RenRect) { x, y, 1, 1 }, &rect_count); 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 */ /* expand rects from cells to pixels */
for (int i = 0; i < rect_count; i++) { for (int i = 0; i < rect_count; i++) {
RenRect *r = &rect_buf[i]; RenRect *r = &rc->rect_buf[i];
r->x *= CELL_SIZE; r->x *= CELL_SIZE;
r->y *= CELL_SIZE; r->y *= CELL_SIZE;
r->width *= CELL_SIZE; r->width *= CELL_SIZE;
r->height *= CELL_SIZE; r->height *= CELL_SIZE;
*r = intersect_rects(*r, screen_rect); *r = intersect_rects(*r, rc->screen_rect);
} }
/* redraw updated regions */ /* redraw updated regions */
for (int i = 0; i < rect_count; i++) { for (int i = 0; i < rect_count; i++) {
/* draw */ /* draw */
RenRect r = rect_buf[i]; RenRect r = rc->rect_buf[i];
ren_set_clip_rect(r); ren_set_clip_rect(r);
cmd = NULL; cmd = NULL;
while (next_command(&cmd)) { while (next_command(rc, &cmd)) {
switch (cmd->type) { switch (cmd->type) {
case SET_CLIP: case SET_CLIP:
ren_set_clip_rect(intersect_rects(cmd->rect, r)); 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 }; RenColor color = { rand(), rand(), rand(), 50 };
ren_draw_rect(r, color); ren_draw_rect(r, color);
} }
@ -322,13 +304,13 @@ void rencache_end_frame(lua_State *L) {
/* update dirty rects */ /* update dirty rects */
if (rect_count > 0) { if (rect_count > 0) {
ren_update_rects(rect_buf, rect_count); ren_update_rects(rc->rect_buf, rect_count);
} }
/* swap cell buffer and reset */ /* swap cell buffer and reset */
unsigned *tmp = cells; unsigned *tmp = rc->cells;
cells = cells_prev; rc->cells = rc->cells_prev;
cells_prev = tmp; rc->cells_prev = tmp;
command_buf_idx = 0; rc->command_buf_idx = 0;
} }

View File

@ -5,13 +5,35 @@
#include <lua.h> #include <lua.h>
#include "renderer.h" #include "renderer.h"
void rencache_show_debug(bool enable); /* a cache over the software renderer -- all drawing operations are stored as
void rencache_set_clip_rect(RenRect rect); ** commands when issued. At the end of the frame we write the commands to a grid
void rencache_draw_rect(RenRect rect, RenColor color); ** of hash values, take the cells that have changed since the previous frame,
int rencache_draw_text(lua_State *L, FontDesc *font_desc, int font_index, const char *text, int x, int y, RenColor color, ** 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); bool draw_subpixel, CPReplaceTable *replacements, RenColor replace_color);
void rencache_invalidate(void); void rencache_invalidate(RenCache *rc);
void rencache_begin_frame(lua_State *L); void rencache_begin_frame(RenCache *rc, lua_State *L);
void rencache_end_frame(lua_State *L); void rencache_end_frame(RenCache *rc, lua_State *L);
#endif #endif