13次发布
0.3.0 | 2024年6月26日 |
---|---|
0.2.0 | 2024年1月23日 |
0.1.9 | 2019年11月3日 |
0.1.8 | 2019年7月14日 |
0.1.1 | 2018年10月19日 |
#1 in 渲染引擎
4,617 每月下载量
用于 14 个crate(9个直接使用)
360KB
7K SLoC
meshopt
此crate为优秀的meshoptimizer C/C++库提供了FFI层和idiomatic Rust包装器。
目的
当GPU渲染三角形网格时,GPU管道的各个阶段都需要处理顶点和索引数据。这些阶段的效率取决于您提供给它们的数据;此库提供算法来帮助优化这些阶段,以及减少网格复杂性和存储开销的算法。
用法
将其添加到您的 Cargo.toml
[dependencies]
meshopt = "0.3.0"
示例
demo
这是一个单一代码库的demo
示例,运行了几乎整个功能矩阵。在demo
中,opt_complete
例程是实现100% GPU性能的方法。可以通过各种打包和编码例程进一步选择CPU改进。
cargo run --release --example demo
encoder
encoder
示例显示了在典型的游戏引擎管道中进行网格优化的最小调用,并将网格序列化为与WebAssembly加载器和meshoptimizer
仓库中存在的THREE.js查看器实验兼容的格式。
- https://github.com/zeux/meshoptimizer/blob/master/tools/OptMeshLoader.js
- https://github.com/zeux/meshoptimizer/blob/master/demo/index.html
cargo run --release --example encoder -- --input pirate.obj --output pirate.optmesh
管道
在优化网格时,通常应通过一系列优化(顺序很重要!)将其传递
- 索引
- 顶点缓存优化
- 过度绘制优化
- 顶点获取优化
- 顶点量化
- (可选) 顶点/索引缓冲区压缩
索引
本库中的大多数算法都假设网格具有顶点缓冲区和索引缓冲区。为了使算法运行良好,以及使GPU高效渲染网格,顶点缓冲区必须没有冗余顶点;您可以从未索引的顶点缓冲区生成索引缓冲区,或使用generate_vertex_remap
重新索引现有的(可能冗余的)索引缓冲区。
生成重映射表后,您可以使用remap_index_buffer
和remap_vertex_buffer
执行重映射。
然后,您可以通过对这些缓冲区调用其他函数来进一步优化结果缓冲区。
顶点缓存优化
当GPU渲染网格时,它必须为每个顶点运行顶点着色器;通常,GPU具有一个内置的固定大小缓存,用于存储变换后的顶点(运行顶点着色器的结果),并使用此缓存来减少顶点着色器调用的次数。此缓存通常很小,16-32个顶点,并且可以有不同的替换策略;为了有效地使用此缓存,您必须重新排序您的三角形以最大化重复使用顶点引用的局部性;此重新排序可以使用optimize_vertex_cache
完成。
过度绘制优化
在变换顶点后,GPU将三角形发送到光栅化,从而生成通常首先通过深度测试的像素,并且通过测试的像素将执行像素着色器以生成最终颜色。随着像素着色器的成本越来越高,减少过度绘制变得越来越重要。虽然在一般情况下,提高过度绘制需要视图相关的操作,但此库提供了一个算法来重新排序三角形以最小化来自所有方向的过度绘制,您应该在顶点缓存优化后运行它;此例程的代码是optimize_overdraw
。
在执行过度绘制优化时,您必须指定一个浮点阈值参数。算法试图在顶点缓存效率和过度绘制之间保持平衡;阈值决定了算法可以妥协顶点缓存命中率的程度,1.05表示优化后的比率最多比优化前差5%。
顶点获取优化
在最终三角形顺序确定后,我们仍然可以优化顶点缓冲区以提高内存效率。在运行顶点着色器之前,GPU必须从顶点缓冲区获取顶点属性;获取通常由内存缓存支持,因此优化数据以符合内存访问的局部性很重要。您可以运行以下代码来完成此操作
为了优化索引/顶点缓冲区以获得顶点获取效率,请调用optimize_vertex_fetch
。
这将重新排序顶点缓冲区中的顶点以尝试提高引用的局部性,并将索引就地重写以匹配;如果顶点数据使用多个流存储,则应使用optimize_vertex_fetch_remap
。此优化必须在最终的索引缓冲区上执行,因为最优顶点顺序取决于三角形顺序。
请注意,算法并不试图精确地模拟缓存替换,而是按使用顺序对顶点进行排序,这通常会产生接近最优的结果。
顶点量化
为了在获取顶点数据时进一步优化内存带宽,并减少存储网格所需的内存量,将顶点属性量化为更小的类型通常是有益的。虽然此优化可以在管道的任何部分运行(有时将量化作为第一步可以提高索引,因为几乎相同的顶点会合并),但通常在所有其他优化之后运行此操作更容易,因为其中一些需要访问float3位置。
量化通常具有领域特定性;通常使用3个8位整数来量化法线,但您可以使用更高精度的量化(例如,使用10_10_10_2格式中的每个组件10位),或不同的编码只使用2个组件。对于位置和纹理坐标数据,最常用的两种存储格式是半精度浮点数和编码位置相对于网格的AABB或UV边界矩形的16位归一化整数。
这里的可能组合数量非常大,但这个库确实提供了构建块,特别是将浮点值量化为归一化整数以及半精度浮点数的函数。
相关例程
quantize_unorm
quantize_snorm
quantize_half
quantize_float
顶点/索引缓冲区压缩
在上述所有优化之后,几何数据对GPU来说是最优的——然而,您不必按原样存储数据。如果存储大小或传输带宽很重要,您可能想压缩顶点和索引数据。虽然有一些网格压缩库,如Google Draco,可用,但它们通常是为了在牺牲顶点/索引顺序(这会使网格在GPU上的渲染效率低下)或解压缩性能的情况下,最大化压缩比。此外,它们通常不支持自定义游戏就绪量化顶点格式,因此需要在加载后重新量化数据,引入额外的量化误差,并使解码速度变慢。
或者,您可以使用通用压缩库,如zstd或Oodle来压缩顶点/索引数据——然而,这些压缩器不是为了利用顶点/索引数据中的冗余而设计的,因此压缩率可能不尽人意。
为此,这个库提供了一种“编码”顶点和索引数据的算法。编码的结果通常比原始数据小得多,并且仍然可以用通用压缩器压缩——因此,您可以直接存储编码数据(对于适度的压缩比和最大的解码性能),或者进一步用zstd等压缩,以最大化压缩率。
要编码,可以使用encode_vertex_buffer
和encode_index_buffer
例程。编码的数据可以直接序列化,或者进一步压缩。在运行时可以使用decode_vertex_buffer
和decode_index_buffer
例程进行解码。
请注意,顶点编码假设顶点缓冲区已针对顶点提取进行了优化,并且顶点已量化;索引编码假设顶点/索引缓冲区已针对顶点缓存和顶点提取进行了优化。将未优化的数据输入到编码器中会产生较差的压缩率。这两个编解码器都是无损的——唯一的损失性步骤是在编码之前发生的量化。
解码函数经过了高度优化,可以直接针对写入组合内存;您有理由相信,在现代台式CPU上,这两个解码器都可以以1-2 GB/s的速度运行。压缩率取决于数据;顶点数据压缩率通常在2-4倍(与已量化数据相比),索引数据压缩率在5-6倍(与原始16位索引数据相比)。通用无损压缩器可以进一步提高这些结果。
三角形带转换
在大多数硬件上,索引三角形列表是驱动GPU的最有效方式。然而,在某些情况下,三角形带可能更有益。
- 在一些较旧的GPU上,绘制三角形带可能更有效率。
- 在极端内存受限的系统上,三角形带的索引缓冲区可以节省一些内存。
这个库提供了stripify
例程,用于将顶点缓存优化的三角形列表转换为三角形带;可以使用unstripify
例程执行相反操作。
通常,您应该预计三角形条带比三角形列表具有大约50-60%的索引数量(每个三角形约1.5-1.8个索引)并且具有大约5%更差的ACMR。请注意,三角形条带需要重启索引支持才能进行渲染;使用退化三角形连接条带是不受支持的。
效率分析器
虽然获取精确性能数据的唯一方法是在目标GPU上测量性能,但以GPU无关的方式测量这些优化的影响可能很有价值。为此,该库为所有三个主要优化例程提供了分析器。对于每个优化都有一个相应的分析函数,例如 analyze_overdraw
,它返回一个包含统计信息的结构。
analyze_vertex_cache
返回顶点缓存统计信息。常用的指标是ACMR - 平均缓存缺失率,它是顶点调用总数与三角形数的比率。最坏情况的ACMR是3(GPU必须为每个三角形处理3个顶点);在常规网格上,最优的ACMR接近0.5。在真实网格中,它通常在[0.5..1.5]范围内,具体取决于顶点分割的数量。另一个有用的指标是ATVR - 平均转换顶点率 - 它表示顶点着色器调用与总顶点数的比率,并且不管网格拓扑如何,最佳情况都是1.0(每个顶点转换一次)。
analyze_vertex_fetch
返回顶点提取统计信息。它使用的主要指标是过度提取 - 从顶点缓冲区读取的字节数与顶点缓冲区总字节数之间的比率。假设非冗余顶点缓冲区,最佳情况是1.0 - 每个字节只提取一次。
analyze_overdraw
返回过度绘制统计信息。它使用的主要指标是过度绘制 - 从多个正交摄像机测量的像素着色器调用数与覆盖像素总数的比率。过度绘制的最佳情况是1.0 - 每个像素只着色一次。
请注意,所有分析器都使用有关相关GPU单元的近似模型,因此您将获得的数字只是实际性能的大致近似。
许可证
根据您的选择许可
- Apache许可证版本2.0(《LICENSE-APACHE》或http://www.apache.org/licenses/LICENSE-2.0)
- MIT许可证(《LICENSE-MIT》或http://opensource.org/licenses/MIT)
。
鸣谢和特别感谢
- Arseny Kapoulkine(C/C++库的作者)
- Daniel Collin(代码审查)
- Jake Shadle(代码审查)
- Thomas Herzog(贡献)
- Alexandru Ene(贡献)
- Ralf Jung(贡献)
- Maxime Rouyrre(贡献)
- Shiwei Wang(贡献)
- Simon Chopin(贡献)
贡献
除非您明确声明,否则您提交给此crate的任何有意提交的贡献,如Apache-2.0许可证中定义的,应按上述方式双重许可,没有任何额外的条款或条件。
欢迎贡献;请查看问题跟踪器以查看已记录的已知改进。
行为准则
meshopt crate的贡献是根据贡献者公约组织的,meshopt的维护者@gwihlidal承诺将介入以维护该行为准则。
依赖项
~0.4–1.2MB
~26K SLoC