mirror of
https://github.com/dankamongmen/notcurses
synced 2025-03-09 09:09:03 -04:00
[yield] two threads #1613
This commit is contained in:
parent
d3d418bb07
commit
c2ed7d19fe
2
NEWS.md
2
NEWS.md
@ -12,7 +12,7 @@ rearrangements of Notcurses.
|
||||
deprecated functionality, ABI3 ought require small changes, if any.
|
||||
|
||||
* 2.4.8 (not yet released)
|
||||
* Added `notcurses_canpixel()` and `notcurses_osversion()`.
|
||||
* Added new functions `notcurses_canpixel()` and `notcurses_osversion()`.
|
||||
* `notcurses_get()` now evaluates its timeout against `CLOCK_MONOTONIC`
|
||||
instead of `CLOCK_REALTIME`.
|
||||
|
||||
|
247
src/demo/yield.c
247
src/demo/yield.c
@ -1,65 +1,122 @@
|
||||
#include <pthread.h>
|
||||
#include "demo.h"
|
||||
|
||||
#define MAXITER 1024
|
||||
// the only dynamic information which needs be shared between the two threads
|
||||
// is the last polyfill origin, the total filled count, and whose turn it is.
|
||||
|
||||
static int maxy;
|
||||
static int maxx;
|
||||
static int total;
|
||||
|
||||
struct marsh {
|
||||
int total;
|
||||
int maxy;
|
||||
int maxx;
|
||||
struct ncvisual* wmv; // single, shared between threads
|
||||
int id; // unique for each thread, mono-increasing
|
||||
int* turn; // whose turn it is to prep the ncvisual, shared
|
||||
int* rturn; // whose turn it is to render, shared
|
||||
int* polyy; // last shared polyfill origin (y)
|
||||
int* polyx; // last shared polyfill origin (x)
|
||||
uint32_t* polypixel; // last shared polyfill pixel (rgba)
|
||||
int* filled; // shared, how many have we filled?
|
||||
unsigned* done; // shared, are we done?
|
||||
struct ncvisual* ncv; // our copy of the ncv
|
||||
struct ncplane* label; // single, shared between threads
|
||||
struct notcurses* nc;
|
||||
struct ncvisual_options* vopts;
|
||||
struct ncvisual_options vopts; // each has their own copy
|
||||
struct timespec tspec; // delay param, copy per thread
|
||||
};
|
||||
|
||||
// guard turn
|
||||
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
|
||||
|
||||
// guard rturn
|
||||
static pthread_mutex_t rlock = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_cond_t rcond = PTHREAD_COND_INITIALIZER;
|
||||
|
||||
static int
|
||||
yielder(const void* vmarsh){
|
||||
const struct marsh* m = vmarsh;
|
||||
long tfilled = 0;
|
||||
yielder(struct marsh* m){
|
||||
#define MAXITER (1024 / 2)
|
||||
int iters = 0;
|
||||
// less than this, and we exit almost immediately. more than this, and we
|
||||
// run closer to twenty seconds. 11/50 it is, then. pixels are different.
|
||||
const long threshold_painted = m->total * 11 / 50;
|
||||
while(tfilled < threshold_painted && iters < MAXITER){
|
||||
//fprintf(stderr, "%d/%d tfilled: %ld thresh: %ld total: %ld\n", m->maxy, m->maxx, tfilled, threshold_painted, m->total);
|
||||
const long threshold_painted = total * 11 / 50;
|
||||
while(*m->filled < threshold_painted && iters < MAXITER && !*m->done){
|
||||
//fprintf(stderr, "%d/%d tfilled: %ld thresh: %ld total: %ld\n", maxy, maxx, *m->filled, threshold_painted, total);
|
||||
int pfilled = 0;
|
||||
pthread_mutex_lock(&lock);
|
||||
while(*m->turn != m->id && !*m->done){
|
||||
pthread_cond_wait(&cond, &lock);
|
||||
}
|
||||
// the first time the first thread runs, it does not pick up the previous
|
||||
// polyfill origin (as there was none). all other runs, we do.
|
||||
if(iters || m->id){
|
||||
ncvisual_polyfill_yx(m->ncv, *m->polyy, *m->polyx, *m->polypixel);
|
||||
}
|
||||
do{
|
||||
++iters;
|
||||
int x = rand() % m->maxx;
|
||||
int y = rand() % m->maxy;
|
||||
uint32_t pixel = 0;
|
||||
if(ncvisual_at_yx(m->wmv, y, x, &pixel) < 0){
|
||||
return -1;
|
||||
}
|
||||
if(ncpixel_a(pixel) != 0xff){ // don't do areas we've already done
|
||||
int x = rand() % maxx;
|
||||
int y = rand() % maxy;
|
||||
ncvisual_at_yx(m->ncv, y, x, m->polypixel);
|
||||
if(ncpixel_a(*m->polypixel) != 0xff){ // don't do areas we've already done
|
||||
continue;
|
||||
}
|
||||
if(ncpixel_g(pixel) < 0x80){ // only do land, which is whiter than blue
|
||||
if(ncpixel_g(*m->polypixel) < 0x80){ // only do land, which is whiter than blue
|
||||
continue;
|
||||
}
|
||||
ncpixel_set_a(&pixel, 0xfe);
|
||||
ncpixel_set_rgb8(&pixel, (rand() % 128) + 128, 0, ncpixel_b(pixel) / 4);
|
||||
pfilled = ncvisual_polyfill_yx(m->wmv, y, x, pixel);
|
||||
ncpixel_set_a(m->polypixel, 0xfe);
|
||||
ncpixel_set_rgb8(m->polypixel, (rand() % 128) + 128, 0, ncpixel_b(*m->polypixel) / 4);
|
||||
pfilled = ncvisual_polyfill_yx(m->ncv, y, x, *m->polypixel);
|
||||
if(pfilled < 0){
|
||||
return -1;
|
||||
}else if(pfilled){
|
||||
*m->polyy = y;
|
||||
*m->polyx = x;
|
||||
}
|
||||
// it's possible that nothing changed (pfilled == 0), but render anyway
|
||||
// so that it never looks like we locked up
|
||||
DEMO_RENDER(m->nc);
|
||||
}while(pfilled == 0);
|
||||
tfilled += pfilled;
|
||||
if(ncvisual_blit(m->nc, m->wmv, m->vopts) == NULL){
|
||||
*m->turn = !*m->turn;
|
||||
pthread_mutex_unlock(&lock);
|
||||
pthread_cond_signal(&cond);
|
||||
|
||||
pthread_mutex_lock(&rlock);
|
||||
while(*m->rturn != m->id && !*m->done){
|
||||
pthread_cond_wait(&rcond, &rlock);
|
||||
}
|
||||
ncplane_printf_aligned(m->label, 0, NCALIGN_CENTER, "Yield: %3.1f%%", ((double)*m->filled * 100) / threshold_painted);
|
||||
ncplane_reparent(m->vopts.n, m->label);
|
||||
ncplane_move_below(m->vopts.n, m->label);
|
||||
if(ncvisual_blit(m->nc, m->ncv, &m->vopts) == NULL){
|
||||
return -1;
|
||||
}
|
||||
if(tfilled > threshold_painted){
|
||||
tfilled = threshold_painted; // don't allow printing of 100.1% etc
|
||||
*m->filled += pfilled;
|
||||
if(*m->filled > threshold_painted){
|
||||
*m->filled = threshold_painted; // don't allow printing of 100.1% etc
|
||||
}
|
||||
ncplane_printf_aligned(m->label, 0, NCALIGN_CENTER, "Yield: %3.1f%%", ((double)tfilled * 100) / threshold_painted);
|
||||
DEMO_RENDER(m->nc);
|
||||
if(demo_render(m->nc)){
|
||||
pthread_mutex_unlock(&rlock);
|
||||
return -1;
|
||||
}
|
||||
*m->rturn = !*m->rturn;
|
||||
ncplane_reparent(m->vopts.n, m->vopts.n);
|
||||
pthread_mutex_unlock(&rlock);
|
||||
pthread_cond_signal(&rcond);
|
||||
// FIXME only sleep if we didn't take enough time updating ncvisual!
|
||||
demo_nanosleep(m->nc, &m->tspec);
|
||||
}
|
||||
pthread_mutex_lock(&lock);
|
||||
*m->done = 1;
|
||||
pthread_mutex_unlock(&lock);
|
||||
pthread_cond_signal(&cond);
|
||||
pthread_cond_signal(&rcond);
|
||||
return 0;
|
||||
#undef MAXITER
|
||||
}
|
||||
|
||||
static void*
|
||||
yielder_thread(void* vmarsh){
|
||||
struct marsh* m = vmarsh;
|
||||
if(yielder(m)){
|
||||
return NULL;
|
||||
}
|
||||
return NULL; // FIXME indicate failure/success
|
||||
}
|
||||
|
||||
int yield_demo(struct notcurses* nc){
|
||||
@ -73,39 +130,22 @@ int yield_demo(struct notcurses* nc){
|
||||
// flicker, but it does minimize it.
|
||||
ncplane_erase(std);
|
||||
char* pic = find_data("worldmap.png");
|
||||
struct ncvisual* wmv = ncvisual_from_file(pic);
|
||||
struct ncvisual* v1 = ncvisual_from_file(pic);
|
||||
struct ncvisual* v2 = ncvisual_from_file(pic);
|
||||
free(pic);
|
||||
if(wmv == NULL){
|
||||
if(v1 == NULL || v2 == NULL){
|
||||
ncvisual_destroy(v1);
|
||||
ncvisual_destroy(v2);
|
||||
return -1;
|
||||
}
|
||||
// can we do bitmaps?
|
||||
const bool bitmaps = (notcurses_check_pixel_support(nc) > 0);
|
||||
struct ncplane_options nopts = {
|
||||
.y = 1,
|
||||
.rows = dimy - 1,
|
||||
.rows = dimy - 1 - bitmaps, // FIXME
|
||||
.cols = dimx,
|
||||
.name = "wmap",
|
||||
};
|
||||
struct ncvisual_options vopts = {
|
||||
.scaling = NCSCALE_STRETCH,
|
||||
.blitter = NCBLIT_PIXEL,
|
||||
};
|
||||
vopts.n = ncplane_create(std, &nopts);
|
||||
if(vopts.n == NULL){
|
||||
ncvisual_destroy(wmv);
|
||||
return -1;
|
||||
}
|
||||
int maxy, maxx;
|
||||
if(ncvisual_blitter_geom(nc, wmv, &vopts, &maxy, &maxx, NULL, NULL, NULL)){
|
||||
ncvisual_destroy(wmv);
|
||||
ncplane_destroy(vopts.n);
|
||||
return -1;
|
||||
}
|
||||
if(ncvisual_blit(nc, wmv, &vopts) == NULL){
|
||||
ncvisual_destroy(wmv);
|
||||
ncplane_destroy(vopts.n);
|
||||
return -1;
|
||||
}
|
||||
struct ncplane_options labopts = {
|
||||
.y = 3,
|
||||
.x = NCALIGN_CENTER,
|
||||
@ -116,21 +156,69 @@ int yield_demo(struct notcurses* nc){
|
||||
};
|
||||
struct ncplane* label = ncplane_create(std, &labopts);
|
||||
if(label == NULL){
|
||||
ncvisual_destroy(wmv);
|
||||
ncplane_destroy(vopts.n);
|
||||
ncvisual_destroy(v1);
|
||||
ncvisual_destroy(v2);
|
||||
return -1;
|
||||
}
|
||||
struct marsh marsh = {
|
||||
.total = maxy * maxx,
|
||||
.maxy = maxy,
|
||||
.maxx = maxx,
|
||||
.wmv = wmv,
|
||||
int turn = 0;
|
||||
int rturn = 0;
|
||||
unsigned done = 0;
|
||||
int filled = 0;
|
||||
uint32_t polypixel = 0;
|
||||
int polyx = 0;
|
||||
int polyy = 0;
|
||||
struct marsh m1 = {
|
||||
.id = 0,
|
||||
.label = label,
|
||||
.nc = nc,
|
||||
.vopts = &vopts,
|
||||
.ncv = v1,
|
||||
.done = &done,
|
||||
.polyy = &polyy,
|
||||
.polyx = &polyx,
|
||||
.polypixel = &polypixel,
|
||||
.turn = &turn,
|
||||
.filled = &filled,
|
||||
.rturn = &rturn,
|
||||
.vopts = {
|
||||
.scaling = NCSCALE_STRETCH,
|
||||
.blitter = NCBLIT_PIXEL,
|
||||
},
|
||||
};
|
||||
m1.vopts.n = ncplane_create(std, &nopts);
|
||||
if(m1.vopts.n == NULL){
|
||||
ncvisual_destroy(v1);
|
||||
ncvisual_destroy(v2);
|
||||
ncplane_destroy(label);
|
||||
return -1;
|
||||
}
|
||||
struct marsh m2 = {
|
||||
.id = 1,
|
||||
.label = label,
|
||||
.nc = nc,
|
||||
.ncv = v2,
|
||||
.done = &done,
|
||||
.polyy = &polyy,
|
||||
.polyx = &polyx,
|
||||
.polypixel = &polypixel,
|
||||
.turn = &turn,
|
||||
.filled = &filled,
|
||||
.rturn = &rturn,
|
||||
.vopts = {
|
||||
.scaling = NCSCALE_STRETCH,
|
||||
.blitter = NCBLIT_PIXEL,
|
||||
},
|
||||
};
|
||||
m2.vopts.n = ncpile_create(nc, &nopts);
|
||||
if(m2.vopts.n == NULL){
|
||||
ncvisual_destroy(v1);
|
||||
ncvisual_destroy(v2);
|
||||
ncplane_destroy(label);
|
||||
ncplane_destroy(m1.vopts.n);
|
||||
return -1;
|
||||
}
|
||||
if(bitmaps){
|
||||
timespec_div(&demodelay, 10, &marsh.tspec);
|
||||
timespec_div(&demodelay, 10, &m1.tspec);
|
||||
timespec_div(&demodelay, 10, &m2.tspec);
|
||||
}
|
||||
uint64_t basechan = 0;
|
||||
ncchannels_set_bg_alpha(&basechan, NCALPHA_TRANSPARENT);
|
||||
@ -140,15 +228,38 @@ int yield_demo(struct notcurses* nc){
|
||||
ncplane_set_fg_rgb8(label, 0xff, 0xff, 0xff);
|
||||
ncplane_set_styles(label, NCSTYLE_BOLD);
|
||||
ncplane_printf_aligned(label, 0, NCALIGN_CENTER, "Yield: %03.1f%%", 0.0);
|
||||
if(ncvisual_blitter_geom(nc, v1, &m1.vopts, &maxy, &maxx, NULL, NULL, NULL)){
|
||||
ncplane_destroy(label);
|
||||
ncvisual_destroy(v1);
|
||||
ncvisual_destroy(v2);
|
||||
ncplane_destroy(m2.vopts.n);
|
||||
ncplane_destroy(m1.vopts.n);
|
||||
return -1;
|
||||
}
|
||||
if(ncvisual_blit(nc, v1, &m1.vopts) == NULL){
|
||||
ncplane_destroy(label);
|
||||
ncvisual_destroy(v1);
|
||||
ncvisual_destroy(v2);
|
||||
ncplane_destroy(m2.vopts.n);
|
||||
ncplane_destroy(m1.vopts.n);
|
||||
return -1;
|
||||
}
|
||||
total = maxx * maxy;
|
||||
|
||||
DEMO_RENDER(nc);
|
||||
demo_nanosleep(nc, &demodelay);
|
||||
|
||||
int ret = yielder(&marsh);
|
||||
pthread_t t1, t2;
|
||||
int ret = 0;
|
||||
// FIXME error checks
|
||||
pthread_create(&t1, NULL, yielder_thread, &m1);
|
||||
pthread_create(&t2, NULL, yielder_thread, &m2);
|
||||
ret = pthread_join(t1, NULL) | pthread_join(t2, NULL);
|
||||
|
||||
ncplane_destroy(label);
|
||||
ncvisual_destroy(wmv);
|
||||
ncplane_destroy(vopts.n);
|
||||
ncplane_destroy(m1.vopts.n);
|
||||
ncplane_destroy(m2.vopts.n);
|
||||
ncvisual_destroy(v1);
|
||||
ncvisual_destroy(v2);
|
||||
ncplane_erase(std);
|
||||
return ret;
|
||||
}
|
||||
|
@ -186,9 +186,11 @@ int sprite_wipe(const notcurses* nc, sprixel* s, int ycell, int xcell){
|
||||
// update said auxvec, but needn't actually change the glyph. auxvec will
|
||||
// be entirely 0s coming from pixel_trans_auxvec().
|
||||
if(s->n->tam[idx].auxvector == NULL){
|
||||
s->n->tam[idx].auxvector = nc->tcache.pixel_trans_auxvec(&nc->tcache);
|
||||
if(s->n->tam[idx].auxvector == NULL){
|
||||
return -1;
|
||||
if(nc->tcache.pixel_trans_auxvec){
|
||||
s->n->tam[idx].auxvector = nc->tcache.pixel_trans_auxvec(&nc->tcache);
|
||||
if(s->n->tam[idx].auxvector == NULL){
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
// no need to update to INVALIDATED; no redraw is necessary
|
||||
|
Loading…
x
Reference in New Issue
Block a user