ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/pmap.c
Revision: 2.16
Committed: Wed Nov 21 19:33:30 2018 UTC (5 years, 5 months ago) by rschregle
Content type: text/plain
Branch: MAIN
Changes since 2.15: +24 -14 lines
Log Message:
Fixed potential infinite loop in photonParticipate() if photon leaves scene

File Contents

# User Rev Content
1 greg 2.9 #ifndef lint
2 rschregle 2.16 static const char RCSid[] = "$Id: pmap.c,v 2.15 2018/06/07 19:26:04 rschregle Exp $";
3 greg 2.9 #endif
4 rschregle 2.11
5 rschregle 2.13
6 greg 2.1 /*
7 rschregle 2.11 ======================================================================
8 greg 2.1 Photon map main module
9    
10     Roland Schregle (roland.schregle@{hslu.ch, gmail.com})
11     (c) Fraunhofer Institute for Solar Energy Systems,
12 rschregle 2.4 (c) Lucerne University of Applied Sciences and Arts,
13 rschregle 2.11 supported by the Swiss National Science Foundation (SNSF, #147053)
14     ======================================================================
15 greg 2.1
16 rschregle 2.16 $Id: pmap.c,v 2.15 2018/06/07 19:26:04 rschregle Exp $
17 greg 2.1 */
18    
19    
20     #include "pmap.h"
21     #include "pmapmat.h"
22     #include "pmapsrc.h"
23     #include "pmaprand.h"
24     #include "pmapio.h"
25     #include "pmapbias.h"
26     #include "pmapdiag.h"
27     #include "otypes.h"
28     #include <time.h>
29 rschregle 2.13 #if NIX
30     #include <sys/stat.h>
31     #include <sys/mman.h>
32     #include <sys/wait.h>
33     #endif
34 greg 2.1
35    
36     void savePmaps (const PhotonMap **pmaps, int argc, char **argv)
37     {
38     unsigned t;
39    
40     for (t = 0; t < NUM_PMAP_TYPES; t++) {
41     if (pmaps [t])
42 greg 2.7 savePhotonMap(pmaps [t], pmaps [t] -> fileName, argc, argv);
43 greg 2.1 }
44     }
45    
46    
47    
48     static int photonParticipate (RAY *ray)
49     /* Trace photon through participating medium. Returns 1 if passed through,
50     or 0 if absorbed and $*%&ed. Analogon to rayparticipate(). */
51     {
52     int i;
53     RREAL cosTheta, cosPhi, du, dv;
54     const float cext = colorAvg(ray -> cext),
55 rschregle 2.16 albedo = colorAvg(ray -> albedo),
56     gecc2 = ray -> gecc * ray -> gecc;
57 greg 2.1 FVECT u, v;
58     COLOR cvext;
59    
60     /* Mean free distance until interaction with medium */
61     ray -> rmax = -log(pmapRandom(mediumState)) / cext;
62    
63     while (!localhit(ray, &thescene)) {
64 rschregle 2.16 if (!incube(&thescene, ray -> rop)) {
65     /* Terminate photon if it has leaked from the scene */
66     #ifdef DEBUG_PMAP
67     fprintf(stderr,
68     "Volume photon leaked from scene at [%.3f %.3f %.3f]\n",
69     ray -> rop [0], ray -> rop [1], ray -> rop [2]);
70     #endif
71     return 0;
72     }
73    
74 greg 2.1 setcolor(cvext, exp(-ray -> rmax * ray -> cext [0]),
75     exp(-ray -> rmax * ray -> cext [1]),
76     exp(-ray -> rmax * ray -> cext [2]));
77    
78     /* Modify ray color and normalise */
79     multcolor(ray -> rcol, cvext);
80     colorNorm(ray -> rcol);
81     VCOPY(ray -> rorg, ray -> rop);
82    
83 rschregle 2.15 #if 0
84 rschregle 2.11 if (albedo > FTINY && ray -> rlvl > 0)
85 rschregle 2.15 #else
86     /* Store volume photons unconditionally in mist to also account for
87     direct inscattering from sources */
88     if (albedo > FTINY)
89 rschregle 2.16 #endif
90 greg 2.1 /* Add to volume photon map */
91 rschregle 2.11 newPhoton(volumePmap, ray);
92 greg 2.1
93     /* Absorbed? */
94 rschregle 2.11 if (pmapRandom(rouletteState) > albedo)
95     return 0;
96 greg 2.1
97     /* Colour bleeding without attenuation (?) */
98     multcolor(ray -> rcol, ray -> albedo);
99     scalecolor(ray -> rcol, 1 / albedo);
100    
101     /* Scatter photon */
102 rschregle 2.16 cosTheta = ray -> gecc <= FTINY
103     ? 2 * pmapRandom(scatterState) - 1
104     : 0.5 * (1 + gecc2 -
105     (1 - gecc2) / (1 - ray -> gecc + 2 * ray -> gecc *
106     pmapRandom(scatterState))) / ray -> gecc;
107 greg 2.1
108     cosPhi = cos(2 * PI * pmapRandom(scatterState));
109     du = dv = sqrt(1 - cosTheta * cosTheta); /* sin(theta) */
110     du *= cosPhi;
111     dv *= sqrt(1 - cosPhi * cosPhi); /* sin(phi) */
112    
113     /* Get axes u & v perpendicular to photon direction */
114     i = 0;
115     do {
116     v [0] = v [1] = v [2] = 0;
117     v [i++] = 1;
118     fcross(u, v, ray -> rdir);
119     } while (normalize(u) < FTINY);
120     fcross(v, ray -> rdir, u);
121    
122     for (i = 0; i < 3; i++)
123     ray -> rdir [i] = du * u [i] + dv * v [i] +
124     cosTheta * ray -> rdir [i];
125     ray -> rlvl++;
126     ray -> rmax = -log(pmapRandom(mediumState)) / cext;
127     }
128 rschregle 2.16
129     /* Passed through medium until intersecting local object */
130 greg 2.1 setcolor(cvext, exp(-ray -> rot * ray -> cext [0]),
131     exp(-ray -> rot * ray -> cext [1]),
132     exp(-ray -> rot * ray -> cext [2]));
133    
134     /* Modify ray color and normalise */
135     multcolor(ray -> rcol, cvext);
136 rschregle 2.16 colorNorm(ray -> rcol);
137    
138 greg 2.1 return 1;
139     }
140    
141    
142    
143     void tracePhoton (RAY *ray)
144     /* Follow photon as it bounces around... */
145     {
146     long mod;
147 rschregle 2.13 OBJREC *mat, *port = NULL;
148    
149     if (!ray -> parent) {
150     /* !!! PHOTON PORT REJECTION SAMPLING HACK: get photon port for
151     * !!! primary ray from ray -> ro, then reset the latter to NULL so
152     * !!! as not to interfere with localhit() */
153     port = ray -> ro;
154     ray -> ro = NULL;
155     }
156 greg 2.1
157     if (ray -> rlvl > photonMaxBounce) {
158 rschregle 2.5 #ifdef PMAP_RUNAWAY_WARN
159 greg 2.1 error(WARNING, "runaway photon!");
160 rschregle 2.5 #endif
161 greg 2.1 return;
162     }
163 rschregle 2.5
164 greg 2.1 if (colorAvg(ray -> cext) > FTINY && !photonParticipate(ray))
165     return;
166 rschregle 2.13
167 greg 2.1 if (localhit(ray, &thescene)) {
168     mod = ray -> ro -> omod;
169 rschregle 2.13
170     if (port && ray -> ro != port) {
171     /* !!! PHOTON PORT REJECTION SAMPLING HACK !!!
172     * Terminate photon if emitted from port without intersecting it;
173     * this can happen when the port's partitions extend beyond its
174     * actual geometry, e.g. with polygons. Since the total flux
175     * relayed by the port is based on the (in this case) larger
176     * partition area, it is overestimated; terminating these photons
177     * constitutes rejection sampling and thereby compensates any bias
178     * incurred by the overestimated flux. */
179     #ifdef PMAP_PORTREJECT_WARN
180     sprintf(errmsg, "photon outside port %s", ray -> ro -> oname);
181     error(WARNING, errmsg);
182     #endif
183     return;
184     }
185    
186 greg 2.1 if ((ray -> clipset && inset(ray -> clipset, mod)) || mod == OVOID) {
187     /* Transfer ray if modifier is VOID or clipped within antimatta */
188     RAY tray;
189     photonRay(ray, &tray, PMAP_XFER, NULL);
190     tracePhoton(&tray);
191     }
192     else {
193     /* Scatter for modifier material */
194     mat = objptr(mod);
195     photonScatter [mat -> otype] (mat, ray);
196     }
197     }
198     }
199    
200    
201    
202     static void preComputeGlobal (PhotonMap *pmap)
203 rschregle 2.11 /* Precompute irradiance from global photons for final gathering for
204     a random subset of finalGather * pmap -> numPhotons photons, and builds
205     the photon map, discarding the original photons. */
206     /* !!! NOTE: PRECOMPUTATION WITH OOC CURRENTLY WITHOUT CACHE !!! */
207     {
208     unsigned long i, numPreComp;
209     unsigned j;
210     PhotonIdx pIdx;
211     Photon photon;
212     RAY ray;
213     PhotonMap nuPmap;
214 greg 2.1
215 rschregle 2.11 repComplete = numPreComp = finalGather * pmap -> numPhotons;
216 greg 2.1
217 rschregle 2.13 if (verbose) {
218     sprintf(errmsg,
219     "\nPrecomputing irradiance for %ld global photons\n",
220 rschregle 2.11 numPreComp);
221 greg 2.1 eputs(errmsg);
222 rschregle 2.13 #if NIX
223 greg 2.1 fflush(stderr);
224 rschregle 2.13 #endif
225 greg 2.1 }
226    
227 rschregle 2.11 /* Copy photon map for precomputed photons */
228     memcpy(&nuPmap, pmap, sizeof(PhotonMap));
229    
230     /* Zero counters, init new heap and extents */
231     nuPmap.numPhotons = 0;
232     initPhotonHeap(&nuPmap);
233    
234     for (j = 0; j < 3; j++) {
235     nuPmap.minPos [j] = FHUGE;
236     nuPmap.maxPos [j] = -FHUGE;
237 greg 2.1 }
238 rschregle 2.11
239 greg 2.1 /* Record start time, baby */
240     repStartTime = time(NULL);
241 rschregle 2.11 #ifdef SIGCONT
242     signal(SIGCONT, pmapPreCompReport);
243     #endif
244 greg 2.1 repProgress = 0;
245    
246 rschregle 2.11 photonRay(NULL, &ray, PRIMARY, NULL);
247     ray.ro = NULL;
248    
249     for (i = 0; i < numPreComp; i++) {
250     /* Get random photon from stratified distribution in source heap to
251 rschregle 2.13 * avoid duplicates and clustering */
252 rschregle 2.11 pIdx = firstPhoton(pmap) +
253     (unsigned long)((i + pmapRandom(pmap -> randState)) /
254     finalGather);
255     getPhoton(pmap, pIdx, &photon);
256    
257     /* Init dummy photon ray with intersection at photon position */
258     VCOPY(ray.rop, photon.pos);
259     for (j = 0; j < 3; j++)
260     ray.ron [j] = photon.norm [j] / 127.0;
261    
262     /* Get density estimate at photon position */
263     photonDensity(pmap, &ray, ray.rcol);
264    
265     /* Append photon to new heap from ray */
266     newPhoton(&nuPmap, &ray);
267 greg 2.1
268 rschregle 2.11 /* Update progress */
269 greg 2.1 repProgress++;
270    
271     if (photonRepTime > 0 && time(NULL) >= repLastTime + photonRepTime)
272     pmapPreCompReport();
273 rschregle 2.11 #ifdef SIGCONT
274     else signal(SIGCONT, pmapPreCompReport);
275     #endif
276 greg 2.1 }
277    
278 rschregle 2.11 /* Flush heap */
279     flushPhotonHeap(&nuPmap);
280    
281     #ifdef SIGCONT
282     signal(SIGCONT, SIG_DFL);
283     #endif
284    
285     /* Trash original pmap, replace with precomputed one */
286     deletePhotons(pmap);
287     memcpy(pmap, &nuPmap, sizeof(PhotonMap));
288 greg 2.1
289 rschregle 2.13 if (verbose) {
290     eputs("\nRebuilding precomputed photon map\n");
291     #if NIX
292 greg 2.1 fflush(stderr);
293 rschregle 2.13 #endif
294 greg 2.1 }
295 rschregle 2.11
296     /* Rebuild underlying data structure, destroying heap */
297     buildPhotonMap(pmap, NULL, NULL, 1);
298 greg 2.1 }
299    
300    
301    
302 rschregle 2.11 typedef struct {
303     unsigned long numPhotons [NUM_PMAP_TYPES],
304     numEmitted, numComplete;
305     } PhotonCnt;
306    
307    
308    
309     void distribPhotons (PhotonMap **pmaps, unsigned numProc)
310     {
311     EmissionMap emap;
312 rschregle 2.13 char errmsg2 [128], shmFname [PMAP_TMPFNLEN];
313 rschregle 2.11 unsigned t, srcIdx, proc;
314     double totalFlux = 0;
315     int shmFile, stat, pid;
316     PhotonMap *pm;
317     PhotonCnt *photonCnt;
318 greg 2.1
319 rschregle 2.8 for (t = 0; t < NUM_PMAP_TYPES && !pmaps [t]; t++);
320 rschregle 2.11
321 greg 2.1 if (t >= NUM_PMAP_TYPES)
322 rschregle 2.11 error(USER, "no photon maps defined in distribPhotons");
323 greg 2.1
324     if (!nsources)
325 rschregle 2.11 error(USER, "no light sources in distribPhotons");
326 greg 2.1
327     /* ===================================================================
328     * INITIALISATION - Set up emission and scattering funcs
329     * =================================================================== */
330     emap.samples = NULL;
331     emap.maxPartitions = MAXSPART;
332     emap.partitions = (unsigned char*)malloc(emap.maxPartitions >> 1);
333     if (!emap.partitions)
334 rschregle 2.11 error(INTERNAL, "can't allocate source partitions in distribPhotons");
335 greg 2.1
336     /* Initialise all defined photon maps */
337     for (t = 0; t < NUM_PMAP_TYPES; t++)
338 rschregle 2.11 if (pmaps [t]) {
339     initPhotonMap(pmaps [t], t);
340     /* Open photon heapfile */
341     initPhotonHeap(pmaps [t]);
342     /* Per-subprocess target count */
343     pmaps [t] -> distribTarget /= numProc;
344 rschregle 2.13
345     if (!pmaps [t] -> distribTarget)
346     error(INTERNAL, "no photons to distribute in distribPhotons");
347 rschregle 2.11 }
348 greg 2.1
349     initPhotonEmissionFuncs();
350     initPhotonScatterFuncs();
351    
352 rschregle 2.14 /* Get photon ports from modifier list */
353     getPhotonPorts(photonPortList);
354 greg 2.1
355     /* Get photon sensor modifiers */
356     getPhotonSensors(photonSensorList);
357    
358 rschregle 2.13 #if NIX
359 rschregle 2.11 /* Set up shared mem for photon counters (zeroed by ftruncate) */
360 rschregle 2.13 strcpy(shmFname, PMAP_TMPFNAME);
361 rschregle 2.11 shmFile = mkstemp(shmFname);
362    
363 rschregle 2.13 if (shmFile < 0 || ftruncate(shmFile, sizeof(*photonCnt)) < 0)
364     error(SYSTEM, "failed shared mem init in distribPhotons");
365 rschregle 2.11
366     photonCnt = mmap(NULL, sizeof(*photonCnt), PROT_READ | PROT_WRITE,
367     MAP_SHARED, shmFile, 0);
368    
369     if (photonCnt == MAP_FAILED)
370 rschregle 2.13 error(SYSTEM, "failed mapping shared memory in distribPhotons");
371     #else
372     /* Allocate photon counters statically on Windoze */
373     if (!(photonCnt = malloc(sizeof(PhotonCnt))))
374     error(SYSTEM, "failed trivial malloc in distribPhotons");
375     photonCnt -> numEmitted = photonCnt -> numComplete = 0;
376     #endif /* NIX */
377    
378     if (verbose) {
379     sprintf(errmsg, "\nIntegrating flux from %d sources", nsources);
380    
381     if (photonPorts) {
382     sprintf(errmsg2, " via %d ports", numPhotonPorts);
383     strcat(errmsg, errmsg2);
384     }
385    
386     strcat(errmsg, "\n");
387     eputs(errmsg);
388     }
389 greg 2.1
390     /* ===================================================================
391     * FLUX INTEGRATION - Get total photon flux from light sources
392     * =================================================================== */
393 rschregle 2.11 for (srcIdx = 0; srcIdx < nsources; srcIdx++) {
394 greg 2.1 unsigned portCnt = 0;
395     emap.src = source + srcIdx;
396    
397 rschregle 2.11 do { /* Need at least one iteration if no ports! */
398 greg 2.1 emap.port = emap.src -> sflags & SDISTANT ? photonPorts + portCnt
399     : NULL;
400     photonPartition [emap.src -> so -> otype] (&emap);
401    
402 rschregle 2.13 if (verbose) {
403     sprintf(errmsg, "\tIntegrating flux from source %s ",
404 greg 2.1 source [srcIdx].so -> oname);
405 rschregle 2.13
406 greg 2.1 if (emap.port) {
407     sprintf(errmsg2, "via port %s ",
408     photonPorts [portCnt].so -> oname);
409     strcat(errmsg, errmsg2);
410     }
411 rschregle 2.13
412     sprintf(errmsg2, "(%lu partitions)\n", emap.numPartitions);
413 greg 2.1 strcat(errmsg, errmsg2);
414     eputs(errmsg);
415 rschregle 2.13 #if NIX
416 greg 2.1 fflush(stderr);
417 rschregle 2.13 #endif
418 greg 2.1 }
419    
420     for (emap.partitionCnt = 0; emap.partitionCnt < emap.numPartitions;
421     emap.partitionCnt++) {
422     initPhotonEmission(&emap, pdfSamples);
423     totalFlux += colorAvg(emap.partFlux);
424     }
425    
426     portCnt++;
427     } while (portCnt < numPhotonPorts);
428     }
429    
430     if (totalFlux < FTINY)
431     error(USER, "zero flux from light sources");
432 rschregle 2.13
433     /* Record start time for progress reports */
434     repStartTime = time(NULL);
435    
436     if (verbose) {
437     sprintf(errmsg, "\nPhoton distribution @ %d procs\n", numProc);
438     eputs(errmsg);
439     }
440 greg 2.1
441 rschregle 2.11 /* MAIN LOOP */
442     for (proc = 0; proc < numProc; proc++) {
443 rschregle 2.13 #if NIX
444 rschregle 2.11 if (!(pid = fork())) {
445 rschregle 2.13 /* SUBPROCESS ENTERS HERE; open and mmapped files inherited */
446     #else
447     if (1) {
448     /* No subprocess under Windoze */
449     #endif
450     /* Local photon counters for this subprocess */
451 rschregle 2.11 unsigned passCnt = 0, prePassCnt = 0;
452     unsigned long lastNumPhotons [NUM_PMAP_TYPES];
453     unsigned long localNumEmitted = 0; /* Num photons emitted by this
454     subprocess alone */
455 greg 2.1
456 rschregle 2.11 /* Seed RNGs from PID for decorellated photon distribution */
457     pmapSeed(randSeed + proc, partState);
458 rschregle 2.13 pmapSeed(randSeed + (proc + 1) % numProc, emitState);
459     pmapSeed(randSeed + (proc + 2) % numProc, cntState);
460     pmapSeed(randSeed + (proc + 3) % numProc, mediumState);
461     pmapSeed(randSeed + (proc + 4) % numProc, scatterState);
462     pmapSeed(randSeed + (proc + 5) % numProc, rouletteState);
463 rschregle 2.15
464     #ifdef DEBUG_PMAP
465     /* Output child process PID after random delay to prevent corrupted
466     * console output due to race condition */
467     usleep(1e6 * pmapRandom(rouletteState));
468     fprintf(stderr, "Proc %d: PID = %d "
469     "(waiting 10 sec to attach debugger...)\n",
470     proc, getpid());
471     /* Allow time for debugger to attach to child process */
472     sleep(10);
473     #endif
474    
475 greg 2.1 for (t = 0; t < NUM_PMAP_TYPES; t++)
476 rschregle 2.11 lastNumPhotons [t] = 0;
477    
478     /* =============================================================
479     * 2-PASS PHOTON DISTRIBUTION
480     * Pass 1 (pre): emit fraction of target photon count
481     * Pass 2 (main): based on outcome of pass 1, estimate remaining
482     * number of photons to emit to approximate target
483     * count
484     * ============================================================= */
485 greg 2.1 do {
486 rschregle 2.11 double numEmit;
487 greg 2.1
488 rschregle 2.11 if (!passCnt) {
489     /* INIT PASS 1 */
490     /* Skip if no photons contributed after sufficient
491     * iterations; make it clear to user which photon maps are
492     * missing so (s)he can check geometry and materials */
493     if (++prePassCnt > maxPreDistrib) {
494 rschregle 2.13 sprintf(errmsg, "proc %d: too many prepasses", proc);
495 rschregle 2.11
496     for (t = 0; t < NUM_PMAP_TYPES; t++)
497     if (pmaps [t] && !pmaps [t] -> numPhotons) {
498     sprintf(errmsg2, ", no %s photons stored",
499     pmapName [t]);
500     strcat(errmsg, errmsg2);
501     }
502    
503     error(USER, errmsg);
504     break;
505 greg 2.1 }
506 rschregle 2.11
507     /* Num to emit is fraction of minimum target count */
508     numEmit = FHUGE;
509 greg 2.1
510 rschregle 2.11 for (t = 0; t < NUM_PMAP_TYPES; t++)
511     if (pmaps [t])
512     numEmit = min(pmaps [t] -> distribTarget, numEmit);
513    
514     numEmit *= preDistrib;
515 greg 2.1 }
516 rschregle 2.11 else {
517     /* INIT PASS 2 */
518     /* Based on the outcome of the predistribution we can now
519     * estimate how many more photons we have to emit for each
520     * photon map to meet its respective target count. This
521     * value is clamped to 0 in case the target has already been
522     * exceeded in the pass 1. */
523     double maxDistribRatio = 0;
524    
525     /* Set the distribution ratio for each map; this indicates
526     * how many photons of each respective type are stored per
527     * emitted photon, and is used as probability for storing a
528     * photon by newPhoton(). Since this biases the photon
529     * density, newPhoton() promotes the flux of stored photons
530     * to compensate. */
531     for (t = 0; t < NUM_PMAP_TYPES; t++)
532     if ((pm = pmaps [t])) {
533     pm -> distribRatio = (double)pm -> distribTarget /
534     pm -> numPhotons - 1;
535    
536     /* Check if photon map "overflowed", i.e. exceeded its
537     * target count in the prepass; correcting the photon
538     * flux via the distribution ratio is no longer
539     * possible, as no more photons of this type will be
540     * stored, so notify the user rather than deliver
541     * incorrect results. In future we should handle this
542     * more intelligently by using the photonFlux in each
543     * photon map to individually correct the flux after
544     * distribution. */
545     if (pm -> distribRatio <= FTINY) {
546     sprintf(errmsg, "%s photon map overflow in "
547     "prepass, reduce -apD", pmapName [t]);
548     error(INTERNAL, errmsg);
549     }
550    
551     maxDistribRatio = max(pm -> distribRatio,
552     maxDistribRatio);
553     }
554 greg 2.1
555 rschregle 2.11 /* Normalise distribution ratios and calculate number of
556     * photons to emit in main pass */
557     for (t = 0; t < NUM_PMAP_TYPES; t++)
558     if ((pm = pmaps [t]))
559     pm -> distribRatio /= maxDistribRatio;
560    
561     if ((numEmit = localNumEmitted * maxDistribRatio) < FTINY)
562     /* No photons left to distribute in main pass */
563     break;
564     }
565    
566 rschregle 2.13 /* Update shared completion counter for progreport by parent */
567 rschregle 2.11 photonCnt -> numComplete += numEmit;
568    
569     /* PHOTON DISTRIBUTION LOOP */
570     for (srcIdx = 0; srcIdx < nsources; srcIdx++) {
571     unsigned portCnt = 0;
572     emap.src = source + srcIdx;
573    
574     do { /* Need at least one iteration if no ports! */
575     emap.port = emap.src -> sflags & SDISTANT
576     ? photonPorts + portCnt : NULL;
577     photonPartition [emap.src -> so -> otype] (&emap);
578    
579 rschregle 2.13 if (verbose && !proc) {
580     /* Output from subproc 0 only to avoid race condition
581     * on console I/O */
582 rschregle 2.11 if (!passCnt)
583 rschregle 2.13 sprintf(errmsg, "\tPREPASS %d on source %s ",
584 rschregle 2.11 prePassCnt, source [srcIdx].so -> oname);
585     else
586 rschregle 2.13 sprintf(errmsg, "\tMAIN PASS on source %s ",
587 rschregle 2.11 source [srcIdx].so -> oname);
588 rschregle 2.13
589 rschregle 2.11 if (emap.port) {
590     sprintf(errmsg2, "via port %s ",
591     photonPorts [portCnt].so -> oname);
592     strcat(errmsg, errmsg2);
593     }
594 rschregle 2.13
595 rschregle 2.11 sprintf(errmsg2, "(%lu partitions)\n",
596     emap.numPartitions);
597     strcat(errmsg, errmsg2);
598     eputs(errmsg);
599 rschregle 2.13 #if NIX
600 rschregle 2.11 fflush(stderr);
601 rschregle 2.13 #endif
602 rschregle 2.11 }
603 greg 2.1
604 rschregle 2.11 for (emap.partitionCnt = 0; emap.partitionCnt < emap.numPartitions;
605     emap.partitionCnt++) {
606     double partNumEmit;
607     unsigned long partEmitCnt;
608    
609     /* Get photon origin within current source partishunn
610     * and build emission map */
611     photonOrigin [emap.src -> so -> otype] (&emap);
612     initPhotonEmission(&emap, pdfSamples);
613    
614     /* Number of photons to emit from ziss partishunn --
615     * proportional to flux; photon ray weight and scalar
616 rschregle 2.13 * flux are uniform (latter only varying in RGB). */
617 rschregle 2.11 partNumEmit = numEmit * colorAvg(emap.partFlux) /
618     totalFlux;
619     partEmitCnt = (unsigned long)partNumEmit;
620    
621     /* Probabilistically account for fractional photons */
622     if (pmapRandom(cntState) < partNumEmit - partEmitCnt)
623     partEmitCnt++;
624    
625     /* Update local and shared (global) emission counter */
626     photonCnt -> numEmitted += partEmitCnt;
627     localNumEmitted += partEmitCnt;
628    
629     /* Integer counter avoids FP rounding errors during
630     * iteration */
631     while (partEmitCnt--) {
632     RAY photonRay;
633    
634     /* Emit photon based on PDF and trace through scene
635     * until absorbed/leaked */
636     emitPhoton(&emap, &photonRay);
637 rschregle 2.13 #if 1
638     if (emap.port)
639     /* !!! PHOTON PORT REJECTION SAMPLING HACK: set
640     * !!! photon port as fake hit object for
641     * !!! primary ray to check for intersection in
642     * !!! tracePhoton() */
643     photonRay.ro = emap.port -> so;
644     #endif
645 rschregle 2.11 tracePhoton(&photonRay);
646     }
647 rschregle 2.13
648 rschregle 2.11 /* Update shared global photon count for each pmap */
649     for (t = 0; t < NUM_PMAP_TYPES; t++)
650     if (pmaps [t]) {
651     photonCnt -> numPhotons [t] +=
652     pmaps [t] -> numPhotons - lastNumPhotons [t];
653     lastNumPhotons [t] = pmaps [t] -> numPhotons;
654     }
655 rschregle 2.13 #if !NIX
656     /* Synchronous progress report on Windoze */
657     if (!proc && photonRepTime > 0 &&
658     time(NULL) >= repLastTime + photonRepTime) {
659     repEmitted = repProgress = photonCnt -> numEmitted;
660     repComplete = photonCnt -> numComplete;
661     pmapDistribReport();
662     }
663     #endif
664 rschregle 2.11 }
665 greg 2.1
666 rschregle 2.11 portCnt++;
667     } while (portCnt < numPhotonPorts);
668     }
669    
670     for (t = 0; t < NUM_PMAP_TYPES; t++)
671     if (pmaps [t] && !pmaps [t] -> numPhotons) {
672     /* Double preDistrib in case a photon map is empty and
673     * redo pass 1 --> possibility of infinite loop for
674     * pathological scenes (e.g. absorbing materials) */
675     preDistrib *= 2;
676     break;
677 greg 2.1 }
678 rschregle 2.11
679 rschregle 2.13 if (t >= NUM_PMAP_TYPES)
680 rschregle 2.11 /* No empty photon maps found; now do pass 2 */
681     passCnt++;
682     } while (passCnt < 2);
683    
684 rschregle 2.13 /* Flush heap buffa for every pmap one final time;
685     * avoids potential data corruption! */
686 rschregle 2.11 for (t = 0; t < NUM_PMAP_TYPES; t++)
687     if (pmaps [t]) {
688     flushPhotonHeap(pmaps [t]);
689 rschregle 2.13 /* Heap file closed automatically on exit
690     fclose(pmaps [t] -> heap); */
691 rschregle 2.11 #ifdef DEBUG_PMAP
692 rschregle 2.13 sprintf(errmsg, "Proc %d: total %ld photons\n", proc,
693 rschregle 2.11 pmaps [t] -> numPhotons);
694     eputs(errmsg);
695     #endif
696     }
697 rschregle 2.13 #if NIX
698     /* Terminate subprocess */
699 rschregle 2.11 exit(0);
700 rschregle 2.13 #endif
701 greg 2.1 }
702 rschregle 2.11 else if (pid < 0)
703     error(SYSTEM, "failed to fork subprocess in distribPhotons");
704     }
705    
706 rschregle 2.13 #if NIX
707 rschregle 2.11 /* PARENT PROCESS CONTINUES HERE */
708     #ifdef SIGCONT
709 rschregle 2.13 /* Enable progress report signal handler */
710 rschregle 2.11 signal(SIGCONT, pmapDistribReport);
711 rschregle 2.13 #endif
712     /* Wait for subprocesses complete while reporting progress */
713 rschregle 2.11 proc = numProc;
714     while (proc) {
715     while (waitpid(-1, &stat, WNOHANG) > 0) {
716     /* Subprocess exited; check status */
717     if (!WIFEXITED(stat) || WEXITSTATUS(stat))
718     error(USER, "failed photon distribution");
719    
720     --proc;
721     }
722    
723     /* Nod off for a bit and update progress */
724     sleep(1);
725 rschregle 2.13
726     /* Asynchronous progress report from shared subprocess counters */
727 rschregle 2.11 repEmitted = repProgress = photonCnt -> numEmitted;
728 rschregle 2.13 repComplete = photonCnt -> numComplete;
729 rschregle 2.11
730 rschregle 2.13 repProgress = repComplete = 0;
731 greg 2.1 for (t = 0; t < NUM_PMAP_TYPES; t++)
732 rschregle 2.11 if ((pm = pmaps [t])) {
733     /* Get global photon count from shmem updated by subprocs */
734 rschregle 2.13 repProgress += pm -> numPhotons = photonCnt -> numPhotons [t];
735     repComplete += pm -> distribTarget;
736 greg 2.1 }
737 rschregle 2.13 repComplete *= numProc;
738 rschregle 2.11
739     if (photonRepTime > 0 && time(NULL) >= repLastTime + photonRepTime)
740     pmapDistribReport();
741     #ifdef SIGCONT
742     else signal(SIGCONT, pmapDistribReport);
743     #endif
744     }
745 rschregle 2.13 #endif /* NIX */
746 greg 2.1
747     /* ===================================================================
748 rschregle 2.11 * POST-DISTRIBUTION - Set photon flux and build data struct for photon
749     * storage, etc.
750 greg 2.1 * =================================================================== */
751 rschregle 2.11 #ifdef SIGCONT
752 rschregle 2.13 /* Reset signal handler */
753 rschregle 2.11 signal(SIGCONT, SIG_DFL);
754     #endif
755 greg 2.1 free(emap.samples);
756    
757 rschregle 2.13 /* Set photon flux */
758 rschregle 2.11 totalFlux /= photonCnt -> numEmitted;
759 rschregle 2.13 #if NIX
760 rschregle 2.11 /* Photon counters no longer needed, unmap shared memory */
761     munmap(photonCnt, sizeof(*photonCnt));
762     close(shmFile);
763 rschregle 2.13 unlink(shmFname);
764 rschregle 2.11 #else
765 rschregle 2.13 free(photonCnt);
766 rschregle 2.11 #endif
767 rschregle 2.13 if (verbose)
768     eputs("\n");
769    
770 greg 2.1 for (t = 0; t < NUM_PMAP_TYPES; t++)
771 rschregle 2.8 if (pmaps [t]) {
772 rschregle 2.13 if (verbose) {
773     sprintf(errmsg, "Building %s photon map\n", pmapName [t]);
774 greg 2.1 eputs(errmsg);
775 rschregle 2.13 #if NIX
776 greg 2.1 fflush(stderr);
777 rschregle 2.13 #endif
778 greg 2.1 }
779 rschregle 2.11
780     /* Build underlying data structure; heap is destroyed */
781     buildPhotonMap(pmaps [t], &totalFlux, NULL, numProc);
782 greg 2.1 }
783 rschregle 2.13
784 greg 2.1 /* Precompute photon irradiance if necessary */
785 rschregle 2.13 if (preCompPmap) {
786     if (verbose)
787     eputs("\n");
788 greg 2.1 preComputeGlobal(preCompPmap);
789 rschregle 2.13 }
790    
791     if (verbose)
792     eputs("\n");
793 greg 2.1 }