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/tests/embedder_test_compositor.h"
6
7 #include "flutter/fml/logging.h"
8 #include "flutter/shell/platform/embedder/tests/embedder_assertions.h"
9 #include "third_party/skia/include/core/SkSurface.h"
10
11 namespace flutter {
12 namespace testing {
13
EmbedderTestCompositor(sk_sp<GrContext> context)14 EmbedderTestCompositor::EmbedderTestCompositor(sk_sp<GrContext> context)
15 : context_(context) {
16 FML_CHECK(context_);
17 }
18
19 EmbedderTestCompositor::~EmbedderTestCompositor() = default;
20
SetRenderTargetType(RenderTargetType type)21 void EmbedderTestCompositor::SetRenderTargetType(RenderTargetType type) {
22 type_ = type;
23 }
24
CreateBackingStore(const FlutterBackingStoreConfig * config,FlutterBackingStore * backing_store_out)25 bool EmbedderTestCompositor::CreateBackingStore(
26 const FlutterBackingStoreConfig* config,
27 FlutterBackingStore* backing_store_out) {
28 switch (type_) {
29 case RenderTargetType::kOpenGLFramebuffer:
30 return CreateFramebufferRenderSurface(config, backing_store_out);
31 case RenderTargetType::kOpenGLTexture:
32 return CreateTextureRenderSurface(config, backing_store_out);
33 case RenderTargetType::kSoftwareBuffer:
34 return CreateSoftwareRenderSurface(config, backing_store_out);
35 }
36 FML_CHECK(false);
37 return false;
38 }
39
CollectBackingStore(const FlutterBackingStore * backing_store)40 bool EmbedderTestCompositor::CollectBackingStore(
41 const FlutterBackingStore* backing_store) {
42 // We have already set the destruction callback for the various backing
43 // stores. Our user_data is just the canvas from that backing store and does
44 // not need to be explicitly collected. Embedders might have some other state
45 // they want to collect though.
46 return true;
47 }
48
UpdateOffscrenComposition(const FlutterLayer ** layers,size_t layers_count)49 bool EmbedderTestCompositor::UpdateOffscrenComposition(
50 const FlutterLayer** layers,
51 size_t layers_count) {
52 last_composition_ = nullptr;
53
54 auto surface_size = SkISize::Make(800, 600);
55
56 auto surface = SkSurface::MakeRenderTarget(
57 context_.get(), // context
58 SkBudgeted::kNo, // budgeted
59 SkImageInfo::MakeN32Premul(surface_size), // image info
60 1, // sample count
61 kTopLeft_GrSurfaceOrigin, // surface origin
62 nullptr, // surface properties
63 false // create mipmaps
64 );
65
66 if (!surface) {
67 FML_LOG(ERROR) << "Could not update the off-screen composition.";
68 return false;
69 }
70
71 auto canvas = surface->getCanvas();
72
73 // This has to be transparent because we are going to be compositing this
74 // sub-hierarchy onto the on-screen surface.
75 canvas->clear(SK_ColorTRANSPARENT);
76
77 for (size_t i = 0; i < layers_count; ++i) {
78 const auto* layer = layers[i];
79
80 sk_sp<SkImage> platform_renderered_contents;
81
82 sk_sp<SkImage> layer_image;
83 SkIPoint canvas_offset = SkIPoint::Make(0, 0);
84
85 switch (layer->type) {
86 case kFlutterLayerContentTypeBackingStore:
87 layer_image =
88 reinterpret_cast<SkSurface*>(layer->backing_store->user_data)
89 ->makeImageSnapshot();
90
91 break;
92 case kFlutterLayerContentTypePlatformView:
93 layer_image =
94 platform_view_renderer_callback_
95 ? platform_view_renderer_callback_(*layer, context_.get())
96 : nullptr;
97 canvas_offset = SkIPoint::Make(layer->offset.x, layer->offset.y);
98 break;
99 };
100
101 if (!layer_image && layer->type != kFlutterLayerContentTypePlatformView) {
102 FML_LOG(ERROR) << "Could not snapshot layer in test compositor: "
103 << *layer;
104 return false;
105 }
106
107 // The image rendered by Flutter already has the correct offset and
108 // transformation applied. The layers offset is meant for the platform.
109 canvas->drawImage(layer_image.get(), canvas_offset.x(), canvas_offset.y());
110 }
111
112 last_composition_ = surface->makeImageSnapshot();
113
114 if (!last_composition_) {
115 FML_LOG(ERROR) << "Could not update the contents of the sub-composition.";
116 return false;
117 }
118
119 if (next_scene_callback_) {
120 auto last_composition_snapshot = last_composition_->makeRasterImage();
121 FML_CHECK(last_composition_snapshot);
122 auto callback = next_scene_callback_;
123 next_scene_callback_ = nullptr;
124 callback(std::move(last_composition_snapshot));
125 }
126
127 return true;
128 }
129
GetLastComposition()130 sk_sp<SkImage> EmbedderTestCompositor::GetLastComposition() {
131 return last_composition_;
132 }
133
Present(const FlutterLayer ** layers,size_t layers_count)134 bool EmbedderTestCompositor::Present(const FlutterLayer** layers,
135 size_t layers_count) {
136 if (!UpdateOffscrenComposition(layers, layers_count)) {
137 FML_LOG(ERROR)
138 << "Could not update the off-screen composition in the test compositor";
139 return false;
140 }
141
142 // If the test has asked to access the layers and renderers being presented.
143 // Access the same and present it to the test for its test assertions.
144 if (next_present_callback_) {
145 auto callback = next_present_callback_;
146 next_present_callback_ = nullptr;
147 callback(layers, layers_count);
148 }
149
150 return true;
151 }
152
CreateFramebufferRenderSurface(const FlutterBackingStoreConfig * config,FlutterBackingStore * backing_store_out)153 bool EmbedderTestCompositor::CreateFramebufferRenderSurface(
154 const FlutterBackingStoreConfig* config,
155 FlutterBackingStore* backing_store_out) {
156 const auto image_info =
157 SkImageInfo::MakeN32Premul(config->size.width, config->size.height);
158
159 auto surface =
160 SkSurface::MakeRenderTarget(context_.get(), // context
161 SkBudgeted::kNo, // budgeted
162 image_info, // image info
163 1, // sample count
164 kTopLeft_GrSurfaceOrigin, // surface origin
165 nullptr, // surface properties
166 false // mipmaps
167
168 );
169
170 if (!surface) {
171 FML_LOG(ERROR) << "Could not create render target for compositor layer.";
172 return false;
173 }
174
175 GrBackendRenderTarget render_target = surface->getBackendRenderTarget(
176 SkSurface::BackendHandleAccess::kDiscardWrite_BackendHandleAccess);
177
178 if (!render_target.isValid()) {
179 FML_LOG(ERROR) << "Backend render target was invalid.";
180 return false;
181 }
182
183 GrGLFramebufferInfo framebuffer_info = {};
184 if (!render_target.getGLFramebufferInfo(&framebuffer_info)) {
185 FML_LOG(ERROR) << "Could not access backend framebuffer info.";
186 return false;
187 }
188
189 backing_store_out->type = kFlutterBackingStoreTypeOpenGL;
190 backing_store_out->user_data = surface.get();
191 backing_store_out->open_gl.type = kFlutterOpenGLTargetTypeFramebuffer;
192 backing_store_out->open_gl.framebuffer.target = framebuffer_info.fFormat;
193 backing_store_out->open_gl.framebuffer.name = framebuffer_info.fFBOID;
194 // The balancing unref is in the destruction callback.
195 surface->ref();
196 backing_store_out->open_gl.framebuffer.user_data = surface.get();
197 backing_store_out->open_gl.framebuffer.destruction_callback =
198 [](void* user_data) { reinterpret_cast<SkSurface*>(user_data)->unref(); };
199
200 return true;
201 }
202
CreateTextureRenderSurface(const FlutterBackingStoreConfig * config,FlutterBackingStore * backing_store_out)203 bool EmbedderTestCompositor::CreateTextureRenderSurface(
204 const FlutterBackingStoreConfig* config,
205 FlutterBackingStore* backing_store_out) {
206 const auto image_info =
207 SkImageInfo::MakeN32Premul(config->size.width, config->size.height);
208
209 auto surface =
210 SkSurface::MakeRenderTarget(context_.get(), // context
211 SkBudgeted::kNo, // budgeted
212 image_info, // image info
213 1, // sample count
214 kTopLeft_GrSurfaceOrigin, // surface origin
215 nullptr, // surface properties
216 false // mipmaps
217
218 );
219
220 if (!surface) {
221 FML_LOG(ERROR) << "Could not create render target for compositor layer.";
222 return false;
223 }
224
225 GrBackendTexture render_texture = surface->getBackendTexture(
226 SkSurface::BackendHandleAccess::kDiscardWrite_BackendHandleAccess);
227
228 if (!render_texture.isValid()) {
229 FML_LOG(ERROR) << "Backend render texture was invalid.";
230 return false;
231 }
232
233 GrGLTextureInfo texture_info = {};
234 if (!render_texture.getGLTextureInfo(&texture_info)) {
235 FML_LOG(ERROR) << "Could not access backend texture info.";
236 return false;
237 }
238
239 backing_store_out->type = kFlutterBackingStoreTypeOpenGL;
240 backing_store_out->user_data = surface.get();
241 backing_store_out->open_gl.type = kFlutterOpenGLTargetTypeTexture;
242 backing_store_out->open_gl.texture.target = texture_info.fTarget;
243 backing_store_out->open_gl.texture.name = texture_info.fID;
244 backing_store_out->open_gl.texture.format = texture_info.fFormat;
245 // The balancing unref is in the destruction callback.
246 surface->ref();
247 backing_store_out->open_gl.texture.user_data = surface.get();
248 backing_store_out->open_gl.texture.destruction_callback =
249 [](void* user_data) { reinterpret_cast<SkSurface*>(user_data)->unref(); };
250
251 return true;
252 }
253
CreateSoftwareRenderSurface(const FlutterBackingStoreConfig * config,FlutterBackingStore * backing_store_out)254 bool EmbedderTestCompositor::CreateSoftwareRenderSurface(
255 const FlutterBackingStoreConfig* config,
256 FlutterBackingStore* backing_store_out) {
257 auto surface = SkSurface::MakeRaster(
258 SkImageInfo::MakeN32Premul(config->size.width, config->size.height));
259
260 if (!surface) {
261 FML_LOG(ERROR)
262 << "Could not create the render target for compositor layer.";
263 return false;
264 }
265
266 SkPixmap pixmap;
267 if (!surface->peekPixels(&pixmap)) {
268 FML_LOG(ERROR) << "Could not peek pixels of pixmap.";
269 return false;
270 }
271
272 backing_store_out->type = kFlutterBackingStoreTypeSoftware;
273 backing_store_out->user_data = surface.get();
274 backing_store_out->software.allocation = pixmap.addr();
275 backing_store_out->software.row_bytes = pixmap.rowBytes();
276 backing_store_out->software.height = pixmap.height();
277 // The balancing unref is in the destruction callback.
278 surface->ref();
279 backing_store_out->software.user_data = surface.get();
280 backing_store_out->software.destruction_callback = [](void* user_data) {
281 reinterpret_cast<SkSurface*>(user_data)->unref();
282 };
283
284 return true;
285 }
286
SetNextPresentCallback(PresentCallback next_present_callback)287 void EmbedderTestCompositor::SetNextPresentCallback(
288 PresentCallback next_present_callback) {
289 FML_CHECK(!next_present_callback_);
290 next_present_callback_ = next_present_callback;
291 }
292
SetNextSceneCallback(NextSceneCallback next_scene_callback)293 void EmbedderTestCompositor::SetNextSceneCallback(
294 NextSceneCallback next_scene_callback) {
295 FML_CHECK(!next_scene_callback_);
296 next_scene_callback_ = next_scene_callback;
297 }
298
SetPlatformViewRendererCallback(PlatformViewRendererCallback callback)299 void EmbedderTestCompositor::SetPlatformViewRendererCallback(
300 PlatformViewRendererCallback callback) {
301 platform_view_renderer_callback_ = callback;
302 }
303
304 } // namespace testing
305 } // namespace flutter
306