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