Mercurial > cgi-bin > hgwebdir.cgi > PR > Applications > Vthread > Vthread__Best_Effort_Msg__Bench
view c-ray-mt.c @ 0:4ae1d7ffb1ae
Initial pthreads version
| author | Merten Sach <msach@mailbox.tu-berlin.de> |
|---|---|
| date | Wed, 03 Aug 2011 14:26:31 +0200 |
| parents | |
| children | 3840d91821c4 |
line source
1 /* c-ray-mt - a simple multithreaded raytracing filter.
2 * Copyright (C) 2006 John Tsiombikas <nuclear@siggraph.org>
3 *
4 * You are free to use, modify and redistribute this program under the
5 * terms of the GNU General Public License v2 or (at your option) later.
6 * see "http://www.gnu.org/licenses/gpl.txt" for details.
7 * ---------------------------------------------------------------------
8 * Usage:
9 * compile: just type make
10 * (add any arch-specific optimizations for your compiler in CFLAGS first)
11 * run: cat scene | ./c-ray-mt [-t num-threads] >foo.ppm
12 * (on broken systems such as windows try: c-ray-mt -i scene -o foo.ppm)
13 * enjoy: display foo.ppm
14 * (with imagemagick, or use your favorite image viewer)
15 * ---------------------------------------------------------------------
16 * Scene file format:
17 * # sphere (many)
18 * s x y z rad r g b shininess reflectivity
19 * # light (many)
20 * l x y z
21 * # camera (one)
22 * c x y z fov tx ty tz
23 * ---------------------------------------------------------------------
24 */
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <math.h>
29 #include <ctype.h>
30 #include <errno.h>
31 #include <pthread.h>
32 #include "VPThread_lib/VPThread.h"
34 #define VER_MAJOR 1
35 #define VER_MINOR 1
36 #define VER_STR "c-ray-mt v%d.%d\n"
38 #if !defined(unix) && !defined(__unix__)
39 #ifdef __MACH__
40 #define unix 1
41 #define __unix__ 1
42 #endif /* __MACH__ */
43 #endif /* unix */
45 /* find the appropriate way to define explicitly sized types */
46 /* for C99 or GNU libc (also mach's libc) we can use stdint.h */
47 #if (__STDC_VERSION__ >= 199900) || defined(__GLIBC__) || defined(__MACH__)
48 #include <stdint.h>
49 #elif defined(unix) || defined(__unix__) /* some UNIX systems have them in sys/types.h */
50 #include <sys/types.h>
51 #elif defined(__WIN32__) || defined(WIN32) /* the nameless one */
52 typedef unsigned __int8 uint8_t;
53 typedef unsigned __int32 uint32_t;
54 #endif /* sized type detection */
56 struct vec3 {
57 double x, y, z;
58 };
60 struct ray {
61 struct vec3 orig, dir;
62 };
64 struct material {
65 struct vec3 col; /* color */
66 double spow; /* specular power */
67 double refl; /* reflection intensity */
68 };
70 struct sphere {
71 struct vec3 pos;
72 double rad;
73 struct material mat;
74 struct sphere *next;
75 };
77 struct spoint {
78 struct vec3 pos, normal, vref; /* position, normal and view reflection */
79 double dist; /* parametric distance of intersection along the ray */
80 };
82 struct camera {
83 struct vec3 pos, targ;
84 double fov;
85 };
87 struct thread_data {
88 pthread_t tid;
89 int sl_start, sl_count;
91 uint32_t *pixels;
92 };
94 void render_scanline(int xsz, int ysz, int sl, uint32_t *fb, int samples);
95 struct vec3 trace(struct ray ray, int depth);
96 struct vec3 shade(struct sphere *obj, struct spoint *sp, int depth);
97 struct vec3 reflect(struct vec3 v, struct vec3 n);
98 struct vec3 cross_product(struct vec3 v1, struct vec3 v2);
99 struct ray get_primary_ray(int x, int y, int sample);
100 struct vec3 get_sample_pos(int x, int y, int sample);
101 struct vec3 jitter(int x, int y, int s);
102 int ray_sphere(const struct sphere *sph, struct ray ray, struct spoint *sp);
103 void load_scene(FILE *fp);
104 unsigned long get_msec(void);
106 void *thread_func(void *tdata);
108 #define MAX_LIGHTS 16 /* maximum number of lights */
109 #define RAY_MAG 1000.0 /* trace rays of this magnitude */
110 #define MAX_RAY_DEPTH 5 /* raytrace recursion limit */
111 #define FOV 0.78539816 /* field of view in rads (pi/4) */
112 #define HALF_FOV (FOV * 0.5)
113 #define ERR_MARGIN 1e-6 /* an arbitrary error margin to avoid surface acne */
115 /* bit-shift ammount for packing each color into a 32bit uint */
116 #ifdef LITTLE_ENDIAN
117 #define RSHIFT 16
118 #define BSHIFT 0
119 #else /* big endian */
120 #define RSHIFT 0
121 #define BSHIFT 16
122 #endif /* endianess */
123 #define GSHIFT 8 /* this is the same in both byte orders */
125 /* some helpful macros... */
126 #define SQ(x) ((x) * (x))
127 #define MAX(a, b) ((a) > (b) ? (a) : (b))
128 #define MIN(a, b) ((a) < (b) ? (a) : (b))
129 #define DOT(a, b) ((a).x * (b).x + (a).y * (b).y + (a).z * (b).z)
130 #define NORMALIZE(a) do {\
131 double len = sqrt(DOT(a, a));\
132 (a).x /= len; (a).y /= len; (a).z /= len;\
133 } while(0);
135 /* global state */
136 int xres = 800;
137 int yres = 600;
138 int rays_per_pixel = 1;
139 double aspect = 1.333333;
140 struct sphere *obj_list;
141 struct vec3 lights[MAX_LIGHTS];
142 int lnum = 0;
143 struct camera cam;
145 int thread_num = 1;
146 struct thread_data *threads;
148 int start = 0;
149 pthread_mutex_t start_mutex = PTHREAD_MUTEX_INITIALIZER;
150 pthread_cond_t start_cond = PTHREAD_COND_INITIALIZER;
152 #define NRAN 1024
153 #define MASK (NRAN - 1)
154 struct vec3 urand[NRAN];
155 int irand[NRAN];
157 unsigned long rend_time, start_time;
159 const char *usage = {
160 "Usage: c-ray-mt [options]\n"
161 " Reads a scene file from stdin, writes the image to stdout, and stats to stderr.\n\n"
162 "Options:\n"
163 " -t <num> how many threads to use (default: 1)\n"
164 " -s WxH where W is the width and H the height of the image\n"
165 " -r <rays> shoot <rays> rays per pixel (antialiasing)\n"
166 " -i <file> read from <file> instead of stdin\n"
167 " -o <file> write to <file> instead of stdout\n"
168 " -h this help screen\n\n"
169 };
171 void raytrace(uint32_t *pixels);
173 int main(int argc, char **argv) {
174 int i;
175 uint32_t *pixels;
176 double sl, sl_per_thread;
177 FILE *infile = stdin, *outfile = stdout;
179 for(i=1; i<argc; i++) {
180 if(argv[i][0] == '-' && argv[i][2] == 0) {
181 char *sep;
182 switch(argv[i][1]) {
183 case 't':
184 if(!isdigit(argv[++i][0])) {
185 fprintf(stderr, "-t mus be followed by the number of worker threads to spawn\n");
186 return EXIT_FAILURE;
187 }
188 thread_num = atoi(argv[i]);
189 if(!thread_num) {
190 fprintf(stderr, "invalid number of threads specified: %d\n", thread_num);
191 return EXIT_FAILURE;
192 }
193 break;
195 case 's':
196 if(!isdigit(argv[++i][0]) || !(sep = strchr(argv[i], 'x')) || !isdigit(*(sep + 1))) {
197 fputs("-s must be followed by something like \"640x480\"\n", stderr);
198 return EXIT_FAILURE;
199 }
200 xres = atoi(argv[i]);
201 yres = atoi(sep + 1);
202 aspect = (double)xres / (double)yres;
203 break;
205 case 'i':
206 if(!(infile = fopen(argv[++i], "rb"))) {
207 fprintf(stderr, "failed to open input file %s: %s\n", argv[i], strerror(errno));
208 return EXIT_FAILURE;
209 }
210 break;
212 case 'o':
213 if(!(outfile = fopen(argv[++i], "wb"))) {
214 fprintf(stderr, "failed to open output file %s: %s\n", argv[i], strerror(errno));
215 return EXIT_FAILURE;
216 }
217 break;
219 case 'r':
220 if(!isdigit(argv[++i][0])) {
221 fputs("-r must be followed by a number (rays per pixel)\n", stderr);
222 return EXIT_FAILURE;
223 }
224 rays_per_pixel = atoi(argv[i]);
225 break;
227 case 'h':
228 fputs(usage, stdout);
229 return 0;
231 default:
232 fprintf(stderr, "unrecognized argument: %s\n", argv[i]);
233 fputs(usage, stderr);
234 return EXIT_FAILURE;
235 }
236 } else {
237 fprintf(stderr, "unrecognized argument: %s\n", argv[i]);
238 fputs(usage, stderr);
239 return EXIT_FAILURE;
240 }
241 }
244 if(!(pixels = malloc(xres * yres * sizeof *pixels))) {
245 perror("pixel buffer allocation failed");
246 return EXIT_FAILURE;
247 }
248 load_scene(infile);
250 raytrace(pixels);
252 /* output statistics to stderr */
253 fprintf(stderr, "Rendering took: %lu seconds (%lu milliseconds)\n", rend_time / 1000, rend_time);
255 /* output the image */
256 fprintf(outfile, "P6\n%d %d\n255\n", xres, yres);
257 for(i=0; i<xres * yres; i++) {
258 fputc((pixels[i] >> RSHIFT) & 0xff, outfile);
259 fputc((pixels[i] >> GSHIFT) & 0xff, outfile);
260 fputc((pixels[i] >> BSHIFT) & 0xff, outfile);
261 }
262 fflush(outfile);
264 if(infile != stdin) fclose(infile);
265 if(outfile != stdout) fclose(outfile);
267 struct sphere *walker = obj_list;
268 while(walker) {
269 struct sphere *tmp = walker;
270 walker = walker->next;
271 free(tmp);
272 }
273 free(pixels);
274 free(threads);
275 return 0;
276 }
278 /* this is run after the VMS is set up*/
279 void raytrace(uint32_t *pixels)
280 {
281 int i;
282 double sl, sl_per_thread;
284 /* initialize the random number tables for the jitter */
285 for(i=0; i<NRAN; i++) urand[i].x = (double)rand() / RAND_MAX - 0.5;
286 for(i=0; i<NRAN; i++) urand[i].y = (double)rand() / RAND_MAX - 0.5;
287 for(i=0; i<NRAN; i++) irand[i] = (int)(NRAN * ((double)rand() / RAND_MAX));
289 if(thread_num > yres) {
290 fprintf(stderr, "more threads than scanlines specified, reducing number of threads to %d\n", yres);
291 thread_num = yres;
292 }
294 if(!(threads = malloc(thread_num * sizeof *threads))) {
295 perror("failed to allocate thread table");
296 exit(EXIT_FAILURE);
297 }
299 sl = 0.0;
300 sl_per_thread = (double)yres / (double)thread_num;
301 for(i=0; i<thread_num; i++) {
302 threads[i].sl_start = (int)sl;
303 sl += sl_per_thread;
304 threads[i].sl_count = (int)sl - threads[i].sl_start;
305 threads[i].pixels = pixels;
307 if(pthread_create(&threads[i].tid, 0, thread_func, &threads[i]) != 0) {
308 perror("failed to spawn thread");
309 exit(EXIT_FAILURE);
310 }
311 }
312 threads[thread_num - 1].sl_count = yres - threads[thread_num - 1].sl_start;
314 fprintf(stderr, VER_STR, VER_MAJOR, VER_MINOR);
316 pthread_mutex_lock(&start_mutex);
317 start_time = get_msec();
318 start = 1;
319 pthread_cond_broadcast(&start_cond);
320 pthread_mutex_unlock(&start_mutex);
322 for(i=0; i<thread_num; i++) {
323 pthread_join(threads[i].tid, 0);
324 }
325 rend_time = get_msec() - start_time;
326 }
328 /* render a frame of xsz/ysz dimensions into the provided framebuffer */
329 void render_scanline(int xsz, int ysz, int sl, uint32_t *fb, int samples) {
330 int i, s;
331 double rcp_samples = 1.0 / (double)samples;
333 for(i=0; i<xsz; i++) {
334 double r, g, b;
335 r = g = b = 0.0;
337 for(s=0; s<samples; s++) {
338 struct vec3 col = trace(get_primary_ray(i, sl, s), 0);
339 r += col.x;
340 g += col.y;
341 b += col.z;
342 }
344 r = r * rcp_samples;
345 g = g * rcp_samples;
346 b = b * rcp_samples;
348 fb[sl * xsz + i] = ((uint32_t)(MIN(r, 1.0) * 255.0) & 0xff) << RSHIFT |
349 ((uint32_t)(MIN(g, 1.0) * 255.0) & 0xff) << GSHIFT |
350 ((uint32_t)(MIN(b, 1.0) * 255.0) & 0xff) << BSHIFT;
351 }
352 }
354 /* trace a ray throught the scene recursively (the recursion happens through
355 * shade() to calculate reflection rays if necessary).
356 */
357 struct vec3 trace(struct ray ray, int depth) {
358 struct vec3 col;
359 struct spoint sp, nearest_sp;
360 struct sphere *nearest_obj = 0;
361 struct sphere *iter = obj_list->next;
363 /* if we reached the recursion limit, bail out */
364 if(depth >= MAX_RAY_DEPTH) {
365 col.x = col.y = col.z = 0.0;
366 return col;
367 }
369 /* find the nearest intersection ... */
370 while(iter) {
371 if(ray_sphere(iter, ray, &sp)) {
372 if(!nearest_obj || sp.dist < nearest_sp.dist) {
373 nearest_obj = iter;
374 nearest_sp = sp;
375 }
376 }
377 iter = iter->next;
378 }
380 /* and perform shading calculations as needed by calling shade() */
381 if(nearest_obj) {
382 col = shade(nearest_obj, &nearest_sp, depth);
383 } else {
384 col.x = col.y = col.z = 0.0;
385 }
387 return col;
388 }
390 /* Calculates direct illumination with the phong reflectance model.
391 * Also handles reflections by calling trace again, if necessary.
392 */
393 struct vec3 shade(struct sphere *obj, struct spoint *sp, int depth) {
394 int i;
395 struct vec3 col = {0, 0, 0};
397 /* for all lights ... */
398 for(i=0; i<lnum; i++) {
399 double ispec, idiff;
400 struct vec3 ldir;
401 struct ray shadow_ray;
402 struct sphere *iter = obj_list->next;
403 int in_shadow = 0;
405 ldir.x = lights[i].x - sp->pos.x;
406 ldir.y = lights[i].y - sp->pos.y;
407 ldir.z = lights[i].z - sp->pos.z;
409 shadow_ray.orig = sp->pos;
410 shadow_ray.dir = ldir;
412 /* shoot shadow rays to determine if we have a line of sight with the light */
413 while(iter) {
414 if(ray_sphere(iter, shadow_ray, 0)) {
415 in_shadow = 1;
416 break;
417 }
418 iter = iter->next;
419 }
421 /* and if we're not in shadow, calculate direct illumination with the phong model. */
422 if(!in_shadow) {
423 NORMALIZE(ldir);
425 idiff = MAX(DOT(sp->normal, ldir), 0.0);
426 ispec = obj->mat.spow > 0.0 ? pow(MAX(DOT(sp->vref, ldir), 0.0), obj->mat.spow) : 0.0;
428 col.x += idiff * obj->mat.col.x + ispec;
429 col.y += idiff * obj->mat.col.y + ispec;
430 col.z += idiff * obj->mat.col.z + ispec;
431 }
432 }
434 /* Also, if the object is reflective, spawn a reflection ray, and call trace()
435 * to calculate the light arriving from the mirror direction.
436 */
437 if(obj->mat.refl > 0.0) {
438 struct ray ray;
439 struct vec3 rcol;
441 ray.orig = sp->pos;
442 ray.dir = sp->vref;
443 ray.dir.x *= RAY_MAG;
444 ray.dir.y *= RAY_MAG;
445 ray.dir.z *= RAY_MAG;
447 rcol = trace(ray, depth + 1);
448 col.x += rcol.x * obj->mat.refl;
449 col.y += rcol.y * obj->mat.refl;
450 col.z += rcol.z * obj->mat.refl;
451 }
453 return col;
454 }
456 /* calculate reflection vector */
457 struct vec3 reflect(struct vec3 v, struct vec3 n) {
458 struct vec3 res;
459 double dot = v.x * n.x + v.y * n.y + v.z * n.z;
460 res.x = -(2.0 * dot * n.x - v.x);
461 res.y = -(2.0 * dot * n.y - v.y);
462 res.z = -(2.0 * dot * n.z - v.z);
463 return res;
464 }
466 struct vec3 cross_product(struct vec3 v1, struct vec3 v2) {
467 struct vec3 res;
468 res.x = v1.y * v2.z - v1.z * v2.y;
469 res.y = v1.z * v2.x - v1.x * v2.z;
470 res.z = v1.x * v2.y - v1.y * v2.x;
471 return res;
472 }
474 /* determine the primary ray corresponding to the specified pixel (x, y) */
475 struct ray get_primary_ray(int x, int y, int sample) {
476 struct ray ray;
477 float m[3][3];
478 struct vec3 i, j = {0, 1, 0}, k, dir, orig, foo;
480 k.x = cam.targ.x - cam.pos.x;
481 k.y = cam.targ.y - cam.pos.y;
482 k.z = cam.targ.z - cam.pos.z;
483 NORMALIZE(k);
485 i = cross_product(j, k);
486 j = cross_product(k, i);
487 m[0][0] = i.x; m[0][1] = j.x; m[0][2] = k.x;
488 m[1][0] = i.y; m[1][1] = j.y; m[1][2] = k.y;
489 m[2][0] = i.z; m[2][1] = j.z; m[2][2] = k.z;
491 ray.orig.x = ray.orig.y = ray.orig.z = 0.0;
492 ray.dir = get_sample_pos(x, y, sample);
493 ray.dir.z = 1.0 / HALF_FOV;
494 ray.dir.x *= RAY_MAG;
495 ray.dir.y *= RAY_MAG;
496 ray.dir.z *= RAY_MAG;
498 dir.x = ray.dir.x + ray.orig.x;
499 dir.y = ray.dir.y + ray.orig.y;
500 dir.z = ray.dir.z + ray.orig.z;
501 foo.x = dir.x * m[0][0] + dir.y * m[0][1] + dir.z * m[0][2];
502 foo.y = dir.x * m[1][0] + dir.y * m[1][1] + dir.z * m[1][2];
503 foo.z = dir.x * m[2][0] + dir.y * m[2][1] + dir.z * m[2][2];
505 orig.x = ray.orig.x * m[0][0] + ray.orig.y * m[0][1] + ray.orig.z * m[0][2] + cam.pos.x;
506 orig.y = ray.orig.x * m[1][0] + ray.orig.y * m[1][1] + ray.orig.z * m[1][2] + cam.pos.y;
507 orig.z = ray.orig.x * m[2][0] + ray.orig.y * m[2][1] + ray.orig.z * m[2][2] + cam.pos.z;
509 ray.orig = orig;
510 ray.dir.x = foo.x + orig.x;
511 ray.dir.y = foo.y + orig.y;
512 ray.dir.z = foo.z + orig.z;
514 return ray;
515 }
518 struct vec3 get_sample_pos(int x, int y, int sample) {
519 struct vec3 pt;
520 static double sf = 0.0;
522 if(sf == 0.0) {
523 sf = 1.5 / (double)xres;
524 }
526 pt.x = ((double)x / (double)xres) - 0.5;
527 pt.y = -(((double)y / (double)yres) - 0.65) / aspect;
529 if(sample) {
530 struct vec3 jt = jitter(x, y, sample);
531 pt.x += jt.x * sf;
532 pt.y += jt.y * sf / aspect;
533 }
534 return pt;
535 }
537 /* jitter function taken from Graphics Gems I. */
538 struct vec3 jitter(int x, int y, int s) {
539 struct vec3 pt;
540 pt.x = urand[(x + (y << 2) + irand[(x + s) & MASK]) & MASK].x;
541 pt.y = urand[(y + (x << 2) + irand[(y + s) & MASK]) & MASK].y;
542 return pt;
543 }
545 /* Calculate ray-sphere intersection, and return {1, 0} to signify hit or no hit.
546 * Also the surface point parameters like position, normal, etc are returned through
547 * the sp pointer if it is not NULL.
548 */
549 int ray_sphere(const struct sphere *sph, struct ray ray, struct spoint *sp) {
550 double a, b, c, d, sqrt_d, t1, t2;
552 a = SQ(ray.dir.x) + SQ(ray.dir.y) + SQ(ray.dir.z);
553 b = 2.0 * ray.dir.x * (ray.orig.x - sph->pos.x) +
554 2.0 * ray.dir.y * (ray.orig.y - sph->pos.y) +
555 2.0 * ray.dir.z * (ray.orig.z - sph->pos.z);
556 c = SQ(sph->pos.x) + SQ(sph->pos.y) + SQ(sph->pos.z) +
557 SQ(ray.orig.x) + SQ(ray.orig.y) + SQ(ray.orig.z) +
558 2.0 * (-sph->pos.x * ray.orig.x - sph->pos.y * ray.orig.y - sph->pos.z * ray.orig.z) - SQ(sph->rad);
560 if((d = SQ(b) - 4.0 * a * c) < 0.0) return 0;
562 sqrt_d = sqrt(d);
563 t1 = (-b + sqrt_d) / (2.0 * a);
564 t2 = (-b - sqrt_d) / (2.0 * a);
566 if((t1 < ERR_MARGIN && t2 < ERR_MARGIN) || (t1 > 1.0 && t2 > 1.0)) return 0;
568 if(sp) {
569 if(t1 < ERR_MARGIN) t1 = t2;
570 if(t2 < ERR_MARGIN) t2 = t1;
571 sp->dist = t1 < t2 ? t1 : t2;
573 sp->pos.x = ray.orig.x + ray.dir.x * sp->dist;
574 sp->pos.y = ray.orig.y + ray.dir.y * sp->dist;
575 sp->pos.z = ray.orig.z + ray.dir.z * sp->dist;
577 sp->normal.x = (sp->pos.x - sph->pos.x) / sph->rad;
578 sp->normal.y = (sp->pos.y - sph->pos.y) / sph->rad;
579 sp->normal.z = (sp->pos.z - sph->pos.z) / sph->rad;
581 sp->vref = reflect(ray.dir, sp->normal);
582 NORMALIZE(sp->vref);
583 }
584 return 1;
585 }
587 /* Load the scene from an extremely simple scene description file */
588 #define DELIM " \t\n"
589 void load_scene(FILE *fp) {
590 char line[256], *ptr, type;
592 obj_list = malloc(sizeof(struct sphere));
593 obj_list->next = 0;
595 while((ptr = fgets(line, 256, fp))) {
596 int i;
597 struct vec3 pos, col;
598 double rad, spow, refl;
600 while(*ptr == ' ' || *ptr == '\t') ptr++;
601 if(*ptr == '#' || *ptr == '\n') continue;
603 if(!(ptr = strtok(line, DELIM))) continue;
604 type = *ptr;
606 for(i=0; i<3; i++) {
607 if(!(ptr = strtok(0, DELIM))) break;
608 *((double*)&pos.x + i) = atof(ptr);
609 }
611 if(type == 'l') {
612 lights[lnum++] = pos;
613 continue;
614 }
616 if(!(ptr = strtok(0, DELIM))) continue;
617 rad = atof(ptr);
619 for(i=0; i<3; i++) {
620 if(!(ptr = strtok(0, DELIM))) break;
621 *((double*)&col.x + i) = atof(ptr);
622 }
624 if(type == 'c') {
625 cam.pos = pos;
626 cam.targ = col;
627 cam.fov = rad;
628 continue;
629 }
631 if(!(ptr = strtok(0, DELIM))) continue;
632 spow = atof(ptr);
634 if(!(ptr = strtok(0, DELIM))) continue;
635 refl = atof(ptr);
637 if(type == 's') {
638 struct sphere *sph = malloc(sizeof *sph);
639 sph->next = obj_list->next;
640 obj_list->next = sph;
642 sph->pos = pos;
643 sph->rad = rad;
644 sph->mat.col = col;
645 sph->mat.spow = spow;
646 sph->mat.refl = refl;
647 } else {
648 fprintf(stderr, "unknown type: %c\n", type);
649 }
650 }
651 }
654 /* provide a millisecond-resolution timer for each system */
655 #if defined(unix) || defined(__unix__)
656 #include <time.h>
657 #include <sys/time.h>
658 unsigned long get_msec(void) {
659 static struct timeval timeval, first_timeval;
661 gettimeofday(&timeval, 0);
662 if(first_timeval.tv_sec == 0) {
663 first_timeval = timeval;
664 return 0;
665 }
666 return (timeval.tv_sec - first_timeval.tv_sec) * 1000 + (timeval.tv_usec - first_timeval.tv_usec) / 1000;
667 }
668 #elif defined(__WIN32__) || defined(WIN32)
669 #include <windows.h>
670 unsigned long get_msec(void) {
671 return GetTickCount();
672 }
673 #else
674 #error "I don't know how to measure time on your platform"
675 #endif
677 void *thread_func(void *tdata) {
678 int i;
679 struct thread_data *td = (struct thread_data*)tdata;
681 pthread_mutex_lock(&start_mutex);
682 while(!start) {
683 pthread_cond_wait(&start_cond, &start_mutex);
684 }
685 pthread_mutex_unlock(&start_mutex);
687 for(i=0; i<td->sl_count; i++) {
688 render_scanline(xres, yres, i + td->sl_start, td->pixels, rays_per_pixel);
689 }
691 return 0;
692 }
