一个最基本的多线程3D渲染器方案

2024-08-31 06:28

本文主要是介绍一个最基本的多线程3D渲染器方案,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

概括

渲染器仍然是大多数现代视频游戏的主要组件。通常,这些流水线通过 3D 图形应用程序接口(如 DirectX、OpenGL )暴露出来。现代,多核 CPU 已广泛应用于游戏机和个人电脑。为了确保 GPU 能持续不断地处理数据,渲染器必须充分利用这一优势。通常在屏幕上渲染出 3D 图像的工作流程依赖于向GPU 发送的可执行命令队列。

可以采用不同的方法将 CPU 与 GPU 分离。例如,一种常见的技术是使用双缓冲或三缓冲方案,CPU 在第 N 帧创建命令,GPU 在第 N+1 帧消耗命令,如图 1 所示。另外,GPU 也可以在同一帧内消耗 数据,以减少延迟,如图 2 所示。一个潜在的缺点是,在帧的早期可能难以避免 GPU 的一些停滞。另一方面,与双缓冲方法相比,内存占用要小得多,这对于内存有限的嵌入式系统尤为重要。

内存模型

无论渲染队列是如何创建的,渲染器最终都可能受到内存访问速度的限制,尤其是当图形命令是通过单线程生成时。近年来,GPU 处理能力与内存延迟之间的性能差距越来越大,由于系统内存中昂贵的数据访问速度,GPU 的工作变得更加困难,如图 3 所示。一个典型的帧会被细分为多个处理流程,例如渲染阴影贴图、渲染主场景和渲染全屏后处理效果。渲染过程中的工作单元通常被称为批次,由一组渲染状态、着色器和几何元素组成,如下表所示:

//setting up a batch
setRenderStates(...);
bindTextures(...);
setShaders(...);
setShaderConstants(...);
setVertexBuffer(...);
setIndexBuffer(...);
drawCall(...);

设置批次主要包括设置渲染对象所需的各种资源的地址。在创建批次的过程中,渲染代码必须遍历场景图,并有可能访问主内存中不同位置的数据。这将导致大量的缓存缺失。对于(out-of-order)乱序CPU(如 Xbox 360 和 PlayStation 3 游戏机中的 PowerPC 芯片)来说,每次缓存缺失都可能导致处理器停滞数百个周期,从而极大地影响性能。这个问题可以通过(out-of-order)乱序cpu来缓解,因为在以前的指令正在等待数据准备就绪时,可能会执行其他指令。

也许有人会说,可以通过重组数据结构来避免许多缓存命中率低的问题。的确,采用缓存感知或缓存忽略算法[2]会有所帮助,尤其是在场景图管理方面,但遗憾的是,图形库也需要自行访问和处理一些数据。顶点缓冲区和索引缓冲区等大型结构通常存储在 GPU 的本地内存中,因此不会造成问题,但着色器常量和纹理配置等许多类型的渲染状态需要每帧复制到命令缓冲区。因此,需要其他解决方案来克服缓存的限制。

并行的构建渲染队列

为确保在最短时间内创建渲染队列,并最大限度地减少内存带宽限制,我们的解决方案使用多个线程,每个线程负责创建一个渲染命令子集。不过,这只有在 3D 图形库提供这种粒度时才有可能实现。值得庆幸的是,Xbox 360、PlayStation 3 和 DirectX 11 开发人员都能做到这一点。在PlayStation 3 中,单元宽带引擎的协同处理单元(SPU)是纯粹的矢量处理器,擅长几何处理。这意味着它们可以轻松执行图形操作,在必要时帮助 GPU 分担工作。为了并行创建渲染队列,一种常见的模式是让主命令缓冲区引用在次命令缓冲区内创建的渲染队列。这些渲染队列在不同的执行单元上并行创建,如图 4 所示。通常情况下,这些辅助缓冲区会处理帧的一个子集,粒度可以是单个绘制调用,也可以是整个通道。

通过将绘制调用及其图形状态的创建并行分配到多个命令缓冲区,创建渲染队列的整体延迟大大降低,这在使用图 2 所示方案时尤为有用。此外,这还使 渲染器更易于扩展到各种配置的多核架构,并有助于生成现代视频游戏中常见的成千上万次绘制调用。

并行化模型

3D渲染器非常适合并行化,因为每次绘制调用 通常可被视为一个独立的工作单元。如以下代码所示,每个任务都会将一大块数据 与对该数据进行操作的一些逻辑配对,如以下代码所示。

int TaskMain(...)
{const int sourceAddr = ...; // source address of dataconst int count = ...; // number of elementsconst int dataSize = count * sizeof(gfxObject);// On some platforms like the PS3,// you may have to keep the data local to the executing unitgfxObject *buffer = (gfxObject *) Allocate(dataSize);DmaGet(buffer, sourceAddr, dataSize);DmaWait(...); // barrier to wait for the data// let's do some workfor (int i = 0; i < count; ++i){buffer[i]->update();}// On some platforms like the PS3,// you may have to store back the data to system memoryDmaPut(buffer, sourceAddr, dataSize);DmaWait(....); // barrier to wait for the data
}

如图 5 所示,这种模式提供了一种高效的并行范式,每个任务都可以排队,并由可用的处理单元处理。这也避免了更标准的多线程方法的一些问题,因为任务提供了更精细的细分,可以更轻松地应对不均衡的计算,而多线程架构可能最终会等待某个特定子系统完成任务。

同步CPU和GPU

在多线程环境中,GPU 和 CPU 的同步处理更为困难,因为多个处理单元可能都想访问相同的数据。为了避免出现任何不一致和竞赛条件,有必要采用诸如互斥或原子等同步原语。通常情况下,GPU 可以报告其在特定内存区域的进度。例如,当 GPU 完成某个特定命令后,它可以将指定值或 “报告 ”写入 CPU 可访问的

在多线程环境中,GPU 和 CPU 的同步处理更为困难,因为多个处理单元可能都想访问相同的数据。为了避免出现任何不一致和竞赛条件,有必要采用诸如互斥或原子等同步原语。通常情况下,GPU 可以报告其在特定内存区域的进度。例如,当 GPU 完成一个特定的命令后,它可以向 CPU 可访问的位置写入一个指定的值或 “报告”,以表示完成。根据架构的不同,CPU 可以轮询报告值或接收某种系统回调。表 1 列出了一些可能的配置。
 

与任何多线程环境一样,需要特别注意避免潜在的竞赛条件或死锁,因为 CPU 和 GPU 都可能以不可预测的时间模式生成和消耗数据。

使用额外的处理资源

在许多游戏中,可用处理单元之间的处理负载并不完全平衡。在这种情况下,值得将通常在主 CPU 上执行的一些操作转移到每帧都可能有空闲时间的其他单元上。例如,密集度较低的图形应用程序可以使用 GPGPU(通用 GPU)代码,利用 CUDA 等技术将物理或人工智能模拟从 CPU 上卸载下来。另一方面,愿意挑战实时 3D 图形极限的游戏可能希望使用空闲的 CPU 内核来执行某些图形操作。如果这些内核恰好提供了一种有趣的 ISA(指令集架构),具有 SIMD 指令,例如 Cell 处理器上的 SPU,那么就有可能卸载 GPU 来执行以下几种操作:

- 几何处理,包括创建地形、树木、贴花或细分表面的程序算法。
- 物理和粒子系统更新。
- 视锥体剔除或遮挡剔除。
- 软件渲染,特别是闭塞查询和后期处理效果。

降低对内存带宽的压力

CPU 和 GPU 的性能都在飞速提升,但内存速度却没有跟上同样的曲线,因此内存带宽成为了一个重要的瓶颈。因此,必须考虑任何有助于最大限度降低对内存系统要求的技术,即使这意味着需要使用更多的 CPU 或 GPU 周期。可以使用的一些技术包括打包着色器输入和输出属性,或在可用的情况下使用 GPU 的细分单元。在 CPU 方面,特殊的几何图形剔除可以避免发送多达 70% 的基元,这些基元最终会被 GPU 丢弃(背向、离屏、零尺寸和退化基元)。另一种技术是在软件中生成一个粗深度缓冲区来执行遮挡查询,而不是依赖 GPU [1],因为后者可能涉及从视频内存中读取数据,而这通常是一条缓慢的路径。

并行执行图形操作

到目前为止,我们已经讨论了一些有助于提高整体性能的技术,但我们还可以更进一步,让不同的处理单元并行工作,以创建一帧的最终图像。现代游戏在一帧内使用多个渲染目标,其中有些目标并不是由 GPU 同时访问的。例如,在 GPU 开始渲染下一帧时,通常可以通过 SPU 等其他处理单元访问后置缓冲区,以执行一些后期处理效果。这样,在保持相同帧频的情况下,最多可以有一整帧的时间来渲染特效,但代价是要增加一帧的延迟(见图 6)。

[1] Johan Andersson."The Intersection of Game Engines and GPUs: Current & Future". Graphics Hardware 2008 .
[2] Sebastien Schertenleib."An Effective Cache-Oblivious Implementation of the ABT Tree". Game Programming Gems 5 , Charles River Media, 2005.

这篇关于一个最基本的多线程3D渲染器方案的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1123138

相关文章

无人叉车3d激光slam多房间建图定位异常处理方案-墙体画线地图切分方案

墙体画线地图切分方案 针对问题:墙体两侧特征混淆误匹配,导致建图和定位偏差,表现为过门跳变、外月台走歪等 ·解决思路:预期的根治方案IGICP需要较长时间完成上线,先使用切分地图的工程化方案,即墙体两侧切分为不同地图,在某一侧只使用该侧地图进行定位 方案思路 切分原理:切分地图基于关键帧位置,而非点云。 理论基础:光照是直线的,一帧点云必定只能照射到墙的一侧,无法同时照到两侧实践考虑:关

高效+灵活,万博智云全球发布AWS无代理跨云容灾方案!

摘要 近日,万博智云推出了基于AWS的无代理跨云容灾解决方案,并与拉丁美洲,中东,亚洲的合作伙伴面向全球开展了联合发布。这一方案以AWS应用环境为基础,将HyperBDR平台的高效、灵活和成本效益优势与无代理功能相结合,为全球企业带来实现了更便捷、经济的数据保护。 一、全球联合发布 9月2日,万博智云CEO Michael Wong在线上平台发布AWS无代理跨云容灾解决方案的阐述视频,介绍了

基本知识点

1、c++的输入加上ios::sync_with_stdio(false);  等价于 c的输入,读取速度会加快(但是在字符串的题里面和容易出现问题) 2、lower_bound()和upper_bound() iterator lower_bound( const key_type &key ): 返回一个迭代器,指向键值>= key的第一个元素。 iterator upper_bou

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

【IPV6从入门到起飞】5-1 IPV6+Home Assistant(搭建基本环境)

【IPV6从入门到起飞】5-1 IPV6+Home Assistant #搭建基本环境 1 背景2 docker下载 hass3 创建容器4 浏览器访问 hass5 手机APP远程访问hass6 更多玩法 1 背景 既然电脑可以IPV6入站,手机流量可以访问IPV6网络的服务,为什么不在电脑搭建Home Assistant(hass),来控制你的设备呢?@智能家居 @万物互联

JavaFX应用更新检测功能(在线自动更新方案)

JavaFX开发的桌面应用属于C端,一般来说需要版本检测和自动更新功能,这里记录一下一种版本检测和自动更新的方法。 1. 整体方案 JavaFX.应用版本检测、自动更新主要涉及一下步骤: 读取本地应用版本拉取远程版本并比较两个版本如果需要升级,那么拉取更新历史弹出升级控制窗口用户选择升级时,拉取升级包解压,重启应用用户选择忽略时,本地版本标志为忽略版本用户选择取消时,隐藏升级控制窗口 2.

如何选择SDR无线图传方案

在开源软件定义无线电(SDR)领域,有几个项目提供了无线图传的解决方案。以下是一些开源SDR无线图传方案: 1. **OpenHD**:这是一个远程高清数字图像传输的开源解决方案,它使用SDR技术来实现高清视频的无线传输。OpenHD项目提供了一个完整的工具链,包括发射器和接收器的硬件设计以及相应的软件。 2. **USRP(Universal Software Radio Periphera

MiniGPT-3D, 首个高效的3D点云大语言模型,仅需一张RTX3090显卡,训练一天时间,已开源

项目主页:https://tangyuan96.github.io/minigpt_3d_project_page/ 代码:https://github.com/TangYuan96/MiniGPT-3D 论文:https://arxiv.org/pdf/2405.01413 MiniGPT-3D在多个任务上取得了SoTA,被ACM MM2024接收,只拥有47.8M的可训练参数,在一张RTX

多线程解析报表

假如有这样一个需求,当我们需要解析一个Excel里多个sheet的数据时,可以考虑使用多线程,每个线程解析一个sheet里的数据,等到所有的sheet都解析完之后,程序需要提示解析完成。 Way1 join import java.time.LocalTime;public class Main {public static void main(String[] args) thro

C 语言的基本数据类型

C 语言的基本数据类型 注:本文面向 C 语言初学者,如果你是熟手,那就不用看了。 有人问我,char、short、int、long、float、double 等这些关键字到底是什么意思,如果说他们是数据类型的话,那么为啥有这么多数据类型呢? 如果写了一句: int a; 那么执行的时候在内存中会有什么变化呢? 橡皮泥大家都玩过吧,一般你买橡皮泥的时候,店家会赠送一些模板。 上