ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/util/rtcontrib.c
Revision: 1.67
Committed: Tue Apr 10 05:16:50 2012 UTC (12 years, 1 month ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 1.66: +58 -3 lines
Log Message:
Eliminated file thread locking in parent process

File Contents

# User Rev Content
1 greg 1.1 #ifndef lint
2 greg 1.67 static const char RCSid[] = "$Id: rtcontrib.c,v 1.66 2012/02/23 06:03:46 greg Exp $";
3 greg 1.1 #endif
4     /*
5     * Gather rtrace output to compute contributions from particular sources
6     */
7    
8 greg 1.67 /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
9     Need to refactor code by forking a subprocess for each
10     rtrace call to take output and accumulate it into bins
11     for the parent process. This will avoid our current
12     bottleneck around processing output queues, computing
13     bins and the associated buffer growth, which can be crazy
14     (gigabytes/subprocess). Each child process will return
15     a ray number and a fully computed and ready-to-output
16     record of modifiers and their summed bins. These will
17     be queued and sorted by the parent for ordered output.
18     XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX*/
19    
20 greg 1.4 #include "standard.h"
21 greg 1.1 #include <ctype.h>
22 greg 1.7 #include <signal.h>
23 greg 1.1 #include "platform.h"
24     #include "rtprocess.h"
25     #include "selcall.h"
26     #include "color.h"
27     #include "resolu.h"
28     #include "lookup.h"
29     #include "calcomp.h"
30    
31 greg 1.65 #ifdef _WIN32
32 greg 1.66 typedef int ssize_t;
33 greg 1.65 #endif
34    
35 greg 1.19 #ifndef MAXMODLIST
36 greg 1.1 #define MAXMODLIST 1024 /* maximum modifiers we'll track */
37 greg 1.19 #endif
38 greg 1.1
39 greg 1.64 ssize_t treebufsiz = BUFSIZ; /* current tree buffer size */
40 greg 1.1
41 greg 1.2 typedef double DCOLOR[3]; /* double-precision color */
42    
43 greg 1.1 /*
44 greg 1.31 * The MODCONT structure is used to accumulate ray contributions
45 greg 1.1 * for a particular modifier, which may be subdivided into bins
46 greg 1.45 * if binv evaluates > 0. If outspec contains a %s in it, this will
47 greg 1.1 * be replaced with the modifier name. If outspec contains a %d in it,
48     * this will be used to create one output file per bin, otherwise all bins
49     * will be written to the same file, in order. If the global outfmt
50     * is 'c', then a 4-byte RGBE pixel will be output for each bin value
51     * and the file will conform to a RADIANCE image if xres & yres are set.
52     */
53     typedef struct {
54     const char *outspec; /* output file specification */
55     const char *modname; /* modifier name */
56     EPNODE *binv; /* bin value expression */
57     int nbins; /* number of accumulation bins */
58 greg 1.2 DCOLOR cbin[1]; /* contribution bins (extends struct) */
59 greg 1.1 } MODCONT; /* modifier contribution */
60    
61     static void mcfree(void *p) { epfree((*(MODCONT *)p).binv); free(p); }
62    
63     LUTAB modconttab = LU_SINIT(NULL,mcfree); /* modifier lookup table */
64    
65 greg 1.31 /*
66     * The STREAMOUT structure holds an open FILE pointer and a count of
67 greg 1.45 * the number of RGB triplets per record, or 0 if unknown.
68 greg 1.31 */
69     typedef struct {
70     FILE *ofp; /* output file pointer */
71 greg 1.45 int outpipe; /* output is to a pipe */
72 greg 1.31 int reclen; /* triplets/record */
73 greg 1.45 int xr, yr; /* output resolution for picture */
74 greg 1.31 } STREAMOUT;
75    
76     /* close output stream and free record */
77     static void
78     closestream(void *p)
79     {
80     STREAMOUT *sop = (STREAMOUT *)p;
81 greg 1.33 int status;
82 greg 1.45 if (sop->outpipe)
83 greg 1.33 status = pclose(sop->ofp);
84 greg 1.31 else
85 greg 1.33 status = fclose(sop->ofp);
86     if (status)
87     error(SYSTEM, "error closing output stream");
88 greg 1.31 free(p);
89     }
90 greg 1.1
91 greg 1.31 LUTAB ofiletab = LU_SINIT(free,closestream); /* output file table */
92 greg 1.1
93 greg 1.14 #define OF_MODIFIER 01
94     #define OF_BIN 02
95    
96 greg 1.32 STREAMOUT *getostream(const char *ospec, const char *mname, int bn, int noopen);
97 greg 1.14 int ofname(char *oname, const char *ospec, const char *mname, int bn);
98     void printheader(FILE *fout, const char *info);
99 greg 1.45 void printresolu(FILE *fout, int xr, int yr);
100 greg 1.1
101     /*
102     * The rcont structure is used to manage i/o with a particular
103     * rtrace child process. Input is passed unchanged from stdin,
104     * and output is processed in input order and accumulated according
105     * to the corresponding modifier and bin number.
106     */
107     struct rtproc {
108     struct rtproc *next; /* next in list of processes */
109     SUBPROC pd; /* rtrace pipe descriptors */
110 greg 1.2 unsigned long raynum; /* ray number for this tree */
111 greg 1.58 size_t bsiz; /* ray tree buffer length */
112 greg 1.2 char *buf; /* ray tree buffer */
113 greg 1.58 size_t nbr; /* number of bytes from rtrace */
114 greg 1.5 }; /* rtrace process buffer */
115 greg 1.1
116     /* rtrace command and defaults */
117 greg 1.18 char *rtargv[256+2*MAXMODLIST] = { "rtrace",
118 greg 1.51 "-dj", ".9", "-dr", "3",
119 greg 1.42 "-ab", "1", "-ad", "350", };
120 greg 1.40
121 greg 1.13 int rtargc = 9;
122 greg 1.1 /* overriding rtrace options */
123 greg 1.40 char *myrtopts[] = { "-h-", "-x", "1", "-y", "0",
124 greg 1.6 "-dt", "0", "-as", "0", "-aa", "0", NULL };
125 greg 1.1
126 greg 1.62 #define RTCOEFF "-o~~~TmWdp" /* compute coefficients only */
127     #define RTCONTRIB "-o~~~TmVdp" /* compute ray contributions */
128 greg 1.40
129 greg 1.1 struct rtproc rt0; /* head of rtrace process list */
130    
131     struct rtproc *rt_unproc = NULL; /* unprocessed ray trees */
132    
133 greg 1.44 #define PERSIST_NONE 0 /* no persist file */
134     #define PERSIST_SINGLE 1 /* user set -P persist */
135     #define PERSIST_PARALL 2 /* user set -PP persist */
136     #define PERSIST_OURS 3 /* -PP persist belongs to us */
137     int persist_state = PERSIST_NONE; /* persist file state */
138     char persistfn[] = "pfXXXXXX"; /* our persist file name, if set */
139 greg 1.1
140     int gargc; /* global argc */
141     char **gargv; /* global argv */
142     #define progname gargv[0]
143    
144     char *octree; /* global octree argument */
145    
146     int inpfmt = 'a'; /* input format */
147     int outfmt = 'a'; /* output format */
148    
149     int header = 1; /* output header? */
150 greg 1.35 int force_open = 0; /* truncate existing output? */
151 greg 1.46 int recover = 0; /* recover previous output? */
152 greg 1.49 int accumulate = 1; /* input rays per output record */
153 greg 1.1 int xres = 0; /* horiz. output resolution */
154     int yres = 0; /* vert. output resolution */
155    
156 greg 1.49 int account; /* current accumulation count */
157 greg 1.14 unsigned long raysleft; /* number of rays left to trace */
158 greg 1.1 long waitflush; /* how long until next flush */
159    
160     unsigned long lastray = 0; /* last ray number sent */
161     unsigned long lastdone = 0; /* last ray processed */
162    
163 greg 1.2 int using_stdout = 0; /* are we using stdout? */
164    
165 greg 1.1 const char *modname[MAXMODLIST]; /* ordered modifier name list */
166     int nmods = 0; /* number of modifiers */
167    
168 greg 1.29 #define queue_length() (lastray - lastdone)
169    
170 greg 1.31 MODCONT *growmodifier(MODCONT *mp, int nb);
171     MODCONT *addmodifier(char *modn, char *outf, char *binv, int bincnt);
172     void addmodfile(char *fname, char *outf, char *binv, int bincnt);
173 greg 1.1
174 greg 1.5 void init(int np);
175 greg 1.1 int done_rprocs(struct rtproc *rtp);
176 greg 1.45 void reload_output(void);
177 greg 1.14 void recover_output(FILE *fin);
178     void trace_contribs(FILE *fin);
179 greg 1.1 struct rtproc *wait_rproc(void);
180     struct rtproc *get_rproc(void);
181 greg 1.3 void queue_raytree(struct rtproc *rtp);
182     void process_queue(void);
183 greg 1.1
184 greg 1.14 void put_contrib(const DCOLOR cnt, FILE *fout);
185 greg 1.2 void add_contrib(const char *modn);
186 greg 1.49 void done_contrib(int navg);
187 greg 1.1
188 greg 1.67 #ifdef getc_unlocked /* avoid nasty overheads */
189     #undef getc
190     #define getc getc_unlocked
191     #undef putc
192     #define putc putc_unlocked
193     #undef ferror
194     #define ferror ferror_unlocked
195     static int
196     fread_unl(void *ptr, int size, int nitems, FILE *fp)
197     {
198     char *p = (char *)ptr;
199     int len = size*nitems;
200     while (len-- > 0) {
201     int c = getc_unlocked(fp);
202     if (c == EOF)
203     return((p - (char *)ptr)/size);
204     *p++ = c;
205     }
206     return(nitems);
207     }
208     #undef fread
209     #define fread fread_unl
210     static int
211     fwrite_unl(const void *ptr, int size, int nitems, FILE *fp)
212     {
213     const char *p = (const char *)ptr;
214     int len = size*nitems;
215     while (len-- > 0)
216     putc_unlocked(*p++, fp);
217     if (ferror_unlocked(fp))
218     return(0);
219     return(nitems);
220     }
221     #undef fwrite
222     #define fwrite fwrite_unl
223     #endif
224    
225 greg 1.29 /* return number of open rtrace processes */
226     static int
227     nrtprocs(void)
228     {
229     int nrtp = 0;
230     struct rtproc *rtp;
231    
232     for (rtp = &rt0; rtp != NULL; rtp = rtp->next)
233     nrtp += rtp->pd.running;
234     return(nrtp);
235     }
236    
237 greg 1.1 /* set input/output format */
238     static void
239     setformat(const char *fmt)
240     {
241     switch (fmt[0]) {
242     case 'f':
243     case 'd':
244 greg 1.10 SET_FILE_BINARY(stdin);
245     /* fall through */
246     case 'a':
247 greg 1.1 inpfmt = fmt[0];
248     break;
249     default:
250     goto fmterr;
251     }
252     switch (fmt[1]) {
253     case '\0':
254     outfmt = inpfmt;
255     return;
256     case 'a':
257     case 'f':
258     case 'd':
259     case 'c':
260     outfmt = fmt[1];
261     break;
262     default:
263     goto fmterr;
264     }
265     if (!fmt[2])
266     return;
267     fmterr:
268     sprintf(errmsg, "Illegal i/o format: -f%s", fmt);
269     error(USER, errmsg);
270     }
271    
272     /* gather rays from rtrace and output contributions */
273     int
274     main(int argc, char *argv[])
275     {
276 greg 1.40 int contrib = 0;
277 greg 1.1 int nprocs = 1;
278     char *curout = NULL;
279     char *binval = NULL;
280 greg 1.31 int bincnt = 0;
281 greg 1.5 char fmt[8];
282 greg 1.2 int i, j;
283 greg 1.25 /* need at least one argument */
284     if (argc < 2) {
285     fprintf(stderr,
286 greg 1.40 "Usage: %s [-n nprocs][-V][-r][-e expr][-f source][-o ospec][-b binv] {-m mod | -M file} [rtrace options] octree\n",
287 greg 1.25 argv[0]);
288     exit(1);
289     }
290 greg 1.2 /* global program name */
291 greg 1.1 gargv = argv;
292 greg 1.11 /* initialize calcomp routines */
293 greg 1.1 esupport |= E_VARIABLE|E_FUNCTION|E_INCHAN|E_RCONST|E_REDEFW;
294     esupport &= ~(E_OUTCHAN);
295 greg 1.11 varset("PI", ':', PI);
296 greg 1.1 /* get our options */
297 greg 1.2 for (i = 1; i < argc-1; i++) {
298     /* expand arguments */
299     while ((j = expandarg(&argc, &argv, i)) > 0)
300     ;
301     if (j < 0) {
302 greg 1.52 fprintf(stderr, "%s: cannot expand '%s'\n",
303 greg 1.2 argv[0], argv[i]);
304     exit(1);
305     }
306     if (argv[i][0] == '-')
307     switch (argv[i][1]) {
308     case 'n': /* number of processes */
309 greg 1.21 if (argv[i][2] || i >= argc-2) break;
310 greg 1.2 nprocs = atoi(argv[++i]);
311     if (nprocs <= 0)
312     error(USER, "illegal number of processes");
313     continue;
314 greg 1.40 case 'V': /* output contributions */
315     switch (argv[i][2]) {
316     case '\0':
317     contrib = !contrib;
318     continue;
319     case '+': case '1':
320     case 'T': case 't':
321     case 'Y': case 'y':
322     contrib = 1;
323     continue;
324     case '-': case '0':
325     case 'F': case 'f':
326     case 'N': case 'n':
327     contrib = 0;
328     continue;
329     }
330     break;
331 greg 1.49 case 'c': /* input rays per output */
332     if (argv[i][2] || i >= argc-2) break;
333     accumulate = atoi(argv[++i]);
334     continue;
335 greg 1.40 case 'r': /* recover output */
336     if (argv[i][2]) break;
337 greg 1.46 recover = 1;
338 greg 1.40 continue;
339 greg 1.2 case 'h': /* output header? */
340     switch (argv[i][2]) {
341     case '\0':
342     header = !header;
343     continue;
344 greg 1.3 case '+': case '1':
345     case 'T': case 't':
346     case 'Y': case 'y':
347 greg 1.2 header = 1;
348     continue;
349 greg 1.3 case '-': case '0':
350     case 'F': case 'f':
351     case 'N': case 'n':
352 greg 1.2 header = 0;
353     continue;
354     }
355     break;
356 greg 1.35 case 'f': /* file or force or format */
357 greg 1.2 if (!argv[i][2]) {
358 greg 1.9 char *fpath;
359 greg 1.21 if (i >= argc-2) break;
360 greg 1.9 fpath = getpath(argv[++i],
361     getrlibpath(), R_OK);
362     if (fpath == NULL) {
363     sprintf(errmsg,
364     "cannot find file '%s'",
365     argv[i]);
366     error(USER, errmsg);
367     }
368     fcompile(fpath);
369 greg 1.2 continue;
370     }
371 greg 1.35 if (argv[i][2] == 'o') {
372 greg 1.46 force_open = 1;
373 greg 1.35 continue;
374     }
375 greg 1.2 setformat(argv[i]+2);
376     continue;
377     case 'e': /* expression */
378 greg 1.21 if (argv[i][2] || i >= argc-2) break;
379 greg 1.2 scompile(argv[++i], NULL, 0);
380 greg 1.1 continue;
381 greg 1.2 case 'o': /* output file spec. */
382 greg 1.21 if (argv[i][2] || i >= argc-2) break;
383 greg 1.2 curout = argv[++i];
384 greg 1.1 continue;
385 greg 1.2 case 'x': /* horiz. output resolution */
386 greg 1.21 if (argv[i][2] || i >= argc-2) break;
387 greg 1.2 xres = atoi(argv[++i]);
388 greg 1.1 continue;
389 greg 1.2 case 'y': /* vert. output resolution */
390 greg 1.21 if (argv[i][2] || i >= argc-2) break;
391 greg 1.2 yres = atoi(argv[++i]);
392     continue;
393 greg 1.31 case 'b': /* bin expression/count */
394     if (i >= argc-2) break;
395     if (argv[i][2] == 'n') {
396 greg 1.54 bincnt = (int)(eval(argv[++i]) + .5);
397 greg 1.31 continue;
398     }
399     if (argv[i][2]) break;
400 greg 1.2 binval = argv[++i];
401     continue;
402     case 'm': /* modifier name */
403 greg 1.21 if (argv[i][2] || i >= argc-2) break;
404 greg 1.2 rtargv[rtargc++] = "-ti";
405     rtargv[rtargc++] = argv[++i];
406 greg 1.31 addmodifier(argv[i], curout, binval, bincnt);
407 greg 1.1 continue;
408 greg 1.17 case 'M': /* modifier file */
409 greg 1.21 if (argv[i][2] || i >= argc-2) break;
410 greg 1.19 rtargv[rtargc++] = "-tI";
411     rtargv[rtargc++] = argv[++i];
412 greg 1.31 addmodfile(argv[i], curout, binval, bincnt);
413 greg 1.17 continue;
414 greg 1.39 case 'P': /* persist file */
415 greg 1.44 if (i >= argc-2) break;
416     persist_state = (argv[i][2] == 'P') ?
417     PERSIST_PARALL : PERSIST_SINGLE;
418     rtargv[rtargc++] = argv[i];
419     rtargv[rtargc++] = argv[++i];
420     continue;
421 greg 1.1 }
422 greg 1.2 rtargv[rtargc++] = argv[i]; /* assume rtrace option */
423 greg 1.1 }
424 greg 1.49 if (accumulate <= 0) /* no output flushing for single record */
425 greg 1.45 xres = yres = 0;
426 greg 1.2 /* set global argument list */
427     gargc = argc; gargv = argv;
428 greg 1.1 /* add "mandatory" rtrace settings */
429 greg 1.2 for (j = 0; myrtopts[j] != NULL; j++)
430     rtargv[rtargc++] = myrtopts[j];
431 greg 1.40 rtargv[rtargc++] = contrib ? RTCONTRIB : RTCOEFF;
432 greg 1.2 /* just asking for defaults? */
433     if (!strcmp(argv[i], "-defaults")) {
434 greg 1.56 char nps[8], sxres[16], syres[16];
435 greg 1.1 char *rtpath;
436 greg 1.49 printf("-c %-5d\t\t\t# accumulated rays per record\n",
437     accumulate);
438 greg 1.40 printf("-V%c\t\t\t\t# output %s\n", contrib ? '+' : '-',
439     contrib ? "contributions" : "coefficients");
440 greg 1.1 fflush(stdout); /* report OUR options */
441 greg 1.56 rtargv[rtargc++] = "-n";
442     sprintf(nps, "%d", nprocs);
443     rtargv[rtargc++] = nps;
444 greg 1.1 rtargv[rtargc++] = header ? "-h+" : "-h-";
445     sprintf(fmt, "-f%c%c", inpfmt, outfmt);
446     rtargv[rtargc++] = fmt;
447     rtargv[rtargc++] = "-x";
448     sprintf(sxres, "%d", xres);
449     rtargv[rtargc++] = sxres;
450     rtargv[rtargc++] = "-y";
451     sprintf(syres, "%d", yres);
452     rtargv[rtargc++] = syres;
453     rtargv[rtargc++] = "-defaults";
454     rtargv[rtargc] = NULL;
455     rtpath = getpath(rtargv[0], getenv("PATH"), X_OK);
456     if (rtpath == NULL) {
457     eputs(rtargv[0]);
458     eputs(": command not found\n");
459     exit(1);
460     }
461     execv(rtpath, rtargv);
462     perror(rtpath); /* execv() should not return */
463     exit(1);
464 greg 1.5 }
465     if (nprocs > 1) { /* add persist file if parallel */
466 greg 1.44 if (persist_state == PERSIST_SINGLE)
467     error(USER, "use -PP option for multiple processes");
468     if (persist_state == PERSIST_NONE) {
469     rtargv[rtargc++] = "-PP";
470     rtargv[rtargc++] = mktemp(persistfn);
471     persist_state = PERSIST_OURS;
472     }
473 greg 1.2 }
474 greg 1.1 /* add format string */
475     sprintf(fmt, "-f%cf", inpfmt);
476     rtargv[rtargc++] = fmt;
477     /* octree argument is last */
478 greg 1.2 if (i <= 0 || i != argc-1 || argv[i][0] == '-')
479     error(USER, "missing octree argument");
480     rtargv[rtargc++] = octree = argv[i];
481 greg 1.1 rtargv[rtargc] = NULL;
482 greg 1.46 /* start rtrace & recover if requested */
483 greg 1.1 init(nprocs);
484 greg 1.46 /* compute contributions */
485     trace_contribs(stdin);
486     /* clean up */
487 greg 1.1 quit(0);
488     }
489    
490 schorsch 1.26 #ifndef SIGALRM
491     #define SIGALRM SIGTERM
492     #endif
493 greg 1.1 /* kill persistent rtrace process */
494     static void
495     killpersist(void)
496     {
497     FILE *fp = fopen(persistfn, "r");
498 schorsch 1.26 RT_PID pid;
499 greg 1.1
500     if (fp == NULL)
501     return;
502     if (fscanf(fp, "%*s %d", &pid) != 1 || kill(pid, SIGALRM) < 0)
503     unlink(persistfn);
504     fclose(fp);
505     }
506    
507     /* close rtrace processes and clean up */
508     int
509     done_rprocs(struct rtproc *rtp)
510     {
511     int st0, st1 = 0;
512    
513     if (rtp->next != NULL) { /* close last opened first! */
514     st1 = done_rprocs(rtp->next);
515     free((void *)rtp->next);
516     rtp->next = NULL;
517     }
518     st0 = close_process(&rtp->pd);
519     if (st0 < 0)
520     error(WARNING, "unknown return status from rtrace process");
521     else if (st0 > 0)
522     return(st0);
523     return(st1);
524     }
525    
526     /* exit with status */
527     void
528     quit(int status)
529     {
530     int rtstat;
531    
532 greg 1.45 if (persist_state == PERSIST_OURS) /* terminate waiting rtrace */
533 greg 1.1 killpersist();
534     /* clean up rtrace process(es) */
535     rtstat = done_rprocs(&rt0);
536     if (status == 0)
537     status = rtstat;
538     exit(status); /* flushes all output streams */
539     }
540    
541 greg 1.5 /* start rtrace processes and initialize */
542 greg 1.1 void
543     init(int np)
544     {
545     struct rtproc *rtp;
546     int i;
547     int maxbytes;
548 greg 1.2 /* make sure we have something to do */
549     if (!nmods)
550     error(USER, "No modifiers specified");
551 greg 1.1 /* assign ray variables */
552     scompile("Dx=$1;Dy=$2;Dz=$3;", NULL, 0);
553     scompile("Px=$4;Py=$5;Pz=$6;", NULL, 0);
554     /* set up signal handling */
555 greg 1.3 signal(SIGINT, quit);
556     #ifdef SIGHUP
557     signal(SIGHUP, quit);
558     #endif
559     #ifdef SIGTERM
560     signal(SIGTERM, quit);
561     #endif
562     #ifdef SIGPIPE
563 greg 1.1 signal(SIGPIPE, quit);
564     #endif
565     rtp = &rt0; /* start rtrace process(es) */
566     for (i = 0; i++ < np; ) {
567     errno = 0;
568     maxbytes = open_process(&rtp->pd, rtargv);
569     if (maxbytes == 0) {
570     eputs(rtargv[0]);
571     eputs(": command not found\n");
572     exit(1);
573     }
574     if (maxbytes < 0)
575     error(SYSTEM, "cannot start rtrace process");
576     if (maxbytes > treebufsiz)
577     treebufsiz = maxbytes;
578 greg 1.2 rtp->raynum = 0;
579 greg 1.1 rtp->bsiz = 0;
580     rtp->buf = NULL;
581 greg 1.2 rtp->nbr = 0;
582 greg 1.1 if (i == np) /* last process? */
583     break;
584     if (i == 1)
585     sleep(2); /* wait for persist file */
586     rtp->next = (struct rtproc *)malloc(sizeof(struct rtproc));
587     if (rtp->next == NULL)
588     error(SYSTEM, "out of memory in init");
589     rtp = rtp->next;
590     }
591     rtp->next = NULL; /* terminate list */
592     if (yres > 0) {
593     if (xres > 0)
594 greg 1.14 raysleft = (unsigned long)xres*yres;
595 greg 1.1 else
596     raysleft = yres;
597     } else
598     raysleft = 0;
599 greg 1.49 if ((account = accumulate) > 0)
600     raysleft *= accumulate;
601 greg 1.57 waitflush = (yres > 0) & (xres > 1) ? 0 : xres;
602 greg 1.46 if (!recover)
603     return;
604     /* recover previous values */
605 greg 1.49 if (accumulate <= 0)
606 greg 1.46 reload_output();
607     else
608     recover_output(stdin);
609 greg 1.1 }
610    
611 greg 1.31 /* grow modifier to accommodate more bins */
612     MODCONT *
613     growmodifier(MODCONT *mp, int nb)
614     {
615     if (nb <= mp->nbins)
616     return mp;
617     mp = (MODCONT *)realloc(mp, sizeof(MODCONT) + sizeof(DCOLOR)*(nb-1));
618     if (mp == NULL)
619     error(SYSTEM, "out of memory in growmodifier");
620     memset(mp->cbin+mp->nbins, 0, sizeof(DCOLOR)*(nb-mp->nbins));
621     mp->nbins = nb;
622     return mp;
623     }
624    
625 greg 1.1 /* add modifier to our list to track */
626     MODCONT *
627 greg 1.31 addmodifier(char *modn, char *outf, char *binv, int bincnt)
628 greg 1.1 {
629     LUENT *lep = lu_find(&modconttab, modn);
630     MODCONT *mp;
631 greg 1.32 int i;
632 greg 1.1
633     if (lep->data != NULL) {
634     sprintf(errmsg, "duplicate modifier '%s'", modn);
635     error(USER, errmsg);
636     }
637     if (nmods >= MAXMODLIST)
638 greg 1.54 error(INTERNAL, "too many modifiers");
639 greg 1.1 modname[nmods++] = modn; /* XXX assumes static string */
640     lep->key = modn; /* XXX assumes static string */
641     mp = (MODCONT *)malloc(sizeof(MODCONT));
642     if (mp == NULL)
643     error(SYSTEM, "out of memory in addmodifier");
644     mp->outspec = outf; /* XXX assumes static string */
645     mp->modname = modn; /* XXX assumes static string */
646 greg 1.54 if (binv == NULL)
647     binv = "0"; /* use single bin if unspecified */
648     mp->binv = eparse(binv);
649     if (mp->binv->type == NUM) { /* check value if constant */
650     bincnt = (int)(evalue(mp->binv) + 1.5);
651     if (bincnt != 1) {
652     sprintf(errmsg, "illegal non-zero constant for bin (%s)",
653     binv);
654     error(USER, errmsg);
655     }
656     }
657     mp->nbins = 1; /* initialize results holder */
658 greg 1.1 setcolor(mp->cbin[0], 0., 0., 0.);
659 greg 1.54 if (bincnt > 1)
660 greg 1.31 mp = growmodifier(mp, bincnt);
661 greg 1.32 lep->data = (char *)mp;
662     /* allocate output streams */
663 greg 1.45 for (i = bincnt; i-- > 0; )
664 greg 1.32 getostream(mp->outspec, mp->modname, i, 1);
665 greg 1.1 return mp;
666     }
667    
668 greg 1.17 /* add modifiers from a file list */
669     void
670 greg 1.31 addmodfile(char *fname, char *outf, char *binv, int bincnt)
671 greg 1.17 {
672     char *mname[MAXMODLIST];
673     int i;
674 greg 1.20 /* find the file & store strings */
675     if (wordfile(mname, getpath(fname, getrlibpath(), R_OK)) < 0) {
676     sprintf(errmsg, "cannot find modifier file '%s'", fname);
677     error(SYSTEM, errmsg);
678     }
679 greg 1.17 for (i = 0; mname[i]; i++) /* add each one */
680 greg 1.31 addmodifier(mname[i], outf, binv, bincnt);
681 greg 1.17 }
682    
683 greg 1.1 /* put string to stderr */
684     void
685     eputs(char *s)
686     {
687     static int midline = 0;
688    
689     if (!*s) return;
690     if (!midline) {
691     fputs(progname, stderr);
692     fputs(": ", stderr);
693     }
694     fputs(s, stderr);
695     midline = s[strlen(s)-1] != '\n';
696     }
697    
698 greg 1.14 /* construct output file name and return flags whether modifier/bin present */
699     int
700     ofname(char *oname, const char *ospec, const char *mname, int bn)
701     {
702     const char *mnp = NULL;
703     const char *bnp = NULL;
704     const char *cp;
705    
706     if (ospec == NULL)
707     return -1;
708     for (cp = ospec; *cp; cp++) /* check format position(s) */
709     if (*cp == '%') {
710     do
711     ++cp;
712     while (isdigit(*cp));
713     switch (*cp) {
714     case '%':
715     break;
716     case 's':
717     if (mnp != NULL)
718     return -1;
719     mnp = cp;
720     break;
721     case 'd':
722 greg 1.55 case 'i':
723     case 'o':
724     case 'x':
725     case 'X':
726 greg 1.14 if (bnp != NULL)
727     return -1;
728     bnp = cp;
729     break;
730     default:
731     return -1;
732     }
733     }
734     if (mnp != NULL) { /* create file name */
735     if (bnp != NULL) {
736     if (bnp > mnp)
737     sprintf(oname, ospec, mname, bn);
738     else
739     sprintf(oname, ospec, bn, mname);
740     return OF_MODIFIER|OF_BIN;
741     }
742     sprintf(oname, ospec, mname);
743     return OF_MODIFIER;
744     }
745     if (bnp != NULL) {
746     sprintf(oname, ospec, bn);
747     return OF_BIN;
748     }
749     strcpy(oname, ospec);
750     return 0;
751     }
752    
753 greg 1.1 /* write header to the given output stream */
754     void
755 greg 1.14 printheader(FILE *fout, const char *info)
756 greg 1.1 {
757     extern char VersionID[];
758 greg 1.63 /* copy octree header */
759     if (octree[0] == '!') {
760     newheader("RADIANCE", fout);
761     fputs(octree+1, fout);
762     if (octree[strlen(octree)-1] != '\n')
763     fputc('\n', fout);
764     } else {
765     FILE *fin = fopen(octree, "r");
766     if (fin == NULL)
767     quit(1);
768     checkheader(fin, "ignore", fout);
769     fclose(fin);
770     }
771 greg 1.1 printargs(gargc-1, gargv, fout); /* add our command */
772     fprintf(fout, "SOFTWARE= %s\n", VersionID);
773     fputnow(fout);
774 greg 1.14 if (info != NULL) /* add extra info if given */
775     fputs(info, fout);
776 greg 1.1 switch (outfmt) { /* add output format */
777     case 'a':
778     fputformat("ascii", fout);
779     break;
780     case 'f':
781     fputformat("float", fout);
782     break;
783     case 'd':
784     fputformat("double", fout);
785     break;
786     case 'c':
787     fputformat(COLRFMT, fout);
788     break;
789     }
790     fputc('\n', fout);
791 greg 1.30 }
792    
793     /* write resolution string to given output stream */
794     void
795 greg 1.45 printresolu(FILE *fout, int xr, int yr)
796 greg 1.30 {
797 greg 1.45 if ((xr > 0) & (yr > 0)) /* resolution string */
798     fprtresolu(xr, yr, fout);
799 greg 1.1 }
800    
801 greg 1.32 /* Get output stream pointer (open and write header if new and noopen==0) */
802 greg 1.31 STREAMOUT *
803 greg 1.32 getostream(const char *ospec, const char *mname, int bn, int noopen)
804 greg 1.31 {
805 greg 1.45 static const DCOLOR nocontrib = BLKCOLOR;
806 greg 1.31 static STREAMOUT stdos;
807     int ofl;
808     char oname[1024];
809     LUENT *lep;
810     STREAMOUT *sop;
811 greg 1.1
812     if (ospec == NULL) { /* use stdout? */
813 greg 1.32 if (!noopen && !using_stdout) {
814 greg 1.10 if (outfmt != 'a')
815     SET_FILE_BINARY(stdout);
816     if (header)
817 greg 1.14 printheader(stdout, NULL);
818 greg 1.45 printresolu(stdout, xres, yres);
819 greg 1.57 if (waitflush > 0)
820     fflush(stdout);
821 greg 1.45 stdos.xr = xres; stdos.yr = yres;
822 greg 1.31 using_stdout = 1;
823 greg 1.10 }
824 greg 1.31 stdos.ofp = stdout;
825 greg 1.32 stdos.reclen += noopen;
826 greg 1.31 return &stdos;
827 greg 1.1 }
828 greg 1.14 ofl = ofname(oname, ospec, mname, bn); /* get output name */
829     if (ofl < 0) {
830     sprintf(errmsg, "bad output format '%s'", ospec);
831     error(USER, errmsg);
832     }
833     lep = lu_find(&ofiletab, oname); /* look it up */
834 greg 1.1 if (lep->key == NULL) /* new entry */
835 greg 1.14 lep->key = strcpy((char *)malloc(strlen(oname)+1), oname);
836 greg 1.31 sop = (STREAMOUT *)lep->data;
837     if (sop == NULL) { /* allocate stream */
838     sop = (STREAMOUT *)malloc(sizeof(STREAMOUT));
839     if (sop == NULL)
840     error(SYSTEM, "out of memory in getostream");
841 greg 1.45 sop->outpipe = oname[0] == '!';
842     sop->reclen = 0;
843 greg 1.32 sop->ofp = NULL; /* open iff noopen==0 */
844 greg 1.45 sop->xr = xres; sop->yr = yres;
845 greg 1.31 lep->data = (char *)sop;
846 greg 1.46 if (!sop->outpipe & !force_open & !recover &&
847     access(oname, F_OK) == 0) {
848 greg 1.45 errno = EEXIST; /* file exists */
849     goto openerr;
850     }
851 greg 1.31 }
852 greg 1.32 if (!noopen && sop->ofp == NULL) { /* open output stream */
853 greg 1.34 long i;
854 greg 1.14 if (oname[0] == '!') /* output to command */
855 greg 1.31 sop->ofp = popen(oname+1, "w");
856 greg 1.45 else /* else open file */
857 greg 1.31 sop->ofp = fopen(oname, "w");
858 greg 1.45 if (sop->ofp == NULL)
859     goto openerr;
860 greg 1.10 if (outfmt != 'a')
861 greg 1.31 SET_FILE_BINARY(sop->ofp);
862 greg 1.67 #ifdef getc_unlocked /* avoid lock/unlock overhead */
863     flockfile(sop->ofp);
864     #endif
865 greg 1.14 if (header) {
866     char info[512];
867     char *cp = info;
868 greg 1.32 if (ofl & OF_MODIFIER || sop->reclen == 1) {
869 greg 1.30 sprintf(cp, "MODIFIER=%s\n", mname);
870     while (*cp) ++cp;
871     }
872 greg 1.14 if (ofl & OF_BIN) {
873     sprintf(cp, "BIN=%d\n", bn);
874     while (*cp) ++cp;
875     }
876     *cp = '\0';
877 greg 1.31 printheader(sop->ofp, info);
878 greg 1.14 }
879 greg 1.49 if (accumulate > 0) { /* global resolution */
880 greg 1.47 sop->xr = xres; sop->yr = yres;
881     }
882 greg 1.45 printresolu(sop->ofp, sop->xr, sop->yr);
883 greg 1.2 /* play catch-up */
884 greg 1.49 for (i = accumulate > 0 ? lastdone/accumulate : 0; i--; ) {
885 greg 1.45 int j = sop->reclen;
886     if (j <= 0) j = 1;
887     while (j--)
888     put_contrib(nocontrib, sop->ofp);
889 greg 1.2 if (outfmt == 'a')
890 greg 1.31 putc('\n', sop->ofp);
891 greg 1.2 }
892 greg 1.57 if (waitflush > 0)
893 greg 1.31 fflush(sop->ofp);
894 greg 1.1 }
895 greg 1.45 sop->reclen += noopen; /* add to length if noopen */
896     return sop; /* return output stream */
897     openerr:
898     sprintf(errmsg, "cannot open '%s' for writing", oname);
899     error(SYSTEM, errmsg);
900     return NULL; /* pro forma return */
901 greg 1.1 }
902    
903     /* read input ray into buffer */
904     int
905     getinp(char *buf, FILE *fp)
906     {
907 greg 1.45 double dv[3], *dvp;
908     float *fvp;
909 greg 1.4 char *cp;
910     int i;
911    
912 greg 1.1 switch (inpfmt) {
913     case 'a':
914 greg 1.4 cp = buf; /* make sure we get 6 floats */
915     for (i = 0; i < 6; i++) {
916     if (fgetword(cp, buf+127-cp, fp) == NULL)
917 greg 1.36 return -1;
918     if (i >= 3)
919     dv[i-3] = atof(cp);
920 greg 1.4 if ((cp = fskip(cp)) == NULL || *cp)
921 greg 1.36 return -1;
922 greg 1.4 *cp++ = ' ';
923     }
924     getc(fp); /* get/put eol */
925     *cp-- = '\0'; *cp = '\n';
926 greg 1.36 if (DOT(dv,dv) <= FTINY*FTINY)
927     return 0; /* dummy ray */
928 greg 1.1 return strlen(buf);
929     case 'f':
930 greg 1.67 if (fread(buf, sizeof(float), 6, fp) != 6)
931 greg 1.36 return -1;
932 greg 1.45 fvp = (float *)buf + 3;
933     if (DOT(fvp,fvp) <= FTINY*FTINY)
934 greg 1.36 return 0; /* dummy ray */
935 greg 1.1 return sizeof(float)*6;
936     case 'd':
937 greg 1.67 if (fread(buf, sizeof(double), 6, fp) != 6)
938 greg 1.36 return -1;
939 greg 1.45 dvp = (double *)buf + 3;
940     if (DOT(dvp,dvp) <= FTINY*FTINY)
941 greg 1.36 return 0; /* dummy ray */
942 greg 1.1 return sizeof(double)*6;
943     }
944     error(INTERNAL, "botched input format");
945 greg 1.36 return -1; /* pro forma return */
946 greg 1.1 }
947    
948 greg 1.2 static float rparams[9]; /* traced ray parameters */
949 greg 1.1
950     /* return channel (ray) value */
951     double
952     chanvalue(int n)
953     {
954     if (--n < 0 || n >= 6)
955     error(USER, "illegal channel number ($N)");
956 greg 1.2 return rparams[n+3];
957 greg 1.1 }
958    
959 greg 1.2 /* add current ray contribution to the appropriate modifier bin */
960 greg 1.1 void
961 greg 1.2 add_contrib(const char *modn)
962 greg 1.1 {
963     LUENT *le = lu_find(&modconttab, modn);
964     MODCONT *mp = (MODCONT *)le->data;
965     int bn;
966    
967     if (mp == NULL) {
968     sprintf(errmsg, "unexpected modifier '%s' from rtrace", modn);
969     error(USER, errmsg);
970     }
971 greg 1.2 eclock++; /* get bin number */
972 greg 1.1 bn = (int)(evalue(mp->binv) + .5);
973     if (bn <= 0)
974     bn = 0;
975 greg 1.31 else if (bn >= mp->nbins) /* new bin */
976     le->data = (char *)(mp = growmodifier(mp, bn+1));
977 greg 1.2 addcolor(mp->cbin[bn], rparams);
978 greg 1.1 }
979    
980     /* output newline to ASCII file and/or flush as requested */
981     static int
982     puteol(const LUENT *e, void *p)
983     {
984 greg 1.31 STREAMOUT *sop = (STREAMOUT *)e->data;
985 greg 1.1
986     if (outfmt == 'a')
987 greg 1.31 putc('\n', sop->ofp);
988 greg 1.1 if (!waitflush)
989 greg 1.31 fflush(sop->ofp);
990     if (ferror(sop->ofp)) {
991 greg 1.1 sprintf(errmsg, "write error on file '%s'", e->key);
992     error(SYSTEM, errmsg);
993     }
994     return 0;
995     }
996    
997 greg 1.2 /* put out ray contribution to file */
998     void
999 greg 1.14 put_contrib(const DCOLOR cnt, FILE *fout)
1000 greg 1.2 {
1001     float fv[3];
1002     COLR cv;
1003    
1004     switch (outfmt) {
1005     case 'a':
1006     fprintf(fout, "%.6e\t%.6e\t%.6e\t", cnt[0], cnt[1], cnt[2]);
1007     break;
1008     case 'f':
1009 greg 1.46 copycolor(fv, cnt);
1010 greg 1.2 fwrite(fv, sizeof(float), 3, fout);
1011     break;
1012     case 'd':
1013     fwrite(cnt, sizeof(double), 3, fout);
1014     break;
1015     case 'c':
1016     setcolr(cv, cnt[0], cnt[1], cnt[2]);
1017     fwrite(cv, sizeof(cv), 1, fout);
1018     break;
1019     default:
1020     error(INTERNAL, "botched output format");
1021     }
1022     }
1023    
1024 greg 1.45 /* output ray tallies and clear for next accumulation */
1025 greg 1.1 void
1026 greg 1.49 done_contrib(int navg)
1027 greg 1.1 {
1028 greg 1.49 double sf = 1.;
1029 greg 1.31 int i, j;
1030     MODCONT *mp;
1031     STREAMOUT *sop;
1032 greg 1.49 /* set average scaling */
1033     if (navg > 1)
1034     sf = 1. / (double)navg;
1035 greg 1.1 /* output modifiers in order */
1036     for (i = 0; i < nmods; i++) {
1037     mp = (MODCONT *)lu_find(&modconttab,modname[i])->data;
1038 greg 1.49 if (navg > 1) /* average scaling */
1039     for (j = mp->nbins; j--; )
1040     scalecolor(mp->cbin[j], sf);
1041 greg 1.31 sop = getostream(mp->outspec, mp->modname, 0,0);
1042     put_contrib(mp->cbin[0], sop->ofp);
1043 greg 1.12 if (mp->nbins > 3 && /* minor optimization */
1044 greg 1.31 sop == getostream(mp->outspec, mp->modname, 1,0))
1045 greg 1.12 for (j = 1; j < mp->nbins; j++)
1046 greg 1.31 put_contrib(mp->cbin[j], sop->ofp);
1047 greg 1.12 else
1048     for (j = 1; j < mp->nbins; j++)
1049 greg 1.14 put_contrib(mp->cbin[j],
1050 greg 1.31 getostream(mp->outspec,mp->modname,j,0)->ofp);
1051 greg 1.45 /* clear for next time */
1052 greg 1.2 memset(mp->cbin, 0, sizeof(DCOLOR)*mp->nbins);
1053 greg 1.1 }
1054     --waitflush; /* terminate records */
1055     lu_doall(&ofiletab, puteol, NULL);
1056 greg 1.2 if (using_stdout & (outfmt == 'a'))
1057     putc('\n', stdout);
1058     if (!waitflush) {
1059 greg 1.57 waitflush = (yres > 0) & (xres > 1) ? 0 : xres;
1060 greg 1.2 if (using_stdout)
1061     fflush(stdout);
1062     }
1063 greg 1.1 }
1064    
1065 greg 1.3 /* queue completed ray tree produced by rtrace process */
1066     void
1067     queue_raytree(struct rtproc *rtp)
1068     {
1069     struct rtproc *rtu, *rtl = NULL;
1070     /* insert following ray order */
1071     for (rtu = rt_unproc; rtu != NULL; rtu = (rtl=rtu)->next)
1072     if (rtp->raynum < rtu->raynum)
1073     break;
1074     rtu = (struct rtproc *)malloc(sizeof(struct rtproc));
1075     if (rtu == NULL)
1076     error(SYSTEM, "out of memory in queue_raytree");
1077     *rtu = *rtp;
1078     if (rtl == NULL) {
1079     rtu->next = rt_unproc;
1080     rt_unproc = rtu;
1081     } else {
1082     rtu->next = rtl->next;
1083     rtl->next = rtu;
1084     }
1085     rtp->raynum = 0; /* clear path for next ray tree */
1086     rtp->bsiz = 0;
1087     rtp->buf = NULL;
1088     rtp->nbr = 0;
1089     }
1090    
1091     /* process completed ray trees from our queue */
1092 greg 1.1 void
1093 greg 1.3 process_queue(void)
1094 greg 1.1 {
1095 greg 1.3 char modname[128];
1096     /* ray-ordered queue */
1097     while (rt_unproc != NULL && rt_unproc->raynum == lastdone+1) {
1098     struct rtproc *rtp = rt_unproc;
1099 greg 1.2 int n = rtp->nbr;
1100 greg 1.1 const char *cp = rtp->buf;
1101     while (n > 0) { /* process rays */
1102 greg 1.2 register char *mnp = modname;
1103 greg 1.1 /* skip leading tabs */
1104     while (n > 0 && *cp == '\t') {
1105     cp++; n--;
1106     }
1107 greg 1.2 if (!n || !(isalpha(*cp) | (*cp == '_')))
1108     error(USER, "bad modifier name from rtrace");
1109     /* get modifier name */
1110 greg 1.40 while (n > 1 && *cp != '\t') {
1111     if (mnp - modname >= sizeof(modname)-2)
1112     error(INTERNAL, "modifier name too long");
1113 greg 1.1 *mnp++ = *cp++; n--;
1114     }
1115     *mnp = '\0';
1116 greg 1.2 cp++; n--; /* eat following tab */
1117 greg 1.1 if (n < (int)(sizeof(float)*9))
1118     error(USER, "incomplete ray value from rtrace");
1119     /* add ray contribution */
1120 greg 1.2 memcpy(rparams, cp, sizeof(float)*9);
1121 greg 1.1 cp += sizeof(float)*9; n -= sizeof(float)*9;
1122 greg 1.2 add_contrib(modname);
1123 greg 1.1 }
1124 greg 1.49 /* time to produce record? */
1125     if (account > 0 && !--account)
1126     done_contrib(account = accumulate);
1127 greg 1.1 lastdone = rtp->raynum;
1128 greg 1.36 if (rtp->buf != NULL) /* free up buffer space */
1129     free(rtp->buf);
1130 greg 1.3 rt_unproc = rtp->next;
1131     free(rtp); /* done with this ray tree */
1132 greg 1.1 }
1133     }
1134    
1135     /* wait for rtrace process to finish with ray tree */
1136     struct rtproc *
1137     wait_rproc(void)
1138     {
1139     struct rtproc *rtfree = NULL;
1140     fd_set readset, errset;
1141 greg 1.58 ssize_t nr;
1142 greg 1.1 struct rtproc *rt;
1143     int n;
1144    
1145     do {
1146     nr = 0; /* prepare select call */
1147     FD_ZERO(&readset); FD_ZERO(&errset); n = 0;
1148     for (rt = &rt0; rt != NULL; rt = rt->next) {
1149     if (rt->raynum) {
1150     FD_SET(rt->pd.r, &readset);
1151     ++nr;
1152     }
1153     FD_SET(rt->pd.r, &errset);
1154     if (rt->pd.r >= n)
1155     n = rt->pd.r + 1;
1156     }
1157     if (!nr) /* no rays pending */
1158     break;
1159     if (nr > 1) { /* call select for multiple proc's */
1160     errno = 0;
1161     if (select(n, &readset, NULL, &errset, NULL) < 0)
1162     error(SYSTEM, "select call error in wait_rproc()");
1163     } else
1164     FD_ZERO(&errset);
1165     nr = 0;
1166     for (rt = &rt0; rt != NULL; rt = rt->next) {
1167     if (!FD_ISSET(rt->pd.r, &readset) &&
1168     !FD_ISSET(rt->pd.r, &errset))
1169     continue;
1170     if (rt->buf == NULL) {
1171     rt->bsiz = treebufsiz;
1172 greg 1.62 rt->buf = (char *)malloc(rt->bsiz);
1173 greg 1.2 } else if (rt->nbr + BUFSIZ > rt->bsiz) {
1174 greg 1.1 if (rt->bsiz + BUFSIZ <= treebufsiz)
1175     rt->bsiz = treebufsiz;
1176 greg 1.64 else if ((treebufsiz = rt->bsiz += BUFSIZ) < 0)
1177     error(INTERNAL,
1178     "ray buffer does not fit memory");
1179 greg 1.1 rt->buf = (char *)realloc(rt->buf, rt->bsiz);
1180     }
1181     if (rt->buf == NULL)
1182     error(SYSTEM, "out of memory in wait_rproc");
1183 greg 1.64 nr = rt->bsiz - rt->nbr;
1184     if (nr & ~0x7fffffff) /* avoid 32-bit OS issues */
1185     nr = 0x7fffffff;
1186     nr = read(rt->pd.r, rt->buf+rt->nbr, nr);
1187     if (nr < 0)
1188     error(SYSTEM, "read error from rtrace");
1189     if (!nr)
1190 greg 1.1 error(USER, "rtrace process died");
1191 greg 1.2 rt->nbr += nr; /* advance & check */
1192 greg 1.62 if (rt->nbr >= 6 && !memcmp(rt->buf+rt->nbr-6,
1193     "~\t~\t~\t", 6)) {
1194     rt->nbr -= 6; /* elide terminator */
1195 greg 1.3 queue_raytree(rt);
1196 greg 1.1 rtfree = rt; /* ready for next ray */
1197     }
1198     }
1199     } while ((rtfree == NULL) & (nr > 0)); /* repeat until ready or out */
1200     return rtfree;
1201     }
1202    
1203     /* return next available rtrace process */
1204     struct rtproc *
1205     get_rproc(void)
1206     {
1207     struct rtproc *rtp;
1208     /* check for idle rtrace */
1209     for (rtp = &rt0; rtp != NULL; rtp = rtp->next)
1210     if (!rtp->raynum)
1211     return rtp;
1212     return wait_rproc(); /* need to wait for one */
1213     }
1214    
1215     /* trace ray contributions (main loop) */
1216     void
1217 greg 1.5 trace_contribs(FILE *fin)
1218 greg 1.1 {
1219 greg 1.49 static int ignore_warning_given = 0;
1220 greg 1.1 char inpbuf[128];
1221     int iblen;
1222     struct rtproc *rtp;
1223     /* loop over input */
1224 greg 1.36 while ((iblen = getinp(inpbuf, fin)) >= 0) {
1225 greg 1.49 if (!iblen && accumulate != 1) {
1226     if (!ignore_warning_given++)
1227     error(WARNING,
1228     "dummy ray(s) ignored during accumulation\n");
1229     continue;
1230     }
1231 greg 1.53 if (!iblen || /* need flush/reset? */
1232 greg 1.36 queue_length() > 10*nrtprocs() ||
1233     lastray+1 < lastray) {
1234 greg 1.1 while (wait_rproc() != NULL)
1235 greg 1.3 process_queue();
1236 greg 1.62 lastdone = lastray = 0;
1237 greg 1.1 }
1238     rtp = get_rproc(); /* get avail. rtrace process */
1239 greg 1.36 rtp->raynum = ++lastray; /* assign ray */
1240     if (iblen) { /* trace ray if valid */
1241     writebuf(rtp->pd.w, inpbuf, iblen);
1242     } else { /* else bypass dummy ray */
1243 greg 1.53 queue_raytree(rtp); /* queue empty ray/record */
1244     if ((yres <= 0) | (xres <= 0))
1245     waitflush = 1; /* flush right after */
1246 greg 1.36 }
1247 greg 1.38 process_queue(); /* catch up with results */
1248 greg 1.14 if (raysleft && !--raysleft)
1249 greg 1.38 break; /* preemptive EOI */
1250 greg 1.1 }
1251     while (wait_rproc() != NULL) /* process outstanding rays */
1252 greg 1.3 process_queue();
1253 greg 1.49 if (accumulate <= 0)
1254     done_contrib(0); /* output tallies */
1255     else if (account < accumulate) {
1256     error(WARNING, "partial accumulation in final record");
1257     done_contrib(accumulate - account);
1258     }
1259 greg 1.14 if (raysleft)
1260 greg 1.2 error(USER, "unexpected EOF on input");
1261 greg 1.14 lu_done(&ofiletab); /* close output files */
1262     }
1263    
1264 greg 1.46 /* get ray contribution from previous file */
1265     static int
1266     get_contrib(DCOLOR cnt, FILE *finp)
1267     {
1268     COLOR fv;
1269     COLR cv;
1270    
1271     switch (outfmt) {
1272     case 'a':
1273     return(fscanf(finp,"%lf %lf %lf",&cnt[0],&cnt[1],&cnt[2]) == 3);
1274     case 'f':
1275     if (fread(fv, sizeof(fv[0]), 3, finp) != 3)
1276     return(0);
1277     copycolor(cnt, fv);
1278     return(1);
1279     case 'd':
1280     return(fread(cnt, sizeof(cnt[0]), 3, finp) == 3);
1281     case 'c':
1282     if (fread(cv, sizeof(cv), 1, finp) != 1)
1283     return(0);
1284     colr_color(fv, cv);
1285     copycolor(cnt, fv);
1286     return(1);
1287     default:
1288     error(INTERNAL, "botched output format");
1289     }
1290     return(0); /* pro forma return */
1291     }
1292    
1293     /* close output file opened for input */
1294 greg 1.14 static int
1295 greg 1.46 myclose(const LUENT *e, void *p)
1296 greg 1.14 {
1297 greg 1.31 STREAMOUT *sop = (STREAMOUT *)e->data;
1298    
1299 greg 1.46 if (sop->ofp == NULL)
1300 greg 1.50 return(0);
1301 greg 1.46 fclose(sop->ofp);
1302     sop->ofp = NULL;
1303 greg 1.50 return(0);
1304 greg 1.14 }
1305    
1306 greg 1.45 /* load previously accumulated values */
1307     void
1308     reload_output(void)
1309     {
1310     int i, j;
1311     MODCONT *mp;
1312     int ofl;
1313     char oname[1024];
1314     char *fmode = "rb";
1315     char *outvfmt;
1316     LUENT *ment, *oent;
1317 greg 1.46 int xr, yr;
1318 greg 1.45 STREAMOUT sout;
1319 greg 1.46 DCOLOR rgbv;
1320 greg 1.45
1321     switch (outfmt) {
1322     case 'a':
1323     outvfmt = "ascii";
1324     fmode = "r";
1325     break;
1326     case 'f':
1327     outvfmt = "float";
1328     break;
1329     case 'd':
1330     outvfmt = "double";
1331     break;
1332     case 'c':
1333     outvfmt = COLRFMT;
1334     break;
1335     default:
1336     error(INTERNAL, "botched output format");
1337     return;
1338     }
1339 greg 1.46 /* reload modifier values */
1340 greg 1.45 for (i = 0; i < nmods; i++) {
1341     ment = lu_find(&modconttab,modname[i]);
1342     mp = (MODCONT *)ment->data;
1343     if (mp->outspec == NULL)
1344     error(USER, "cannot reload from stdout");
1345     if (mp->outspec[0] == '!')
1346     error(USER, "cannot reload from command");
1347 greg 1.46 for (j = 0; ; j++) { /* load each modifier bin */
1348 greg 1.45 ofl = ofname(oname, mp->outspec, mp->modname, j);
1349     if (ofl < 0)
1350     error(USER, "bad output file specification");
1351     oent = lu_find(&ofiletab, oname);
1352     if (oent->data != NULL) {
1353     sout = *(STREAMOUT *)oent->data;
1354     } else {
1355     sout.reclen = 0;
1356 greg 1.46 sout.outpipe = 0;
1357     sout.xr = xres; sout.yr = yres;
1358 greg 1.45 sout.ofp = NULL;
1359     }
1360 greg 1.46 if (sout.ofp == NULL) { /* open output as input */
1361     sout.ofp = fopen(oname, fmode);
1362     if (sout.ofp == NULL) {
1363     if (j)
1364     break; /* assume end of modifier */
1365     sprintf(errmsg, "missing reload file '%s'",
1366     oname);
1367     error(WARNING, errmsg);
1368     break;
1369     }
1370 greg 1.67 #ifdef getc_unlocked /* avoid lock/unlock overhead */
1371     flockfile(sout.ofp);
1372     #endif
1373 greg 1.46 if (header && checkheader(sout.ofp, outvfmt, NULL) != 1) {
1374     sprintf(errmsg, "format mismatch for '%s'",
1375     oname);
1376     error(USER, errmsg);
1377     }
1378     if ((sout.xr > 0) & (sout.yr > 0) &&
1379     (!fscnresolu(&xr, &yr, sout.ofp) ||
1380 greg 1.47 (xr != sout.xr) |
1381     (yr != sout.yr))) {
1382 greg 1.46 sprintf(errmsg, "resolution mismatch for '%s'",
1383     oname);
1384     error(USER, errmsg);
1385     }
1386 greg 1.45 }
1387 greg 1.46 /* read in RGB value */
1388     if (!get_contrib(rgbv, sout.ofp)) {
1389 greg 1.48 if (!j) {
1390     fclose(sout.ofp);
1391 greg 1.46 break; /* ignore empty file */
1392 greg 1.48 }
1393 greg 1.47 if (j < mp->nbins) {
1394 greg 1.46 sprintf(errmsg, "missing data in '%s'",
1395     oname);
1396     error(USER, errmsg);
1397     }
1398     break;
1399     }
1400     if (j >= mp->nbins) /* grow modifier size */
1401     ment->data = (char *)(mp = growmodifier(mp, j+1));
1402     copycolor(mp->cbin[j], rgbv);
1403 greg 1.48 if (oent->key == NULL) /* new file entry */
1404     oent->key = strcpy((char *)
1405     malloc(strlen(oname)+1), oname);
1406     if (oent->data == NULL)
1407     oent->data = (char *)malloc(sizeof(STREAMOUT));
1408     *(STREAMOUT *)oent->data = sout;
1409 greg 1.45 }
1410     }
1411 greg 1.46 lu_doall(&ofiletab, myclose, NULL); /* close all files */
1412     }
1413    
1414     /* seek on the given output file */
1415     static int
1416     myseeko(const LUENT *e, void *p)
1417     {
1418     STREAMOUT *sop = (STREAMOUT *)e->data;
1419     off_t nbytes = *(off_t *)p;
1420    
1421     if (sop->reclen > 1)
1422     nbytes = nbytes * sop->reclen;
1423     if (fseeko(sop->ofp, nbytes, SEEK_CUR) < 0) {
1424     sprintf(errmsg, "seek error on file '%s'", e->key);
1425     error(SYSTEM, errmsg);
1426     }
1427     return 0;
1428 greg 1.45 }
1429    
1430 greg 1.14 /* recover output if possible */
1431     void
1432     recover_output(FILE *fin)
1433     {
1434 greg 1.31 off_t lastout = -1;
1435     int outvsiz, recsiz;
1436     char *outvfmt;
1437     int i, j;
1438     MODCONT *mp;
1439     int ofl;
1440     char oname[1024];
1441     LUENT *ment, *oent;
1442     STREAMOUT sout;
1443     off_t nvals;
1444     int xr, yr;
1445 greg 1.14
1446     switch (outfmt) {
1447     case 'a':
1448     error(USER, "cannot recover ASCII output");
1449     return;
1450     case 'f':
1451     outvsiz = sizeof(float)*3;
1452     outvfmt = "float";
1453     break;
1454     case 'd':
1455     outvsiz = sizeof(double)*3;
1456     outvfmt = "double";
1457     break;
1458     case 'c':
1459     outvsiz = sizeof(COLR);
1460     outvfmt = COLRFMT;
1461     break;
1462     default:
1463     error(INTERNAL, "botched output format");
1464     return;
1465     }
1466     /* check modifier outputs */
1467     for (i = 0; i < nmods; i++) {
1468     ment = lu_find(&modconttab,modname[i]);
1469     mp = (MODCONT *)ment->data;
1470     if (mp->outspec == NULL)
1471     error(USER, "cannot recover from stdout");
1472 greg 1.31 if (mp->outspec[0] == '!')
1473     error(USER, "cannot recover from command");
1474 greg 1.14 for (j = 0; ; j++) { /* check each bin's file */
1475     ofl = ofname(oname, mp->outspec, mp->modname, j);
1476     if (ofl < 0)
1477     error(USER, "bad output file specification");
1478     oent = lu_find(&ofiletab, oname);
1479 greg 1.31 if (oent->data != NULL) {
1480     sout = *(STREAMOUT *)oent->data;
1481     } else {
1482 greg 1.45 sout.reclen = 0;
1483 greg 1.46 sout.outpipe = 0;
1484 greg 1.31 sout.ofp = NULL;
1485     }
1486     if (sout.ofp != NULL) { /* already open? */
1487 greg 1.14 if (ofl & OF_BIN)
1488     continue;
1489     break;
1490     }
1491     /* open output */
1492 greg 1.31 sout.ofp = fopen(oname, "rb+");
1493     if (sout.ofp == NULL) {
1494 greg 1.23 if (j)
1495     break; /* assume end of modifier */
1496     sprintf(errmsg, "missing recover file '%s'",
1497     oname);
1498 greg 1.45 error(WARNING, errmsg);
1499     break;
1500 greg 1.23 }
1501 greg 1.31 nvals = lseek(fileno(sout.ofp), 0, SEEK_END);
1502 greg 1.14 if (nvals <= 0) {
1503     lastout = 0; /* empty output, quit here */
1504 greg 1.31 fclose(sout.ofp);
1505 greg 1.14 break;
1506     }
1507 greg 1.45 if (!sout.reclen) {
1508 greg 1.31 if (!(ofl & OF_BIN)) {
1509 greg 1.14 sprintf(errmsg,
1510 greg 1.32 "need -bn to recover file '%s'",
1511 greg 1.14 oname);
1512 greg 1.32 error(USER, errmsg);
1513 greg 1.14 }
1514 greg 1.31 recsiz = outvsiz;
1515     } else
1516     recsiz = outvsiz * sout.reclen;
1517    
1518     lseek(fileno(sout.ofp), 0, SEEK_SET);
1519     if (header && checkheader(sout.ofp, outvfmt, NULL) != 1) {
1520     sprintf(errmsg, "format mismatch for '%s'",
1521     oname);
1522     error(USER, errmsg);
1523     }
1524 greg 1.48 sout.xr = xres; sout.yr = yres;
1525     if ((sout.xr > 0) & (sout.yr > 0) &&
1526 greg 1.31 (!fscnresolu(&xr, &yr, sout.ofp) ||
1527 greg 1.48 (xr != sout.xr) |
1528     (yr != sout.yr))) {
1529 greg 1.31 sprintf(errmsg, "resolution mismatch for '%s'",
1530     oname);
1531     error(USER, errmsg);
1532 greg 1.14 }
1533 greg 1.31 nvals = (nvals - (off_t)ftell(sout.ofp)) / recsiz;
1534     if ((lastout < 0) | (nvals < lastout))
1535 greg 1.14 lastout = nvals;
1536 greg 1.31 if (oent->key == NULL) /* new entry */
1537 greg 1.14 oent->key = strcpy((char *)
1538     malloc(strlen(oname)+1), oname);
1539 greg 1.31 if (oent->data == NULL)
1540     oent->data = (char *)malloc(sizeof(STREAMOUT));
1541     *(STREAMOUT *)oent->data = sout;
1542 greg 1.14 if (!(ofl & OF_BIN))
1543     break; /* no bin separation */
1544     }
1545     if (!lastout) { /* empty output */
1546 greg 1.16 error(WARNING, "no previous data to recover");
1547 greg 1.14 lu_done(&ofiletab); /* reclose all outputs */
1548     return;
1549     }
1550 greg 1.31 if (j > mp->nbins) /* reallocate modifier bins */
1551     ment->data = (char *)(mp = growmodifier(mp, j));
1552 greg 1.14 }
1553 greg 1.15 if (lastout < 0) {
1554 greg 1.16 error(WARNING, "no output files to recover");
1555 greg 1.15 return;
1556     }
1557 greg 1.49 if (raysleft && lastout >= raysleft/accumulate) {
1558 greg 1.24 error(WARNING, "output appears to be complete");
1559     /* XXX should read & discard input? */
1560     quit(0);
1561     }
1562 greg 1.14 /* seek on all files */
1563     nvals = lastout * outvsiz;
1564     lu_doall(&ofiletab, myseeko, &nvals);
1565 greg 1.28 /* skip repeated input */
1566 greg 1.14 for (nvals = 0; nvals < lastout; nvals++)
1567 greg 1.36 if (getinp(oname, fin) < 0)
1568 greg 1.14 error(USER, "unexpected EOF on input");
1569 greg 1.49 lastray = lastdone = (unsigned long)lastout * accumulate;
1570 greg 1.14 if (raysleft)
1571     raysleft -= lastray;
1572 greg 1.1 }