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