diff options
Diffstat (limited to 'nebula2.c')
-rw-r--r-- | nebula2.c | 212 |
1 files changed, 192 insertions, 20 deletions
@@ -11,6 +11,35 @@ #include "render.h" #include "mutex_helpers.h" +#include "SFMT/SFMT.h" + +/* Bailout is currently hard coded... perhaps we should move this to the config file? */ +#define BAILOUT 8 + +inline static long +fast_floor(double input) { + return (long) input - (input > 0 ? 0 : 1); +} + +inline static void +calc_mandelbrot(double cx, double cy, double* zx, double* zy) { + double ty; + ty = (*zy) * (*zy) - (*zx) * (*zx) + cy; + *zx = 2.0 * (*zy) * (*zx) + cx; + *zy = ty; +} + +void +precalc_nebula_params(config_t* conf, double* conv, double* mult_x, double* mult_y, int* hw, int* hh) { + *conv = ((conf->width < conf->height) ? conf->width : conf->height) / 4.0; + + *mult_x = (double) (0.8 * conf->width / *conv) / (UINT32_MAX / 2.0); + *mult_y = (double) (0.8 * conf->height / *conv) / (UINT32_MAX / 2.0); + + *hw = conf->width / 2; + *hh = conf->height / 2; +} + void usage(void) { fputs("nebula2 needs the name of a config file as 1st argument.\n", stderr); @@ -101,12 +130,20 @@ nebula_data_destroy(nebula_data_t* nd) { free(nd); } +typedef struct { + int x, y; +} pos_t; + /* Data of a single worker */ typedef struct { int id; pthread_mutex_t* mu; int ok; + pos_t* pointlist; + + sfmt_t* sfmt_state; + config_t* conf; nebula_data_t* nd; @@ -114,11 +151,49 @@ typedef struct { int thread_started; } worker_data_t; +inline static pos_t +calc_pos(double zx, double zy, size_t width, size_t height, double conv, int hw, int hh) { + pos_t rv; + rv.x = fast_floor(zx * conv) + hw; + rv.y = fast_floor(zy * conv) + hh; + + if((rv.x < 0) || (rv.y < 0) || (rv.x >= width) || (rv.y >= height)) { + rv.x = -1; + } + + return rv; +} + /* The background worker */ void* worker(void* _wd) { - worker_data_t* wd = _wd; - nebula_data_t* nd = wd->nd; + /* Precalculated data (scaling factors etc.) */ + double conv, mult_x, mult_y; + int hw, hh; + + /* Mandelbrot point vars */ + uint64_t xy; + double cx, cy, zx, zy; + + /* Misc... */ + int todo; + int iter, maxiter, mii; + size_t off, mapsize; + pos_t pos; + + /* Aliases */ + worker_data_t* wd = _wd; + nebula_data_t* nd = wd->nd; + config_t* conf = wd->conf; + sfmt_t* sfmt_state = wd->sfmt_state; + pos_t* pointlist; + uint32_t* map = nd->map; + size_t width = conf->width; + size_t height = conf->height; + + precalc_nebula_params(conf, &conv, &mult_x, &mult_y, &hw, &hh); + mapsize = width * height; + maxiter = conf->iters[conf->iters_n - 1]; for(;; ) { jobrq_set(nd, wd->id); @@ -126,10 +201,71 @@ worker(void* _wd) { if(!wd->ok) { return NULL; } - /* TODO: Do magic... */ + + for(todo = conf->jobsize; todo--; ) { + xy = sfmt_genrand_uint64(sfmt_state); + cx = ((int32_t) (xy >> 32)) * mult_x; + cy = ((int32_t) (xy & UINT32_MAX)) * mult_y; + zx = zy = .0; + + pointlist = wd->pointlist; + for(iter = 0; iter < maxiter; iter++) { + calc_mandelbrot(cx, cy, &zx, &zy); + *(pointlist++) = calc_pos(zx, zy, width, height, conv, hw, hh); + if((zx * zx) + (zy * zy) > BAILOUT) { + for(mii = 0; iter > conf->iters[mii]; mii++) {} + off = mii * mapsize; + do { + /* + * To be 100% accurate, we would need to synchronize the access to the map here. + * We ignore this, since collision should be seldom. + */ + pos = *(--pointlist); + if(pos.x < 0) { + continue; + } + map[off + width * pos.y + pos.x]++; + } while(iter-- > 0); + break; + } + } + } } } +sfmt_t* +init_sfmt(void) { + sfmt_t* sfmt_state = NULL; + uint32_t seed; + FILE* fh = NULL; + + if(!(sfmt_state = malloc(sizeof(sfmt_t)))) { + goto failed; + } + + if(!(fh = fopen("/dev/urandom", "rb"))) { + goto failed; + } + + if(fread(&seed, sizeof(uint32_t), 1, fh) != 1) { + goto failed; + } + + fclose(fh); + + sfmt_init_gen_rand(sfmt_state, seed); + + return sfmt_state; +failed: + if(sfmt_state) { + free(sfmt_state); + } + if(fh) { + fclose(fh); + } + return NULL; +} + /* Init and run a worker */ int worker_init(worker_data_t* wd, int id, config_t* conf, nebula_data_t* nd) { @@ -139,17 +275,40 @@ worker_init(worker_data_t* wd, int id, config_t* conf, nebula_data_t* nd) { wd->nd = nd; wd->thread_started = 0; + wd->mu = NULL; + wd->pointlist = NULL; + wd->sfmt_state = NULL; + + if(!(wd->sfmt_state = init_sfmt())) { + goto failed; + } + if(!(wd->mu = mutex_create())) { - return 0; + goto failed; + } + + if(!(wd->pointlist = malloc(sizeof(pos_t) * conf->iters[conf->iters_n - 1]))) { + goto failed; } if(pthread_create(&(wd->thread), NULL, worker, wd) != 0) { - mutex_destroy(wd->mu); - return 0; + goto failed; } wd->thread_started = 1; return 1; + +failed: + if(wd->mu) { + mutex_destroy(wd->mu); + } + if(wd->pointlist) { + free(wd->pointlist); + } + if(wd->sfmt_state) { + free(wd->sfmt_state); + } + return 0; } void @@ -157,6 +316,12 @@ worker_cleanup(worker_data_t* wd) { if(wd->thread_started) { pthread_join(wd->thread, NULL); } + if(wd->pointlist) { + free(wd->pointlist); + } + if(wd->sfmt_state) { + free(wd->sfmt_state); + } mutex_destroy(wd->mu); } @@ -191,12 +356,26 @@ setup_sighandler(void) { return 1; } +void +stop_workers(nebula_data_t* nd, worker_data_t* workers, int* workers_alive) { + int rq; + + while(*workers_alive > 0) { + rq = jobrq_get(nd); + if(rq >= 0) { + workers[rq].ok = 0; + pthread_mutex_unlock(workers[rq].mu); + (*workers_alive)--; + } + } +} + int nebula2(config_t* conf) { int rv = 1; nebula_data_t* nd = NULL; uint32_t jobs_done; - worker_data_t* workers; + worker_data_t* workers = NULL; int i; int rq; int workers_alive = 0; @@ -212,11 +391,11 @@ nebula2(config_t* conf) { } nd->jobs_todo = conf->jobs - jobs_done; - if(!(workers = calloc(conf->procn, sizeof(worker_data_t)))) { + if(!(workers = calloc(conf->threads, sizeof(worker_data_t)))) { fputs("Could not allocate memory for worker data.\n", stderr); goto tidyup; } - for(i = 0; i < conf->procn; i++) { + for(i = 0; i < conf->threads; i++) { if(!(worker_init(&(workers[i]), i, conf, nd))) { fputs("Could not init worker.\n", stderr); goto tidyup; @@ -229,7 +408,6 @@ nebula2(config_t* conf) { goto tidyup; } - /* We need to manually unlock the set mutex once, so the first worker is allowed to set jobrq. */ while(nd->jobs_todo > 0) { rq = jobrq_get(nd); if(rq < 0) { @@ -240,30 +418,24 @@ nebula2(config_t* conf) { (nd->jobs_todo)--; pthread_mutex_unlock(workers[rq].mu); } + stop_workers(nd, workers, &workers_alive); if(!(state_save(conf, nd->map, conf->jobs - nd->jobs_todo))) { fprintf(stderr, "Error while saving state: %s\n", strerror(errno)); goto tidyup; } - + rv = render(conf, nd->map) ? 0 : 1; tidyup: - while(workers_alive > 0) { - rq = jobrq_get(nd); - if(rq >= 0) { - workers[rq].ok = 0; - pthread_mutex_unlock(workers[rq].mu); - workers_alive--; - } - } + stop_workers(nd, workers, &workers_alive); if(nd) { nebula_data_destroy(nd); } if(workers) { - for(i = 0; i < conf->procn; i++) { + for(i = 0; i < conf->threads; i++) { worker_cleanup(&(workers[i])); } free(workers); |