【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 项目。

相关推荐
大黄说说2 小时前
Rust 入门到实战:构建安全、高性能的下一代系统
开发语言·安全·rust
好家伙VCC2 小时前
# 发散创新:用 Rust构建高并发虚拟世界引擎核心模块在当今游戏开发与元宇宙构建中,**虚拟世界的性能瓶颈往往不是图形渲染,而是底
java·开发语言·python·rust·图形渲染
Mr -老鬼2 小时前
前后端联调避坑!Vue优先IPv6导致接口不通,Rust Salvo这样解决
前端·vue.js·rust
Source.Liu3 小时前
【glam】断言宏解析
rust·glam
咚为3 小时前
告别 lazy_static:深度解析 Rust OnceCell 的前世今生与实战
开发语言·后端·rust
Java水解19 小时前
Rust异步编程实战:构建高性能网络应用
后端·rust
土豆125019 小时前
Rust 实战:手把手教你开发一个命令行工具
前端·rust
勇敢牛牛_19 小时前
【aiway】基于 Rust 开发的 API + AI 网关
开发语言·后端·网关·ai·rust
Source.Liu1 天前
【Iced】`lib.rs` 源码分析 - `iced_core` 核心库
rust·iced