What happens between an application drawing a picture and that picture being lit up on a phone screen: which block issues each read, which bus it travels on, what the panel does with it when it arrives — and what every word in that sentence means. Ten figures, one ARM64 phone-class SoC.一个应用画出一张图,到这张图在手机屏幕上亮起来,中间到底发生了什么:哪个模块发出了每一次读、走的是哪根总线、到了屏那边又是怎么点亮的——以及这句话里每个词是什么意思。十张图,一颗 ARM64 手机芯片。
The structure is checked. Which block issues the read, which driver owns the DMA, what the vendor says is inside its own display controller — all of it read out of mainline Linux, tree v7.0-9887-ga43fac2b5813, and every claim of that kind on this page carries a file and a line number. The numbers are a representative configuration chosen so the proportions are honest: bus widths, clock rates and capacities are not copied from any vendor datasheet, and none of them describe a specific product. The argument only depends on the ratio — a frame is megabytes and on-chip memory is kilobytes — and that gap is three orders of magnitude on every chip of this class.结构是查过的。哪个模块发读请求、DMA 归哪个驱动管、厂商自己说它的显示控制器里面有什么——都是从主线 Linux 源码里读出来的,树是 v7.0-9887-ga43fac2b5813,本页每一条这类论断都给了文件名和行号。数字是一种代表性配置,为的是让量级对得上:总线位宽、频率、容量都不是从哪家厂商的手册上抄的,也没有一个是在描述某颗具体芯片。论点只依赖那个比例——一帧是兆字节,片上存储是千字节——这个差距在任何这一类芯片上都是三个数量级。
Every one of these is standard vocabulary in display and graphics work, and every one of them is used later on this page. Two questions each: what it is, and what breaks without it. The mechanism — registers, bit layouts, ordering — is left to the section that owns the concept, because a definition that opens with a register map teaches copying rather than judgement.下面每一个都是显示和图形领域的标准术语,也都会在本页后面用到。每个词答两问:这是什么,以及没有它会怎样。机制细节——寄存器、位布局、生效顺序——留给拥有这个概念的那一节讲,因为一上来就摆寄存器表,读者学到的是照抄而不是判断。
One complete copy of everything on screen. On a 1080×2400 panel with four bytes per pixel that is 9.89 MiB of contiguous memory, and there are 60 of them every second.屏幕上所有内容的一份完整拷贝。1080×2400 的屏,每个像素 4 字节,一帧就是 9.89 MiB 一整块连着的内存;一秒钟要有 60 份。
Without it: Without the word you cannot say how big anything is, and the whole argument on this page is an argument about size.没有它会怎样:这是本页所有数字的单位。后面每一句「放不下」「太贵了」都是拿它当尺子量出来的。
The range of memory addresses that a frame lives in. It is an address, not a package — nobody ever hands it to anybody.一帧所在的那段内存地址。它是一个地址,不是一个包裹——从头到尾没有谁把它「交」给谁。
Without it: Treat it as a package and every later sentence goes wrong: you start saying the GPU uploads it to the display, which never happens on a phone.没有它会怎样:把它当包裹,后面每句话都会错。「GPU 把帧交给显示控制器」这种说法就是这么来的,而手机上没有这一步。
The display controller reading the framebuffer out one line at a time, paced by the panel's pixel clock. It runs 60 times a second and never stops while the screen is on.显示控制器按屏幕的像素时钟,一行一行把帧缓冲读出来。一秒钟 60 遍,屏幕亮着它就没停过。
Without it: Miss one beat and the panel is short one line of pixels. This is the one job on the chip that cannot be rescheduled.没有它会怎样:慢一拍,屏幕就少一行像素。这是整颗芯片上唯一一件不能改期的事。
Whichever block of memory scanout is reading right now. It is a role, not an address — the same memory is the front buffer this frame and the back buffer the next.此刻正在被扫描输出读的那一块。这是一个角色,不是一个地址——同一块内存,这一帧叫前缓冲,下一帧就叫后缓冲。
Whichever block the GPU is drawing into right now. At the next vsync the two roles change places.此刻正在被 GPU 画的那一块。下一次 vsync 一到,两个角色就对调。
Without it: With only one block, scanout would be reading the same bytes the GPU is rewriting, and you would see half of the old frame above half of the new one.没有它会怎样:只有一块的话,扫描输出读的正是 GPU 正在改的那些字节,屏幕上会是上半张旧的、下半张新的。
Writing a different address into the display controller's framebuffer base register. Four bytes. Nothing is copied and neither buffer moves.往显示控制器的帧缓冲基址寄存器里写另一个地址。四个字节。没有任何东西被拷贝,两块缓冲一动不动。
Without it: This is where almost everyone assumes a copy happens. It does not, and that assumption is what makes the rest of the pipeline impossible to reason about.没有它会怎样:几乎所有人都以为这一步发生了拷贝。没有。而这个误解一旦成立,后面整条通路就没法讲清楚了。
What you see when the base address changes while scanout is midway down the screen: the top of one frame sitting above the bottom of another.扫描输出扫到屏幕一半时基址被改掉,屏上就是上半张属于一帧、下半张属于另一帧。
The gap between finishing one frame and starting the next. Register writes are held and take effect at this instant, not when software issued them.一帧扫完到下一帧开始之间的那段空隙。寄存器的新值被压着,到这一刻才生效,而不是软件写下去的那一刻。
Without it: Without the hold, every flip would land mid-screen and tear.没有它会怎样:没有这个「压着等」,每次翻页都会落在屏幕中间,每次都撕裂。
A small on-chip SRAM inside the display controller that holds a handful of lines — sixteen on the hardware cited here, not one frame.显示控制器片上的一小块 SRAM,只存十几行——本页引的这颗是 16 行,不是一帧。
Without it: Without it, a single DRAM read latency would starve the pixel clock and put a visible band across the screen.没有它会怎样:没有它,DDR 一次读延迟就够让像素断流,屏幕上当场出现一道横条。
The display controller's own read engine. It issues addresses and receives data by itself, with no CPU involvement after setup.显示控制器自己的读引擎。配好之后它自己发地址、自己收数据,CPU 再不参与。
Without it: Without it the CPU would have to move roughly 500 MB every second and do nothing else.没有它会怎样:没有它,CPU 每秒得搬大约 500 MB,然后就干不了别的了。
Any block that can start a read or a write on the bus by itself. The GPU, the display controller, the camera pipeline and the video codec are all masters; memory is not.任何能自己在总线上发起读写的模块。GPU、显示控制器、摄像头通路、视频编解码都是主设备;内存不是。
Without it: Adding a GPU adds a master, not a destination. That one sentence is the whole shape of this page.没有它会怎样:加一个 GPU,加的是一个主设备,不是一个目的地。这一句话就是整页的形状。
Two different buses. APB is narrow and slow and carries register writes; AXI is wide and fast and carries pixels. They are physically separate wires.两根不同的总线。APB 又窄又慢,走寄存器;AXI 又宽又快,走像素。它们是物理上分开的两组线。
Without it: On one shared bus a single register write could cut into a pixel burst; scanout has no slack to absorb that.没有它会怎样:合成一根的话,一次寄存器写就可能插进像素突发中间;而扫描输出没有余量能吸收这一下。
The GPU cuts the screen into 16×16 tiles and finishes one tile entirely inside on-chip memory before writing it out once.GPU 把屏幕切成 16×16 的小格,一格从头到尾在片上算完,算完才往内存写一次。
Without it: Without it every layer of overdraw would be a read and a write of external memory, and on a phone that memory is shared with five other blocks.没有它会怎样:没有它,每一层重叠都要读一次写一次外部内存;而手机上那块内存是跟另外五个模块共用的。
Stacking several layers into one image by alpha. On a phone this is a piece of hardware inside the display controller, passed through on the way to the panel.把好几个图层按透明度叠成一张。手机上这是显示控制器里的一块硬件,像素路过时顺手就叠好了。
Without it: If the GPU did it instead, composing would cost one extra full-frame write plus one extra full-frame read — about 1.2 GB/s of pure traffic.没有它会怎样:改由 GPU 来做的话,合成要多写一整帧、多读一整帧——大约每秒 1.2 GB 的纯搬运。
A wire running backwards from the panel's driver chip to the SoC, saying “I have just finished a refresh”.从屏幕的驱动 IC 反向接回芯片的一根线,意思是「我刚扫完一遍」。
Without it: Without it a command-mode panel has no way to know when writing to it is safe.没有它会怎样:没有它,命令模式的屏就无从知道什么时候写才不会撕裂。
The line buffer running empty while the pixel clock keeps going. The panel receives whatever was on the wires.行缓冲空了,而像素时钟不会等。屏幕收到的就是那一瞬间线上碰巧是什么。
Without it: This is what a bandwidth problem actually looks like from the outside: a band across the screen, not a slow app.没有它会怎样:带宽出问题时,从外面看到的就是这个:屏幕上一道横条,而不是「应用变慢了」。
Read the two rows against the bar in the middle. Everything above writes into that memory, everything below reads out of it, and no arrow ever goes from one block straight to another. That is the shape of the answer: on a phone, blocks do not hand frames to each other. They take turns touching the same addresses.把上下两行对着中间那条内存看。上面的往里写,下面的往外读,没有一根箭头是从一个模块直接连到另一个模块的。这就是答案的形状:手机上模块之间不传帧,它们只是轮流去碰同一批地址。
This is the constraint everything else is downstream of, so it is worth making it visible rather than asserting it. The right-hand side draws one frame as a ruler and then draws every on-chip memory on that same ruler. The GPU tile buffer comes out one and a half pixels wide.后面所有的设计都是从这一条约束推出来的,所以值得把它画出来而不是说出来。右半边把一帧画成一把尺子,再把片上每一块存储按同一个比例画在下面。GPU 的 tile 缓冲量出来只有一个半像素宽。
rockchip_drm_vop2.c:500 says in as many words that a Cluster window has a 2048 x 16 line buffer.那为什么不干脆做一颗 SRAM 够大的芯片?因为片上 SRAM 每比特占的硅片面积是 DRAM 的几十倍。能在片内装下一帧的芯片会贵到没人买得起,而且它也只装得下一帧。16 行不是我为了讲故事简化的:rockchip_drm_vop2.c:500 原话就是「a Cluster window has 2048 x 16 line buffer」。
The previous figure proved the GPU cannot hold a frame. The obvious next question is how it draws one anyway, and the answer is that it never handles a frame at all. The screen is cut into 10 125 tiles of 16 by 16 pixels, and one tile is rasterised, depth-tested and blended to completion inside on-chip memory before a single byte is written out.上一张图证明了 GPU 装不下一帧。那接下来自然要问:它到底怎么把一帧画出来的?答案是它根本不处理一帧。 屏幕被切成 10 125 个 16×16 的小方格,一格的光栅化、深度测试、混合全部在片上做完,做完才往外写一次。
A phone screen is never one image: a status bar, an application, a video layer and a cursor are all on screen at the same time, each in its own buffer at its own address. Something has to combine them, and the surprising part is where. Not in the GPU, and not through memory.手机屏幕从来不是一张图:状态栏、应用、视频层、光标同时在屏上,各自在自己的缓冲里、各自有自己的地址。总得有谁把它们合起来——意外的是合成发生的地方。不在 GPU 里,也不经过内存。
rockchip_drm_vop2.c:41-68. Six hardware windows each pull their own layer out of memory with their own fetch DMA, a selector routes any of them to any layer slot, and one overlay block blends them on the way past. The blended result is never written back — it goes straight out of the chip as a pixel stream. That is why a phone can play video under a translucent status bar at 60 Hz without waking the GPU at all.这里的结构不是我编的,照的是厂商自己写进驱动的那张框图:rockchip_drm_vop2.c:41-68。六个硬件窗口各用自己的取数 DMA 从内存里拉自己那一层,选择器可以把任一层送到任一个槽,一个混合器在像素路过时把它们叠好。叠好的结果一个字节都没回内存,直接以像素流的形式出芯片。这就是为什么手机能在半透明状态栏底下以 60 Hz 放视频,而 GPU 全程没醒过。
These two words cause more confusion than anything else in the pipeline, because they sound like locations and they are not. They are roles that two fixed blocks of memory take turns holding. The figure draws the same two rectangles at identical coordinates in all three columns, so the only thing your eye can find changing is which one is which colour.整条通路里最容易把人绕晕的就是这两个词,因为它们听起来像地点,其实不是。它们是两块固定的内存轮流担任的两个岗位。这张图把同样两个矩形在三列里画在完全相同的坐标上,于是眼睛能找到的唯一变化,就是哪一块现在是什么颜色。
VOP2_WIN_YRGB_MST (rockchip_drm_vop2.c:1370), and it is held until vsync rather than taking effect when software writes it — let the address change mid-scan and you get tearing by definition.为什么是两块不是一块?一块的话,扫描输出读的正是 GPU 正在改的字节。为什么不是三块?三缓冲是真实存在的,多花 9.89 MiB 换 GPU 不必等翻页,代价是多一帧延迟。这里的寄存器是 VOP2_WIN_YRGB_MST(rockchip_drm_vop2.c:1370),它被压到 vsync 才生效,而不是软件写下去就生效——让地址在扫描中途改掉,撕裂就是定义上的必然。
Everything so far happened inside one piece of silicon. This is where the pixels physically leave it, travel down a flex cable, and get turned back into light. It is also the only stage where the signal's form actually changes: parallel inside the chip, serial on the cable, parallel again inside the panel.到这里为止,一切都发生在同一片硅上。这一步像素真的离开它、走过一根排线、再变回光。这也是整条通路上唯一一处信号形态真的变了的地方:片内是并行的,排线上是串行的,进屏又变回并行。
SET_TEAR_ON = 0x35 (include/video/mipi_display.h:115, issued at drm_mipi_dsi.c:1380). Lane count is not fixed by the standard; each panel declares its own (include/drm/drm_mipi_dsi.h:192), and four is simply what 1080p at 60 Hz needs. Read ①②③ as the serialising half, ④ as the chip inside the panel that turns the packets back into parallel, and ⑤ and ⑥ as the two drivers that actually address the glass.屏是一个独立的器件,有自己的控制器。它的驱动 IC 把包解成并行,驱动 3240 路源极输出——一次驱动一整行子像素——同时栅极驱动决定 2400 行里哪一行接收它。这张图里只有一根线是朝回走的:TE 信号,用 DCS 命令 SET_TEAR_ON = 0x35 打开(include/video/mipi_display.h:115,发命令的地方在 drm_mipi_dsi.c:1380)。lane 数不是标准定死的,每块屏自己声明(include/drm/drm_mipi_dsi.h:192),四条只是 1080p 60 Hz 需要这么多。顺 ①②③ 看是串行化,④ 是屏里那颗把包解回并行的芯片,⑤⑥ 才是真正去点亮玻璃的两组驱动。
Every figure so far has ended with “and that is why sharing one DRAM is the right design”. It is worth being honest about what it costs, because that cost is where most real display bugs actually live.前面每张图的结论都是「所以共用一块 DDR 是对的设计」。那也该把代价说清楚,因为真实的显示故障大多就住在这个代价里。
Everything above describes a phone, where the GPU and the display controller share one DRAM. On a desktop machine with a discrete graphics card there really is a second memory across a link, and a copy really does happen. That case is worth drawing because it is where the vocabulary everyone uses came from — and then got applied to hardware where it does not fit.上面说的都是手机,GPU 和显示控制器共用一块 DDR。桌面机插独立显卡的话,隔着一根链路真的有第二块内存,拷贝也真的会发生。这种情况值得画出来,因为大家嘴上那套词汇正是从这儿来的,然后被套到了并不适用的硬件上。
include/drm/ttm/ttm_placement.h:51, the copy path is amdgpu_move_blit → amdgpu_copy_buffer (amdgpu_ttm.c:383, :2484), and scanout is forced into VRAM at amdgpu_display.c:560.留意是谁发起的。请求起于显卡这边,所以这次拷贝是拉不是推:发起方是显卡自己的搬运引擎,不是处理器。这跟手机上是同一条规律——谁要数据谁发读请求,变的只是内存的块数。归属定义在 include/drm/ttm/ttm_placement.h:51,拷贝走的是 amdgpu_move_blit → amdgpu_copy_buffer(amdgpu_ttm.c:383、:2484),扫描输出被强制放在 VRAM,见 amdgpu_display.c:560。
grep -c dma_addr in drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c returns 0.它们在显示控制器驱动里。DSI 桥根本不读内存——像素是别人喂给它的。在 drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c 里跑 grep -c dma_addr 得到 0。Tree v7.0-9887-ga43fac2b5813. Driver names are named because the code was read; the figures stay generic because the principle is general.树是 v7.0-9887-ga43fac2b5813。驱动名写出来是因为代码确实读了;图里保持通用名,因为原理是通用的。
drivers/gpu/drm/rockchip/rockchip_drm_vop2.c:41-68 — the vendor's own block diagram in the driver header: six windows, six layer selectors, three overlays, three video ports, an output selector.——驱动头部厂商自己画的框图:六个窗口、六个层选择器、三个混合器、三个视频端口、一个输出选择。rockchip_drm_vop2.c:500 — “a Cluster window has 2048 x 16 line buffer”. Not a simplification, and not one frame.——原文「a Cluster window has 2048 x 16 line buffer」。不是我简化的,也不是一帧。rockchip_drm_vop2.c:1370 · vop2_win_write(win, VOP2_WIN_YRGB_MST, yrgb_mst)drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c — grep -c dma_addr returns 0. It never issues a read.——grep -c dma_addr 得到 0。它从不发读请求。drivers/gpu/drm/panfrost/panfrost_regs.h · panfrost_mmu.c:820include/video/mipi_display.h:115 — MIPI_DCS_SET_TEAR_ON = 0x35, issued at drm_mipi_dsi.c:1380. Lane count per panel:——MIPI_DCS_SET_TEAR_ON = 0x35,发命令的地方在 drm_mipi_dsi.c:1380。每块屏自己的 lane 数: include/drm/drm_mipi_dsi.h:192include/drm/ttm/ttm_placement.h:51 · amdgpu_ttm.c:383 · :2484 · sdma_v5_2.c:2060 · amdgpu_display.c:560All ten are templates you can copy十张全都是可以直接抄走的模板
They live in templates/diagrams/ as arm64-display-path.svg, what-is-a-frame.svg, gpu-tile-render.svg, display-controller-inside.svg, buffer-roles.svg, dsi-panel-inside.svg, bus-tenants.svg, display-vocabulary.svg, display-triage.svg and vram-copy-flow.svg, next to thirty-nine more built the same way. All of them follow references/diagram-craft.md and pass the design-review checks. The animated flow lines stop under prefers-reduced-motion.它们在 templates/diagrams/ 下,叫 arm64-display-path.svg、what-is-a-frame.svg、gpu-tile-render.svg、display-controller-inside.svg、buffer-roles.svg、dsi-panel-inside.svg、bus-tenants.svg、display-vocabulary.svg、display-triage.svg 和 vram-copy-flow.svg,旁边还有三十九张同一套画法的图。全部遵循 references/diagram-craft.md,并通过 design-review 的检查。会动的流动线在 prefers-reduced-motion 下会停。