Skip to content

Commit 77c1656

Browse files
committed
[New] en version
1 parent 041cb7f commit 77c1656

File tree

2 files changed

+75
-59
lines changed

2 files changed

+75
-59
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
layout: post
3+
title: "Extreme Performance: Render Only Visible Parts - ListView"
4+
categories: [VSCode, UI]
5+
---
6+

4. Renderer process - 渲染进程/2024-11-18-VSCode:Centralized Animation Frame Scheduling-en.md

Lines changed: 69 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,75 @@
11
---
22
layout: post
3-
title: "VSCodeCentralized Animation Frame Scheduling"
3+
title: "VSCode: Centralized Animation Frame Scheduling"
44
categories: [VSCode, UI]
55
---
66

7-
## 简介
8-
今天介绍一个VSCode源码中一个简单且小巧的功能。在此之前我先简单介绍一下`requestAnimationFrame`这个原生API。
7+
## Introduction
8+
Today, we'll explore a simple and elegant feature within the VSCode source code. Before diving in, let’s first look at the native `requestAnimationFrame` API.
99

1010
## `requestAnimationFrame` API
1111

12-
### 工作原理
13-
`requestAnimationFrame` 会将提供的回调函数放入队列,并在下一次浏览器重绘前调用该函数。与传统的 `setTimeout` 不同,`requestAnimationFrame` 的优势在于:
14-
- **与屏幕刷新同步**:浏览器会在适当的时间调用回调函数,通常是每秒 60 帧(即 16.67ms 间隔)。
15-
- **节能效果**:当页面处于后台或不可见状态时,浏览器会暂停 `requestAnimationFrame` 的调用,从而节省资源。
16-
- **平滑的动画**:由于与浏览器刷新周期一致,动画会显得更加流畅。
12+
### How It Works
13+
`requestAnimationFrame` queues a callback to be invoked before the next browser repaint. Compared to `setTimeout`, it offers several advantages:
14+
- **Synchronization with screen refresh**: The callback is invoked at an appropriate time, typically 60 frames per second (16.67ms interval).
15+
- **Energy efficiency**: When the page is in the background or hidden, the browser halts `requestAnimationFrame` calls to conserve resources.
16+
- **Smooth animations**: Animations appear smoother due to synchronization with the browser’s refresh cycle.
1717

18-
### 基本用法
18+
### Basic Usage
1919
```javascript
2020
function animate() {
2121
console.log('Animating...');
2222
}
2323
requestAnimationFrame(animate);
2424
```
25-
### 问题点
26-
当应用中的多个模块独立调用 `requestAnimationFrame` 时,可能出现以下问题:
27-
1. **缺乏全局优先级控制**:浏览器无法直接区分任务的优先级,导致关键任务与次要任务并行执行,影响性能。
28-
2. **复杂任务调度**:对于需要动态更新任务优先级或取消任务的场景,原生 API 支持不足。
2925

30-
为了解决这些问题,VSCode 设计了一套中央式动画帧调度器来管理动画和 UI 渲染任务。
26+
### Issues
27+
When multiple modules independently call `requestAnimationFrame`, issues can arise:
28+
1. **Lack of global priority control**: The browser cannot distinguish between critical and minor tasks, leading to potential performance bottlenecks.
29+
2. **Complex task scheduling**: The native API offers limited support for dynamically updating priorities or canceling tasks.
3130

32-
## 中央调度器
33-
> 相关文件:`src\vs\base\browser\dom.ts`
31+
To address these challenges, VSCode employs a centralized animation frame scheduler for managing animation and UI rendering tasks.
3432

35-
`VSCode`整个软件中避免直接使用`window.requestsAnimationFrame`,而是提供了以下两个APIs:
36-
```ts
33+
## Centralized Scheduler
34+
> Relevant File: `src\vs\base\browser\dom.ts`
35+
36+
VSCode avoids directly using `window.requestAnimationFrame`. Instead, it provides the following two APIs:
37+
```typescript
3738
/**
3839
* Schedule a callback to be run at the next animation frame.
39-
* This allows multiple parties to register callbacks that should run at the next animation frame.
40-
* If currently in an animation frame, `runner` will be executed immediately.
41-
* @return token that can be used to cancel the scheduled runner (only if `runner` was not executed immediately).
40+
* Allows multiple parties to register callbacks to run at the next animation frame.
41+
* If currently in an animation frame, `runner` executes immediately.
42+
* @return token to cancel the scheduled runner (only if `runner` was not executed immediately).
4243
*/
4344
export let runAtThisOrScheduleAtNextAnimationFrame: (targetWindow: Window, runner: () => void, priority?: number) => IDisposable;
45+
4446
/**
4547
* Schedule a callback to be run at the next animation frame.
46-
* This allows multiple parties to register callbacks that should run at the next animation frame.
47-
* If currently in an animation frame, `runner` will be executed at the next animation frame.
48-
* @return token that can be used to cancel the scheduled runner.
48+
* Allows multiple parties to register callbacks to run at the next animation frame.
49+
* If currently in an animation frame, `runner` executes at the next animation frame.
50+
* @return token to cancel the scheduled runner.
4951
*/
5052
export let scheduleAtNextAnimationFrame: (targetWindow: Window, runner: () => void, priority?: number) => IDisposable;
5153
```
5254

53-
VSCode把函数的定义写在了一个 immediate call function 里面。因为代码量很少,我会直接把大部分代码复制过来。首先是在 function body 中定义了一些map,用来全局储存数据:
54-
```ts
55-
(function () {
56-
// The runners scheduled at the next animation frame
57-
const NEXT_QUEUE = new Map<number /* window ID */, AnimationFrameQueueItem[]>();
58-
// The runners scheduled at the current animation frame
59-
const CURRENT_QUEUE = new Map<number /* window ID */, AnimationFrameQueueItem[]>();
60-
// A flag to keep track if the native requestAnimationFrame was already called
61-
const animFrameRequested = new Map<number /* window ID */, boolean>();
62-
// A flag to indicate if currently handling a native requestAnimationFrame callback
63-
const inAnimationFrameRunner = new Map<number /* window ID */, boolean>();
55+
These functions are defined within an immediately invoked function expression (IIFE) for encapsulation. Here’s a simplified breakdown of the code:
6456

57+
### Data Structures
58+
Maps are used to store global data:
59+
```typescript
60+
(function () {
61+
const NEXT_QUEUE = new Map<number, AnimationFrameQueueItem[]>();
62+
const CURRENT_QUEUE = new Map<number, AnimationFrameQueueItem[]>();
63+
const animFrameRequested = new Map<number, boolean>();
64+
const inAnimationFrameRunner = new Map<number, boolean>();
6565
// ...
6666
})();
6767
```
6868

69-
`runAtThisOrScheduleAtNextAnimationFrame``scheduleAtNextAnimationFrame`的定义如下:
70-
```ts
69+
### API Implementations
70+
#### `scheduleAtNextAnimationFrame`
71+
Schedules tasks for the next animation frame:
72+
```typescript
7173
(function () {
7274
// ...
7375
scheduleAtNextAnimationFrame = (targetWindow: Window, runner: () => void, priority: number = 0) => {
@@ -85,7 +87,14 @@ VSCode把函数的定义写在了一个 immediate call function 里面。因为
8587
}
8688
return item;
8789
};
90+
})();
91+
```
8892

93+
#### `runAtThisOrScheduleAtNextAnimationFrame`
94+
Runs the task immediately if within an animation frame, otherwise schedules it for the next frame:
95+
```typescript
96+
(function () {
97+
// ...
8998
runAtThisOrScheduleAtNextAnimationFrame = (targetWindow: Window, runner: () => void, priority?: number) => {
9099
const targetWindowId = getWindowId(targetWindow);
91100
if (inAnimationFrameRunner.get(targetWindowId)) {
@@ -103,10 +112,9 @@ VSCode把函数的定义写在了一个 immediate call function 里面。因为
103112
};
104113
})();
105114
```
106-
* `AnimationFrameQueueItem` 是任务的基本单位。每个任务都被封装成一个实例,包含了以下信息:
107-
* **任务逻辑**`runner`):需要执行的具体函数。
108-
* **优先级**`priority`):用于控制任务执行的顺序。
109-
* **取消标志**`_canceled`):支持任务的动态取消。
115+
116+
### Task Representation
117+
Each task is encapsulated in an `AnimationFrameQueueItem`:
110118
```typescript
111119
class AnimationFrameQueueItem {
112120
private _runner: () => void;
@@ -133,33 +141,35 @@ class AnimationFrameQueueItem {
133141
}
134142
}
135143
```
136-
调度的核心逻辑是 `animationFrameRunner` 函数,它通过 `requestAnimationFrame` 在每帧执行任务:
137-
1.`NEXT_QUEUE` 中提取任务到 `CURRENT_QUEUE`
138-
2.`CURRENT_QUEUE` 按优先级排序。
139-
3. 按顺序依次执行任务。
144+
145+
### Core Scheduling Logic
146+
The `animationFrameRunner` function manages task execution:
147+
1. Transfers tasks from `NEXT_QUEUE` to `CURRENT_QUEUE`.
148+
2. Sorts tasks in `CURRENT_QUEUE` by priority.
149+
3. Executes tasks sequentially:
140150
```typescript
141151
(function () {
142152
// ...
143153
const animationFrameRunner = (targetWindowId: number) => {
144-
animFrameRequested.set(targetWindowId, false);
154+
animFrameRequested.set(targetWindowId, false);
145155

146-
const currentQueue = NEXT_QUEUE.get(targetWindowId) ?? [];
147-
CURRENT_QUEUE.set(targetWindowId, currentQueue);
148-
NEXT_QUEUE.set(targetWindowId, []);
156+
const currentQueue = NEXT_QUEUE.get(targetWindowId) ?? [];
157+
CURRENT_QUEUE.set(targetWindowId, currentQueue);
158+
NEXT_QUEUE.set(targetWindowId, []);
149159

150-
while (currentQueue.length > 0) {
151-
currentQueue.sort(AnimationFrameQueueItem.sort);
152-
const top = currentQueue.shift()!;
153-
top.execute(); // actual animation execution
154-
}
155-
// ...
156-
};
160+
while (currentQueue.length > 0) {
161+
currentQueue.sort(AnimationFrameQueueItem.sort);
162+
const top = currentQueue.shift()!;
163+
top.execute();
164+
}
165+
// ...
166+
};
157167
})();
158168
```
159169

160-
## 额外内容
161-
VSCode还提供了一些简单的helper function方便做测试或者补丁等等:
162-
```ts
170+
## Additional Utilities
171+
VSCode provides helper functions for early or late scheduling:
172+
```typescript
163173
export function measure(targetWindow: Window, callback: () => void): IDisposable {
164174
return scheduleAtNextAnimationFrame(targetWindow, callback, 10000 /* must be early */);
165175
}

0 commit comments

Comments
 (0)