Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F61978954
pmapcontrib.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Fri, May 10, 04:27
Size
37 KB
Mime Type
text/x-c
Expires
Sun, May 12, 04:27 (1 d, 23 h)
Engine
blob
Format
Raw Data
Handle
17542779
Attached To
R10977 RADIANCE Photon Map
pmapcontrib.c
View Options
#ifndef lint
static
const
char
RCSid
[]
=
"$Id: pmapcontrib.c,v 2.19 2018/11/08 00:54:07 greg Exp $"
;
#endif
/*
=========================================================================
Photon map routines for precomputed light source contributions; these
routines interface to mkpmap.
Roland Schregle (roland.schregle@{hslu.ch, gmail.com})
(c) Lucerne University of Applied Sciences and Arts,
supported by the Swiss National Science Foundation
(SNSF #147053, "Daylight Redirecting Components",
SNSF #179067, "Light Fields for Spatio-Temporal Glare Assessment")
=========================================================================
$Id: pmapcontrib.c,v 2.19 2018/11/08 00:54:07 greg Exp $
*/
#include "pmapcontrib.h"
#include "pmapmat.h"
#include "pmapsrc.h"
#include "pmaprand.h"
#include "pmapio.h"
#include "pmapdiag.h"
#include "otypes.h"
#include "otspecial.h"
#if NIX
#include <sys/mman.h>
#include <sys/wait.h>
#endif
MODCONT
*
addSourceModifier
(
LUTAB
*
srcContribs
,
unsigned
*
numSrcContrib
,
char
*
mod
,
char
*
binParm
,
char
*
binVal
,
int
binCnt
)
/* Add light source modifier mod to contribution lookup table srcContribs,
and update numSrcContrib. Return initialised contribution data for this
modifier. */
{
LUENT
*
lutEntry
=
lu_find
(
srcContribs
,
mod
);
MODCONT
*
contrib
;
EPNODE
*
eBinVal
;
if
(
lutEntry
->
data
)
{
/* Reject duplicate modifiers */
sprintf
(
errmsg
,
"duplicate light source modifier %s"
,
mod
);
error
(
USER
,
errmsg
);
}
if
(
*
numSrcContrib
>=
MAXMODLIST
)
{
sprintf
(
errmsg
,
"too many modifiers (max. %d)"
,
MAXMODLIST
);
error
(
INTERNAL
,
errmsg
);
}
lutEntry
->
key
=
mod
;
if
(
!
binVal
)
/* Fall back to single bin if no value specified */
binVal
=
"0"
;
eBinVal
=
eparse
(
binVal
);
if
(
eBinVal
->
type
==
NUM
)
{
/* Bin value is constant */
binCnt
=
(
int
)(
evalue
(
eBinVal
)
+
1.5
);
if
(
binCnt
!=
1
)
{
sprintf
(
errmsg
,
"invalid non-zero constant for bin %s"
,
binVal
);
error
(
USER
,
errmsg
);
}
}
else
if
(
binCnt
<=
0
)
{
sprintf
(
errmsg
,
"undefined/invalid bin count for modifier %s"
,
mod
);
error
(
USER
,
errmsg
);
}
/* Allocate and init contributions */
contrib
=
(
MODCONT
*
)
malloc
(
sizeof
(
MODCONT
)
+
sizeof
(
DCOLOR
)
*
(
binCnt
-
1
)
);
if
(
!
contrib
)
error
(
SYSTEM
,
"out of memory in addSourceModifier"
);
contrib
->
outspec
=
NULL
;
contrib
->
modname
=
mod
;
contrib
->
params
=
binParm
;
contrib
->
nbins
=
binCnt
;
contrib
->
binv
=
eBinVal
;
contrib
->
bin0
=
0
;
memset
(
contrib
->
cbin
,
0
,
sizeof
(
DCOLOR
)
*
binCnt
);
lutEntry
->
data
=
lutEntry
->
data
=
(
char
*
)
contrib
;
++
(
*
numSrcContrib
);
return
(
contrib
);
}
void
addSourceModfile
(
LUTAB
*
srcContribs
,
unsigned
*
numSrcContrib
,
char
*
modFile
,
char
*
binParm
,
char
*
binVal
,
int
binCnt
)
/* Add light source modifiers from file modFile to contribution lookup table
* srcContribs, and update numSrcContrib.
* NOTE: This code is adapted from rcontrib.c */
{
char
*
mods
[
MAXMODLIST
];
int
i
;
/* Find file and store strings */
i
=
wordfile
(
mods
,
MAXMODLIST
,
getpath
(
modFile
,
getrlibpath
(),
R_OK
));
if
(
i
<
0
)
{
sprintf
(
errmsg
,
"can't open modifier file %s"
,
modFile
);
error
(
SYSTEM
,
errmsg
);
}
if
(
*
numSrcContrib
+
i
>=
MAXMODLIST
-
1
)
{
sprintf
(
errmsg
,
"too many modifiers (max. %d) in file %s"
,
MAXMODLIST
-
1
,
modFile
);
error
(
INTERNAL
,
errmsg
);
}
for
(
i
=
0
;
mods
[
i
];
i
++
)
/* Add each modifier */
addSourceModifier
(
srcContribs
,
numSrcContrib
,
mods
[
i
],
binParm
,
binVal
,
binCnt
);
}
static
int
originBin
(
LUTAB
*
srcContribs
,
const
RAY
*
ray
)
/* Map origin ray to its bin for light source ray -> rsrc.
Return invalid bin -1 if mapping failed. */
{
const
SRCREC
*
src
;
const
OBJREC
*
srcMod
;
const
MODCONT
*
srcCont
;
double
binReal
;
int
bin
;
/* Check we have a valid ray and contribution LUT */
if
(
!
ray
||
!
srcContribs
)
return
-
1
;
src
=
&
source
[
ray
->
rsrc
];
srcMod
=
findmaterial
(
src
->
so
);
srcCont
=
(
MODCONT
*
)
lu_find
(
srcContribs
,
srcMod
->
oname
)
->
data
;
if
(
!
srcCont
)
/* We're not interested in this source (modifier not in contrib LUT) */
return
-
1
;
if
(
srcCont
->
binv
->
type
!=
NUM
)
{
/* Binning function defined; set up shadow ray pointing to source
for evaluation */
RAY
srcRay
;
int
i
;
rayorigin
(
&
srcRay
,
SHADOW
,
NULL
,
NULL
);
srcRay
.
rsrc
=
ray
->
rsrc
;
VCOPY
(
srcRay
.
rorg
,
ray
->
rop
);
for
(
i
=
0
;
i
<
3
;
i
++
)
srcRay
.
rdir
[
i
]
=
-
ray
->
rdir
[
i
];
if
(
!
(
src
->
sflags
&
SDISTANT
?
sourcehit
(
&
srcRay
)
:
(
*
ofun
[
srcMod
->
otype
].
funp
)(
srcMod
,
&
srcRay
)
))
/* (Redundant?) sanity check for valid source ray? */
return
-
1
;
worldfunc
(
RCCONTEXT
,
&
srcRay
);
set_eparams
((
char
*
)
srcCont
->
params
);
}
if
((
binReal
=
evalue
(
srcCont
->
binv
))
<
-
.5
)
return
-
1
;
if
((
bin
=
binReal
+
.5
)
>=
srcCont
->
nbins
)
{
error
(
WARNING
,
"Ignoring invalid bin in photonRayBin"
);
return
-
1
;
}
return
bin
;
}
static
PhotonOrigin
newPhotonOrigin
(
PhotonMap
*
pmap
,
const
RAY
*
orgRay
,
FILE
*
orgHeap
)
/* Add origin ray for emitted photon and save light source index, and
* binned direction for contrib photons. The current origin is stored in
* pmap -> lastContribOrg. If the previous origin spawned photons (i.e.
* has srcIdx >= 0), it's appended to orgHeap. If orgRay == NULL, the
* current origin is still flushed, but no new origin is set.
* Returns updated origin counter pmap -> numContribOrg. */
{
if
(
!
pmap
||
!
orgHeap
)
return
0
;
/* Check if last origin ray has spawned photons (srcIdx >= 0, see
* newPhoton()), in which case we save it to the origin heap file
* before clobbering it. (Note this is short-term storage, so we doan'
* need da portable I/O stuff here). */
if
(
pmap
->
lastContribOrg
.
srcIdx
>=
0
)
{
if
(
!
fwrite
(
&
pmap
->
lastContribOrg
,
sizeof
(
PhotonContribOrigin
),
1
,
orgHeap
))
error
(
SYSTEM
,
"failed writing contrib photon origin in newPhotonOrigin"
);
pmap
->
numContribOrg
++
;
if
(
!
pmap
->
numContribOrg
||
pmap
->
numContribOrg
>
PMAP_MAXORIGIN
)
/* numContribOrg overflowed (wrapped around) or exceeded max */
error
(
INTERNAL
,
"contrib photon origin overflow in newPhotonOrigin"
);
}
/* Mark this origin unused with a negative source index until its path
spawns a photon (see newPhoton()) */
pmap
->
lastContribOrg
.
srcIdx
=
-
1
;
/* Map ray to bin in anticipation that this origin will be used, since the
ray will be lost once a photon is spawned */
pmap
->
lastContribOrg
.
srcBin
=
originBin
(
pmap
->
srcContrib
,
orgRay
);
return
pmap
->
numContribOrg
;
}
static
PhotonOrigin
buildOrigins
(
PhotonMap
*
pmap
,
FILE
**
orgHeap
,
char
**
orgHeapFname
,
PhotonOrigin
*
orgOfs
,
unsigned
numHeaps
)
/* Consolidate per-subprocess photon origin heaps into the origin array
* pmap -> contribOrg. Returns offset for origin index linearisation in
* pmap -> numContribOrg. The heap files in orgHeap are closed on return. */
{
PhotonOrigin
heapLen
;
unsigned
heap
;
if
(
!
pmap
||
!
orgHeap
||
!
orgOfs
||
!
numHeaps
)
return
0
;
pmap
->
numContribOrg
=
0
;
for
(
heap
=
0
;
heap
<
numHeaps
;
heap
++
)
{
orgOfs
[
heap
]
=
pmap
->
numContribOrg
;
if
(
fseek
(
orgHeap
[
heap
],
0
,
SEEK_END
)
<
0
)
error
(
SYSTEM
,
"failed contrib photon origin seek in buildOrigins"
);
pmap
->
numContribOrg
+=
heapLen
=
ftell
(
orgHeap
[
heap
])
/
sizeof
(
PhotonContribOrigin
);
if
(
!
(
pmap
->
contribOrg
=
realloc
(
pmap
->
contribOrg
,
pmap
->
numContribOrg
*
sizeof
(
PhotonContribOrigin
)
)))
error
(
SYSTEM
,
"failed contrib photon origin alloc in buildOrigins"
);
rewind
(
orgHeap
[
heap
]);
if
(
fread
(
pmap
->
contribOrg
+
orgOfs
[
heap
],
sizeof
(
PhotonContribOrigin
),
heapLen
,
orgHeap
[
heap
]
)
!=
heapLen
)
error
(
SYSTEM
,
"failed reading contrib photon origin in buildOrigins"
);
fclose
(
orgHeap
[
heap
]);
unlink
(
orgHeapFname
[
heap
]);
}
return
pmap
->
numContribOrg
;
}
void
photonContrib
(
PhotonMap
*
pmap
,
RAY
*
ray
,
COLOR
irrad
)
/* Locate photons near ray -> rop which originated from the light source
modifier indexed by ray -> rsrc, and accumulate their contributions
in pmap -> srcContrib. Return total contributions in irrad. */
{
const
OBJREC
*
srcMod
=
findmaterial
(
source
[
ray
->
rsrc
].
so
);
MODCONT
*
srcContrib
=
(
MODCONT
*
)
lu_find
(
pmap
->
srcContrib
,
srcMod
->
oname
)
->
data
;
Photon
*
photon
;
COLOR
flux
;
PhotonSearchQueueNode
*
sqn
;
float
r2
,
invArea
;
unsigned
i
;
/* Zero bins for this source modifier */
memset
(
srcContrib
->
cbin
,
0
,
sizeof
(
DCOLOR
)
*
srcContrib
->
nbins
);
setcolor
(
irrad
,
0
,
0
,
0
);
if
(
!
srcContrib
||
!
pmap
->
maxGather
)
/* Modifier not in LUT or zero bandwidth */
return
;
/* Lookup photons */
pmap
->
squeue
.
tail
=
0
;
/* Pass light source index to filter in findPhotons() via
pmap -> lastContribOrg */
pmap
->
lastContribOrg
.
srcIdx
=
ray
->
rsrc
;
findPhotons
(
pmap
,
ray
);
/* Need at least 2 photons */
if
(
pmap
->
squeue
.
tail
<
2
)
{
#ifdef PMAP_NONEFOUND
sprintf
(
errmsg
,
"no photons found on %s at (%.3f, %.3f, %.3f)"
,
ray
->
ro
?
ray
->
ro
->
oname
:
"<null>"
,
ray
->
rop
[
0
],
ray
->
rop
[
1
],
ray
->
rop
[
2
]
);
error
(
WARNING
,
errmsg
);
#endif
return
;
}
/* Average radius^2 between furthest two photons to improve accuracy
and get inverse search area 1 / (PI * r^2). */
/* XXX: OMIT extra normalisation factor 1/PI for ambient calculation? */
sqn
=
pmap
->
squeue
.
node
+
1
;
r2
=
max
(
sqn
->
dist2
,
(
sqn
+
1
)
->
dist2
);
r2
=
0.25
*
(
pmap
->
maxDist2
+
r2
+
2
*
sqrt
(
pmap
->
maxDist2
*
r2
));
invArea
=
1
/
(
PI
*
PI
*
r2
);
/* Skip the extra photon */
for
(
i
=
1
;
i
<
pmap
->
squeue
.
tail
;
i
++
,
sqn
++
)
{
/* Get photon's contribution to density estimate */
photon
=
getNearestPhoton
(
&
pmap
->
squeue
,
sqn
->
idx
);
getPhotonFlux
(
photon
,
flux
);
scalecolor
(
flux
,
invArea
);
#ifdef PMAP_EPANECHNIKOV
/* Apply Epanechnikov kernel to photon flux based on photon distance */
scalecolor
(
flux
,
2
*
(
1
-
sqn
->
dist2
/
r2
));
#endif
addcolor
(
irrad
,
flux
);
addcolor
(
srcContrib
->
cbin
[
photonSrcBin
(
pmap
,
photon
)],
flux
);
}
return
;
}
static
void
preComputeContrib
(
PhotonMap
*
pmap
)
/* Precompute contributions for a random subset of finalGather *
pmap -> numPhotons photons, and build the precomputed contribution photon
map, discarding the original photons. */
/* !!! NOTE: PRECOMPUTATION WITH OOC CURRENTLY WITHOUT CACHE !!! */
{
unsigned
long
i
,
numPreComp
;
unsigned
j
;
PhotonIdx
pIdx
;
Photon
photon
;
RAY
ray
;
PhotonMap
nuPmap
;
repComplete
=
numPreComp
=
finalGather
*
pmap
->
numPhotons
;
if
(
verbose
)
{
sprintf
(
errmsg
,
"
\n
Precomputing contributions for %ld photons
\n
"
,
numPreComp
);
eputs
(
errmsg
);
#if NIX
fflush
(
stderr
);
#endif
}
/* Copy photon map for precomputed photons */
memcpy
(
&
nuPmap
,
pmap
,
sizeof
(
PhotonMap
));
/* Zero counters, init new heap and extents */
nuPmap
.
numPhotons
=
0
;
initPhotonHeap
(
&
nuPmap
);
for
(
j
=
0
;
j
<
3
;
j
++
)
{
nuPmap
.
minPos
[
j
]
=
FHUGE
;
nuPmap
.
maxPos
[
j
]
=
-
FHUGE
;
}
/* Record start time, baby */
repStartTime
=
time
(
NULL
);
#ifdef SIGCONT
signal
(
SIGCONT
,
pmapPreCompReport
);
#endif
repProgress
=
0
;
photonRay
(
NULL
,
&
ray
,
PRIMARY
,
NULL
);
ray
.
ro
=
NULL
;
for
(
i
=
0
;
i
<
numPreComp
;
i
++
)
{
/* Get random photon from stratified distribution in source heap to
* avoid duplicates and clustering */
pIdx
=
firstPhoton
(
pmap
)
+
(
unsigned
long
)(
(
i
+
pmapRandom
(
pmap
->
randState
))
/
finalGather
);
getPhoton
(
pmap
,
pIdx
,
&
photon
);
/* Init dummy photon ray with intersection and normal at photon
position. Set emitting light source index from origin. */
VCOPY
(
ray
.
rop
,
photon
.
pos
);
ray
.
rsrc
=
photonSrcIdx
(
pmap
,
&
photon
);
for
(
j
=
0
;
j
<
3
;
j
++
)
ray
.
ron
[
j
]
=
photon
.
norm
[
j
]
/
127.0
;
/* Get contributions at photon position */
photonContrib
(
pmap
,
&
ray
,
ray
.
rcol
);
/* Append photon to new heap from ray */
newPhoton
(
&
nuPmap
,
&
ray
);
/* TODO: wavelet xform on contribs here? */
/* waveletEncode(...); */
/* Update progress */
repProgress
++
;
if
(
photonRepTime
>
0
&&
time
(
NULL
)
>=
repLastTime
+
photonRepTime
)
pmapPreCompReport
();
#ifdef SIGCONT
else
signal
(
SIGCONT
,
pmapPreCompReport
);
#endif
}
/* Flush heap */
flushPhotonHeap
(
&
nuPmap
);
#ifdef SIGCONT
signal
(
SIGCONT
,
SIG_DFL
);
#endif
/* Trash original pmap, replace with precomputed one */
deletePhotons
(
pmap
);
memcpy
(
pmap
,
&
nuPmap
,
sizeof
(
PhotonMap
));
if
(
verbose
)
{
eputs
(
"
\n
Rebuilding precomputed contribution photon map
\n
"
);
#if NIX
fflush
(
stderr
);
#endif
}
/* Rebuild underlying data structure, destroying heap */
buildPhotonMap
(
pmap
,
NULL
,
NULL
,
1
);
/* TODO: Save wavelet coeffs here? */
}
/* Defs for photon emission counter array passed by sub-processes to parent
* via shared memory */
typedef
unsigned
long
PhotonContribCnt
;
/* Indices for photon emission counter array: num photons stored and num
* emitted per source */
#define PHOTONCNT_NUMPHOT 0
#define PHOTONCNT_NUMEMIT(n) (1 + n)
void
distribPhotonContrib
(
PhotonMap
*
pmap
,
LUTAB
*
srcContrib
,
unsigned
numSrcContrib
,
unsigned
numProc
)
{
EmissionMap
emap
;
char
errmsg2
[
128
],
shmFname
[
PMAP_TMPFNLEN
];
unsigned
srcIdx
,
proc
;
int
shmFile
,
stat
,
pid
;
double
*
srcFlux
,
/* Emitted flux per light source */
srcDistribTarget
;
/* Target photon count per source */
PhotonContribCnt
*
photonCnt
;
/* Photon emission counter array */
unsigned
photonCntSize
=
sizeof
(
PhotonContribCnt
)
*
PHOTONCNT_NUMEMIT
(
nsources
);
FILE
**
orgHeap
=
NULL
;
char
**
orgHeapFname
=
NULL
;
PhotonOrigin
*
orgOfs
=
NULL
;
if
(
!
pmap
)
error
(
USER
,
"no contribution photon map specified"
);
if
(
!
nsources
)
error
(
USER
,
"no light sources"
);
if
(
nsources
>
MAXMODLIST
)
error
(
USER
,
"too many light sources"
);
if
(
!
srcContrib
||
!
numSrcContrib
)
error
(
USER
,
"no modifiers specified for contribution photon map"
);
/* Allocate photon flux per light source; this differs for every
* source as all sources contribute the same number of distributed
* photons (srcDistribTarget), hence the number of photons emitted per
* source does not correlate with its emitted flux. The resulting flux
* per photon is therefore adjusted individually for each source. */
if
(
!
(
srcFlux
=
calloc
(
nsources
,
sizeof
(
double
))))
error
(
SYSTEM
,
"can't allocate source flux in distribPhotonContrib"
);
/* ===================================================================
* INITIALISATION - Set up emission and scattering funcs
* =================================================================== */
emap
.
samples
=
NULL
;
emap
.
src
=
NULL
;
emap
.
maxPartitions
=
MAXSPART
;
emap
.
partitions
=
(
unsigned
char
*
)
malloc
(
emap
.
maxPartitions
>>
1
);
if
(
!
emap
.
partitions
)
error
(
USER
,
"can't allocate source partitions in distribPhotonContrib"
);
/* Initialise contrib photon map */
initPhotonMap
(
pmap
,
PMAP_TYPE_CONTRIB
);
initPmapContrib
(
srcContrib
);
initPhotonHeap
(
pmap
);
initPhotonEmissionFuncs
();
initPhotonScatterFuncs
();
/* Per-subprocess / per-source target counts */
pmap
->
distribTarget
/=
numProc
;
srcDistribTarget
=
nsources
?
(
double
)
pmap
->
distribTarget
/
nsources
:
0
;
if
(
!
pmap
->
distribTarget
)
error
(
INTERNAL
,
"no photons to distribute in distribPhotonContrib"
);
/* Get photon ports from modifier list */
getPhotonPorts
(
photonPortList
);
/* Get photon sensor modifiers */
getPhotonSensors
(
photonSensorList
);
#if NIX
/* Set up shared mem for photon counters (zeroed by ftruncate) */
strcpy
(
shmFname
,
PMAP_TMPFNAME
);
shmFile
=
mkstemp
(
shmFname
);
if
(
shmFile
<
0
||
ftruncate
(
shmFile
,
photonCntSize
)
<
0
)
error
(
SYSTEM
,
"failed shared mem init in distribPhotonContrib"
);
photonCnt
=
mmap
(
NULL
,
photonCntSize
,
PROT_READ
|
PROT_WRITE
,
MAP_SHARED
,
shmFile
,
0
);
if
(
photonCnt
==
MAP_FAILED
)
error
(
SYSTEM
,
"failed shared mem mapping in distribPhotonContrib"
);
#else
/* Allocate photon counters statically on Windoze */
if
(
!
(
photonCnt
=
malloc
(
photonCntSize
)))
error
(
SYSTEM
,
"failed trivial malloc in distribPhotonContrib"
);
for
(
srcIdx
=
0
;
srcIdx
<
PHOTONCNT_NUMEMIT
(
nsources
);
srcIdx
++
)
photonCnt
[
srcIdx
]
=
0
;
#endif
/* NIX */
if
(
verbose
)
{
sprintf
(
errmsg
,
"
\n
Integrating flux from %d sources"
,
nsources
);
if
(
photonPorts
)
{
sprintf
(
errmsg2
,
" via %d ports"
,
numPhotonPorts
);
strcat
(
errmsg
,
errmsg2
);
}
strcat
(
errmsg
,
"
\n
"
);
eputs
(
errmsg
);
}
/* =============================================================
* FLUX INTEGRATION - Get total flux emitted from sources/ports
* ============================================================= */
for
(
srcIdx
=
0
;
srcIdx
<
nsources
;
srcIdx
++
)
{
unsigned
portCnt
=
0
;
const
OBJREC
*
srcMod
=
findmaterial
(
source
[
srcIdx
].
so
);
srcFlux
[
srcIdx
]
=
0
;
/* Skip this source if its contributions are not sought */
if
(
!
lu_find
(
pmap
->
srcContrib
,
srcMod
->
oname
)
->
data
)
{
sprintf
(
errmsg
,
"ignoring contributions from source %s"
,
source
[
srcIdx
].
so
->
oname
);
error
(
WARNING
,
errmsg
);
continue
;
}
emap
.
src
=
source
+
srcIdx
;
do
{
/* Need at least one iteration if no ports! */
emap
.
port
=
emap
.
src
->
sflags
&
SDISTANT
?
photonPorts
+
portCnt
:
NULL
;
photonPartition
[
emap
.
src
->
so
->
otype
]
(
&
emap
);
if
(
verbose
)
{
sprintf
(
errmsg
,
"
\t
Integrating flux from source %s "
,
source
[
srcIdx
].
so
->
oname
);
if
(
emap
.
port
)
{
sprintf
(
errmsg2
,
"via port %s "
,
photonPorts
[
portCnt
].
so
->
oname
);
strcat
(
errmsg
,
errmsg2
);
}
sprintf
(
errmsg2
,
"(%lu partitions)
\n
"
,
emap
.
numPartitions
);
strcat
(
errmsg
,
errmsg2
);
eputs
(
errmsg
);
#if NIX
fflush
(
stderr
);
#endif
}
for
(
emap
.
partitionCnt
=
0
;
emap
.
partitionCnt
<
emap
.
numPartitions
;
emap
.
partitionCnt
++
)
{
initPhotonEmission
(
&
emap
,
pdfSamples
);
srcFlux
[
srcIdx
]
+=
colorAvg
(
emap
.
partFlux
);
}
portCnt
++
;
}
while
(
portCnt
<
numPhotonPorts
);
if
(
srcFlux
[
srcIdx
]
<
FTINY
)
{
sprintf
(
errmsg
,
"source %s has zero emission"
,
source
[
srcIdx
].
so
->
oname
);
error
(
WARNING
,
errmsg
);
}
}
/* Allocate & init per-subprocess contribution origin heap files */
orgHeap
=
calloc
(
numProc
,
sizeof
(
FILE
*
));
orgHeapFname
=
calloc
(
numProc
,
sizeof
(
char
*
));
orgOfs
=
calloc
(
numProc
,
sizeof
(
PhotonOrigin
));
if
(
!
orgHeap
||
!
orgHeapFname
||
!
orgOfs
)
error
(
SYSTEM
,
"failed contribution origin heap allocation "
"in distribPhotonContrib"
);
for
(
proc
=
0
;
proc
<
numProc
;
proc
++
)
{
orgHeapFname
[
proc
]
=
malloc
(
PMAP_TMPFNLEN
);
if
(
!
orgHeapFname
[
proc
])
error
(
SYSTEM
,
"failed contribution origin heap file allocation "
"in distribPhotonContrib"
);
mktemp
(
strcpy
(
orgHeapFname
[
proc
],
PMAP_TMPFNAME
));
if
(
!
(
orgHeap
[
proc
]
=
fopen
(
orgHeapFname
[
proc
],
"w+b"
)))
error
(
SYSTEM
,
"failed opening contribution origin heap file "
"in distribPhotonContrib"
);
}
/* Record start time for progress reports */
repStartTime
=
time
(
NULL
);
if
(
verbose
)
{
sprintf
(
errmsg
,
"
\n
Photon distribution @ %d procs
\n
"
,
numProc
);
eputs
(
errmsg
);
}
/* MAIN LOOP */
for
(
proc
=
0
;
proc
<
numProc
;
proc
++
)
{
#if NIX
if
(
!
(
pid
=
fork
()))
{
/* SUBPROCESS ENTERS HERE; opened and mmapped files inherited */
#else
if
(
1
)
{
/* No subprocess under Windoze */
#endif
/* Local photon counters for this subprocess */
unsigned
long
lastNumPhotons
=
0
,
localNumEmitted
=
0
;
double
photonFluxSum
=
0
;
/* Accum. photon flux */
/* Seed RNGs from PID for decorellated photon distribution */
pmapSeed
(
randSeed
+
proc
,
partState
);
pmapSeed
(
randSeed
+
(
proc
+
1
)
%
numProc
,
emitState
);
pmapSeed
(
randSeed
+
(
proc
+
2
)
%
numProc
,
cntState
);
pmapSeed
(
randSeed
+
(
proc
+
3
)
%
numProc
,
mediumState
);
pmapSeed
(
randSeed
+
(
proc
+
4
)
%
numProc
,
scatterState
);
pmapSeed
(
randSeed
+
(
proc
+
5
)
%
numProc
,
rouletteState
);
#ifdef PMAP_SIGUSR
{
double
partNumEmit
;
unsigned
long
partEmitCnt
;
double
srcPhotonFlux
,
avgPhotonFlux
;
unsigned
portCnt
,
passCnt
,
prePassCnt
;
float
srcPreDistrib
;
double
srcNumEmit
;
/* # to emit from source */
unsigned
long
srcNumDistrib
;
/* # stored */
void
sigUsrDiags
()
/* Loop diags via SIGUSR1 */
{
sprintf
(
errmsg
,
"********************* Proc %d Diags *********************
\n
"
"srcIdx = %d (%s)
\n
portCnt = %d (%s)
\n
passCnt = %d
\n
"
"srcFlux = %f
\n
srcPhotonFlux = %f
\n
avgPhotonFlux = %f
\n
"
"partNumEmit = %f
\n
partEmitCnt = %lu
\n\n
"
,
proc
,
srcIdx
,
findmaterial
(
source
[
srcIdx
].
so
)
->
oname
,
portCnt
,
photonPorts
[
portCnt
].
so
->
oname
,
passCnt
,
srcFlux
[
srcIdx
],
srcPhotonFlux
,
avgPhotonFlux
,
partNumEmit
,
partEmitCnt
);
eputs
(
errmsg
);
fflush
(
stderr
);
}
}
#endif
#ifdef PMAP_SIGUSR
signal
(
SIGUSR1
,
sigUsrDiags
);
#endif
#ifdef DEBUG_PMAP
/* Output child process PID after random delay to prevent corrupted
* console output due to race condition */
usleep
(
1e6
*
pmapRandom
(
rouletteState
));
fprintf
(
stderr
,
"Proc %d: PID = %d (waiting 10 sec to attach debugger...)
\n
"
,
proc
,
getpid
()
);
/* Allow time for debugger to attach to child process */
sleep
(
10
);
#endif
/* =============================================================
* 2-PASS PHOTON DISTRIBUTION
* Pass 1 (pre): emit fraction of target photon count
* Pass 2 (main): based on outcome of pass 1, estimate remaining
* number of photons to emit to approximate target
* count
* ============================================================= */
for
(
srcIdx
=
0
;
srcIdx
<
nsources
;
srcIdx
++
)
{
#ifndef PMAP_SIGUSR
unsigned
portCnt
,
passCnt
=
0
,
prePassCnt
=
0
;
float
srcPreDistrib
=
preDistrib
;
double
srcNumEmit
=
0
;
/* # to emit from source */
unsigned
long
srcNumDistrib
=
pmap
->
numPhotons
;
/* #stored */
#else
passCnt
=
prePassCnt
=
0
;
srcPreDistrib
=
preDistrib
;
srcNumEmit
=
0
;
/* # to emit from source */
srcNumDistrib
=
pmap
->
numPhotons
;
/* # stored */
#endif
if
(
srcFlux
[
srcIdx
]
<
FTINY
)
/* Source has zero emission or was skipped in prepass
because its contributions are not sought */
continue
;
while
(
passCnt
<
2
)
{
if
(
!
passCnt
)
{
/* INIT PASS 1 */
if
(
++
prePassCnt
>
maxPreDistrib
)
{
/* Warn if no photons contributed after sufficient
* iterations; only output from subprocess 0 to reduce
* console clutter */
if
(
!
proc
)
{
sprintf
(
errmsg
,
"source %s: too many prepasses, skipped"
,
source
[
srcIdx
].
so
->
oname
);
error
(
WARNING
,
errmsg
);
}
break
;
}
/* Num to emit is fraction of target count */
srcNumEmit
=
srcPreDistrib
*
srcDistribTarget
;
}
else
{
/* INIT PASS 2 */
#ifndef PMAP_SIGUSR
double
srcPhotonFlux
,
avgPhotonFlux
;
#endif
/* Based on the outcome of the predistribution we can now
* figure out how many more photons we have to emit from
* the current source to meet the target count,
* srcDistribTarget. This value is clamped to 0 in case
* the target has already been exceeded in pass 1.
* srcNumEmit and srcNumDistrib is the number of photons
* emitted and distributed (stored) from the current
* source in pass 1, respectively. */
srcNumDistrib
=
pmap
->
numPhotons
-
srcNumDistrib
;
srcNumEmit
*=
srcNumDistrib
?
max
(
srcDistribTarget
/
srcNumDistrib
,
1
)
-
1
:
0
;
if
(
!
srcNumEmit
)
/* No photons left to distribute in main pass */
break
;
srcPhotonFlux
=
srcFlux
[
srcIdx
]
/
srcNumEmit
;
avgPhotonFlux
=
photonFluxSum
/
(
srcIdx
+
1
);
if
(
avgPhotonFlux
>
FTINY
&&
srcPhotonFlux
/
avgPhotonFlux
<
FTINY
)
{
/* Skip source if its photon flux is grossly below the
* running average, indicating negligible contributions
* at the expense of excessive distribution time; only
* output from subproc 0 to reduce console clutter */
if
(
!
proc
)
{
sprintf
(
errmsg
,
"source %s: itsy bitsy photon flux, skipped"
,
source
[
srcIdx
].
so
->
oname
);
error
(
WARNING
,
errmsg
);
}
srcNumEmit
=
0
;
/* Or just break??? */
}
/* Update sum of photon flux per light source */
photonFluxSum
+=
srcPhotonFlux
;
}
portCnt
=
0
;
do
{
/* Need at least one iteration if no ports! */
emap
.
src
=
source
+
srcIdx
;
emap
.
port
=
emap
.
src
->
sflags
&
SDISTANT
?
photonPorts
+
portCnt
:
NULL
;
photonPartition
[
emap
.
src
->
so
->
otype
]
(
&
emap
);
if
(
verbose
&&
!
proc
)
{
/* Output from subproc 0 only to avoid race condition
* on console I/O */
if
(
!
passCnt
)
sprintf
(
errmsg
,
"
\t
PREPASS %d on source %s "
,
prePassCnt
,
source
[
srcIdx
].
so
->
oname
);
else
sprintf
(
errmsg
,
"
\t
MAIN PASS on source %s "
,
source
[
srcIdx
].
so
->
oname
);
if
(
emap
.
port
)
{
sprintf
(
errmsg2
,
"via port %s "
,
photonPorts
[
portCnt
].
so
->
oname
);
strcat
(
errmsg
,
errmsg2
);
}
sprintf
(
errmsg2
,
"(%lu partitions)
\n
"
,
emap
.
numPartitions
);
strcat
(
errmsg
,
errmsg2
);
eputs
(
errmsg
);
#if NIX
fflush
(
stderr
);
#endif
}
for
(
emap
.
partitionCnt
=
0
;
emap
.
partitionCnt
<
emap
.
numPartitions
;
emap
.
partitionCnt
++
)
{
#ifndef PMAP_SIGUSR
double
partNumEmit
;
unsigned
long
partEmitCnt
;
#endif
/* Get photon origin within current source partishunn
* and build emission map */
photonOrigin
[
emap
.
src
->
so
->
otype
]
(
&
emap
);
initPhotonEmission
(
&
emap
,
pdfSamples
);
/* Number of photons to emit from ziss partishunn;
* scale according to its normalised contribushunn to
* the emitted source flux */
partNumEmit
=
srcNumEmit
*
colorAvg
(
emap
.
partFlux
)
/
srcFlux
[
srcIdx
];
partEmitCnt
=
(
unsigned
long
)
partNumEmit
;
/* Probabilistically account for fractional photons */
if
(
pmapRandom
(
cntState
)
<
partNumEmit
-
partEmitCnt
)
partEmitCnt
++
;
/* Update local and shared global emission counter */
photonCnt
[
PHOTONCNT_NUMEMIT
(
srcIdx
)]
+=
partEmitCnt
;
localNumEmitted
+=
partEmitCnt
;
/* Integer counter avoids FP rounding errors during
* iteration */
while
(
partEmitCnt
--
)
{
RAY
photonRay
;
/* Emit photon according to PDF (if any), allocate
* associated contribution origin, and trace through
* scene until absorbed/leaked; emitPhoton() sets the
* emitting light source index in photonRay */
emitPhoton
(
&
emap
,
&
photonRay
);
#if 1
if
(
emap
.
port
)
/* !!! PHOTON PORT REJECTION SAMPLING HACK: set
* !!! photon port as fake hit object for
* !!! photon ray to check for intersection in
* !!! tracePhoton() */
photonRay
.
ro
=
emap
.
port
->
so
;
#endif
newPhotonOrigin
(
pmap
,
&
photonRay
,
orgHeap
[
proc
]);
/* Set subprocess index in photonRay for post-
* distrib contribution origin index linearisation;
* this is propagated with the origin index in
* photonRay and set for photon hits by newPhoton() */
PMAP_SETRAYPROC
(
&
photonRay
,
proc
);
tracePhoton
(
&
photonRay
);
}
/* Update shared global photon count */
photonCnt
[
PHOTONCNT_NUMPHOT
]
+=
pmap
->
numPhotons
-
lastNumPhotons
;
lastNumPhotons
=
pmap
->
numPhotons
;
#if !NIX
/* Synchronous progress report on Windoze */
if
(
!
proc
&&
photonRepTime
>
0
&&
time
(
NULL
)
>=
repLastTime
+
photonRepTime
)
{
unsigned
s
;
repComplete
=
pmap
->
distribTarget
*
numProc
;
repProgress
=
photonCnt
[
PHOTONCNT_NUMPHOT
];
for
(
repEmitted
=
0
,
s
=
0
;
s
<
nsources
;
s
++
)
repEmitted
+=
photonCnt
[
PHOTONCNT_NUMEMIT
(
s
)];
pmapDistribReport
();
}
#endif
}
portCnt
++
;
}
while
(
portCnt
<
numPhotonPorts
);
if
(
pmap
->
numPhotons
==
srcNumDistrib
)
{
/* Double predistrib factor in case no photons were stored
* for this source and redo pass 1 */
srcPreDistrib
*=
2
;
}
else
{
/* Now do pass 2 */
passCnt
++
;
}
}
}
/* Flush heap buffa one final time to prevent data corruption */
flushPhotonHeap
(
pmap
);
/* Flush last contribution origin to origin heap file */
newPhotonOrigin
(
pmap
,
NULL
,
orgHeap
[
proc
]);
/* Heap files closed automatically on exit
fclose(pmap -> heap);
fclose(orgHeap [proc]); */
#ifdef DEBUG_PMAP
sprintf
(
errmsg
,
"Proc %d total %ld photons
\n
"
,
proc
,
pmap
->
numPhotons
);
eputs
(
errmsg
);
fflush
(
stderr
);
#endif
#ifdef PMAP_SIGUSR
signal
(
SIGUSR1
,
SIG_DFL
);
#endif
#if NIX
/* Terminate subprocess */
exit
(
0
);
#endif
}
else
if
(
pid
<
0
)
error
(
SYSTEM
,
"failed to fork subprocess in distribPhotonContrib"
);
}
#if NIX
/* PARENT PROCESS CONTINUES HERE */
#ifdef SIGCONT
/* Enable progress report signal handler */
signal
(
SIGCONT
,
pmapDistribReport
);
#endif
/* Wait for subprocesses to complete while reporting progress */
proc
=
numProc
;
while
(
proc
)
{
while
(
waitpid
(
-
1
,
&
stat
,
WNOHANG
)
>
0
)
{
/* Subprocess exited; check status */
if
(
!
WIFEXITED
(
stat
)
||
WEXITSTATUS
(
stat
))
error
(
USER
,
"failed photon distribution"
);
--
proc
;
}
/* Nod off for a bit and update progress */
sleep
(
1
);
/* Asynchronous progress report from shared subprocess counters */
repComplete
=
pmap
->
distribTarget
*
numProc
;
repProgress
=
photonCnt
[
PHOTONCNT_NUMPHOT
];
for
(
repEmitted
=
0
,
srcIdx
=
0
;
srcIdx
<
nsources
;
srcIdx
++
)
repEmitted
+=
photonCnt
[
PHOTONCNT_NUMEMIT
(
srcIdx
)];
/* Get global photon count from shmem updated by subprocs */
pmap
->
numPhotons
=
photonCnt
[
PHOTONCNT_NUMPHOT
];
if
(
photonRepTime
>
0
&&
time
(
NULL
)
>=
repLastTime
+
photonRepTime
)
pmapDistribReport
();
#ifdef SIGCONT
else
signal
(
SIGCONT
,
pmapDistribReport
);
#endif
}
#endif
/* NIX */
/* ================================================================
* POST-DISTRIBUTION - Set photon flux and build kd-tree, etc.
* ================================================================ */
#ifdef SIGCONT
/* Reset signal handler */
signal
(
SIGCONT
,
SIG_DFL
);
#endif
free
(
emap
.
samples
);
if
(
!
pmap
->
numPhotons
)
error
(
USER
,
"empty contribution photon map"
);
/* Load per-subprocess contribution origins into pmap -> contribOrg */
/* Dumb compilers apparently need the char** cast */
pmap
->
numContribOrg
=
buildOrigins
(
pmap
,
orgHeap
,
(
char
**
)
orgHeapFname
,
orgOfs
,
numProc
);
if
(
!
pmap
->
numContribOrg
)
error
(
INTERNAL
,
"no origins in contribution photon map"
);
/* Set photon flux per source */
/* TODO: HOW/WHERE DO WE HANDLE COEFFICIENT MODE??? */
for
(
srcIdx
=
0
;
srcIdx
<
nsources
;
srcIdx
++
)
srcFlux
[
srcIdx
]
/=
photonCnt
[
PHOTONCNT_NUMEMIT
(
srcIdx
)];
#if NIX
/* Photon counters no longer needed, unmap shared memory */
munmap
(
photonCnt
,
sizeof
(
*
photonCnt
));
close
(
shmFile
);
unlink
(
shmFname
);
#else
free
(
photonCnt
);
#endif
if
(
verbose
)
{
eputs
(
"
\n
Building contribution photon map...
\n
"
);
#if NIX
fflush
(
stderr
);
#endif
}
/* Build underlying data structure; heap is destroyed */
buildPhotonMap
(
pmap
,
srcFlux
,
orgOfs
,
numProc
);
/* Precompute binned photon contributions */
if
(
verbose
)
eputs
(
"
\n
"
);
preComputeContrib
(
contribPmap
);
/* Free per-subprocess origin heap files */
for
(
proc
=
0
;
proc
<
numProc
;
proc
++
)
free
(
orgHeapFname
[
proc
]);
free
(
orgHeapFname
);
free
(
orgHeap
);
free
(
orgOfs
);
if
(
verbose
)
eputs
(
"
\n
"
);
}
Event Timeline
Log In to Comment