ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

brush-sort:WebGPU 兼容的高性能 GPU 基数排序在 brush 3D 高斯溅射渲染中的应用

brush-sort:WebGPU 兼容的高性能 GPU 基数排序在 brush 3D 高斯溅射渲染中的应用 计算机视觉图形学深度学习【免费下载链接】brush3D Reconstruction for all项目地址https://gitcode.com/GitHub_Trending/br/brush点击查看免费下载导读本文以 crates/brush-sort/README.md 为核心深入讲解 brush3D Reconstruction for all一个基于 Rust Burn/CubeCL 的 3D 高斯溅射3D Gaussian Splatting渲染与训练项目中用于 GPU 端基数排序radix sort的brush-sortcrate。你将理解它如何支持按任意位数排序、以间接调度indirect dispatch处理 GPU 已知元素数量的数组两大能力掌握其基于 FidelityFX Radix Sort 的 5 阶段内核流水线count → reduce → scan → scan-add → scatter的完整原理并看到它在渲染器中深度排序与 Tile 排序两个关键环节的真实调用方式以及配套的测试与基准使用方法。一、模块定位为什么 3D 高斯溅射渲染需要 GPU 排序brush 是一个面向 WebGPU 的 3D 高斯溅射渲染与训练项目项目描述为 3D Reconstruction for all渲染核心位于 crates/brush-render。在它的光栅化流水线中排序是一个无法回避的基础操作涉及两个典型场景深度排序Depth Sort高斯溅射以半透明 blob 的方式绘制必须按到相机的深度从远到近或近到远排序才能得到正确的混色结果Tile 排序Tile Sort渲染器把屏幕划分成若干 tile需要把每个高斯与它覆盖的 tile 的对应关系intersection 记录按 tile id 排序以便按 tile 顺序紧凑地打包光栅化任务。这两处排序的数据规模非常可观——单个场景可能包含上百万个高斯、数千万条 intersection 记录见后文测试用例而排序结果必须在 GPU 上立即被后续 kernel 消费不能来回拷贝到 CPU。因此brush 把排序实现为纯 GPU 的 WebGPU 兼容基数排序也就是 crates/brush-sort 这个 crate。二、brush-sort 是什么WebGPU 兼容的基数排序实现crates/brush-sort/README.md 对该模块的定位非常精炼WebGPU compatible radix sort. Its based on this implementation, which in turn is based on FidelityFX Radix sort. It allows sorting up to a given number of bits, and sorting an array with a GPU known number of elements using indirect dispatches.翻译过来即一个 WebGPU 兼容的基数排序。它基于 googlefonts/compute-shader-101 的一个实现PR #31而后者又源自 AMD 的FidelityFX Radix Sort算法。其两大核心能力是支持按任意给定的位数排序up to a given number of bits排序范围可以是任意 bit 数最多 32 位支持对 GPU 已知元素数量的数组进行间接调度排序元素数量由 GPU 端 buffer 决定运行时才知道通过间接调度indirect dispatch启动排序 kernel无需 CPU 回读。在 brush 的整体架构中brush-sort与 crates/brush-scanGPU 前缀和/扫描原语、crates/brush-cubeCubeTensor 创建与设备抽象协作构成 crates/brush-render 渲染流水线的底层基础设施。2.1 依赖关系与构建配置从 crates/brush-sort/Cargo.toml 可以看到它的依赖设计brush-cube提供CubeTensor、CubeDevice、create_tensor/create_tensor_from_slice等张量与设备工具brush-scan提供block_scan、cube_exclusive_sum、cube_sum、lds_index等扫描原语以及BLOCK_SIZE、ELEMENTS_PER_THREAD、WG常量burn、burn-wgpu、burn-cubecl通过 Burn 框架获得 WebGPU/CubeCL 后端与张量类型系统bytemuck用于安全地做零拷贝位模式转换tracing用于性能 trace 跨度如Radix sort、DepthSort、Tile sort等 trace_span。dev-dependencies 则包括rand随机数据生成、tokio异步测试、divan基准框架以及wasm-bindgen-testWebAssembly 环境测试。它还声明了一个独立基准目标[[bench]] name sort_bench harness false path benches/sort_bench.rs三、算法架构基于 FidelityFX Radix Sort 的五阶段内核流水线brush-sort的核心入口是 src/lib.rs 中的radix_argsort函数——注意它是argsort除了返回排序后的 keys还会同步返回对应的 values即原下标因为 values 通常被初始化为 0..n 的索引这正符合渲染器拿到排序后的下标再做 gather的需求。3.1 常量与每趟位宽在 src/kernels.rs 中定义了关键的算法参数pub const WG_USIZE: usize WG as usize; // 每 workgroup 线程数WG 256 pub const BITS_PER_PASS: u32 4; // 每趟处理 4 bit pub const BIN_COUNT: u32 1 BITS_PER_PASS; // 16 个 bin0x0 ~ 0xF pub const BIN_COUNT_USIZE: usize BIN_COUNT as usize;其中WG256、ELEMENTS_PER_THREAD4、BLOCK_SIZE WG × ELEMENTS_PER_THREAD 1024这些扫描相关的常量从brush-scan复用见 crates/brush-scan/src/kernels.rs。每趟处理 4 位16 个 bin因此排序sorting_bits位需要ceil(sorting_bits / 4)趟见for pass in 0..sorting_bits.div_ceil(4)排序 32 位 key 需要 8 趟排序 10 位 key渲染器 Tile 排序场景需要 3 趟见 benches/sort_bench.rs 中的注释。3.2 入口函数radix_argsort的调用约束radix_argsort(input_keys, input_values, sorting_bits)首先做一系列防御性断言src/lib.rskeys 与 values 的长度必须相等sorting_bits 32即最多排序 32 位keys 与 values 必须是连续内存contiguous的CubeTensor。随后它计算出调度规模let max_n input_keys.shape()[0] as u32; let max_needed_wgs max_n.div_ceil(BLOCK_SIZE); let num_wgs_count max_n.div_ceil(BLOCK_SIZE); let num_reduce_wgs_count num_wgs_count.div_ceil(BLOCK_SIZE) * BIN_COUNT;这些数量被写入num_keys_bufI32 张量供各 kernel 以间接调度的方式自行计算边界这正是 README 所说 sorting an array with a GPU known number of elements using indirect dispatches 的实现方式——CPU 侧只需要按最大可能规模发起一次调度kernel 内部通过读取num_keys_arr[0]并配合terminate!()提前退出多余 workgroup见各 kernel 开头的if group_id num_wgs { terminate!(); }。3.3 五阶段内核一趟排序的完整数据流每一趟一个 4-bit pass依次启动 5 个 kernelsrc/lib.rs阶段 1 ——sort_count_kernel构建直方图每个 workgroup 负责BLOCK_SIZE1024个元素用共享内存中的 16 个原子计数器Shared::[Atomicu32]::new_slice(BIN_COUNT_USIZE)统计本块内每个 bin 的元素个数然后把直方图按counts[bin * num_wgs group_id]的布局写入全局计数 buffersrc/kernels.rs。阶段 2 ——sort_reduce_kernel块直方图规约把每个 bin 的各块计数累加为该 bin 的全局块级总量。由于num_wgs可能很大这里再做一层块的块BIN_COUNT * ceil(num_wgs / BLOCK_SIZE)个 reduce workgroup每个 reduce workgroup 用cube_sum把ELEMENTS_PER_THREAD个块的计数求和结果写入reducedbuffersrc/kernels.rs。阶段 3 ——sort_scan_kernel跨块前缀和对reducedbuffer 做分块 exclusive scan。它使用block_scan以BLOCK_SIZE为 chunk 循环推进并用carry跨 chunk 传递累加值——这也是解决大规模数据reduce 块数超过 BLOCK_SIZE时前缀和正确性的关键src/kernels.rs。阶段 4 ——sort_scan_add_kernel扫描结果回填把上一阶段得到的每个 bin 的全局偏移加回对应的块直方图得到每个块内各 bin 的排他性起始偏移写入 counts buffer供 scatter 阶段直接使用src/kernels.rs。阶段 5 ——sort_scatter_kernel就地重排scatter这是最复杂的内核src/kernels.rs。每个 workgroup 处理自己负责的BLOCK_SIZE个元素先用cube_exclusive_sum配合打包计数技巧把每个线程 4-bit key 的每 2 bit 打包进一个 u32 的不同字节一次 cube scan 同时得到 2 个 2-bit bin 的排他偏移见packed_input 1u32 (bit_key * 8u32)与后续bin_offsets解包在共享内存中完成块内重排再用块内局部直方图与bin_offset_cache阶段 4 得到的全局偏移合成total_offset global_offset local_offset把 (key, value) 对写入输出 buffer 的正确位置out[total_offset] local_key; out_values[total_offset] local_value。一趟结束后cur_keys/cur_vals更新为输出张量进入下一趟直到sorting_bits全部处理完返回(cur_keys, cur_vals)。3.4 与 brush-scan 的复用关系brush-sort大量复用brush-scan提供的原语block_scan共享内存块扫描、cube_exclusive_sum/cube_sumcube 级排他前缀和/求和内部采用 plane/subgroup 两阶段扫描MAX_PLANES 32适配不同硬件 wave 宽度见 crates/brush-scan/src/kernels.rs以及lds_index冲突无关的共享内存布局索引。这也解释了为什么brush-scan的源码注释明确写着GPU scans shared across brusha cube-wide block scan for kernels that need a prefix sum in shared memory (the radix sort)...。四、实战调用渲染器中的深度排序与 Tile 排序在 crates/brush-render/src/render.rs 中radix_argsort被调用了两次是观察它真实用途的最佳样例。4.1 深度排序Depth Sort渲染器先把所有高斯投影到相机并求交得到depths每个可见高斯的深度与global_from_presort_gid排序前的全局 id随后以32 位为排序范围进行深度排序crates/brush-render/src/render.rslet (_, global_from_compact_gid) tracing::trace_span!(DepthSort) .in_scope(|| radix_argsort(depths, global_from_presort_gid, 32));返回的global_from_compact_gid是按深度排序后的紧凑全局 id之后用它通过int_gather重新排列intersect_counts并交给prefix_sum计算每个高斯的累计 tile 命中数crates/brush-render/src/render.rs。注意深度是 f32这里以 32 位排序意味着算法对 f32 的位模式直接按无符号整数语义排序——这对应基准中Random32场景的注释what the gaussian-splat depth sort does internally for non-negative floats。4.2 Tile 排序Tile Sort对于每条 intersection 记录渲染器先生成tile_id_from_isect和compact_gid_from_isect然后根据 tile 总数计算所需的排序位数crates/brush-render/src/render.rslet bits u32::BITS - num_tiles.leading_zeros(); let (tile_id_from_isect, compact_gid_from_isect) tracing::trace_span!(Tile sort) .in_scope(|| radix_argsort(tile_id_from_isect, compact_gid_from_isect, bits));这里bits u32::BITS - num_tiles.leading_zeros()是覆盖全部 tile id 所需的最小位数充分体现了 README 所说支持排序任意位数的实用价值——例如 512×512 图像约 1024 个 tile只需 10 位3 趟即可完成排序相比固定 32 位省下大量调度开销。排序后的数组再交给get_tile_offsets生成每个 tile 的起始偏移供光栅化 kernel 按 tile 顺序消费crates/brush-render/src/render.rs。五、质量保障测试与基准5.1 正确性测试src/lib.rs 内置了 4 组测试全部通过wasm_bindgen_test(unsupported tokio::test)同时支持 WebAssembly 浏览器环境与原生 tokio 异步环境使用的设备是CubeDevice::Wgpu(brush_cube::test_helpers::test_device())即真实的 WebGPU 设备test_sorting对 15 个包含小值、大值如2^24 123、重复值、0、递增/递减模式的 key 数组迭代 128 次与 CPU 参考 argsort 逐元素比对 keys 和 valuestest_sorting_big模拟一批高斯的数据形态——按区间随机生成约上万条带重叠的区间键类似高斯投影到 tile 的分布验证真实负载下的正确性test_sorting_large3000 万30_000_000个元素验证大规模下 2D dispatch 的正确性并抽查若干下标确认 values 确实指向原始索引test_sorting_above_scan_block_size7000 万70_000_000个元素——这是针对一个真实回归 bug的测试排序在约 67M keys 时存在静默数据损坏silent corruption根因是num_reduce_wgs BLOCK_SIZE时sort_scan_kernel的扫描越界。修复后该测试逐元素验证 7000 万个 key 与对应 value 的完全正确注释见 src/lib.rs。5.2 微基准divanbenches/sort_bench.rs 提供了独立于渲染的微基准用divanharness false手动main测量纯排序开销。要点包括两种 key 形态TileIdskey 范围 [0, 1024)匹配渲染器 512×512 图像的 tile 预算10 bit → 3 趟和Random32全 32 位随机 key8 趟四个规模档位1M普通帧、10M中等帧、30M旧版本约最大可用规模、70M刚越过 ~67M 扫描悬崖见 benches/sort_bench.rs计时方式输入 buffer 在计时区外预分配计时区内只包含radix_argsort调度 最小化回读只读最后一个 key强制 GPU 完成从而测得纯粹的排序 dispatch 时间u32 直传用create_tensor_from_slice(.., DType::U32)直接构造CubeTensor避免 Burn 的 i32 类型构造器对高位为 1 的 key 报错从而覆盖完整的 u32 取值范围。运行基准的方式cargo bench -p brush-sort运行测试的方式原生设备cargo test -p brush-sort六、小结与使用建议brush-sort是 brush 渲染流水线中GPU 侧有序性的基石它基于 FidelityFX Radix Sort 的 5 阶段流水线count → reduce → scan → scan-add → scatter每趟处理 4 位16 bin通过间接调度支持 GPU 已知元素数量的大规模数组并通过可配置的sorting_bits在排序位数与调度趟数之间做出精细权衡——深度排序用满 32 位Tile 排序则只使用覆盖 tile id 所需的最小位数。从其实现与测试中可以提炼出几条实用经验按需选择排序位数bits u32::BITS - num_tiles.leading_zeros()这类最小位数计算能显著减少趟数是支持任意位数排序这一设计最直接的收益大规模数据需关注扫描悬崖sort_scan_kernel以BLOCK_SIZE为 chunk 的循环扫描设计正是为了避免 reduce 结果超过单块容量时的越界损坏测试test_sorting_above_scan_block_size70M 元素验证了这一点间接调度依赖 GPU 已知的计数 buffer所有 kernel 通过num_keys_arr[0]读取元素数量并terminate!()空 workgroupCPU 侧无需回读即可安全处理运行时才知道的数组长度输入必须 contiguousradix_argsort对 keys/values 有连续性断言调用方需先into_contiguous或确保切片来源连续。如果要在自己的模块中使用brush-sort可以参考渲染器的调用模式准备一对等长的CubeTensorkeys 原始索引 values计算需要的排序位数然后调用radix_argsort获得排序后的 (keys, values) 对再以 values 作为 gather 索引驱动后续 kernel。赞分享计算机视觉图形学深度学习【免费下载链接】brush3D Reconstruction for all项目地址https://gitcode.com/GitHub_Trending/br/brush点击查看免费下载相关推荐Make Me a Hanzi9000 汉字的笔画动画与结构数据你的学习应用离它只差一次 cloneMake Me a Hanzi9000 汉字的笔画动画与结构数据你的学习应用离它只差一次 clone 你半夜想给娃做一个练字 App然后卡住了 假设你是数据集教育Brush 3D高斯泼溅技术实现跨平台高性能渲染架构深度解析在实时3D渲染技术快速迭代的今天Brush项目通过创新的高斯泼溅算法为跨平台高性能渲染提供了完整的工程解决方案。该技术采用椭球体表示法替代传统多边形网格在计算机视觉图形学深度学习Brush 3D高斯泼溅技术跨平台实时渲染的完整实战指南在当今快速发展的3D图形领域传统渲染技术面临着性能瓶颈和硬件依赖的挑战。Brush项目以其创新的高斯泼溅算法为3D重建和实时渲染带来了革命性突破。这项技术不计算机视觉图形学深度学习上一篇AReaL 近端对数概率近似Proximal Log-Probability Approximation为解耦 PPO 省掉一次全量前向传播下一篇tsParticles Sounds 插件配置完全指南为粒子事件绑定音频播放与合成音效创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表