图层与加速 — 窗口的物理,和「先测量,再优化」的完整示范¶
这一章讲什么: 上一章的鼠标光标用「背景色擦旧、新位置画新」,拖过非背景区就毁图。 解法是所有窗口系统的底层物理:图层——桌面一层、鼠标一层,按高度从底往顶依次画。 建这套系统之前,先补上动态内存(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 表达 「有或没有」)27。data_ 是「数组的数组」(每行一个 vector),构造函数分两步把它撑成 二维:先 resize 出行数,再逐行 resize 出列数——书里专门画了撑开前、后的两张图28。 DrawTo(writer, position) 是窗口的对外出口:把窗口内容画到 position 指定的屏幕位置, 有没有透明色走两条路——没设就逐像素全画,设了就跳过等于透明色的那些点29。
WindowWriter 与 this 引用。 窗口自己的写手是内部类 WindowWriter,它继承 PixelWriter,
内部只持有一个「指向所属窗口的引用」30。这个引用的初始化有个值得照抄的写法:
窗口成员变量的初始化表写 writer_{*this}——窗口实例把自己(*this)交给内部写手,
于是「每个窗口都带着自己的笔」31。这种「内部类持有父实例引用」的做法很通用
(Linux 内核用 container_of 宏玩同款,内存更省)32。
Layer:一个席位。 Layer 只存三样:ID、位置 pos_、窗口指针;Move/MoveRelative 只改 坐标、绝不重绘——「窗口本身并不存储自己在平面上的位置,而是由图层来管理位置」33。 这是刻意划清职责:窗口管「画什么」,图层管「画在哪、在第几层」34。
LayerManager:两张表。 成员两层:layers_ 用 unique_ptr 存所有图层(含隐藏的), layer_stack_ 只存可见图层的指针、数组顺序就是叠加顺序(开头最底、末尾最顶)35。 几个方法的实现细节,后面章节都会复用:
- NewLayer() 用 latest_id_ 发号,返回新图层引用,于是调用方可以写成方法链
NewLayer().SetWindow(...)一行接龙36; - FindLayer(id) 用 std::find_if 在 layers_ 里找,查找条件(谓词)写成 lambda
[id]捕获 要查的编号37; - Draw() 是一条范围 for:沿 layer_stack_ 从底到顶逐层 DrawTo38;
- Hide(id) 只做一件事:把图层指针从 layer_stack_ 里 erase 掉——图层还活着,只是退出了 叠加序列39;
- UpDown(id, h) 的语义前文说过,实现是 vector 的「删旧位、插新位」两条游标操作, 要处理「删了一个元素之后 end() 游标错位」的边界,书里专门画图讲40。
Move 的方法链与「不重绘」约定。 Layer 的 Move/MoveRelative 都返回 Layer&
(自身引用),所以 mouse_layer->Move(newpos) 之后还能接着 .DrawTo(...) 串下去;
更重要的是注释里写死的约定:「不执行重绘」——移动只改坐标,画不画、什么时候画,
由调用方决定49。这个约定是第 10 章「Move 自动重绘前后区域」的起点:先把「移动」和
「重绘」拆开,才能精确控制只补画变化的部分。
UpDown 的三个特殊值。 高度参数 new_height 的语义要背下来:负数 = 隐藏(从 layer_stack_ 移除);0 = 挪到最底;超过当前层数 = 置顶50。实现上它先把越界的值 收敛到合法区间,再处理两种情形——目标图层本来藏着(直接插入)还是可见着(先从旧位 删除;若删完新位置越过了末尾,游标要回退一格再插)51。这套「数组里搬元素」的逻辑 写起来烦,但它是窗口管理器的地基:第 15 章的活动窗口、第 24 章的新窗置顶,全是 UpDown 的调用方52。
控制台迁进图层。 图层机制就位后,控制台立刻「换房」:SetWriter() 把控制台的
输出目标换成自己的窗口,并立刻 Refresh()——把 buffer_ 里记着的字符整屏重放到新窗口,
不然切换瞬间之前的内容会凭空消失41;之后每印一行字符串(PutString)就顺手要求
重绘控制台这一层42。printk 从此画在窗口上而不是直接戳屏幕——这个 Redirect 模式
就是第 15 章「主任务统一绘图」的雏形。
3.4 sbrk 的规范与堆的脾气(9.2 节细节)¶
普通 new 能用的前提是 malloc 能工作,而 malloc 工作的前提是 sbrk 守规矩。sbrk 的规范 有三条:把「程序断点」移动 incr 字节;成功返回移动前的值;失败把 errno 置为 ENOMEM 并返回 -143。实现里两个全局变量 program_break 和 program_break_end 分别记堆的 头尾,初值由 InitializeHeap() 一次性从内存管理器要 128 MiB(64×512 帧)定下来44; 每次 sbrk 先检查「断点移动后会不会越过末尾」,越界就报错,没越界才挪动并返回旧值45。 顺便一提:这个堆是所有应用共享的——MikanOS 的应用跑在各自终端任务里,但 new/malloc 还住在内核的老堆里;应用有了独立地址空间(第 19 章)之后,这套 sbrk 也要跟着搬, 那是后话46。
3.5 叠加系统落成时的两个遗留问题(9.3 节收尾)¶
图层系统画完的第一天并不太平,书里记下了两个「已知欠债」:
- 鼠标拖动还是瞬移。MouseCursor 的擦旧画新搬到了图层上,但「移动」本身仍是一次 Move + 全量重绘——没有中间帧,拖起来一跳一跳。顺滑的拖动要等第 10 章引入 「只重绘变化区域」才解决47;
- 控制台与图层抢画笔。控制台刚开始直接往帧缓冲区写,图层一重绘就把它盖掉—— 解法就是上一小节的「控制台迁进图层」(SetWriter + Refresh + PutString 触发重绘), 谁后画谁说了算,规矩从此统一48。
这两个遗留问题的价值在于:它们不是 bug,而是架构演进的路标——每一个「先这么着」 都在下一章等到自己的机制。
最后把 9.3 节的数据结构 trio 再压成一张速查表,后面十章会反复引用:
| 结构 | 管什么 | 关键方法 | 重绘责任 |
|---|---|---|---|
| Window | 画什么(像素数组 + 透明色) | DrawTo、Writer | 无(被动被画) |
| Layer | 画在哪、第几层(位置 + 高度) | Move/MoveRelative(只改坐标)、DrawTo | 无(「不执行重绘」) |
| LayerManager | 谁先谁后(两张表) | NewLayer/UpDown/Hide/Draw | Draw 统一画,可带范围 |
读第 10 章之后的任何窗口代码,都可以先问一句:这句代码动的是表里哪一层的哪张表53。
3.6 Local APIC 定时器:没有单位的秒表¶
定时器都有一个计数器寄存器,值按固定节奏增减;处理前后各读一次,差值就是耗时9。 选 Local APIC 定时器因为最省事:它是一组 MMIO 寄存器—— LVT Timer(中断开关/模式)、Initial Count(初值)、Current Count(当前值)、Divide Configuration(分频)10。 用法:Mask=1(不发中断,单次模式),StartLAPICTimer() 写 Initial Count = 0xffffffff, Elapsed() 返回初值减当前值的差——「只想测量速度加快的程度,因此没有必要转换成秒」11。 第 11 章会回来把它校准成真时钟并接成周期中断。
3.4 阴影缓冲区:用空间换调用次数¶
瓶颈定位:207 万像素的背景,每次重绘走 207 万次「函数调用 + 格式转换」12。 两条加速路:加快每次写(批量 memcpy)、减少要写的东西;先做前者13。
阴影缓冲区(shadow buffer)是一块和窗口同尺寸、但像素格式与帧缓冲区一致的隐藏内存:
程序员照旧往 Window::data_ 写 PixelColor,Write() 顺手把它转换成帧缓冲区格式写进阴影区;
重绘时 DrawTo() 对不透明窗口直接 memcpy 阴影区 → 帧缓冲区——格式相同,可以整体搬14。
代价是内存:1920×1080×4 字节 ≈ 8 MiB,「现代计算机主内存以 GB 计,为提高速度这个容量不算太大」15。
实现载体是新的 FrameBuffer 类:它既包装真帧缓冲区(config.frame_buffer 非空时用现成内存), 也充当阴影缓冲区(传 nullptr 则内部分配);核心是 Copy(src, pos)——带越界裁剪的矩形 memcpy, 源和目标格式不同直接拒绝16。LayerManager 的绘图目标从 PixelWriter* 换成 FrameBuffer*, 加速自然生效17。透明窗口(鼠标光标)没这个福气,只能逐像素画——但「该区域非常小」18。
效果:2.5 亿 → 370 万,约 67 倍19。
3.5 滚动:把「重画」改成「搬」¶
滚动为什么贵(约 1 亿)?老算法:整屏填充背景 + 逐字符重绘 25 行; WriteString 内部也是逐像素 Write()20。 新算法一句话:把第 2~25 行的像素矩形 memcpy 上移一行,空出的最后一行 FillRectangle 填背景21。 载体是 FrameBuffer::Move(dst, src 矩形)——注意上移从下往上逐行拷、下移从上往下, 方向错了会覆盖还没拷走的源数据22。顺带红利:Window::Move 就位后,控制台不再需要 buffer_ 记全屏字符——滚动从此与「内容」无关,只是像素搬家23。
效果:约 1 亿 → 850 万,约 11 倍;「这样的速度提升已经足够了」24。
4. 作者的判断与证据¶
判断:「如果不经测量就尝试改进,则可能无法很好地理解效果,最终源码变得不必要的复杂, 尽管几乎没改进。」 这句 9.4 节开头的告诫,是全书工程纪律的纲领,也是本章实际执行的方式: 每次优化前后都有数字(2.5 亿/370 万;1 亿/850 万)25。
判断:用 8 MiB 内存换 67 倍速度,值。 阴影缓冲区的空间代价被明确掂量过——这是「空间换时间」 决策的教科书示范,附带诚实的边界(透明窗口不适用)26。
证据: 全部数字来自 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 倍)。
- 图层模型三件套:Window(图)、Layer(位)、LayerManager(序);重绘 = 从底到顶依次 DrawTo;
- UpDown 的语义:负值隐藏、0 底层、超界置顶;可见顺序就是 layer_stack_ 数组顺序;
- 智能指针分工:unique_ptr 表达「独占拥有」,shared_ptr 表达「共享、引用计数归零才销毁」;
- new/malloc 的入场券是 sbrk():维护程序断点;堆 = 一次性向自家内存管理器要的一大块;
- 计时没有单位也能干活:前后计数差值 + 只比倍数,是性能回归的最低配置;
- 阴影缓冲区 = 格式与帧缓冲区对齐的隐藏副本,让 memcpy 代替逐像素 Write——空间换时间;
- 帧缓冲区内「搬像素」要按方向逐行拷,否则覆盖源数据;
- 「先测量,再优化」不是口号:本章每一次改动都有优化前后的数字对。
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 128MiB | 9.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 与 Refresh | 9.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(搜「向上还是向下」) |
-
出处:「9.2 new运算符」(
text/66-ch09-01-9-1-osbook-day09a.txt:23,搜「sbrk」;:36,搜「caddr_t sbrk」;:42,搜「增加前的程序断点」)。 ↩ -
出处:「9.2 new运算符」(
text/66-ch09-01-9-1-osbook-day09a.txt:48,搜「初始值」);:75,搜「128MiB」)。 ↩ -
出处:「9.5 加速叠加过程」(
text/69-ch09-05-9-5-osbook-day09c.txt:174附近注释,搜「很有用」——原书脚注:「std::vector::resize()使用new运算符分配内存。之前实现的sbrk()很有用!」)。 ↩ -
出处:「9.3 叠加过程的原理」(
text/67-ch09-03-9-3.txt:9,搜「无限大的平面」;:16,搜「窗口」;:204,搜「窗口本身并不存储其在平面上的位置」)。 ↩ -
出处:「9.3 叠加过程的原理」(
text/67-ch09-03-9-3.txt:205,搜「两个不同的东西」)。 ↩ -
出处:「9.3 叠加过程的原理」(
text/67-ch09-03-9-3.txt:326,搜「从最下面的图层开始向上绘制」)。 ↩ -
出处:「9.3 叠加过程的原理」(
text/67-ch09-03-9-3.txt:346,搜「UpDown」;:362,搜「基本策略」)。 ↩ -
出处:「9.3」专栏9.1(
text/67-ch09-03-9-3.txt:448,搜「智能指针是普通指针的多功能版本」;text/68-ch09-04-9-4-osbook-day09b.txt:10,搜「引用计数加1」)。 ↩ -
出处:「9.4 叠加过程的时间测量」(
text/68-ch09-04-9-4-osbook-day09b.txt:31,搜「计数器」;:32,搜「差值」)。 ↩ -
出处:「9.4」(
text/68-ch09-04-9-4-osbook-day09b.txt:47,搜「LVT Timer」);表9.1 列出四个寄存器)。 ↩ -
出处:「9.4」(
text/68-ch09-04-9-4-osbook-day09b.txt:36,搜「没有必要进行转换」;:97,搜「0xffffffff」)。 ↩ -
出处:「9.5 加速叠加过程」(
text/69-ch09-05-9-5-osbook-day09c.txt:31,搜「207万」;:32,搜「耗费大量时间」)。 ↩ -
出处:「9.5」(
text/69-ch09-05-9-5-osbook-day09c.txt:37,搜「加快将窗口图片写入帧缓冲区」;:40,搜「减少要写入的像素数量」)。 ↩ -
出处:「9.5」(
text/69-ch09-05-9-5-osbook-day09c.txt:65,搜「阴影缓冲区是一个内存」);:78,搜「memcpy将阴影缓冲区的内容复制」)。 ↩ -
出处:「9.5」(
text/69-ch09-05-9-5-osbook-day09c.txt:81,搜「8MiB」)。 ↩ -
出处:「9.5」(
text/69-ch09-05-9-5-osbook-day09c.txt:132,搜「FrameBuffer类」;:178,搜「数据格式是否相同」;:196,搜「超出范围」)。 ↩ -
出处:「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 处)。 ↩ -
出处:「9.5」(
text/69-ch09-05-9-5-osbook-day09c.txt:122,搜「别无选择」;:124,搜「不会受到太大的影响」)。 ↩ -
出处:「9.5/9.6」(
text/70-ch09-06-9-6-osbook-day09d.txt:6,搜「370万」;:7,搜「67倍」)。 ↩ -
出处:「9.7 更快的滚动过程」(
text/71-ch09-07-9-7-osbook-day09e.txt:12,搜「当if语句为假时」;:14,搜「导致速度变慢的原因」)。 ↩ -
出处:「9.7」(
text/71-ch09-07-9-7-osbook-day09e.txt:16,搜「移动像素」;:18,搜「大大减少」)。 ↩ -
出处:「9.7」(
text/71-ch09-07-9-7-osbook-day09e.txt:47,搜「向上还是向下」;:48,搜「以免覆盖源数据」)。 ↩ -
出处:「9.7」(
text/71-ch09-07-9-7-osbook-day09e.txt:34,搜「不再需要buffer_」)。 ↩ -
出处:「9.7」(
text/71-ch09-07-9-7-osbook-day09e.txt:76,搜「850万」);:77,搜「11倍」;:78,搜「完全不生涩了」)。 ↩ -
出处:「9.4」(
text/68-ch09-04-9-4-osbook-day09b.txt:27,搜「建议都要测量」)。 ↩ -
出处:「9.5」(
text/69-ch09-05-9-5-osbook-day09c.txt:81,搜「8MiB」;:82,搜「不算太大」)。 ↩ -
出处:「9.3 叠加过程的原理」(
text/67-ch09-03-9-3.txt:25,搜「Window类的定义」;:21,搜「无值状态」)。 ↩ -
出处:「9.3 叠加过程的原理」(
text/67-ch09-03-9-3.txt:77,搜「构造函数」;:81,搜「resize」;:88,搜「图9.5」)。 ↩ -
出处:「9.3 叠加过程的原理」(
text/67-ch09-03-9-3.txt:96,搜「DrawTo()是一个使用指定的PixelWriter绘制窗口内容的方法」;:99,搜「分为两个过程」)。 ↩ -
出处:「9.3 叠加过程的原理」(
text/67-ch09-03-9-3.txt:128,搜「WindowWriter类的定义」;:149,搜「作为成员变量引用」)。 ↩ -
出处:「9.3 叠加过程的原理」(
text/67-ch09-03-9-3.txt:154,搜「*this」;:158,搜「指向Window类的一个实例」)。 ↩ -
出处:「9.3 叠加过程的原理」(
text/67-ch09-03-9-3.txt:161,搜「container_of」)。 ↩ -
出处:「9.3 叠加过程的原理」(
text/67-ch09-03-9-3.txt:204,搜「并不存储其在平面上的位置」)。 ↩ -
出处:「9.3 叠加过程的原理」(
text/67-ch09-03-9-3.txt:205,搜「两个不同的东西」)。 ↩ -
出处:「9.3 叠加过程的原理」(
text/67-ch09-03-9-3.txt:220,搜「LayerManager」;:262,搜「layer_stack_」;:264,搜「最前面的图层」)。 ↩ -
出处:「9.3 叠加过程的原理」(
text/67-ch09-03-9-3.txt:280,搜「NewLayer()」;:286,搜「方法链」)。 ↩ -
出处:「9.3 叠加过程的原理」(
text/67-ch09-03-9-3.txt:294,搜「FindLayer」;:302,搜「捕获」;:306,搜「谓词」)。 ↩ -
出处:「9.3 叠加过程的原理」(
text/67-ch09-03-9-3.txt:326,搜「从最下面的图层开始向上绘制」;:332,搜「范围for语句」)。 ↩ -
出处:「9.3 叠加过程的原理」(
text/67-ch09-03-9-3.txt:335,搜「Hide()」;:345,搜「erase」)。 ↩ -
出处:「9.3 叠加过程的原理」(
text/67-ch09-03-9-3.txt:346,搜「UpDown()」;:362,搜「基本策略」;:366,搜「跳过末尾」)。 ↩ -
出处:「9.3 叠加过程的原理」(
text/67-ch09-03-9-3.txt:404,搜「SetWriter」;:412,搜「Refresh()」;:416,搜「buffer_」)。 ↩ -
出处:「9.3 叠加过程的原理」(
text/67-ch09-03-9-3.txt:428,搜「PutString()重绘屏幕」——按段号定位)。 ↩ -
出处:「9.2 new运算符」(
text/66-ch09-01-9-1-osbook-day09a.txt:36,搜「caddr_t sbrk」;:42,搜「增加前的程序断点」;:45,搜「ENOMEM」)。 ↩ -
出处:「9.2 new运算符」(
text/66-ch09-01-9-1-osbook-day09a.txt:75,搜「128MiB」;:82,搜「heap_start」)。 ↩ -
出处:「9.2 new运算符」(
text/66-ch09-01-9-1-osbook-day09a.txt:62,搜「初始值默认为」;:67,搜「prev_break」)。 ↩ -
出处:「9.2 new运算符」(
text/66-ch09-01-9-1-osbook-day09a.txt:48,搜「初始值」——program_break 初值由 InitializeHeap 设置,应用独立堆是后续章节的事)。 ↩ -
出处:「9.3 叠加过程的原理」(
text/67-ch09-03-9-3.txt:445,搜「非常慢」;:445,搜「下一节」——日文残页同段中文,鼠标移动慢与闪烁留给 9.4 之后处理)。 ↩ -
出处:「9.3 叠加过程的原理」(
text/67-ch09-03-9-3.txt:401,搜「控制台类」;:403,搜「被图层机制所取代」)。 ↩ -
出处:「9.3 叠加过程的原理」(
text/67-ch09-03-9-3.txt:182,搜「不执行重绘」;:197,搜「MoveRelative()使用相对坐标」)。 ↩ -
出处:「9.3 叠加过程的原理」(
text/67-ch09-03-9-3.txt:346,搜「UpDown」;:348,搜「负值表示隐藏」;:237,搜「new_height为负数」)。 ↩ -
出处:「9.3 叠加过程的原理」(
text/67-ch09-03-9-3.txt:353,搜「两条if语句」;:362,搜「基本策略」;:366,搜「向前移动1」)。 ↩ -
出处:「9.3 叠加过程的原理」(
text/67-ch09-03-9-3.txt:369,搜「准备工作完成」;:397,搜「UpDown()方法」——新图层从隐藏到可见都用 UpDown)。 ↩ -
出处:「9.3 叠加过程的原理」(
text/67-ch09-03-9-3.txt:17,搜「首先创建」;:212,搜「图层本身没有任何意义」——LayerManager 是叠加的必需前提)。 ↩