10. CPU-GPU协作渲染

1.GPU是如何知道要渲染对象

2.CPU 怎么知道 GPU 渲染完毕

3.GPU 的显存数据是什么时机上传的


1.GPU是如何知道要渲染对象

csharp 复制代码
GPU是典型的"被动执行设备", 自己不会主动渲染, 所有渲染任务都由CPU通过"命令缓冲区(Command Buffer)"下方, 流程分

四步:

1).CPU准备"渲染材料" + "执行指令"

游戏每帧, CPU会先完成"应用阶段"的工作

a.准备数据: 计算模型的世界坐标, 骨骼动画顶点, 材质参数(比如颜色, 纹理采样器), 将这些数据整理成GPU能识别的格式

b.准备指令: 明确告诉GPU"要做什么", 比如:

- 绑定哪个Shader程序

- 绑定哪个纹理/顶点缓冲区

- 执行DrawCall(绘制命令, 比如"绘制这个模型的1000个三角形")

- 设置渲染目标(比如把画面画到帧缓冲区, 还是RenderTexture)
csharp 复制代码
2).CPU把"指令 + 数据地址"打包到命令缓冲区

CPU不会直接和GPU对话, 而是通过图形驱动, 把上述"指令 + 数据在显存的地址"写入一块GPU可以访问的内存区域(命令缓冲

区)

a.命令缓冲区里不存原始数据(比如顶点, 纹理), 只存"数据的显存地址"和"操作指令"(比如: 绑定地址0x123的纹理)

b.Unity的CommandBuffer类, 就是对底层命令缓冲区的封装 - 可以手动创建CommandBuffer, 添加绘制指令, 再提交给GPU

实现自定义渲染逻辑
csharp 复制代码
3).CPU把命令缓冲区提交到GPU的命令队列

当CPU把一帧的渲染指令都打包进命令缓冲区后, 会通过图形API(比如: Dx12的ExecuteCommandLists), 将命令缓冲区提交

到GPU的命令队列

a.命令队列是GPU内部的"任务排队区", 按"先提交先执行"的顺序处理

b.此时CPU的工作就暂时结束了, 可以去处理下一轮的游戏逻辑, 不用等GPU渲染完
csharp 复制代码
4).GPU主动"取任务 + 执行渲染"

GPU内部有一个"命令处理器(Command Processor)", 会持续轮询命令队列

a.一旦发现队列里有新的命令缓冲区, 就将它取出来

b.按指令顺序执行: 绑定shader -> 绑定显存数据 -> 执行DrawCall -> 逐像素渲染

c.整个过程完全由GPU硬件独立完成, 无需CPU干预
csharp 复制代码
Unity中的直观例子:

脚本中调用Graphics.DrawMesh(mesh, matrix, material, layer), 本质就是让CPU往命令缓冲区里添加了一条"绘制这个"

"Mesh"的指令, 最终提交给GPU执行

2.CPU 怎么知道 GPU 渲染完毕

csharp 复制代码
CPU和GPU是"异步并行工作"的(CPU处理下一帧逻时, GPU还在渲染上一帧), CPU要获知GPU渲染完成,靠的是硬件同步原语,核

心有两种方式:

1).同步等待(阻塞CPU) - 低性能, 仅调试用

这是最直接但最影响性能的方式, 原理是"CPU主动等GPU发完成新号"

a.CPU提交命令缓冲区时, 会创建一个"围栏(Fence)", 将这个围栏和命令缓冲区绑定

b.围栏有两种状态: 未触发(GPU未完成)/已触发(GPU完成)

c.CPU调用WaitForFence()函数, 主动等待围栏状态变为"已触发" - 此时CPU会完全阻塞, 不处理任何任务,直到GPU渲染完毕

d.GPU执行命令缓冲区的所有指令后, 会自动把绑定的围栏标记为"已触发", CPU收到信号后才继续工作
csharp 复制代码
Unity中的对应操作

调用Graphics.WaitForPresent()或AsyncGPUReadback.WaitForCompletion()本质就是触发同步等待, 游戏运行时绝对要避免

csharp 复制代码
2).异步通知(不阻塞CPU) - 高性能, 游戏开发主流

这是最优解, 原理是"GPU完成后主动给CPU发回调信号, CPU无需等待, 可继续处理其他任务"

a.CPU提交命令缓冲区时, 注册一个回调函数, 并绑定一个"信号量(Semaphore)"

b.GPU执行完渲染指令后, 会触发信号量, 并通过图形驱动通知CPU"任务完成"

c.CPU收到信号后, 在空闲时执行回调函数(比如: 处理渲染结果, 读取RenderTexture像素, 更新UI显示)

d.整个过程CPU完全不阻塞, 始终在处理游戏逻辑, 只有收到信号后才花少量时间执行回调
csharp 复制代码
Unity中的核心应用

a.AsyncGPUReadback: 读取显存中RenderTexture的像素数据时, 用AsyncGPUReadback.Request(texture, (request)=>{

if(request.hasError) return; var data = request.GetData<Color>(); }) ------ 这个Lambda表达式就是异步回调函数

GPU读取完成后才会执行

b.URP/HDRP的后处理回调: 比如在渲染完成后执行高斯模糊, 本质也是GPU触发的异步回调

3.GPU 的显存数据是什么时机上传的

csharp 复制代码
显存数据(纹理, 顶点, shader等)的上传时机, 核心原则是"静态资源一次性上传, 动态资源每帧按需上传", 完全由CPU主动

发起, 分两种场景:

1).静态资源(长期复用) - 加载时一次性上传, 常驻显存

静态资源表示"游戏运行中很少变化的资源", 比如场景模型, UI纹理, Shader程序,上传时机资源加载完成后, 第一次使用前

a.CPU从硬盘加载资源(比如AssetBundle加载纹理/模型), 先把压缩数据放到内存

b.CPU调用图形API(比如Dx12 CreateCommittedResources), 向GPU申请一块显存空间

c.CPU通过PCle总线, 把内存中的静态资源数据一次性拷贝到显存(压缩格式, 比如ETC2/ASTC, 无需解压)

d.资源上传完成后, 会在显存中常驻, 直到游戏退出或主动释放(比如Texture2D.Destroy)

e.后续每帧渲染时, CPU只需在命令缓冲区中"绑定显存地址", 无需重复上传
csharp 复制代码
Texture2d.uploadedToGPU - 这个属性为true时, 说明该纹理已经上传到显存, 内存中只保留一份轻量级数据, 原始数据会

被Unity自动释放(节省内存)

2).动态资源(每帧变化) - 每帧提交DrawCall前, 实时上传

动态资源指"每帧都会变化的资源", 比如粒子系统的顶点数据, 骨骼动画的蒙皮顶点, 动态生成的RenderTexture, 上传时机

是"每帧CPU准备渲染指令时, 提交DrawCall前"

a.每帧游戏逻辑阶段, CPU计算动态资源的最新数据(比如粒子的新位置, 骨骼的新姿态), 更新内存中的顶点缓冲区

b.CPU调用"更新显存"指令(比如Dx12的UpdateSubresource), 将内存中更新后的动态数据拷贝到显存的"动态区域"

- 这里的拷贝是"增量拷贝(只传变化的部分, 不是全量)", 减少PCIe总线压力

c.数据上传完成后, CPU才会把"绑定该动态资源"的指令写入命令缓冲区, 提交给GPU

d.GPU渲染时, 直接从显存的动态区域读取最新数据

csharp 复制代码
延迟上传(懒加载) - Unity的默认优化

Unity对静态资源默认采用"延迟上传"策略

a.资源加载后, 不会立刻上传到显存, 而是等"第一次被渲染时"才触发上传

b.比如你加载了一个场景纹理, 但前10帧都没有用到它, Unity不会浪费显存和PCIe带宽区上传, 直到第11帧它被绑定到材质

上, 才会由CPU发起上传
csharp 复制代码
CPU-GPU协作 + 数据上传的一帧流程

a.CPU游戏逻辑阶段: 计算 AI、物理碰撞、更新模型位置, 生成动态顶点数据

b.CPU数据上传阶段

- 静态资源: 若未上传则延迟上传

- 动态资源: 增量拷贝到显存动态区域

c.CPU指令打包阶段: 构建命令缓冲区, 写入"绑定资源 + 执行 DrawCall"指令

d.CPU提交阶段: 把命令缓冲区提交到GPU命令队列, 注册异步回调

e.GPU执行阶段: 从命令队列取指令, 执行渲染, 输出画面到屏幕

f.同步阶段: GPU渲染完成, 触发信号量, CPU执行回调(比如读取渲染结果)

g.回到步骤1, 开始下一帧
相关推荐
小贺儿开发1 天前
Unity3D 智慧城市管理平台
数据库·人工智能·unity·智慧城市·数据可视化
June bug2 天前
【领域知识】休闲游戏一次发版全流程:Google Play + Apple App Store
unity
星夜泊客2 天前
C# 基础:为什么类可以在静态方法中创建自己的实例?
开发语言·经验分享·笔记·unity·c#·游戏引擎
dzj20212 天前
PointerEnter、PointerExit、PointerDown、PointerUp——鼠标点击物体,则开始旋转,鼠标离开或者松开物体,则停止旋转
unity·pointerdown·pointerup
心前阳光2 天前
Unity 模拟父子关系
android·unity·游戏引擎
在路上看风景2 天前
26. Mipmap
unity
咸鱼永不翻身2 天前
Unity视频资源压缩详解
unity·游戏引擎·音视频
在路上看风景2 天前
4.2 OverDraw
unity
在路上看风景2 天前
1.10 CDN缓存
unity
ellis19702 天前
Unity插件SafeArea Helper适配异形屏详解
unity