Questions about Programs of OptiX 7


I am learning OptiX 7. After reading over the documentation and examples, there are things I still do not understand:

1. How to assign programs like CH, AH to custom geometries ?
From optixCutouts example, there are modules for sphere and triangle. I can see OptixProgramGroups are linked with the pipeline, but I can not figure out how the programs are associated with sphere and triangle, and how OptiX trace could find correct programs for different geometries.

2. How to use optixContinuationCall/optixDirectCall ?
Assuming I have only one primitive, one ray type, and two continuation callables in OptiXProgramGroup. My SBT has only one record. Using optixContinuationCall(0) successfully call the first callable, but optixContinuationCall(1) doesn’t call the second callable and produce corrupted result(kind of random noise). In this case, how can I call the second one ?
I also tried 2 records in My SBT, then optixContinuationCall(1) calls the first callable with the second SBT. Which makes sense for me, but optixContinuationCall(2) result in corrupted result.

My callables:

extern "C"  float3 __continuation_callable__first()
    return make_float3(0.1f, 1.0f, 0.2f);

extern "C"  float3 __continuation_callable__second()
    return make_float3(1.0f, 0.1f, 0.2f);

I created my callables:

OptixProgramGroupDesc callable_prog_group_desc[2]; //

callable_prog_group_desc[0] = {};
callable_prog_group_desc[0].kind = OPTIX_PROGRAM_GROUP_KIND_CALLABLES;
callable_prog_group_desc[0].callables.moduleCC = module;
callable_prog_group_desc[0].callables.entryFunctionNameCC = "__continuation_callable__first";

callable_prog_group_desc[1] = {};
callable_prog_group_desc[1].kind = OPTIX_PROGRAM_GROUP_KIND_CALLABLES;
callable_prog_group_desc[1].callables.moduleCC = module;
callable_prog_group_desc[1].callables.entryFunctionNameCC = "__continuation_callable__second";

    2,   // num program groups

I linked my programs

OptixProgramGroup program_groups[] = { callable_prog_group, raygen_prog_group, miss_prog_group, hitgroup_prog_group };

OptixPipelineLinkOptions pipeline_link_options = {};
pipeline_link_options.maxTraceDepth = 5;
pipeline_link_options.debugLevel = OPTIX_COMPILE_DEBUG_LEVEL_FULL;
pipeline_link_options.overrideUsesMotionBlur = false;

OptixPipelineCompileOptions pipeline_compile_options = {};
pipeline_compile_options.usesMotionBlur = false;
pipeline_compile_options.traversableGraphFlags = OPTIX_TRAVERSABLE_GRAPH_FLAG_ALLOW_SINGLE_LEVEL_INSTANCING;
pipeline_compile_options.numPayloadValues = 3;
pipeline_compile_options.numAttributeValues = 3;
pipeline_compile_options.exceptionFlags = OPTIX_EXCEPTION_FLAG_NONE;
pipeline_compile_options.pipelineLaunchParamsVariableName = "params";

    sizeof(program_groups) / sizeof(program_groups[0]),,


Hi X_Hirtzlin, welcome!

Your program groups and your custom geometries meet in the SBT. To understand the connections, it’s good that you looked at the pipeline. You just need to also look carefully at the createSBT() function and also the buildGeomAccel() and buildInstanceAccel() functions. You wire your program groups into the SBT via optixSbtRecordPackHeader, and then custom geometry programs are called during traversal via the SBT offsets you put in your acceleration structures.

I can see one problem in your code, which is that you created 2 program groups for your 2 callables, but then you only put 1 of them in your pipeline. Since callable_prog_group is an array, using it directly is the same as pointing to the first item in your array. You’ll need 5 program groups rather than 4, using both callable_prog_group[0] and callable_prog_group[1].

Then make sure you have 5 SBT entries as well, and make sure to re-evaluate the SBT offsets you’ve used in various places, if necessary. I can’t see what you did for your SBT, so if your second callable is still producing corrupt results, the SBT usage is the place to start looking.

I hope that helps!


By the way, make sure to use Direct Callables for simple functions like these. Continuation Callables are what you use if you need to call optixTrace() inside them.


Hi David, thank you very much for such detailed response :)
Additional questions come below:

Now I understand how parameters of program are stored in SBT, and how geometries could find them. Here comes an additional question: can I have an OptiXProgramGroup only used by a specific geometry type?

For example, in optixCutouts, can I make a __anyhit__ah_sphere and a __anyhit__ah_triangle for sphere and triangle respectively? You know, both sphere and triangle go into __anyhit__ah currently, a dynamic branch is used. Imaging I have several custom geometries, would a dynamic switch case kills the performance?

Yes, you are right. Now I understand thanks.

Yes. Thank you for the suggestion :)

Yes, you can easily split into two anyhit programs. You would make two program groups, and use those in your SBT entries instead of the single combined program.

The optixCutouts sample combined them to demonstrate that a combined anyhit is possible with two different geometry types (it’s a decent example of using optixIsTriangleHit()) and it also reduces the amount of code needed for the sample by a little bit. It would be simple to split the anyhit program into two and remove the branch. I’d even recommend going through the exercise of modifying your copy of optixCutouts this way.

It’s good to be cautious and aware of branching generally, but in this case, the performance is not affected very much because very few warps would take both branches; the scene only has a small number of large objects, so most warps will hit the same geometry as a group. Best practices in a complex production scene would be to split the anyhit program since threads would be much more likely to diverge and take both branches within each warp.


Hi David,

I have successfully splitted the anyhit program to remove the branch, and it worked as expected.

Here comes an additional question:
Can I have different HitGroupRecord for triangles and spheres?

Regarding optixCutOuts example, I want to have Sphere structure only for sphere hit group, and vertices & texture coordinate only for triangles. It seems I can not do that, as shader binding table assumes a record has the same stride. Naturally, I will have to go to unions or external buffer pointers, is that true?

Thank you

Yes you can have different HitGroupRecords for different geometry. The stride requirement only means that your records need to be equally spaced in memory. (This makes it fast to find them during rendering.)

Take a look at the samples_exp/optixWhitted example, which has separate HitGroupRecords for spheres and parallelograms. Specifically look at the HitGroupData struct in optixWhitted.h.


Thank you for the confirmation, David.