clray
view src/rt.cc @ 55:df239a52a091
extensive render stats for the CPU raytracer
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sat, 11 Sep 2010 03:00:21 +0100 |
parents | 6a30f27fa1e6 |
children | 3d13924b22e6 |
line source
1 #include <stdio.h>
2 #include <string.h>
3 #include <math.h>
4 #include <assert.h>
5 #include "rt.h"
6 #include "ogl.h"
7 #include "ocl.h"
8 #include "scene.h"
9 #include "timer.h"
10 #include "common.h"
12 // kernel arguments
13 enum {
14 KARG_FRAMEBUFFER,
15 KARG_RENDER_INFO,
16 KARG_FACES,
17 KARG_MATLIB,
18 KARG_LIGHTS,
19 KARG_PRIM_RAYS,
20 KARG_XFORM,
21 KARG_INVTRANS_XFORM,
22 KARG_KDTREE,
24 NUM_KERNEL_ARGS
25 };
27 static void update_render_info();
28 static Ray get_primary_ray(int x, int y, int w, int h, float vfov_deg);
29 static float *create_kdimage(const KDNodeGPU *kdtree, int num_nodes, int *xsz_ret, int *ysz_ret);
31 static Face *faces;
32 static Ray *prim_rays;
33 static CLProgram *prog;
34 static int global_size;
37 static RendInfo rinf;
38 static RenderStats rstat;
39 static int saved_iter_val;
41 static long timing_sample_sum;
42 static long num_timing_samples;
44 extern bool dbg_frame_time;
47 bool init_renderer(int xsz, int ysz, Scene *scn, unsigned int tex)
48 {
49 // render info
50 rinf.ambient[0] = rinf.ambient[1] = rinf.ambient[2] = 0.0;
51 rinf.ambient[3] = 0.0;
53 rinf.xsz = xsz;
54 rinf.ysz = ysz;
55 rinf.num_faces = scn->get_num_faces();
56 rinf.num_lights = scn->get_num_lights();
57 rinf.max_iter = saved_iter_val = 6;
58 rinf.cast_shadows = true;
60 /* calculate primary rays */
61 prim_rays = new Ray[xsz * ysz];
63 for(int i=0; i<ysz; i++) {
64 for(int j=0; j<xsz; j++) {
65 prim_rays[i * xsz + j] = get_primary_ray(j, i, xsz, ysz, 45.0);
66 }
67 }
68 dbg_set_primary_rays(prim_rays); // give them to the debug renderer
70 /* setup opencl */
71 prog = new CLProgram("render");
72 if(!prog->load("src/rt.cl")) {
73 return false;
74 }
76 if(!(faces = (Face*)scn->get_face_buffer())) {
77 fprintf(stderr, "failed to create face buffer\n");
78 return false;
79 }
81 const KDNodeGPU *kdbuf = scn->get_kdtree_buffer();
82 if(!kdbuf) {
83 fprintf(stderr, "failed to create kdtree buffer\n");
84 return false;
85 }
87 int kdimg_xsz, kdimg_ysz;
88 float *kdimg_pixels = create_kdimage(kdbuf, scn->get_num_kdnodes(), &kdimg_xsz, &kdimg_ysz);
90 /* setup argument buffers */
91 #ifdef CLGL_INTEROP
92 prog->set_arg_texture(KARG_FRAMEBUFFER, ARG_WR, tex);
93 #else
94 prog->set_arg_image(KARG_FRAMEBUFFER, ARG_WR, xsz, ysz);
95 #endif
96 prog->set_arg_buffer(KARG_RENDER_INFO, ARG_RD, sizeof rinf, &rinf);
97 prog->set_arg_buffer(KARG_FACES, ARG_RD, rinf.num_faces * sizeof(Face), faces);
98 prog->set_arg_buffer(KARG_MATLIB, ARG_RD, scn->get_num_materials() * sizeof(Material), scn->get_materials());
99 prog->set_arg_buffer(KARG_LIGHTS, ARG_RD, scn->get_num_lights() * sizeof(Light), scn->get_lights());
100 prog->set_arg_buffer(KARG_PRIM_RAYS, ARG_RD, xsz * ysz * sizeof *prim_rays, prim_rays);
101 prog->set_arg_buffer(KARG_XFORM, ARG_RD, 16 * sizeof(float));
102 prog->set_arg_buffer(KARG_INVTRANS_XFORM, ARG_RD, 16 * sizeof(float));
103 //prog->set_arg_buffer(KARG_KDTREE, ARG_RD, scn->get_num_kdnodes() * sizeof *kdbuf, kdbuf);
104 prog->set_arg_image(KARG_KDTREE, ARG_RD, kdimg_xsz, kdimg_ysz, kdimg_pixels);
106 delete [] kdimg_pixels;
109 if(prog->get_num_args() < NUM_KERNEL_ARGS) {
110 return false;
111 }
113 const char *opt = "-Isrc -cl-mad-enable -cl-single-precision-constant -cl-fast-relaxed-math";
114 if(!prog->build(opt)) {
115 return false;
116 }
118 //delete [] prim_rays; now dbg_renderer handles them
120 global_size = xsz * ysz;
123 init_dbg_renderer(xsz, ysz, scn, tex);
124 return true;
125 }
127 void destroy_renderer()
128 {
129 delete prog;
131 destroy_dbg_renderer();
133 if(num_timing_samples) {
134 printf("rendertime mean: %ld msec\n", timing_sample_sum / num_timing_samples);
135 }
136 }
138 bool render()
139 {
140 // XXX do we need to call glFinish ?
142 long tm0 = get_msec();
144 #ifdef CLGL_INTEROP
145 cl_event ev;
146 CLMemBuffer *texbuf = prog->get_arg_buffer(KARG_FRAMEBUFFER);
148 if(!acquire_gl_object(texbuf, &ev)) {
149 return false;
150 }
152 // make sure that we will wait for the acquire to finish before running
153 prog->set_wait_event(ev);
154 #endif
156 if(!prog->run(1, global_size)) {
157 return false;
158 }
160 #ifdef CLGL_INTEROP
161 if(!release_gl_object(texbuf, &ev)) {
162 return false;
163 }
164 clWaitForEvents(1, &ev);
165 #endif
167 #ifndef CLGL_INTEROP
168 /* if we don't compile in CL/GL interoperability support, we need
169 * to copy the output buffer to the OpenGL texture used to displaying
170 * the image.
171 */
172 CLMemBuffer *mbuf = prog->get_arg_buffer(KARG_FRAMEBUFFER);
173 void *fb = map_mem_buffer(mbuf, MAP_RD);
174 if(!fb) {
175 fprintf(stderr, "FAILED\n");
176 return false;
177 }
179 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, rinf.xsz, rinf.ysz, GL_RGBA, GL_FLOAT, fb);
180 unmap_mem_buffer(mbuf);
181 #endif
183 long msec = get_msec() - tm0;
184 timing_sample_sum += msec;
185 num_timing_samples++;
187 if(dbg_frame_time) {
188 printf("rendered in %ld msec\n", msec);
189 }
190 return true;
191 }
194 void set_xform(float *matrix, float *invtrans)
195 {
196 CLMemBuffer *mbuf_xform = prog->get_arg_buffer(KARG_XFORM);
197 CLMemBuffer *mbuf_invtrans = prog->get_arg_buffer(KARG_INVTRANS_XFORM);
198 assert(mbuf_xform && mbuf_invtrans);
200 float *mem = (float*)map_mem_buffer(mbuf_xform, MAP_WR);
201 memcpy(mem, matrix, 16 * sizeof *mem);
202 unmap_mem_buffer(mbuf_xform);
204 mem = (float*)map_mem_buffer(mbuf_invtrans, MAP_WR);
205 memcpy(mem, invtrans, 16 * sizeof *mem);
206 unmap_mem_buffer(mbuf_invtrans);
207 }
210 const RendInfo *get_render_info()
211 {
212 return &rinf;
213 }
215 const RenderStats *get_render_stats()
216 {
217 return &rstat;
218 }
220 void print_render_stats(FILE *fp)
221 {
222 fprintf(fp, "-- render stats --\n");
223 fprintf(fp, "> timing\n");
224 fprintf(fp, " render time (msec): %lu\n", rstat.render_time);
225 fprintf(fp, " tex update time (msec): %lu\n", rstat.tex_update_time);
226 fprintf(fp, "> counters\n");
227 fprintf(fp, " AABB tests: %d\n", rstat.aabb_tests);
228 fprintf(fp, " AABB tests per ray (min/max/avg): %d/%d/%f\n",
229 rstat.min_aabb_tests, rstat.max_aabb_tests, rstat.avg_aabb_tests);
230 fprintf(fp, " triangle tests: %d\n", rstat.triangle_tests);
231 fprintf(fp, " triangle tests per ray (min/max/avg): %d/%d/%f\n",
232 rstat.min_triangle_tests, rstat.max_triangle_tests, rstat.avg_triangle_tests);
233 fprintf(fp, " rays cast: %dp %dr %ds (sum: %d)\n", rstat.prim_rays,
234 rstat.refl_rays, rstat.shadow_rays, rstat.rays_cast);
235 fprintf(fp, " rays per second: %d\n", rstat.rays_per_sec);
236 fprintf(fp, " BRDF evaluations: %d\n", rstat.brdf_evals);
237 fputc('\n', fp);
238 }
240 void set_render_option(int opt, bool val)
241 {
242 switch(opt) {
243 case ROPT_ITER:
244 case ROPT_REFL:
245 rinf.max_iter = val ? saved_iter_val : 0;
246 break;
248 case ROPT_SHAD:
249 rinf.cast_shadows = val;
250 break;
252 default:
253 return;
254 }
256 update_render_info();
257 }
259 void set_render_option(int opt, int val)
260 {
261 switch(opt) {
262 case ROPT_ITER:
263 rinf.max_iter = saved_iter_val = val;
264 break;
266 case ROPT_SHAD:
267 rinf.cast_shadows = val;
268 break;
270 case ROPT_REFL:
271 rinf.max_iter = val ? saved_iter_val : 0;
272 break;
274 default:
275 return;
276 }
278 update_render_info();
279 }
281 void set_render_option(int opt, float val)
282 {
283 set_render_option(opt, (int)val);
284 }
286 bool get_render_option_bool(int opt)
287 {
288 switch(opt) {
289 case ROPT_ITER:
290 return rinf.max_iter;
291 case ROPT_SHAD:
292 return rinf.cast_shadows;
293 case ROPT_REFL:
294 return rinf.max_iter == saved_iter_val;
295 default:
296 break;
297 }
298 return false;
299 }
301 int get_render_option_int(int opt)
302 {
303 switch(opt) {
304 case ROPT_ITER:
305 return rinf.max_iter;
306 case ROPT_SHAD:
307 return rinf.cast_shadows ? 1 : 0;
308 case ROPT_REFL:
309 return rinf.max_iter == saved_iter_val ? 1 : 0;
310 default:
311 break;
312 }
313 return -1;
314 }
316 float get_render_option_float(int opt)
317 {
318 return (float)get_render_option_int(opt);
319 }
321 static void update_render_info()
322 {
323 if(!prog) {
324 return;
325 }
327 CLMemBuffer *mbuf = prog->get_arg_buffer(KARG_RENDER_INFO);
328 assert(mbuf);
330 RendInfo *rinf_ptr = (RendInfo*)map_mem_buffer(mbuf, MAP_WR);
331 *rinf_ptr = rinf;
332 unmap_mem_buffer(mbuf);
333 }
335 static Ray get_primary_ray(int x, int y, int w, int h, float vfov_deg)
336 {
337 float vfov = M_PI * vfov_deg / 180.0;
338 float aspect = (float)w / (float)h;
340 float ysz = 2.0;
341 float xsz = aspect * ysz;
343 float px = ((float)x / (float)w) * xsz - xsz / 2.0;
344 float py = 1.0 - ((float)y / (float)h) * ysz;
345 float pz = 1.0 / tan(0.5 * vfov);
347 float mag = sqrt(px * px + py * py + pz * pz);
349 px = px * RAY_MAG / mag;
350 py = py * RAY_MAG / mag;
351 pz = pz * RAY_MAG / mag;
353 Ray ray = {{0, 0, 0, 1}, {px, py, -pz, 1}};
354 return ray;
355 }
357 #define MIN(a, b) ((a) < (b) ? (a) : (b))
359 static float *create_kdimage(const KDNodeGPU *kdtree, int num_nodes, int *xsz_ret, int *ysz_ret)
360 {
361 int ysz = MIN(num_nodes, KDIMG_MAX_HEIGHT);
362 int columns = (num_nodes - 1) / KDIMG_MAX_HEIGHT + 1;
363 int xsz = KDIMG_NODE_WIDTH * columns;
365 printf("creating kdtree image %dx%d (%d nodes)\n", xsz, ysz, num_nodes);
367 float *img = new float[4 * xsz * ysz];
368 memset(img, 0, 4 * xsz * ysz * sizeof *img);
370 for(int i=0; i<num_nodes; i++) {
371 int x = KDIMG_NODE_WIDTH * (i / KDIMG_MAX_HEIGHT);
372 int y = i % KDIMG_MAX_HEIGHT;
374 float *ptr = img + (y * xsz + x) * 4;
376 *ptr++ = kdtree[i].aabb.min[0];
377 *ptr++ = kdtree[i].aabb.min[1];
378 *ptr++ = kdtree[i].aabb.min[2];
379 *ptr++ = 0.0;
381 *ptr++ = kdtree[i].aabb.max[0];
382 *ptr++ = kdtree[i].aabb.max[1];
383 *ptr++ = kdtree[i].aabb.max[2];
384 *ptr++ = 0.0;
386 for(int j=0; j<MAX_NODE_FACES; j++) {
387 *ptr++ = j < kdtree[i].num_faces ? (float)kdtree[i].face_idx[j] : 0.0f;
388 }
390 *ptr++ = (float)kdtree[i].num_faces;
391 *ptr++ = (float)kdtree[i].left;
392 *ptr++ = (float)kdtree[i].right;
393 *ptr++ = 0.0;
394 }
396 if(xsz_ret) *xsz_ret = xsz;
397 if(ysz_ret) *ysz_ret = ysz;
398 return img;
399 }