ReactOS 图形系统分析(3):显示驱动 DDI framebuf.dll
1. 概述
framebuf.dll 是 ReactOS 的 通用帧缓冲显示驱动(Framebuffer Display Driver) ,源代码位于 win32ss/drivers/displays/framebuf/。它是显示栈中实现 DDI(Device Driver Interface) 的组件,直接向 win32k.sys 的 GDI 引擎注册绘图回调。
在 VirtualBox 虚拟机中,该驱动被实际加载(Loading: \SystemRoot\System32\framebuf.dll),与 vgapnp.sys(VBE miniport)、videoprt.sys(视频端口驱动)构成完整显示栈:
win32k.sys (GDI 引擎)
↓ DDI 回调调用
framebuf.dll (显示驱动,本分析对象)
↓ EngDeviceIoControl (IOCTL)
videoprt.sys (视频端口驱动)
↓ HwStartIO (VRP)
vgapnp.sys (VBE miniport)
↓ INT 10 BIOS
VBE BIOS / 帧缓冲内存 (VirtualBox 虚拟显卡)
framebuf 如何通过 videoprt 把图像显示到屏幕:
整个显示过程分为建立映射阶段 和持续渲染阶段,framebuf 只在建立阶段通过 IOCTL 主动请求 videoprt 服务,渲染阶段则完全不经过 videoprt:
① 建立映射(一次性,由 DrvEnableSurface 完成):
framebuf ──IOCTL_VIDEO_SET_CURRENT_MODE──▶ videoprt ──VRP──▶ vgapnp ──INT 10 0x4F02──▶ BIOS 切换显示模式
framebuf ──IOCTL_VIDEO_MAP_VIDEO_MEMORY─▶ videoprt ──VRP──▶ vgapnp ──VideoPortMapMemory──▶ 得到帧缓冲虚拟地址
│
framebuf ◀────────────────────── 帧缓冲虚拟地址 (ScreenPtr) ◀─────────────────────────────────┘
具体过程:framebuf 调用 EngDeviceIoControl 下发 IOCTL_VIDEO_SET_CURRENT_MODE,videoprt 的 VideoPortForwardDeviceControl 将其翻译为 VIDEO_REQUEST_PACKET 转发给 miniport 的 HwStartIO,vgapnp 通过 Int10CallBios(0x4F02) 让 VBE BIOS 把显卡切到目标模式;随后 framebuf 再下发 IOCTL_VIDEO_MAP_VIDEO_MEMORY,vgapnp 根据 VBE 模式信息中的 PhysBasePtr(显存物理地址)调用 VideoPortMapMemory 把它映射为内核虚拟地址并返回给 framebuf,存入 ppdev->ScreenPtr。最后 framebuf 用这个指针调用 EngCreateBitmap 创建"像素数据直接指向显存"的引擎位图,并 EngAssociateSurface 关联为显示表面。
② 持续渲染(每次画面更新,不经过 videoprt):
应用程序绘图 → gdi32.dll → win32k (ntgdi) → GDI 引擎 (eng/) → DIB 引擎软件渲染
→ 直接写入 EngCreateBitmap 指向的显存虚拟地址 (ScreenPtr)
→ 显卡硬件把显存内容输出到屏幕
此时 framebuf 创建的表面和显存是同一块内存 (EngCreateBitmap 的 pvBits 直接传入 ScreenPtr,不复制像素)。GDI 引擎每次绘图都直接向这块内存写入颜色数据,写入完成后屏幕立刻反映最新内容------因为该内存就是显卡的帧缓冲(对 VirtualBox 而言就是 VRAM,VirtualBox 将其内容显示到宿主窗口)。此阶段 videoprt 和 vgapnp 完全不再参与,只有三种情况会再次穿过 videoprt:
| 场景 | IOCTL | 目的 |
|---|---|---|
| 调色板变化(8bpp) | IOCTL_VIDEO_SET_COLOR_REGISTERS |
vgapnp 写 DAC 端口 / 调用 BIOS 0x4F09 更新硬件调色板 |
| 全屏切换 | IOCTL_VIDEO_SET_CURRENT_MODE / IOCTL_VIDEO_RESET_DEVICE |
DrvAssertMode 重新设置或复位模式 |
| 关机/销毁 | IOCTL_VIDEO_UNMAP_VIDEO_MEMORY |
DrvDisableSurface 解除显存映射 |
#mermaid-svg-2dh9XWCZhLnIzPs1{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-2dh9XWCZhLnIzPs1 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-2dh9XWCZhLnIzPs1 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-2dh9XWCZhLnIzPs1 .error-icon{fill:#552222;}#mermaid-svg-2dh9XWCZhLnIzPs1 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-2dh9XWCZhLnIzPs1 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-2dh9XWCZhLnIzPs1 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-2dh9XWCZhLnIzPs1 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-2dh9XWCZhLnIzPs1 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-2dh9XWCZhLnIzPs1 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-2dh9XWCZhLnIzPs1 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-2dh9XWCZhLnIzPs1 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-2dh9XWCZhLnIzPs1 .marker.cross{stroke:#333333;}#mermaid-svg-2dh9XWCZhLnIzPs1 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-2dh9XWCZhLnIzPs1 p{margin:0;}#mermaid-svg-2dh9XWCZhLnIzPs1 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-2dh9XWCZhLnIzPs1 .cluster-label text{fill:#333;}#mermaid-svg-2dh9XWCZhLnIzPs1 .cluster-label span{color:#333;}#mermaid-svg-2dh9XWCZhLnIzPs1 .cluster-label span p{background-color:transparent;}#mermaid-svg-2dh9XWCZhLnIzPs1 .label text,#mermaid-svg-2dh9XWCZhLnIzPs1 span{fill:#333;color:#333;}#mermaid-svg-2dh9XWCZhLnIzPs1 .node rect,#mermaid-svg-2dh9XWCZhLnIzPs1 .node circle,#mermaid-svg-2dh9XWCZhLnIzPs1 .node ellipse,#mermaid-svg-2dh9XWCZhLnIzPs1 .node polygon,#mermaid-svg-2dh9XWCZhLnIzPs1 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-2dh9XWCZhLnIzPs1 .rough-node .label text,#mermaid-svg-2dh9XWCZhLnIzPs1 .node .label text,#mermaid-svg-2dh9XWCZhLnIzPs1 .image-shape .label,#mermaid-svg-2dh9XWCZhLnIzPs1 .icon-shape .label{text-anchor:middle;}#mermaid-svg-2dh9XWCZhLnIzPs1 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-2dh9XWCZhLnIzPs1 .rough-node .label,#mermaid-svg-2dh9XWCZhLnIzPs1 .node .label,#mermaid-svg-2dh9XWCZhLnIzPs1 .image-shape .label,#mermaid-svg-2dh9XWCZhLnIzPs1 .icon-shape .label{text-align:center;}#mermaid-svg-2dh9XWCZhLnIzPs1 .node.clickable{cursor:pointer;}#mermaid-svg-2dh9XWCZhLnIzPs1 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-2dh9XWCZhLnIzPs1 .arrowheadPath{fill:#333333;}#mermaid-svg-2dh9XWCZhLnIzPs1 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-2dh9XWCZhLnIzPs1 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-2dh9XWCZhLnIzPs1 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2dh9XWCZhLnIzPs1 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-2dh9XWCZhLnIzPs1 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2dh9XWCZhLnIzPs1 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-2dh9XWCZhLnIzPs1 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-2dh9XWCZhLnIzPs1 .cluster text{fill:#333;}#mermaid-svg-2dh9XWCZhLnIzPs1 .cluster span{color:#333;}#mermaid-svg-2dh9XWCZhLnIzPs1 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-2dh9XWCZhLnIzPs1 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-2dh9XWCZhLnIzPs1 rect.text{fill:none;stroke-width:0;}#mermaid-svg-2dh9XWCZhLnIzPs1 .icon-shape,#mermaid-svg-2dh9XWCZhLnIzPs1 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2dh9XWCZhLnIzPs1 .icon-shape p,#mermaid-svg-2dh9XWCZhLnIzPs1 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-2dh9XWCZhLnIzPs1 .icon-shape .label rect,#mermaid-svg-2dh9XWCZhLnIzPs1 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2dh9XWCZhLnIzPs1 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-2dh9XWCZhLnIzPs1 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-2dh9XWCZhLnIzPs1 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 渲染阶段(持续,绕过 videoprt)
建立阶段(一次)
IOCTL_SET_CURRENT_MODE
IOCTL_MAP_VIDEO_MEMORY
HwStartIO
0x4F02/VideoPortMapMemory
帧缓冲虚拟地址
EngCreateBitmap
pvBits=ScreenPtr
直接写像素
显存内容
framebuf
DrvEnableSurface
videoprt
IRP→VRP 转发
vgapnp
显存/BIOS
GDI 显示表面
(与显存同一块内存)
应用程序
GDI 引擎 DIB 软件渲染
显卡输出到屏幕
总结: framebuf 通过 videoprt(及 miniport)完成"模式设置 + 显存映射"后,就把 videoprt 的桥梁作用"用完即弃"------之后的画面显示是 GDI 引擎向显存直写 + 显卡硬件自动刷新的纯内存操作,videoprt 只在模式/调色板/复位等控制性操作时才重新介入。
1.2 颜色数据(像素)在帧缓冲中的结构
framebuf 写入帧缓冲的"颜色数据"不是简单的 RGB 三元组,而是按显示模式位深打包的原始像素值。其结构由 GDI 表面(SURFOBJ)与像素格式共同决定。
① 表面内存布局 --- SURFOBJ 三要素
EngCreateBitmap 创建的表面在内核中对应 SURFOBJ 结构,GDI 引擎通过它定位每个像素:
c
typedef struct _SURFOBJ {
DHSURF dhsurf;
HSURF hsurf;
DHPDEV dhpdev; // → 关联的 PDEV(framebuf 的 PPDEV)
HDEV hdev;
SIZEL sizlBitmap; // 表面尺寸(宽×高,即 ScreenWidth×ScreenHeight)
ULONG cjBits; // 像素数据总字节数
PVOID pvBits; // 像素数据起始地址(= 帧缓冲 ScreenPtr)
PVOID pvScan0; // 第一行起始地址(TOP-DOWN 时 = pvBits)
LONG lDelta; // 行距:一行像素的字节数(= ScreenDelta/pitch)
ULONG iUniq;
ULONG iBitmapFormat; // 像素格式(BMF_8BPP/16BPP/24BPP/32BPP)
USHORT iType; // STYPE_BITMAP
...
} SURFOBJ;
像素寻址公式(任意坐标 (x, y)):
像素地址 = pvScan0 + y × lDelta + x × (BitsPerPixel / 8)
其中 lDelta(= ScreenDelta)来自 VBE 模式信息的 BytesPerScanLine/LinBytesPerScanLine,可能大于 宽 × 每像素字节数(存在行尾填充对齐),因此相邻两行的首像素地址差固定为 lDelta。
② 四种像素格式的位结构
帧缓冲内存是一维字节流,按每像素占用字节数解析:
#mermaid-svg-wOdrhwsaYUezJW35{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-wOdrhwsaYUezJW35 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-wOdrhwsaYUezJW35 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-wOdrhwsaYUezJW35 .error-icon{fill:#552222;}#mermaid-svg-wOdrhwsaYUezJW35 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-wOdrhwsaYUezJW35 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-wOdrhwsaYUezJW35 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-wOdrhwsaYUezJW35 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-wOdrhwsaYUezJW35 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-wOdrhwsaYUezJW35 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-wOdrhwsaYUezJW35 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-wOdrhwsaYUezJW35 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-wOdrhwsaYUezJW35 .marker.cross{stroke:#333333;}#mermaid-svg-wOdrhwsaYUezJW35 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-wOdrhwsaYUezJW35 p{margin:0;}#mermaid-svg-wOdrhwsaYUezJW35 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-wOdrhwsaYUezJW35 .cluster-label text{fill:#333;}#mermaid-svg-wOdrhwsaYUezJW35 .cluster-label span{color:#333;}#mermaid-svg-wOdrhwsaYUezJW35 .cluster-label span p{background-color:transparent;}#mermaid-svg-wOdrhwsaYUezJW35 .label text,#mermaid-svg-wOdrhwsaYUezJW35 span{fill:#333;color:#333;}#mermaid-svg-wOdrhwsaYUezJW35 .node rect,#mermaid-svg-wOdrhwsaYUezJW35 .node circle,#mermaid-svg-wOdrhwsaYUezJW35 .node ellipse,#mermaid-svg-wOdrhwsaYUezJW35 .node polygon,#mermaid-svg-wOdrhwsaYUezJW35 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-wOdrhwsaYUezJW35 .rough-node .label text,#mermaid-svg-wOdrhwsaYUezJW35 .node .label text,#mermaid-svg-wOdrhwsaYUezJW35 .image-shape .label,#mermaid-svg-wOdrhwsaYUezJW35 .icon-shape .label{text-anchor:middle;}#mermaid-svg-wOdrhwsaYUezJW35 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-wOdrhwsaYUezJW35 .rough-node .label,#mermaid-svg-wOdrhwsaYUezJW35 .node .label,#mermaid-svg-wOdrhwsaYUezJW35 .image-shape .label,#mermaid-svg-wOdrhwsaYUezJW35 .icon-shape .label{text-align:center;}#mermaid-svg-wOdrhwsaYUezJW35 .node.clickable{cursor:pointer;}#mermaid-svg-wOdrhwsaYUezJW35 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-wOdrhwsaYUezJW35 .arrowheadPath{fill:#333333;}#mermaid-svg-wOdrhwsaYUezJW35 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-wOdrhwsaYUezJW35 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-wOdrhwsaYUezJW35 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wOdrhwsaYUezJW35 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-wOdrhwsaYUezJW35 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wOdrhwsaYUezJW35 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-wOdrhwsaYUezJW35 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-wOdrhwsaYUezJW35 .cluster text{fill:#333;}#mermaid-svg-wOdrhwsaYUezJW35 .cluster span{color:#333;}#mermaid-svg-wOdrhwsaYUezJW35 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-wOdrhwsaYUezJW35 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-wOdrhwsaYUezJW35 rect.text{fill:none;stroke-width:0;}#mermaid-svg-wOdrhwsaYUezJW35 .icon-shape,#mermaid-svg-wOdrhwsaYUezJW35 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wOdrhwsaYUezJW35 .icon-shape p,#mermaid-svg-wOdrhwsaYUezJW35 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-wOdrhwsaYUezJW35 .icon-shape .label rect,#mermaid-svg-wOdrhwsaYUezJW35 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wOdrhwsaYUezJW35 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-wOdrhwsaYUezJW35 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-wOdrhwsaYUezJW35 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 帧缓冲内存字节流 (低地址 → 高地址)
像素(0,0)
像素(1,0)
像素(2,0)
...
BMF_8BPP(1 字节/像素)--- 调色板索引:
bit: 7 6 5 4 3 2 1 0
┌─────────────────────────────────────────────┐
│ 8 位调色板索引 (0~255) │
└─────────────────────────────────────────────┘
写入的值是索引 而非颜色本身,颜色由硬件调色板(CLUT,256 项 × 32 位 RGB)映射。framebuf 通过 IOCTL_VIDEO_SET_COLOR_REGISTERS 维护这份 CLUT。
BMF_16BPP(2 字节/像素,USHORT)--- 高彩色:
布局由 RedMask/GreenMask/BlueMask 决定,常见两种:
RGB565 (RedMask=0xF800): RGB555 (RedMask=0x7C00):
bit: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 bit: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
│ R R R R R G G G G G G B B B B B │ │ R R R R R G G G G G B B B B B │
内存中(小端):低字节在前。R/G/B 各分量从 8 位精度的 COLORREF 右移截断得到。
BMF_24BPP(3 字节/像素)--- 真彩色:
byte: 0 1 2
┌──────────┬──────────┬──────────┐
│ Blue │ Green │ Red │
└──────────┴──────────┴──────────┘
内存顺序为 B-G-R (与 32 位值 0x00RRGGBB 小端存储一致)。每像素 3 字节,无填充。
BMF_32BPP(4 字节/像素,ULONG)--- 真彩色 + 保留字节:
dword 值 (小端): 0x00RRGGBB
内存字节顺序: byte0=Blue byte1=Green byte2=Red byte3=0x00(保留/Alpha)
┌──────────┬──────────┬──────────┬──────────┐
│ Blue │ Green │ Red │ 0x00 │
└──────────┴──────────┴──────────┴──────────┘
③ 颜色掩码(Color Mask)的作用
对于 16/32bpp,R/G/B 分量在像素值中的位置不固定 ,由 VBE 模式信息返回的掩码决定(framebuf 存入 ppdev->RedMask/GreenMask/BlueMask,VirtualBox 32bpp 典型值为 0xFF0000 / 0xFF00 / 0xFF):
像素值 = (R << RedFieldPosition) | (G << GreenFieldPosition) | (B << BlueFieldPosition)
例如 RGB565:R 左移 11 位、G 左移 5 位、B 左移 0 位。掩码还用于:
- 创建 DirectDraw 像素格式(
ppdev->ddpfDisplay) - 8bpp 以上的默认调色板(
EngCreatePalette(PAL_BITFIELDS, ..., RedMask, GreenMask, BlueMask)) - framebuf 本身不转换颜色,转换由 GDI 引擎的 XLATEOBJ 完成
④ 颜色转换流程(COLORREF → 像素值)
GDI 引擎绘图时传入的 COLORREF 是 0x00BBGGRR 格式,写入帧缓冲前经过转换:
应用程序: COLORREF = 0x00BBGGRR (红=0xFF0000)
↓ GDI 引擎 / XLATEOBJ 颜色翻译
像素值 = 0x00RRGGBB (按目标格式掩码重排,如 32bpp)
↓ DIB 引擎按小端写入
帧缓冲字节: [BB] [GG] [RR] [00]
↓ 显卡硬件按模式解析
屏幕显示该像素颜色
关键点: framebuf 表面是 DIB 格式位图,GDI 引擎直接按 iBitmapFormat 对应的 DIB 函数表(DibFunctionsForBitmapFormat[],见 win32ss/gdi/dib/dib.c)完成"COLORREF → 像素值 → 写显存"全流程;framebuf 只提供 ScreenPtr 这个目标内存地址。
1.1 模块文件构成
| 文件 | 功能 |
|---|---|
| enable.c(file:///d:/reactos/win32ss/drivers/displays/framebuf/enable.c) | DrvEnableDriver(DDI 函数表)、DrvEnablePDEV/CompletePDEV/DisablePDEV、DirectDraw 使能 |
| screen.c(file:///d:/reactos/win32ss/drivers/displays/framebuf/screen.c) | 模式枚举(GetAvailableModes)、模式选择、GDIINFO/DEVINFO 填充、DrvGetModes |
| surface.c(file:///d:/reactos/win32ss/drivers/displays/framebuf/surface.c) | DrvEnableSurface(设置模式+映射帧缓冲+创建表面)、DrvDisableSurface、DrvAssertMode |
| palette.c(file:///d:/reactos/win32ss/drivers/displays/framebuf/palette.c) | 默认调色板初始化、IntSetPalette(CLUT 下发)、DrvSetPalette |
| pointer.c(file:///d:/reactos/win32ss/drivers/displays/framebuf/pointer.c) | 鼠标指针(软件光标经 GDI,或实验性硬件光标) |
| dd.c(file:///d:/reactos/win32ss/drivers/displays/framebuf/dd.c) | DirectDraw 2D 回调(DdCanCreateSurface、DdCreateSurface) |
| ddenable.c(file:///d:/reactos/win32ss/drivers/displays/framebuf/ddenable.c) | DirectDraw 初始化(DrvEnableDirectDraw、DrvGetDirectDrawInfo) |
| framebuf.h(file:///d:/reactos/win32ss/drivers/displays/framebuf/framebuf.h) | PDEV 结构定义、函数声明 |
1.2 构建配置
cmake
add_library(framebuf MODULE ${SOURCE} framebuf.rc)
set_module_type(framebuf kerneldll ENTRYPOINT DrvEnableDriver 12) # 内核 DLL,入口 DrvEnableDriver
add_importlibs(framebuf win32k) # 导入 win32k(Eng* 引擎函数)
add_cd_file(TARGET framebuf DESTINATION reactos/system32) # 安装到 system32
关键点: 类型为 kerneldll(非驱动),安装到 system32(而非 drivers),入口点是 DrvEnableDriver,导入 win32k 以调用 Eng* 引擎服务函数。
2. DDI 函数表与驱动入口
2.1 DrvEnableDriver --- 驱动入口
win32k 加载 framebuf.dll 后调用其导出入口 DrvEnableDriver,获取 DDI 函数表:
c
static DRVFN DrvFunctionTable[] =
{
{INDEX_DrvEnablePDEV, (PFN)DrvEnablePDEV},
{INDEX_DrvCompletePDEV, (PFN)DrvCompletePDEV},
{INDEX_DrvDisablePDEV, (PFN)DrvDisablePDEV},
{INDEX_DrvEnableSurface, (PFN)DrvEnableSurface},
{INDEX_DrvDisableSurface, (PFN)DrvDisableSurface},
{INDEX_DrvAssertMode, (PFN)DrvAssertMode},
{INDEX_DrvGetModes, (PFN)DrvGetModes},
{INDEX_DrvSetPalette, (PFN)DrvSetPalette},
{INDEX_DrvSetPointerShape, (PFN)DrvSetPointerShape},
{INDEX_DrvMovePointer, (PFN)DrvMovePointer},
{INDEX_DrvEnableDirectDraw, (PFN)DrvEnableDirectDraw},
{INDEX_DrvDisableDirectDraw, (PFN)DrvDisableDirectDraw},
};
BOOL APIENTRY DrvEnableDriver(ULONG iEngineVersion, ULONG cj, PDRVENABLEDATA pded)
{
pded->c = sizeof(DrvFunctionTable) / sizeof(DRVFN); // 12 个 DDI 函数
pded->pdrvfn = DrvFunctionTable;
pded->iDriverVersion = DDI_DRIVER_VERSION_NT5; // NT 5.x DDI 版本
return TRUE;
}
特点: 函数表很小(12 项)------framebuf 没有实现 DrvBitBlt、DrvTextOut、DrvStrokePath 等绘图函数。所有绘图操作完全由 GDI 引擎的软件渲染(DIB 引擎)完成,framebuf 只负责提供"画布"(帧缓冲表面)和模式管理。这使它成为一个极简但通用的显示驱动。
3. 核心数据结构 --- PDEV
c
typedef struct _PDEV
{
HANDLE hDriver; // 设备句柄(EngDeviceIoControl 用)
HDEV hDevEng; // 引擎设备句柄(DrvCompletePDEV 保存)
HSURF hSurfEng; // 引擎表面句柄(DrvEnableSurface 创建)
ULONG ModeIndex; // 当前模式索引(vgapnp 的 ModeIndex)
ULONG ScreenWidth; // 屏幕宽(像素)
ULONG ScreenHeight; // 屏幕高(像素)
ULONG ScreenDelta; // 每行字节数(stride/pitch)
BYTE BitsPerPixel; // 每像素位数 (8/16/24/32)
ULONG RedMask, GreenMask, BlueMask; // 颜色掩码
BYTE PaletteShift; // 8bpp 调色板移位
PVOID ScreenPtr; // 帧缓冲指针(映射后的虚拟地址)
HPALETTE DefaultPalette; // 默认调色板
PALETTEENTRY *PaletteEntries; // 8bpp 调色板条目
#ifdef EXPERIMENTAL_MOUSE_CURSOR_SUPPORT
/* 实验性硬件光标支持 */
VIDEO_POINTER_ATTRIBUTES PointerAttributes;
XLATEOBJ *PointerXlateObject;
HSURF PointerColorSurface;
HSURF PointerMaskSurface;
HSURF PointerSaveSurface;
POINTL PointerHotSpot;
#endif
/* DirectX 支持 */
DWORD iDitherFormat; // 抖动格式(BMF_8BPP ~ BMF_32BPP)
ULONG MemHeight; // 显存高度(可能大于屏幕高度)
ULONG MemWidth; // 显存宽度
DWORD dwHeap; // DirectDraw 堆数量
VIDEOMEMORY* pvmList; // DirectDraw 视频内存描述
BOOL bDDInitialized; // DirectDraw 初始化标志
DDPIXELFORMAT ddpfDisplay; // DirectDraw 像素格式
} PDEV, *PPDEV;
PDEV 生命周期:
DrvEnablePDEV → 分配 PDEV + IntInitScreenInfo + IntInitDefaultPalette
DrvCompletePDEV → 保存 hDevEng
DrvEnableSurface → 设置模式 + 映射帧缓冲 + EngCreateBitmap + EngAssociateSurface
... 桌面运行 ...
DrvDisableSurface → 删除表面 + 解除帧缓冲映射
DrvDisablePDEV → 删除调色板 + 释放 PDEV
4. 屏幕信息初始化 --- screen.c
4.1 模式枚举 --- GetAvailableModes
通过 EngDeviceIoControl 向 miniport 查询模式:
c
DWORD GetAvailableModes(HANDLE hDriver, PVIDEO_MODE_INFORMATION *ModeInfo, DWORD *ModeInfoSize)
{
/* 1. 查询模式数量 */
EngDeviceIoControl(hDriver, IOCTL_VIDEO_QUERY_NUM_AVAIL_MODES, NULL, 0,
&Modes, sizeof(VIDEO_NUM_MODES), &ulTemp);
/* 2. 分配缓冲区并查询全部模式 */
*ModeInfo = EngAllocMem(0, Modes.NumModes * Modes.ModeInformationLength, ALLOC_TAG);
EngDeviceIoControl(hDriver, IOCTL_VIDEO_QUERY_AVAIL_MODES, NULL, 0,
*ModeInfo, Modes.NumModes * Modes.ModeInformationLength, &ulTemp);
/* 3. 过滤不支持的显示驱动模式(内部机制:Length 置 0) */
while (ulTemp--)
{
if ((ModeInfoPtr->NumberOfPlanes != 1) || // 必须单平面
!(ModeInfoPtr->AttributeFlags & VIDEO_MODE_GRAPHICS) || // 必须图形模式
((BitsPerPlane != 8) && (BitsPerPlane != 16) &&
(BitsPerPlane != 24) && (BitsPerPlane != 32))) // 必须 8/16/24/32bpp
{
ModeInfoPtr->Length = 0; // 标记不支持
}
}
}
关键过滤规则: framebuf 只支持 1 平面、图形模式、8/16/24/32 位色深。vgapnp 返回的模式(含 15bpp 等)在此被过滤。
4.2 模式选择 --- IntInitScreenInfo
#mermaid-svg-eILtR6dNcYXYuozd{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-eILtR6dNcYXYuozd .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-eILtR6dNcYXYuozd .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-eILtR6dNcYXYuozd .error-icon{fill:#552222;}#mermaid-svg-eILtR6dNcYXYuozd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-eILtR6dNcYXYuozd .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-eILtR6dNcYXYuozd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-eILtR6dNcYXYuozd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-eILtR6dNcYXYuozd .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-eILtR6dNcYXYuozd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-eILtR6dNcYXYuozd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-eILtR6dNcYXYuozd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-eILtR6dNcYXYuozd .marker.cross{stroke:#333333;}#mermaid-svg-eILtR6dNcYXYuozd svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-eILtR6dNcYXYuozd p{margin:0;}#mermaid-svg-eILtR6dNcYXYuozd .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-eILtR6dNcYXYuozd .cluster-label text{fill:#333;}#mermaid-svg-eILtR6dNcYXYuozd .cluster-label span{color:#333;}#mermaid-svg-eILtR6dNcYXYuozd .cluster-label span p{background-color:transparent;}#mermaid-svg-eILtR6dNcYXYuozd .label text,#mermaid-svg-eILtR6dNcYXYuozd span{fill:#333;color:#333;}#mermaid-svg-eILtR6dNcYXYuozd .node rect,#mermaid-svg-eILtR6dNcYXYuozd .node circle,#mermaid-svg-eILtR6dNcYXYuozd .node ellipse,#mermaid-svg-eILtR6dNcYXYuozd .node polygon,#mermaid-svg-eILtR6dNcYXYuozd .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-eILtR6dNcYXYuozd .rough-node .label text,#mermaid-svg-eILtR6dNcYXYuozd .node .label text,#mermaid-svg-eILtR6dNcYXYuozd .image-shape .label,#mermaid-svg-eILtR6dNcYXYuozd .icon-shape .label{text-anchor:middle;}#mermaid-svg-eILtR6dNcYXYuozd .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-eILtR6dNcYXYuozd .rough-node .label,#mermaid-svg-eILtR6dNcYXYuozd .node .label,#mermaid-svg-eILtR6dNcYXYuozd .image-shape .label,#mermaid-svg-eILtR6dNcYXYuozd .icon-shape .label{text-align:center;}#mermaid-svg-eILtR6dNcYXYuozd .node.clickable{cursor:pointer;}#mermaid-svg-eILtR6dNcYXYuozd .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-eILtR6dNcYXYuozd .arrowheadPath{fill:#333333;}#mermaid-svg-eILtR6dNcYXYuozd .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-eILtR6dNcYXYuozd .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-eILtR6dNcYXYuozd .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-eILtR6dNcYXYuozd .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-eILtR6dNcYXYuozd .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-eILtR6dNcYXYuozd .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-eILtR6dNcYXYuozd .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-eILtR6dNcYXYuozd .cluster text{fill:#333;}#mermaid-svg-eILtR6dNcYXYuozd .cluster span{color:#333;}#mermaid-svg-eILtR6dNcYXYuozd div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-eILtR6dNcYXYuozd .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-eILtR6dNcYXYuozd rect.text{fill:none;stroke-width:0;}#mermaid-svg-eILtR6dNcYXYuozd .icon-shape,#mermaid-svg-eILtR6dNcYXYuozd .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-eILtR6dNcYXYuozd .icon-shape p,#mermaid-svg-eILtR6dNcYXYuozd .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-eILtR6dNcYXYuozd .icon-shape .label rect,#mermaid-svg-eILtR6dNcYXYuozd .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-eILtR6dNcYXYuozd .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-eILtR6dNcYXYuozd .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-eILtR6dNcYXYuozd :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
否
是
是
否
IntInitScreenInfo
GetAvailableModes 获取模式列表
pDevMode 全为 0?
(无指定模式)
选择第一个支持的模式
匹配 dmPelsWidth/dmPelsHeight/
dmBitsPerPel/dmDisplayFrequency
找到匹配模式?
返回 FALSE
保存到 PDEV
ModeIndex/ScreenWidth/Height/Delta
BitsPerPixel/RedMask 等
填充 GDIINFO
版本/技术/分辨率/色深/刷新率
填充 DEVINFO
默认字体 System/MS Sans Serif/Courier
flGraphicsCaps
BitsPerPixel == 8?
GCAPS_PALMANAGED
iDitherFormat = BMF_8BPP
PaletteShift = 8 - DAC 位数
16/24/32 bpp
iDitherFormat = BMF_16BPP/24BPP/32BPP
ulNumColors = -1 (真彩色)
返回 TRUE
GDIINFO 关键字段:
ulTechnology = DT_RASDISPLAY--- 光栅显示设备cBitsPixel / cPlanes--- 色深与平面数ulDACRed/Green/Blue--- DAC 位数(来自 miniport 模式信息)ciDevice--- 颜色特性(优先从IOCTL_VIDEO_QUERY_COLOR_CAPABILITIES获取,失败用默认 sRGB 色度值)ulHTOutputFormat--- 半色调输出格式(HT_FORMAT_8BPP/16BPP/24BPP/32BPP)
DEVINFO 关键字段:
lfDefaultFont = "System"(16x7,粗体)--- 系统默认字体lfAnsiVarFont = "MS Sans Serif"(12x9)--- 可变宽字体lfAnsiFixFont = "Courier"(12x9)--- 固定宽字体iDitherFormat--- framebuf 支持的位图格式flGraphicsCaps = GCAPS_PALMANAGED(仅 8bpp)
4.3 DrvGetModes --- 模式列表输出
DrvGetModes 被 win32k 调用以枚举可用显示模式(配合显示控制面板/设置程序):
pdm == NULL时返回所需缓冲大小(ModeCount * sizeof(DEVMODEW))- 否则逐个模式填充
DEVMODEW(设备名 "framebuf"、位深、分辨率、刷新率)
5. 表面管理 --- surface.c
5.1 DrvEnableSurface --- 创建显示表面
这是 framebuf 最核心的函数,完成"硬件帧缓冲 → GDI 表面"的绑定:
#mermaid-svg-EquCmJHyXehO6Tq2{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-EquCmJHyXehO6Tq2 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-EquCmJHyXehO6Tq2 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-EquCmJHyXehO6Tq2 .error-icon{fill:#552222;}#mermaid-svg-EquCmJHyXehO6Tq2 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-EquCmJHyXehO6Tq2 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-EquCmJHyXehO6Tq2 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-EquCmJHyXehO6Tq2 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-EquCmJHyXehO6Tq2 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-EquCmJHyXehO6Tq2 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-EquCmJHyXehO6Tq2 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-EquCmJHyXehO6Tq2 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-EquCmJHyXehO6Tq2 .marker.cross{stroke:#333333;}#mermaid-svg-EquCmJHyXehO6Tq2 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-EquCmJHyXehO6Tq2 p{margin:0;}#mermaid-svg-EquCmJHyXehO6Tq2 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-EquCmJHyXehO6Tq2 .cluster-label text{fill:#333;}#mermaid-svg-EquCmJHyXehO6Tq2 .cluster-label span{color:#333;}#mermaid-svg-EquCmJHyXehO6Tq2 .cluster-label span p{background-color:transparent;}#mermaid-svg-EquCmJHyXehO6Tq2 .label text,#mermaid-svg-EquCmJHyXehO6Tq2 span{fill:#333;color:#333;}#mermaid-svg-EquCmJHyXehO6Tq2 .node rect,#mermaid-svg-EquCmJHyXehO6Tq2 .node circle,#mermaid-svg-EquCmJHyXehO6Tq2 .node ellipse,#mermaid-svg-EquCmJHyXehO6Tq2 .node polygon,#mermaid-svg-EquCmJHyXehO6Tq2 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-EquCmJHyXehO6Tq2 .rough-node .label text,#mermaid-svg-EquCmJHyXehO6Tq2 .node .label text,#mermaid-svg-EquCmJHyXehO6Tq2 .image-shape .label,#mermaid-svg-EquCmJHyXehO6Tq2 .icon-shape .label{text-anchor:middle;}#mermaid-svg-EquCmJHyXehO6Tq2 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-EquCmJHyXehO6Tq2 .rough-node .label,#mermaid-svg-EquCmJHyXehO6Tq2 .node .label,#mermaid-svg-EquCmJHyXehO6Tq2 .image-shape .label,#mermaid-svg-EquCmJHyXehO6Tq2 .icon-shape .label{text-align:center;}#mermaid-svg-EquCmJHyXehO6Tq2 .node.clickable{cursor:pointer;}#mermaid-svg-EquCmJHyXehO6Tq2 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-EquCmJHyXehO6Tq2 .arrowheadPath{fill:#333333;}#mermaid-svg-EquCmJHyXehO6Tq2 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-EquCmJHyXehO6Tq2 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-EquCmJHyXehO6Tq2 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-EquCmJHyXehO6Tq2 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-EquCmJHyXehO6Tq2 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-EquCmJHyXehO6Tq2 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-EquCmJHyXehO6Tq2 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-EquCmJHyXehO6Tq2 .cluster text{fill:#333;}#mermaid-svg-EquCmJHyXehO6Tq2 .cluster span{color:#333;}#mermaid-svg-EquCmJHyXehO6Tq2 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-EquCmJHyXehO6Tq2 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-EquCmJHyXehO6Tq2 rect.text{fill:none;stroke-width:0;}#mermaid-svg-EquCmJHyXehO6Tq2 .icon-shape,#mermaid-svg-EquCmJHyXehO6Tq2 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-EquCmJHyXehO6Tq2 .icon-shape p,#mermaid-svg-EquCmJHyXehO6Tq2 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-EquCmJHyXehO6Tq2 .icon-shape .label rect,#mermaid-svg-EquCmJHyXehO6Tq2 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-EquCmJHyXehO6Tq2 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-EquCmJHyXehO6Tq2 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-EquCmJHyXehO6Tq2 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 失败
成功
失败
成功
8
16
24
32
其他
NULL
失败
成功
DrvEnableSurface
EngDeviceIoControl
IOCTL_VIDEO_SET_CURRENT_MODE
设置 ModeIndex
返回 NULL
EngDeviceIoControl
IOCTL_VIDEO_MAP_VIDEO_MEMORY
映射帧缓冲
ppdev->ScreenPtr = FrameBufferBase
(帧缓冲虚拟地址)
BitsPerPixel?
IntSetPalette 下发 256 色
BitmapType = BMF_8BPP
BMF_16BPP
BMF_24BPP
BMF_32BPP
EngCreateBitmap
创建引擎位图
(ScreenSize, ScreenDelta, BitmapType,
BMF_TOPDOWN, ScreenPtr)
EngAssociateSurface
将表面关联到 hDevEng 设备
EngDeleteSurface
ppdev->hSurfEng = hSurface
返回 hSurface
c
HSURF APIENTRY DrvEnableSurface(DHPDEV dhpdev)
{
/* 1. 设置显示模式(调 vgapnp 的 0x4F02) */
EngDeviceIoControl(ppdev->hDriver, IOCTL_VIDEO_SET_CURRENT_MODE,
&ppdev->ModeIndex, sizeof(ULONG), NULL, 0, &ulTemp);
/* 2. 映射帧缓冲(调 vgapnp 的 VideoPortMapMemory) */
VideoMemory.RequestedVirtualAddress = NULL;
EngDeviceIoControl(ppdev->hDriver, IOCTL_VIDEO_MAP_VIDEO_MEMORY,
&VideoMemory, sizeof(VIDEO_MEMORY),
&VideoMemoryInfo, sizeof(VIDEO_MEMORY_INFORMATION), &ulTemp);
ppdev->ScreenPtr = VideoMemoryInfo.FrameBufferBase;
/* 3. 围绕帧缓冲创建引擎位图(关键:不复制像素,直接指向帧缓冲) */
hSurface = (HSURF)EngCreateBitmap(ScreenSize, ppdev->ScreenDelta, BitmapType,
BMF_TOPDOWN, // 自顶向下布局
ppdev->ScreenPtr); // 像素数据 = 帧缓冲
/* 4. 关联表面到设备 */
EngAssociateSurface(hSurface, ppdev->hDevEng, 0);
}
设计精髓: EngCreateBitmap 的 pvBits 参数直接传入帧缓冲指针。GDI 引擎后续的所有软件渲染(DIB 引擎 BitBlt/TextOut/线条)都直接写帧缓冲内存,无需任何硬件加速,天然适配所有 VBE 兼容显卡。
5.2 DrvDisableSurface --- 销毁表面
c
VOID APIENTRY DrvDisableSurface(DHPDEV dhpdev)
{
EngDeleteSurface(ppdev->hSurfEng); // 删除引擎表面
ppdev->hSurfEng = NULL;
/* 解除帧缓冲映射 */
VideoMemory.RequestedVirtualAddress = ppdev->ScreenPtr;
EngDeviceIoControl(ppdev->hDriver, IOCTL_VIDEO_UNMAP_VIDEO_MEMORY,
&VideoMemory, sizeof(VIDEO_MEMORY), NULL, 0, &ulTemp);
}
5.3 DrvAssertMode --- 模式断言(全屏切换用)
c
BOOL APIENTRY DrvAssertMode(DHPDEV dhpdev, BOOL bEnable)
{
if (bEnable)
{
/* 重新设置模式 + 8bpp 时重设调色板 */
EngDeviceIoControl(ppdev->hDriver, IOCTL_VIDEO_SET_CURRENT_MODE, ...);
}
else
{
/* 调用 miniport 复位设备到已知状态 */
EngDeviceIoControl(ppdev->hDriver, IOCTL_VIDEO_RESET_DEVICE, ...);
}
}
6. 调色板管理 --- palette.c
6.1 默认调色板初始化 --- IntInitDefaultPalette
#mermaid-svg-azYncUNalBMXzQjO{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-azYncUNalBMXzQjO .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-azYncUNalBMXzQjO .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-azYncUNalBMXzQjO .error-icon{fill:#552222;}#mermaid-svg-azYncUNalBMXzQjO .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-azYncUNalBMXzQjO .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-azYncUNalBMXzQjO .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-azYncUNalBMXzQjO .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-azYncUNalBMXzQjO .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-azYncUNalBMXzQjO .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-azYncUNalBMXzQjO .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-azYncUNalBMXzQjO .marker{fill:#333333;stroke:#333333;}#mermaid-svg-azYncUNalBMXzQjO .marker.cross{stroke:#333333;}#mermaid-svg-azYncUNalBMXzQjO svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-azYncUNalBMXzQjO p{margin:0;}#mermaid-svg-azYncUNalBMXzQjO .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-azYncUNalBMXzQjO .cluster-label text{fill:#333;}#mermaid-svg-azYncUNalBMXzQjO .cluster-label span{color:#333;}#mermaid-svg-azYncUNalBMXzQjO .cluster-label span p{background-color:transparent;}#mermaid-svg-azYncUNalBMXzQjO .label text,#mermaid-svg-azYncUNalBMXzQjO span{fill:#333;color:#333;}#mermaid-svg-azYncUNalBMXzQjO .node rect,#mermaid-svg-azYncUNalBMXzQjO .node circle,#mermaid-svg-azYncUNalBMXzQjO .node ellipse,#mermaid-svg-azYncUNalBMXzQjO .node polygon,#mermaid-svg-azYncUNalBMXzQjO .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-azYncUNalBMXzQjO .rough-node .label text,#mermaid-svg-azYncUNalBMXzQjO .node .label text,#mermaid-svg-azYncUNalBMXzQjO .image-shape .label,#mermaid-svg-azYncUNalBMXzQjO .icon-shape .label{text-anchor:middle;}#mermaid-svg-azYncUNalBMXzQjO .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-azYncUNalBMXzQjO .rough-node .label,#mermaid-svg-azYncUNalBMXzQjO .node .label,#mermaid-svg-azYncUNalBMXzQjO .image-shape .label,#mermaid-svg-azYncUNalBMXzQjO .icon-shape .label{text-align:center;}#mermaid-svg-azYncUNalBMXzQjO .node.clickable{cursor:pointer;}#mermaid-svg-azYncUNalBMXzQjO .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-azYncUNalBMXzQjO .arrowheadPath{fill:#333333;}#mermaid-svg-azYncUNalBMXzQjO .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-azYncUNalBMXzQjO .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-azYncUNalBMXzQjO .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-azYncUNalBMXzQjO .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-azYncUNalBMXzQjO .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-azYncUNalBMXzQjO .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-azYncUNalBMXzQjO .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-azYncUNalBMXzQjO .cluster text{fill:#333;}#mermaid-svg-azYncUNalBMXzQjO .cluster span{color:#333;}#mermaid-svg-azYncUNalBMXzQjO div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-azYncUNalBMXzQjO .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-azYncUNalBMXzQjO rect.text{fill:none;stroke-width:0;}#mermaid-svg-azYncUNalBMXzQjO .icon-shape,#mermaid-svg-azYncUNalBMXzQjO .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-azYncUNalBMXzQjO .icon-shape p,#mermaid-svg-azYncUNalBMXzQjO .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-azYncUNalBMXzQjO .icon-shape .label rect,#mermaid-svg-azYncUNalBMXzQjO .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-azYncUNalBMXzQjO .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-azYncUNalBMXzQjO .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-azYncUNalBMXzQjO :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
IntInitDefaultPalette
BitsPerPixel > 8?
EngCreatePalette(PAL_BITFIELDS)
用 RedMask/GreenMask/BlueMask
创建真彩色调色板
分配 256 个 PALETTEENTRY
生成 8-3-3 全彩渐变调色板
R=(i>>5)&7, G=(i>>3)&3, B=i&7
嵌入 20 色系统 BASEPALETTE
前 10 项在索引 0-9
后 10 项在索引 246-255
EngCreatePalette(PAL_INDEXED, 256)
创建索引调色板
保存到 ppdev->DefaultPalette
并写入 pDevInfo->hpalDefault
返回结果
BASEPALETTE(20 色系统基础色): 包含窗口边框、按钮等 GUI 元素必需的标准颜色(黑、暗红/绿/蓝、亮红/绿/蓝、灰阶等)。
6.2 调色板下发 --- IntSetPalette
c
BOOL APIENTRY IntSetPalette(DHPDEV dhpdev, PPALETTEENTRY ppalent, ULONG iStart, ULONG cColors)
{
/* 构造 VIDEO_CLUT 结构 */
ClutSize = sizeof(VIDEO_CLUT) + (cColors * sizeof(ULONG));
pClut->FirstEntry = iStart;
pClut->NumEntries = cColors;
memcpy(&pClut->LookupTable[0].RgbLong, ppalent, sizeof(ULONG) * cColors);
/* 8bpp 调色板移位(适配 DAC 位数) */
if (ppdev->PaletteShift)
{
pClut->LookupTable[i].RgbArray.Red >>= PaletteShift; // 同理 Green/Blue
}
/* 通过 IOCTL 下发到 miniport(vgapnp → DAC 端口或 BIOS 0x4F09) */
EngDeviceIoControl(ppdev->hDriver, IOCTL_VIDEO_SET_COLOR_REGISTERS,
pClut, ClutSize, NULL, 0, &cColors);
}
6.3 DrvSetPalette --- GDI 调色板实现回调
win32k 在调色板需要实现时调用,先通过 PALOBJ_cGetColors 从 PALOBJ 取出颜色,再调用 IntSetPalette 下发。
7. 鼠标指针 --- pointer.c
7.1 默认实现(软件光标)
EXPERIMENTAL_MOUSE_CURSOR_SUPPORT 默认未定义,因此使用最简单的软件光标方案------完全委托给 GDI 引擎:
c
ULONG APIENTRY DrvSetPointerShape(...)
{
/* 拒绝自行绘制,让 GDI 引擎在表面上层绘制指针 */
return EngSetPointerShape(pso, psoMask, psoColor, pxlo, xHot, yHot, x, y, prcl, fl);
}
VOID APIENTRY DrvMovePointer(...)
{
EngMovePointer(pso, x, y, prcl);
}
GDI 引擎会在桌面绘制后把光标画在表面最上层(软件光标,无需硬件支持)。
7.2 实验性硬件光标实现
若定义 EXPERIMENTAL_MOUSE_CURSOR_SUPPORT,则实现完整的硬件光标逻辑:
IntShowMousePointer:先把光标下的像素保存到PointerSaveSurface,再把光标(颜色表面 + 掩码表面)通过EngBitBlt叠加到屏幕IntHideMousePointer:用保存的像素恢复被光标覆盖的区域DrvSetPointerShape:创建颜色表面(光标彩色图)、掩码表面(AND/XOR 掩码)、保存表面DrvMovePointer:先隐藏再移动到新位置再显示
该实现通过 EngBitBlt(ROP 0xAACC 等)实现光标合成,属于 GDI 级软件光标增强。
8. DirectDraw 支持 --- dd.c / ddenable.c
framebuf 提供基础 DirectDraw HAL 支持(让 DirectX 应用能创建显存表面)。
8.1 DrvEnableDirectDraw --- DirectDraw 使能
c
BOOL APIENTRY DrvEnableDirectDraw(DHPDEV dhpdev, DD_CALLBACKS *pCallBacks, ...)
{
/* 设置像素格式 */
ppdev->ddpfDisplay.dwRGBBitCount = ppdev->BitsPerPixel;
ppdev->ddpfDisplay.dwRBitMask = ppdev->RedMask; // 颜色掩码
ppdev->ddpfDisplay.dwGBitMask = ppdev->GreenMask;
ppdev->ddpfDisplay.dwBBitMask = ppdev->BlueMask;
ppdev->ddpfDisplay.dwFlags = DDPF_RGB;
/* 8bpp → DDPF_PALETTEINDEXED8; 32bpp → Alpha 掩码 0xFF000000 */
/* 注册 HAL 回调 */
pCallBacks->CanCreateSurface = (PDD_CANCREATESURFACE)DdCanCreateSurface;
pCallBacks->CreateSurface = (PDD_CREATESURFACE)DdCreateSurface;
}
8.2 DdCanCreateSurface / DdCreateSurface
DdCanCreateSurface:拒绝 Z-Buffer/显存表面、不同像素格式的创建(仅支持与显示格式相同的表面)DdCreateSurface:为 DirectDraw 表面分配内存(系统内存/显存)
8.3 DrvGetDirectDrawInfo --- 显存堆描述
c
BOOL APIENTRY DrvGetDirectDrawInfo(DHPDEV dhpdev, DD_HALINFO *pHalInfo, ...)
{
/* 计算显存总量 = (MemHeight - ScreenHeight) * ScreenDelta */
pHalInfo->ddCaps.dwVidMemTotal = (ppdev->MemHeight - ppdev->ScreenHeight) * ppdev->ScreenDelta;
/* 主表面信息 */
pHalInfo->vmiData.pvPrimary = ppdev->ScreenPtr; // 帧缓冲指针
pHalInfo->vmiData.lDisplayPitch = ppdev->ScreenDelta;
/* 显存堆:屏幕下方剩余区域为 offscreen heap */
pvmList->fpStart = ppdev->ScreenHeight * ppdev->ScreenDelta;
pvmList->fpEnd = ppdev->MemHeight * ppdev->ScreenDelta - 1;
}
9. 与上下层的完整交互
9.1 IOCTL 依赖清单(framebuf → videoprt/miniport)
| IOCTL | 用途 | 下层处理 |
|---|---|---|
IOCTL_VIDEO_QUERY_NUM_AVAIL_MODES |
查询模式数量 | vgapnp VBEQueryNumAvailModes |
IOCTL_VIDEO_QUERY_AVAIL_MODES |
查询模式列表 | vgapnp VBEQueryAvailModes |
IOCTL_VIDEO_QUERY_COLOR_CAPABILITIES |
颜色特性 | miniport |
IOCTL_VIDEO_SET_CURRENT_MODE |
设置显示模式 | vgapnp VBESetCurrentMode (0x4F02) |
IOCTL_VIDEO_MAP_VIDEO_MEMORY |
映射帧缓冲 | vgapnp VBEMapVideoMemory |
IOCTL_VIDEO_UNMAP_VIDEO_MEMORY |
解除映射 | vgapnp VBEUnmapVideoMemory |
IOCTL_VIDEO_RESET_DEVICE |
复位设备 | vgapnp VBEResetDevice |
IOCTL_VIDEO_SET_COLOR_REGISTERS |
设置调色板 | vgapnp VBESetColorRegisters |
9.2 启动加载时序
vgapnp.sys videoprt.sys framebuf.dll win32k.sys vgapnp.sys videoprt.sys framebuf.dll win32k.sys #mermaid-svg-fzItc7sN9Ar6yCdP{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-fzItc7sN9Ar6yCdP .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-fzItc7sN9Ar6yCdP .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-fzItc7sN9Ar6yCdP .error-icon{fill:#552222;}#mermaid-svg-fzItc7sN9Ar6yCdP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-fzItc7sN9Ar6yCdP .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-fzItc7sN9Ar6yCdP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-fzItc7sN9Ar6yCdP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-fzItc7sN9Ar6yCdP .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-fzItc7sN9Ar6yCdP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-fzItc7sN9Ar6yCdP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-fzItc7sN9Ar6yCdP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-fzItc7sN9Ar6yCdP .marker.cross{stroke:#333333;}#mermaid-svg-fzItc7sN9Ar6yCdP svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-fzItc7sN9Ar6yCdP p{margin:0;}#mermaid-svg-fzItc7sN9Ar6yCdP .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-fzItc7sN9Ar6yCdP text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-fzItc7sN9Ar6yCdP .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-fzItc7sN9Ar6yCdP .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-fzItc7sN9Ar6yCdP .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-fzItc7sN9Ar6yCdP .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-fzItc7sN9Ar6yCdP #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-fzItc7sN9Ar6yCdP .sequenceNumber{fill:white;}#mermaid-svg-fzItc7sN9Ar6yCdP #sequencenumber{fill:#333;}#mermaid-svg-fzItc7sN9Ar6yCdP #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-fzItc7sN9Ar6yCdP .messageText{fill:#333;stroke:none;}#mermaid-svg-fzItc7sN9Ar6yCdP .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-fzItc7sN9Ar6yCdP .labelText,#mermaid-svg-fzItc7sN9Ar6yCdP .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-fzItc7sN9Ar6yCdP .loopText,#mermaid-svg-fzItc7sN9Ar6yCdP .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-fzItc7sN9Ar6yCdP .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-fzItc7sN9Ar6yCdP .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-fzItc7sN9Ar6yCdP .noteText,#mermaid-svg-fzItc7sN9Ar6yCdP .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-fzItc7sN9Ar6yCdP .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-fzItc7sN9Ar6yCdP .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-fzItc7sN9Ar6yCdP .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-fzItc7sN9Ar6yCdP .actorPopupMenu{position:absolute;}#mermaid-svg-fzItc7sN9Ar6yCdP .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-fzItc7sN9Ar6yCdP .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-fzItc7sN9Ar6yCdP .actor-man circle,#mermaid-svg-fzItc7sN9Ar6yCdP line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-fzItc7sN9Ar6yCdP :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 桌面就绪,GDI 软件渲染直写帧缓冲 读注册表 InstalledDisplayDrivers="framebuf"加载 framebuf.dllDrvEnableDriver → 获取 DDI 函数表DrvEnablePDEV (DEVMODEW 指定分辨率)IOCTL_VIDEO_QUERY_NUM_AVAIL_MODESIOCTL_VIDEO_QUERY_AVAIL_MODESGetAvailableModes 过滤 → 选择模式 → 填 GDIINFO/DEVINFODrvCompletePDEV (hDevEng)DrvEnableSurfaceIOCTL_VIDEO_SET_CURRENT_MODEIOCTL_VIDEO_MAP_VIDEO_MEMORY帧缓冲虚拟地址EngCreateBitmap(帧缓冲) + EngAssociateSurface
9.3 运行时渲染路径
#mermaid-svg-VrRukHlODsBvErdC{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-VrRukHlODsBvErdC .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-VrRukHlODsBvErdC .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-VrRukHlODsBvErdC .error-icon{fill:#552222;}#mermaid-svg-VrRukHlODsBvErdC .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-VrRukHlODsBvErdC .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-VrRukHlODsBvErdC .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-VrRukHlODsBvErdC .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-VrRukHlODsBvErdC .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-VrRukHlODsBvErdC .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-VrRukHlODsBvErdC .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-VrRukHlODsBvErdC .marker{fill:#333333;stroke:#333333;}#mermaid-svg-VrRukHlODsBvErdC .marker.cross{stroke:#333333;}#mermaid-svg-VrRukHlODsBvErdC svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-VrRukHlODsBvErdC p{margin:0;}#mermaid-svg-VrRukHlODsBvErdC .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-VrRukHlODsBvErdC .cluster-label text{fill:#333;}#mermaid-svg-VrRukHlODsBvErdC .cluster-label span{color:#333;}#mermaid-svg-VrRukHlODsBvErdC .cluster-label span p{background-color:transparent;}#mermaid-svg-VrRukHlODsBvErdC .label text,#mermaid-svg-VrRukHlODsBvErdC span{fill:#333;color:#333;}#mermaid-svg-VrRukHlODsBvErdC .node rect,#mermaid-svg-VrRukHlODsBvErdC .node circle,#mermaid-svg-VrRukHlODsBvErdC .node ellipse,#mermaid-svg-VrRukHlODsBvErdC .node polygon,#mermaid-svg-VrRukHlODsBvErdC .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-VrRukHlODsBvErdC .rough-node .label text,#mermaid-svg-VrRukHlODsBvErdC .node .label text,#mermaid-svg-VrRukHlODsBvErdC .image-shape .label,#mermaid-svg-VrRukHlODsBvErdC .icon-shape .label{text-anchor:middle;}#mermaid-svg-VrRukHlODsBvErdC .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-VrRukHlODsBvErdC .rough-node .label,#mermaid-svg-VrRukHlODsBvErdC .node .label,#mermaid-svg-VrRukHlODsBvErdC .image-shape .label,#mermaid-svg-VrRukHlODsBvErdC .icon-shape .label{text-align:center;}#mermaid-svg-VrRukHlODsBvErdC .node.clickable{cursor:pointer;}#mermaid-svg-VrRukHlODsBvErdC .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-VrRukHlODsBvErdC .arrowheadPath{fill:#333333;}#mermaid-svg-VrRukHlODsBvErdC .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-VrRukHlODsBvErdC .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-VrRukHlODsBvErdC .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-VrRukHlODsBvErdC .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-VrRukHlODsBvErdC .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-VrRukHlODsBvErdC .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-VrRukHlODsBvErdC .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-VrRukHlODsBvErdC .cluster text{fill:#333;}#mermaid-svg-VrRukHlODsBvErdC .cluster span{color:#333;}#mermaid-svg-VrRukHlODsBvErdC div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-VrRukHlODsBvErdC .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-VrRukHlODsBvErdC rect.text{fill:none;stroke-width:0;}#mermaid-svg-VrRukHlODsBvErdC .icon-shape,#mermaid-svg-VrRukHlODsBvErdC .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-VrRukHlODsBvErdC .icon-shape p,#mermaid-svg-VrRukHlODsBvErdC .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-VrRukHlODsBvErdC .icon-shape .label rect,#mermaid-svg-VrRukHlODsBvErdC .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-VrRukHlODsBvErdC .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-VrRukHlODsBvErdC .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-VrRukHlODsBvErdC :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 调色板变更
模式切换/全屏
应用程序
gdi32.dll
win32k ntgdi
(对象/裁剪/变换)
GDI 引擎 eng/
(DIB 软件渲染)
framebuf 表面
(EngCreateBitmap 的位图)
帧缓冲内存
(ScreenPtr 直接映射)
VirtualBox 虚拟显卡
DrvSetPalette
DrvAssertMode
IOCTL 下发 CLUT
关键:framebuf 不实现任何绘图 DDI(DrvBitBlt/DrvTextOut 等),全部渲染由 GDI 引擎 DIB 引擎完成,直接写入帧缓冲。 这是"通用帧缓冲"设计------只依赖硬件支持线性帧缓冲,与具体显卡厂商无关。
10. 设计特点总结
- 零硬件加速:不实现绘图回调,渲染全部由 GDI 引擎软件完成------最大兼容性,适配所有提供线性帧缓冲的显卡。
- 帧缓冲直连 :
EngCreateBitmap(pvBits = ScreenPtr)让 GDI 引擎直接渲染到硬件显存,无中间拷贝。 - 模式过滤严格:只接受 1 平面、图形模式、8/16/24/32bpp。
- 双模式调色板:8bpp 索引调色板(含 20 色系统基础色)+ 真彩色位域调色板(PAL_BITFIELDS)。
- 软件光标默认 :委托
EngSetPointerShape由 GDI 引擎绘制;硬件光标为实验性代码(默认关闭)。 - DirectDraw 基础支持:提供显存表面创建与主表面信息,使 DirectX 应用可用(通过 reactx 驱动)。
- PNP/模式切换完备:DrvAssertMode 支持全屏独占模式切换,DrvGetModes 支持模式枚举。
- 依赖最小化:仅导入 win32k(Eng* 服务)与内核库,通过 IOCTL 与 miniport 解耦。
11. 源码索引
| 文件 | 关键内容 |
|---|---|
| enable.c(file:///d:/reactos/win32ss/drivers/displays/framebuf/enable.c) | DrvFunctionTable、DrvEnableDriver、DrvEnablePDEV、DrvCompletePDEV、DrvDisablePDEV |
| screen.c(file:///d:/reactos/win32ss/drivers/displays/framebuf/screen.c) | GetAvailableModes、IntInitScreenInfo、DrvGetModes、默认字体定义 |
| surface.c(file:///d:/reactos/win32ss/drivers/displays/framebuf/surface.c) | DrvEnableSurface、DrvDisableSurface、DrvAssertMode |
| palette.c(file:///d:/reactos/win32ss/drivers/displays/framebuf/palette.c) | BASEPALETTE、IntInitDefaultPalette、IntSetPalette、DrvSetPalette |
| pointer.c(file:///d:/reactos/win32ss/drivers/displays/framebuf/pointer.c) | 软件光标委托、实验性硬件光标(IntShow/HideMousePointer) |
| dd.c(file:///d:/reactos/win32ss/drivers/displays/framebuf/dd.c) | DdCanCreateSurface、DdCreateSurface |
| ddenable.c(file:///d:/reactos/win32ss/drivers/displays/framebuf/ddenable.c) | DrvEnableDirectDraw、DrvGetDirectDrawInfo |
| framebuf.h(file:///d:/reactos/win32ss/drivers/displays/framebuf/framebuf.h) | PDEV 结构、DirectX 支持字段 |
| CMakeLists.txt(file:///d:/reactos/win32ss/drivers/displays/framebuf/CMakeLists.txt) | 构建配置(kerneldll,入口 DrvEnableDriver) |
关联模块:
- 视频端口驱动:videoprt/(file:///d:/reactos/win32ss/drivers/videoprt/videoprt.c)(提供 IOCTL 服务)
- VBE miniport:miniport/vbe/(file:///d:/reactos/win32ss/drivers/miniport/vbe/vbemp.c)(模式/帧缓冲/调色板实际执行)
- win32k 加载:ntuser/display.c(file:///d:/reactos/win32ss/user/ntuser/display.c)(InstalledDisplayDrivers 指定 framebuf)
- GDI 引擎软件渲染:win32ss/gdi/eng/(file:///d:/reactos/win32ss/gdi/eng/bitblt.c) 与 win32ss/gdi/dib/(file:///d:/reactos/win32ss/gdi/dib/dib.c)(实际执行像素操作)
本文档基于 ReactOS 源代码
win32ss/drivers/displays/framebuf/分析(2026 年 8 月)