| 1 | #ifndef lint | 
| 2 | static const char RCSid[] = "$Id: pmapcontrib.c,v 2.19 2018/11/08 00:54:07 greg Exp $"; | 
| 3 | #endif | 
| 4 |  | 
| 5 | /* | 
| 6 | ====================================================================== | 
| 7 | Photon map for light source contributions | 
| 8 |  | 
| 9 | Roland Schregle (roland.schregle@{hslu.ch, gmail.com}) | 
| 10 | (c) Lucerne University of Applied Sciences and Arts, | 
| 11 | supported by the Swiss National Science Foundation (SNSF, #147053) | 
| 12 | ====================================================================== | 
| 13 |  | 
| 14 | $Id: pmapcontrib.c,v 2.19 2018/11/08 00:54:07 greg Exp $ | 
| 15 | */ | 
| 16 |  | 
| 17 |  | 
| 18 | #include "pmapcontrib.h" | 
| 19 | #include "pmapmat.h" | 
| 20 | #include "pmapsrc.h" | 
| 21 | #include "pmaprand.h" | 
| 22 | #include "pmapio.h" | 
| 23 | #include "pmapdiag.h" | 
| 24 | #include "rcontrib.h" | 
| 25 | #include "otypes.h" | 
| 26 | #include "otspecial.h" | 
| 27 | #if NIX | 
| 28 | #include <sys/mman.h> | 
| 29 | #include <sys/wait.h> | 
| 30 | #endif | 
| 31 |  | 
| 32 |  | 
| 33 | static PhotonPrimaryIdx newPhotonPrimary (PhotonMap *pmap, | 
| 34 | const RAY *primRay, | 
| 35 | FILE *primHeap) | 
| 36 | /* Add primary ray for emitted photon and save light source index, origin on | 
| 37 | * source, and emitted direction; used by contrib photons. The current | 
| 38 | * primary is stored in pmap -> lastPrimary.  If the previous primary | 
| 39 | * contributed photons (has srcIdx >= 0), it's appended to primHeap.  If | 
| 40 | * primRay == NULL, the current primary is still flushed, but no new primary | 
| 41 | * is set.  Returns updated primary counter pmap -> numPrimary.  */ | 
| 42 | { | 
| 43 | if (!pmap || !primHeap) | 
| 44 | return 0; | 
| 45 |  | 
| 46 | /* Check if last primary ray has spawned photons (srcIdx >= 0, see | 
| 47 | * newPhoton()), in which case we save it to the primary heap file | 
| 48 | * before clobbering it */ | 
| 49 | if (pmap -> lastPrimary.srcIdx >= 0) { | 
| 50 | if (!fwrite(&pmap -> lastPrimary, sizeof(PhotonPrimary), 1, primHeap)) | 
| 51 | error(SYSTEM, "failed writing photon primary in newPhotonPrimary"); | 
| 52 |  | 
| 53 | pmap -> numPrimary++; | 
| 54 | if (pmap -> numPrimary > PMAP_MAXPRIMARY) | 
| 55 | error(INTERNAL, "photon primary overflow in newPhotonPrimary"); | 
| 56 | } | 
| 57 |  | 
| 58 | /* Mark unused with negative source index until path spawns a photon (see | 
| 59 | * newPhoton()) */ | 
| 60 | pmap -> lastPrimary.srcIdx = -1; | 
| 61 |  | 
| 62 | if (primRay) { | 
| 63 | FVECT dvec; | 
| 64 |  | 
| 65 | #ifdef PMAP_PRIMARYDIR | 
| 66 | /* Reverse incident direction to point to light source */ | 
| 67 | dvec [0] = -primRay -> rdir [0]; | 
| 68 | dvec [1] = -primRay -> rdir [1]; | 
| 69 | dvec [2] = -primRay -> rdir [2]; | 
| 70 | pmap -> lastPrimary.dir = encodedir(dvec); | 
| 71 | #endif | 
| 72 | #ifdef PMAP_PRIMARYPOS | 
| 73 | VCOPY(pmap -> lastPrimary.pos, primRay -> rop); | 
| 74 | #endif | 
| 75 | } | 
| 76 |  | 
| 77 | return pmap -> numPrimary; | 
| 78 | } | 
| 79 |  | 
| 80 |  | 
| 81 |  | 
| 82 | #ifdef DEBUG_PMAP | 
| 83 | static int checkPrimaryHeap (FILE *file) | 
| 84 | /* Check heap for ordered primaries */ | 
| 85 | { | 
| 86 | Photon   p, lastp; | 
| 87 | int      i, dup; | 
| 88 |  | 
| 89 | rewind(file); | 
| 90 | memset(&lastp, 0, sizeof(lastp)); | 
| 91 |  | 
| 92 | while (fread(&p, sizeof(p), 1, file)) { | 
| 93 | dup = 1; | 
| 94 |  | 
| 95 | for (i = 0; i <= 2; i++) { | 
| 96 | if (p.pos [i] < thescene.cuorg [i] || | 
| 97 | p.pos [i] > thescene.cuorg [i] + thescene.cusize) { | 
| 98 |  | 
| 99 | sprintf(errmsg, "corrupt photon in heap at [%f, %f, %f]\n", | 
| 100 | p.pos [0], p.pos [1], p.pos [2]); | 
| 101 | error(WARNING, errmsg); | 
| 102 | } | 
| 103 |  | 
| 104 | dup &= p.pos [i] == lastp.pos [i]; | 
| 105 | } | 
| 106 |  | 
| 107 | if (dup) { | 
| 108 | sprintf(errmsg, | 
| 109 | "consecutive duplicate photon in heap at [%f, %f, %f]\n", | 
| 110 | p.pos [0], p.pos [1], p.pos [2]); | 
| 111 | error(WARNING, errmsg); | 
| 112 | } | 
| 113 | } | 
| 114 |  | 
| 115 | return 0; | 
| 116 | } | 
| 117 | #endif | 
| 118 |  | 
| 119 |  | 
| 120 |  | 
| 121 | static PhotonPrimaryIdx buildPrimaries (PhotonMap *pmap, FILE **primaryHeap, | 
| 122 | char **primaryHeapFname, | 
| 123 | PhotonPrimaryIdx *primaryOfs, | 
| 124 | unsigned numHeaps) | 
| 125 | /* Consolidate per-subprocess photon primary heaps into the primary array | 
| 126 | * pmap -> primaries. Returns offset for primary index linearisation in | 
| 127 | * numPrimary. The heap files in primaryHeap are closed on return. */ | 
| 128 | { | 
| 129 | PhotonPrimaryIdx  heapLen; | 
| 130 | unsigned          heap; | 
| 131 |  | 
| 132 | if (!pmap || !primaryHeap || !primaryOfs || !numHeaps) | 
| 133 | return 0; | 
| 134 |  | 
| 135 | pmap -> numPrimary = 0; | 
| 136 |  | 
| 137 | for (heap = 0; heap < numHeaps; heap++) { | 
| 138 | primaryOfs [heap] = pmap -> numPrimary; | 
| 139 |  | 
| 140 | if (fseek(primaryHeap [heap], 0, SEEK_END) < 0) | 
| 141 | error(SYSTEM, "failed photon primary seek in buildPrimaries"); | 
| 142 | pmap -> numPrimary += heapLen = ftell(primaryHeap [heap]) / | 
| 143 | sizeof(PhotonPrimary); | 
| 144 |  | 
| 145 | pmap -> primaries = realloc(pmap -> primaries, | 
| 146 | pmap -> numPrimary * | 
| 147 | sizeof(PhotonPrimary)); | 
| 148 | if (!pmap -> primaries) | 
| 149 | error(SYSTEM, "failed photon primary alloc in buildPrimaries"); | 
| 150 |  | 
| 151 | rewind(primaryHeap [heap]); | 
| 152 | if (fread(pmap -> primaries + primaryOfs [heap], sizeof(PhotonPrimary), | 
| 153 | heapLen, primaryHeap [heap]) != heapLen) | 
| 154 | error(SYSTEM, "failed reading photon primaries in buildPrimaries"); | 
| 155 |  | 
| 156 | fclose(primaryHeap [heap]); | 
| 157 | unlink(primaryHeapFname [heap]); | 
| 158 | } | 
| 159 |  | 
| 160 | return pmap -> numPrimary; | 
| 161 | } | 
| 162 |  | 
| 163 |  | 
| 164 |  | 
| 165 | /* Defs for photon emission counter array passed by sub-processes to parent | 
| 166 | * via shared memory */ | 
| 167 | typedef  unsigned long  PhotonContribCnt; | 
| 168 |  | 
| 169 | /* Indices for photon emission counter array: num photons stored and num | 
| 170 | * emitted per source */ | 
| 171 | #define  PHOTONCNT_NUMPHOT    0 | 
| 172 | #define  PHOTONCNT_NUMEMIT(n) (1 + n) | 
| 173 |  | 
| 174 |  | 
| 175 |  | 
| 176 |  | 
| 177 |  | 
| 178 |  | 
| 179 | void distribPhotonContrib (PhotonMap* pm, unsigned numProc) | 
| 180 | { | 
| 181 | EmissionMap       emap; | 
| 182 | char              errmsg2 [128], shmFname [PMAP_TMPFNLEN]; | 
| 183 | unsigned          srcIdx, proc; | 
| 184 | int               shmFile, stat, pid; | 
| 185 | double            *srcFlux,         /* Emitted flux per light source */ | 
| 186 | srcDistribTarget; /* Target photon count per source */ | 
| 187 | PhotonContribCnt  *photonCnt;       /* Photon emission counter array */ | 
| 188 | unsigned          photonCntSize = sizeof(PhotonContribCnt) * | 
| 189 | PHOTONCNT_NUMEMIT(nsources); | 
| 190 | FILE              **primaryHeap = NULL; | 
| 191 | char              **primaryHeapFname = NULL; | 
| 192 | PhotonPrimaryIdx  *primaryOfs = NULL; | 
| 193 |  | 
| 194 | if (!pm) | 
| 195 | error(USER, "no photon map defined in distribPhotonContrib"); | 
| 196 |  | 
| 197 | if (!nsources) | 
| 198 | error(USER, "no light sources in distribPhotonContrib"); | 
| 199 |  | 
| 200 | /* Allocate photon flux per light source; this differs for every | 
| 201 | * source as all sources contribute the same number of distributed | 
| 202 | * photons (srcDistribTarget), hence the number of photons emitted per | 
| 203 | * source does not correlate with its emitted flux. The resulting flux | 
| 204 | * per photon is therefore adjusted individually for each source. */ | 
| 205 | if (!(srcFlux = calloc(nsources, sizeof(double)))) | 
| 206 | error(SYSTEM, "can't allocate source flux in distribPhotonContrib"); | 
| 207 |  | 
| 208 | /* =================================================================== | 
| 209 | * INITIALISATION - Set up emission and scattering funcs | 
| 210 | * =================================================================== */ | 
| 211 | emap.samples = NULL; | 
| 212 | emap.src = NULL; | 
| 213 | emap.maxPartitions = MAXSPART; | 
| 214 | emap.partitions = (unsigned char*)malloc(emap.maxPartitions >> 1); | 
| 215 | if (!emap.partitions) | 
| 216 | error(USER, "can't allocate source partitions in distribPhotonContrib"); | 
| 217 |  | 
| 218 | /* Initialise contrib photon map */ | 
| 219 | initPhotonMap(pm, PMAP_TYPE_CONTRIB); | 
| 220 | initPhotonHeap(pm); | 
| 221 | initPhotonEmissionFuncs(); | 
| 222 | initPhotonScatterFuncs(); | 
| 223 |  | 
| 224 | /* Per-subprocess / per-source target counts */ | 
| 225 | pm -> distribTarget /= numProc; | 
| 226 | srcDistribTarget = nsources ? (double)pm -> distribTarget / nsources : 0; | 
| 227 |  | 
| 228 | if (!pm -> distribTarget) | 
| 229 | error(INTERNAL, "no photons to distribute in distribPhotonContrib"); | 
| 230 |  | 
| 231 | /* Get photon ports from modifier list */ | 
| 232 | getPhotonPorts(photonPortList); | 
| 233 |  | 
| 234 | /* Get photon sensor modifiers */ | 
| 235 | getPhotonSensors(photonSensorList); | 
| 236 |  | 
| 237 | #if NIX | 
| 238 | /* Set up shared mem for photon counters (zeroed by ftruncate) */ | 
| 239 | strcpy(shmFname, PMAP_TMPFNAME); | 
| 240 | shmFile = mkstemp(shmFname); | 
| 241 |  | 
| 242 | if (shmFile < 0 || ftruncate(shmFile, photonCntSize) < 0) | 
| 243 | error(SYSTEM, "failed shared mem init in distribPhotonContrib"); | 
| 244 |  | 
| 245 | photonCnt = mmap(NULL, photonCntSize, PROT_READ | PROT_WRITE, | 
| 246 | MAP_SHARED, shmFile, 0); | 
| 247 |  | 
| 248 | if (photonCnt == MAP_FAILED) | 
| 249 | error(SYSTEM, "failed shared mem mapping in distribPhotonContrib"); | 
| 250 | #else | 
| 251 | /* Allocate photon counters statically on Windoze */ | 
| 252 | if (!(photonCnt = malloc(photonCntSize))) | 
| 253 | error(SYSTEM, "failed trivial malloc in distribPhotonContrib"); | 
| 254 |  | 
| 255 | for (srcIdx = 0; srcIdx < PHOTONCNT_NUMEMIT(nsources); srcIdx++) | 
| 256 | photonCnt [srcIdx] = 0; | 
| 257 | #endif /* NIX */ | 
| 258 |  | 
| 259 | if (verbose) { | 
| 260 | sprintf(errmsg, "\nIntegrating flux from %d sources", nsources); | 
| 261 |  | 
| 262 | if (photonPorts) { | 
| 263 | sprintf(errmsg2, " via %d ports", numPhotonPorts); | 
| 264 | strcat(errmsg, errmsg2); | 
| 265 | } | 
| 266 |  | 
| 267 | strcat(errmsg, "\n"); | 
| 268 | eputs(errmsg); | 
| 269 | } | 
| 270 |  | 
| 271 | /* ============================================================= | 
| 272 | * FLUX INTEGRATION - Get total flux emitted from sources/ports | 
| 273 | * ============================================================= */ | 
| 274 | for (srcIdx = 0; srcIdx < nsources; srcIdx++) { | 
| 275 | unsigned portCnt = 0; | 
| 276 | srcFlux [srcIdx] = 0; | 
| 277 | emap.src = source + srcIdx; | 
| 278 |  | 
| 279 | do {  /* Need at least one iteration if no ports! */ | 
| 280 | emap.port = emap.src -> sflags & SDISTANT ? photonPorts + portCnt | 
| 281 | : NULL; | 
| 282 | photonPartition [emap.src -> so -> otype] (&emap); | 
| 283 |  | 
| 284 | if (verbose) { | 
| 285 | sprintf(errmsg, "\tIntegrating flux from source %s ", | 
| 286 | source [srcIdx].so -> oname); | 
| 287 |  | 
| 288 | if (emap.port) { | 
| 289 | sprintf(errmsg2, "via port %s ", | 
| 290 | photonPorts [portCnt].so -> oname); | 
| 291 | strcat(errmsg, errmsg2); | 
| 292 | } | 
| 293 |  | 
| 294 | sprintf(errmsg2, "(%lu partitions)\n", emap.numPartitions); | 
| 295 | strcat(errmsg, errmsg2); | 
| 296 | eputs(errmsg); | 
| 297 | #if NIX | 
| 298 | fflush(stderr); | 
| 299 | #endif | 
| 300 | } | 
| 301 |  | 
| 302 | for (emap.partitionCnt = 0; emap.partitionCnt < emap.numPartitions; | 
| 303 | emap.partitionCnt++) { | 
| 304 | initPhotonEmission(&emap, pdfSamples); | 
| 305 | srcFlux [srcIdx] += colorAvg(emap.partFlux); | 
| 306 | } | 
| 307 |  | 
| 308 | portCnt++; | 
| 309 | } while (portCnt < numPhotonPorts); | 
| 310 |  | 
| 311 | if (srcFlux [srcIdx] < FTINY) { | 
| 312 | sprintf(errmsg, "source %s has zero emission", | 
| 313 | source [srcIdx].so -> oname); | 
| 314 | error(WARNING, errmsg); | 
| 315 | } | 
| 316 | } | 
| 317 |  | 
| 318 | /* Allocate & init per-subprocess primary heap files */ | 
| 319 | primaryHeap = calloc(numProc, sizeof(FILE*)); | 
| 320 | primaryHeapFname = calloc(numProc, sizeof(char*)); | 
| 321 | primaryOfs = calloc(numProc, sizeof(PhotonPrimaryIdx)); | 
| 322 | if (!primaryHeap || !primaryHeapFname || !primaryOfs) | 
| 323 | error(SYSTEM, "failed primary heap allocation in " | 
| 324 | "distribPhotonContrib"); | 
| 325 |  | 
| 326 | for (proc = 0; proc < numProc; proc++) { | 
| 327 | primaryHeapFname [proc] = malloc(PMAP_TMPFNLEN); | 
| 328 | if (!primaryHeapFname [proc]) | 
| 329 | error(SYSTEM, "failed primary heap file allocation in " | 
| 330 | "distribPhotonContrib"); | 
| 331 |  | 
| 332 | mktemp(strcpy(primaryHeapFname [proc], PMAP_TMPFNAME)); | 
| 333 | if (!(primaryHeap [proc] = fopen(primaryHeapFname [proc], "w+b"))) | 
| 334 | error(SYSTEM, "failed opening primary heap file in " | 
| 335 | "distribPhotonContrib"); | 
| 336 | } | 
| 337 |  | 
| 338 | /* Record start time for progress reports */ | 
| 339 | repStartTime = time(NULL); | 
| 340 |  | 
| 341 | if (verbose) { | 
| 342 | sprintf(errmsg, "\nPhoton distribution @ %d procs\n", numProc); | 
| 343 | eputs(errmsg); | 
| 344 | } | 
| 345 |  | 
| 346 | /* MAIN LOOP */ | 
| 347 | for (proc = 0; proc < numProc; proc++) { | 
| 348 | #if NIX | 
| 349 | if (!(pid = fork())) { | 
| 350 | /* SUBPROCESS ENTERS HERE; opened and mmapped files inherited */ | 
| 351 | #else | 
| 352 | if (1) { | 
| 353 | /* No subprocess under Windoze */ | 
| 354 | #endif | 
| 355 | /* Local photon counters for this subprocess */ | 
| 356 | unsigned long  lastNumPhotons = 0, localNumEmitted = 0; | 
| 357 | double         photonFluxSum = 0;   /* Accum. photon flux */ | 
| 358 |  | 
| 359 | /* Seed RNGs from PID for decorellated photon distribution */ | 
| 360 | pmapSeed(randSeed + proc, partState); | 
| 361 | pmapSeed(randSeed + (proc + 1) % numProc, emitState); | 
| 362 | pmapSeed(randSeed + (proc + 2) % numProc, cntState); | 
| 363 | pmapSeed(randSeed + (proc + 3) % numProc, mediumState); | 
| 364 | pmapSeed(randSeed + (proc + 4) % numProc, scatterState); | 
| 365 | pmapSeed(randSeed + (proc + 5) % numProc, rouletteState); | 
| 366 |  | 
| 367 | #ifdef PMAP_SIGUSR | 
| 368 | double partNumEmit; | 
| 369 | unsigned long partEmitCnt; | 
| 370 | double srcPhotonFlux, avgPhotonFlux; | 
| 371 | unsigned       portCnt, passCnt, prePassCnt; | 
| 372 | float          srcPreDistrib; | 
| 373 | double         srcNumEmit;     /* # to emit from source */ | 
| 374 | unsigned long  srcNumDistrib;  /* # stored */ | 
| 375 |  | 
| 376 | void sigUsrDiags() | 
| 377 | /* Loop diags via SIGUSR1 */ | 
| 378 | { | 
| 379 | sprintf(errmsg, | 
| 380 | "********************* Proc %d Diags *********************\n" | 
| 381 | "srcIdx = %d (%s)\nportCnt = %d (%s)\npassCnt = %d\n" | 
| 382 | "srcFlux = %f\nsrcPhotonFlux = %f\navgPhotonFlux = %f\n" | 
| 383 | "partNumEmit = %f\npartEmitCnt = %lu\n\n", | 
| 384 | proc, srcIdx, findmaterial(source [srcIdx].so) -> oname, | 
| 385 | portCnt, photonPorts [portCnt].so -> oname, | 
| 386 | passCnt, srcFlux [srcIdx], srcPhotonFlux, avgPhotonFlux, | 
| 387 | partNumEmit, partEmitCnt); | 
| 388 | eputs(errmsg); | 
| 389 | fflush(stderr); | 
| 390 | } | 
| 391 | #endif | 
| 392 |  | 
| 393 | #ifdef PMAP_SIGUSR | 
| 394 | signal(SIGUSR1, sigUsrDiags); | 
| 395 | #endif | 
| 396 |  | 
| 397 | #ifdef DEBUG_PMAP | 
| 398 | /* Output child process PID after random delay to prevent corrupted | 
| 399 | * console output due to race condition */ | 
| 400 | usleep(1e6 * pmapRandom(rouletteState)); | 
| 401 | fprintf(stderr, "Proc %d: PID = %d " | 
| 402 | "(waiting 10 sec to attach debugger...)\n", | 
| 403 | proc, getpid()); | 
| 404 | /* Allow time for debugger to attach to child process */ | 
| 405 | sleep(10); | 
| 406 | #endif | 
| 407 |  | 
| 408 | /* ============================================================= | 
| 409 | * 2-PASS PHOTON DISTRIBUTION | 
| 410 | * Pass 1 (pre):  emit fraction of target photon count | 
| 411 | * Pass 2 (main): based on outcome of pass 1, estimate remaining | 
| 412 | *                number of photons to emit to approximate target | 
| 413 | *                count | 
| 414 | * ============================================================= */ | 
| 415 | for (srcIdx = 0; srcIdx < nsources; srcIdx++) { | 
| 416 | #ifndef PMAP_SIGUSR | 
| 417 | unsigned       portCnt, passCnt = 0, prePassCnt = 0; | 
| 418 | float          srcPreDistrib = preDistrib; | 
| 419 | double         srcNumEmit = 0;       /* # to emit from source */ | 
| 420 | unsigned long  srcNumDistrib = pm -> numPhotons;  /* # stored */ | 
| 421 | #else | 
| 422 | passCnt = prePassCnt = 0; | 
| 423 | srcPreDistrib = preDistrib; | 
| 424 | srcNumEmit = 0;       /* # to emit from source */ | 
| 425 | srcNumDistrib = pm -> numPhotons;  /* # stored */ | 
| 426 | #endif | 
| 427 |  | 
| 428 | if (srcFlux [srcIdx] < FTINY) | 
| 429 | continue; | 
| 430 |  | 
| 431 | while (passCnt < 2) { | 
| 432 | if (!passCnt) { | 
| 433 | /* INIT PASS 1 */ | 
| 434 | if (++prePassCnt > maxPreDistrib) { | 
| 435 | /* Warn if no photons contributed after sufficient | 
| 436 | * iterations; only output from subprocess 0 to reduce | 
| 437 | * console clutter */ | 
| 438 | if (!proc) { | 
| 439 | sprintf(errmsg, | 
| 440 | "source %s: too many prepasses, skipped", | 
| 441 | source [srcIdx].so -> oname); | 
| 442 | error(WARNING, errmsg); | 
| 443 | } | 
| 444 |  | 
| 445 | break; | 
| 446 | } | 
| 447 |  | 
| 448 | /* Num to emit is fraction of target count */ | 
| 449 | srcNumEmit = srcPreDistrib * srcDistribTarget; | 
| 450 | } | 
| 451 | else { | 
| 452 | /* INIT PASS 2 */ | 
| 453 | #ifndef PMAP_SIGUSR | 
| 454 | double srcPhotonFlux, avgPhotonFlux; | 
| 455 | #endif | 
| 456 |  | 
| 457 | /* Based on the outcome of the predistribution we can now | 
| 458 | * figure out how many more photons we have to emit from | 
| 459 | * the current source to meet the target count, | 
| 460 | * srcDistribTarget. This value is clamped to 0 in case | 
| 461 | * the target has already been exceeded in pass 1. | 
| 462 | * srcNumEmit and srcNumDistrib is the number of photons | 
| 463 | * emitted and distributed (stored) from the current | 
| 464 | * source in pass 1, respectively. */ | 
| 465 | srcNumDistrib = pm -> numPhotons - srcNumDistrib; | 
| 466 | srcNumEmit *= srcNumDistrib | 
| 467 | ? max(srcDistribTarget/srcNumDistrib, 1) - 1 | 
| 468 | : 0; | 
| 469 |  | 
| 470 | if (!srcNumEmit) | 
| 471 | /* No photons left to distribute in main pass */ | 
| 472 | break; | 
| 473 |  | 
| 474 | srcPhotonFlux = srcFlux [srcIdx] / srcNumEmit; | 
| 475 | avgPhotonFlux = photonFluxSum / (srcIdx + 1); | 
| 476 |  | 
| 477 | if (avgPhotonFlux > FTINY && | 
| 478 | srcPhotonFlux / avgPhotonFlux < FTINY) { | 
| 479 | /* Skip source if its photon flux is grossly below the | 
| 480 | * running average, indicating negligible contributions | 
| 481 | * at the expense of excessive distribution time; only | 
| 482 | * output from subproc 0 to reduce console clutter */ | 
| 483 | if (!proc) { | 
| 484 | sprintf(errmsg, | 
| 485 | "source %s: itsy bitsy photon flux, skipped", | 
| 486 | source [srcIdx].so -> oname); | 
| 487 | error(WARNING, errmsg); | 
| 488 | } | 
| 489 |  | 
| 490 | srcNumEmit = 0;   /* Or just break??? */ | 
| 491 | } | 
| 492 |  | 
| 493 | /* Update sum of photon flux per light source */ | 
| 494 | photonFluxSum += srcPhotonFlux; | 
| 495 | } | 
| 496 |  | 
| 497 | portCnt = 0; | 
| 498 | do {    /* Need at least one iteration if no ports! */ | 
| 499 | emap.src = source + srcIdx; | 
| 500 | emap.port = emap.src -> sflags & SDISTANT | 
| 501 | ? photonPorts + portCnt : NULL; | 
| 502 | photonPartition [emap.src -> so -> otype] (&emap); | 
| 503 |  | 
| 504 | if (verbose && !proc) { | 
| 505 | /* Output from subproc 0 only to avoid race condition | 
| 506 | * on console I/O */ | 
| 507 | if (!passCnt) | 
| 508 | sprintf(errmsg, "\tPREPASS %d on source %s ", | 
| 509 | prePassCnt, source [srcIdx].so -> oname); | 
| 510 | else | 
| 511 | sprintf(errmsg, "\tMAIN PASS on source %s ", | 
| 512 | source [srcIdx].so -> oname); | 
| 513 |  | 
| 514 | if (emap.port) { | 
| 515 | sprintf(errmsg2, "via port %s ", | 
| 516 | photonPorts [portCnt].so -> oname); | 
| 517 | strcat(errmsg, errmsg2); | 
| 518 | } | 
| 519 |  | 
| 520 | sprintf(errmsg2, "(%lu partitions)\n", | 
| 521 | emap.numPartitions); | 
| 522 | strcat(errmsg, errmsg2); | 
| 523 | eputs(errmsg); | 
| 524 | #if NIX | 
| 525 | fflush(stderr); | 
| 526 | #endif | 
| 527 | } | 
| 528 |  | 
| 529 | for (emap.partitionCnt = 0; emap.partitionCnt < emap.numPartitions; | 
| 530 | emap.partitionCnt++) { | 
| 531 | #ifndef PMAP_SIGUSR | 
| 532 | double partNumEmit; | 
| 533 | unsigned long partEmitCnt; | 
| 534 | #endif | 
| 535 |  | 
| 536 | /* Get photon origin within current source partishunn | 
| 537 | * and build emission map */ | 
| 538 | photonOrigin [emap.src -> so -> otype] (&emap); | 
| 539 | initPhotonEmission(&emap, pdfSamples); | 
| 540 |  | 
| 541 | /* Number of photons to emit from ziss partishunn; | 
| 542 | * scale according to its normalised contribushunn to | 
| 543 | * the emitted source flux */ | 
| 544 | partNumEmit = srcNumEmit * colorAvg(emap.partFlux) / | 
| 545 | srcFlux [srcIdx]; | 
| 546 | partEmitCnt = (unsigned long)partNumEmit; | 
| 547 |  | 
| 548 | /* Probabilistically account for fractional photons */ | 
| 549 | if (pmapRandom(cntState) < partNumEmit - partEmitCnt) | 
| 550 | partEmitCnt++; | 
| 551 |  | 
| 552 | /* Update local and shared global emission counter */ | 
| 553 | photonCnt [PHOTONCNT_NUMEMIT(srcIdx)] += partEmitCnt; | 
| 554 | localNumEmitted += partEmitCnt; | 
| 555 |  | 
| 556 | /* Integer counter avoids FP rounding errors during | 
| 557 | * iteration */ | 
| 558 | while (partEmitCnt--) { | 
| 559 | RAY photonRay; | 
| 560 |  | 
| 561 | /* Emit photon according to PDF (if any), allocate | 
| 562 | * associated primary ray, and trace through scene | 
| 563 | * until absorbed/leaked; emitPhoton() sets the | 
| 564 | * emitting light source index in photonRay */ | 
| 565 | emitPhoton(&emap, &photonRay); | 
| 566 | #if 1 | 
| 567 | if (emap.port) | 
| 568 | /* !!!  PHOTON PORT REJECTION SAMPLING HACK: set | 
| 569 | * !!!  photon port as fake hit object for | 
| 570 | * !!!  primary ray to check for intersection in | 
| 571 | * !!!  tracePhoton() */ | 
| 572 | photonRay.ro = emap.port -> so; | 
| 573 | #endif | 
| 574 | newPhotonPrimary(pm, &photonRay, primaryHeap[proc]); | 
| 575 | /* Set subprocess index in photonRay for post- | 
| 576 | * distrib primary index linearisation; this is | 
| 577 | * propagated with the primary index in photonRay | 
| 578 | * and set for photon hits by newPhoton() */ | 
| 579 | PMAP_SETRAYPROC(&photonRay, proc); | 
| 580 | tracePhoton(&photonRay); | 
| 581 | } | 
| 582 |  | 
| 583 | /* Update shared global photon count */ | 
| 584 | photonCnt [PHOTONCNT_NUMPHOT] += pm -> numPhotons - | 
| 585 | lastNumPhotons; | 
| 586 | lastNumPhotons = pm -> numPhotons; | 
| 587 | #if !NIX | 
| 588 | /* Synchronous progress report on Windoze */ | 
| 589 | if (!proc && photonRepTime > 0 && | 
| 590 | time(NULL) >= repLastTime + photonRepTime) { | 
| 591 | unsigned s; | 
| 592 | repComplete = pm -> distribTarget * numProc; | 
| 593 | repProgress = photonCnt [PHOTONCNT_NUMPHOT]; | 
| 594 |  | 
| 595 | for (repEmitted = 0, s = 0; s < nsources; s++) | 
| 596 | repEmitted += photonCnt [PHOTONCNT_NUMEMIT(s)]; | 
| 597 |  | 
| 598 | pmapDistribReport(); | 
| 599 | } | 
| 600 | #endif | 
| 601 | } | 
| 602 |  | 
| 603 | portCnt++; | 
| 604 | } while (portCnt < numPhotonPorts); | 
| 605 |  | 
| 606 | if (pm -> numPhotons == srcNumDistrib) { | 
| 607 | /* Double predistrib factor in case no photons were stored | 
| 608 | * for this source and redo pass 1 */ | 
| 609 | srcPreDistrib *= 2; | 
| 610 | } | 
| 611 | else { | 
| 612 | /* Now do pass 2 */ | 
| 613 | passCnt++; | 
| 614 | } | 
| 615 | } | 
| 616 | } | 
| 617 |  | 
| 618 | /* Flush heap buffa one final time to prevent data corruption */ | 
| 619 | flushPhotonHeap(pm); | 
| 620 | /* Flush final photon primary to primary heap file */ | 
| 621 | newPhotonPrimary(pm, NULL, primaryHeap [proc]); | 
| 622 | /* Heap files closed automatically on exit | 
| 623 | fclose(pm -> heap); | 
| 624 | fclose(primaryHeap [proc]); */ | 
| 625 |  | 
| 626 | #ifdef DEBUG_PMAP | 
| 627 | sprintf(errmsg, "Proc %d total %ld photons\n", proc, | 
| 628 | pm -> numPhotons); | 
| 629 | eputs(errmsg); | 
| 630 | fflush(stderr); | 
| 631 | #endif | 
| 632 |  | 
| 633 | #ifdef PMAP_SIGUSR | 
| 634 | signal(SIGUSR1, SIG_DFL); | 
| 635 | #endif | 
| 636 |  | 
| 637 | #if NIX | 
| 638 | /* Terminate subprocess */ | 
| 639 | exit(0); | 
| 640 | #endif | 
| 641 | } | 
| 642 | else if (pid < 0) | 
| 643 | error(SYSTEM, "failed to fork subprocess in distribPhotonContrib"); | 
| 644 | } | 
| 645 |  | 
| 646 | #if NIX | 
| 647 | /* PARENT PROCESS CONTINUES HERE */ | 
| 648 | #ifdef SIGCONT | 
| 649 | /* Enable progress report signal handler */ | 
| 650 | signal(SIGCONT, pmapDistribReport); | 
| 651 | #endif | 
| 652 | /* Wait for subprocesses to complete while reporting progress */ | 
| 653 | proc = numProc; | 
| 654 | while (proc) { | 
| 655 | while (waitpid(-1, &stat, WNOHANG) > 0) { | 
| 656 | /* Subprocess exited; check status */ | 
| 657 | if (!WIFEXITED(stat) || WEXITSTATUS(stat)) | 
| 658 | error(USER, "failed photon distribution"); | 
| 659 |  | 
| 660 | --proc; | 
| 661 | } | 
| 662 |  | 
| 663 | /* Nod off for a bit and update progress  */ | 
| 664 | sleep(1); | 
| 665 |  | 
| 666 | /* Asynchronous progress report from shared subprocess counters */ | 
| 667 | repComplete = pm -> distribTarget * numProc; | 
| 668 | repProgress = photonCnt [PHOTONCNT_NUMPHOT]; | 
| 669 |  | 
| 670 | for (repEmitted = 0, srcIdx = 0; srcIdx < nsources; srcIdx++) | 
| 671 | repEmitted += photonCnt [PHOTONCNT_NUMEMIT(srcIdx)]; | 
| 672 |  | 
| 673 | /* Get global photon count from shmem updated by subprocs */ | 
| 674 | pm -> numPhotons = photonCnt [PHOTONCNT_NUMPHOT]; | 
| 675 |  | 
| 676 | if (photonRepTime > 0 && time(NULL) >= repLastTime + photonRepTime) | 
| 677 | pmapDistribReport(); | 
| 678 | #ifdef SIGCONT | 
| 679 | else signal(SIGCONT, pmapDistribReport); | 
| 680 | #endif | 
| 681 | } | 
| 682 | #endif /* NIX */ | 
| 683 |  | 
| 684 | /* ================================================================ | 
| 685 | * POST-DISTRIBUTION - Set photon flux and build kd-tree, etc. | 
| 686 | * ================================================================ */ | 
| 687 | #ifdef SIGCONT | 
| 688 | /* Reset signal handler */ | 
| 689 | signal(SIGCONT, SIG_DFL); | 
| 690 | #endif | 
| 691 | free(emap.samples); | 
| 692 |  | 
| 693 | if (!pm -> numPhotons) | 
| 694 | error(USER, "empty contribution photon map"); | 
| 695 |  | 
| 696 | /* Load per-subprocess primary rays into pm -> primary array */ | 
| 697 | /* Dumb compilers apparently need the char** cast */ | 
| 698 | pm -> numPrimary = buildPrimaries(pm, primaryHeap, | 
| 699 | (char**)primaryHeapFname, | 
| 700 | primaryOfs, numProc); | 
| 701 | if (!pm -> numPrimary) | 
| 702 | error(INTERNAL, "no primary rays in contribution photon map"); | 
| 703 |  | 
| 704 | /* Set photon flux per source */ | 
| 705 | for (srcIdx = 0; srcIdx < nsources; srcIdx++) | 
| 706 | srcFlux [srcIdx] /= photonCnt [PHOTONCNT_NUMEMIT(srcIdx)]; | 
| 707 | #if NIX | 
| 708 | /* Photon counters no longer needed, unmap shared memory */ | 
| 709 | munmap(photonCnt, sizeof(*photonCnt)); | 
| 710 | close(shmFile); | 
| 711 | unlink(shmFname); | 
| 712 | #else | 
| 713 | free(photonCnt); | 
| 714 | #endif | 
| 715 |  | 
| 716 | if (verbose) { | 
| 717 | eputs("\nBuilding contribution photon map...\n"); | 
| 718 | #if NIX | 
| 719 | fflush(stderr); | 
| 720 | #endif | 
| 721 | } | 
| 722 |  | 
| 723 | /* Build underlying data structure; heap is destroyed */ | 
| 724 | buildPhotonMap(pm, srcFlux, primaryOfs, numProc); | 
| 725 |  | 
| 726 | /* Free per-subprocess primary heap files */ | 
| 727 | for (proc = 0; proc < numProc; proc++) | 
| 728 | free(primaryHeapFname [proc]); | 
| 729 |  | 
| 730 | free(primaryHeapFname); | 
| 731 | free(primaryHeap); | 
| 732 | free(primaryOfs); | 
| 733 |  | 
| 734 | if (verbose) | 
| 735 | eputs("\n"); | 
| 736 | } |