Back to index

radiance  4R0+20100331
raypcalls.c
Go to the documentation of this file.
00001 #ifndef lint
00002 static const char    RCSid[] = "$Id: raypcalls.c,v 2.26 2009/12/16 01:06:50 greg Exp $";
00003 #endif
00004 /*
00005  *  raypcalls.c - interface for parallel rendering using Radiance
00006  *
00007  *  External symbols declared in ray.h
00008  */
00009 
00010 #include "copyright.h"
00011 
00012 /*
00013  *  These calls are designed similarly to the ones in raycalls.c,
00014  *  but allow for multiple rendering processes on the same host
00015  *  machine.  There is no sense in specifying more child processes
00016  *  than you have processor cores, but one child may help by allowing
00017  *  asynchronous ray computation in an interactive program, and
00018  *  will protect the caller from fatal rendering errors.
00019  *
00020  *  You should first read and understand the header in raycalls.c,
00021  *  as some things are explained there that are not repated here.
00022  *
00023  *  The first step is opening one or more rendering processes
00024  *  with a call to ray_pinit(oct, nproc).  Before calling fork(),
00025  *  ray_pinit() loads the octree and data structures into the
00026  *  caller's memory, and ray_popen() synchronizes the ambient
00027  *  file, if any.  Shared memory permits all sorts of queries
00028  *  that wouldn't be possible otherwise without causing any real
00029  *  memory overhead, since all the static data are shared
00030  *  between processes.  Rays are traced using a simple
00031  *  queuing mechanism, explained below.
00032  *
00033  *  The ray queue buffers RAYQLEN rays before sending to
00034  *  children, each of which may internally buffer RAYQLEN rays
00035  *  during evaluation.  Rays are not returned in the order
00036  *  they are sent when multiple processes are open.
00037  *
00038  *  Rays are queued and returned by a single
00039  *  ray_pqueue() call.  A ray_pqueue() return
00040  *  value of 0 indicates that no rays are ready
00041  *  and the queue is not yet full.  A return value of 1
00042  *  indicates that a ray was returned, though it is probably
00043  *  not the one you just requested.  Rays may be identified by
00044  *  the rno member of the RAY struct, which is incremented
00045  *  by the rayorigin() call, or may be set explicitly by
00046  *  the caller.  Below is an example call sequence:
00047  *
00048  *     myRay.rorg = ( ray origin point )
00049  *     myRay.rdir = ( normalized ray direction )
00050  *     myRay.rmax = ( maximum length, or zero for no limit )
00051  *     rayorigin(&myRay, PRIMARY, NULL, NULL);
00052  *     myRay.rno = ( my personal ray identifier )
00053  *     if (ray_pqueue(&myRay) == 1)
00054  *            { do something with results }
00055  *
00056  *  Note the differences between this and the simpler ray_trace()
00057  *  call.  In particular, the call may or may not return a value
00058  *  in the passed ray structure.  Also, you need to call rayorigin()
00059  *  yourself, which is normally called for you by ray_trace().  The
00060  *  benefit is that ray_pqueue() will trace rays faster in
00061  *  proportion to the number of CPUs you have available on your
00062  *  system.  If the ray queue is full before the call, ray_pqueue()
00063  *  will block until a result is ready so it can queue this one.
00064  *  The global int ray_pnidle indicates the number of currently idle
00065  *  children.  If you want to check for completed rays without blocking,
00066  *  or get the results from rays that have been queued without
00067  *  queuing any new ones, the ray_presult() call is for you:
00068  *
00069  *     if (ray_presult(&myRay, 1) == 1)
00070  *            { do something with results }
00071  *
00072  *  If the second argument is 1, the call won't block when
00073  *  results aren't ready, but will immediately return 0.
00074  *  If the second argument is 0, the call will block
00075  *  until a value is available, returning 0 only if the
00076  *  queue is completely empty.  Setting the second argument
00077  *  to -1 returns 0 unless a ray is ready in the queue and
00078  *  no system calls are needed.  A negative return value
00079  *  indicates that a rendering process died.  If this
00080  *  happens, ray_pclose(0) is automatically called to close
00081  *  all child processes, and ray_pnprocs is set to zero.
00082  *
00083  *  If you just want to fill the ray queue without checking for
00084  *  results, check ray_pnidle and call ray_psend():
00085  *
00086  *     while (ray_pnidle) {
00087  *            ( set up ray )
00088  *            ray_psend(&myRay);
00089  *     }
00090  *
00091  *  Note that it is a mistake to call ra_psend() when
00092  *  ray_pnidle is zero, and nothing will be sent in
00093  *  this case.  Otherwise, the ray_presult() and/or ray_pqueue()
00094  *  functions may be called subsequently to read back the results
00095  *  of rays queued by ray_psend().
00096  *
00097  *  When you are done, you may call ray_pdone(1) to close
00098  *  all child processes and clean up memory used by Radiance.
00099  *  Any queued ray calculations will be awaited and discarded.
00100  *  As with ray_done(), ray_pdone(0) hangs onto data files
00101  *  and fonts that are likely to be used in subsequent renderings.
00102  *  Whether you need to clean up memory or not, you should
00103  *  at least call ray_pclose(0) to await the child processes.
00104  *  The caller should define a quit() function that calls
00105  *  ray_pclose(0) if ray_pnprocs > 0.
00106  *
00107  *  Warning:  You cannot affect any of the rendering processes
00108  *  by changing global parameter values onece ray_pinit() has
00109  *  been called.  Changing global parameters will have no effect
00110  *  until the next call to ray_pinit(), which restarts everything.
00111  *  If you just want to reap children so that you can alter the
00112  *  rendering parameters without reloading the scene, use the
00113  *  ray_pclose(0) and ray_popen(nproc) calls to close
00114  *  then restart the child processes after the changes are made.
00115  *
00116  *  Note:  These routines are written to coordinate with the
00117  *  definitions in raycalls.c, and in fact depend on them.
00118  *  If you want to trace a ray and get a result synchronously,
00119  *  use the ray_trace() call to compute it in the parent process.
00120  *  This will not interfere with any subprocess calculations,
00121  *  but beware that a fatal error may end with a call to quit().
00122  *
00123  *  Note:  One of the advantages of using separate processes
00124  *  is that it gives the calling program some immunity from
00125  *  fatal rendering errors.  As discussed in raycalls.c,
00126  *  Radiance tends to throw up its hands and exit at the
00127  *  first sign of trouble, calling quit() to return control
00128  *  to the top level.  Although you can avoid exit() with
00129  *  your own longjmp() in quit(), the cleanup afterwards
00130  *  is always suspect.  Through the use of subprocesses,
00131  *  we avoid this pitfall by closing the processes and
00132  *  returning a negative value from ray_pqueue() or
00133  *  ray_presult().  If you get a negative value from either
00134  *  of these calls, you can assume that the processes have
00135  *  been cleaned up with a call to ray_pclose(), though you
00136  *  will have to call ray_pdone() yourself if you want to
00137  *  free memory.  Obviously, you cannot continue rendering
00138  *  without risking further errors, but otherwise your
00139  *  process should not be compromised.
00140  */
00141 
00142 #include  "rtprocess.h"
00143 #include  "ray.h"
00144 #include  "ambient.h"
00145 #include  <sys/types.h>
00146 #include  <sys/wait.h>
00147 #include  "selcall.h"
00148 
00149 #ifndef RAYQLEN
00150 #define RAYQLEN             12            /* # rays to send at once */
00151 #endif
00152 
00153 #ifndef MAX_RPROCS
00154 #if (FD_SETSIZE/2-4 < 64) 
00155 #define MAX_NPROCS   (FD_SETSIZE/2-4)
00156 #else
00157 #define MAX_NPROCS   64            /* max. # rendering processes */
00158 #endif
00159 #endif
00160 
00161 extern char   *shm_boundary;              /* boundary of shared memory */
00162 
00163 int           ray_pnprocs = 0;     /* number of child processes */
00164 int           ray_pnidle = 0;             /* number of idle children */
00165 
00166 static struct child_proc {
00167        int    pid;                        /* child process id */
00168        int    fd_send;                    /* write to child here */
00169        int    fd_recv;                    /* read from child here */
00170        int    npending;                   /* # rays in process */
00171        RNUMBER       rno[RAYQLEN];               /* working on these rays */
00172 } r_proc[MAX_NPROCS];                     /* our child processes */
00173 
00174 static RAY    r_queue[2*RAYQLEN];  /* ray i/o buffer */
00175 static int    r_send_next = 0;     /* next send ray placement */
00176 static int    r_recv_first = RAYQLEN;     /* position of first unreported ray */
00177 static int    r_recv_next = RAYQLEN;      /* next received ray placement */
00178 
00179 #define sendq_full() (r_send_next >= RAYQLEN)
00180 
00181 static int ray_pflush(void);
00182 static void ray_pchild(int fd_in, int fd_out);
00183 
00184 
00185 void
00186 ray_pinit(           /* initialize ray-tracing processes */
00187        char   *otnm,
00188        int    nproc
00189 )
00190 {
00191        if (nobjects > 0)           /* close old calculation */
00192               ray_pdone(0);
00193 
00194        ray_init(otnm);                    /* load the shared scene */
00195 
00196        ray_popen(nproc);           /* fork children */
00197 }
00198 
00199 
00200 static int
00201 ray_pflush(void)                   /* send queued rays to idle children */
00202 {
00203        int    nc, n, nw, i, sfirst;
00204 
00205        if ((ray_pnidle <= 0) | (r_send_next <= 0))
00206               return(0);           /* nothing we can send */
00207        
00208        sfirst = 0;                 /* divvy up labor */
00209        nc = ray_pnidle;
00210        for (i = ray_pnprocs; nc && i--; ) {
00211               if (r_proc[i].npending > 0)
00212                      continue;     /* child looks busy */
00213               n = (r_send_next - sfirst)/nc--;
00214               if (!n)
00215                      continue;
00216                                    /* smuggle set size in crtype */
00217               r_queue[sfirst].crtype = n;
00218               nw = writebuf(r_proc[i].fd_send, (char *)&r_queue[sfirst],
00219                             sizeof(RAY)*n);
00220               if (nw != sizeof(RAY)*n)
00221                      return(-1);   /* write error */
00222               r_proc[i].npending = n;
00223               while (n--)          /* record ray IDs */
00224                      r_proc[i].rno[n] = r_queue[sfirst+n].rno;
00225               sfirst += r_proc[i].npending;
00226               ray_pnidle--;        /* now she's busy */
00227        }
00228        if (sfirst != r_send_next)
00229               error(CONSISTENCY, "code screwup in ray_pflush()");
00230        r_send_next = 0;
00231        return(sfirst);                    /* return total # sent */
00232 }
00233 
00234 
00235 int
00236 ray_psend(                  /* add a ray to our send queue */
00237        RAY    *r
00238 )
00239 {
00240        int    rv;
00241 
00242        if ((r == NULL) | (ray_pnidle <= 0))
00243               return(0);
00244                                    /* flush output if necessary */
00245        if (sendq_full() && (rv = ray_pflush()) <= 0)
00246               return(rv);
00247 
00248        r_queue[r_send_next++] = *r;
00249        return(1);
00250 }
00251 
00252 
00253 int
00254 ray_pqueue(                 /* queue a ray for computation */
00255        RAY    *r
00256 )
00257 {
00258        if (r == NULL)
00259               return(0);
00260                                    /* check for full send queue */
00261        if (sendq_full()) {
00262               RAY    mySend = *r;
00263                                    /* wait for a result */
00264               if (ray_presult(r, 0) <= 0)
00265                      return(-1);
00266                                    /* put new ray in queue */
00267               r_queue[r_send_next++] = mySend;
00268 
00269               return(1);
00270        }
00271                                    /* else add ray to send queue */
00272        r_queue[r_send_next++] = *r;
00273                                    /* check for returned ray... */
00274        if (r_recv_first >= r_recv_next)
00275               return(0);
00276                                    /* ...one is sitting in queue */
00277        *r = r_queue[r_recv_first++];
00278        return(1);
00279 }
00280 
00281 
00282 int
00283 ray_presult(         /* check for a completed ray */
00284        RAY    *r,
00285        int    poll
00286 )
00287 {
00288        static struct timeval       tpoll; /* zero timeval struct */
00289        static fd_set readset, errset;
00290        int    n, ok;
00291        register int  pn;
00292 
00293        if (r == NULL)
00294               return(0);
00295                                    /* check queued results first */
00296        if (r_recv_first < r_recv_next) {
00297               *r = r_queue[r_recv_first++];
00298               return(1);
00299        }
00300        if (poll < 0)               /* immediate polling mode? */
00301               return(0);
00302 
00303        n = ray_pnprocs - ray_pnidle;      /* pending before flush? */
00304 
00305        if (ray_pflush() < 0)              /* send new rays to process */
00306               return(-1);
00307                                    /* reset receive queue */
00308        r_recv_first = r_recv_next = RAYQLEN;
00309 
00310        if (!poll)                  /* count newly sent unless polling */
00311               n = ray_pnprocs - ray_pnidle;
00312        if (n <= 0)                 /* return if nothing to await */
00313               return(0);
00314        if (!poll && ray_pnprocs == 1)     /* one process -> skip select() */
00315               FD_SET(r_proc[0].fd_recv, &readset);
00316 
00317 getready:                          /* any children waiting for us? */
00318        for (pn = ray_pnprocs; pn--; )
00319               if (FD_ISSET(r_proc[pn].fd_recv, &readset) ||
00320                             FD_ISSET(r_proc[pn].fd_recv, &errset))
00321                      break;
00322                                    /* call select() if we must */
00323        if (pn < 0) {
00324               FD_ZERO(&readset); FD_ZERO(&errset); n = 0;
00325               for (pn = ray_pnprocs; pn--; ) {
00326                      if (r_proc[pn].npending > 0)
00327                             FD_SET(r_proc[pn].fd_recv, &readset);
00328                      FD_SET(r_proc[pn].fd_recv, &errset);
00329                      if (r_proc[pn].fd_recv >= n)
00330                             n = r_proc[pn].fd_recv + 1;
00331               }
00332                                    /* find out who is ready */
00333               while ((n = select(n, &readset, (fd_set *)NULL, &errset,
00334                             poll ? &tpoll : (struct timeval *)NULL)) < 0)
00335                      if (errno != EINTR) {
00336                             error(WARNING,
00337                                    "select call failed in ray_presult()");
00338                             ray_pclose(0);
00339                             return(-1);
00340                      }
00341               if (n > 0)           /* go back and get it */
00342                      goto getready;
00343               return(0);           /* else poll came up empty */
00344        }
00345        if (r_recv_next + r_proc[pn].npending > sizeof(r_queue)/sizeof(RAY))
00346               error(CONSISTENCY, "buffer shortage in ray_presult()");
00347 
00348                                    /* read rendered ray data */
00349        n = readbuf(r_proc[pn].fd_recv, (char *)&r_queue[r_recv_next],
00350                      sizeof(RAY)*r_proc[pn].npending);
00351        if (n > 0) {
00352               r_recv_next += n/sizeof(RAY);
00353               ok = (n == sizeof(RAY)*r_proc[pn].npending);
00354        } else
00355               ok = 0;
00356                                    /* reset child's status */
00357        FD_CLR(r_proc[pn].fd_recv, &readset);
00358        if (n <= 0)
00359               FD_CLR(r_proc[pn].fd_recv, &errset);
00360        r_proc[pn].npending = 0;
00361        ray_pnidle++;
00362                                    /* check for rendering errors */
00363        if (!ok) {
00364               ray_pclose(0);              /* process died -- clean up */
00365               return(-1);
00366        }
00367                                    /* preen returned rays */
00368        for (n = r_recv_next - r_recv_first; n--; ) {
00369               register RAY  *rp = &r_queue[r_recv_first + n];
00370               rp->rno = r_proc[pn].rno[n];
00371               rp->parent = NULL;
00372               rp->newcset = rp->clipset = NULL;
00373               rp->rox = NULL;
00374               rp->slights = NULL;
00375        }
00376                                    /* return first ray received */
00377        *r = r_queue[r_recv_first++];
00378        return(1);
00379 }
00380 
00381 
00382 void
00383 ray_pdone(           /* reap children and free data */
00384        int    freall
00385 )
00386 {
00387        ray_pclose(0);                     /* close child processes */
00388 
00389        if (shm_boundary != NULL) { /* clear shared memory boundary */
00390               free((void *)shm_boundary);
00391               shm_boundary = NULL;
00392        }
00393 
00394        ray_done(freall);           /* free rendering data */
00395 }
00396 
00397 
00398 static void
00399 ray_pchild(   /* process rays (never returns) */
00400        int    fd_in,
00401        int    fd_out
00402 )
00403 {
00404        int    n;
00405        register int  i;
00406                                    /* flag child process for quit() */
00407        ray_pnprocs = -1;
00408                                    /* read each ray request set */
00409        while ((n = read(fd_in, (char *)r_queue, sizeof(r_queue))) > 0) {
00410               int    n2;
00411               if (n < sizeof(RAY))
00412                      break;
00413                                    /* get smuggled set length */
00414               n2 = sizeof(RAY)*r_queue[0].crtype - n;
00415               if (n2 < 0)
00416                      error(INTERNAL, "buffer over-read in ray_pchild()");
00417               if (n2 > 0) {        /* read the rest of the set */
00418                      i = readbuf(fd_in, (char *)r_queue + n, n2);
00419                      if (i != n2)
00420                             break;
00421                      n += n2;
00422               }
00423               n /= sizeof(RAY);
00424                                    /* evaluate rays */
00425               for (i = 0; i < n; i++) {
00426                      r_queue[i].crtype = r_queue[i].rtype;
00427                      r_queue[i].parent = NULL;
00428                      r_queue[i].clipset = NULL;
00429                      r_queue[i].slights = NULL;
00430                      r_queue[i].rlvl = 0;
00431                      samplendx++;
00432                      rayclear(&r_queue[i]);
00433                      rayvalue(&r_queue[i]);
00434               }
00435                                    /* write back our results */
00436               i = writebuf(fd_out, (char *)r_queue, sizeof(RAY)*n);
00437               if (i != sizeof(RAY)*n)
00438                      error(SYSTEM, "write error in ray_pchild()");
00439        }
00440        if (n)
00441               error(SYSTEM, "read error in ray_pchild()");
00442        ambsync();
00443        quit(0);                    /* normal exit */
00444 }
00445 
00446 
00447 void
00448 ray_popen(                  /* open the specified # processes */
00449        int    nadd
00450 )
00451 {
00452                                    /* check if our table has room */
00453        if (ray_pnprocs + nadd > MAX_NPROCS)
00454               nadd = MAX_NPROCS - ray_pnprocs;
00455        if (nadd <= 0)
00456               return;
00457        ambsync();                  /* load any new ambient values */
00458        if (shm_boundary == NULL) { /* first child process? */
00459               preload_objs();             /* preload auxiliary data */
00460                                    /* set shared memory boundary */
00461               shm_boundary = (char *)malloc(16);
00462               strcpy(shm_boundary, "SHM_BOUNDARY");
00463        }
00464        fflush(NULL);               /* clear pending output */
00465        while (nadd--) {            /* fork each new process */
00466               int    p0[2], p1[2];
00467               if (pipe(p0) < 0 || pipe(p1) < 0)
00468                      error(SYSTEM, "cannot create pipe");
00469               if ((r_proc[ray_pnprocs].pid = fork()) == 0) {
00470                      int    pn;    /* close others' descriptors */
00471                      for (pn = ray_pnprocs; pn--; ) {
00472                             close(r_proc[pn].fd_send);
00473                             close(r_proc[pn].fd_recv);
00474                      }
00475                      close(p0[0]); close(p1[1]);
00476                      close(0);     /* don't share stdin */
00477                                    /* following call never returns */
00478                      ray_pchild(p1[0], p0[1]);
00479               }
00480               if (r_proc[ray_pnprocs].pid < 0)
00481                      error(SYSTEM, "cannot fork child process");
00482               close(p1[0]); close(p0[1]);
00483               /*
00484                * Close write stream on exec to avoid multiprocessing deadlock.
00485                * No use in read stream without it, so set flag there as well.
00486                */
00487               fcntl(p1[1], F_SETFD, FD_CLOEXEC);
00488               fcntl(p0[0], F_SETFD, FD_CLOEXEC);
00489               r_proc[ray_pnprocs].fd_send = p1[1];
00490               r_proc[ray_pnprocs].fd_recv = p0[0];
00491               r_proc[ray_pnprocs].npending = 0;
00492               ray_pnprocs++;
00493               ray_pnidle++;
00494        }
00495 }
00496 
00497 
00498 void
00499 ray_pclose(          /* close one or more child processes */
00500        int    nsub
00501 )
00502 {
00503        static int    inclose = 0;
00504        RAY    res;
00505                                    /* check recursion */
00506        if (inclose)
00507               return;
00508        inclose++;
00509                                    /* check no child / in child */
00510        if (ray_pnprocs <= 0)
00511               return;
00512                                    /* check argument */
00513        if ((nsub <= 0) | (nsub > ray_pnprocs))
00514               nsub = ray_pnprocs;
00515                                    /* clear our ray queue */
00516        while (ray_presult(&res,0) > 0)
00517               ;
00518        r_send_next = 0;            /* hard reset in case of error */
00519        r_recv_first = r_recv_next = RAYQLEN;
00520                                    /* clean up children */
00521        while (nsub--) {
00522               int    status;
00523               ray_pnprocs--;
00524               close(r_proc[ray_pnprocs].fd_send);
00525               if (waitpid(r_proc[ray_pnprocs].pid, &status, 0) < 0)
00526                      status = 127<<8;
00527               close(r_proc[ray_pnprocs].fd_recv);
00528               if (status) {
00529                      sprintf(errmsg,
00530                             "rendering process %d exited with code %d",
00531                                    r_proc[ray_pnprocs].pid, status>>8);
00532                      error(WARNING, errmsg);
00533               }
00534               ray_pnidle--;
00535        }
00536        inclose--;
00537 }