• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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