2 GASes insert into 1 IAS problem


I want to put 2 GASes, one with triangle mesh (importing from .gltf files) and the other with custom primitive types (a hard-coded sphere) into 1 IAS. Both of them contains only one BuildInput.
Should I use the same handle when calling optixAccelbuild for each GAS? And pass this handle to IAS and then build IAS?
Another question is that, is the d_gas_output (in the optixWhitted example) points to the GAS info stored in device? So should these 2 GASes for triangle mesh and custom primitive type use the same CUdeviceptr or different ones?
Thanks in advance!

Best regards,

Please read this chapter of the OptiX Programming Guide:

Should I use the same handle when calling optixAccelbuild for each GAS? And pass this handle to IAS and then build IAS?

No, each of the primitive types needs to be built into a separate geometry acceleration structure (GAS) using optixAccelBuild and each has it’s unique device memory pointer and traversable handle.
(It’s not allowed to mix different primitive types in a single GAS.)

Another question is that, is the d_gas_output (in the optixWhitted example) points to the GAS info stored in device?

After the two GAS have been build, you have two different GAS output buffers containing the AS itself and two different traversable handles which identify these for the OptiX ray traversal.
That pair of device pointer and traversable handle together are your acceleration structure and both need to stay intact as long as you use the AS for ray tracing. Same for the instance AS.

Then you assign the two GAS traversable handles to two different OptixInstance structures which are then used as input for the instance acceleration structure (IAS) in a third optixAccelBuild call and the traversable handle of that is your top-level handle you put into your launch parameter block from which you can read it for the optixTrace calls inside the device code.

So should these 2 GASes for triangle mesh and custom primitive type use the same CUdeviceptr or different ones?

Each acceleration structure has its own memory! How you allocated that, with individual CUDA malloc calls or for example with an an arena allocator managing bigger blocks of device memory from which you get sub-buffers, is completely your choice. You just need to make sure that you allocate enough memory as specified by the optixAccelComputeMemoryUsage calls.

Thanks for the quick and thorough answer!
I have created two separate GASes and IAS as you described and I have set the sbtoffset in instances as 0 and 2 because I have two ray types and number of SBT record is 1 for both GASes. When building the GAS of the hard-coded sphere I have set the flags to disable anyhit. But after launching the ray, it only goes into the closest hit program of the imported triangle mesh geometry but none of the radiance ray can enter the intersection program of the sphere. And here is my program groups:

OptixProgramGroup program_groups[] =
        m_radiance_hit_group,  // trace of radiance ray for triangle mesh geometry
        m_occlusion_hit_group,  // trace of occlusion ray for triangle mesh geometry

        radiance_sphere_prog_group,   // trace of radiance ray for sphere, including IS and CH program
        occlusion_sphere_prog_group  // trace of occlusion ray for sphere, for now it only contains one IS program

What could be the reason for this and how can analyse it? Thanks again!

That’s usually a sign for an incorrectly setup shader binding table.
You either call into the incorrect program (SBT offset) or forgot to define the sphere intersection program inside the OptixProgramGroups although the comment says it’s including IS and CH programs which would happen in a different code than the excerpt you’ve given, means that is meaningless for the analysis of the problem.
Built in triangles do not have an intersection program and custom primitives or the built-in curve and sphere primitives in OptiX 7.6 require one or you cannot reach their hit programs.

I would recommend to try one GAS and only its hit records in the SBT at a time. If either works individually it’s the wrong SBT offset.
If the sphere doesn’t work alone, then it’s something with its programs.

You could try enabling the OptiX validation mode when debugging the issue:
and add and exception program to see if there is any other problem in the device code:

I have tried only with sphere and it doesn’t work alone so it might be the program problem.
Will it cause problem if I have nothing inside the CH program of the sphere?
I haven’t write it yet. I have used the similar code in sphere.cu for intersection program and only add one printf sentence to test if the radiance ray can enter IS program.
If it is okay for keeping CH empty or set CH module to nullptr, I will keep finding the issues.
Best regards,

If the OptixProgramGroup contains nullptr for the module and program name that’s allowed and faster than providing an empty program.
Have a look at the listing 6.11 here:

You should always default-initialize all OptiX structures to contain zeros (with ={};or a memset()). This will prevent errors in case future OptiX versions add fields to existing structures as it happened with multiple OptiX versions so far.
So if you do that with your program group structures, there isn’t a need to explicitly set unused module and program name fields to nullptr except for code clarity.

Note that OptiX 7.6.0 supports spheres as a built-in geometric primitive and you don’t need to program an own intersection program for those but can get the one OptiX provides:

Did you enable support for custom primitives inside the OptixPipelineCompile usesPrimitiveTypeFlags?
Please have a look at listing 8.1 in above link.

Thank you very much!
Finally, I have found out it is the traverasable graph flag problem, I should set the flag to OPTIX_TRAVERSABLE_GRAPH_FLAG_ALLOW_ANY, then it works. Before it was set to OPTIX_TRAVERSABLE_GRAPH_FLAG_ALLOW_SINGLE_LEVEL_INSTANCING and as described in the API, in my case I shouldn’t use this flag.

Best regards,

I haven’t enable this actually, I will also enable this!
Thank you very much!

Dear Moderator,

I have another question about compile of cuda file. Now I cannot see any compile error if I make mistakes in cuda file. If there is any error, I can only see:
Caught exception: sutil::samplePTXFilePath couldn’t locate long_optix_2v.cu for sample long_optix_2v
How can I see the compile error for cuda file? Thanks again!

Best regards,

That should simply work when compiling CUDA *.cu files to *.ptx or *.optixir files using NVCC.
But depending on the OptiX SDK version you’re using, there was an error in one of the CMake scripts (using function instead of macro) which resulted in that effect and has been fixed with the newest OptiX SDK 7.6.0 release.

More information in this forum thread:

Thank you very much! I am using 7.5 release and I have changed that and it works well now!

Best regards,

Dear moderator,
I have another general question. Iwant to simulate the diffraction of electromagnetic wave and it only happens at the edge of geometry. I think the best way to do it for now would be firstly finding all the edges in the geometry (only depend on the geometry file) and then I will be able to calculate the hitpoint on each edges due to the characteristic of diffraction. Then I will trace from the tx position to hitpoint and hitpoint to rx position to see if there is any occlusion, if not then it is a valid diffraction path.
To do this, I want to put all the possible hitpoint into launch parameters. With hitpoint, then I also know the ray direction, and I just need a occulsion trace. But it is recommended not to put too much data in launch parameters right? Since the imported city geometry might have many edges which means many possible hitpoints. So how can I do it?
Thanks in advance!

Best regards,

You do not put the data itself into the launch parameters but a pointer to the desired data structure in global memory (device VRAM) into the launch parameter block, similar to any other input or output buffer CUdeviceptr you have in your launch parameters already.
That way you can store arbitrarily complex data structures behind a single pointer.

A discussion about that can be found in this thread:

Here’s another post explaining the basics with more links:

Thanks for your quick answer! I will try this!