Geomancy
Linear is nice, but slow. Those are naughty, but a bit faster.
- All data types are monomorphic, unpacked and specialized.
Mat4 and Vec4 are ByteArray#.
Mat4xMat4 and Mat4xVec4 is done with SIMD.
The Numbers
Storing a list of transformations (e.g. rendering instance data):
poke
10
geomancy: OK
42.3 ns ± 1.4 ns, 0.47x
linear: OK
90.5 ns ± 3.1 ns
linear/T: OK
81.6 ns ± 2.7 ns
...
1000
geomancy: OK
2.85 μs ± 37 ns, 0.36x
linear: OK
7.93 μs ± 132 ns
linear/T: OK
7.08 μs ± 348 ns
Folding down a gloss-style scene tree is where it is all started:
multiply
2
geomancy: OK
12.3 ns ± 164 ps, 0.34x
linear: OK
36.1 ns ± 1.3 ns
...
1000
geomancy: OK
7.71 μs ± 93 ns, 0.23x
linear: OK
34.0 μs ± 1.3 μs
Add that time to the poking that’ll follow.
Sure, it is in the lower microseconds range, but this budget can be used elsewhere.
Benchmarking and comparing
stack bench --ba '--csv baseline.csv --stdev 2 --timeout 120'
stack bench --ba '--baseline baseline.csv --fail-if-slower 5'
Conventions
Matrix layout
Transforms produced, composed, and applied to mimic the GLSL order (col-major):
vec4 vPosOut = P * V * M * vPosIn;
vPosOut = (p <> v <> m) !* vPosIn
This way you don’t have to transpose your transforms or fiddle with layout annotations.
Projections / Views
Geomancy.Vulkan.Projection is using the “reverse-depth” trick that remaps the vulkan default [0; 1] range to [1; 0].
This grants extra precision with one less parameter to specify (you only need “near” now), but makes handedness reasoning tricky.
The default depth range the coordinate is left-handed (+X right, +Y down, +Z forward).
But after reversing the depth it has to be paired with a right-handed view function like Geomancy.Vulkan.View.lookAtRH.
The intended up vector is still vec3 0 (-1) 0 – +Y down.
Silly as it sounds, this matches the XY plane of the window with XY plane in front of a “first person” camera.
Rotations
Axis rotations (using rotateQ) will appear clockwise when looking along the axis.
Angle rotations follow Tait-Bryan angles (heading/elevation/bank or yaw/pitch/roll) in the y-x-z frame.
rotateZ (time * rate) will follow the clock hands in 2D scenes and roll in 3D.
rotateX will follow the sun from sunrise to sunset, increasing elevation / pitching UP.
rotateY will turn you right, increasing yaw/heading eastwards.
Using Geomancy.Quaternion.intrinsic roll pitch yaw will make a rotation from the 3 angles in one go.
You can use it to rotate a point directly (e.g vec3 0 0 1 to get a direction vector from Quaternion) or commit to a matrix using Transform.rotateQ.

You’re of course free to define your own transforms, just copy the modules and tune to your liking.
Just make sure that you use matching row/column constructors and the math layer will do the rest, fast.
GLSL-like functions
To further facilitate conversion between the host and shader code Geomancy.Gl.Funs provides common functions like glFract and smoothstep.