ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/RtraceSimulManager.cpp
Revision: 2.21
Committed: Sat Nov 9 00:10:49 2024 UTC (5 months, 3 weeks ago) by greg
Branch: MAIN
Changes since 2.20: +9 -2 lines
Log Message:
fix: Changes to source-tracing requires child process restart

File Contents

# User Rev Content
1 greg 2.1 #ifndef lint
2 greg 2.21 static const char RCSid[] = "$Id: RtraceSimulManager.cpp,v 2.20 2024/11/07 18:37:11 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.13 NewHeader(octn); // get octree header
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 greg 2.13 RadSimulManager::NewHeader(const char *inspec)
50 greg 2.12 {
51 greg 2.13 if (hlen) {
52     free(header); header = NULL; hlen = 0;
53 greg 2.12 }
54 greg 2.13 if (!inspec || !*inspec)
55 greg 2.12 return false;
56 greg 2.13 if (inspec[0] == '!') // save command as header?
57     return AddHeader(inspec+1);
58     // attempt to read from file
59     FILE *fp = fopen(inspec, "rb");
60     if (!fp) return false;
61 greg 2.12 bool ok = (getheader(fp, add2header, this) >= 0);
62     fclose(fp);
63     return ok;
64     }
65    
66 greg 2.13 // Add a string to header (adds newline if missing)
67 greg 2.12 bool
68     RadSimulManager::AddHeader(const char *str)
69     {
70     if (!str) return false;
71     int len = strlen(str);
72 greg 2.14 while (len && (str[len-1] == '\n') | (str[len-1] == '\r'))
73 greg 2.13 --len; // don't copy EOL(s)
74     if (!len)
75     return false;
76     if (hlen)
77     header = (char *)realloc(header, hlen+len+2);
78     else
79 greg 2.12 header = (char *)malloc(len+2);
80 greg 2.13 if (!header) {
81     hlen = 0; // XXX should report?
82     return false;
83 greg 2.12 }
84 greg 2.13 memcpy(header+hlen, str, len);
85     hlen += len;
86     header[hlen++] = '\n'; // add single EOL
87     header[hlen] = '\0';
88 greg 2.12 return true;
89     }
90    
91     // helper function to check for white-space and quotations
92     static int
93     check_special(const char *s)
94     {
95     int space_found = 0;
96    
97     while (*s) {
98     if ((*s == '"') | (*s == '\''))
99     return *s; // quotes have priority
100     if (isspace(*s))
101     space_found = *s;
102     s++;
103     }
104     return space_found;
105     }
106    
107     // Append program line to header
108     bool
109 greg 2.15 RadSimulManager::AddHeader(int ac, char *av[])
110 greg 2.12 {
111     if ((ac <= 0) | !av) return false;
112     int len = 0;
113     int n;
114     for (n = 0; n < ac; n++) {
115     if (!av[n]) return false;
116     len += strlen(av[n]) + 3;
117     }
118 greg 2.13 if (hlen) // add to header
119 greg 2.12 header = (char *)realloc(header, hlen+len+1);
120 greg 2.13 else
121 greg 2.12 header = (char *)malloc(len+1);
122 greg 2.13
123 greg 2.12 for (n = 0; n < ac; n++) {
124     int c = check_special(av[n]);
125 greg 2.13 len = strlen(av[n]);
126     if (c | !len) { // need to quote argument?
127     if ((c == '"') | (c == '\n')) c = '\'';
128 greg 2.12 else c = '"';
129     header[hlen++] = c;
130     strcpy(header+hlen, av[n]);
131 greg 2.13 hlen += len;
132 greg 2.12 header[hlen++] = c;
133     } else {
134     strcpy(header+hlen, av[n]);
135 greg 2.13 hlen += len;
136 greg 2.12 }
137     header[hlen++] = ' ';
138     }
139     header[hlen-1] = '\n'; // terminate line
140     header[hlen] = '\0';
141     return true;
142     }
143    
144 greg 2.17 // Look for specific header keyword, return value
145     const char *
146     RadSimulManager::GetHeadStr(const char *key, bool inOK) const
147     {
148     if (!key | !hlen || strchr(key, '\n'))
149     return NULL;
150     if (inOK) // skip leading spaces?
151     while (isspace(*key)) key++;
152    
153     const int klen = strlen(key);
154     if (!klen)
155     return NULL;
156     const char * cp = header;
157     while (*cp) {
158     if (inOK) { // skip leading spaces?
159     while (isspace(*cp) && *cp++ != '\n')
160     ;
161     if (cp[-1] == '\n')
162     continue;
163     }
164     if (!strncmp(cp, key, klen))
165     return cp+klen; // found it!
166    
167     while (*cp && *cp++ != '\n')
168     ;
169     }
170     return NULL;
171     }
172    
173 greg 2.5 // How many processors are available?
174 greg 2.2 int
175     RadSimulManager::GetNCores()
176     {
177 greg 2.6 return sysconf(_SC_NPROCESSORS_ONLN);
178 greg 2.2 }
179    
180 greg 2.1 // Set number of computation threads (0 => #cores)
181     int
182     RadSimulManager::SetThreadCount(int nt)
183     {
184 greg 2.6 if (!Ready())
185     return 0;
186    
187     if (nt <= 0) nt = castonly ? 1 : GetNCores();
188    
189     if (nt == 1)
190     ray_pclose(ray_pnprocs);
191     else if (nt < ray_pnprocs)
192     ray_pclose(ray_pnprocs - nt);
193     else if (nt > ray_pnprocs)
194     ray_popen(nt - ray_pnprocs);
195 greg 2.5
196 greg 2.6 return NThreads();
197 greg 2.1 }
198    
199 greg 2.4 // Assign ray to subthread (fails if NThreads()<2)
200     bool
201     RadSimulManager::SplitRay(RAY *r)
202     {
203 greg 2.6 if (!ray_pnprocs || ThreadsAvailable() < 1)
204 greg 2.4 return false;
205    
206 greg 2.6 return (ray_psend(r) > 0);
207 greg 2.4 }
208    
209     // Process a ray (in subthread), optional result
210     bool
211     RadSimulManager::ProcessRay(RAY *r)
212     {
213 greg 2.6 if (!Ready()) return false;
214    
215     if (!ray_pnprocs) { // single-threaded mode?
216 greg 2.4 samplendx++;
217     rayvalue(r);
218     return true;
219     }
220 greg 2.6 int rv = ray_pqueue(r);
221     if (rv < 0) {
222     error(WARNING, "ray tracing process(es) died");
223 greg 2.4 return false;
224     }
225 greg 2.6 return (rv > 0);
226 greg 2.4 }
227    
228     // Wait for next result (or fail)
229     bool
230     RadSimulManager::WaitResult(RAY *r)
231     {
232 greg 2.6 if (!ray_pnprocs)
233     return false;
234    
235     return (ray_presult(r, 0) > 0);
236 greg 2.4 }
237    
238 greg 2.1 // Close octree, free data, return status
239     int
240 greg 2.3 RadSimulManager::Cleanup(bool everything)
241 greg 2.1 {
242 greg 2.19 if (ray_pnprocs < 0)
243     return 0; // skip in child process
244 greg 2.13 NewHeader();
245 greg 2.6 if (!ray_pnprocs)
246 greg 2.11 ray_done(everything);
247     else
248 greg 2.5 ray_pdone(everything);
249 greg 2.1 return 0;
250     }
251    
252     // How many threads are currently unoccupied?
253     int
254     RadSimulManager::ThreadsAvailable() const
255     {
256 greg 2.6 if (!ray_pnprocs) return 1;
257    
258 greg 2.5 return ray_pnidle;
259 greg 2.1 }
260    
261     // Global pointer to simulation manager for trace call-back (only one)
262     static const RtraceSimulManager * ourRTsimMan = NULL;
263    
264 greg 2.7 // Call-back for trace output
265     void
266 greg 2.1 RtraceSimulManager::RTracer(RAY *r)
267     {
268     (*ourRTsimMan->traceCall)(r, ourRTsimMan->tcData);
269     }
270    
271 greg 2.6 // Call-back for FIFO output
272     int
273     RtraceSimulManager::Rfifout(RAY *r)
274     {
275     return (*ourRTsimMan->cookedCall)(r, ourRTsimMan->ccData);
276     }
277    
278 greg 2.1 // Check for changes to render flags & adjust accordingly
279     bool
280     RtraceSimulManager::UpdateMode()
281     {
282     if (!cookedCall)
283     rtFlags &= ~RTdoFIFO;
284     if (!traceCall)
285     rtFlags &= ~RTtraceSources;
286     if (rtFlags & RTimmIrrad)
287     rtFlags &= ~RTlimDist;
288    
289 greg 2.20 int misMatch = (rtFlags ^ curFlags) & RTmask;
290 greg 2.1 // updates based on toggled flags
291 greg 2.21 if (((misMatch & RTtraceSources) != 0) & (nsources > 0)) {
292     int nt = NThreads();
293     if (nt > 1) {
294     if (FlushQueue() < 0)
295     return false;
296     SetThreadCount(1);
297     }
298 greg 2.18 int sn = nsources;
299 greg 2.1 if (rtFlags & RTtraceSources) {
300 greg 2.18 srcFollowed.NewBitMap(nsources);
301     while (sn--) {
302     if (source[sn].sflags & SFOLLOW)
303     continue;
304 greg 2.1 source[sn].sflags |= SFOLLOW;
305 greg 2.18 srcFollowed.Set(sn);
306     }
307     } else {
308     while (sn--)
309     if (srcFollowed.Check(sn))
310     source[sn].sflags &= ~SFOLLOW;
311     srcFollowed.NewBitMap(0);
312     }
313 greg 2.21 if (nt > 1) SetThreadCount(nt);
314 greg 2.1 }
315 greg 2.4 if (misMatch & RTdoFIFO && FlushQueue() < 0)
316     return false;
317 greg 2.1 curFlags = rtFlags;
318 greg 2.6 // update callbacks
319     if (traceCall)
320 greg 2.1 trace = RTracer;
321 greg 2.6 else if (trace == RTracer)
322 greg 2.1 trace = NULL;
323 greg 2.6 if (rtFlags & RTdoFIFO)
324     ray_fifo_out = Rfifout;
325     else if (ray_fifo_out == Rfifout)
326     ray_fifo_out = NULL;
327     if ((trace != RTracer) & (ray_fifo_out != Rfifout)) {
328 greg 2.1 ourRTsimMan = NULL;
329 greg 2.6 } else if (ourRTsimMan != this) {
330     if (ourRTsimMan)
331     error(WARNING, "Competing top-level simulation managers?");
332     ourRTsimMan = this;
333 greg 2.1 }
334     return true;
335     }
336    
337     extern "C" int m_normal(OBJREC *m, RAY *r);
338    
339 greg 2.6 // compute irradiance rather than radiance
340 greg 2.1 static void
341     rayirrad(RAY *r)
342     {
343     /* pretend we hit surface */
344     r->rxt = r->rot = 1e-5;
345     VSUM(r->rop, r->rorg, r->rdir, r->rot);
346     r->ron[0] = -r->rdir[0];
347     r->ron[1] = -r->rdir[1];
348     r->ron[2] = -r->rdir[2];
349     r->rod = 1.0;
350     /* compute result */
351     r->revf = raytrace;
352     m_normal(&Lamb, r);
353     r->revf = rayirrad;
354     }
355    
356 greg 2.6 // compute first ray intersection only
357 greg 2.1 static void
358     raycast(RAY *r)
359     {
360     if (!localhit(r, &thescene)) {
361     if (r->ro == &Aftplane) { /* clipped */
362     r->ro = NULL;
363     r->rot = FHUGE;
364     } else
365     sourcehit(r);
366     }
367     }
368    
369     // Add ray bundle to queue w/ optional 1st ray ID
370     int
371     RtraceSimulManager::EnqueueBundle(const FVECT orig_direc[], int n, RNUMBER rID0)
372     {
373     int nqueued = 0;
374     RAY res;
375    
376     if (!Ready())
377     return -1;
378    
379     if (castonly && !cookedCall)
380 greg 2.8 error(INTERNAL, "EnqueueBundle() called in castonly mode without cookedCall");
381 greg 2.1
382     if (!UpdateMode()) // update rendering mode if requested
383     return -1;
384    
385 greg 2.9 if (rID0 && curFlags&RTdoFIFO)
386     error(INTERNAL, "Ray number assignment unsupported with FIFO");
387    
388 greg 2.1 while (n-- > 0) { // queue each ray
389     VCOPY(res.rorg, orig_direc[0]);
390     VCOPY(res.rdir, orig_direc[1]);
391 greg 2.16 res.rmax = .0;
392 greg 2.1 orig_direc += 2;
393     rayorigin(&res, PRIMARY, NULL, NULL);
394 greg 2.9 res.rno = rID0 ? (lastRayID = rID0++) : ++lastRayID;
395 greg 2.1 if (curFlags & RTimmIrrad)
396     res.revf = rayirrad;
397     else if (castonly)
398     res.revf = raycast;
399 greg 2.4 double d = normalize(res.rdir);
400     bool sendRes = (cookedCall != NULL);
401 greg 2.10 if (d > .0) { // direction vector is valid?
402 greg 2.1 if (curFlags & RTlimDist)
403     res.rmax = d;
404 greg 2.7 if (((curFlags&RTdoFIFO) != 0) & (ray_pnprocs > 0)) {
405     if (ray_fifo_in(&res) < 0)
406     return -1;
407 greg 2.4 sendRes = false;
408 greg 2.6 } else
409     sendRes &= ProcessRay(&res);
410 greg 2.1 } else if (ThreadsAvailable() < NThreads() &&
411 greg 2.4 FlushQueue() < 0)
412 greg 2.1 return -1;
413 greg 2.6 // may be dummy ray
414     if (sendRes && (*cookedCall)(&res, ccData) < 0)
415     return -1;
416 greg 2.3 nqueued++;
417 greg 2.1 }
418     return nqueued;
419     }
420    
421     // Finish pending rays and complete callbacks
422 greg 2.4 int
423 greg 2.1 RtraceSimulManager::FlushQueue()
424     {
425 greg 2.7 if (curFlags & RTdoFIFO) {
426     if (ray_pnprocs)
427     return ray_fifo_flush();
428     return 0;
429     }
430 greg 2.4 int nsent = 0;
431     RAY res;
432    
433     while (WaitResult(&res)) {
434     if (!cookedCall) continue;
435 greg 2.6 int rv = (*cookedCall)(&res, ccData);
436     if (rv < 0) return -1;
437     nsent += rv;
438 greg 2.4 }
439     return nsent;
440 greg 2.1 }