diff --git a/Rmakefile b/Rmakefile index 92147bb..9afc2cf 100644 --- a/Rmakefile +++ b/Rmakefile @@ -1,557 +1,559 @@ # RCSid: $Id: Rmakefile,v 2.87 2021/01/19 23:31:47 greg Exp $ # # Compiles for ray tracing programs. # OPT = -O MACH = -DBSD CFLAGS = -I../common -L../lib $(OPT) $(MACH) SPECIAL = CC = cc AR = ar MLIB = -lm LINT = lint LINTFLAGS = -DBSD # # The following are user-definable: # DESTDIR = . INSTDIR = /usr/local/bin INSTALL = cp # # The following paths must exist and be relative to root: # DEVDIR = $(INSTDIR)/dev LIBDIR = /usr/local/lib/ray # # Library routines: # RLIB = ../lib/libradiance.a RCLIB = ../lib/libraycalls.a LIBS = -lrtrad $(MLIB) # # Device drivers for rvu (see also devtable.c): # DOBJS = devtable.o devcomm.o editline.o x11.o x11twind.o \ colortab.o DSRC = devtable.c devcomm.c editline.c x11.c x11twind.c \ colortab.c DLIBS = -lX11 # # Standard object files: # RTOBJS = rtmain.o rtrace.o duphead.o persist.o RTSRC = rtmain.c rtrace.c duphead.c persist.c RPOBJS = rpmain.o rpict.o srcdraw.o duphead.o persist.o RPSRC = rpmain.c rpict.c srcdraw.c duphead.c persist.c RVOBJS = rvmain.o rview.o rv2.o rv3.o $(DOBJS) RVSRC = rvmain.c rview.c rv2.c rv3.c $(DSRC) RCOBJS = rcmain.o rcontrib.o rc2.o rc3.o RCSRC = rcmain.c rcontrib.c rc2.c rc3.c RLOBJS = raycalls.o raypcalls.o rayfifo.o RLSRC = raycalls.c raypcalls.c rayfifo.c ROBJS = $(RAYOBJS) $(SURFOBJS) $(MATOBJS) \ $(MODOBJS) $(SUPPOBJS) $(PMOBJS) RSRC = $(RAYSRC) $(SURFSRC) $(MATSRC) \ $(MODSRC) $(SUPPSRC) RAYOBJS = ambcomp.o ambient.o ambio.o freeobjmem.o initotypes.o \ preload.o raytrace.o renderopts.o RAYSRC = ambcomp.c ambient.c ambio.c freeobjmem.c initotypes.c \ preload.c raytrace.c renderopts.c SURFOBJS = source.o sphere.o srcobstr.o srcsupp.o srcsamp.o virtuals.o \ o_face.o o_cone.o o_instance.o o_mesh.o SURFSRC = sphere.c source.c srcobstr.c srcsupp.c srcsamp.c virtuals.c \ o_face.c srcsamp.c o_cone.c o_instance.c o_mesh.c MATOBJS = aniso.o normal.o dielectric.o m_clip.o glass.o m_brdf.o \ m_mirror.o m_direct.o m_mist.o fprism.o m_alias.o m_bsdf.o \ ashikhmin.o MATSRC = aniso.c normal.c dielectric.c m_clip.c glass.c m_brdf.c \ m_mirror.c m_direct.c m_mist.c fprism.c m_alias.c m_bsdf.c \ ashikhmin.c MODOBJS = p_func.o t_func.o p_data.o t_data.o text.o mx_func.o mx_data.o MODSRC = p_func.c t_func.c p_data.c t_data.c text.c mx_func.c mx_data.c SUPPOBJS = func.o noise3.o data.o SUPPSRC = func.c noise3.c data.c PMOBJS = pmap.o pmapsrc.o pmapmat.o pmaprand.o pmapio.o pmapdata.o \ pmapdens.o pmapbias.o pmapparm.o pmapamb.o pmapray.o \ pmapopt.o pmapdiag.o pmaptype.o morton.o oococt.o oocsort.o \ oocbuild.o oocnn.o ooccache.o pmutil.o pmaproi.o pmapfilt.o \ $(PMRCOBJS) PMSRC = pmap.c pmapsrc.c pmapmat.c pmaprand.c pmapio.c pmapdata.c \ pmapdens.c pmapbias.c pmapparm.c pmapamb.c pmapray.c \ pmapopt.c pmapdiag.c pmaptype.c morton.c oococt.c oocsort.c \ oocbuild.c oocnn.c ooccache.c pmutil.c pmapfilt.c pmaproi.c \ pmapkdt.c pmaptkdt.c pmapooc.c $(PMRCSRC) PMRCOBJS = pmapcontrib.o pmcontrib2.o pmcontrib3.o pmcontrib4.o \ - pmcontribsort.o wavelet.o wavelet2.o wavelet3.o mrgbe.o + pmcontribsort.o pmcontribcache.o wavelet.o wavelet2.o wavelet3.o \ + mrgbe.o PMRCSRC = pmapcontrib.c pmcontrib2.c pmcontrib3.c pmcontrib4.c \ - pmcontribsort.c wavelet.c wavelet2.c wavelet3.c mrgbe.c + pmcontribsort.c pmcontribcache.c wavelet.c wavelet2.c wavelet3.c \ + mrgbe.c HEADERS = ambient.h ray.h data.h otspecial.h source.h # # What this makefile produces: # PROGS = $(DESTDIR)/rtrace $(DESTDIR)/rpict $(DESTDIR)/rvu $(DESTDIR)/rcontrib \ $(DESTDIR)/lookamb $(DESTDIR)/mkpmap $(DESTDIR)/pmapdump all: $(PROGS) $(RCLIB) $(SPECIAL) install: all rayinit.cal $(INSTALL) $(PROGS) $(INSTDIR) rm -f $(LIBDIR)/rayinit.cal cp rayinit.cal $(LIBDIR) ogl: clean: set nonomatch; rm -f $(PROGS) *.o lint: $(RVSRC) $(LINT) $(LINTFLAGS) -DRVIEW $(RVSRC) $(LIBS) # Preocmputed contrib pmap unit tests wavelet-test: wavelet.c wavelet.h $(CC) $(CFLAGS) -DWAVELET_TEST_1D -DWAVELET_DBG \ -o wavelet-test -g wavelet.c $(MLIB) wavelet2-test: wavelet2.c wavelet2.h mrgbe.c mrgbe.h $(CC) $(CFLAGS) -DWAVELET_TEST_2D -DWAVELET_TEST_mRGBE -DWAVELET_DBG \ -o wavelet2-test -g wavelet2.c mrgbe.c $(MLIB) wavelet3-test: wavelet3.c wavelet2.c wavelet2.h mrgbe.c mrgbe.h $(CC) $(CFLAGS) -DWAVELET_TEST_2DPAD -DWAVELET_TEST_mRGBE -DWAVELET_DBG \ -o wavelet3-test -g wavelet3.c wavelet2.c mrgbe.c $(MLIB) mrgbe-test: mrgbe.c mrgbe.h $(CC) $(CFLAGS) -DmRGBE_TEST -o mrgbe-test -g mrgbe.c $(LIBS) pmapcontrib-test: pmapcontrib.c pmapcontrib.h $(CC) $(CFLAGS) -DPMAP_CONTRIB -DPMAP_CONTRIB_TEST -o pmapcontrib-test \ -g pmapcontrib.c $(LIBS) # # Links: # $(DESTDIR)/rtrace: $(RTOBJS) $(RCLIB) $(RLIB) $(CC) $(CFLAGS) -o $(DESTDIR)/rtrace $(RTOBJS) $(RCLIB) $(RLIB) $(LIBS) $(DESTDIR)/rpict: $(RPOBJS) $(RLIB) $(CC) $(CFLAGS) -o $(DESTDIR)/rpict $(RPOBJS) $(RLIB) $(LIBS) $(DESTDIR)/rvu: $(RVOBJS) $(RCLIB) $(RLIB) $(CC) $(CFLAGS) -o $(DESTDIR)/rvu $(RVOBJS) $(RCLIB) \ $(RLIB) $(LIBS) $(DLIBS) $(DESTDIR)/rcontrib: $(RCOBJS) $(RLIB) $(CC) $(CFLAGS) -o $(DESTDIR)/rcontrib $(RCOBJS) $(RLIB) $(LIBS) $(DESTDIR)/lookamb: lookamb.o ambio.o $(CC) $(CFLAGS) -o $(DESTDIR)/lookamb lookamb.o ambio.o $(LIBS) $(DESTDIR)/mkpmap: mkpmap.o $(RLIB) $(CC) $(CFLAGS) -o $(DESTDIR)/mkpmap mkpmap.o $(RLIB) $(LIBS) $(DESTDIR)/pmapdump: pmapdump.o pmaptype.o pmapparm.o $(CC) $(CFLAGS) -o pmapdump pmapdump.o pmaptype.o pmapparm.o $(RLIB) $(LIBS) $(RLIB): $(ROBJS) Version.o rm -f $(RLIB) $(AR) rc $(RLIB) $(ROBJS) Version.o -ranlib $(RLIB) $(RCLIB): $(RLOBJS) rm -f $(RCLIB) $(AR) rc $(RCLIB) $(RLOBJS) -ranlib $(RCLIB) # # Uncomment the following to model dispersion: # dielectric.o: dielectric.c source.h $(CC) $(CFLAGS) -DDISPERSE -c dielectric.c # end of dispersion compiles. devcomm.o: devcomm.c $(CC) $(CFLAGS) -DDEVPATH=\"$(DEVDIR)\" -c devcomm.c # # Version module: # Version.c: VERSION $(RSRC) $(HEADERS) ( cat VERSION ; date ; whoami ; hostname ) > Version.c ed - Version.c < verscript.ed || rm Version.c # # Include dependencies: # ambio.o colortab.o data.o devcomm.o \ devmain.o lookamb.o rview.o x11.o: ../common/color.h freeobjmem.o o_cone.o srcsupp.o: ../common/cone.h data.o freeobjmem.o m_brdf.o mx_data.o \ p_data.o raycalls.o t_data.o: data.h devcomm.o devmain.o devtable.o \ editline.o x11.o: driver.h freeobjmem.o o_face.o srcsupp.o: ../common/face.h ambient.o raytrace.o rpmain.o rtmain.o \ rtrace.o rvmain.o rv2.o rv3.o: ../common/octree.h o_instance.o: ../common/instance.h ambient.o aniso.o ashikhmin.o dielectric.o freeobjmem.o func.o glass.o \ initotypes.o m_brdf.o m_bsdf.o m_direct.o m_mirror.o normal.o o_cone.o \ preload.o raycalls.o raytrace.o rtrace.o rv2.o source.o sphere.o \ srcsupp.o text.o srcdraw.o srcobstr.o virtuals.o: ../common/otypes.h ambient.o ambcomp.o aniso.o ashikhmin.o normal.o raycalls.o raytrace.o \ rpict.o rvmain.o rtmain.o rpmain.o rcmain.o persist.o source.o rv3.o \ srcsamp.o virtuals.o: ../common/random.h ambcomp.o ambient.o aniso.o ashikhmin.o dielectric.o freeobjmem.o func.o \ glass.o m_bsdf.o m_brdf.o m_clip.o m_direct.o m_mirror.o m_mist.o mx_data.o \ o_mesh.o mx_func.o normal.o o_cone.o o_face.o o_instance.o p_data.o p_func.o \ raycalls.o raypcalls.o rayfifo.o raytrace.o rpict.o rtrace.o rv2.o rv3.o rview.o \ source.o sphere.o srcdraw.o srcobstr.o srcsamp.o srcsupp.o t_data.o t_func.o \ text.o rpmain.o rtmain.o rvmain.o virtuals.o m_alias.o rcmain.o \ rcontrib.o rc2.o rc3.o: ray.h \ ../common/standard.h ../common/rtmisc.h ../common/rtio.h ../common/rtmath.h \ ../common/rterror.h ../common/octree.h \ ../common/mat4.h ../common/fvect.h ../common/object.h ../common/color.h rv2.o rv3.o rview.o: rpaint.h driver.h ../common/view.h ../common/resolu.h m_direct.o m_mirror.o m_mist.o dielectric.o raycalls.o \ rpict.o rpmain.o rtmain.o rvmain.o source.o srcdraw.o \ srcobstr.o srcsamp.o srcsupp.o virtuals.o: source.h cone.o data.o devcomm.o initotypes.o fprism.o preload.o \ duphead.o octree.o: ../common/standard.h ../common/rtmisc.h \ ../common/rtio.h ../common/rtmath.h \ ../common/rterror.h ../common/mat4.h ../common/fvect.h ambio.o: ../common/fvect.h ambient.o initotypes.o m_alias.o pmapcontrib.o pmapdata.o pmapsrc.o \ pmcontrib4.o raytrace.o rtrace.o rv2.o rv3.o source.o \ srcdraw.o srcobstr.o virtuals.o: otspecial.h rpmain.o rtmain.o rvmain.o rpict.o \ srcdraw.o: ../common/view.h ../common/resolu.h rpict.o: ../common/hilbert.h x11.o x11twind.o: x11twind.h x11.o: x11icon.h ambient.o ambcomp.o ambio.o lookamb.o raycalls.o: ambient.h data.o rpmain.o rtmain.o rvmain.o rpict.o rtrace.o \ rv2.o: ../common/resolu.h aniso.o func.o m_brdf.o m_direct.o mx_data.o mx_func.o p_data.o \ p_func.o t_data.o t_func.o: func.h ../common/calcomp.h preload.o: data.h func.h ../common/object.h ../common/face.h \ ../common/cone.h ../common/instance.h ../common/mesh.h \ ../common/color.h ../common/bsdf.h ../common/otypes.h rtmain.o rpmain.o rvmain.o persist.o duphead.o \ renderopts.o rpict.o: ../common/paths.h freeobjmem.o raycalls.o text.o: ../common/font.h raypcalls.o: ../common/selcall.h o_mesh.o: ../common/mesh.h noise3.o: ../common/calcomp.h aniso.o ashikhmin.o dielectric.o freeobjmem.o glass.o initotypes.o \ m_alias.o m_brdf.o m_clip.o m_direct.o m_mirror.o m_mist.o \ mx_data.o mx_func.o normal.o o_cone.o o_face.o o_instance.o \ o_mesh.o p_data.o p_func.o source.o sphere.o t_data.o t_func.o \ srcobstr.o text.o: rtotypes.h m_bsdf.o: ambient.h source.h func.h \ ../common/calcomp.h ../common/bsdf.h ../common/random.h rcmain.o rcontrib.o rc2.o rc3.o: rcontrib.h \ ../common/platform.h ../common/paths.h ../common/lookup.h \ func.h ../common/calcomp.h ../common/rtprocess.h ambient.o rcmain.o: ambient.h rcmain.o: source.h rcontrib.o: source.h ../common/otypes.h rc2.o: ../common/resolu.h rc3.o: ../common/selcall.h # # Photon map include dependencies (via 'gcc -MM -I../common') # TODO: Update after adding pmaproi module! # ambient.o: pmapparm.h pmaptype.h pmapamb.h pmapdata.h dielectric.o glass.o normal.o m_brdf.o m_bsdf.o ashikhmin.o aniso.o: \ pmapparm.h pmaptype.h pmapmat.h pmap.h pmapdata.h raycalls.o rpmain.o rcmain.o rtmain.o rvmain.o: \ pmapparm.h pmaptype.h pmapray.h rcmain.o: pmapparm.h pmaptype.h pmapray.h pmapcontrib.h pmapdata.h raytrace.o: pmapparm.h pmaptype.h pmap.h pmapdata.h renderopts.o: pmapparm.h pmaptype.h pmapopt.h rpict.o: pmapparm.h pmaptype.h pmapbias.h pmapdata.h pmapdiag.h source.o: pmapparm.h pmaptype.h pmap.h pmapdata.h pmapsrc.h pmapbias.o: pmapbias.c pmapbias.h pmapdata.h ray.h ../common/standard.h \ ../common/copyright.h ../common/rtio.h ../common/rtmisc.h \ ../common/rtmath.h ../common/tiff.h ../common/mat4.h ../common/fvect.h \ ../common/rterror.h ../common/octree.h ../common/object.h \ ../common/color.h pmapparm.h pmaptype.h ../common/lookup.h pmap.h \ pmaprand.h pmapdiag.o: pmapdiag.c pmapdiag.h ../common/platform.h pmapdata.h ray.h \ ../common/standard.h ../common/copyright.h ../common/rtio.h \ ../common/rtmisc.h ../common/rtmath.h ../common/tiff.h ../common/mat4.h \ ../common/fvect.h ../common/rterror.h ../common/octree.h \ ../common/object.h ../common/color.h pmapparm.h pmaptype.h \ ../common/lookup.h pmapio.o: pmapio.c pmapio.h pmapdata.h ray.h ../common/standard.h \ ../common/copyright.h ../common/rtio.h ../common/rtmisc.h \ ../common/rtmath.h ../common/tiff.h ../common/mat4.h ../common/fvect.h \ ../common/rterror.h ../common/octree.h ../common/object.h \ ../common/color.h pmapparm.h pmaptype.h ../common/paths.h \ ../common/lookup.h pmapdiag.h ../common/platform.h ../common/resolu.h \ pmapkdt.h pmapkdt.c pmapooc.h pmapooc.c pmapray.o: pmapray.c pmapray.h ray.h ../common/standard.h \ ../common/copyright.h ../common/rtio.h ../common/rtmisc.h \ ../common/rtmath.h ../common/tiff.h ../common/mat4.h ../common/fvect.h \ ../common/rterror.h ../common/octree.h ../common/object.h \ ../common/color.h pmapparm.h pmaptype.h pmap.h pmapdata.h \ ../common/lookup.h pmaptype.o: pmaptype.c pmaptype.h ooccache.o: ooccache.c ooccache.h oocsort.o: oocsort.c oocsort.h ../common/fvect.h morton.h oocbuild.o: oocbuild.c oococt.h morton.h ../common/fvect.h ooccache.h \ oocsort.h oococt.o: oococt.c oococt.h morton.h ../common/fvect.h ooccache.h \ ../common/rtio.h oocnn.o: oocnn.c oocnn.h oococt.h morton.h ../common/fvect.h \ ooccache.h pmapfilt.h ../common/lookup.h oocsort.h pmapfilt.o: pmapfilt.c pmapfilt.h ../common/lookup.h pmapdata.h ray.h \ ../common/standard.h ../common/copyright.h ../common/rtio.h \ ../common/rtmisc.h ../common/rtmath.h ../common/tiff.h ../common/mat4.h \ ../common/fvect.h ../common/rterror.h ../common/octree.h \ ../common/object.h ../common/color.h pmapparm.h pmaptype.h \ ../common/paths.h ../common/random.h source.h otspecial.h \ pmapkdt.h pmapkdt.c pmapooc.h pmapooc.c pmapdens.o: pmapdens.c pmapdens.h pmap.h pmapparm.h pmaptype.h pmapdata.h \ ray.h ../common/standard.h ../common/copyright.h ../common/rtio.h \ ../common/rtmisc.h ../common/rtmath.h ../common/tiff.h ../common/mat4.h \ ../common/fvect.h ../common/rterror.h ../common/octree.h \ ../common/object.h ../common/color.h ../common/paths.h \ ../common/lookup.h pmapbias.h ../common/otypes.h \ pmapkdt.h pmapkdt.c pmaptkdt.h pmaptkdt.c pmapooc.h pmapooc.c pmapdump.o: pmapdump.c pmap.h pmapparm.h pmaptype.h pmapdata.h ray.h \ ../common/standard.h ../common/copyright.h ../common/rtio.h \ ../common/rtmisc.h ../common/rtmath.h ../common/tiff.h ../common/mat4.h \ ../common/fvect.h ../common/rterror.h ../common/octree.h \ ../common/object.h ../common/color.h ../common/paths.h \ ../common/lookup.h pmapio.h ../common/rtio.h \ ../common/resolu.h ../common/random.h \ pmapkdt.h pmapkdt.c pmaptkdt.h pmaptkdt.c pmapooc.h pmapooc.c pmapdata.o: pmapdata.c pmapdata.h ray.h ../common/standard.h \ ../common/copyright.h ../common/rtio.h ../common/rtmisc.h \ ../common/rtmath.h ../common/tiff.h ../common/mat4.h ../common/fvect.h \ ../common/rterror.h ../common/octree.h ../common/object.h \ ../common/color.h pmapparm.h pmaptype.h ../common/paths.h \ ../common/lookup.h pmapdens.h pmap.h pmaprand.h pmapmat.h \ ../common/otypes.h ../common/random.h source.h otspecial.h \ pmapkdt.h pmapkdt.c pmaptkdt.h pmaptkdt.c pmapooc.h pmapooc.c pmap.o: pmap.c pmap.h pmapparm.h pmaptype.h pmapdata.h ray.h \ ../common/standard.h ../common/copyright.h ../common/rtio.h \ ../common/rtmisc.h ../common/rtmath.h ../common/tiff.h ../common/mat4.h \ ../common/fvect.h ../common/rterror.h ../common/octree.h \ ../common/object.h ../common/color.h ../common/paths.h \ ../common/lookup.h pmapmat.h pmapsrc.h source.h pmaprand.h pmapio.h \ pmapdens.h pmapbias.h pmapdiag.h ../common/platform.h ../common/otypes.h \ pmapkdt.h pmapkdt.c pmaptkdt.h pmaptkdt.c pmapooc.h pmapooc.c pmapamb.o: pmapamb.c pmapamb.h pmapdata.h ray.h ../common/standard.h \ ../common/copyright.h ../common/rtio.h ../common/rtmisc.h \ ../common/rtmath.h ../common/tiff.h ../common/mat4.h ../common/fvect.h \ ../common/rterror.h ../common/octree.h ../common/object.h \ ../common/color.h pmapparm.h pmaptype.h ../common/paths.h \ ../common/lookup.h pmap.h \ pmapkdt.h pmapkdt.c pmapooc.h pmapooc.c pmapmat.o: pmapmat.c pmapmat.h pmap.h pmapparm.h pmaptype.h pmapdata.h \ ray.h ../common/standard.h ../common/copyright.h ../common/rtio.h \ ../common/rtmisc.h ../common/rtmath.h ../common/tiff.h ../common/mat4.h \ ../common/fvect.h ../common/rterror.h ../common/octree.h \ ../common/object.h ../common/color.h ../common/paths.h \ ../common/lookup.h pmaprand.h ../common/otypes.h data.h func.h \ ../common/calcomp.h ../common/bsdf.h ../common/ccolor.h \ ../common/platform.h \ pmapkdt.h pmapkdt.c pmapooc.h pmapooc.c pmapsrc.o: pmapsrc.c pmapsrc.h ray.h ../common/standard.h \ ../common/copyright.h ../common/rtio.h ../common/rtmisc.h \ ../common/rtmath.h ../common/tiff.h ../common/mat4.h ../common/fvect.h \ ../common/rterror.h ../common/octree.h ../common/object.h \ ../common/color.h pmapparm.h pmaptype.h source.h pmap.h pmapdata.h \ ../common/paths.h ../common/lookup.h pmaprand.h \ ../common/otypes.h otspecial.h \ pmapkdt.h pmapkdt.c pmapooc.h pmapooc.c pmapopt.o: pmapopt.c ray.h ../common/standard.h ../common/copyright.h \ ../common/rtio.h ../common/rtmisc.h ../common/rtmath.h ../common/tiff.h \ ../common/mat4.h ../common/fvect.h ../common/rterror.h \ ../common/octree.h ../common/object.h ../common/color.h pmapparm.h \ pmaptype.h pmapparm.o: pmapparm.c pmapparm.h pmaptype.h pmapdata.h ray.h \ ../common/standard.h ../common/copyright.h ../common/rtio.h \ ../common/rtmisc.h ../common/rtmath.h ../common/tiff.h ../common/mat4.h \ ../common/fvect.h ../common/rterror.h ../common/octree.h \ ../common/object.h ../common/color.h ../common/paths.h ../common/lookup.h \ pmapkdt.h pmapkdt.c pmapooc.h pmapooc.c pmutil.o: pmutil.h pmutil.c pmap.h pmapparm.h pmaptype.h pmapdata.h ray.h \ ../common/standard.h ../common/copyright.h ../common/rtio.h \ ../common/rtmisc.h ../common/rtmath.h ../common/tiff.h ../common/mat4.h \ ../common/fvect.h ../common/rterror.h ../common/octree.h \ ../common/object.h ../common/color.h ../common/paths.h \ ../common/lookup.h pmapio.h \ pmapkdt.h pmapkdt.c pmapooc.h pmapooc.c pmaproi.o: pmaproi.c pmaproi.h ray.h ../common/standard.h \ ../common/copyright.h ../common/rtio.h ../common/rtmisc.h \ ../common/rtmath.h ../common/tiff.h ../common/mat4.h ../common/fvect.h \ ../common/rterror.h ../common/octree.h ../common/object.h \ ../common/color.h pmapparm.h pmaptype.h otspecial.h ../common/otypes.h mkpmap.o: mkpmap.c pmap.h pmapparm.h pmaptype.h ray.h \ ../common/standard.h ../common/copyright.h ../common/rtio.h \ ../common/rtmisc.h ../common/rtmath.h ../common/tiff.h ../common/mat4.h \ ../common/fvect.h ../common/rterror.h ../common/octree.h \ ../common/object.h ../common/color.h pmapdata.h ../common/paths.h \ ../common/lookup.h pmapkdt.h pmapfilt.h ../common/fvect.h pmaptkdt.h \ pmutil.h pmapmat.h pmapsrc.h source.h pmapcontrib.h mrgbe.h rcontrib.h \ ../common/platform.h ../common/rtprocess.h ../common/paths.h func.h \ ../common/calcomp.h pmaprand.h pmaproi.h pmapio.h ambient.h \ ../common/resolu.h mrgbe.o: mrgbe.c mrgbe.h wavelet2.o: wavelet2.c wavelet2.h wavelet3.o: wavelet3.c wavelet2.h pmapcontrib.o: pmapcontrib.c pmapcontrib.h pmapdata.h ray.h \ ../common/standard.h ../common/copyright.h ../common/rtio.h \ ../common/rtmisc.h ../common/rtmath.h ../common/tiff.h ../common/mat4.h \ ../common/fvect.h ../common/rterror.h ../common/octree.h \ ../common/object.h ../common/color.h pmapparm.h pmaptype.h \ ../common/paths.h ../common/lookup.h pmapkdt.h pmapfilt.h \ ../common/fvect.h pmaptkdt.h wavelet2.h mrgbe.h rcontrib.h \ ../common/platform.h ../common/rtprocess.h ../common/paths.h func.h \ ../common/calcomp.h pmapdiag.h pmaprand.h pmapmat.h pmap.h pmutil.h \ pmapsrc.h source.h otspecial.h ../common/otypes.h pmcontrib2.o: pmcontrib2.c pmapcontrib.h pmapdata.h ray.h \ ../common/standard.h ../common/copyright.h ../common/rtio.h \ ../common/rtmisc.h ../common/rtmath.h ../common/tiff.h ../common/mat4.h \ ../common/fvect.h ../common/rterror.h ../common/octree.h \ ../common/object.h ../common/color.h pmapparm.h pmaptype.h \ ../common/paths.h ../common/lookup.h pmapkdt.h pmapfilt.h \ ../common/fvect.h pmaptkdt.h wavelet2.h mrgbe.h rcontrib.h \ ../common/platform.h ../common/rtprocess.h ../common/paths.h func.h \ ../common/calcomp.h pmapdiag.h pmaprand.h pmapmat.h pmap.h pmutil.h \ pmaproi.h pmapsrc.h source.h pmapio.h otspecial.h pmcontrib3.o: pmcontrib3.c pmapcontrib.h pmapdata.h ray.h \ ../common/standard.h ../common/copyright.h ../common/rtio.h \ ../common/rtmisc.h ../common/rtmath.h ../common/tiff.h ../common/mat4.h \ ../common/fvect.h ../common/rterror.h ../common/octree.h \ ../common/object.h ../common/color.h pmapparm.h pmaptype.h \ ../common/paths.h ../common/lookup.h pmapkdt.h pmapfilt.h \ ../common/fvect.h pmaptkdt.h wavelet2.h mrgbe.h rcontrib.h \ ../common/platform.h ../common/rtprocess.h ../common/paths.h func.h \ ../common/calcomp.h pmapdiag.h pmapio.h pmcontrib4.o: pmcontrib4.c pmapcontrib.h pmapdata.h ray.h \ ../common/standard.h ../common/copyright.h ../common/rtio.h \ ../common/rtmisc.h ../common/rtmath.h ../common/tiff.h ../common/mat4.h \ ../common/fvect.h ../common/rterror.h ../common/octree.h \ ../common/object.h ../common/color.h pmapparm.h pmaptype.h \ ../common/paths.h ../common/lookup.h pmapkdt.h pmapfilt.h \ ../common/fvect.h pmaptkdt.h wavelet2.h mrgbe.h rcontrib.h \ ../common/platform.h ../common/rtprocess.h ../common/paths.h func.h \ ../common/calcomp.h pmapmat.h pmap.h pmutil.h pmapsrc.h source.h \ pmaprand.h pmapio.h pmapdiag.h ../common/otypes.h otspecial.h pmcontribsort.o: pmcontribsort.c oocsort.h ../common/fvect.h morton.h \ pmapcontrib.h pmapdata.h ray.h ../common/standard.h \ ../common/copyright.h ../common/rtio.h ../common/rtmisc.h \ ../common/rtmath.h ../common/tiff.h ../common/mat4.h ../common/fvect.h \ ../common/rterror.h ../common/octree.h ../common/object.h \ ../common/color.h pmapparm.h pmaptype.h ../common/paths.h \ ../common/lookup.h pmapkdt.h pmapfilt.h pmaptkdt.h wavelet2.h mrgbe.h \ rcontrib.h ../common/platform.h ../common/rtprocess.h ../common/paths.h \ func.h ../common/calcomp.h diff --git a/ooccache.c b/ooccache.c index d209cb2..bda5fc3 100644 --- a/ooccache.c +++ b/ooccache.c @@ -1,432 +1,449 @@ #ifndef lint static const char RCSid[] = "$Id: ooccache.c,v 2.2 2017/08/14 21:12:10 rschregle Exp $"; #endif /* ======================================================================= Cache for out-of-core octree. Uses an LRU page replacement scheme. Page table nodes are stored in order of reference in a doubly linked list, with the most recently used (MRU) page at the head, and the least recently used (LRU) at the tail. Nodes are referenced by the page's base address via a hashtable for constant time access (in the absence of hash collisions). Hash collisions can be limited by adjusting the hashtable load factor OOC_CACHE_LOAD; this is the fraction of the number of pages that will actually be cached. 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) ======================================================================= $Id: ooccache.c,v 2.2 2017/08/14 21:12:10 rschregle Exp $ */ #if !defined(_WIN32) && !defined(_WIN64) || defined(PMAP_OOC) /* No Windoze support for now */ #include #include #include "ooccache.h" static OOC_CacheIdx OOC_PrevPrime (OOC_CacheIdx n) /* Return largest prime number <= n */ { OOC_CacheIdx n2 = n + 1, i; do { n2--; for (i = 2; n2 % i && i <= n2 >> 1; i++); } while (!(n2 % i)); return n2; } int OOC_CacheInit (OOC_Cache *cache, unsigned numPages, - unsigned recPerPage, unsigned recSize) + unsigned recPerPage, unsigned recSize +) +/* Initialise cache containing up to numPages pages; this is rounded up the + * next prime number to reduce hash clustering and collisions. The page + * size recPerPage is specified in units of records of size recSize bytes. + * Returns 0 on success, else -1. */ { OOC_CacheIdx i; if (!cache) return -1; /* Clamp number of pages if necessary */ if (numPages < OOC_CACHE_MINPAGES) { numPages = OOC_CACHE_MINPAGES; fputs("OOC_CacheInit: clamping num pages to min\n", stderr); } if (numPages >= OOC_CACHEIDX_NULL) { numPages = OOC_CACHEIDX_NULL - 1; fputs("OOC_CacheInit: clamping num pages to max\n", stderr); } /* Round numPages to next prime to reduce hash clustering & collisions */ numPages = OOC_PrevPrime(numPages); cache -> recPerPage = recPerPage; cache -> numPages = numPages; cache -> recSize = recSize; cache -> pageSize = (unsigned long)cache -> recPerPage * - cache -> recSize; + cache -> recSize; cache -> pageCnt = 0; cache -> numHits = cache -> numReads = cache -> numColl = cache -> numRept = 0; cache -> mru = cache -> lru = OOC_CACHEIDX_NULL; cache -> lastKey = 0; cache -> lastPage = NULL; /* Allocate & init hashtable */ if (!(cache -> pageTable = calloc(numPages, sizeof(OOC_CacheNode)))) { perror("OOC_InitCache: failed pagetable allocation"); return -1; } for (i = 0; i < numPages; i++) { cache -> pageTable [i].data = NULL; cache -> pageTable [i].key = 0; cache -> pageTable [i].prev = cache -> pageTable [i].next = OOC_CACHEIDX_NULL; } #ifdef OOC_CACHE_STATS fprintf(stderr, - "OOC_InitCache: caching %d pages @ %d records (%.2f Mb)\n", - cache -> numPages, cache -> recPerPage, - (float)cache -> numPages * cache -> pageSize / (1L << 20)); + "OOC_InitCache: caching %d pages @ %d records (%.2f Mb)\n", + cache -> numPages, cache -> recPerPage, + (float)cache -> numPages * cache -> pageSize / (1L << 20) + ); #endif return 0; } static OOC_CacheIdx OOC_CacheFind (OOC_Cache *cache, OOC_CacheKey key) /* Return index to pagetable node for given key; collisions are resolved * via the probe sequence defined by OOC_CACHE_NEXT() */ { OOC_CacheIdx idx = OOC_CACHE_HASH(key, cache -> numPages); OOC_CacheNode *page = cache -> pageTable + idx; /* Find matching pagetable node starting from idx and stop at the first * empty node; a full table would result in an infinite loop! */ while (page -> data && page -> key != key) { idx = OOC_CACHE_NEXT(idx, cache -> numPages); page = cache -> pageTable + idx; cache -> numColl++; } return idx; } static void OOC_CacheUnlink (OOC_Cache *cache, OOC_CacheIdx pageIdx) /* Unlink page from history chain, updating LRU in cache if necessary */ { OOC_CacheNode *page = cache -> pageTable + pageIdx; /* Unlink prev/next index */ if (page -> prev != OOC_CACHEIDX_NULL) cache -> pageTable [page -> prev].next = page -> next; if (page -> next != OOC_CACHEIDX_NULL) cache -> pageTable [page -> next].prev = page -> prev; /* Update LRU index */ if (pageIdx == cache -> lru) cache -> lru = page -> prev; } static void OOC_CacheRelink (OOC_Cache *cache, OOC_CacheIdx pageIdx) /* Update history chain with new page index */ { OOC_CacheNode *page = cache -> pageTable + pageIdx; if (page -> prev != OOC_CACHEIDX_NULL) cache -> pageTable [page -> prev].next = pageIdx; if (page -> next != OOC_CACHEIDX_NULL) cache -> pageTable [page -> next].prev = pageIdx; } static void OOC_CacheSetMRU (OOC_Cache *cache, OOC_CacheIdx pageIdx) /* Set page as most recently used and move to head of history chain */ { OOC_CacheNode *page = cache -> pageTable + pageIdx; OOC_CacheUnlink(cache, pageIdx); page -> prev = OOC_CACHEIDX_NULL; page -> next = cache -> mru; cache -> pageTable [cache -> mru].prev = pageIdx; cache -> mru = pageIdx; } static void *OOC_CacheDelete (OOC_Cache *cache, OOC_CacheIdx pageIdx) /* Delete cache node at pageIdx and tidy up fragmentation in pagetable to * minimise collisions. Returns pointer to deleted node's page data * * !!! Note cache -> pageCnt is NOT decremented as this is immediately * !!! followed by a call to OOC_CacheNew() in OOC_CacheData() */ { OOC_CacheNode *page = cache -> pageTable + pageIdx, *nextPage; OOC_CacheIdx nextIdx; void *delData = page -> data, *nextData; /* Unlink page from history chain */ OOC_CacheUnlink(cache, pageIdx); /* Free pagetable node */ page -> data = NULL; nextIdx = OOC_CACHE_NEXT(pageIdx, cache -> numPages); nextPage = cache -> pageTable + nextIdx; /* Close gaps in probe sequence until empty node found */ while ((nextData = nextPage -> data)) { /* Mark next node as free and reprobe for its key */ nextPage -> data = NULL; pageIdx = OOC_CacheFind(cache, nextPage -> key); page = cache -> pageTable + pageIdx; /* Set page data pointer & reactivate page */ page -> data = nextData; if (pageIdx != nextIdx) { /* Next page maps to freed node; relocate to close gap and update * history chain and LRU/MRU if necessary */ page -> key = nextPage -> key; /* memcpy() faster ? */ page -> prev = nextPage -> prev; page -> next = nextPage -> next; OOC_CacheRelink(cache, pageIdx); if (cache -> lru == nextIdx) cache -> lru = pageIdx; if (cache -> mru == nextIdx) cache -> mru = pageIdx; } nextIdx = OOC_CACHE_NEXT(nextIdx, cache -> numPages); nextPage = cache -> pageTable + nextIdx; } return delData; } -static OOC_CacheNode *OOC_CacheNew (OOC_Cache *cache, OOC_CacheIdx pageIdx, - OOC_CacheKey pageKey, void *pageData) +static OOC_CacheNode *OOC_CacheNew (OOC_Cache *cache, OOC_CacheIdx pageIdx, + OOC_CacheKey pageKey, void *pageData +) /* Init new pagetable node with given index, key and data. Returns pointer * to new node */ { OOC_CacheNode *page = cache -> pageTable + pageIdx; page -> prev = page -> next = OOC_CACHEIDX_NULL; page -> key = pageKey; page -> data = pageData; return page; } #ifdef OOC_CACHE_CHECK static int OOC_CacheCheck (OOC_Cache *cache) /* Run sanity checks on hashtable and page list */ { OOC_CacheIdx i, pageCnt = 0; OOC_CacheNode *page; /* Check pagetable mapping */ for (i = 0, page = cache->pageTable; i < cache->numPages; i++, page++) if (page -> data) { /* Check hash func maps page key to this node */ if (OOC_CacheFind(cache, page -> key) != i) { fputs("OOC_CacheCheck: hashing inconsistency\n", stderr); return -1; } pageCnt++; } if (pageCnt != cache -> pageCnt) { fputs("OOC_CacheCheck: pagetable count inconsistency\n", stderr); return -1; } if (cache -> mru == OOC_CACHEIDX_NULL || - cache -> lru == OOC_CACHEIDX_NULL) { + cache -> lru == OOC_CACHEIDX_NULL + ) { fputs("OOC_CacheCheck: undefined MRU/LRU\n", stderr); return -1; } pageCnt = 0; i = cache -> mru; /* Check page history */ while (i != OOC_CACHEIDX_NULL) { /* Check link to previous page (should be none for MRU) */ page = cache -> pageTable + i; if (i == cache -> mru) if (page -> prev != OOC_CACHEIDX_NULL) { fputs("OOC_CacheCheck: MRU inconsistency\n", stderr); return -1; } else; else if (page -> prev == OOC_CACHEIDX_NULL || - cache -> pageTable [page -> prev].next != i) { + cache -> pageTable [page -> prev].next != i + ) { fputs("OOC_CacheCheck: prev page inconsistency\n", stderr); return -1; } /* Check link to next page node (should be none for LRU) */ if (i == cache -> lru) if (page -> next != OOC_CACHEIDX_NULL) { fputs("OOC_CacheCheck: LRU inconsistency\n", stderr); return -1; } else; else if (page -> next != OOC_CACHEIDX_NULL && - cache -> pageTable [page -> next].prev != i) { + cache -> pageTable [page -> next].prev != i + ) { fputs("OOC_CacheCheck: next page inconsistency\n", stderr); return -1; } /* Check LRU is at tail of page history */ if (page -> next == OOC_CACHEIDX_NULL && i != cache -> lru) { fputs("OOC_CacheCheck: page history tail not LRU\n", stderr); return -1; } i = page -> next; pageCnt++; } if (pageCnt != cache -> pageCnt) { fputs("OOC_CacheCheck: page history count inconsistency\n", stderr); return -1; } return 0; } #endif void *OOC_CacheData (OOC_Cache *cache, FILE *file, unsigned long recIdx) +/* Return pointer to cached record with index recIdx, loading from file and + * possibly evicting the LRU page. If file == NULL, an empty cache record + * is returned. */ { const OOC_CacheKey pageKey = recIdx / cache -> recPerPage; OOC_CacheNode *page = NULL; /* We assume locality of reference, and that it's likely the same page * will therefore be accessed sequentially; in this case we just reuse * the pagetable index */ if (pageKey != cache -> lastKey || !cache -> pageCnt) { OOC_CacheIdx pageIdx = OOC_CacheFind(cache, pageKey); void *pageData; page = cache -> pageTable + pageIdx; if (!page -> data) { /* Page not cached; insert in pagetable */ const unsigned long filePos = cache -> pageSize * pageKey; if (cache -> pageCnt < OOC_CACHE_LOAD * cache -> numPages) { /* Cache not fully loaded; allocate new page data */ if (!(pageData = calloc(cache -> recPerPage, cache -> recSize))) { perror("OOC_CacheData: failed page allocation"); return NULL; } /* Init LRU and MRU if necessary */ if (cache -> mru == OOC_CACHEIDX_NULL || cache -> lru == OOC_CACHEIDX_NULL) cache -> mru = cache -> lru = pageIdx; cache -> pageCnt++; } else { /* Cache fully loaded; adding more entries would aggravate * hash collisions, so evict LRU page. We reuse its * allocated page data and then overwrite it. */ pageData = OOC_CacheDelete(cache, cache -> lru); /* Update page index as it may have moved forward in probe * sequence after deletion */ pageIdx = OOC_CacheFind(cache, pageKey); } /* Init new pagetable node, will be linked in history below */ page = OOC_CacheNew(cache, pageIdx, pageKey, pageData); - /* Load page data from file; the last page may be truncated */ - if (!pread(fileno(file), page -> data, cache -> pageSize, filePos)) { + /* Load page data from file if not NULL; the last page may be + * truncated */ + if (file && + !pread(fileno(file), page -> data, cache -> pageSize, filePos) + ) { fputs("OOC_CacheData: failed seek/read from data stream", stderr); return NULL; } } else /* Page in cache */ cache -> numHits++; if (cache -> mru != pageIdx) /* Cached page is now most recently used; unlink and move to front * of history chain and update MRU */ OOC_CacheSetMRU(cache, pageIdx); /* Update last page to detect repeat accesses */ cache -> lastKey = pageKey; cache -> lastPage = page; } else { /* Repeated page, skip hashing and just reuse last page */ page = cache -> lastPage; cache -> numHits++; cache -> numRept++; } cache -> numReads++; #ifdef OOC_CACHE_CHECK if (OOC_CacheCheck(cache)) return NULL; #endif /* Return pointer to record within cached page */ return page -> data + (recIdx % cache -> recPerPage) * cache -> recSize; } void OOC_DeleteCache (OOC_Cache *cache) +/* Delete cache and free allocated pages */ { OOC_CacheIdx i; for (i = 0; i < cache -> numPages; i++) if (cache -> pageTable [i].data) free(cache -> pageTable [i].data); free(cache -> pageTable); cache -> pageTable = NULL; cache -> pageCnt = 0; cache -> mru = cache -> lru = OOC_CACHEIDX_NULL; } #endif /* NIX / PMAP_OOC */ diff --git a/ooccache.h b/ooccache.h index 4be39e1..476eef4 100644 --- a/ooccache.h +++ b/ooccache.h @@ -1,97 +1,99 @@ /* ======================================================================= Cache for out-of-core octree. Uses an LRU page replacement scheme. Page table nodes are stored in order of reference in a doubly linked list, with the most recently used (MRU) page at the head, and the least recently used (LRU) at the tail. Nodes are referenced by the page's base address via a hashtable for constant time access (in the absence of hash collisions). Hash collisions can be limited by adjusting the hashtable load factor OOC_CACHE_LOAD; this is the fraction of the number of pages that will actually be cached. 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) ======================================================================= $Id: ooccache.h,v 2.1 2016/05/17 17:39:47 rschregle Exp $ */ #ifndef OOC_CACHE_H #define OOC_CACHE_H #include #include /* Hashtable load factor to limit hash collisions */ #define OOC_CACHE_LOAD 0.75 /* Minimum number of cache pages */ #define OOC_CACHE_MINPAGES 8 /* Hashing function for key -> pagetable mapping (assumes n is prime) */ #define OOC_CACHE_PRIME 19603 #define OOC_CACHE_HASH(k,n) ((k) * OOC_CACHE_PRIME % (n)) /* Next index in pagetable probe sequence for collision resolution */ #define OOC_CACHE_NEXT(i,n) (((i) + 1) % (n)) /* Cache key and index types */ typedef uint32_t OOC_CacheKey; typedef uint32_t OOC_CacheIdx; /* Undefined value for cache index */ #define OOC_CACHEIDX_NULL UINT32_MAX typedef struct { OOC_CacheKey key; /* Key to resolve hashing collisions */ OOC_CacheIdx prev, next; /* Previous/next page in history */ void *data; /* Start of page data */ } OOC_CacheNode; typedef struct { unsigned recSize, /* Data record size in bytes */ recPerPage; /* Page size in number of records */ unsigned long pageSize; /* Page size in bytes (precomp) */ OOC_CacheIdx pageCnt, /* Num pages used */ numPages; /* Pagetable size */ /* NOTE: The hashtable load factor limits the number of pages actually * used, such that pageCnt <= OOC_CACHE_LOAD * numPages */ OOC_CacheNode *pageTable; /* Pagetable with numPages nodes */ OOC_CacheIdx mru, lru; /* Most/least recently used page == head/tail of page history */ OOC_CacheKey lastKey; /* Previous key to detect repeat lookups */ OOC_CacheNode *lastPage; /* Previous page for repeat lookups */ unsigned long numReads, /* Statistics counters */ numHits, numColl, numRept; } OOC_Cache; /* Initialise cache containing up to numPages pages; this is rounded up * the next prime number to reduce hash clustering and collisions. The * page size recPerPage is specified in units of records of size recSize * bytes. Returns 0 on success, else -1. */ int OOC_CacheInit (OOC_Cache *cache, unsigned numPages, - unsigned recPerPage, unsigned recSize); + unsigned recPerPage, unsigned recSize + ); /* Return pointer to cached record with index recIdx, loading from file - * and possibly evicting the LRU page */ + * and possibly evicting the LRU page. If file == NULL, an empty cache + * record is returned. */ void *OOC_CacheData (OOC_Cache *cache, FILE *file, unsigned long recIdx); /* Delete cache and free allocated pages */ void OOC_DeleteCache (OOC_Cache *cache); #endif diff --git a/pmapcontrib.c b/pmapcontrib.c index 4eb3cb2..397181b 100644 --- a/pmapcontrib.c +++ b/pmapcontrib.c @@ -1,1141 +1,1148 @@ #ifndef lint static const char RCSid[] = "$Id: pmapcontrib.c,v 2.20 2021/02/16 20:06:06 greg Exp $"; #endif /* ========================================================================= Photon map routines for precomputed light source contributions. These routines handle contribution binning, compression and encoding, and are used by 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.20 2021/02/16 20:06:06 greg Exp $ */ #include "pmapcontrib.h" #include "pmapdiag.h" #include "pmaprand.h" #include "pmapmat.h" #include "pmapsrc.h" #include "pmutil.h" #include "otspecial.h" #include "otypes.h" #include "lookup.h" #ifdef PMAP_CONTRIB /* The following are convenient placeholders interfacing to mkpmap and rcontrib. They can be externally set via initPmapContribTab() and referenced within the contrib pmap modules. These variables can then be safely ignored by rtrace/rpict/rvu, without annoying linking errors. */ /* Global pointer to rcontrib's contribution binning LUT */ LUTAB *pmapContribTab = NULL; /* Contribution/coefficient mode flag */ int *pmapContribMode; extern void SDdisk2square(double sq[2], double diskx, double disky); #if 0 int xy2lin (unsigned scDim, int x, int y) /* Serialise 2D contribution coords in (scDim x scDim) Shirley-Chiu square to 1D index. Returns -1 if coordinates invalid */ { return x < 0 || y < 0 ? -1 : (x * scDim) + y; } void lin2xy (unsigned scDim, int bin, int *x, int *y) /* Deserialise 1D contribution index to 2D coordinates in (scDim x scDim) * Shirley-Chiu square. Returns -1 if bin invalid */ { *x = bin < 0 ? -1 : bin / scDim; *y = bin < 0 ? -1 : bin % scDim; } #endif static int ray2bin (const RAY *ray, unsigned scDim) /* Map ray dir (pointing away from origin) to its 1D bin in an (scDim x scDim) Shirley-Chiu square. Returns -1 if mapped bin is invalid (e.g. behind plane) */ { static int scRHS, varInit = 0; static FVECT scNorm, scUp; unsigned scBin [2]; FVECT diskPlane; RREAL dz, diskx, disky, rad, diskd2, scCoord [2]; if (!varInit) { /* Lazy init shirley-Chiu mapping orientation from function variables */ scRHS = varvalue(PMAP_CONTRIB_SCRHS); scNorm [0] = varvalue(PMAP_CONTRIB_SCNORMX); scNorm [1] = varvalue(PMAP_CONTRIB_SCNORMY); scNorm [2] = varvalue(PMAP_CONTRIB_SCNORMZ); scUp [0] = varvalue(PMAP_CONTRIB_SCUPX); scUp [1] = varvalue(PMAP_CONTRIB_SCUPY); scUp [2] = varvalue(PMAP_CONTRIB_SCUPZ); varInit ^= 1; } /* Map incident direction to disk */ dz = DOT(ray -> rdir, scNorm); /* normal proj */ if (dz > 0) { fcross(diskPlane, scUp, scNorm); /* y-axis in plane, perp to up */ diskPlane [0] *= scRHS; diskx = DOT(ray -> rdir, diskPlane); disky = DOT(ray -> rdir, scUp) - dz * DOT(ray -> rdir, scUp); diskd2 = diskx * diskx + disky * disky; /* in-plane length^2 */ rad = dz>FTINY ? sqrt((1 - dz*dz) / diskd2) : 0; /* radial factor */ disky *= rad; /* diskx, disky now in range [-1, 1] */ /* Apply Shirley-Chiu mapping of (diskx, disky) to square */ SDdisk2square(scCoord, diskx, disky); /* Map Shirley-Chiu square coords to 1D bin */ scBin [0] = scCoord [0] * scDim; scBin [1] = scCoord [1] * scDim; return PMAP_CONTRIB_XY2LIN(scDim, scBin [0], scBin [1]); } else return -1; } /* ------------------ CONTRIBSRC STUFF --------------------- */ #ifndef PMAP_CONTRIB_TEST MODCONT *addContribModifier (LUTAB *contribTab, unsigned *numContribs, char *mod, char *binParm, int binCnt ) /* Add light source modifier mod to contribution lookup table contribTab, and update numContribs. Return initialised contribution data for this modifier. This code adapted from rcontrib.c:addmodifier(). */ { LUENT *lutEntry = lu_find(contribTab, mod); MODCONT *contrib; EPNODE *eBinVal; if (lutEntry -> data) { /* Reject duplicate modifiers */ sprintf(errmsg, "duplicate light source modifier %s", mod); error(USER, errmsg); } if (*numContribs >= MAXMODLIST) { sprintf(errmsg, "too many modifiers (max. %d)", MAXMODLIST); error(INTERNAL, errmsg); } lutEntry -> key = mod; 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 addContribModifier()"); 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; ++(*numContribs); return(contrib); } void addContribModfile (LUTAB *contribTab, unsigned *numContribs, char *modFile, char *binParm, int binCnt ) /* Add light source modifiers from file modFile to contribution lookup table * contribTab, and update numContribs. * 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 (*numContribs + 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 */ addContribModifier(contribTab, numContribs, mods [i], binParm, binCnt ); } static int contribSourceBin (LUTAB *contribs, const RAY *ray) /* Map contribution source ray to its bin for light source ray -> rsrc, using Shirley-Chiu disk-to-square mapping. Return invalid bin -1 if mapping failed. */ { const SRCREC *src; const OBJREC *srcMod; const MODCONT *srcCont; RAY srcRay; int bin, i; /* Check we have a valid ray and contribution LUT */ if (!ray || !contribs) return -1; src = &source [ray -> rsrc]; srcMod = findmaterial(src -> so); srcCont = (MODCONT*)lu_find(contribs, srcMod -> oname) -> data; if (!srcCont) /* Not interested in this source (modifier not in contrib LUT) */ return -1; /* Set up shadow ray pointing to source for disk2square mapping */ 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; #if 0 worldfunc(RCCONTEXT, &srcRay); #endif set_eparams((char*)srcCont -> params); if ((bin = ray2bin(&srcRay, sqrt(srcCont -> nbins))) < 0) error(WARNING, "Ignoring invalid bin in contribSourceBin()"); return bin; } PhotonContribSourceIdx newPhotonContribSource (PhotonMap *pmap, const RAY *contribSrcRay, FILE *contribSrcHeap ) /* Add contribution source ray for emitted contribution photon and save * light source index and binned direction. The current contribution * source is stored in pmap -> lastContribSrc. If the previous contrib * source spawned photons (i.e. has srcIdx >= 0), it's appended to * contribSrcHeap. If contribSrcRay == NULL, the current contribution * source is still flushed, but no new source is set. Returns updated * contribution source counter pmap -> numContribSrc. */ { if (!pmap || !contribSrcHeap) return 0; /* Check if last contribution source has spawned photons (srcIdx >= 0, * see newPhoton()), in which case we save it to the heap file before * clobbering it. (Note this is short-term storage, so we doan' need * da portable I/O stuff here). */ if (pmap -> lastContribSrc.srcIdx >= 0) { if (!fwrite(&pmap -> lastContribSrc, sizeof(PhotonContribSource), 1, contribSrcHeap )) error(SYSTEM, "failed writing photon contrib source in " "newPhotonContribSource()" ); pmap -> numContribSrc++; if (!pmap -> numContribSrc || pmap -> numContribSrc > PMAP_MAXCONTRIBSRC ); } if (contribSrcRay) { /* Mark this contribution source unused with a negative source index until its path spawns a photon (see newPhoton()) */ pmap -> lastContribSrc.srcIdx = -1; /* Map ray to bin in anticipation that this contrib source will be used, since the ray will be lost once a photon is spawned */ pmap -> lastContribSrc.srcBin = contribSourceBin( pmapContribTab, contribSrcRay ); if (pmap -> lastContribSrc.srcBin < 0) { /* Warn if invalid bin, but trace photon nonetheless. It will count as emitted to prevent bias, but will not be stored in newPhoton(), as it contributes zero flux */ sprintf(errmsg, "invalid bin for light source %s, " "contribution photons will be discarded", source [contribSrcRay -> rsrc].so -> oname ); error(WARNING, errmsg); } } return pmap -> numContribSrc; } PhotonContribSourceIdx buildContribSources (PhotonMap *pmap, FILE **contribSrcHeap, char **contribSrcHeapFname, PhotonContribSourceIdx *contribSrcOfs, unsigned numHeaps ) /* Consolidate per-subprocess contribution source heaps into array * pmap -> contribSrc. Returns offset for contribution source index * linearisation in pmap -> numContribSrc. The heap files in * contribSrcHeap are closed on return. */ { PhotonContribSourceIdx heapLen; unsigned heap; if (!pmap || !contribSrcHeap || !contribSrcOfs || !numHeaps) return 0; pmap -> numContribSrc = 0; for (heap = 0; heap < numHeaps; heap++) { contribSrcOfs [heap] = pmap -> numContribSrc; if (fseek(contribSrcHeap [heap], 0, SEEK_END) < 0) error(SYSTEM, "failed photon contrib source seek " "in buildContribSources()" ); pmap -> numContribSrc += heapLen = ftell(contribSrcHeap [heap]) / sizeof(PhotonContribSource); if (!(pmap -> contribSrc = realloc(pmap -> contribSrc, pmap -> numContribSrc * sizeof(PhotonContribSource) ))) error(SYSTEM, "failed photon contrib source alloc " "in buildContribSources()" ); rewind(contribSrcHeap [heap]); if (fread(pmap -> contribSrc + contribSrcOfs [heap], sizeof(PhotonContribSource), heapLen, contribSrcHeap [heap] ) != heapLen) error(SYSTEM, "failed reading photon contrib source " "in buildContribSources()" ); fclose(contribSrcHeap [heap]); unlink(contribSrcHeapFname [heap]); } return pmap -> numContribSrc; } /* ----------------- CONTRIB PMAP ENCODING STUFF -------------------- */ static int coeffCompare (const void *c1, const void *c2) /* Comparison function to REVERSE sort thresholded coefficients */ { const PreComputedContribCoeff *tcoeff1 = c1, *tcoeff2 = c2; /* Use dot product as magnitude to compare _absolute_ values */ const WAVELET_COEFF v1 = DOT(tcoeff1 -> coeff, tcoeff1 -> coeff), v2 = DOT(tcoeff2 -> coeff, tcoeff2 -> coeff); if (v1 < v2) return 1; else if (v1 > v2) return -1; else return 0; } static int thresholdContribs (PreComputedContrib *preCompContrib) /* Threshold wavelet detail coefficients in preCompContrib -> waveletMatrix [1..coeffDim-1] [1..coeffDim-1] by keeping the (preCompContrib -> nCompressedCoeffs) largest of these and returning them in preCompContrib -> compressedCoeffs along with their original order. NOTE: preCompContrib -> waveletMatrix [0][0] is the average wavelet coefficient and excluded from thresholding. Returns 0 on success. */ { unsigned i, j, coeffDim, coeffIdx; WaveletMatrix2 waveletMatrix; PreComputedContribCoeff *threshCoeffs; if (!preCompContrib || !(coeffDim = preCompContrib -> coeffDim) || !(threshCoeffs = preCompContrib -> compressedCoeffs) || !(waveletMatrix = preCompContrib -> waveletMatrix) ) /* Should be initialised by caller! */ return -1; /* Set up coefficient buffer for compression (thresholding), skipping * coefficient at waveletMatrix [0][0] since it's the average and * therefore not thresholded. The 2D coefficient indices (matrix * coords) are linearised to 1D. */ for (i = 0; i < coeffDim; i++) for (j = 0; j < coeffDim; j++) { coeffIdx = PMAP_CONTRIB_XY2LIN(coeffDim, i, j); if (coeffIdx) { /* Set up pointers to coeffs (sorted faster than 3 doubles) and remember original (linearised) index prior to sort */ threshCoeffs [coeffIdx - 1].idx = coeffIdx; threshCoeffs [coeffIdx - 1].coeff = (WAVELET_COEFF*)&( waveletMatrix [i] [j] ); } } /* REVERSE sort coeffs by magnitude; the non-thresholded coeffs will then start at threshCoeffs [0] (note nCoeffs == coeffDim^2) */ /* XXX: PARTITIONING WOULD ACTUALLY BE SUFFICIENT AND FASTER! */ qsort(threshCoeffs, preCompContrib -> nCoeffs - 1, sizeof(PreComputedContribCoeff), coeffCompare ); return 0; } static int encodeContribs (PreComputedContrib *preCompContrib, float compressRatio ) /* Apply wavelet transform to input matrix preCompContrib -> waveletMatrix and compress according to compressRatio, storing thresholded and mRGBE-encoded coefficients in preCompContrib -> mrgbeCoeffs. Note that the average coefficient is not encoded, and returned in preCompContrib -> waveletMatrix [0][0]. Returns 0 on success. */ { unsigned i, j, k, scDim; WaveletMatrix2 waveletMatrix, tWaveletMatrix; PreComputedContribCoeff *threshCoeffs; mRGBERange *mrgbeRange; mRGBE *mrgbeCoeffs; WAVELET_COEFF absCoeff; #ifdef PMAP_CONTRIB_DBG WaveletCoeff3 decCoeff; unsigned decIdx; #endif if (!preCompContrib || !preCompContrib -> mrgbeCoeffs || !preCompContrib -> compressedCoeffs || !(waveletMatrix = preCompContrib -> waveletMatrix) || !(tWaveletMatrix = preCompContrib -> tWaveletMatrix) || !(scDim = preCompContrib -> scDim) ) /* Should be initialised by the caller! */ return -1; #ifdef PMAP_CONTRIB_DBG for (i = 0; i < scDim; i++) { for (j = 0; j < scDim; j++) { for (k = 0; k < 3; k++) { #if 0 /* Set contributions to bins for debugging */ waveletMatrix [i] [j] [k] = PMAP_CONTRIB_XY2LIN(scDim, i, j); #else /* Replace contribs with "bump" function */ waveletMatrix [i] [j] [k] = (1. - fabs(i - scDim/2. + 0.5) / (scDim/2. - 0.5)) * (1. - fabs(j - scDim/2. + 0.5) / (scDim/2. - 0.5)); #endif } #if 0 /* Dump contribs prior to encoding for debugging */ printf("% 7.3g\t", colorAvg(waveletMatrix [i] [j])); } putchar('\n'); } putchar('\n'); #else } } #endif #endif /* Do 2D wavelet transform on preCompContrib -> waveletMatrix */ if (padWaveletXform2(waveletMatrix, tWaveletMatrix, scDim, NULL) != preCompContrib -> coeffDim ) error(INTERNAL, "failed wavelet transform in encodeContribs()"); /* Compress wavelet detail coeffs by thresholding */ if (thresholdContribs(preCompContrib) < 0) error(INTERNAL, "failed wavelet compression in encodeContribs()"); threshCoeffs = preCompContrib -> compressedCoeffs; /* Init per-channel coefficient range for mRGBE encoding */ mrgbeRange = &preCompContrib -> mrgbeRange; setcolor(mrgbeRange -> min, FHUGE, FHUGE, FHUGE); setcolor(mrgbeRange -> max, 0, 0, 0); /* Update per-channel coefficient range */ for (i = 0; i < preCompContrib -> nCompressedCoeffs; i++) { for (k = 0; k < 3; k++) { #ifdef PMAP_CONTRIB_DBG #if 0 /* Replace wavelet coefficient with bin for debugging, ordering bins linearly */ threshCoeffs [i].coeff [k] = threshCoeffs [i].idx = i + 1; #endif #endif absCoeff = fabs(threshCoeffs [i].coeff [k]); if (absCoeff < mrgbeRange -> min [k]) mrgbeRange -> min [k] = absCoeff; if (absCoeff > mrgbeRange -> max [k]) mrgbeRange -> max [k] = absCoeff; } } if (preCompContrib -> nCompressedCoeffs == 1) /* Maximum compression with just 1 (!) compressed detail coeff (is * this even useful?), so mRGBE range is undefined since min & max * coincide; set minimum to 0, maximum to the single remaining * coefficient */ setcolor(mrgbeRange -> min, 0, 0, 0); /* Init mRGBE coefficient normalisation from range */ if (!mRGBEinit(mrgbeRange, mrgbeRange -> min, mrgbeRange -> max)) return -1; mrgbeCoeffs = preCompContrib -> mrgbeCoeffs; /* Encode wavelet detail coefficients to mRGBE */ for (i = 0; i < preCompContrib -> nCompressedCoeffs; i++) { /* XXX: threshCoeffs [i].idx could overflow the mRGBE data field, * since there are more padded coefficients than bins!!! * SORT BY IDX AND USE AN INCREMENTAL INDEXING HERE INSTEAD??? */ mrgbeCoeffs [i] = mRGBEencode(threshCoeffs [i].coeff, mrgbeRange, threshCoeffs [i].idx ); #ifdef PMAP_CONTRIB_DBG /* Encoding sanity check */ decIdx = mRGBEdecode(mrgbeCoeffs [i], mrgbeRange, decCoeff); #if 0 if (decCoeff != threshCoeffs [i].idx || sqrt(dist2(decCoeff, threshCoeffs [i].coeff)) > 1.7 ) error(CONSISTENCY, "failed sanity check in encodeContribs()"); #endif for (k = 0; k < 3; k++) if (decCoeff [k] / threshCoeffs [i].coeff [k] < 0) error(CONSISTENCY, "coefficient sign reversal in encodeContribs()" ); #endif } return 0; } static void initContribHeap (PhotonMap *pmap) /* Initialise precomputed contribution heap */ { int fdFlags; if (!pmap -> contribHeap) { /* Open heap file for precomputed contributions */ mktemp(strcpy(pmap -> contribHeapFname, PMAP_TMPFNAME)); if (!(pmap -> contribHeap = fopen(pmap -> contribHeapFname, "w+b") ) ) error(SYSTEM, "failed opening precomputed contribution " "heap file in initContribHeap()" ); #ifdef F_SETFL /* XXX is there an alternative needed for Wind0z? */ fdFlags = fcntl(fileno(pmap -> contribHeap), F_GETFL); fcntl(fileno(pmap -> contribHeap), F_SETFL, fdFlags | O_APPEND); #endif /* ftruncate(fileno(pmap -> heap), 0); */ } } static MODCONT *getPhotonContrib (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 and pointer to binned contributions (or NULL if none were found). */ { const OBJREC *srcMod = findmaterial(source [ray->rsrc].so); MODCONT *contrib = (MODCONT*)lu_find( pmapContribTab, srcMod -> oname ) -> data; Photon *photon; COLOR flux; PhotonSearchQueueNode *sqn; float r2, norm; unsigned i; /* Zero bins for this source modifier */ memset(contrib -> cbin, 0, sizeof(DCOLOR) * contrib -> nbins); setcolor(irrad, 0, 0, 0); if (!contrib || !pmap -> maxGather) /* Modifier not in LUT or zero bandwidth */ return NULL; /* Lookup photons */ pmap -> squeue.tail = 0; /* Pass light source index to filter in findPhotons() via pmap -> lastContribSrc */ pmap -> lastContribSrc.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 : "", ray -> rop [0], ray -> rop [1], ray -> rop [2] ); error(WARNING, errmsg); #endif return NULL; } /* Avg radius^2 between furthest two photons to improve accuracy */ sqn = pmap -> squeue.node + 1; r2 = max(sqn -> dist2, (sqn + 1) -> dist2); r2 = 0.25 * (pmap -> maxDist2 + r2 + 2 * sqrt(pmap -> maxDist2 * r2)); /* XXX: OMIT extra normalisation factor 1/PI for ambient calc? */ #ifdef PMAP_EPANECHNIKOV /* Normalise accumulated flux by Epanechnikov kernel integral in 2D (see Eq. 4.4, p.76 in Silverman, "Density Estimation for Statistics and Data Analysis", 1st Ed., 1986, and Wann Jensen, "Realistic Image Synthesis using Photon Mapping"), include RADIANCE-specific lambertian factor PI */ norm = 2 / (PI * PI * r2); #else /* Normalise accumulated flux by search area PI * r^2, including RADIANCE-specific lambertian factor PI */ norm = 1 / (PI * PI * r2); #endif /* 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, norm); #ifdef PMAP_EPANECHNIKOV /* Apply Epanechnikov kernel to photon flux based on distance */ scalecolor(flux, 1 - sqn -> dist2 / r2); #endif addcolor(irrad, flux); addcolor(contrib -> cbin [photonSrcBin(pmap, photon)], flux); } return contrib; } void freePreCompContribNode (void *p) /* Clean up precomputed contribution LUT entry */ { PhotonMap *preCompContribPmap = (PhotonMap*)p; PreComputedContrib *preCompContrib; if (preCompContribPmap) { preCompContrib = (PreComputedContrib*)( preCompContribPmap -> preCompContrib ); if (preCompContrib) { /* Free primary and transposed wavelet matrices */ freeWaveletMatrix2(preCompContrib -> waveletMatrix, preCompContrib -> coeffDim ); freeWaveletMatrix2(preCompContrib -> tWaveletMatrix, preCompContrib -> coeffDim ); /* Free thresholded coefficients */ free(preCompContrib -> compressedCoeffs); /* Free mRGBE encoded coefficients */ free(preCompContrib -> mrgbeCoeffs); free(preCompContrib -> waveletFname); + + if (preCompContrib -> cache) { + /* Free contribution cache */ + OOC_DeleteCache(preCompContrib -> cache); + free(preCompContrib -> cache); + } } /* Clean up precomputed contrib photon map */ deletePhotons(preCompContribPmap); free(preCompContribPmap); } } void initPreComputedContrib (PreComputedContrib *preCompContrib) /* Initialise precomputed contribution container in photon map */ { preCompContrib -> waveletFname = NULL; preCompContrib -> waveletFile = NULL; preCompContrib -> waveletMatrix = preCompContrib -> tWaveletMatrix = NULL; preCompContrib -> compressedCoeffs = NULL; setcolor(preCompContrib -> mrgbeRange.min, 0, 0, 0); setcolor(preCompContrib -> mrgbeRange.max, 0, 0, 0); setcolor(preCompContrib -> mrgbeRange.norm, 0, 0, 0); preCompContrib -> mrgbeCoeffs = NULL; preCompContrib -> scDim = preCompContrib -> nBins = preCompContrib -> coeffDim = preCompContrib -> nCoeffs = preCompContrib -> nNonZeroCoeffs = preCompContrib -> nCompressedCoeffs = preCompContrib -> contribSize = 0; + preCompContrib -> cache = NULL; } void preComputeContrib (PhotonMap *pmap) /* Precompute contributions for a random subset of (finalGather * pmap -> numPhotons) photons, and return the per-modifier precomputed contribution photon maps in LUT preCompContribTab, discarding the original photons. */ { unsigned long p, numPreComp; unsigned i, scDim, nBins, coeffDim, nCoeffs, nCompressedCoeffs; PhotonIdx pIdx; Photon photon; RAY ray; MODCONT *binnedContribs; COLR mrgbeRange32 [2]; LUENT *preCompContribNode; PhotonMap *preCompContribPmap, nuPmap; PreComputedContrib *preCompContrib; LUTAB lutInit = LU_SINIT(NULL, freePreCompContribNode); WaveletMatrix2 waveletMatrix; #ifdef PMAP_CONTRIB_DBG RAY dbgRay; #endif if (verbose) { sprintf(errmsg, "\nPrecomputing contributions for %ld photons\n", numPreComp ); eputs(errmsg); #if NIX fflush(stderr); #endif } /* Init new parent photon map and set output filename */ initPhotonMap(&nuPmap, pmap -> type); nuPmap.fileName = pmap -> fileName; /* Set contrib/coeff mode in new parent photon map */ nuPmap.contribMode = pmap -> contribMode; /* Allocate and init LUT containing per-modifier child photon maps */ if (!(nuPmap.preCompContribTab = malloc(sizeof(LUTAB)))) error(SYSTEM, "out of memory allocating LUT in preComputeContrib()" ); memcpy(nuPmap.preCompContribTab, &lutInit, sizeof(LUTAB)); /* Record start time, baby */ repStartTime = time(NULL); #ifdef SIGCONT signal(SIGCONT, pmapPreCompReport); #endif repComplete = numPreComp = finalGather * pmap -> numPhotons; repProgress = 0; photonRay(NULL, &ray, PRIMARY, NULL); ray.ro = NULL; for (p = 0; p < numPreComp; p++) { /* Get random photon from stratified distribution in source heap * to avoid duplicates and clustering */ pIdx = firstPhoton(pmap) + (unsigned long)( (p + 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 (i = 0; i < 3; i++) ray.ron [i] = photon.norm [i] / 127.0; #ifndef PMAP_CONTRIB_DBG /* Get contributions at photon position; retry with another photon if no contribs found */ binnedContribs = getPhotonContrib(pmap, &ray, ray.rcol); #else /* Get all contribs from same photon for debugging */ /* Position must differ otherwise too many identical photon keys * will cause ooC octree to overflow! */ VCOPY(dbgRay.rop, photon.pos); getPhoton(pmap, 0, &photon); memcpy(&dbgRay, &ray, sizeof(RAY)); dbgRay.rsrc = photonSrcIdx(pmap, &photon); for (i = 0; i < 3; i++) dbgRay.ron [i] = photon.norm [i] / 127.0; binnedContribs = getPhotonContrib(pmap, &dbgRay, dbgRay.rcol); #endif if (binnedContribs) { #if 0 if (!(p & PMAP_CNTUPDATE)) printf("Precomputing contribution photon %lu / %lu\n", p, numPreComp ); #endif /* Found contributions at photon position, so generate * precomputed photon */ if (!(preCompContribNode = lu_find(nuPmap.preCompContribTab, binnedContribs -> modname) ) ) error(SYSTEM, "out of memory allocating LUT entry in " "preComputeContrib()" ); if (!preCompContribNode -> key) { /* New LUT node for precomputed contribs for this modifier */ preCompContribNode -> key = (char*)binnedContribs -> modname; preCompContribNode -> data = (char*)( preCompContribPmap = malloc(sizeof(PhotonMap)) ); if (preCompContribPmap) { /* Init new child photon map and its contributions */ initPhotonMap(preCompContribPmap, PMAP_TYPE_CONTRIB_CHILD ); initPhotonHeap(preCompContribPmap); initContribHeap(preCompContribPmap); preCompContrib = (PreComputedContrib*)( preCompContribPmap -> preCompContrib = malloc(sizeof(PreComputedContrib)) ); initPreComputedContrib(preCompContrib); } if (!preCompContribPmap || !preCompContrib) error(SYSTEM, "out of memory allocating new photon map " "in preComputeContrib()" ); /* Set output filename from parent photon map */ preCompContribPmap -> fileName = nuPmap.fileName; /* Set contrib/coeff mode from parent photon map */ preCompContribPmap -> contribMode = nuPmap.contribMode; /* Set Shirley-Chiu square & wavelet matrix dimensions (number of bins resp. coefficients). */ preCompContrib -> scDim = scDim = sqrt( preCompContrib -> nBins = nBins = binnedContribs -> nbins ); if (scDim * scDim != nBins) /* Mkpmap shoulda took care of dis */ error(INTERNAL, "contribution bin count not " "integer square in preComputeContrib()" ); if (nBins > 1) { /* BINNING ENABLED; set up wavelet xform & compression. Get dimensions of wavelet coefficient matrix after boundary padding, and number of nonzero coefficients. The number of compressed coefficients is fixed and based on the number of nonzero coefficients (minus one as the average coefficient is not thresholded), since zero coeffs are already thresholded by default. */ preCompContrib -> coeffDim = coeffDim = padWaveletXform2( NULL, NULL, scDim, &preCompContrib -> nNonZeroCoeffs ); preCompContrib -> nCoeffs = nCoeffs = coeffDim * coeffDim; nCompressedCoeffs = (1 - compressRatio) * (preCompContrib -> nNonZeroCoeffs - 1); if (nCompressedCoeffs < 1) { error(WARNING, "maximum contribution compression, clamping number " "of wavelet coefficients in preComputeContrib()" ); nCompressedCoeffs = 1; } if (nCompressedCoeffs >= preCompContrib -> nCoeffs) { error(WARNING, "minimum contribution compression, clamping number " "of wavelet coefficients in preComputeContrib()" ); nCompressedCoeffs = nCoeffs - 1; } preCompContrib -> nCompressedCoeffs = nCompressedCoeffs; /* Lazily allocate primary and transposed wavelet * coefficient matrix */ preCompContrib -> waveletMatrix = waveletMatrix = allocWaveletMatrix2(coeffDim); preCompContrib -> tWaveletMatrix = allocWaveletMatrix2( coeffDim ); if (!waveletMatrix || !preCompContrib -> tWaveletMatrix) error(SYSTEM, "out of memory allocating wavelet " "coefficient matrix in preComputeContrib()" ); /* Lazily allocate thresholded detail coefficients (minus the average coeff) */ preCompContrib -> compressedCoeffs = calloc( nCoeffs - 1, sizeof(PreComputedContribCoeff) ); /* Lazily alloc mRGBE-encoded compressed wavelet coeffs */ preCompContrib -> mrgbeCoeffs = calloc( nCompressedCoeffs, sizeof(mRGBE) ); if (!preCompContrib -> compressedCoeffs || !preCompContrib -> mrgbeCoeffs ) error(SYSTEM, "out of memory allocating compressed " "contribution buffer in preComputeContrib()" ); /* Set size of compressed contributions, in bytes */ preCompContrib -> contribSize = PMAP_CONTRIB_ENCSIZE( nCompressedCoeffs ); } } else { /* LUT node already exists for this modifier; use its data */ preCompContribPmap = (PhotonMap*)preCompContribNode -> data; preCompContrib = (PreComputedContrib*)( preCompContribPmap -> preCompContrib ); scDim = preCompContrib -> scDim; nBins = preCompContrib -> nBins; nCoeffs = preCompContrib -> nCoeffs; nCompressedCoeffs = preCompContrib -> nCompressedCoeffs; waveletMatrix = preCompContrib -> waveletMatrix; } if (nBins > 1) { /* Binning enabled; copy binned contribs to wavelet xform * input matrix, then compress & encode */ for (i = 0; i < scDim; i++) memcpy(waveletMatrix [i], binnedContribs->cbin + PMAP_CONTRIB_XY2LIN(scDim, i, 0), scDim * WAVELET_COEFFSIZE ); if (encodeContribs(preCompContrib, compressRatio)) error(INTERNAL, "failed contribution " "compression/encoding in preComputeContrib()" ); /* Dump encoded wavelet coeffs to contrib heap file, prepended by mRGBE range in 32-bit RGBE. NOTE: minimum and maximum are reversed in the file to facilitate handling the special (but pointless) case of a single compressed coeff (see below) */ setcolr(mrgbeRange32 [0], preCompContrib -> mrgbeRange.max [0], preCompContrib -> mrgbeRange.max [1], preCompContrib -> mrgbeRange.max [2] ); setcolr(mrgbeRange32 [1], preCompContrib -> mrgbeRange.min [0], preCompContrib -> mrgbeRange.min [1], preCompContrib -> mrgbeRange.min [2] ); /* Dump only mRGBE maximum if 1 compressed coeff (maximum * compression, pointless as it may be!), since minimum is * implicitly zero and can be omitted to save space */ putbinary(mrgbeRange32, sizeof(COLR), 1 + (nCompressedCoeffs > 1), preCompContribPmap -> contribHeap ); if (putbinary(preCompContrib -> mrgbeCoeffs, sizeof(mRGBE), nCompressedCoeffs, preCompContribPmap -> contribHeap ) != nCompressedCoeffs ) error(SYSTEM, "failed writing to contribution heap " "in preComputeContrib()" ); } /* Set photon flux to coarsest average wavelet coefficient waveletMatrix [0] [0]. IF BINNING IS DISABLED, this is the total contribution from all directions */ copycolor(ray.rcol, waveletMatrix [0] [0]); /* HACK: signal newPhoton() to set precomputed photon's contribution source from ray -> rsrc */ /* XXX: DO WE STILL NEED THIS CRAP AFTER PRECOMP, SINCE CHILD PMAPS ARE PER-MODIFIER ANYWAY? */ preCompContribPmap -> lastContribSrc.srcIdx = -2; /* Append photon to new heap from ray and increment total count for all modifiers in parent photon map */ newPhoton(preCompContribPmap, &ray); nuPmap.numPhotons++; } /* Update progress */ repProgress++; if (photonRepTime > 0 && time(NULL) >= repLastTime + photonRepTime) pmapPreCompReport(); #ifdef SIGCONT else signal(SIGCONT, pmapPreCompReport); #endif } /* Trash original pmap and its leaf file, and replace with new parent, which now acts as container for the per-modifier child pmaps */ unlink(pmap -> store.leafFname); deletePhotons(pmap); memcpy(pmap, &nuPmap, sizeof(PhotonMap)); #ifdef SIGCONT signal(SIGCONT, SIG_DFL); #endif /* Bail out if no photons could be precomputed */ if (!pmap -> numPhotons) error(USER, "no contribution photons precomputed; try increasing -am" ); /* Build per-modifier precomputed photon maps from their contribution heaps */ lu_doall(pmap -> preCompContribTab, buildPreCompContribPmap, NULL); } #else /* ------------------------------------------------------------------- U N I T T E S T S ------------------------------------------------------------------- */ #include #include #include "func.h" int main (int argc, char *argv []) { unsigned i, scdim, nsamp, numTheta = 0, numPhi = 0; double t, p; RAY ray; int bin; if (argc < 3) { fprintf(stderr, "%s [=; ..]\n", argv [0] ); fprintf(stderr, "Default variable defs: %s\n", PMAP_CONTRIB_SCDEFAULTS ); fputs("\nMissing Shirley-Chiu dimension scDim>1, " "number of samples nsamp\n", stderr ); return -1; } if (!(scdim = atoi(argv [1]))) { fputs("Invalid Shirley-Chiu dimension\n", stderr); return -1; } if (!(nsamp = atoi(argv [2]))) { fputs("Invalid num samples\n", stderr); return -1; } else { numTheta = (int)(sqrt(nsamp) / 2); numPhi = 4* numTheta; } /* (Doan' need to) Init cal func routines for binning */ #if 0 initfunc(); setcontext(RCCONTEXT); #endif /* Compile default orientation variables for contribution binning */ scompile(PMAP_CONTRIB_SCDEFAULTS, NULL, 0); /* Compile custom orientation variabls from command line */ for (i = 3; i < argc; i++) scompile(argv [i], NULL, 0); for (i = 0; i < nsamp; i++) { #if 0 /* Random */ t = 0.5 * PI * drand48(); p = 2 * PI * drand48(); #else /* Stratified */ t = 0.5 * PI * ((i % numTheta) + drand48()) / numTheta; p = 2.0 * PI * ((i / numTheta) + drand48()) / numPhi; #endif ray.rdir [0] = sin(t) * cos(p); ray.rdir [1] = sin(t) * sin(p); ray.rdir [2] = cos(t); bin = ray2bin(&ray, scdim); printf("%.3f\t%.3f\t%.3f\t-->\t%d\n", ray.rdir [0], ray.rdir [1], ray.rdir [2], bin ); } return 0; } #endif #endif /* PMAP_CONTRIB */ diff --git a/pmapcontrib.h b/pmapcontrib.h index 6afd308..046145c 100644 --- a/pmapcontrib.h +++ b/pmapcontrib.h @@ -1,225 +1,224 @@ /* RCSid $Id: pmapcontrib.h,v 2.5 2016/05/17 17:39:47 rschregle Exp $ */ /* ========================================================================= Photon map routines for precomputed light source contributions; these routines interface to mkpmap and rcontrib. 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.h,v 2.5 2016/05/17 17:39:47 rschregle Exp $ */ -/* TODO: SEGFAULTs with mkpmap ... -bn 3 ... !!! */ - #ifndef _PMAPCONTRIB_H #define _PMAPCONTRIB_H #if defined(PMAP_OOC) && !defined(PMAP_CONTRIB) #define PMAP_CONTRIB #endif #include "pmapdata.h" #include "wavelet2.h" #include "mrgbe.h" #ifndef MAXPROCESS #include "rcontrib.h" #endif #ifndef MAXMODLIST /* Max number of contributing sources */ #define MAXMODLIST 1024 #endif /* Filename templates for per-modifier contrib photon maps and wavelet coeffs; these are held in a separate subdirectory PMAP_CONTRIB_DIR */ #define PMAP_CONTRIB_DIR "%s.rc" #define PMAP_CONTRIB_FILE "%s/%s.pm" #define PMAP_CONTRIB_WAVELETFILE "%s/%s.wvt" /* The following variables can be specified to override the orientation of the Shirley-Chiu mapping (see also disk2square.cal). We use the built-in functions in disk2square.c for efficiency and in order to not depend on an external function file. These variables merely mimic the function file interace. RHS : right-hand-coordinate system (-1 for left-hand) rNx, rNy, rNz : surface normal Ux, Uy, Uz : up vector (defines phi = 0) */ #define PMAP_CONTRIB_SCRHS "RHS" #define PMAP_CONTRIB_SCNORMX "rNx" #define PMAP_CONTRIB_SCNORMY "rNy" #define PMAP_CONTRIB_SCNORMZ "rNz" #define PMAP_CONTRIB_SCUPX "Ux" #define PMAP_CONTRIB_SCUPY "Uy" #define PMAP_CONTRIB_SCUPZ "Uz" #define PMAP_CONTRIB_SCDEFAULTS ( \ "RHS=1; rNx=0; rNy=0; rNz=1; Ux=0; Uy=1; Uz=0;" \ ) /* Maximum Shirley-Chiu bin dimension */ #define PMAP_CONTRIB_MAXBINS (mRGBE_DATAMAX) /* Size of encoded coefficients in wavelet file (in bytes) as a function * of the number of compressed coefficients nComp. * NOTE: the coefficient range minimum is omitted from the mRGBE range if * nComp == 1, since it is implicitly zero! */ #define PMAP_CONTRIB_ENCSIZE(nComp) ( \ sizeof(COLR) * (1 + ((nComp) > 1)) + sizeof(mRGBE) * (nComp) \ ) /* Serialise 2D coordinates (x,y) in (dim x dim) Shirley-Chiu square to 1D index. Returns -1 if coordinates invalid */ #define PMAP_CONTRIB_XY2LIN(dim, x, y) ( \ (x) < 0 || (y) < 0 ? -1 : (x) * (dim) + (y) \ ) /* Deserialise 1D index idx to 2D coordinates (x,y) in (dim x dim) Shirley-Chiu square. Returns -1 if bin invalid */ #define PMAP_CONTRIB_LIN2X(dim, idx) ((idx) < 0 ? -1 : (idx) / (dim)) #define PMAP_CONTRIB_LIN2Y(dim, idx) ((idx) < 0 ? -1 : (idx) % (dim)) /* Struct for wavelet coeff thresholding; saves original coeff index */ typedef struct { WAVELET_COEFF *coeff; unsigned idx; } PreComputedContribCoeff; typedef struct { char *waveletFname; FILE *waveletFile; WaveletMatrix2 waveletMatrix, tWaveletMatrix; /* Wavelet coeff compression/encoding buffer */ PreComputedContribCoeff *compressedCoeffs; mRGBERange mrgbeRange; mRGBE *mrgbeCoeffs; unsigned scDim, nBins, coeffDim, nCoeffs, nCompressedCoeffs, nNonZeroCoeffs; unsigned long contribSize; + OOC_Cache *cache; } PreComputedContrib; /* The following are convenient placeholders interfacing to mkpmap and rcontrib. They can be externally set via initPmapContribTab() and referenced within the contrib pmap modules. These variables can then be safely ignored by rtrace/rpict/rvu, without annoying linking errors. */ /* Global pointer to rcontrib's contribution binning LUT */ extern LUTAB *pmapContribTab; /* Contribution/coefficient mode flag */ extern int *pmapContribMode; #if 0 int xy2lin (unsigned scDim, int x, int y); /* Serialise 2D contribution coords in (scDim x scDim) Shirley-Chiu square to 1D index. Returns -1 if coordinates invalid */ void lin2xy (unsigned scDim, int bin, int *x, int *y); /* Deserialise 1D contribution index to 2D coordinates in (scDim x scDim) Shirley-Chiu square. Returns -1 if bin invalid */ #endif MODCONT *addContribModifier(LUTAB *contribTab, unsigned *numContribs, char *mod, char *binParm, int binCnt ); /* Add light source modifier mod to contribution lookup table contribsTab, and update numContribs. Return initialised contribution data for this modifier. */ void addContribModfile(LUTAB *contribTab, unsigned *numContribs, char *modFile, char *binParm, int binCnt ); /* Add light source modifiers from file modFile to contribution lookup * table contribTab, and update numContribs. */ void freePreCompContribNode (void *p); /* Clean up precomputed contribution LUT entry */ void initPmapContribTab (LUTAB *contribTab, int *contribMode); /* Set contribution table and contrib/coeff mode flag (interface to rcmain.c, see also pmapContribTab above) */ void initPmapContrib (PhotonMap *pmap); /* Initialise contribution photon map and check modifiers. NOTE: pmapContribTab must be set before via initPmapContribTab() */ PhotonContribSourceIdx newPhotonContribSource (PhotonMap *pmap, const RAY *contribSrcRay, FILE *contribSrcHeap ); /* Add contribution source ray for emitted contribution photon and save * light source index and binned direction. The current contribution * source is stored in pmap -> lastContribSrc. If the previous * contribution source spawned photons (i.e. has srcIdx >= 0), it's * appended to contribSrcHeap. If contribSrcRay == NULL, the current * contribution source is still flushed, but no new source is set. * Returns updated contribution source counter pmap -> numContribSrc. */ PhotonContribSourceIdx buildContribSources (PhotonMap *pmap, FILE **contribSrcHeap, char **contribSrcHeapFname, PhotonContribSourceIdx *contribSrcOfs, unsigned numHeaps ); /* Consolidate per-subprocess contribution sources heaps into array * pmap -> contribSrc. Returns offset for contribution source index * linearisation in pmap -> numContribSrc. The heap files in * contribSrcHeap are closed on return. */ void initPreComputedContrib (PreComputedContrib *preCompContrib); /* Initialise precomputed contribution container in photon map */ void preComputeContrib (PhotonMap *pmap); /* Precompute contributions for a random subset of (finalGather * pmap -> numPhotons) photons, and init the per-modifier precomputed contribution photon maps in LUT pmap -> preCompContribTab, discarding the original photons. */ void distribPhotonContrib (PhotonMap *pmap, LUTAB *contribTab, unsigned numContribs, int *contribMode, unsigned numProc ); /* Emit photons with binned light source contributions, precompute their contributions and build photon map */ int buildPreCompContribPmap (const LUENT *preCompContribNode, void *p); /* Build per-modifier precomputed photon map. Returns 0 on success. */ void saveContribPhotonMap (const PhotonMap *pmap, const char *fname, int argc, char **argv ); /* Save contribution pmap and its per-modifier precomputed children */ void loadContribPhotonMap (PhotonMap *pmap, const char *fname); /* Load contribution pmap and its per-modifier precomputed children */ void getPreCompPhotonContrib (PhotonMap *pmap, RAY *ray, COLOR totalContrib ); /* Get precomputed light source contributions at ray -> rop for all specified modifiers and accumulate them in pmapContribTab (which maps to rcontrib's binning LUT). Also return total precomputed contribs. */ #endif diff --git a/pmapdata.h b/pmapdata.h index 50c4f7c..9efb605 100644 --- a/pmapdata.h +++ b/pmapdata.h @@ -1,436 +1,433 @@ /* RCSid $Id: pmapdata.h,v 2.14 2020/04/08 15:14:21 rschregle Exp $ */ /* ========================================================================= Photon map types and interface to nearest neighbour lookups in underlying point cloud data structure. The default data structure is an in-core kd-tree (see pmapkdt.{h,c}). This can be overriden with the PMAP_OOC compiletime switch, which enables an out-of-core octree (see oococt.{h,c}). Defining PMAP_FLOAT_FLUX stores photon flux as floats rather than packed RGBE for greater precision; this may be necessary when the flux differs significantly in individual colour channels, e.g. with highly saturated colours. Roland Schregle (roland.schregle@{hslu.ch, gmail.com}) (c) Fraunhofer Institute for Solar Energy Systems, supported by the German Research Foundation (DFG LU-204/10-2, "Fassadenintegrierte Regelsysteme" (FARESYS)) (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") (c) Tokyo University of Science, supported by the JSPS Grants-in-Aid for Scientific Research (KAKENHI JP19KK0115, "Three-Dimensional Light Flow") ========================================================================= $Id: pmapdata.h,v 2.14 2020/04/08 15:14:21 rschregle Exp $ */ #ifndef PMAPDATA_H #define PMAPDATA_H #ifndef NIX #if defined(_WIN32) || defined(_WIN64) #define NIX 0 #else #define NIX 1 #endif #endif #if (defined(PMAP_OOC) && !NIX) #error "OOC currently only supported on NIX -- tuff luck." #endif #include "ray.h" #include "pmaptype.h" #include "paths.h" #include "lookup.h" #include /* Source of a contribution photon. This consists of the emitting light source and binned direction. These records are only used to precompute contribution photons. They are referenced by contribution photons (see contribIdx field in struct Photon below) in a surjective mapping, since multiple photons may share the same emitting source and direction if they lie along its associated path. For this reason it is more efficient to factor this data out of the photons themselves and consolidate it here until the photons have been precomputed, after which it is no longer needed. */ typedef struct { int16 srcIdx, /* Index of emitting light source */ srcBin; /* Binned incident direction */ } PhotonContribSource; typedef uint32 PhotonPathID; typedef uint32 PhotonContribSourceIdx; #define PMAP_MAXCONTRIBSRC UINT32_MAX #define photonSrcIdx(pm, p) ((pm) -> contribSrc \ ? (pm) -> contribSrc [(p) -> aux.contribSrc].srcIdx \ : (p) -> aux.pathID\ ) #define photonSrcBin(pm, p) ( \ (pm) -> contribSrc [(p) -> aux.contribSrc].srcBin \ ) #define photonSrcMod(pm, p) findmaterial(source [photonSrcIdx(pm, p)].so) /* Multipurpose auxiliary photon attribute type */ typedef union { /* Photon's propagation distance (= path length / time of flight) for temporal light flow */ float pathLen; /* Index into contribution photon's emitting source and binned direction; see struct PhotonContribSource above */ PhotonContribSourceIdx contribSrc; /* Unique path ID for all other photon types */ PhotonPathID pathID; } PhotonAuxAttrib; /* Macros for photon's generating subprocess field */ #ifdef PMAP_OOC #define PMAP_PROCBITS 7 #else #define PMAP_PROCBITS 5 #endif #define PMAP_MAXPROC (1 << PMAP_PROCBITS) #define PMAP_GETRAYPROC(r) ((r) -> crtype >> 8) #define PMAP_SETRAYPROC(r,p) ((r) -> crtype |= p << 8) typedef struct { float pos [3]; /* Photon position */ signed char norm [3]; /* Encoded normal / incident direction [volume photons] */ union { struct { #ifndef PMAP_OOC unsigned char discr : 2; /* kd-tree discriminator axis */ #endif unsigned char caustic : 1; /* Specularly scattered (=caustic) */ /* Photon's generating subprocess index, used for primary ray * index linearisation when building contrib pmaps; note this is * reduced for kd-tree to accommodate the discriminator field */ unsigned char proc : PMAP_PROCBITS; }; unsigned char flags; }; /* Photon flux in watts or lumen / photon contribution [contrib photons] / average wavelet coefficient [precomputed contrib photons] */ #ifdef PMAP_FLOAT_FLUX COLOR flux; #else COLR flux; #endif /* Auxiliary field; this is a multipurpose, type-specific field used by the following photon types (as identified by enum PhotonMapType in pmaptype.h): PMAP_TYPE_CONTRIB: Index into photon map's contrib origin array. PMAP_TYPE_TEMPLIGHTFLOW: Distance travelled by photon / time of flight All others: Photon path ID. */ PhotonAuxAttrib aux; } Photon; /* Define PMAP_FLOAT_FLUX to store photon flux as floats instead of * compact RGBE, which was found to improve accuracy in analytical * validation. */ #ifdef PMAP_FLOAT_FLUX #define setPhotonFlux(p,f) copycolor((p) -> flux, f) #define getPhotonFlux(p,f) copycolor(f, (p) -> flux) #else #define setPhotonFlux(p,f) setcolr((p)->flux, (f)[0], (f)[1], (f)[2]) #define getPhotonFlux(p,f) colr_color(f, (p) -> flux) #endif /* Define search queue and underlying data struct types */ #ifdef PMAP_OOC #include "pmapooc.h" #else #include "pmapkdt.h" #include "pmaptkdt.h" #endif /* Mean size of heapfile write buffer, in number of photons */ #define PMAP_HEAPBUFSIZE 1e6 /* Mean idle time between heap locking attempts, in usec */ #define PMAP_HEAPBUFSLEEP 2e6 /* Temporary filename for photon heaps */ #define PMAP_TMPFNAME TEMPLATE #define PMAP_TMPFNLEN (TEMPLEN + 1) /* Forward declarations */ struct PhotonMap; struct PreComputedContrib; struct PhotonBiasCompNode; typedef struct PhotonMap { PhotonMapType type; /* See pmaptype.h */ char *fileName; /* Photon map file */ /* ================================================================ * PRE/POST-BUILD STORAGE * ================================================================ */ FILE *heap; /* Unsorted photon heap prior to construction of store */ char heapFname [sizeof(PMAP_TMPFNAME)]; Photon *heapBuf; /* Write buffer for above */ unsigned long heapBufLen, /* Current & max size of heapBuf */ heapBufSize; PhotonStorage store; /* Photon storage in space subdividing data struct */ /* ================================================================ * PHOTON DISTRIBUTION STUFF * ================================================================ */ unsigned long distribTarget, /* Num stored specified by user */ numPhotons; /* Num actually stored */ float distribRatio; /* Probability of photon storage */ COLOR photonFlux; /* Average photon flux */ unsigned short randState [3]; /* Local RNG state */ /* ================================================================ * PHOTON LOOKUP STUFF * ================================================================ */ union { /* Flags passed to findPhotons() */ char lookupCaustic : 1; char lookupFlags; }; PhotonSearchQueue squeue; /* Search queue for photon lookups */ unsigned minGather, /* Specified min/max photons per */ maxGather; /* density estimate */ /* NOTE: All distances are SQUARED */ float maxDist2, /* Max search radius */ maxDist0, /* Initial value for above */ maxDist2Limit, /* Hard limit for above */ gatherTolerance; /* Fractional deviation from minGather/ maxGather for short lookup */ void (*lookup)( struct PhotonMap*, RAY*, COLOR ); /* Callback for type-specific photon * lookup (usually density estimate) */ /* ================================================================ * TRANSIENT PHOTON STUFF * ================================================================ */ double velocity, /* Speed of light in units of scene geometry [1/sec] */ time, /* Photons' time of flight for transient lookups */ minPathLen, maxPathLen, avgPathLen; /* Min/max/avg path length */ /* ================================================================ * CONTRIBUTION PHOTON STUFF * ================================================================ */ PhotonContribSource *contribSrc, /* Contribution source array */ lastContribSrc; /* Current contrib source */ PhotonContribSourceIdx numContribSrc; /* Number of contrib sources */ LUTAB *preCompContribTab; /* LUT for per-modifier precomp contrib child photon maps (in parent) or NULL (in child) */ struct PreComputedContrib *preCompContrib; /* Precomputed contribs (in child) or NULL (in parent) */ FILE *contribHeap; /* Out-of-core heap containing unsorted precomputed contrib photon bins prior to construction of store */ char contribHeapFname [sizeof(PMAP_TMPFNAME)]; int contribMode; /* If 0, photon map contains precomputed coefficients, else contributions */ - #ifdef PMAP_OOC - OOC_Cache *contribCache; - #endif /* ================================================================ * BIAS COMPENSATION STUFF * ================================================================ */ struct PhotonBiasCompNode *biasCompHist; /* Bias compensation history */ /* ================================================================ * STATISTIX * ================================================================ */ unsigned long totalGathered, /* Total photons gathered */ numDensity, /* Num density estimates */ numLookups, /* Counters for short photon lookups */ numShortLookups; unsigned minGathered, /* Min/max photons actually gathered */ maxGathered, /* per density estimate */ shortLookupPct; /* % of short lookups for stats */ float minError, /* Min/max/rms density estimate error */ maxError, rmsError, CoGdist, /* Avg distance to centre of gravity */ maxPos [3], /* Max & min photon positions */ minPos [3]; FVECT CoG; /* Centre of gravity (avg photon pos) */ #ifdef PMAP_PATHFILT /* ================================================================ * PHOTON PATH FILTERING STUFF * ================================================================ */ LUTAB *pathLUT; /* Photon path lookup table to filter volume photons */ char **pathLUTKeys; /* Preallocated buffer to store keys for path lookup table */ unsigned numPathLUTKeys; /* Num keys currently in key buffer (= next free entry at tail) */ #endif } PhotonMap; /* Photon maps by type (see PhotonMapType) */ extern PhotonMap *photonMaps []; /* Macros for specific photon map types */ #define globalPmap (photonMaps [PMAP_TYPE_GLOBAL]) #define preCompPmap (photonMaps [PMAP_TYPE_PRECOMP]) #define causticPmap (photonMaps [PMAP_TYPE_CAUSTIC]) #define directPmap (photonMaps [PMAP_TYPE_DIRECT]) #define contribPmap (photonMaps [PMAP_TYPE_CONTRIB]) #define volumePmap (photonMaps [PMAP_TYPE_VOLUME]) #define transientPmap (photonMaps [PMAP_TYPE_TRANSIENT]) #ifdef PMAP_PHOTONFLOW /* Transient lightflow has precedence */ #define lightFlowPmap (transLightFlowPmap \ ? transLightFlowPmap : photonMaps [PMAP_TYPE_LIGHTFLOW] \ ) #define transLightFlowPmap (photonMaps [PMAP_TYPE_TRANSLIGHTFLOW]) #else #define lightflowPmap NULL #define transLightFlowPmap NULL #endif /* Photon map type tests */ #define isGlobalPmap(p) ((p) -> type == PMAP_TYPE_GLOBAL) #define isCausticPmap(p) ((p) -> type == PMAP_TYPE_CAUSTIC) #define isContribPmap(p) ((p) -> type == PMAP_TYPE_CONTRIB) #define isContribChildPmap(p) ((p) -> type == PMAP_TYPE_CONTRIB_CHILD) #define isVolumePmap(p) ((p) -> type == PMAP_TYPE_VOLUME) #define isTransientPmap(p) ((p) -> type == PMAP_TYPE_TRANSIENT) #ifdef PMAP_PHOTONFLOW /* lightflow also implies transient lightflow */ #define isLightFlowPmap(p) ( \ (p) -> type == PMAP_TYPE_LIGHTFLOW || isTransLightFlowPmap(p) \ ) #define isTransLightFlowPmap(p) ( \ (p) -> type == PMAP_TYPE_TRANSLIGHTFLOW \ ) #endif void initPhotonMap (PhotonMap *pmap, PhotonMapType t); /* Initialise empty photon map of specified type */ int newPhoton (PhotonMap *pmap, const RAY *ray); /* Create new photon with ray's direction, intersection point, and flux, and append to unsorted photon heap pmap -> heap. The photon is accepted with probability pmap -> distribRatio for global density control; if the photon is rejected, -1 is returned, else 0. The flux is scaled by ray -> rweight and 1 / pmap -> distribRatio. */ void initPhotonHeap (PhotonMap *pmap); /* Open photon heap file */ void flushPhotonHeap (PhotonMap *pmap); /* Flush photon heap buffa pmap -> heapBuf to heap file pmap -> heap; * used by newPhoton() and to finalise heap in distribPhotons(). */ void buildPhotonMap (PhotonMap *pmap, double *photonFlux, PhotonContribSourceIdx *contribSrcOfs, unsigned nproc ); /* Postprocess unsorted photon heap pmap -> heap and build underlying data * structure pmap -> store. This is prerequisite to photon lookups with * findPhotons(). */ /* PhotonFlux is the flux per photon averaged over RGB; this is * multiplied with each photon's flux during the postprocess. In the * case of a contribution photon map, this is an array with a separate * flux specific to each light source due to non-uniform photon emission; * Otherwise it is referenced as a scalar value. Flux is not scaled if * photonFlux == NULL. */ /* Photon map construction may be parallelised if nproc > 1, if * supported. The heap is destroyed on return. */ /* OriginOfs is an array of index offsets for the contribution photon * origins in pmap->contribOrg generated by each of the nproc subprocesses * during contrib photon distribution (see distribPhotonContrib()). These * offsets are used to linearise the photon origin indices in the * postprocess. This linearisation is skipped if originOfs == NULL, * e.g. when building a global/caustic/volume photon map, where the * origins are serial path IDs. */ void findPhotons (PhotonMap* pmap, const RAY *ray); /* Find pmap -> squeue.len closest photons to ray -> rop with similar normal. For volume photons ray -> rorg is used and the normal is ignored (being the incident direction in this case). Found photons are placed search queue starting with the furthest photon at pmap -> squeue.node, and pmap -> squeue.tail being the number actually found. */ Photon *find1Photon (PhotonMap *pmap, const RAY *ray, Photon *photon, void *photonIdx ); /* Find single closest photon to ray -> rop with similar normal. Return NULL if none found, else the supplied Photon* buffer and the photon's index/ID (if photonIdx != NULL), indicating that it contains a valid photon. */ void getPhoton (PhotonMap *pmap, PhotonIdx idx, Photon *photon); /* Retrieve photon referenced by idx from pmap -> store */ Photon *getNearestPhoton (const PhotonSearchQueue *squeue, PhotonIdx idx); /* Retrieve photon from NN search queue after calling findPhotons() */ PhotonIdx firstPhoton (const PhotonMap *pmap); /* Index to first photon, to be passed to getPhoton(). Indices to * subsequent photons can be optained via increment operator (++) */ void deletePhotons (PhotonMap*); /* Free dem mammaries... */ #endif diff --git a/pmcontrib4.c b/pmcontrib4.c index c84eacb..dae44b3 100644 --- a/pmcontrib4.c +++ b/pmcontrib4.c @@ -1,581 +1,611 @@ #ifndef lint static const char RCSid[] = "$Id: pmcontrib2.c,v 2.5 2018/11/08 00:54:07 greg Exp $"; #endif /* ========================================================================= Photon map routines for precomputed light source contributions. These routines interface to rcontrib. 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$ */ -#include "pmapcontrib.h" +#include "pmcontribcache.h" #ifdef PMAP_CONTRIB #include "pmapmat.h" #include "pmapsrc.h" #include "pmaprand.h" #include "pmapio.h" #include "pmapdiag.h" #include "otypes.h" #include "otspecial.h" #include "oocnn.h" #include "random.h" void initPmapContribTab (LUTAB *contribTab, int *contribMode) /* Set contribution table (interface to rcmain.c) */ { pmapContribTab = contribTab; pmapContribMode = contribMode; } /* ------------------ CONTRIB PMAP LOADING STUFF --------------------- */ static int checkContribModifier (const LUENT *modContNode, void *p) /* Check for valid modifier in LUT entry */ { MODCONT *modCont = (MODCONT*)modContNode -> data; char *modName = (char*)modCont -> modname; OBJECT modObj = modifier(modName); const PhotonMap *pmap = (PhotonMap*)p; if (modObj == OVOID) { sprintf(errmsg, "invalid modifier %s", modName); error(USER, errmsg); } if (!islight(objptr(modObj) -> otype)) { sprintf(errmsg, "%s is not a light source modifier", modName); error(USER, errmsg); } /* Warn if number of bins exceeds lookup bandwidth, as this is * guaranteed to result in empty bins! */ if (pmap -> maxGather < modCont -> nbins) { sprintf(errmsg, "contribution photon lookup bandwidth too low " "for modifier %s, some bins will be zero", modName ); error(WARNING, errmsg); } return 0; } void initPmapContrib (PhotonMap *pmap) /* Set up photon map contributions and get binning parameters */ { unsigned t; /* Avoid incompatible photon maps */ for (t = 0; t < NUM_PMAP_TYPES; t++) if (photonMaps [t] && t != PMAP_TYPE_CONTRIB) { sprintf(errmsg, "%s photon map does not support contributions", pmapName [t] ); error(USER, errmsg); } if (!pmapContribTab) error(INTERNAL, "contribution LUT not initialised in initPmapContrib()" ); /* Check for valid contribution modifiers */ lu_doall(pmapContribTab, checkContribModifier, pmap); /* Set callback to collect contributions */ if (pmap) { pmap -> lookup = getPreCompPhotonContrib; /* XXX: Need tighter tolerance for contribution photon lookups? */ pmap -> gatherTolerance = 1.0; } } static int loadPreCompContrib (const LUENT *modContNode, void *p) /* Load child contribution photon map for current contrib modifier entry */ { MODCONT *modCont = (MODCONT*)modContNode -> data; char *modName = (char*)modCont -> modname; PhotonMap *preCompContribPmap; const PhotonMap *parentPmap = (const PhotonMap*)p; LUENT *preCompContribNode; PreComputedContrib *preCompContrib; char dirName [1024]; /* Allocate new LUT entry for child precomputed contribution pmap */ preCompContribNode = lu_find(parentPmap -> preCompContribTab, modName); if (!preCompContribNode) error(SYSTEM, "out of memory allocating LUT entry in loadPreCompContrib()" ); preCompContribNode -> key = modName; /* Allocate child precomputed contribution photon map */ preCompContribNode -> data = (char*)( preCompContribPmap = malloc(sizeof(PhotonMap)) ); if (!preCompContribPmap) error(SYSTEM, "out of memory allocating precomputed contribution " "photon map in loadPreCompContrib()" ); /* Set subdirectory from parent photon map's filename */ snprintf(dirName, sizeof(dirName), PMAP_CONTRIB_DIR, parentPmap -> fileName ); /* Allocate & set child pmap's filename from subdir and modifier */ preCompContribPmap -> fileName = malloc(strlen(parentPmap -> fileName) + strlen(modName) + strlen(PMAP_CONTRIB_FILE) ); if (!preCompContribPmap -> fileName) error(SYSTEM, "out of memory allocating filename in " "loadPreCompContrib()" ); snprintf(preCompContribPmap -> fileName, sizeof(dirName) + 4, PMAP_CONTRIB_FILE, dirName, modName ); /* Load child precomp contrib photon map for current modifier; loadPhotonMap() will not recurse and call loadContribPhotonMap() again because preCompContribPmap -> preCompContribTab == NULL */ loadPhotonMap(preCompContribPmap, preCompContribPmap -> fileName); /* Pass parent pmap's lookup parameters on to child */ preCompContribPmap -> minGather = parentPmap -> minGather; preCompContribPmap -> maxGather = parentPmap -> maxGather; preCompContribPmap -> gatherTolerance = parentPmap -> gatherTolerance; /* Override contrib/coefficient mode if it doesn't match precomputed */ if (*pmapContribMode != preCompContribPmap -> contribMode) { sprintf(errmsg, "photon map contains precomputed %s, overriding rcontrib setting", preCompContribPmap -> contribMode ? "contributions" : "coefficients" ); error(WARNING, errmsg); *pmapContribMode = preCompContribPmap -> contribMode; } /* NOTE: preCompContribPmap -> preCompContrib is initialised by * loadPhotonMap() */ preCompContrib = (PreComputedContrib*)( preCompContribPmap -> preCompContrib ); /* Set number of bins and wavelet coefficients */ preCompContrib -> nBins = preCompContrib -> scDim * preCompContrib -> scDim; preCompContrib -> nCoeffs = preCompContrib -> coeffDim * preCompContrib -> coeffDim; /* Set wavelet coefficient filename */ preCompContrib -> waveletFname = malloc(strlen(dirName) + strlen(modName) + strlen(PMAP_CONTRIB_WAVELETFILE) ); if (!preCompContribPmap -> fileName) error(SYSTEM, "out of memory allocating filename in " "loadPreCompContrib()" ); snprintf(preCompContrib -> waveletFname, sizeof(dirName) + 5, PMAP_CONTRIB_WAVELETFILE, dirName, modName ); return 0; } void loadContribPhotonMap (PhotonMap *pmap, const char *fname) /* Load contribution pmap and its per-modifier precomputed children */ { LUTAB lutInit = LU_SINIT(NULL, freePreCompContribNode); /* Allocate & init LUT containing per-modifier child contrib pmaps */ if (!(pmap -> preCompContribTab = malloc(sizeof(lutInit)))) error(SYSTEM, "out of memory allocating precomputed contribution " "LUT in loadContribPhotonMap()" ); memcpy(pmap -> preCompContribTab, &lutInit, sizeof(lutInit)); /* NOTE: filename already set in loadPhotonMap() pmap -> fileName = (char*)fname; */ /* Init contrib photon map for light source contributions */ initPmapContrib(pmap); /* Load child contribution photon maps for each modifier in contribTab */ lu_doall(pmapContribTab, loadPreCompContrib, pmap); } static int decodeContribs (PreComputedContrib *preCompContrib) /* Decode and decompress mRGBE-encoded wavelet coefficients in preCompContrib -> mrgbeCoeffs, apply inverse wavelet transform and return decoded contributions in the wavelet coefficient matrix preCompContrib -> waveletMatrix. NOTE: THE WAVELET COEFFICIENT MATRIX IS ASSUMED TO BE ZEROED, with the average wavelet coefficient in the upper left of the matrix (i.e. preCompContrib -> waveletMatrix [0][0]). Returns 0 on success, else -1. */ { unsigned c, i, j, coeffIdx, scDim, coeffDim, nCoeffs, nCompressedCoeffs; WaveletMatrix2 waveletMatrix; mRGBERange *mrgbeRange; mRGBE *mrgbeCoeffs; DCOLOR fCoeff; if (!preCompContrib || !preCompContrib -> mrgbeCoeffs || !(waveletMatrix = preCompContrib -> waveletMatrix) || !preCompContrib -> tWaveletMatrix ) /* Should be initialised by caller! */ return -1; scDim = preCompContrib -> scDim; coeffDim = preCompContrib -> coeffDim; nCoeffs = preCompContrib -> nCoeffs; nCompressedCoeffs = preCompContrib -> nCompressedCoeffs; mrgbeCoeffs = preCompContrib -> mrgbeCoeffs; mrgbeRange = &preCompContrib -> mrgbeRange; /* Init mRGBE coefficient normalisation from range */ if (!mRGBEinit(mrgbeRange, mrgbeRange -> min, mrgbeRange -> max)) return -1; /* Decode mRGBE coefficients and populate wavelet coefficient matrix, omitting average at waveletMatrix [0][0]. NOTE: The matrix should be initialised to zero so */ for (c = 0; c < nCompressedCoeffs; c++) { coeffIdx = mRGBEdecode(mrgbeCoeffs [c], mrgbeRange, fCoeff); #ifdef PMAP_CONTRIB_DBG /* Check for invalid decoded coefficients */ if (coeffIdx < 1 || coeffIdx >= nCoeffs) error(CONSISTENCY, "wavelet coefficient index out of range in decodeContribs()" ); #endif i = PMAP_CONTRIB_LIN2X(coeffDim, coeffIdx); j = PMAP_CONTRIB_LIN2Y(coeffDim, coeffIdx); copycolor(waveletMatrix [i] [j], fCoeff); } /* Do inverse 2D wavelet transform on preCompContrib -> waveletMatrix (which actually maps to binnedContribs) */ if (!padWaveletInvXform2(waveletMatrix, preCompContrib -> tWaveletMatrix, coeffDim, scDim ) ) error(INTERNAL, "failed inverse wavelet transform in decodeContribs()" ); #ifdef PMAP_CONTRIB_DBG for (i = 0; i < scDim; i++) { for (j = 0; j < scDim; j++) { /* Dump decoded contribs for debugging */ printf("% 7.3g\t", colorAvg(waveletMatrix [i][j])); /* Warn if coefficient is negative; this _can_ happen due to loss of precision by the mRGBE encoding */ if (min(min(waveletMatrix [i][j][0], waveletMatrix [i][j][1]), waveletMatrix [i][j][2] ) < 0 ) error(WARNING, "negative contributions in getPreCompContrib()"); } putchar('\n'); } putchar('\n'); #endif return 0; } static void getPreCompContribByPhoton (const Photon *photon, OOC_DataIdx photonIdx, PreComputedContrib *preCompContrib, DCOLOR *binnedContribs ) /* Fetch and decode mRGBE-encoded wavelet coefficients for given photon from wavelet file at offset photonIdx, perform inverse wavelet xform, and return the reconstructed binned contributions in binnedContribs (which is assumed to be variable). Returns 0 on success, else -1. */ /* XXX: HERE binnedContribs POINTS TO CACHE PAGE DATA! */ { unsigned i, j, scDim, coeffDim, nCompressedCoeffs; COLR mrgbeRange32 [2]; COLOR fCoeff; if (!binnedContribs || !preCompContrib) /* Shouldn't happen */ error(INTERNAL, "contributions not initialised in getPreCompContrib()" ); if (preCompContrib -> nBins <= 1) /* No binning, so nothing to decode */ return; scDim = preCompContrib -> scDim; coeffDim = preCompContrib -> coeffDim; nCompressedCoeffs = preCompContrib -> nCompressedCoeffs; /* Lazily initialise preCompContrib */ if (!preCompContrib -> waveletFile) { /* Lazily open wavelet coefficient file */ preCompContrib -> waveletFile = fopen( preCompContrib -> waveletFname, "rb" ); if (!preCompContrib -> waveletFile) { sprintf(errmsg, "failed to open wavelet coefficient file %s " "in getPreCompContrib()", preCompContrib -> waveletFname ); error(SYSTEM, errmsg); } /* Set record size of encoded coeffs in wavelet file */ preCompContrib -> contribSize = PMAP_CONTRIB_ENCSIZE( nCompressedCoeffs ); /* Lazily allocate buffer for mRGBE wavelet coeffs */ preCompContrib -> mrgbeCoeffs = calloc( nCompressedCoeffs, sizeof(mRGBE) ); if (!preCompContrib -> mrgbeCoeffs) error(SYSTEM, "out of memory allocating decoded wavelet " "coefficients in getPreCompContrib()" ); /* Lazily allocate primary and transposed wavelet matrices */ preCompContrib -> waveletMatrix = allocWaveletMatrix2(coeffDim); preCompContrib -> tWaveletMatrix = allocWaveletMatrix2(coeffDim); if (!preCompContrib -> waveletMatrix || !preCompContrib -> tWaveletMatrix ) error(SYSTEM, "out of memory allocating wavelet coefficient " "matrix in getPreCompContrib()" ); } /* Seek to photon index in wavelet file and read mRGBE normalisation and encoded bins */ if (fseek(preCompContrib -> waveletFile, photonIdx * preCompContrib -> contribSize, SEEK_SET ) < 0 ) error(SYSTEM, "failed seek in wavelet coefficient file " "in getPreCompContrib()" ); /* Read only mRGBE maximum if 1 compressed bin (maximum compression), * since minimum is implicitly zero and therefore omitted */ getbinary(mrgbeRange32, sizeof(COLR), 1 + (nCompressedCoeffs > 1), preCompContrib -> waveletFile ); if (getbinary(preCompContrib -> mrgbeCoeffs, sizeof(mRGBE), nCompressedCoeffs, preCompContrib -> waveletFile ) != nCompressedCoeffs ) error(SYSTEM, "failed reading from wavelet coefficient file " "in getPreCompContrib()" ); /* Get mRGBE range (NOTE min/max are reversed in the coeff file) */ colr_color(fCoeff, mrgbeRange32 [0]); copycolor(preCompContrib -> mrgbeRange.max, fCoeff); if (nCompressedCoeffs > 1) { colr_color(fCoeff, mrgbeRange32 [1]); copycolor(preCompContrib -> mrgbeRange.min, fCoeff); } else { /* Single compressed bin; mRGBE minimum is implicitly zero */ setcolor(preCompContrib -> mrgbeRange.min, 0, 0, 0); } /* Zero wavelet coefficient matrix and set average coefficient from photon flux; this coefficient is expected by the inverse transform routine in the upper left of the matrix (i.e. preCompContrib -> waveletMatrix [0] [0]) */ zeroCoeffs2(preCompContrib -> waveletMatrix, coeffDim); getPhotonFlux((Photon*)photon, fCoeff); copycolor(preCompContrib -> waveletMatrix [0] [0], fCoeff); /* All set, now decode mRGBE coeffs and invert wavelet transform */ if (decodeContribs(preCompContrib)) error(INTERNAL, "failed contribution decoding/decompression " "in getPreCompContrib()" ); /* Copy decoded contributions from wavelet coefficient matrix rows */ for (i = 0; i < scDim; i++) memcpy(binnedContribs + PMAP_CONTRIB_XY2LIN(scDim, i, 0), preCompContrib -> waveletMatrix [i], scDim * WAVELET_COEFFSIZE ); } typedef struct { const RAY *ray; RREAL rayCoeff [3]; COLORV *totalContrib; } PreCompContribRCData; static int getPreCompContribByMod (const LUENT *preCompContribTabNode, void *p ) /* Fetch and decode precomputed contributions from closest photon in pmap * referenced by preCompContribTabNode, and accumulate in pmapContribTab * for the corresponding modifier */ { PhotonMap *preCompContribPmap = (PhotonMap*)( preCompContribTabNode -> data ); PreComputedContrib *preCompContrib; const char *modName = preCompContribTabNode -> key; MODCONT *modCont; PreCompContribRCData *rcData = (PreCompContribRCData*)p; LUENT *contribTabNode; Photon photon; OOC_DataIdx photonIdx; COLOR photonContrib; unsigned b, k; if (!preCompContribPmap || !rcData || !(preCompContrib = (PreComputedContrib*)( preCompContribPmap -> preCompContrib )) ) { sprintf(errmsg, "missing precomputed contributions for modifier %s", modName ); error(INTERNAL, errmsg); } /* Find rcontrib's LUT entry for this modifier */ contribTabNode = lu_find(pmapContribTab, modName); if (!contribTabNode || !contribTabNode -> key || !(modCont = (MODCONT*)contribTabNode -> data) ) { sprintf(errmsg, "missing contribution LUT entry for modifier %s", modName ); error(INTERNAL, errmsg); } /* Reallocate rcontrib bins if they don't match precomputed */ if (modCont -> nbins != preCompContrib -> nBins) { contribTabNode -> data = realloc(modCont, sizeof(MODCONT) + sizeof(DCOLOR) * (preCompContrib -> nBins - 1) ); if (!contribTabNode -> data) { sprintf(errmsg, "out of memory reallocating contribution bins " "for modifier %s", modCont -> modname ); error(SYSTEM, errmsg); } else { sprintf(errmsg, "bin count mismatch for modifier %s, resizing", modCont -> modname ); error(WARNING, errmsg); } modCont = (MODCONT*)contribTabNode -> data; modCont -> nbins = preCompContrib -> nBins; memset(modCont -> cbin, 0, sizeof(DCOLOR) * modCont -> nbins); } /* Fetch closest photon and its contributions */ if (find1Photon(preCompContribPmap, rcData -> ray, &photon, - &photonIdx - )) { - /* XXX: TEMPORARY BUFFER FOR DECODED BINS, PENDING CONTRIB CACHE */ - DCOLOR binnedContribs [preCompContrib -> nBins]; - + &photonIdx + ) + ) { if (preCompContrib -> nBins > 1) { - /* BINNING ENABLED; fetch and decode bins for found photon */ - getPreCompContribByPhoton(&photon, photonIdx, preCompContrib, - binnedContribs - ); + /* BINNING ENABLED; lazily init contrib cache if necessary */ + if (initContribCache(preCompContrib)) { + /* CACHE ENABLED; fetch cache page for found photon */ + DCOLOR *cacheBins; + + if (!getContribCache(preCompContrib, photonIdx, &cacheBins)) { + /* PAGE NOT CACHED; decode contribs into new cache page */ + getPreCompContribByPhoton(&photon, photonIdx, preCompContrib, + cacheBins + ); + } + else; /* PAGE CACHED; (re)use decoded contribs from cache! */ + + for (b = 0; b < preCompContrib -> nBins; b++) { + /* Accumulate binned contributions into rcontrib's LUT entry + for this modifier, weighted by ray coefficient. Also sum + total contributions. */ + /* NOTE: Using multcolor() on the decoded contribs would + modify them in the cache, so use a temp variable here. */ + for (k = 0; k < 3; k++) + photonContrib [k] = cacheBins [b] [k] * + rcData -> rayCoeff [k]; + + addcolor(modCont -> cbin [b], photonContrib); + addcolor(rcData -> totalContrib, photonContrib); + } + } + else { + /* CACHE DISABLED; decode contribs into temp array on stack */ + DCOLOR tempBins [preCompContrib -> nBins]; + + getPreCompContribByPhoton(&photon, photonIdx, preCompContrib, + tempBins + ); + + for (b = 0; b < preCompContrib -> nBins; b++) { + /* Accumulate binned contributions into rcontrib's LUT entry + for this modifier, weighted by ray coefficient. Also sum + total contributions. */ + for (k = 0; k < 3; k++) + photonContrib [k] = tempBins [b] [k] * + rcData -> rayCoeff [k]; + + addcolor(modCont -> cbin [b], photonContrib); + addcolor(rcData -> totalContrib, photonContrib); + } + } } else { - /* NO BINNING; get single contribution directly from photon */ + /* NO BINNING; get single contribution directly from photon, + scale by ray coefficient and sum to total contribs */ getPhotonFlux(&photon, photonContrib); - copycolor(binnedContribs [0], photonContrib); - } - - for (b = 0; b < preCompContrib -> nBins; b++) { - /* Accumulate binned contributions into rcontrib's LUT entry for - this modifier, weighted by ray coefficient. Also sum total - contributions. */ - /* NOTE: Using multcolor() on the binned contribs would modify them - in the cache, so we use a temp variable here */ - for (k = 0; k < 3; k++) - photonContrib [k] = binnedContribs [b] [k] * - rcData -> rayCoeff [k]; - - addcolor(modCont -> cbin [b], photonContrib); + multcolor(photonContrib, rcData -> rayCoeff); + addcolor(modCont -> cbin [0], photonContrib); addcolor(rcData -> totalContrib, photonContrib); } } return 0; } void getPreCompPhotonContrib (PhotonMap *pmap, RAY *ray, COLOR totalContrib ) /* Get precomputed light source contributions at ray -> rop for all specified modifiers and accumulate them in pmapContribTab (which maps to rcontrib's binning LUT). Also return total precomputed contribs. */ { PreCompContribRCData rcData; /* Ignore sources */ if (ray -> ro && islight(objptr(ray -> ro -> omod) -> otype)) return; /* Get cumulative path coefficient up to photon lookup point */ raycontrib(rcData.rayCoeff, ray, PRIMARY); setcolor(totalContrib, 0, 0, 0); /* Rcontrib results passed to per-modifier child contrib pmaps */ rcData.ray = ray; rcData.totalContrib = totalContrib; /* Iterate over child contribution photon maps for each modifier and * collect their contributions */ lu_doall(pmap -> preCompContribTab, getPreCompContribByMod, &rcData); } #endif /* PMAP_CONTRIB */ diff --git a/pmcontribcache.c b/pmcontribcache.c new file mode 100644 index 0000000..e5cb0b3 --- /dev/null +++ b/pmcontribcache.c @@ -0,0 +1,93 @@ +#ifndef lint +static const char RCSid[] = "$Id$"; +#endif + +/* + ========================================================================= + Routines to cache decoded bins from precomputed contribution photons. + Derived from ooccache.c + + Roland Schregle (roland.schregle@{hslu.ch, gmail.com}) + (c) Lucerne University of Applied Sciences and Arts, + supported by the Swiss National Science Foundation + (SNSF #179067, "Light Fields for Spatio-Temporal Glare Assessment") + ========================================================================= + + $Id$ +*/ + + + +#include "pmcontribcache.h" +#ifdef PMAP_CONTRIB + + + +OOC_Cache *initContribCache (PreComputedContrib *preCompContrib) +/* Initialise cache for precomputed contribution photons. Returns pointer to + * allocated and initialised cache in preCompContrib -> cache, or NULL if + * caching is disabled */ +{ + static char warn = 1; + + if (!preCompContrib -> cache) { + if (pmapCacheSize > 0) { + /* Allocate cache pages for contribution bins */ + const unsigned numPages = pmapCacheSize / preCompContrib -> nBins; + + /* As far as the OOC_Cache is concerned, a cached "record" consists + * of a set of bins associated with a single photon */ + if (!(preCompContrib -> cache = malloc(sizeof(OOC_Cache))) || + OOC_CacheInit(preCompContrib -> cache, numPages, 1, + preCompContrib -> nBins * sizeof(DCOLOR) + ) + ) + error(SYSTEM, "failed to init contribution photon cache"); + } + else { + if (warn) { + /* Caching disabled; issue one-shot warning */ + error(WARNING, "contribution photon cache DISABLED"); + warn = 0; + } + + return NULL; + } + } + + return preCompContrib -> cache; +} + + + +int getContribCache (const PreComputedContrib *preCompContrib, + OOC_DataIdx idx, DCOLOR **contribBins +) +/* Retrieve cached bins for contribution photon with unique index idx. + Returns pointer contribBins to the cached bins or NULL if caching failed. + Returns 1 if the bins were already cached, else 0, indicating the bins + must be populated by the caller. */ +{ + OOC_Cache *cache; + unsigned long lastNumHits; + + if (!contribBins || !preCompContrib || + !(cache = preCompContrib -> cache) + ) { + /* Junk params or caching disabled; return NULL pointer */ + *contribBins = NULL; + return 0; + } + + /* Caching enabled; retrieve (potentially new) bins from page table */ + lastNumHits = cache -> numHits; + + if (!(*contribBins = OOC_CacheData(cache, NULL, idx))) + return 0; + + /* Check if OOC_CacheData() incremented the cache hit counter, + * indicating to caller whether the bins were already in the cache */ + return cache -> numHits != lastNumHits; +} + +#endif /* PMAP_CONTRIB */ diff --git a/pmcontribcache.h b/pmcontribcache.h new file mode 100644 index 0000000..9854ea0 --- /dev/null +++ b/pmcontribcache.h @@ -0,0 +1,39 @@ +/* RCSid $Id$ */ + +/* + ========================================================================= + Routines to cache decoded bins from precomputed contribution photons. + Derived from ooccache.h + + Roland Schregle (roland.schregle@{hslu.ch, gmail.com}) + (c) Lucerne University of Applied Sciences and Arts, + supported by the Swiss National Science Foundation + (SNSF #179067, "Light Fields for Spatio-Temporal Glare Assessment") + ========================================================================= + + $Id: pmapcontrib.h,v 2.5 2016/05/17 17:39:47 rschregle Exp $ +*/ + + + +#ifndef _PMAPCONTRIB_CACHE_H + #define _PMAPCONTRIB_CACHE_H + + #include "pmapcontrib.h" + + + /* Initialise cache for precomputed contribution photons. Returns + * pointer to allocated and initialised cache in preCompContrib -> cache, + * or NULL if caching is disabled */ + OOC_Cache *initContribCache (PreComputedContrib *preCompContrib); + + + /* Retrieve cached bins for contribution photon with unique index idx. + Returns pointer contribBins to the cached bins or NULL if caching + failed. Returns 1 if the bins were already cached, else 0, indicating + the bins must be populated by the caller. */ + int getContribCache (const PreComputedContrib *preCompContrib, + OOC_DataIdx idx, DCOLOR **contribBins + ); + +#endif \ No newline at end of file