Chapter 115: Memory Bandwidth Optimization
本章讨论一个具体问题:当一帧的 GPU 时间主要受内存带宽限制时,如何从视觉结果、资源读写、访问模式和 profiler counter 中定位瓶颈,并把优化动作落到 texture format、buffer layout、mip streaming、压缩格式和资源生命周期上。
带宽瓶颈通常出现在高分辨率渲染、deferred shading、post-processing、shadow、SSR、TAA、particle 和 compute-driven pipeline 中。它的画面表现可能只是帧率下降,真正的代价发生在 GPU 从显存、统一内存、L2、texture cache、render target compression 或 tile memory 中搬运数据的路径上。读者读完本章应能追踪一帧中哪些 pass 在读写大量数据,判断访问模式是否破坏 cache 与 coalescing,再用带宽证据选择资源压缩、布局调整或流式加载策略。
贯穿本章的材料是一帧 4K deferred renderer:G-buffer pass 写入 albedo、normal、material、velocity 和 depth,lighting pass 读取这些纹理并写 HDR color,随后 bloom、TAA、tone mapping 和 UI pass 继续读写中间 render target。这个场景的症状是分辨率从 1440p 提升到 4K 后,GPU frame time 接近按像素数增长,shader 指令没有明显增加,cache miss、memory throughput 和 render target bandwidth 同时升高。这个症状把问题指向带宽路径,而不先指向 draw call 数量或 shader 算术量。
分析带宽优化时要把材料、结论和方法分开。材料是一帧的 pass 列表、资源格式、分辨率、采样次数、buffer 访问方式和 counter。结论回答当前瓶颈来自 texture fetch、buffer load/store、render target write、resolve/copy 还是 streaming。方法是一套可复用顺序:先按 pass 定位带宽峰值,再按资源读写量估算理论成本,再看 cache/coalescing 证据,最后选择改变格式、布局、采样范围或生命周期的优化动作。
115.1 GPU 内存带宽的限制及影响
GPU 内存带宽表示单位时间内 GPU 能从内存层级读取和写入的数据量。图形管线中的带宽消耗来自 texture sampling、buffer access、render target output、depth/stencil、MSAA resolve、copy、clear、post-process ping-pong、readback 和资源上传。它的上限受显存接口、统一内存带宽、cache 层级、压缩路径、tile memory、功耗策略和当前并发工作负载共同约束。
在贯穿帧中,4K 分辨率是带宽压力的放大器。3840×2160 大约有 829 万像素;一个 full-screen pass 每像素读取 4 个 16 字节纹理并写入一个 8 字节 HDR render target,单 pass 的理想读写量已经接近 (4 × 16 + 8) × 829万 ≈ 597 MB。这个估算还没有包含 cache line 粒度、过滤采样、mipmap、MSAA、blend、compression metadata、layout transition、resolve 和后续 pass 的重复读写。只要多个全屏 pass 串联,带宽很快会成为 frame time 的主导成本。
带宽瓶颈的可观察症状有稳定模式。第一,帧时间随分辨率增长明显,降低分辨率或减少 full-screen pass 会带来接近线性的收益。第二,shader ALU 指令减少后收益有限,因为 GPU 等待数据返回的时间仍占主导。第三,render target 格式、G-buffer 数量、history buffer 和后处理链改变后,GPU 时间立即变化。第四,profiler 中 memory throughput、L2 traffic、texture throughput、color/depth write bandwidth 或 cache miss 指标靠近平台上限。
带宽限制会影响 GPU 执行方式。shader wave 或 warp 发出 texture fetch、buffer load 或 store 指令后,如果数据没有命中本地 cache,执行单元需要等待更远层级返回数据。GPU 可以通过切换到其他 wave 隐藏部分延迟;当所有可调度 wave 都在等待内存,计算单元出现 memory stall。此时提升 occupancy 可能提供一些隐藏延迟的空间,但根因仍是单位像素或单位线程需要搬运的数据太多。
带宽成本要按资源路径分层判断。texture fetch 经过 texture unit、format decode、filtering、mip selection 和 texture cache;structured buffer 或 storage buffer 访问更依赖连续地址和 coalescing;render target write 受像素格式、blend、MSAA、compression 和 tile resolve 影响;copy/resolve 主要受读写量和格式转换影响。把这些路径混成一个“内存慢”会导致优化动作失焦。
对贯穿帧的第一步判断应从 pass 级读写量开始。G-buffer pass 写多个 render target,lighting pass 读多个 G-buffer 并写 HDR color,TAA pass 读当前帧、history、velocity 和 depth,bloom chain 反复 downsample/upsample。每个 pass 的数据量可以用 像素数 × 每像素读写字节数 × 采样次数 做初算。这个初算给出带宽预算的下界,profiler counter 用来验证真实成本高于下界的原因。
下面的路径图用于把带宽问题从画面症状收敛到具体资源。它只覆盖单机实时渲染中的 frame 级分析,网络传输、视频编码和离线资产压缩不在本章范围内。
这条路径的核心点是先定位 pass,再定位资源。带宽优化很少由一个全局开关完成;它通常由多个局部动作叠加,包括减少 G-buffer 字节数、压缩纹理、降低无效采样、让 buffer 连续访问、缩短中间 render target 生命周期和减少 full-screen ping-pong。
115.2 Data Locality and Memory Access Pattern Cost
Data locality 指访问数据在时间和空间上是否集中。空间局部性表示相邻线程访问相邻地址,时间局部性表示同一批数据在短时间内被重复使用。GPU 的 cache、coalescing 和 texture unit 都偏好局部性强的访问模式;局部性差会把一次逻辑读取放大成多次 memory transaction,并让更多 wave 等待内存。
Coalescing 是并行线程的内存请求合并过程。在一个 wave 或 warp 中,如果 lane 0 到 lane 31 读取连续地址,硬件可以把多个 lane 的读取合并成少量内存事务。如果每个 lane 根据随机索引读取不同 cache line,请求数量上升,L1/L2 命中率下降,memory throughput 增加,shader stall 随之升高。对于 structured buffer、storage buffer、SSBO、UAV 和 compute shader 数据结构,这个规律尤其直接。
贯穿帧中的 lighting pass 表面上只是 full-screen shader。它按屏幕顺序访问 G-buffer,因此 albedo、normal、roughness 和 depth 的 2D texture fetch 具备较好的空间局部性。真正可能破坏局部性的路径是 tile/cluster light list、shadow atlas 随机采样、SSR ray marching、TAA history reprojection 和 material indirection。它们把每个像素的读取地址从屏幕连续关系转成由深度、运动向量、光源索引或材质 ID 决定的跳转关系。
访问模式成本要按执行粒度解释。Fragment shader 的相邻像素通常同属一个 quad 或 wave 的邻近 lane,采样同一 mip level 的相邻 texel 时 texture cache 命中率较高。Compute shader 的线程排列由 workgroup 和 dispatch mapping 决定;如果 thread_id.x 对应图像 x 坐标,连续线程读取连续 texel,访问更容易合并。如果线性索引被哈希、间接表或不规则链表打散,同样的算法会消耗更多带宽。
Buffer layout 会改变 coalescing。Array of Structs 把一个对象的多个属性放在一起,适合每次读取完整对象;Struct of Arrays 把同类属性连续存放,适合 shader 只读取其中少量字段。以 particle update 为例,如果当前 pass 只读取 position 和 velocity,SoA 能让 position buffer 和 velocity buffer 形成连续读取;AoS 可能把 color、lifetime、random seed 也带入 cache line,增加无效数据搬运。
下面的简化 HLSL 片段用于说明读取粒度如何影响带宽。它是简化片段,只展示同一 pass 中“读取需要字段”和“搬运无用字段”的差异。
struct ParticleAoS
{
float3 position;
float3 velocity;
float4 color;
float lifetime;
uint randomSeed;
};
StructuredBuffer<ParticleAoS> particlesAoS;
StructuredBuffer<float4> particlePosition;
StructuredBuffer<float4> particleVelocity;
float3 UpdatePositionAoS(uint id, float dt)
{
ParticleAoS p = particlesAoS[id];
return p.position + p.velocity * dt;
}
float3 UpdatePositionSoA(uint id, float dt)
{
float3 position = particlePosition[id].xyz;
float3 velocity = particleVelocity[id].xyz;
return position + velocity * dt;
}
这个例子要证明布局必须跟当前 pass 的访问字段匹配:只读 position 和 velocity 的 pass 应让这两个字段连续、对齐、少带无用字节;需要完整材质参数的 pass 可以接受 AoS 或紧凑 struct。判断顺序是先列出当前 shader 实际读取字段,再看线程访问索引是否连续,最后计算每个 lane 搬运的有效字节占比。
Texture access pattern 的成本还受 mip level 和过滤模式影响。采样高分辨率纹理但屏幕投影很小,会让多个像素访问跨越较大的 texel 区域,cache 利用率下降并产生 aliasing。正确的 mipmap 让远处物体读取更小 mip level,减少带宽并提升采样稳定性。Anisotropic filtering 会增加采样点数量,它提高斜视角纹理质量,也会增加 texture unit 和 bandwidth 压力。
随机访问并非一律错误。SSR、voxel cone tracing、ray tracing denoising、particle binning 和 GPU culling 都可能需要不规则访问。工程判断要看随机读取是否发生在高像素覆盖、高采样次数、高频 pass 中。如果随机访问只发生在低分辨率 buffer 或少量可见元素上,成本可控;如果它发生在 4K full-screen pass 的每个像素上,优化优先级上升。
115.3 优化纹理/缓冲访问行为
纹理和缓冲优化的目标是减少每次 pass 必须搬运的数据,并让剩余访问尽量命中 cache 或形成连续 memory transaction。优化顺序应从“格式是否过宽”开始,再检查“采样范围是否过大”,随后检查“布局是否匹配当前 pass”,最后处理 alignment、padding、mip streaming 和中间资源生命周期。
Texture format 是最直接的带宽杠杆。RGBA16F 每像素 8 字节,RGBA8 每像素 4 字节,R8 每像素 1 字节,block compression 的物理存储通常更低。G-buffer 中的 albedo 通常不需要 HDR float;roughness、metallic、AO 可以打包到少量通道;normal 可以使用两通道编码、octahedral encoding 或平台支持的压缩格式。格式调整要同时检查质量、blend 支持、filter 支持、render target 支持和后续 shader decode 成本。
贯穿帧的 G-buffer 可以先做字节级审计。原始设计可能使用 albedo RGBA8、normal RGBA16F、material RGBA8、velocity RG16F、depth D32F。如果 normal 改成两通道 RG16F 或合适的 packed encoding,material 把 roughness/metallic/AO/specular 打包进一个 RGBA8,depth 改成满足精度需求的格式,lighting pass 每像素读取量会下降。收益来自所有后续读取 G-buffer 的 pass,而不仅来自写入阶段。
Mipmap 与 streaming 改变的是“读哪个分辨率的数据”。远处材质、反射 probe、shadow、virtual texture page 和 environment map 都应按屏幕投影选择 mip 或 resident page。缺少 mipmap 会让远处物体读取过高分辨率 texel;streaming 过激会带来缺页、模糊或上传尖峰。稳定策略是根据屏幕占用、相机速度、材质优先级和内存预算计算 mip residency,并把上传分摊到多个 frame。
Buffer layout 优化要贴合 shader 的读取字段。Uniform/constant buffer 适合小量、频繁复用、广播式读取的数据;structured/storage buffer 适合大量对象数据;texture buffer 或 2D texture 适合需要 filtering、format decode 或空间 cache 的数据。对象列表、light list、bone matrix、particle state 和 material table 的布局应分别按访问模式设计,不能套用同一种 struct。
Alignment 和 padding 会影响每次 transaction 的边界。很多 API 和硬件对 uniform buffer offset、vertex attribute stride、row pitch、texture copy alignment 和 storage buffer 访问有明确限制。工程上应把对齐要求集中在资源分配层,shader 读取层只面对已经对齐的结构。对齐会增加少量 padding,但它换来更稳定的读取边界和更少的跨 cache line 访问。
下面的资源审计表用于贯穿帧。它是一种把“资源格式”和“带宽动作”放在同一行检查的方法,具体条目要按项目资源表调整。
| 资源 | 原始用途 | 带宽风险 | 优化动作 | 验证证据 |
|---|---|---|---|---|
| Albedo | lighting pass 读取 | 使用过宽 HDR 格式会增加每像素读取量 | 使用 RGBA8 或压缩贴图,保持色彩空间转换正确 | lighting pass texture bytes 降低,画面色彩一致 |
| Normal | lighting 和 SSAO 读取 | RGBA16F 成本高,且 alpha 通道常闲置 | 使用 RG16F、packed normal 或 BC5 类格式 | 法线 debug view 连续,specular 无明显断层 |
| Material | roughness/metallic/AO | 多张小纹理会增加采样和 cache 压力 | 通道打包,统一采样坐标 | texture fetch 数下降,材质边界正确 |
| HDR Color | 后处理链读写 | 多次 full-screen ping-pong 放大写带宽 | 合并 pass、降低中间格式或半分辨率处理 | render target bandwidth 降低,bloom/TAA 稳定 |
| Light List | tiled lighting | 随机 buffer 读取破坏 coalescing | tile 连续存储,按屏幕 tile 组织索引 | buffer load stall 降低,光照结果一致 |
优化纹理访问时要区分 source texture 和 render target。Source texture 可以使用 BC、ETC、ASTC 等 GPU 可采样压缩格式;render target 通常依赖硬件颜色压缩、tile compression 或专用 render target compression,不能随意使用离线 block compression 作为写入目标。Microsoft 的 Direct3D 11 texture block compression documentation 把 BC6H 和 BC7 分别放到 HDR color 与高质量 RGB/RGBA 压缩语境中,这类资料适合支撑“源纹理格式选择”,不直接推出所有平台的 render target 写入策略。
Pass 合并能减少中间资源读写。Tone mapping、color grading、vignette、gamma correction 和简单 UI composite 有时可以合并;bloom、TAA、SSR、DoF 这类依赖 history、邻域采样或多尺度结构的 pass 则要保留清晰边界。合并决策看三件事:是否能复用同一输入、是否减少一次 full-screen write/read、是否增加 shader register pressure 或分支复杂度。合并后的 shader 如果导致 occupancy 降低或 cache 行为变差,收益会被抵消。
115.4 压缩与流式资源管理技巧
压缩减少带宽压力的方式有两类。第一类是 GPU 采样压缩格式,例如 BCn、ETC2、ASTC、PVRTC 等,它们让源纹理以块压缩形式驻留在 GPU 内存中,由 texture unit 解码。第二类是运行时或硬件内部压缩,例如 render target compression、depth compression、delta color compression 和 tile compression,它们减少 render target、depth 或 tile resolve 的实际读写量。工程上要把两类压缩分开配置和验证。
源纹理压缩适合 albedo、normal、mask、roughness、environment、lightmap 和 decal。BC7 常用于高质量 LDR color,BC5 常用于两通道 normal,BC6H 常用于 HDR environment 或 lightmap;移动平台常见 ASTC 或 ETC2。压缩格式选择依赖平台支持、纹理内容、质量目标、alpha 需求、线性/非线性色彩空间和工具链。跨平台引擎通常在资产构建阶段生成多个目标格式包,把运行时转换大批贴图从 frame 路径中移出。
压缩带来的收益不只体现在内存占用。GPU 从内存读取压缩块,再在 texture unit 中解码,通常能减少外部带宽需求;代价是纹理质量误差、压缩块伪影、格式支持差异、离线编码时间和某些采样路径的限制。粗糙度、法线和 UI 纹理对伪影的敏感度不同,因此压缩决策要按内容类型制定。Normal map 的压缩伪影会改变光照,mask 纹理的误差可能导致材质边界闪烁,color 纹理的误差常由艺术容忍度决定。
运行时 render target compression 依赖硬件和 API 状态。GPU 对颜色、depth、stencil 或 tile 的内部压缩通常要求资源状态、clear 值、格式、store/load 行为和访问方式满足特定条件。频繁把 render target 当作 storage image 随机写、在多个 layout 间反复转换、使用不利于压缩的格式或进行非局部读写,都会削弱压缩收益。此类压缩的判断主要依赖 profiler counter,例如 compression ratio、color/depth bandwidth、tile store/load 和 render target write bytes。
流式资源管理解决的是“当前 frame 实际需要哪些 mip、page、buffer range”。Texture streaming 按相机、屏幕投影和材质优先级加载合适 mip;virtual texturing 把大纹理拆成 page,按可见区域提交 page 请求;mesh/animation/material streaming 按 LOD 和可见性上传 buffer 子范围。它们的共同目标是减少驻留资源和无效高分辨率读取,同时控制上传带宽和 frame latency。
贯穿帧中的 streaming 风险通常出现在相机快速移动或高频材质切换时。若系统在同一 frame 上传大量 mip,会把 CPU-GPU transfer、copy queue、barrier 和 cache 污染叠加到渲染 pass 上。稳定做法是把请求、预算、上传和可见性分成多个阶段:本帧先记录缺失 page,按优先级排队,在后续 frame 分批上传,并让 shader 对缺失 mip 使用可接受的 fallback。这个策略把带宽尖峰变成可控的多帧成本。
Compression 与 streaming 可以叠加。压缩格式降低每个 mip 或 page 的物理大小,streaming 降低本帧需要驻留和访问的 mip/page 数量。二者结合时要检查三个边界:资产包是否包含目标平台可直接采样的压缩格式;page 粒度是否与压缩块对齐;上传路径是否支持压缩数据的 row pitch、slice pitch 和 layout 要求。跨 API 移植时,这些边界比“压缩率数字”更容易导致工程问题。
下面的判断顺序适用于把压缩和 streaming 引入现有 renderer。先按资源类型分类:source texture、render target、depth、buffer、history。再确认目标平台支持的格式和采样能力。接着在资产构建阶段生成压缩 mip chain 或 page。随后在运行时记录 residency、upload bytes、missing page 和 quality fallback。最后用 profiler 同时观察 memory throughput、texture cache、upload/copy queue 和画面稳定性。这个顺序把格式、资源生命周期和视觉质量放在同一条验证链上。
压缩有明确代价。源纹理压缩会引入编码误差;render target compression 可能受访问方式影响;streaming 会增加调度复杂度、内存碎片和质量回退逻辑。工程选择应以 frame 中的真实带宽证据为前提。对于只占少量像素或低频访问的资源,改格式收益有限;对于 4K full-screen pass 高频读取的 G-buffer、history、shadow 和 post-process buffer,压缩与格式收窄的优先级更高。
115.5 性能观察与带宽使用监控方法
带宽观察要回答五个问题:哪个 pass 产生带宽峰值,读写的是哪些资源,cache 是否命中,访问是否连续,优化后画面和 counter 是否同时改善。工具名称会随平台变化,但证据类型稳定存在,包括 GPU timestamp、draw/pass marker、memory throughput、texture throughput、L2 hit rate、cache miss、render target bandwidth、compression ratio、stall reason 和 copy/upload bytes。
一个可复用观察流程从 marker 开始。先在 renderer 中给 G-buffer、lighting、shadow、TAA、bloom、tone mapping 和 UI pass 添加 GPU marker。再用 timestamp 或 profiler timeline 找到 frame 中耗时最大的 pass。随后打开该 pass 的资源列表,记录输入 texture、buffer、render target 格式、尺寸、mip、sample count、访问状态和 shader。最后查看 counter,把耗时和带宽、cache、stall 证据对应起来。
Profiler counter 的解释要按平台文档校准。NVIDIA Nsight、AMD Radeon GPU Profiler、Intel GPA、Xcode GPU tools 和 RenderDoc 可见的 counter 名称、采样粒度和硬件含义不同。AMD 的 Radeon GPU Profiler manual 明确把 RGP 放在 DirectX 12、Vulkan、OpenCL 和 HIP 应用分析语境中,并列出其硬件与系统支持范围;这提醒我们,counter 结论必须绑定工具、API、驱动和 GPU 家族。跨平台文章或引擎日志应记录这些条件。
带宽 counter 的解释要和资源估算互相验证。如果某个 4K full-screen pass 理论读写量约 600 MB,而 profiler 显示远高于这个量,原因可能是多次采样、cache miss、MSAA resolve、格式转换、compression 失效、重复 pass 或额外 copy。如果 profiler 显示接近理论下界,优化空间更可能来自降低格式、减少 pass 数或降低分辨率,继续微调 cache 的收益通常较小。
Cache hit rate 需要结合访问模式。高 cache miss 加高 memory throughput,通常指向随机访问、过大 working set、mip 不匹配或 layout 破坏局部性。高 memory throughput 加高 cache hit,可能表示本来就有大量顺序读写,例如多张全屏 render target。低 throughput 加高 stall,则可能是同步、barrier、依赖链或少量高延迟随机请求。只看单个 counter 容易误判,至少要把 throughput、hit rate、stall 和 pass timing 放在一起看。
Render target bandwidth 要单独观察。Deferred renderer 中,G-buffer 写入、HDR color 写入、post-process ping-pong、MSAA resolve 和 depth store 都可能占据大量写带宽。优化动作包括缩窄格式、减少 MRT 数量、合并轻量 pass、使用半分辨率 buffer、合理设置 load/store action、减少不必要 clear/copy、保持有利于硬件压缩的状态。Tile-based GPU 还要关注 tile store/load;immediate-mode GPU 更常直接体现为显存读写压力。
Compression ratio counter 能证明硬件压缩是否生效。若颜色或深度压缩率下降,检查 clear 值、格式、layout transition、storage 写入、随机访问、MSAA、blend 和 resolve 路径。若源纹理已使用 BC/ASTC 但 memory throughput 仍高,检查采样次数、mip level、anisotropic filtering、cache miss 和纹理尺寸。压缩率改善但 frame time 不动,说明当前瓶颈可能转移到了 shader ALU、同步等待或 CPU 提交。
观察结果要形成优化闭环。一次带宽优化提交应记录四类信息:修改前后的资源格式和尺寸,修改前后的 pass timing,修改前后的 memory/cache/compression counter,修改前后的画面差异。画面差异要用 debug view、差分图、材质边界、法线高光、TAA ghosting、bloom 稳定性和低端设备回放验证。只有 frame time 下降且视觉误差在目标范围内,优化才算完成。
对贯穿帧,最终排查顺序如下。先确认 GPU frame time 随分辨率增长,排除 CPU submit 主导。再按 marker 找到 lighting、TAA 或 bloom 的带宽峰值。然后估算 G-buffer、history 和 HDR target 的每像素读写量。接着观察 texture throughput、L2 hit、render target bandwidth 和 compression ratio。最后按证据选择格式收窄、G-buffer 打包、pass 合并、半分辨率处理、source texture 压缩、mip streaming 或 buffer layout 调整。这个顺序能把“带宽优化”从经验判断变成可复查的工程流程。
最小自检任务
你维护一个 4K deferred renderer。当前帧包含 G-buffer、lighting、TAA、bloom 和 tone mapping。G-buffer 使用 albedo RGBA8、normal RGBA16F、materialA RGBA8、materialB RGBA8、velocity RG16F 和 depth D32F。Lighting pass 每像素读取全部 G-buffer,读取 tiled light list,并写入 RGBA16F HDR color。TAA pass 读取 HDR color、history RGBA16F、velocity 和 depth,并写入新的 history。Profiler 显示 GPU frame time 从 1440p 到 4K 接近按像素数增长,lighting 与 TAA 的 memory throughput 高,shader ALU utilization 不高,TAA 的 cache hit rate 低,render target bandwidth 高。
请给出一套带宽瓶颈判断和优化方案。要求说明先看哪些证据,如何判断 lighting 与 TAA 的带宽来源,至少提出四个资源或访问模式级优化动作,并说明每个动作应如何验证。
答案要点
先用 GPU marker 和 timestamp 确认 lighting 与 TAA 是热点 pass,再用 像素数 × 每像素读写字节数 × 采样次数 估算两个 pass 的带宽下界。4K 后 frame time 接近按像素数增长,memory throughput 与 render target bandwidth 同时升高,ALU utilization 不高,这组证据支持带宽主导判断。若 CPU submit time、queue wait 或同步等待占主导,优化方向会转到 Chapter 113 和后续 profiling 章节。
Lighting pass 的带宽来源主要是全屏读取多张 G-buffer、读取 light list 和写 RGBA16F HDR color。先审计 normal RGBA16F 是否可改成 RG16F、packed normal 或合适的压缩/编码;再把 materialA 与 materialB 的有效通道合并,减少采样次数;随后检查 tiled light list 是否按屏幕 tile 连续存储,使相邻像素读取相邻 light index。验证时看 lighting pass 的 texture bytes、buffer load stall、L2/cache hit、画面 normal debug view 和材质边界。
TAA pass 的带宽来源是 HDR color、history、velocity、depth 的全屏读取和新 history 写入。先检查 history 是否必须使用 RGBA16F,可根据 HDR 范围和后续 tone mapping 需求测试 RGB10A2、R11G11B10F 或半分辨率/分离通道方案;再检查 motion vector 是否让 history reprojection 形成大范围随机采样;随后减少 TAA 与 tone mapping、sharpen 或 color grading 之间的中间 ping-pong。验证时看 TAA cache hit、history bandwidth、ghosting、细节稳定性和差分图。
四个可执行优化动作是:压缩或收窄 G-buffer normal;合并 material 通道并减少 texture fetch;优化 tiled light list 的连续布局;降低 HDR/history 中间格式或合并后处理 pass。可追加 source texture 压缩、mip streaming、半分辨率 bloom 和 load/store action 调整。每个动作都要记录修改前后的 pass timing、memory throughput、cache hit、render target bandwidth、compression ratio 和画面质量。若 counter 改善但 frame time 不变,应重新检查 shader ALU、同步等待或 CPU 提交是否成为新的限制。
本章知识点总结
- 带宽定义:GPU 内存带宽表示单位时间内 GPU 从内存层级读取和写入的数据量,渲染中的 texture、buffer、render target、resolve 和 copy 都会占用它。
- 4K 放大:高分辨率会按像素数放大全屏 pass 的读写量,多张 G-buffer 与后处理 ping-pong 会迅速推高带宽成本。
- 症状判断:帧时间随分辨率增长、ALU 利用率不高、memory throughput 升高和 render target bandwidth 升高共同支持带宽主导判断。
- 路径分层:texture fetch、buffer load/store、render target write 和 copy/resolve 经过不同资源路径,优化动作要按路径选择。
- 局部性原则:空间局部性和时间局部性决定 cache 与 coalescing 效果,连续访问通常比随机访问产生更少 memory transaction。
- 布局匹配:AoS 与 SoA 的取舍取决于当前 pass 实际读取字段,布局优化要提升有效字节占比。
- 纹理格式:收窄 G-buffer、打包 material 通道、使用合适 normal encoding 和降低中间 target 格式能直接减少每像素读写字节数。
- Mip 策略:mipmap 与 streaming 让远处或低屏幕占用资源读取更小数据,降低带宽并提升采样稳定性。
- 压缩分类:源纹理压缩和硬件 render target compression 属于不同路径,前者依赖资产格式,后者依赖硬件、API 状态和访问方式。
- 流式管理:texture streaming、virtual texturing 和 buffer range upload 通过 residency 与分批上传控制本帧实际资源压力。
- Counter 解释:memory throughput、cache hit、stall、render target bandwidth 和 compression ratio 要结合 pass timing 与资源估算共同分析。
- 优化闭环:每次带宽优化都要同时记录资源变化、counter 变化、pass timing 和画面差异,确认性能收益与视觉质量边界。