Skip to content

Commit 3930ac1

Browse files
authored
Hint freed (flutter#19842)
* Hint the VM when a layer or picture goes out of scope
1 parent 93a1790 commit 3930ac1

28 files changed

+247
-47
lines changed

flow/compositor_context.cc

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99

1010
namespace flutter {
1111

12-
CompositorContext::CompositorContext(fml::Milliseconds frame_budget)
13-
: raster_time_(frame_budget), ui_time_(frame_budget) {}
12+
CompositorContext::CompositorContext(Delegate& delegate)
13+
: delegate_(delegate),
14+
raster_time_(delegate.GetFrameBudget()),
15+
ui_time_(delegate.GetFrameBudget()) {}
1416

1517
CompositorContext::~CompositorContext() = default;
1618

@@ -23,8 +25,11 @@ void CompositorContext::BeginFrame(ScopedFrame& frame,
2325
}
2426

2527
void CompositorContext::EndFrame(ScopedFrame& frame,
26-
bool enable_instrumentation) {
27-
raster_cache_.SweepAfterFrame();
28+
bool enable_instrumentation,
29+
size_t freed_hint) {
30+
freed_hint += raster_cache_.SweepAfterFrame();
31+
delegate_.OnCompositorEndFrame(freed_hint);
32+
2833
if (enable_instrumentation) {
2934
raster_time_.Stop();
3035
}
@@ -64,7 +69,7 @@ CompositorContext::ScopedFrame::ScopedFrame(
6469
}
6570

6671
CompositorContext::ScopedFrame::~ScopedFrame() {
67-
context_.EndFrame(*this, instrumentation_enabled_);
72+
context_.EndFrame(*this, instrumentation_enabled_, uncached_external_size_);
6873
}
6974

7075
RasterStatus CompositorContext::ScopedFrame::Raster(

flow/compositor_context.h

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,18 @@ enum class RasterStatus {
3737

3838
class CompositorContext {
3939
public:
40+
class Delegate {
41+
public:
42+
/// Called at the end of a frame with approximately how many bytes mightbe
43+
/// freed if a GC ran now.
44+
///
45+
/// This method is called from the raster task runner.
46+
virtual void OnCompositorEndFrame(size_t freed_hint) = 0;
47+
48+
/// Time limit for a smooth frame. See `Engine::GetDisplayRefreshRate`.
49+
virtual fml::Milliseconds GetFrameBudget() = 0;
50+
};
51+
4052
class ScopedFrame {
4153
public:
4254
ScopedFrame(CompositorContext& context,
@@ -67,6 +79,8 @@ class CompositorContext {
6779
virtual RasterStatus Raster(LayerTree& layer_tree,
6880
bool ignore_raster_cache);
6981

82+
void add_external_size(size_t size) { uncached_external_size_ += size; }
83+
7084
private:
7185
CompositorContext& context_;
7286
GrDirectContext* gr_context_;
@@ -76,11 +90,12 @@ class CompositorContext {
7690
const bool instrumentation_enabled_;
7791
const bool surface_supports_readback_;
7892
fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger_;
93+
size_t uncached_external_size_ = 0;
7994

8095
FML_DISALLOW_COPY_AND_ASSIGN(ScopedFrame);
8196
};
8297

83-
CompositorContext(fml::Milliseconds frame_budget = fml::kDefaultFrameBudget);
98+
explicit CompositorContext(Delegate& delegate);
8499

85100
virtual ~CompositorContext();
86101

@@ -108,6 +123,7 @@ class CompositorContext {
108123
Stopwatch& ui_time() { return ui_time_; }
109124

110125
private:
126+
Delegate& delegate_;
111127
RasterCache raster_cache_;
112128
TextureRegistry texture_registry_;
113129
Counter frame_count_;
@@ -116,7 +132,9 @@ class CompositorContext {
116132

117133
void BeginFrame(ScopedFrame& frame, bool enable_instrumentation);
118134

119-
void EndFrame(ScopedFrame& frame, bool enable_instrumentation);
135+
void EndFrame(ScopedFrame& frame,
136+
bool enable_instrumentation,
137+
size_t freed_hint);
120138

121139
FML_DISALLOW_COPY_AND_ASSIGN(CompositorContext);
122140
};

flow/layers/layer.cc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@
99

1010
namespace flutter {
1111

12-
Layer::Layer()
12+
Layer::Layer(size_t external_size)
1313
: paint_bounds_(SkRect::MakeEmpty()),
1414
unique_id_(NextUniqueID()),
15-
needs_system_composite_(false) {}
15+
needs_system_composite_(false),
16+
external_size_(external_size) {}
1617

1718
Layer::~Layer() = default;
1819

flow/layers/layer.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,14 @@ struct PrerollContext {
6767
// Informs whether a layer needs to be system composited.
6868
bool child_scene_layer_exists_below = false;
6969
#endif
70+
size_t uncached_external_size = 0;
7071
};
7172

7273
// Represents a single composited layer. Created on the UI thread but then
7374
// subquently used on the Rasterizer thread.
7475
class Layer {
7576
public:
76-
Layer();
77+
Layer(size_t external_size = 0);
7778
virtual ~Layer();
7879

7980
virtual void Preroll(PrerollContext* context, const SkMatrix& matrix);
@@ -178,6 +179,8 @@ class Layer {
178179

179180
uint64_t unique_id() const { return unique_id_; }
180181

182+
size_t external_size() const { return external_size_; }
183+
181184
protected:
182185
#if defined(LEGACY_FUCHSIA_EMBEDDER)
183186
bool child_layer_exists_below_ = false;
@@ -187,6 +190,7 @@ class Layer {
187190
SkRect paint_bounds_;
188191
uint64_t unique_id_;
189192
bool needs_system_composite_;
193+
size_t external_size_ = 0;
190194

191195
static uint64_t NextUniqueID();
192196

flow/layers/layer_tree.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ bool LayerTree::Preroll(CompositorContext::ScopedFrame& frame,
5858
device_pixel_ratio_};
5959

6060
root_layer_->Preroll(&context, frame.root_surface_transformation());
61+
frame.add_external_size(context.uncached_external_size);
6162
return context.surface_needs_readback;
6263
}
6364

flow/layers/layer_tree_unittests.cc

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@
1515
namespace flutter {
1616
namespace testing {
1717

18-
class LayerTreeTest : public CanvasTest {
18+
class LayerTreeTest : public CanvasTest, public CompositorContext::Delegate {
1919
public:
2020
LayerTreeTest()
2121
: layer_tree_(SkISize::Make(64, 64), 1.0f),
22-
compositor_context_(fml::kDefaultFrameBudget),
22+
compositor_context_(*this),
2323
root_transform_(SkMatrix::Translate(1.0f, 1.0f)),
2424
scoped_frame_(compositor_context_.AcquireFrame(nullptr,
2525
&mock_canvas(),
@@ -33,11 +33,24 @@ class LayerTreeTest : public CanvasTest {
3333
CompositorContext::ScopedFrame& frame() { return *scoped_frame_.get(); }
3434
const SkMatrix& root_transform() { return root_transform_; }
3535

36+
// |CompositorContext::Delegate|
37+
void OnCompositorEndFrame(size_t freed_hint) override {
38+
last_freed_hint_ = freed_hint;
39+
}
40+
41+
// |CompositorContext::Delegate|
42+
fml::Milliseconds GetFrameBudget() override {
43+
return fml::kDefaultFrameBudget;
44+
}
45+
46+
size_t last_freed_hint() { return last_freed_hint_; }
47+
3648
private:
3749
LayerTree layer_tree_;
3850
CompositorContext compositor_context_;
3951
SkMatrix root_transform_;
4052
std::unique_ptr<CompositorContext::ScopedFrame> scoped_frame_;
53+
size_t last_freed_hint_ = 0;
4154
};
4255

4356
TEST_F(LayerTreeTest, PaintingEmptyLayerDies) {

flow/layers/picture_layer.cc

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@ namespace flutter {
1111
PictureLayer::PictureLayer(const SkPoint& offset,
1212
SkiaGPUObject<SkPicture> picture,
1313
bool is_complex,
14-
bool will_change)
15-
: offset_(offset),
14+
bool will_change,
15+
size_t external_size)
16+
: Layer(external_size),
17+
offset_(offset),
1618
picture_(std::move(picture)),
1719
is_complex_(is_complex),
1820
will_change_(will_change) {}
@@ -26,6 +28,7 @@ void PictureLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) {
2628

2729
SkPicture* sk_picture = picture();
2830

31+
bool cached = false;
2932
if (auto* cache = context->raster_cache) {
3033
TRACE_EVENT0("flutter", "PictureLayer::RasterCache (Preroll)");
3134

@@ -34,8 +37,13 @@ void PictureLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) {
3437
#ifndef SUPPORT_FRACTIONAL_TRANSLATION
3538
ctm = RasterCache::GetIntegralTransCTM(ctm);
3639
#endif
37-
cache->Prepare(context->gr_context, sk_picture, ctm,
38-
context->dst_color_space, is_complex_, will_change_);
40+
cached = cache->Prepare(context->gr_context, sk_picture, ctm,
41+
context->dst_color_space, is_complex_, will_change_,
42+
external_size());
43+
}
44+
45+
if (!cached) {
46+
context->uncached_external_size += external_size();
3947
}
4048

4149
SkRect bounds = sk_picture->cullRect().makeOffset(offset_.x(), offset_.y());

flow/layers/picture_layer.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ class PictureLayer : public Layer {
1818
PictureLayer(const SkPoint& offset,
1919
SkiaGPUObject<SkPicture> picture,
2020
bool is_complex,
21-
bool will_change);
21+
bool will_change,
22+
size_t external_size);
2223

2324
SkPicture* picture() const { return picture_.get().get(); }
2425

flow/layers/picture_layer_unittests.cc

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ using PictureLayerTest = SkiaGPUObjectLayerTest;
2424
TEST_F(PictureLayerTest, PaintBeforePrerollInvalidPictureDies) {
2525
const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f);
2626
auto layer = std::make_shared<PictureLayer>(
27-
layer_offset, SkiaGPUObject<SkPicture>(), false, false);
27+
layer_offset, SkiaGPUObject<SkPicture>(), false, false, 0);
2828

2929
EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
3030
"picture_\\.get\\(\\)");
@@ -35,7 +35,8 @@ TEST_F(PictureLayerTest, PaintBeforePreollDies) {
3535
const SkRect picture_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f);
3636
auto mock_picture = SkPicture::MakePlaceholder(picture_bounds);
3737
auto layer = std::make_shared<PictureLayer>(
38-
layer_offset, SkiaGPUObject(mock_picture, unref_queue()), false, false);
38+
layer_offset, SkiaGPUObject(mock_picture, unref_queue()), false, false,
39+
0);
3940

4041
EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty());
4142
EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
@@ -47,7 +48,8 @@ TEST_F(PictureLayerTest, PaintingEmptyLayerDies) {
4748
const SkRect picture_bounds = SkRect::MakeEmpty();
4849
auto mock_picture = SkPicture::MakePlaceholder(picture_bounds);
4950
auto layer = std::make_shared<PictureLayer>(
50-
layer_offset, SkiaGPUObject(mock_picture, unref_queue()), false, false);
51+
layer_offset, SkiaGPUObject(mock_picture, unref_queue()), false, false,
52+
0);
5153

5254
layer->Preroll(preroll_context(), SkMatrix());
5355
EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty());
@@ -62,7 +64,7 @@ TEST_F(PictureLayerTest, PaintingEmptyLayerDies) {
6264
TEST_F(PictureLayerTest, InvalidPictureDies) {
6365
const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f);
6466
auto layer = std::make_shared<PictureLayer>(
65-
layer_offset, SkiaGPUObject<SkPicture>(), false, false);
67+
layer_offset, SkiaGPUObject<SkPicture>(), false, false, 0);
6668

6769
// Crashes reading a nullptr.
6870
EXPECT_DEATH_IF_SUPPORTED(layer->Preroll(preroll_context(), SkMatrix()), "");
@@ -75,7 +77,10 @@ TEST_F(PictureLayerTest, SimplePicture) {
7577
const SkRect picture_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f);
7678
auto mock_picture = SkPicture::MakePlaceholder(picture_bounds);
7779
auto layer = std::make_shared<PictureLayer>(
78-
layer_offset, SkiaGPUObject(mock_picture, unref_queue()), false, false);
80+
layer_offset, SkiaGPUObject(mock_picture, unref_queue()), false, false,
81+
1000);
82+
83+
EXPECT_EQ(layer->external_size(), 1000ul);
7984

8085
layer->Preroll(preroll_context(), SkMatrix());
8186
EXPECT_EQ(layer->paint_bounds(),

flow/raster_cache.cc

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ void RasterCache::Prepare(PrerollContext* context,
141141
Entry& entry = layer_cache_[cache_key];
142142
entry.access_count++;
143143
entry.used_this_frame = true;
144+
entry.external_size = layer->external_size();
144145
if (!entry.image) {
145146
entry.image = RasterizeLayer(context, layer, ctm, checkerboard_images_);
146147
}
@@ -181,7 +182,8 @@ bool RasterCache::Prepare(GrDirectContext* context,
181182
const SkMatrix& transformation_matrix,
182183
SkColorSpace* dst_color_space,
183184
bool is_complex,
184-
bool will_change) {
185+
bool will_change,
186+
size_t external_size) {
185187
// Disabling caching when access_threshold is zero is historic behavior.
186188
if (access_threshold_ == 0) {
187189
return false;
@@ -207,6 +209,7 @@ bool RasterCache::Prepare(GrDirectContext* context,
207209

208210
// Creates an entry, if not present prior.
209211
Entry& entry = picture_cache_[cache_key];
212+
entry.external_size = external_size;
210213
if (entry.access_count < access_threshold_) {
211214
// Frame threshold has not yet been reached.
212215
return false;
@@ -260,11 +263,12 @@ bool RasterCache::Draw(const Layer* layer,
260263
return false;
261264
}
262265

263-
void RasterCache::SweepAfterFrame() {
264-
SweepOneCacheAfterFrame(picture_cache_);
265-
SweepOneCacheAfterFrame(layer_cache_);
266+
size_t RasterCache::SweepAfterFrame() {
267+
size_t removed_size = SweepOneCacheAfterFrame(picture_cache_);
268+
removed_size += SweepOneCacheAfterFrame(layer_cache_);
266269
picture_cached_this_frame_ = 0;
267270
TraceStatsToTimeline();
271+
return removed_size;
268272
}
269273

270274
void RasterCache::Clear() {

0 commit comments

Comments
 (0)