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

# User Rev Content
1 greg 2.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     }