跪拜 Guibai
← Back to the summary

The Six Layers That Turn an Android View.draw Call Into Screen Pixels

Android Graphics System Panorama

The Android graphics system spans applications, system services, and hardware, with many components and confusing terminology. This article walks through the system from application to hardware: six layers, how layers collaborate, and how typical scenarios plug in. It covers only the big picture, not implementation details.

1. What Problems Does the Graphics System Solve

An app UI, a video, and the system status bar all run on screen simultaneously — different sources, different rhythms — and must be composited into a single output frame. The graphics system handles several things at once:

2. Six Layers from Application to Hardware

Use a layered perspective to build a holistic understanding: each layer solves one category of problem, and layers connect through well-defined interfaces. View.draw and Canvas, which app developers interact with most often, sit in the upper-middle section of the layers — they are the entry point for drawing intent. The bottom layers — GPU, HWC, Display — determine how pixels actually reach the screen. The middle layers progressively translate intent into screen-ready graphics data.

Application side
  1 App UI Layer — View · ViewGroup · Drawable · Window
        │  View.draw
        ▼
  2 Drawing Interface Layer — Canvas · DisplayList/RenderNode · Picture
        │  RenderThread playback
        ▼
  3 Rendering Engine Layer — Skia · OpenGL ES · Vulkan
        │  Write to Surface
        ▼
System side
  4 Buffer Channel Layer — Surface · BufferQueue · GraphicBuffer · Fence
        │  Submit Buffer
        ▼
  5 Composition Coordination Layer — WindowManager · SurfaceControl · SurfaceFlinger · Layer
        │  Deliver composition
        ▼
Hardware side
  6 Hardware Display Layer — Gralloc · GPU · HWC · Display

Layer 1: App UI Layer

Describes interface structure and content, answering "what to draw and how to lay it out."

This layer does not care how pixels are generated; it only produces a description of what the interface should look like.

Layer 2: Drawing Interface Layer

Expresses View drawing intent as a replayable sequence of drawing operations, decoupled from the specific rendering backend. Under hardware acceleration, canvas.drawText inside onDraw does not immediately paint to the screen; it is first recorded as a command and handed to the render thread for later execution.

Canvas occupies a somewhat special position: it is both the interface called by the application and the input to the rendering engine. Here it is placed in the "abstraction and recording of commands" layer; execution is delegated to the rendering engine.

Layer 3: Rendering Engine Layer

Turns drawing commands or graphics API calls into pixels. The first two layers only describe what to draw; this layer turns commands into pixels.

The pixels produced by this layer are ultimately written into the Surface at Layer 4.

Layer 4: Buffer Channel Layer

Responsible for the transfer and synchronization of graphics data between components.

The data flow model: the producer dequeues a Buffer from BufferQueue → writes content → submits it back to the queue → the consumer acquires and uses it → returns it when done.[^1]

Layer 5: Composition Coordination Layer

The status bar, app windows, and navigation bar are each drawn independently and must ultimately be stacked into a single picture. This layer manages windows and layers, compositing multiple layers into the final image.

The defining characteristic of this layer is the separation of control flow and data flow: WindowManager/SurfaceControl handles layer configuration, while SurfaceFlinger handles buffer composition.

Layer 6: Hardware Display Layer

Provides graphics memory, compute power, and physical output. The logic of the preceding five layers ultimately executes on the hardware at this layer.

3. Relationships Between Layers

The layering provides a vertical understanding. This section ties them together along two main threads: data flow (how an image goes from drawing commands to screen pixels) and control flow (how windows and layers are configured). The two threads intersect at different layers, with the Producer/Consumer model running throughout.

Data Flow: From Drawing Commands to Screen Pixels

View tree traversal
   │ View.draw
   ▼
Canvas records commands ──► DisplayList/RenderNode
                          │ RenderThread playback
                          ▼
Skia / GLES / Vulkan ──► Generate pixels
   │ Write
   ▼
Surface ──► BufferQueue ──► SurfaceFlinger acquires Buffer
                                  │ Composite
                                  ▼
                           HWC / GPU composition
                                  │
                                  ▼
                              Display presents to screen

A normal app window follows this entire path: the application side (Layers 1–3) generates pixels and writes them into a Surface; the system side (Layers 4–6) composites the Buffers from multiple Surfaces and presents them to the screen. Scenarios such as Camera and video decoding, acting as producers, can plug in directly at Layers 3–4, bypassing the View system of Layers 1–2.[^2]

Control Flow: Window and Layer Configuration

Alongside the data flow runs a parallel control flow:

WindowManager / SurfaceControl
   │ Configure Layer properties (position, z-order, transform, visibility)
   ▼
SurfaceFlinger ──► Composite visible Layers according to configuration ──► HWC/Display

Window is the application-side window abstraction at Layer 1; its actual management happens at Layer 5: WindowManager creates and configures Layers via SurfaceControl, and SurfaceFlinger determines composition order and effects based on that configuration. When the interface changes, the control flow (window/layer property updates) and data flow (redrawing) work in concert.

The Producer/Consumer Model Throughout

A unified model runs through Layers 3–5:

The same producer can connect to different consumers: a Camera's Buffer can go to SurfaceFlinger for on-screen preview, to ImageReader for analysis, and to an encoder for recording — the only difference is which two ends BufferQueue connects.

Decoupling Achieved by Layering

Each layer solves a category of decoupling problem:

4. Typical Graphics Scenarios

Using the layered perspective to view different scenarios, the differences lie in which layer they plug into and who the consumer is:

Scenario Producer Consumer / Output
Normal app window HWUI/Skia SurfaceFlinger
Direct Canvas drawing CPU/Skia Canvas SurfaceFlinger
OpenGL ES game GLES/EGL SurfaceFlinger
Vulkan game Vulkan Swapchain SurfaceFlinger
Camera preview Camera HAL SurfaceFlinger or SurfaceTexture
Video playback MediaCodec Decoder SurfaceFlinger or SurfaceTexture
Video encoding Camera/GLES MediaCodec Encoder
Image analysis Camera/Codec ImageReader
Screen recording SurfaceFlinger/Virtual Display MediaCodec Encoder

For each scenario, you must clarify: who the producer is, which Surface it writes to, which two ends BufferQueue connects, who ultimately consumes it, and whether it is composited to the screen. SurfaceView, SurfaceTexture, and TextureView are connection schemes for different scenarios, not separate architectural layers.[^2]

5. Common Questions

The role of multiple buffering With single buffering, after the producer finishes drawing a frame, it must wait for the display to finish reading before drawing the next frame; during that time the GPU/CPU are idle. Multiple buffering lets the producer draw to a back buffer while the display reads from the front buffer, forming a pipeline that improves throughput and reduces jank. Triple buffering goes further: even if one frame hasn't finished rendering, the producer always has a buffer available to write to.

How BufferQueue handles mismatched production and consumption speeds The queue acts as a reservoir between the two ends: when production is fast, buffers accumulate up to the limit, at which point the producer is blocked or frames are dropped; when production is slow, the consumer reuses the previous frame. Multiple buffering lets the two ends with mismatched speeds operate at their own rhythms without blocking each other.

Why avoid copying entire frames of pixels A single frame contains a lot of data (1080p RGBA is about 8MB); copying is time-consuming and wastes bandwidth and power. Android uses Gralloc to allocate graphics memory that can be shared and accessed by GPU, CPU, and HWC. Components pass memory handles rather than the data itself, achieving zero-copy transfer.

The performance and power trade-off between HWC and GPU HWC is display controller hardware; compositing multiple layers with it uses less power than using the GPU: it is dedicated hardware, does not occupy the GPU, and can even let the GPU sleep. SurfaceFlinger prioritizes HWC composition; only when HWC capabilities are exceeded (complex transforms, too many layers, special blend modes) does it fall back to the GPU, which then both consumes power and occupies application rendering resources.

The role of Fence Graphics tasks (GPU rendering, display readout, Camera output) are asynchronous hardware operations; after the CPU submits them, it cannot synchronously wait for completion. Fence is a kernel synchronization primitive that marks "when this buffer has finished being written or read." Without it, a consumer might read a half-finished product, or a producer might overwrite a buffer still being read. It uses signaling instead of busy-waiting, guaranteeing correct ordering in asynchronous hardware pipelines.

Behavioral differences between SurfaceView and TextureView SurfaceView owns an independent Surface and is handed directly to SurfaceFlinger for composition as a separate layer; it does not participate in the View system. Performance is good, but transforms and transparency are limited, and gaps can appear when layered with Views. TextureView treats its content as a texture: it first writes to a Buffer, then composites it as a texture into the View's drawing result, fully integrated into the View hierarchy. It supports arbitrary transforms and transparency, at the cost of one extra texture composition step. The essential difference: independent layer composition versus texture integration into the View hierarchy.

Why some videos cannot be screenshotted DRM-protected content (such as Widevine L1) is decoded in secure hardware and output to protected buffers marked as protected, which the GPU or CPU are not allowed to read. Taking a screenshot requires reading the framebuffer; SurfaceFlinger marks such layers as secure and skips them during screenshot capture. This is a content security mechanism.

Where differences in Buffer size at the same resolution come from It depends on pixel format and allocation method. At the same resolution, RGBA_8888 (4 bytes per pixel) is twice the size of RGB_565 (2 bytes per pixel); whether frame buffer compression (such as AFBC) is enabled, and differences in alignment and layout caused by different usage flags, all change the actual memory footprint.

The difference between physical and virtual displays A physical Display is connected to real screen hardware, outputs via HWC, and is directly visible to the user. A virtual Display has no real hardware; it outputs the composition result to a Surface that can be consumed by an application, used for screen recording, casting, etc. The composition result is not presented directly to a screen but sent out as a buffer.

The division of labor between WindowManager and SurfaceFlinger WindowManager manages window semantics and policy (creation, properties, z-order, focus); SurfaceFlinger manages pixel-level composition (collecting buffers, compositing, output). Separated, WM does not need to understand composition, and SF does not need to understand window policy. SF runs in a separate process; composition has strong real-time requirements and cannot be blocked by window management logic. The two are bridged by SurfaceControl: WM configures layers, SF executes composition.

Summary

From View.draw to screen pixels, you pass through six layers — App UI, Drawing Interface, Rendering Engine, Buffer Channel, Composition Coordination, and Hardware Display — threaded through by two main lines: data flow and control flow. With this framework in mind, when you dive into specialized topics on BufferQueue, SurfaceFlinger, or HWC, each piece can be mapped to a specific layer and main thread.

[^1]: Android Graphics Overview [^2]: Graphics Architecture

Comments

Top 1 from juejin.cn, machine-translated. The original thread is authoritative.

王三岁_

Great article, thanks for sharing.