← Blog
✎publicación de blog

Mojo can reach ray-tracing hardware it does not expose

A single thin ray of light entering a small box made of smaller nested boxes on a dark grid, the far side of the box left dark, the AMARBARO mark centered in the foreground.

Mojo 1.1.0 can emit the RDNA3 BVH ray-intersection instruction for gfx1100 from a 20-line kernel, but it has only been compiled, never run.

Mojo has no ray-tracing API. A 20-line probe makes it emit one anyway. For gfx1100, the RDNA3 target of the RX 7900 XTX, the assembly contains:

image_bvh_intersect_ray v[0:3], [v4, v3, v[0:2], v[0:2], v[0:2]], s[4:7]

That is the hardware instruction that intersects a ray with a node of a bounding volume hierarchy (BVH), the tree a ray tracer walks. We have never run it. The honest summary is a compile result with a long list of things that are not there yet.

The obvious route fails on a vector width

Mojo can call an LLVM intrinsic by name through llvm_intrinsic. The BVH intrinsic wants its ray operands as <3 x float>, a three-wide vector. Mojo's SIMD types are powers of two, so there is no three-wide float to pass. The straightforward call cannot express that argument.

Building the vector one element at a time gets through

The probe skips the SIMD type. It builds a vector<3xf32> in MLIR directly: start from llvm.mlir.poison, put a value in with llvm.insertelement, and call the intrinsic through pop.call_llvm_intrinsic with hasSideEffects = false. The compile entry point is _compile_code with the target amdgpu:gfx1100, which returns the assembly instead of launching anything.

I reran it today on Mojo 1.1.0, from both the kinestride and fly-connectome environments, and the instruction above is in the output. Mojo's crash reporter printed a stack dump and said it could not start its handler; the probe still exited 0 and printed the assembly.

The line matches the shape the RDNA3 manual gives for the instruction (section 10.9.1). Four result registers, a 128-bit descriptor in s[4:7], and five address groups in the non-sequential-address form (10.9.2): node pointer, ray extent, then origin, direction and inverse direction as three registers each. That is the right grammar. It says nothing about whether the arguments mean anything.

Everything after the compile is missing

Look at the register groups again. Origin, direction and inverse direction are all v[0:2]. The probe passes one vector, o, three times, and sets only its first component. The other two components of each vector are undefined (poison). The result is a valid instruction with garbage arguments.

The descriptor is worse. The manual's resource definition for the BVH instruction (10.9.3) needs:

  • a type field of 0x8 in bits 127:124;
  • a base address in bits 39:0, 256-byte aligned;
  • a size in bits 105:64, the number of nodes minus one.

The probe's descriptor is four zero words. Its type field is 0, its base is null and its size says one node at address zero. And there is no node in memory at all: the sections I read give the instruction's operands and the descriptor, but we have not written down the layout of a box node, and we have not built one.

Also missing, plainly:

  • The kernel has never been launched, because _compile_code hands back text.
  • It leans on private entry points (_compile_code, raw __mlir_op), so a Mojo release can break it without warning.
  • We have not checked that marking a memory-reading instruction hasSideEffects = false is safe once real nodes are written by an earlier kernel.

A compile is evidence of reachability, not of function.

What counts as proof:

  • A one-ray launch that returns the four child pointers of a box node in the order of their hit distance.
  • That result matching a plain software ray-box test on the same node.
  • A ray that misses, to show how a miss comes back.

What does not count:

  • The assembly line above.
  • A launch that does not hang.
  • A returned value we cannot compare with something computed another way.

What would prove it works

One ray, one box node, one thread. Build a BVH texture holding a single box node with four children, write a descriptor with type 0x8, a 256-byte-aligned base and size zero, and fill all three vectors properly. Launch one thread and read the four result words. For a box node the manual says the hardware returns the four child pointers sorted by hit distance. Run the same ray through a software slab test on the same node and compare.

Do it for a ray that hits two of the four children and for one that hits none. If the order matches and the miss comes back in a form we can decode, the instruction is reachable from Mojo and means what the manual says. If the card hangs, or the words do not match the software result, the claim shrinks to "Mojo can emit it."

Why bother

The fly's renderer plan is a software BVH first, then the hardware instruction checked against it. The software tree is the oracle, and the probe is the first step toward the check. We rejected Vulkan ray tracing because Mojo has no Vulkan bindings and the only other route was HIP to Vulkan interop.

What it cost

One commit, no shipped feature, and no speed number: the probe is not on the physics path and has no timing. What we gave up is the right to say "ray tracing in Mojo," which is why this post is about a single line of assembly.

How to prove this wrong

Compile the probe for amdgpu:gfx1100 and grep for image_bvh_intersect_ray. If it is absent on your Mojo version, the claim fails for that version. The harder claim, that the instruction works from Mojo, is deliberately not made here.

Provenance: Mojo 1.1.0 (8189361e), target gfx1100, RDNA3 ISA Reference Guide (15 August 2023) section 10.9. The probe is kinestride/rt_bvh_probe.mojo, also committed as agent/body_engine/rt_bvh_probe.mojo in fly-connectome (commit 8520947). Compile only; no GPU run.

Comentarios

Aún no hay comentarios.

Iniciar sesión para comentar.