foo
[erebus2020] / liberebus / src / erebus.c
index af2b2d9..dbc253b 100644 (file)
@@ -1,16 +1,37 @@
 #include <stdio.h>
+#include <pthread.h>
 #include "erebus.h"
+#include "tpool.h"
+
+struct render_job {
+       struct erb_rend *erb;
+       int x, y, width, height;
+       struct render_job *next;
+};
 
 struct erb_rend {
-       int fb_width, fb_height, fb_npix;
+       int fb_width, fb_height, fb_size;
        float *fb_pixels;
-       int *fb_nsamples;
 };
 
+static void proc_render_job(void *cls);
+static struct render_job *alloc_job(void);
+static void free_job(struct render_job *job);
+
+static struct thread_pool *tpool;
+
+
 struct erb_rend *erb_create(void)
 {
        struct erb_rend *erb;
 
+       if(!tpool) {
+               if(!(tpool = tpool_create(0))) {
+                       fprintf(stderr, "erb_create: fatal error, failed to create thread pool!\n");
+                       return 0;
+               }
+       }
+
        if(!(erb = calloc(1, sizeof *erb))) {
                return 0;
        }
@@ -27,36 +48,25 @@ void erb_destroy(struct erb_rend *erb)
 int erb_allocframe(struct erb_rend *erb, int width, int height)
 {
        float *newfb;
-       int *newns;
-       int npix;
+       int sz;
 
        if(width == erb->fb_width && height == erb->fb_height) {
                return 0;
        }
 
-       npix = width * height;
-
-       if(!(newfb = malloc(npix * 3 * sizeof *erb->fb_pixels))) {
-               goto err;
-       }
-       if(!(newns = malloc(npix * sizeof *erb->fb_nsamples))) {
-               free(newfb);
-               goto err;
+       sz = width * height * 4 * sizeof *erb->fb_pixels;
+       if(!(newfb = malloc(sz))) {
+               fprintf(stderr, "erb_allocframe: failed to allocate %dx%d framebuffer\n", width, height);
+               return -1;
        }
 
        free(erb->fb_pixels);
-       free(erb->fb_nsamples);
 
        erb->fb_pixels = newfb;
-       erb->fb_nsamples = newns;
        erb->fb_width = width;
        erb->fb_height = height;
-       erb->fb_npix = npix;
+       erb->fb_size = sz;
        return 0;
-
-err:
-       fprintf(stderr, "erb_allocframe: failed to allocate %dx%d framebuffer\n", width, height);
-       return -1;
 }
 
 float *erb_getframe(struct erb_rend *erb)
@@ -66,30 +76,128 @@ float *erb_getframe(struct erb_rend *erb)
 
 void erb_begin(struct erb_rend *erb)
 {
-       memset(erb->fb_pixels, 0, erb->fb_npix * 3 * sizeof *erb->fb_pixels);
-       memset(erb->fb_nsamples, 0, erb->fb_npix * sizeof *erb->fb_nsamples);
+       memset(erb->fb_pixels, 0, erb->fb_size);
 }
 
 float *erb_end(struct erb_rend *erb)
 {
-       int i, n;
+       int i, npix = erb->fb_width * erb->fb_height;
        float s;
        float *pptr = erb->fb_pixels;
-       int *nptr = erb->fb_nsamples;
 
-       for(i=0; i<erb->fb_npix; i++) {
-               n = *nptr;
-               if(n > 1) {
-                       s = 1.0f / n;
+       for(i=0; i<npix; i++) {
+               if(pptr[3] > 1) {
+                       s = 1.0f / pptr[3];
                        *pptr++ *= s;
                        *pptr++ *= s;
                        *pptr++ *= s;
-                       *nptr++ = 1;
+                       *pptr++ = 1.0f;
                } else {
-                       pptr += 3;
-                       nptr++;
+                       pptr += 4;
                }
        }
 
        return erb->fb_pixels;
 }
+
+void erb_queue_frame(struct erb_rend *erb)
+{
+       erb_queue_block(erb, 0, 0, erb->fb_width, erb->fb_height);
+}
+
+void erb_queue_block(struct erb_rend *erb, int x, int y, int width, int height)
+{
+       struct render_job *job;
+
+       if(!(job = alloc_job())) {
+               fprintf(stderr, "erb_queue_block: failed to allocate rendering job\n");
+               return;
+       }
+
+       job->erb = erb;
+       job->x = x;
+       job->y = y;
+       job->width = width;
+       job->height = height;
+
+       tpool_enqueue(tpool, job, proc_render_job, 0);
+}
+
+void erb_wait(struct erb_rend *erb)
+{
+       /* XXX should we have a per-renderer instance thread pool, to wait only for our own jobs? */
+       tpool_wait(tpool);
+}
+
+void erb_primary_ray(struct erb_rend *erb, struct erb_ray *ray, int sample)
+{
+       /* TODO */
+}
+
+void erb_sample_ray(struct erb_rend *erb, struct erb_ray *ray, float *col)
+{
+       /* TODO */
+       col[0] = fmod(col[0] + 1.0f, 1.0f);
+       col[1] = col[2] = fmod(col[1] + 0.33f, 1.0f);
+}
+
+static void proc_render_job(void *cls)
+{
+       int i, j, fboffs;
+       struct erb_rend *erb;
+       struct render_job *job = cls;
+       float *fbptr;
+       struct erb_ray ray;
+
+       erb = job->erb;
+       fboffs = job->y * erb->fb_width + job->x;
+       fbptr = erb->fb_pixels + fboffs * 4;
+
+       for(i=0; i<job->height; i++) {
+               for(j=0; j<job->width; j++) {
+                       erb_primary_ray(erb, &ray, (int)fbptr[3]);
+                       erb_sample_ray(erb, &ray, fbptr);
+                       fbptr += 4;
+               }
+               fbptr += (erb->fb_width - job->width) * 4;
+       }
+       free_job(job);
+}
+
+
+#define MAX_JOB_POOL   128
+static struct render_job *job_pool;
+static int num_free_jobs;
+static pthread_mutex_t job_pool_lock = PTHREAD_MUTEX_INITIALIZER;
+
+static struct render_job *alloc_job(void)
+{
+       struct render_job *job;
+
+       pthread_mutex_lock(&job_pool_lock);
+       if(!job_pool) {
+               pthread_mutex_unlock(&job_pool_lock);
+               return malloc(sizeof *job);
+       }
+
+       job = job_pool;
+       job_pool = job->next;
+       num_free_jobs--;
+       pthread_mutex_unlock(&job_pool_lock);
+       return job;
+}
+
+static void free_job(struct render_job *job)
+{
+       pthread_mutex_lock(&job_pool_lock);
+       if(num_free_jobs >= MAX_JOB_POOL) {
+               pthread_mutex_unlock(&job_pool_lock);
+               free(job);
+               return;
+       }
+
+       job->next = job_pool;
+       job_pool = job;
+       num_free_jobs++;
+       pthread_mutex_unlock(&job_pool_lock);
+}