Skip to content

Cache metrics include uncached pictures #92238

@flar

Description

@flar

Internal: b/204226054

The engine RasterCache contains maps with entries for all trackable pictures and layers. Most of those entries have an image associated with them, but some of them may appear in the map without an image if they represent pictures that either haven't reached their initial threshold of usage to be cached (3 frames by default), or if there were too many items cached on the frame when they were eligible and their cache entry was held up for another quieter frame.

Regardless of the reason, it might be implied that the picture and layer counts in the cache metrics represent pictures and layers that are actually cached rather than entries that are there for preliminary tracking purposes (or, if the entry represents an animating picture, it may never be cached). To that end, we should probably only return the numbers for those entries that are actually populated (i.e. they have an image representation) in the cache.

Here is an example that demonstrates the problem. It may look like a big mess, but it is 200 individual widget trees each inside a RepaintBoundary for isolation and each of which is complex enough to cache, but which is also changing on every frame. In the cache metrics in the trace you will see a count of 400 pictures "cached" with no memory being used. The reason there are twice as many cache entries as widgets animating is due to the cache containing entries for 2 frames in a row, half of which will be evicted at the end of the frame (in fact, immediately after the cache statistics are saved to the timeline trace).

(Note that on a macos desktop run there is an additional entry of about 1/3 of a megabyte for a total of 401 entries, but on a Moto G4 there are only the 400 entries and no memory consumed.)

Code sample
import 'dart:math';

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
  static Random rng = Random();
  static Color get randomColor => Color.fromARGB(rng.nextInt(256), rng.nextInt(256), rng.nextInt(256), rng.nextInt(256));

  late AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(vsync: this);
    _controller.repeat(period: const Duration(seconds: 5));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Stack(
          children: <Widget>[
            AnimatedBuilder(
              animation: _controller,
              builder: (context, child) => Center(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: List<Widget>.generate(20, (index) => Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: List<Widget>.generate(10, (index) => RepaintBoundary(
                      child: Container(
                        width: 20,
                        height: 20,
                        color: randomColor,
                        child: Center(
                          child: Column(
                            mainAxisAlignment: MainAxisAlignment.center,
                            children: List<Widget>.generate(4, (index) => Row(
                              mainAxisAlignment: MainAxisAlignment.center,
                              children: List<Widget>.generate(4, (index) => Container(
                                width: 4,
                                height: 4,
                                color: randomColor,
                              )),
                            )),
                          )
                        ),
                      ),
                    )),
                  )),
                ),
              ),
            )
          ],
        ),
      ),
    );
  }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    P0Critical issues such as a build break or regressionc: contributor-productivityTeam-specific productivity, code health, technical debt.customer: money (g3)engineflutter/engine related. See also e: labels.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions