experimental threaded xray #1607

This commit is contained in:
nick black 2021-04-30 02:56:20 -04:00 committed by Nick Black
parent 9a1499bd02
commit 5947f3d4d2
2 changed files with 174 additions and 17 deletions

View File

@ -342,6 +342,10 @@ the standard pile. This ought become a reality soon.
Sprixels interact poorly with multiple planes, and such usage is discouraged.
This situation might improve in the future.
Multiple threads may not currently call **ncvisual_render** concurrently
using the same **ncvisual**, even if targeting distinct **ncplane**s. This
will likely change in the future.
# SEE ALSO
**notcurses(3)**,

View File

@ -1,4 +1,6 @@
#include "demo.h"
#include <pthread.h>
#include <stdatomic.h>
// FIXME turn this into one large plane and move the plane, ratrher than
// manually redrawing each time
@ -58,6 +60,114 @@ make_slider(struct notcurses* nc, int dimy, int dimx){
return n;
}
static atomic_bool cancelled;
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t render_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
// initialized per run
static struct marsh {
struct notcurses* nc;
struct ncvisual* ncv; // video stream
// this state is guarded by the lock; always signal after an update.
int next_frame; // next frame to render. first thread to get the
// lock will be doing even frames, and the other
// thread will be doing odd.
int last_frame_rendered; // a thread cannot render the standard pile using
// its plane N until last_frame_rendered >= N - 1.
int last_frame_written; // a thread cannot rasterize and write its frame N
// until last_frame_written >= N - 1.
struct ncplane* slider; // text plane at top, sliding to the left
float dm; // delay multiplier
} marsh;
// returns the index of the next frame, which can immediately begin to be
// rendered onto the thread's plane. last_frame ought be -1 the first time
// a thread calls this function.
static int
get_next_frame(struct ncvisual* ncv, struct ncvisual_options* vopts,
int last_frame){
int ret;
pthread_mutex_lock(&lock);
if(last_frame < 0){
ret = marsh.next_frame++;
}else{
while(marsh.next_frame != last_frame + 2){
pthread_cond_wait(&cond, &lock);
}
++marsh.next_frame;
ret = last_frame + 2;
if(ncvisual_decode(ncv)){
ret = -1;
}else if(ncvisual_render(marsh.nc, ncv, vopts) == NULL){
ret = -1;
}
}
pthread_mutex_unlock(&lock);
if(ret == last_frame + 2 || ret == -1){
pthread_cond_signal(&cond);
}
return ret;
}
static void*
xray_thread(void *vplane){
int frame = -1;
struct ncvisual_options vopts = {
.x = NCALIGN_CENTER,
.y = NCALIGN_CENTER,
.scaling = NCSCALE_STRETCH,
.n = vplane,
.blitter = NCBLIT_PIXEL,
.flags = NCVISUAL_OPTION_VERALIGNED | NCVISUAL_OPTION_HORALIGNED
| NCVISUAL_OPTION_ADDALPHA,
};
while(!cancelled){
frame = get_next_frame(marsh.ncv, &vopts, frame);
if(frame < 0){
cancelled = true;
return NULL;
}
// only one thread can render the standard pile at a time
pthread_mutex_lock(&render_lock);
while(marsh.last_frame_rendered + 1 != frame){
pthread_cond_wait(&cond, &render_lock);
}
pthread_mutex_unlock(&render_lock);
int x = ncplane_x(marsh.slider);
if(ncplane_move_yx(marsh.slider, -1, x - 1)){
cancelled = true;
return NULL;
}
// FIXME swap our plane into stdplane, and swap old one out
if(ncpile_render(vopts.n)){
cancelled = true;
return NULL;
}
// and only one thread can write at a time
pthread_mutex_lock(&render_lock);
marsh.last_frame_rendered = frame;
pthread_cond_signal(&cond);
while(marsh.last_frame_written + 1 != frame){
pthread_cond_wait(&cond, &render_lock);
}
pthread_mutex_unlock(&render_lock);
if(ncpile_rasterize(vopts.n)){
cancelled = true;
return NULL;
}
pthread_mutex_lock(&render_lock);
marsh.last_frame_written = frame;
pthread_mutex_unlock(&render_lock);
pthread_cond_signal(&cond);
}
return NULL;
}
static int
perframecb(struct ncvisual* ncv, struct ncvisual_options* vopts,
const struct timespec* tspec, void* vnewplane){
@ -80,6 +190,27 @@ perframecb(struct ncvisual* ncv, struct ncvisual_options* vopts,
return 0;
}
// make two planes, both the size of the standard plane less one row at the
// bottom. the first is in the standard pile, but the second is in its own.
static int
make_planes(struct notcurses* nc, struct ncplane** t1, struct ncplane** t2){
int dimy, dimx;
struct ncplane* stdn = notcurses_stddim_yx(nc, &dimy, &dimx);
// FIXME want a resizecb
struct ncplane_options opts = {
.rows = dimy - 1,
.cols = dimx,
};
*t1 = ncplane_create(stdn, &opts);
*t2 = ncpile_create(nc, &opts);
if(!*t1 || !*t2){
ncplane_destroy(*t1);
ncplane_destroy(*t2);
return -1;
}
return 0;
}
int xray_demo(struct notcurses* nc){
if(!notcurses_canopen_videos(nc)){
return 0;
@ -93,8 +224,8 @@ int xray_demo(struct notcurses* nc){
if(ncv == NULL){
return -1;
}
struct ncplane* newpanel = make_slider(nc, dimy, dimx);
if(newpanel == NULL){
struct ncplane* slider = make_slider(nc, dimy, dimx);
if(slider == NULL){
ncvisual_destroy(ncv);
return -1;
}
@ -102,23 +233,45 @@ int xray_demo(struct notcurses* nc){
ncchannels_set_fg_alpha(&stdc, CELL_ALPHA_TRANSPARENT);
ncchannels_set_bg_alpha(&stdc, CELL_ALPHA_TRANSPARENT);
ncplane_set_base(notcurses_stdplane(nc), "", 0, stdc);
struct ncvisual_options vopts = {
.y = NCALIGN_CENTER,
.x = NCALIGN_CENTER,
.scaling = NCSCALE_SCALE_HIRES,
.blitter = NCBLIT_PIXEL,
.flags = NCVISUAL_OPTION_NODEGRADE // to test for NCBLIT_PIXEL
| NCVISUAL_OPTION_VERALIGNED | NCVISUAL_OPTION_HORALIGNED
| NCVISUAL_OPTION_ADDALPHA,
};
float dm = 0;
// returns non-zero if the selected blitter isn't available
if(ncvisual_blitter_geom(nc, ncv, &vopts, NULL, NULL, NULL, NULL, NULL)){
vopts.flags &= ~NCVISUAL_OPTION_NODEGRADE;
dm = 0.5 * delaymultiplier;
if(notcurses_check_pixel_support(nc) < 1){
marsh.dm = 0.5 * delaymultiplier;
}
int ret = ncvisual_stream(nc, ncv, dm, perframecb, &vopts, newpanel);
pthread_t tid1, tid2;
struct ncplane* t1;
struct ncplane* t2;
if(make_planes(nc, &t1, &t2)){
ncvisual_destroy(ncv);
ncplane_destroy(slider);
return -1;
}
marsh.slider = slider;
marsh.nc = nc;
marsh.ncv = ncv;
marsh.next_frame = 0;
marsh.last_frame_rendered = -1;
marsh.last_frame_written = -1;
cancelled = false;
if(pthread_create(&tid1, NULL, xray_thread, t1)){
ncvisual_destroy(ncv);
ncplane_destroy(slider);
ncplane_destroy(t1);
ncplane_destroy(t2);
return -1;
}
if(pthread_create(&tid2, NULL, xray_thread, t2)){
cancelled = 1;
pthread_join(tid1, NULL);
ncvisual_destroy(ncv);
ncplane_destroy(slider);
ncplane_destroy(t1);
ncplane_destroy(t2);
return -1;
}
int ret = pthread_join(tid1, NULL) | pthread_join(tid2, NULL);
ncvisual_destroy(ncv);
ncplane_destroy(newpanel);
ncplane_destroy(slider);
ncplane_destroy(t1);
ncplane_destroy(t2);
return ret;
}