#include <stdio.h>
+#include <pthread.h>
#include "erebus.h"
+#include "tpool.h"
+
+struct render_job {
+ struct erb_rend *erb;
+ unsigned int id;
+ struct erb_rect rect;
+ 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;
+
+ erb_done_func donecb;
+ void *donecls;
+
+ float vfov;
};
+static void proc_render_job(void *cls);
+static void done_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;
}
+
+ erb->vfov = cgm_deg_to_rad(50.0f);
+
return 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)
return erb->fb_pixels;
}
+void erb_setfov(struct erb_rend *erb, float vfov_deg)
+{
+ erb->vfov = cgm_deg_to_rad(vfov_deg);
+}
+
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_set_done_callback(struct erb_rend *erb, erb_done_func donecb, void *cls)
+{
+ erb->donecb = donecb;
+ erb->donecls = cls;
+}
+
+void erb_queue_frame(struct erb_rend *erb, unsigned int job_id)
+{
+ erb_queue_block(erb, job_id, 0, 0, erb->fb_width, erb->fb_height);
+}
+
+void erb_queue_block(struct erb_rend *erb, unsigned int job_id, 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->id = job_id;
+ job->rect.x = x;
+ job->rect.y = y;
+ job->rect.w = width;
+ job->rect.h = height;
+
+ tpool_enqueue(tpool, job, proc_render_job, done_render_job);
+}
+
+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->rect.y * erb->fb_width + job->rect.x;
+ fbptr = erb->fb_pixels + fboffs * 4;
+
+ for(i=0; i<job->rect.h; i++) {
+ for(j=0; j<job->rect.w; j++) {
+ erb_primary_ray(erb, &ray, (int)fbptr[3]);
+ erb_sample_ray(erb, &ray, fbptr);
+ fbptr += 4;
+ }
+ fbptr += (erb->fb_width - job->rect.w) * 4;
+ }
+}
+
+static void done_render_job(void *cls)
+{
+ struct erb_rend *erb;
+ struct render_job *job = cls;
+
+ erb = job->erb;
+ if(erb->donecb) {
+ erb->donecb(job->id, &job->rect, erb->donecls);
+ }
+
+ 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);
+}