summaryrefslogtreecommitdiff
path: root/nebula2.c
diff options
context:
space:
mode:
Diffstat (limited to 'nebula2.c')
-rw-r--r--nebula2.c212
1 files changed, 192 insertions, 20 deletions
diff --git a/nebula2.c b/nebula2.c
index 9f0ae19..1e0a948 100644
--- a/nebula2.c
+++ b/nebula2.c
@@ -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);