break apart render/rasterize #155

pull/274/head
nick black 5 years ago
parent d9b721971a
commit 56a54b5441
No known key found for this signature in database
GPG Key ID: 5F43400C21CBFACC

@ -1491,13 +1491,6 @@ cell_noforeground_p(const cell* c){
return cell_simple_p(c) && isspace(c->gcluster);
}
// True if the cell does not generate background pixels. Only the FULL BLOCK
// glyph has this property, AFAIK.
static inline bool
cell_nobackground_p(const struct ncplane* n, const cell* c){
return !cell_simple_p(c) && !strcmp(cell_extended_gcluster(n, c), "\xe2\x96\x88");
}
static inline int
cell_load_simple(struct ncplane* n, cell* c, char ch){
cell_release(n, c);

@ -320,6 +320,14 @@ cell_debug(const ncplane* p, const cell* c){
}
}
// True if the cell does not generate background pixels. Only the FULL BLOCK
// glyph has this property, AFAIK.
// FIXME set a bit, doing this at load time
static inline bool
cell_nobackground_p(const egcpool* e, const cell* c){
return !cell_simple_p(c) && !strcmp(egcpool_extended_gcluster(e, c), "\xe2\x96\x88");
}
// no CLOCK_MONOTONIC_RAW on FreeBSD as of 12.0 :/
#ifndef CLOCK_MONOTONIC_RAW
#define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC

@ -11,6 +11,7 @@ mutex_unlock(void* vlock){
static int
blocking_write(int fd, const char* buf, size_t buflen){
//fprintf(stderr, "writing %zu to %d...\n", buflen, fd);
size_t written = 0;
do{
ssize_t w = write(fd, buf + written, buflen - written);
@ -82,7 +83,7 @@ update_render_stats(const struct timespec* time1, const struct timespec* time0,
// 256 colors, this is the 16 normal ones, 6x6x6 color cubes, and 32 greys.
// it's probably better to sample the darker regions rather than cover so much
// chroma, but whatever....FIXME
static inline int
/*static inline int
prep_optimized_palette(notcurses* nc, FILE* out __attribute__ ((unused))){
if(nc->RGBflag){
return 0; // DirectColor, no need to write palette
@ -92,7 +93,7 @@ prep_optimized_palette(notcurses* nc, FILE* out __attribute__ ((unused))){
}
// FIXME
return 0;
}
}*/
// reshape the shadow framebuffer to match the stdplane's dimensions, throwing
// away the old one.
@ -244,7 +245,7 @@ visible_cell(cell* c, int y, int x, ncplane* n, int* previousz){
// number of columns occupied by this EGC (only an approximation; it's actually
// a property of the font being used).
static int
term_putc(FILE* out, const ncplane* n, const cell* c){
term_putc(FILE* out, const egcpool* e, const cell* c){
if(cell_simple_p(c)){
if(c->gcluster == 0 || iscntrl(c->gcluster)){
// fprintf(stderr, "[ ]\n");
@ -266,7 +267,7 @@ term_putc(FILE* out, const ncplane* n, const cell* c){
}
}
}else{
const char* ext = extended_gcluster(n, c);
const char* ext = egcpool_extended_gcluster(e, c);
// fprintf(stderr, "[%s]\n", ext);
#ifdef __USE_GNU
if(fputs_unlocked(ext, out) < 0){ // FIXME check for short write?
@ -483,23 +484,161 @@ cellcmp_and_dupfar(egcpool* dampool, cell* damcell, const ncplane* srcplane,
return 1;
}
// Producing the frame requires three steps:
// * render -- build up a flat framebuffer from a set of ncplanes
// * rasterize -- build up a UTF-8 stream of escapes and EGCs
// * refresh -- write the stream to the emulator
static inline int
notcurses_render_internal(notcurses* nc){
notcurses_rasterize(notcurses* nc, unsigned char* damagemap){
FILE* out = nc->rstate.mstreamfp;
int ret = 0;
int y, x;
FILE* out = nc->rstate.mstreamfp;
fseeko(out, 0, SEEK_SET);
// we only need to emit a coordinate if it was damaged. the damagemap is a
// bit per coordinate, rows by rows, column by column within a row, with the
// MSB being the first coordinate.
size_t damageidx = 0;
unsigned char damagemask = 0x80;
// don't write a clearscreen. we only update things that have been changed.
// we explicitly move the cursor at the beginning of each output line, so no
// need to home it expliticly.
prep_optimized_palette(nc, out); // FIXME do what on failure?
// if this fails, struggle bravely on. we can live without a lastframe.
reshape_shadow_fb(nc);
for(y = 0 ; y < nc->stdscr->leny ; ++y){
// how many characters have we elided? it's not worthwhile to invoke a
// cursor movement with cup if we only elided one or two. set to INT_MAX
// whenever we're on a new line.
int needmove = INT_MAX;
for(x = 0 ; x < nc->stdscr->lenx ; ++x){
unsigned r, g, b, br, bg, bb;
const cell* srccell = &nc->lastframe[y * nc->lfdimx + x];
// cell c;
// memcpy(c, srccell, sizeof(*c)); // unsafe copy of gcluster
//fprintf(stderr, "COPYING: %d from %p\n", c->gcluster, &nc->pool);
// const char* egc = pool_egc_copy(&nc->pool, srccell);
// c->gcluster = 0; // otherwise cell_release() will blow up
//fprintf(stderr, "idx: %zu word: 0x%02x\n", damageidx, damagemap[damageidx]);
if((damagemap[damageidx] & damagemask) == 0){
// no need to emit a cell; what we rendered appears to already be
// here. no updates are performed to elision state nor lastframe.
++nc->stats.cellelisions;
if(needmove < INT_MAX){
++needmove;
}
if(cell_double_wide_p(srccell)){
if(needmove < INT_MAX){
++needmove;
}
++nc->stats.cellelisions;
}
}else{
++nc->stats.cellemissions;
if(needmove == 1 && nc->cuf1){
ret |= term_emit("cuf1", tiparm(nc->cuf1), out, false);
}else if(needmove){
ret |= term_emit("cup", tiparm(nc->cup, y, x), out, false);
}
needmove = 0;
// set the style. this can change the color back to the default; if it
// does, we need update our elision possibilities.
bool normalized;
ret |= term_setstyles(nc, out, &nc->rstate.curattr, srccell, &normalized);
if(normalized){
nc->rstate.defaultelidable = true;
nc->rstate.bgelidable = false;
nc->rstate.fgelidable = false;
}
// we allow these to be set distinctly, but terminfo only supports using
// them both via the 'op' capability. unless we want to generate the 'op'
// escapes ourselves, if either is set to default, we first send op, and
// then a turnon for whichever aren't default.
// if our cell has a default foreground *or* background, we can elide the
// default set iff one of:
// * we are a partial glyph, and the previous was default on both, or
// * we are a no-foreground glyph, and the previous was default background, or
// * we are a no-background glyph, and the previous was default foreground
bool noforeground = cell_noforeground_p(srccell);
bool nobackground = cell_nobackground_p(&nc->pool, srccell);
if((!noforeground && cell_fg_default_p(srccell)) || (!nobackground && cell_bg_default_p(srccell))){
if(!nc->rstate.defaultelidable){
++nc->stats.defaultemissions;
ret |= term_emit("op", nc->op, out, false);
}else{
++nc->stats.defaultelisions;
}
// if either is not default, this will get turned off
nc->rstate.defaultelidable = true;
nc->rstate.fgelidable = false;
nc->rstate.bgelidable = false;
}
// if our cell has a non-default foreground, we can elide the non-default
// foreground set iff either:
// * the previous was non-default, and matches what we have now, or
// * we are a no-foreground glyph (iswspace() is true)
if(!cell_fg_default_p(srccell)){
if(!noforeground){
cell_fg_rgb(srccell, &r, &g, &b);
if(nc->rstate.fgelidable && nc->rstate.lastr == r && nc->rstate.lastg == g && nc->rstate.lastb == b){
++nc->stats.fgelisions;
}else{
ret |= term_fg_rgb8(nc, out, r, g, b);
++nc->stats.fgemissions;
nc->rstate.fgelidable = true;
}
nc->rstate.lastr = r; nc->rstate.lastg = g; nc->rstate.lastb = b;
nc->rstate.defaultelidable = false;
}else{
++nc->stats.fgelisions;
}
}
if(!cell_bg_default_p(srccell)){
if(!nobackground){
cell_bg_rgb(srccell, &br, &bg, &bb);
if(nc->rstate.bgelidable && nc->rstate.lastbr == br && nc->rstate.lastbg == bg && nc->rstate.lastbb == bb){
++nc->stats.bgelisions;
}else{
ret |= term_bg_rgb8(nc, out, br, bg, bb);
++nc->stats.bgemissions;
nc->rstate.bgelidable = true;
}
nc->rstate.lastbr = br; nc->rstate.lastbg = bg; nc->rstate.lastbb = bb;
nc->rstate.defaultelidable = false;
}else{
++nc->stats.bgelisions;
}
}
//fprintf(stderr, "[%02d/%02d] 0x%02x 0x%02x 0x%02x\n", y, x, r, g, b);
ret |= term_putc(out, &nc->pool, srccell);
}
if((damagemask >>= 1u) == 0){
damagemask = 0x80;
++damageidx;
}
}
}
ret |= fflush(out);
//fflush(nc->ttyfp);
if(blocking_write(nc->ttyfd, nc->rstate.mstream, nc->rstate.mstrsize)){
ret = -1;
}
/*fprintf(stderr, "%lu/%lu %lu/%lu %lu/%lu\n", defaultelisions, defaultemissions,
fgelisions, fgemissions, bgelisions, bgemissions);*/
if(nc->renderfp){
fprintf(nc->renderfp, "%s\n", nc->rstate.mstream);
}
if(ret < 0){
return ret;
}
return nc->rstate.mstrsize;
}
static inline int
notcurses_render_internal(notcurses* nc, unsigned char* damagevec){
int ret = 0;
int y, x;
// if this fails, struggle bravely on. we can live without a lastframe.
reshape_shadow_fb(nc);
for(y = 0 ; y < nc->stdscr->leny ; ++y){
// track the depth of our glyph, to see if we need need to stomp a wide
// glyph we're following.
int depth = 0;
@ -507,7 +646,6 @@ notcurses_render_internal(notcurses* nc){
// anything, *BUT* we must handle higher planes bisecting our wide glyph.
bool inright = false;
for(x = 0 ; x < nc->stdscr->lenx ; ++x){
unsigned r, g, b, br, bg, bb;
ncplane* p;
cell c; // no need to initialize
p = visible_cell(&c, y, x, nc->top, &depth);
@ -517,7 +655,6 @@ notcurses_render_internal(notcurses* nc){
if((x + 1 >= nc->stdscr->lenx && cell_double_wide_p(&c))){
continue; // needmove will be reset as we restart the line
}
bool damaged = false;
if(depth > 0){ // we are above the previous source plane
if(inright){ // wipe out the character to the left
// FIXME do this by keeping an offset for the memstream, and
@ -527,16 +664,12 @@ notcurses_render_internal(notcurses* nc){
cell_init(prev);
// FIXME technically we need rerun the visible cell search...? gross
cell_load_simple(NULL, prev, ' ');
// FIXME this space will be the wrong color, methinks?
term_emit("cup", tiparm(nc->cup, y, x - 1), out, false);
fputc(' ', out);
inright = false;
//if(cell_simple_p(&c)){
//fprintf(stderr, "WENT BACK NOW FOR %c\n", c.gcluster);
//}else{
//fprintf(stderr, "WENT BACK NOW FOR %s\n", extended_gcluster(p, &c));
//}
damaged = true;
}
}
// lastframe has already been sized to match the current size, so no need
@ -548,120 +681,16 @@ notcurses_render_internal(notcurses* nc){
inright = false;
continue;
}
// check the damage map, unless we must repair damage done
if(cellcmp_and_dupfar(&nc->pool, oldcell, p, &c) == 0){
if(!damaged){
// no need to emit a cell; what we rendered appears to already be
// here. no updates are performed to elision state nor lastframe.
++nc->stats.cellelisions;
if(needmove < INT_MAX){
++needmove;
}
if(cell_double_wide_p(&c)){
if(needmove < INT_MAX){
++needmove;
}
++nc->stats.cellelisions;
inright = !inright;
}else{
inright = false;
}
continue;
}
}
}
++nc->stats.cellemissions;
if(needmove == 1 && nc->cuf1){
ret |= term_emit("cuf1", tiparm(nc->cuf1), out, false);
}else if(needmove){
ret |= term_emit("cup", tiparm(nc->cup, y, x), out, false);
}
needmove = 0;
// set the style. this can change the color back to the default; if it
// does, we need update our elision possibilities.
bool normalized;
term_setstyles(nc, out, &nc->rstate.curattr, &c, &normalized);
if(normalized){
nc->rstate.defaultelidable = true;
nc->rstate.bgelidable = false;
nc->rstate.fgelidable = false;
}
// we allow these to be set distinctly, but terminfo only supports using
// them both via the 'op' capability. unless we want to generate the 'op'
// escapes ourselves, if either is set to default, we first send op, and
// then a turnon for whichever aren't default.
// if our cell has a default foreground *or* background, we can elide the
// default set iff one of:
// * we are a partial glyph, and the previous was default on both, or
// * we are a no-foreground glyph, and the previous was default background, or
// * we are a no-background glyph, and the previous was default foreground
bool noforeground = cell_noforeground_p(&c);
bool nobackground = cell_nobackground_p(p, &c);
if((!noforeground && cell_fg_default_p(&c)) || (!nobackground && cell_bg_default_p(&c))){
if(!nc->rstate.defaultelidable){
++nc->stats.defaultemissions;
term_emit("op", nc->op, out, false);
}else{
++nc->stats.defaultelisions;
}
// if either is not default, this will get turned off
nc->rstate.defaultelidable = true;
nc->rstate.fgelidable = false;
nc->rstate.bgelidable = false;
}
// if our cell has a non-default foreground, we can elide the non-default
// foreground set iff either:
// * the previous was non-default, and matches what we have now, or
// * we are a no-foreground glyph (iswspace() is true)
if(!cell_fg_default_p(&c)){
if(!noforeground){
cell_fg_rgb(&c, &r, &g, &b);
if(nc->rstate.fgelidable && nc->rstate.lastr == r && nc->rstate.lastg == g && nc->rstate.lastb == b){
++nc->stats.fgelisions;
}else{
term_fg_rgb8(nc, out, r, g, b);
++nc->stats.fgemissions;
nc->rstate.fgelidable = true;
}
nc->rstate.lastr = r; nc->rstate.lastg = g; nc->rstate.lastb = b;
nc->rstate.defaultelidable = false;
}else{
++nc->stats.fgelisions;
}
}
if(!cell_bg_default_p(&c)){
if(!nobackground){
cell_bg_rgb(&c, &br, &bg, &bb);
if(nc->rstate.bgelidable && nc->rstate.lastbr == br && nc->rstate.lastbg == bg && nc->rstate.lastbb == bb){
++nc->stats.bgelisions;
}else{
term_bg_rgb8(nc, out, br, bg, bb);
++nc->stats.bgemissions;
nc->rstate.bgelidable = true;
}
nc->rstate.lastbr = br; nc->rstate.lastbg = bg; nc->rstate.lastbb = bb;
nc->rstate.defaultelidable = false;
}else{
++nc->stats.bgelisions;
// check the damage map
if(cellcmp_and_dupfar(&nc->pool, oldcell, p, &c)){
//fprintf(stderr, "setting damagevec idx %d mask %u\n", (y * nc->stdscr->lenx + x) / CHAR_BIT, (0x80 >> ((y * nc->stdscr->lenx + x) % CHAR_BIT)));
damagevec[(y * nc->stdscr->lenx + x) / CHAR_BIT] |=
(0x80 >> ((y * nc->stdscr->lenx + x) % CHAR_BIT));
}
}
//fprintf(stderr, "[%02d/%02d] 0x%02x 0x%02x 0x%02x %p\n", y, x, r, g, b, p);
term_putc(out, p, &c);
inright = cell_double_wide_p(&c);
}
}
ret |= fflush(out);
//fflush(nc->ttyfp);
if(blocking_write(nc->ttyfd, nc->rstate.mstream, nc->rstate.mstrsize)){
ret = -1;
}
/*fprintf(stderr, "%lu/%lu %lu/%lu %lu/%lu\n", defaultelisions, defaultemissions,
fgelisions, fgemissions, bgelisions, bgemissions);*/
if(nc->renderfp){
fprintf(nc->renderfp, "%s\n", nc->rstate.mstream);
}
if(ret){
return ret;
}
@ -674,7 +703,15 @@ int notcurses_render(notcurses* nc){
clock_gettime(CLOCK_MONOTONIC_RAW, &start);
pthread_mutex_lock(&nc->lock);
pthread_cleanup_push(mutex_unlock, &nc->lock);
int bytes = notcurses_render_internal(nc);
int bytes = -1;
size_t damagevecsize = nc->stdscr->leny * nc->stdscr->lenx / CHAR_BIT +
!!(nc->stdscr->leny * nc->stdscr->lenx % CHAR_BIT);
unsigned char* damagevec = malloc(damagevecsize);
memset(damagevec, 0, damagevecsize);
if(notcurses_render_internal(nc, damagevec) >= 0){
bytes = notcurses_rasterize(nc, damagevec);
}
free(damagevec);
int dimy, dimx;
notcurses_resize(nc, &dimy, &dimx);
clock_gettime(CLOCK_MONOTONIC_RAW, &done);

Loading…
Cancel
Save