ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/raypcalls.c
Revision: 2.1
Committed: Sat Feb 22 02:07:29 2003 UTC (21 years, 2 months ago) by greg
Content type: text/plain
Branch: MAIN
Log Message:
Changes and check-in for 3.5 release
Includes new source files and modifications not recorded for many years
See ray/doc/notes/ReleaseNotes for notes between 3.1 and 3.5 release

File Contents

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