Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-04-09 07:49:03

0001 /**
0002 Six.cc
0003 ========
0004 
0005 Enables the new workflow CSGFoundry geometry developed for use with the all new OptiX 7 API 
0006 to be used with the old OptiX API versions 5 and 6. 
0007 
0008 BUT it seems that although it has been seen to work with simple geometries 
0009 this approach is on shaky ground as pointer arithmetic is prohibited in OptiX < 7 code. 
0010 This is surprising as opticks/CSG CUDA-centric implementation makes lots of 
0011 use of pointer arithmetic and has proved to work with OptiX 5 on macOS.  
0012 
0013 Due to this uncertainty should not put great efforts into this Six backward compatibility code. 
0014 However some effort is warranted as it enables convenient local laptop development. 
0015 OptiX 7 not being usable on my ancient macOS laptop due to CUDA version restriction.  
0016 
0017 About the shaky ground
0018 --------------------------
0019 
0020 https://forums.developer.nvidia.com/t/sending-float-array-from-host-to-device/65391/2
0021 
0022 droettger::
0023 
0024     RT_PROGRAM void func()
0025     {
0026       // Access floats inside the device copy with index in range [0, bufferOfFloats.size() - 1].
0027       // Always use operator[] to access buffer elements!
0028       // There is no pointer arithmetic allowed on device side buffers in OptiX.
0029       float f = bufferOfFloats[index];
0030     }
0031 
0032 Why does it work ?
0033 -------------------
0034 
0035 Maybe because pointer arithmetic is all being done on constant input buffers ?
0036  
0037 
0038 **/
0039 
0040 #include <iostream>
0041 
0042 #include "SLOG.hh"
0043 #include "SSys.hh"
0044 #include "SStr.hh"
0045 #include "SPath.hh"
0046 #include "NP.hh"
0047 #include "scuda.h"
0048 #include "squad.h"
0049 #include "sqat4.h"
0050 #include "OpticksCSG.h"
0051 
0052 //#include "Opticks.hh"
0053 // TODO: replace use of Opticks here (solid selection, emm)
0054 
0055 #include "Params.h"
0056 
0057 #include "CSGFoundry.h"
0058 #include "CSGSolid.h"
0059 #include "CSGPrim.h"
0060 #include "CSGNode.h"
0061 
0062 #include "Frame.h"
0063 #include "Six.h"
0064 
0065 #include "SGeoConfig.hh"
0066 
0067     
0068 Six::Six(const char* ptx_path_, const char* geo_ptx_path_, Params* params_)
0069     :
0070     emm(SGeoConfig::EnabledMergedMesh()),
0071     context(optix::Context::create()),
0072     material(context->createMaterial()),
0073 
0074     d_pixel(nullptr),
0075     d_isect(nullptr),
0076     d_photon(nullptr),
0077  
0078     params(params_),
0079     ptx_path(strdup(ptx_path_)),
0080     geo_ptx_path(strdup(geo_ptx_path_)),
0081     entry_point_index(0u),
0082     optix_device_ordinal(0u),
0083     foundry(nullptr),
0084     pindex(SSys::getenvint("PINDEX", 0))
0085 {
0086     initContext();
0087     initPipeline(); 
0088     initFrame(); 
0089     updateContext(); 
0090 }
0091 
0092 void Six::initContext()
0093 {
0094     LOG(info) << " pindex " << pindex ; 
0095     context->setRayTypeCount(1);
0096     context->setPrintEnabled(true);
0097     context->setPrintBufferSize(1024);
0098     context->setPrintLaunchIndex(pindex); 
0099     context->setEntryPointCount(1);
0100 }
0101 
0102 void Six::initPipeline()
0103 {
0104     LOG(info); 
0105     context->setRayGenerationProgram( entry_point_index, context->createProgramFromPTXFile( ptx_path , "raygen" ));
0106     context->setMissProgram(          entry_point_index, context->createProgramFromPTXFile( ptx_path , "miss" ));
0107     material->setClosestHitProgram(   entry_point_index, context->createProgramFromPTXFile( ptx_path , "closest_hit" ));
0108 }
0109 
0110 void Six::initFrame()
0111 {
0112     pixel_buffer = context->createBuffer( RT_BUFFER_OUTPUT, RT_FORMAT_UNSIGNED_BYTE4, params->width, params->height);
0113     context["pixel_buffer"]->set( pixel_buffer );  // formerly pixels_buffer
0114     d_pixel = (uchar4*)pixel_buffer->getDevicePointer(optix_device_ordinal); 
0115 
0116     isect_buffer = context->createBuffer( RT_BUFFER_OUTPUT, RT_FORMAT_FLOAT4, params->width, params->height);
0117     context["isect_buffer"]->set( isect_buffer );   // formerly posi_buffer
0118     d_isect = (float4*)isect_buffer->getDevicePointer(optix_device_ordinal); 
0119 
0120     photon_buffer = context->createBuffer( RT_BUFFER_OUTPUT, RT_FORMAT_USER, params->width, params->height);
0121     photon_buffer->setElementSize( sizeof(quad4) ); 
0122     context["photon_buffer"]->set( photon_buffer );  // formerly isect_buffer
0123     d_photon = (quad4*)photon_buffer->getDevicePointer(optix_device_ordinal); 
0124 
0125     LOG(info) 
0126         << " params->width " << params->width
0127         << " params->height " << params->height
0128         << " optix_device_ordinal " << optix_device_ordinal
0129         ;
0130 }
0131 
0132 /**
0133 Six::updateContext
0134 -------------------
0135 
0136 Populates context with values from the hostside params. 
0137 
0138 **/
0139 
0140 
0141 void Six::updateContext()
0142 {
0143     LOG(info) 
0144         << " params.cameratype " << params->cameratype 
0145         << " params.tmin " << params->tmin
0146         ; 
0147 
0148     context[ "tmin"]->setFloat( params->tmin );  
0149     context[ "eye"]->setFloat( params->eye.x, params->eye.y, params->eye.z  );  
0150     context[ "U"  ]->setFloat( params->U.x, params->U.y, params->U.z  );  
0151     context[ "V"  ]->setFloat( params->V.x, params->V.y, params->V.z  );  
0152     context[ "W"  ]->setFloat( params->W.x, params->W.y, params->W.z  );  
0153     context[ "radiance_ray_type"   ]->setUint( 0u );  
0154     context[ "cameratype"   ]->setUint( params->cameratype );  
0155     context[ "raygenmode"   ]->setUint( params->raygenmode ); 
0156 
0157 
0158 }
0159 
0160 /**
0161 Six::createContextInputBuffer
0162 -------------------------------
0163 
0164 Create shim optix::Buffer from CUDA buffer pointers 
0165 
0166 The setDevicePointer fails for output buffers due to OptiX limitation::
0167 
0168     libc++abi.dylib: terminating with uncaught exception of type optix::Exception: Unknown error (Details: 
0169     Function "RTresult _rtBufferSetDevicePointer(RTbuffer, int, void *)" caught exception: 
0170     rtBufferSetPointer() not allowed for output or input/output buffers)
0171 
0172 The optix-pdf (OptiX 5.0) p82 documents this limitation::
0173 
0174     Calling rtBufferSetDevicePointer on output or input/output buffers is not allowed.
0175 
0176 Worked around this limitation by generalizing Frame to work with externally 
0177 provided CUDA device pointers obtained from the real OptiX < 7 optix::Buffer  
0178 
0179 **/
0180 
0181 template<typename T>
0182 void Six::createContextInputBuffer( T* d_ptr, unsigned num_item, const char* name )
0183 {
0184     LOG(info) << name << " " << d_ptr << ( d_ptr == nullptr ? " EMPTY " : "" ); 
0185 
0186     // NB OptiX limitation the setDevicePointer throws exception for non-INPUT buffers
0187     unsigned type = RT_BUFFER_INPUT ; 
0188     RTformat format = RT_FORMAT_USER ; 
0189     optix::Buffer buffer = context->createBuffer( type, format, num_item );
0190     buffer->setElementSize( sizeof(T) ); 
0191     if(d_ptr)
0192     {
0193         buffer->setDevicePointer(optix_device_ordinal, d_ptr ); 
0194     }
0195     context[name]->set( buffer );
0196 }
0197 
0198 template void Six::createContextInputBuffer( CSGNode*,  unsigned, const char* ) ; 
0199 template void Six::createContextInputBuffer( qat4*,     unsigned, const char* ) ; 
0200 template void Six::createContextInputBuffer( float*,    unsigned, const char* ) ; 
0201 
0202 
0203 void Six::setFoundry(const CSGFoundry* foundry_) 
0204 {
0205     foundry = foundry_ ; 
0206     createGeom(); 
0207 }
0208     
0209 void Six::createGeom()
0210 {
0211     LOG(info) << "[" ; 
0212     createContextInputBuffers(); 
0213     createGAS(); 
0214     createIAS(); 
0215     LOG(info) << "]" ; 
0216 }
0217 
0218 
0219 /**
0220 Six::createContextInputBuffers : Shim optix::Buffer facade from CUDA device pointers
0221 --------------------------------------------------------------------------------------
0222 
0223 NB the CSGPrim prim_buffer is not here as that is specific 
0224 to each geometry "solid"
0225 
0226 These CSGNode float4 planes and qat4 inverse transforms are 
0227 here because those are globally referenced.
0228 
0229 This is equivalent to foundry upload with SBT.cc in OptiX 7 
0230 
0231 NB no significant uploading is done here, 
0232 the CSGFoundry buffers having already been uploaded to device.  
0233 
0234 **/
0235 void Six::createContextInputBuffers()
0236 {
0237     createContextInputBuffer<CSGNode>( foundry->d_node, foundry->getNumNode(), "node_buffer" ); 
0238     createContextInputBuffer<qat4>(    foundry->d_itra, foundry->getNumItra(), "itra_buffer" ); 
0239     createContextInputBuffer<float4>(  foundry->d_plan, foundry->getNumPlan(), "plan_buffer" ); 
0240 }
0241 
0242 
0243 /**
0244 Six::createGeometry
0245 ----------------------
0246 
0247 Invoked from Six::createGAS_Standard for each compound solid index. 
0248 Creates optix::Geometry corresponding to each CSGSolid referencing 
0249 its CSGPrim via its *prim_buffer*  
0250 
0251 **/
0252 
0253 optix::Geometry Six::createGeometry(unsigned solid_idx)
0254 {
0255     const CSGSolid* so = foundry->solid.data() + solid_idx ; 
0256     unsigned primOffset = so->primOffset ;  
0257     unsigned numPrim = so->numPrim ; 
0258     CSGPrim* d_pr = foundry->d_prim + primOffset ; 
0259 
0260     LOG(info) 
0261         << " solid_idx " << std::setw(3) << solid_idx
0262         << " numPrim " << std::setw(3) << numPrim 
0263         << " primOffset " << std::setw(3) << primOffset
0264         << " d_pr " << d_pr
0265         ;
0266 
0267     optix::Geometry solid = context->createGeometry();
0268     solid->setPrimitiveCount( numPrim );
0269     solid->setBoundingBoxProgram( context->createProgramFromPTXFile( geo_ptx_path , "bounds" ) );
0270     solid->setIntersectionProgram( context->createProgramFromPTXFile( geo_ptx_path , "intersect" ) ) ; 
0271 
0272     optix::Buffer prim_buffer = context->createBuffer( RT_BUFFER_INPUT, RT_FORMAT_USER, numPrim );
0273     prim_buffer->setElementSize( sizeof(CSGPrim) ); 
0274     prim_buffer->setDevicePointer(optix_device_ordinal, d_pr ); 
0275     solid["prim_buffer"]->set( prim_buffer );
0276  
0277     return solid ; 
0278 }
0279 
0280 void Six::createGAS()
0281 {
0282     if( solid_selection.size() == 0  )
0283     {
0284         createGAS_Standard();  
0285     }
0286     else
0287     {
0288         createGAS_Selection();  
0289     }
0290 }
0291 
0292 void Six::createGAS_Standard()
0293 {
0294     unsigned num_solid = foundry->getNumSolid();   
0295     LOG(info) << "num_solid " << num_solid ;  
0296 
0297     for(unsigned i=0 ; i < num_solid ; i++)
0298     {
0299         if(SGeoConfig::IsEnabledMergedMesh(i))
0300         {
0301             LOG(info) << " create optix::Geometry solid/ mm " << i ; 
0302             optix::Geometry solid = createGeometry(i); 
0303             solids[i] = solid ;  
0304         }
0305         else
0306         {
0307             LOG(error) << " emm skipping " << i ; 
0308         }
0309     }
0310 }
0311 
0312 void Six::createGAS_Selection()
0313 {
0314     unsigned num_solid = solid_selection.size() ;   
0315     LOG(info) << "num_solid " << num_solid ;  
0316     for(unsigned i=0 ; i < num_solid ; i++)
0317     {
0318         unsigned solidIdx = solid_selection[i] ; 
0319         optix::Geometry solid = createGeometry(solidIdx); 
0320         solids[solidIdx] = solid ;  
0321     }
0322 }
0323 
0324 
0325 optix::Geometry Six::getGeometry(unsigned solid_idx) const 
0326 {
0327     unsigned count = solids.count(solid_idx); 
0328     LOG_IF(fatal, count == 0) << " FAILED to find solid_idx " << solid_idx ; 
0329     assert( count <= 1 ) ; 
0330     return solids.at(solid_idx); 
0331 }
0332 
0333 
0334 void Six::createIAS()
0335 {
0336     if( solid_selection.size() == 0 )
0337     {
0338         createIAS_Standard(); 
0339     }
0340     else
0341     {
0342         createIAS_Selection(); 
0343     }
0344 }
0345 
0346 void Six::createIAS_Standard()
0347 {
0348     unsigned num_ias = foundry->getNumUniqueIAS() ; 
0349     for(unsigned i=0 ; i < num_ias ; i++)
0350     {
0351         unsigned ias_idx = 0 ; 
0352         optix::Group ias = createIAS(ias_idx); 
0353         groups.push_back(ias); 
0354     }
0355 }
0356 
0357 void Six::createIAS_Selection()
0358 {
0359     unsigned ias_idx = 0 ; 
0360     optix::Group ias = createSolidSelectionIAS( ias_idx, solid_selection ); 
0361     groups.push_back(ias); 
0362 }
0363 
0364 optix::Group Six::createIAS(unsigned ias_idx)
0365 {
0366     unsigned tot_inst = foundry->getNumInst(); 
0367     unsigned num_inst = foundry->getNumInstancesIAS(ias_idx, emm); 
0368     LOG(info) 
0369         << " ias_idx " << ias_idx
0370         << " tot_inst " << tot_inst
0371         << " num_inst " << num_inst 
0372         << " emm(hex) " << std::hex << emm << std::dec
0373         ; 
0374     assert( num_inst > 0); 
0375 
0376     std::vector<qat4> inst ; 
0377     foundry->getInstanceTransformsIAS(inst, ias_idx, emm ); 
0378     assert( inst.size() == num_inst );  
0379 
0380     optix::Group ias = createIAS(inst); 
0381     return ias ; 
0382 }
0383 
0384 /**
0385 Six::createSolidSelectionIAS
0386 ------------------------------
0387 
0388 Non-standard "presentation" geometry.
0389 
0390 **/
0391 
0392 optix::Group Six::createSolidSelectionIAS(unsigned ias_idx, const std::vector<unsigned>& solid_selection)
0393 {
0394     unsigned num_select = solid_selection.size() ; 
0395     assert( num_select > 0 ); 
0396     float mxe = foundry->getMaxExtent(solid_selection); 
0397 
0398 
0399     std::vector<qat4> inst ; 
0400     int ins_idx = 0 ; 
0401     unsigned middle = num_select/2 ; 
0402 
0403     for(unsigned i=0 ; i < num_select ; i++)
0404     {
0405         int ii = int(i) - int(middle) ; 
0406 
0407         int gas_idx = solid_selection[i] ; 
0408         int sensor_identifier = -1 ; 
0409         int sensor_index = -1 ; 
0410 
0411         qat4 q ; 
0412         q.setIdentity(ins_idx, gas_idx, sensor_identifier, sensor_index  );
0413         q.q3.f.x = 2.0*mxe*float(ii) ;   
0414 
0415         inst.push_back(q); 
0416         ins_idx += 1 ; 
0417     }
0418 
0419     optix::Group ias = createIAS(inst); 
0420     return ias ; 
0421 }
0422 
0423 
0424 
0425 /**
0426 Six::createIAS
0427 ---------------
0428 
0429 group
0430     xform
0431         perxform
0432             pergi
0433     xform
0434         perxform
0435             pergi
0436     xform
0437         perxform
0438             pergi
0439     ...
0440 
0441 **/
0442 
0443 optix::Group Six::createIAS(const std::vector<qat4>& inst )
0444 {
0445     unsigned num_inst = inst.size() ;
0446     LOG(info) << " num_inst " << num_inst ; 
0447 
0448     const char* accel = "Trbvh" ; 
0449     optix::Acceleration instance_accel = context->createAcceleration(accel);
0450     optix::Acceleration group_accel  = context->createAcceleration(accel);
0451 
0452     optix::Group group = context->createGroup();
0453     group->setChildCount( num_inst );
0454     group->setAcceleration( group_accel );  
0455 
0456     for(unsigned i=0 ; i < num_inst ; i++)
0457     {
0458         const qat4& qc = inst[i] ; 
0459 
0460         int ins_idx,  gas_idx, sensor_identifier, sensor_index ;
0461         qc.getIdentity(ins_idx,  gas_idx, sensor_identifier, sensor_index );
0462 
0463         /*
0464         if( ins_idx != i )
0465         {
0466             LOG(info) 
0467                 << " i " << i  
0468                 << " ins_idx " << ins_idx  
0469                 << " gas_idx " << gas_idx  
0470                 ;
0471         }        
0472         //assert( ins_idx == i );   when emm skipping this doesnt match, ins_idx didnt account for emm perhaps  
0473         */
0474 
0475         const float* qcf = qc.cdata(); 
0476         qat4 q(qcf);        // copy to clear identity before passing to OptiX
0477         q.clearIdentity(); 
0478 
0479         optix::Transform xform = context->createTransform();
0480         bool transpose = true ; 
0481         xform->setMatrix(transpose, q.data(), 0); 
0482         group->setChild(i, xform);
0483 
0484         // here referencing the GAS into the IAS with gas_idx
0485         optix::GeometryInstance pergi = createGeometryInstance(gas_idx, ins_idx); 
0486         optix::GeometryGroup perxform = context->createGeometryGroup();
0487         perxform->addChild(pergi); 
0488         perxform->setAcceleration(instance_accel) ; 
0489         xform->setChild(perxform);
0490     }
0491     return group ;
0492 }
0493 
0494 optix::GeometryInstance Six::createGeometryInstance(unsigned gas_idx, unsigned ins_idx)
0495 {
0496     //LOG(info) << " gas_idx " << gas_idx << " ins_idx " << ins_idx  ;   
0497     optix::Geometry solid = getGeometry(gas_idx); 
0498     optix::GeometryInstance pergi = context->createGeometryInstance() ;
0499     pergi->setMaterialCount(1);
0500     pergi->setMaterial(0, material );
0501     pergi->setGeometry(solid);
0502     pergi["identity"]->setUint(ins_idx);
0503     return pergi ; 
0504 }
0505 
0506 void Six::setTop(const char* spec)
0507 {
0508     char c = spec[0]; 
0509     assert( c == 'i' || c == 'g' );  
0510     int idx = atoi( spec + 1 );  
0511 
0512     LOG(info) << "spec " << spec ; 
0513     if( c == 'i' )
0514     {
0515         assert( idx < int(groups.size()) ); 
0516         optix::Group grp = groups[idx]; 
0517         context["top_object"]->set( grp );
0518     }
0519     else if( c == 'g' )
0520     {
0521         assert( idx < int(solids.size()) ); 
0522 
0523         optix::GeometryGroup gg = context->createGeometryGroup();
0524         gg->setChildCount(1);
0525      
0526         unsigned identity = 1u + idx ;  
0527         optix::GeometryInstance pergi = createGeometryInstance(idx, identity); 
0528         gg->setChild( 0, pergi );
0529         gg->setAcceleration( context->createAcceleration("Trbvh") );
0530 
0531         context["top_object"]->set( gg );
0532     }
0533 }
0534 
0535 void Six::launch(unsigned width, unsigned height, unsigned depth)
0536 {
0537     LOG(info) << "[ width " << width << " height " << height << " depth " << depth  ; 
0538     context->launch( entry_point_index , width, height, depth );  
0539     LOG(info) << "]" ; 
0540 }
0541