1 |
/* RCSid $Id: RcontribSimulManager.h,v 2.11 2025/08/18 17:55:03 greg Exp $ */ |
2 |
/* |
3 |
* RcontribSimulManager.h |
4 |
* |
5 |
* Rcontrib simulation manager class declaration |
6 |
* |
7 |
* Created by Greg Ward on 10/11/2024. |
8 |
*/ |
9 |
|
10 |
#ifndef RcontribSimulManager_h |
11 |
#define RcontribSimulManager_h |
12 |
|
13 |
#include "RtraceSimulManager.h" |
14 |
#include "RdataShare.h" |
15 |
#include "lookup.h" |
16 |
#include "rtprocess.h" |
17 |
|
18 |
/* |
19 |
* As with other single-object classes, many global variable affect |
20 |
* behavior. Besides rendering parameters, there are spectral parameters |
21 |
* and output dimensions taken from the environment. |
22 |
* |
23 |
* NOTE: "record" and "row" are used interchangeably throughout. |
24 |
*/ |
25 |
|
26 |
extern char RCCONTEXT[]; // global rcontrib context |
27 |
|
28 |
class RcontribSimulManager; // need forward decl |
29 |
|
30 |
/// Shared data object for record output (includes header; may be write-only) |
31 |
class RcontribOutput { |
32 |
RcontribOutput * next; // next in sorted list |
33 |
char * ofname; // output file name |
34 |
uint32 rowCountPos; // row count position in header |
35 |
void * rowp; // current row memory |
36 |
bool NewHeader(const RcontribSimulManager *rcp); |
37 |
int CheckHeader(const RcontribSimulManager *rcp); |
38 |
public: |
39 |
RdataShare * rData; // data sharing object |
40 |
size_t rowBytes; // byte count per row |
41 |
const char * omod; // single modifier (or NULL) |
42 |
int32 obin; // single output bin (or -1) |
43 |
uint32 begData; // start of data (type-aligned) |
44 |
int32 curRow; // current output row |
45 |
uint32 nRows; // total number of rows |
46 |
RcontribOutput(const char *fnm = NULL) { |
47 |
next = NULL; |
48 |
omod = NULL; |
49 |
obin = -1; |
50 |
rData = NULL; |
51 |
ofname = savqstr(fnm); |
52 |
rowBytes = 0; |
53 |
nRows = 0; |
54 |
rowp = NULL; curRow = -1; |
55 |
} |
56 |
~RcontribOutput() { |
57 |
DoneRow(); |
58 |
delete rData; |
59 |
delete next; |
60 |
freeqstr(ofname); |
61 |
} |
62 |
/// Return output channel name |
63 |
const char * GetName() const { |
64 |
if (rData) return rData->GetName(); |
65 |
return ofname; |
66 |
} |
67 |
/// Update output row count |
68 |
bool SetRowsDone(int r) { |
69 |
if (!rData | (0 > r) | (r > nRows)) return false; |
70 |
char * rbuf = (char *)rData->GetMemory(rowCountPos, 17, 0); |
71 |
sprintf(rbuf, "%-16d", r); |
72 |
rbuf[16] = '\n'; // replaces nul byte |
73 |
return rData->ReleaseMemory(rbuf, RDSwrite); |
74 |
} |
75 |
/// Get buffer for indicated row (contents may be random) |
76 |
void * GetRow(int r) { |
77 |
if (!rData | (r < 0)) return NULL; |
78 |
if (r != curRow) { |
79 |
DoneRow(); |
80 |
if (r < nRows) |
81 |
rowp = rData->GetMemory(begData + r*rowBytes, |
82 |
rowBytes, 0); |
83 |
if (rowp) curRow = r; |
84 |
} |
85 |
return rowp; |
86 |
} |
87 |
/// Current row with byte offset |
88 |
void * InsertionP(int coffset) const { |
89 |
if (!rowp | (coffset < 0) | (coffset >= rowBytes)) |
90 |
return NULL; |
91 |
return (char *)rowp + coffset; |
92 |
} |
93 |
/// Release current row, writing contents |
94 |
void DoneRow() { |
95 |
if (rowp) rData->ReleaseMemory(rowp, RDSwrite); |
96 |
rowp = NULL; curRow = -1; |
97 |
} |
98 |
/// Get next in list |
99 |
const RcontribOutput * Next() const { |
100 |
return next; |
101 |
} |
102 |
RcontribOutput * Next() { |
103 |
return next; |
104 |
} |
105 |
/// RcontribSimulManager gets full access |
106 |
friend class RcontribSimulManager; |
107 |
}; |
108 |
|
109 |
typedef double DCOLORV; // color accumulator type |
110 |
|
111 |
/// Free an RcontribMod |
112 |
extern lut_free_t FreeRcMod; |
113 |
|
114 |
/* |
115 |
* General RcontribSimulManager class operation: |
116 |
* |
117 |
* 1) Call LoadOctree(), then alter the header as desired |
118 |
* 2) Set number of spectral samples (NCSAMP) and call SetDataFormat() |
119 |
* 3) Set xres and yres to desired dimensions (xres>0 for picture output) |
120 |
* 4) Call AddModifier() and AddModFile() to indicate tracked modifiers |
121 |
* 5) Set outOp and cdsF according to desired output/recovery |
122 |
* 6) Set desired computation flags via SetFlag() |
123 |
* 7) Call PrepOutput() to open output channels |
124 |
* 8) Call SetThreadCount() to fork children if desired |
125 |
* 9) Set accum to the number of ray samples per record |
126 |
* 10) Call ComputeRecord() with accum ray samples |
127 |
* 11) Continue until GetRowMax() records have been sent |
128 |
* 12) Call Cleanup() |
129 |
* |
130 |
* The order of some of these calls may be changed. Technically, the octree |
131 |
* may be loaded anytime before PrepOutput() is called. Also, SetThreadCount() |
132 |
* may be called anytime *after* PrepOutput(), and may be interleaved with |
133 |
* calls to ComputeRecord(). The accum setting may be changed at any time. |
134 |
* Finally, it is possible to restart the output using ResetRow(), and |
135 |
* a zero argument will rewind to the beginning, whence all records |
136 |
* may be recalculated. The previous output rows are not zeroed or deleted, |
137 |
* but are overwritten as the calculation proceeds from the new starting point. |
138 |
* However, the output file(s) will indicate in the NROWS= line in the header |
139 |
* that only the newly calculated rows are present. If you wish to start over |
140 |
* with a different set of modifiers or outputs, call ClearModifiers() instead, |
141 |
* which keeps the current octree in memory. This call also returns to single |
142 |
* process mode if any children were running. |
143 |
* |
144 |
* It is not possible to write to standard output, but the output |
145 |
* model is quite flexible thanks to the RdataShare polymorphic class. |
146 |
* The current default output class creates a shared, memory-mapped file, |
147 |
* which is the most efficient object on most systems. |
148 |
* |
149 |
* ASCII output is not supported, so full data recovery is. |
150 |
*/ |
151 |
|
152 |
/// Output channel opening options: new/exclusive, overwrite if exists, or recover data |
153 |
enum RCOutputOp {RCOnew=0, RCOforce, RCOrecover}; |
154 |
|
155 |
/// Converts above to RdataShare open flags (may be adjusted by calling program) |
156 |
extern int RSDOflags[]; |
157 |
|
158 |
/// Call-back function type to create named data channel (freed using "delete" operator) |
159 |
typedef RdataShare * RcreateDataShareF(const char *name, RCOutputOp op, size_t siz); |
160 |
|
161 |
/// Our default data share function |
162 |
extern RcreateDataShareF defDataShare; |
163 |
|
164 |
/// Modifiable ray-tracing flags for rcontrib |
165 |
#define RCcontrib (RTmask+1) // compute contributions? (r.t. coefficients) |
166 |
#define RCmask (RTlimDist|RTimmIrrad|RCcontrib) |
167 |
|
168 |
/// rcontrib-like simulation manager (at most one such object) |
169 |
class RcontribSimulManager : protected RtraceSimulManager { |
170 |
protected: |
171 |
static RayReportCall RctCall; // our callback for traced rays |
172 |
ABitMap rowsDone; // bit mask of completed rows |
173 |
uint32 rInPos; // which row (record) is next on input? |
174 |
uby8 nChan; // NCSAMP setting for this calculation |
175 |
char dtyp; // data type ('f', 'd', or 'c') |
176 |
uint16 dsiz; // N-component element size in bytes |
177 |
RcontribOutput * outList; // ordered list of output channels |
178 |
LUTAB modLUT; // modifier lookup table |
179 |
SUBPROC * kid; // array of child processes |
180 |
int32 * kidRow; // row assigned to each child |
181 |
int nkids; // child process count (-1 in child) |
182 |
bool UpdateRowsDone(int r); |
183 |
int GetChild(bool forceWait = false); |
184 |
bool StartKids(int n2go); |
185 |
int StopKids(int n2end = 0); |
186 |
void RunChild(); |
187 |
public: |
188 |
RCOutputOp outOp; // output operation |
189 |
RcreateDataShareF * cdsF; // data share creator |
190 |
int xres, yres; // output (picture) size |
191 |
uint32 accum; // # rays to accumulate per record |
192 |
RcontribSimulManager(const char *octn = NULL) |
193 |
: RtraceSimulManager(NULL, NULL, octn) { |
194 |
rInPos = 0; |
195 |
nChan = 0; |
196 |
dtyp = 'f'; |
197 |
dsiz = 0; |
198 |
outList = NULL; |
199 |
memset(&modLUT, 0, sizeof(modLUT)); |
200 |
modLUT.hashf = lu_shash; |
201 |
modLUT.keycmp = strcmp; |
202 |
modLUT.freek = efree; |
203 |
modLUT.freed = FreeRcMod; |
204 |
kid = NULL; kidRow = NULL; nkids = 0; |
205 |
outOp = RCOnew; |
206 |
cdsF = &defDataShare; |
207 |
xres = yres = 0; |
208 |
accum = 1; |
209 |
if (octname) { |
210 |
SetTraceCall(&RctCall, this); |
211 |
rtFlags |= RTtraceSources; |
212 |
UpdateMode(); |
213 |
} |
214 |
} |
215 |
~RcontribSimulManager() { |
216 |
if (nkids >= 0) ClearModifiers(); |
217 |
} |
218 |
/// Check modifiable ray-tracing computation flag(s) |
219 |
bool HasFlag(int fl) const { |
220 |
return ((rtFlags & RCmask & fl) != 0); |
221 |
} |
222 |
/// Set/reset modifiable ray-tracing computation flag(s) |
223 |
bool SetFlag(int fl, bool val = true) { |
224 |
if (!(fl &= RCmask)) return false; |
225 |
if (val) rtFlags |= fl; |
226 |
else rtFlags &= ~fl; |
227 |
return true; |
228 |
} |
229 |
/// Load octree and prepare renderer |
230 |
bool LoadOctree(const char *octn) { |
231 |
if (octname) Cleanup(false); |
232 |
if (!RtraceSimulManager::LoadOctree(octn)) |
233 |
return false; |
234 |
SetTraceCall(&RctCall, this); |
235 |
rtFlags |= RTtraceSources; |
236 |
return UpdateMode(); |
237 |
} |
238 |
/// Prepare header from previous input (or clear) |
239 |
bool NewHeader(const char *inspec=NULL) { |
240 |
return RtraceSimulManager::NewHeader(inspec); |
241 |
} |
242 |
/// Add a string to header (adds newline if none) |
243 |
bool AddHeader(const char *str) { |
244 |
return RtraceSimulManager::AddHeader(str); |
245 |
} |
246 |
/// Append program line to header |
247 |
bool AddHeader(int ac, char *av[]) { |
248 |
return RtraceSimulManager::AddHeader(ac, av); |
249 |
} |
250 |
/// Get current header length in bytes |
251 |
int GetHeadLen() const { |
252 |
return RtraceSimulManager::GetHeadLen(); |
253 |
} |
254 |
/// Get header lines if any |
255 |
const char * GetHeadStr() const { |
256 |
return RtraceSimulManager::GetHeadStr(); |
257 |
} |
258 |
/// Look for specific header keyword, return value |
259 |
const char * GetHeadStr(const char *key, bool inOK = false) const { |
260 |
return RtraceSimulManager::GetHeadStr(key, inOK); |
261 |
} |
262 |
/// Set output format ('f', 'd', or 'c'), call before mods |
263 |
bool SetDataFormat(int ty); |
264 |
/// Get current format (and element size in bytes) |
265 |
int GetFormat(int *siz = NULL) const { |
266 |
if (siz) *siz = dsiz; |
267 |
return dtyp; |
268 |
} |
269 |
/// Add a modifier and arguments, create output(s) |
270 |
bool AddModifier(const char *modn, const char *outspec, |
271 |
const char *prms = NULL, |
272 |
const char *binval = NULL, int bincnt = 1); |
273 |
/// Add a file of modifiers with associated arguments |
274 |
bool AddModFile(const char *modfn, const char *outspec, |
275 |
const char *prms = NULL, |
276 |
const char *binval = NULL, int bincnt = 1); |
277 |
/// Get named rcontrib output (or list) |
278 |
const RcontribOutput * GetOutput(const char *nm = NULL) const { |
279 |
if (!nm) return outList; |
280 |
const RcontribOutput * op = outList; |
281 |
while (op && strcmp(op->GetName(), nm)) |
282 |
op = op->next; |
283 |
return op; |
284 |
} |
285 |
/// Open output channels and return # completed rows |
286 |
int PrepOutput(); |
287 |
/// Are we ready to compute some records? |
288 |
bool Ready() const { |
289 |
return (rowsDone.Length() > 0) & (accum > 0); |
290 |
} |
291 |
/// Set number of computation threads (0 => #cores) |
292 |
int SetThreadCount(int nt = 0); |
293 |
/// Check thread count (1 means no multi-processing) |
294 |
int NThreads() const { |
295 |
return nkids + !nkids; |
296 |
} |
297 |
/// What is maximum row? |
298 |
int GetRowMax() const { |
299 |
if (!outList) return yres * (xres + !xres); |
300 |
return outList->nRows; |
301 |
} |
302 |
/// Get current row count (# rows sent for computation) |
303 |
int GetRowCount() const { |
304 |
return rInPos; |
305 |
} |
306 |
/// Get # rows completed |
307 |
int GetRowFinished() const { |
308 |
if (!nkids) return rInPos; |
309 |
uint32 nDone = rowsDone.Find(0, false); |
310 |
if (nDone == ABMend) |
311 |
return rowsDone.Length(); |
312 |
return nDone; |
313 |
} |
314 |
/// Add a ray/bundle to compute next record (n=accum) |
315 |
int ComputeRecord(const FVECT orig_direc[]); |
316 |
/// Finish pending rays if multi-processing |
317 |
bool FlushQueue() { |
318 |
if (nkids <= 0) return true; |
319 |
while (GetChild(true) >= 0) |
320 |
; |
321 |
return true; |
322 |
} |
323 |
/// Rewind calculation (previous results unchanged) |
324 |
bool ResetRow(int r); |
325 |
/// Clear the modifiers and close all outputs |
326 |
void ClearModifiers() { |
327 |
if (rowsDone.Length()) { |
328 |
SetThreadCount(1); |
329 |
rowsDone.NewBitMap(0); |
330 |
rInPos = 0; |
331 |
} |
332 |
lu_done(&modLUT); |
333 |
delete outList; outList = NULL; |
334 |
nChan = 0; |
335 |
} |
336 |
/// Close octree, free data, return status |
337 |
int Cleanup(bool everything = false) { |
338 |
ClearModifiers(); |
339 |
cow_doneshare(); |
340 |
if (everything) { |
341 |
dtyp = 'f'; |
342 |
outOp = RCOnew; |
343 |
cdsF = &defDataShare; |
344 |
xres = yres = 0; |
345 |
accum = 1; |
346 |
rtFlags &= ~RCmask; |
347 |
} |
348 |
return RtraceSimulManager::Cleanup(everything); |
349 |
} |
350 |
}; |
351 |
|
352 |
extern const char * formstr(int f); // string from format |
353 |
|
354 |
#endif /* RcontribSimulManager_h */ |