1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "flutter/shell/platform/embedder/embedder_external_view_embedder.h"
6
7 #include <algorithm>
8
9 #include "flutter/shell/platform/embedder/embedder_render_target.h"
10
11 namespace flutter {
12
EmbedderExternalViewEmbedder(CreateRenderTargetCallback create_render_target_callback,PresentCallback present_callback)13 EmbedderExternalViewEmbedder::EmbedderExternalViewEmbedder(
14 CreateRenderTargetCallback create_render_target_callback,
15 PresentCallback present_callback)
16 : create_render_target_callback_(create_render_target_callback),
17 present_callback_(present_callback) {
18 FML_DCHECK(create_render_target_callback_);
19 FML_DCHECK(present_callback_);
20 }
21
22 EmbedderExternalViewEmbedder::~EmbedderExternalViewEmbedder() = default;
23
Reset()24 void EmbedderExternalViewEmbedder::Reset() {
25 pending_recorders_.clear();
26 pending_params_.clear();
27 composition_order_.clear();
28 }
29
30 // |ExternalViewEmbedder|
CancelFrame()31 void EmbedderExternalViewEmbedder::CancelFrame() {
32 Reset();
33 }
34
MakeBackingStoreConfig(const SkISize & size)35 static FlutterBackingStoreConfig MakeBackingStoreConfig(const SkISize& size) {
36 FlutterBackingStoreConfig config = {};
37
38 config.struct_size = sizeof(config);
39
40 config.size.width = size.width();
41 config.size.height = size.height();
42
43 return config;
44 }
45
46 // |ExternalViewEmbedder|
BeginFrame(SkISize frame_size,GrContext * context)47 void EmbedderExternalViewEmbedder::BeginFrame(SkISize frame_size,
48 GrContext* context) {
49 Reset();
50 pending_frame_size_ = frame_size;
51
52 // Decide if we want to discard the previous root render target.
53 if (root_render_target_) {
54 auto surface = root_render_target_->GetRenderSurface();
55 // This is unlikely to happen but the embedder could have given the
56 // rasterizer a render target the previous frame that Skia could not
57 // materialize into a renderable surface. Discard the target and try again.
58 if (!surface) {
59 root_render_target_ = nullptr;
60 } else {
61 auto last_surface_size =
62 SkISize::Make(surface->width(), surface->height());
63 if (pending_frame_size_ != last_surface_size) {
64 root_render_target_ = nullptr;
65 }
66 }
67 }
68
69 // If there is no root render target, create one now. This will be accessed by
70 // the rasterizer before the submit call layer to access the surface surface
71 // canvas.
72 if (!root_render_target_) {
73 root_render_target_ = create_render_target_callback_(
74 context, MakeBackingStoreConfig(pending_frame_size_));
75 }
76 }
77
78 // |ExternalViewEmbedder|
PrerollCompositeEmbeddedView(int view_id,std::unique_ptr<EmbeddedViewParams> params)79 void EmbedderExternalViewEmbedder::PrerollCompositeEmbeddedView(
80 int view_id,
81 std::unique_ptr<EmbeddedViewParams> params) {
82 FML_DCHECK(pending_recorders_.count(view_id) == 0);
83 FML_DCHECK(pending_params_.count(view_id) == 0);
84 FML_DCHECK(std::find(composition_order_.begin(), composition_order_.end(),
85 view_id) == composition_order_.end());
86
87 pending_recorders_[view_id] = std::make_unique<SkPictureRecorder>();
88 pending_params_[view_id] = *params;
89 composition_order_.push_back(view_id);
90 }
91
92 // |ExternalViewEmbedder|
GetCurrentCanvases()93 std::vector<SkCanvas*> EmbedderExternalViewEmbedder::GetCurrentCanvases() {
94 std::vector<SkCanvas*> canvases;
95 for (const auto& recorder : pending_recorders_) {
96 canvases.push_back(recorder.second->beginRecording(
97 pending_frame_size_.width(), pending_frame_size_.height()));
98 }
99 return canvases;
100 }
101
102 // |ExternalViewEmbedder|
CompositeEmbeddedView(int view_id)103 SkCanvas* EmbedderExternalViewEmbedder::CompositeEmbeddedView(int view_id) {
104 auto found = pending_recorders_.find(view_id);
105 if (found == pending_recorders_.end()) {
106 FML_DCHECK(false) << "Attempted to composite a view that was not "
107 "pre-rolled.";
108 return nullptr;
109 }
110 return found->second->getRecordingCanvas();
111 }
112
MakeLayer(const SkISize & frame_size,const FlutterBackingStore * store)113 static FlutterLayer MakeLayer(const SkISize& frame_size,
114 const FlutterBackingStore* store) {
115 FlutterLayer layer = {};
116
117 layer.struct_size = sizeof(layer);
118 layer.type = kFlutterLayerContentTypeBackingStore;
119 layer.backing_store = store;
120
121 layer.offset.x = 0.0;
122 layer.offset.y = 0.0;
123
124 layer.size.width = frame_size.width();
125 layer.size.height = frame_size.height();
126
127 return layer;
128 }
129
MakePlatformView(FlutterPlatformViewIdentifier identifier)130 static FlutterPlatformView MakePlatformView(
131 FlutterPlatformViewIdentifier identifier) {
132 FlutterPlatformView view = {};
133
134 view.struct_size = sizeof(view);
135
136 view.identifier = identifier;
137
138 return view;
139 }
140
MakeLayer(const EmbeddedViewParams & params,const FlutterPlatformView & platform_view)141 static FlutterLayer MakeLayer(const EmbeddedViewParams& params,
142 const FlutterPlatformView& platform_view) {
143 FlutterLayer layer = {};
144
145 layer.struct_size = sizeof(layer);
146 layer.type = kFlutterLayerContentTypePlatformView;
147 layer.platform_view = &platform_view;
148
149 layer.offset.x = params.offsetPixels.x();
150 layer.offset.y = params.offsetPixels.y();
151
152 layer.size.width = params.sizePoints.width();
153 layer.size.height = params.sizePoints.height();
154
155 return layer;
156 }
157
158 // |ExternalViewEmbedder|
SubmitFrame(GrContext * context)159 bool EmbedderExternalViewEmbedder::SubmitFrame(GrContext* context) {
160 std::map<FlutterPlatformViewIdentifier, FlutterPlatformView>
161 presented_platform_views;
162 // Layers may contain pointers to platform views in the collection above.
163 std::vector<FlutterLayer> presented_layers;
164 Registry render_targets_used;
165
166 if (!root_render_target_) {
167 FML_LOG(ERROR)
168 << "Could not acquire the root render target from the embedder.";
169 return false;
170 }
171
172 {
173 // The root surface is expressed as a layer.
174 EmbeddedViewParams params;
175 params.offsetPixels = SkPoint::Make(0, 0);
176 params.sizePoints = pending_frame_size_;
177 presented_layers.push_back(
178 MakeLayer(pending_frame_size_, root_render_target_->GetBackingStore()));
179 }
180
181 for (const auto& view_id : composition_order_) {
182 FML_DCHECK(pending_recorders_.count(view_id) == 1);
183 FML_DCHECK(pending_params_.count(view_id) == 1);
184
185 const auto& params = pending_params_.at(view_id);
186 auto& recorder = pending_recorders_.at(view_id);
187
188 auto picture = recorder->finishRecordingAsPicture();
189 if (!picture) {
190 FML_LOG(ERROR) << "Could not finish recording into the picture before "
191 "on-screen composition.";
192 return false;
193 }
194
195 const auto backing_store_config =
196 MakeBackingStoreConfig(pending_frame_size_);
197
198 RegistryKey registry_key(view_id, backing_store_config);
199
200 auto found_render_target = registry_.find(registry_key);
201
202 // Find a cached render target in the registry. If none exists, ask the
203 // embedder for a new one.
204 std::shared_ptr<EmbedderRenderTarget> render_target;
205 if (found_render_target == registry_.end()) {
206 render_target =
207 create_render_target_callback_(context, backing_store_config);
208 } else {
209 render_target = found_render_target->second;
210 }
211
212 if (!render_target) {
213 FML_LOG(ERROR) << "Could not acquire external render target for "
214 "on-screen composition.";
215 return false;
216 }
217
218 render_targets_used[registry_key] = render_target;
219
220 auto render_surface = render_target->GetRenderSurface();
221 auto render_canvas = render_surface ? render_surface->getCanvas() : nullptr;
222
223 if (!render_canvas) {
224 FML_LOG(ERROR)
225 << "Could not acquire render canvas for on-screen rendering.";
226 return false;
227 }
228
229 render_canvas->clear(SK_ColorTRANSPARENT);
230 render_canvas->drawPicture(picture);
231 render_canvas->flush();
232
233 // Indicate a layer for the platform view. Add to `presented_platform_views`
234 // in order to keep at allocated just for the scope of the current method.
235 // The layers presented to the embedder will contain a back pointer to this
236 // struct. It is safe to deallocate when the embedder callback is done.
237 presented_platform_views[view_id] = MakePlatformView(view_id);
238 presented_layers.push_back(
239 MakeLayer(params, presented_platform_views.at(view_id)));
240
241 // Indicate a layer for the backing store containing contents rendered by
242 // Flutter.
243 presented_layers.push_back(
244 MakeLayer(pending_frame_size_, render_target->GetBackingStore()));
245 }
246
247 {
248 std::vector<const FlutterLayer*> presented_layers_pointers;
249 presented_layers_pointers.reserve(presented_layers.size());
250 for (const auto& layer : presented_layers) {
251 presented_layers_pointers.push_back(&layer);
252 }
253 present_callback_(std::move(presented_layers_pointers));
254 }
255
256 registry_ = std::move(render_targets_used);
257
258 return true;
259 }
260
261 // |ExternalViewEmbedder|
GetRootSurface()262 sk_sp<SkSurface> EmbedderExternalViewEmbedder::GetRootSurface() {
263 return root_render_target_ ? root_render_target_->GetRenderSurface()
264 : nullptr;
265 }
266
267 } // namespace flutter
268