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 (2 days, 23 hours ago) by greg
Branch: MAIN
CVS Tags: HEAD
Changes since 2.23: +9 -8 lines
Log Message:
perf(rtcontrib,rxfluxmtx): Minor optimization

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: RcontribSimulManager.cpp,v 2.23 2025/11/11 02:08:52 greg Exp $";
3 #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 EPNODE * binv; // bin expression (NULL if 1 bin)
39 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 // Struct used to assign record calculation to child
50 struct RowAssignment {
51 uint32 row; // row to do
52 uint32 ac; // accumulation count
53 };
54
55 static const char ROW_DONE[] = "ROW FINISHED\n";
56
57 // allocate rcontrib accumulator
58 static RcontribMod *
59 NewRcMod(const char *prms, const char *binexpr, int ncbins)
60 {
61 if (binexpr && !*binexpr) binexpr = NULL;
62 if (!prms) prms = "";
63 if ((ncbins > 1) & !binexpr) {
64 error(USER, "missing bin expression");
65 return NULL;
66 }
67 if (ncbins < 1) ncbins = 1;
68
69 RcontribMod * mp = (RcontribMod *)ecalloc(1, sizeof(RcontribMod) +
70 sizeof(DCOLORV)*(NCSAMP*ncbins-1) +
71 strlen(prms)+1);
72
73 if (binexpr) { // get/check bin expression
74 mp->binv = eparse(const_cast<char *>(binexpr));
75 if (mp->binv->type == NUM) { // constant expression (0)?
76 if ((int)evalue(mp->binv) != 0) {
77 sprintf(errmsg, "illegal non-zero constant for bin (%s)",
78 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 }
91 mp->params = strcpy((char *)(mp->cbin + ncbins*NCSAMP), prms);
92 mp->nbins = ncbins;
93 return mp;
94 }
95
96 // Free an RcontribMod (public for RcontribSimulManager constructor)
97 void
98 FreeRcMod(void *p)
99 {
100 if (!p) return;
101 EPNODE * bep = (*(RcontribMod *)p).binv;
102 if (bep) epfree(bep, true);
103 efree(p);
104 }
105
106 // 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 // Standard file data share function
120 RdataShare *
121 fileDataShare(const char *name, RCOutputOp op, size_t siz)
122 {
123 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 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 }
140
141 // Memory-mapped data share function
142 RdataShare *
143 mapDataShare(const char *name, RCOutputOp op, size_t siz)
144 {
145 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 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 }
162
163 // 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 int i;
195
196 if (!r->ro || (i = r->ro->omod) == OVOID)
197 return 0; // hit nothing
198
199 if (r->rsrc >= 0 && source[r->rsrc].so != r->ro)
200 return 0; // shadow ray not on source
201
202 const char * mname = objptr(i)->oname;
203 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 if (rcp->HasFlag(RCcontrib)) { // pre-emptive check for zero
209 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
217 int bi = 0; // get bin index
218 if (mp->binv) {
219 worldfunc(RCCONTEXT, r);
220 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 if (!dvp) {
228 sprintf(errmsg, "bad bin number for '%s' (%d ignored)", mname, bi);
229 error(WARNING, errmsg);
230 return 0;
231 }
232 SCOLOR contr;
233 raycontrib(contr, r, PRIMARY); // compute coefficient
234 if (rcp->HasFlag(RCcontrib))
235 smultscolor(contr, r->rcol); // -> value contribution
236
237 for (i = 0; i < NCSAMP; i++)
238 *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 if (!modn | !outspec || !*modn | !*outspec) {
266 error(WARNING, "ignoring bad call to AddModifier()");
267 return false;
268 }
269 if (*outspec == '!') {
270 error(USER, "command output not supported by RcontribSimulManager");
271 return false;
272 }
273 if (!nChan) { // initial call?
274 if ((xres < 0) | (yres <= 0)) {
275 error(USER, "xres, yres must be set before first modifier");
276 return false;
277 }
278 if (!SetDataFormat(dtyp))
279 return false;
280 nChan = NCSAMP;
281 } else if (nChan != NCSAMP) {
282 error(USER, "# spectral channels must be fixed in AddModifier()");
283 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 if (!modndx || (binndx > 0) & (modndx > binndx))
306 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 if (binndx) { // append output image/bin list
335 op->rowBytes += dsiz;
336 op->obin = bin0;
337 for (bincnt = 1; bincnt < mp->nbins; bincnt++) {
338 if (!modndx || (binndx > 0) & (modndx > binndx))
339 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 op->rowBytes += mp->nbins*dsiz;
358 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 if (!AddModifier(mod, outspec, prms, binval, bincnt)) {
379 fclose(fp);
380 return false;
381 }
382 fclose(fp);
383 return true;
384 }
385
386 // call-back to check if modifier has been loaded
387 static int
388 checkModExists(const LUENT *lp, void *p)
389 {
390 OBJECT mod = modifier(lp->key);
391
392 if ((mod != OVOID) & (mod < nsceneobjs))
393 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 // Prepare output channels and return # completed rows
401 int
402 RcontribSimulManager::PrepOutput()
403 {
404 if (!outList || !RtraceSimulManager::Ready()) {
405 error(INTERNAL, "PrepOutput() called before octree & modifiers set");
406 return -1;
407 }
408 if (!cdsF) {
409 error(INTERNAL, "missing RdataShare constructor call (cdsF)");
410 return -1;
411 }
412 if (lu_doall(&modLUT, checkModExists, NULL) < 0)
413 return -1;
414
415 outList->nRows = yres * (xres + !xres); // all outputs have same #rows
416 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 op->nRows = outList->nRows;
423 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 if (remWarnings > 0) {
432 sprintf(errmsg, "recovered output '%s' is complete",
433 op->GetName());
434 error(WARNING, --remWarnings ? errmsg : "etc...");
435 }
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 return nrDone = rInPos;
449 }
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 if (!rcp->xres | (rowBytes > esiz)) {
477 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 sprintf(hdr+begData, "%s %g %g %g %g\n", WLSPLTSTR,
484 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 if ((rcp->xres > 0) & (rowBytes == esiz)) { // tack on resolution string?
515 sprintf(hdr+begData, PIXSTDFMT, rcp->yres, rcp->xres);
516 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 if (!(cp = findArgs(hdr, FMTSTR, begData)) ||
562 strncmp(cp, formstr(etyp), strlen(formstr(etyp)))) {
563 sprintf(errmsg, "expected %s%s in '%s'", FMTSTR, formstr(etyp), GetName());
564 error(USER, errmsg);
565 return -1;
566 }
567 // check #columns
568 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 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 if ((rcp->xres > 0) & (rowBytes == esiz)) { // check/skip resolution string?
584 char rbuf[64];
585 sprintf(rbuf, PIXSTDFMT, rcp->yres, rcp->xres);
586 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 nrDone = rInPos = r;
613 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 error(CONSISTENCY, "unsupported output type in putModContrib()");
660 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 int k = GetChild(false); // updates output rows
679 if (k < 0) return -1; // someone died?
680 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 fd_set readset, errset;
725 FD_ZERO(&readset); FD_ZERO(&errset);
726 for (pn = nkids; pn--; ) {
727 if (kidRow[pn] < 0) { // child already ready?
728 if (forceWait) continue;
729 return pn; // good enough
730 }
731 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 }
736 if (!n) // every child is idle?
737 return -1;
738 // wait on "busy" child(ren)
739 while ((n = select(n, &readset, NULL, &errset, NULL)) <= 0)
740 if (errno != EINTR) {
741 error(SYSTEM, "select call failed in GetChild()");
742 return -1;
743 }
744 char buf[sizeof(ROW_DONE)] = "X";
745 pn = -1; // get flags set by select
746 for (n = nkids; n--; )
747 if (kidRow[n] >= 0 &&
748 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 // update output row counts
756 UpdateRowsDone(kidRow[n]);
757 kidRow[n] = -1; // flag child available
758 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 GetRowFinished();
772 if (nrDone <= r)
773 return true; // nothing to update, yet
774 for (RcontribOutput *op = outList; op; op = op->next)
775 if (!op->SetRowsDone(nrDone))
776 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 if (rass.ac > accum) {
796 efree(vecList);
797 vecList = (FVECT *)emalloc(sizeof(FVECT)*2*rass.ac);
798 }
799 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 // signal this row is done
809 if (write(1, ROW_DONE, sizeof(ROW_DONE)) != sizeof(ROW_DONE))
810 exit(1);
811 }
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 while (nkids-- > 0) {
839 close(kid[nkids].r);
840 close(kid[nkids].w);
841 }
842 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 return NThreads();
893 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 return NThreads();
906 }