核心内容讲解
这篇论文介绍了一个名为 FlashGS 的技术,它是一种用于高效渲染大规模和高分辨率3D场景的方法。简单来说,它通过优化3D高斯绘制(3D Gaussian Splatting)的过程,让计算机能够更快地生成高质量的3D图像。
以下是对论文内容的简化解读:
1. 研究背景
-
3D渲染的挑战:传统的3D渲染方法(如NeRF)虽然能生成逼真的图像,但速度慢,无法实现实时渲染。而3D高斯绘制(3DGS)是一种新的方法,可以在实时渲染中快速生成高质量图像。
-
现有问题:尽管3DGS很快,但在处理大规模或高分辨率场景时,计算和内存资源的压力仍然很大。例如,城市级别的场景或高分辨率图像需要处理大量的高斯分布(Gaussians),这会导致性能瓶颈。
2. FlashGS的核心思想
-
优化3DGS:FlashGS通过一系列算法和硬件优化,解决了3DGS在大规模场景下的性能问题。
-
主要优化策略:
-
消除冗余计算:通过精确的几何算法减少不必要的计算。
-
优化内存访问:通过改进内存管理,减少内存访问的延迟。
-
硬件加速:通过CUDA(一种GPU编程技术)优化,充分利用显卡的计算能力。
-
流水线优化:通过多级流水线技术,让计算和内存访问更高效。
-
3. FlashGS的主要贡献
-
算法改进:提出了一个新的3DGS渲染算法,通过精确的交集测试减少了冗余计算。
-
性能提升:相比现有的3DGS方法,FlashGS在速度上平均提升了4倍,同时减少了内存使用。
-
硬件优化:通过CUDA和GPU的特性,实现了高效的并行计算。
-
高质量渲染:FlashGS在提升速度的同时,保持了图像的高质量,没有明显的质量损失。
4. 实验结果
-
速度提升:FlashGS在多个数据集上进行了测试,结果显示它可以在RTX 3090显卡上实现实时渲染,即使在高分辨率(如4K)和大规模场景下也能保持超过100FPS的速度。
-
内存优化:FlashGS的内存使用量比现有的3DGS方法减少了49%,这对于处理复杂场景非常重要。
-
图像质量:FlashGS生成的图像质量与3DGS相同,没有明显的质量下降。
5. 总结
FlashGS通过优化算法和硬件加速,显著提升了3D高斯绘制的性能,使其能够高效处理大规模和高分辨率的3D场景。这项技术对于需要实时渲染的应用(如虚拟现实、实时导航等)具有重要意义。
重点技术讲解
warp
在GPU编程和并行计算中,Warp 是一个重要的概念,特别是在NVIDIA CUDA架构中。理解Warp的工作原理对于优化GPU程序的性能至关重要。以下是对Warp的详细解释:
1. 什么是Warp?
在NVIDIA GPU中,Warp 是一组同时执行相同指令的线程(Thread)。具体来说,一个Warp通常包含32个线程,这些线程在同一个流多处理器(Streaming Multiprocessor,简称SM)上并行执行。
-
线程(Thread):GPU中的最小执行单元,每个线程执行相同的代码(Kernel),但可以处理不同的数据。
-
Warp:32个线程的集合,这些线程在同一个SM上同步执行相同的指令。
-
SM(Streaming Multiprocessor):GPU中的一个执行单元,包含多个执行核心、寄存器、缓存等资源,用于执行线程。
2. Warp的工作原理
当GPU执行一个Kernel时,它会将线程分组成Warp。每个Warp中的线程会同时执行相同的指令,但处理不同的数据。这种执行方式称为单指令多数据(SIMD)。
-
指令同步执行:Warp中的所有线程会同步执行相同的指令。如果某个线程需要等待(例如,内存访问延迟),整个Warp都会被阻塞,直到该线程完成。
-
线程发散(Thread Divergence):如果Warp中的线程执行不同的代码路径(例如,条件分支),这会导致线程发散。线程发散会降低Warp的执行效率,因为GPU需要分别处理每条路径。
3. 为什么Warp重要?
理解Warp的工作原理对于优化GPU程序的性能至关重要,原因如下:
-
提高硬件利用率:通过确保Warp中的线程尽可能执行相同的路径,可以充分利用GPU的并行计算能力,减少资源浪费。
-
减少线程发散:线程发散会导致Warp的执行效率下降。通过优化代码逻辑,减少条件分支,可以降低线程发散的影响。
-
优化内存访问:Warp中的线程可以共享内存访问,减少全局内存的访问延迟。例如,通过使用共享内存(Shared Memory)和常量内存(Constant Memory),可以显著提高内存访问效率。
4. Warp在FlashGS中的应用
在FlashGS的实现中,Warp被用于优化计算和内存访问效率。具体来说:
-
自适应调度策略(Adaptive Scheduling):根据高斯分布的大小,FlashGS将任务分配给不同的Warp。对于较大的高斯分布,任务会被分配到多个Warp中,以平衡计算负载。
-
减少线程发散(Warp Divergence Control):FlashGS通过将条件检查移至预处理阶段,减少了渲染阶段的线程发散。这确保了Warp中的线程尽可能执行相同的路径,从而提高了执行效率。
-
粗粒度工作分配(Coarse-grained Workload Partition):FlashGS通过调整任务分配的粒度,使单个Warp处理多个任务组。这不仅充分利用了快速内存单元(如寄存器),还减少了全局内存的访问次数。
5. 总结
Warp是GPU编程中的一个核心概念,它通过将线程分组成32个一组的集合来提高并行计算效率。理解Warp的工作原理,并通过优化代码逻辑减少线程发散,可以显著提高GPU程序的性能。在FlashGS中,通过自适应调度策略、减少线程发散和粗粒度工作分配,Warp被有效地利用来提升渲染性能。
详细讲解
这篇论文的核心内容是关于 FlashGS,一种用于高效渲染大规模和高分辨率3D场景的优化方法。它基于3D高斯绘制(3D Gaussian Splatting,简称3DGS)技术,并通过一系列算法和硬件优化,显著提升了渲染性能。以下是对论文的详细介绍:
1. 研究背景与动机
1.1 3D渲染的挑战
-
NeRF(Neural Radiance Fields):一种流行的3D场景表示方法,通过深度学习生成逼真的图像。然而,NeRF需要对每条光线进行大量采样,导致渲染速度慢,无法实现实时渲染。
-
3D高斯绘制(3DGS):一种新兴的3D渲染技术,通过将场景表示为一组3D高斯分布(Gaussians),并将其投影到图像平面上进行光栅化(rasterization),从而实现快速渲染。3DGS在实时渲染中表现出色,但在大规模或高分辨率场景下,计算和内存资源的压力仍然很大。
1.2 现有方法的局限性
-
大规模场景问题:在城市级别的场景或高分辨率图像中,3DGS需要处理大量的高斯分布,导致计算和内存开销显著增加。
-
现有优化方法不足:虽然有一些方法尝试通过压缩或剪枝减少高斯分布的数量,但这些方法的性能提升有限。例如,GScore 提出了一种针对移动GPU的硬件加速方案,但主要针对移动端优化,对桌面级GPU的支持有限。
1.3 FlashGS的动机
-
深入分析瓶颈:作者通过对3DGS渲染过程的详细分析,发现其性能瓶颈主要集中在以下几个方面:
-
冗余计算:在预处理阶段,许多高斯分布与图像平面上的瓦片(tiles)的交集测试是多余的。
-
内存访问瓶颈:3DGS的光栅化过程存在计算与内存访问的不平衡,导致硬件资源利用率低。
-
大规模场景的挑战:在大规模场景中,高斯分布的数量和每个高斯的尺寸都会显著增加,进一步加剧了性能问题。
-
-
FlashGS的目标:通过算法优化和硬件加速,解决上述瓶颈问题,实现大规模和高分辨率场景的高效渲染。
2. FlashGS的核心设计与优化
2.1 精确交集算法(Precise Intersection Algorithm)
-
问题描述:在3DGS中,高斯分布投影到图像平面上后,需要与瓦片进行交集测试。然而,传统的交集测试方法(如AABB,轴对齐边界框)会产生大量冗余的高斯-瓦片对,导致后续排序和渲染过程的计算开销增加。
-
FlashGS的解决方案:
-
考虑透明度(Opacity):FlashGS引入透明度信息来缩小高斯分布的有效范围。通过透明度阈值(如1/255)确定高斯分布的边界,从而减少冗余计算。
-
精确交集测试:FlashGS采用两阶段过滤方法:
-
粗略过滤:使用与高斯分布投影椭圆紧密相切的矩形(而不是包围圆的正方形)进行初步筛选。
-
精确测试:对于与矩形相交的瓦片,进一步检查其是否与椭圆相交。
-
-
几何简化:通过几何变换,将椭圆与矩形的交集问题转化为线段重叠问题,避免直接求解复杂的二次方程,从而降低计算开销。
-
2.2 优化的渲染流程(Refined and Balanced Workflow)
-
问题描述:3DGS的渲染流程涉及多个步骤,包括预处理、排序和渲染。这些步骤的计算模式不同,且存在计算与内存访问的不平衡。
-
FlashGS的解决方案:
-
平衡计算与内存访问:FlashGS通过重新组织计算流程,将计算密集型和内存访问密集型操作交错执行,避免某一阶段的硬件资源过载。
-
内核融合(Kernel Fusion):在预处理阶段,FlashGS将精确交集计算与关键值对(key-value pairs)的生成融合在一起,减少内存读写操作,并通过内核融合复用中间结果。
-
减少冗余操作:通过精确交集算法减少冗余的高斯-瓦片对,从而减轻排序阶段的内存访问开销和渲染阶段的计算开销。
-
2.3 硬件加速与优化
-
问题描述:3DGS的光栅化过程在GPU上存在计算与内存访问的瓶颈,导致硬件资源利用率低。
-
FlashGS的解决方案:
-
低延迟执行流水线(Low-latency Execution Pipelining):
-
两级预取策略:FlashGS采用软件流水线技术,在渲染过程中,提前从全局内存中获取高斯分布的索引和详细信息,从而最大化计算与内存访问的重叠。
-
减少指令延迟:通过优化指令调度,减少因内存访问延迟导致的计算停滞。
-
-
减少线程发散(Warp Divergence Control):通过将条件检查移至更粗粒度的阶段,减少线程发散,确保同一Warp内的线程尽可能执行相同的路径。
-
粗粒度任务分配(Coarse-grained Workload Partition):通过调整任务分配的粒度,让单个线程处理多个任务组,充分利用快速内存单元(如寄存器),并暴露更多数据共享机会。
-
3. FlashGS的实现细节
3.1 预处理阶段(Preprocessing)
-
代数简化(Algebraic Simplification):
- FlashGS通过代数变换减少单个瓦片-椭圆交集的计算开销。例如,通过避免求解二次方程的根,直接比较线段端点坐标来判断线段是否重叠。
-
自适应调度策略(Adaptive Size-aware Scheduling):
- FlashGS根据高斯分布的大小动态调整任务分配策略。对于小椭圆,直接在当前线程中完成精确交集计算;对于大椭圆,将任务分配给多个线程(如Warp),从而实现高效计算。
3.2 渲染阶段(Rendering)
-
两级流水线优化:
- 在渲染过程中,FlashGS通过软件流水线技术,将全局内存访问与计算操作交错执行,减少因内存延迟导致的计算停滞。
-
减少线程发散:
- 通过将透明度检查移至预处理阶段,减少渲染阶段的条件分支,从而提高执行效率。
-
粗粒度任务分配:
- 通过调整任务分配的粒度,让单个线程处理多个任务组,充分利用快速内存单元,并暴露更多数据共享机会。
3.3 内存管理与指令优化
-
内存管理优化:
-
使用常量内存(Constant Memory)存储全局共享数据,减少全局内存访问的延迟。
-
提前进行动态内存分配,避免频繁的内存分配操作导致的性能开销。
-
-
指令优化:
-
通过将指数运算转换为对数形式,减少计算开销,并利用GPU的融合乘加(FMA)指令提高效率。
-
显式指定对数和指数运算的底数为2,以利用GPU硬件指令的优化。
-
4. 实验结果与分析
4.1 实验设置
-
测试平台:实验在NVIDIA RTX 3090(消费级显卡)和A100(数据中心级显卡)上进行。
-
数据集:使用6个代表性数据集,包括室内场景(如Playroom)、室外场景(如Truck和Train)以及大规模高分辨率场景(如MatrixCity和Rubble)。
-
基线方法:与3DGS和GScore进行对比。GScore是一种针对移动GPU的硬件加速方案,但无法直接在桌面级GPU上测试。
4.2 性能对比
-
速度提升:
-
FlashGS在所有数据集和分辨率下均显著优于3DGS。在RTX 3090上,FlashGS的平均速度提升为7.2倍,最高可达30.53倍。
-
在大规模和高分辨率场景(如MatrixCity-4K)中,FlashGS的速度提升最为显著,平均速度提升为13.64倍。
-
与GScore相比,FlashGS的速度提升是GScore的3.87倍。
-
-
实时渲染能力:
-
FlashGS在RTX 3090上能够实现实时渲染,即使在高分辨率和大规模场景下,帧率也能保持在100FPS以上。
-
在A100上,FlashGS的平均速度略慢于RTX 3090,但仍然能够实现实时渲染,平均帧率在123.8-423.7FPS之间。
-
4.3 运行时间分析
-
时间分解:
-
FlashGS在预处理、排序和渲染阶段的运行时间占比分别为19.6%、29.1%和47.6%,而3DGS分别为13.2%、25.4%和59.6%。
-
FlashGS在预处理和渲染阶段的优化显著减少了计算开销,同时通过减少冗余的高斯-瓦片对,减轻了排序阶段的内存访问压力。
-
-
指令和内存访问分析:
-
FlashGS在渲染阶段的指令数量显著减少,减少了1到2个数量级。
-
在预处理阶段,FlashGS的全局内存访问次数减少了43%-87%,进一步证明了优化的有效性。
-
4.4 敏感性分析
-
不同GPU的性能:
-
在A100上,FlashGS的渲染速度略慢于RTX 3090,主要是因为A100的FP32计算性能较低。
-
尽管如此,FlashGS在A100上仍然能够实现实时渲染,平均帧率在123.8-423.7FPS之间。
-
-
不同场景的性能:
- 在大规模和高分辨率场景中,FlashGS的速度提升最为显著。这是因为这些场景中高斯分布的数量和每个高斯的尺寸都较大,FlashGS的精确交集算法能够显著减少冗余计算。
4.5 图像质量
-
PSNR对比:
-
FlashGS与3DGS生成的图像质量相同,PSNR值保持一致(如31.52),说明FlashGS在优化性能的同时,没有损失图像质量。
-
FlashGS仅减少了冗余的高斯分布,而没有采用剪枝或量化等可能导致质量损失的策略。
-
4.6 内存使用
-
内存分配对比:
-
FlashGS的内存使用量显著低于3DGS和gsplat(一种优化的3DGS实现)。
-
例如,在MatrixCity数据集的第800帧中,FlashGS的内存分配为6.83GB,而3DGS为13.45GB,gsplat为9.83GB。
-
FlashGS通过减少冗余的高斯-瓦片对,显著减少了内存使用量。
-
5. 结论
FlashGS通过优化算法设计和硬件加速,显著提升了3D高斯绘制的渲染性能。它不仅解决了3DGS在大规模和高分辨率场景下的性能瓶颈,还实现了高效的内存管理和高质量的图像渲染。FlashGS在多种场景和分辨率下均表现出色,证明了其在实时3D渲染领域的强大潜力。
6. 总结与展望
-
FlashGS的贡献:
-
提出了一个新的3DGS渲染算法,通过精确交集算法和优化的渲染流程,显著减少了冗余计算。
-
在GPU上实现了高效的硬件加速,通过两级流水线优化和内存管理策略,提高了硬件资源利用率。
-
在大规模和高分辨率场景下表现出色,能够实现实时渲染,同时保持高质量的图像输出。
-
-
未来工作:
-
作者提到,FlashGS的优化策略还可以进一步扩展到其他3D渲染技术,如NeRF的变体。
-
未来可以探索多GPU并行化,以进一步提升渲染性能。
-
原文翻译
FlashGS:高效的大规模和高分辨率3D高斯绘制
作者:Guofeng Feng†, Siyan Chen†, Rong Fu*, Zimu Liao, Yi Wang, Tao Liu, Zhilin Pei, Hengjie Li, Xingcheng Zhang, Bo Dai
单位:上海人工智能实验室
摘要
本文介绍了FlashGS,这是一个开源的CUDA Python库,旨在通过算法和内核级别的优化,实现高效的可微分3D高斯绘制光栅化。FlashGS基于对渲染过程的全面分析开发,以提升计算效率并推动该技术的广泛应用。论文提出了一系列优化策略,包括消除冗余、高效的流水线设计、精细的控制和调度机制以及内存访问优化,这些策略被精心集成以提升光栅化过程的性能。我们在多种合成和真实世界的大规模场景中对FlashGS的性能进行了广泛的评估,涵盖了各种图像分辨率。实验结果表明,FlashGS在移动消费级GPU上平均加速了4倍,并且减少了内存消耗。这些结果突出了FlashGS卓越的性能和资源优化能力,使其成为3D渲染领域的一个强大工具。
关键词:3D高斯绘制、CUDA优化、大规模实时渲染
1. 引言
神经辐射场(NeRF)因其能够生成逼真的图像渲染而变得流行,但由于需要对每条光线采样大量点以进行像素渲染,从而限制了其实时能力。最近,一种名为3D高斯绘制(3DGS)的新表示方法作为一种可行的替代方案脱颖而出。该技术已被证明能够实现实时渲染速度,为室内环境的实时探索开辟了新的可能性,对沉浸式自由视角导航、虚拟房产参观和交互式虚拟现实游戏体验等实际应用具有深远意义。
尽管3DGS具有优势,但在城市级场景或由消费级GPS接收器记录的高质量场景中,实时渲染大规模或高分辨率区域仍然受到有限的计算和内存资源的限制。这是因为随着图像规模和分辨率的增加,高斯分布的数量和每个高斯的尺寸也会增加。现有工作广泛采用压缩或剪枝方法来避免存储或计算过多的高斯分布,但这些方法并未带来显著的性能提升。最近的一项工作,GScore,尝试分析原始3DGS算法,但主要通过设计一种新的特定领域的硬件来解决移动GPU的限制。为了克服现有工作的不足,我们对单个消费级GPU上的3DGS渲染过程进行了分析,识别出渲染性能瓶颈,例如高斯与瓦片在预处理阶段的粗略交集测试,以及体积渲染中的大量冗余计算。我们进一步进行了详细的性能分析,提取出阻碍性能的关键因素:1)在预处理阶段生成的一些高斯-瓦片对在实际渲染过程中并未使用。2)在整个光栅化算法的不同阶段存在严重的计算和内存访问瓶颈。基于我们的深入观察,我们提出了FlashGS,一种具有高效系统级优化的新型3DGS渲染算法。具体来说,我们首先引入了一种新的光栅化工作流设计,构建了更高效的渲染流水线。为了实现这一新的渲染过程,我们首先进行了几何和代数简化,以减轻高计算成本。我们提出了新的运行时调度策略,以利用硬件资源,包括线程之间的负载平衡和发散消除,以及两阶段预取执行流水线,以重叠计算和内存访问操作。我们还优化了内存管理,以更好地利用GPU内存层次结构。此外,我们通过直接应用汇编级优化进一步提升了性能。与3DGS、GScore和gsplat相比,FlashGS实现了高达4倍的速度提升和49%的内存减少,同时保持了相同的高质量指标峰值信噪比(PSNR)。据我们所知,这是首次深入分析、精心重新设计并高效实现基于单个消费级GPU的3DGS体积渲染的工作。我们使得在低开销的情况下实现大规模和高分辨率场景的高效渲染成为可能。我们的主要贡献如下:
-
我们对原始3DGS算法和当前最先进的研究进行了深入研究,识别出大规模和高分辨率场景中的主要挑战,并指出了性能瓶颈的根本问题。
-
我们提出了一个新的算法FlashGS,包括一个精确的冗余消除算法和一个高效的渲染执行工作流。
-
我们在GPU上系统地实现了FlashGS,包括计算、内存管理和调度的全面优化。
-
我们在代表性数据集上测试了FlashGS,证明了它在保持高图像质量和低内存使用的同时显著提升了渲染速度。
2 背景与相关工作
我们简要概述了新视图合成及其现有方法,特别是辐射场方法。然后,我们介绍了当前最先进的技术——3D高斯绘制(3DGS)。最后,我们简要报告了3DGS的一些最新改进。
2.1 新视图合成
新视图合成是3D重建中的一个重要方法。它基于一组从某些视角(源姿态)拍摄的输入图像,生成从新视角(目标姿态)的新图像。在重建之前,提出了一些表示方法来构建3D场景。传统方法使用点云、体素或网格来描述空间结构。然而,这些传统的显式和离散表示可能导致信息丢失,从而导致场景表示不完整。
神经辐射场(NeRF)是一种广泛采用的有吸引力的表示方法,它利用深度学习技术。它通过多层感知器(MLPs)隐式地建模3D场景,这种连续函数能够实现对场景的精细表示。一些后续工作专注于通过优化模型来提升合成图像的质量。Mip-NeRF是一种多尺度模型,它通过投射锥体并编码视锥体的位置和大小来解决NeRF的混叠问题。一些方法利用点云的深度监督,使模型更快收敛。NeRF中MLP所需的大量计算导致了训练和渲染过程的缓慢,这激发了大量关于高效训练和推理的工作。一种常见技术是将预训练结果存储到简化的数据结构中,例如稀疏体素网格和八叉树。其他工作提出或整合了新的特征与MLPs,以实现更快的训练或推理,例如体素网格中的特征。一个显著的工作极大地加速了NeRF,Instant-NGP引入了一种多分辨率哈希编码网格,并同时与优化的MLPs一起训练。
2.2 3D高斯绘制
3D高斯绘制(3DGS)通过一组高斯椭球体来建模场景,允许通过将这些高斯分布光栅化为高质量图像来实现快速渲染。图2展示了3DGS的机制。光栅化器可以粗略地分为3个步骤:预处理(preprocessCUDA和DuplicateWithKeys)、排序和渲染。原始实现中的预处理步骤包含两个主要操作。首先,经过视锥体裁剪后的$P$个3D高斯分布被投影到图像平面上,形成具有均值$\mu’ \in \mathbb{R}^2$和各向异性协方差$\Sigma’ \in \mathbb{R}^{2\times2}$的2D高斯分布。然后,预处理管道为每个对应的2D高斯分配一个轴对齐边界框(AABB)。3DGS使用AABB来获取与椭圆相交的瓦片,并且对于由某个高斯分配的AABB,DuplicateWithKeys操作遍历所有与AABB重叠的瓦片,并将每个瓦片与高斯组织为键值对。键由瓦片索引和高斯的深度组成,值是高斯索引。排序过程根据深度对$N$个键值对进行排序,用于从前到后渲染,使用Nvidia CUB库中的RadixSort。最终的渲染过程获得像素颜色的时间复杂度为$O(N)$(工作量按瓦片划分),并且每个像素颜色根据不透明度$\alpha$和透射率进行计算。
2.3 3DGS的改进
尽管3DGS在3D重建中取得了显著进展,但仍有许多改进空间。一些工作通过增强3DGS的采样过程进一步提升了图像质量(逼真度),以消除模糊和伪影。Yan等人分析了3DGS渲染中的混叠效应,并引入了一种多尺度3D高斯表示,用于渲染场景的不同细节层次,通过改进采样频率实现。Zhang等人提出了FreGS,这是一个利用正则化执行从粗到细的高质量高斯密集化的新型框架,以缓解过度构造问题。这些方面不是本文的重点,读者可以在一些综述中找到更多细节。在一些高分辨率、大规模场景中,3DGS可以生成数百万甚至更多的(巨大)高斯分布,对内存单元造成巨大压力。这激发了一些专注于优化内存使用的工作,可以分为两大类:1)利用模型压缩中广泛使用的技术,如剪枝和编码。Scaffold-GS利用底层场景几何结构,采用锚点生长和剪枝策略,有效减少冗余高斯分布,渲染时不会损失质量。EAGLES应用向量量化(VQ)对每个点的属性进行量化,以压缩3D高斯点云。LightGaussian进一步引入数据蒸馏,减少球谐系数的阶数,并提出了一种基于高斯全局重要性的VQ策略。2)引入多GPU并行化以避免单个GPU的内存限制。Grendel是一个可扩展(最多32个GPU)的分布式3DGS训练系统,将3DGS参数分布在多个GPU上,并使用稀疏通信和动态负载平衡机制。Chen等人提出了DoGaussian,它将场景分解为$K$个块,并应用交替方向乘子法(ADMM)分布式训练3DGS。然而,只有少数论文专注于原始3DGS算法的设计和实现,并尝试在计算过程中高效支持关键操作。这构成了我们工作的直接动机。gsplat是一个CUDA加速的可微分光栅化库,用于3DGS,实现了更快且更节省内存的渲染。Durvasula等人对基于光栅化的微分渲染(包括3DGS)进行了性能分析,并引入了一种使用warp级归约的新原语,以加速梯度计算中的昂贵原子操作。GScore是一种特定的硬件加速单元,用于在移动GPU上高效支持渲染流水线,并基于对基于高斯的渲染的分析进行了一些算法协同设计。
3 观察与动机
我们对3D高斯绘制的渲染过程进行了详细分析。首先,我们通过对运行时分解的分析,获得了渲染过程中的关键步骤。基于此,我们识别出几个显著的性能问题,这些问题是我们的优化设计的灵感来源。如图3所示,我们在A100和V100 GPU上使用代表性大规模数据集MatrixCity测试了3D高斯绘制。从上到下,它显示了渲染过程中关键操作的时间比例。渲染(renderCUDA)是主要的性能瓶颈,占总时间的约60%。高斯排序(SortPairs)和前面生成未排序键值列表(DuplicateWithKeys)的时间占了近20%。预处理时间在10%以内,但也不容忽视。鉴于这些步骤并非独立,我们对它们进行了全面研究。
3.1 观察1:过多的冗余计算阻碍了高效渲染
我们观察到光栅化流水线中存在几种计算冗余。图4显示了从MatrixCity数据集训练的场景中6帧的渲染过程中键值对分箱过程。分配的键值对数量远大于实际覆盖AABB或投影椭圆的瓦片数量。也就是说,预处理内核分配了未被投影椭圆覆盖的瓦片,创建了冗余的键值对,导致排序和渲染过程中的效率低下。图5中显示的三种冗余因素导致了这些现象。我们发现图5中的冗余I和II导致分配的AABB覆盖了比实际AABB更多的瓦片,如图4所示。冗余III导致AABB覆盖的瓦片数量与投影椭圆之间的差异。
-
忽略高斯的不透明度:在定义椭圆时,原始实现采用了三西格玛法则,即数据点与均值的绝对差大于$3\sigma$的情况极为罕见,并使用$3\Sigma’$定义椭圆的边界。然而,考虑不透明度可以缩小椭圆的尺寸,从而减少AABB的面积和键值对的数量。当不透明度足够低时,过估计问题更为显著。如图6所示,MatrixCity数据集中的smallcity场景中,65.1%的高斯中心的不透明度小于0.35,这进一步证明了在定义椭圆时考虑不透明度的重要性。
-
定义的AABB覆盖了过多的瓦片:
preprocessCUDA
操作将分配的AABB定义为半径为投影椭圆半长轴的圆的边界框。对于扁率较高的椭球体,AABB比实际椭圆的AABB大得多,尤其是在椭球体高度扁平时,这引入了键值对分箱、排序和渲染过程中的计算冗余。 -
duplicateWithKeys
内核分配了冗余的瓦片:AABB中的所有瓦片都被遍历并分配到键值对中,即使瓦片完全不被椭圆覆盖,这将导致后续排序和渲染过程中的冗余计算。
3.2 观察2:不恰当的执行流水线降低了硬件利用率
我们发现3DGS的主要步骤之间存在计算和内存访问的不平衡,这可能会对相应的硬件单元造成过大压力,导致计算或内存瓶颈。预处理步骤(算法1中的preprocessCUDA
)的内核具有线性复杂度$O(P)$,其中$P$是高斯分布的数量。每个高斯分布都与一个较大的球谐系数数组相关。这导致由于大量的读写操作(算法1的第17-22行)而对缓存带宽造成显著压力。在光栅化阶段,3DGS首先将高斯球投影到瓦片上以获得高斯-瓦片对(DuplicateWithKeys
)。其渐进复杂度取决于高斯分布的数量和它们投影覆盖的瓦片数量$O(P + N)$。如算法2所示,在此步骤中,除了少数位运算外,所有操作都涉及对全局内存的读写。在渲染过程中,大量的像素级计算引入了计算瓶颈,而上述两个步骤主要是内存访问操作。这导致整个3DGS执行流程中计算和内存访问之间的不平衡,可能导致GPU硬件资源的利用率不足。
4 FlashGS设计
为了解决上述问题,我们分两个阶段优化高斯渲染过程。首先,我们设计了一个更精确的交集算法,以减轻后续与冗余高斯计算相关的计算瓶颈,并使用几何简化来避免昂贵的交集计算。此外,我们根据每个内核/微内核的特性重新组织和重构了计算过程,以防止工作负载不平衡。如图7所示,整个过程可以从FlashGS减少的误判瓦片键值对数量以及算法和运行时调度的优化中显著受益。
4.1 高效且精确的交集算法
如图8所示,精确交集算法可以减少后续计算中涉及的高斯分布数量,从而减轻计算和内存访问的强度/压力。这至关重要,因为对于两个关键瓶颈——排序和渲染,它们的工作量与要处理的高斯分布数量成正比。我们需要在执行精确交集计算之前确认由高斯分布投影的椭圆的有效范围。如第3.1节所述,对于3D图像重建,我们不仅要考虑一般高斯分布在有效范围上的影响,还要考虑不透明度的变化。3D高斯绘制应用三西格玛法则来获得有效椭圆:
\[r = 3\sqrt{\lambda} \quad (1)\]其中,$\lambda$表示投影椭圆的半长轴长度,对应于协方差矩阵的特征值,$r$是从椭圆抽象出来的圆的半径。考虑到第3.1节中的统计数据,65%的高斯中心的不透明度相当低([0, 0.35],这个范围将在接下来的段落中解释),这促使我们将不透明度$\alpha$纳入以获得有效范围:
\[\alpha = \alpha_0 \times \exp\left(-\frac{r^2}{2\lambda}\right) \quad (2)\]其中,$\alpha_0$是高斯的初始(中心)不透明度。当$\alpha$衰减到某个阈值$\tau$时,我们认为这是椭圆的边界,如公式(3)所述:
\[r = \sqrt{2\ln\left(\frac{\alpha_0}{\tau}\right)\lambda} \quad (3)\]鉴于RGB通常有256个离散值,通常取$\tau = \frac{1}{255}$。当$\alpha_0 \leq 0.35$时,我们的方法优于三西格玛法则;反之,则继续使用$3\sigma$。
我们采用精确交集算法来消除无效高斯分布。考虑到椭圆可能是相当细长的,直接计算高斯投影椭圆与瓦片之间的交集成本较高,这可能会引入大量对无效高斯分布的不必要计算。我们通过两阶段过滤过程减少直接交集计算的开销:(1)使用边界框(注意:我们计算一个与椭圆紧密相切的矩形,而不是像原始算法中那样使用包围椭圆的圆的正方形)来设置粗略范围。(2)对于与边界框相交或包含在边界框内的瓦片,检查它们是否与椭圆相交。
对于每个瓦片的交集,我们使用几何等价变换来避免直接求解多个二次方程的成本,考虑到矩形瓦片不容易用简单方程表示。我们的关键观察是,椭圆与矩形之间的交集可以转化为确定椭圆在矩形每条边所在直线上的投影线段是否与矩形边的线段重叠的问题,如图9所示。如果它们相交,我们找到被椭圆包围的线段上的端点。然后,我们使用这些端点来确定该线段是否与矩形的对应边重叠。如算法3所示,我们首先将椭圆与矩形之间的交集分为两种情况。第一种情况很直接:如果椭圆的中心在矩形内,则必定存在重叠。对于更复杂的情况,即椭圆的中心在矩形外,我们求解椭圆与矩形每条边所在直线的交集。
4.2 精细且平衡的工作流
3DGS渲染的工作流是一个涉及多个步骤的复杂过程,如上所述。此外,不同的子组件可能具有不同的计算模式,这些子计算并非完全独立。因此,我们需要从更全面的角度进行系统分析和优化。我们的关键思想是尽可能平衡每个部分的计算成本,以避免潜在的瓶颈。我们实现这一想法的方法是将不同类型的运算在时间线上摊销,以防止某个事务突发并造成过大压力。我们从不同操作中提取计算瓶颈和内存访问瓶颈,并将它们交织在一起,形成更高效的执行工作流。
我们观察到,在执行渲染之前,有几个重要的操作,包括前面提到的精确交集、计算和记录一些与高斯分布相关的参数,以及为排序构建键值对。在基线实现中,这两个函数都是带宽受限的,涉及大量的内存读写操作。然而,我们观察到后续的渲染过程是计算受限的。这是由于基线算法中粗糙的高斯交集方法导致了大量误判(false positives)。因此,我们可以在预处理阶段进行更多的计算。这不仅有助于平衡带宽受限的部分,还能减少后续逐像素渲染的开销。这正是我们采用前面提到的精确交集算法的关键动机之一。在预处理阶段,我们检查高斯分布的可见性,然后计算并存储后续步骤所需的各种信息,例如深度、高斯半径和坐标。在原始算法中,每个处理与瓦片相交的高斯分布的线程都需要访问全局内存,以读取球谐系数并在预处理的最后写入多个高斯信息。这导致了显著的带宽竞争,使得工作流在全局内存访问期间因内核之间的隐式同步而停滞。我们将新的精确交集算法与DuplicateWithKeys()
结合,精确生成有效的高斯键值对。如前所述,准确计算瓦片与投影椭圆之间的交集是复杂的,需要求解多个方程。因此,在此过程中的许多操作都是计算密集型的,使得内存带宽资源有剩余。因此,我们重新组织了这两个操作。通过在全局内存写入操作的延迟期间交错执行精确交集计算,我们确保了在整个执行工作流中,GPU的计算单元和内存带宽都能高效利用。我们的方法实现了较高的计算与内存访问比率。如图10所示,精确交集操作减少了冗余的高斯分布,从而减轻了排序阶段的内存访问开销以及渲染阶段的额外计算。在我们的新预处理阶段,我们可以通过内核融合重用一些中间结果,直接从交集过渡到键值对生成,从而减少内存读写操作的次数。尽管精确交集和合并操作的总计算量和内存访问操作可能增加,但我们为编译器和硬件提供了更多的调度机会,以实现更好的平衡并在更长的时间线上摊销。
5 FlashGS实现
在本节中,我们在第4节提出的FlashGS算法基础上进行了高效的实现,以充分利用算法的优势,同时避免复杂计算带来的额外开销。在我们的实现中,我们通过在计算、调度和内存管理方面应用优化,进一步增强了运行时的硬件利用率。
5.1 预处理
在新的预处理阶段(如算法4所示),我们针对每个瓦片与椭圆的精确交集算法引入了大量的复杂计算,这可能会显著阻碍将该算法应用于当前最先进的工作中。尽管我们在第4.2节中通过将精确交集与一些内存访问操作绑定来平衡计算与内存比率,但高效实现这一算法仍然至关重要。我们在两个关键方面对算法进行了优化:1)应用代数简化以减少单个瓦片-椭圆交集的开销;2)提出一种自适应调度策略,以平衡整个高斯投影椭圆的交集任务。
5.1.1 代数简化
我们使用代数等价变换来避免高成本操作(指令)。在第4.1节中的几何变换中,为了判断两条线段是否重叠,我们可以比较它们的端点坐标。假设两条线段分别为$[a_i, b_i]$和$[a_j, b_j]$,如果它们的交集非空,则满足公式(4):
\[\min(b_i, b_j) - \max(a_i, a_j) \geq 0 \quad (4)\]具体来说,这等价于检查公式(5),即一条线段的右端点(end)在另一条线段的左端点(start)之后,且一条线段的左端点(start)在另一条线段的右端点(end)之前:
\[a_i \leq b_j \quad \text{且} \quad a_j \geq b_i \quad (5)\]寻找相交线段的端点需要求解二次方程,这涉及到高成本的操作,如除法和开方。假设一个瓦片的边由线段$[a_i, b_i]$表示,相交线段$[a_j, b_j]$(如果存在)是直线(瓦片边所在的直线)和椭圆形成的二次方程的两个根。如公式(6)所示,其中$\Delta = B^2 - 4AC$,且$A, B, C$是二次方程$AX^2 + BX + C = 0$的系数:
\[a_i \leq \frac{-B + \sqrt{\Delta}}{2A} \quad \text{且} \quad b_i \geq \frac{-B - \sqrt{\Delta}}{2A} \quad (6)\]不失一般性,假设$A > 0$,则该条件可以转换为公式(7),以避免高成本操作,如除法和开方,其中两个表达式$e_1 = 2Aa_i + B$和$e_2 = 2Ab_i + B$:
\[(e_1 \leq 0 \quad \text{或} \quad e_1^2 \leq \Delta) \quad \text{且} \quad (e_2 \geq 0 \quad \text{或} \quad e_2^2 \leq \Delta) \quad (7)\]5.1.2 自适应尺寸感知调度
精确的每瓦片交集算法还意味着不同高斯分布的交集任务的工作量可能会显著不同,因为不同大小的椭圆覆盖的瓦片数量可能会有很大差异。在原始3DGS交集算法中,基于边界框的方法不存在这个问题,因为可以通过边界框的顶点直接定位覆盖区域,而忽略边界框的大小。如图11所示,对于不同大小的高斯分布,得到的边界框(即需要相交的瓦片数量)可能会有很大差异。我们根据高斯分布的大小自动选择处理方法,以实现高效的调度。为了确保每个线程的工作量平衡,我们使用自适应映射。当椭圆很小时,仅需要对一个瓦片进行计算时,我们继续使用当前线程进行该高斯分布的精确交集。当椭圆较大时,我们将工作量重新映射到一个线程组(在我们的实现中,我们选择了一个warp来平衡同步开销,考虑到在实践中,椭圆覆盖的瓦片数量很少超过32个)。因此,我们实现了对整个高斯投影椭圆的高效计算,它可以与不同数量的瓦片相交。应用了上述所有优化后,我们的新处理内核如算法4所示。
5.2 渲染
为了解决3DGS执行过程中硬件利用率低下的问题,我们从上到下实现了两级流水线优化。我们调整了应用程序的执行工作流和指令分发。
5.2.1 低延迟执行流水线
在渲染过程中,每个瓦片都会执行与其相关高斯分布的计算,例如计算透射率和颜色。这些计算可能会根据累积的不透明度$\alpha$饱和度提前终止。这些计算依赖于预先从全局内存中加载的预处理和排序后的高斯列表,需要分两步从全局内存中读取高斯信息:(1)根据当前瓦片信息获取高斯索引;(2)使用索引检索存储的具体信息,例如高斯中心的像素坐标、椭圆的二次形式和不透明度等。全局内存访问的高延迟可能会阻碍计算流水线的高效执行,尤其是上述两个全局内存访问步骤存在依赖关系。
我们的核心策略是在内存访问延迟期间尽可能多地发出指令,以缓解由数据依赖导致的停顿。具体来说,我们引入了两级预取策略,以最大化计算和内存访问之间的重叠,如图12所示。采用软件流水线方法,在第$i$步的计算中,我们首先从全局内存中获取第$i+2$步的高斯索引。然后,使用已经获取的索引,从全局内存中检索第$i+1$步的具体高斯信息。最后,使用在第$i-1$步获取的高斯信息执行第$i$步的渲染计算。我们在细粒度的重新排列中尽可能多地重叠独立指令,从而实现更高效的指令分发流水线。
5.2.2 线程发散控制
Warp内的线程发散会导致某些线程停滞,影响执行效率。核心思想是最小化条件分支或将条件检查移至更粗粒度的层次(更粗粒度),从而确保Warp内的线程尽可能执行相同的路径。在原始3DGS算法的渲染步骤中,每个线程在计算像素时都必须检查不透明度和透射率,以执行早期停止,并在写回之前确定像素是否有效。我们将不透明度检查移至预处理阶段,以实现有效的高斯计算。这种在更粗粒度的高斯级别上的预先过滤消除了冗余,取代了在渲染过程中每个像素的条件检查。
5.2.3 粗粒度工作分配
我们通过调整任务分配的粒度,使单个线程处理多个任务组,从而促进公共数据共享。这种较大的粒度主要基于以下两个考虑因素。首先,它旨在充分利用快速内存单元(例如寄存器),因此我们不能将缓冲区设置得过小(例如,仅缓冲少量数据用于单个项目)。其次,它还考虑了共享数据的问题。在渲染过程中,还存在一定数量的公共计算。我们以组为单位向编译器提供一组任务,以暴露更多的潜在共享机会。具体来说,我们暴露了公共子表达式消除(CSE)的机会,通过减少FLOPs进一步提高计算性能。
5.3 通用优化
除了上述对一些关键内核的高效并行算法实现外,我们还应用了几种通用技术来优化FlashGS计算中的内存和指令使用,以实现进一步的性能提升。
5.3.1 内存管理
3DGS涉及大量的内存操作,包括读取、写入和分配。我们需要精心设计以利用GPU的层次化内存和内存带宽。
-
利用常量内存减少延迟:我们仔细分析了算法并提取了特殊参数,以高效利用GPU的内存层次结构。核心思想是利用特殊的内存访问模式,以最大限度地利用加载到更快内存层次结构中的数据。我们在预处理步骤中使用常量内存,这在所有线程都需要读取某些预定义输入信息(例如投影矩阵和变换矩阵)时非常有益。通过利用CUDA 12.1的大参数传递特性,我们直接将这些数据作为参数传递给内核,而不是它们的内存位置的指针。这确保了这些参数被放置在常量内存中,而不是全局内存中。鉴于全局内存延迟约为500个周期,而具有缓存的常量内存访问延迟仅为约5个周期,我们的数据放置策略不仅提高了访问速度,还减少了由于频繁全局内存访问而导致的L1缓存带宽压力。
-
减少动态内存分配:我们避免了由于系统调用开销和内存碎片化导致的频繁内存分配的性能下降。我们将动态内存分配操作及相关预处理操作提取到初始阶段。例如,对于同一场景的不同视图的计算,可以提前进行统一的预处理。
5.3.2 汇编优化
我们还利用显式和隐式优化,以更好地利用GPU支持的高效指令。在高斯计算中,由于高斯分布的指数部分,直接调用可能会导致高开销。通过将指数表达式中的直接乘法转换为对数形式,我们首先执行加法,然后执行指数运算。这种方法的好处是它允许编译器使用融合乘加(FMA)指令,其开销与单个MUL或ADD指令相同。此外,在执行对数和指数运算时,我们明确指定底数为2,因为GPU的特殊函数单元(SFU)硬件指令针对底数2进行了优化,允许直接高效地调用fast ex2.approx.ftz.f32
PTX指令。
6 评估
6.1 实验设置
6.1.1 测试平台
我们在两个NVIDIA GPU平台上进行了实验。大多数测试在消费级RTX 3090(Ampere架构,24GB GDDR6X内存,CUDA计算能力8.6)上进行。我们使用A100(Ampere架构,80GB HBM2e,CUDA计算能力8.0)数据中心GPU来研究性能敏感性。我们使用GCC 10.5.0和CUDA 12.0的NVCC编译代码。我们还将FlashGS集成到Python中作为一个可调用模块,以增强其可用性。
6.1.2 数据集
我们在6个代表性数据集上全面测试了FlashGS和3DGS,这些数据集涵盖了11个不同分辨率的场景,每个场景包含数百到数千帧。其中,Truck和Train是Tanks & Temple中的室外场景;Playroom和DrJohnson是DeepBlending中的室内场景。3DGS声称在这些数据集上能够以1080p分辨率实现实时渲染。我们还使用了另外两个大规模和高分辨率的数据集,这些数据集超出了3DGS实现实时渲染的能力。MatrixCity是一个包含数千张1080p分辨率航拍图像的综合性和大规模高质量数据集。Rubble是Mill19中由无人机拍摄的高分辨率图像。我们在每个数据集上使用3DGS训练了30K次迭代,以获得用于渲染的高斯模型。
6.1.3 基线方法
我们将FlashGS与3DGS和GScore的最新优化进行了对比。由于GScore是作为硬件加速单元实现的,我们无法直接对其进行测试。他们声称GScore通过其交集和调度技术实现了1.86倍的速度提升。
6.2 总体性能
如表1所示,FlashGS的光栅化流水线在所有性能指标上均优于原始3DGS算法,覆盖了不同场景和分辨率的所有数据集。我们可以在RTX 3090上始终实现>100FPS的渲染速度,即使在高分辨率和大规模数据集(如Rubble)中,最低帧率也能达到107.3 FPS。这表明FlashGS即使在极其大规模和高分辨率的情况下也能实现实时渲染。FlashGS在Matrixcity数据集的4K分辨率下实现了高达30.53倍的速度提升,平均速度提升为12.18倍。我们在所有11个场景中平均实现了7.2倍的速度提升,而在7个大规模或高分辨率测试中,速度提升达到了8.6倍,是GScore结果的3.87倍。FlashGS在不同场景中的所有帧中实现了2.29倍到30.53倍的速度提升,全面证明了我们设计和优化的通用性。
6.3 运行时间分析
在本节中,我们比较并分析了FlashGS与3DGS的运行时间分解,以揭示我们出色性能的来源。图13展示了6个代表性帧的光栅化时间和分解情况,这些帧展示了最大或最小加速比。这表明我们在预处理、排序和渲染等所有阶段都实现了加速。在FlashGS中,这些阶段分别占总时间的平均19.6%、29.1%和47.6%,而在3DGS中,它们分别占13.2%、25.4%和59.6%。预处理和渲染阶段的优化已经在前面讨论过,而排序阶段的加速主要是由于应用了我们的精确交集算法后,需要排序的键值对数量减少。我们将在下面进行更详细的分析。
6.3.1 性能分析结果
我们进一步通过内存和计算单元的分析结果来证明我们在渲染和预处理阶段的优化,如图14所示。图中显示,我们在渲染阶段减少了指令的发出,缓解了计算受限的问题。这证明了我们精确交集算法和低延迟渲染优化的有效性。与3DGS相比,发出的总指令数量显著减少了1到2个数量级。对于预处理阶段的内存访问事务,我们也减少了43%-87%的全局内存访问。
图15进一步展示了渲染阶段发出指令数量显著减少的原因,这在光栅化中占主导地位。在基于瓦片的渲染中,发出的总指令数量是瓦片数量和每个瓦片的指令数量的乘积。因此,这一减少主要来自两个方面。如图15所示,由于我们的交集优化,渲染的键值对数量减少了68%-96%。每个瓦片的计算涉及的指令数量也减少了67%-71%,因为我们进一步在指令级别优化了renderCUDA
内核。生成的键值对数量的减少也使排序过程受益,因为它显著减少了需要排序的列表大小。此外,存储这些键值对所需的内存量也减少了。
6.4 敏感性分析
在本节中,我们研究了FlashGS在不同GPU和场景下的性能敏感性。
6.4.1 A100上的性能
我们还在A100 GPU上进行了表1中展示的实验,结果同样展示在表1中。与RTX 3090相比,A100上的总光栅化时间平均慢了1.43倍。这主要是因为渲染步骤占主导地位,如图13所示,它严重依赖于FP32计算,而A100的FP32峰值性能仅为19.5TFLOPS,RTX 3090则为35.6TFLOPS。尽管排序等渲染前的操作主要是内存带宽受限的,且A100的带宽高于RTX 3090,但这些优势无法弥补渲染性能的下降。然而,我们在A100上对所有数据集的3DGS实现了显著的加速,在表1中的11个场景中,平均帧率为123.8-423.7 FPS,始终能够实现实时渲染。
6.4.2 不同场景下的性能
从表1中,我们观察到两个主要结果:
-
对于相同的数据集,4K分辨率下的加速比高于1080p。
-
在不同数据集中,大规模和高分辨率场景(如MatrixCity-4K)实现了最高的加速比。这主要是由于我们的精确交集计算,其中渲染中的计算负载与高斯分布的数量以及每个高斯分布与瓦片的交集数量成正比。在包含更多对象的大场景中,高斯分布的数量增加;对于高分辨率,高斯分布变大以渲染更精细的细节。在这种情况下,消除冗余的效果更为显著。如果场景非常小且简单,每个高斯分布可能非常小,仅与一个瓦片相交,这将导致我们的精确交集改进较低。
6.5 图像质量
图17展示了原MatrixCity大规模数据集在1080p分辨率下的最复杂帧,以比较输出质量。我们比较了FlashGS与3DGS之间的峰值信噪比(PSNR),这是计算机图形学中的标准指标(越高越好)。结果显示,FlashGS没有改变质量,保持了31.52的PSNR,同时加速了8.57倍。这是合理的,因为我们的精确交集算法仅减少了误判冗余。而且,我们没有在实现中应用剪枝或量化策略,因此没有精度损失。
6.6 图像质量
图17展示了MatrixCity大规模数据集的两个代表性帧。我们比较了FlashGS与3DGS之间的峰值信噪比(PSNR),结果表明FlashGS并未改变图像质量。这是合理的,因为我们的精确交集算法仅减少了误判冗余。此外,我们在实现中没有应用剪枝或量化策略,因此没有精度损失。
6.7 内存比较
FlashGS的内存分配少于3DGS和gsplat,最多可减少49.2%。表2比较了在NVIDIA A100 GPU上渲染第800帧之前和之后的不同模型的内存使用情况,包括gsplat(packed
设置为True
和False
)、原始3DGS和FlashGS。具体来说,当packed
设置为False
时,gsplat渲染后的最大内存分配为10.75 GB;而当packed
设置为True
时,最大内存分配减少到9.83 GB,显著降低了内存使用。这种减少是因为,当packed
设置为True
时,光栅化过程更加节省内存,将中间张量打包到稀疏张量布局中。这在每个相机只能看到场景一小部分的大场景中特别有益,大大减少了内存使用。然而,这也引入了一些运行时开销。
相比之下,原始3DGS使用了最多的内存,渲染后的内存分配为13.45 GB,这在处理复杂场景时是一个重要的考虑因素。FlashGS通过静态分配方法确保内存使用的连贯性和可预测性,最大内存分配为6.83 GB,低于其他模型。这表明其在处理大规模场景时的卓越效率和性能。静态分配方法有效地避免了内存分配和释放过程中的波动,从而实现更稳定的内存管理。
每种方法生成的键值对(kv pairs)数量是决定渲染时内存使用的关键因素。原始3DGS模型生成了56,148,670个键值对,导致其较高的内存消耗。相比之下,gsplat在packed
设置为False
和True
时分别生成了56,996,302和56,998,101个键值对,略有增加。这证明了其内存减少来自于压缩技术。而FlashGS仅生成了3,436,142个键值对,显著减少了内存使用。
7 结论
我们提出了FlashGS,它能够实现实时渲染大规模和高分辨率场景。在本文中,我们通过精心设计的算法和高度优化的实现,解决了原始3DGS中存在的冗余和不恰当的计算与内存比率问题,从而实现了一个快速的渲染流水线。FlashGS在GPU上的渲染性能显著优于现有方法,同时实现了高效的内存管理,同时保持了高质量的图像渲染。
如果还有其他需要补充或修改的地方,请随时告诉我!
算法1 preprocessCUDA
输入:$P$,相机位姿(cam_pose),$s$,$R$,$\sigma$,球谐函数(shs),视图矩阵(viewmatrix),投影矩阵(projm atrix),$o$
输出:半径(radii),$x_{I}’$,深度(depths),$\Sigma$,$c$,圆锥不透明度(conic_opacity),被触及的图块(tiles_touched)
1: 如果 $idx \geq P$,则
2: 返回
3: $p_view \leftarrow$ 在视锥体中(in_frustum())
4: 如果 非$p_view$,则
5: 返回
6: $p_proj \leftarrow$ 投影(project())
7: $\Sigma \leftarrow$ 计算三维协方差(computeCov3D())
8: $\Sigma’ \leftarrow$ 计算二维协方差(computeCov2D())
9: $\lambda_1, \lambda_2 \leftarrow$ 特征值(eigenvalues($\Sigma’$))
10: $radii \leftarrow \lceil 3 \times \sqrt{\max(\lambda_1, \lambda_2)}\rceil$
11: $x_{I}’ \leftarrow$ 归一化设备坐标到像素坐标(ndc2Pix())
12: $rect_min, rect_max \leftarrow$ 获取矩形(getRect())
13: 如果 $(rect_max.x - rect_min.x) \times (rect_max.y - rect_min.y) == 0$,则
14: 返回
15: $c[idx] \leftarrow$ 从球谐函数计算颜色(computeColorFromSH())
16: $depths[idx] \leftarrow p_view.z$
17: $radii[idx] \leftarrow radii$
18: $x_{I}’[idx] \leftarrow$ 点图像(point_image)
19: $conic_opacity[idx] \leftarrow$ (圆锥,不透明度[$idx$])((conic, opacities[$idx$]))
20: $tiles_touched[idx] \leftarrow (rect_max.y - rect_min.y) \times (rect_max.x - rect_min.x)$
算法2 duplicateWithKeys
输入:$P$,$x_{I}’$,深度(depths),偏移量(offsets,每个高斯的图块计数前缀和),半径(radii)
输出:未排序的高斯键(gaussian_keys_unsorted,用于存储键值对的数组),未排序的高斯值(gaussian_values_unsorted,用于存储键值对的数组)
1: $idx \leftarrow$ 线程排名(thread_rank())
2: 如果 $idx \geq P$,则
3: 返回
4: 如果 $radii[idx] > 0$,则
5: $off \leftarrow (idx == 0)? 0 : offsets[idx - 1]$
6: $rect_min, rect_max \leftarrow$ 获取矩形(getRect())
7: 对于 $rect_min.y$ 到 $rect_max.y$ 的 $y$,执行
8: 对于 $rect_min.x$ 到 $rect_max.x$ 的 $x$,执行
9: $gaussian_keys_unsorted[off] \leftarrow$ 键(key)
10: $gaussian_values_unsorted[off] \leftarrow idx$
11: $off \leftarrow off + 1$
算法3 判断矩形图块是否需要渲染的算法
输入:矩形$R$,椭圆$E$
输出:一个布尔值,指示该图块是否需要渲染
1: 如果椭圆$E$的中心点位于矩形$R$内,则
2: 返回 true
3: 否则
4: 对于矩形$R$的每条边$e$,执行
5: $l \leftarrow$ 包含线段$e$的直线
6: 如果直线$l$与椭圆$E$的交集不为空,且交点位于边$e$上,则
7: 返回 true
8: 返回 false
算法4 preprocessCUDA
输入:$o$,$\sigma$,球谐函数(shs),视图矩阵(viewmatrix),投影矩阵(projm atrix),相机原点($o_{cam}$),宽度($W$),高度($H$),水平视场角正切(tan_foux),垂直视场角正切(tan_fovy),水平焦距(focal_x),垂直焦距(focal_y)
输出:$x_{I}’$,偏移量(offset),深度(depths),$\Sigma$,$c$,圆锥不透明度(conic_opacity),未排序的高斯键(gaussian_keys_unsorted),未排序的高斯值(gaussian_values_unsorted)
1: 如果 $idx_vec < P$,则
2: 初始化 $p_orig$,$p_view$
3: 如果 $p_view.z > 0.2$ 且 不透明度(opacity)> $1.0/255$,则
4: $p_proj \leftarrow$ 投影(project())
5: $\Sigma’ \leftarrow$ 计算二维协方差(computeCov2D())
6: $x_{I}’ \leftarrow$ 归一化设备坐标到像素坐标(ndc2Pix())
7: $rect_min, rect_max \leftarrow$ 获取矩形(getRect())
8: $point_valid \leftarrow$ (边界框不为零)
9: 如果 $point_valid$ 且 边界框为单个图块,则
10: 如果图块与椭圆相交或包含椭圆中心,则
11: $key \leftarrow$ 计算图块键(compute tile key)
12: $offset \leftarrow$ 原子加(atomicAdd(curr_offset, 1))
13: $gaussian_keys_unsorted[offset] \leftarrow key$
14: $gaussian_values_unsorted[offset] \leftarrow idx_vec$
15: 对于 warp 中的每个线程,执行
16: 如果 是多个图块(multi_tiles),则
17: $parameters \leftarrow$ 打乱值(shuffle values)
18: 对于从 $rect_min.y$ 到 $rect_max.y$ 的 $y$,执行
19: 对于从 $rect_min.x$ 到 $rect_max.x$ 的 $x$,执行
20: 如果 有效(valid),则
21: $key \leftarrow$ 计算图块键(compute tile key)
22: 存储键和值(store key and value)
23: 如果 任何线程有有效点,则
24: $c[idx_vec] \leftarrow$ 从球谐函数计算颜色(computeColorFromSH())
25: 存储深度(depth),点的 xy 坐标(point_xy),圆锥不透明度(conic_opacity)
评论