ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/Development/ray/src/rt/RcontribSimulManager.cpp
Revision: 2.24
Committed: Wed Nov 12 19:41:21 2025 UTC (30 hours, 24 minutes ago) by greg
Branch: MAIN
CVS Tags: HEAD
Changes since 2.23: +9 -8 lines
Log Message:
perf(rtcontrib,rxfluxmtx): Minor optimization

File Contents

# User Rev Content
1 greg 2.1 #ifndef lint
2 greg 2.24 static const char RCSid[] = "$Id: RcontribSimulManager.cpp,v 2.23 2025/11/11 02:08:52 greg Exp $";
3 greg 2.1 #endif
4     /*
5     * RcontribSimulManager.cpp
6     *
7     * Rcontrib simulation manager implementation
8     *
9     * Created by Greg Ward on 10/17/2024.
10     */
11    
12     #include <unistd.h>
13     #include <ctype.h>
14     #include "platform.h"
15     #include "selcall.h"
16     #include "RcontribSimulManager.h"
17     #include "func.h"
18     #include "resolu.h"
19     #include "source.h"
20    
21     char RCCONTEXT[] = "RC.";
22    
23     extern const char HDRSTR[];
24     extern const char BIGEND[];
25     extern const char FMTSTR[];
26    
27     // new/exclusive, overwrite if exists, or recover data
28     int RSDOflags[] = {RDSwrite|RDSexcl|RDSextend, RDSwrite|RDSextend,
29     RDSread|RDSwrite};
30    
31     static const char ROWZEROSTR[] = "NROWS=0000000000000000\n";
32     #define LNROWSTR 6
33    
34     // Modifier channel for recording contributions (no constructor/destructor)
35     struct RcontribMod {
36     RcontribOutput * opl; // pointer to first output channel
37     char * params; // parameters string
38 greg 2.4 EPNODE * binv; // bin expression (NULL if 1 bin)
39 greg 2.1 int nbins; // bin count this modifier
40     int coffset; // column offset in bytes
41     DCOLORV cbin[1]; // bin accumulator (extends struct)
42     /// Access specific (spectral) color bin
43     DCOLORV * operator[](int n) {
44     if ((n < 0) | (n >= nbins)) return NULL;
45     return cbin + n*NCSAMP;
46     }
47     };
48    
49 greg 2.2 // Struct used to assign record calculation to child
50 greg 2.1 struct RowAssignment {
51     uint32 row; // row to do
52     uint32 ac; // accumulation count
53     };
54    
55 greg 2.14 static const char ROW_DONE[] = "ROW FINISHED\n";
56    
57 greg 2.15 // allocate rcontrib accumulator
58     static RcontribMod *
59 greg 2.1 NewRcMod(const char *prms, const char *binexpr, int ncbins)
60     {
61 greg 2.16 if (binexpr && !*binexpr) binexpr = NULL;
62 greg 2.1 if (!prms) prms = "";
63 greg 2.10 if ((ncbins > 1) & !binexpr) {
64 greg 2.8 error(USER, "missing bin expression");
65 greg 2.4 return NULL;
66     }
67 greg 2.10 if (ncbins < 1) ncbins = 1;
68    
69 greg 2.1 RcontribMod * mp = (RcontribMod *)ecalloc(1, sizeof(RcontribMod) +
70     sizeof(DCOLORV)*(NCSAMP*ncbins-1) +
71     strlen(prms)+1);
72    
73 greg 2.11 if (binexpr) { // get/check bin expression
74 greg 2.4 mp->binv = eparse(const_cast<char *>(binexpr));
75 greg 2.10 if (mp->binv->type == NUM) { // constant expression (0)?
76 greg 2.11 if ((int)evalue(mp->binv) != 0) {
77     sprintf(errmsg, "illegal non-zero constant for bin (%s)",
78 greg 2.10 binexpr);
79     error(USER, errmsg);
80     }
81     if (ncbins > 1) {
82     sprintf(errmsg, "bad bin count (%d should be 1)", ncbins);
83     error(USER, errmsg);
84     }
85     epfree(mp->binv, true);
86     mp->binv = NULL;
87     prms = "";
88     ncbins = 1;
89     }
90 greg 2.4 }
91 greg 2.10 mp->params = strcpy((char *)(mp->cbin + ncbins*NCSAMP), prms);
92 greg 2.1 mp->nbins = ncbins;
93     return mp;
94     }
95    
96 greg 2.15 // Free an RcontribMod (public for RcontribSimulManager constructor)
97 greg 2.1 void
98     FreeRcMod(void *p)
99     {
100     if (!p) return;
101 greg 2.4 EPNODE * bep = (*(RcontribMod *)p).binv;
102     if (bep) epfree(bep, true);
103 greg 2.1 efree(p);
104     }
105    
106 greg 2.15 // Get format identifier
107     const char *
108     formstr(int f)
109     {
110     switch (f) {
111     case 'a': return("ascii");
112     case 'f': return("float");
113     case 'd': return("double");
114     case 'c': return(NCSAMP==3 ? COLRFMT : SPECFMT);
115     }
116     return("unknown");
117     }
118    
119 greg 2.17 // Standard file data share function
120 greg 2.15 RdataShare *
121 greg 2.17 fileDataShare(const char *name, RCOutputOp op, size_t siz)
122     {
123 greg 2.18 if (op == RCOrecover && access(name, R_OK|W_OK) < 0) {
124     sprintf(errmsg, "cannot recover from '%s'", name);
125     error(SYSTEM, errmsg);
126     return NULL;
127     }
128 greg 2.21 RdataShare * rds = new RdataShareFile(name, RSDOflags[op],
129     siz*(op != RCOforce));
130    
131     if (!rds || (op == RCOforce && rds->Resize(siz) < siz)) {
132     delete rds;
133     sprintf(errmsg, "cannot create %lu byte output file '%s'",
134     (unsigned long)siz, name);
135     error(SYSTEM, errmsg);
136     return NULL;
137     }
138     return rds;
139 greg 2.17 }
140    
141     // Memory-mapped data share function
142     RdataShare *
143     mapDataShare(const char *name, RCOutputOp op, size_t siz)
144 greg 2.15 {
145 greg 2.18 if (op == RCOrecover && access(name, R_OK|W_OK) < 0) {
146     sprintf(errmsg, "cannot recover from '%s'", name);
147     error(SYSTEM, errmsg);
148     return NULL;
149     }
150 greg 2.21 RdataShare * rds = new RdataShareMap(name, RSDOflags[op],
151     siz*(op != RCOforce));
152    
153     if (!rds || (op == RCOforce && rds->Resize(siz) < siz)) {
154     delete rds;
155     sprintf(errmsg, "cannot create %lu byte output map '%s'",
156     (unsigned long)siz, name);
157     error(SYSTEM, errmsg);
158     return NULL;
159     }
160     return rds;
161 greg 2.15 }
162    
163 greg 2.1 // Set output format ('f', 'd', or 'c')
164     bool
165     RcontribSimulManager::SetDataFormat(int ty)
166     {
167     if (outList) {
168     error(INTERNAL, "cannot call SetDataFormat() after AddModifier()");
169     return false;
170     }
171     switch (ty) {
172     case 'f':
173     dsiz = sizeof(float)*NCSAMP;
174     break;
175     case 'd':
176     dsiz = sizeof(double)*NCSAMP;
177     break;
178     case 'c':
179     dsiz = LSCOLR;
180     break;
181     default:
182     sprintf(errmsg, "unsupported output format '%c'", ty);
183     error(INTERNAL, errmsg);
184     return false;
185     }
186     dtyp = ty;
187     return true;
188     }
189    
190     // static call-back for rcontrib ray-tracing
191     int
192     RcontribSimulManager::RctCall(RAY *r, void *cd)
193     {
194 greg 2.24 int i;
195    
196     if (!r->ro || (i = r->ro->omod) == OVOID)
197     return 0; // hit nothing
198    
199 greg 2.1 if (r->rsrc >= 0 && source[r->rsrc].so != r->ro)
200 greg 2.24 return 0; // shadow ray not on source
201 greg 2.1
202 greg 2.24 const char * mname = objptr(i)->oname;
203 greg 2.1 RcontribSimulManager * rcp = (RcontribSimulManager *)cd;
204     RcontribMod * mp = (RcontribMod *)lu_find(&rcp->modLUT,mname)->data;
205     if (!mp)
206     return 0; // not in our modifier list
207    
208 greg 2.24 if (rcp->HasFlag(RCcontrib)) { // pre-emptive check for zero
209 greg 2.23 for (i = NCSAMP; i--; )
210     if (r->rcoef[i]*r->rcol[i] > FTINY)
211     break;
212     if (i < 0)
213     return 0; // zero contribution
214     } else if (sintens(r->rcoef) <= FTINY)
215     return 0; // zero coefficient
216 greg 2.22
217 greg 2.23 int bi = 0; // get bin index
218 greg 2.4 if (mp->binv) {
219 greg 2.5 worldfunc(RCCONTEXT, r);
220 greg 2.4 set_eparams(mp->params);
221     double bval = evalue(mp->binv);
222     if (bval <= -.5)
223     return 0; // silently ignore negative bin index
224     bi = int(bval + .5);
225     }
226     DCOLORV * dvp = (*mp)[bi];
227 greg 2.1 if (!dvp) {
228 greg 2.4 sprintf(errmsg, "bad bin number for '%s' (%d ignored)", mname, bi);
229 greg 2.1 error(WARNING, errmsg);
230     return 0;
231     }
232     SCOLOR contr;
233     raycontrib(contr, r, PRIMARY); // compute coefficient
234 greg 2.7 if (rcp->HasFlag(RCcontrib))
235 greg 2.1 smultscolor(contr, r->rcol); // -> value contribution
236 greg 2.11
237 greg 2.23 for (i = 0; i < NCSAMP; i++)
238 greg 2.1 *dvp++ += contr[i]; // accumulate color/spectrum
239     return 1;
240     }
241    
242     // check for given format type in string, return last char position
243     static int
244     hasFormat(const char *tst, const char *match)
245     {
246     static const char allPoss[] = "%diouxXfFeEgGaAcsb";
247     const char * s = tst;
248     while (*s) {
249     if (*s++ != '%')
250     continue;
251     while (!strchr(allPoss, *s))
252     s++;
253     if (strchr(match, *s))
254     break;
255     s++;
256     }
257     return (*s != '\0')*(s - tst);
258     }
259    
260     // Add a modifier and arguments, opening output file(s)
261     bool
262     RcontribSimulManager::AddModifier(const char *modn, const char *outspec,
263     const char *prms, const char *binval, int bincnt)
264     {
265 greg 2.8 if (!modn | !outspec || !*modn | !*outspec) {
266 greg 2.1 error(WARNING, "ignoring bad call to AddModifier()");
267     return false;
268     }
269 greg 2.12 if (*outspec == '!') {
270     error(USER, "command output not supported by RcontribSimulManager");
271     return false;
272     }
273 greg 2.1 if (!nChan) { // initial call?
274 greg 2.7 if ((xres < 0) | (yres <= 0)) {
275 greg 2.8 error(USER, "xres, yres must be set before first modifier");
276 greg 2.7 return false;
277     }
278 greg 2.1 if (!SetDataFormat(dtyp))
279     return false;
280     nChan = NCSAMP;
281     } else if (nChan != NCSAMP) {
282 greg 2.11 error(USER, "# spectral channels must be fixed in AddModifier()");
283 greg 2.1 return false;
284     }
285     if (Ready()) {
286     error(INTERNAL, "call to AddModifier() after PrepOutput()");
287     return false;
288     }
289     LUENT * lp = lu_find(&modLUT, modn);
290     if (lp->data) {
291     sprintf(errmsg, "duplicate modifier '%s'", modn);
292     error(USER, errmsg);
293     return false;
294     }
295     if (!lp->key) // create new entry
296     lp->key = strcpy((char *)emalloc(strlen(modn)+1), modn);
297    
298     RcontribMod * mp = NewRcMod(prms, binval, bincnt);
299     if (!mp) return false;
300     lp->data = (char *)mp;
301     const int modndx = hasFormat(outspec, "sb");
302     const int binndx = hasFormat(outspec, "diouxX");
303     int bin0 = 0;
304     char fnbuf[512];
305 greg 2.8 if (!modndx || (binndx > 0) & (modndx > binndx))
306 greg 2.1 sprintf(fnbuf, outspec, bin0, modn);
307     else
308     sprintf(fnbuf, outspec, modn, bin0);
309     RcontribOutput * olast = NULL;
310     RcontribOutput * op;
311     for (op = outList; op; op = (olast=op)->next) {
312     if (strcmp(op->GetName(), fnbuf))
313     continue;
314     if (modndx) { // this ain't right
315     sprintf(errmsg, "output name collision for '%s'", fnbuf);
316     error(USER, errmsg);
317     return false;
318     }
319     if ((binndx > 0) & (xres > 0)) {
320     sprintf(fnbuf, outspec, ++bin0);
321     continue; // each bin goes to another image
322     }
323     mp->coffset = op->rowBytes; // else add to what's there
324     break;
325     }
326     if (!op) { // create new output channel?
327     op = new RcontribOutput(fnbuf);
328     if (olast) olast->next = op;
329     else outList = op;
330     }
331     mp->opl = op; // first (maybe only) output channel
332     if (modndx) // remember modifier if part of name
333     op->omod = lp->key;
334 greg 2.8 if (binndx) { // append output image/bin list
335 greg 2.1 op->rowBytes += dsiz;
336     op->obin = bin0;
337     for (bincnt = 1; bincnt < mp->nbins; bincnt++) {
338 greg 2.8 if (!modndx || (binndx > 0) & (modndx > binndx))
339 greg 2.1 sprintf(fnbuf, outspec, bin0+bincnt, modn);
340     else
341     sprintf(fnbuf, outspec, modn, bin0+bincnt);
342     if (!op->next) {
343     olast = op;
344     olast->next = op = new RcontribOutput(fnbuf);
345     if (modndx) op->omod = lp->key;
346     op->obin = bin0+bincnt;
347     } else {
348     op = op->next;
349     CHECK(op->obin != bin0+bincnt, CONSISTENCY,
350     "bin number mismatch in AddModifier()");
351     }
352     CHECK(op->rowBytes != mp->coffset, CONSISTENCY,
353     "row offset mismatch in AddModifier()");
354     op->rowBytes += dsiz;
355     }
356     } else // else send all results to this channel
357 greg 2.8 op->rowBytes += mp->nbins*dsiz;
358 greg 2.1 return true;
359     }
360    
361     // Add a file of modifiers with associated arguments
362     bool
363     RcontribSimulManager::AddModFile(const char *modfn, const char *outspec,
364     const char *prms,
365     const char *binval, int bincnt)
366     {
367     char * path = getpath(const_cast<char *>(modfn),
368     getrlibpath(), R_OK);
369     FILE * fp;
370     if (!path || !(fp = fopen(path, "r"))) {
371     sprintf(errmsg, "cannot %s modifier file '%s'",
372     path ? "open" : "find", modfn);
373     error(SYSTEM, errmsg);
374     return false;
375     }
376     char mod[MAXSTR];
377     while (fgetword(mod, sizeof(mod), fp))
378 greg 2.11 if (!AddModifier(mod, outspec, prms, binval, bincnt)) {
379     fclose(fp);
380 greg 2.1 return false;
381 greg 2.11 }
382 greg 2.1 fclose(fp);
383     return true;
384     }
385    
386 greg 2.4 // call-back to check if modifier has been loaded
387     static int
388     checkModExists(const LUENT *lp, void *p)
389     {
390 greg 2.11 OBJECT mod = modifier(lp->key);
391    
392     if ((mod != OVOID) & (mod < nsceneobjs))
393 greg 2.4 return 1;
394    
395     sprintf(errmsg, "tracked modifier '%s' not found in main scene", lp->key);
396     error(WARNING, errmsg);
397     return 0;
398     }
399    
400 greg 2.1 // Prepare output channels and return # completed rows
401     int
402     RcontribSimulManager::PrepOutput()
403     {
404     if (!outList || !RtraceSimulManager::Ready()) {
405 greg 2.11 error(INTERNAL, "PrepOutput() called before octree & modifiers set");
406 greg 2.1 return -1;
407     }
408 greg 2.5 if (!cdsF) {
409 greg 2.11 error(INTERNAL, "missing RdataShare constructor call (cdsF)");
410 greg 2.5 return -1;
411     }
412 greg 2.4 if (lu_doall(&modLUT, checkModExists, NULL) < 0)
413     return -1;
414    
415 greg 2.11 outList->nRows = yres * (xres + !xres); // all outputs have same #rows
416 greg 2.1 int remWarnings = 20;
417     for (RcontribOutput *op = outList; op; op = op->next) {
418     if (op->rData) {
419     error(INTERNAL, "output channel already open in PrepOutput()");
420     return -1;
421     }
422 greg 2.11 op->nRows = outList->nRows;
423 greg 2.1 op->rData = (*cdsF)(op->ofname, outOp,
424     GetHeadLen()+1024 + op->nRows*op->rowBytes);
425     freeqstr(op->ofname); op->ofname = NULL;
426     if (outOp == RCOrecover) {
427     int rd = op->CheckHeader(this);
428     if (rd < 0)
429     return -1;
430     if (rd >= op->nRows) {
431 greg 2.18 if (remWarnings > 0) {
432 greg 2.11 sprintf(errmsg, "recovered output '%s' is complete",
433 greg 2.1 op->GetName());
434 greg 2.11 error(WARNING, --remWarnings ? errmsg : "etc...");
435 greg 2.1 }
436     rd = op->nRows;
437     }
438     if (!rInPos | (rInPos > rd)) rInPos = rd;
439     } else if (!op->NewHeader(this))
440     return -1;
441     // make sure there's room
442     if (op->rData->GetSize() < op->begData + op->nRows*op->rowBytes &&
443     !op->rData->Resize(op->begData + op->nRows*op->rowBytes))
444     return -1; // calls error() for us
445     }
446     rowsDone.NewBitMap(outList->nRows); // create row completion map
447     rowsDone.ClearBits(0, rInPos, true);
448 greg 2.19 return nrDone = rInPos;
449 greg 2.1 }
450    
451     // Create header in open write-only channel
452     bool
453     RcontribOutput::NewHeader(const RcontribSimulManager *rcp)
454     {
455     int headlim = rcp->GetHeadLen() + 1024;
456     char * hdr = (char *)rData->GetMemory(0, headlim, 0);
457     int esiz;
458     const int etyp = rcp->GetFormat(&esiz);
459    
460     strcpy(hdr, HDRSTR);
461     strcat(hdr, "RADIANCE\n");
462     begData = strlen(hdr);
463     strcpy(hdr+begData, rcp->GetHeadStr());
464     begData += rcp->GetHeadLen();
465     if (omod) {
466     sprintf(hdr+begData, "MODIFIER=%s\n", omod);
467     begData += strlen(hdr+begData);
468     }
469     if (obin >= 0) {
470     sprintf(hdr+begData, "BIN=%d\n", obin);
471     begData += strlen(hdr+begData);
472     }
473     strcpy(hdr+begData, ROWZEROSTR);
474     rowCountPos = begData+LNROWSTR;
475     begData += sizeof(ROWZEROSTR)-1;
476 greg 2.7 if (!rcp->xres | (rowBytes > esiz)) {
477 greg 2.1 sprintf(hdr+begData, "NCOLS=%d\n", int(rowBytes/esiz));
478     begData += strlen(hdr+begData);
479     }
480     sprintf(hdr+begData, "%s%d\n", NCOMPSTR, NCSAMP);
481     begData += strlen(hdr+begData);
482     if (NCSAMP > 3) {
483 greg 2.20 sprintf(hdr+begData, "%s %g %g %g %g\n", WLSPLTSTR,
484 greg 2.1 WLPART[0], WLPART[1], WLPART[2], WLPART[3]);
485     begData += strlen(hdr+begData);
486     }
487     if (etyp != 'c') {
488     sprintf(hdr+begData, "%s%d\n", BIGEND, nativebigendian());
489     begData += strlen(hdr+begData);
490     }
491     int align = 0;
492     switch (etyp) {
493     case 'f':
494     align = sizeof(float);
495     break;
496     case 'd':
497     align = sizeof(double);
498     break;
499     case 'c':
500     break;
501     default:
502     error(INTERNAL, "unsupported data type in NewHeader()");
503     return false;
504     }
505     strcpy(hdr+begData, FMTSTR); // write format string
506     begData += strlen(hdr+begData);
507     strcpy(hdr+begData, formstr(etyp));
508     begData += strlen(hdr+begData);
509     if (align) // align data at end of header
510     while ((begData+2) % align)
511     hdr[begData++] = ' ';
512     hdr[begData++] = '\n'; // EOL for data format
513     hdr[begData++] = '\n'; // end of nominal header
514 greg 2.7 if ((rcp->xres > 0) & (rowBytes == esiz)) { // tack on resolution string?
515     sprintf(hdr+begData, PIXSTDFMT, rcp->yres, rcp->xres);
516 greg 2.1 begData += strlen(hdr+begData);
517     }
518     return rData->ReleaseMemory(hdr, RDSwrite);
519     }
520    
521     // find string argument in header for named variable
522     static const char *
523     findArgs(const char *hdr, const char *vnm, int len)
524     {
525     const char * npos = strnstr(hdr, vnm, len);
526    
527     if (!npos) return NULL;
528    
529     npos += strlen(vnm); // find start of (first) argument
530     len -= npos - hdr;
531     while (len-- > 0 && (*npos == '=') | isspace(*npos))
532     if (*npos++ == '\n')
533     return NULL;
534    
535     return (len >= 0) ? npos : NULL;
536     }
537    
538     // Load and check header in read/write channel
539     int
540     RcontribOutput::CheckHeader(const RcontribSimulManager *rcp)
541     {
542     int esiz;
543     const int etyp = rcp->GetFormat(&esiz);
544     const int maxlen = rcp->GetHeadLen() + 1024;
545     char * hdr = (char *)rData->GetMemory(0, maxlen, RDSread);
546     const char * cp;
547     // find end of header
548     if (!hdr || !(cp = strnstr(hdr, "\n\n", maxlen))) {
549     sprintf(errmsg, "cannot find end of header in '%s'", GetName());
550     error(USER, errmsg);
551     return -1;
552     }
553     begData = cp - hdr + 1; // increment again at end
554     // check # components
555     if (((cp = findArgs(hdr, NCOMPSTR, begData)) ? atoi(cp) : 3) != NCSAMP) {
556     sprintf(errmsg, "expected %s%d in '%s'", NCOMPSTR, NCSAMP, GetName());
557     error(USER, errmsg);
558     return -1;
559     }
560     // check format
561 greg 2.3 if (!(cp = findArgs(hdr, FMTSTR, begData)) ||
562     strncmp(cp, formstr(etyp), strlen(formstr(etyp)))) {
563 greg 2.1 sprintf(errmsg, "expected %s%s in '%s'", FMTSTR, formstr(etyp), GetName());
564     error(USER, errmsg);
565     return -1;
566     }
567     // check #columns
568 greg 2.19 if ((cp = findArgs(hdr, "NCOLS=", begData)) ? (atoi(cp)*esiz != rowBytes)
569     : (!rcp->xres | (rowBytes > esiz))) {
570     sprintf(errmsg, "expected NCOLS=%d in '%s'", int(rowBytes/esiz), GetName());
571 greg 2.1 error(USER, errmsg);
572     return -1;
573     }
574     // find row count
575     if (!(cp = findArgs(hdr, "NROWS=", begData))) {
576     sprintf(errmsg, "missing NROWS in '%s'", GetName());
577     error(USER, errmsg);
578     return -1;
579     }
580     rowCountPos = cp - hdr;
581     int rlast = atoi(cp);
582     begData++; // advance past closing EOL
583 greg 2.7 if ((rcp->xres > 0) & (rowBytes == esiz)) { // check/skip resolution string?
584 greg 2.1 char rbuf[64];
585 greg 2.7 sprintf(rbuf, PIXSTDFMT, rcp->yres, rcp->xres);
586 greg 2.1 int rlen = strlen(rbuf);
587     if (strncmp(rbuf, hdr+begData, rlen)) {
588     sprintf(errmsg, "bad resolution string in '%s'", GetName());
589     error(USER, errmsg);
590     return -1;
591     }
592     begData += rlen;
593     }
594     // XXX assume the rest is OK: endianness, wavelength splits, modifier, bin
595     return rData->ReleaseMemory(hdr, 0) ? rlast : -1;
596     }
597    
598     // Rewind calculation (previous results unchanged)
599     bool
600     RcontribSimulManager::ResetRow(int r)
601     {
602     if (!rowsDone.Length() | (0 > r) | (r >= rInPos)) {
603     error(WARNING, "ignoring bad call to ResetRow()");
604     return (r == rInPos);
605     }
606     FlushQueue(); // finish current and reset
607     for (RcontribOutput *op = outList; op; op = op->next)
608     if (!op->SetRowsDone(r))
609     return false;
610    
611     rowsDone.ClearBits(r, rInPos-r, false);
612 greg 2.19 nrDone = rInPos = r;
613 greg 2.1 return true;
614     }
615    
616     // call-back for averaging each modifier's contributions to assigned channel(s)
617     static int
618     putModContrib(const LUENT *lp, void *p)
619     {
620     RcontribMod * mp = (RcontribMod *)lp->data;
621     const RcontribSimulManager * rcp = (const RcontribSimulManager *)p;
622     const double sca = 1./rcp->accum;
623     const DCOLORV * dvp = mp->cbin;
624     RcontribOutput * op = mp->opl;
625     int i, n;
626    
627     switch (rcp->GetFormat()) { // conversion based on output type
628     case 'd': {
629     double * dvo = (double *)op->InsertionP(mp->coffset);
630     for (n = mp->nbins; n--; ) {
631     for (i = 0; i < NCSAMP; i++)
632     *dvo++ = *dvp++ * sca;
633     if ((op->obin >= 0) & (n > 0))
634     dvo = (double *)(op = op->Next())->InsertionP(mp->coffset);
635     }
636     } break;
637     case 'f': {
638     float * fvo = (float *)op->InsertionP(mp->coffset);
639     for (n = mp->nbins; n--; ) {
640     for (i = 0; i < NCSAMP; i++)
641     *fvo++ = float(*dvp++ * sca);
642     if ((op->obin >= 0) & (n > 0))
643     fvo = (float *)(op = op->Next())->InsertionP(mp->coffset);
644     }
645     } break;
646     case 'c': {
647     COLRV * cvo = (COLRV *)op->InsertionP(mp->coffset);
648     for (n = mp->nbins; n--; ) {
649     SCOLOR scol;
650     for (i = 0; i < NCSAMP; i++)
651     scol[i] = COLORV(*dvp++ * sca);
652     scolor_scolr(cvo, scol);
653     cvo += LSCOLR;
654     if ((op->obin >= 0) & (n > 0))
655     cvo = (COLRV *)(op = op->Next())->InsertionP(mp->coffset);
656     }
657     } break;
658     default:
659 greg 2.13 error(CONSISTENCY, "unsupported output type in putModContrib()");
660 greg 2.1 return -1;
661     }
662     // clear for next tally
663     memset(mp->cbin, 0, sizeof(DCOLORV)*NCSAMP*mp->nbins);
664     return 1;
665     }
666    
667     // Add a ray/bundle to compute next record
668     int
669     RcontribSimulManager::ComputeRecord(const FVECT orig_direc[])
670     {
671     if (!Ready())
672     return 0;
673     if (rInPos >= outList->nRows) {
674     error(WARNING, "ComputeRecord() called after last record");
675     return 0;
676     }
677     if (nkids > 0) { // in parent process?
678 greg 2.14 int k = GetChild(false); // updates output rows
679     if (k < 0) return -1; // someone died?
680 greg 2.1 RowAssignment rass;
681     rass.row = kidRow[k] = rInPos++;
682     rass.ac = accum;
683     if (write(kid[k].w, &rass, sizeof(rass)) != sizeof(rass) ||
684     writebuf(kid[k].w, orig_direc, sizeof(FVECT)*2*accum)
685     != sizeof(FVECT)*2*accum) {
686     error(SYSTEM, "cannot write to child; dead process?");
687     return -1;
688     }
689     return accum; // tracing/output happens in child
690     }
691     if (NCSAMP != nChan) {
692     error(INTERNAL, "number of color channels must remain fixed");
693     return -1;
694     }
695     // actual work is done here...
696     if (EnqueueBundle(orig_direc, accum) < 0)
697     return -1;
698    
699     RcontribOutput * op; // prepare output buffers
700     for (op = outList; op; op = op->next)
701     if (!op->GetRow(rInPos))
702     return -1;
703     // convert averages & clear
704     if (lu_doall(&modLUT, putModContrib, this) < 0)
705     return -1;
706     // write buffers
707     for (op = outList; op; op = op->next)
708     op->DoneRow();
709    
710     if (!nkids) // update row if solo process
711     UpdateRowsDone(rInPos++); // XXX increment here is critical
712    
713     return accum;
714     }
715    
716     // Get next available child, returning index or -1 if forceWait & idling
717     int
718     RcontribSimulManager::GetChild(bool forceWait)
719     {
720     if (nkids <= 0)
721     return -1;
722     // take inventory
723     int pn, n = 0;
724 greg 2.14 fd_set readset, errset;
725     FD_ZERO(&readset); FD_ZERO(&errset);
726 greg 2.1 for (pn = nkids; pn--; ) {
727     if (kidRow[pn] < 0) { // child already ready?
728     if (forceWait) continue;
729     return pn; // good enough
730     }
731 greg 2.14 FD_SET(kid[pn].r, &readset); // will check on this one
732     FD_SET(kid[pn].r, &errset);
733     if (kid[pn].r >= n)
734     n = kid[pn].r + 1;
735 greg 2.1 }
736     if (!n) // every child is idle?
737     return -1;
738     // wait on "busy" child(ren)
739 greg 2.14 while ((n = select(n, &readset, NULL, &errset, NULL)) <= 0)
740 greg 2.1 if (errno != EINTR) {
741     error(SYSTEM, "select call failed in GetChild()");
742     return -1;
743     }
744 greg 2.14 char buf[sizeof(ROW_DONE)] = "X";
745 greg 2.1 pn = -1; // get flags set by select
746     for (n = nkids; n--; )
747     if (kidRow[n] >= 0 &&
748 greg 2.14 FD_ISSET(kid[n].r, &readset) |
749     FD_ISSET(kid[n].r, &errset)) {
750     // check for error
751     if (FD_ISSET(kid[n].r, &errset) ||
752     read(kid[n].r, buf, sizeof(ROW_DONE)) <= 0 ||
753     memcmp(buf, ROW_DONE, sizeof(ROW_DONE)))
754     return -1;
755 greg 2.1 // update output row counts
756 greg 2.14 UpdateRowsDone(kidRow[n]);
757     kidRow[n] = -1; // flag child available
758 greg 2.1 pn = n;
759     }
760     return pn;
761     }
762    
763     // Update row completion bitmap and update outputs (does not change rInPos)
764     bool
765     RcontribSimulManager::UpdateRowsDone(int r)
766     {
767     if (!rowsDone.TestAndSet(r)) {
768     error(WARNING, "redundant call to UpdateRowsDone()");
769     return false;
770     }
771 greg 2.19 GetRowFinished();
772     if (nrDone <= r)
773 greg 2.1 return true; // nothing to update, yet
774     for (RcontribOutput *op = outList; op; op = op->next)
775 greg 2.19 if (!op->SetRowsDone(nrDone))
776 greg 2.1 return false;
777     return true; // up-to-date
778     }
779    
780     // Run rcontrib child process (never returns)
781     void
782     RcontribSimulManager::RunChild()
783     {
784     FVECT * vecList = NULL;
785     RowAssignment rass;
786     ssize_t nr;
787    
788     accum = 0;
789     errno = 0;
790     while ((nr = read(0, &rass, sizeof(rass))) == sizeof(rass)) {
791     if (!rass.ac) {
792     error(CONSISTENCY, "bad accumulator count in child");
793     exit(1);
794     }
795 greg 2.13 if (rass.ac > accum) {
796     efree(vecList);
797     vecList = (FVECT *)emalloc(sizeof(FVECT)*2*rass.ac);
798     }
799 greg 2.1 accum = rass.ac;
800     rInPos = rass.row;
801    
802     if (readbuf(0, vecList, sizeof(FVECT)*2*accum) !=
803     sizeof(FVECT)*2*accum)
804     break;
805    
806     if (ComputeRecord(vecList) <= 0)
807     exit(1);
808 greg 2.14 // signal this row is done
809     if (write(1, ROW_DONE, sizeof(ROW_DONE)) != sizeof(ROW_DONE))
810     exit(1);
811 greg 2.1 }
812     if (nr) {
813     error(SYSTEM, "read error in child process");
814     exit(1);
815     }
816     exit(0);
817     }
818    
819     // Add child processes as indicated
820     bool
821     RcontribSimulManager::StartKids(int n2go)
822     {
823     if ((n2go <= 0) | (nkids + n2go <= 1))
824     return false;
825    
826     if (!nkids)
827     cow_memshare(); // preload objects
828    
829     n2go += nkids; // => desired brood size
830     kid = (SUBPROC *)erealloc(kid, sizeof(SUBPROC)*n2go);
831     kidRow = (int32 *)erealloc(kidRow, sizeof(int32)*n2go);
832    
833     fflush(stdout); // shouldn't use, anyway
834     while (nkids < n2go) {
835     kid[nkids] = sp_inactive;
836     int rv = open_process(&kid[nkids], NULL);
837     if (!rv) { // in child process?
838 greg 2.14 while (nkids-- > 0) {
839     close(kid[nkids].r);
840 greg 2.1 close(kid[nkids].w);
841 greg 2.14 }
842 greg 2.1 free(kid); free(kidRow);
843     kid = NULL; kidRow = NULL;
844     RunChild(); // should never return
845     _exit(1);
846     }
847     if (rv < 0) {
848     error(SYSTEM, "cannot fork worker process");
849     return false;
850     }
851     kidRow[nkids++] = -1; // newborn is ready
852     }
853     return true;
854     }
855    
856     // Reap the indicated number of children (all if 0)
857     int
858     RcontribSimulManager::StopKids(int n2end)
859     {
860     if (nkids <= 0)
861     return 0;
862     FlushQueue();
863     int status = 0;
864     if (!n2end | (n2end >= nkids)) { // end all subprocesses?
865     status = close_processes(kid, nkids);
866     free(kid); free(kidRow);
867     kid = NULL; kidRow = NULL;
868     nkids = 0;
869     } else { // else shrink family
870     int st;
871     while (n2end-- > 0)
872     if ((st = close_process(&kid[--nkids])) > 0)
873     status = st;
874     // could call realloc(), but it hardly matters
875     }
876     if (status) {
877     sprintf(errmsg, "non-zero (%d) status from child", status);
878     error(WARNING, errmsg);
879     }
880     return status;
881     }
882    
883     // Set number of computation threads (0 => #cores)
884     int
885     RcontribSimulManager::SetThreadCount(int nt)
886     {
887     if (!Ready()) {
888     error(INTERNAL, "must call PrepOutput() before SetThreadCount()");
889     return 0;
890     }
891     if (nt < 0)
892 greg 2.6 return NThreads();
893 greg 2.1 if (!nt) nt = GetNCores();
894     int status = 0;
895     if (nt == 1)
896     status = StopKids();
897     else if (nt < nkids)
898     status = StopKids(nkids-nt);
899     else if (nt > nkids)
900     StartKids(nt-nkids);
901     if (status) {
902     sprintf(errmsg, "non-zero (%d) status from child", status);
903     error(WARNING, errmsg);
904     }
905 greg 2.6 return NThreads();
906 greg 2.1 }