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 (8 months, 4 weeks ago) by greg
Branch: MAIN
Changes since 2.11: +107 -2 lines
Log Message:
feat(rxtrace): Added header manipulation to base class

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: RtraceSimulManager.cpp,v 2.11 2024/07/08 23:46:04 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); // load header if we can
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 *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 // How many processors are available?
138 int
139 RadSimulManager::GetNCores()
140 {
141 return sysconf(_SC_NPROCESSORS_ONLN);
142 }
143
144 // Set number of computation threads (0 => #cores)
145 int
146 RadSimulManager::SetThreadCount(int nt)
147 {
148 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
160 return NThreads();
161 }
162
163 // Assign ray to subthread (fails if NThreads()<2)
164 bool
165 RadSimulManager::SplitRay(RAY *r)
166 {
167 if (!ray_pnprocs || ThreadsAvailable() < 1)
168 return false;
169
170 return (ray_psend(r) > 0);
171 }
172
173 // Process a ray (in subthread), optional result
174 bool
175 RadSimulManager::ProcessRay(RAY *r)
176 {
177 if (!Ready()) return false;
178
179 if (!ray_pnprocs) { // single-threaded mode?
180 samplendx++;
181 rayvalue(r);
182 return true;
183 }
184 int rv = ray_pqueue(r);
185 if (rv < 0) {
186 error(WARNING, "ray tracing process(es) died");
187 return false;
188 }
189 return (rv > 0);
190 }
191
192 // Wait for next result (or fail)
193 bool
194 RadSimulManager::WaitResult(RAY *r)
195 {
196 if (!ray_pnprocs)
197 return false;
198
199 return (ray_presult(r, 0) > 0);
200 }
201
202 // Close octree, free data, return status
203 int
204 RadSimulManager::Cleanup(bool everything)
205 {
206 if (!ray_pnprocs)
207 ray_done(everything);
208 else
209 ray_pdone(everything);
210 return 0;
211 }
212
213 // How many threads are currently unoccupied?
214 int
215 RadSimulManager::ThreadsAvailable() const
216 {
217 if (!ray_pnprocs) return 1;
218
219 return ray_pnidle;
220 }
221
222 // Global pointer to simulation manager for trace call-back (only one)
223 static const RtraceSimulManager * ourRTsimMan = NULL;
224
225 // Call-back for trace output
226 void
227 RtraceSimulManager::RTracer(RAY *r)
228 {
229 (*ourRTsimMan->traceCall)(r, ourRTsimMan->tcData);
230 }
231
232 // Call-back for FIFO output
233 int
234 RtraceSimulManager::Rfifout(RAY *r)
235 {
236 return (*ourRTsimMan->cookedCall)(r, ourRTsimMan->ccData);
237 }
238
239 // 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 if (misMatch & RTdoFIFO && FlushQueue() < 0)
261 return false;
262 curFlags = rtFlags;
263 // update callbacks
264 if (traceCall)
265 trace = RTracer;
266 else if (trace == RTracer)
267 trace = NULL;
268 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 ourRTsimMan = NULL;
274 } else if (ourRTsimMan != this) {
275 if (ourRTsimMan)
276 error(WARNING, "Competing top-level simulation managers?");
277 ourRTsimMan = this;
278 }
279 return true;
280 }
281
282 extern "C" int m_normal(OBJREC *m, RAY *r);
283
284 // compute irradiance rather than radiance
285 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 // compute first ray intersection only
302 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 error(INTERNAL, "EnqueueBundle() called in castonly mode without cookedCall");
326
327 if (!UpdateMode()) // update rendering mode if requested
328 return -1;
329
330 if (rID0 && curFlags&RTdoFIFO)
331 error(INTERNAL, "Ray number assignment unsupported with FIFO");
332
333 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 res.rno = rID0 ? (lastRayID = rID0++) : ++lastRayID;
339 if (curFlags & RTimmIrrad)
340 res.revf = rayirrad;
341 else if (castonly)
342 res.revf = raycast;
343 double d = normalize(res.rdir);
344 bool sendRes = (cookedCall != NULL);
345 if (d > .0) { // direction vector is valid?
346 if (curFlags & RTlimDist)
347 res.rmax = d;
348 if (((curFlags&RTdoFIFO) != 0) & (ray_pnprocs > 0)) {
349 if (ray_fifo_in(&res) < 0)
350 return -1;
351 sendRes = false;
352 } else
353 sendRes &= ProcessRay(&res);
354 } else if (ThreadsAvailable() < NThreads() &&
355 FlushQueue() < 0)
356 return -1;
357 // may be dummy ray
358 if (sendRes && (*cookedCall)(&res, ccData) < 0)
359 return -1;
360 nqueued++;
361 }
362 return nqueued;
363 }
364
365 // Finish pending rays and complete callbacks
366 int
367 RtraceSimulManager::FlushQueue()
368 {
369 if (curFlags & RTdoFIFO) {
370 if (ray_pnprocs)
371 return ray_fifo_flush();
372 return 0;
373 }
374 int nsent = 0;
375 RAY res;
376
377 while (WaitResult(&res)) {
378 if (!cookedCall) continue;
379 int rv = (*cookedCall)(&res, ccData);
380 if (rv < 0) return -1;
381 nsent += rv;
382 }
383 return nsent;
384 }