Add sorted all-hit TLAS traversal for ray path rasterization - #19
Conversation
|
Sorry, I just noticed that I accidentally included formatting changes in |
Extend `all_hits!` to accept and populate an `instance_indices_out` buffer with 1-based TLAS instance indices alongside hit metadata and distances. Duplicate suppression now also checks the instance index, so coplanar hits from different instances are correctly retained as separate hits.
rename variables consistantly with other functions (e.g. closest_hit)
|
No worries ;) Happy to get this in once ready :) |
|
@SimonDanisch, I have a question: I can't manage to build a correct TLAS on the GPU (I use Metal). When I build the mutable TLAS on But when I build the TLAS directly on the Metal backend, using (roughly): tlas = Raycore.TLAS(metal_backend)
push!(tlas, mesh_or_instances...)
Raycore.sync!(tlas)
static_tlas = tlas.static_tlasand then run the same RayCore traversal kernels on Metal, I get very different hit stacks. I checked, and the issue shows up before ArchimedLight's reductions, it's the raw RayCore outputs that differ at the hit level, even for missing hits. So my current fix is to build it on the CPU first and adapt it to the GPU, so instead of: tlas_build_backend = metal_backendI do: tlas_build_backend = KernelAbstractions.CPU()
static_tlas = Adapt.adapt(metal_backend, tlas.static_tlas)A few observations from my diagnostics:
Does RayCore currently support building TLAS directly on Metal.jl? If so, did you see weird things happening with I can try and provide a minimal reproducer if it's useful. Else I can try debugging myself, but I don't have the expertise so I'll probably rely heavily on AI tools. |
|
Yeah metal has a problem with the used atomics ( i think in the KA.sort! implementation). |
|
OK @SimonDanisch let's give it a try. I have Codex Pro for a bit, let's put it to good use. So should I start in Metal directly or KA? |
|
I think the problem is: perm = AK.sortperm(morton_codes)So the MWE might just be: using Metal
import AcceleratedKernels as AK
codes = rand(UInt32, 10_000)
mt_codes = MtlArray(codes)
perm = AK.sortperm(mt_codes)
Array(perm) == sortperm(codes)I would prompt to make this work first without touching AK, since as far as I can tell the code is correct on other platforms, so if we really want to have Metal work like all the other platforms, and deliver on the promise to be able to just switch out the backends, the fix should go into metal. |
|
Thanks a lot @SimonDanisch, I opened a PR on Metal and another on Atomix. The issue was that Metal atomics were always treated as relaxed, which is fast but does not guarantee that GPU threads see each other’s BVH writes before continuing the refit. That should be fixed now. Let's wait for a merge and release of both packages (if they agree). |
This allows to give a neutral value for GPU padding/workgroup lanes
|
I use scene_aabb = backend isa KA.CPU ? mapreduce(world_bound, ∪, primitives, init=init) : AK.mapreduce(world_bound, ∪, primitives; init=init, neutral=init) |
This PR adds
all_hits!forStaticTLAStraversal to collect every unique hit along a ray into caller-provided buffers, sorted by distance.I need this to implement the rasterization algorithm for ArchimedLight, because it uses the full sequence of intersections along each ray to compute rediffusion.
What changed
Implements
all_hits!and exports it. We first add a single-pass TLAS/BLAS traversal that writes triangle metadata, hit distance, per-ray hit count and overflow status. Then we keep the closestmax_hitshits when the output stack is full. Suppress duplicate hits with matching metadata and near-identical distance.I added some tests in
test/test_instanced_bvh.jl.Important note: I used Codex to implement this feature.