建筑物混凝土裂缝剥落泛碱碳化缺陷识别分割数据集labelme格式5276张10类别
2026/1/9 21:32:32
GPU是典型的"被动执行设备",自己不会主动渲染,所有渲染任务都由CPU通过"命令缓冲区(Command Buffer)"下方,流程分 四步:1).CPU准备"渲染材料"+"执行指令"游戏每帧,CPU会先完成"应用阶段"的工作 a.准备数据:计算模型的世界坐标,骨骼动画顶点,材质参数(比如颜色,纹理采样器),将这些数据整理成GPU能识别的格式 b.准备指令:明确告诉GPU"要做什么",比如:-绑定哪个Shader程序-绑定哪个纹理/顶点缓冲区-执行DrawCall(绘制命令,比如"绘制这个模型的1000个三角形")-设置渲染目标(比如把画面画到帧缓冲区,还是RenderTexture)2).CPU把"指令 + 数据地址"打包到命令缓冲区 CPU不会直接和GPU对话,而是通过图形驱动,把上述"指令 + 数据在显存的地址"写入一块GPU可以访问的内存区域(命令缓冲 区)a.命令缓冲区里不存原始数据(比如顶点,纹理),只存"数据的显存地址"和"操作指令"(比如:绑定地址0x123的纹理)b.Unity的CommandBuffer类,就是对底层命令缓冲区的封装-可以手动创建CommandBuffer,添加绘制指令,再提交给GPU 实现自定义渲染逻辑3).CPU把命令缓冲区提交到GPU的命令队列 当CPU把一帧的渲染指令都打包进命令缓冲区后,会通过图形API(比如:Dx12的ExecuteCommandLists),将命令缓冲区提交 到GPU的命令队列 a.命令队列是GPU内部的"任务排队区",按"先提交先执行"的顺序处理 b.此时CPU的工作就暂时结束了,可以去处理下一轮的游戏逻辑,不用等GPU渲染完4).GPU主动"取任务 + 执行渲染"GPU内部有一个"命令处理器(Command Processor)",会持续轮询命令队列 a.一旦发现队列里有新的命令缓冲区,就将它取出来 b.按指令顺序执行:绑定shader->绑定显存数据->执行DrawCall->逐像素渲染 c.整个过程完全由GPU硬件独立完成,无需CPU干预Unity中的直观例子:脚本中调用Graphics.DrawMesh(mesh,matrix,material,layer),本质就是让CPU往命令缓冲区里添加了一条"绘制这个""Mesh"的指令,最终提交给GPU执行CPU和GPU是"异步并行工作"的(CPU处理下一帧逻时,GPU还在渲染上一帧),CPU要获知GPU渲染完成,靠的是硬件同步原语,核 心有两种方式:1).同步等待(阻塞CPU)-低性能,仅调试用 这是最直接但最影响性能的方式,原理是"CPU主动等GPU发完成新号"a.CPU提交命令缓冲区时,会创建一个"围栏(Fence)",将这个围栏和命令缓冲区绑定 b.围栏有两种状态:未触发(GPU未完成)/已触发(GPU完成)c.CPU调用WaitForFence()函数,主动等待围栏状态变为"已触发"-此时CPU会完全阻塞,不处理任何任务,直到GPU渲染完毕 d.GPU执行命令缓冲区的所有指令后,会自动把绑定的围栏标记为"已触发",CPU收到信号后才继续工作Unity中的对应操作 调用Graphics.WaitForPresent()或AsyncGPUReadback.WaitForCompletion()本质就是触发同步等待,游戏运行时绝对要避免2).异步通知(不阻塞CPU)-高性能,游戏开发主流 这是最优解,原理是"GPU完成后主动给CPU发回调信号, CPU无需等待, 可继续处理其他任务"a.CPU提交命令缓冲区时,注册一个回调函数,并绑定一个"信号量(Semaphore)"b.GPU执行完渲染指令后,会触发信号量,并通过图形驱动通知CPU"任务完成"c.CPU收到信号后,在空闲时执行回调函数(比如:处理渲染结果,读取RenderTexture像素,更新UI显示)d.整个过程CPU完全不阻塞,始终在处理游戏逻辑,只有收到信号后才花少量时间执行回调Unity中的核心应用 a.AsyncGPUReadback:读取显存中RenderTexture的像素数据时,用AsyncGPUReadback.Request(texture,(request)=>{if(request.hasError)return;vardata=request.GetData<Color>();})—— 这个Lambda表达式就是异步回调函数 GPU读取完成后才会执行 b.URP/HDRP的后处理回调:比如在渲染完成后执行高斯模糊,本质也是GPU触发的异步回调显存数据(纹理,顶点,shader等)的上传时机,核心原则是"静态资源一次性上传, 动态资源每帧按需上传",完全由CPU主动 发起,分两种场景:1).静态资源(长期复用)-加载时一次性上传,常驻显存 静态资源表示"游戏运行中很少变化的资源",比如场景模型,UI纹理,Shader程序,上传时机资源加载完成后,第一次使用前 a.CPU从硬盘加载资源(比如AssetBundle加载纹理/模型),先把压缩数据放到内存 b.CPU调用图形API(比如Dx12CreateCommittedResources),向GPU申请一块显存空间 c.CPU通过PCle总线,把内存中的静态资源数据一次性拷贝到显存(压缩格式,比如ETC2/ASTC,无需解压)d.资源上传完成后,会在显存中常驻,直到游戏退出或主动释放(比如Texture2D.Destroy)e.后续每帧渲染时,CPU只需在命令缓冲区中"绑定显存地址",无需重复上传Texture2d.uploadedToGPU-这个属性为true时,说明该纹理已经上传到显存,内存中只保留一份轻量级数据,原始数据会 被Unity自动释放(节省内存)2).动态资源(每帧变化)-每帧提交DrawCall前,实时上传 动态资源指"每帧都会变化的资源",比如粒子系统的顶点数据,骨骼动画的蒙皮顶点,动态生成的RenderTexture,上传时机 是"每帧CPU准备渲染指令时, 提交DrawCall前"a.每帧游戏逻辑阶段,CPU计算动态资源的最新数据(比如粒子的新位置,骨骼的新姿态),更新内存中的顶点缓冲区 b.CPU调用"更新显存"指令(比如Dx12的UpdateSubresource),将内存中更新后的动态数据拷贝到显存的"动态区域"-这里的拷贝是"增量拷贝(只传变化的部分, 不是全量)",减少PCIe总线压力 c.数据上传完成后,CPU才会把"绑定该动态资源"的指令写入命令缓冲区,提交给GPU d.GPU渲染时,直接从显存的动态区域读取最新数据延迟上传(懒加载)-Unity的默认优化 Unity对静态资源默认采用"延迟上传"策略 a.资源加载后,不会立刻上传到显存,而是等"第一次被渲染时"才触发上传 b.比如你加载了一个场景纹理,但前10帧都没有用到它,Unity不会浪费显存和PCIe带宽区上传,直到第11帧它被绑定到材质 上,才会由CPU发起上传CPU-GPU协作+数据上传的一帧流程 a.CPU游戏逻辑阶段:计算 AI、物理碰撞、更新模型位置,生成动态顶点数据 b.CPU数据上传阶段-静态资源:若未上传则延迟上传-动态资源:增量拷贝到显存动态区域 c.CPU指令打包阶段:构建命令缓冲区,写入"绑定资源 + 执行 DrawCall"指令 d.CPU提交阶段:把命令缓冲区提交到GPU命令队列,注册异步回调 e.GPU执行阶段:从命令队列取指令,执行渲染,输出画面到屏幕 f.同步阶段:GPU渲染完成,触发信号量,CPU执行回调(比如读取渲染结果)g.回到步骤1,开始下一帧