iridia-exec
A bounded CPU reference executor for iridia::Program: it runs a lifted shader on explicit inputs, whatever bytecode it was lifted from, and returns exactly what it wrote. It is for checking a translation (against values worked out by hand, or against Microsoft's WARP rasterizer), not for rendering: it is deterministic and slow.
Part of iridia, which turns compiled Direct3D shaders into naga modules. This crate depends on iridia; the readers (iridia-dxbc, iridia-dxil, iridia-d3d9, iridia-mojo) do not depend on it.
What it runs
- Entry-point programs (pixel, vertex, compute, mesh).
Executable::newchecks a program and compiles it once into a flat form (typed operations on four-lane word registers, pointers folded to word offsets, dead operations dropped). ARunnerexecutes one invocation, a 2 x 2 quad, or several invocations in lockstep (masked where they diverge, each with its own steps and budget) against aBackend, which answers what is outside the program: texture samples, loads, queries, unordered-access effects.ProgramShaderputs a vertex or pixel program behind thePixelShadertrait, in the register vocabulary of the D3D bytecode it came from: inputs and outputs by signature register, constant buffers and samplers by bind slot. - Stages WGSL has no entry point for. Their programs lift as functions, which
Executable::functionscompiles:geometry(the input assembler's topologies and a geometry program's strips),tessellationandtessellator(hull and domain programs over a port of Microsoft's reference fixed-function tessellator),mesh(a thread group's meshlet),PipelinewithScene(a ray tracing pipeline:DispatchRays,TraceRay, hit groups, miss and callable programs, over acceleration structures the caller builds; inline ray queries too). draw: a small software rasterizer for an indexed triangle list (vertex program, clipping, viewport, rasterization, pixel program, depth test, render targets), for re-running a recorded draw on its programs and setting its pixels against the game's.evaluate: a pixel shader over a grid of UVs, to bake a procedural material to textures. The caller states everything the program reads (the constant layout and values, a texture per sampled slot, a value per used input) and which outputs it wants; the evaluator has no policy of its own and refuses what it is not given.evaluate_periodicbakes to a texture that tiles.probe: runs one vertex of a vertex program with chosen constants to see what it does with them (what a matrix or a table is).textureandviews: a 2D texture as the executor samples it (levels, colour transfer, the sampler state's addressing and bias, the filtering the caller chooses) and resource views bound by slot.
Example
Lift a pixel shader and run it once. Its constant buffer holds f = (0.25, 2, 4, 8) and u = (7, 2, 0, 0); the HLSL computes min(f.x, f.y) and max(f.x, f.y) into target 0 and u.x / u.y and u.x % u.y into target 1:
Guarantees and limits
- What a run does follows the program's
D3dModes: a program that says its body spells out D3D's integer division, conversions andmin/maxruns as written, and one that expects denormals flushed to zero has every 32-bit float operation flush its operands and result (sign kept). Anything the program does that its module cannot make valid (an index past the end of a local array, an integer division by zero in the module) is an error of the run, never a silent value. Errors name their bytecode and location. - Every run has a step budget (
STEP_LIMIT). - Sampling belongs to the caller: a scalar execution cannot infer derivatives, filtering or addressing, so a
Samplercallback answers texture samples and the caller states the footprint, addressing, filtering and transfer. A quad run supplies the derivatives of its four invocations. - A construct the executor does not run is refused by
Executable::newwith its name. A ray tracing program is not an entry point; it runs inside aPipeline. - The tessellator is a port of the reference tessellator Microsoft published with the Direct3D 11.3 functional specification (MIT licence; the notice is kept in
src/tessellator.rs). Its output is held against that reference's recorded output intests/fixtures/tessellator_reference.txt.
Tests
cargo test -p iridia-exec needs no game files: it runs fixtures through both iridia-dxbc and iridia-dxil (development dependencies, a cycle cargo allows), holding the interface read from either to the values worked out from the HLSL (tests/interface.rs), the evaluator to outputs worked out by hand (tests/evaluate.rs), lockstep lanes against one pixel after another with diverging control flow (tests/lanes.rs), and the stage drivers to hand-worked meshes, strips, patches and scenes. The comparisons against WARP live in iridia-dxbc and iridia-dxil (tests/exec_warp*.rs).
Licence
GPL-3.0-or-later, apart from the tessellator port noted above.