用short替代float存储顶点属性可使单顶点显存从32字节降至16字节,带宽直降50%,显著提升gpu缓存命中率;但须严格满足4字节结构体对齐、short偏移为偶数、opengl中设gl_short+归一化参数、vulkan中用vk_format_r16g16b16_sint等整数格式,且仅适用于归一化位置、法线、uv等精度容忍度高的属性。

用 short 替代 float 存储顶点属性,是降低大规模 3D 游戏顶点数组内存带宽最直接有效的手段之一——前提是结构设计正确、硬件适配得当。
为什么 short 能显著压低带宽占用
每个 float 占 4 字节,而 short(有符号 16 位整数)仅占 2 字节。一个典型顶点若含位置(x,y,z)、法线(x,y,z)、UV(u,v),共 8 个分量:
- 全 float:8 × 4 = 32 字节/顶点
- 全 short(归一化后):8 × 2 = 16 字节/顶点 → 带宽直降 50%
在百万级顶点场景中,节省可达十几 MB 显存;对 Android UMA 架构而言,这等同于减少同等大小的系统 PSS 内存压力,同时提升 GPU L1/L2 缓存命中率——尤其在 Mali-G78 或 Adreno 6xx 等移动端 GPU 上,小数据块更易填满缓存行,减少外部内存读取次数。
必须满足的内存对齐与格式声明规则
GPU 不会“猜测”你存的是什么。它只按你声明的格式和偏移去读内存。错一对齐或错一个参数,轻则渲染错乱,重则驱动拒绝绑定:
- 结构体总大小需为 4 字节对齐:例如
{short x,y,z;}实际占 8 字节(补 2 字节填充),不能按 6 字节硬排 - 属性偏移量须对齐自身尺寸:short 类型起始偏移必须是偶数,float 必须是 4 的倍数
- OpenGL ES 中调用
glVertexAttribPointer时,type必须设为GL_SHORT,normalized根据用途选GL_TRUE(映射 [-1.0, 1.0])或GL_FALSE(保留原始整数值) - Vulkan 中需显式使用
VK_FORMAT_R16G16B16_SINT等整数格式,不可混用 float 格式描述符
哪些顶点属性适合用 short,哪些坚决不行
精度与硬件支持决定适用边界:
- 适合:模型空间归一化后的位置(如 [-100,100] 缩放到 [-32768,32767])、法线/切线(单位向量,天然适合归一化 short)、UV 坐标(缩放后可覆盖 0–1 范围,精度达 1/65535)
- 不适合:世界坐标(动态范围大、精度易丢失)、骨骼动画中的矩阵元素、实时计算的顶点偏移(如顶点动画、morph target delta)
- 注意:Adreno 支持
GL_INT_2_10_10_10_REV格式,法线/切线四元组(xyzw)可用 4 字节紧凑表达,比 3×short 更省且对齐友好
验证优化效果的关键指标
不能只看理论节省,要结合真实 GPU 计数器判断是否真正缓解了瓶颈:
- Android 上用 AGI 抓帧,重点关注:顶点内存读取带宽(建议平均 ≤500 MBps,峰值 ≤1.5 GBps)、平均字节数/顶点(建议 ≤32 字节)
- 对比优化前后
adb shell dumpsys meminfo <package></package>中的 PSS 变化,确认显存压力下降 - 若带宽未降,检查是否仍存在顶点属性交错(interleaved)导致缓存效率低下,可尝试将 position、normal、uv 拆到不同缓冲区流(separate streams)










