added thread pool, started fleshing out the render job system
[erebus2020] / liberebus / src / erebus.c
1 #include <stdio.h>
2 #include <pthread.h>
3 #include "erebus.h"
4 #include "tpool.h"
5
6 struct render_job {
7         struct erb_rend *erb;
8         int x, y, width, height;
9         struct render_job *next;
10 };
11
12 struct erb_rend {
13         int fb_width, fb_height, fb_npix;
14         float *fb_pixels;
15         int *fb_nsamples;
16 };
17
18 static void proc_render_job(void *cls);
19 static struct render_job *alloc_job(void);
20 static void free_job(struct render_job *job);
21
22 static struct thread_pool *tpool;
23
24
25 struct erb_rend *erb_create(void)
26 {
27         struct erb_rend *erb;
28
29         if(!tpool) {
30                 if(!(tpool = tpool_create(0))) {
31                         fprintf(stderr, "erb_create: fatal error, failed to create thread pool!\n");
32                         return 0;
33                 }
34         }
35
36         if(!(erb = calloc(1, sizeof *erb))) {
37                 return 0;
38         }
39         return erb;
40 }
41
42 void erb_destroy(struct erb_rend *erb)
43 {
44         if(!erb) return;
45         free(erb->fb_pixels);
46         free(erb);
47 }
48
49 int erb_allocframe(struct erb_rend *erb, int width, int height)
50 {
51         float *newfb;
52         int *newns;
53         int npix;
54
55         if(width == erb->fb_width && height == erb->fb_height) {
56                 return 0;
57         }
58
59         npix = width * height;
60
61         if(!(newfb = malloc(npix * 3 * sizeof *erb->fb_pixels))) {
62                 goto err;
63         }
64         if(!(newns = malloc(npix * sizeof *erb->fb_nsamples))) {
65                 free(newfb);
66                 goto err;
67         }
68
69         free(erb->fb_pixels);
70         free(erb->fb_nsamples);
71
72         erb->fb_pixels = newfb;
73         erb->fb_nsamples = newns;
74         erb->fb_width = width;
75         erb->fb_height = height;
76         erb->fb_npix = npix;
77         return 0;
78
79 err:
80         fprintf(stderr, "erb_allocframe: failed to allocate %dx%d framebuffer\n", width, height);
81         return -1;
82 }
83
84 float *erb_getframe(struct erb_rend *erb)
85 {
86         return erb->fb_pixels;
87 }
88
89 void erb_begin(struct erb_rend *erb)
90 {
91         memset(erb->fb_pixels, 0, erb->fb_npix * 3 * sizeof *erb->fb_pixels);
92         memset(erb->fb_nsamples, 0, erb->fb_npix * sizeof *erb->fb_nsamples);
93 }
94
95 float *erb_end(struct erb_rend *erb)
96 {
97         int i, n;
98         float s;
99         float *pptr = erb->fb_pixels;
100         int *nptr = erb->fb_nsamples;
101
102         for(i=0; i<erb->fb_npix; i++) {
103                 n = *nptr;
104                 if(n > 1) {
105                         s = 1.0f / n;
106                         *pptr++ *= s;
107                         *pptr++ *= s;
108                         *pptr++ *= s;
109                         *nptr++ = 1;
110                 } else {
111                         pptr += 3;
112                         nptr++;
113                 }
114         }
115
116         return erb->fb_pixels;
117 }
118
119 void erb_queue_frame(struct erb_rend *erb)
120 {
121         erb_queue_block(erb, 0, 0, erb->fb_width, erb->fb_height);
122 }
123
124 void erb_queue_block(struct erb_rend *erb, int x, int y, int width, int height)
125 {
126         struct render_job *job;
127
128         if(!(job = alloc_job())) {
129                 fprintf(stderr, "erb_queue_block: failed to allocate rendering job\n");
130                 return;
131         }
132
133         job->erb = erb;
134         job->x = x;
135         job->y = y;
136         job->width = width;
137         job->height = height;
138
139         tpool_enqueue(tpool, job, proc_render_job, 0);
140 }
141
142 void erb_wait(struct erb_rend *erb)
143 {
144         /* XXX should we have a per-renderer instance thread pool, to wait only for our own jobs? */
145         tpool_wait(tpool);
146 }
147
148 void erb_primary_ray(struct erb_rend *erb, struct erb_ray *ray, int sample)
149 {
150         /* TODO */
151 }
152
153 void erb_sample_ray(struct erb_rend *erb, struct erb_ray *ray, float *col)
154 {
155         /* TODO */
156         col[0] = fmod(col[0] + 1.0f, 1.0f);
157         col[1] = col[2] = fmod(col[1] + 0.33f, 1.0f);
158 }
159
160 static void proc_render_job(void *cls)
161 {
162         int i, j, fboffs;
163         struct erb_rend *erb;
164         struct render_job *job = cls;
165         float *fbptr;
166         int *nsptr;
167         struct erb_ray ray;
168
169         erb = job->erb;
170         fboffs = job->y * erb->fb_width + job->x;
171         fbptr = erb->fb_pixels + fboffs * 3;
172         nsptr = erb->fb_nsamples + fboffs;
173
174         for(i=0; i<job->height; i++) {
175                 for(j=0; j<job->width; j++) {
176                         erb_primary_ray(erb, &ray, *nsptr++);
177                         erb_sample_ray(erb, &ray, fbptr);
178                         fbptr += 3;
179                 }
180         }
181         free_job(job);
182 }
183
184
185 #define MAX_JOB_POOL    128
186 static struct render_job *job_pool;
187 static int num_free_jobs;
188 static pthread_mutex_t job_pool_lock = PTHREAD_MUTEX_INITIALIZER;
189
190 static struct render_job *alloc_job(void)
191 {
192         struct render_job *job;
193
194         pthread_mutex_lock(&job_pool_lock);
195         if(!job_pool) {
196                 pthread_mutex_unlock(&job_pool_lock);
197                 return malloc(sizeof *job);
198         }
199
200         job = job_pool;
201         job_pool = job->next;
202         num_free_jobs--;
203         pthread_mutex_unlock(&job_pool_lock);
204         return job;
205 }
206
207 static void free_job(struct render_job *job)
208 {
209         pthread_mutex_lock(&job_pool_lock);
210         if(num_free_jobs >= MAX_JOB_POOL) {
211                 pthread_mutex_unlock(&job_pool_lock);
212                 free(job);
213                 return;
214         }
215
216         job->next = job_pool;
217         job_pool = job;
218         num_free_jobs++;
219         pthread_mutex_unlock(&job_pool_lock);
220 }