| 1 | /* RCSid $Id: RcontribSimulManager.h,v 2.13 2025/10/23 16:33:39 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 | /// Provided data share function | 
| 162 | extern RcreateDataShareF        fileDataShare, mapDataShare; | 
| 163 |  | 
| 164 | #ifndef defDataShare | 
| 165 | #define defDataShare            mapDataShare    // default data share creator | 
| 166 | #endif | 
| 167 |  | 
| 168 | /// Modifiable ray-tracing flags for rcontrib | 
| 169 | #define RCcontrib               (RTmask+1)      // compute contributions? (r.t. coefficients) | 
| 170 | #define RCmask                  (RTlimDist|RTimmIrrad|RCcontrib) | 
| 171 |  | 
| 172 | /// rcontrib-like simulation manager (at most one such object) | 
| 173 | class RcontribSimulManager : protected RtraceSimulManager { | 
| 174 | protected: | 
| 175 | static RayReportCall    RctCall;        // our callback for traced rays | 
| 176 | ABitMap                 rowsDone;       // bit mask of completed rows | 
| 177 | mutable uint32          nrDone;         // current contiguous #rows done | 
| 178 | uint32                  rInPos;         // which row (record) is next on input? | 
| 179 | uby8                    nChan;          // NCSAMP setting for this calculation | 
| 180 | char                    dtyp;           // data type ('f', 'd', or 'c') | 
| 181 | uint16                  dsiz;           // N-component element size in bytes | 
| 182 | RcontribOutput *        outList;        // ordered list of output channels | 
| 183 | LUTAB                   modLUT;         // modifier lookup table | 
| 184 | SUBPROC *               kid;            // array of child processes | 
| 185 | int32 *                 kidRow;         // row assigned to each child | 
| 186 | int                     nkids;          // child process count (-1 in child) | 
| 187 | bool                    UpdateRowsDone(int r); | 
| 188 | int                     GetChild(bool forceWait = false); | 
| 189 | bool                    StartKids(int n2go); | 
| 190 | int                     StopKids(int n2end = 0); | 
| 191 | void                    RunChild(); | 
| 192 | public: | 
| 193 | RCOutputOp              outOp;          // output operation | 
| 194 | RcreateDataShareF *     cdsF;           // data share creator | 
| 195 | int                     xres, yres;     // output (picture) size | 
| 196 | uint32                  accum;          // # rays to accumulate per record | 
| 197 | RcontribSimulManager(const char *octn = NULL) | 
| 198 | : RtraceSimulManager(NULL, NULL, octn) { | 
| 199 | nrDone = rInPos = 0; | 
| 200 | nChan = 0; | 
| 201 | dtyp = 'f'; | 
| 202 | dsiz = 0; | 
| 203 | outList = NULL; | 
| 204 | memset(&modLUT, 0, sizeof(modLUT)); | 
| 205 | modLUT.hashf = lu_shash; | 
| 206 | modLUT.keycmp = strcmp; | 
| 207 | modLUT.freek = efree; | 
| 208 | modLUT.freed = FreeRcMod; | 
| 209 | kid = NULL; kidRow = NULL; nkids = 0; | 
| 210 | outOp = RCOnew; | 
| 211 | cdsF = &defDataShare; | 
| 212 | xres = yres = 0; | 
| 213 | accum = 1; | 
| 214 | if (octname) { | 
| 215 | SetTraceCall(&RctCall, this); | 
| 216 | rtFlags |= RTtraceSources; | 
| 217 | UpdateMode(); | 
| 218 | } | 
| 219 | } | 
| 220 | ~RcontribSimulManager() { | 
| 221 | if (nkids >= 0) ClearModifiers(); | 
| 222 | } | 
| 223 | /// Check modifiable ray-tracing computation flag(s) | 
| 224 | bool                    HasFlag(int fl) const { | 
| 225 | return ((rtFlags & RCmask & fl) != 0); | 
| 226 | } | 
| 227 | /// Set/reset modifiable ray-tracing computation flag(s) | 
| 228 | bool                    SetFlag(int fl, bool val = true) { | 
| 229 | if (!(fl &= RCmask)) return false; | 
| 230 | if (val) rtFlags |= fl; | 
| 231 | else rtFlags &= ~fl; | 
| 232 | return true; | 
| 233 | } | 
| 234 | /// Load octree and prepare renderer | 
| 235 | bool                    LoadOctree(const char *octn) { | 
| 236 | if (octname) Cleanup(false); | 
| 237 | if (!RtraceSimulManager::LoadOctree(octn)) | 
| 238 | return false; | 
| 239 | SetTraceCall(&RctCall, this); | 
| 240 | rtFlags |= RTtraceSources; | 
| 241 | return UpdateMode(); | 
| 242 | } | 
| 243 | /// Prepare header from previous input (or clear) | 
| 244 | bool                    NewHeader(const char *inspec=NULL) { | 
| 245 | return RtraceSimulManager::NewHeader(inspec); | 
| 246 | } | 
| 247 | /// Add a string to header (adds newline if none) | 
| 248 | bool                    AddHeader(const char *str) { | 
| 249 | return RtraceSimulManager::AddHeader(str); | 
| 250 | } | 
| 251 | /// Append program line to header | 
| 252 | bool                    AddHeader(int ac, char *av[]) { | 
| 253 | return RtraceSimulManager::AddHeader(ac, av); | 
| 254 | } | 
| 255 | /// Get current header length in bytes | 
| 256 | int                     GetHeadLen() const { | 
| 257 | return RtraceSimulManager::GetHeadLen(); | 
| 258 | } | 
| 259 | /// Get header lines if any | 
| 260 | const char *            GetHeadStr() const { | 
| 261 | return RtraceSimulManager::GetHeadStr(); | 
| 262 | } | 
| 263 | /// Look for specific header keyword, return value | 
| 264 | const char *            GetHeadStr(const char *key, bool inOK = false) const { | 
| 265 | return RtraceSimulManager::GetHeadStr(key, inOK); | 
| 266 | } | 
| 267 | /// Set output format ('f', 'd', or 'c'), call before mods | 
| 268 | bool                    SetDataFormat(int ty); | 
| 269 | /// Get current format (and element size in bytes) | 
| 270 | int                     GetFormat(int *siz = NULL) const { | 
| 271 | if (siz) *siz = dsiz; | 
| 272 | return dtyp; | 
| 273 | } | 
| 274 | /// Add a modifier and arguments, create output(s) | 
| 275 | bool                    AddModifier(const char *modn, const char *outspec, | 
| 276 | const char *prms = NULL, | 
| 277 | const char *binval = NULL, int bincnt = 1); | 
| 278 | /// Add a file of modifiers with associated arguments | 
| 279 | bool                    AddModFile(const char *modfn, const char *outspec, | 
| 280 | const char *prms = NULL, | 
| 281 | const char *binval = NULL, int bincnt = 1); | 
| 282 | /// Get named rcontrib output (or list) | 
| 283 | const RcontribOutput *  GetOutput(const char *nm = NULL) const { | 
| 284 | if (!nm) return outList; | 
| 285 | const RcontribOutput *  op = outList; | 
| 286 | while (op && strcmp(op->GetName(), nm)) | 
| 287 | op = op->next; | 
| 288 | return op; | 
| 289 | } | 
| 290 | /// Open output channels and return # completed rows | 
| 291 | int                     PrepOutput(); | 
| 292 | /// Are we ready to compute some records? | 
| 293 | bool                    Ready() const { | 
| 294 | return (rowsDone.Length() > 0) & (accum > 0); | 
| 295 | } | 
| 296 | /// Set number of computation threads (0 => #cores) | 
| 297 | int                     SetThreadCount(int nt = 0); | 
| 298 | /// Check thread count (1 means no multi-processing) | 
| 299 | int                     NThreads() const { | 
| 300 | return nkids + !nkids; | 
| 301 | } | 
| 302 | /// What is maximum row? | 
| 303 | int                     GetRowMax() const { | 
| 304 | if (!outList) return yres * (xres + !xres); | 
| 305 | return outList->nRows; | 
| 306 | } | 
| 307 | /// Get current row count (# rows sent for computation) | 
| 308 | int                     GetRowCount() const { | 
| 309 | return rInPos; | 
| 310 | } | 
| 311 | /// Get # rows completed | 
| 312 | int                     GetRowFinished() const { | 
| 313 | if (!nkids) return nrDone = rInPos; | 
| 314 | nrDone = rowsDone.Find(nrDone, false); | 
| 315 | if (nrDone == ABMend) | 
| 316 | nrDone = rowsDone.Length(); | 
| 317 | return nrDone; | 
| 318 | } | 
| 319 | /// Add a ray/bundle to compute next record (n=accum) | 
| 320 | int                     ComputeRecord(const FVECT orig_direc[]); | 
| 321 | /// Finish pending rays if multi-processing | 
| 322 | bool                    FlushQueue() { | 
| 323 | if (nkids <= 0) return true; | 
| 324 | while (GetChild(true) >= 0) | 
| 325 | ; | 
| 326 | return true; | 
| 327 | } | 
| 328 | /// Rewind calculation (previous results unchanged) | 
| 329 | bool                    ResetRow(int r); | 
| 330 | /// Clear the modifiers and close all outputs | 
| 331 | void                    ClearModifiers() { | 
| 332 | if (rowsDone.Length()) { | 
| 333 | SetThreadCount(1); | 
| 334 | rowsDone.NewBitMap(0); | 
| 335 | nrDone = rInPos = 0; | 
| 336 | } | 
| 337 | lu_done(&modLUT); | 
| 338 | delete outList; outList = NULL; | 
| 339 | nChan = 0; | 
| 340 | } | 
| 341 | /// Close octree, free data, return status | 
| 342 | int                     Cleanup(bool everything = false) { | 
| 343 | ClearModifiers(); | 
| 344 | cow_doneshare(); | 
| 345 | if (everything) { | 
| 346 | dtyp = 'f'; | 
| 347 | outOp = RCOnew; | 
| 348 | cdsF = &defDataShare; | 
| 349 | xres = yres = 0; | 
| 350 | accum = 1; | 
| 351 | rtFlags &= ~RCmask; | 
| 352 | } | 
| 353 | return RtraceSimulManager::Cleanup(everything); | 
| 354 | } | 
| 355 | }; | 
| 356 |  | 
| 357 | extern const char *     formstr(int f);         // string from format | 
| 358 |  | 
| 359 | #endif /* RcontribSimulManager_h */ |