Skip to content

Fix ray masking in hybrid occlusion traversal - #636

Open
BrianSharpe wants to merge 1 commit into
RenderKit:masterfrom
BrianSharpe:fix-packettrace-occlusion-multihit-issue
Open

BrianSharpe wants to merge 1 commit into
RenderKit:masterfrom
BrianSharpe:fix-packettrace-occlusion-multihit-issue

Conversation

@BrianSharpe

@BrianSharpe BrianSharpe commented Oct 2, 2026 •

Copy link
Copy Markdown

This fixes a bug where Embree executes the FilterFunction many times for a single hit during a call to rtcOccluded1M(), even on LOW and MEDIUM build quality. A workaround was to either avoid RTC_INTERSECT_CONTEXT_FLAG_COHERENT, or fall back to rtcOccluded1(), but this fixes the bug properly.

Disclaimer:
We encountered these bugs firsthand in our renderer. But have used ChatGPT to...

  • diagnose the issue
  • create the fix
  • write the following description
  • also author two standalone reproducible C programs (so you can verify before + after behavior)

repro programs
embree-pr-repro.zip

Description:
Two packet occlusion paths call the primitive intersector with !terminated instead of the rays active for the current leaf. This can repeat a filter callback for the same ray/primitive hit when the filter rejects the hit and continues traversal. Filters that accumulate contributions, such as additive emission or transparency, can therefore produce incorrect results.

The affected paths are:

  • Hybrid traversal with motion blur: the computed valid_leaf mask is ignored, allowing rays to test temporal references outside their times.
  • Coherent traversal: the leaf's m_active mask is ignored, allowing rays from other direction octants to test the same primitive repeatedly.

Both calls now pass the active leaf mask and restrict the returned termination mask to those same lanes. Masking the result is necessary because ArrayIntersectorK_1::occluded() returns !valid0, which also includes lanes excluded from its input.

Two standalone C reproducers are attached, with build instructions.
Compile against Embree 4 and run on an AVX2 CPU:
motion_occlusion isa=avx2
coherent_occlusion isa=avx2

The motion example uses rotating sphere instances and mixed ray times. Before the fix, packet traversal reports 128 entries instead of 64.

The coherent example uses one static triangle. Before the fix, two/four direction octants produce 128/256 hits instead of 64.

Both use MEDIUM build quality. Scalar traversal and control cases report 64 throughout. After the fix, all cases report 64. Each program checks every ray and returns 1 on failure or 0 on success.

Validated as C with MSVC 19.42 against master 3d9cb89b and patched commit df361682, using AVX and AVX2.

Restrict primitive tests and termination updates to active leaf rays in
both packet paths, preventing duplicate filter callbacks.
@stefanatwork stefanatwork self-assigned this Oct 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants