Back to index

radiance  4R0+20100331
rayfifo.c
Go to the documentation of this file.
00001 #ifndef lint
00002 static const char RCSid[] = "$Id: rayfifo.c,v 2.4 2009/12/13 19:13:04 greg Exp $";
00003 #endif
00004 /*
00005  *  rayfifo.c - parallelize ray queue that respects order
00006  *
00007  *  External symbols declared in ray.h
00008  */
00009 
00010 #include "copyright.h"
00011 
00012 /*
00013  *  These routines are essentially an adjunct to raypcalls.c, providing
00014  *  a convenient means to get first-in/first-out behavior from multiple
00015  *  processor cores.  The interface is quite simple, with two functions
00016  *  and a callback, which must be defined by the calling program.  The
00017  *  hand-off for finished rays is assigned to ray_fifo_out, which takes
00018  *  a single pointer to the finished ray and returns a non-negative
00019  *  integer.  If there is an exceptional condition where termination
00020  *  is desired, a negative value may be returned.
00021  *
00022  *  The ray_fifo_in() call takes a ray that has been initialized in
00023  *  the same manner as for the ray_pqueue() call, i.e., rayorigin()
00024  *  has been called and the origin, direction and maximum distance
00025  *  have all been assigned.  However, the ray number will be reset
00026  *  by ray_fifo_in() according to the number of rays traced since the
00027  *  last call to ray_fifo_flush().  This final call completes all
00028  *  pending ray calculations and frees the FIFO buffer.  If any of
00029  *  the automatic calls to the ray_fifo_out callback return a
00030  *  negative value, processing stops and -1 is returned.
00031  *
00032  *  Note:  The ray passed to ray_fifo_in() may be overwritten
00033  *  arbitrarily, since it is passed to ray_pqueue().
00034  */
00035 
00036 #include  "ray.h"
00037 #include  <string.h>
00038 
00039 int           (*ray_fifo_out)(RAY *r) = NULL;    /* ray output callback */
00040 
00041 static RAY    *r_fifo_buf = NULL;         /* circular FIFO out buffer */
00042 static int    r_fifo_len = 0;                    /* allocated FIFO length */
00043 static RNUMBER       r_fifo_start = 1;           /* first awaited ray */
00044 static RNUMBER       r_fifo_end = 1;                    /* one past FIFO last */
00045 static RNUMBER       r_fifo_next = 1;            /* next ray assignment */
00046 
00047 #define r_fifo(rn)   (&r_fifo_buf[(rn)&(r_fifo_len-1)])
00048 
00049 
00050 static void
00051 ray_fifo_growbuf(void)      /* double buffer size (or set to minimum if NULL) */
00052 {
00053        RAY    *old_buf = r_fifo_buf;
00054        int    old_len = r_fifo_len;
00055        int    i;
00056 
00057        if (r_fifo_buf == NULL)
00058               r_fifo_len = 1<<5;
00059        else
00060               r_fifo_len <<= 1;
00061                                           /* allocate new */
00062        r_fifo_buf = (RAY *)calloc(r_fifo_len, sizeof(RAY));
00063        if (r_fifo_buf == NULL)
00064               error(SYSTEM, "out of memory in ray_fifo_growbuf");
00065        if (old_buf == NULL)
00066               return;
00067                                           /* copy old & free */
00068        for (i = r_fifo_start; i < r_fifo_end; i++)
00069               *r_fifo(i) = old_buf[i&(old_len-1)];
00070 
00071        free(old_buf);
00072 }
00073 
00074 
00075 static int
00076 ray_fifo_push(              /* send finished ray to output (or queue it) */
00077        RAY *r
00078 )
00079 {
00080        int    rv, nsent = 0;
00081 
00082        if (ray_fifo_out == NULL)
00083               error(INTERNAL, "ray_fifo_out is NULL");
00084        if ((r->rno < r_fifo_start) | (r->rno >= r_fifo_next))
00085               error(INTERNAL, "unexpected ray number in ray_fifo_push()");
00086 
00087        if (r->rno > r_fifo_start) {              /* insert into output queue */
00088               while (r->rno - r_fifo_start >= r_fifo_len)
00089                      ray_fifo_growbuf();  /* need more space */
00090               *r_fifo(r->rno) = *r;
00091               if (r->rno >= r_fifo_end)
00092                      r_fifo_end = r->rno + 1;
00093               return(0);
00094        }
00095                      /* r->rno == r_fifo_start, so transfer ray(s) */
00096        do {
00097               rv = (*ray_fifo_out)(r);
00098               r->rno = 0;                 /* flag this entry complete */
00099               if (rv < 0)
00100                      return(-1);
00101               nsent += rv;
00102               if (++r_fifo_start < r_fifo_end)
00103                      r = r_fifo(r_fifo_start);
00104               else if (r_fifo_start > r_fifo_end)
00105                      r_fifo_end = r_fifo_start;
00106        } while (r->rno == r_fifo_start);
00107 
00108        return(nsent);
00109 }
00110 
00111 
00112 int
00113 ray_fifo_in(         /* add ray to FIFO */
00114        RAY    *r
00115 )
00116 {
00117        static int    incall = 0;          /* prevent recursion */
00118        int           rv, rval = 0;
00119 
00120        if (incall++)
00121               error(INTERNAL, "recursive call to ray_fifo_in()");
00122 
00123        if (r_fifo_start >= 1L<<30) {             /* reset our counters */
00124               if ((rv = ray_fifo_flush()) < 0)
00125                      {--incall; return(-1);}
00126               rval += rv;
00127        }
00128                                           /* queue ray */
00129        r->rno = r_fifo_next++;
00130        if ((rv = ray_pqueue(r)) < 0)
00131               {--incall; return(-1);}
00132 
00133        if (!rv)                           /* no result this time? */
00134               {--incall; return(rval);}
00135        
00136        do {                               /* else send/queue result */
00137               if ((rv = ray_fifo_push(r)) < 0)
00138                      {--incall; return(-1);}
00139               rval += rv;
00140 
00141        } while (ray_presult(r, -1) > 0);  /* empty in-core queue */
00142 
00143        --incall; return(rval);
00144 }
00145 
00146 
00147 int
00148 ray_fifo_flush(void) /* flush everything and release buffer */
00149 {
00150        RAY    myRay;
00151        int    rv, rval = 0;
00152                                           /* clear parallel queue */
00153        while ((rv = ray_presult(&myRay, 0)) > 0 &&
00154                      (rv = ray_fifo_push(&myRay)) >= 0)
00155               rval += rv;
00156 
00157        if (rv < 0)                        /* check for exception */
00158               return(-1);
00159 
00160        if (r_fifo_start != r_fifo_end)
00161               error(INTERNAL, "could not empty queue in ray_fifo_flush()");
00162 
00163        if (r_fifo_buf != NULL) {
00164               free(r_fifo_buf);
00165               r_fifo_buf = NULL; r_fifo_len = 0;
00166        }
00167        r_fifo_next = r_fifo_end = r_fifo_start = 1;
00168 
00169        return(rval);
00170 }