ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/rc3.c
Revision: 2.25
Committed: Wed Nov 15 18:02:53 2023 UTC (5 months, 2 weeks ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 2.24: +24 -18 lines
Log Message:
feat(rpict,rtrace,rcontrib,rtpict): Hyperspectral rendering (except photon map)

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: rc3.c,v 2.24 2020/09/09 21:28:19 greg Exp $";
3 #endif
4 /*
5 * Accumulate ray contributions for a set of materials
6 * Controlling process for multiple children
7 */
8
9 #include <signal.h>
10 #include "rcontrib.h"
11 #include "selcall.h"
12
13 #define MAXIQ (int)(PIPE_BUF/(sizeof(FVECT)*2))
14
15 /* Modifier contribution queue (results waiting to be output) */
16 typedef struct s_binq {
17 RNUMBER ndx; /* index for this entry */
18 RNUMBER nadded; /* accumulated so far */
19 struct s_binq *next; /* next in queue */
20 MODCONT *mca[1]; /* contrib. array (extends struct) */
21 } BINQ;
22
23 static BINQ *out_bq = NULL; /* output bin queue */
24 static BINQ *free_bq = NULL; /* free queue entries */
25
26 static SUBPROC kidpr[MAXPROCESS]; /* our child processes */
27
28 static struct {
29 RNUMBER r1; /* assigned ray starting index */
30 FILE *infp; /* file pointer to read from process */
31 int nr; /* number of rays to sum (0 if free) */
32 } kida[MAXPROCESS]; /* our child process i/o */
33
34
35 /* Get new bin queue entry */
36 static BINQ *
37 new_binq()
38 {
39 BINQ *bp;
40 int i;
41
42 if (free_bq != NULL) { /* something already available? */
43 bp = free_bq;
44 free_bq = bp->next;
45 bp->next = NULL;
46 bp->nadded = 0;
47 return(bp);
48 }
49 /* else allocate fresh */
50 bp = (BINQ *)malloc(sizeof(BINQ) + sizeof(MODCONT *)*(nmods-1));
51 if (bp == NULL)
52 goto memerr;
53 for (i = nmods; i--; ) {
54 MODCONT *mp = (MODCONT *)lu_find(&modconttab,modname[i])->data;
55 bp->mca[i] = (MODCONT *)malloc(mcsize(mp->nbins));
56 if (bp->mca[i] == NULL)
57 goto memerr;
58 memcpy(bp->mca[i], mp, sizeof(MODCONT)-sizeof(DCOLORV));
59 /* memset(bp->mca[i]->cbin, 0, DCOLORSIZ*mp->nbins); */
60 }
61 bp->ndx = 0;
62 bp->nadded = 0;
63 bp->next = NULL;
64 return(bp);
65 memerr:
66 error(SYSTEM, "out of memory in new_binq()");
67 return(NULL);
68 }
69
70
71 /* Free a bin queue entry */
72 static void
73 free_binq(BINQ *bp)
74 {
75 int i;
76
77 if (bp == NULL) { /* signal to release our free list */
78 while ((bp = free_bq) != NULL) {
79 free_bq = bp->next;
80 for (i = nmods; i--; )
81 free(bp->mca[i]);
82 /* Note: we don't own bp->mca[i]->binv */
83 free(bp);
84 }
85 return;
86 }
87 /* clear sums for next use */
88 /* for (i = nmods; i--; )
89 memset(bp->mca[i]->cbin, 0, DCOLORSIZ*bp->mca[i]->nbins);
90 */
91 if (bp->next != NULL)
92 error(CONSISTENCY, "free_binq() handed list");
93 bp->ndx = 0;
94 bp->next = free_bq; /* push onto free list */
95 free_bq = bp;
96 }
97
98
99 /* Add modifier values to accumulation record in queue and clear */
100 static void
101 queue_modifiers()
102 {
103 MODCONT *mpin, *mpout;
104 DCOLORV *ssrc, *sdst;
105 int i;
106
107 if ((accumulate > 0) | (out_bq == NULL))
108 error(CONSISTENCY, "bad call to queue_modifiers()");
109
110 for (i = nmods; i--; ) {
111 mpin = (MODCONT *)lu_find(&modconttab,modname[i])->data;
112 mpout = out_bq->mca[i];
113 ssrc = mcbin(mpin, mpin->nbins);
114 sdst = mcbin(mpout, mpout->nbins);
115 while (sdst > mpout->cbin)
116 *--sdst += *--ssrc;
117 memset(mpin->cbin, 0, DCOLORSIZ*mpin->nbins);
118 }
119 out_bq->nadded++;
120 }
121
122
123 /* Sum one modifier record into another (updates nadded) */
124 static void
125 add_modbin(BINQ *dst, BINQ *src)
126 {
127 MODCONT *mpin, *mpout;
128 DCOLORV *ssrc, *sdst;
129 int i;
130
131 for (i = nmods; i--; ) {
132 mpin = src->mca[i];
133 mpout = dst->mca[i];
134 ssrc = mcbin(mpin, mpin->nbins);
135 sdst = mcbin(mpout, mpout->nbins);
136 while (sdst > mpout->cbin)
137 *--sdst += *--ssrc;
138 }
139 dst->nadded += src->nadded;
140 }
141
142
143 /* Queue values for later output */
144 static void
145 queue_output(BINQ *bp)
146 {
147 BINQ *b_last, *b_cur;
148
149 if (accumulate <= 0) { /* just accumulating? */
150 if (out_bq == NULL) {
151 bp->next = NULL;
152 out_bq = bp;
153 } else {
154 add_modbin(out_bq, bp);
155 free_binq(bp);
156 }
157 return;
158 }
159 b_last = NULL; /* insert in output queue */
160 for (b_cur = out_bq; b_cur != NULL && b_cur->ndx < bp->ndx;
161 b_cur = b_cur->next)
162 b_last = b_cur;
163
164 if (b_last != NULL) {
165 bp->next = b_cur;
166 b_last->next = bp;
167 } else {
168 bp->next = out_bq;
169 out_bq = bp;
170 }
171 if (accumulate == 1) /* no accumulation? */
172 return;
173 b_cur = out_bq; /* else merge accumulation entries */
174 while (b_cur->next != NULL) {
175 if (b_cur->nadded >= accumulate ||
176 (b_cur->ndx-1)/accumulate !=
177 (b_cur->next->ndx-1)/accumulate) {
178 b_cur = b_cur->next;
179 continue;
180 }
181 add_modbin(b_cur, b_cur->next);
182 b_last = b_cur->next;
183 b_cur->next = b_last->next;
184 b_last->next = NULL;
185 free_binq(b_last);
186 }
187 }
188
189
190 /* Count number of records ready for output */
191 static int
192 queue_ready()
193 {
194 int nready = 0;
195 BINQ *bp;
196
197 for (bp = out_bq; bp != NULL && bp->nadded >= accumulate &&
198 bp->ndx == lastdone+nready*accumulate+1;
199 bp = bp->next)
200 ++nready;
201
202 return(nready);
203 }
204
205
206 /* Catch up with output queue by producing ready results */
207 static int
208 output_catchup(int nmax)
209 {
210 int nout = 0;
211 BINQ *bp;
212 int i;
213 /* output ready results */
214 while (out_bq != NULL && out_bq->nadded >= accumulate
215 && out_bq->ndx == lastdone+1) {
216 if ((nmax > 0) & (nout >= nmax))
217 break;
218 bp = out_bq; /* pop off first entry */
219 out_bq = bp->next;
220 bp->next = NULL;
221 for (i = 0; i < nmods; i++) /* output record */
222 mod_output(bp->mca[i]);
223 end_record();
224 free_binq(bp); /* free this entry */
225 lastdone += accumulate;
226 ++nout;
227 }
228 return(nout);
229 }
230
231
232 /* Put a zero record in results queue & output */
233 void
234 put_zero_record(int ndx)
235 {
236 BINQ *bp = new_binq();
237 int i;
238
239 for (i = nmods; i--; )
240 memset(bp->mca[i]->cbin, 0, DCOLORSIZ*bp->mca[i]->nbins);
241 bp->ndx = ndx;
242 bp->nadded = 1;
243 queue_output(bp);
244 output_catchup(0);
245 }
246
247
248 /* Get results from child process and add to queue */
249 static void
250 queue_results(int k)
251 {
252 BINQ *bq = new_binq(); /* get results holder */
253 int j;
254
255 bq->ndx = kida[k].r1;
256 bq->nadded = kida[k].nr;
257 /* read from child */
258 for (j = 0; j < nmods; j++)
259 if (getbinary(bq->mca[j]->cbin, DCOLORSIZ, bq->mca[j]->nbins,
260 kida[k].infp) != bq->mca[j]->nbins)
261 error(SYSTEM, "read error from render process");
262
263 queue_output(bq); /* put results in output queue */
264 kida[k].nr = 0; /* mark child as available */
265 }
266
267
268 /* callback to set output spec to NULL (stdout) */
269 static int
270 set_stdout(const LUENT *le, void *p)
271 {
272 (*(MODCONT *)le->data).outspec = NULL;
273 return(0);
274 }
275
276
277 /* Start child processes if we can (call only once in parent!) */
278 int
279 in_rchild()
280 {
281 int rval;
282
283 while (nchild < nproc) { /* fork until target reached */
284 errno = 0;
285 rval = open_process(&kidpr[nchild], NULL);
286 if (rval < 0)
287 error(SYSTEM, "open_process() call failed");
288 if (rval == 0) { /* if in child, set up & return true */
289 lu_doall(&modconttab, &set_stdout, NULL);
290 lu_done(&ofiletab);
291 while (nchild--) { /* don't share other pipes */
292 close(kidpr[nchild].w);
293 fclose(kida[nchild].infp);
294 }
295 inpfmt = (sizeof(RREAL)==sizeof(double)) ? 'd' : 'f';
296 outfmt = 'd';
297 header = 0;
298 yres = 0;
299 raysleft = 0;
300 if (accumulate == 1) {
301 waitflush = xres = 1;
302 account = accumulate = 1;
303 } else { /* parent controls accumulation */
304 waitflush = xres = 0;
305 account = accumulate = 0;
306 }
307 return(1); /* return "true" in child */
308 }
309 if (rval != PIPE_BUF)
310 error(CONSISTENCY, "bad value from open_process()");
311 /* connect to child's output */
312 kida[nchild].infp = fdopen(kidpr[nchild].r, "rb");
313 if (kida[nchild].infp == NULL)
314 error(SYSTEM, "out of memory in in_rchild()");
315 kida[nchild++].nr = 0; /* mark as available */
316 }
317 #ifdef getc_unlocked
318 for (rval = nchild; rval--; ) /* avoid mutex overhead */
319 flockfile(kida[rval].infp);
320 #endif
321 return(0); /* return "false" in parent */
322 }
323
324
325 /* Close child processes */
326 void
327 end_children(int immed)
328 {
329 int i;
330
331 #ifdef SIGKILL /* error mode -- quick exit */
332 for (i = nchild*immed; i-- > 0; )
333 kill(kidpr[nchild].pid, SIGKILL);
334 #endif
335 if ((i = close_processes(kidpr, nchild)) > 0 && !immed) {
336 sprintf(errmsg, "rendering process returned bad status (%d)",
337 i);
338 error(WARNING, errmsg);
339 }
340 while (nchild-- > 0)
341 fclose(kida[nchild].infp);
342 }
343
344
345 /* Wait for the next available child, managing output queue simultaneously */
346 static int
347 next_child_nq(int flushing)
348 {
349 static struct timeval polling;
350 struct timeval *pmode;
351 fd_set readset, errset;
352 int i, n, nr, nqr;
353
354 if (!flushing) /* see if there's one free */
355 for (i = nchild; i--; )
356 if (!kida[i].nr)
357 return(i);
358
359 nqr = queue_ready(); /* choose blocking mode or polling */
360 if ((nqr > 0) & !flushing)
361 pmode = &polling;
362 else
363 pmode = NULL;
364 tryagain: /* catch up with output? */
365 if (pmode == &polling) {
366 if (nqr > nchild) /* don't get too far behind */
367 nqr -= output_catchup(nqr-nchild);
368 } else if (nqr > 0) /* clear output before blocking */
369 nqr -= output_catchup(0);
370 /* prepare select() call */
371 FD_ZERO(&readset); FD_ZERO(&errset);
372 n = nr = 0;
373 for (i = nchild; i--; ) {
374 if (kida[i].nr) {
375 FD_SET(kidpr[i].r, &readset);
376 ++nr;
377 }
378 FD_SET(kidpr[i].r, &errset);
379 if (kidpr[i].r >= n)
380 n = kidpr[i].r + 1;
381 }
382 if (!nr) /* nothing to wait for? */
383 return(-1);
384 if ((nr > 1) | (pmode == &polling)) {
385 errno = 0;
386 nr = select(n, &readset, NULL, &errset, pmode);
387 if (!nr) {
388 pmode = NULL; /* try again, blocking this time */
389 goto tryagain;
390 }
391 if (nr < 0)
392 error(SYSTEM, "select() error in next_child_nq()");
393 } else
394 FD_ZERO(&errset);
395 n = -1; /* read results from child(ren) */
396 for (i = nchild; i--; ) {
397 if (FD_ISSET(kidpr[i].r, &errset))
398 error(USER, "rendering process died");
399 if (FD_ISSET(kidpr[i].r, &readset))
400 queue_results(n = i);
401 }
402 return(n); /* first available child */
403 }
404
405
406 /* Run parental oversight loop */
407 void
408 parental_loop()
409 {
410 const int qlimit = (accumulate == 1) ? 1 : MAXIQ-1;
411 int ninq = 0;
412 FVECT orgdir[2*MAXIQ];
413 int i, n;
414 /* load rays from stdin & process */
415 #ifdef getc_unlocked
416 flockfile(stdin); /* avoid lock/unlock overhead */
417 #endif
418 while (getvec(orgdir[2*ninq]) == 0 && getvec(orgdir[2*ninq+1]) == 0) {
419 const int zero_ray = orgdir[2*ninq+1][0] == 0.0 &&
420 (orgdir[2*ninq+1][1] == 0.0) &
421 (orgdir[2*ninq+1][2] == 0.0);
422 ninq += !zero_ray;
423 /* Zero ray cannot go in input queue */
424 if (zero_ray ? ninq : ninq >= qlimit ||
425 lastray/accumulate != (lastray+ninq)/accumulate) {
426 i = next_child_nq(0); /* manages output */
427 n = ninq;
428 if (accumulate > 1) /* need terminator? */
429 memset(orgdir[2*n++], 0, sizeof(FVECT)*2);
430 n *= sizeof(FVECT)*2; /* send assignment */
431 if (writebuf(kidpr[i].w, (char *)orgdir, n) != n)
432 error(SYSTEM, "pipe write error");
433 kida[i].r1 = lastray+1;
434 lastray += kida[i].nr = ninq; /* mark as busy */
435 if (lastray < lastdone) { /* RNUMBER wrapped? */
436 while (next_child_nq(1) >= 0)
437 ;
438 lastray -= ninq;
439 lastdone = lastray %= accumulate;
440 }
441 ninq = 0;
442 }
443 if (zero_ray) { /* put bogus record? */
444 if ((yres <= 0) | (xres <= 1) &&
445 (lastray+1) % accumulate == 0) {
446 while (next_child_nq(1) >= 0)
447 ; /* clear the queue */
448 lastdone = lastray = accumulate-1;
449 waitflush = 1; /* flush next */
450 }
451 put_zero_record(++lastray);
452 }
453 if (!morays())
454 break; /* preemptive EOI */
455 }
456 while (next_child_nq(1) >= 0) /* empty results queue */
457 ;
458 if (account < accumulate) {
459 error(WARNING, "partial accumulation in final record");
460 free_binq(out_bq); /* XXX just ignore it */
461 out_bq = NULL;
462 }
463 free_binq(NULL); /* clean up */
464 lu_done(&ofiletab);
465 if (raysleft)
466 error(USER, "unexpected EOF on input");
467 }
468
469
470 /* Wait for the next available child by monitoring "to" pipes */
471 static int
472 next_child_ready()
473 {
474 fd_set writeset, errset;
475 int i, n;
476
477 for (i = nchild; i--; ) /* see if there's one free first */
478 if (!kida[i].nr)
479 return(i);
480 /* prepare select() call */
481 FD_ZERO(&writeset); FD_ZERO(&errset);
482 n = 0;
483 for (i = nchild; i--; ) {
484 FD_SET(kidpr[i].w, &writeset);
485 FD_SET(kidpr[i].r, &errset);
486 if (kidpr[i].w >= n)
487 n = kidpr[i].w + 1;
488 if (kidpr[i].r >= n)
489 n = kidpr[i].r + 1;
490 }
491 errno = 0;
492 n = select(n, NULL, &writeset, &errset, NULL);
493 if (n < 0)
494 error(SYSTEM, "select() error in next_child_ready()");
495 n = -1; /* identify waiting child */
496 for (i = nchild; i--; ) {
497 if (FD_ISSET(kidpr[i].r, &errset))
498 error(USER, "rendering process died");
499 if (FD_ISSET(kidpr[i].w, &writeset))
500 kida[n = i].nr = 0;
501 }
502 return(n); /* first available child */
503 }
504
505
506 /* Modified parental loop for full accumulation mode (-c 0) */
507 void
508 feeder_loop()
509 {
510 static int ignore_warning_given = 0;
511 int ninq = 0;
512 FVECT orgdir[2*MAXIQ];
513 int i, n;
514 /* load rays from stdin & process */
515 #ifdef getc_unlocked
516 flockfile(stdin); /* avoid lock/unlock overhead */
517 #endif
518 while (getvec(orgdir[2*ninq]) == 0 && getvec(orgdir[2*ninq+1]) == 0) {
519 if (orgdir[2*ninq+1][0] == 0.0 && /* asking for flush? */
520 (orgdir[2*ninq+1][1] == 0.0) &
521 (orgdir[2*ninq+1][2] == 0.0)) {
522 if (!ignore_warning_given++)
523 error(WARNING,
524 "dummy ray(s) ignored during accumulation\n");
525 continue;
526 }
527 if (++ninq >= MAXIQ) {
528 i = next_child_ready(); /* get eager child */
529 n = sizeof(FVECT)*2 * ninq; /* give assignment */
530 if (writebuf(kidpr[i].w, (char *)orgdir, n) != n)
531 error(SYSTEM, "pipe write error");
532 kida[i].r1 = lastray+1;
533 lastray += kida[i].nr = ninq;
534 if (lastray < lastdone) /* RNUMBER wrapped? */
535 lastdone = lastray = 0;
536 ninq = 0;
537 }
538 if (!morays())
539 break; /* preemptive EOI */
540 }
541 if (ninq) { /* polish off input */
542 i = next_child_ready();
543 n = sizeof(FVECT)*2 * ninq;
544 if (writebuf(kidpr[i].w, (char *)orgdir, n) != n)
545 error(SYSTEM, "pipe write error");
546 kida[i].r1 = lastray+1;
547 lastray += kida[i].nr = ninq;
548 ninq = 0;
549 }
550 memset(orgdir, 0, sizeof(FVECT)*2); /* get results */
551 for (i = nchild; i--; ) {
552 writebuf(kidpr[i].w, (char *)orgdir, sizeof(FVECT)*2);
553 queue_results(i);
554 }
555 if (recover) /* and from before? */
556 queue_modifiers();
557 end_children(0); /* free up file descriptors */
558 for (i = 0; i < nmods; i++)
559 mod_output(out_bq->mca[i]); /* output accumulated record */
560 end_record();
561 free_binq(out_bq); /* clean up */
562 out_bq = NULL;
563 free_binq(NULL);
564 lu_done(&ofiletab);
565 if (raysleft)
566 error(USER, "unexpected EOF on input");
567 }