小米图形面试回忆:一帧图像是怎么跑到屏幕上的?

好久不见,最近整理了几个读者反馈的图形渲染岗位的技术面试问题。我觉的这几个问题出现的频率很高,而且容易被连环追问,今天挑出来讲讲。

现如今顶级 AI 的编程能力上已经超过 99.9% 程序员,但是从最近刚入职的一些读者的反馈来看,技术面试中八股文依然占有重要的权重。

描述一帧图像从 App 绘制到最终显示在屏幕上的完整流程。这个流程很容易说不全,可以分为五个阶段。
UI 线程在 VSync 到来后执行 measure、layout、draw,其中 draw 只是把绘制指令录进 DisplayList,并不产生像素;

RenderThread 将 DisplayList 翻译成 OpenGL ES 或 Vulkan 调用,从 BufferQueue 申请空 buffer 交给 GPU 绘制,完成后 queueBuffer 归还并附上 fence,绘制是否结束由 fence 通知下游,CPU 不做阻塞等待;

SurfaceFlinger 收到 VSync 后遍历各可见 Layer,acquireBuffer 取出最新 buffer,计算 Z 序、裁剪与透明度,整理成图层清单;

随后 HWC 优先用硬件图层直接叠加合成(Device Composition,GPU 不参与、功耗最低),当图层数超出 overlay 数量或需要模糊等复杂效果时退回 GPU 合成(Client Composition,功耗明显上升);

最终提交显示控制器逐行扫描输出,SurfaceFlinger 持 ReleaseFence,待该 buffer 扫描完毕才归还 App 复用。核心是 App 只负责把内容画进 buffer,合成与送显由 SurfaceFlinger 决定。

BufferQueue 的工作机制是什么?为什么需要三重缓冲?双缓冲有什么问题?

简单滴说:

  • 双缓冲:前台被显示、后台被渲染。若渲染超时,前台显示完后无新帧可切换 → 卡顿
  • 三重缓冲:增加一个中间缓冲区,允许 App 在 SurfaceFlinger 还在消费上一帧时就开始准备下一帧

BufferQueue 是图形数据的生产者—消费者队列,核心是四个动作:dequeueBuffer 取空 buffer、queueBuffer 写完归还、acquireBuffer 消费者取用、releaseBuffer 用完归还;

取与还之间存在同步约束: dequeueBuffer 拿到的 buffer 可能仍被消费者持有,此时返回 ReleaseFence,需等其 signal 后才能安全写入,反向 queueBuffer 可附 AcquireFence,消费者必须等它 signal 才能读取内容,正是这套机制让 GPU 不必等 CPU,App 提交命令后即可继续下一帧。

双缓冲的问题在于速率不匹配:两块 buffer 乒乓,只要这一帧渲染略微超时或两个 VSync 未能对齐,屏幕扫完当前帧准备切换时下一帧尚未画完,只能继续显示旧帧,用户感知为一次停顿;

三重缓冲则增加一块 buffer 错开使用,屏幕扫描、App 工作、备用各占一块,App 不必因上一帧未上屏而停工,代价是多一块内存与可能多一帧延迟——本质上是以空间换流水线连续性,不保证不丢帧。

OpenGL ES 渲染管线有哪些阶段?每个阶段功能是什么?

这个问题前面已经出现多次,属于超高频的问题。

管线共八个阶段,顺序固定,其中仅顶点着色器和片元着色器可编程:

  • 顶点着色器按顶点执行 MVP 变换、法线变换与纹理坐标传递;
  • 图元装配把顶点拼成三角形;裁剪与背面剔除丢弃不可见图元;
  • 光栅化把三角形离散为片元;
  • 片元着色器按像素执行光照计算与纹理采样;
  • 再经过逐片元测试(深度、模板、混合);
  • 写入 Framebuffer。

本文来自作者投稿,版权归原作者所有。如需转载,请注明出处:https://www.nxrte.com/jishu/72450.html

赞 (0)

相关推荐