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