Skip to content

Commit d34dd71

Browse files
committed
Fix hang/crash in native path code
An optimization for paths is to only create a texture for the original native Path object, and have all copies of that object use that texture. This works in most cases, but sometimes that original path object may get destroyed (when the SDK path object is finalized) while we are still referencing and using that object in the DisplayList code. This causes undefined errors such as crashes and hanging as we iterate through the operations of a destroyed (and garbage-filled) path object. The fix is to use the existing ResourceCache to refcount the original path until we are done with it. Issue #6414050 Analytics Dogfood App crashes reliably on Jellybean Change-Id: I5dbec5c069f7d6a1e68c13424f454976a7d188e9
1 parent 5380cdc commit d34dd71

File tree

4 files changed

+137
-0
lines changed

4 files changed

+137
-0
lines changed

libs/hwui/DisplayListRenderer.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,11 @@ void DisplayList::clearResources() {
184184
}
185185
mPaths.clear();
186186

187+
for (size_t i = 0; i < mSourcePaths.size(); i++) {
188+
caches.resourceCache.decrementRefcount(mSourcePaths.itemAt(i));
189+
}
190+
mSourcePaths.clear();
191+
187192
for (size_t i = 0; i < mMatrices.size(); i++) {
188193
delete mMatrices.itemAt(i);
189194
}
@@ -242,6 +247,12 @@ void DisplayList::initFromDisplayListRenderer(const DisplayListRenderer& recorde
242247
mPaths.add(paths.itemAt(i));
243248
}
244249

250+
const SortedVector<SkPath*> &sourcePaths = recorder.getSourcePaths();
251+
for (size_t i = 0; i < sourcePaths.size(); i++) {
252+
mSourcePaths.add(sourcePaths.itemAt(i));
253+
caches.resourceCache.incrementRefcount(sourcePaths.itemAt(i));
254+
}
255+
245256
const Vector<SkMatrix*> &matrices = recorder.getMatrices();
246257
for (size_t i = 0; i < matrices.size(); i++) {
247258
mMatrices.add(matrices.itemAt(i));
@@ -1273,6 +1284,11 @@ void DisplayListRenderer::reset() {
12731284
mShaders.clear();
12741285
mShaderMap.clear();
12751286

1287+
for (size_t i = 0; i < mSourcePaths.size(); i++) {
1288+
caches.resourceCache.decrementRefcount(mSourcePaths.itemAt(i));
1289+
}
1290+
mSourcePaths.clear();
1291+
12761292
mPaints.clear();
12771293
mPaintMap.clear();
12781294

libs/hwui/DisplayListRenderer.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,7 @@ class DisplayList {
487487

488488
Vector<SkPaint*> mPaints;
489489
Vector<SkPath*> mPaths;
490+
SortedVector<SkPath*> mSourcePaths;
490491
Vector<SkMatrix*> mMatrices;
491492
Vector<SkiaShader*> mShaders;
492493

@@ -634,6 +635,10 @@ class DisplayListRenderer: public OpenGLRenderer {
634635
return mPaths;
635636
}
636637

638+
const SortedVector<SkPath*>& getSourcePaths() const {
639+
return mSourcePaths;
640+
}
641+
637642
const Vector<SkMatrix*>& getMatrices() const {
638643
return mMatrices;
639644
}
@@ -750,6 +755,10 @@ class DisplayListRenderer: public OpenGLRenderer {
750755
mPathMap.replaceValueFor(path, pathCopy);
751756
mPaths.add(pathCopy);
752757
}
758+
if (mSourcePaths.indexOf(path) < 0) {
759+
Caches::getInstance().resourceCache.incrementRefcount(path);
760+
mSourcePaths.add(path);
761+
}
753762

754763
addInt((int) pathCopy);
755764
}
@@ -830,6 +839,8 @@ class DisplayListRenderer: public OpenGLRenderer {
830839
Vector<SkPath*> mPaths;
831840
DefaultKeyedVector<SkPath*, SkPath*> mPathMap;
832841

842+
SortedVector<SkPath*> mSourcePaths;
843+
833844
Vector<SkiaShader*> mShaders;
834845
DefaultKeyedVector<SkiaShader*, SkiaShader*> mShaderMap;
835846

tests/HwAccelerationTest/AndroidManifest.xml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,15 @@
657657
</intent-filter>
658658
</activity>
659659

660+
<activity
661+
android:name="PathDestructionActivity"
662+
android:label="_PathDestruction">
663+
<intent-filter>
664+
<action android:name="android.intent.action.MAIN" />
665+
<category android:name="android.intent.category.LAUNCHER" />
666+
</intent-filter>
667+
</activity>
668+
660669
<activity
661670
android:name="TransformsAndAnimationsActivity"
662671
android:label="_TransformAnim">
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
* Copyright (C) 2012 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.android.test.hwui;
18+
19+
import android.app.Activity;
20+
import android.content.Context;
21+
import android.graphics.Canvas;
22+
import android.graphics.Paint;
23+
import android.graphics.Path;
24+
import android.os.Bundle;
25+
import android.util.MathUtils;
26+
import android.view.View;
27+
28+
/**
29+
* The point of this test is to ensure that we can cause many paths to be created, drawn,
30+
* and destroyed without causing hangs or crashes. This tests the native reference counting
31+
* scheme in particular, because we should be able to have the Java-level path finalized
32+
* without destroying the underlying native path object until we are done referencing it
33+
* in pending DisplayLists.
34+
*/
35+
public class PathDestructionActivity extends Activity {
36+
37+
private static final int MIN_SIZE = 20;
38+
@Override
39+
protected void onCreate(Bundle savedInstanceState) {
40+
super.onCreate(savedInstanceState);
41+
42+
MyView view = new MyView(this);
43+
setContentView(view);
44+
}
45+
46+
private static class MyView extends View {
47+
Paint strokePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
48+
Paint fillPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
49+
Paint fillAndStrokePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
50+
51+
private MyView(Context context) {
52+
super(context);
53+
strokePaint.setStyle(Paint.Style.STROKE);
54+
fillPaint.setStyle(Paint.Style.FILL);
55+
fillAndStrokePaint.setStyle(Paint.Style.FILL_AND_STROKE);
56+
}
57+
58+
private Path getRandomPath() {
59+
float left, top, right, bottom;
60+
left = MathUtils.random(getWidth() - MIN_SIZE);
61+
top = MathUtils.random(getHeight() - MIN_SIZE);
62+
right = left + MathUtils.random(getWidth() - left);
63+
bottom = top + MathUtils.random(getHeight() - top);
64+
Path path = new Path();
65+
path.moveTo(left, top);
66+
path.lineTo(right, top);
67+
path.lineTo(right, bottom);
68+
path.lineTo(left, bottom);
69+
path.close();
70+
return path;
71+
}
72+
73+
private int getRandomColor() {
74+
int red = MathUtils.random(255);
75+
int green = MathUtils.random(255);
76+
int blue = MathUtils.random(255);
77+
return 0xff000000 | red << 16 | green << 8 | blue;
78+
}
79+
80+
@Override
81+
protected void onDraw(Canvas canvas) {
82+
Path path;
83+
for (int i = 0; i < 15; ++i) {
84+
path = getRandomPath();
85+
strokePaint.setColor(getRandomColor());
86+
canvas.drawPath(path, strokePaint);
87+
path = null;
88+
path = getRandomPath();
89+
fillPaint.setColor(getRandomColor());
90+
canvas.drawPath(path, fillPaint);
91+
path = null;
92+
path = getRandomPath();
93+
fillAndStrokePaint.setColor(getRandomColor());
94+
canvas.drawPath(path, fillAndStrokePaint);
95+
path = null;
96+
}
97+
98+
invalidate();
99+
}
100+
}
101+
}

0 commit comments

Comments
 (0)