3. 渲染管线基础:固定功能管线 vs 可编程管线、顶点着色器、片段着色器、光栅化单元

渲染管线是 GPU 执行图形绘制任务的核心流水线。在 SA8155 平台上,理解从固定功能到可编程的演进,以及顶点着色器、片段着色器和光栅化单元的具体职责,是驱动开发中正确配置硬件状态、实现图形 API 映射的前提。

3.1 固定功能管线 vs 可编程管线

早期 GPU 采用固定功能管线,每个阶段由硬件逻辑直接完成,开发者只能通过状态参数(如光照模型、纹理混合模式)进行有限调整。现代 GPU(包括 SA8155 集成的 Adreno GPU)则采用可编程管线,允许开发者通过着色器程序自定义顶点处理和片段处理逻辑。

对比维度 固定功能管线 可编程管线
灵活性 低,仅支持预设的变换、光照、纹理操作 高,开发者可编写任意顶点/片段处理逻辑
硬件实现 专用逻辑单元,功耗低但功能固定 统一着色器核心,通过指令调度实现通用计算
API 映射 OpenGL ES 1.x 固定管线状态机 OpenGL ES 2.0+ / Vulkan 着色器模块
SA8155 支持 兼容模式(通过驱动模拟或硬件回退) 原生支持,Adreno 6xx 架构专为可编程设计
驱动复杂度 状态验证与寄存器配置 着色器编译、链接、常量缓冲区管理
驱动开发提示: 在 SA8155 的 GPU 驱动中,固定功能管线通常被映射为可编程管线的一组固定着色器程序。驱动在内部生成对应的顶点/片段着色器,以兼容旧版 API 调用。理解这一映射关系,有助于调试遗留应用兼容性问题。
关键演进: 从固定功能到可编程,核心变化在于将“光照计算”和“纹理组合”从硬件逻辑转移到开发者控制的着色器代码中。驱动需要管理着色器程序的加载、编译、链接以及 uniform 变量的更新。
固定功能管线 vs 可编程管线架构对比 固定功能管线 顶点变换(固定矩阵栈) 光照计算(固定模型) 纹理混合(固定公式) 硬件逻辑固定,不可编程 可编程管线 顶点着色器(用户自定义) 片段着色器(用户自定义) 统一着色器核心调度 指令级可编程,灵活度高

3.2 顶点着色器(Vertex Shader)

顶点着色器是渲染管线的第一个可编程阶段。它处理每个输入顶点,执行坐标变换、顶点属性计算、光照计算等操作。在 SA8155 的 Adreno GPU 中,顶点着色器运行在统一着色器阵列上,与片段着色器共享计算资源。

  • 输入: 顶点属性(位置、法线、纹理坐标、颜色等),由应用通过顶点缓冲区提供。
  • 输出: 变换后的顶点坐标(必须写入 gl_Position),以及传递给片段着色器的 varying 变量。
  • 典型操作: 模型-视图-投影矩阵变换、顶点蒙皮(骨骼动画)、顶点位移、法线变换。
  • 驱动职责: 编译顶点着色器源码、分配 uniform 缓冲区、管理顶点属性绑定。
SA8155 平台注意: Adreno 6xx GPU 的顶点着色器支持最多 16 个 4 分量向量作为输入属性。驱动在解析顶点属性布局时,必须确保属性偏移和步长与硬件限制对齐,否则会导致 GPU 页面错误。
// 一个典型的顶点着色器示例(GLSL ES 3.0)
#version 300 es
layout(location = 0) in vec3 a_position;
layout(location = 1) in vec3 a_normal;
layout(location = 2) in vec2 a_texcoord;

uniform mat4 u_modelViewProjection;
uniform mat4 u_normalMatrix;

out vec3 v_normal;
out vec2 v_texcoord;

void main() {
    // 顶点位置变换
    gl_Position = u_modelViewProjection * vec4(a_position, 1.0);
    // 法线变换(用于片段着色器光照计算)
    v_normal = mat3(u_normalMatrix) * a_normal;
    // 传递纹理坐标
    v_texcoord = a_texcoord;
}

3.3 光栅化单元(Rasterizer)

光栅化是固定功能单元,负责将顶点着色器输出的图元(点、线、三角形)转换为片段(fragment)集合。每个片段对应屏幕上的一个像素位置,并携带深度值、插值后的 varying 变量等信息。光栅化单元是连接顶点处理与片段处理的桥梁。

光栅化子阶段 功能描述 SA8155 硬件特性
图元装配 将顶点流组装为三角形、线段或点 支持索引缓冲区,减少重复顶点传输
裁剪与背面剔除 移除视口外的图元,剔除背向面 硬件加速裁剪,支持用户自定义裁剪平面
透视校正插值 对 varying 变量进行透视校正插值 Adreno 6xx 支持高精度插值,减少透视伪影
深度偏移 解决深度冲突(如阴影贴图) 支持多边形偏移(glPolygonOffset)
多重采样 生成子采样点用于抗锯齿 支持 2x/4x MSAA,硬件采样位置可编程
驱动配置要点: 光栅化单元的状态由 VkPipelineRasterizationStateCreateInfo(Vulkan)或 glEnable/glDisable(OpenGL ES)控制。驱动需要将 API 状态转换为 Adreno 硬件寄存器中的 GRAS(光栅化)和 RB(渲染后端)配置。
光栅化单元内部流程 顶点着色器输出 图元装配 裁剪/背面剔除 透视校正插值 片段(Fragment)输出

3.4 片段着色器(Fragment Shader)

片段着色器处理光栅化后生成的每个片段,计算最终像素颜色和深度值。它是实现视觉效果(如光照、纹理、阴影、后期处理)的核心阶段。在 SA8155 的 Adreno GPU 上,片段着色器与顶点着色器运行在相同的统一着色器核心上,但拥有独立的执行上下文和资源访问权限。

  • 输入: 插值后的 varying 变量、片段坐标(gl_FragCoord)、前向深度(gl_FrontFacing)。
  • 输出: 颜色值(gl_FragColor 或输出变量)、深度值(gl_FragDepth)。
  • 典型操作: 纹理采样、逐像素光照(Blinn-Phong、PBR)、Alpha 测试、颜色混合。
  • 驱动职责: 管理纹理单元绑定、编译片段着色器、处理渲染目标输出。
性能优化提示: 在 SA8155 上,片段着色器的执行开销通常占 GPU 总时间的 60%-80%。驱动应优先优化片段着色器中的纹理采样次数和数学运算复杂度。使用 mediump 精度修饰符可以显著降低功耗和带宽占用。
// 一个典型的片段着色器示例(GLSL ES 3.0)
#version 300 es
precision mediump float;

in vec3 v_normal;
in vec2 v_texcoord;

uniform sampler2D u_diffuseTexture;
uniform vec3 u_lightDirection;
uniform vec3 u_lightColor;

layout(location = 0) out vec4 fragColor;

void main() {
    // 归一化法线
    vec3 normal = normalize(v_normal);
    // 漫反射光照计算
    float diff = max(dot(normal, u_lightDirection), 0.0);
    vec3 diffuse = u_lightColor * diff;
    // 纹理采样
    vec4 texColor = texture(u_diffuseTexture, v_texcoord);
    // 最终颜色输出
    fragColor = vec4(diffuse * texColor.rgb, texColor.a);
}

3.5 渲染管线完整流程与 API 映射

将上述阶段串联起来,形成完整的渲染管线。在 SA8155 驱动中,每个阶段都对应特定的硬件单元和 API 状态配置。

SA8155 渲染管线完整流程与 API 映射 应用/驱动提交 顶点着色器 光栅化单元 片段着色器 输出合并/混合 API 映射关系 • Vulkan: VkShaderModule (VS/FS) → VkPipeline → VkCommandBuffer 提交 • OpenGL ES: glShaderSource → glCompileShader → glAttachShader → glLinkProgram → glUseProgram • 驱动内部: API 调用 → KGSL (Kernel Graphics Support Layer) → Adreno 固件调度
驱动开发核心: 渲染管线的每个阶段在 SA8155 驱动中都有对应的硬件状态对象。顶点着色器和片段着色器通过着色器编译器生成 Adreno 微码;光栅化单元通过 GRAS 寄存器配置;输出合并阶段通过 RB(Render Backend)寄存器控制。驱动必须确保这些状态在每次绘制调用前正确同步。