编程语言研究书库

图层与加速 — 窗口的物理,和「先测量,再优化」的完整示范

图层与加速 — 窗口的物理,和「先测量,再优化」的完整示范

这一章讲什么: 上一章的鼠标光标用「背景色擦旧、新位置画新」,拖过非背景区就毁图。 解法是所有窗口系统的底层物理:图层——桌面一层、鼠标一层,按高度从底往顶依次画。 建这套系统之前,先补上动态内存(new/malloc);建成之后,它慢到没法用—— 于是本章上演全书最完整的一次「先测量,再优化」:2.5 亿 → 370 万 → 850 万。

1. 先看现象:一次完整的性能循环

主线例子,鼠标移动一次引发的屏幕重绘,以及它如何被一步步治快:

  问题:鼠标移动 → LayerManager::Draw() 全屏重绘
      │  1920×1080 背景 = 207 万像素,每像素一次 Write() 调用 + 格式转换
      │  Local APIC 定时器测得:约 2.5 亿计数(单位不重要,看比值)
      ▼
  优化一(9.5):阴影缓冲区
      每个窗口偷偷备一份「帧缓冲区格式」的副本(shadow_buffer_),
      平时 Write() 同时写两份;重绘时 DrawTo() 直接 memcpy 阴影缓冲区 → 帧缓冲区
      → 测得:约 370 万计数,快了约 67 倍
  问题二(9.6):控制台滚一行(到屏幕底部时)居然要约 1 亿计数
      ▼
  优化二(9.7):Window::Move —— 帧缓冲区内「搬像素」
      滚动 = 把第 2~25 行的像素整体 memcpy 上移 16 行,只重绘最后一行
      → 测得:约 850 万计数,比原来快约 11 倍,「鼠标光标的移动已经完全不生涩了」

  图说:三个数字(2.5亿 → 370万;1亿 → 850万)全部来自作者的 QEMU 实测,
  单位是 Local APIC 计数器的差值——绝对值不可搬,倍数关系可信。

2. 顶层全景

  9.1–9.2 补地基:sbrk() 与程序断点 → new/malloc 解锁(堆:一次性向内存管理器要 128 MiB)
  9.3     建模:Window(绘图区域+透明色)→ Layer(位置+高度,不存图)→ LayerManager(两张表)
  9.4     上秒表:Local APIC 定时器(Initial Count → Current Count 的差值)
  9.5     加速重绘:FrameBuffer(阴影缓冲区)+ Copy() 处理越界裁剪 → LayerManager 改用 FrameBuffer
  9.6–9.7 滚动专项:测量(1亿)→ Window::Move(帧缓冲区内搬像素,注意上/下移方向不同)

  图说:第 9 章结束时,MikanOS 有了现代 GUI 的地基,也有了量化的性能观。

3. 核心原理

3.1 sbrk 与堆:new 的入场券

内存管理器就绪后,第一个愿望是普通的 new。libc++ 的 new 底层调 malloc,Newlib 的 malloc 则需要你实现 sbrk():它维护「程序断点」(program break)——malloc 可用区域的末尾; sbrk(incr) 把断点后移/前移 incr 字节,返回移动前的值,失败置 errno 返回 -11。 初始值和上限由你定:InitializeHeap() 一次性向内存管理器要 64×512 帧 = 128 MiB 当堆, 头尾两个全局变量喂给 sbrk()2。——「之前实现的 sbrk() 很有用!」注释出现在 vector::resize 里, 整个 libc++ 就此盘活3。

3.2 三层模型:Window / Layer / LayerManager

概念切分是本章的精华4:

层是什么关键设计
Window一块有限大小的绘图区域(像素数组 + 透明色)data_ 是程序员视角的 PixelColor;透明色用 std::optional(「有值/无值」)表示
Layer无限平面上的一个「席位」:原点坐标 + 高度不存图,只引用 Window;Move/MoveRelative 只改坐标,不重绘
LayerManager所有图层的管家两张表:layers_(全部,unique_ptr 拥有)与 layer_stack_(可见顺序,末尾=最前)

为什么分 Window 和 Layer?「管理重叠的图层和表示显示区域的窗口是两个不同的东西」—— 职责分离让透明色、位置、可见性各自只在一处被管理5。 重绘规则一句话:按 layer_stack_ 从底到顶,每个 Layer::DrawTo() 调 Window::DrawTo() 画到屏幕6。 高度调整 UpDown(id, h):负值隐藏、0 是底层、超界置顶——实现是 vector 的 erase + insert, 要处理「删一个元素后 end() 游标错位」的边界7。窗口所有权用智能指针: LayerManager 用 unique_ptr 独占图层,Layer 用 shared_ptr 共享窗口(窗口可能被多处引用); 专栏 9.1 把 shared_ptr 的引用计数机制讲透了8。顺带一提,容器查找函数返回的迭代器(iterator,指向元素的指针式句柄,如 FindLayer 用的 std::find_if)也在原书这一章登场——它是指针的表亲,本章后面碰到的容器操作都会遇到。

3.3 原书 9.3 节补完:窗口、图层、管理器的全部机制

9.3 节是全书最长的一节(约 1.7 万字),上面只取了它的骨架;这一小节把骨架上的肉补回来, 因为后面十章的所有 GUI 功能都长在这些肉上。

Window:一块「绘图区域」的全部成员。 Window 类的成员只有五样: 宽高、像素数组 data_、专属写手 writer_、透明色 transparent_colour_(用 std::optional 表达 「有或没有」)9。data_ 是「数组的数组」(每行一个 vector),构造函数分两步把它撑成 二维:先 resize 出行数,再逐行 resize 出列数——书里专门画了撑开前、后的两张图10。 DrawTo(writer, position) 是窗口的对外出口:把窗口内容画到 position 指定的屏幕位置, 有没有透明色走两条路——没设就逐像素全画,设了就跳过等于透明色的那些点11。

WindowWriter 与 this 引用。 窗口自己的写手是内部类 WindowWriter,它继承 PixelWriter, 内部只持有一个「指向所属窗口的引用」12。这个引用的初始化有个值得照抄的写法: 窗口成员变量的初始化表写 writer_{*this}——窗口实例把自己(*this)交给内部写手, 于是「每个窗口都带着自己的笔」13。这种「内部类持有父实例引用」的做法很通用 (Linux 内核用 container_of 宏玩同款,内存更省)14。

Layer:一个席位。 Layer 只存三样:ID、位置 pos_、窗口指针;Move/MoveRelative 只改 坐标、绝不重绘——「窗口本身并不存储自己在平面上的位置,而是由图层来管理位置」15。 这是刻意划清职责:窗口管「画什么」,图层管「画在哪、在第几层」16。

LayerManager:两张表。 成员两层:layers_ 用 unique_ptr 存所有图层(含隐藏的), layer_stack_ 只存可见图层的指针、数组顺序就是叠加顺序(开头最底、末尾最顶)17。 几个方法的实现细节,后面章节都会复用:

  • NewLayer() 用 latest_id_ 发号,返回新图层引用,于是调用方可以写成方法链 NewLayer().SetWindow(...) 一行接龙18;
  • FindLayer(id) 用 std::find_if 在 layers_ 里找,查找条件(谓词)写成 lambda [id] 捕获 要查的编号19;
  • Draw() 是一条范围 for:沿 layer_stack_ 从底到顶逐层 DrawTo20;
  • Hide(id) 只做一件事:把图层指针从 layer_stack_ 里 erase 掉——图层还活着,只是退出了 叠加序列21;
  • UpDown(id, h) 的语义前文说过,实现是 vector 的「删旧位、插新位」两条游标操作, 要处理「删了一个元素之后 end() 游标错位」的边界,书里专门画图讲22。

Move 的方法链与「不重绘」约定。 Layer 的 Move/MoveRelative 都返回 Layer& (自身引用),所以 mouse_layer->Move(newpos) 之后还能接着 .DrawTo(...) 串下去; 更重要的是注释里写死的约定:「不执行重绘」——移动只改坐标,画不画、什么时候画, 由调用方决定23。这个约定是第 10 章「Move 自动重绘前后区域」的起点:先把「移动」和 「重绘」拆开,才能精确控制只补画变化的部分。

UpDown 的三个特殊值。 高度参数 new_height 的语义要背下来:负数 = 隐藏(从 layer_stack_ 移除);0 = 挪到最底;超过当前层数 = 置顶24。实现上它先把越界的值 收敛到合法区间,再处理两种情形——目标图层本来藏着(直接插入)还是可见着(先从旧位 删除;若删完新位置越过了末尾,游标要回退一格再插)25。这套「数组里搬元素」的逻辑 写起来烦,但它是窗口管理器的地基:第 15 章的活动窗口、第 24 章的新窗置顶,全是 UpDown 的调用方26。

控制台迁进图层。 图层机制就位后,控制台立刻「换房」:SetWriter() 把控制台的 输出目标换成自己的窗口,并立刻 Refresh()——把 buffer_ 里记着的字符整屏重放到新窗口, 不然切换瞬间之前的内容会凭空消失27;之后每印一行字符串(PutString)就顺手要求 重绘控制台这一层28。printk 从此画在窗口上而不是直接戳屏幕——这个 Redirect 模式 就是第 15 章「主任务统一绘图」的雏形。

3.4 sbrk 的规范与堆的脾气(9.2 节细节)

普通 new 能用的前提是 malloc 能工作,而 malloc 工作的前提是 sbrk 守规矩。sbrk 的规范 有三条:把「程序断点」移动 incr 字节;成功返回移动前的值;失败把 errno 置为 ENOMEM 并返回 -129。实现里两个全局变量 program_break 和 program_break_end 分别记堆的 头尾,初值由 InitializeHeap() 一次性从内存管理器要 128 MiB(64×512 帧)定下来30; 每次 sbrk 先检查「断点移动后会不会越过末尾」,越界就报错,没越界才挪动并返回旧值31。 顺便一提:这个堆是所有应用共享的——MikanOS 的应用跑在各自终端任务里,但 new/malloc 还住在内核的老堆里;应用有了独立地址空间(第 19 章)之后,这套 sbrk 也要跟着搬, 那是后话32。

3.5 叠加系统落成时的两个遗留问题(9.3 节收尾)

图层系统画完的第一天并不太平,书里记下了两个「已知欠债」:

  1. 鼠标拖动还是瞬移。MouseCursor 的擦旧画新搬到了图层上,但「移动」本身仍是一次 Move + 全量重绘——没有中间帧,拖起来一跳一跳。顺滑的拖动要等第 10 章引入 「只重绘变化区域」才解决33;
  2. 控制台与图层抢画笔。控制台刚开始直接往帧缓冲区写,图层一重绘就把它盖掉—— 解法就是上一小节的「控制台迁进图层」(SetWriter + Refresh + PutString 触发重绘), 谁后画谁说了算,规矩从此统一34。

这两个遗留问题的价值在于:它们不是 bug,而是架构演进的路标——每一个「先这么着」 都在下一章等到自己的机制。

最后把 9.3 节的数据结构 trio 再压成一张速查表,后面十章会反复引用:

结构管什么关键方法重绘责任
Window画什么(像素数组 + 透明色)DrawTo、Writer无(被动被画)
Layer画在哪、第几层(位置 + 高度)Move/MoveRelative(只改坐标)、DrawTo无(「不执行重绘」)
LayerManager谁先谁后(两张表)NewLayer/UpDown/Hide/DrawDraw 统一画,可带范围

读第 10 章之后的任何窗口代码,都可以先问一句:这句代码动的是表里哪一层的哪张表35。

3.6 Local APIC 定时器:没有单位的秒表

定时器都有一个计数器寄存器,值按固定节奏增减;处理前后各读一次,差值就是耗时36。 选 Local APIC 定时器因为最省事:它是一组 MMIO 寄存器—— LVT Timer(中断开关/模式)、Initial Count(初值)、Current Count(当前值)、Divide Configuration(分频)37。 用法:Mask=1(不发中断,单次模式),StartLAPICTimer() 写 Initial Count = 0xffffffff, Elapsed() 返回初值减当前值的差——「只想测量速度加快的程度,因此没有必要转换成秒」38。 第 11 章会回来把它校准成真时钟并接成周期中断。

3.4 阴影缓冲区:用空间换调用次数

瓶颈定位:207 万像素的背景,每次重绘走 207 万次「函数调用 + 格式转换」39。 两条加速路:加快每次写(批量 memcpy)、减少要写的东西;先做前者40。

阴影缓冲区(shadow buffer)是一块和窗口同尺寸、但像素格式与帧缓冲区一致的隐藏内存: 程序员照旧往 Window::data_ 写 PixelColor,Write() 顺手把它转换成帧缓冲区格式写进阴影区; 重绘时 DrawTo() 对不透明窗口直接 memcpy 阴影区 → 帧缓冲区——格式相同,可以整体搬41。 代价是内存:1920×1080×4 字节 ≈ 8 MiB,「现代计算机主内存以 GB 计,为提高速度这个容量不算太大」42。

实现载体是新的 FrameBuffer 类:它既包装真帧缓冲区(config.frame_buffer 非空时用现成内存), 也充当阴影缓冲区(传 nullptr 则内部分配);核心是 Copy(src, pos)——带越界裁剪的矩形 memcpy, 源和目标格式不同直接拒绝43。LayerManager 的绘图目标从 PixelWriter* 换成 FrameBuffer*, 加速自然生效44。透明窗口(鼠标光标)没这个福气,只能逐像素画——但「该区域非常小」45。

效果:2.5 亿 → 370 万,约 67 倍46。

3.5 滚动:把「重画」改成「搬」

滚动为什么贵(约 1 亿)?老算法:整屏填充背景 + 逐字符重绘 25 行; WriteString 内部也是逐像素 Write()47。 新算法一句话:把第 2~25 行的像素矩形 memcpy 上移一行,空出的最后一行 FillRectangle 填背景48。 载体是 FrameBuffer::Move(dst, src 矩形)——注意上移从下往上逐行拷、下移从上往下, 方向错了会覆盖还没拷走的源数据49。顺带红利:Window::Move 就位后,控制台不再需要 buffer_ 记全屏字符——滚动从此与「内容」无关,只是像素搬家50。

效果:约 1 亿 → 850 万,约 11 倍;「这样的速度提升已经足够了」51。

4. 作者的判断与证据

判断:「如果不经测量就尝试改进,则可能无法很好地理解效果,最终源码变得不必要的复杂, 尽管几乎没改进。」 这句 9.4 节开头的告诫,是全书工程纪律的纲领,也是本章实际执行的方式: 每次优化前后都有数字(2.5 亿/370 万;1 亿/850 万)52。

判断:用 8 MiB 内存换 67 倍速度,值。 阴影缓冲区的空间代价被明确掂量过——这是「空间换时间」 决策的教科书示范,附带诚实的边界(透明窗口不适用)53。

证据: 全部数字来自 Local APIC 计数器实测,附截图;单位刻意不换算成秒, 只比快慢——「测量加速比」的姿势本身就是教学点。

5. 边界与局限

  • 全屏重绘仍是兜底路径:本章只优化了「写得快」,没做「只重画变化的区域」 (damage rectangle 要到第 10、15 章才登场);移动窗口依然会整窗重绘。
  • 数字全部来自 QEMU 模拟器:作者提醒「测量时请尽可能确保运行环境良好」;真机数值会变, 倍数关系大体可信。
  • 8 MiB 阴影缓冲区 × 每窗口一份:窗口多了内存代价线性涨,教学系统不在乎,真系统要做 全局合成缓冲等更精巧的设计。
  • Copy/Move 的越界裁剪逻辑是 bug 高发区:书里专门画图(图 9.15)讲 src 超出目标时的阴影区域。

6. 可带走的

本章主线例子,一行写完: Draw 全屏 2.5 亿 → 阴影缓冲区 memcpy 370 万(67 倍)→ 滚动 1 亿 → Move 搬像素 850 万(11 倍)。

  1. 图层模型三件套:Window(图)、Layer(位)、LayerManager(序);重绘 = 从底到顶依次 DrawTo;
  2. UpDown 的语义:负值隐藏、0 底层、超界置顶;可见顺序就是 layer_stack_ 数组顺序;
  3. 智能指针分工:unique_ptr 表达「独占拥有」,shared_ptr 表达「共享、引用计数归零才销毁」;
  4. new/malloc 的入场券是 sbrk():维护程序断点;堆 = 一次性向自家内存管理器要的一大块;
  5. 计时没有单位也能干活:前后计数差值 + 只比倍数,是性能回归的最低配置;
  6. 阴影缓冲区 = 格式与帧缓冲区对齐的隐藏副本,让 memcpy 代替逐像素 Write——空间换时间;
  7. 帧缓冲区内「搬像素」要按方向逐行拷,否则覆盖源数据;
  8. 「先测量,再优化」不是口号:本章每一次改动都有优化前后的数字对。

7. 原文地图

主题原书章原文位置
为什么叠加、new 前置9.1 叠加过程概述(osbook_day09a)text/66-ch09-01-9-1-osbook-day09a.txt:4(搜「桌面背景色填充原位置」) · text/66-ch09-01-9-1-osbook-day09a.txt:8(搜「使用new分配内存」)
sbrk 规范与程序断点9.3 叠加过程的原理text/66-ch09-01-9-1-osbook-day09a.txt:23(搜「sbrk」)——正文在 text/66-ch09-01-9-1-osbook-day09a.txt:32 起
InitializeHeap 128MiB9.3 叠加过程的原理text/66-ch09-01-9-1-osbook-day09a.txt:75(搜「128MiB」)
图层=无限平面、窗口定义9.3 叠加过程的原理text/67-ch09-03-9-3.txt:2(搜「两个图层」) · text/67-ch09-03-9-3.txt:16(搜「窗口」)
Window 与透明色9.3 叠加过程的原理text/67-ch09-03-9-3.txt:25(搜「Window类的定义」) · text/67-ch09-03-9-3.txt:117(搜「SetTransparentColor」)
WindowWriter 与 this 引用9.3 叠加过程的原理text/67-ch09-03-9-3.txt:68(搜「*this」)
Layer/LayerManager 数据结构9.3 叠加过程的原理text/67-ch09-03-9-3.txt:164(搜「Layer类」) · text/67-ch09-03-9-3.txt:220(搜「LayerManager」) · text/67-ch09-03-9-3.txt:262(搜「layer_stack_」)
智能指针专栏9.3 叠加过程的原理text/67-ch09-03-9-3.txt:447(搜「智能指针」) · text/68-ch09-04-9-4-osbook-day09b.txt:10(搜「引用计数」)
Draw 从底到顶9.3 叠加过程的原理text/67-ch09-03-9-3.txt:326(搜「从最下面的图层开始向上绘制」)
UpDown 语义与实现9.3 叠加过程的原理text/67-ch09-03-9-3.txt:346(搜「UpDown」) · text/67-ch09-03-9-3.txt:348(搜「负值表示隐藏」)
控制台换 window 与 Refresh9.3 叠加过程的原理text/67-ch09-03-9-3.txt:404(搜「SetWriter」) · text/67-ch09-03-9-3.txt:424(搜「PutString」)
「先测量」纲领9.4 叠加过程的时间测量(osbook_day09b)text/68-ch09-04-9-4-osbook-day09b.txt:27(搜「建议都要测量」)
Local APIC 寄存器与单次模式9.4 叠加过程的时间测量(osbook_day09b)text/68-ch09-04-9-4-osbook-day09b.txt:47(搜「LVT Timer」) · text/68-ch09-04-9-4-osbook-day09b.txt:82(搜「单次」)
2.5 亿计数首次测量9.4 叠加过程的时间测量(osbook_day09b)text/69-ch09-05-9-5-osbook-day09c.txt:7(搜「2.5亿」)
207 万像素瓶颈分析9.5 加速叠加过程(osbook_day09c)text/69-ch09-05-9-5-osbook-day09c.txt:31(搜「207万」) · text/69-ch09-05-9-5-osbook-day09c.txt:33(搜「两种可能的加速策略」)
阴影缓冲区设计9.5 加速叠加过程(osbook_day09c)text/69-ch09-05-9-5-osbook-day09c.txt:65(搜「阴影缓冲区是一个内存」) · text/69-ch09-05-9-5-osbook-day09c.txt:81(搜「8MiB」)
FrameBuffer 与 Copy 裁剪9.5 加速叠加过程(osbook_day09c)text/69-ch09-05-9-5-osbook-day09c.txt:132(搜「FrameBuffer类」) · text/69-ch09-05-9-5-osbook-day09c.txt:190(搜「数据格式是否相同」) · text/69-ch09-05-9-5-osbook-day09c.txt:214(搜「FrameBuffer* screen_」)
67 倍与 11 倍9.5 加速叠加过程(osbook_day09c)text/70-ch09-06-9-6-osbook-day09d.txt:7(搜「67倍」) · text/71-ch09-07-9-7-osbook-day09e.txt:77(搜「11倍」)
滚动测量 1 亿9.6 测量滚动过程时间(osbook_day09d)text/70-ch09-06-9-6-osbook-day09d.txt:35(搜「1亿」)
滚动改搬像素与方向问题9.7 更快的滚动过程(osbook_day09e)text/71-ch09-07-9-7-osbook-day09e.txt:16(搜「移动像素」) · text/71-ch09-07-9-7-osbook-day09e.txt:47(搜「向上还是向下」)

Footnotes

  1. 出处:「9.2 new运算符」(text/66-ch09-01-9-1-osbook-day09a.txt:23,搜「sbrk」;:36,搜「caddr_t sbrk」;:42,搜「增加前的程序断点」)。 ↩

  2. 出处:「9.2 new运算符」(text/66-ch09-01-9-1-osbook-day09a.txt:48,搜「初始值」);:75,搜「128MiB」)。 ↩

  3. 出处:「9.5 加速叠加过程」(text/69-ch09-05-9-5-osbook-day09c.txt:174 附近注释,搜「很有用」——原书脚注:「std::vector::resize()使用new运算符分配内存。之前实现的sbrk()很有用!」)。 ↩

  4. 出处:「9.3 叠加过程的原理」(text/67-ch09-03-9-3.txt:9,搜「无限大的平面」;:16,搜「窗口」;:204,搜「窗口本身并不存储其在平面上的位置」)。 ↩

  5. 出处:「9.3 叠加过程的原理」(text/67-ch09-03-9-3.txt:205,搜「两个不同的东西」)。 ↩

  6. 出处:「9.3 叠加过程的原理」(text/67-ch09-03-9-3.txt:326,搜「从最下面的图层开始向上绘制」)。 ↩

  7. 出处:「9.3 叠加过程的原理」(text/67-ch09-03-9-3.txt:346,搜「UpDown」;:362,搜「基本策略」)。 ↩

  8. 出处:「9.3」专栏9.1(text/67-ch09-03-9-3.txt:448,搜「智能指针是普通指针的多功能版本」;text/68-ch09-04-9-4-osbook-day09b.txt:10,搜「引用计数加1」)。 ↩

  9. 出处:「9.3 叠加过程的原理」(text/67-ch09-03-9-3.txt:25,搜「Window类的定义」;:21,搜「无值状态」)。 ↩

  10. 出处:「9.3 叠加过程的原理」(text/67-ch09-03-9-3.txt:77,搜「构造函数」;:81,搜「resize」;:88,搜「图9.5」)。 ↩

  11. 出处:「9.3 叠加过程的原理」(text/67-ch09-03-9-3.txt:96,搜「DrawTo()是一个使用指定的PixelWriter绘制窗口内容的方法」;:99,搜「分为两个过程」)。 ↩

  12. 出处:「9.3 叠加过程的原理」(text/67-ch09-03-9-3.txt:128,搜「WindowWriter类的定义」;:149,搜「作为成员变量引用」)。 ↩

  13. 出处:「9.3 叠加过程的原理」(text/67-ch09-03-9-3.txt:154,搜「*this」;:158,搜「指向Window类的一个实例」)。 ↩

  14. 出处:「9.3 叠加过程的原理」(text/67-ch09-03-9-3.txt:161,搜「container_of」)。 ↩

  15. 出处:「9.3 叠加过程的原理」(text/67-ch09-03-9-3.txt:204,搜「并不存储其在平面上的位置」)。 ↩

  16. 出处:「9.3 叠加过程的原理」(text/67-ch09-03-9-3.txt:205,搜「两个不同的东西」)。 ↩

  17. 出处:「9.3 叠加过程的原理」(text/67-ch09-03-9-3.txt:220,搜「LayerManager」;:262,搜「layer_stack_」;:264,搜「最前面的图层」)。 ↩

  18. 出处:「9.3 叠加过程的原理」(text/67-ch09-03-9-3.txt:280,搜「NewLayer()」;:286,搜「方法链」)。 ↩

  19. 出处:「9.3 叠加过程的原理」(text/67-ch09-03-9-3.txt:294,搜「FindLayer」;:302,搜「捕获」;:306,搜「谓词」)。 ↩

  20. 出处:「9.3 叠加过程的原理」(text/67-ch09-03-9-3.txt:326,搜「从最下面的图层开始向上绘制」;:332,搜「范围for语句」)。 ↩

  21. 出处:「9.3 叠加过程的原理」(text/67-ch09-03-9-3.txt:335,搜「Hide()」;:345,搜「erase」)。 ↩

  22. 出处:「9.3 叠加过程的原理」(text/67-ch09-03-9-3.txt:346,搜「UpDown()」;:362,搜「基本策略」;:366,搜「跳过末尾」)。 ↩

  23. 出处:「9.3 叠加过程的原理」(text/67-ch09-03-9-3.txt:182,搜「不执行重绘」;:197,搜「MoveRelative()使用相对坐标」)。 ↩

  24. 出处:「9.3 叠加过程的原理」(text/67-ch09-03-9-3.txt:346,搜「UpDown」;:348,搜「负值表示隐藏」;:237,搜「new_height为负数」)。 ↩

  25. 出处:「9.3 叠加过程的原理」(text/67-ch09-03-9-3.txt:353,搜「两条if语句」;:362,搜「基本策略」;:366,搜「向前移动1」)。 ↩

  26. 出处:「9.3 叠加过程的原理」(text/67-ch09-03-9-3.txt:369,搜「准备工作完成」;:397,搜「UpDown()方法」——新图层从隐藏到可见都用 UpDown)。 ↩

  27. 出处:「9.3 叠加过程的原理」(text/67-ch09-03-9-3.txt:404,搜「SetWriter」;:412,搜「Refresh()」;:416,搜「buffer_」)。 ↩

  28. 出处:「9.3 叠加过程的原理」(text/67-ch09-03-9-3.txt:428,搜「PutString()重绘屏幕」——按段号定位)。 ↩

  29. 出处:「9.2 new运算符」(text/66-ch09-01-9-1-osbook-day09a.txt:36,搜「caddr_t sbrk」;:42,搜「增加前的程序断点」;:45,搜「ENOMEM」)。 ↩

  30. 出处:「9.2 new运算符」(text/66-ch09-01-9-1-osbook-day09a.txt:75,搜「128MiB」;:82,搜「heap_start」)。 ↩

  31. 出处:「9.2 new运算符」(text/66-ch09-01-9-1-osbook-day09a.txt:62,搜「初始值默认为」;:67,搜「prev_break」)。 ↩

  32. 出处:「9.2 new运算符」(text/66-ch09-01-9-1-osbook-day09a.txt:48,搜「初始值」——program_break 初值由 InitializeHeap 设置,应用独立堆是后续章节的事)。 ↩

  33. 出处:「9.3 叠加过程的原理」(text/67-ch09-03-9-3.txt:445,搜「非常慢」;:445,搜「下一节」——日文残页同段中文,鼠标移动慢与闪烁留给 9.4 之后处理)。 ↩

  34. 出处:「9.3 叠加过程的原理」(text/67-ch09-03-9-3.txt:401,搜「控制台类」;:403,搜「被图层机制所取代」)。 ↩

  35. 出处:「9.3 叠加过程的原理」(text/67-ch09-03-9-3.txt:17,搜「首先创建」;:212,搜「图层本身没有任何意义」——LayerManager 是叠加的必需前提)。 ↩

  36. 出处:「9.4 叠加过程的时间测量」(text/68-ch09-04-9-4-osbook-day09b.txt:31,搜「计数器」;:32,搜「差值」)。 ↩

  37. 出处:「9.4」(text/68-ch09-04-9-4-osbook-day09b.txt:47,搜「LVT Timer」);表9.1 列出四个寄存器)。 ↩

  38. 出处:「9.4」(text/68-ch09-04-9-4-osbook-day09b.txt:36,搜「没有必要进行转换」;:97,搜「0xffffffff」)。 ↩

  39. 出处:「9.5 加速叠加过程」(text/69-ch09-05-9-5-osbook-day09c.txt:31,搜「207万」;:32,搜「耗费大量时间」)。 ↩

  40. 出处:「9.5」(text/69-ch09-05-9-5-osbook-day09c.txt:37,搜「加快将窗口图片写入帧缓冲区」;:40,搜「减少要写入的像素数量」)。 ↩

  41. 出处:「9.5」(text/69-ch09-05-9-5-osbook-day09c.txt:65,搜「阴影缓冲区是一个内存」);:78,搜「memcpy将阴影缓冲区的内容复制」)。 ↩

  42. 出处:「9.5」(text/69-ch09-05-9-5-osbook-day09c.txt:81,搜「8MiB」)。 ↩

  43. 出处:「9.5」(text/69-ch09-05-9-5-osbook-day09c.txt:132,搜「FrameBuffer类」;:178,搜「数据格式是否相同」;:196,搜「超出范围」)。 ↩

  44. 出处:「9.5」(text/69-ch09-05-9-5-osbook-day09c.txt:214,搜「FrameBuffer* screen_」——按 sed 偏移,实际在 text/69-ch09-05-9-5-osbook-day09c.txt 代码9.39 处)。 ↩

  45. 出处:「9.5」(text/69-ch09-05-9-5-osbook-day09c.txt:122,搜「别无选择」;:124,搜「不会受到太大的影响」)。 ↩

  46. 出处:「9.5/9.6」(text/70-ch09-06-9-6-osbook-day09d.txt:6,搜「370万」;:7,搜「67倍」)。 ↩

  47. 出处:「9.7 更快的滚动过程」(text/71-ch09-07-9-7-osbook-day09e.txt:12,搜「当if语句为假时」;:14,搜「导致速度变慢的原因」)。 ↩

  48. 出处:「9.7」(text/71-ch09-07-9-7-osbook-day09e.txt:16,搜「移动像素」;:18,搜「大大减少」)。 ↩

  49. 出处:「9.7」(text/71-ch09-07-9-7-osbook-day09e.txt:47,搜「向上还是向下」;:48,搜「以免覆盖源数据」)。 ↩

  50. 出处:「9.7」(text/71-ch09-07-9-7-osbook-day09e.txt:34,搜「不再需要buffer_」)。 ↩

  51. 出处:「9.7」(text/71-ch09-07-9-7-osbook-day09e.txt:76,搜「850万」);:77,搜「11倍」;:78,搜「完全不生涩了」)。 ↩

  52. 出处:「9.4」(text/68-ch09-04-9-4-osbook-day09b.txt:27,搜「建议都要测量」)。 ↩

  53. 出处:「9.5」(text/69-ch09-05-9-5-osbook-day09c.txt:81,搜「8MiB」;:82,搜「不算太大」)。 ↩

On this page