【glam】线性代数库 lib.rs 文件解析

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 字节对齐,包括 Mat2Mat3AMat4QuatVec3AVec4Affine2Affine3A。带 A 后缀的类型是标量类型的 SIMD 优化版本,例如 Vec3 使用 f32 存储,而 Vec3A 使用 SIMD 存储。

当目标平台不支持 SIMD 时,这些类型仍保持 16 字节对齐和内部填充,以确保不同架构之间的对象大小和布局一致。当 SIMD 不可用时,存在标量数学回退实现。计划在未来稳定版 Rust 中出现其他 SIMD 架构时增加支持。

目前仅支持 x86/x86_64 平台的 SSE2、Aarch64 平台的 NEON 以及 WASM 平台的 simd128。

Vec3A 和 Mat3A

Vec3AVec3 类型的 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

Affine2Affine3Affine3A 由线性变换矩阵和平移向量组成。它们表示游戏开发中常用的 2D 和 3D 仿射变换。

Affine3Vec3Mat3 组成,而 Affine3AMat3AVec3A 组成。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

Mat4Affine3A 之间的性能更为接近,仿射类型在求逆操作上更快。

操作 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 类型实现,通过实现 DerefDerefMut 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-assertglam-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 类型实现了 serdeSerializeDeserialize trait,确保无论是否启用 SIMD 支持,序列化和反序列化的结果完全相同。

SIMD 版本实现了 core::fmt::Debugcore::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 类型的 Arbitrary trait 实现
  • bytemuck - 转换为字节切片
  • encase - encase trait 实现
  • libm - 使用 libm 数学函数替代 std
  • mint - 与其他 3D 数学库互操作
  • rand - 所有 glam 类型的 Distribution trait 实现
  • rkyv - 所有 glam 类型的 ArchiveSerializeDeserialize 实现。注意,启用和未启用 scalar-math 特性时的序列化不互操作。但在 glam 的其他构建版本之间应正常工作。当前不支持字节序转换
  • bytecheck - 使用 rkyv 特性时执行存档验证
  • serde - 所有 glam 类型的 SerializeDeserialize 实现。注意,启用和未启用 SIMD 的构建版本之间序列化应正常工作
  • speedy - 所有 glam 类型的 speedyReadableWritable 实现
  • 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 是一个高性能、类型丰富的线性代数库,主要特点包括:

  1. 完整的数学类型体系:支持多种精度的向量、矩阵、四元数和仿射变换
  2. SIMD 优化:充分利用现代 CPU 的 SIMD 指令集提升性能
  3. 灵活的编译时配置:通过特性开关适应不同场景(no_std、调试断言、序列化等)
  4. 友好的 API 设计:支持直接元素访问、向量重组等便捷功能
  5. 跨平台一致性:确保不同平台间的行为一致
  6. 模块化组织:代码结构清晰,易于维护和扩展

该库特别适合游戏开发、计算机图形学和需要高性能数学计算的 Rust 项目。

相关推荐
星栈独行1 天前
Makepad 应用如何读文件、调接口、保存数据
前端·程序人生·ui·rust·github
guyoung1 天前
BoxAgnts 工具系统(7)——Skill 模板、Agent 代理与 Cron 调度
rust·agent·ai编程
分布式存储与RustFS1 天前
基于Rust的国产开源对象存储RustFS:S3 Table对Iceberg数据湖的适配详解
rust·开源·iceberg·对象存储·rustfs·minio平替·s3 table
Jinkxs2 天前
Rust 性能优化全流程:从 flamegraph 定位瓶颈到 unsafe 与 SIMD 加速,响应快 2 倍
开发语言·性能优化·rust
星栈独行2 天前
Rust + Makepad 应用怎么打包发布:Windows、macOS、Linux 全平台交付
windows·程序人生·macos·ui·rust
fox_lht2 天前
15.3.改进我们之前的输入、输出项目
开发语言·后端·学习·rust
guyoung2 天前
BoxAgnts 工具系统(6)——多 Provider 适配与 Agent 查询循环
rust·agent·ai编程
星栈2 天前
Rust + Makepad 应用怎么打包发布:Windows、macOS、Linux 全平台交付
前端·rust
MageGojo2 天前
R-Shell开源项目实战解析:用Rust打造命令行SSH工具,支持连接管理、远程执行、SFTP与MCP
运维·rust·开源项目·命令行工具·ssh客户端·mcp
techdashen3 天前
Cargo 1.94 开发周期全解析
开发语言·后端·rust