ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/RtraceSimulManager.cpp
Revision: 2.12
Committed: Sat Aug 3 01:54:46 2024 UTC (9 months ago) by greg
Branch: MAIN
Changes since 2.11: +107 -2 lines
Log Message:
feat(rxtrace): Added header manipulation to base class

File Contents

# User Rev Content
1 greg 2.1 #ifndef lint
2 greg 2.12 static const char RCSid[] = "$Id: RtraceSimulManager.cpp,v 2.11 2024/07/08 23:46:04 greg Exp $";
3 greg 2.1 #endif
4     /*
5     * RtraceSimulManager.cpp
6     *
7     * Rtrace simulation manager class implementation
8     *
9     * Created by Greg Ward on 2/2/2023.
10     */
11    
12 greg 2.5 #include <unistd.h>
13 greg 2.12 #include <ctype.h>
14 greg 2.1 #include "RtraceSimulManager.h"
15     #include "source.h"
16    
17     // Load octree and prepare renderer
18     bool
19     RadSimulManager::LoadOctree(const char *octn)
20     {
21     if (octname) { // already running?
22     if (octn && !strcmp(octn, octname))
23     return true;
24 greg 2.6 Cleanup();
25 greg 2.1 }
26 greg 2.12 if (!octn) // don't support stdin octree
27 greg 2.1 return false;
28    
29 greg 2.12 NewHeader(octn); // load header if we can
30 greg 2.1 ray_init((char *)octn);
31     return true;
32     }
33    
34 greg 2.12 // callback function for loading header
35     static int
36     add2header(char *str, void *p)
37     {
38     if (isheadid(str))
39     return 0;
40     if (isformat(str))
41     return 0;
42    
43     return (*(RadSimulManager *)p).AddHeader(str);
44     }
45    
46     // Prepare header from previous input (or clear)
47     // Normally called during octree load
48     bool
49     RadSimulManager::NewHeader(const char *fname)
50     {
51     if (header) {
52     free(header); header = NULL;
53     }
54     if (!fname || fname[0] == '!')
55     return false;
56     FILE *fp = fopen(fname, "rb");
57     bool ok = (getheader(fp, add2header, this) >= 0);
58     fclose(fp);
59     return ok;
60     }
61    
62     // Add a string to header (adds newline if none)
63     bool
64     RadSimulManager::AddHeader(const char *str)
65     {
66     if (!str) return false;
67     int len = strlen(str);
68     if (!len || str[0] == '\n') return false;
69     int olen = 0;
70     if (header) {
71     olen = strlen(header);
72     header = (char *)realloc(header, olen+len+2);
73     } else
74     header = (char *)malloc(len+2);
75     if (!header) return false;
76     strcpy(header+olen, str);
77     if (str[len-1] != '\n') {
78     header[olen+len++] = '\n';
79     header[olen+len] = '\0';
80     }
81     return true;
82     }
83    
84     // helper function to check for white-space and quotations
85     static int
86     check_special(const char *s)
87     {
88     int space_found = 0;
89    
90     while (*s) {
91     if ((*s == '"') | (*s == '\''))
92     return *s; // quotes have priority
93     if (isspace(*s))
94     space_found = *s;
95     s++;
96     }
97     return space_found;
98     }
99    
100     // Append program line to header
101     bool
102     RadSimulManager::AddHeader(int ac, const char *av[])
103     {
104     if ((ac <= 0) | !av) return false;
105     int len = 0;
106     int n;
107     for (n = 0; n < ac; n++) {
108     if (!av[n]) return false;
109     len += strlen(av[n]) + 3;
110     }
111     int hlen = 0;
112     if (header) { // add to header
113     hlen = strlen(header);
114     header = (char *)realloc(header, hlen+len+1);
115     } else
116     header = (char *)malloc(len+1);
117     for (n = 0; n < ac; n++) {
118     int c = check_special(av[n]);
119     if (c) { // need to quote argument?
120     if (c == '"') c = '\'';
121     else c = '"';
122     header[hlen++] = c;
123     strcpy(header+hlen, av[n]);
124     hlen += strlen(av[n]);
125     header[hlen++] = c;
126     } else {
127     strcpy(header+hlen, av[n]);
128     hlen += strlen(av[n]);
129     }
130     header[hlen++] = ' ';
131     }
132     header[hlen-1] = '\n'; // terminate line
133     header[hlen] = '\0';
134     return true;
135     }
136    
137 greg 2.5 // How many processors are available?
138 greg 2.2 int
139     RadSimulManager::GetNCores()
140     {
141 greg 2.6 return sysconf(_SC_NPROCESSORS_ONLN);
142 greg 2.2 }
143    
144 greg 2.1 // Set number of computation threads (0 => #cores)
145     int
146     RadSimulManager::SetThreadCount(int nt)
147     {
148 greg 2.6 if (!Ready())
149     return 0;
150    
151     if (nt <= 0) nt = castonly ? 1 : GetNCores();
152    
153     if (nt == 1)
154     ray_pclose(ray_pnprocs);
155     else if (nt < ray_pnprocs)
156     ray_pclose(ray_pnprocs - nt);
157     else if (nt > ray_pnprocs)
158     ray_popen(nt - ray_pnprocs);
159 greg 2.5
160 greg 2.6 return NThreads();
161 greg 2.1 }
162    
163 greg 2.4 // Assign ray to subthread (fails if NThreads()<2)
164     bool
165     RadSimulManager::SplitRay(RAY *r)
166     {
167 greg 2.6 if (!ray_pnprocs || ThreadsAvailable() < 1)
168 greg 2.4 return false;
169    
170 greg 2.6 return (ray_psend(r) > 0);
171 greg 2.4 }
172    
173     // Process a ray (in subthread), optional result
174     bool
175     RadSimulManager::ProcessRay(RAY *r)
176     {
177 greg 2.6 if (!Ready()) return false;
178    
179     if (!ray_pnprocs) { // single-threaded mode?
180 greg 2.4 samplendx++;
181     rayvalue(r);
182     return true;
183     }
184 greg 2.6 int rv = ray_pqueue(r);
185     if (rv < 0) {
186     error(WARNING, "ray tracing process(es) died");
187 greg 2.4 return false;
188     }
189 greg 2.6 return (rv > 0);
190 greg 2.4 }
191    
192     // Wait for next result (or fail)
193     bool
194     RadSimulManager::WaitResult(RAY *r)
195     {
196 greg 2.6 if (!ray_pnprocs)
197     return false;
198    
199     return (ray_presult(r, 0) > 0);
200 greg 2.4 }
201    
202 greg 2.1 // Close octree, free data, return status
203     int
204 greg 2.3 RadSimulManager::Cleanup(bool everything)
205 greg 2.1 {
206 greg 2.6 if (!ray_pnprocs)
207 greg 2.11 ray_done(everything);
208     else
209 greg 2.5 ray_pdone(everything);
210 greg 2.1 return 0;
211     }
212    
213     // How many threads are currently unoccupied?
214     int
215     RadSimulManager::ThreadsAvailable() const
216     {
217 greg 2.6 if (!ray_pnprocs) return 1;
218    
219 greg 2.5 return ray_pnidle;
220 greg 2.1 }
221    
222     // Global pointer to simulation manager for trace call-back (only one)
223     static const RtraceSimulManager * ourRTsimMan = NULL;
224    
225 greg 2.7 // Call-back for trace output
226     void
227 greg 2.1 RtraceSimulManager::RTracer(RAY *r)
228     {
229     (*ourRTsimMan->traceCall)(r, ourRTsimMan->tcData);
230     }
231    
232 greg 2.6 // Call-back for FIFO output
233     int
234     RtraceSimulManager::Rfifout(RAY *r)
235     {
236     return (*ourRTsimMan->cookedCall)(r, ourRTsimMan->ccData);
237     }
238    
239 greg 2.1 // Check for changes to render flags & adjust accordingly
240     bool
241     RtraceSimulManager::UpdateMode()
242     {
243     rtFlags &= RTmask;
244     if (!cookedCall)
245     rtFlags &= ~RTdoFIFO;
246     if (!traceCall)
247     rtFlags &= ~RTtraceSources;
248     if (rtFlags & RTimmIrrad)
249     rtFlags &= ~RTlimDist;
250    
251     int misMatch = rtFlags ^ curFlags;
252     // updates based on toggled flags
253     if (misMatch & RTtraceSources) {
254     if (rtFlags & RTtraceSources) {
255     for (int sn = 0; sn < nsources; sn++)
256     source[sn].sflags |= SFOLLOW;
257     } else // cannot undo this...
258     rtFlags |= RTtraceSources;
259     }
260 greg 2.4 if (misMatch & RTdoFIFO && FlushQueue() < 0)
261     return false;
262 greg 2.1 curFlags = rtFlags;
263 greg 2.6 // update callbacks
264     if (traceCall)
265 greg 2.1 trace = RTracer;
266 greg 2.6 else if (trace == RTracer)
267 greg 2.1 trace = NULL;
268 greg 2.6 if (rtFlags & RTdoFIFO)
269     ray_fifo_out = Rfifout;
270     else if (ray_fifo_out == Rfifout)
271     ray_fifo_out = NULL;
272     if ((trace != RTracer) & (ray_fifo_out != Rfifout)) {
273 greg 2.1 ourRTsimMan = NULL;
274 greg 2.6 } else if (ourRTsimMan != this) {
275     if (ourRTsimMan)
276     error(WARNING, "Competing top-level simulation managers?");
277     ourRTsimMan = this;
278 greg 2.1 }
279     return true;
280     }
281    
282     extern "C" int m_normal(OBJREC *m, RAY *r);
283    
284 greg 2.6 // compute irradiance rather than radiance
285 greg 2.1 static void
286     rayirrad(RAY *r)
287     {
288     /* pretend we hit surface */
289     r->rxt = r->rot = 1e-5;
290     VSUM(r->rop, r->rorg, r->rdir, r->rot);
291     r->ron[0] = -r->rdir[0];
292     r->ron[1] = -r->rdir[1];
293     r->ron[2] = -r->rdir[2];
294     r->rod = 1.0;
295     /* compute result */
296     r->revf = raytrace;
297     m_normal(&Lamb, r);
298     r->revf = rayirrad;
299     }
300    
301 greg 2.6 // compute first ray intersection only
302 greg 2.1 static void
303     raycast(RAY *r)
304     {
305     if (!localhit(r, &thescene)) {
306     if (r->ro == &Aftplane) { /* clipped */
307     r->ro = NULL;
308     r->rot = FHUGE;
309     } else
310     sourcehit(r);
311     }
312     }
313    
314     // Add ray bundle to queue w/ optional 1st ray ID
315     int
316     RtraceSimulManager::EnqueueBundle(const FVECT orig_direc[], int n, RNUMBER rID0)
317     {
318     int nqueued = 0;
319     RAY res;
320    
321     if (!Ready())
322     return -1;
323    
324     if (castonly && !cookedCall)
325 greg 2.8 error(INTERNAL, "EnqueueBundle() called in castonly mode without cookedCall");
326 greg 2.1
327     if (!UpdateMode()) // update rendering mode if requested
328     return -1;
329    
330 greg 2.9 if (rID0 && curFlags&RTdoFIFO)
331     error(INTERNAL, "Ray number assignment unsupported with FIFO");
332    
333 greg 2.1 while (n-- > 0) { // queue each ray
334     VCOPY(res.rorg, orig_direc[0]);
335     VCOPY(res.rdir, orig_direc[1]);
336     orig_direc += 2;
337     rayorigin(&res, PRIMARY, NULL, NULL);
338 greg 2.9 res.rno = rID0 ? (lastRayID = rID0++) : ++lastRayID;
339 greg 2.1 if (curFlags & RTimmIrrad)
340     res.revf = rayirrad;
341     else if (castonly)
342     res.revf = raycast;
343 greg 2.4 double d = normalize(res.rdir);
344     bool sendRes = (cookedCall != NULL);
345 greg 2.10 if (d > .0) { // direction vector is valid?
346 greg 2.1 if (curFlags & RTlimDist)
347     res.rmax = d;
348 greg 2.7 if (((curFlags&RTdoFIFO) != 0) & (ray_pnprocs > 0)) {
349     if (ray_fifo_in(&res) < 0)
350     return -1;
351 greg 2.4 sendRes = false;
352 greg 2.6 } else
353     sendRes &= ProcessRay(&res);
354 greg 2.1 } else if (ThreadsAvailable() < NThreads() &&
355 greg 2.4 FlushQueue() < 0)
356 greg 2.1 return -1;
357 greg 2.6 // may be dummy ray
358     if (sendRes && (*cookedCall)(&res, ccData) < 0)
359     return -1;
360 greg 2.3 nqueued++;
361 greg 2.1 }
362     return nqueued;
363     }
364    
365     // Finish pending rays and complete callbacks
366 greg 2.4 int
367 greg 2.1 RtraceSimulManager::FlushQueue()
368     {
369 greg 2.7 if (curFlags & RTdoFIFO) {
370     if (ray_pnprocs)
371     return ray_fifo_flush();
372     return 0;
373     }
374 greg 2.4 int nsent = 0;
375     RAY res;
376    
377     while (WaitResult(&res)) {
378     if (!cookedCall) continue;
379 greg 2.6 int rv = (*cookedCall)(&res, ccData);
380     if (rv < 0) return -1;
381     nsent += rv;
382 greg 2.4 }
383     return nsent;
384 greg 2.1 }