Back to home page

EIC code displayed by LXR

 
 

    


Warning, file /eic-opticks/CSGOptiX/CSGOptiX.cu was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /**
0002 CSGOptiX.cu
0003 ===================
0004 
0005 NB: ONLY CODE THAT MUST BE HERE DUE TO OPTIX DEPENDENCY SHOULD BE HERE
0006 everything else should be located elsewhere : mostly in qudarap: sevent, qsim
0007 or the sysrap basis types sphoton quad4 quad2 etc.. where the code is reusable
0008 and more easily tested.
0009 
0010 Functions
0011 -----------
0012 
0013 trace
0014     populate quad2 prd by call to optixTrace
0015 
0016 make_color
0017     minimal normal "shader"
0018 
0019 render
0020     raygen function : calling trace and "shading" pixels
0021 
0022 simulate
0023     raygen function : qsim::generate_photon, bounce while loop, qsim::propagate
0024 
0025     * ifndef PRODUCTION sctx::trace sctx::point record the propagation point-by-point
0026 
0027 simtrace
0028     raygen function : qsim.h generate_photon_simtrace, trace, sevent::add_simtrace
0029 
0030 __raygen__rg
0031     calls one of the above raygen functions depending on params.raygenmode
0032 
0033 __miss_ms
0034     default quad2 prd for rays that miss
0035 
0036 __closesthit__ch
0037     populate quad2 prd for rays that intersect
0038 
0039 __intersection__is
0040     converts OptiX HitGroupData into corresponding CSGNode and calls intersect_prim
0041     giving float4 isect: (normal_at_intersect, distance)
0042 
0043 **/
0044 
0045 #include <optix.h>
0046 
0047 #include "SRG.h"
0048 #include "scuda.h"
0049 #include "squad.h"
0050 #include "sqat4.h"
0051 #include "sphoton.h"
0052 #include "sphotonlite.h"
0053 #include "scerenkov.h"
0054 #include "sstate.h"
0055 
0056 #ifndef PRODUCTION
0057 #include "stag.h"
0058 #include "sseq.h"
0059 #include "srec.h"
0060 #endif
0061 
0062 #include "sevent.h"
0063 #include "sctx.h"
0064 
0065 // simulation
0066 #include <curand_kernel.h>
0067 
0068 #include "qrng.h"
0069 #include "qsim.h"
0070 
0071 #include "csg_intersect_leaf.h"
0072 #include "csg_intersect_node.h"
0073 #include "csg_intersect_tree.h"
0074 
0075 #include "Binding.h"
0076 #include "Params.h"
0077 #include "CSGOptiXHelpers.h"
0078 
0079 #include "scuda_pointer.h"
0080 #include "SOPTIX_getPRD.h"
0081 
0082 extern "C" { __constant__ Params params ;  }
0083 
0084 /**
0085 trace : pure function, with no use of params, everything via args
0086 -------------------------------------------------------------------
0087 
0088 refine:false
0089     does single optixTrace
0090 
0091 refine:true
0092     does a second optixTrace if 99 percent of the distance returned by the
0093     first optixTrace exceeds the refine_distance argument.
0094     This attempts to improve the precision of long distance intersects
0095     by doing a 2nd closer intersect.
0096 
0097 
0098 Outcome of trace is to populate *prd* using two 32-bit payload values to
0099 pass its 64-bit pointer between the raygen, intersection, closest-hit
0100 and miss programs.
0101 **/
0102 
0103 template<bool refine>
0104 static __forceinline__ __device__ void trace(
0105         OptixTraversableHandle handle,
0106         float3                 ray_origin,
0107         float3                 ray_direction,
0108         float                  tmin,
0109         float                  tmax,
0110         quad2*                 prd,
0111         unsigned               visibilityMask,
0112         float                  refine_distance
0113         )
0114 {
0115     const float rayTime = 0.0f ;
0116     OptixRayFlags rayFlags = OPTIX_RAY_FLAG_DISABLE_ANYHIT ;   // OPTIX_RAY_FLAG_NONE
0117     const unsigned SBToffset = 0u ;
0118     const unsigned SBTstride = 1u ;
0119     const unsigned missSBTIndex = 0u ;
0120     uint32_t p0, p1 ;
0121     packPointer( prd, p0, p1 ); // scuda_pointer.h : pack prd addr from RG program into two uint32_t passed as payload
0122 
0123     if(!refine)
0124     {
0125         optixTrace(
0126             handle,
0127             ray_origin,
0128             ray_direction,
0129             tmin,
0130             tmax,
0131             rayTime,
0132             visibilityMask,
0133             rayFlags,
0134             SBToffset,
0135             SBTstride,
0136             missSBTIndex,
0137             p0, p1
0138             );
0139     }
0140     else
0141     {
0142         optixTrace(
0143             handle,
0144             ray_origin,
0145             ray_direction,
0146             tmin,
0147             tmax,
0148             rayTime,
0149             visibilityMask,
0150             rayFlags,
0151             SBToffset,
0152             SBTstride,
0153             missSBTIndex,
0154             p0, p1
0155             );
0156 
0157         float t_approx = 0.99f*prd->distance() ;
0158         // decrease to avoid imprecise first intersect from
0159         // leading to giving a miss or other intersect the 2nd time
0160         if( t_approx > refine_distance )
0161         {
0162             float3 closer_ray_origin = ray_origin + t_approx*ray_direction ;
0163             optixTrace(
0164                 handle,
0165                 closer_ray_origin,
0166                 ray_direction,
0167                 tmin,
0168                 tmax,
0169                 rayTime,
0170                 visibilityMask,
0171                 rayFlags,
0172                 SBToffset,
0173                 SBTstride,
0174                 missSBTIndex,
0175                 p0, p1
0176                 );
0177 
0178             prd->distance_add( t_approx );
0179         }
0180     }
0181 
0182 }
0183 
0184 //#if !defined(PRODUCTION) && defined(WITH_RENDER)
0185 #if defined(WITH_RENDER)
0186 
0187 __forceinline__ __device__ uchar4 make_normal_pixel( const float3& normal, float depth )  // pure
0188 {
0189     return make_uchar4(
0190             static_cast<uint8_t>( clamp( normal.x, 0.0f, 1.0f ) *255.0f ),
0191             static_cast<uint8_t>( clamp( normal.y, 0.0f, 1.0f ) *255.0f ),
0192             static_cast<uint8_t>( clamp( normal.z, 0.0f, 1.0f ) *255.0f ),
0193             static_cast<uint8_t>( clamp( depth   , 0.0f, 1.0f ) *255.0f )
0194             );
0195 }
0196 
0197 __forceinline__ __device__ uchar4 make_zdepth_pixel( float depth )  // pure
0198 {
0199     return make_uchar4(
0200             static_cast<uint8_t>( clamp( depth   , 0.0f, 1.0f ) *255.0f ),
0201             static_cast<uint8_t>( clamp( depth   , 0.0f, 1.0f ) *255.0f ),
0202             static_cast<uint8_t>( clamp( depth   , 0.0f, 1.0f ) *255.0f ),
0203             static_cast<uint8_t>( clamp( depth   , 0.0f, 1.0f ) *255.0f )
0204             );
0205 }
0206 
0207 
0208 
0209 /**
0210 render : non-pure, uses params for viewpoint inputs and pixels output
0211 -----------------------------------------------------------------------
0212 
0213 Bugs with normal(0.f,0.f,0.f) via normalizing yields diddled_normal(nan,nan,nan)
0214 which make_color manages to clamp to (0,0,0) black.
0215 
0216 **/
0217 
0218 static __forceinline__ __device__ void render( const uint3& idx, const uint3& dim, quad2* prd )
0219 {
0220 
0221 #if defined(DEBUG_PIDX)
0222     //if(idx.x == 10 && idx.y == 10) printf("//CSGOptiX.cu:render idx(%d,%d,%d) dim(%d,%d,%d) \n", idx.x, idx.y, idx.z, dim.x, dim.y, dim.z );
0223 #endif
0224 
0225     float2 d = 2.0f * make_float2(
0226             static_cast<float>(idx.x)/static_cast<float>(dim.x),
0227             static_cast<float>(idx.y)/static_cast<float>(dim.y)
0228             ) - 1.0f;
0229 
0230 
0231     const unsigned cameratype = params.cameratype ;
0232     const float3 dxyUV = d.x * params.U + params.V * ( params.traceyflip ? -d.y : d.y ) ;
0233     const float3 origin    = cameratype == 0u ? params.eye                     : params.eye + dxyUV    ;
0234     const float3 direction = cameratype == 0u ? normalize( dxyUV + params.W )  : normalize( params.W ) ;
0235     //                           cameratype 0u:perspective,                    1u:orthographic
0236 
0237     trace<false>(
0238         params.handle,
0239         origin,
0240         direction,
0241         params.tmin,
0242         params.tmax,
0243         prd,
0244         params.vizmask,
0245         params.PropagateRefineDistance
0246     );
0247 
0248 #if defined(DEBUG_PIDX)
0249     //if(idx.x == 10 && idx.y == 10) printf("//CSGOptiX.cu:render prd.distance(%7.3f)  prd.lposcost(%7.3f)  \n", prd->distance(), prd->lposcost()  );
0250 #endif
0251 
0252 
0253 
0254 
0255 
0256     const float3* normal = prd->normal();
0257 
0258 #if defined(DEBUG_PIDX)
0259     //if(idx.x == 10 && idx.y == 10) printf("//CSGOptiX.cu:render normal(%7.3f,%7.3f,%7.3f)  \n", normal->x, normal->y, normal->z );
0260 #endif
0261 
0262     float3 diddled_normal = normalize(*normal)*0.5f + 0.5f ; // diddling lightens the render, with mid-grey "pedestal"
0263 
0264     float eye_z = -prd->distance()*dot(params.WNORM, direction) ;
0265     const float& A = params.ZPROJ.z ;
0266     const float& B = params.ZPROJ.w ;
0267     float zdepth = cameratype == 0u ? -(A + B/eye_z) : A*eye_z + B  ;  // cf SGLM::zdepth1
0268 
0269     if( prd->is_boundary_miss() ) zdepth = 0.999f ;
0270     // setting miss zdepth to 1.f give black miss pixels, 0.999f gives expected mid-grey from normal of (0.f,0.f,0.f)
0271     // previously with zdepth of zero for miss pixels found that OpenGL record rendering did not
0272     // appear infront of the grey miss pixels : because they were behind them (zdepth > 0.f ) presumably
0273 
0274     unsigned index = idx.y * params.width + idx.x ;
0275 
0276     if(params.pixels)
0277     {
0278 #if defined(DEBUG_PIDX)
0279         //if(idx.x == 10 && idx.y == 10) printf("//CSGOptiX.cu:render/params.pixels diddled_normal(%7.3f,%7.3f,%7.3f)  \n", diddled_normal.x, diddled_normal.y, diddled_normal.z );
0280 #endif
0281         params.pixels[index] = params.rendertype == 0 ? make_normal_pixel( diddled_normal, zdepth ) : make_zdepth_pixel( zdepth ) ;
0282     }
0283     if(params.isect)
0284     {
0285         float3 position = origin + direction*prd->distance() ;
0286         params.isect[index]  = make_float4( position.x, position.y, position.z, __uint_as_float(prd->identity())) ;
0287     }
0288 
0289 
0290 }
0291 #endif
0292 
0293 
0294 #if defined(WITH_SIMULATE)
0295 
0296 /**
0297 simulate : uses params for input: gensteps, seeds and output photons
0298 ----------------------------------------------------------------------
0299 
0300 This method aims to get as much as possible of its functionality from
0301 separately implemented and tested headers.
0302 
0303 The big thing that CSGOptiX provides is geometry intersection, only that must be here.
0304 Everything else should be implemented and tested elsewhere, mostly in QUDARap headers.
0305 
0306 Hence this "simulate" needs to act as a coordinator.
0307 Params take central role in enabling this:
0308 
0309 
0310 Params
0311 ~~~~~~~
0312 
0313 * CPU side params including qsim.h sevent.h pointers instanciated in CSGOptiX::CSGOptiX
0314   and populated by CSGOptiX::init methods before being uploaded by CSGOptiX::prepareParam
0315 
0316 COMPARE WITH qsim::mock_propagate
0317 
0318 
0319 
0320 Is cycling of the launch_idx.x (unsigned)idx possible here ? NOT YET
0321 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0322 
0323 * 0xffffffff/1e9 = 4.29 billion photons in one launch would need VRAM of 275 GB::
0324 
0325     In [102]: (0xffffffff*4*4*4)/1e9
0326     Out[102]: 274.87790688   # 275 GB of photons in one launch ?
0327 
0328 
0329 * suggests should limit max_slot to 0xffffffff (4.29 billion, on top of VRAM requirements)
0330 * current heuristic limit of 250M photons when limited by 32 GB VRAM
0331 
0332 * NOTE THE ABSOLUTE photon_idx IS LIKELY BEING CYCLED HOWEVER
0333 
0334 
0335 tmin/tmin0 avoiding light leakage "tunnelling"
0336 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0337 
0338 The ray trace tmin is used to prevent rays with origin "on" a surface (ie very close to it)
0339 from intersecting with the surface when trying to escape from it.
0340 For example on reflecting from a surface multiple intersects can be avoided
0341 with a suitably chosed tmin. Using the same tmin for all steps of the photon is simple
0342 but can have undesirable consequences. For example when generation by scintillation/cerenkov/torch
0343 or scatter/reemission happens within tmin of a surface the photon might "tunnel"
0344 through the surface. Globally reducing tmin would reduce the probability of tunneling but
0345 would cause repeated intersection issues for rays starting "on" surfaces.
0346 
0347 To avoid this dilemma a second tmin0 and the photon step flags that cause
0348 tmin0 to be used for the next ray trace can be configured.
0349 As shown in the below table the default photon step flags that cause tmin0
0350 to be used for the next ray trace are TO,CK,SI,SC,RE which
0351 are all things (generation, scattering, reemission) that mostly happen
0352 in the bulk away from boundaries where there is less concern regarding
0353 self intersection. As self-intersection is not so much of a concern tmin0
0354 might be set to zero. However, there could then be a problem with self intersection
0355 in cases where these things happen within close to surfaces.
0356 
0357 +----------------------------------+-----------------------------------------+-------------------+
0358 |  envvar                          |  method                                 | default           |
0359 +==================================+=========================================+===================+
0360 | OPTICKS_PROPAGATE_EPSILON        |  SEventConfig::PropagateEpsilon()       |  0.05f            |
0361 +----------------------------------+-----------------------------------------+-------------------+
0362 | OPTICKS_PROPAGATE_EPSILON0       |  SEventConfig::PropagateEpsilon0()      |  0.05f            |
0363 +----------------------------------+-----------------------------------------+-------------------+
0364 | OPTICKS_PROPAGATE_EPSILON0_MASK  |  SEventConfig::PropagateEpsilon0Mask()  |  TO,CK,SI,SC,RE   |
0365 +----------------------------------+-----------------------------------------+-------------------+
0366 
0367 **/
0368 
0369 static __forceinline__ __device__ void simulate( const uint3& launch_idx, const uint3& dim, quad2* prd )
0370 {
0371     sevent* evt = params.evt ;
0372     if (launch_idx.x >= evt->num_seed) return;   // was evt->num_photon
0373 
0374     unsigned idx = launch_idx.x ;
0375     unsigned genstep_idx = evt->seed[idx] ;
0376     const quad6& gs = evt->genstep[genstep_idx] ;
0377     // genstep needs the raw index, from zero for each genstep slice sub-launch
0378 
0379     unsigned long long photon_idx = params.photon_slot_offset + idx ;
0380     // 2025/10/20 change from unsigned to avoid clocking photon_idx and duplicating
0381     //
0382     // rng_state access and array recording needs the absolute photon_idx
0383     // for multi-launch and single-launch simulation to match.
0384     // The offset hides the technicality of the multi-launch from output.
0385 
0386     qsim* sim = params.sim ;
0387 
0388     RNG rng ;
0389     sim->rng->init( rng, sim->evt->index, photon_idx );
0390 
0391     sctx ctx = {} ;
0392     ctx.evt = evt ;   // sevent.h
0393     ctx.prd = prd ;   // squad.h quad2
0394 
0395     ctx.idx = idx ;
0396     ctx.pidx = photon_idx ;
0397 
0398 #if !defined(PRODUCTION) && defined(DEBUG_PIDX)
0399     ctx.pidx_debug = sim->base->pidx == photon_idx ;
0400 #endif
0401 
0402     sim->generate_photon(ctx.p, rng, gs, photon_idx, genstep_idx );
0403     // Only Cerenkov and scintillation gensteps store a source-material line in
0404     // q0.u.z. TORCH and FRAME use that slot for a genstep id, so leave the
0405     // carried line invalid for all other genstep types.
0406     const unsigned gentype = gs.q0.u.x;
0407     const bool carries_matline = CSGOptiX_GenstepCarriesMaterialLine(gentype);
0408     ctx.current_matline = carries_matline ? gs.q0.u.z : 0xFFFFFFFFu;
0409 
0410     FlowAction command = FlowAction::Start;
0411     int bounce = 0 ;
0412     if(evt->record) ctx.point(bounce);
0413     while( bounce < evt->max_bounce && ctx.p.time < params.max_time )
0414     {
0415         float tmin = ( ctx.p.orient_boundary_flag & params.PropagateEpsilon0Mask ) ? params.tmin0 : params.tmin ;
0416 
0417         // A dormant reorder intrinsic can change dispatch; only a compile-time opt-out removes it.
0418 #if defined(SIMPHONY_ENABLE_SER)
0419         if (params.SerInterval > 0 && (bounce % params.SerInterval) == 0)
0420         {
0421             switch (params.SerHint)
0422             {
0423             case 0:
0424                 optixReorder();
0425                 break;
0426             case 1:
0427                 optixReorder(bounce / params.SerInterval, 4);
0428                 break;
0429             }
0430         }
0431 #endif
0432 
0433 #ifdef SIMPHONY_RNG_REBUILD
0434         unsigned rng_p = ( rng.ctr.x << 2 ) | ( rng.STATE & 3u ) ;
0435 #endif
0436 
0437         // intersect query filling (quad2)prd
0438         switch(params.PropagateRefine)
0439         {
0440             case 0u: trace<false>( params.handle, ctx.p.pos, ctx.p.mom, tmin, params.tmax, prd, params.vizmask, params.PropagateRefineDistance );  break ;
0441             case 1u: trace<true>(  params.handle, ctx.p.pos, ctx.p.mom, tmin, params.tmax, prd, params.vizmask, params.PropagateRefineDistance );  break ;
0442         }
0443 
0444 #ifdef SIMPHONY_RNG_REBUILD
0445         sim->rng->init( rng, sim->evt->index, photon_idx );
0446         skipahead( (unsigned long long)( rng_p - ( ( rng.ctr.x << 2 ) | ( rng.STATE & 3u ) ) ), &rng ) ;
0447 #endif
0448 
0449         if( prd->boundary() == 0xffffu ) break ; // SHOULD ONLY HAPPEN FOR PHOTONS STARTING OUTSIDE WORLD
0450         // propagate can do nothing meaningful without a boundary
0451 
0452         // HMM: normalize here or within CSG ? Actually only needed for
0453         // geometry with active scaling, such as ellipsoid.
0454         // TODO: move this so its only done when needed
0455         //     ~/o/notes/issues/CSGOptiX_simulate_avoid_normalizing_every_normal.rst
0456         //
0457 
0458         float3* normal = prd->normal();
0459         *normal = normalize(*normal);
0460 
0461 #ifndef PRODUCTION
0462         ctx.trace(bounce);
0463 #endif
0464         command = sim->propagate(bounce, rng, ctx);
0465         bounce++;
0466         if(evt->record) ctx.point(bounce) ;
0467         if(command == FlowAction::Break) break;
0468     }
0469 #ifndef PRODUCTION
0470     ctx.end();  // write seq, tag, flat
0471 #endif
0472 
0473 
0474     if( evt->photon )
0475     {
0476         evt->photon[idx] = ctx.p ;  // *idx* (not *photon_idx*) as needs to go from zero for photons from a slice of genstep array
0477     }
0478 
0479     if( evt->photonlite )
0480     {
0481         sphotonlite l ;
0482         l.init( ctx.p.identity, ctx.p.time, ctx.p.flagmask );
0483         l.set_lpos(prd->lposcost(), prd->lposfphi() );
0484         evt->photonlite[idx] = l ;  // *idx* (not *photon_idx*) as needs to go from zero for photons from a slice of genstep array
0485     }
0486 
0487 
0488 
0489 }
0490 
0491 #endif
0492 
0493 
0494 //#if !defined(PRODUCTION) && defined(WITH_SIMTRACE)
0495 #if defined(WITH_SIMTRACE)
0496 
0497 /**
0498 simtrace
0499 ----------
0500 
0501 Used for making 2D cross section views of geometry intersects
0502 
0503 Note how seeding is still needed here despite the highly artificial
0504 nature of the center-extent grid of gensteps as the threads of the launch
0505 still needs to access different gensteps across the grid.
0506 
0507 TODO: Compose frames of pixels, isect and "fphoton" within the cegs window
0508 using the positions of the intersect "photons".
0509 Note that multiple threads may be writing to the same pixel
0510 that is apparently not a problem, just which does it is uncontrolled.
0511 
0512 unsigned index = iz * params.width + ix ;
0513 if( index > 0 )
0514 {
0515     params.pixels[index] = make_uchar4( 255u, 0u, 0u, 255u) ;
0516     params.isect[index] = make_float4( ipos.x, ipos.y, ipos.z, uint_as_float(identity)) ;
0517     params.fphoton[index] = p ;
0518 }
0519 **/
0520 
0521 
0522 static __forceinline__ __device__ void simtrace( const uint3& launch_idx, const uint3& dim, quad2* prd )
0523 {
0524     unsigned idx = launch_idx.x ;
0525     sevent* evt  = params.evt ;
0526     if (idx >= evt->num_simtrace) return;    // num_slot for multi launch simtrace ?
0527 
0528     unsigned genstep_idx = evt->seed[idx] ;
0529     unsigned photon_idx  = params.photon_slot_offset + idx ;
0530     // photon_idx same as idx for first launch, offset beyond first for multi-launch
0531 
0532 #if defined(DEBUG_PIDX)
0533     if(photon_idx == 0) printf("//CSGOptiX.cu : simtrace idx %d photon_idx %d  genstep_idx %d evt->num_simtrace %ld \n", idx, photon_idx, genstep_idx, evt->num_simtrace );
0534 #endif
0535 
0536     const quad6& gs = evt->genstep[genstep_idx] ;
0537 
0538     qsim* sim = params.sim ;
0539     RNG rng ;
0540     sim->rng->init(rng, 0, photon_idx) ;
0541 
0542     quad4 p ;
0543     sim->generate_photon_simtrace(p, rng, gs, photon_idx, genstep_idx );
0544 
0545 
0546     // HUH: this is not the layout of sevent::add_simtrace
0547     const float3& pos = (const float3&)p.q0.f  ;
0548     const float3& mom = (const float3&)p.q1.f ;
0549 
0550 
0551 #if defined(DEBUG_PIDX)
0552     if(photon_idx == 0) printf("//CSGOptiX.cu : simtrace idx %d pos.xyz %7.3f,%7.3f,%7.3f mom.xyz %7.3f,%7.3f,%7.3f  \n", idx, pos.x, pos.y, pos.z, mom.x, mom.y, mom.z );
0553 #endif
0554 
0555     switch(params.PropagateRefine)
0556     {
0557         case 0u: trace<false>( params.handle, pos, mom, params.tmin, params.tmax, prd, params.vizmask, params.PropagateRefineDistance );  break ;
0558         case 1u: trace<true>(  params.handle, pos, mom, params.tmin, params.tmax, prd, params.vizmask, params.PropagateRefineDistance );  break ;
0559     }
0560 
0561     evt->add_simtrace( idx, p, prd, params.tmin );  // sevent
0562     // not photon_idx, needs to go from zero for photons from a slice of genstep array
0563 }
0564 #endif
0565 
0566 /**
0567 for angular efficiency need intersection point in object frame to get the angles
0568 **/
0569 
0570 extern "C" __global__ void __raygen__rg_dummy()
0571 {
0572 }
0573 
0574 extern "C" __global__ void __raygen__rg()
0575 {
0576     const uint3 idx = optixGetLaunchIndex();
0577     const uint3 dim = optixGetLaunchDimensions();
0578 
0579     //bool midpix = idx.x == dim.x/2 && idx.y == dim.y/2 && idx.z == dim.z/2 ;
0580     //if(midpix) printf("//__raygen_rg.midpix params.raygenmode %d  \n", params.raygenmode);
0581 
0582     quad2 prd ;
0583     prd.zero();
0584 
0585 
0586     switch( params.raygenmode )
0587     {
0588 
0589 #ifdef WITH_SIMULATE
0590         case SRG_SIMULATE:  simulate( idx, dim, &prd ) ; break ;
0591 #endif
0592 #ifdef WITH_RENDER
0593         case SRG_RENDER:    render(   idx, dim, &prd ) ; break ;
0594 #endif
0595 #ifdef WITH_SIMTRACE
0596         case SRG_SIMTRACE:  simtrace( idx, dim, &prd ) ; break ;
0597 #endif
0598     }
0599 
0600 }
0601 
0602 
0603 
0604 /**
0605 __miss__ms
0606 -------------
0607 
0608 * missing "normal" is somewhat render specific and this is used for
0609   all raygenmode but Miss should never happen with real simulations
0610 * Miss can happen with simple geometry testing however when shoot
0611   rays from outside the "world"
0612 
0613 **/
0614 
0615 
0616 extern "C" __global__ void __miss__ms()
0617 {
0618     MissData* ms  = reinterpret_cast<MissData*>( optixGetSbtDataPointer() );
0619     const unsigned ii_id = 0xffffffffu ;
0620     const unsigned gp_bd = 0xffffffffu ;
0621     const float lposcost = 0.f ;
0622     const float lposfphi = 0.f ;
0623 
0624     quad2* prd = SOPTIX_getPRD<quad2>();
0625 
0626     prd->q0.f.x = ms->r ;
0627     prd->q0.f.y = ms->g ;
0628     prd->q0.f.z = ms->b ;
0629     prd->q0.f.w = 1.f ;     // attempt to allow rendering infront of miss pixels, but dont work
0630 
0631     prd->q1.u.x = 0u ;
0632     prd->q1.u.y = 0u ;
0633     prd->q1.u.z = 0u ;
0634     prd->q1.u.w = 0u ;
0635 
0636     prd->set_iindex_identity_(ii_id);
0637     prd->set_globalPrimIdx_boundary_(gp_bd);
0638     prd->set_lpos(lposcost, lposfphi);   // __miss__ms
0639 }
0640 
0641 /**
0642 __closesthit__ch : complete the per-ray data after intersection
0643 ============================================================================
0644 
0645 optixGetInstanceIndex (aka iindex)
0646     0-based index within IAS
0647 
0648 optixGetInstanceId (aka identity)
0649     user supplied instanceId,
0650     see SBT::collectInstances, sysrap/sqat4.h sqat4::get_IAS_OptixInstance_instanceId
0651     from July 2023: carries sensor_identifier+1 as needed for QPMT
0652 
0653 optixGetPrimitiveIndex (aka prim_idx)
0654     (not currently propagated)
0655     local index of AABB within the GAS,
0656     see SBT::createGAS / SOPTIX_BuildInput_CPA  (1+index-of-CSGPrim within CSGSolid/GAS).
0657     Note that instanced solids adds little to the number of AABB,
0658     most come from unfortunate repeated usage of prims in the non-instanced global
0659     GAS with repeatIdx 0 (JUNO up to ~4000)
0660 
0661 optixGetRayTmax
0662     In intersection and CH returns the current smallest reported hitT or the tmax passed into rtTrace
0663     if no hit has been reported
0664 
0665 
0666 optixGetPrimitiveType
0667     OPTIX_PRIMITIVE_TYPE_CUSTOM   = 0x2500    ## 9472 : GET THIS
0668     OPTIX_PRIMITIVE_TYPE_TRIANGLE = 0x2531    ## 9521 : HUH:GETTING ZERO WHEN EXPECT THIS ?
0669 
0670 
0671 ana:CH
0672    intersect IS program populates most of the prd per-ray-data struct
0673    including the trace distance and local normal at intersect
0674    this CH program just adds instance info and transforms the normal
0675    from object to world space
0676 
0677 tri:CH
0678    builtin triangles have no user defined intersect program, so this tri:CH
0679    program must populate everything that the ana:IS and ana:CH does
0680 
0681 tri:boundary
0682 
0683 
0684 ana:normals
0685    Calculation done by each shape implementation, no other choice ?
0686 
0687 tri:normals
0688    Possibilities:
0689 
0690    1. normal from cross product of vertex positions,
0691       GPU float precision calc
0692 
0693    2. normal from barycentric weighted vertex normals
0694       (probably best for sphere/torus or similar with small triangles)
0695 
0696    3. pick normal of one of the vertices,
0697       profits from double precision vertex normal calculated
0698       ahead of time
0699       (probably best for cube or similar with large triangles)
0700 
0701    Which is best depends on the shape and how the input
0702    vertex normals are calculated.
0703 
0704 **/
0705 
0706 extern "C" __global__ void __closesthit__ch()
0707 {
0708     unsigned iindex = optixGetInstanceIndex() ;
0709     unsigned identity = optixGetInstanceId() ;
0710     unsigned iindex_identity = (( iindex & 0xffffu ) << 16 ) | ( identity & 0xffffu ) ;
0711 
0712     OptixPrimitiveType type = optixGetPrimitiveType(); // HUH: getting type 0, when expect OPTIX_PRIMITIVE_TYPE_TRIANGLE
0713     const HitGroupData* hg = reinterpret_cast<HitGroupData*>( optixGetSbtDataPointer() );
0714 
0715 #if defined(DEBUG_PIDX)
0716     //const uint3 idx = optixGetLaunchIndex();
0717     //if(idx.x == 10 && idx.y == 10) printf("//__closesthit__ch idx(%u,%u,%u) type %d \n", idx.x, idx.y, idx.z, type);
0718     //if(identity == 52264 || identity == 52265 || identity == 52266) printf("//__closesthit__ch iindex %u type %d identity %d \n", iindex, type, identity );
0719 #endif
0720 
0721     if(type == OPTIX_PRIMITIVE_TYPE_TRIANGLE || type == 0)  // WHY GETTING ZERO HERE ?
0722     {
0723         const TriMesh& mesh = hg->mesh ;
0724         unsigned globalPrimIdx_boundary = (( mesh.globalPrimIdx & 0xffffu ) << 16 ) | ( mesh.boundary & 0xffffu ) ;
0725         const unsigned prim_idx = optixGetPrimitiveIndex();
0726         const float2   barys    = optixGetTriangleBarycentrics();
0727 
0728         uint3 tri = mesh.indice[ prim_idx ];
0729         const float3 P0 = mesh.vertex[ tri.x ];
0730         const float3 P1 = mesh.vertex[ tri.y ];
0731         const float3 P2 = mesh.vertex[ tri.z ];
0732         const float3 P = ( 1.0f-barys.x-barys.y)*P0 + barys.x*P1 + barys.y*P2;
0733 
0734         //const float3 N0 = mesh.normal[ tri.x ];
0735         //const float3 N1 = mesh.normal[ tri.y ];
0736         //const float3 N2 = mesh.normal[ tri.z ];
0737         //const float3 Ng = ( 1.0f-barys.x-barys.y)*N0 + barys.x*N1 + barys.y*N2; // guesss
0738         // local normal from  bary-weighted vertex normals
0739 
0740         const float3 Ng = cross( P1-P0, P2-P0 );
0741         // local normal from cross product of vectors between vertices : HMM is winding order correct : TODO: check sense of normal
0742 
0743         const float3 N = normalize( optixTransformNormalFromObjectToWorldSpace( Ng ) );
0744 
0745         float t = optixGetRayTmax() ;
0746 
0747         // cannot get Object frame ray_origin/direction in CH (only IS,AH)
0748         //const float3 ray_origin = optixGetObjectRayOrigin();
0749         //const float3 ray_direction = optixGetObjectRayDirection();
0750         //const float3 lpos = ray_origin + t*ray_direction  ;
0751         // HMM: could use P to give the local position ?
0752 
0753         float lposcost = normalize_cost(P); // scuda.h  "cosTheta" z/len of local frame position
0754         float lposfphi = normalize_fphi(P);
0755 
0756 
0757         quad2* prd = SOPTIX_getPRD<quad2>();
0758 
0759         prd->q0.f.x = N.x ;
0760         prd->q0.f.y = N.y ;
0761         prd->q0.f.z = N.z ;
0762         prd->q0.f.w = t ;
0763 
0764         prd->set_iindex_identity_( iindex_identity ) ;
0765         prd->set_globalPrimIdx_boundary_(  globalPrimIdx_boundary ) ;
0766         prd->set_lpos(lposcost, lposfphi);   // __closesthit__ch.TRIANGLE
0767 
0768     }
0769     else if(type == OPTIX_PRIMITIVE_TYPE_CUSTOM)
0770     {
0771         //const CustomPrim& cpr = hg->prim ;
0772         quad2* prd = SOPTIX_getPRD<quad2>();
0773 
0774         prd->set_iindex_identity_( iindex_identity ) ;
0775 
0776         float3* normal = prd->normal();
0777         *normal = optixTransformNormalFromObjectToWorldSpace( *normal ) ;
0778     }
0779 }
0780 
0781 /**
0782 __intersection__is
0783 ----------------------
0784 
0785 HitGroupData provides the nodeOffset and globalPrimIdx of the intersected CSGPrim.
0786 Which Prim gets intersected relies on the CSGPrim::setSbtIndexOffset
0787 
0788 Note that optixReportIntersection returns a bool, but that is
0789 only relevant when using anyHit as it provides a way to ignore hits.
0790 But Opticks does not use anyHit so the returned bool should
0791 always be true.
0792 
0793 The intersection program stores hit data directly in the shared per-ray data.
0794 
0795 **/
0796 
0797 extern "C" __global__ void __intersection__is()
0798 {
0799 
0800 #if defined(DEBUG_PIDXYZ)
0801     const uint3 idx = optixGetLaunchIndex();
0802     const uint3 dim = optixGetLaunchDimensions();
0803     bool dumpxyz = idx.x == params.pidxyz.x && idx.y == params.pidxyz.y && idx.z == params.pidxyz.z ;
0804     //if(dumpxyz) printf("//__intersection__is  idx(%u,%u,%u) dim(%u,%u,%u) dumpxyz:%d \n", idx.x, idx.y, idx.z, dim.x, dim.y, dim.z, dumpxyz);
0805 #else
0806     bool dumpxyz = false ;
0807 #endif
0808 
0809 
0810     HitGroupData* hg  = (HitGroupData*)optixGetSbtDataPointer();
0811     int nodeOffset = hg->prim.nodeOffset ;
0812     int globalPrimIdx = hg->prim.globalPrimIdx ;
0813 
0814     const CSGNode* node = params.node + nodeOffset ;  // root of tree
0815     const float4* plan = params.plan ;
0816     const qat4*   itra = params.itra ;
0817 
0818     const float  t_min = optixGetRayTmin() ;
0819     const float3 ray_origin = optixGetObjectRayOrigin();
0820     const float3 ray_direction = optixGetObjectRayDirection();
0821 
0822     float4 isect ; // .xyz normal .w distance
0823     isect.x = 0.f ;
0824     isect.y = 0.f ;
0825     isect.z = 0.f ;
0826     isect.w = 0.f ;
0827 
0828     bool valid_isect = intersect_prim(isect, node, plan, itra, t_min , ray_origin, ray_direction, dumpxyz );
0829     if(valid_isect)
0830     {
0831         const float3 lpos = ray_origin + isect.w*ray_direction ;
0832         const float lposcost = normalize_cost(lpos);  // scuda.h cosTheta z/len of local(aka Object) frame position
0833         const float lposfphi = normalize_fphi(lpos);
0834 
0835         const unsigned hitKind = 0u ;     // only up to 127:0x7f : could use to customize how attributes interpreted
0836         const unsigned boundary = node->boundary() ;  // all CSGNode in the tree for one CSGPrim tree have same boundary
0837         const unsigned globalPrimIdx_boundary = (( globalPrimIdx & 0xffffu ) << 16 ) | ( boundary & 0xffffu ) ;
0838 
0839         // Coincident sibling faces can produce equal entering and exiting hits.
0840         // Move only the reported exiting distance to the next representable float
0841         // during photon simulation so the entering sibling wins an exact tie.
0842         // This one-ULP ordering change cannot overstep a representable physical
0843         // gap. Keep the unbiased isect.w in the PRD for position advancement.
0844         const float cosI = dot(ray_direction, make_float3(isect.x, isect.y, isect.z));
0845         const float t_report = CSGOptiX_ReportedIntersectionDistance(isect.w, params.raygenmode, cosI);
0846 
0847         if (optixReportIntersection(t_report, hitKind))
0848         {
0849             quad2* prd = SOPTIX_getPRD<quad2>(); // access prd addr from RG program
0850             prd->q0.f = isect ;  // .w:distance and .xyz:normal which starts as the local frame one
0851             prd->set_globalPrimIdx_boundary_(globalPrimIdx_boundary) ;
0852             prd->set_lpos(lposcost, lposfphi);    // __intersection__is.CUSTOM
0853         }
0854 
0855    }
0856 
0857 #if defined(DEBUG_PIDXYZ)
0858     //if(dumpxyz) printf("//__intersection__is  idx(%u,%u,%u) dim(%u,%u,%u) dumpxyz:%d valid_isect:%d\n", idx.x, idx.y, idx.z, dim.x, dim.y, dim.z, dumpxyz, valid_isect);
0859 #endif
0860 
0861 }
0862 // story begins with intersection