[roll] Fixes for the Flutter roll

Includes https://fuchsia-review.googlesource.com/c/topaz/+/241557

Change-Id: I96705121db9197d0dff91136464d24e4aa02b290
diff --git a/app/term/view_controller.cc b/app/term/view_controller.cc
index d6a664b..0173b85 100644
--- a/app/term/view_controller.cc
+++ b/app/term/view_controller.cc
@@ -13,6 +13,7 @@
 #include "lib/fxl/strings/string_printf.h"
 #include "lib/ui/input/cpp/formatting.h"
 #include "third_party/skia/include/core/SkFont.h"
+#include "third_party/skia/include/core/SkFontMetrics.h"
 #include "third_party/skia/include/core/SkPaint.h"
 #include "topaz/app/term/key_util.h"
 #include "topaz/app/term/pty_server.h"
diff --git a/public/lib/settings/lib/audio.pb.dart b/public/lib/settings/lib/audio.pb.dart
index 9e63f73..c41a078 100644
--- a/public/lib/settings/lib/audio.pb.dart
+++ b/public/lib/settings/lib/audio.pb.dart
@@ -1,34 +1,34 @@
 ///
 //  Generated code. Do not modify.
+//  source: audio.proto
 ///
-// ignore_for_file: non_constant_identifier_names,library_prefixes
+// ignore_for_file: non_constant_identifier_names,library_prefixes,unused_import
 
 // ignore: UNUSED_SHOWN_NAME
-import 'dart:core' show int, bool, double, String, List, override;
+import 'dart:core' show int, bool, double, String, List, Map, override;
 
-import 'package:protobuf/protobuf.dart';
+import 'package:protobuf/protobuf.dart' as $pb;
 
-class Audio extends GeneratedMessage {
-  static final BuilderInfo _i = new BuilderInfo('Audio')
-    ..a<double>(1, 'gain', PbFieldType.OD)
+class Audio extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = new $pb.BuilderInfo('Audio')
+    ..a<double>(1, 'gain', $pb.PbFieldType.OD)
     ..aOB(2, 'muted')
     ..hasRequiredFields = false
   ;
 
   Audio() : super();
-  Audio.fromBuffer(List<int> i, [ExtensionRegistry r = ExtensionRegistry.EMPTY]) : super.fromBuffer(i, r);
-  Audio.fromJson(String i, [ExtensionRegistry r = ExtensionRegistry.EMPTY]) : super.fromJson(i, r);
+  Audio.fromBuffer(List<int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) : super.fromBuffer(i, r);
+  Audio.fromJson(String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) : super.fromJson(i, r);
   Audio clone() => new Audio()..mergeFromMessage(this);
-  BuilderInfo get info_ => _i;
+  Audio copyWith(void Function(Audio) updates) => super.copyWith((message) => updates(message as Audio));
+  $pb.BuilderInfo get info_ => _i;
   static Audio create() => new Audio();
-  static PbList<Audio> createRepeated() => new PbList<Audio>();
-  static Audio getDefault() {
-    if (_defaultInstance == null) _defaultInstance = new _ReadonlyAudio();
-    return _defaultInstance;
-  }
+  Audio createEmptyInstance() => create();
+  static $pb.PbList<Audio> createRepeated() => new $pb.PbList<Audio>();
+  static Audio getDefault() => _defaultInstance ??= create()..freeze();
   static Audio _defaultInstance;
   static void $checkItem(Audio v) {
-    if (v is! Audio) checkItemFailed(v, 'Audio');
+    if (v is! Audio) $pb.checkItemFailed(v, _i.qualifiedMessageName);
   }
 
   double get gain => $_getN(0);
@@ -42,5 +42,3 @@
   void clearMuted() => clearField(2);
 }
 
-class _ReadonlyAudio extends Audio with ReadonlyMessageMixin {}
-
diff --git a/public/lib/settings/lib/audio.pbenum.dart b/public/lib/settings/lib/audio.pbenum.dart
index 325f725..4eac8b0 100644
--- a/public/lib/settings/lib/audio.pbenum.dart
+++ b/public/lib/settings/lib/audio.pbenum.dart
@@ -1,5 +1,6 @@
 ///
 //  Generated code. Do not modify.
+//  source: audio.proto
 ///
-// ignore_for_file: non_constant_identifier_names,library_prefixes
+// ignore_for_file: non_constant_identifier_names,library_prefixes,unused_import
 
diff --git a/public/lib/settings/lib/audio.pbjson.dart b/public/lib/settings/lib/audio.pbjson.dart
index c4fc531..b963c5c 100644
--- a/public/lib/settings/lib/audio.pbjson.dart
+++ b/public/lib/settings/lib/audio.pbjson.dart
@@ -1,7 +1,8 @@
 ///
 //  Generated code. Do not modify.
+//  source: audio.proto
 ///
-// ignore_for_file: non_constant_identifier_names,library_prefixes
+// ignore_for_file: non_constant_identifier_names,library_prefixes,unused_import
 
 const Audio$json = const {
   '1': 'Audio',
diff --git a/public/lib/settings/lib/audio.pbserver.dart b/public/lib/settings/lib/audio.pbserver.dart
index b57e4d2..a179994 100644
--- a/public/lib/settings/lib/audio.pbserver.dart
+++ b/public/lib/settings/lib/audio.pbserver.dart
@@ -1,7 +1,8 @@
 ///
 //  Generated code. Do not modify.
+//  source: audio.proto
 ///
-// ignore_for_file: non_constant_identifier_names,library_prefixes
+// ignore_for_file: non_constant_identifier_names,library_prefixes,unused_import
 
 export 'audio.pb.dart';
 
diff --git a/runtime/flutter_runner/vulkan_surface.h b/runtime/flutter_runner/vulkan_surface.h
index 8222d3d..8604ab9 100644
--- a/runtime/flutter_runner/vulkan_surface.h
+++ b/runtime/flutter_runner/vulkan_surface.h
@@ -11,6 +11,7 @@
 #include <array>
 #include <memory>
 
+#include "flutter/flow/raster_cache_key.h"
 #include "flutter/flow/scene_update_context.h"
 #include "flutter/fml/macros.h"
 #include "flutter/vulkan/vulkan_command_buffer.h"
@@ -115,6 +116,40 @@
   // if the swap was not successful.
   bool BindToImage(sk_sp<GrContext> context, VulkanImage vulkan_image);
 
+  // Flutter may retain a |VulkanSurface| for a |flow::Layer| subtree to improve
+  // the performance. The |retained_key_| identifies which layer subtree this
+  // |VulkanSurface| is retained for. The key has two parts. One is the pointer
+  // to the root of that layer subtree: |retained_key_.id()|. Another is the
+  // transformation matrix: |retained_key_.matrix()|. We need the matrix part
+  // because a different matrix would invalidate the pixels (raster cache) in
+  // this |VulkanSurface|.
+  const flow::LayerRasterCacheKey& GetRetainedKey() const {
+    return retained_key_;
+  }
+
+  // For better safety in retained rendering, Flutter uses a retained
+  // |EntityNode| associated with the retained surface instead of using the
+  // retained surface directly. Hence Flutter can't modify the surface during
+  // retained rendering.
+  const scenic::EntityNode& GetRetainedNode() {
+    used_in_retained_rendering_ = true;
+    return *retained_node_;
+  }
+
+  // Check whether the retained surface (and its associated |EntityNode|) is
+  // used in the current frame or not. If unused, the |VulkanSurfacePool| will
+  // try to recycle the surface. This flag is reset after each frame.
+  bool IsUsedInRetainedRendering() const { return used_in_retained_rendering_; }
+  void ResetIsUsedInRetainedRendering() { used_in_retained_rendering_ = false; }
+
+  // Let this surface own the retained EntityNode associated with it (see
+  // |GetRetainedNode|), and set the retained key (see |GetRetainedKey|).
+  void SetRetainedInfo(const flow::LayerRasterCacheKey& key,
+                       std::unique_ptr<scenic::EntityNode> node) {
+    retained_key_ = key;
+    retained_node_ = std::move(node);
+  }
+
  private:
   static constexpr int kSizeHistorySize = 4;
 
@@ -159,6 +194,12 @@
   size_t age_ = 0;
   bool valid_ = false;
 
+  flow::LayerRasterCacheKey retained_key_ =
+      {nullptr, SkMatrix::MakeScale(1, 1)};
+  std::unique_ptr<scenic::EntityNode> retained_node_ = nullptr;
+
+  std::atomic<bool> used_in_retained_rendering_ = false;
+
   FML_DISALLOW_COPY_AND_ASSIGN(VulkanSurface);
 };
 
diff --git a/runtime/flutter_runner/vulkan_surface_pool.cc b/runtime/flutter_runner/vulkan_surface_pool.cc
index 589fe57..5a0795f 100644
--- a/runtime/flutter_runner/vulkan_surface_pool.cc
+++ b/runtime/flutter_runner/vulkan_surface_pool.cc
@@ -31,7 +31,7 @@
 
 VulkanSurfacePool::~VulkanSurfacePool() {}
 
-std::unique_ptr<flow::SceneUpdateContext::SurfaceProducerSurface>
+std::unique_ptr<VulkanSurface>
 VulkanSurfacePool::AcquireSurface(const SkISize& size) {
   auto surface = GetCachedOrCreateSurface(size);
 
@@ -48,7 +48,7 @@
   return surface;
 }
 
-std::unique_ptr<flow::SceneUpdateContext::SurfaceProducerSurface>
+std::unique_ptr<VulkanSurface>
 VulkanSurfacePool::GetCachedOrCreateSurface(const SkISize& size) {
   // First try to find a surface that exactly matches |size|.
   {
@@ -121,16 +121,42 @@
     return;
   }
 
-  uintptr_t surface_key = reinterpret_cast<uintptr_t>(vulkan_surface.get());
 
-  auto insert_iterator = pending_surfaces_.insert(std::make_pair(
-      surface_key,               // key
-      std::move(vulkan_surface)  // value
-      ));
-
-  if (insert_iterator.second) {
-    insert_iterator.first->second->SignalWritesFinished(
-        std::bind(&VulkanSurfacePool::RecycleSurface, this, surface_key));
+  const flow::LayerRasterCacheKey& retained_key =
+      vulkan_surface->GetRetainedKey();
+  if (retained_key.id() != nullptr) {
+    // Add the surface to |retained_surfaces_| if its retained key has a non-
+    // null layer (|retained_key.id()|).
+    //
+    // We have to add the entry to |retained_surfaces_| map early when it's
+    // still pending (|is_pending| = true). Otherwise (if we add the surface
+    // later when |SignalRetainedReady| is called), Flutter would fail to find
+    // the retained node before the painting is done (which could take multiple
+    // frames). Flutter would then create a new |VulkanSurface| for the layer
+    // upon the failed lookup. The new |VulkanSurface| would invalidate this
+    // surface, and before the new |VulkanSurface| is done painting, another
+    // newer |VulkanSurface| is likely to be created to replace the new
+    // |VulkanSurface|. That would make the retained rendering much less useful
+    // in improving the performance.
+    auto insert_iterator = retained_surfaces_.insert(std::make_pair(
+        retained_key,
+        RetainedSurface({true, std::move(vulkan_surface)})
+    ));
+    if (insert_iterator.second) {
+      insert_iterator.first->second.vk_surface->SignalWritesFinished(
+          std::bind(&VulkanSurfacePool::SignalRetainedReady, this, retained_key));
+    }
+  } else {
+    uintptr_t surface_key = reinterpret_cast<uintptr_t>(vulkan_surface.get());
+    auto insert_iterator = pending_surfaces_.insert(std::make_pair(
+        surface_key,               // key
+        std::move(vulkan_surface)  // value
+    ));
+    if (insert_iterator.second) {
+      insert_iterator.first->second->SignalWritesFinished(
+          std::bind(&VulkanSurfacePool::RecyclePendingSurface,
+                    this, surface_key));
+    }
   }
 }
 
@@ -147,7 +173,7 @@
   return surface;
 }
 
-void VulkanSurfacePool::RecycleSurface(uintptr_t surface_key) {
+void VulkanSurfacePool::RecyclePendingSurface(uintptr_t surface_key) {
   // Before we do anything, we must clear the surface from the collection of
   // pending surfaces.
   auto found_in_pending = pending_surfaces_.find(surface_key);
@@ -160,19 +186,42 @@
   auto surface_to_recycle = std::move(found_in_pending->second);
   pending_surfaces_.erase(found_in_pending);
 
+  RecycleSurface(std::move(surface_to_recycle));
+}
+
+void VulkanSurfacePool::RecycleSurface(std::unique_ptr<VulkanSurface> surface) {
   // The surface may have become invalid (for example it the fences could
   // not be reset).
-  if (!surface_to_recycle->IsValid()) {
+  if (!surface->IsValid()) {
     return;
   }
 
   // Recycle the buffer by putting it in the list of available surfaces if we
   // have not reached the maximum amount of cached surfaces.
   if (available_surfaces_.size() < kMaxSurfaces) {
-    available_surfaces_.push_back(std::move(surface_to_recycle));
+    available_surfaces_.push_back(std::move(surface));
   }
 }
 
+void VulkanSurfacePool::RecycleRetainedSurface(
+    const flow::LayerRasterCacheKey& key) {
+  auto it = retained_surfaces_.find(key);
+  if (it == retained_surfaces_.end()) {
+    return;
+  }
+
+  // The surface should not be pending.
+  FML_DCHECK(!it->second.is_pending);
+
+  auto surface_to_recycle = std::move(it->second.vk_surface);
+  retained_surfaces_.erase(it);
+  RecycleSurface(std::move(surface_to_recycle));
+}
+
+void VulkanSurfacePool::SignalRetainedReady(flow::LayerRasterCacheKey key) {
+  retained_surfaces_[key].is_pending = false;
+}
+
 void VulkanSurfacePool::AgeAndCollectOldBuffers() {
   TRACE_DURATION("flutter", "VulkanSurfacePool::AgeAndCollectOldBuffers");
 
@@ -205,6 +254,25 @@
     }
   }
 
+  // Recycle retained surfaces that are not used and not pending in this frame.
+  //
+  // It's safe to recycle any retained surfaces that are not pending no matter
+  // whether they're used or not. Hence if there's memory pressure, feel free to
+  // recycle all retained surfaces that are not pending.
+  std::vector<flow::LayerRasterCacheKey> recycle_keys;
+  for (auto&[key, retained_surface] : retained_surfaces_) {
+    if (retained_surface.is_pending ||
+      retained_surface.vk_surface->IsUsedInRetainedRendering()) {
+      // Reset the flag for the next frame
+      retained_surface.vk_surface->ResetIsUsedInRetainedRendering();
+    } else {
+      recycle_keys.push_back(key);
+    }
+  }
+  for (auto& key : recycle_keys) {
+    RecycleRetainedSurface(key);
+  }
+
   TraceStats();
 }
 
@@ -257,6 +325,7 @@
                 "Created", trace_surfaces_created_,               //
                 "Reused", trace_surfaces_reused_,                 //
                 "PendingInCompositor", pending_surfaces_.size(),  //
+                "Retained", retained_surfaces_.size(),            //
                 "SkiaCacheResources", skia_resources,             //
                 "SkiaCacheBytes", skia_bytes,                     //
                 "SkiaCachePurgeable", skia_cache_purgeable        //
diff --git a/runtime/flutter_runner/vulkan_surface_pool.h b/runtime/flutter_runner/vulkan_surface_pool.h
index 452816d..b000963 100644
--- a/runtime/flutter_runner/vulkan_surface_pool.h
+++ b/runtime/flutter_runner/vulkan_surface_pool.h
@@ -25,8 +25,7 @@
 
   ~VulkanSurfacePool();
 
-  std::unique_ptr<flow::SceneUpdateContext::SurfaceProducerSurface>
-  AcquireSurface(const SkISize& size);
+  std::unique_ptr<VulkanSurface> AcquireSurface(const SkISize& size);
 
   void SubmitSurface(
       std::unique_ptr<flow::SceneUpdateContext::SurfaceProducerSurface>
@@ -38,22 +37,56 @@
   // small as they can be.
   void ShrinkToFit();
 
+  // For |VulkanSurfaceProducer::HasRetainedNode|.
+  bool HasRetainedNode(const flow::LayerRasterCacheKey& key) const {
+    return retained_surfaces_.find(key) != retained_surfaces_.end();
+  }
+  // For |VulkanSurfaceProducer::GetRetainedNode|.
+  const scenic::EntityNode& GetRetainedNode(
+      const flow::LayerRasterCacheKey& key)  {
+    FML_DCHECK(HasRetainedNode(key));
+    return retained_surfaces_[key].vk_surface->GetRetainedNode();
+  }
+
+
  private:
+  // Struct for retained_surfaces_ map.
+  struct RetainedSurface {
+    // If |is_pending| is true, the |vk_surface| is still under painting
+    // (similar to those in |pending_surfaces_|) so we can't recycle the
+    // |vk_surface| yet.
+    bool is_pending;
+    std::unique_ptr<VulkanSurface> vk_surface;
+  };
+
   vulkan::VulkanProvider& vulkan_provider_;
   sk_sp<GrContext> context_;
   scenic::Session* scenic_session_;
   std::vector<std::unique_ptr<VulkanSurface>> available_surfaces_;
   std::unordered_map<uintptr_t, std::unique_ptr<VulkanSurface>>
       pending_surfaces_;
+
+  // Retained surfaces keyed by the layer that created and used the surface.
+  flow::LayerRasterCacheKey::Map<RetainedSurface>
+      retained_surfaces_;
+
   size_t trace_surfaces_created_ = 0;
   size_t trace_surfaces_reused_ = 0;
 
-  std::unique_ptr<flow::SceneUpdateContext::SurfaceProducerSurface>
-  GetCachedOrCreateSurface(const SkISize& size);
+  std::unique_ptr<VulkanSurface> GetCachedOrCreateSurface(const SkISize& size);
 
   std::unique_ptr<VulkanSurface> CreateSurface(const SkISize& size);
 
-  void RecycleSurface(uintptr_t surface_key);
+  void RecycleSurface(std::unique_ptr<VulkanSurface> surface);
+
+  void RecyclePendingSurface(uintptr_t surface_key);
+
+  // Clear the |is_pending| flag of the retained surface.
+  void SignalRetainedReady(flow::LayerRasterCacheKey key);
+
+  // Remove the corresponding surface from |retained_surfaces| and recycle it.
+  // The surface must not be pending.
+  void RecycleRetainedSurface(const flow::LayerRasterCacheKey& key);
 
   void TraceStats();
 
diff --git a/runtime/flutter_runner/vulkan_surface_producer.cc b/runtime/flutter_runner/vulkan_surface_producer.cc
index d438a0f..633ca84 100644
--- a/runtime/flutter_runner/vulkan_surface_producer.cc
+++ b/runtime/flutter_runner/vulkan_surface_producer.cc
@@ -220,10 +220,15 @@
 }
 
 std::unique_ptr<flow::SceneUpdateContext::SurfaceProducerSurface>
-VulkanSurfaceProducer::ProduceSurface(const SkISize& size) {
+VulkanSurfaceProducer::ProduceSurface(
+    const SkISize& size,
+    const flow::LayerRasterCacheKey& layer_key,
+    std::unique_ptr<scenic::EntityNode> entity_node) {
   FML_DCHECK(valid_);
   last_produce_time_ = fxl::TimePoint::Now();
-  return surface_pool_->AcquireSurface(size);
+  auto surface = surface_pool_->AcquireSurface(size);
+  surface->SetRetainedInfo(layer_key, std::move(entity_node));
+  return surface;
 }
 
 void VulkanSurfaceProducer::SubmitSurface(
diff --git a/runtime/flutter_runner/vulkan_surface_producer.h b/runtime/flutter_runner/vulkan_surface_producer.h
index 00f82cb..6e4fd14 100644
--- a/runtime/flutter_runner/vulkan_surface_producer.h
+++ b/runtime/flutter_runner/vulkan_surface_producer.h
@@ -30,13 +30,26 @@
 
   // |flow::SceneUpdateContext::SurfaceProducer|
   std::unique_ptr<flow::SceneUpdateContext::SurfaceProducerSurface>
-  ProduceSurface(const SkISize& size) override;
+  ProduceSurface(const SkISize& size,
+                 const flow::LayerRasterCacheKey& layer_key,
+                 std::unique_ptr<scenic::EntityNode> entity_node) override;
 
   // |flow::SceneUpdateContext::SurfaceProducer|
   void SubmitSurface(
       std::unique_ptr<flow::SceneUpdateContext::SurfaceProducerSurface> surface)
       override;
 
+  // |flow::SceneUpdateContext::HasRetainedNode|
+  bool HasRetainedNode(const flow::LayerRasterCacheKey& key) const override {
+    return surface_pool_->HasRetainedNode(key);
+  }
+
+  // |flow::SceneUpdateContext::GetRetainedNode|
+  const scenic::EntityNode& GetRetainedNode(
+      const flow::LayerRasterCacheKey& key) override {
+    return surface_pool_->GetRetainedNode(key);
+  }
+
   void OnSurfacesPresented(
       std::vector<
           std::unique_ptr<flow::SceneUpdateContext::SurfaceProducerSurface>>
diff --git a/tools/dart_dependencies.yaml b/tools/dart_dependencies.yaml
index cb42761..8f677eb 100644
--- a/tools/dart_dependencies.yaml
+++ b/tools/dart_dependencies.yaml
@@ -14,8 +14,8 @@
   googleapis: ^0.52.0+1
   html_unescape: ^1.0.1
   markdown: ^2.0.2
-  protobuf: ^0.10.4
-  protoc_plugin: ^0.10.5
+  protobuf: ^0.13.2
+  protoc_plugin: ^15.0.3
   strings: ^0.1.0
   tuple: ^1.0.2
   uuid: ^1.0.3