ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/util/rtcontrib.c
Revision: 1.69
Committed: Tue Apr 8 23:46:05 2014 UTC (10 years, 6 months ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.68: +1 -1 lines
State: FILE REMOVED
Log Message:
Formally retired rtcontrib

File Contents

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