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

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: RtraceSimulManager.cpp,v 2.20 2024/11/07 18:37:11 greg Exp $";
3 #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 #include <unistd.h>
13 #include <ctype.h>
14 #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 Cleanup();
25 }
26 if (!octn) // don't support stdin octree
27 return false;
28
29 NewHeader(octn); // get octree header
30 ray_init((char *)octn);
31 return true;
32 }
33
34 // 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 *inspec)
50 {
51 if (hlen) {
52 free(header); header = NULL; hlen = 0;
53 }
54 if (!inspec || !*inspec)
55 return false;
56 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 bool ok = (getheader(fp, add2header, this) >= 0);
62 fclose(fp);
63 return ok;
64 }
65
66 // Add a string to header (adds newline if missing)
67 bool
68 RadSimulManager::AddHeader(const char *str)
69 {
70 if (!str) return false;
71 int len = strlen(str);
72 while (len && (str[len-1] == '\n') | (str[len-1] == '\r'))
73 --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 header = (char *)malloc(len+2);
80 if (!header) {
81 hlen = 0; // XXX should report?
82 return false;
83 }
84 memcpy(header+hlen, str, len);
85 hlen += len;
86 header[hlen++] = '\n'; // add single EOL
87 header[hlen] = '\0';
88 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 RadSimulManager::AddHeader(int ac, char *av[])
110 {
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 if (hlen) // add to header
119 header = (char *)realloc(header, hlen+len+1);
120 else
121 header = (char *)malloc(len+1);
122
123 for (n = 0; n < ac; n++) {
124 int c = check_special(av[n]);
125 len = strlen(av[n]);
126 if (c | !len) { // need to quote argument?
127 if ((c == '"') | (c == '\n')) c = '\'';
128 else c = '"';
129 header[hlen++] = c;
130 strcpy(header+hlen, av[n]);
131 hlen += len;
132 header[hlen++] = c;
133 } else {
134 strcpy(header+hlen, av[n]);
135 hlen += len;
136 }
137 header[hlen++] = ' ';
138 }
139 header[hlen-1] = '\n'; // terminate line
140 header[hlen] = '\0';
141 return true;
142 }
143
144 // 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 // How many processors are available?
174 int
175 RadSimulManager::GetNCores()
176 {
177 return sysconf(_SC_NPROCESSORS_ONLN);
178 }
179
180 // Set number of computation threads (0 => #cores)
181 int
182 RadSimulManager::SetThreadCount(int nt)
183 {
184 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
196 return NThreads();
197 }
198
199 // Assign ray to subthread (fails if NThreads()<2)
200 bool
201 RadSimulManager::SplitRay(RAY *r)
202 {
203 if (!ray_pnprocs || ThreadsAvailable() < 1)
204 return false;
205
206 return (ray_psend(r) > 0);
207 }
208
209 // Process a ray (in subthread), optional result
210 bool
211 RadSimulManager::ProcessRay(RAY *r)
212 {
213 if (!Ready()) return false;
214
215 if (!ray_pnprocs) { // single-threaded mode?
216 samplendx++;
217 rayvalue(r);
218 return true;
219 }
220 int rv = ray_pqueue(r);
221 if (rv < 0) {
222 error(WARNING, "ray tracing process(es) died");
223 return false;
224 }
225 return (rv > 0);
226 }
227
228 // Wait for next result (or fail)
229 bool
230 RadSimulManager::WaitResult(RAY *r)
231 {
232 if (!ray_pnprocs)
233 return false;
234
235 return (ray_presult(r, 0) > 0);
236 }
237
238 // Close octree, free data, return status
239 int
240 RadSimulManager::Cleanup(bool everything)
241 {
242 if (ray_pnprocs < 0)
243 return 0; // skip in child process
244 NewHeader();
245 if (!ray_pnprocs)
246 ray_done(everything);
247 else
248 ray_pdone(everything);
249 return 0;
250 }
251
252 // How many threads are currently unoccupied?
253 int
254 RadSimulManager::ThreadsAvailable() const
255 {
256 if (!ray_pnprocs) return 1;
257
258 return ray_pnidle;
259 }
260
261 // Global pointer to simulation manager for trace call-back (only one)
262 static const RtraceSimulManager * ourRTsimMan = NULL;
263
264 // Call-back for trace output
265 void
266 RtraceSimulManager::RTracer(RAY *r)
267 {
268 (*ourRTsimMan->traceCall)(r, ourRTsimMan->tcData);
269 }
270
271 // Call-back for FIFO output
272 int
273 RtraceSimulManager::Rfifout(RAY *r)
274 {
275 return (*ourRTsimMan->cookedCall)(r, ourRTsimMan->ccData);
276 }
277
278 // 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 int misMatch = (rtFlags ^ curFlags) & RTmask;
290 // updates based on toggled flags
291 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 int sn = nsources;
299 if (rtFlags & RTtraceSources) {
300 srcFollowed.NewBitMap(nsources);
301 while (sn--) {
302 if (source[sn].sflags & SFOLLOW)
303 continue;
304 source[sn].sflags |= SFOLLOW;
305 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 if (nt > 1) SetThreadCount(nt);
314 }
315 if (misMatch & RTdoFIFO && FlushQueue() < 0)
316 return false;
317 curFlags = rtFlags;
318 // update callbacks
319 if (traceCall)
320 trace = RTracer;
321 else if (trace == RTracer)
322 trace = NULL;
323 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 ourRTsimMan = NULL;
329 } else if (ourRTsimMan != this) {
330 if (ourRTsimMan)
331 error(WARNING, "Competing top-level simulation managers?");
332 ourRTsimMan = this;
333 }
334 return true;
335 }
336
337 extern "C" int m_normal(OBJREC *m, RAY *r);
338
339 // compute irradiance rather than radiance
340 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 // compute first ray intersection only
357 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 error(INTERNAL, "EnqueueBundle() called in castonly mode without cookedCall");
381
382 if (!UpdateMode()) // update rendering mode if requested
383 return -1;
384
385 if (rID0 && curFlags&RTdoFIFO)
386 error(INTERNAL, "Ray number assignment unsupported with FIFO");
387
388 while (n-- > 0) { // queue each ray
389 VCOPY(res.rorg, orig_direc[0]);
390 VCOPY(res.rdir, orig_direc[1]);
391 res.rmax = .0;
392 orig_direc += 2;
393 rayorigin(&res, PRIMARY, NULL, NULL);
394 res.rno = rID0 ? (lastRayID = rID0++) : ++lastRayID;
395 if (curFlags & RTimmIrrad)
396 res.revf = rayirrad;
397 else if (castonly)
398 res.revf = raycast;
399 double d = normalize(res.rdir);
400 bool sendRes = (cookedCall != NULL);
401 if (d > .0) { // direction vector is valid?
402 if (curFlags & RTlimDist)
403 res.rmax = d;
404 if (((curFlags&RTdoFIFO) != 0) & (ray_pnprocs > 0)) {
405 if (ray_fifo_in(&res) < 0)
406 return -1;
407 sendRes = false;
408 } else
409 sendRes &= ProcessRay(&res);
410 } else if (ThreadsAvailable() < NThreads() &&
411 FlushQueue() < 0)
412 return -1;
413 // may be dummy ray
414 if (sendRes && (*cookedCall)(&res, ccData) < 0)
415 return -1;
416 nqueued++;
417 }
418 return nqueued;
419 }
420
421 // Finish pending rays and complete callbacks
422 int
423 RtraceSimulManager::FlushQueue()
424 {
425 if (curFlags & RTdoFIFO) {
426 if (ray_pnprocs)
427 return ray_fifo_flush();
428 return 0;
429 }
430 int nsent = 0;
431 RAY res;
432
433 while (WaitResult(&res)) {
434 if (!cookedCall) continue;
435 int rv = (*cookedCall)(&res, ccData);
436 if (rv < 0) return -1;
437 nsent += rv;
438 }
439 return nsent;
440 }