ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/raypcalls.c
Revision: 2.18
Committed: Fri Feb 8 18:27:31 2008 UTC (16 years, 2 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.17: +3 -5 lines
Log Message:
Created stub routines as placeholder for Windows version of raypcalls

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: raypcalls.c,v 2.17 2007/09/18 19:10:02 greg Exp $";
3 #endif
4 /*
5 * raypcalls.c - interface for parallel rendering using Radiance
6 *
7 * External symbols declared in ray.h
8 */
9
10 #include "copyright.h"
11
12 /*
13 * These calls are designed similarly to the ones in raycalls.c,
14 * but allow for multiple rendering processes on the same host
15 * machine. There is no sense in specifying more child processes
16 * than you have processors, but one child may help by allowing
17 * asynchronous ray computation in an interactive program, and
18 * will protect the caller from fatal rendering errors.
19 *
20 * You should first read and undrstand the header in raycalls.c,
21 * as some things are explained there that are not repated here.
22 *
23 * The first step is opening one or more rendering processes
24 * with a call to ray_pinit(oct, nproc). Before calling fork(),
25 * ray_pinit() loads the octree and data structures into the
26 * caller's memory, and ray_popen() synchronizes the ambient
27 * file, if any. Shared memory permits all sorts of queries
28 * that wouldn't be possible otherwise, without causing any real
29 * memory overhead, since all the static data are shared
30 * between processes. Rays are then traced using a simple
31 * queuing mechanism, explained below.
32 *
33 * The ray queue buffers RAYQLEN rays before sending to
34 * children, each of which may internally buffer RAYQLEN rays.
35 *
36 * Rays are queued and returned by a single
37 * ray_pqueue() call. A ray_pqueue() return
38 * value of 0 indicates that no rays are ready
39 * and the queue is not yet full. A return value of 1
40 * indicates that a ray was returned, though it is probably
41 * not the one you just requested. Rays may be identified by
42 * the rno member of the RAY struct, which is incremented
43 * by the rayorigin() call, or may be set explicitly by
44 * the caller. Below is an example call sequence:
45 *
46 * myRay.rorg = ( ray origin point )
47 * myRay.rdir = ( normalized ray direction )
48 * myRay.rmax = ( maximum length, or zero for no limit )
49 * rayorigin(&myRay, PRIMARY, NULL, NULL);
50 * myRay.rno = ( my personal ray identifier )
51 * if (ray_pqueue(&myRay) == 1)
52 * { do something with results }
53 *
54 * Note the differences between this and the simpler ray_trace()
55 * call. In particular, the call may or may not return a value
56 * in the passed ray structure. Also, you need to call rayorigin()
57 * yourself, which is normally called for you by ray_trace(). The
58 * benefit is that ray_pqueue() will trace rays faster in
59 * proportion to the number of CPUs you have available on your
60 * system. If the ray queue is full before the call, ray_pqueue()
61 * will block until a result is ready so it can queue this one.
62 * The global int ray_pnidle indicates the number of currently idle
63 * children. If you want to check for completed rays without blocking,
64 * or get the results from rays that have been queued without
65 * queuing any new ones, the ray_presult() call is for you:
66 *
67 * if (ray_presult(&myRay, 1) == 1)
68 * { do something with results }
69 *
70 * If the second argument is 1, the call won't block when
71 * results aren't ready, but will immediately return 0.
72 * If the second argument is 0, the call will block
73 * until a value is available, returning 0 only if the
74 * queue is completely empty. A negative return value
75 * indicates that a rendering process died. If this
76 * happens, ray_close(0) is automatically called to close
77 * all child processes, and ray_pnprocs is set to zero.
78 *
79 * If you just want to fill the ray queue without checking for
80 * results, check ray_pnidle and call ray_psend():
81 *
82 * while (ray_pnidle) {
83 * ( set up ray )
84 * ray_psend(&myRay);
85 * }
86 *
87 * Note that it is a fatal error to call ra_psend() when
88 * ray_pnidle is zero. The ray_presult() and/or ray_pqueue()
89 * functions may be called subsequently to read back the results.
90 *
91 * When you are done, you may call ray_pdone(1) to close
92 * all child processes and clean up memory used by Radiance.
93 * Any queued ray calculations will be awaited and discarded.
94 * As with ray_done(), ray_pdone(0) hangs onto data files
95 * and fonts that are likely to be used in subsequent renderings.
96 * Whether you want to bother cleaning up memory or not, you
97 * should at least call ray_pclose(0) to clean the child processes.
98 *
99 * Warning: You cannot affect any of the rendering processes
100 * by changing global parameter values onece ray_pinit() has
101 * been called. Changing global parameters will have no effect
102 * until the next call to ray_pinit(), which restarts everything.
103 * If you just want to reap children so that you can alter the
104 * rendering parameters without reloading the scene, use the
105 * ray_pclose(0) and ray_popen(nproc) calls to close
106 * then restart the child processes after the changes are made.
107 *
108 * Note: These routines are written to coordinate with the
109 * definitions in raycalls.c, and in fact depend on them.
110 * If you want to trace a ray and get a result synchronously,
111 * use the ray_trace() call to compute it in the parent process.
112 * This will not interfere with any subprocess calculations,
113 * but beware that a fatal error may end with a call to quit().
114 *
115 * Note: One of the advantages of using separate processes
116 * is that it gives the calling program some immunity from
117 * fatal rendering errors. As discussed in raycalls.c,
118 * Radiance tends to throw up its hands and exit at the
119 * first sign of trouble, calling quit() to return control
120 * to the top level. Although you can avoid exit() with
121 * your own longjmp() in quit(), the cleanup afterwards
122 * is always suspect. Through the use of subprocesses,
123 * we avoid this pitfall by closing the processes and
124 * returning a negative value from ray_pqueue() or
125 * ray_presult(). If you get a negative value from either
126 * of these calls, you can assume that the processes have
127 * been cleaned up with a call to ray_close(), though you
128 * will have to call ray_pdone() yourself if you want to
129 * free memory. Obviously, you cannot continue rendering
130 * without risking further errors, but otherwise your
131 * process should not be compromised.
132 */
133
134 #include "rtprocess.h"
135 #include "ray.h"
136 #include "ambient.h"
137 #include <sys/types.h>
138 #include <sys/wait.h>
139 #include "selcall.h"
140
141 #ifndef RAYQLEN
142 #define RAYQLEN 12 /* # rays to send at once */
143 #endif
144
145 #ifndef MAX_RPROCS
146 #if (FD_SETSIZE/2-4 < 64)
147 #define MAX_NPROCS (FD_SETSIZE/2-4)
148 #else
149 #define MAX_NPROCS 64 /* max. # rendering processes */
150 #endif
151 #endif
152
153 extern char *shm_boundary; /* boundary of shared memory */
154
155 int ray_pnprocs = 0; /* number of child processes */
156 int ray_pnidle = 0; /* number of idle children */
157
158 static struct child_proc {
159 int pid; /* child process id */
160 int fd_send; /* write to child here */
161 int fd_recv; /* read from child here */
162 int npending; /* # rays in process */
163 unsigned long rno[RAYQLEN]; /* working on these rays */
164 } r_proc[MAX_NPROCS]; /* our child processes */
165
166 static RAY r_queue[2*RAYQLEN]; /* ray i/o buffer */
167 static int r_send_next; /* next send ray placement */
168 static int r_recv_first; /* position of first unreported ray */
169 static int r_recv_next; /* next receive ray placement */
170
171 #define sendq_full() (r_send_next >= RAYQLEN)
172
173 static int ray_pflush(void);
174 static void ray_pchild(int fd_in, int fd_out);
175
176
177 extern void
178 ray_pinit( /* initialize ray-tracing processes */
179 char *otnm,
180 int nproc
181 )
182 {
183 if (nobjects > 0) /* close old calculation */
184 ray_pdone(0);
185
186 ray_init(otnm); /* load the shared scene */
187
188 preload_objs(); /* preload auxiliary data */
189
190 /* set shared memory boundary */
191 shm_boundary = (char *)malloc(16);
192 strcpy(shm_boundary, "SHM_BOUNDARY");
193
194 r_send_next = 0; /* set up queue */
195 r_recv_first = r_recv_next = RAYQLEN;
196
197 ray_popen(nproc); /* fork children */
198 }
199
200
201 static int
202 ray_pflush(void) /* send queued rays to idle children */
203 {
204 int nc, n, nw, i, sfirst;
205
206 if ((ray_pnidle <= 0) | (r_send_next <= 0))
207 return(0); /* nothing we can send */
208
209 sfirst = 0; /* divvy up labor */
210 nc = ray_pnidle;
211 for (i = ray_pnprocs; nc && i--; ) {
212 if (r_proc[i].npending > 0)
213 continue; /* child looks busy */
214 n = (r_send_next - sfirst)/nc--;
215 if (!n)
216 continue;
217 /* smuggle set size in crtype */
218 r_queue[sfirst].crtype = n;
219 nw = writebuf(r_proc[i].fd_send, (char *)&r_queue[sfirst],
220 sizeof(RAY)*n);
221 if (nw != sizeof(RAY)*n)
222 return(-1); /* write error */
223 r_proc[i].npending = n;
224 while (n--) /* record ray IDs */
225 r_proc[i].rno[n] = r_queue[sfirst+n].rno;
226 sfirst += r_proc[i].npending;
227 ray_pnidle--; /* now she's busy */
228 }
229 if (sfirst != r_send_next)
230 error(CONSISTENCY, "code screwup in ray_pflush");
231 r_send_next = 0;
232 return(sfirst); /* return total # sent */
233 }
234
235
236 extern void
237 ray_psend( /* add a ray to our send queue */
238 RAY *r
239 )
240 {
241 if (r == NULL)
242 return;
243 /* flush output if necessary */
244 if (sendq_full() && ray_pflush() <= 0)
245 error(INTERNAL, "ray_pflush failed in ray_psend");
246
247 r_queue[r_send_next++] = *r;
248 }
249
250
251 extern int
252 ray_pqueue( /* queue a ray for computation */
253 RAY *r
254 )
255 {
256 if (r == NULL)
257 return(0);
258 /* check for full send queue */
259 if (sendq_full()) {
260 RAY mySend;
261 int rval;
262 mySend = *r;
263 /* wait for a result */
264 rval = ray_presult(r, 0);
265 /* put new ray in queue */
266 r_queue[r_send_next++] = mySend;
267 return(rval); /* done */
268 }
269 /* else add ray to send queue */
270 r_queue[r_send_next++] = *r;
271 /* check for returned ray... */
272 if (r_recv_first >= r_recv_next)
273 return(0);
274 /* ...one is sitting in queue */
275 *r = r_queue[r_recv_first++];
276 return(1);
277 }
278
279
280 extern int
281 ray_presult( /* check for a completed ray */
282 RAY *r,
283 int poll
284 )
285 {
286 static struct timeval tpoll; /* zero timeval struct */
287 static fd_set readset, errset;
288 int n, ok;
289 register int pn;
290
291 if (r == NULL)
292 return(0);
293 /* check queued results first */
294 if (r_recv_first < r_recv_next) {
295 *r = r_queue[r_recv_first++];
296 /* make sure send queue has room */
297 if (sendq_full() && ray_pflush() <= 0)
298 return(-1);
299 return(1);
300 }
301 n = ray_pnprocs - ray_pnidle; /* pending before flush? */
302
303 if (ray_pflush() < 0) /* send new rays to process */
304 return(-1);
305 /* reset receive queue */
306 r_recv_first = r_recv_next = RAYQLEN;
307
308 if (!poll) /* count newly sent unless polling */
309 n = ray_pnprocs - ray_pnidle;
310 if (n <= 0) /* return if nothing to await */
311 return(0);
312 if (!poll && ray_pnprocs == 1) /* one process -> skip select() */
313 FD_SET(r_proc[0].fd_recv, &readset);
314
315 getready: /* any children waiting for us? */
316 for (pn = ray_pnprocs; pn--; )
317 if (FD_ISSET(r_proc[pn].fd_recv, &readset) ||
318 FD_ISSET(r_proc[pn].fd_recv, &errset))
319 break;
320 /* call select if we must */
321 if (pn < 0) {
322 FD_ZERO(&readset); FD_ZERO(&errset); n = 0;
323 for (pn = ray_pnprocs; pn--; ) {
324 if (r_proc[pn].npending > 0)
325 FD_SET(r_proc[pn].fd_recv, &readset);
326 FD_SET(r_proc[pn].fd_recv, &errset);
327 if (r_proc[pn].fd_recv >= n)
328 n = r_proc[pn].fd_recv + 1;
329 }
330 /* find out who is ready */
331 while ((n = select(n, &readset, (fd_set *)NULL, &errset,
332 poll ? &tpoll : (struct timeval *)NULL)) < 0)
333 if (errno != EINTR) {
334 error(WARNING,
335 "select call failed in ray_presult");
336 ray_pclose(0);
337 return(-1);
338 }
339 if (n > 0) /* go back and get it */
340 goto getready;
341 return(0); /* else poll came up empty */
342 }
343 if (r_recv_next + r_proc[pn].npending > sizeof(r_queue)/sizeof(RAY))
344 error(CONSISTENCY, "buffer shortage in ray_presult()");
345
346 /* read rendered ray data */
347 n = readbuf(r_proc[pn].fd_recv, (char *)&r_queue[r_recv_next],
348 sizeof(RAY)*r_proc[pn].npending);
349 if (n > 0) {
350 r_recv_next += n/sizeof(RAY);
351 ok = (n == sizeof(RAY)*r_proc[pn].npending);
352 } else
353 ok = 0;
354 /* reset child's status */
355 FD_CLR(r_proc[pn].fd_recv, &readset);
356 if (n <= 0)
357 FD_CLR(r_proc[pn].fd_recv, &errset);
358 r_proc[pn].npending = 0;
359 ray_pnidle++;
360 /* check for rendering errors */
361 if (!ok) {
362 ray_pclose(0); /* process died -- clean up */
363 return(-1);
364 }
365 /* preen returned rays */
366 for (n = r_recv_next - r_recv_first; n--; ) {
367 register RAY *rp = &r_queue[r_recv_first + n];
368 rp->rno = r_proc[pn].rno[n];
369 rp->parent = NULL;
370 rp->newcset = rp->clipset = NULL;
371 rp->rox = NULL;
372 rp->slights = NULL;
373 }
374 /* return first ray received */
375 *r = r_queue[r_recv_first++];
376 return(1);
377 }
378
379
380 extern void
381 ray_pdone( /* reap children and free data */
382 int freall
383 )
384 {
385 ray_pclose(0); /* close child processes */
386
387 if (shm_boundary != NULL) { /* clear shared memory boundary */
388 free((void *)shm_boundary);
389 shm_boundary = NULL;
390 }
391 ray_done(freall); /* free rendering data */
392 }
393
394
395 static void
396 ray_pchild( /* process rays (never returns) */
397 int fd_in,
398 int fd_out
399 )
400 {
401 int n;
402 register int i;
403 /* flag child process for quit() */
404 ray_pnprocs = -1;
405 /* read each ray request set */
406 while ((n = read(fd_in, (char *)r_queue, sizeof(r_queue))) > 0) {
407 int n2;
408 if (n < sizeof(RAY))
409 break;
410 /* get smuggled set length */
411 n2 = sizeof(RAY)*r_queue[0].crtype - n;
412 if (n2 < 0)
413 error(INTERNAL, "buffer over-read in ray_pchild");
414 if (n2 > 0) { /* read the rest of the set */
415 i = readbuf(fd_in, (char *)r_queue + n, n2);
416 if (i != n2)
417 break;
418 n += n2;
419 }
420 n /= sizeof(RAY);
421 /* evaluate rays */
422 for (i = 0; i < n; i++) {
423 r_queue[i].crtype = r_queue[i].rtype;
424 r_queue[i].parent = NULL;
425 r_queue[i].clipset = NULL;
426 r_queue[i].slights = NULL;
427 samplendx++;
428 rayclear(&r_queue[i]);
429 rayvalue(&r_queue[i]);
430 }
431 /* write back our results */
432 i = writebuf(fd_out, (char *)r_queue, sizeof(RAY)*n);
433 if (i != sizeof(RAY)*n)
434 error(SYSTEM, "write error in ray_pchild");
435 }
436 if (n)
437 error(SYSTEM, "read error in ray_pchild");
438 ambsync();
439 quit(0); /* normal exit */
440 }
441
442
443 extern void
444 ray_popen( /* open the specified # processes */
445 int nadd
446 )
447 {
448 /* check if our table has room */
449 if (ray_pnprocs + nadd > MAX_NPROCS)
450 nadd = MAX_NPROCS - ray_pnprocs;
451 if (nadd <= 0)
452 return;
453 ambsync(); /* load any new ambient values */
454 fflush(NULL); /* clear pending output */
455 while (nadd--) { /* fork each new process */
456 int p0[2], p1[2];
457 if (pipe(p0) < 0 || pipe(p1) < 0)
458 error(SYSTEM, "cannot create pipe");
459 if ((r_proc[ray_pnprocs].pid = fork()) == 0) {
460 int pn; /* close others' descriptors */
461 for (pn = ray_pnprocs; pn--; ) {
462 close(r_proc[pn].fd_send);
463 close(r_proc[pn].fd_recv);
464 }
465 close(p0[0]); close(p1[1]);
466 /* following call never returns */
467 ray_pchild(p1[0], p0[1]);
468 }
469 if (r_proc[ray_pnprocs].pid < 0)
470 error(SYSTEM, "cannot fork child process");
471 close(p1[0]); close(p0[1]);
472 /*
473 * Close write stream on exec to avoid multiprocessing deadlock.
474 * No use in read stream without it, so set flag there as well.
475 */
476 fcntl(p1[1], F_SETFD, FD_CLOEXEC);
477 fcntl(p0[0], F_SETFD, FD_CLOEXEC);
478 r_proc[ray_pnprocs].fd_send = p1[1];
479 r_proc[ray_pnprocs].fd_recv = p0[0];
480 r_proc[ray_pnprocs].npending = 0;
481 ray_pnprocs++;
482 ray_pnidle++;
483 }
484 }
485
486
487 extern void
488 ray_pclose( /* close one or more child processes */
489 int nsub
490 )
491 {
492 static int inclose = 0;
493 RAY res;
494 /* check recursion */
495 if (inclose)
496 return;
497 inclose++;
498 /* check argument */
499 if ((nsub <= 0) | (nsub > ray_pnprocs))
500 nsub = ray_pnprocs;
501 /* clear our ray queue */
502 while (ray_presult(&res,0) > 0)
503 ;
504 /* clean up children */
505 while (nsub--) {
506 int status;
507 ray_pnprocs--;
508 close(r_proc[ray_pnprocs].fd_recv);
509 close(r_proc[ray_pnprocs].fd_send);
510 if (waitpid(r_proc[ray_pnprocs].pid, &status, 0) < 0)
511 status = 127<<8;
512 if (status) {
513 sprintf(errmsg,
514 "rendering process %d exited with code %d",
515 r_proc[ray_pnprocs].pid, status>>8);
516 error(WARNING, errmsg);
517 }
518 ray_pnidle--;
519 }
520 inclose--;
521 }
522
523
524 void
525 quit(ec) /* make sure exit is called */
526 int ec;
527 {
528 if (ray_pnprocs > 0) /* close children if any */
529 ray_pclose(0);
530 exit(ec);
531 }