--- ray/src/util/rtcontrib.c 2007/11/15 18:32:34 1.44 +++ ray/src/util/rtcontrib.c 2012/04/12 01:56:07 1.68 @@ -1,10 +1,23 @@ #ifndef lint -static const char RCSid[] = "$Id: rtcontrib.c,v 1.44 2007/11/15 18:32:34 greg Exp $"; +static const char RCSid[] = "$Id: rtcontrib.c,v 1.68 2012/04/12 01:56:07 greg Exp $"; #endif /* * Gather rtrace output to compute contributions from particular sources */ +/*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + Need to refactor code by forking a subprocess for each + rtrace call to take output and accumulate it into bins + for the parent process. This will avoid our current + bottleneck around processing output queues. We'll sum into + bins and avoid the associated buffer growth, which can be crazy + now (gigabytes/subprocess). Each child process will return + a ray number and a fully computed and ready-to-output + record of modifiers and their bin totals. These will + be queued and sorted by the parent for ordered output or + accumulated for all rays if -c 0 is in play. + XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX*/ + #include "standard.h" #include #include @@ -16,18 +29,26 @@ static const char RCSid[] = "$Id: rtcontrib.c,v 1.44 2 #include "lookup.h" #include "calcomp.h" +#ifdef _WIN32 +typedef int ssize_t; +#endif + #ifndef MAXMODLIST #define MAXMODLIST 1024 /* maximum modifiers we'll track */ #endif -int treebufsiz = BUFSIZ; /* current tree buffer size */ +#ifndef RNUMBER +#define RNUMBER unsigned long /* ray counter (>= sizeof pointer) */ +#endif +ssize_t treebufsiz = BUFSIZ; /* current tree buffer size */ + typedef double DCOLOR[3]; /* double-precision color */ /* * The MODCONT structure is used to accumulate ray contributions * for a particular modifier, which may be subdivided into bins - * if binv is non-zero. If outspec contains a %s in it, this will + * if binv evaluates > 0. If outspec contains a %s in it, this will * be replaced with the modifier name. If outspec contains a %d in it, * this will be used to create one output file per bin, otherwise all bins * will be written to the same file, in order. If the global outfmt @@ -46,16 +67,15 @@ static void mcfree(void *p) { epfree((*(MODCONT *)p).b LUTAB modconttab = LU_SINIT(NULL,mcfree); /* modifier lookup table */ -#define CNT_UNKNOWN 0 /* unknown record length */ -#define CNT_PIPE (-1) /* output to a pipe */ /* * The STREAMOUT structure holds an open FILE pointer and a count of - * the number of RGB triplets per record, or CNT_UNKNOWN (0) if - * unknown or CNT_PIPE (-1) if writing to a command. + * the number of RGB triplets per record, or 0 if unknown. */ typedef struct { FILE *ofp; /* output file pointer */ + int outpipe; /* output is to a pipe */ int reclen; /* triplets/record */ + int xr, yr; /* output resolution for picture */ } STREAMOUT; /* close output stream and free record */ @@ -64,7 +84,7 @@ closestream(void *p) { STREAMOUT *sop = (STREAMOUT *)p; int status; - if (sop->reclen == CNT_PIPE) + if (sop->outpipe) status = pclose(sop->ofp); else status = fclose(sop->ofp); @@ -81,7 +101,7 @@ LUTAB ofiletab = LU_SINIT(free,closestream); /* output STREAMOUT *getostream(const char *ospec, const char *mname, int bn, int noopen); int ofname(char *oname, const char *ospec, const char *mname, int bn); void printheader(FILE *fout, const char *info); -void printresolu(FILE *fout); +void printresolu(FILE *fout, int xr, int yr); /* * The rcont structure is used to manage i/o with a particular @@ -92,15 +112,15 @@ void printresolu(FILE *fout); struct rtproc { struct rtproc *next; /* next in list of processes */ SUBPROC pd; /* rtrace pipe descriptors */ - unsigned long raynum; /* ray number for this tree */ - int bsiz; /* ray tree buffer length */ + RNUMBER raynum; /* ray number for this tree */ + size_t bsiz; /* ray tree buffer length */ char *buf; /* ray tree buffer */ - int nbr; /* number of bytes from rtrace */ + size_t nbr; /* number of bytes from rtrace */ }; /* rtrace process buffer */ /* rtrace command and defaults */ char *rtargv[256+2*MAXMODLIST] = { "rtrace", - "-dj", ".5", "-dr", "3", + "-dj", ".9", "-dr", "3", "-ab", "1", "-ad", "350", }; int rtargc = 9; @@ -108,8 +128,8 @@ int rtargc = 9; char *myrtopts[] = { "-h-", "-x", "1", "-y", "0", "-dt", "0", "-as", "0", "-aa", "0", NULL }; -#define RTCOEFF "-o~~TmWdp" /* compute coefficients only */ -#define RTCONTRIB "-o~~TmVdp" /* compute ray contributions */ +#define RTCOEFF "-o~~~TmWdp" /* compute coefficients only */ +#define RTCONTRIB "-o~~~TmVdp" /* compute ray contributions */ struct rtproc rt0; /* head of rtrace process list */ @@ -133,14 +153,17 @@ int outfmt = 'a'; /* output format */ int header = 1; /* output header? */ int force_open = 0; /* truncate existing output? */ +int recover = 0; /* recover previous output? */ +int accumulate = 1; /* input rays per output record */ int xres = 0; /* horiz. output resolution */ int yres = 0; /* vert. output resolution */ -unsigned long raysleft; /* number of rays left to trace */ +int account; /* current accumulation count */ +RNUMBER raysleft; /* number of rays left to trace */ long waitflush; /* how long until next flush */ -unsigned long lastray = 0; /* last ray number sent */ -unsigned long lastdone = 0; /* last ray processed */ +RNUMBER lastray = 0; /* last ray number sent */ +RNUMBER lastdone = 0; /* last ray processed */ int using_stdout = 0; /* are we using stdout? */ @@ -155,6 +178,7 @@ void addmodfile(char *fname, char *outf, char *binv, i void init(int np); int done_rprocs(struct rtproc *rtp); +void reload_output(void); void recover_output(FILE *fin); void trace_contribs(FILE *fin); struct rtproc *wait_rproc(void); @@ -164,8 +188,45 @@ void process_queue(void); void put_contrib(const DCOLOR cnt, FILE *fout); void add_contrib(const char *modn); -void done_contrib(void); +void done_contrib(int navg); +#ifdef getc_unlocked /* avoid nasty overheads */ +#undef getc +#define getc getc_unlocked +#undef putc +#define putc putc_unlocked +#undef ferror +#define ferror ferror_unlocked +static int +fread_unl(void *ptr, int size, int nitems, FILE *fp) +{ + char *p = (char *)ptr; + int len = size*nitems; + while (len-- > 0) { + int c = getc_unlocked(fp); + if (c == EOF) + return((p - (char *)ptr)/size); + *p++ = c; + } + return(nitems); +} +#undef fread +#define fread fread_unl +static int +fwrite_unl(const void *ptr, int size, int nitems, FILE *fp) +{ + const char *p = (const char *)ptr; + int len = size*nitems; + while (len-- > 0) + putc_unlocked(*p++, fp); + if (ferror_unlocked(fp)) + return(0); + return(nitems); +} +#undef fwrite +#define fwrite fwrite_unl +#endif + /* return number of open rtrace processes */ static int nrtprocs(void) @@ -219,7 +280,6 @@ main(int argc, char *argv[]) { int contrib = 0; int nprocs = 1; - int recover = 0; char *curout = NULL; char *binval = NULL; int bincnt = 0; @@ -244,7 +304,7 @@ main(int argc, char *argv[]) while ((j = expandarg(&argc, &argv, i)) > 0) ; if (j < 0) { - fprintf(stderr, "%s: cannot expand '%s'", + fprintf(stderr, "%s: cannot expand '%s'\n", argv[0], argv[i]); exit(1); } @@ -273,9 +333,13 @@ main(int argc, char *argv[]) continue; } break; + case 'c': /* input rays per output */ + if (argv[i][2] || i >= argc-2) break; + accumulate = atoi(argv[++i]); + continue; case 'r': /* recover output */ if (argv[i][2]) break; - recover++; + recover = 1; continue; case 'h': /* output header? */ switch (argv[i][2]) { @@ -310,7 +374,7 @@ main(int argc, char *argv[]) continue; } if (argv[i][2] == 'o') { - force_open++; + force_open = 1; continue; } setformat(argv[i]+2); @@ -334,7 +398,7 @@ main(int argc, char *argv[]) case 'b': /* bin expression/count */ if (i >= argc-2) break; if (argv[i][2] == 'n') { - bincnt = atoi(argv[++i]); + bincnt = (int)(eval(argv[++i]) + .5); continue; } if (argv[i][2]) break; @@ -362,6 +426,8 @@ main(int argc, char *argv[]) } rtargv[rtargc++] = argv[i]; /* assume rtrace option */ } + if (accumulate <= 0) /* no output flushing for single record */ + xres = yres = 0; /* set global argument list */ gargc = argc; gargv = argv; /* add "mandatory" rtrace settings */ @@ -370,12 +436,16 @@ main(int argc, char *argv[]) rtargv[rtargc++] = contrib ? RTCONTRIB : RTCOEFF; /* just asking for defaults? */ if (!strcmp(argv[i], "-defaults")) { - char sxres[16], syres[16]; + char nps[8], sxres[16], syres[16]; char *rtpath; - printf("-n %-2d\t\t\t\t# number of processes\n", nprocs); + printf("-c %-5d\t\t\t# accumulated rays per record\n", + accumulate); printf("-V%c\t\t\t\t# output %s\n", contrib ? '+' : '-', contrib ? "contributions" : "coefficients"); fflush(stdout); /* report OUR options */ + rtargv[rtargc++] = "-n"; + sprintf(nps, "%d", nprocs); + rtargv[rtargc++] = nps; rtargv[rtargc++] = header ? "-h+" : "-h-"; sprintf(fmt, "-f%c%c", inpfmt, outfmt); rtargv[rtargc++] = fmt; @@ -414,11 +484,11 @@ main(int argc, char *argv[]) error(USER, "missing octree argument"); rtargv[rtargc++] = octree = argv[i]; rtargv[rtargc] = NULL; - /* start rtrace */ + /* start rtrace & recover if requested */ init(nprocs); - if (recover) /* perform recovery if requested */ - recover_output(stdin); - trace_contribs(stdin); /* compute contributions */ + /* compute contributions */ + trace_contribs(stdin); + /* clean up */ quit(0); } @@ -464,7 +534,7 @@ quit(int status) { int rtstat; - if (persist_state == PERSIST_OURS) /* terminate persistent rtrace */ + if (persist_state == PERSIST_OURS) /* terminate waiting rtrace */ killpersist(); /* clean up rtrace process(es) */ rtstat = done_rprocs(&rt0); @@ -526,12 +596,21 @@ init(int np) rtp->next = NULL; /* terminate list */ if (yres > 0) { if (xres > 0) - raysleft = (unsigned long)xres*yres; + raysleft = (RNUMBER)xres*yres; else raysleft = yres; } else raysleft = 0; - waitflush = xres; + if ((account = accumulate) > 0) + raysleft *= accumulate; + waitflush = (yres > 0) & (xres > 1) ? 0 : xres; + if (!recover) + return; + /* recover previous values */ + if (accumulate <= 0) + reload_output(); + else + recover_output(stdin); } /* grow modifier to accommodate more bins */ @@ -561,7 +640,7 @@ addmodifier(char *modn, char *outf, char *binv, int bi error(USER, errmsg); } if (nmods >= MAXMODLIST) - error(USER, "too many modifiers"); + error(INTERNAL, "too many modifiers"); modname[nmods++] = modn; /* XXX assumes static string */ lep->key = modn; /* XXX assumes static string */ mp = (MODCONT *)malloc(sizeof(MODCONT)); @@ -569,19 +648,24 @@ addmodifier(char *modn, char *outf, char *binv, int bi error(SYSTEM, "out of memory in addmodifier"); mp->outspec = outf; /* XXX assumes static string */ mp->modname = modn; /* XXX assumes static string */ - if (binv != NULL) - mp->binv = eparse(binv); - else - mp->binv = eparse("0"); - mp->nbins = 1; + if (binv == NULL) + binv = "0"; /* use single bin if unspecified */ + mp->binv = eparse(binv); + if (mp->binv->type == NUM) { /* check value if constant */ + bincnt = (int)(evalue(mp->binv) + 1.5); + if (bincnt != 1) { + sprintf(errmsg, "illegal non-zero constant for bin (%s)", + binv); + error(USER, errmsg); + } + } + mp->nbins = 1; /* initialize results holder */ setcolor(mp->cbin[0], 0., 0., 0.); - if (mp->binv->type == NUM) /* assume one bin if constant */ - bincnt = 1; - else if (bincnt > 1) + if (bincnt > 1) mp = growmodifier(mp, bincnt); lep->data = (char *)mp; /* allocate output streams */ - for (i = outf==NULL || outf[0]=='!' ? 0 : bincnt; i-- > 0; ) + for (i = bincnt; i-- > 0; ) getostream(mp->outspec, mp->modname, i, 1); return mp; } @@ -640,6 +724,10 @@ ofname(char *oname, const char *ospec, const char *mna mnp = cp; break; case 'd': + case 'i': + case 'o': + case 'x': + case 'X': if (bnp != NULL) return -1; bnp = cp; @@ -672,12 +760,19 @@ void printheader(FILE *fout, const char *info) { extern char VersionID[]; - FILE *fin = fopen(octree, "r"); - - if (fin == NULL) - quit(1); - checkheader(fin, "ignore", fout); /* copy octree header */ - fclose(fin); + /* copy octree header */ + if (octree[0] == '!') { + newheader("RADIANCE", fout); + fputs(octree+1, fout); + if (octree[strlen(octree)-1] != '\n') + fputc('\n', fout); + } else { + FILE *fin = fopen(octree, "r"); + if (fin == NULL) + quit(1); + checkheader(fin, "ignore", fout); + fclose(fin); + } printargs(gargc-1, gargv, fout); /* add our command */ fprintf(fout, "SOFTWARE= %s\n", VersionID); fputnow(fout); @@ -702,19 +797,17 @@ printheader(FILE *fout, const char *info) /* write resolution string to given output stream */ void -printresolu(FILE *fout) +printresolu(FILE *fout, int xr, int yr) { - if (xres > 0) { - if (yres > 0) /* resolution string */ - fprtresolu(xres, yres, fout); - fflush(fout); - } + if ((xr > 0) & (yr > 0)) /* resolution string */ + fprtresolu(xr, yr, fout); } /* Get output stream pointer (open and write header if new and noopen==0) */ STREAMOUT * getostream(const char *ospec, const char *mname, int bn, int noopen) { + static const DCOLOR nocontrib = BLKCOLOR; static STREAMOUT stdos; int ofl; char oname[1024]; @@ -723,12 +816,14 @@ getostream(const char *ospec, const char *mname, int b if (ospec == NULL) { /* use stdout? */ if (!noopen && !using_stdout) { - stdos.reclen = 0; if (outfmt != 'a') SET_FILE_BINARY(stdout); if (header) printheader(stdout, NULL); - printresolu(stdout); + printresolu(stdout, xres, yres); + if (waitflush > 0) + fflush(stdout); + stdos.xr = xres; stdos.yr = yres; using_stdout = 1; } stdos.ofp = stdout; @@ -748,24 +843,30 @@ getostream(const char *ospec, const char *mname, int b sop = (STREAMOUT *)malloc(sizeof(STREAMOUT)); if (sop == NULL) error(SYSTEM, "out of memory in getostream"); - sop->reclen = oname[0] == '!' ? CNT_PIPE : CNT_UNKNOWN; + sop->outpipe = oname[0] == '!'; + sop->reclen = 0; sop->ofp = NULL; /* open iff noopen==0 */ + sop->xr = xres; sop->yr = yres; lep->data = (char *)sop; + if (!sop->outpipe & !force_open & !recover && + access(oname, F_OK) == 0) { + errno = EEXIST; /* file exists */ + goto openerr; + } } if (!noopen && sop->ofp == NULL) { /* open output stream */ long i; if (oname[0] == '!') /* output to command */ sop->ofp = popen(oname+1, "w"); - else if (!force_open && access(oname, F_OK) == 0) - errno = EEXIST; /* file exists */ - else /* else open it */ + else /* else open file */ sop->ofp = fopen(oname, "w"); - if (sop->ofp == NULL) { - sprintf(errmsg, "cannot open '%s' for writing", oname); - error(SYSTEM, errmsg); - } + if (sop->ofp == NULL) + goto openerr; if (outfmt != 'a') SET_FILE_BINARY(sop->ofp); +#ifdef getc_unlocked /* avoid lock/unlock overhead */ + flockfile(sop->ofp); +#endif if (header) { char info[512]; char *cp = info; @@ -780,29 +881,36 @@ getostream(const char *ospec, const char *mname, int b *cp = '\0'; printheader(sop->ofp, info); } - printresolu(sop->ofp); + if (accumulate > 0) { /* global resolution */ + sop->xr = xres; sop->yr = yres; + } + printresolu(sop->ofp, sop->xr, sop->yr); /* play catch-up */ - for (i = sop->reclen > 1 ? sop->reclen*lastdone : lastdone; - i--; ) { - static const DCOLOR nocontrib = BLKCOLOR; - put_contrib(nocontrib, sop->ofp); + for (i = accumulate > 0 ? lastdone/accumulate : 0; i--; ) { + int j = sop->reclen; + if (j <= 0) j = 1; + while (j--) + put_contrib(nocontrib, sop->ofp); if (outfmt == 'a') putc('\n', sop->ofp); } - if (xres > 0) + if (waitflush > 0) fflush(sop->ofp); } - if (sop->reclen != CNT_PIPE) /* add to length if noopen */ - sop->reclen += noopen; - return sop; /* return open stream */ + sop->reclen += noopen; /* add to length if noopen */ + return sop; /* return output stream */ +openerr: + sprintf(errmsg, "cannot open '%s' for writing", oname); + error(SYSTEM, errmsg); + return NULL; /* pro forma return */ } /* read input ray into buffer */ int getinp(char *buf, FILE *fp) { - double dv[3]; - float fv[3]; + double dv[3], *dvp; + float *fvp; char *cp; int i; @@ -824,17 +932,17 @@ getinp(char *buf, FILE *fp) return 0; /* dummy ray */ return strlen(buf); case 'f': - if (fread(buf, sizeof(float), 6, fp) < 6) + if (fread(buf, sizeof(float), 6, fp) != 6) return -1; - memcpy(fv, buf+3*sizeof(float), 3*sizeof(float)); - if (DOT(fv,fv) <= FTINY*FTINY) + fvp = (float *)buf + 3; + if (DOT(fvp,fvp) <= FTINY*FTINY) return 0; /* dummy ray */ return sizeof(float)*6; case 'd': - if (fread(buf, sizeof(double), 6, fp) < 6) + if (fread(buf, sizeof(double), 6, fp) != 6) return -1; - memcpy(dv, buf+3*sizeof(double), 3*sizeof(double)); - if (DOT(dv,dv) <= FTINY*FTINY) + dvp = (double *)buf + 3; + if (DOT(dvp,dvp) <= FTINY*FTINY) return 0; /* dummy ray */ return sizeof(double)*6; } @@ -903,9 +1011,7 @@ put_contrib(const DCOLOR cnt, FILE *fout) fprintf(fout, "%.6e\t%.6e\t%.6e\t", cnt[0], cnt[1], cnt[2]); break; case 'f': - fv[0] = cnt[0]; - fv[1] = cnt[1]; - fv[2] = cnt[2]; + copycolor(fv, cnt); fwrite(fv, sizeof(float), 3, fout); break; case 'd': @@ -920,16 +1026,23 @@ put_contrib(const DCOLOR cnt, FILE *fout) } } -/* output ray tallies and clear for next primary */ +/* output ray tallies and clear for next accumulation */ void -done_contrib(void) +done_contrib(int navg) { + double sf = 1.; int i, j; MODCONT *mp; STREAMOUT *sop; + /* set average scaling */ + if (navg > 1) + sf = 1. / (double)navg; /* output modifiers in order */ for (i = 0; i < nmods; i++) { mp = (MODCONT *)lu_find(&modconttab,modname[i])->data; + if (navg > 1) /* average scaling */ + for (j = mp->nbins; j--; ) + scalecolor(mp->cbin[j], sf); sop = getostream(mp->outspec, mp->modname, 0,0); put_contrib(mp->cbin[0], sop->ofp); if (mp->nbins > 3 && /* minor optimization */ @@ -940,7 +1053,7 @@ done_contrib(void) for (j = 1; j < mp->nbins; j++) put_contrib(mp->cbin[j], getostream(mp->outspec,mp->modname,j,0)->ofp); - /* clear for next ray tree */ + /* clear for next time */ memset(mp->cbin, 0, sizeof(DCOLOR)*mp->nbins); } --waitflush; /* terminate records */ @@ -948,7 +1061,7 @@ done_contrib(void) if (using_stdout & (outfmt == 'a')) putc('\n', stdout); if (!waitflush) { - waitflush = xres; + waitflush = (yres > 0) & (xres > 1) ? 0 : xres; if (using_stdout) fflush(stdout); } @@ -1013,7 +1126,9 @@ process_queue(void) cp += sizeof(float)*9; n -= sizeof(float)*9; add_contrib(modname); } - done_contrib(); /* sum up contributions & output */ + /* time to produce record? */ + if (account > 0 && !--account) + done_contrib(account = accumulate); lastdone = rtp->raynum; if (rtp->buf != NULL) /* free up buffer space */ free(rtp->buf); @@ -1028,7 +1143,7 @@ wait_rproc(void) { struct rtproc *rtfree = NULL; fd_set readset, errset; - int nr; + ssize_t nr; struct rtproc *rt; int n; @@ -1059,23 +1174,29 @@ wait_rproc(void) continue; if (rt->buf == NULL) { rt->bsiz = treebufsiz; - rt->buf = (char *)malloc(treebufsiz); + rt->buf = (char *)malloc(rt->bsiz); } else if (rt->nbr + BUFSIZ > rt->bsiz) { if (rt->bsiz + BUFSIZ <= treebufsiz) rt->bsiz = treebufsiz; - else - treebufsiz = rt->bsiz += BUFSIZ; + else if ((treebufsiz = rt->bsiz += BUFSIZ) < 0) + error(INTERNAL, + "ray buffer does not fit memory"); rt->buf = (char *)realloc(rt->buf, rt->bsiz); } if (rt->buf == NULL) error(SYSTEM, "out of memory in wait_rproc"); - nr = read(rt->pd.r, rt->buf+rt->nbr, rt->bsiz-rt->nbr); - if (nr <= 0) + nr = rt->bsiz - rt->nbr; + if (nr & ~0x7fffffff) /* avoid 32-bit OS issues */ + nr = 0x7fffffff; + nr = read(rt->pd.r, rt->buf+rt->nbr, nr); + if (nr < 0) + error(SYSTEM, "read error from rtrace"); + if (!nr) error(USER, "rtrace process died"); rt->nbr += nr; /* advance & check */ - if (rt->nbr >= 4 && !memcmp(rt->buf+rt->nbr-4, - "~\t~\t", 4)) { - rt->nbr -= 4; /* elide terminator */ + if (rt->nbr >= 6 && !memcmp(rt->buf+rt->nbr-6, + "~\t~\t~\t", 6)) { + rt->nbr -= 6; /* elide terminator */ queue_raytree(rt); rtfree = rt; /* ready for next ray */ } @@ -1100,27 +1221,33 @@ get_rproc(void) void trace_contribs(FILE *fin) { + static int ignore_warning_given = 0; char inpbuf[128]; int iblen; struct rtproc *rtp; /* loop over input */ while ((iblen = getinp(inpbuf, fin)) >= 0) { - if (!iblen || /* need reset? */ + if (!iblen && accumulate != 1) { + if (!ignore_warning_given++) + error(WARNING, + "dummy ray(s) ignored during accumulation\n"); + continue; + } + if (!iblen || /* need flush/reset? */ queue_length() > 10*nrtprocs() || lastray+1 < lastray) { while (wait_rproc() != NULL) process_queue(); - if (lastray+1 < lastray) - lastdone = lastray = 0; + lastdone = lastray = 0; } rtp = get_rproc(); /* get avail. rtrace process */ rtp->raynum = ++lastray; /* assign ray */ if (iblen) { /* trace ray if valid */ writebuf(rtp->pd.w, inpbuf, iblen); } else { /* else bypass dummy ray */ - queue_raytree(rtp); /* empty tree */ - if ((yres <= 0) | (waitflush > 1)) - waitflush = 1; /* flush after this */ + queue_raytree(rtp); /* queue empty ray/record */ + if ((yres <= 0) | (xres <= 0)) + waitflush = 1; /* flush right after */ } process_queue(); /* catch up with results */ if (raysleft && !--raysleft) @@ -1128,11 +1255,167 @@ trace_contribs(FILE *fin) } while (wait_rproc() != NULL) /* process outstanding rays */ process_queue(); + if (accumulate <= 0) + done_contrib(0); /* output tallies */ + else if (account < accumulate) { + error(WARNING, "partial accumulation in final record"); + done_contrib(accumulate - account); + } if (raysleft) error(USER, "unexpected EOF on input"); lu_done(&ofiletab); /* close output files */ } +/* get ray contribution from previous file */ +static int +get_contrib(DCOLOR cnt, FILE *finp) +{ + COLOR fv; + COLR cv; + + switch (outfmt) { + case 'a': + return(fscanf(finp,"%lf %lf %lf",&cnt[0],&cnt[1],&cnt[2]) == 3); + case 'f': + if (fread(fv, sizeof(fv[0]), 3, finp) != 3) + return(0); + copycolor(cnt, fv); + return(1); + case 'd': + return(fread(cnt, sizeof(cnt[0]), 3, finp) == 3); + case 'c': + if (fread(cv, sizeof(cv), 1, finp) != 1) + return(0); + colr_color(fv, cv); + copycolor(cnt, fv); + return(1); + default: + error(INTERNAL, "botched output format"); + } + return(0); /* pro forma return */ +} + +/* close output file opened for input */ +static int +myclose(const LUENT *e, void *p) +{ + STREAMOUT *sop = (STREAMOUT *)e->data; + + if (sop->ofp == NULL) + return(0); + fclose(sop->ofp); + sop->ofp = NULL; + return(0); +} + +/* load previously accumulated values */ +void +reload_output(void) +{ + int i, j; + MODCONT *mp; + int ofl; + char oname[1024]; + char *fmode = "rb"; + char *outvfmt; + LUENT *ment, *oent; + int xr, yr; + STREAMOUT sout; + DCOLOR rgbv; + + switch (outfmt) { + case 'a': + outvfmt = "ascii"; + fmode = "r"; + break; + case 'f': + outvfmt = "float"; + break; + case 'd': + outvfmt = "double"; + break; + case 'c': + outvfmt = COLRFMT; + break; + default: + error(INTERNAL, "botched output format"); + return; + } + /* reload modifier values */ + for (i = 0; i < nmods; i++) { + ment = lu_find(&modconttab,modname[i]); + mp = (MODCONT *)ment->data; + if (mp->outspec == NULL) + error(USER, "cannot reload from stdout"); + if (mp->outspec[0] == '!') + error(USER, "cannot reload from command"); + for (j = 0; ; j++) { /* load each modifier bin */ + ofl = ofname(oname, mp->outspec, mp->modname, j); + if (ofl < 0) + error(USER, "bad output file specification"); + oent = lu_find(&ofiletab, oname); + if (oent->data != NULL) { + sout = *(STREAMOUT *)oent->data; + } else { + sout.reclen = 0; + sout.outpipe = 0; + sout.xr = xres; sout.yr = yres; + sout.ofp = NULL; + } + if (sout.ofp == NULL) { /* open output as input */ + sout.ofp = fopen(oname, fmode); + if (sout.ofp == NULL) { + if (j) + break; /* assume end of modifier */ + sprintf(errmsg, "missing reload file '%s'", + oname); + error(WARNING, errmsg); + break; + } +#ifdef getc_unlocked /* avoid lock/unlock overhead */ + flockfile(sout.ofp); +#endif + if (header && checkheader(sout.ofp, outvfmt, NULL) != 1) { + sprintf(errmsg, "format mismatch for '%s'", + oname); + error(USER, errmsg); + } + if ((sout.xr > 0) & (sout.yr > 0) && + (!fscnresolu(&xr, &yr, sout.ofp) || + (xr != sout.xr) | + (yr != sout.yr))) { + sprintf(errmsg, "resolution mismatch for '%s'", + oname); + error(USER, errmsg); + } + } + /* read in RGB value */ + if (!get_contrib(rgbv, sout.ofp)) { + if (!j) { + fclose(sout.ofp); + break; /* ignore empty file */ + } + if (j < mp->nbins) { + sprintf(errmsg, "missing data in '%s'", + oname); + error(USER, errmsg); + } + break; + } + if (j >= mp->nbins) /* grow modifier size */ + ment->data = (char *)(mp = growmodifier(mp, j+1)); + copycolor(mp->cbin[j], rgbv); + if (oent->key == NULL) /* new file entry */ + oent->key = strcpy((char *) + malloc(strlen(oname)+1), oname); + if (oent->data == NULL) + oent->data = (char *)malloc(sizeof(STREAMOUT)); + *(STREAMOUT *)oent->data = sout; + } + } + lu_doall(&ofiletab, myclose, NULL); /* close all files */ +} + /* seek on the given output file */ static int myseeko(const LUENT *e, void *p) @@ -1201,7 +1484,8 @@ recover_output(FILE *fin) if (oent->data != NULL) { sout = *(STREAMOUT *)oent->data; } else { - sout.reclen = CNT_UNKNOWN; + sout.reclen = 0; + sout.outpipe = 0; sout.ofp = NULL; } if (sout.ofp != NULL) { /* already open? */ @@ -1216,7 +1500,8 @@ recover_output(FILE *fin) break; /* assume end of modifier */ sprintf(errmsg, "missing recover file '%s'", oname); - error(USER, errmsg); + error(WARNING, errmsg); + break; } nvals = lseek(fileno(sout.ofp), 0, SEEK_END); if (nvals <= 0) { @@ -1224,7 +1509,7 @@ recover_output(FILE *fin) fclose(sout.ofp); break; } - if (sout.reclen == CNT_UNKNOWN) { + if (!sout.reclen) { if (!(ofl & OF_BIN)) { sprintf(errmsg, "need -bn to recover file '%s'", @@ -1241,10 +1526,11 @@ recover_output(FILE *fin) oname); error(USER, errmsg); } - if ((xres > 0) & (yres > 0) && + sout.xr = xres; sout.yr = yres; + if ((sout.xr > 0) & (sout.yr > 0) && (!fscnresolu(&xr, &yr, sout.ofp) || - xr != xres || - yr != yres)) { + (xr != sout.xr) | + (yr != sout.yr))) { sprintf(errmsg, "resolution mismatch for '%s'", oname); error(USER, errmsg); @@ -1273,7 +1559,7 @@ recover_output(FILE *fin) error(WARNING, "no output files to recover"); return; } - if (raysleft && lastout >= raysleft) { + if (raysleft && lastout >= raysleft/accumulate) { error(WARNING, "output appears to be complete"); /* XXX should read & discard input? */ quit(0); @@ -1285,7 +1571,7 @@ recover_output(FILE *fin) for (nvals = 0; nvals < lastout; nvals++) if (getinp(oname, fin) < 0) error(USER, "unexpected EOF on input"); - lastray = lastdone = (unsigned long)lastout; + lastray = lastdone = (RNUMBER)lastout * accumulate; if (raysleft) raysleft -= lastray; }