WebGL扩展与移动端
桌面 Chrome 能跑不等于手机能跑。移动端的差异不是「API 名字」,而是 limits、可渲染格式、上下文会被系统收回、GPU 会降频。生产默认 WebGL2:实例化、VAO、多 render target 已是核心,不要按 WebGL1 扩展表抄。
1. 必查 MAX_TEXTURE_SIZE(以及一串 limits)
export interface GlLimits {
maxTextureSize: number;
maxRenderbufferSize: number;
maxViewport: readonly [number, number];
maxColorAttachments: number;
}
export function readGlLimits(gl: WebGL2RenderingContext): GlLimits {
const vp = gl.getParameter(gl.MAX_VIEWPORT_DIMS) as Int32Array;
return {
maxTextureSize: gl.getParameter(gl.MAX_TEXTURE_SIZE) as number,
maxRenderbufferSize: gl.getParameter(gl.MAX_RENDERBUFFER_SIZE) as number,
maxViewport: [vp[0] ?? 0, vp[1] ?? 0],
maxColorAttachments: gl.getParameter(gl.MAX_COLOR_ATTACHMENTS) as number,
};
}
旧移动 GPU 常见 4096;桌面 8192 / 16384。超了:纹理黑、FBO incomplete、或缩小 drawing buffer。图集和离屏按 探测值 切块,不要写死 8192。DPR 封顶见 05。
还要看:MAX_VERTEX_UNIFORM_VECTORS、MAX_TEXTURE_IMAGE_UNITS、MAX_SAMPLES。超 uniform 的失败形态是链接成功、画面错,不一定有清晰 log。
2. 浮点纹理:采样 ≠ 可渲染 ≠ 线性过滤
WebGL2 核心能 创建 部分 float / half 格式当纹理。这不表示:
- 能绑成 FBO 颜色附件(要
EXT_color_buffer_float/EXT_color_buffer_half_float); - 能
LINEAR过滤(要OES_texture_float_linear/OES_texture_half_float_linear)。
export interface FloatCaps {
renderable: boolean;
halfRenderable: boolean;
floatLinear: boolean;
}
export function detectFloatCaps(gl: WebGL2RenderingContext): FloatCaps {
return {
renderable: gl.getExtension("EXT_color_buffer_float") !== null,
halfRenderable: gl.getExtension("EXT_color_buffer_half_float") !== null,
floatLinear: gl.getExtension("OES_texture_float_linear") !== null,
};
}
适用:HDR 中间缓冲、G-buffer、粒子模拟读回纹理。
不适用:假设 iOS / 低端 Android 能线性采样 RGBA32F。失败就降半精度、关线性、或把 pass 挪回 Canvas 2D 小分辨率。
WEBGL_depth_texture 在 WebGL2 不需要(深度纹理核心有)。查它当能力位会误判。
3. 实例化是 WebGL2 核心
drawArraysInstanced、drawElementsInstanced、vertexAttribDivisor 不必 ANGLE_instanced_arrays。只有明确降级 WebGL1 才 get 该扩展。合批见 06。
移动端实例化能跑,不等于万实例 × 复杂 FS 能 60fps。先砍 overdraw 和纹理带宽。
4. iOS 丢失与热节流
Safari / WKWebView 在内存压力、切 App、系统杀 GPU 时发 webglcontextlost。必须 preventDefault + 资源表重建,见 04。iOS 上「偶发白屏」优先查丢失,而不是 shader。
热节流:掉帧时 EXT_disjoint_timer_query_webgl2 的 GPU_DISJOINT_EXT 会跳。温度上来之后同一套 draw 变慢,不是合批突然坏了。对策:封顶 DPR、降粒子、降 shadow pass;不要在节流时加大分辨率「补偿清晰度」。
failIfMajorPerformanceCaveat: true:避免软件实现冒充 GPU。移动端拿到 caveat 时应走 2D / 静态图,不要静默 5fps。
5. 其它移动端边角
| 项目 | 处理 |
|---|---|
precision highp float 在 FS | 部分 GPU 降 mediump;颜色条带时显式探测 |
| 动态循环 / 非 constant 索引 | 编译失败只在真机 |
antialias: true | 隐式 MSAA 吃带宽;需要时自建 FBO |
| 视频纹理 | texImage2D 视频帧 + CORS;不要经 2D canvas 多拷一次 |
| 后台 | hidden 停 rAF 上传,见 04 提交 |
图标、可复制文字、无障碍不要用 GL 冒充 DOM,走 SVG。
6. 失败形态
| 症状 | 原因 |
|---|---|
| 大图黑、小图好 | 超 MAX_TEXTURE_SIZE |
| HDR pass 桌面好、iOS 黑 | 无 EXT_color_buffer_float |
| float 贴图马赛克 | 无 float linear,NEAREST 或未降级 |
| 切回 App 白屏 | 丢失未重建 |
| 过一会儿掉到 20fps | 热节流,还在 3x DPR + MSAA |
getContext 成功但 5fps | 软件光栅,未设 caveat |
权威资料
- WebGL 2.0 spec — extensions
- WebGL Extension Registry
- MDN — MAX_TEXTURE_SIZE
- MDN — EXT_color_buffer_float
核对日期:2026-08-26