Compare commits

...

5 Commits

Author SHA1 Message Date
Francesco Abbate 1b39a4cb37 WIP: introducing the RenCache as a Lua parameter
WIP, probably doesn't compile.

Working to introduce the RenCache object as an argument to the renderer.draw_*
operations from the Lua side.

To not disrupt the existing code the first argument, the RenCache, is added optionally.
If not given the global window's RenCache object is used.
2021-06-25 15:33:12 +02:00
Francesco Abbate 4e9208f768 WIP: working on the draw_image rencache command
Work in progress, do not compile and it is largely incomplete.

The idea is to implement a new command into the rencache, DRAW_IMAGE.
The command should blit into the rendering surface a rectangular
portion of a given image.

We should have a "repository" of images to be used for this operations.
Each image should be identified by an image ID. The image ID in turn
should have three components:

- the node the image belongs to (each node can potentially produce
  multiple images).
- an identifier of a specific rect withing a specific image of the node.
  For example
  for a DocView we may divide the document in pages and draw each
  page in a different image. We should in addition divide the image in
  rects so that an update in the image does not trigger the update
  of the whole image but only of a pert of it.
  The page and the rect within would provide a way to index
  the image.
- an identifier of the "revision" of the image. The idea is that to
  trigger an update upon a DRAW_IMAGE command the image_id has to
  change. If the image is always the same the "revision" number will
  track updates within an existing image.

In turn we should allow the views to render into an image and then
issue rencache commands to draw parts of the image (rects) using
the DRAW_IMAGE command. This part is not yet implemented.
2021-06-14 17:48:06 +02:00
Francesco Abbate 23c6b182f6 Initialize global window's RenSurface only once 2021-06-11 15:51:22 +02:00
Francesco Abbate ed3acbc29b Introduce RenSurface as a target for rencache
Previously rencache was always implicitly performing rendering in
the application's window. Now by using RenSurface the rencache
is explicit about where the rendering operations should be done.

In turn the RenSurface object can point to the surface underlying
a RenWindow object or to an offline surface. In the first case
the rendering operations are done in an active window while in the
second case are done into an intermediary surface.

Currently RenSurface is only used pointing to a RenWindow. The plan
is to let some Views write into offline surface and let them perform
a new rencache command to copy the surface into the final output.
2021-06-11 15:26:29 +02:00
Francesco Abbate 2b6867b4bc 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.
2021-06-09 18:07:30 +02:00
10 changed files with 306 additions and 159 deletions

View File

@ -7,6 +7,7 @@
#define API_TYPE_FONT "Font" #define API_TYPE_FONT "Font"
#define API_TYPE_REPLACE "Replace" #define API_TYPE_REPLACE "Replace"
#define API_TYPE_RENCACHE "RenCache"
void api_load_libs(lua_State *L); void api_load_libs(lua_State *L);

View File

@ -2,6 +2,7 @@
#include "renderer.h" #include "renderer.h"
#include "rencache.h" #include "rencache.h"
extern RenCache *window_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;
@ -21,16 +22,27 @@ static RenColor checkcolor(lua_State *L, int idx, int def) {
} }
static RenCache *opt_rencache_arg(lua_State *L, int *index) {
RenCache *rencache;
if (lua_touserdata(L, 1)) {
return luaL_checkudata(L, *(index++), API_TYPE_RENCACHE);
}
return window_rencache;
}
static int f_show_debug(lua_State *L) { static int f_show_debug(lua_State *L) {
luaL_checkany(L, 1); int index = 1;
rencache_show_debug(lua_toboolean(L, 1)); RenCache *rencache = opt_rencache_arg(L, &index);
luaL_checkany(L, index);
rencache_show_debug(rencache, lua_toboolean(L, index));
return 0; return 0;
} }
static int f_get_size(lua_State *L) { static int f_get_size(lua_State *L) {
int w, h; int w, h;
ren_get_size(&w, &h); ren_get_size(window_ren_surface, &w, &h);
lua_pushnumber(L, w); lua_pushnumber(L, w);
lua_pushnumber(L, h); lua_pushnumber(L, h);
return 2; return 2;
@ -38,59 +50,69 @@ 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); int index = 1;
RenCache *rencache = opt_rencache_arg(L, &index);
rencache_begin_frame(rencache, window_ren_surface, 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); int index = 1;
RenCache *rencache = opt_rencache_arg(L, &index);
rencache_end_frame(rencache, L);
return 0; return 0;
} }
static int f_set_clip_rect(lua_State *L) { static int f_set_clip_rect(lua_State *L) {
RenRect rect; RenRect rect;
rect.x = luaL_checknumber(L, 1); int index = 1;
rect.y = luaL_checknumber(L, 2); RenCache *rencache = opt_rencache_arg(L, &index);
rect.width = luaL_checknumber(L, 3); rect.x = luaL_checknumber(L, index);
rect.height = luaL_checknumber(L, 4); rect.y = luaL_checknumber(L, index + 1);
rencache_set_clip_rect(rect); rect.width = luaL_checknumber(L, index + 2);
rect.height = luaL_checknumber(L, index + 3);
rencache_set_clip_rect(rencache, rect);
return 0; return 0;
} }
static int f_draw_rect(lua_State *L) { static int f_draw_rect(lua_State *L) {
RenRect rect; RenRect rect;
rect.x = luaL_checknumber(L, 1); int index = 1;
rect.y = luaL_checknumber(L, 2); RenCache *rencache = opt_rencache_arg(L, &index);
rect.width = luaL_checknumber(L, 3); rect.x = luaL_checknumber(L, index);
rect.height = luaL_checknumber(L, 4); rect.y = luaL_checknumber(L, index + 1);
RenColor color = checkcolor(L, 5, 255); rect.width = luaL_checknumber(L, index + 2);
rencache_draw_rect(rect, color); rect.height = luaL_checknumber(L, index + 3);
RenColor color = checkcolor(L, index + 4, 255);
rencache_draw_rect(rencache, rect, color);
return 0; return 0;
} }
static int draw_text_subpixel_impl(lua_State *L, bool draw_subpixel) { static int draw_text_subpixel_impl(lua_State *L, bool draw_subpixel) {
FontDesc *font_desc = luaL_checkudata(L, 1, API_TYPE_FONT); int index = 1;
const char *text = luaL_checkstring(L, 2); RenCache *rencache = opt_rencache_arg(L, &index);
FontDesc *font_desc = luaL_checkudata(L, index, API_TYPE_FONT);
const char *text = luaL_checkstring(L, index + 1);
/* The coordinate below will be in subpixel iff draw_subpixel is true. /* The coordinate below will be in subpixel iff draw_subpixel is true.
Otherwise it will be in pixels. */ Otherwise it will be in pixels. */
int x_subpixel = luaL_checknumber(L, 3); int x_subpixel = luaL_checknumber(L, index + 2);
int y = luaL_checknumber(L, 4); int y = luaL_checknumber(L, index + 3);
RenColor color = checkcolor(L, 5, 255); RenColor color = checkcolor(L, index + 4, 255);
CPReplaceTable *rep_table; CPReplaceTable *rep_table;
RenColor replace_color; RenColor replace_color;
if (lua_gettop(L) >= 7) { if (lua_gettop(L) >= index + 6) {
rep_table = luaL_checkudata(L, 6, API_TYPE_REPLACE); rep_table = luaL_checkudata(L, index + 5, API_TYPE_REPLACE);
replace_color = checkcolor(L, 7, 255); replace_color = checkcolor(L, index + 6, 255);
} else { } else {
rep_table = NULL; rep_table = NULL;
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;
} }
@ -123,6 +145,14 @@ int luaopen_renderer_replacements(lua_State *L);
int luaopen_renderer(lua_State *L) { int luaopen_renderer(lua_State *L) {
luaL_newlib(L, lib); luaL_newlib(L, lib);
window_rencache = lua_newuserdata(L, sizeof(RenCache));
rencache_init(window_rencache);
luaL_setmetatable(L, API_TYPE_RENCACHE);
lua_pushvalue(L, -1);
luaL_ref(L, -1);
lua_setfield(L, -2, "window");
luaopen_renderer_font(L); luaopen_renderer_font(L);
lua_setfield(L, -2, "font"); lua_setfield(L, -2, "font");
luaopen_renderer_replacements(L); luaopen_renderer_replacements(L);

View File

@ -74,7 +74,7 @@ static int f_get_width(lua_State *L) {
const char *text = luaL_checkstring(L, 2); const char *text = luaL_checkstring(L, 2);
/* By calling ren_get_font_width with NULL as third arguments /* By calling ren_get_font_width with NULL as third arguments
we will obtain the width in points. */ we will obtain the width in points. */
int w = ren_get_font_width(self, text, NULL); int w = ren_get_font_width(window_ren_surface, self, text, NULL);
lua_pushnumber(L, w); lua_pushnumber(L, w);
return 1; return 1;
} }
@ -82,7 +82,7 @@ static int f_get_width(lua_State *L) {
static int f_subpixel_scale(lua_State *L) { static int f_subpixel_scale(lua_State *L) {
FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT); FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT);
lua_pushnumber(L, ren_get_font_subpixel_scale(self)); lua_pushnumber(L, ren_get_font_subpixel_scale(window_ren_surface, self));
return 1; return 1;
} }
@ -92,14 +92,14 @@ static int f_get_width_subpixel(lua_State *L) {
int subpixel_scale; int subpixel_scale;
/* We need to pass a non-null subpixel_scale pointer to force /* We need to pass a non-null subpixel_scale pointer to force
subpixel width calculation. */ subpixel width calculation. */
lua_pushnumber(L, ren_get_font_width(self, text, &subpixel_scale)); lua_pushnumber(L, ren_get_font_width(window_ren_surface, self, text, &subpixel_scale));
return 1; return 1;
} }
static int f_get_height(lua_State *L) { static int f_get_height(lua_State *L) {
FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT); FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT);
lua_pushnumber(L, ren_get_font_height(self) ); lua_pushnumber(L, ren_get_font_height(window_ren_surface, self) );
return 1; return 1;
} }

View File

@ -13,7 +13,7 @@
#endif #endif
extern SDL_Window *window; extern SDL_Window *window;
extern RenCache *window_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 *window_rencache;
static double get_scale(void) { static double get_scale(void) {
#ifdef _WIN32 #ifdef _WIN32

View File

@ -5,18 +5,10 @@
#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, DRAW_IMAGE };
typedef struct { typedef struct {
int8_t type; int8_t type;
@ -29,6 +21,7 @@ typedef struct {
FontDesc *font_desc; FontDesc *font_desc;
CPReplaceTable *replacements; CPReplaceTable *replacements;
RenColor replace_color; RenColor replace_color;
uint32_t image_id; /* FIXME: do not add a new field but use unions or alias another field. */
char text[0]; char text[0];
} Command; } Command;
@ -41,18 +34,6 @@ typedef struct FontRef FontRef;
FontRef font_refs[FONT_REFS_MAX]; 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 +104,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,51 +119,70 @@ 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_init(RenCache *rc) {
rc->cells_prev = rc->cells_buf1;
rc->cells = rc->cells_buf2;
} }
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,
void rencache_draw_image(RenCache *rc, int image_id, int x, int y, RenRect image_rect) {
RenRect rect = {x, y, image_rect.width, image_rect.height};
if (!rects_overlap(rc->screen_rect, rect)) { return; }
Command *cmd = push_command(rc, DRAW_IMAGE, COMMAND_BARE_SIZE + 2 * sizeof(int));
int *img_coord = (int *) cmd->text;
if (cmd) {
cmd->rect = rect;
cmd->image_id = image_id;
img_coord[0] = image_rect.x;
img_coord[1] = image_rect.y;
}
}
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)
{ {
int subpixel_scale; int subpixel_scale;
int w_subpixel = ren_get_font_width(font_desc, text, &subpixel_scale); int w_subpixel = ren_get_font_width(rc->ren_surface, font_desc, text, &subpixel_scale);
RenRect rect; RenRect rect;
rect.x = (draw_subpixel ? ren_font_subpixel_round(x, subpixel_scale, -1) : x); rect.x = (draw_subpixel ? ren_font_subpixel_round(x, subpixel_scale, -1) : x);
rect.y = y; rect.y = y;
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(rc->ren_surface, 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 +200,26 @@ 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, RenSurface *ren, 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(ren, &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);
} }
rc->ren_surface = ren;
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,108 +228,115 @@ 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++) {
const int *image_coord;
/* draw */ /* draw */
RenRect r = rect_buf[i]; RenRect r = rc->rect_buf[i];
ren_set_clip_rect(r); ren_set_clip_rect(rc->ren_surface, 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(rc->ren_surface, intersect_rects(cmd->rect, r));
break; break;
case DRAW_RECT: case DRAW_RECT:
ren_draw_rect(cmd->rect, cmd->color); ren_draw_rect(rc->ren_surface, cmd->rect, cmd->color);
break; break;
case DRAW_TEXT: case DRAW_TEXT:
font_desc_set_tab_size(cmd->font_desc, cmd->tab_size); font_desc_set_tab_size(cmd->font_desc, cmd->tab_size);
ren_draw_text(cmd->font_desc, cmd->text, cmd->rect.x, cmd->rect.y, cmd->color, ren_draw_text(rc->ren_surface, cmd->font_desc, cmd->text, cmd->rect.x, cmd->rect.y, cmd->color,
cmd->replacements, cmd->replace_color); cmd->replacements, cmd->replace_color);
break; break;
case DRAW_TEXT_SUBPIXEL: case DRAW_TEXT_SUBPIXEL:
font_desc_set_tab_size(cmd->font_desc, cmd->tab_size); font_desc_set_tab_size(cmd->font_desc, cmd->tab_size);
ren_draw_text_subpixel(cmd->font_desc, cmd->text, ren_draw_text_subpixel(rc->ren_surface, cmd->font_desc, cmd->text,
cmd->subpixel_scale * cmd->rect.x + cmd->x_subpixel_offset, cmd->rect.y, cmd->color, cmd->subpixel_scale * cmd->rect.x + cmd->x_subpixel_offset, cmd->rect.y, cmd->color,
cmd->replacements, cmd->replace_color); cmd->replacements, cmd->replace_color);
break; break;
case DRAW_IMAGE:
image_coord = (const int *) cmd->text;
ren_draw_image(rc->ren_surface, cmd->image_id, image_coord[0], image_coord[1], cmd->rect);
break;
} }
} }
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(rc->ren_surface, r, color);
} }
} }
/* 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->ren_surface, 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;
rc->ren_surface = NULL;
} }

View File

@ -5,13 +5,37 @@
#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;
RenSurface *ren_surface;
};
typedef struct RenCache RenCache;
void rencache_init(RenCache *rc);
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, RenSurface *ren, lua_State *L);
void rencache_end_frame(lua_State *L); void rencache_end_frame(RenCache *rc, lua_State *L);
#endif #endif

View File

@ -33,6 +33,7 @@ struct RenFont {
}; };
static RenWindow window_renderer = {0}; static RenWindow window_renderer = {0};
RenSurface window_ren_surface[1] = {{SurfaceWindow, &window_renderer}};
static void* check_alloc(void *ptr) { static void* check_alloc(void *ptr) {
if (!ptr) { if (!ptr) {
@ -59,6 +60,26 @@ static const char* utf8_to_codepoint(const char *p, unsigned *dst) {
return p + 1; return p + 1;
} }
static int ren_surface_scale(RenSurface *ren) {
if (ren->type == SurfaceTexture) {
return ((RenTexture *) ren->data)->surface_scale;
}
return renwin_surface_scale((RenWindow *) ren->data);
}
static SDL_Surface *ren_surface_get_surface(RenSurface *ren) {
if (ren->type == SurfaceTexture) {
return ((RenTexture *) ren->data)->surface;
}
return renwin_get_surface((RenWindow *) ren->data);
}
static RenRect ren_surface_clip(RenSurface *ren) {
if (ren->type == SurfaceTexture) {
return ((RenTexture *) ren->data)->clip;
}
return ((RenWindow *) ren->data)->clip;
}
void ren_cp_replace_init(CPReplaceTable *rep_table) { void ren_cp_replace_init(CPReplaceTable *rep_table) {
rep_table->size = 0; rep_table->size = 0;
@ -97,35 +118,66 @@ void ren_free_window_resources() {
void ren_init(SDL_Window *win) { void ren_init(SDL_Window *win) {
assert(win); assert(win);
window_renderer.window = win; window_renderer.window = win;
window_renderer.initial_frame = true;
renwin_init_surface(&window_renderer); renwin_init_surface(&window_renderer);
renwin_clip_to_surface(&window_renderer); renwin_clip_to_surface(&window_renderer);
} }
void ren_resize_window() { void ren_resize_window() {
renwin_resize_surface(&window_renderer); renwin_resize_surface(&window_renderer);
} }
/* FIXME: all this stuff. */
#define IMAGE_CELL_SIZE 96
#define IMAGE_CELL_X 80
void ren_update_rects(RenRect *rects, int count) { static inline int image_cell_idx(int x, int y) {
static bool initial_frame = true; return x + y * IMAGE_CELLS_X;
if (initial_frame) { }
renwin_show_window(&window_renderer);
initial_frame = false; void ren_update_rects(RenSurface *ren, RenRect *rects, int count) {
if (ren->type == SurfaceTexture) {
RenTexture *rentex = (RenTexture *) ren->data;
for (int i = 0; i < count; i++) {
RenRect irect = image_rect_to_index(rects[i]);
/* Increment the revision number for all cells that needs to be updated,
i.e. those that overlaps with one of the update rectangles. */
for (int x = irect.x; x <= irect.x + irect.width; x++) {
for (int y = irect.y; y <= irect.y + irect.height; y++) {
int idx = image_cell_idx(x, y);
rentex->revisions[idx] += 1:
}
}
}
} else {
RenWindow *renwin = (RenWindow *) ren->data;
if (renwin->initial_frame) {
renwin_show_window(renwin);
renwin->initial_frame = false;
}
renwin_update_rects(renwin, rects, count);
} }
renwin_update_rects(&window_renderer, rects, count);
} }
void ren_set_clip_rect(RenRect rect) { // FIXME: duplicated from renwindow.c
renwin_set_clip_rect(&window_renderer, rect); static RenRect scaled_rect(const RenRect rect, const int scale) {
return (RenRect) {rect.x * scale, rect.y * scale, rect.width * scale, rect.height * scale};
}
void ren_set_clip_rect(RenSurface *ren, RenRect rect) {
if (ren->type == SurfaceTexture) {
RenTexture *rentex = (RenTexture *) ren->data;
rentex->clip = scaled_rect(rect, rentex->surface_scale);
} else {
renwin_set_clip_rect((RenWindow *) ren->data, rect);
}
} }
void ren_get_size(int *x, int *y) { void ren_get_size(RenSurface *ren, int *x, int *y) {
RenWindow *ren = &window_renderer; const int scale = ren_surface_scale(ren);
const int scale = renwin_surface_scale(ren); SDL_Surface *surface = ren_surface_get_surface(ren);
SDL_Surface *surface = renwin_get_surface(ren);
*x = surface->w / scale; *x = surface->w / scale;
*y = surface->h / scale; *y = surface->h / scale;
} }
@ -237,11 +289,11 @@ int ren_get_font_tab_size(RenFont *font) {
/* Important: if subpixel_scale is NULL we will return width in points. Otherwise we will /* Important: if subpixel_scale is NULL we will return width in points. Otherwise we will
return width in subpixels. */ return width in subpixels. */
int ren_get_font_width(FontDesc *font_desc, const char *text, int *subpixel_scale) { int ren_get_font_width(RenSurface *ren, FontDesc *font_desc, const char *text, int *subpixel_scale) {
int x = 0; int x = 0;
const char *p = text; const char *p = text;
unsigned codepoint; unsigned codepoint;
const int surface_scale = renwin_surface_scale(&window_renderer); const int surface_scale = ren_surface_scale(ren);
RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale); RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale);
while (*p) { while (*p) {
p = utf8_to_codepoint(p, &codepoint); p = utf8_to_codepoint(p, &codepoint);
@ -259,8 +311,8 @@ int ren_get_font_width(FontDesc *font_desc, const char *text, int *subpixel_scal
} }
int ren_get_font_height(FontDesc *font_desc) { int ren_get_font_height(RenSurface *ren, FontDesc *font_desc) {
const int surface_scale = renwin_surface_scale(&window_renderer); const int surface_scale = ren_surface_scale(ren);
RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale); RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale);
return (font->height + surface_scale / 2) / surface_scale; return (font->height + surface_scale / 2) / surface_scale;
} }
@ -284,10 +336,10 @@ static inline RenColor blend_pixel(RenColor dst, RenColor src) {
d += dr; \ d += dr; \
} }
void ren_draw_rect(RenRect rect, RenColor color) { void ren_draw_rect(RenSurface *ren, RenRect rect, RenColor color) {
if (color.a == 0) { return; } if (color.a == 0) { return; }
const int surface_scale = renwin_surface_scale(&window_renderer); const int surface_scale = ren_surface_scale(ren);
/* transforms coordinates in pixels. */ /* transforms coordinates in pixels. */
rect.x *= surface_scale; rect.x *= surface_scale;
@ -295,7 +347,7 @@ void ren_draw_rect(RenRect rect, RenColor color) {
rect.width *= surface_scale; rect.width *= surface_scale;
rect.height *= surface_scale; rect.height *= surface_scale;
const RenRect clip = window_renderer.clip; const RenRect clip = ren_surface_clip(ren);
int x1 = rect.x < clip.x ? clip.x : rect.x; int x1 = rect.x < clip.x ? clip.x : rect.x;
int y1 = rect.y < clip.y ? clip.y : rect.y; int y1 = rect.y < clip.y ? clip.y : rect.y;
int x2 = rect.x + rect.width; int x2 = rect.x + rect.width;
@ -303,7 +355,7 @@ void ren_draw_rect(RenRect rect, RenColor color) {
x2 = x2 > clip.x + clip.width ? clip.x + clip.width : x2; x2 = x2 > clip.x + clip.width ? clip.x + clip.width : x2;
y2 = y2 > clip.y + clip.height ? clip.y + clip.height : y2; y2 = y2 > clip.y + clip.height ? clip.y + clip.height : y2;
SDL_Surface *surface = renwin_get_surface(&window_renderer); SDL_Surface *surface = ren_surface_get_surface(ren);
RenColor *d = (RenColor*) surface->pixels; RenColor *d = (RenColor*) surface->pixels;
d += x1 + y1 * surface->w; d += x1 + y1 * surface->w;
int dr = surface->w - (x2 - x1); int dr = surface->w - (x2 - x1);
@ -333,11 +385,11 @@ static FR_Clip_Area clip_area_from_rect(const RenRect r) {
} }
static void draw_text_impl(RenFont *font, const char *text, int x_subpixel, int y_pixel, RenColor color, static void draw_text_impl(RenSurface *ren, RenFont *font, const char *text, int x_subpixel, int y_pixel, RenColor color,
CPReplaceTable *replacements, RenColor replace_color) CPReplaceTable *replacements, RenColor replace_color)
{ {
SDL_Surface *surf = renwin_get_surface(&window_renderer); SDL_Surface *surf = ren_surface_get_surface(ren);
FR_Clip_Area clip = clip_area_from_rect(window_renderer.clip); FR_Clip_Area clip = clip_area_from_rect(ren_surface_clip(ren));
const char *p = text; const char *p = text;
unsigned codepoint; unsigned codepoint;
const FR_Color color_fr = { .r = color.r, .g = color.g, .b = color.b }; const FR_Color color_fr = { .r = color.r, .g = color.g, .b = color.b };
@ -364,21 +416,21 @@ static void draw_text_impl(RenFont *font, const char *text, int x_subpixel, int
} }
void ren_draw_text_subpixel(FontDesc *font_desc, const char *text, int x_subpixel, int y, RenColor color, void ren_draw_text_subpixel(RenSurface *ren, FontDesc *font_desc, const char *text, int x_subpixel, int y, RenColor color,
CPReplaceTable *replacements, RenColor replace_color) CPReplaceTable *replacements, RenColor replace_color)
{ {
const int surface_scale = renwin_surface_scale(&window_renderer); const int surface_scale = ren_surface_scale(ren);
RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale); RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale);
draw_text_impl(font, text, x_subpixel, surface_scale * y, color, replacements, replace_color); draw_text_impl(ren, font, text, x_subpixel, surface_scale * y, color, replacements, replace_color);
} }
void ren_draw_text(FontDesc *font_desc, const char *text, int x, int y, RenColor color, void ren_draw_text(RenSurface *ren, FontDesc *font_desc, const char *text, int x, int y, RenColor color,
CPReplaceTable *replacements, RenColor replace_color) CPReplaceTable *replacements, RenColor replace_color)
{ {
const int surface_scale = renwin_surface_scale(&window_renderer); const int surface_scale = ren_surface_scale(ren);
RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale); RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale);
const int subpixel_scale = surface_scale * FR_Subpixel_Scale(font->renderer); const int subpixel_scale = surface_scale * FR_Subpixel_Scale(font->renderer);
draw_text_impl(font, text, subpixel_scale * x, surface_scale * y, color, replacements, replace_color); draw_text_impl(ren, font, text, subpixel_scale * x, surface_scale * y, color, replacements, replace_color);
} }
// Could be declared as static inline // Could be declared as static inline
@ -395,8 +447,17 @@ int ren_font_subpixel_round(int width, int subpixel_scale, int orientation) {
} }
int ren_get_font_subpixel_scale(FontDesc *font_desc) { int ren_get_font_subpixel_scale(RenSurface *ren, FontDesc *font_desc) {
const int surface_scale = renwin_surface_scale(&window_renderer); const int surface_scale = ren_surface_scale(ren);
RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale); RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale);
return FR_Subpixel_Scale(font->renderer) * surface_scale; return FR_Subpixel_Scale(font->renderer) * surface_scale;
} }
void ren_draw_image(RenSurface *ren, uint32_t image_id, int image_x, int image_y, RenRect rect) {
SDL_Rect image_rect;
SDL_Surface *image_surf = image_get(image_id, &image_rect, image_x, image_y);
SDL_Surface *surface = ren_surface_get_surface(ren);
SDL_Rect dst_rect = {rect.x, rect.y, 0, 0};
SDL_BlitSurface(image_surf,image_rect, surface, &dst_rect);
}

View File

@ -21,6 +21,23 @@ enum {
typedef struct { uint8_t b, g, r, a; } RenColor; typedef struct { uint8_t b, g, r, a; } RenColor;
typedef struct { int x, y, width, height; } RenRect; typedef struct { int x, y, width, height; } RenRect;
// FIXME: ensure this Max is okay or use dynamic allocation.
#define TEXTURE_REVS_MAX 256
struct RenTexture {
SDL_Surface *surface;
int surface_scale;
RenRect clip; /* Clipping rect in pixel coordinates. */
int revisions[TEXTURE_REVS_MAX];
};
typedef struct RenTexture RenTexture;
enum { SurfaceTexture, SurfaceWindow };
typedef struct {
int type; /* Type of surface, RenSurfaceTexture or RenSurfaceWindow. */
void *data; /* Can be a RenTexture or RenWindow pointer based on type. */
} RenSurface;
struct CPReplace { struct CPReplace {
unsigned codepoint_src; unsigned codepoint_src;
unsigned codepoint_dst; unsigned codepoint_dst;
@ -34,14 +51,14 @@ struct CPReplaceTable {
}; };
typedef struct CPReplaceTable CPReplaceTable; typedef struct CPReplaceTable CPReplaceTable;
void ren_init(SDL_Window *win); void ren_init(SDL_Window *win);
void ren_resize_window(); void ren_resize_window();
void ren_update_rects(RenRect *rects, int count);
void ren_set_clip_rect(RenRect rect);
void ren_get_size(int *x, int *y); /* Reports the size in points. */
void ren_free_window_resources(); void ren_free_window_resources();
void ren_update_rects(RenSurface *ren, RenRect *rects, int count);
void ren_set_clip_rect(RenSurface *ren, RenRect rect);
void ren_get_size(RenSurface *ren, int *x, int *y); /* Reports the size in points. */
RenImage* ren_new_image(int width, int height); RenImage* ren_new_image(int width, int height);
void ren_free_image(RenImage *image); void ren_free_image(RenImage *image);
@ -51,18 +68,21 @@ void ren_free_font(RenFont *font);
void ren_set_font_tab_size(RenFont *font, int n); void ren_set_font_tab_size(RenFont *font, int n);
int ren_get_font_tab_size(RenFont *font); int ren_get_font_tab_size(RenFont *font);
int ren_get_font_width(FontDesc *font_desc, const char *text, int *subpixel_scale); int ren_get_font_width(RenSurface *ren, FontDesc *font_desc, const char *text, int *subpixel_scale);
int ren_get_font_height(FontDesc *font_desc); int ren_get_font_height(RenSurface *ren, FontDesc *font_desc);
int ren_get_font_subpixel_scale(FontDesc *font_desc); int ren_get_font_subpixel_scale(RenSurface *ren, FontDesc *font_desc);
int ren_font_subpixel_round(int width, int subpixel_scale, int orientation); int ren_font_subpixel_round(int width, int subpixel_scale, int orientation);
void ren_draw_rect(RenRect rect, RenColor color); void ren_draw_rect(RenSurface *ren, RenRect rect, RenColor color);
void ren_draw_text(FontDesc *font_desc, const char *text, int x, int y, RenColor color, CPReplaceTable *replacements, RenColor replace_color); void ren_draw_text(RenSurface *ren, FontDesc *font_desc, const char *text, int x, int y, RenColor color, CPReplaceTable *replacements, RenColor replace_color);
void ren_draw_text_subpixel(FontDesc *font_desc, const char *text, int x_subpixel, int y, RenColor color, CPReplaceTable *replacements, RenColor replace_color); void ren_draw_text_subpixel(RenSurface *ren, FontDesc *font_desc, const char *text, int x_subpixel, int y, RenColor color, CPReplaceTable *replacements, RenColor replace_color);
void ren_draw_image(RenSurface *ren, uint32_t image_id, int image_x, int image_y, RenRect rect);
void ren_cp_replace_init(CPReplaceTable *rep_table); void ren_cp_replace_init(CPReplaceTable *rep_table);
void ren_cp_replace_free(CPReplaceTable *rep_table); void ren_cp_replace_free(CPReplaceTable *rep_table);
void ren_cp_replace_add(CPReplaceTable *rep_table, const char *src, const char *dst); void ren_cp_replace_add(CPReplaceTable *rep_table, const char *src, const char *dst);
void ren_cp_replace_clear(CPReplaceTable *rep_table); void ren_cp_replace_clear(CPReplaceTable *rep_table);
extern RenSurface window_ren_surface[1];
#endif #endif

View File

@ -1,9 +1,11 @@
#include <SDL.h> #include <SDL.h>
#include <stdbool.h>
#include "renderer.h" #include "renderer.h"
struct RenWindow { struct RenWindow {
SDL_Window *window; SDL_Window *window;
RenRect clip; /* Clipping rect in pixel coordinates. */ RenRect clip; /* Clipping rect in pixel coordinates. */
bool initial_frame;
#ifdef LITE_USE_SDL_RENDERER #ifdef LITE_USE_SDL_RENDERER
SDL_Renderer *renderer; SDL_Renderer *renderer;
SDL_Texture *texture; SDL_Texture *texture;