3.7 KiB
Architecture
This document describes the high-level architecture of glam. While glam is
not a large library there are some complexities to its implementation. The
rational and explanation of these follows.
Design goals
There overarching design goals of glam are:
- Good out of the box performance using SIMD when available
- Has a simple public interface
- Is fast to compile
- Follow Rust standard library conventions and API guidelines where possible
- High quality rustdoc generated document
SIMD
One of the core premises of glam was that using SSE2 instructions on x86 and
x86_64 architectures gave better performance than using Rust's built in f32
type. For more on this finding see Optimising path tracing with SIMD.
I also wanted to have a f32 fallback when SIMD was not available.
No generics
Because internally storage could be a SIMD vector intrinsic like __m128 on
x86 or say an array of f32 if SSE2 was not available, a simple generic
parameter like Vec4<T> could not be used. The T would specify the public
facing type, but not storage. Perhaps this could be achieved with a second
generic parameter for storage, e.g. Vec4<f32, __m128> or Vec4<f32, [f32; 4]>
but I felt that such a design would introduce a lot of complexity that end users
would ultimately be burdened with, so it's not something that was pursued.
Generics can also increase compile time and code size which is something glam wants to avoid.
No traits
glam also mostly avoids using traits in the public interface. Primarily
because there wasn't a good reason to. A Vec3 is not an interface, it is a
concrete type. The secondary reason is traits fragment documentation. If the
functionality of a Vec3 is implemented across a number of different traits
then the documentation of all of the Vec3 methods will be on the individual
traits, not the Vec3 itself. This makes it harder for users to find what
methods a struct actually implements as the documentation is not in one place.
Conversely glam does use traits for swizzle methods so that the documentation
for these methods is on the trait and not the Vec2, Vec3, Vec4 and so on
structs. There are many swizzle methods which would clutter the documentation,
making them a trait means they won't pollute documentation.
Support common primitives
Initially glam only supported f32 which kept the internal implementation
relatively simple. However users also wanted support for other primitives types
like f64, i32 and u32. Because glam avoids using generics adding
support for other primitive types without a lot of code duplication required
some additional complexity in implementation.
High level structure
glam supports a number of permutations of vector, quaternion and matrix types
for f32, f64, i32 and u32 primitives, with SSE2 or wasm32 for some f32
types and scalar fallbacks if SIMD is not available.
Component access via Deref
The Deref trait is used to provide direct access to SIMD vector components
like .x, .y and so on. The Deref implementation will return XYZ<T>
structure on which the vector components are accessible. Unfortunately if users
dereference the public types they will see confusing errors messages about
XYZ types but this on balance seemed preferable to needing to setter and
getting methods to read and write component values.
Code generation
See [codegen/README.md] for information on glam's code generation process.