glam 是一个简单快速的线性代数库,专为游戏和图形编程设计。
特性
f32类型- 向量: [
Vec2], [Vec3], [Vec3A] 和 [Vec4] - 方阵: [
Mat2], [Mat3], [Mat3A] 和 [Mat4] - 四元数: [
Quat] - 仿射变换类型: [
Affine2], [Affine3] 和 [Affine3A]
- 向量: [
f64类型- 向量: [
DVec2], [DVec3] 和 [DVec4] - 方阵: [
DMat2], [DMat3] 和 [DMat4] - 四元数: [
DQuat] - 仿射变换类型: [
DAffine2] 和 [DAffine3]
- 向量: [
i8类型- 向量: [
I8Vec2], [I8Vec3] 和 [I8Vec4]
- 向量: [
u8类型- 向量: [
U8Vec2], [U8Vec3] 和 [U8Vec4]
- 向量: [
i16类型- 向量: [
I16Vec2], [I16Vec3] 和 [I16Vec4]
- 向量: [
u16类型- 向量: [
U16Vec2], [U16Vec3] 和 [U16Vec4]
- 向量: [
i32类型- 向量: [
IVec2], [IVec3] 和 [IVec4]
- 向量: [
u32类型- 向量: [
UVec2], [UVec3] 和 [UVec4]
- 向量: [
i64类型- 向量: [
I64Vec2], [I64Vec3] 和 [I64Vec4]
- 向量: [
u64类型- 向量: [
U64Vec2], [U64Vec3] 和 [U64Vec4]
- 向量: [
isize类型- 向量: [
ISizeVec2], [ISizeVec3] 和 [ISizeVec4]
- 向量: [
usize类型- 向量: [
USizeVec2], [USizeVec3] 和 [USizeVec4]
- 向量: [
bool类型- 向量: [
BVec2], [BVec3] 和 [BVec4]
- 向量: [
SIMD 支持
glam 在设计上充分利用 SIMD 指令。许多 f32 类型使用 128 位 SIMD 向量类型进行存储和实现。使用 SIMD 通常能获得比使用基本数值类型(如 f32)更好的性能。
部分 glam 类型使用 SIMD 存储,意味着它们按 16 字节对齐,包括 Mat2、Mat3A、Mat4、Quat、Vec3A、Vec4、Affine2 和 Affine3A。带 A 后缀的类型是标量类型的 SIMD 优化版本,例如 Vec3 使用 f32 存储,而 Vec3A 使用 SIMD 存储。
当目标平台不支持 SIMD 时,这些类型仍保持 16 字节对齐和内部填充,以确保不同架构之间的对象大小和布局一致。当 SIMD 不可用时,存在标量数学回退实现。计划在未来稳定版 Rust 中出现其他 SIMD 架构时增加支持。
目前仅支持 x86/x86_64 平台的 SSE2、Aarch64 平台的 NEON 以及 WASM 平台的 simd128。
Vec3A 和 Mat3A
Vec3A 是 Vec3 类型的 SIMD 优化版本,由于需要 16 字节对齐,Vec3A 包含 4 字节填充,总大小为 16 字节。Mat3A 由三个 Vec3A 列组成。
| 类型 | f32 字节数 |
对齐字节数 | 总大小 | 填充字节 |
|---|---|---|---|---|
[Vec3] |
12 | 4 | 12 | 0 |
[Vec3A] |
12 | 16 | 16 | 4 |
[Mat3] |
36 | 4 | 36 | 0 |
[Mat3A] |
36 | 16 | 48 | 12 |
尽管存在空间浪费,SIMD 实现在 mathbench 基准测试中通常优于 f32 实现。
glam 将 [Vec3] 视为默认的 3D 向量类型,而 [Vec3A] 是用于优化的特殊情况。当方法需要返回 3D 向量时,通常返回 [Vec3]。
提供了从 [Vec4] 转换到 [Vec3A] 以及 [Vec3] 和 [Vec3A] 之间相互转换的 [From] trait 实现。
rust
use glam::{Vec3, Vec3A, Vec4};
let v4 = Vec4::new(1.0, 2.0, 3.0, 4.0);
// 从 `Vec4` 转换到 `Vec3A`,如果支持 SIMD,这是无操作转换
// 使用显式方法而不是 From 实现,因为转换过程中会丢失数据
let v3a = Vec3A::from_vec4(v4);
assert_eq!(Vec3A::new(1.0, 2.0, 3.0), v3a);
// 从 `Vec3A` 转换到 `Vec3`
let v3 = Vec3::from(v3a);
assert_eq!(Vec3::new(1.0, 2.0, 3.0), v3);
// 从 `Vec3` 转换到 `Vec3A`
let v3a = Vec3A::from(v3);
assert_eq!(Vec3A::new(1.0, 2.0, 3.0), v3a);
Affine2, Affine3 和 Affine3A
Affine2、Affine3 和 Affine3A 由线性变换矩阵和平移向量组成。它们表示游戏开发中常用的 2D 和 3D 仿射变换。
Affine3 由 Vec3 和 Mat3 组成,而 Affine3A 由 Mat3A 和 Vec3A 组成。Affine3A 通常性能更好,但按 16 字节对齐,大小为 64 字节,而 Affine3 大小为 48 字节。
下表显示 Affine2 相对于 Mat3A 以及 Mat3A 相对于 Mat3 的性能优势。
| 操作 | Mat3 |
Mat3A |
Affine2 |
|---|---|---|---|
| 求逆 | 11.4±0.09ns | 7.1±0.09ns | 5.4±0.06ns |
| 自乘 | 10.5±0.04ns | 5.2±0.05ns | 4.0±0.05ns |
| 变换点 (point2) | 2.7±0.02ns | 2.7±0.03ns | 2.8±0.04ns |
| 变换向量 (vector2) | 2.6±0.01ns | 2.6±0.03ns | 2.3±0.02ns |
Mat4 和 Affine3A 之间的性能更为接近,仿射类型在求逆操作上更快。
| 操作 | Mat4 |
Affine3A |
|---|---|---|
| 求逆 | 15.9±0.11ns | 10.8±0.06ns |
| 自乘 | 7.3±0.05ns | 7.0±0.06ns |
| 变换点 (point3) | 3.6±0.02ns | 4.3±0.04ns |
| 变换点 (point3a) | 3.0±0.02ns | 3.0±0.04ns |
| 变换向量 (vector3) | 4.1±0.02ns | 3.9±0.04ns |
| 变换向量 (vector3a) | 2.8±0.02ns | 2.8±0.02ns |
基准测试在 Intel Core i7-4710HQ 处理器上进行。
线性代数约定
glam 将向量解释为列矩阵(也称为列向量),意味着用矩阵变换向量时,矩阵放在左侧。
rust
use glam::{Mat3, Vec3};
let m = Mat3::IDENTITY;
let x = Vec3::X;
let v = m * x;
assert_eq!(v, x);
矩阵在内存中以列主序存储。
所有角度使用弧度。Rust 提供了 f32::to_radians() 和 f64::to_radians() 方法用于从角度转换。
直接元素访问
由于某些类型内部可能使用 SIMD 类型实现,通过实现 Deref 和 DerefMut trait 支持直接访问向量元素。
rust
use glam::Vec3A;
let mut v = Vec3A::new(1.0, 2.0, 3.0);
assert_eq!(3.0, v.z);
v.z += 1.0;
assert_eq!(4.0, v.z);
glam 断言
glam 在运行时不对方法参数进行有效性检查。例如需要归一化向量的方法(如 Quat::from_axis_angle(axis, angle))不会检查轴向量是否有效归一化。为帮助捕获 glam 的意外误用,可以启用 debug-glam-assert 或 glam-assert 特性,添加参数有效性检查。
向量重组
glam 向量类型提供函数,用于重新排序向量元素,包括从向量元素创建不同大小的向量。
重组函数通过 trait 实现,为每个向量类型添加这些功能。这主要是由于重组函数众多,可能会掩盖文档中其他向量函数。这些 trait 是 [Vec2Swizzles]、[Vec3Swizzles] 和 [Vec4Swizzles]。
注意,[Vec3A] 的 [Vec3Swizzles] 实现对于 3 元素重组将返回 [Vec3A],所有其他实现将返回 [Vec3]。
rust
use glam::{swizzles::*, Vec2, Vec3, Vec3A, Vec4};
let v = Vec4::new(1.0, 2.0, 3.0, 4.0);
// 反转 `v` 的元素,如果支持 SIMD,将使用向量混洗指令
let wzyx = v.wzyx();
assert_eq!(Vec4::new(4.0, 3.0, 2.0, 1.0), wzyx);
// 将 `v` 的 yzw 元素重组为 `Vec3`
let yzw = v.yzw();
assert_eq!(Vec3::new(2.0, 3.0, 4.0), yzw);
// 将 `Vec4` 重组为 `Vec3A`,先重组 `Vec4` 然后转换为 `Vec3A`
// 如果支持 SIMD,将使用向量混洗指令。混洗后的 `Vec4` 的最后一个元素将被 `Vec3A` 忽略
let yzw = Vec3A::from_vec4(v.yzwx());
assert_eq!(Vec3A::new(2.0, 3.0, 4.0), yzw);
// 可以从 `Vec4` 重组为 `Vec2`
let xy = v.xy();
assert_eq!(Vec2::new(1.0, 2.0), xy);
// 也可以重组回来
let yyxx = xy.yyxx();
assert_eq!(Vec4::new(2.0, 2.0, 1.0, 1.0), yyxx);
SIMD 和标量一致性
glam 类型实现了 serde 的 Serialize 和 Deserialize trait,确保无论是否启用 SIMD 支持,序列化和反序列化的结果完全相同。
SIMD 版本实现了 core::fmt::Debug 和 core::fmt::Display trait,使其输出与标量版本相同。
rust
use glam::Vec4;
let a = Vec4::new(1.0, 2.0, 3.0, 4.0);
assert_eq!(format!("{}", a), "[1, 2, 3, 4]");
可选特性
所有 glam 依赖都是可选的,但部分用于测试和基准测试。
approx- 近似浮点数比较的 trait 和宏arbitrary- 所有glam类型的Arbitrarytrait 实现bytemuck- 转换为字节切片encase-encasetrait 实现libm- 使用libm数学函数替代stdmint- 与其他 3D 数学库互操作rand- 所有glam类型的Distributiontrait 实现rkyv- 所有glam类型的Archive、Serialize和Deserialize实现。注意,启用和未启用scalar-math特性时的序列化不互操作。但在glam的其他构建版本之间应正常工作。当前不支持字节序转换bytecheck- 使用rkyv特性时执行存档验证serde- 所有glam类型的Serialize和Deserialize实现。注意,启用和未启用 SIMD 的构建版本之间序列化应正常工作speedy- 所有glam类型的speedy的Readable和Writable实现zerocopy- 安全内存转换的 zerocopy trait 实现
特性开关
std- 默认特性,无依赖nostd-libm- 当std不可用时使用libm数学函数scalar-math- 禁用 SIMD 支持,所有类型使用原生对齐debug-glam-assert- 在调试版本中添加断言,检查传入glam的参数有效性,帮助捕获运行时错误glam-assert- 在所有版本中添加断言,检查传入glam的参数有效性,帮助捕获运行时错误cuda- 强制glam类型匹配预期的 cuda 对齐方式fast-math- 默认情况下,glam 尝试在所有平台上提供逐位相同的结果。启用此特性将启用平台特定优化,可能与其他平台的结果不同。中间库不应使用此特性,而应将决策留给最终二进制构建core-simd- 通过可移植 simd 模块启用 SIMD 支持。这是一个不稳定特性,需要 nightly Rust 工具链和std支持
最低支持的 Rust 版本 (MSRV)
最低支持的 Rust 版本是 1.68.2。
模块组织
glam 的代码采用模块化组织,根模块 (lib.rs) 定义了以下结构:
Crate 级别属性
rust
#![doc(html_root_url = "https://docs.rs/glam/0.32.1")]
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(target_arch = "spirv", feature(repr_simd))]
#![cfg_attr(target_arch = "wasm64", feature(simd_wasm64))]
#![deny(
rust_2018_compatibility,
rust_2018_idioms,
future_incompatible,
nonstandard_style
)]
#![allow(clippy::wrong_self_convention)]
#![cfg_attr(
all(feature = "core-simd", not(feature = "scalar-math")),
feature(portable_simd)
)]
编译时检查
rust
#[cfg(all(
not(feature = "std"),
not(feature = "libm"),
not(feature = "nostd-libm")
))]
compile_error!(
"You must specify a math backend. Consider enabling either `std`, `libm`, or `nostd-libm`."
);
内部模块
rust
#[macro_use]
mod macros;
mod align16;
mod deref;
mod euler;
mod features;
平台特定 SIMD 实现
rust
#[cfg(all(target_arch = "aarch64", not(any(feature = "core-simd", feature = "scalar-math"))))]
mod neon;
#[cfg(target_arch = "spirv")]
mod spirv;
#[cfg(all(target_feature = "sse2", not(any(feature = "core-simd", feature = "scalar-math"))))]
mod sse2;
#[cfg(all(target_feature = "simd128", not(any(feature = "core-simd", feature = "scalar-math"))))]
mod wasm;
#[cfg(all(feature = "core-simd", not(feature = "scalar-math")))]
mod coresimd;
公共类型模块
rust
pub mod bool;
pub use self::bool::*;
pub mod f32;
pub use self::f32::*;
pub mod f64;
pub use self::f64::*;
// ... 整数类型模块 ...
工具模块
rust
pub mod swizzles;
pub use self::swizzles::{Vec2Swizzles, Vec3Swizzles, Vec4Swizzles};
pub use euler::EulerRot;
mod float;
pub use float::FloatExt;
总结
glam 是一个高性能、类型丰富的线性代数库,主要特点包括:
- 完整的数学类型体系:支持多种精度的向量、矩阵、四元数和仿射变换
- SIMD 优化:充分利用现代 CPU 的 SIMD 指令集提升性能
- 灵活的编译时配置:通过特性开关适应不同场景(no_std、调试断言、序列化等)
- 友好的 API 设计:支持直接元素访问、向量重组等便捷功能
- 跨平台一致性:确保不同平台间的行为一致
- 模块化组织:代码结构清晰,易于维护和扩展
该库特别适合游戏开发、计算机图形学和需要高性能数学计算的 Rust 项目。