@ohos-graphics/stability-code-review 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +169 -0
- package/SKILL.md +518 -0
- package/bin/install.js +165 -0
- package/config/rules.yaml +445 -0
- package/config/whitelist.yaml +52 -0
- package/package.json +40 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_001.md +275 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_002.md +273 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_003.md +305 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_004.md +350 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_005.md +301 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_006.md +320 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_007.md +432 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_008.md +394 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_009.md +425 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_010.md +472 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_011.md +204 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_012.md +210 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_013.md +226 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_014.md +222 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_015.md +256 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_016.md +269 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_017.md +222 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_018.md +336 -0
- package/references/ConcurrencyStability/StabilityCodeReview_ConcurrencyStability_001.md +414 -0
- package/references/ConcurrencyStability/StabilityCodeReview_ConcurrencyStability_002.md +335 -0
- package/references/ConcurrencyStability/StabilityCodeReview_ConcurrencyStability_003.md +284 -0
- package/references/ConcurrencyStability/StabilityCodeReview_ConcurrencyStability_004.md +313 -0
- package/references/ConcurrencyStability/StabilityCodeReview_ConcurrencyStability_005.md +364 -0
- package/references/ExceptionHandling/StabilityCodeReview_ExceptionHandling_001.md +142 -0
- package/references/ExceptionHandling/StabilityCodeReview_ExceptionHandling_002.md +222 -0
- package/references/ExceptionHandling/StabilityCodeReview_ExceptionHandling_003.md +383 -0
- package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_001.md +258 -0
- package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_002.md +131 -0
- package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_003.md +220 -0
- package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_004.md +224 -0
- package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_005.md +250 -0
- package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_006.md +153 -0
- package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_007.md +169 -0
- package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_008.md +153 -0
- package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_009.md +144 -0
- package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_010.md +152 -0
- package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_011.md +221 -0
- package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_012.md +318 -0
- package/references/InitializationOrder/StabilityCodeReview_InitializationOrder_001.md +411 -0
- package/references/Lifecycle/StabilityCodeReview_Lifecycle_001.md +255 -0
- package/references/Lifecycle/StabilityCodeReview_Lifecycle_002.md +177 -0
- package/references/MemoryStability/StabilityCodeReview_MemoryStability_001.md +332 -0
- package/references/MemoryStability/StabilityCodeReview_MemoryStability_002.md +261 -0
- package/references/MemoryStability/StabilityCodeReview_MemoryStability_003.md +428 -0
- package/references/MemoryStability/StabilityCodeReview_MemoryStability_004.md +400 -0
- package/references/MemoryStability/StabilityCodeReview_MemoryStability_005.md +364 -0
- package/references/MemoryStability/StabilityCodeReview_MemoryStability_006.md +359 -0
- package/references/MemoryStability/StabilityCodeReview_MemoryStability_007.md +279 -0
- package/references/PROBLEM_TEMPLATE.md +65 -0
- package/references/PerformanceStability/StabilityCodeReview_PerformanceStability_001.md +380 -0
- package/references/PerformanceStability/StabilityCodeReview_PerformanceStability_002.md +437 -0
- package/references/REPORT_TEMPLATE.csv +5 -0
- package/references/REPORT_TEMPLATE.md +132 -0
- package/references/RULE_DEVELOPMENT_GUIDE.md +711 -0
- package/references/RULE_INDEX.md +101 -0
- package/references/RULE_TEMPLATE.md +192 -0
- package/references/ResourceManagement/StabilityCodeReview_ResourceManagement_001.md +334 -0
- package/references/ResourceManagement/StabilityCodeReview_ResourceManagement_002.md +425 -0
- package/references/ResourceManagement/StabilityCodeReview_ResourceManagement_003.md +420 -0
- package/references/ResourceManagement/StabilityCodeReview_ResourceManagement_004.md +409 -0
- package/references/ResourceManagement/StabilityCodeReview_ResourceManagement_005.md +445 -0
- package/references/ResourceManagement/StabilityCodeReview_ResourceManagement_006.md +384 -0
- package/references/ResourceManagement/StabilityCodeReview_ResourceManagement_007.md +395 -0
- package/scripts/add-rule.py +423 -0
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
---
|
|
2
|
+
rule_id: "StabilityCodeReview_GraphicsStability_005"
|
|
3
|
+
name: "RS主线程禁止GPU Context操作"
|
|
4
|
+
category: "图形稳定性"
|
|
5
|
+
severity: "HIGH"
|
|
6
|
+
language: ["cpp", "c++"]
|
|
7
|
+
author: "OH-Department7 Stability Team"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# RS主线程禁止GPU Context操作
|
|
11
|
+
|
|
12
|
+
## 问题描述
|
|
13
|
+
|
|
14
|
+
RSMainThread类(RS主线程)不能做任何与GPU Context相关的操作。**RSMainThread类负责渲染的Prepare及以前阶段**,包括处理事务数据、执行动画、计算遮挡、准备渲染数据等。GPU Context操作(如flush、submit、MakeCurrent、SwapBuffers等)属于Process阶段,应在RSUniRenderThread类执行。RSMainThread类执行这些操作会阻塞Prepare阶段,导致卡顿或ANR。
|
|
15
|
+
|
|
16
|
+
**渲染流程说明**:
|
|
17
|
+
- RSMainThread类:负责Prepare及以前阶段(OnVsync → ProcessCommand → Animate → Prepare → CalcOcclusion → Sync)
|
|
18
|
+
- RSUniRenderThread类:负责Process及以后阶段(Render → OnDraw → GPU操作 → 资源管理)
|
|
19
|
+
|
|
20
|
+
## 检测示例
|
|
21
|
+
|
|
22
|
+
### 错误示例
|
|
23
|
+
|
|
24
|
+
```cpp
|
|
25
|
+
// 错误示例1:RS主线程执行GPU flush
|
|
26
|
+
void RSMainThread::RenderFrame()
|
|
27
|
+
{
|
|
28
|
+
auto context = GetGPUContext();
|
|
29
|
+
|
|
30
|
+
// 错误:主线程执行flush
|
|
31
|
+
context->flush(); // 错误:阻塞主线程
|
|
32
|
+
|
|
33
|
+
// 错误:主线程执行submit
|
|
34
|
+
context->submit(); // 错误:阻塞主线程
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// 错误示例2:RS主线程执行SwapBuffers
|
|
38
|
+
void RSMainThread::OnSurfaceChanged()
|
|
39
|
+
{
|
|
40
|
+
if (IsMainThread()) {
|
|
41
|
+
// 错误:主线程执行SwapBuffers
|
|
42
|
+
SwapBuffers(); // 错误:阻塞主线程
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// 错误示例3:RS主线程创建GPU Context
|
|
47
|
+
void RSMainThread::Initialize()
|
|
48
|
+
{
|
|
49
|
+
// 错误:主线程创建GPU Context
|
|
50
|
+
auto context = GrDirectContext::MakeGL(); // 错误
|
|
51
|
+
|
|
52
|
+
// 错误:主线程MakeCurrent
|
|
53
|
+
MakeCurrent(context); // 错误
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### 正确示例
|
|
58
|
+
|
|
59
|
+
```cpp
|
|
60
|
+
// 正确示例1:渲染线程执行GPU Context操作
|
|
61
|
+
void RenderThread::OnRender()
|
|
62
|
+
{
|
|
63
|
+
auto context = GetGPUContext();
|
|
64
|
+
|
|
65
|
+
// 正确:渲染线程执行flush
|
|
66
|
+
context->flush(); // 正确
|
|
67
|
+
|
|
68
|
+
// 正确:渲染线程执行submit
|
|
69
|
+
context->submit(); // 正确
|
|
70
|
+
|
|
71
|
+
// 正确:渲染线程SwapBuffers
|
|
72
|
+
SwapBuffers(); // 正确
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// 正确示例2:主线程通过命令通知渲染线程
|
|
76
|
+
void RSMainThread::RequestRender()
|
|
77
|
+
{
|
|
78
|
+
// 正确:发送渲染命令
|
|
79
|
+
RenderCommand cmd;
|
|
80
|
+
cmd.type = RENDER_TYPE_FLUSH;
|
|
81
|
+
SubmitToRenderThread(cmd); // 正确:不直接操作GPU
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// 正确示例3:主线程只负责命令构建
|
|
85
|
+
void RSMainThread::BuildRenderCommands()
|
|
86
|
+
{
|
|
87
|
+
// 正确:构建渲染命令,不执行GPU操作
|
|
88
|
+
auto node = GetRenderNode();
|
|
89
|
+
auto cmd = BuildCommandFromNode(node); // 正确
|
|
90
|
+
|
|
91
|
+
SubmitToRenderThread(cmd); // 正确
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## 风险流分析(RiskFlow)
|
|
96
|
+
|
|
97
|
+
- **RISK_SOURCE**: RSMainThread类执行GPU Context操作
|
|
98
|
+
- **RISK_TYPE**: 线程职责越界
|
|
99
|
+
- **RISK_PATH**: RSMainThread类操作GPU Context -> 阻塞主线程 -> UI卡顿或崩溃
|
|
100
|
+
- **IMPACT_POINT**: UI卡顿、渲染异常
|
|
101
|
+
|
|
102
|
+
## 影响分析(ImpactAnalysis)
|
|
103
|
+
|
|
104
|
+
- **Trigger**: RSMainThread类调用GPU Context相关API
|
|
105
|
+
- **Propagation**: 阻塞主线程执行
|
|
106
|
+
- **Consequence**: UI卡顿、响应超时、ANR
|
|
107
|
+
- **Mitigation**: 将GPU Context操作移至渲染线程执行
|
|
108
|
+
|
|
109
|
+
## 误报排除
|
|
110
|
+
|
|
111
|
+
| 场景 | 识别特征 | 处理方式 |
|
|
112
|
+
|------|----------|----------|
|
|
113
|
+
| NOPROTECT 标记 | 有 // NOPROTECT 注释 | 不报 |
|
|
114
|
+
| 渲染线程上下文 | RSUniRenderThread::, RenderThread:: | 不报 |
|
|
115
|
+
| 获取Context指针 | GetGPUContext() | 不报(允许获取)|
|
|
116
|
+
| 内存清理场景 | context->abandonedContext(), PurgeCache | 不报(内存清理)|
|
|
117
|
+
|
|
118
|
+
### GPU Context操作的场景区分
|
|
119
|
+
|
|
120
|
+
RSMainThread禁止常规的GPU Context操作,但内存清理等特定场景允许:
|
|
121
|
+
|
|
122
|
+
#### ❌ 禁止场景(阻塞主线程的GPU操作)
|
|
123
|
+
|
|
124
|
+
```cpp
|
|
125
|
+
// 错误场景1:主线程执行渲染flush
|
|
126
|
+
void RSMainThread::RenderFrame()
|
|
127
|
+
{
|
|
128
|
+
auto context = GetGPUContext();
|
|
129
|
+
context->flush(); // ❌ 禁止:阻塞主线程
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// 错误场景2:主线程执行SwapBuffers
|
|
133
|
+
void RSMainThread::OnVsync()
|
|
134
|
+
{
|
|
135
|
+
SwapBuffers(); // ❌ 禁止:阻塞主线程
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// 错误场景3:主线程执行GPU绘制
|
|
139
|
+
void RSMainThread::DrawContent()
|
|
140
|
+
{
|
|
141
|
+
auto context = GetGPUContext();
|
|
142
|
+
context->MakeCurrent(); // ❌ 禁止:GPU绑定
|
|
143
|
+
DrawOnGPU(); // ❌ 禁止:GPU绘制
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
#### ✅ 允许场景(内存清理和非阻塞操作)
|
|
148
|
+
|
|
149
|
+
```cpp
|
|
150
|
+
// 正确场景1:内存清理时abandonedContext
|
|
151
|
+
void RSMainThread::PurgeMemory()
|
|
152
|
+
{
|
|
153
|
+
auto context = GetGPUContext();
|
|
154
|
+
if (context) {
|
|
155
|
+
context->abandonedContext(); // ✅ 允许:内存清理,不阻塞
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// 正确场景2:释放GPU资源(内存压力响应)
|
|
160
|
+
void RSMainThread::OnMemoryPressure()
|
|
161
|
+
{
|
|
162
|
+
auto context = GetGPUContext();
|
|
163
|
+
context->purgeCache(); // ✅ 允许:清理GPU缓存,释放内存
|
|
164
|
+
|
|
165
|
+
// 内存清理场景,不执行渲染
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// 正确场景3:销毁Context(进程退出)
|
|
169
|
+
void RSMainThread::OnDestroy()
|
|
170
|
+
{
|
|
171
|
+
auto context = GetGPUContext();
|
|
172
|
+
if (context) {
|
|
173
|
+
context->release(); // ✅ 允许:资源释放,不渲染
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// 正确场景4:获取Context但不操作
|
|
178
|
+
void RSMainThread::CheckContextValid()
|
|
179
|
+
{
|
|
180
|
+
auto context = GetGPUContext(); // ✅ 允许:仅获取指针
|
|
181
|
+
if (context && context->isValid()) { // ✅ 允许:仅判断有效性
|
|
182
|
+
// 不执行GPU渲染操作
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### GPU Context操作分类
|
|
188
|
+
|
|
189
|
+
根据是否阻塞主线程,将GPU Context操作分为两类:
|
|
190
|
+
|
|
191
|
+
- **阻塞型操作**(RSMainThread禁止):
|
|
192
|
+
- `flush()` - 等待GPU完成
|
|
193
|
+
- `submit()` - 提交渲染命令
|
|
194
|
+
- `SwapBuffers()` - 交换缓冲区
|
|
195
|
+
- `MakeCurrent()` - GPU绑定
|
|
196
|
+
- 实际的GPU绘制操作
|
|
197
|
+
- 等待GPU Fence
|
|
198
|
+
|
|
199
|
+
- **非阻塞型操作**(RSMainThread可能允许):
|
|
200
|
+
- `abandonedContext()` - 放弃Context(内存清理)
|
|
201
|
+
- `purgeCache()` - 清理GPU缓存(内存压力)
|
|
202
|
+
- `release()` - 释放资源(进程退出)
|
|
203
|
+
- `isValid()` - 判断有效性(不执行操作)
|
|
204
|
+
- `GetGPUContext()` - 获取指针(不执行操作)
|
|
205
|
+
|
|
206
|
+
### 检测规则细化
|
|
207
|
+
|
|
208
|
+
**应该报的情况**:
|
|
209
|
+
- RSMainThread中调用`context->flush()`
|
|
210
|
+
- RSMainThread中调用`context->submit()`
|
|
211
|
+
- RSMainThread中调用`SwapBuffers()`
|
|
212
|
+
- RSMainThread中调用`context->MakeCurrent()`
|
|
213
|
+
- RSMainThread中执行GPU绘制操作
|
|
214
|
+
|
|
215
|
+
**不应该报的情况**:
|
|
216
|
+
- RSMainThread中调用`context->abandonedContext()`(内存清理)
|
|
217
|
+
- RSMainThread中调用`context->purgeCache()`(内存清理)
|
|
218
|
+
- RSMainThread中调用`context->release()`(资源释放)
|
|
219
|
+
- RSMainThread中调用`GetGPUContext()`(仅获取)
|
|
220
|
+
- RSMainThread中调用`context->isValid()`(仅判断)
|
|
221
|
+
- RSUniRenderThread中的任何GPU Context操作
|
|
222
|
+
## 测试用例
|
|
223
|
+
|
|
224
|
+
### 触发用例(应该报)
|
|
225
|
+
|
|
226
|
+
```cpp
|
|
227
|
+
// RSMainThread中执行GPU Context操作
|
|
228
|
+
void RSMainThread::OnRender()
|
|
229
|
+
{
|
|
230
|
+
context->flush(); // 应该报:主线程不应flush
|
|
231
|
+
context->submit(); // 应该报
|
|
232
|
+
SwapBuffers(); // 应该报
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### 安全用例(不应该报)
|
|
237
|
+
|
|
238
|
+
```cpp
|
|
239
|
+
// 渲染线程执行GPU Context操作
|
|
240
|
+
void RenderThread::OnRender()
|
|
241
|
+
{
|
|
242
|
+
context->flush(); // 不报:渲染线程允许
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// 主线程获取Context但不操作
|
|
246
|
+
void RSMainThread::GetContext()
|
|
247
|
+
{
|
|
248
|
+
auto context = GetGPUContext(); // 不报:允许获取
|
|
249
|
+
}
|
|
250
|
+
```
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
---
|
|
2
|
+
rule_id: "StabilityCodeReview_GraphicsStability_006"
|
|
3
|
+
name: "Surface/Image跨线程跨Context操作风险"
|
|
4
|
+
category: "图形稳定性"
|
|
5
|
+
severity: "HIGH"
|
|
6
|
+
language: ["cpp", "c++"]
|
|
7
|
+
author: "OH-Department7 Stability Team"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Surface/Image跨线程跨Context操作风险
|
|
11
|
+
|
|
12
|
+
## 问题描述
|
|
13
|
+
|
|
14
|
+
Surface/Image应尽量避免跨线程/跨Context操作,如果必须多线程访问,应首先考虑使用BackendTexture创建新的Surface/Image,否则必须要保证Surface/Image在同一把锁的保护范围内。跨线程/跨Context操作GPU资源会导致状态不一致,引发GPU崩溃。
|
|
15
|
+
|
|
16
|
+
## 检测示例
|
|
17
|
+
|
|
18
|
+
### 错误示例
|
|
19
|
+
|
|
20
|
+
```cpp
|
|
21
|
+
// 错误示例1:跨线程访问Surface无锁保护
|
|
22
|
+
class SharedSurfaceManager {
|
|
23
|
+
public:
|
|
24
|
+
sk_sp<Surface> surface_;
|
|
25
|
+
|
|
26
|
+
void ThreadA_Draw() {
|
|
27
|
+
// 错误:线程A直接访问共享Surface
|
|
28
|
+
auto canvas = surface_->getCanvas(); // 错误:无锁保护
|
|
29
|
+
canvas->drawRect(rect, paint);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
void ThreadB_Draw() {
|
|
33
|
+
// 错误:线程B直接访问同一Surface
|
|
34
|
+
auto canvas = surface_->getCanvas(); // 错误:无锁保护
|
|
35
|
+
canvas->drawCircle(center, radius, paint);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// 错误示例2:跨Context使用Image
|
|
40
|
+
void CrossContextUsage()
|
|
41
|
+
{
|
|
42
|
+
auto image = CreateImageOnContext1(); // 在Context1创建
|
|
43
|
+
|
|
44
|
+
// 错误:在Context2使用
|
|
45
|
+
auto canvas2 = context2_surface->getCanvas();
|
|
46
|
+
canvas2->drawImage(image); // 错误:跨Context使用
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// 错误示例3:多线程共享Image无保护
|
|
50
|
+
std::shared_ptr<Image> g_sharedImage;
|
|
51
|
+
|
|
52
|
+
void Thread1() {
|
|
53
|
+
g_sharedImage->readPixels(info, pixels); // 错误:无保护
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
void Thread2() {
|
|
57
|
+
g_sharedImage->makeSubset(region); // 错误:无保护
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### 正确示例
|
|
62
|
+
|
|
63
|
+
```cpp
|
|
64
|
+
// 正确示例1:使用BackendTexture创建新Surface
|
|
65
|
+
void SafeCrossThreadAccess()
|
|
66
|
+
{
|
|
67
|
+
BackendTexture backendTexture = surface1->getBackendTexture();
|
|
68
|
+
|
|
69
|
+
// 正确:线程B使用BackendTexture创建新的Surface
|
|
70
|
+
auto surface2 = Surface::MakeFromBackendTexture(
|
|
71
|
+
context2, backendTexture, origin, 1, colorType, nullptr, nullptr); // 正确
|
|
72
|
+
|
|
73
|
+
auto canvas2 = surface2->getCanvas(); // 正确:独立Surface
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// 正确示例2:使用锁保护Surface访问
|
|
77
|
+
class LockedSurfaceManager {
|
|
78
|
+
public:
|
|
79
|
+
sk_sp<Surface> surface_;
|
|
80
|
+
std::mutex mutex_;
|
|
81
|
+
|
|
82
|
+
void ThreadA_Draw() {
|
|
83
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
84
|
+
auto canvas = surface_->getCanvas(); // 正确:有锁保护
|
|
85
|
+
canvas->drawRect(rect, paint);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
void ThreadB_Draw() {
|
|
89
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
90
|
+
auto canvas = surface_->getCanvas(); // 正确:有锁保护
|
|
91
|
+
canvas->drawCircle(center, radius, paint);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// 正确示例3:每个线程独立创建Image
|
|
96
|
+
void ThreadSafeImageUsage()
|
|
97
|
+
{
|
|
98
|
+
// 正确:每个线程创建独立Image
|
|
99
|
+
BackendTexture texture = GetSharedBackendTexture();
|
|
100
|
+
|
|
101
|
+
// 线程A
|
|
102
|
+
auto imageA = Image::MakeFromBackendTexture(contextA, texture, origin, colorType, nullptr, nullptr); // 正确
|
|
103
|
+
|
|
104
|
+
// 线程B
|
|
105
|
+
auto imageB = Image::MakeFromBackendTexture(contextB, texture, origin, colorType, nullptr, nullptr); // 正确
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## 风险流分析(RiskFlow)
|
|
110
|
+
|
|
111
|
+
- **RISK_SOURCE**: Surface/Image跨线程/跨Context操作
|
|
112
|
+
- **RISK_TYPE**: 线程安全风险
|
|
113
|
+
- **RISK_PATH**: 跨线程访问Surface/Image -> 数据竞争 -> GPU资源状态不一致
|
|
114
|
+
- **IMPACT_POINT**: GPU崩溃、渲染异常
|
|
115
|
+
|
|
116
|
+
## 影响分析(ImpactAnalysis)
|
|
117
|
+
|
|
118
|
+
- **Trigger**: 多线程同时访问同一Surface/Image
|
|
119
|
+
- **Propagation**: GPU资源状态不一致
|
|
120
|
+
- **Consequence**: 渲染异常、GPU崩溃、黑屏
|
|
121
|
+
- **Mitigation**: 使用BackendTexture创建新Surface/Image,或用锁保护访问
|
|
122
|
+
|
|
123
|
+
## 误报排除
|
|
124
|
+
|
|
125
|
+
| 场景 | 识别特征 | 处理方式 |
|
|
126
|
+
|------|----------|----------|
|
|
127
|
+
| NOPROTECT 标记 | 有 // NOPROTECT 注释 | 不报 |
|
|
128
|
+
| 使用BackendTexture | MakeFromBackendTexture | 不报 |
|
|
129
|
+
| 有锁保护 | lock_guard、unique_lock | 不报 |
|
|
130
|
+
## 测试用例
|
|
131
|
+
|
|
132
|
+
### 触发用例(应该报)
|
|
133
|
+
|
|
134
|
+
```cpp
|
|
135
|
+
// 跨线程访问Surface无锁保护
|
|
136
|
+
std::thread t1([&]() {
|
|
137
|
+
surface->getCanvas()->drawRect(rect, paint); // 应该报:无保护
|
|
138
|
+
});
|
|
139
|
+
std::thread t2([&]() {
|
|
140
|
+
surface->getCanvas()->drawCircle(c, r, paint); // 应该报
|
|
141
|
+
});
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### 安全用例(不应该报)
|
|
145
|
+
|
|
146
|
+
```cpp
|
|
147
|
+
// 使用锁保护
|
|
148
|
+
std::lock_guard<std::mutex> lock(mutex);
|
|
149
|
+
surface->getCanvas()->drawRect(rect, paint); // 不报:有锁
|
|
150
|
+
|
|
151
|
+
// 使用BackendTexture创建新Surface
|
|
152
|
+
auto surface2 = Surface::MakeFromBackendTexture(...); // 不报
|
|
153
|
+
```
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
---
|
|
2
|
+
rule_id: "StabilityCodeReview_GraphicsStability_007"
|
|
3
|
+
name: "Surface/Image创建和释放线程一致性"
|
|
4
|
+
category: "图形稳定性"
|
|
5
|
+
severity: "HIGH"
|
|
6
|
+
language: ["cpp", "c++"]
|
|
7
|
+
author: "OH-Department7 Stability Team"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Surface/Image创建和释放线程一致性
|
|
11
|
+
|
|
12
|
+
## 问题描述
|
|
13
|
+
|
|
14
|
+
Surface/Image涉及GPU资源,创建和释放应处于同一线程。跨线程创建和释放会导致GPU驱动状态不一致,引发资源泄漏或提前释放。
|
|
15
|
+
|
|
16
|
+
## 检测示例
|
|
17
|
+
|
|
18
|
+
### 错误示例
|
|
19
|
+
|
|
20
|
+
```cpp
|
|
21
|
+
// 错误示例1:主线程创建,渲染线程释放
|
|
22
|
+
void MainThread_Create()
|
|
23
|
+
{
|
|
24
|
+
auto surface = Surface::MakeRenderTarget(context, info); // 主线程创建
|
|
25
|
+
g_surface = surface; // 传递给全局
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
void RenderThread_Release()
|
|
29
|
+
{
|
|
30
|
+
// 错误:渲染线程释放主线程创建的Surface
|
|
31
|
+
g_surface.reset(); // 错误:跨线程释放
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// 错误示例2:异步创建,同步释放
|
|
35
|
+
class AsyncImageCreator {
|
|
36
|
+
public:
|
|
37
|
+
void CreateAsync() {
|
|
38
|
+
std::thread t([this]() {
|
|
39
|
+
image_ = Image::MakeFromEncoded(data); // 异步线程创建
|
|
40
|
+
});
|
|
41
|
+
t.detach();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
void ReleaseSync() {
|
|
45
|
+
// 错误:主线程释放异步线程创建的Image
|
|
46
|
+
image_.reset(); // 错误:跨线程释放
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private:
|
|
50
|
+
sk_sp<Image> image_;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// 错误示例3:回调创建,主线程释放
|
|
54
|
+
void OnTextureLoadedCallback(Texture* texture)
|
|
55
|
+
{
|
|
56
|
+
g_image = Image::MakeFromTexture(texture); // IO线程创建
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
void MainThread_Cleanup()
|
|
60
|
+
{
|
|
61
|
+
// 错误:主线程释放IO线程创建的Image
|
|
62
|
+
g_image = nullptr; // 错误:跨线程释放
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 正确示例
|
|
67
|
+
|
|
68
|
+
```cpp
|
|
69
|
+
// 正确示例1:同一线程创建和释放
|
|
70
|
+
void RenderThread_Lifecycle()
|
|
71
|
+
{
|
|
72
|
+
// 正确:渲染线程创建
|
|
73
|
+
auto surface = Surface::MakeRenderTarget(context, info);
|
|
74
|
+
|
|
75
|
+
// 使用Surface
|
|
76
|
+
RenderWithSurface(surface);
|
|
77
|
+
|
|
78
|
+
// 正确:渲染线程释放
|
|
79
|
+
surface.reset(); // 正确:同一线程释放
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// 正确示例2:主线程管理全生命周期
|
|
83
|
+
class SurfaceManager {
|
|
84
|
+
public:
|
|
85
|
+
void Create() {
|
|
86
|
+
// 正确:主线程创建
|
|
87
|
+
surface_ = Surface::MakeRenderTarget(context_, info_);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
void Release() {
|
|
91
|
+
// 正确:主线程释放
|
|
92
|
+
surface_.reset(); // 正确:同一线程释放
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private:
|
|
96
|
+
sk_sp<Surface> surface_;
|
|
97
|
+
GrDirectContext* context_;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
// 正确示例3:通过BackendTexture跨线程安全传递
|
|
101
|
+
void ThreadA_CreateBackendTexture()
|
|
102
|
+
{
|
|
103
|
+
auto surface = Surface::MakeRenderTarget(contextA, info);
|
|
104
|
+
BackendTexture backend = surface->getBackendTexture();
|
|
105
|
+
|
|
106
|
+
// 正确:传递BackendTexture而非Surface
|
|
107
|
+
SendToThreadB(backend); // 正确
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
void ThreadB_CreateFromBackend()
|
|
111
|
+
{
|
|
112
|
+
BackendTexture backend = ReceiveFromThreadA();
|
|
113
|
+
// 正确:线程B创建新的Surface
|
|
114
|
+
auto surface = Surface::MakeFromBackendTexture(contextB, backend, ...); // 正确
|
|
115
|
+
|
|
116
|
+
// 正确:线程B释放自己创建的Surface
|
|
117
|
+
surface.reset(); // 正确
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## 风险流分析(RiskFlow)
|
|
122
|
+
|
|
123
|
+
- **RISK_SOURCE**: Surface/Image创建和释放不在同一线程
|
|
124
|
+
- **RISK_TYPE**: 线程安全违规
|
|
125
|
+
- **RISK_PATH**: 跨线程创建和释放 -> GPU驱动状态不一致 -> 资源泄漏或提前释放
|
|
126
|
+
- **IMPACT_POINT**: GPU资源泄漏或提前释放
|
|
127
|
+
|
|
128
|
+
## 影响分析(ImpactAnalysis)
|
|
129
|
+
|
|
130
|
+
- **Trigger**: 跨线程释放GPU资源
|
|
131
|
+
- **Propagation**: GPU驱动状态不一致
|
|
132
|
+
- **Consequence**: GPU崩溃、内存泄漏、渲染异常
|
|
133
|
+
- **Mitigation**: 确保Surface/Image在同一线程创建和释放
|
|
134
|
+
|
|
135
|
+
## 误报排除
|
|
136
|
+
|
|
137
|
+
| 场景 | 识别特征 | 处理方式 |
|
|
138
|
+
|------|----------|----------|
|
|
139
|
+
| NOPROTECT 标记 | 有 // NOPROTECT 注释 | 不报 |
|
|
140
|
+
| 同线程创建释放 | 上下文分析确认同线程 | 不报 |
|
|
141
|
+
| 使用BackendTexture | MakeFromBackendTexture | 不报 |
|
|
142
|
+
## 测试用例
|
|
143
|
+
|
|
144
|
+
### 触发用例(应该报)
|
|
145
|
+
|
|
146
|
+
```cpp
|
|
147
|
+
// 在MainThread创建,在RenderThread释放
|
|
148
|
+
void MainThread_Create()
|
|
149
|
+
{
|
|
150
|
+
sk_sp<Surface> surface = Surface::MakeRenderTarget(...); // 主线程创建
|
|
151
|
+
g_surface = surface;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
void RenderThread_Release()
|
|
155
|
+
{
|
|
156
|
+
g_surface.reset(); // 应该报:跨线程释放
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### 安全用例(不应该报)
|
|
161
|
+
|
|
162
|
+
```cpp
|
|
163
|
+
// 同一线程创建和释放
|
|
164
|
+
void RenderThread_Lifecycle()
|
|
165
|
+
{
|
|
166
|
+
sk_sp<Surface> surface = Surface::MakeRenderTarget(...); // 渲染线程创建
|
|
167
|
+
surface.reset(); // 不报:同一线程释放
|
|
168
|
+
}
|
|
169
|
+
```
|