Optix 7.0: Payload data set using optixSetPayload_x() lost if anyhit program calls optixIgnoreIntersection()

I’ve found what I think it a bug in Optix 7.0: if you set per ray data payload values in an anyhit program directly on the register values using optixSetPayload_x() the values appear to get lost if you call optixIgnoreIntersection().

I ran into the problem because I’m trying to accumulate values along a ray where the order of intersection doesn’t matter (for an ‘X-Ray’ style render), but trying to copy how coloured occlusions for glass work in the optixWhitted example in the SDK didn’t create the expected results.

I’ve got around the problem by using a pointer to the per ray data payload instead of using the register values directly, which seems to work.

(I think the __anyhit__glass_occlusion() program in the optixWhitted example that comes with the SDK is affected by this problem.)

Could you please provide some more information on your system configuration?
OS version, installed GPU(s), VRAM amount, display driver version, OptiX major.minor.micro version, CUDA toolkit version used to generate the input PTX, host compiler version.

Ubuntu 18.04
2x RTX 2080Ti connected with NVLink, but current application only using one GPU
VRAM: 11GB on each GPU
Display Driver: 440.44
Optix 7.0.0
CUDA 10.2
nvcc 10.2.89
gcc 7.4.0

Thanks, that will be helpful if this needs to be reproduced.

The payload changes inside the anyhit program should actually persist.
The optixIgnoreIntersection() would only affect the intersection attributes which need to be the ones from the closest intersection when finally reaching the closest hit program.

Could you please check if there is a mismatch between different payloads in your application?
That’s something which happened in the past when payloads were not behaving like expected.
Or maybe a missing optixGetPayload and optixSetPayload pair when accumulating the result?

(The optixWhitted program is kind of weird since it doesn’t actually produce the shadow for a thick shell hollow glass sphere which might be on purpose for historic reasons.)

I’ve tried setting up a more minimal test case, and something strange seems to be going on. I’m now not getting the accumulation of values I’m expecting even when setting the PRD using packed / unpacked pointers.

I’ve now got a case where with the same code for dealing with hits:

  1. If I’m running it as a ‘closesthit’ program I get the results I expect (only showing the values from the first primitive the camera rays are hitting)

  2. If I’m running it as a ‘anyhit’ program without optixIgnoreIntersection() I get similar results to the closest hit program, but with occasional places where the result appears to have two or more hits along a camera ray combined.

  3. If I’m running it as a ‘anyhit’ program with optixIgnoreIntersection() I don’t get any results back from the hits.

It’s getting quite confusing! Is there anything that I should be setting to get accumulated results from a single hit from each primitive along rays apart from:

  1. Using an ‘anyhit’ hit program
  2. Setting the aabbArray.flags to ‘OPTIX_GEOMETRY_FLAG_REQUIRE_SINGLE_ANYHIT_CALL’ for all the primitives
  3. Calling optixIgnoreIntersection() in the hit program.

?

For the per-ray data I’m using:

struct XrayPRD
{
float4 result;
unsigned int seed;
};

For the hit programs as described above I’m using the following (where dt is the chord distance through the sphere primitives, which the intersection program seems to be setting correctly from what I’m seeing in cases 1 and 2 as described above):

template
forceinline device T* getPRD()
{
const uint32_t u0 = optixGetPayload_0();
const uint32_t u1 = optixGetPayload_1();
return reinterpret_cast<T*>( unpackPointer( u0, u1 ) );
}

extern “C” global void __closesthit__radiance_xray()
{
float dt = int_as_float( optixGetAttribute_0() );
XrayPRD &prd = (XrayPRD)getPRD();
prd.result = prd.result + make_float4(make_float3(dt), 1.0f);
}

extern “C” global void __anyhit__radiance_xray_noIgnore()
{
float dt = int_as_float( optixGetAttribute_0() );
XrayPRD &prd = (XrayPRD)getPRD();
prd.result = prd.result + make_float4(make_float3(dt), 1.0f);
}

extern “C” global void __anyhit__radiance_xray_withIgnore()
{
float dt = int_as_float( optixGetAttribute_0() );
XrayPRD &prd = (XrayPRD)getPRD();
prd.result = prd.result + make_float4(make_float3(dt), 1.0f);
optixIgnoreIntersection();
}

I’ve worked it out: the problem was the miss program which was over writing the accumulated result from all the hits. I hadn’t realized the miss program was getting called even if the ray hit objects, but of course than makes complete sense if you’re calling optixIgnoreIntersection() at every hit.

Sorry for the noise: I’ve now got a system working as expected. Simply replacing the miss program with a NOP solved the issue.

Happy to send code to you if you think it might be a useful example showing how to accumulate density data.