Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
embedder_gl_unittests.cc
Go to the documentation of this file.
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
6#define FML_USED_ON_EMBEDDER
7
8#include <atomic>
9#include <string>
10#include <vector>
11
12#include "GLES3/gl3.h"
15#include "flutter/fml/file.h"
17#include "flutter/fml/mapping.h"
21#include "flutter/fml/paths.h"
25#include "flutter/fml/thread.h"
37#include "third_party/skia/include/core/SkSurface.h"
39
40// CREATE_NATIVE_ENTRY is leaky by design
41// NOLINTBEGIN(clang-analyzer-core.StackAddressEscape)
42
43namespace flutter::testing {
44
46
47TEST_F(EmbedderTest, CanCreateOpenGLRenderingEngine) {
48 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
49 EmbedderConfigBuilder builder(context);
50 builder.SetSurface(DlISize(1, 1));
51 auto engine = builder.LaunchEngine();
52 ASSERT_TRUE(engine.is_valid());
53}
54
55//------------------------------------------------------------------------------
56/// If an incorrectly configured compositor is set on the engine, the engine
57/// must fail to launch instead of failing to render a frame at a later point in
58/// time.
59///
61 MustPreventEngineLaunchWhenRequiredCompositorArgsAreAbsent) {
62 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
63 EmbedderConfigBuilder builder(context);
64 builder.SetSurface(DlISize(1, 1));
65 builder.SetCompositor();
66 builder.GetCompositor().create_backing_store_callback = nullptr;
67 builder.GetCompositor().collect_backing_store_callback = nullptr;
68 builder.GetCompositor().present_layers_callback = nullptr;
69 builder.GetCompositor().present_view_callback = nullptr;
70 auto engine = builder.LaunchEngine();
71 ASSERT_FALSE(engine.is_valid());
72}
73
74//------------------------------------------------------------------------------
75/// Either present_layers_callback or present_view_callback must be provided,
76/// but not both, otherwise the engine must fail to launch instead of failing to
77/// render a frame at a later point in time.
78///
79TEST_F(EmbedderTest, LaunchFailsWhenMultiplePresentCallbacks) {
80 auto& context = GetEmbedderContext<EmbedderTestContextSoftware>();
81 EmbedderConfigBuilder builder(context);
82 builder.SetSurface(DlISize(1, 1));
83 builder.SetCompositor();
84 builder.GetCompositor().present_layers_callback =
85 [](const FlutterLayer** layers, size_t layers_count, void* user_data) {
86 return true;
87 };
88 builder.GetCompositor().present_view_callback =
89 [](const FlutterPresentViewInfo* info) { return true; };
90 auto engine = builder.LaunchEngine();
91 ASSERT_FALSE(engine.is_valid());
92}
93
94//------------------------------------------------------------------------------
95/// Must be able to render to a custom compositor whose render targets are fully
96/// complete OpenGL textures.
97///
98TEST_F(EmbedderTest, CompositorMustBeAbleToRenderToOpenGLFramebuffer) {
99 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
100
101 EmbedderConfigBuilder builder(context);
102 builder.SetSurface(DlISize(800, 600));
103 builder.SetCompositor();
104 builder.SetDartEntrypoint("can_composite_platform_views");
105
106 builder.SetRenderTargetType(
108
109 fml::CountDownLatch latch(3);
110 context.GetCompositor().SetNextPresentCallback(
112 size_t layers_count) {
113 ASSERT_EQ(layers_count, 3u);
114
115 {
116 FlutterBackingStore backing_store = *layers[0]->backing_store;
117 backing_store.struct_size = sizeof(backing_store);
118 backing_store.type = kFlutterBackingStoreTypeOpenGL;
119 backing_store.did_update = true;
121
122 FlutterRect paint_region_rects[] = {
123 FlutterRectMakeLTRB(0, 0, 800, 600),
124 };
125 FlutterRegion paint_region = {
126 .struct_size = sizeof(FlutterRegion),
127 .rects_count = 1,
128 .rects = paint_region_rects,
129 };
130 FlutterBackingStorePresentInfo present_info = {
132 .paint_region = &paint_region,
133 };
134
135 FlutterLayer layer = {};
136 layer.struct_size = sizeof(layer);
138 layer.backing_store = &backing_store;
139 layer.size = FlutterSizeMake(800.0, 600.0);
140 layer.offset = FlutterPointMake(0, 0);
141 layer.backing_store_present_info = &present_info;
142
143 ASSERT_EQ(*layers[0], layer);
144 }
145
146 {
149 platform_view.identifier = 42;
150
151 FlutterLayer layer = {};
152 layer.struct_size = sizeof(layer);
155 layer.size = FlutterSizeMake(123.0, 456.0);
156 layer.offset = FlutterPointMake(1.0, 2.0);
157
158 ASSERT_EQ(*layers[1], layer);
159 }
160
161 {
162 FlutterBackingStore backing_store = *layers[2]->backing_store;
163 backing_store.struct_size = sizeof(backing_store);
164 backing_store.type = kFlutterBackingStoreTypeOpenGL;
165 backing_store.did_update = true;
167
168 FlutterRect paint_region_rects[] = {
169 FlutterRectMakeLTRB(2, 3, 800, 600),
170 };
171 FlutterRegion paint_region = {
172 .struct_size = sizeof(FlutterRegion),
173 .rects_count = 1,
174 .rects = paint_region_rects,
175 };
176 FlutterBackingStorePresentInfo present_info = {
178 .paint_region = &paint_region,
179 };
180
181 FlutterLayer layer = {};
182 layer.struct_size = sizeof(layer);
184 layer.backing_store = &backing_store;
185 layer.size = FlutterSizeMake(800.0, 600.0);
186 layer.offset = FlutterPointMake(0.0, 0.0);
187 layer.backing_store_present_info = &present_info;
188
189 ASSERT_EQ(*layers[2], layer);
190 }
191
192 latch.CountDown();
193 });
194
195 context.AddNativeCallback(
196 "SignalNativeTest",
198 [&latch](Dart_NativeArguments args) { latch.CountDown(); }));
199
200 auto engine = builder.LaunchEngine();
201
202 // Send a window metrics events so frames may be scheduled.
203 FlutterWindowMetricsEvent event = {};
204 event.struct_size = sizeof(event);
205 event.width = 800;
206 event.height = 600;
207 event.pixel_ratio = 1.0;
208 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
209 kSuccess);
210 ASSERT_TRUE(engine.is_valid());
211
212 latch.Wait();
213}
214
215//------------------------------------------------------------------------------
216/// Layers in a hierarchy containing a platform view should not be cached. The
217/// other layers in the hierarchy should be, however.
218TEST_F(EmbedderTest, RasterCacheDisabledWithPlatformViews) {
219 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
220
221 EmbedderConfigBuilder builder(context);
222 builder.SetSurface(DlISize(800, 600));
223 builder.SetCompositor();
224 builder.SetDartEntrypoint("can_composite_platform_views_with_opacity");
225
226 builder.SetRenderTargetType(
228
229 fml::CountDownLatch setup(3);
230 fml::CountDownLatch verify(1);
231
232 context.GetCompositor().SetNextPresentCallback(
234 size_t layers_count) {
235 ASSERT_EQ(layers_count, 3u);
236
237 {
238 FlutterBackingStore backing_store = *layers[0]->backing_store;
239 backing_store.struct_size = sizeof(backing_store);
240 backing_store.type = kFlutterBackingStoreTypeOpenGL;
241 backing_store.did_update = true;
243
244 FlutterRect paint_region_rects[] = {
245 FlutterRectMakeLTRB(0, 0, 800, 600),
246 };
247 FlutterRegion paint_region = {
248 .struct_size = sizeof(FlutterRegion),
249 .rects_count = 1,
250 .rects = paint_region_rects,
251 };
252 FlutterBackingStorePresentInfo present_info = {
254 .paint_region = &paint_region,
255 };
256
257 FlutterLayer layer = {};
258 layer.struct_size = sizeof(layer);
260 layer.backing_store = &backing_store;
261 layer.size = FlutterSizeMake(800.0, 600.0);
262 layer.offset = FlutterPointMake(0, 0);
263 layer.backing_store_present_info = &present_info;
264
265 ASSERT_EQ(*layers[0], layer);
266 }
267
268 {
271 platform_view.identifier = 42;
272
273 FlutterLayer layer = {};
274 layer.struct_size = sizeof(layer);
277 layer.size = FlutterSizeMake(123.0, 456.0);
278 layer.offset = FlutterPointMake(1.0, 2.0);
279
280 ASSERT_EQ(*layers[1], layer);
281 }
282
283 {
284 FlutterBackingStore backing_store = *layers[2]->backing_store;
285 backing_store.struct_size = sizeof(backing_store);
286 backing_store.type = kFlutterBackingStoreTypeOpenGL;
287 backing_store.did_update = true;
289
290 FlutterRect paint_region_rects[] = {
291 FlutterRectMakeLTRB(3, 3, 800, 600),
292 };
293 FlutterRegion paint_region = {
294 .struct_size = sizeof(FlutterRegion),
295 .rects_count = 1,
296 .rects = paint_region_rects,
297 };
298 FlutterBackingStorePresentInfo present_info = {
300 .paint_region = &paint_region,
301 };
302
303 FlutterLayer layer = {};
304 layer.struct_size = sizeof(layer);
306 layer.backing_store = &backing_store;
307 layer.size = FlutterSizeMake(800.0, 600.0);
308 layer.offset = FlutterPointMake(0.0, 0.0);
309 layer.backing_store_present_info = &present_info;
310
311 ASSERT_EQ(*layers[2], layer);
312 }
313
314 setup.CountDown();
315 });
316
317 context.AddNativeCallback(
318 "SignalNativeTest",
320 [&setup](Dart_NativeArguments args) { setup.CountDown(); }));
321
322 UniqueEngine engine = builder.LaunchEngine();
323
324 // Send a window metrics events so frames may be scheduled.
325 FlutterWindowMetricsEvent event = {};
326 event.struct_size = sizeof(event);
327 event.width = 800;
328 event.height = 600;
329 event.pixel_ratio = 1.0;
330 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
331 kSuccess);
332 ASSERT_TRUE(engine.is_valid());
333
334 setup.Wait();
335 const flutter::Shell& shell = ToEmbedderEngine(engine.get())->GetShell();
337 const flutter::RasterCache& raster_cache =
338 shell.GetRasterizer()->compositor_context()->raster_cache();
339 // 3 layers total, but one of them had the platform view. So the cache
340 // should only have 2 entries.
341 ASSERT_EQ(raster_cache.GetCachedEntriesCount(), 2u);
342 verify.CountDown();
343 });
344
345 verify.Wait();
346}
347
348//------------------------------------------------------------------------------
349/// The RasterCache should normally be enabled.
350///
351TEST_F(EmbedderTest, RasterCacheEnabled) {
352 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
353
354 EmbedderConfigBuilder builder(context);
355 builder.SetSurface(DlISize(800, 600));
356 builder.SetCompositor();
357 builder.SetDartEntrypoint("can_composite_with_opacity");
358
359 builder.SetRenderTargetType(
361
362 fml::CountDownLatch setup(3);
363 fml::CountDownLatch verify(1);
364
365 context.GetCompositor().SetNextPresentCallback(
367 size_t layers_count) {
368 ASSERT_EQ(layers_count, 1u);
369
370 {
371 FlutterBackingStore backing_store = *layers[0]->backing_store;
372 backing_store.struct_size = sizeof(backing_store);
373 backing_store.type = kFlutterBackingStoreTypeOpenGL;
374 backing_store.did_update = true;
376
377 FlutterRect paint_region_rects[] = {
378 FlutterRectMakeLTRB(0, 0, 800, 600),
379 };
380 FlutterRegion paint_region = {
381 .struct_size = sizeof(FlutterRegion),
382 .rects_count = 1,
383 .rects = paint_region_rects,
384 };
385 FlutterBackingStorePresentInfo present_info = {
387 .paint_region = &paint_region,
388 };
389
390 FlutterLayer layer = {};
391 layer.struct_size = sizeof(layer);
393 layer.backing_store = &backing_store;
394 layer.size = FlutterSizeMake(800.0, 600.0);
395 layer.offset = FlutterPointMake(0, 0);
396 layer.backing_store_present_info = &present_info;
397
398 ASSERT_EQ(*layers[0], layer);
399 }
400
401 setup.CountDown();
402 });
403
404 context.AddNativeCallback(
405 "SignalNativeTest",
407 [&setup](Dart_NativeArguments args) { setup.CountDown(); }));
408
409 UniqueEngine engine = builder.LaunchEngine();
410
411 // Send a window metrics events so frames may be scheduled.
412 FlutterWindowMetricsEvent event = {};
413 event.struct_size = sizeof(event);
414 event.width = 800;
415 event.height = 600;
416 event.pixel_ratio = 1.0;
417 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
418 kSuccess);
419 ASSERT_TRUE(engine.is_valid());
420
421 setup.Wait();
422 const flutter::Shell& shell = ToEmbedderEngine(engine.get())->GetShell();
424 const flutter::RasterCache& raster_cache =
425 shell.GetRasterizer()->compositor_context()->raster_cache();
426 ASSERT_EQ(raster_cache.GetCachedEntriesCount(), 1u);
427 verify.CountDown();
428 });
429
430 verify.Wait();
431}
432
433//------------------------------------------------------------------------------
434/// Must be able to render using a custom compositor whose render targets for
435/// the individual layers are OpenGL textures.
436///
437TEST_F(EmbedderTest, CompositorMustBeAbleToRenderToOpenGLTexture) {
438 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
439
440 EmbedderConfigBuilder builder(context);
441 builder.SetSurface(DlISize(800, 600));
442 builder.SetCompositor();
443 builder.SetDartEntrypoint("can_composite_platform_views");
444
445 builder.SetRenderTargetType(
447
448 fml::CountDownLatch latch(3);
449 context.GetCompositor().SetNextPresentCallback(
451 size_t layers_count) {
452 ASSERT_EQ(layers_count, 3u);
453
454 {
455 FlutterBackingStore backing_store = *layers[0]->backing_store;
456 backing_store.struct_size = sizeof(backing_store);
457 backing_store.type = kFlutterBackingStoreTypeOpenGL;
458 backing_store.did_update = true;
460
461 FlutterRect paint_region_rects[] = {
462 FlutterRectMakeLTRB(0, 0, 800, 600),
463 };
464 FlutterRegion paint_region = {
465 .struct_size = sizeof(FlutterRegion),
466 .rects_count = 1,
467 .rects = paint_region_rects,
468 };
469 FlutterBackingStorePresentInfo present_info = {
471 .paint_region = &paint_region,
472 };
473
474 FlutterLayer layer = {};
475 layer.struct_size = sizeof(layer);
477 layer.backing_store = &backing_store;
478 layer.size = FlutterSizeMake(800.0, 600.0);
479 layer.offset = FlutterPointMake(0, 0);
480 layer.backing_store_present_info = &present_info;
481
482 ASSERT_EQ(*layers[0], layer);
483 }
484
485 {
488 platform_view.identifier = 42;
489
490 FlutterLayer layer = {};
491 layer.struct_size = sizeof(layer);
494 layer.size = FlutterSizeMake(123.0, 456.0);
495 layer.offset = FlutterPointMake(1.0, 2.0);
496
497 ASSERT_EQ(*layers[1], layer);
498 }
499
500 {
501 FlutterBackingStore backing_store = *layers[2]->backing_store;
502 backing_store.struct_size = sizeof(backing_store);
503 backing_store.type = kFlutterBackingStoreTypeOpenGL;
504 backing_store.did_update = true;
506
507 FlutterRect paint_region_rects[] = {
508 FlutterRectMakeLTRB(2, 3, 800, 600),
509 };
510 FlutterRegion paint_region = {
511 .struct_size = sizeof(FlutterRegion),
512 .rects_count = 1,
513 .rects = paint_region_rects,
514 };
515 FlutterBackingStorePresentInfo present_info = {
517 .paint_region = &paint_region,
518 };
519
520 FlutterLayer layer = {};
521 layer.struct_size = sizeof(layer);
523 layer.backing_store = &backing_store;
524 layer.size = FlutterSizeMake(800.0, 600.0);
525 layer.offset = FlutterPointMake(0.0, 0.0);
526 layer.backing_store_present_info = &present_info;
527
528 ASSERT_EQ(*layers[2], layer);
529 }
530
531 latch.CountDown();
532 });
533
534 context.AddNativeCallback(
535 "SignalNativeTest",
537 [&latch](Dart_NativeArguments args) { latch.CountDown(); }));
538
539 auto engine = builder.LaunchEngine();
540
541 // Send a window metrics events so frames may be scheduled.
542 FlutterWindowMetricsEvent event = {};
543 event.struct_size = sizeof(event);
544 event.width = 800;
545 event.height = 600;
546 event.pixel_ratio = 1.0;
547 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
548 kSuccess);
549 ASSERT_TRUE(engine.is_valid());
550
551 latch.Wait();
552}
553
554//------------------------------------------------------------------------------
555/// Must be able to render using a custom compositor whose render target for the
556/// individual layers are software buffers.
557///
558TEST_F(EmbedderTest, CompositorMustBeAbleToRenderToSoftwareBuffer) {
559 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
560
561 EmbedderConfigBuilder builder(context);
562 builder.SetSurface(DlISize(800, 600));
563 builder.SetCompositor();
564 builder.SetDartEntrypoint("can_composite_platform_views");
565
566 builder.SetRenderTargetType(
568
569 fml::CountDownLatch latch(3);
570 context.GetCompositor().SetNextPresentCallback(
572 size_t layers_count) {
573 ASSERT_EQ(layers_count, 3u);
574
575 {
576 FlutterBackingStore backing_store = *layers[0]->backing_store;
577 backing_store.struct_size = sizeof(backing_store);
579 backing_store.did_update = true;
580 ASSERT_FLOAT_EQ(
581 backing_store.software.row_bytes * backing_store.software.height,
582 800 * 4 * 600.0);
583
584 FlutterRect paint_region_rects[] = {
585 FlutterRectMakeLTRB(0, 0, 800, 600),
586 };
587 FlutterRegion paint_region = {
588 .struct_size = sizeof(FlutterRegion),
589 .rects_count = 1,
590 .rects = paint_region_rects,
591 };
592 FlutterBackingStorePresentInfo present_info = {
594 .paint_region = &paint_region,
595 };
596
597 FlutterLayer layer = {};
598 layer.struct_size = sizeof(layer);
600 layer.backing_store = &backing_store;
601 layer.size = FlutterSizeMake(800.0, 600.0);
602 layer.offset = FlutterPointMake(0, 0);
603 layer.backing_store_present_info = &present_info;
604
605 ASSERT_EQ(*layers[0], layer);
606 }
607
608 {
611 platform_view.identifier = 42;
612
613 FlutterLayer layer = {};
614 layer.struct_size = sizeof(layer);
617 layer.size = FlutterSizeMake(123.0, 456.0);
618 layer.offset = FlutterPointMake(1.0, 2.0);
619
620 ASSERT_EQ(*layers[1], layer);
621 }
622
623 {
624 FlutterBackingStore backing_store = *layers[2]->backing_store;
625 backing_store.struct_size = sizeof(backing_store);
627 backing_store.did_update = true;
628
629 FlutterRect paint_region_rects[] = {
630 FlutterRectMakeLTRB(2, 3, 800, 600),
631 };
632 FlutterRegion paint_region = {
633 .struct_size = sizeof(FlutterRegion),
634 .rects_count = 1,
635 .rects = paint_region_rects,
636 };
637 FlutterBackingStorePresentInfo present_info = {
639 .paint_region = &paint_region,
640 };
641
642 FlutterLayer layer = {};
643 layer.struct_size = sizeof(layer);
645 layer.backing_store = &backing_store;
646 layer.size = FlutterSizeMake(800.0, 600.0);
647 layer.offset = FlutterPointMake(0.0, 0.0);
648 layer.backing_store_present_info = &present_info;
649
650 ASSERT_EQ(*layers[2], layer);
651 }
652
653 latch.CountDown();
654 });
655
656 context.AddNativeCallback(
657 "SignalNativeTest",
659 [&latch](Dart_NativeArguments args) { latch.CountDown(); }));
660
661 auto engine = builder.LaunchEngine();
662
663 // Send a window metrics events so frames may be scheduled.
664 FlutterWindowMetricsEvent event = {};
665 event.struct_size = sizeof(event);
666 event.width = 800;
667 event.height = 600;
668 event.pixel_ratio = 1.0;
669 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
670 kSuccess);
671 ASSERT_TRUE(engine.is_valid());
672
673 latch.Wait();
674}
675
676//------------------------------------------------------------------------------
677/// Test the layer structure and pixels rendered when using a custom compositor.
678///
679TEST_F(EmbedderTest, CompositorMustBeAbleToRenderKnownScene) {
680 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
681
682 EmbedderConfigBuilder builder(context);
683 builder.SetSurface(DlISize(800, 600));
684 builder.SetCompositor();
685 builder.SetDartEntrypoint("can_composite_platform_views_with_known_scene");
686
687 builder.SetRenderTargetType(
689
690 fml::CountDownLatch latch(5);
691
692 auto scene_image = context.GetNextSceneImage();
693
694 context.GetCompositor().SetNextPresentCallback(
696 size_t layers_count) {
697 ASSERT_EQ(layers_count, 5u);
698
699 // Layer Root
700 {
701 FlutterBackingStore backing_store = *layers[0]->backing_store;
702 backing_store.type = kFlutterBackingStoreTypeOpenGL;
703 backing_store.did_update = true;
705
706 FlutterRect paint_region_rects[] = {
707 FlutterRectMakeLTRB(0, 0, 800, 600),
708 };
709 FlutterRegion paint_region = {
710 .struct_size = sizeof(FlutterRegion),
711 .rects_count = 1,
712 .rects = paint_region_rects,
713 };
714 FlutterBackingStorePresentInfo present_info = {
716 .paint_region = &paint_region,
717 };
718
719 FlutterLayer layer = {};
720 layer.struct_size = sizeof(layer);
722 layer.backing_store = &backing_store;
723 layer.size = FlutterSizeMake(800.0, 600.0);
724 layer.offset = FlutterPointMake(0.0, 0.0);
725 layer.backing_store_present_info = &present_info;
726
727 ASSERT_EQ(*layers[0], layer);
728 }
729
730 // Layer 1
731 {
734 platform_view.identifier = 1;
735
736 FlutterLayer layer = {};
737 layer.struct_size = sizeof(layer);
740 layer.size = FlutterSizeMake(50.0, 150.0);
741 layer.offset = FlutterPointMake(20.0, 20.0);
742
743 ASSERT_EQ(*layers[1], layer);
744 }
745
746 // Layer 2
747 {
748 FlutterBackingStore backing_store = *layers[2]->backing_store;
749 backing_store.type = kFlutterBackingStoreTypeOpenGL;
750 backing_store.did_update = true;
752
753 FlutterRect paint_region_rects[] = {
754 FlutterRectMakeLTRB(30, 30, 80, 180),
755 };
756 FlutterRegion paint_region = {
757 .struct_size = sizeof(FlutterRegion),
758 .rects_count = 1,
759 .rects = paint_region_rects,
760 };
761 FlutterBackingStorePresentInfo present_info = {
763 .paint_region = &paint_region,
764 };
765
766 FlutterLayer layer = {};
767 layer.struct_size = sizeof(layer);
769 layer.backing_store = &backing_store;
770 layer.size = FlutterSizeMake(800.0, 600.0);
771 layer.offset = FlutterPointMake(0.0, 0.0);
772 layer.backing_store_present_info = &present_info;
773
774 ASSERT_EQ(*layers[2], layer);
775 }
776
777 // Layer 3
778 {
781 platform_view.identifier = 2;
782
783 FlutterLayer layer = {};
784 layer.struct_size = sizeof(layer);
787 layer.size = FlutterSizeMake(50.0, 150.0);
788 layer.offset = FlutterPointMake(40.0, 40.0);
789
790 ASSERT_EQ(*layers[3], layer);
791 }
792
793 // Layer 4
794 {
795 FlutterBackingStore backing_store = *layers[4]->backing_store;
796 backing_store.type = kFlutterBackingStoreTypeOpenGL;
797 backing_store.did_update = true;
799
800 FlutterRect paint_region_rects[] = {
801 FlutterRectMakeLTRB(50, 50, 100, 200),
802 };
803 FlutterRegion paint_region = {
804 .struct_size = sizeof(FlutterRegion),
805 .rects_count = 1,
806 .rects = paint_region_rects,
807 };
808 FlutterBackingStorePresentInfo present_info = {
810 .paint_region = &paint_region,
811 };
812
813 FlutterLayer layer = {};
814 layer.struct_size = sizeof(layer);
816 layer.backing_store = &backing_store;
817 layer.size = FlutterSizeMake(800.0, 600.0);
818 layer.offset = FlutterPointMake(0.0, 0.0);
819 layer.backing_store_present_info = &present_info;
820
821 ASSERT_EQ(*layers[4], layer);
822 }
823
824 latch.CountDown();
825 });
826
827 context.GetCompositor().SetPlatformViewRendererCallback(
828 [&](const FlutterLayer& layer,
829 GrDirectContext* context) -> sk_sp<SkImage> {
830 auto surface = CreateRenderSurface(layer, context);
831 auto canvas = surface->getCanvas();
832 FML_CHECK(canvas != nullptr);
833
834 switch (layer.platform_view->identifier) {
835 case 1: {
836 SkPaint paint;
837 // See dart test for total order.
838 paint.setColor(SK_ColorGREEN);
839 paint.setAlpha(127);
840 const auto& rect =
841 SkRect::MakeWH(layer.size.width, layer.size.height);
842 canvas->drawRect(rect, paint);
843 latch.CountDown();
844 } break;
845 case 2: {
846 SkPaint paint;
847 // See dart test for total order.
848 paint.setColor(SK_ColorMAGENTA);
849 paint.setAlpha(127);
850 const auto& rect =
851 SkRect::MakeWH(layer.size.width, layer.size.height);
852 canvas->drawRect(rect, paint);
853 latch.CountDown();
854 } break;
855 default:
856 // Asked to render an unknown platform view.
857 FML_CHECK(false)
858 << "Test was asked to composite an unknown platform view.";
859 }
860
861 return surface->makeImageSnapshot();
862 });
863
864 context.AddNativeCallback(
865 "SignalNativeTest",
867 [&latch](Dart_NativeArguments args) { latch.CountDown(); }));
868
869 auto engine = builder.LaunchEngine();
870
871 // Send a window metrics events so frames may be scheduled.
872 FlutterWindowMetricsEvent event = {};
873 event.struct_size = sizeof(event);
874 event.width = 800;
875 event.height = 600;
876 event.pixel_ratio = 1.0;
877 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
878 kSuccess);
879 ASSERT_TRUE(engine.is_valid());
880
881 latch.Wait();
882
883 ASSERT_TRUE(ImageMatchesFixture("compositor.png", scene_image));
884
885 // There should no present calls on the root surface.
886 ASSERT_EQ(context.GetSurfacePresentCount(), 0u);
887}
888
889//------------------------------------------------------------------------------
890/// Custom compositor must play nicely with a custom task runner. The raster
891/// thread merging mechanism must not interfere with the custom compositor.
892///
893TEST_F(EmbedderTest, CustomCompositorMustWorkWithCustomTaskRunner) {
894 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
895
896 EmbedderConfigBuilder builder(context);
897
898 builder.SetSurface(DlISize(800, 600));
899 builder.SetCompositor();
900 builder.SetDartEntrypoint("can_composite_platform_views");
901
902 auto platform_task_runner = CreateNewThread("test_platform_thread");
903 static std::mutex engine_mutex;
906
907 EmbedderTestTaskRunner test_task_runner(
908 platform_task_runner, [&](FlutterTask task) {
909 std::scoped_lock lock(engine_mutex);
910 if (!engine.is_valid()) {
911 return;
912 }
913 ASSERT_EQ(FlutterEngineRunTask(engine.get(), &task), kSuccess);
914 });
915
916 builder.SetRenderTargetType(
918
919 fml::CountDownLatch latch(3);
920 context.GetCompositor().SetNextPresentCallback(
922 size_t layers_count) {
923 ASSERT_EQ(layers_count, 3u);
924
925 {
926 FlutterBackingStore backing_store = *layers[0]->backing_store;
927 backing_store.struct_size = sizeof(backing_store);
928 backing_store.type = kFlutterBackingStoreTypeOpenGL;
929 backing_store.did_update = true;
931
932 FlutterRect paint_region_rects[] = {
933 FlutterRectMakeLTRB(0, 0, 800, 600),
934 };
935 FlutterRegion paint_region = {
936 .struct_size = sizeof(FlutterRegion),
937 .rects_count = 1,
938 .rects = paint_region_rects,
939 };
940 FlutterBackingStorePresentInfo present_info = {
942 .paint_region = &paint_region,
943 };
944
945 FlutterLayer layer = {};
946 layer.struct_size = sizeof(layer);
948 layer.backing_store = &backing_store;
949 layer.size = FlutterSizeMake(800.0, 600.0);
950 layer.offset = FlutterPointMake(0, 0);
951 layer.backing_store_present_info = &present_info;
952
953 ASSERT_EQ(*layers[0], layer);
954 }
955
956 {
959 platform_view.identifier = 42;
960
961 FlutterRect paint_region_rects[] = {
962 FlutterRectMakeLTRB(2, 3, 800, 600),
963 };
964 FlutterRegion paint_region = {
965 .struct_size = sizeof(FlutterRegion),
966 .rects_count = 1,
967 .rects = paint_region_rects,
968 };
969 FlutterBackingStorePresentInfo present_info = {
971 .paint_region = &paint_region,
972 };
973
974 FlutterLayer layer = {};
975 layer.struct_size = sizeof(layer);
978 layer.size = FlutterSizeMake(123.0, 456.0);
979 layer.offset = FlutterPointMake(1.0, 2.0);
980 layer.backing_store_present_info = &present_info;
981
982 ASSERT_EQ(*layers[1], layer);
983 }
984
985 {
986 FlutterBackingStore backing_store = *layers[2]->backing_store;
987 backing_store.struct_size = sizeof(backing_store);
988 backing_store.type = kFlutterBackingStoreTypeOpenGL;
989 backing_store.did_update = true;
991
992 FlutterRect paint_region_rects[] = {
993 FlutterRectMakeLTRB(2, 3, 800, 600),
994 };
995 FlutterRegion paint_region = {
996 .struct_size = sizeof(FlutterRegion),
997 .rects_count = 1,
998 .rects = paint_region_rects,
999 };
1000 FlutterBackingStorePresentInfo present_info = {
1002 .paint_region = &paint_region,
1003 };
1004
1005 FlutterLayer layer = {};
1006 layer.struct_size = sizeof(layer);
1008 layer.backing_store = &backing_store;
1009 layer.size = FlutterSizeMake(800.0, 600.0);
1010 layer.offset = FlutterPointMake(0.0, 0.0);
1011 layer.backing_store_present_info = &present_info;
1012
1013 ASSERT_EQ(*layers[2], layer);
1014 }
1015
1016 latch.CountDown();
1017 });
1018
1019 const auto task_runner_description =
1020 test_task_runner.GetFlutterTaskRunnerDescription();
1021
1022 builder.SetPlatformTaskRunner(&task_runner_description);
1023
1024 context.AddNativeCallback(
1025 "SignalNativeTest",
1027 [&latch](Dart_NativeArguments args) { latch.CountDown(); }));
1028
1029 platform_task_runner->PostTask([&]() {
1030 std::scoped_lock lock(engine_mutex);
1031 engine = builder.LaunchEngine();
1032 ASSERT_TRUE(engine.is_valid());
1033
1034 // Send a window metrics events so frames may be scheduled.
1035 FlutterWindowMetricsEvent event = {};
1036 event.struct_size = sizeof(event);
1037 event.width = 800;
1038 event.height = 600;
1039 event.pixel_ratio = 1.0;
1040 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
1041 kSuccess);
1042 ASSERT_TRUE(engine.is_valid());
1043 sync_latch.Signal();
1044 });
1045 sync_latch.Wait();
1046
1047 latch.Wait();
1048
1049 platform_task_runner->PostTask([&]() {
1050 std::scoped_lock lock(engine_mutex);
1051 engine.reset();
1052 sync_latch.Signal();
1053 });
1054 sync_latch.Wait();
1055}
1056
1057//------------------------------------------------------------------------------
1058/// Test the layer structure and pixels rendered when using a custom compositor
1059/// and a single layer.
1060///
1061TEST_F(EmbedderTest, CompositorMustBeAbleToRenderWithRootLayerOnly) {
1062 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
1063
1064 EmbedderConfigBuilder builder(context);
1065 builder.SetSurface(DlISize(800, 600));
1066 builder.SetCompositor();
1067 builder.SetDartEntrypoint(
1068 "can_composite_platform_views_with_root_layer_only");
1069
1070 builder.SetRenderTargetType(
1072
1073 fml::CountDownLatch latch(3);
1074
1075 auto scene_image = context.GetNextSceneImage();
1076
1077 context.GetCompositor().SetNextPresentCallback(
1079 size_t layers_count) {
1080 ASSERT_EQ(layers_count, 1u);
1081
1082 // Layer Root
1083 {
1084 FlutterBackingStore backing_store = *layers[0]->backing_store;
1085 backing_store.type = kFlutterBackingStoreTypeOpenGL;
1086 backing_store.did_update = true;
1088
1089 FlutterRect paint_region_rects[] = {
1090 FlutterRectMakeLTRB(0, 0, 800, 600),
1091 };
1092 FlutterRegion paint_region = {
1093 .struct_size = sizeof(FlutterRegion),
1094 .rects_count = 1,
1095 .rects = paint_region_rects,
1096 };
1097 FlutterBackingStorePresentInfo present_info = {
1099 .paint_region = &paint_region,
1100 };
1101
1102 FlutterLayer layer = {};
1103 layer.struct_size = sizeof(layer);
1105 layer.backing_store = &backing_store;
1106 layer.size = FlutterSizeMake(800.0, 600.0);
1107 layer.offset = FlutterPointMake(0.0, 0.0);
1108 layer.backing_store_present_info = &present_info;
1109
1110 ASSERT_EQ(*layers[0], layer);
1111 }
1112
1113 latch.CountDown();
1114 });
1115
1116 context.AddNativeCallback(
1117 "SignalNativeTest",
1119 [&latch](Dart_NativeArguments args) { latch.CountDown(); }));
1120
1121 auto engine = builder.LaunchEngine();
1122
1123 // Send a window metrics events so frames may be scheduled.
1124 FlutterWindowMetricsEvent event = {};
1125 event.struct_size = sizeof(event);
1126 event.width = 800;
1127 event.height = 600;
1128 event.pixel_ratio = 1.0;
1129 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
1130 kSuccess);
1131 ASSERT_TRUE(engine.is_valid());
1132
1133 latch.Wait();
1134
1135 ASSERT_TRUE(
1136 ImageMatchesFixture("compositor_with_root_layer_only.png", scene_image));
1137}
1138
1139//------------------------------------------------------------------------------
1140/// Test the layer structure and pixels rendered when using a custom compositor
1141/// and ensure that a redundant layer is not added.
1142///
1143TEST_F(EmbedderTest, CompositorMustBeAbleToRenderWithPlatformLayerOnBottom) {
1144 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
1145
1146 EmbedderConfigBuilder builder(context);
1147 builder.SetSurface(DlISize(800, 600));
1148 builder.SetCompositor();
1149 builder.SetDartEntrypoint(
1150 "can_composite_platform_views_with_platform_layer_on_bottom");
1151
1152 builder.SetRenderTargetType(
1154
1155 fml::CountDownLatch latch(3);
1156
1157 auto scene_image = context.GetNextSceneImage();
1158
1159 context.GetCompositor().SetNextPresentCallback(
1161 size_t layers_count) {
1162 ASSERT_EQ(layers_count, 2u);
1163
1164 // Layer Root
1165 {
1166 FlutterBackingStore backing_store = *layers[0]->backing_store;
1167 backing_store.type = kFlutterBackingStoreTypeOpenGL;
1168 backing_store.did_update = true;
1170
1171 FlutterRect paint_region_rects[] = {
1172 FlutterRectMakeLTRB(0, 0, 800, 600),
1173 };
1174 FlutterRegion paint_region = {
1175 .struct_size = sizeof(FlutterRegion),
1176 .rects_count = 1,
1177 .rects = paint_region_rects,
1178 };
1179 FlutterBackingStorePresentInfo present_info = {
1181 .paint_region = &paint_region,
1182 };
1183
1184 FlutterLayer layer = {};
1185 layer.struct_size = sizeof(layer);
1187 layer.backing_store = &backing_store;
1188 layer.size = FlutterSizeMake(800.0, 600.0);
1189 layer.offset = FlutterPointMake(0.0, 0.0);
1190 layer.backing_store_present_info = &present_info;
1191
1192 ASSERT_EQ(*layers[0], layer);
1193 }
1194
1195 // Layer 1
1196 {
1199 platform_view.identifier = 1;
1200
1201 FlutterLayer layer = {};
1202 layer.struct_size = sizeof(layer);
1205 layer.size = FlutterSizeMake(50.0, 150.0);
1206 layer.offset = FlutterPointMake(20.0, 20.0);
1207
1208 ASSERT_EQ(*layers[1], layer);
1209 }
1210
1211 latch.CountDown();
1212 });
1213
1214 context.GetCompositor().SetPlatformViewRendererCallback(
1215 [&](const FlutterLayer& layer,
1216 GrDirectContext* context) -> sk_sp<SkImage> {
1217 auto surface = CreateRenderSurface(layer, context);
1218 auto canvas = surface->getCanvas();
1219 FML_CHECK(canvas != nullptr);
1220
1221 switch (layer.platform_view->identifier) {
1222 case 1: {
1223 SkPaint paint;
1224 // See dart test for total order.
1225 paint.setColor(SK_ColorGREEN);
1226 paint.setAlpha(127);
1227 const auto& rect =
1228 SkRect::MakeWH(layer.size.width, layer.size.height);
1229 canvas->drawRect(rect, paint);
1230 latch.CountDown();
1231 } break;
1232 default:
1233 // Asked to render an unknown platform view.
1234 FML_CHECK(false)
1235 << "Test was asked to composite an unknown platform view.";
1236 }
1237
1238 return surface->makeImageSnapshot();
1239 });
1240
1241 context.AddNativeCallback(
1242 "SignalNativeTest",
1244 [&latch](Dart_NativeArguments args) { latch.CountDown(); }));
1245
1246 auto engine = builder.LaunchEngine();
1247
1248 // Send a window metrics events so frames may be scheduled.
1249 FlutterWindowMetricsEvent event = {};
1250 event.struct_size = sizeof(event);
1251 event.width = 800;
1252 event.height = 600;
1253 event.pixel_ratio = 1.0;
1254 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
1255 kSuccess);
1256 ASSERT_TRUE(engine.is_valid());
1257
1258 latch.Wait();
1259
1260 ASSERT_TRUE(ImageMatchesFixture(
1261 "compositor_with_platform_layer_on_bottom.png", scene_image));
1262
1263 ASSERT_EQ(context.GetCompositor().GetPendingBackingStoresCount(), 1u);
1264}
1265
1266//------------------------------------------------------------------------------
1267/// Test the layer structure and pixels rendered when using a custom compositor
1268/// with a root surface transformation.
1269///
1271 CompositorMustBeAbleToRenderKnownSceneWithRootSurfaceTransformation) {
1272 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
1273
1274 EmbedderConfigBuilder builder(context);
1275 builder.SetSurface(DlISize(600, 800));
1276 builder.SetCompositor();
1277 builder.SetDartEntrypoint("can_composite_platform_views_with_known_scene");
1278
1279 builder.SetRenderTargetType(
1281
1282 // This must match the transformation provided in the
1283 // |CanRenderGradientWithoutCompositorWithXform| test to ensure that
1284 // transforms are consistent respected.
1285 const auto root_surface_transformation =
1286 DlMatrix::MakeTranslation({0, 800}) *
1288
1289 context.SetRootSurfaceTransformation(root_surface_transformation);
1290
1291 fml::CountDownLatch latch(5);
1292
1293 auto scene_image = context.GetNextSceneImage();
1294
1295 context.GetCompositor().SetNextPresentCallback(
1297 size_t layers_count) {
1298 ASSERT_EQ(layers_count, 5u);
1299
1300 // Layer Root
1301 {
1302 FlutterBackingStore backing_store = *layers[0]->backing_store;
1303 backing_store.type = kFlutterBackingStoreTypeOpenGL;
1304 backing_store.did_update = true;
1306
1307 FlutterRect paint_region_rects[] = {
1308 FlutterRectMakeLTRB(0, 0, 600, 800),
1309 };
1310 FlutterRegion paint_region = {
1311 .struct_size = sizeof(FlutterRegion),
1312 .rects_count = 1,
1313 .rects = paint_region_rects,
1314 };
1315 FlutterBackingStorePresentInfo present_info = {
1317 .paint_region = &paint_region,
1318 };
1319
1320 FlutterLayer layer = {};
1321 layer.struct_size = sizeof(layer);
1323 layer.backing_store = &backing_store;
1324 layer.size = FlutterSizeMake(600.0, 800.0);
1325 layer.offset = FlutterPointMake(0.0, 0.0);
1326 layer.backing_store_present_info = &present_info;
1327
1328 ASSERT_EQ(*layers[0], layer);
1329 }
1330
1331 // Layer 1
1332 {
1335 platform_view.identifier = 1;
1336
1337 FlutterLayer layer = {};
1338 layer.struct_size = sizeof(layer);
1341 layer.size = FlutterSizeMake(150.0, 50.0);
1342 layer.offset = FlutterPointMake(20.0, 730.0);
1343
1344 ASSERT_EQ(*layers[1], layer);
1345 }
1346
1347 // Layer 2
1348 {
1349 FlutterBackingStore backing_store = *layers[2]->backing_store;
1350 backing_store.type = kFlutterBackingStoreTypeOpenGL;
1351 backing_store.did_update = true;
1353
1354 FlutterRect paint_region_rects[] = {
1355 FlutterRectMakeLTRB(30, 720, 180, 770),
1356 };
1357 FlutterRegion paint_region = {
1358 .struct_size = sizeof(FlutterRegion),
1359 .rects_count = 1,
1360 .rects = paint_region_rects,
1361 };
1362 FlutterBackingStorePresentInfo present_info = {
1364 .paint_region = &paint_region,
1365 };
1366
1367 FlutterLayer layer = {};
1368 layer.struct_size = sizeof(layer);
1370 layer.backing_store = &backing_store;
1371 layer.size = FlutterSizeMake(600.0, 800.0);
1372 layer.offset = FlutterPointMake(0.0, 0.0);
1373 layer.backing_store_present_info = &present_info;
1374
1375 ASSERT_EQ(*layers[2], layer);
1376 }
1377
1378 // Layer 3
1379 {
1382 platform_view.identifier = 2;
1383
1384 FlutterLayer layer = {};
1385 layer.struct_size = sizeof(layer);
1388 layer.size = FlutterSizeMake(150.0, 50.0);
1389 layer.offset = FlutterPointMake(40.0, 710.0);
1390
1391 ASSERT_EQ(*layers[3], layer);
1392 }
1393
1394 // Layer 4
1395 {
1396 FlutterBackingStore backing_store = *layers[4]->backing_store;
1397 backing_store.type = kFlutterBackingStoreTypeOpenGL;
1398 backing_store.did_update = true;
1400
1401 FlutterRect paint_region_rects[] = {
1402 FlutterRectMakeLTRB(50, 700, 200, 750),
1403 };
1404 FlutterRegion paint_region = {
1405 .struct_size = sizeof(FlutterRegion),
1406 .rects_count = 1,
1407 .rects = paint_region_rects,
1408 };
1409 FlutterBackingStorePresentInfo present_info = {
1411 .paint_region = &paint_region,
1412 };
1413
1414 FlutterLayer layer = {};
1415 layer.struct_size = sizeof(layer);
1417 layer.backing_store = &backing_store;
1418 layer.size = FlutterSizeMake(600.0, 800.0);
1419 layer.offset = FlutterPointMake(0.0, 0.0);
1420 layer.backing_store_present_info = &present_info;
1421
1422 ASSERT_EQ(*layers[4], layer);
1423 }
1424
1425 latch.CountDown();
1426 });
1427
1428 context.GetCompositor().SetPlatformViewRendererCallback(
1429 [&](const FlutterLayer& layer,
1430 GrDirectContext* context) -> sk_sp<SkImage> {
1431 auto surface = CreateRenderSurface(layer, context);
1432 auto canvas = surface->getCanvas();
1433 FML_CHECK(canvas != nullptr);
1434
1435 switch (layer.platform_view->identifier) {
1436 case 1: {
1437 SkPaint paint;
1438 // See dart test for total order.
1439 paint.setColor(SK_ColorGREEN);
1440 paint.setAlpha(127);
1441 const auto& rect =
1442 SkRect::MakeWH(layer.size.width, layer.size.height);
1443 canvas->drawRect(rect, paint);
1444 latch.CountDown();
1445 } break;
1446 case 2: {
1447 SkPaint paint;
1448 // See dart test for total order.
1449 paint.setColor(SK_ColorMAGENTA);
1450 paint.setAlpha(127);
1451 const auto& rect =
1452 SkRect::MakeWH(layer.size.width, layer.size.height);
1453 canvas->drawRect(rect, paint);
1454 latch.CountDown();
1455 } break;
1456 default:
1457 // Asked to render an unknown platform view.
1458 FML_CHECK(false)
1459 << "Test was asked to composite an unknown platform view.";
1460 }
1461
1462 return surface->makeImageSnapshot();
1463 });
1464
1465 context.AddNativeCallback(
1466 "SignalNativeTest",
1468 [&latch](Dart_NativeArguments args) { latch.CountDown(); }));
1469
1470 auto engine = builder.LaunchEngine();
1471
1472 // Send a window metrics events so frames may be scheduled.
1473 FlutterWindowMetricsEvent event = {};
1474 event.struct_size = sizeof(event);
1475 // Flutter still thinks it is 800 x 600. Only the root surface is rotated.
1476 event.width = 800;
1477 event.height = 600;
1478 event.pixel_ratio = 1.0;
1479 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
1480 kSuccess);
1481 ASSERT_TRUE(engine.is_valid());
1482
1483 latch.Wait();
1484
1485 ASSERT_TRUE(ImageMatchesFixture("compositor_root_surface_xformation.png",
1486 scene_image));
1487}
1488
1489TEST_F(EmbedderTest, CanRenderSceneWithoutCustomCompositor) {
1490 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
1491
1492 EmbedderConfigBuilder builder(context);
1493
1494 builder.SetDartEntrypoint("can_render_scene_without_custom_compositor");
1495 builder.SetSurface(DlISize(800, 600));
1496
1497 auto rendered_scene = context.GetNextSceneImage();
1498
1499 auto engine = builder.LaunchEngine();
1500 ASSERT_TRUE(engine.is_valid());
1501
1502 // Send a window metrics events so frames may be scheduled.
1503 FlutterWindowMetricsEvent event = {};
1504 event.struct_size = sizeof(event);
1505 event.width = 800;
1506 event.height = 600;
1507 event.pixel_ratio = 1.0;
1508 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
1509 kSuccess);
1510
1511 ASSERT_TRUE(ImageMatchesFixture("scene_without_custom_compositor.png",
1512 rendered_scene));
1513}
1514
1515TEST_F(EmbedderTest, CanRenderSceneWithoutCustomCompositorWithTransformation) {
1516 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
1517
1518 const auto root_surface_transformation =
1519 DlMatrix::MakeTranslation({0, 800}) *
1521
1522 context.SetRootSurfaceTransformation(root_surface_transformation);
1523
1524 EmbedderConfigBuilder builder(context);
1525
1526 builder.SetDartEntrypoint("can_render_scene_without_custom_compositor");
1527 builder.SetSurface(DlISize(600, 800));
1528
1529 auto rendered_scene = context.GetNextSceneImage();
1530
1531 auto engine = builder.LaunchEngine();
1532 ASSERT_TRUE(engine.is_valid());
1533
1534 // Send a window metrics events so frames may be scheduled.
1535 FlutterWindowMetricsEvent event = {};
1536 event.struct_size = sizeof(event);
1537
1538 // Flutter still thinks it is 800 x 600.
1539 event.width = 800;
1540 event.height = 600;
1541 event.pixel_ratio = 1.0;
1542 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
1543 kSuccess);
1544
1545 ASSERT_TRUE(ImageMatchesFixture(
1546 "scene_without_custom_compositor_with_xform.png", rendered_scene));
1547}
1548
1549TEST_P(EmbedderTestMultiBackend, CanRenderGradientWithoutCompositor) {
1550 EmbedderTestContextType backend = GetParam();
1551 auto& context = GetEmbedderContext(backend);
1552 EmbedderConfigBuilder builder(context);
1553 builder.SetDartEntrypoint("render_gradient");
1554 builder.SetSurface(DlISize(800, 600));
1555
1556 auto rendered_scene = context.GetNextSceneImage();
1557
1558 auto engine = builder.LaunchEngine();
1559 ASSERT_TRUE(engine.is_valid());
1560
1561 // Send a window metrics events so frames may be scheduled.
1562 FlutterWindowMetricsEvent event = {};
1563 event.struct_size = sizeof(event);
1564 event.width = 800;
1565 event.height = 600;
1566 event.pixel_ratio = 1.0;
1567 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
1568 kSuccess);
1569
1570 ASSERT_TRUE(ImageMatchesFixture(
1571 FixtureNameForBackend(backend, "gradient.png"), rendered_scene));
1572}
1573
1574TEST_F(EmbedderTest, CanRenderGradientWithoutCompositorWithXform) {
1575 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
1576
1577 const auto root_surface_transformation =
1578 DlMatrix::MakeTranslation({0, 800}) *
1580
1581 context.SetRootSurfaceTransformation(root_surface_transformation);
1582
1583 EmbedderConfigBuilder builder(context);
1584
1585 const auto surface_size = DlISize(600, 800);
1586
1587 builder.SetDartEntrypoint("render_gradient");
1588 builder.SetSurface(surface_size);
1589
1590 auto rendered_scene = context.GetNextSceneImage();
1591
1592 auto engine = builder.LaunchEngine();
1593 ASSERT_TRUE(engine.is_valid());
1594
1595 // Send a window metrics events so frames may be scheduled.
1596 FlutterWindowMetricsEvent event = {};
1597 event.struct_size = sizeof(event);
1598 // Flutter still thinks it is 800 x 600.
1599 event.width = 800;
1600 event.height = 600;
1601 event.pixel_ratio = 1.0;
1602 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
1603 kSuccess);
1604
1605 ASSERT_TRUE(ImageMatchesFixture("gradient_xform.png", rendered_scene));
1606}
1607
1608TEST_P(EmbedderTestMultiBackend, CanRenderGradientWithCompositor) {
1609 EmbedderTestContextType backend = GetParam();
1610 auto& context = GetEmbedderContext(backend);
1611
1612 EmbedderConfigBuilder builder(context);
1613 builder.SetDartEntrypoint("render_gradient");
1614 builder.SetSurface(DlISize(800, 600));
1615 builder.SetCompositor();
1616 builder.SetRenderTargetType(GetRenderTargetFromBackend(backend, true));
1617
1618 auto rendered_scene = context.GetNextSceneImage();
1619
1620 auto engine = builder.LaunchEngine();
1621 ASSERT_TRUE(engine.is_valid());
1622
1623 // Send a window metrics events so frames may be scheduled.
1624 FlutterWindowMetricsEvent event = {};
1625 event.struct_size = sizeof(event);
1626 event.width = 800;
1627 event.height = 600;
1628 event.pixel_ratio = 1.0;
1629 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
1630 kSuccess);
1631
1632 ASSERT_TRUE(ImageMatchesFixture(
1633 FixtureNameForBackend(backend, "gradient.png"), rendered_scene));
1634}
1635
1636TEST_F(EmbedderTest, CanRenderGradientWithCompositorWithXform) {
1637 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
1638
1639 // This must match the transformation provided in the
1640 // |CanRenderGradientWithoutCompositorWithXform| test to ensure that
1641 // transforms are consistent respected.
1642 const auto root_surface_transformation =
1643 DlMatrix::MakeTranslation({0, 800}) *
1645
1646 context.SetRootSurfaceTransformation(root_surface_transformation);
1647
1648 EmbedderConfigBuilder builder(context);
1649
1650 builder.SetDartEntrypoint("render_gradient");
1651 builder.SetSurface(DlISize(600, 800));
1652 builder.SetCompositor();
1653 builder.SetRenderTargetType(
1655
1656 auto rendered_scene = context.GetNextSceneImage();
1657
1658 auto engine = builder.LaunchEngine();
1659 ASSERT_TRUE(engine.is_valid());
1660
1661 // Send a window metrics events so frames may be scheduled.
1662 FlutterWindowMetricsEvent event = {};
1663 event.struct_size = sizeof(event);
1664 // Flutter still thinks it is 800 x 600.
1665 event.width = 800;
1666 event.height = 600;
1667 event.pixel_ratio = 1.0;
1668 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
1669 kSuccess);
1670
1671 ASSERT_TRUE(ImageMatchesFixture("gradient_xform.png", rendered_scene));
1672}
1673
1675 CanRenderGradientWithCompositorOnNonRootLayer) {
1676 EmbedderTestContextType backend = GetParam();
1677 auto& context = GetEmbedderContext(backend);
1678
1679 EmbedderConfigBuilder builder(context);
1680 builder.SetDartEntrypoint("render_gradient_on_non_root_backing_store");
1681 builder.SetSurface(DlISize(800, 600));
1682 builder.SetCompositor();
1683 builder.SetRenderTargetType(GetRenderTargetFromBackend(backend, true));
1684
1685 context.GetCompositor().SetNextPresentCallback(
1687 size_t layers_count) {
1688 ASSERT_EQ(layers_count, 3u);
1689
1690 // Layer Root
1691 {
1692 FlutterBackingStore backing_store = *layers[0]->backing_store;
1693 backing_store.did_update = true;
1694 ConfigureBackingStore(backing_store, backend, true);
1695
1696 FlutterRect paint_region_rects[] = {
1697 FlutterRectMakeLTRB(0, 0, 800, 600),
1698 };
1699 FlutterRegion paint_region = {
1700 .struct_size = sizeof(FlutterRegion),
1701 .rects_count = 1,
1702 .rects = paint_region_rects,
1703 };
1704 FlutterBackingStorePresentInfo present_info = {
1706 .paint_region = &paint_region,
1707 };
1708
1709 FlutterLayer layer = {};
1710 layer.struct_size = sizeof(layer);
1712 layer.backing_store = &backing_store;
1713 layer.size = FlutterSizeMake(800.0, 600.0);
1714 layer.offset = FlutterPointMake(0.0, 0.0);
1715 layer.backing_store_present_info = &present_info;
1716
1717 ASSERT_EQ(*layers[0], layer);
1718 }
1719
1720 // Layer 1
1721 {
1724 platform_view.identifier = 1;
1725
1726 FlutterLayer layer = {};
1727 layer.struct_size = sizeof(layer);
1730 layer.size = FlutterSizeMake(100.0, 200.0);
1731 layer.offset = FlutterPointMake(0.0, 0.0);
1732
1733 ASSERT_EQ(*layers[1], layer);
1734 }
1735
1736 // Layer 2
1737 {
1738 FlutterBackingStore backing_store = *layers[2]->backing_store;
1739 backing_store.did_update = true;
1740 ConfigureBackingStore(backing_store, backend, true);
1741
1742 FlutterRect paint_region_rects[] = {
1743 FlutterRectMakeLTRB(0, 0, 800, 600),
1744 };
1745 FlutterRegion paint_region = {
1746 .struct_size = sizeof(FlutterRegion),
1747 .rects_count = 1,
1748 .rects = paint_region_rects,
1749 };
1750 FlutterBackingStorePresentInfo present_info = {
1752 .paint_region = &paint_region,
1753 };
1754
1755 FlutterLayer layer = {};
1756 layer.struct_size = sizeof(layer);
1758 layer.backing_store = &backing_store;
1759 layer.size = FlutterSizeMake(800.0, 600.0);
1760 layer.offset = FlutterPointMake(0.0, 0.0);
1761 layer.backing_store_present_info = &present_info;
1762
1763 ASSERT_EQ(*layers[2], layer);
1764 }
1765 });
1766
1767 context.GetCompositor().SetPlatformViewRendererCallback(
1768 [&](const FlutterLayer& layer,
1769 GrDirectContext* context) -> sk_sp<SkImage> {
1770 auto surface = CreateRenderSurface(layer, context);
1771 auto canvas = surface->getCanvas();
1772 FML_CHECK(canvas != nullptr);
1773
1774 switch (layer.platform_view->identifier) {
1775 case 1: {
1776 FML_CHECK(layer.size.width == 100);
1777 FML_CHECK(layer.size.height == 200);
1778 // This is occluded anyway. We just want to make sure we see this.
1779 } break;
1780 default:
1781 // Asked to render an unknown platform view.
1782 FML_CHECK(false)
1783 << "Test was asked to composite an unknown platform view.";
1784 }
1785
1786 return surface->makeImageSnapshot();
1787 });
1788
1789 auto rendered_scene = context.GetNextSceneImage();
1790
1791 auto engine = builder.LaunchEngine();
1792 ASSERT_TRUE(engine.is_valid());
1793
1794 // Send a window metrics events so frames may be scheduled.
1795 FlutterWindowMetricsEvent event = {};
1796 event.struct_size = sizeof(event);
1797 event.width = 800;
1798 event.height = 600;
1799 event.pixel_ratio = 1.0;
1800 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
1801 kSuccess);
1802
1803 ASSERT_TRUE(ImageMatchesFixture(
1804 FixtureNameForBackend(backend, "gradient.png"), rendered_scene));
1805}
1806
1807TEST_F(EmbedderTest, CanRenderGradientWithCompositorOnNonRootLayerWithXform) {
1808 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
1809
1810 // This must match the transformation provided in the
1811 // |CanRenderGradientWithoutCompositorWithXform| test to ensure that
1812 // transforms are consistent respected.
1813 const auto root_surface_transformation =
1814 DlMatrix::MakeTranslation({0, 800}) *
1816
1817 context.SetRootSurfaceTransformation(root_surface_transformation);
1818
1819 EmbedderConfigBuilder builder(context);
1820
1821 builder.SetDartEntrypoint("render_gradient_on_non_root_backing_store");
1822 builder.SetSurface(DlISize(600, 800));
1823 builder.SetCompositor();
1824 builder.SetRenderTargetType(
1826
1827 context.GetCompositor().SetNextPresentCallback(
1829 size_t layers_count) {
1830 ASSERT_EQ(layers_count, 3u);
1831
1832 // Layer Root
1833 {
1834 FlutterBackingStore backing_store = *layers[0]->backing_store;
1835 backing_store.type = kFlutterBackingStoreTypeOpenGL;
1836 backing_store.did_update = true;
1838
1839 FlutterRect paint_region_rects[] = {
1840 FlutterRectMakeLTRB(0, 0, 600, 800),
1841 };
1842 FlutterRegion paint_region = {
1843 .struct_size = sizeof(FlutterRegion),
1844 .rects_count = 1,
1845 .rects = paint_region_rects,
1846 };
1847 FlutterBackingStorePresentInfo present_info = {
1849 .paint_region = &paint_region,
1850 };
1851
1852 FlutterLayer layer = {};
1853 layer.struct_size = sizeof(layer);
1855 layer.backing_store = &backing_store;
1856 layer.size = FlutterSizeMake(600.0, 800.0);
1857 layer.offset = FlutterPointMake(0.0, 0.0);
1858 layer.backing_store_present_info = &present_info;
1859
1860 ASSERT_EQ(*layers[0], layer);
1861 }
1862
1863 // Layer 1
1864 {
1867 platform_view.identifier = 1;
1868
1869 FlutterLayer layer = {};
1870 layer.struct_size = sizeof(layer);
1873 layer.size = FlutterSizeMake(200.0, 100.0);
1874 layer.offset = FlutterPointMake(0.0, 700.0);
1875
1876 ASSERT_EQ(*layers[1], layer);
1877 }
1878
1879 // Layer 2
1880 {
1881 FlutterBackingStore backing_store = *layers[2]->backing_store;
1882 backing_store.type = kFlutterBackingStoreTypeOpenGL;
1883 backing_store.did_update = true;
1885
1886 FlutterRect paint_region_rects[] = {
1887 FlutterRectMakeLTRB(0, 0, 600, 800),
1888 };
1889 FlutterRegion paint_region = {
1890 .struct_size = sizeof(FlutterRegion),
1891 .rects_count = 1,
1892 .rects = paint_region_rects,
1893 };
1894 FlutterBackingStorePresentInfo present_info = {
1896 .paint_region = &paint_region,
1897 };
1898
1899 FlutterLayer layer = {};
1900 layer.struct_size = sizeof(layer);
1902 layer.backing_store = &backing_store;
1903 layer.size = FlutterSizeMake(600.0, 800.0);
1904 layer.offset = FlutterPointMake(0.0, 0.0);
1905 layer.backing_store_present_info = &present_info;
1906
1907 ASSERT_EQ(*layers[2], layer);
1908 }
1909 });
1910
1911 context.GetCompositor().SetPlatformViewRendererCallback(
1912 [&](const FlutterLayer& layer,
1913 GrDirectContext* context) -> sk_sp<SkImage> {
1914 auto surface = CreateRenderSurface(layer, context);
1915 auto canvas = surface->getCanvas();
1916 FML_CHECK(canvas != nullptr);
1917
1918 switch (layer.platform_view->identifier) {
1919 case 1: {
1920 FML_CHECK(layer.size.width == 200);
1921 FML_CHECK(layer.size.height == 100);
1922 // This is occluded anyway. We just want to make sure we see this.
1923 } break;
1924 default:
1925 // Asked to render an unknown platform view.
1926 FML_CHECK(false)
1927 << "Test was asked to composite an unknown platform view.";
1928 }
1929
1930 return surface->makeImageSnapshot();
1931 });
1932
1933 auto rendered_scene = context.GetNextSceneImage();
1934
1935 auto engine = builder.LaunchEngine();
1936 ASSERT_TRUE(engine.is_valid());
1937
1938 // Send a window metrics events so frames may be scheduled.
1939 FlutterWindowMetricsEvent event = {};
1940 event.struct_size = sizeof(event);
1941 // Flutter still thinks it is 800 x 600.
1942 event.width = 800;
1943 event.height = 600;
1944 event.pixel_ratio = 1.0;
1945 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
1946 kSuccess);
1947
1948 ASSERT_TRUE(ImageMatchesFixture("gradient_xform.png", rendered_scene));
1949}
1950
1951TEST_F(EmbedderTest, VerifyB141980393) {
1952 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
1953
1954 EmbedderConfigBuilder builder(context);
1955
1956 // The Flutter application is 800 x 600 but rendering on a surface that is 600
1957 // x 800 achieved using a root surface transformation.
1958 const auto root_surface_transformation =
1959 DlMatrix::MakeTranslation({0, 800}) *
1961 const auto flutter_application_rect = DlRect::MakeWH(800, 600);
1962 const auto root_surface_rect =
1963 flutter_application_rect.TransformAndClipBounds(
1964 root_surface_transformation);
1965
1966 ASSERT_FLOAT_EQ(root_surface_rect.GetWidth(), 600.0f);
1967 ASSERT_FLOAT_EQ(root_surface_rect.GetHeight(), 800.0f);
1968
1969 // Configure the fixture for the surface transformation.
1970 context.SetRootSurfaceTransformation(root_surface_transformation);
1971
1972 // Configure the Flutter project args for the root surface transformation.
1973 builder.SetSurface(
1974 DlISize(root_surface_rect.GetWidth(), root_surface_rect.GetHeight()));
1975
1976 // Use a compositor instead of rendering directly to the surface.
1977 builder.SetCompositor();
1978 builder.SetRenderTargetType(
1980
1981 builder.SetDartEntrypoint("verify_b141980393");
1982
1984
1985 context.GetCompositor().SetNextPresentCallback(
1987 size_t layers_count) {
1988 ASSERT_EQ(layers_count, 1u);
1989
1990 // Layer Root
1991 {
1994 platform_view.identifier = 1337;
1995
1996 FlutterLayer layer = {};
1997 layer.struct_size = sizeof(layer);
2000
2001 // From the Dart side. These dimensions match those specified in Dart
2002 // code and are free of root surface transformations.
2003 const double unxformed_top_margin = 31.0;
2004 const double unxformed_bottom_margin = 37.0;
2005 const auto unxformed_platform_view_rect = DlRect::MakeXYWH(
2006 0.0, // x
2007 unxformed_top_margin, // y (top margin)
2008 800, // width
2009 600 - unxformed_top_margin - unxformed_bottom_margin // height
2010 );
2011
2012 // The platform views are in the coordinate space of the root surface
2013 // with top-left origin. The embedder has specified a transformation
2014 // to this surface which it must account for in the coordinates it
2015 // receives here.
2016 const auto xformed_platform_view_rect =
2017 unxformed_platform_view_rect.TransformAndClipBounds(
2018 root_surface_transformation);
2019
2020 // Spell out the value that we are going to be checking below for
2021 // clarity.
2022 ASSERT_EQ(xformed_platform_view_rect,
2023 DlRect::MakeXYWH(31.0, // x
2024 0.0, // y
2025 532.0, // width
2026 800.0 // height
2027 ));
2028
2029 // Verify that the engine is giving us the right size and offset.
2030 layer.offset = FlutterPointMake(xformed_platform_view_rect.GetX(),
2031 xformed_platform_view_rect.GetY());
2032 layer.size = FlutterSizeMake(xformed_platform_view_rect.GetWidth(),
2033 xformed_platform_view_rect.GetHeight());
2034
2035 ASSERT_EQ(*layers[0], layer);
2036 }
2037
2038 latch.Signal();
2039 });
2040
2041 auto engine = builder.LaunchEngine();
2042
2043 // Send a window metrics events so frames may be scheduled.
2044 FlutterWindowMetricsEvent event = {};
2045 event.struct_size = sizeof(event);
2046
2047 // The Flutter application is 800 x 600 rendering on a surface 600 x 800
2048 // achieved via a root surface transformation.
2049 event.width = flutter_application_rect.GetWidth();
2050 event.height = flutter_application_rect.GetHeight();
2051 event.pixel_ratio = 1.0;
2052 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
2053 kSuccess);
2054 ASSERT_TRUE(engine.is_valid());
2055
2056 latch.Wait();
2057}
2058
2059//------------------------------------------------------------------------------
2060/// Asserts that embedders can provide a task runner for the render thread.
2061///
2062TEST_F(EmbedderTest, CanCreateEmbedderWithCustomRenderTaskRunner) {
2063 std::mutex engine_mutex;
2065 fml::AutoResetWaitableEvent task_latch;
2066 bool task_executed = false;
2067 EmbedderTestTaskRunner render_task_runner(
2068 CreateNewThread("custom_render_thread"), [&](FlutterTask task) {
2069 std::scoped_lock engine_lock(engine_mutex);
2070 if (engine.is_valid()) {
2071 ASSERT_EQ(FlutterEngineRunTask(engine.get(), &task), kSuccess);
2072 task_executed = true;
2073 task_latch.Signal();
2074 }
2075 });
2076 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
2077 EmbedderConfigBuilder builder(context);
2078 builder.SetDartEntrypoint("can_render_scene_without_custom_compositor");
2079 builder.SetSurface(DlISize(800, 600));
2080 builder.SetRenderTaskRunner(
2081 &render_task_runner.GetFlutterTaskRunnerDescription());
2082
2083 {
2084 std::scoped_lock lock(engine_mutex);
2085 engine = builder.InitializeEngine();
2086 }
2087
2088 ASSERT_EQ(FlutterEngineRunInitialized(engine.get()), kSuccess);
2089
2090 ASSERT_TRUE(engine.is_valid());
2091
2092 FlutterWindowMetricsEvent event = {};
2093 event.struct_size = sizeof(event);
2094 event.width = 800;
2095 event.height = 600;
2096 event.pixel_ratio = 1.0;
2097 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
2098 kSuccess);
2099 task_latch.Wait();
2100 ASSERT_TRUE(task_executed);
2101 ASSERT_EQ(FlutterEngineDeinitialize(engine.get()), kSuccess);
2102
2103 {
2104 std::scoped_lock engine_lock(engine_mutex);
2105 engine.reset();
2106 }
2107}
2108
2109//------------------------------------------------------------------------------
2110/// Asserts that the render task runner can be the same as the platform task
2111/// runner.
2112///
2114 CanCreateEmbedderWithCustomRenderTaskRunnerTheSameAsPlatformTaskRunner) {
2115 // A new thread needs to be created for the platform thread because the test
2116 // can't wait for assertions to be completed on the same thread that services
2117 // platform task runner tasks.
2118 auto platform_task_runner = CreateNewThread("platform_thread");
2119
2120 static std::mutex engine_mutex;
2121 static UniqueEngine engine;
2122 fml::AutoResetWaitableEvent task_latch;
2123 bool task_executed = false;
2124 EmbedderTestTaskRunner common_task_runner(
2125 platform_task_runner, [&](FlutterTask task) {
2126 std::scoped_lock engine_lock(engine_mutex);
2127 if (engine.is_valid()) {
2128 ASSERT_EQ(FlutterEngineRunTask(engine.get(), &task), kSuccess);
2129 task_executed = true;
2130 task_latch.Signal();
2131 }
2132 });
2133
2134 platform_task_runner->PostTask([&]() {
2135 EmbedderTestContextType backend = GetParam();
2136 auto& context = GetEmbedderContext(backend);
2137 EmbedderConfigBuilder builder(context);
2138 builder.SetDartEntrypoint("can_render_scene_without_custom_compositor");
2139 builder.SetSurface(DlISize(800, 600));
2140 builder.SetRenderTaskRunner(
2141 &common_task_runner.GetFlutterTaskRunnerDescription());
2142 builder.SetPlatformTaskRunner(
2143 &common_task_runner.GetFlutterTaskRunnerDescription());
2144
2145 {
2146 std::scoped_lock lock(engine_mutex);
2147 engine = builder.InitializeEngine();
2148 }
2149
2150 ASSERT_EQ(FlutterEngineRunInitialized(engine.get()), kSuccess);
2151
2152 ASSERT_TRUE(engine.is_valid());
2153
2154 FlutterWindowMetricsEvent event = {};
2155 event.struct_size = sizeof(event);
2156 event.width = 800;
2157 event.height = 600;
2158 event.pixel_ratio = 1.0;
2159 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
2160 kSuccess);
2161 });
2162
2163 task_latch.Wait();
2164
2165 // Don't use the task latch because that may be called multiple time
2166 // (including during the shutdown process).
2167 fml::AutoResetWaitableEvent shutdown_latch;
2168
2169 platform_task_runner->PostTask([&]() {
2170 ASSERT_TRUE(task_executed);
2171 ASSERT_EQ(FlutterEngineDeinitialize(engine.get()), kSuccess);
2172
2173 {
2174 std::scoped_lock engine_lock(engine_mutex);
2175 engine.reset();
2176 }
2177 shutdown_latch.Signal();
2178 });
2179
2180 shutdown_latch.Wait();
2181
2182 {
2183 std::scoped_lock engine_lock(engine_mutex);
2184 // Engine should have been killed by this point.
2185 ASSERT_FALSE(engine.is_valid());
2186 }
2187}
2188
2190 CompositorMustBeAbleToRenderKnownScenePixelRatioOnSurface) {
2191 EmbedderTestContextType backend = GetParam();
2192 auto& context = GetEmbedderContext(backend);
2193
2194 EmbedderConfigBuilder builder(context);
2195 builder.SetSurface(DlISize(800, 600));
2196 builder.SetCompositor();
2197 builder.SetDartEntrypoint("can_display_platform_view_with_pixel_ratio");
2198
2199 builder.SetRenderTargetType(GetRenderTargetFromBackend(backend, false));
2200
2201 fml::CountDownLatch latch(1);
2202
2203 auto rendered_scene = context.GetNextSceneImage();
2204
2205 context.GetCompositor().SetNextPresentCallback(
2207 size_t layers_count) {
2208 ASSERT_EQ(layers_count, 3u);
2209
2210 // Layer 0 (Root)
2211 {
2212 FlutterBackingStore backing_store = *layers[0]->backing_store;
2213 backing_store.did_update = true;
2214 ConfigureBackingStore(backing_store, backend, false);
2215
2216 FlutterRect paint_region_rects[] = {
2217 FlutterRectMakeLTRB(0, 0, 800, 600),
2218 };
2219 FlutterRegion paint_region = {
2220 .struct_size = sizeof(FlutterRegion),
2221 .rects_count = 1,
2222 .rects = paint_region_rects,
2223 };
2224 FlutterBackingStorePresentInfo present_info = {
2226 .paint_region = &paint_region,
2227 };
2228
2229 FlutterLayer layer = {};
2230 layer.struct_size = sizeof(layer);
2232 layer.backing_store = &backing_store;
2233 layer.size = FlutterSizeMake(800.0, 600.0);
2234 layer.offset = FlutterPointMake(0.0, 0.0);
2235 layer.backing_store_present_info = &present_info;
2236
2237 ASSERT_EQ(*layers[0], layer);
2238 }
2239
2240 // Layer 1
2241 {
2244 platform_view.identifier = 42;
2245
2246 FlutterLayer layer = {};
2247 layer.struct_size = sizeof(layer);
2250 layer.size = FlutterSizeMake(800.0, 560.0);
2251 layer.offset = FlutterPointMake(0.0, 40.0);
2252
2253 ASSERT_EQ(*layers[1], layer);
2254 }
2255
2256 // Layer 2
2257 {
2258 FlutterBackingStore backing_store = *layers[2]->backing_store;
2259 backing_store.did_update = true;
2260 ConfigureBackingStore(backing_store, backend, false);
2261
2262 FlutterRect paint_region_rects[] = {
2263 FlutterRectMakeLTRB(0, 0, 800, 600),
2264 };
2265 FlutterRegion paint_region = {
2266 .struct_size = sizeof(FlutterRegion),
2267 .rects_count = 1,
2268 .rects = paint_region_rects,
2269 };
2270 FlutterBackingStorePresentInfo present_info = {
2272 .paint_region = &paint_region,
2273 };
2274
2275 FlutterLayer layer = {};
2276 layer.struct_size = sizeof(layer);
2278 layer.backing_store = &backing_store;
2279 layer.size = FlutterSizeMake(800.0, 600.0);
2280 layer.offset = FlutterPointMake(0.0, 0.0);
2281 layer.backing_store_present_info = &present_info;
2282
2283 ASSERT_EQ(*layers[2], layer);
2284 }
2285
2286 latch.CountDown();
2287 });
2288
2289 auto engine = builder.LaunchEngine();
2290
2291 // Send a window metrics events so frames may be scheduled.
2292 FlutterWindowMetricsEvent event = {};
2293 event.struct_size = sizeof(event);
2294 event.width = 400 * 2.0;
2295 event.height = 300 * 2.0;
2296 event.pixel_ratio = 2.0;
2297 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
2298 kSuccess);
2299 ASSERT_TRUE(engine.is_valid());
2300
2301 latch.Wait();
2302
2303 ASSERT_TRUE(ImageMatchesFixture(
2304 FixtureNameForBackend(backend, "dpr_noxform.png"), rendered_scene));
2305}
2306
2309 CompositorMustBeAbleToRenderKnownScenePixelRatioOnSurfaceWithRootSurfaceXformation) {
2310 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
2311
2312 EmbedderConfigBuilder builder(context);
2313 builder.SetSurface(DlISize(600, 800));
2314 builder.SetCompositor();
2315 builder.SetDartEntrypoint("can_display_platform_view_with_pixel_ratio");
2316
2317 builder.SetRenderTargetType(
2319
2320 const auto root_surface_transformation =
2321 DlMatrix::MakeTranslation({0, 800}) *
2323
2324 context.SetRootSurfaceTransformation(root_surface_transformation);
2325
2326 auto rendered_scene = context.GetNextSceneImage();
2327 fml::CountDownLatch latch(1);
2328
2329 context.GetCompositor().SetNextPresentCallback(
2331 size_t layers_count) {
2332 ASSERT_EQ(layers_count, 3u);
2333
2334 // Layer 0 (Root)
2335 {
2336 FlutterBackingStore backing_store = *layers[0]->backing_store;
2337 backing_store.type = kFlutterBackingStoreTypeOpenGL;
2338 backing_store.did_update = true;
2340
2341 FlutterRect paint_region_rects[] = {
2342 FlutterRectMakeLTRB(0, 0, 600, 800),
2343 };
2344 FlutterRegion paint_region = {
2345 .struct_size = sizeof(FlutterRegion),
2346 .rects_count = 1,
2347 .rects = paint_region_rects,
2348 };
2349 FlutterBackingStorePresentInfo present_info = {
2351 .paint_region = &paint_region,
2352 };
2353
2354 FlutterLayer layer = {};
2355 layer.struct_size = sizeof(layer);
2357 layer.backing_store = &backing_store;
2358 layer.size = FlutterSizeMake(600.0, 800.0);
2359 layer.offset = FlutterPointMake(0.0, 0.0);
2360 layer.backing_store_present_info = &present_info;
2361
2362 ASSERT_EQ(*layers[0], layer);
2363 }
2364
2365 // Layer 1
2366 {
2369 platform_view.identifier = 42;
2370
2371 FlutterLayer layer = {};
2372 layer.struct_size = sizeof(layer);
2375 layer.size = FlutterSizeMake(560.0, 800.0);
2376 layer.offset = FlutterPointMake(40.0, 0.0);
2377
2378 ASSERT_EQ(*layers[1], layer);
2379 }
2380
2381 // Layer 2
2382 {
2383 FlutterBackingStore backing_store = *layers[2]->backing_store;
2384 backing_store.type = kFlutterBackingStoreTypeOpenGL;
2385 backing_store.did_update = true;
2387
2388 FlutterRect paint_region_rects[] = {
2389 FlutterRectMakeLTRB(0, 0, 600, 800),
2390 };
2391 FlutterRegion paint_region = {
2392 .struct_size = sizeof(FlutterRegion),
2393 .rects_count = 1,
2394 .rects = paint_region_rects,
2395 };
2396 FlutterBackingStorePresentInfo present_info = {
2398 .paint_region = &paint_region,
2399 };
2400
2401 FlutterLayer layer = {};
2402 layer.struct_size = sizeof(layer);
2404 layer.backing_store = &backing_store;
2405 layer.size = FlutterSizeMake(600.0, 800.0);
2406 layer.offset = FlutterPointMake(0.0, 0.0);
2407 layer.backing_store_present_info = &present_info;
2408
2409 ASSERT_EQ(*layers[2], layer);
2410 }
2411
2412 latch.CountDown();
2413 });
2414
2415 auto engine = builder.LaunchEngine();
2416
2417 // Send a window metrics events so frames may be scheduled.
2418 FlutterWindowMetricsEvent event = {};
2419 event.struct_size = sizeof(event);
2420 event.width = 400 * 2.0;
2421 event.height = 300 * 2.0;
2422 event.pixel_ratio = 2.0;
2423 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
2424 kSuccess);
2425 ASSERT_TRUE(engine.is_valid());
2426
2427 latch.Wait();
2428
2429 ASSERT_TRUE(ImageMatchesFixture("dpr_xform.png", rendered_scene));
2430}
2431
2433 PushingMutlipleFramesSetsUpNewRecordingCanvasWithCustomCompositor) {
2434 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
2435
2436 EmbedderConfigBuilder builder(context);
2437 builder.SetSurface(DlISize(600, 1024));
2438 builder.SetCompositor();
2439 builder.SetDartEntrypoint("push_frames_over_and_over");
2440
2441 builder.SetRenderTargetType(
2443
2444 const auto root_surface_transformation =
2445 DlMatrix::MakeTranslation({0, 1024}) *
2447
2448 context.SetRootSurfaceTransformation(root_surface_transformation);
2449
2450 auto engine = builder.LaunchEngine();
2451
2452 // Send a window metrics events so frames may be scheduled.
2453 FlutterWindowMetricsEvent event = {};
2454 event.struct_size = sizeof(event);
2455 event.width = 1024;
2456 event.height = 600;
2457 event.pixel_ratio = 1.0;
2458 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
2459 kSuccess);
2460 ASSERT_TRUE(engine.is_valid());
2461
2462 constexpr size_t frames_expected = 10;
2463 fml::CountDownLatch frame_latch(frames_expected);
2464 std::atomic_size_t frames_seen = 0;
2465 context.AddNativeCallback("SignalNativeTest",
2466 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
2467 frames_seen++;
2468 frame_latch.CountDown();
2469 }));
2470 frame_latch.Wait();
2471
2472 ASSERT_GE(frames_seen, frames_expected);
2473
2474 FlutterEngineShutdown(engine.release());
2475}
2476
2478 PushingMutlipleFramesSetsUpNewRecordingCanvasWithoutCustomCompositor) {
2479 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
2480
2481 EmbedderConfigBuilder builder(context);
2482 builder.SetSurface(DlISize(600, 1024));
2483 builder.SetDartEntrypoint("push_frames_over_and_over");
2484
2485 const auto root_surface_transformation =
2486 DlMatrix::MakeTranslation({0, 1024}) *
2488
2489 context.SetRootSurfaceTransformation(root_surface_transformation);
2490
2491 auto engine = builder.LaunchEngine();
2492
2493 // Send a window metrics events so frames may be scheduled.
2494 FlutterWindowMetricsEvent event = {};
2495 event.struct_size = sizeof(event);
2496 event.width = 1024;
2497 event.height = 600;
2498 event.pixel_ratio = 1.0;
2499 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
2500 kSuccess);
2501 ASSERT_TRUE(engine.is_valid());
2502
2503 constexpr size_t frames_expected = 10;
2504 fml::CountDownLatch frame_latch(frames_expected);
2505 std::atomic_size_t frames_seen = 0;
2506 context.AddNativeCallback("SignalNativeTest",
2507 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
2508 frames_seen++;
2509 frame_latch.CountDown();
2510 }));
2511 frame_latch.Wait();
2512
2513 ASSERT_GE(frames_seen, frames_expected);
2514
2515 FlutterEngineShutdown(engine.release());
2516}
2517
2518TEST_P(EmbedderTestMultiBackend, PlatformViewMutatorsAreValid) {
2519 EmbedderTestContextType backend = GetParam();
2520 auto& context = GetEmbedderContext(backend);
2521
2522 EmbedderConfigBuilder builder(context);
2523 builder.SetSurface(DlISize(800, 600));
2524 builder.SetCompositor();
2525 builder.SetDartEntrypoint("platform_view_mutators");
2526
2527 builder.SetRenderTargetType(GetRenderTargetFromBackend(backend, false));
2528
2529 fml::CountDownLatch latch(1);
2530 context.GetCompositor().SetNextPresentCallback(
2532 size_t layers_count) {
2533 ASSERT_EQ(layers_count, 2u);
2534
2535 // Layer 0 (Root)
2536 {
2537 FlutterBackingStore backing_store = *layers[0]->backing_store;
2538 backing_store.did_update = true;
2539 ConfigureBackingStore(backing_store, backend, false);
2540
2541 FlutterRect paint_region_rects[] = {
2542 FlutterRectMakeLTRB(0, 0, 800, 600),
2543 };
2544 FlutterRegion paint_region = {
2545 .struct_size = sizeof(FlutterRegion),
2546 .rects_count = 1,
2547 .rects = paint_region_rects,
2548 };
2549 FlutterBackingStorePresentInfo present_info = {
2551 .paint_region = &paint_region,
2552 };
2553
2554 FlutterLayer layer = {};
2555 layer.struct_size = sizeof(layer);
2557 layer.backing_store = &backing_store;
2558 layer.size = FlutterSizeMake(800.0, 600.0);
2559 layer.offset = FlutterPointMake(0.0, 0.0);
2560 layer.backing_store_present_info = &present_info;
2561
2562 ASSERT_EQ(*layers[0], layer);
2563 }
2564
2565 // Layer 2
2566 {
2569 platform_view.identifier = 42;
2570 platform_view.mutations_count = 3;
2571
2572 FlutterLayer layer = {};
2573 layer.struct_size = sizeof(layer);
2576 layer.size = FlutterSizeMake(800.0, 600.0);
2577 layer.offset = FlutterPointMake(0.0, 0.0);
2578
2579 ASSERT_EQ(*layers[1], layer);
2580
2581 // There are no ordering guarantees.
2582 for (size_t i = 0; i < platform_view.mutations_count; i++) {
2583 FlutterPlatformViewMutation mutation = *platform_view.mutations[i];
2584 switch (mutation.type) {
2586 mutation.clip_rounded_rect =
2587 FlutterRoundedRectMake(SkRRect::MakeRectXY(
2588 SkRect::MakeLTRB(10.0, 10.0, 800.0 - 10.0,
2589 600.0 - 10.0),
2590 14.0, 14.0));
2591 break;
2594 mutation.clip_rect = FlutterRectMake(
2595 DlRect::MakeXYWH(10.0, 10.0, 800.0 - 20.0, 600.0 - 20.0));
2596 break;
2599 mutation.opacity = 128.0 / 255.0;
2600 break;
2602 FML_CHECK(false)
2603 << "There should be no transformation in the test.";
2604 break;
2605 }
2606
2607 ASSERT_EQ(*platform_view.mutations[i], mutation);
2608 }
2609 }
2610 latch.CountDown();
2611 });
2612
2613 auto engine = builder.LaunchEngine();
2614
2615 // Send a window metrics events so frames may be scheduled.
2616 FlutterWindowMetricsEvent event = {};
2617 event.struct_size = sizeof(event);
2618 event.width = 800;
2619 event.height = 600;
2620 event.pixel_ratio = 1.0;
2621 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
2622 kSuccess);
2623 ASSERT_TRUE(engine.is_valid());
2624
2625 latch.Wait();
2626}
2627
2628TEST_F(EmbedderTest, PlatformViewMutatorsAreValidWithPixelRatio) {
2629 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
2630
2631 EmbedderConfigBuilder builder(context);
2632 builder.SetSurface(DlISize(800, 600));
2633 builder.SetCompositor();
2634 builder.SetDartEntrypoint("platform_view_mutators_with_pixel_ratio");
2635
2636 builder.SetRenderTargetType(
2638
2639 fml::CountDownLatch latch(1);
2640 context.GetCompositor().SetNextPresentCallback(
2642 size_t layers_count) {
2643 ASSERT_EQ(layers_count, 2u);
2644
2645 // Layer 0 (Root)
2646 {
2647 FlutterBackingStore backing_store = *layers[0]->backing_store;
2648 backing_store.type = kFlutterBackingStoreTypeOpenGL;
2649 backing_store.did_update = true;
2651
2652 FlutterRect paint_region_rects[] = {
2653 FlutterRectMakeLTRB(0, 0, 800, 600),
2654 };
2655 FlutterRegion paint_region = {
2656 .struct_size = sizeof(FlutterRegion),
2657 .rects_count = 1,
2658 .rects = paint_region_rects,
2659 };
2660 FlutterBackingStorePresentInfo present_info = {
2662 .paint_region = &paint_region,
2663 };
2664
2665 FlutterLayer layer = {};
2666 layer.struct_size = sizeof(layer);
2668 layer.backing_store = &backing_store;
2669 layer.size = FlutterSizeMake(800.0, 600.0);
2670 layer.offset = FlutterPointMake(0.0, 0.0);
2671 layer.backing_store_present_info = &present_info;
2672
2673 ASSERT_EQ(*layers[0], layer);
2674 }
2675
2676 // Layer 2
2677 {
2680 platform_view.identifier = 42;
2681 platform_view.mutations_count = 3;
2682
2683 FlutterLayer layer = {};
2684 layer.struct_size = sizeof(layer);
2687 layer.size = FlutterSizeMake(800.0, 600.0);
2688 layer.offset = FlutterPointMake(0.0, 0.0);
2689
2690 ASSERT_EQ(*layers[1], layer);
2691
2692 // There are no ordering guarantees.
2693 for (size_t i = 0; i < platform_view.mutations_count; i++) {
2694 FlutterPlatformViewMutation mutation = *platform_view.mutations[i];
2695 switch (mutation.type) {
2697 mutation.clip_rounded_rect =
2698 FlutterRoundedRectMake(SkRRect::MakeRectXY(
2699 SkRect::MakeLTRB(5.0, 5.0, 400.0 - 5.0, 300.0 - 5.0),
2700 7.0, 7.0));
2701 break;
2704 mutation.clip_rect = FlutterRectMake(
2705 DlRect::MakeXYWH(5.0, 5.0, 400.0 - 10.0, 300.0 - 10.0));
2706 break;
2709 mutation.opacity = 128.0 / 255.0;
2710 break;
2714 DlMatrix::MakeScale({2.0, 2.0, 1}));
2715 break;
2716 }
2717
2718 ASSERT_EQ(*platform_view.mutations[i], mutation);
2719 }
2720 }
2721 latch.CountDown();
2722 });
2723
2724 auto engine = builder.LaunchEngine();
2725
2726 // Send a window metrics events so frames may be scheduled.
2727 FlutterWindowMetricsEvent event = {};
2728 event.struct_size = sizeof(event);
2729 event.width = 800;
2730 event.height = 600;
2731 event.pixel_ratio = 2.0;
2732 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
2733 kSuccess);
2734 ASSERT_TRUE(engine.is_valid());
2735
2736 latch.Wait();
2737}
2738
2740 PlatformViewMutatorsAreValidWithPixelRatioAndRootSurfaceTransformation) {
2741 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
2742
2743 EmbedderConfigBuilder builder(context);
2744 builder.SetSurface(DlISize(800, 600));
2745 builder.SetCompositor();
2746 builder.SetDartEntrypoint("platform_view_mutators_with_pixel_ratio");
2747
2748 builder.SetRenderTargetType(
2750
2751 static const auto root_surface_transformation =
2752 DlMatrix::MakeTranslation({0, 800}) *
2754
2755 context.SetRootSurfaceTransformation(root_surface_transformation);
2756
2757 fml::CountDownLatch latch(1);
2758 context.GetCompositor().SetNextPresentCallback(
2760 size_t layers_count) {
2761 ASSERT_EQ(layers_count, 2u);
2762
2763 // Layer 0 (Root)
2764 {
2765 FlutterBackingStore backing_store = *layers[0]->backing_store;
2766 backing_store.type = kFlutterBackingStoreTypeOpenGL;
2767 backing_store.did_update = true;
2769
2770 FlutterRect paint_region_rects[] = {
2771 FlutterRectMakeLTRB(0, 0, 600, 800),
2772 };
2773 FlutterRegion paint_region = {
2774 .struct_size = sizeof(FlutterRegion),
2775 .rects_count = 1,
2776 .rects = paint_region_rects,
2777 };
2778 FlutterBackingStorePresentInfo present_info = {
2780 .paint_region = &paint_region,
2781 };
2782
2783 FlutterLayer layer = {};
2784 layer.struct_size = sizeof(layer);
2786 layer.backing_store = &backing_store;
2787 layer.size = FlutterSizeMake(600.0, 800.0);
2788 layer.offset = FlutterPointMake(0.0, 0.0);
2789 layer.backing_store_present_info = &present_info;
2790
2791 ASSERT_EQ(*layers[0], layer);
2792 }
2793
2794 // Layer 2
2795 {
2798 platform_view.identifier = 42;
2799 platform_view.mutations_count = 4;
2800
2801 FlutterLayer layer = {};
2802 layer.struct_size = sizeof(layer);
2805 layer.size = FlutterSizeMake(600.0, 800.0);
2806 layer.offset = FlutterPointMake(0.0, 0.0);
2807
2808 ASSERT_EQ(*layers[1], layer);
2809
2810 // There are no ordering guarantees.
2811 for (size_t i = 0; i < platform_view.mutations_count; i++) {
2812 FlutterPlatformViewMutation mutation = *platform_view.mutations[i];
2813 switch (mutation.type) {
2815 mutation.clip_rounded_rect =
2816 FlutterRoundedRectMake(SkRRect::MakeRectXY(
2817 SkRect::MakeLTRB(5.0, 5.0, 400.0 - 5.0, 300.0 - 5.0),
2818 7.0, 7.0));
2819 break;
2822 mutation.clip_rect = FlutterRectMake(
2823 DlRect::MakeXYWH(5.0, 5.0, 400.0 - 10.0, 300.0 - 10.0));
2824 break;
2827 mutation.opacity = 128.0 / 255.0;
2828 break;
2831 mutation.transformation =
2832 FlutterTransformationMake(root_surface_transformation);
2833
2834 break;
2835 }
2836
2837 ASSERT_EQ(*platform_view.mutations[i], mutation);
2838 }
2839 }
2840 latch.CountDown();
2841 });
2842
2843 auto engine = builder.LaunchEngine();
2844
2845 // Send a window metrics events so frames may be scheduled.
2846 FlutterWindowMetricsEvent event = {};
2847 event.struct_size = sizeof(event);
2848 event.width = 800;
2849 event.height = 600;
2850 event.pixel_ratio = 2.0;
2851 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
2852 kSuccess);
2853 ASSERT_TRUE(engine.is_valid());
2854
2855 latch.Wait();
2856}
2857
2858TEST_F(EmbedderTest, EmptySceneIsAcceptable) {
2859 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
2860
2861 EmbedderConfigBuilder builder(context);
2862 builder.SetSurface(DlISize(800, 600));
2863 builder.SetCompositor();
2864 builder.SetDartEntrypoint("empty_scene");
2866 context.AddNativeCallback(
2867 "SignalNativeTest",
2868 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { latch.Signal(); }));
2869
2870 auto engine = builder.LaunchEngine();
2871
2872 ASSERT_TRUE(engine.is_valid());
2873
2874 FlutterWindowMetricsEvent event = {};
2875 event.struct_size = sizeof(event);
2876 event.width = 800;
2877 event.height = 600;
2878 event.pixel_ratio = 1.0;
2879 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
2880 kSuccess);
2881 latch.Wait();
2882}
2883
2884TEST_F(EmbedderTest, SceneWithNoRootContainerIsAcceptable) {
2885 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
2886
2887 EmbedderConfigBuilder builder(context);
2888 builder.SetSurface(DlISize(800, 600));
2889 builder.SetCompositor();
2890 builder.SetRenderTargetType(
2892 builder.SetDartEntrypoint("scene_with_no_container");
2894 context.AddNativeCallback(
2895 "SignalNativeTest",
2896 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { latch.Signal(); }));
2897
2898 auto engine = builder.LaunchEngine();
2899
2900 ASSERT_TRUE(engine.is_valid());
2901
2902 FlutterWindowMetricsEvent event = {};
2903 event.struct_size = sizeof(event);
2904 event.width = 800;
2905 event.height = 600;
2906 event.pixel_ratio = 1.0;
2907 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
2908 kSuccess);
2909 latch.Wait();
2910}
2911
2912// Verifies that https://skia-review.googlesource.com/c/skia/+/259174 is pulled
2913// into the engine.
2914TEST_F(EmbedderTest, ArcEndCapsAreDrawnCorrectly) {
2915 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
2916
2917 EmbedderConfigBuilder builder(context);
2918 builder.SetSurface(DlISize(800, 1024));
2919 builder.SetCompositor();
2920 builder.SetDartEntrypoint("arc_end_caps_correct");
2921 builder.SetRenderTargetType(
2923
2924 const auto root_surface_transformation =
2925 DlMatrix::MakeScale({1.0, -1.0, 1}) *
2926 DlMatrix::MakeTranslation({1024.0f, -800.0f}) *
2928
2929 context.SetRootSurfaceTransformation(root_surface_transformation);
2930
2931 auto engine = builder.LaunchEngine();
2932
2933 auto scene_image = context.GetNextSceneImage();
2934
2935 ASSERT_TRUE(engine.is_valid());
2936
2937 FlutterWindowMetricsEvent event = {};
2938 event.struct_size = sizeof(event);
2939 event.width = 800;
2940 event.height = 1024;
2941 event.pixel_ratio = 1.0;
2942 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
2943 kSuccess);
2944
2945 ASSERT_TRUE(ImageMatchesFixture("arc_end_caps.png", scene_image));
2946}
2947
2948TEST_F(EmbedderTest, ClipsAreCorrectlyCalculated) {
2949 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
2950
2951 EmbedderConfigBuilder builder(context);
2952 builder.SetSurface(DlISize(400, 300));
2953 builder.SetCompositor();
2954 builder.SetDartEntrypoint("scene_builder_with_clips");
2955 builder.SetRenderTargetType(
2957
2958 const auto root_surface_transformation =
2959 DlMatrix::MakeTranslation({0, 400}) *
2961
2962 context.SetRootSurfaceTransformation(root_surface_transformation);
2963
2965 context.GetCompositor().SetNextPresentCallback(
2967 size_t layers_count) {
2968 ASSERT_EQ(layers_count, 2u);
2969
2970 {
2973 platform_view.identifier = 42;
2974
2975 FlutterLayer layer = {};
2976 layer.struct_size = sizeof(layer);
2979 layer.size = FlutterSizeMake(300.0, 400.0);
2980 layer.offset = FlutterPointMake(0.0, 0.0);
2981
2982 ASSERT_EQ(*layers[0], layer);
2983
2984 bool clip_assertions_checked = false;
2985
2986 // The total transformation on the stack upto the platform view.
2987 const auto total_xformation =
2989
2993 [&](const auto& mutation) {
2994 FlutterRect clip = mutation.clip_rect;
2995
2996 // The test is only set up to supply one clip. Make sure it is
2997 // the one we expect.
2998 const auto rect_to_compare =
2999 DlRect::MakeLTRB(10.0, 10.0, 390, 290);
3000 ASSERT_EQ(clip, FlutterRectMake(rect_to_compare));
3001
3002 // This maps the clip from device space into surface space.
3003 ASSERT_TRUE(total_xformation.IsAligned2D());
3004 DlRect mapped =
3005 rect_to_compare.TransformAndClipBounds(total_xformation);
3006 ASSERT_EQ(mapped, DlRect::MakeLTRB(10, 10, 290, 390));
3007 clip_assertions_checked = true;
3008 });
3009
3010 ASSERT_TRUE(clip_assertions_checked);
3011 }
3012
3013 latch.Signal();
3014 });
3015
3016 auto engine = builder.LaunchEngine();
3017 ASSERT_TRUE(engine.is_valid());
3018
3019 FlutterWindowMetricsEvent event = {};
3020 event.struct_size = sizeof(event);
3021 event.width = 400;
3022 event.height = 300;
3023 event.pixel_ratio = 1.0;
3024 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
3025 kSuccess);
3026
3027 latch.Wait();
3028}
3029
3030TEST_F(EmbedderTest, ComplexClipsAreCorrectlyCalculated) {
3031 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
3032
3033 EmbedderConfigBuilder builder(context);
3034 builder.SetSurface(DlISize(1024, 600));
3035 builder.SetCompositor();
3036 builder.SetDartEntrypoint("scene_builder_with_complex_clips");
3037 builder.SetRenderTargetType(
3039
3040 const auto root_surface_transformation =
3041 DlMatrix::MakeTranslation({0, 1024}) *
3043
3044 context.SetRootSurfaceTransformation(root_surface_transformation);
3045
3047 context.GetCompositor().SetNextPresentCallback(
3049 size_t layers_count) {
3050 ASSERT_EQ(layers_count, 2u);
3051
3052 {
3055 platform_view.identifier = 42;
3056
3057 FlutterLayer layer = {};
3058 layer.struct_size = sizeof(layer);
3061 layer.size = FlutterSizeMake(600.0, 1024.0);
3062 layer.offset = FlutterPointMake(0.0, -256.0);
3063
3064 ASSERT_EQ(*layers[0], layer);
3065
3066 const auto** mutations = platform_view.mutations;
3067
3068 ASSERT_EQ(mutations[0]->type,
3070 ASSERT_EQ(DlMatrixMake(mutations[0]->transformation),
3071 root_surface_transformation);
3072
3073 ASSERT_EQ(mutations[1]->type,
3075 ASSERT_EQ(SkRectMake(mutations[1]->clip_rect),
3076 SkRect::MakeLTRB(0.0, 0.0, 1024.0, 600.0));
3077
3078 ASSERT_EQ(mutations[2]->type,
3080 ASSERT_EQ(DlMatrixMake(mutations[2]->transformation),
3081 DlMatrix::MakeTranslation({512.0, 0.0}));
3082
3083 ASSERT_EQ(mutations[3]->type,
3085 ASSERT_EQ(SkRectMake(mutations[3]->clip_rect),
3086 SkRect::MakeLTRB(0.0, 0.0, 512.0, 600.0));
3087
3088 ASSERT_EQ(mutations[4]->type,
3090 ASSERT_EQ(DlMatrixMake(mutations[4]->transformation),
3091 DlMatrix::MakeTranslation({-256.0, 0.0}));
3092
3093 ASSERT_EQ(mutations[5]->type,
3095 ASSERT_EQ(SkRectMake(mutations[5]->clip_rect),
3096 SkRect::MakeLTRB(0.0, 0.0, 1024.0, 600.0));
3097 }
3098
3099 latch.Signal();
3100 });
3101
3102 auto engine = builder.LaunchEngine();
3103 ASSERT_TRUE(engine.is_valid());
3104
3105 FlutterWindowMetricsEvent event = {};
3106 event.struct_size = sizeof(event);
3107 event.width = 1024;
3108 event.height = 600;
3109 event.pixel_ratio = 1.0;
3110 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
3111 kSuccess);
3112
3113 latch.Wait();
3114}
3115
3116TEST_F(EmbedderTest, ObjectsCanBePostedViaPorts) {
3117 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
3118 EmbedderConfigBuilder builder(context);
3119 builder.SetSurface(DlISize(800, 1024));
3120 builder.SetDartEntrypoint("objects_can_be_posted");
3121
3122 // Synchronously acquire the send port from the Dart end. We will be using
3123 // this to send message. The Dart end will just echo those messages back to us
3124 // for inspection.
3125 FlutterEngineDartPort port = 0;
3127 context.AddNativeCallback("SignalNativeCount",
3128 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
3130 Dart_GetNativeArgument(args, 0));
3131 event.Signal();
3132 }));
3133 auto engine = builder.LaunchEngine();
3134 ASSERT_TRUE(engine.is_valid());
3135 event.Wait();
3136 ASSERT_NE(port, 0);
3137
3138 using Trampoline = std::function<void(Dart_Handle message)>;
3139 Trampoline trampoline;
3140
3141 context.AddNativeCallback("SendObjectToNativeCode",
3142 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
3143 FML_CHECK(trampoline);
3144 auto trampoline_copy = trampoline;
3145 trampoline = nullptr;
3146 trampoline_copy(Dart_GetNativeArgument(args, 0));
3147 }));
3148
3149 // Check null.
3150 {
3151 FlutterEngineDartObject object = {};
3153 trampoline = [&](Dart_Handle handle) {
3154 ASSERT_TRUE(Dart_IsNull(handle));
3155 event.Signal();
3156 };
3157 ASSERT_EQ(FlutterEnginePostDartObject(engine.get(), port, &object),
3158 kSuccess);
3159 event.Wait();
3160 }
3161
3162 // Check bool.
3163 {
3164 FlutterEngineDartObject object = {};
3166 object.bool_value = true;
3167 trampoline = [&](Dart_Handle handle) {
3168 ASSERT_TRUE(tonic::DartConverter<bool>::FromDart(handle));
3169 event.Signal();
3170 };
3171 ASSERT_EQ(FlutterEnginePostDartObject(engine.get(), port, &object),
3172 kSuccess);
3173 event.Wait();
3174 }
3175
3176 // Check int32.
3177 {
3178 FlutterEngineDartObject object = {};
3180 object.int32_value = 1988;
3181 trampoline = [&](Dart_Handle handle) {
3182 ASSERT_EQ(tonic::DartConverter<int32_t>::FromDart(handle), 1988);
3183 event.Signal();
3184 };
3185 ASSERT_EQ(FlutterEnginePostDartObject(engine.get(), port, &object),
3186 kSuccess);
3187 event.Wait();
3188 }
3189
3190 // Check int64.
3191 {
3192 FlutterEngineDartObject object = {};
3194 object.int64_value = 1988;
3195 trampoline = [&](Dart_Handle handle) {
3196 ASSERT_EQ(tonic::DartConverter<int64_t>::FromDart(handle), 1988);
3197 event.Signal();
3198 };
3199 ASSERT_EQ(FlutterEnginePostDartObject(engine.get(), port, &object),
3200 kSuccess);
3201 event.Wait();
3202 }
3203
3204 // Check double.
3205 {
3206 FlutterEngineDartObject object = {};
3208 object.double_value = 1988.0;
3209 trampoline = [&](Dart_Handle handle) {
3210 ASSERT_DOUBLE_EQ(tonic::DartConverter<double>::FromDart(handle), 1988.0);
3211 event.Signal();
3212 };
3213 ASSERT_EQ(FlutterEnginePostDartObject(engine.get(), port, &object),
3214 kSuccess);
3215 event.Wait();
3216 }
3217
3218 // Check string.
3219 {
3220 const char* message = "Hello. My name is Inigo Montoya.";
3221 FlutterEngineDartObject object = {};
3223 object.string_value = message;
3224 trampoline = [&](Dart_Handle handle) {
3226 std::string{message});
3227 event.Signal();
3228 };
3229 ASSERT_EQ(FlutterEnginePostDartObject(engine.get(), port, &object),
3230 kSuccess);
3231 event.Wait();
3232 }
3233
3234 // Check buffer (copied out).
3235 {
3236 std::vector<uint8_t> message;
3237 message.resize(1988);
3238
3239 ASSERT_TRUE(MemsetPatternSetOrCheck(
3241
3243
3244 buffer.struct_size = sizeof(buffer);
3245 buffer.user_data = nullptr;
3246 buffer.buffer_collect_callback = nullptr;
3247 buffer.buffer = message.data();
3248 buffer.buffer_size = message.size();
3249
3250 FlutterEngineDartObject object = {};
3252 object.buffer_value = &buffer;
3253 trampoline = [&](Dart_Handle handle) {
3254 intptr_t length = 0;
3255 Dart_ListLength(handle, &length);
3256 ASSERT_EQ(length, 1988);
3257 // TODO(chinmaygarde); The std::vector<uint8_t> specialization for
3258 // DartConvertor in tonic is broken which is preventing the buffer
3259 // being checked here. Fix tonic and strengthen this check. For now, just
3260 // the buffer length is checked.
3261 event.Signal();
3262 };
3263 ASSERT_EQ(FlutterEnginePostDartObject(engine.get(), port, &object),
3264 kSuccess);
3265 event.Wait();
3266 }
3267
3268 std::vector<uint8_t> message;
3269 fml::AutoResetWaitableEvent buffer_released_latch;
3270
3271 // Check buffer (caller own buffer with zero copy transfer).
3272 {
3273 message.resize(1988);
3274
3275 ASSERT_TRUE(MemsetPatternSetOrCheck(
3277
3279
3280 buffer.struct_size = sizeof(buffer);
3281 buffer.user_data = &buffer_released_latch;
3282 buffer.buffer_collect_callback = +[](void* user_data) {
3283 reinterpret_cast<fml::AutoResetWaitableEvent*>(user_data)->Signal();
3284 };
3285 buffer.buffer = message.data();
3286 buffer.buffer_size = message.size();
3287
3288 FlutterEngineDartObject object = {};
3290 object.buffer_value = &buffer;
3291 trampoline = [&](Dart_Handle handle) {
3292 intptr_t length = 0;
3293 Dart_ListLength(handle, &length);
3294 ASSERT_EQ(length, 1988);
3295 // TODO(chinmaygarde); The std::vector<uint8_t> specialization for
3296 // DartConvertor in tonic is broken which is preventing the buffer
3297 // being checked here. Fix tonic and strengthen this check. For now, just
3298 // the buffer length is checked.
3299 event.Signal();
3300 };
3301 ASSERT_EQ(FlutterEnginePostDartObject(engine.get(), port, &object),
3302 kSuccess);
3303 event.Wait();
3304 }
3305
3306 engine.reset();
3307
3308 // We cannot determine when the VM will GC objects that might have external
3309 // typed data finalizers. Since we need to ensure that we correctly wired up
3310 // finalizers from the embedders, we force the VM to collect all objects but
3311 // just shutting it down.
3312 buffer_released_latch.Wait();
3313}
3314
3315TEST_F(EmbedderTest, CompositorCanPostZeroLayersForPresentation) {
3316 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
3317
3318 EmbedderConfigBuilder builder(context);
3319 builder.SetSurface(DlISize(300, 200));
3320 builder.SetCompositor();
3321 builder.SetDartEntrypoint("empty_scene_posts_zero_layers_to_compositor");
3322 builder.SetRenderTargetType(
3324
3326
3327 context.GetCompositor().SetNextPresentCallback(
3329 size_t layers_count) {
3330 ASSERT_EQ(layers_count, 0u);
3331 latch.Signal();
3332 });
3333
3334 auto engine = builder.LaunchEngine();
3335
3336 FlutterWindowMetricsEvent event = {};
3337 event.struct_size = sizeof(event);
3338 event.width = 300;
3339 event.height = 200;
3340 event.pixel_ratio = 1.0;
3341 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
3342 kSuccess);
3343 ASSERT_TRUE(engine.is_valid());
3344 latch.Wait();
3345
3346 ASSERT_EQ(context.GetCompositor().GetPendingBackingStoresCount(), 0u);
3347}
3348
3349TEST_F(EmbedderTest, CompositorCanPostOnlyPlatformViews) {
3350 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
3351
3352 EmbedderConfigBuilder builder(context);
3353 builder.SetSurface(DlISize(300, 200));
3354 builder.SetCompositor();
3355 builder.SetDartEntrypoint("compositor_can_post_only_platform_views");
3356 builder.SetRenderTargetType(
3358
3360
3361 context.GetCompositor().SetNextPresentCallback(
3363 size_t layers_count) {
3364 ASSERT_EQ(layers_count, 2u);
3365
3366 // Layer 0
3367 {
3370 platform_view.identifier = 42;
3371 FlutterLayer layer = {};
3372 layer.struct_size = sizeof(layer);
3375 layer.size = FlutterSizeMake(300.0, 200.0);
3376 layer.offset = FlutterPointMake(0.0, 0.0);
3377
3378 ASSERT_EQ(*layers[0], layer);
3379 }
3380
3381 // Layer 1
3382 {
3385 platform_view.identifier = 24;
3386 FlutterLayer layer = {};
3387 layer.struct_size = sizeof(layer);
3390 layer.size = FlutterSizeMake(300.0, 200.0);
3391 layer.offset = FlutterPointMake(0.0, 0.0);
3392
3393 ASSERT_EQ(*layers[1], layer);
3394 }
3395 latch.Signal();
3396 });
3397
3398 auto engine = builder.LaunchEngine();
3399
3400 FlutterWindowMetricsEvent event = {};
3401 event.struct_size = sizeof(event);
3402 event.width = 300;
3403 event.height = 200;
3404 event.pixel_ratio = 1.0;
3405 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
3406 kSuccess);
3407 ASSERT_TRUE(engine.is_valid());
3408 latch.Wait();
3409
3410 ASSERT_EQ(context.GetCompositor().GetPendingBackingStoresCount(), 0u);
3411}
3412
3413TEST_F(EmbedderTest, CompositorRenderTargetsAreRecycled) {
3414 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
3415
3416 EmbedderConfigBuilder builder(context);
3417 builder.SetSurface(DlISize(300, 200));
3418 builder.SetCompositor();
3419 builder.SetDartEntrypoint("render_targets_are_recycled");
3420 builder.SetRenderTargetType(
3422
3423 fml::CountDownLatch latch(2);
3424
3425 context.AddNativeCallback("SignalNativeTest",
3426 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
3427 latch.CountDown();
3428 }));
3429
3430 context.GetCompositor().SetNextPresentCallback(
3432 size_t layers_count) {
3433 ASSERT_EQ(layers_count, 20u);
3434 latch.CountDown();
3435 });
3436
3437 auto engine = builder.LaunchEngine();
3438 ASSERT_TRUE(engine.is_valid());
3439
3440 FlutterWindowMetricsEvent event = {};
3441 event.struct_size = sizeof(event);
3442 event.width = 300;
3443 event.height = 200;
3444 event.pixel_ratio = 1.0;
3445 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
3446 kSuccess);
3447
3448 latch.Wait();
3449 ASSERT_EQ(context.GetCompositor().GetPendingBackingStoresCount(), 10u);
3450 ASSERT_EQ(context.GetCompositor().GetBackingStoresCreatedCount(), 10u);
3451 ASSERT_EQ(context.GetCompositor().GetBackingStoresCollectedCount(), 0u);
3452 // Killing the engine should immediately collect all pending render targets.
3453 engine.reset();
3454 ASSERT_EQ(context.GetCompositor().GetPendingBackingStoresCount(), 0u);
3455 ASSERT_EQ(context.GetCompositor().GetBackingStoresCreatedCount(), 10u);
3456 ASSERT_EQ(context.GetCompositor().GetBackingStoresCollectedCount(), 10u);
3457}
3458
3459TEST_F(EmbedderTest, CompositorRenderTargetsAreInStableOrder) {
3460 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
3461
3462 EmbedderConfigBuilder builder(context);
3463 builder.SetSurface(DlISize(300, 200));
3464 builder.SetCompositor();
3465 builder.SetDartEntrypoint("render_targets_are_in_stable_order");
3466 builder.SetRenderTargetType(
3468
3469 fml::CountDownLatch latch(2);
3470
3471 context.AddNativeCallback("SignalNativeTest",
3472 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
3473 latch.CountDown();
3474 }));
3475
3476 size_t frame_count = 0;
3477 std::vector<void*> first_frame_backing_store_user_data;
3478 context.GetCompositor().SetPresentCallback(
3480 size_t layers_count) {
3481 ASSERT_EQ(layers_count, 20u);
3482
3483 if (first_frame_backing_store_user_data.empty()) {
3484 for (size_t i = 0; i < layers_count; ++i) {
3486 first_frame_backing_store_user_data.push_back(
3487 layers[i]->backing_store->user_data);
3488 }
3489 }
3490 return;
3491 }
3492
3493 ASSERT_EQ(first_frame_backing_store_user_data.size(), 10u);
3494
3495 frame_count++;
3496 std::vector<void*> backing_store_user_data;
3497 for (size_t i = 0; i < layers_count; ++i) {
3499 backing_store_user_data.push_back(
3500 layers[i]->backing_store->user_data);
3501 }
3502 }
3503
3504 ASSERT_EQ(backing_store_user_data.size(), 10u);
3505
3506 ASSERT_EQ(first_frame_backing_store_user_data, backing_store_user_data);
3507
3508 if (frame_count == 20) {
3509 latch.CountDown();
3510 }
3511 },
3512 false // one shot
3513 );
3514
3515 auto engine = builder.LaunchEngine();
3516 ASSERT_TRUE(engine.is_valid());
3517
3518 FlutterWindowMetricsEvent event = {};
3519 event.struct_size = sizeof(event);
3520 event.width = 300;
3521 event.height = 200;
3522 event.pixel_ratio = 1.0;
3523 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
3524 kSuccess);
3525
3526 latch.Wait();
3527}
3528
3529TEST_F(EmbedderTest, FrameInfoContainsValidWidthAndHeight) {
3530 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
3531 const auto root_surface_transformation =
3532 DlMatrix::MakeTranslation({0, 1024}) *
3534 context.SetRootSurfaceTransformation(root_surface_transformation);
3535
3536 EmbedderConfigBuilder builder(context);
3537 builder.SetSurface(DlISize(600, 1024));
3538 builder.SetDartEntrypoint("push_frames_over_and_over");
3539 auto engine = builder.LaunchEngine();
3540
3541 // Send a window metrics events so frames may be scheduled.
3542 static FlutterWindowMetricsEvent event = {};
3543 event.struct_size = sizeof(event);
3544 event.width = 1024;
3545 event.height = 600;
3546 event.pixel_ratio = 1.0;
3547 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
3548 kSuccess);
3549 ASSERT_TRUE(engine.is_valid());
3550
3551 static fml::CountDownLatch frame_latch(10);
3552
3553 context.AddNativeCallback("SignalNativeTest",
3554 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
3555 /* Nothing to do. */
3556 }));
3557
3558 context.SetGLGetFBOCallback([](FlutterFrameInfo frame_info) {
3559 // width and height are rotated by 90 deg
3560 ASSERT_EQ(frame_info.size.width, event.height);
3561 ASSERT_EQ(frame_info.size.height, event.width);
3562
3563 frame_latch.CountDown();
3564 });
3565
3566 frame_latch.Wait();
3567}
3568
3569TEST_F(EmbedderTest, MustNotRunWithBothFBOCallbacksSet) {
3570 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
3571 context.SetOpenGLFBOCallBack();
3572
3573 EmbedderConfigBuilder builder(context);
3574 builder.SetSurface(DlISize(600, 1024));
3575
3576 auto engine = builder.LaunchEngine();
3577 ASSERT_FALSE(engine.is_valid());
3578}
3579
3580TEST_F(EmbedderTest, MustNotRunWithBothPresentCallbacksSet) {
3581 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
3582 context.SetOpenGLPresentCallBack();
3583
3584 EmbedderConfigBuilder builder(context);
3585 builder.SetSurface(DlISize(600, 1024));
3586
3587 auto engine = builder.LaunchEngine();
3588 ASSERT_FALSE(engine.is_valid());
3589}
3590
3591TEST_F(EmbedderTest, MustStillRunWhenPopulateExistingDamageIsNotProvided) {
3592 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
3593 context.GetRendererConfig().open_gl.populate_existing_damage = nullptr;
3594
3595 EmbedderConfigBuilder builder(context);
3596 builder.SetSurface(DlISize(1, 1));
3597
3598 auto engine = builder.LaunchEngine();
3599 ASSERT_TRUE(engine.is_valid());
3600}
3601
3602TEST_F(EmbedderTest, MustRunWhenPopulateExistingDamageIsProvided) {
3603 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
3604 context.GetRendererConfig().open_gl.populate_existing_damage =
3605 [](void* context, const intptr_t id,
3606 FlutterDamage* existing_damage) -> void {
3607 return reinterpret_cast<EmbedderTestContextGL*>(context)
3608 ->GLPopulateExistingDamage(id, existing_damage);
3609 };
3610
3611 EmbedderConfigBuilder builder(context);
3612 builder.SetSurface(DlISize(1, 1));
3613 auto engine = builder.LaunchEngine();
3614 ASSERT_TRUE(engine.is_valid());
3615}
3616
3617TEST_F(EmbedderTest, MustRunWithPopulateExistingDamageAndFBOCallback) {
3618 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
3619 context.GetRendererConfig().open_gl.fbo_callback =
3620 [](void* context) -> uint32_t { return 0; };
3621 context.GetRendererConfig().open_gl.fbo_with_frame_info_callback = nullptr;
3622 context.GetRendererConfig().open_gl.populate_existing_damage =
3623 [](void* context, const intptr_t id,
3624 FlutterDamage* existing_damage) -> void {
3625 return reinterpret_cast<EmbedderTestContextGL*>(context)
3626 ->GLPopulateExistingDamage(id, existing_damage);
3627 };
3628
3629 EmbedderConfigBuilder builder(context);
3630 builder.SetSurface(DlISize(1, 1));
3631 auto engine = builder.LaunchEngine();
3632 ASSERT_TRUE(engine.is_valid());
3633}
3634
3636 MustNotRunWhenPopulateExistingDamageButNoOtherFBOCallback) {
3637 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
3638 context.GetRendererConfig().open_gl.fbo_callback = nullptr;
3639 context.GetRendererConfig().open_gl.fbo_with_frame_info_callback = nullptr;
3640 context.GetRendererConfig().open_gl.populate_existing_damage =
3641 [](void* context, const intptr_t id,
3642 FlutterDamage* existing_damage) -> void {
3643 return reinterpret_cast<EmbedderTestContextGL*>(context)
3644 ->GLPopulateExistingDamage(id, existing_damage);
3645 };
3646
3647 EmbedderConfigBuilder builder(context);
3648 builder.SetSurface(DlISize(1, 1));
3649 auto engine = builder.LaunchEngine();
3650 ASSERT_FALSE(engine.is_valid());
3651}
3652
3653TEST_F(EmbedderTest, PresentInfoContainsValidFBOId) {
3654 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
3655 const auto root_surface_transformation =
3656 DlMatrix::MakeTranslation({0, 1024}) *
3658 context.SetRootSurfaceTransformation(root_surface_transformation);
3659
3660 EmbedderConfigBuilder builder(context);
3661 builder.SetSurface(DlISize(600, 1024));
3662 builder.SetDartEntrypoint("push_frames_over_and_over");
3663
3664 auto engine = builder.LaunchEngine();
3665
3666 // Send a window metrics events so frames may be scheduled.
3667 FlutterWindowMetricsEvent event = {};
3668 event.struct_size = sizeof(event);
3669 event.width = 1024;
3670 event.height = 600;
3671 event.pixel_ratio = 1.0;
3672 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
3673 kSuccess);
3674 ASSERT_TRUE(engine.is_valid());
3675
3676 static fml::CountDownLatch frame_latch(10);
3677
3678 context.AddNativeCallback("SignalNativeTest",
3679 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
3680 /* Nothing to do. */
3681 }));
3682
3683 const uint32_t window_fbo_id = context.GetWindowFBOId();
3684 context.SetGLPresentCallback(
3685 [window_fbo_id = window_fbo_id](FlutterPresentInfo present_info) {
3686 ASSERT_EQ(present_info.fbo_id, window_fbo_id);
3687
3688 frame_latch.CountDown();
3689 });
3690
3691 frame_latch.Wait();
3692}
3693
3695 PresentInfoReceivesFullDamageWhenExistingDamageIsWholeScreen) {
3696 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
3697 context.GetRendererConfig().open_gl.populate_existing_damage =
3698 [](void* context, const intptr_t id,
3699 FlutterDamage* existing_damage) -> void {
3700 return reinterpret_cast<EmbedderTestContextGL*>(context)
3701 ->GLPopulateExistingDamage(id, existing_damage);
3702 };
3703 // Return existing damage as the entire screen on purpose.
3705 [](const intptr_t id, FlutterDamage* existing_damage_ptr) {
3706 const size_t num_rects = 1;
3707 // The array must be valid after the callback returns.
3708 static FlutterRect existing_damage_rects[num_rects] = {
3709 FlutterRect{0, 0, 800, 600}};
3710 existing_damage_ptr->num_rects = num_rects;
3711 existing_damage_ptr->damage = existing_damage_rects;
3712 });
3713
3714 EmbedderConfigBuilder builder(context);
3715 builder.SetSurface(DlISize(800, 600));
3716 builder.SetDartEntrypoint("render_gradient_retained");
3717 auto engine = builder.LaunchEngine();
3718 ASSERT_TRUE(engine.is_valid());
3719
3721
3722 // First frame should be entirely rerendered.
3723 context.SetGLPresentCallback([&](FlutterPresentInfo present_info) {
3724 const size_t num_rects = 1;
3725 ASSERT_EQ(present_info.frame_damage.num_rects, num_rects);
3726 ASSERT_EQ(present_info.frame_damage.damage->left, 0);
3727 ASSERT_EQ(present_info.frame_damage.damage->top, 0);
3728 ASSERT_EQ(present_info.frame_damage.damage->right, 800);
3729 ASSERT_EQ(present_info.frame_damage.damage->bottom, 600);
3730
3731 ASSERT_EQ(present_info.buffer_damage.num_rects, num_rects);
3732 ASSERT_EQ(present_info.buffer_damage.damage->left, 0);
3733 ASSERT_EQ(present_info.buffer_damage.damage->top, 0);
3734 ASSERT_EQ(present_info.buffer_damage.damage->right, 800);
3735 ASSERT_EQ(present_info.buffer_damage.damage->bottom, 600);
3736
3737 latch.Signal();
3738 });
3739
3740 // Send a window metrics events so frames may be scheduled.
3741 FlutterWindowMetricsEvent event = {};
3742 event.struct_size = sizeof(event);
3743 event.width = 800;
3744 event.height = 600;
3745 event.pixel_ratio = 1.0;
3746 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
3747 kSuccess);
3748 latch.Wait();
3749
3750 // Because it's the same as the first frame, the second frame damage should
3751 // be empty but, because there was a full existing buffer damage, the buffer
3752 // damage should be the entire screen.
3753 context.SetGLPresentCallback([&](FlutterPresentInfo present_info) {
3754 const size_t num_rects = 1;
3755 ASSERT_EQ(present_info.frame_damage.num_rects, num_rects);
3756 ASSERT_EQ(present_info.frame_damage.damage->left, 0);
3757 ASSERT_EQ(present_info.frame_damage.damage->top, 0);
3758 ASSERT_EQ(present_info.frame_damage.damage->right, 0);
3759 ASSERT_EQ(present_info.frame_damage.damage->bottom, 0);
3760
3761 ASSERT_EQ(present_info.buffer_damage.num_rects, num_rects);
3762 ASSERT_EQ(present_info.buffer_damage.damage->left, 0);
3763 ASSERT_EQ(present_info.buffer_damage.damage->top, 0);
3764 ASSERT_EQ(present_info.buffer_damage.damage->right, 800);
3765 ASSERT_EQ(present_info.buffer_damage.damage->bottom, 600);
3766
3767 latch.Signal();
3768 });
3769
3770 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
3771 kSuccess);
3772 latch.Wait();
3773}
3774
3775TEST_F(EmbedderTest, PresentInfoReceivesEmptyDamage) {
3776 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
3777 context.GetRendererConfig().open_gl.populate_existing_damage =
3778 [](void* context, const intptr_t id,
3779 FlutterDamage* existing_damage) -> void {
3780 return reinterpret_cast<EmbedderTestContextGL*>(context)
3781 ->GLPopulateExistingDamage(id, existing_damage);
3782 };
3783 // Return no existing damage on purpose.
3785 [](const intptr_t id, FlutterDamage* existing_damage_ptr) {
3786 const size_t num_rects = 1;
3787 // The array must be valid after the callback returns.
3788 static FlutterRect existing_damage_rects[num_rects] = {
3789 FlutterRect{0, 0, 0, 0}};
3790 existing_damage_ptr->num_rects = num_rects;
3791 existing_damage_ptr->damage = existing_damage_rects;
3792 });
3793
3794 EmbedderConfigBuilder builder(context);
3795 builder.SetSurface(DlISize(800, 600));
3796 builder.SetDartEntrypoint("render_gradient_retained");
3797 auto engine = builder.LaunchEngine();
3798 ASSERT_TRUE(engine.is_valid());
3799
3801
3802 // First frame should be entirely rerendered.
3803 context.SetGLPresentCallback([&](FlutterPresentInfo present_info) {
3804 const size_t num_rects = 1;
3805 ASSERT_EQ(present_info.frame_damage.num_rects, num_rects);
3806 ASSERT_EQ(present_info.frame_damage.damage->left, 0);
3807 ASSERT_EQ(present_info.frame_damage.damage->top, 0);
3808 ASSERT_EQ(present_info.frame_damage.damage->right, 800);
3809 ASSERT_EQ(present_info.frame_damage.damage->bottom, 600);
3810
3811 ASSERT_EQ(present_info.buffer_damage.num_rects, num_rects);
3812 ASSERT_EQ(present_info.buffer_damage.damage->left, 0);
3813 ASSERT_EQ(present_info.buffer_damage.damage->top, 0);
3814 ASSERT_EQ(present_info.buffer_damage.damage->right, 800);
3815 ASSERT_EQ(present_info.buffer_damage.damage->bottom, 600);
3816
3817 latch.Signal();
3818 });
3819
3820 // Send a window metrics events so frames may be scheduled.
3821 FlutterWindowMetricsEvent event = {};
3822 event.struct_size = sizeof(event);
3823 event.width = 800;
3824 event.height = 600;
3825 event.pixel_ratio = 1.0;
3826 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
3827 kSuccess);
3828 latch.Wait();
3829
3830 // Because it's the same as the first frame, the second frame should not be
3831 // rerendered assuming there is no existing damage.
3832 context.SetGLPresentCallback([&](FlutterPresentInfo present_info) {
3833 const size_t num_rects = 1;
3834 ASSERT_EQ(present_info.frame_damage.num_rects, num_rects);
3835 ASSERT_EQ(present_info.frame_damage.damage->left, 0);
3836 ASSERT_EQ(present_info.frame_damage.damage->top, 0);
3837 ASSERT_EQ(present_info.frame_damage.damage->right, 0);
3838 ASSERT_EQ(present_info.frame_damage.damage->bottom, 0);
3839
3840 ASSERT_EQ(present_info.buffer_damage.num_rects, num_rects);
3841 ASSERT_EQ(present_info.buffer_damage.damage->left, 0);
3842 ASSERT_EQ(present_info.buffer_damage.damage->top, 0);
3843 ASSERT_EQ(present_info.buffer_damage.damage->right, 0);
3844 ASSERT_EQ(present_info.buffer_damage.damage->bottom, 0);
3845
3846 latch.Signal();
3847 });
3848
3849 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
3850 kSuccess);
3851 latch.Wait();
3852}
3853
3854TEST_F(EmbedderTest, PresentInfoReceivesPartialDamage) {
3855 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
3856 context.GetRendererConfig().open_gl.populate_existing_damage =
3857 [](void* context, const intptr_t id,
3858 FlutterDamage* existing_damage) -> void {
3859 return reinterpret_cast<EmbedderTestContextGL*>(context)
3860 ->GLPopulateExistingDamage(id, existing_damage);
3861 };
3862 // Return existing damage as only part of the screen on purpose.
3864 [&](const intptr_t id, FlutterDamage* existing_damage_ptr) {
3865 const size_t num_rects = 1;
3866 // The array must be valid after the callback returns.
3867 static FlutterRect existing_damage_rects[num_rects] = {
3868 FlutterRect{200, 150, 400, 300}};
3869 existing_damage_ptr->num_rects = num_rects;
3870 existing_damage_ptr->damage = existing_damage_rects;
3871 });
3872
3873 EmbedderConfigBuilder builder(context);
3874 builder.SetSurface(DlISize(800, 600));
3875 builder.SetDartEntrypoint("render_gradient_retained");
3876 auto engine = builder.LaunchEngine();
3877 ASSERT_TRUE(engine.is_valid());
3878
3880
3881 // First frame should be entirely rerendered.
3882 context.SetGLPresentCallback([&](FlutterPresentInfo present_info) {
3883 const size_t num_rects = 1;
3884 ASSERT_EQ(present_info.frame_damage.num_rects, num_rects);
3885 ASSERT_EQ(present_info.frame_damage.damage->left, 0);
3886 ASSERT_EQ(present_info.frame_damage.damage->top, 0);
3887 ASSERT_EQ(present_info.frame_damage.damage->right, 800);
3888 ASSERT_EQ(present_info.frame_damage.damage->bottom, 600);
3889
3890 ASSERT_EQ(present_info.buffer_damage.num_rects, num_rects);
3891 ASSERT_EQ(present_info.buffer_damage.damage->left, 0);
3892 ASSERT_EQ(present_info.buffer_damage.damage->top, 0);
3893 ASSERT_EQ(present_info.buffer_damage.damage->right, 800);
3894 ASSERT_EQ(present_info.buffer_damage.damage->bottom, 600);
3895
3896 latch.Signal();
3897 });
3898
3899 // Send a window metrics events so frames may be scheduled.
3900 FlutterWindowMetricsEvent event = {};
3901 event.struct_size = sizeof(event);
3902 event.width = 800;
3903 event.height = 600;
3904 event.pixel_ratio = 1.0;
3905 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
3906 kSuccess);
3907 latch.Wait();
3908
3909 // Because it's the same as the first frame, the second frame damage should be
3910 // empty but, because there was a partial existing damage, the buffer damage
3911 // should represent that partial damage area.
3912 context.SetGLPresentCallback([&](FlutterPresentInfo present_info) {
3913 const size_t num_rects = 1;
3914 ASSERT_EQ(present_info.frame_damage.num_rects, num_rects);
3915 ASSERT_EQ(present_info.frame_damage.damage->left, 0);
3916 ASSERT_EQ(present_info.frame_damage.damage->top, 0);
3917 ASSERT_EQ(present_info.frame_damage.damage->right, 0);
3918 ASSERT_EQ(present_info.frame_damage.damage->bottom, 0);
3919
3920 ASSERT_EQ(present_info.buffer_damage.num_rects, num_rects);
3921 ASSERT_EQ(present_info.buffer_damage.damage->left, 200);
3922 ASSERT_EQ(present_info.buffer_damage.damage->top, 150);
3923 ASSERT_EQ(present_info.buffer_damage.damage->right, 400);
3924 ASSERT_EQ(present_info.buffer_damage.damage->bottom, 300);
3925
3926 latch.Signal();
3927 });
3928
3929 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
3930 kSuccess);
3931 latch.Wait();
3932}
3933
3934TEST_F(EmbedderTest, PopulateExistingDamageReceivesValidID) {
3935 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
3936 context.GetRendererConfig().open_gl.populate_existing_damage =
3937 [](void* context, const intptr_t id,
3938 FlutterDamage* existing_damage) -> void {
3939 return reinterpret_cast<EmbedderTestContextGL*>(context)
3940 ->GLPopulateExistingDamage(id, existing_damage);
3941 };
3942
3943 EmbedderConfigBuilder builder(context);
3944 builder.SetSurface(DlISize(800, 600));
3945 builder.SetDartEntrypoint("render_gradient_retained");
3946 auto engine = builder.LaunchEngine();
3947 ASSERT_TRUE(engine.is_valid());
3948
3949 const uint32_t window_fbo_id = context.GetWindowFBOId();
3950 context.SetGLPopulateExistingDamageCallback(
3951 [window_fbo_id = window_fbo_id](intptr_t id,
3952 FlutterDamage* existing_damage) {
3953 ASSERT_EQ(id, window_fbo_id);
3954 existing_damage->num_rects = 0;
3955 existing_damage->damage = nullptr;
3956 });
3957
3958 // Send a window metrics events so frames may be scheduled.
3959 FlutterWindowMetricsEvent event = {};
3960 event.struct_size = sizeof(event);
3961 event.width = 800;
3962 event.height = 600;
3963 event.pixel_ratio = 1.0;
3964 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
3965 kSuccess);
3966}
3967
3968TEST_F(EmbedderTest, PopulateExistingDamageReceivesInvalidID) {
3969 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
3970 context.GetRendererConfig().open_gl.populate_existing_damage =
3971 [](void* context, const intptr_t id,
3972 FlutterDamage* existing_damage) -> void {
3973 return reinterpret_cast<EmbedderTestContextGL*>(context)
3974 ->GLPopulateExistingDamage(id, existing_damage);
3975 };
3976 // Return a bad FBO ID on purpose.
3978 [](void* context, const FlutterFrameInfo* frame_info) -> uint32_t {
3979 return 123;
3980 };
3981
3982 EmbedderConfigBuilder builder(context);
3983 builder.SetSurface(DlISize(800, 600));
3984 builder.SetDartEntrypoint("render_gradient_retained");
3985 auto engine = builder.LaunchEngine();
3986 ASSERT_TRUE(engine.is_valid());
3987
3988 context.AddNativeCallback("SignalNativeTest",
3989 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
3990 /* Nothing to do. */
3991 }));
3992
3993 const uint32_t window_fbo_id = context.GetWindowFBOId();
3994 context.SetGLPopulateExistingDamageCallback(
3995 [window_fbo_id = window_fbo_id](intptr_t id,
3996 FlutterDamage* existing_damage) {
3997 ASSERT_NE(id, window_fbo_id);
3998 existing_damage->num_rects = 0;
3999 existing_damage->damage = nullptr;
4000 });
4001
4002 // Send a window metrics events so frames may be scheduled.
4003 FlutterWindowMetricsEvent event = {};
4004 event.struct_size = sizeof(event);
4005 event.width = 800;
4006 event.height = 600;
4007 event.pixel_ratio = 1.0;
4008 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
4009 kSuccess);
4010}
4011
4012TEST_F(EmbedderTest, SetSingleDisplayConfigurationWithDisplayId) {
4013 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
4014
4015 EmbedderConfigBuilder builder(context);
4016 builder.SetSurface(DlISize(800, 600));
4017 builder.SetCompositor();
4018 builder.SetDartEntrypoint("empty_scene");
4020 context.AddNativeCallback(
4021 "SignalNativeTest",
4022 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { latch.Signal(); }));
4023
4024 auto engine = builder.LaunchEngine();
4025
4026 ASSERT_TRUE(engine.is_valid());
4027
4028 FlutterEngineDisplay display;
4029 display.struct_size = sizeof(FlutterEngineDisplay);
4030 display.display_id = 1;
4031 display.refresh_rate = 20;
4032
4033 std::vector<FlutterEngineDisplay> displays = {display};
4034
4037 displays.size());
4038 ASSERT_EQ(result, kSuccess);
4039
4041 ASSERT_EQ(shell.GetMainDisplayRefreshRate(), display.refresh_rate);
4042
4043 FlutterWindowMetricsEvent event = {};
4044 event.struct_size = sizeof(event);
4045 event.width = 800;
4046 event.height = 600;
4047 event.pixel_ratio = 1.0;
4048 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
4049 kSuccess);
4050
4051 latch.Wait();
4052}
4053
4054TEST_F(EmbedderTest, SetSingleDisplayConfigurationWithoutDisplayId) {
4055 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
4056
4057 EmbedderConfigBuilder builder(context);
4058 builder.SetSurface(DlISize(800, 600));
4059 builder.SetCompositor();
4060 builder.SetDartEntrypoint("empty_scene");
4062 context.AddNativeCallback(
4063 "SignalNativeTest",
4064 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { latch.Signal(); }));
4065
4066 auto engine = builder.LaunchEngine();
4067
4068 ASSERT_TRUE(engine.is_valid());
4069
4070 FlutterEngineDisplay display;
4071 display.struct_size = sizeof(FlutterEngineDisplay);
4072 display.single_display = true;
4073 display.refresh_rate = 20;
4074
4075 std::vector<FlutterEngineDisplay> displays = {display};
4076
4079 displays.size());
4080 ASSERT_EQ(result, kSuccess);
4081
4083 ASSERT_EQ(shell.GetMainDisplayRefreshRate(), display.refresh_rate);
4084
4085 FlutterWindowMetricsEvent event = {};
4086 event.struct_size = sizeof(event);
4087 event.width = 800;
4088 event.height = 600;
4089 event.pixel_ratio = 1.0;
4090 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
4091 kSuccess);
4092
4093 latch.Wait();
4094}
4095
4096TEST_F(EmbedderTest, SetValidMultiDisplayConfiguration) {
4097 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
4098
4099 EmbedderConfigBuilder builder(context);
4100 builder.SetSurface(DlISize(800, 600));
4101 builder.SetCompositor();
4102 builder.SetDartEntrypoint("empty_scene");
4104 context.AddNativeCallback(
4105 "SignalNativeTest",
4106 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { latch.Signal(); }));
4107
4108 auto engine = builder.LaunchEngine();
4109
4110 ASSERT_TRUE(engine.is_valid());
4111
4112 FlutterEngineDisplay display_1;
4113 display_1.struct_size = sizeof(FlutterEngineDisplay);
4114 display_1.display_id = 1;
4115 display_1.single_display = false;
4116 display_1.refresh_rate = 20;
4117
4118 FlutterEngineDisplay display_2;
4119 display_2.struct_size = sizeof(FlutterEngineDisplay);
4120 display_2.display_id = 2;
4121 display_2.single_display = false;
4122 display_2.refresh_rate = 60;
4123
4124 std::vector<FlutterEngineDisplay> displays = {display_1, display_2};
4125
4128 displays.size());
4129 ASSERT_EQ(result, kSuccess);
4130
4132 ASSERT_EQ(shell.GetMainDisplayRefreshRate(), display_1.refresh_rate);
4133
4134 FlutterWindowMetricsEvent event = {};
4135 event.struct_size = sizeof(event);
4136 event.width = 800;
4137 event.height = 600;
4138 event.pixel_ratio = 1.0;
4139 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
4140 kSuccess);
4141
4142 latch.Wait();
4143}
4144
4145TEST_F(EmbedderTest, MultipleDisplaysWithSingleDisplayTrueIsInvalid) {
4146 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
4147
4148 EmbedderConfigBuilder builder(context);
4149 builder.SetSurface(DlISize(800, 600));
4150 builder.SetCompositor();
4151 builder.SetDartEntrypoint("empty_scene");
4153 context.AddNativeCallback(
4154 "SignalNativeTest",
4155 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { latch.Signal(); }));
4156
4157 auto engine = builder.LaunchEngine();
4158
4159 ASSERT_TRUE(engine.is_valid());
4160
4161 FlutterEngineDisplay display_1;
4162 display_1.struct_size = sizeof(FlutterEngineDisplay);
4163 display_1.display_id = 1;
4164 display_1.single_display = true;
4165 display_1.refresh_rate = 20;
4166
4167 FlutterEngineDisplay display_2;
4168 display_2.struct_size = sizeof(FlutterEngineDisplay);
4169 display_2.display_id = 2;
4170 display_2.single_display = true;
4171 display_2.refresh_rate = 60;
4172
4173 std::vector<FlutterEngineDisplay> displays = {display_1, display_2};
4174
4177 displays.size());
4178 ASSERT_NE(result, kSuccess);
4179
4180 FlutterWindowMetricsEvent event = {};
4181 event.struct_size = sizeof(event);
4182 event.width = 800;
4183 event.height = 600;
4184 event.pixel_ratio = 1.0;
4185 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
4186 kSuccess);
4187
4188 latch.Wait();
4189}
4190
4191TEST_F(EmbedderTest, MultipleDisplaysWithSameDisplayIdIsInvalid) {
4192 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
4193
4194 EmbedderConfigBuilder builder(context);
4195 builder.SetSurface(DlISize(800, 600));
4196 builder.SetCompositor();
4197 builder.SetDartEntrypoint("empty_scene");
4199 context.AddNativeCallback(
4200 "SignalNativeTest",
4201 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { latch.Signal(); }));
4202
4203 auto engine = builder.LaunchEngine();
4204
4205 ASSERT_TRUE(engine.is_valid());
4206
4207 FlutterEngineDisplay display_1;
4208 display_1.struct_size = sizeof(FlutterEngineDisplay);
4209 display_1.display_id = 1;
4210 display_1.single_display = false;
4211 display_1.refresh_rate = 20;
4212
4213 FlutterEngineDisplay display_2;
4214 display_2.struct_size = sizeof(FlutterEngineDisplay);
4215 display_2.display_id = 1;
4216 display_2.single_display = false;
4217 display_2.refresh_rate = 60;
4218
4219 std::vector<FlutterEngineDisplay> displays = {display_1, display_2};
4220
4223 displays.size());
4224 ASSERT_NE(result, kSuccess);
4225
4226 FlutterWindowMetricsEvent event = {};
4227 event.struct_size = sizeof(event);
4228 event.width = 800;
4229 event.height = 600;
4230 event.pixel_ratio = 1.0;
4231 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
4232 kSuccess);
4233
4234 latch.Wait();
4235}
4236
4237TEST_F(EmbedderTest, CompositorRenderTargetsNotRecycledWhenAvoidsCacheSet) {
4238 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
4239
4240 EmbedderConfigBuilder builder(context);
4241 builder.SetSurface(DlISize(300, 200));
4242 builder.SetCompositor(/*avoid_backing_store_cache=*/true);
4243 builder.SetDartEntrypoint("render_targets_are_recycled");
4244 builder.SetRenderTargetType(
4246
4247 const unsigned num_frames = 8;
4248 const unsigned num_engine_layers = 10;
4249 const unsigned num_backing_stores = num_frames * num_engine_layers;
4250 fml::CountDownLatch latch(1 + num_frames); // 1 for native test signal.
4251
4252 context.AddNativeCallback("SignalNativeTest",
4253 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
4254 latch.CountDown();
4255 }));
4256
4257 context.GetCompositor().SetPresentCallback(
4259 size_t layers_count) {
4260 ASSERT_EQ(layers_count, 20u);
4261 latch.CountDown();
4262 },
4263 /*one_shot=*/false);
4264
4265 auto engine = builder.LaunchEngine();
4266 ASSERT_TRUE(engine.is_valid());
4267
4268 FlutterWindowMetricsEvent event = {};
4269 event.struct_size = sizeof(event);
4270 event.width = 300;
4271 event.height = 200;
4272 event.pixel_ratio = 1.0;
4273 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
4274 kSuccess);
4275
4276 latch.Wait();
4277
4278 ASSERT_EQ(context.GetCompositor().GetBackingStoresCreatedCount(),
4279 num_backing_stores);
4280 // Killing the engine should collect all the frames.
4281 engine.reset();
4282 ASSERT_EQ(context.GetCompositor().GetPendingBackingStoresCount(), 0u);
4283}
4284
4285TEST_F(EmbedderTest, SnapshotRenderTargetScalesDownToDriverMax) {
4286 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
4287
4288 EmbedderConfigBuilder builder(context);
4289 builder.SetSurface(DlISize(800, 600));
4290 builder.SetCompositor();
4291
4292 auto max_size = context.GetCompositor().GetGrContext()->maxRenderTargetSize();
4293
4294 context.AddIsolateCreateCallback([&]() {
4295 Dart_Handle snapshot_large_scene = Dart_GetField(
4296 Dart_RootLibrary(), tonic::ToDart("snapshot_large_scene"));
4297 tonic::DartInvoke(snapshot_large_scene, {tonic::ToDart<int64_t>(max_size)});
4298 });
4299
4301 context.AddNativeCallback(
4302 "SnapshotsCallback", CREATE_NATIVE_ENTRY(([&](Dart_NativeArguments args) {
4303 auto get_arg = [&args](int index) {
4304 Dart_Handle dart_image = Dart_GetNativeArgument(args, index);
4305 Dart_Handle internal_image =
4306 Dart_GetField(dart_image, tonic::ToDart("_image"));
4308 internal_image);
4309 };
4310
4311 CanvasImage* big_image = get_arg(0);
4312 ASSERT_EQ(big_image->width(), max_size);
4313 ASSERT_EQ(big_image->height(), max_size / 2);
4314
4315 CanvasImage* small_image = get_arg(1);
4316 ASSERT_TRUE(ImageMatchesFixture(
4317 "snapshot_large_scene.png",
4318 small_image->image()->asSkiaImage()->skia_image()));
4319
4320 latch.Signal();
4321 })));
4322
4323 UniqueEngine engine = builder.LaunchEngine();
4324 ASSERT_TRUE(engine.is_valid());
4325
4326 latch.Wait();
4327}
4328
4329TEST_F(EmbedderTest, ObjectsPostedViaPortsServicedOnSecondaryTaskHeap) {
4330 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
4331 EmbedderConfigBuilder builder(context);
4332 builder.SetSurface(DlISize(800, 1024));
4333 builder.SetDartEntrypoint("objects_can_be_posted");
4334
4335 // Synchronously acquire the send port from the Dart end. We will be using
4336 // this to send message. The Dart end will just echo those messages back to us
4337 // for inspection.
4338 FlutterEngineDartPort port = 0;
4340 context.AddNativeCallback("SignalNativeCount",
4341 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
4343 Dart_GetNativeArgument(args, 0));
4344 event.Signal();
4345 }));
4346 auto engine = builder.LaunchEngine();
4347 ASSERT_TRUE(engine.is_valid());
4348 event.Wait();
4349 ASSERT_NE(port, 0);
4350
4351 using Trampoline = std::function<void(Dart_Handle message)>;
4352 Trampoline trampoline;
4353
4354 context.AddNativeCallback("SendObjectToNativeCode",
4355 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
4356 FML_CHECK(trampoline);
4357 auto trampoline_copy = trampoline;
4358 trampoline = nullptr;
4359 trampoline_copy(Dart_GetNativeArgument(args, 0));
4360 }));
4361
4362 // Send a boolean value and assert that it's received by the right heap.
4363 {
4364 FlutterEngineDartObject object = {};
4366 object.bool_value = true;
4367 trampoline = [&](Dart_Handle handle) {
4368 ASSERT_TRUE(tonic::DartConverter<bool>::FromDart(handle));
4370 EXPECT_EQ(task_grade, fml::TaskSourceGrade::kDartEventLoop);
4371 event.Signal();
4372 };
4373 ASSERT_EQ(FlutterEnginePostDartObject(engine.get(), port, &object),
4374 kSuccess);
4375 event.Wait();
4376 }
4377}
4378
4379TEST_F(EmbedderTest, CreateInvalidBackingstoreOpenGLTexture) {
4380 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
4381 EmbedderConfigBuilder builder(context);
4382 builder.SetSurface(DlISize(800, 600));
4383 builder.SetCompositor();
4384 builder.SetRenderTargetType(
4386 builder.SetDartEntrypoint("invalid_backingstore");
4387
4388 class TestCollectOnce {
4389 public:
4390 // Collect() should only be called once
4391 void Collect() {
4392 ASSERT_FALSE(collected_);
4393 collected_ = true;
4394 }
4395
4396 private:
4397 bool collected_ = false;
4398 };
4400
4401 builder.GetCompositor().create_backing_store_callback =
4402 [](const FlutterBackingStoreConfig* config, //
4403 FlutterBackingStore* backing_store_out, //
4404 void* user_data //
4405 ) {
4406 backing_store_out->type = kFlutterBackingStoreTypeOpenGL;
4407 // Deliberately set this to be invalid
4408 backing_store_out->user_data = nullptr;
4409 backing_store_out->open_gl.type = kFlutterOpenGLTargetTypeTexture;
4410 backing_store_out->open_gl.texture.target = 0;
4411 backing_store_out->open_gl.texture.name = 0;
4412 backing_store_out->open_gl.texture.format = 0;
4413 static TestCollectOnce collect_once_user_data;
4414 collect_once_user_data = {};
4415 backing_store_out->open_gl.texture.user_data = &collect_once_user_data;
4416 backing_store_out->open_gl.texture.destruction_callback =
4417 [](void* user_data) {
4418 reinterpret_cast<TestCollectOnce*>(user_data)->Collect();
4419 };
4420 return true;
4421 };
4422
4423 context.AddNativeCallback(
4424 "SignalNativeTest",
4426 [&latch](Dart_NativeArguments args) { latch.Signal(); }));
4427
4428 auto engine = builder.LaunchEngine();
4429
4430 // Send a window metrics events so frames may be scheduled.
4431 FlutterWindowMetricsEvent event = {};
4432 event.struct_size = sizeof(event);
4433 event.width = 800;
4434 event.height = 600;
4435 event.pixel_ratio = 1.0;
4436 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
4437 kSuccess);
4438 ASSERT_TRUE(engine.is_valid());
4439 latch.Wait();
4440}
4441
4442TEST_F(EmbedderTest, CreateInvalidBackingstoreOpenGLFramebuffer) {
4443 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
4444 EmbedderConfigBuilder builder(context);
4445 builder.SetSurface(DlISize(800, 600));
4446 builder.SetCompositor();
4447 builder.SetRenderTargetType(
4449 builder.SetDartEntrypoint("invalid_backingstore");
4450
4451 class TestCollectOnce {
4452 public:
4453 // Collect() should only be called once
4454 void Collect() {
4455 ASSERT_FALSE(collected_);
4456 collected_ = true;
4457 }
4458
4459 private:
4460 bool collected_ = false;
4461 };
4463
4464 builder.GetCompositor().create_backing_store_callback =
4465 [](const FlutterBackingStoreConfig* config, //
4466 FlutterBackingStore* backing_store_out, //
4467 void* user_data //
4468 ) {
4469 backing_store_out->type = kFlutterBackingStoreTypeOpenGL;
4470 // Deliberately set this to be invalid
4471 backing_store_out->user_data = nullptr;
4472 backing_store_out->open_gl.type = kFlutterOpenGLTargetTypeFramebuffer;
4473 backing_store_out->open_gl.framebuffer.target = 0;
4474 backing_store_out->open_gl.framebuffer.name = 0;
4475 static TestCollectOnce collect_once_user_data;
4476 collect_once_user_data = {};
4477 backing_store_out->open_gl.framebuffer.user_data =
4478 &collect_once_user_data;
4479 backing_store_out->open_gl.framebuffer.destruction_callback =
4480 [](void* user_data) {
4481 reinterpret_cast<TestCollectOnce*>(user_data)->Collect();
4482 };
4483 return true;
4484 };
4485
4486 context.AddNativeCallback(
4487 "SignalNativeTest",
4489 [&latch](Dart_NativeArguments args) { latch.Signal(); }));
4490
4491 auto engine = builder.LaunchEngine();
4492
4493 // Send a window metrics events so frames may be scheduled.
4494 FlutterWindowMetricsEvent event = {};
4495 event.struct_size = sizeof(event);
4496 event.width = 800;
4497 event.height = 600;
4498 event.pixel_ratio = 1.0;
4499 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
4500 kSuccess);
4501 ASSERT_TRUE(engine.is_valid());
4502 latch.Wait();
4503}
4504
4505TEST_F(EmbedderTest, CreateInvalidBackingstoreOpenGLSurface) {
4506 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
4507 EmbedderConfigBuilder builder(context);
4508 builder.SetSurface(DlISize(800, 600));
4509 builder.SetCompositor();
4510 builder.SetRenderTargetType(
4512 builder.SetDartEntrypoint("invalid_backingstore");
4513
4515
4516 builder.GetCompositor().create_backing_store_callback =
4517 [](const FlutterBackingStoreConfig* config, //
4518 FlutterBackingStore* backing_store_out, //
4519 void* user_data //
4520 ) {
4521 backing_store_out->type = kFlutterBackingStoreTypeOpenGL;
4522 backing_store_out->user_data = user_data;
4523 backing_store_out->open_gl.type = kFlutterOpenGLTargetTypeSurface;
4524 backing_store_out->open_gl.surface.user_data = user_data;
4525 // Deliberately set this to an invalid format
4526 backing_store_out->open_gl.surface.format = 0;
4527 backing_store_out->open_gl.surface.make_current_callback = [](void*,
4528 bool*) {
4529 ADD_FAILURE() << "make_current_callback method should not be called";
4530 return true;
4531 };
4532 backing_store_out->open_gl.surface.clear_current_callback = [](void*,
4533 bool*) {
4534 ADD_FAILURE() << "clear_current_callback method should not be called";
4535 return true;
4536 };
4537 backing_store_out->open_gl.surface.destruction_callback =
4538 [](void* user_data) {
4539 FAIL() << "destruction_callback method should not be called";
4540 };
4541
4542 return true;
4543 };
4544
4545 context.AddNativeCallback(
4546 "SignalNativeTest",
4548 [&latch](Dart_NativeArguments args) { latch.Signal(); }));
4549
4550 auto engine = builder.LaunchEngine();
4551
4552 // Send a window metrics events so frames may be scheduled.
4553 FlutterWindowMetricsEvent event = {};
4554 event.struct_size = sizeof(event);
4555 event.width = 800;
4556 event.height = 600;
4557 event.pixel_ratio = 1.0;
4558 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
4559 kSuccess);
4560 ASSERT_TRUE(engine.is_valid());
4561 latch.Wait();
4562}
4563
4564TEST_F(EmbedderTest, ExternalTextureGLRefreshedTooOften) {
4565 TestGLSurface surface(DlISize(100, 100));
4566 auto context = surface.GetGrContext();
4567
4568 typedef void (*glGenTexturesProc)(uint32_t n, uint32_t* textures);
4569 typedef void (*glFinishProc)();
4570
4571 glGenTexturesProc glGenTextures;
4572 glFinishProc glFinish;
4573
4574 glGenTextures = reinterpret_cast<glGenTexturesProc>(
4575 surface.GetProcAddress("glGenTextures"));
4576 glFinish = reinterpret_cast<glFinishProc>(surface.GetProcAddress("glFinish"));
4577
4578 uint32_t name;
4579 glGenTextures(1, &name);
4580
4581 bool resolve_called = false;
4582
4584 [&](int64_t, size_t, size_t) {
4585 resolve_called = true;
4586 auto res = std::make_unique<FlutterOpenGLTexture>();
4587 res->target = GL_TEXTURE_2D;
4588 res->name = name;
4589 res->format = GL_RGBA8;
4590 res->user_data = nullptr;
4591 res->destruction_callback = [](void*) {};
4592 res->width = res->height = 100;
4593 return res;
4594 });
4596
4597 auto skia_surface = surface.GetOnscreenSurface();
4598 DlSkCanvasAdapter canvas(skia_surface->getCanvas());
4599
4600 Texture* texture_ = &texture;
4602 .canvas = &canvas,
4603 .gr_context = context.get(),
4604 };
4605 texture_->Paint(ctx, DlRect::MakeXYWH(0, 0, 100, 100), false,
4607
4608 EXPECT_TRUE(resolve_called);
4609 resolve_called = false;
4610
4611 texture_->Paint(ctx, DlRect::MakeXYWH(0, 0, 100, 100), false,
4613
4614 EXPECT_FALSE(resolve_called);
4615
4616 texture_->MarkNewFrameAvailable();
4617 texture_->Paint(ctx, DlRect::MakeXYWH(0, 0, 100, 100), false,
4619
4620 EXPECT_TRUE(resolve_called);
4621
4622 glFinish();
4623}
4624
4627 PresentInfoReceivesFullScreenDamageWhenPopulateExistingDamageIsNotProvided) {
4628 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
4629 context.GetRendererConfig().open_gl.populate_existing_damage = nullptr;
4630
4631 EmbedderConfigBuilder builder(context);
4632 builder.SetSurface(DlISize(800, 600));
4633 builder.SetDartEntrypoint("render_gradient_retained");
4634 auto engine = builder.LaunchEngine();
4635 ASSERT_TRUE(engine.is_valid());
4636
4638
4639 // First frame should be entirely rerendered.
4640 context.SetGLPresentCallback([&](FlutterPresentInfo present_info) {
4641 const size_t num_rects = 1;
4642 ASSERT_EQ(present_info.frame_damage.num_rects, num_rects);
4643 ASSERT_EQ(present_info.frame_damage.damage->left, 0);
4644 ASSERT_EQ(present_info.frame_damage.damage->top, 0);
4645 ASSERT_EQ(present_info.frame_damage.damage->right, 800);
4646 ASSERT_EQ(present_info.frame_damage.damage->bottom, 600);
4647
4648 ASSERT_EQ(present_info.buffer_damage.num_rects, num_rects);
4649 ASSERT_EQ(present_info.buffer_damage.damage->left, 0);
4650 ASSERT_EQ(present_info.buffer_damage.damage->top, 0);
4651 ASSERT_EQ(present_info.buffer_damage.damage->right, 800);
4652 ASSERT_EQ(present_info.buffer_damage.damage->bottom, 600);
4653
4654 latch.Signal();
4655 });
4656
4657 // Send a window metrics events so frames may be scheduled.
4658 FlutterWindowMetricsEvent event = {};
4659 event.struct_size = sizeof(event);
4660 event.width = 800;
4661 event.height = 600;
4662 event.pixel_ratio = 1.0;
4663 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
4664 kSuccess);
4665 latch.Wait();
4666
4667 // Since populate_existing_damage is not provided, the partial repaint
4668 // functionality is actually disabled. So, the next frame should be entirely
4669 // new frame.
4670 context.SetGLPresentCallback([&](FlutterPresentInfo present_info) {
4671 const size_t num_rects = 1;
4672 ASSERT_EQ(present_info.frame_damage.num_rects, num_rects);
4673 ASSERT_EQ(present_info.frame_damage.damage->left, 0);
4674 ASSERT_EQ(present_info.frame_damage.damage->top, 0);
4675 ASSERT_EQ(present_info.frame_damage.damage->right, 800);
4676 ASSERT_EQ(present_info.frame_damage.damage->bottom, 600);
4677
4678 ASSERT_EQ(present_info.buffer_damage.num_rects, num_rects);
4679 ASSERT_EQ(present_info.buffer_damage.damage->left, 0);
4680 ASSERT_EQ(present_info.buffer_damage.damage->top, 0);
4681 ASSERT_EQ(present_info.buffer_damage.damage->right, 800);
4682 ASSERT_EQ(present_info.buffer_damage.damage->bottom, 600);
4683
4684 latch.Signal();
4685 });
4686
4687 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
4688 kSuccess);
4689 latch.Wait();
4690}
4691
4693 PresentInfoReceivesJoinedDamageWhenExistingDamageContainsMultipleRects) {
4694 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
4695 context.GetRendererConfig().open_gl.populate_existing_damage =
4696 [](void* context, const intptr_t id,
4697 FlutterDamage* existing_damage) -> void {
4698 return reinterpret_cast<EmbedderTestContextGL*>(context)
4699 ->GLPopulateExistingDamage(id, existing_damage);
4700 };
4701 // Return existing damage as the entire screen on purpose.
4703 [](const intptr_t id, FlutterDamage* existing_damage_ptr) {
4704 const size_t num_rects = 2;
4705 // The array must be valid after the callback returns.
4706 static FlutterRect existing_damage_rects[num_rects] = {
4707 FlutterRect{100, 150, 200, 250},
4708 FlutterRect{200, 250, 300, 350},
4709 };
4710 existing_damage_ptr->num_rects = num_rects;
4711 existing_damage_ptr->damage = existing_damage_rects;
4712 });
4713
4714 EmbedderConfigBuilder builder(context);
4715 builder.SetSurface(DlISize(800, 600));
4716 builder.SetDartEntrypoint("render_gradient_retained");
4717 auto engine = builder.LaunchEngine();
4718 ASSERT_TRUE(engine.is_valid());
4719
4721
4722 // First frame should be entirely rerendered.
4723 context.SetGLPresentCallback([&](FlutterPresentInfo present_info) {
4724 const size_t num_rects = 1;
4725 ASSERT_EQ(present_info.frame_damage.num_rects, num_rects);
4726 ASSERT_EQ(present_info.frame_damage.damage->left, 0);
4727 ASSERT_EQ(present_info.frame_damage.damage->top, 0);
4728 ASSERT_EQ(present_info.frame_damage.damage->right, 800);
4729 ASSERT_EQ(present_info.frame_damage.damage->bottom, 600);
4730
4731 ASSERT_EQ(present_info.buffer_damage.num_rects, num_rects);
4732 ASSERT_EQ(present_info.buffer_damage.damage->left, 0);
4733 ASSERT_EQ(present_info.buffer_damage.damage->top, 0);
4734 ASSERT_EQ(present_info.buffer_damage.damage->right, 800);
4735 ASSERT_EQ(present_info.buffer_damage.damage->bottom, 600);
4736
4737 latch.Signal();
4738 });
4739
4740 // Send a window metrics events so frames may be scheduled.
4741 FlutterWindowMetricsEvent event = {};
4742 event.struct_size = sizeof(event);
4743 event.width = 800;
4744 event.height = 600;
4745 event.pixel_ratio = 1.0;
4746 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
4747 kSuccess);
4748 latch.Wait();
4749
4750 // Because it's the same as the first frame, the second frame damage should
4751 // be empty but, because there was a full existing buffer damage, the buffer
4752 // damage should be the entire screen.
4753 context.SetGLPresentCallback([&](FlutterPresentInfo present_info) {
4754 const size_t num_rects = 1;
4755 ASSERT_EQ(present_info.frame_damage.num_rects, num_rects);
4756 ASSERT_EQ(present_info.frame_damage.damage->left, 0);
4757 ASSERT_EQ(present_info.frame_damage.damage->top, 0);
4758 ASSERT_EQ(present_info.frame_damage.damage->right, 0);
4759 ASSERT_EQ(present_info.frame_damage.damage->bottom, 0);
4760
4761 ASSERT_EQ(present_info.buffer_damage.num_rects, num_rects);
4762 ASSERT_EQ(present_info.buffer_damage.damage->left, 100);
4763 ASSERT_EQ(present_info.buffer_damage.damage->top, 150);
4764 ASSERT_EQ(present_info.buffer_damage.damage->right, 300);
4765 ASSERT_EQ(present_info.buffer_damage.damage->bottom, 350);
4766
4767 latch.Signal();
4768 });
4769
4770 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
4771 kSuccess);
4772 latch.Wait();
4773}
4774
4775TEST_F(EmbedderTest, CanRenderWithImpellerOpenGL) {
4776 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
4777 EmbedderConfigBuilder builder(context);
4778
4779 bool present_called = false;
4780 context.SetGLPresentCallback(
4781 [&present_called](FlutterPresentInfo present_info) {
4782 present_called = true;
4783 });
4784
4785 builder.AddCommandLineArgument("--enable-impeller");
4786 builder.SetDartEntrypoint("render_impeller_test");
4787 builder.SetSurface(DlISize(800, 600));
4788 builder.SetCompositor();
4789 builder.SetRenderTargetType(
4791
4792 auto rendered_scene = context.GetNextSceneImage();
4793
4794 auto engine = builder.LaunchEngine();
4795 ASSERT_TRUE(engine.is_valid());
4796
4797 // Bind to an arbitrary FBO in order to verify that Impeller binds to the
4798 // provided FBO during rendering.
4799 typedef void (*glGenFramebuffersProc)(GLsizei n, GLuint* ids);
4800 typedef void (*glBindFramebufferProc)(GLenum target, GLuint framebuffer);
4801 auto glGenFramebuffers = reinterpret_cast<glGenFramebuffersProc>(
4802 context.GLGetProcAddress("glGenFramebuffers"));
4803 auto glBindFramebuffer = reinterpret_cast<glBindFramebufferProc>(
4804 context.GLGetProcAddress("glBindFramebuffer"));
4805 const flutter::Shell& shell = ToEmbedderEngine(engine.get())->GetShell();
4806 fml::AutoResetWaitableEvent raster_event;
4808 GLuint fbo;
4809 glGenFramebuffers(1, &fbo);
4810 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
4811 raster_event.Signal();
4812 });
4813 raster_event.Wait();
4814
4815 // Send a window metrics events so frames may be scheduled.
4816 FlutterWindowMetricsEvent event = {};
4817 event.struct_size = sizeof(event);
4818 event.width = 800;
4819 event.height = 600;
4820 event.pixel_ratio = 1.0;
4821 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
4822 kSuccess);
4823
4824 ASSERT_TRUE(ImageMatchesFixture(
4826 "impeller_test.png"),
4827 rendered_scene));
4828
4829 // The scene will be rendered by the compositor, and the surface present
4830 // callback should not be invoked.
4831 ASSERT_FALSE(present_called);
4832}
4833
4834TEST_F(EmbedderTest, RenderTextureWithImpellerOpenGL) {
4835 constexpr int kWidth = 800;
4836 constexpr int kHeight = 600;
4837 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
4838 EmbedderConfigBuilder builder(context);
4840 context.SetGLPresentCallback(
4841 [&](FlutterPresentInfo present_info) { latch.Signal(); });
4842 builder.AddCommandLineArgument("--enable-impeller");
4843 builder.SetDartEntrypoint("render_texture_impeller_test");
4844 builder.SetSurface(DlISize(kWidth, kHeight));
4845 typedef void (*glGenTexturesProc)(GLsizei n, GLuint* textures);
4846 typedef void (*glBindTextureProc)(GLenum n, GLuint texture);
4847 typedef void (*glTexImage2DProc)(GLenum target, GLint level,
4848 GLint internalformat, GLsizei width,
4849 GLsizei height, GLint border, GLenum format,
4850 GLenum type, const void* pixels);
4851 static glGenTexturesProc glGenTextures = reinterpret_cast<glGenTexturesProc>(
4852 context.GLGetProcAddress("glGenTextures"));
4853 static glBindTextureProc glBindTexture = reinterpret_cast<glBindTextureProc>(
4854 context.GLGetProcAddress("glBindTexture"));
4855 static glTexImage2DProc glTexImage2D = reinterpret_cast<glTexImage2DProc>(
4856 context.GLGetProcAddress("glTexImage2D"));
4857
4858 static GLuint gl_texture = 0;
4859 auto rendered_scene = context.GetNextSceneImage();
4860 context.GetRendererConfig().open_gl.gl_external_texture_frame_callback =
4861 [](void* user_data, int64_t texture_id, size_t width, size_t height,
4862 FlutterOpenGLTexture* texture) -> bool {
4863 std::vector<uint8_t> buffer(kWidth * kHeight * 4);
4864 for (int i = 0; i < kWidth * kHeight / 2; ++i) {
4865 buffer[i * 4 + 0] = 255; // Red channel
4866 buffer[i * 4 + 1] = 0; // Green channel
4867 buffer[i * 4 + 2] = 0; // Blue channel
4868 buffer[i * 4 + 3] = 255; // Alpha channel (fully opaque)
4869 }
4870
4871 for (int i = kWidth * kHeight / 2; i < kWidth * kHeight; ++i) {
4872 buffer[i * 4 + 0] = 0; // Red channel
4873 buffer[i * 4 + 1] = 0; // Green channel
4874 buffer[i * 4 + 2] = 255; // Blue channel
4875 buffer[i * 4 + 3] = 255; // Alpha channel (fully opaque)
4876 }
4877
4878 if (gl_texture == 0) {
4879 glGenTextures(1, &gl_texture);
4880 }
4881 glBindTexture(GL_TEXTURE_2D, gl_texture);
4882 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kWidth, kHeight, 0, GL_RGBA,
4883 GL_UNSIGNED_BYTE, buffer.data());
4884 texture->target = GL_TEXTURE_2D;
4885 texture->name = gl_texture;
4886 texture->format = GL_RGBA8;
4887 texture->destruction_callback = nullptr;
4888 texture->user_data = nullptr;
4889 texture->width = width;
4890 texture->height = height;
4891 return true;
4892 };
4893
4894 auto engine = builder.LaunchEngine();
4895
4896 ASSERT_TRUE(engine.is_valid());
4897
4898 flutter::EmbedderEngine* embedder_engine = ToEmbedderEngine(engine.get());
4899
4900 constexpr int texture_id = 1;
4901 ASSERT_TRUE(embedder_engine->RegisterTexture(texture_id));
4902
4903 // Send a window metrics events so frames may be scheduled.
4904 FlutterWindowMetricsEvent event = {};
4905 event.struct_size = sizeof(event);
4906 event.width = kWidth;
4907 event.height = kHeight;
4908 event.pixel_ratio = 1.0;
4909 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
4910 kSuccess);
4911 latch.Wait();
4912 ASSERT_TRUE(
4913 ImageMatchesFixture("external_texture_impeller.png", rendered_scene));
4914 constexpr int kFrameCount = 5;
4915 for (int i = 0; i < kFrameCount; i++) {
4916 rendered_scene = context.GetNextSceneImage();
4917 ASSERT_TRUE(embedder_engine->MarkTextureFrameAvailable(texture_id));
4918 latch.Wait();
4919 ASSERT_TRUE(
4920 ImageMatchesFixture("external_texture_impeller.png", rendered_scene));
4921 }
4922}
4923
4924TEST_F(EmbedderTest, RenderTextureWithImpellerOpenGLDestructCallback) {
4925 constexpr int kWidth = 800;
4926 constexpr int kHeight = 600;
4927 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
4928 EmbedderConfigBuilder builder(context);
4930 context.SetGLPresentCallback(
4931 [&](FlutterPresentInfo present_info) { latch.Signal(); });
4932 builder.AddCommandLineArgument("--enable-impeller");
4933 builder.SetDartEntrypoint("render_texture_impeller_test");
4934 builder.SetSurface(DlISize(kWidth, kHeight));
4935 typedef void (*glGenTexturesProc)(GLsizei n, GLuint* textures);
4936 typedef void (*glBindTextureProc)(GLenum n, GLuint texture);
4937 typedef void (*glTexImage2DProc)(GLenum target, GLint level,
4938 GLint internalformat, GLsizei width,
4939 GLsizei height, GLint border, GLenum format,
4940 GLenum type, const void* pixels);
4941 static glGenTexturesProc glGenTextures = reinterpret_cast<glGenTexturesProc>(
4942 context.GLGetProcAddress("glGenTextures"));
4943 static glBindTextureProc glBindTexture = reinterpret_cast<glBindTextureProc>(
4944 context.GLGetProcAddress("glBindTexture"));
4945 static glTexImage2DProc glTexImage2D = reinterpret_cast<glTexImage2DProc>(
4946 context.GLGetProcAddress("glTexImage2D"));
4947
4948 auto rendered_scene = context.GetNextSceneImage();
4949
4950 static bool destruction_callback_called = false;
4951 static auto destruction_callback = [](void* user_data) {
4952 GLuint gl_texture =
4953 static_cast<GLuint>(reinterpret_cast<uintptr_t>(user_data));
4954 glDeleteTextures(1, &gl_texture);
4955 destruction_callback_called = true;
4956 };
4957 context.GetRendererConfig().open_gl.gl_external_texture_frame_callback =
4958 [](void* user_data, int64_t texture_id, size_t width, size_t height,
4959 FlutterOpenGLTexture* texture) -> bool {
4960 std::vector<uint8_t> buffer(kWidth * kHeight * 4);
4961 for (int i = 0; i < kWidth * kHeight / 2; ++i) {
4962 buffer[i * 4 + 0] = 255; // Red channel
4963 buffer[i * 4 + 1] = 0; // Green channel
4964 buffer[i * 4 + 2] = 0; // Blue channel
4965 buffer[i * 4 + 3] = 255; // Alpha channel (fully opaque)
4966 }
4967
4968 for (int i = kWidth * kHeight / 2; i < kWidth * kHeight; ++i) {
4969 buffer[i * 4 + 0] = 0; // Red channel
4970 buffer[i * 4 + 1] = 0; // Green channel
4971 buffer[i * 4 + 2] = 255; // Blue channel
4972 buffer[i * 4 + 3] = 255; // Alpha channel (fully opaque)
4973 }
4974
4975 GLuint gl_texture;
4976 glGenTextures(1, &gl_texture);
4977 glBindTexture(GL_TEXTURE_2D, gl_texture);
4978 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kWidth, kHeight, 0, GL_RGBA,
4979 GL_UNSIGNED_BYTE, buffer.data());
4980 texture->target = GL_TEXTURE_2D;
4981 texture->name = gl_texture;
4982 texture->format = GL_RGBA8;
4983 texture->destruction_callback = destruction_callback;
4984 texture->user_data = reinterpret_cast<void*>(gl_texture);
4985 texture->width = width;
4986 texture->height = height;
4987 return true;
4988 };
4989
4990 auto engine = builder.LaunchEngine();
4991
4992 ASSERT_TRUE(engine.is_valid());
4993
4994 flutter::EmbedderEngine* embedder_engine = ToEmbedderEngine(engine.get());
4995
4996 constexpr int texture_id = 1;
4997 ASSERT_TRUE(embedder_engine->RegisterTexture(texture_id));
4998
4999 // Send a window metrics events so frames may be scheduled.
5000 FlutterWindowMetricsEvent event = {};
5001 event.struct_size = sizeof(event);
5002 event.width = kWidth;
5003 event.height = kHeight;
5004 event.pixel_ratio = 1.0;
5005 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
5006 kSuccess);
5007 latch.Wait();
5008 ASSERT_TRUE(
5009 ImageMatchesFixture("external_texture_impeller.png", rendered_scene));
5010
5011 // Render a second frame.
5012 ASSERT_TRUE(embedder_engine->MarkTextureFrameAvailable(texture_id));
5013 latch.Wait();
5014
5015 // After the second frame completes, Impeller will have collected the handle
5016 // of the first frame's texture and called its destruction callback.
5017 ASSERT_TRUE(destruction_callback_called);
5018}
5019
5020TEST_F(EmbedderTest, ImpellerOpenGLImageSnapshot) {
5021 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
5022
5023 bool result = false;
5025 context.AddNativeCallback("NotifyBoolValue",
5026 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
5028 Dart_GetNativeArgument(args, 0));
5029 latch.Signal();
5030 }));
5031
5032 EmbedderConfigBuilder builder(context);
5033 builder.AddCommandLineArgument("--enable-impeller");
5034 builder.SetDartEntrypoint("render_impeller_image_snapshot_test");
5035 builder.SetSurface(DlISize(800, 600));
5036 builder.SetCompositor();
5037 builder.SetRenderTargetType(
5039
5040 auto engine = builder.LaunchEngine();
5041 ASSERT_TRUE(engine.is_valid());
5042 latch.Wait();
5043
5044 ASSERT_TRUE(result);
5045}
5046
5047TEST_F(EmbedderTest, CompositorMustBeAbleToRenderToOpenGLSurface) {
5048 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
5049
5050 EmbedderConfigBuilder builder(context);
5051 builder.SetSurface(DlISize(800, 600));
5052 builder.SetCompositor();
5053 builder.SetDartEntrypoint("can_composite_platform_views");
5054
5055 builder.SetRenderTargetType(
5057
5058 fml::CountDownLatch latch(3);
5059 context.GetCompositor().SetNextPresentCallback(
5061 size_t layers_count) {
5062 ASSERT_EQ(layers_count, 3u);
5063
5064 {
5065 FlutterBackingStore backing_store = *layers[0]->backing_store;
5066 backing_store.struct_size = sizeof(backing_store);
5067 backing_store.type = kFlutterBackingStoreTypeOpenGL;
5068 backing_store.did_update = true;
5070
5071 FlutterRect paint_region_rects[] = {
5072 FlutterRectMakeLTRB(0, 0, 800, 600),
5073 };
5074 FlutterRegion paint_region = {
5075 .struct_size = sizeof(FlutterRegion),
5076 .rects_count = 1,
5077 .rects = paint_region_rects,
5078 };
5079 FlutterBackingStorePresentInfo present_info = {
5081 .paint_region = &paint_region,
5082 };
5083
5084 FlutterLayer layer = {};
5085 layer.struct_size = sizeof(layer);
5087 layer.backing_store = &backing_store;
5088 layer.size = FlutterSizeMake(800.0, 600.0);
5089 layer.offset = FlutterPointMake(0, 0);
5090 layer.backing_store_present_info = &present_info;
5091 ASSERT_EQ(*layers[0], layer);
5092 }
5093
5094 {
5097 platform_view.identifier = 42;
5098
5099 FlutterLayer layer = {};
5100 layer.struct_size = sizeof(layer);
5103 layer.size = FlutterSizeMake(123.0, 456.0);
5104 layer.offset = FlutterPointMake(1.0, 2.0);
5105
5106 ASSERT_EQ(*layers[1], layer);
5107 }
5108
5109 {
5110 FlutterBackingStore backing_store = *layers[2]->backing_store;
5111 backing_store.struct_size = sizeof(backing_store);
5112 backing_store.type = kFlutterBackingStoreTypeOpenGL;
5113 backing_store.did_update = true;
5115
5116 FlutterRect paint_region_rects[] = {
5117 FlutterRectMakeLTRB(2, 3, 800, 600),
5118 };
5119 FlutterRegion paint_region = {
5120 .struct_size = sizeof(FlutterRegion),
5121 .rects_count = 1,
5122 .rects = paint_region_rects,
5123 };
5124 FlutterBackingStorePresentInfo present_info = {
5126 .paint_region = &paint_region,
5127 };
5128
5129 FlutterLayer layer = {};
5130 layer.struct_size = sizeof(layer);
5132 layer.backing_store = &backing_store;
5133 layer.size = FlutterSizeMake(800.0, 600.0);
5134 layer.offset = FlutterPointMake(0.0, 0.0);
5135 layer.backing_store_present_info = &present_info;
5136
5137 ASSERT_EQ(*layers[2], layer);
5138 }
5139
5140 latch.CountDown();
5141 });
5142
5143 context.AddNativeCallback(
5144 "SignalNativeTest",
5146 [&latch](Dart_NativeArguments args) { latch.CountDown(); }));
5147
5148 auto engine = builder.LaunchEngine();
5149
5150 // Send a window metrics events so frames may be scheduled.
5151 FlutterWindowMetricsEvent event = {};
5152 event.struct_size = sizeof(event);
5153 event.width = 800;
5154 event.height = 600;
5155 event.pixel_ratio = 1.0;
5156 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
5157 kSuccess);
5158 ASSERT_TRUE(engine.is_valid());
5159
5160 latch.Wait();
5161}
5162
5163TEST_F(EmbedderTest, CompositorMustBeAbleToRenderKnownSceneToOpenGLSurfaces) {
5164 auto& context = GetEmbedderContext<EmbedderTestContextGL>();
5165
5166 EmbedderConfigBuilder builder(context);
5167 builder.SetSurface(DlISize(800, 600));
5168 builder.SetCompositor();
5169 builder.SetDartEntrypoint("can_composite_platform_views_with_known_scene");
5170
5171 builder.SetRenderTargetType(
5173
5174 fml::CountDownLatch latch(5);
5175
5176 auto scene_image = context.GetNextSceneImage();
5177
5178 context.GetCompositor().SetNextPresentCallback(
5180 size_t layers_count) {
5181 ASSERT_EQ(layers_count, 5u);
5182
5183 // Layer Root
5184 {
5185 FlutterBackingStore backing_store = *layers[0]->backing_store;
5186 backing_store.type = kFlutterBackingStoreTypeOpenGL;
5187 backing_store.did_update = true;
5189
5190 FlutterRect paint_region_rects[] = {
5191 FlutterRectMakeLTRB(0, 0, 800, 600),
5192 };
5193 FlutterRegion paint_region = {
5194 .struct_size = sizeof(FlutterRegion),
5195 .rects_count = 1,
5196 .rects = paint_region_rects,
5197 };
5198 FlutterBackingStorePresentInfo present_info = {
5200 .paint_region = &paint_region,
5201 };
5202
5203 FlutterLayer layer = {};
5204 layer.struct_size = sizeof(layer);
5206 layer.backing_store = &backing_store;
5207 layer.size = FlutterSizeMake(800.0, 600.0);
5208 layer.offset = FlutterPointMake(0.0, 0.0);
5209 layer.backing_store_present_info = &present_info;
5210
5211 ASSERT_EQ(*layers[0], layer);
5212 }
5213
5214 // Layer 1
5215 {
5218 platform_view.identifier = 1;
5219
5220 FlutterLayer layer = {};
5221 layer.struct_size = sizeof(layer);
5224 layer.size = FlutterSizeMake(50.0, 150.0);
5225 layer.offset = FlutterPointMake(20.0, 20.0);
5226
5227 ASSERT_EQ(*layers[1], layer);
5228 }
5229
5230 // Layer 2
5231 {
5232 FlutterBackingStore backing_store = *layers[2]->backing_store;
5233 backing_store.type = kFlutterBackingStoreTypeOpenGL;
5234 backing_store.did_update = true;
5236
5237 FlutterRect paint_region_rects[] = {
5238 FlutterRectMakeLTRB(30, 30, 80, 180),
5239 };
5240 FlutterRegion paint_region = {
5241 .struct_size = sizeof(FlutterRegion),
5242 .rects_count = 1,
5243 .rects = paint_region_rects,
5244 };
5245 FlutterBackingStorePresentInfo present_info = {
5247 .paint_region = &paint_region,
5248 };
5249
5250 FlutterLayer layer = {};
5251 layer.struct_size = sizeof(layer);
5253 layer.backing_store = &backing_store;
5254 layer.size = FlutterSizeMake(800.0, 600.0);
5255 layer.offset = FlutterPointMake(0.0, 0.0);
5256 layer.backing_store_present_info = &present_info;
5257
5258 ASSERT_EQ(*layers[2], layer);
5259 }
5260
5261 // Layer 3
5262 {
5265 platform_view.identifier = 2;
5266
5267 FlutterLayer layer = {};
5268 layer.struct_size = sizeof(layer);
5271 layer.size = FlutterSizeMake(50.0, 150.0);
5272 layer.offset = FlutterPointMake(40.0, 40.0);
5273
5274 ASSERT_EQ(*layers[3], layer);
5275 }
5276
5277 // Layer 4
5278 {
5279 FlutterBackingStore backing_store = *layers[4]->backing_store;
5280 backing_store.type = kFlutterBackingStoreTypeOpenGL;
5281 backing_store.did_update = true;
5283
5284 FlutterRect paint_region_rects[] = {
5285 FlutterRectMakeLTRB(50, 50, 100, 200),
5286 };
5287 FlutterRegion paint_region = {
5288 .struct_size = sizeof(FlutterRegion),
5289 .rects_count = 1,
5290 .rects = paint_region_rects,
5291 };
5292 FlutterBackingStorePresentInfo present_info = {
5294 .paint_region = &paint_region,
5295 };
5296
5297 FlutterLayer layer = {};
5298 layer.struct_size = sizeof(layer);
5300 layer.backing_store = &backing_store;
5301 layer.size = FlutterSizeMake(800.0, 600.0);
5302 layer.offset = FlutterPointMake(0.0, 0.0);
5303 layer.backing_store_present_info = &present_info;
5304
5305 ASSERT_EQ(*layers[4], layer);
5306 }
5307
5308 latch.CountDown();
5309 });
5310
5311 context.GetCompositor().SetPlatformViewRendererCallback(
5312 [&](const FlutterLayer& layer,
5313 GrDirectContext* context) -> sk_sp<SkImage> {
5314 auto surface = CreateRenderSurface(layer, context);
5315 auto canvas = surface->getCanvas();
5316 FML_CHECK(canvas != nullptr);
5317
5318 switch (layer.platform_view->identifier) {
5319 case 1: {
5320 SkPaint paint;
5321 // See dart test for total order.
5322 paint.setColor(SK_ColorGREEN);
5323 paint.setAlpha(127);
5324 const auto& rect =
5325 SkRect::MakeWH(layer.size.width, layer.size.height);
5326 canvas->drawRect(rect, paint);
5327 latch.CountDown();
5328 } break;
5329 case 2: {
5330 SkPaint paint;
5331 // See dart test for total order.
5332 paint.setColor(SK_ColorMAGENTA);
5333 paint.setAlpha(127);
5334 const auto& rect =
5335 SkRect::MakeWH(layer.size.width, layer.size.height);
5336 canvas->drawRect(rect, paint);
5337 latch.CountDown();
5338 } break;
5339 default:
5340 // Asked to render an unknown platform view.
5341 FML_CHECK(false)
5342 << "Test was asked to composite an unknown platform view.";
5343 }
5344
5345 return surface->makeImageSnapshot();
5346 });
5347
5348 context.AddNativeCallback(
5349 "SignalNativeTest",
5351 [&latch](Dart_NativeArguments args) { latch.CountDown(); }));
5352
5353 auto engine = builder.LaunchEngine();
5354
5355 // Send a window metrics events so frames may be scheduled.
5356 FlutterWindowMetricsEvent event = {};
5357 event.struct_size = sizeof(event);
5358 event.width = 800;
5359 event.height = 600;
5360 event.pixel_ratio = 1.0;
5361 ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
5362 kSuccess);
5363 ASSERT_TRUE(engine.is_valid());
5364
5365 latch.Wait();
5366
5367 ASSERT_TRUE(ImageMatchesFixture("compositor.png", scene_image));
5368
5369 // There should no present calls on the root surface.
5370 ASSERT_EQ(context.GetSurfacePresentCount(), 0u);
5371}
5372
5374 EmbedderTestGlVk,
5378
5379} // namespace flutter::testing
5380
5381// NOLINTEND(clang-analyzer-core.StackAddressEscape)
std::unique_ptr< flutter::PlatformViewIOS > platform_view
sk_sp< DlImage > image() const
Definition image.h:56
Backend implementation of |DlCanvas| for |SkCanvas|.
bool MarkTextureFrameAvailable(int64_t texture)
bool RegisterTexture(int64_t texture)
std::function< std::unique_ptr< FlutterOpenGLTexture >(int64_t, size_t, size_t)> ExternalTextureCallback
size_t GetCachedEntriesCount() const
fml::TaskRunnerAffineWeakPtr< Rasterizer > GetRasterizer() const
Rasterizers may only be accessed on the raster task runner.
Definition shell.cc:925
const TaskRunners & GetTaskRunners() const override
If callers wish to interact directly with any shell subcomponents, they must (on the platform thread)...
Definition shell.cc:916
double GetMainDisplayRefreshRate()
Queries the DisplayManager for the main display refresh rate.
Definition shell.cc:2047
fml::RefPtr< fml::TaskRunner > GetRasterTaskRunner() const
virtual void MarkNewFrameAvailable()=0
virtual void Paint(PaintContext &context, const DlRect &bounds, bool freeze, const DlImageSampling sampling)=0
void SetPlatformTaskRunner(const FlutterTaskRunnerDescription *runner)
void SetRenderTargetType(EmbedderTestBackingStoreProducer::RenderTargetType type, FlutterSoftwarePixelFormat software_pixfmt=kFlutterSoftwarePixelFormatNative32)
void SetRenderTaskRunner(const FlutterTaskRunnerDescription *runner)
void SetCompositor(bool avoid_backing_store_cache=false, bool use_present_layers_callback=false)
void SetGLPopulateExistingDamageCallback(GLPopulateExistingDamageCallback callback)
A task runner that we expect the embedder to provide but whose implementation is a real FML task runn...
static TaskSourceGrade GetCurrentTaskSourceGrade()
virtual void PostTask(const fml::closure &task) override
FlutterEngineResult FlutterEngineRunTask(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterTask *task)
Inform the engine to run the specified task. This task has been given to the embedder via the Flutter...
Definition embedder.cc:3354
FlutterEngineResult FlutterEngineSendWindowMetricsEvent(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterWindowMetricsEvent *flutter_metrics)
Definition embedder.cc:2698
FlutterEngineResult FlutterEnginePostDartObject(FLUTTER_API_SYMBOL(FlutterEngine) engine, FlutterEngineDartPort port, const FlutterEngineDartObject *object)
Posts a Dart object to specified send port. The corresponding receive port for send port can be in an...
Definition embedder.cc:3469
FLUTTER_EXPORT FlutterEngineResult FlutterEngineDeinitialize(FLUTTER_API_SYMBOL(FlutterEngine) engine)
Stops running the Flutter engine instance. After this call, the embedder is also guaranteed that no m...
Definition embedder.cc:2674
FlutterEngineResult FlutterEngineShutdown(FLUTTER_API_SYMBOL(FlutterEngine) engine)
Shuts down a Flutter engine instance. The engine handle is no longer valid for any calls in the embed...
Definition embedder.cc:2687
FlutterEngineResult FlutterEngineNotifyDisplayUpdate(FLUTTER_API_SYMBOL(FlutterEngine) raw_engine, const FlutterEngineDisplaysUpdateType update_type, const FlutterEngineDisplay *embedder_displays, size_t display_count)
Posts updates corresponding to display changes to a running engine instance.
Definition embedder.cc:3654
FlutterEngineResult FlutterEngineRunInitialized(FLUTTER_API_SYMBOL(FlutterEngine) engine)
Runs an initialized engine instance. An engine can be initialized via FlutterEngineInitialize....
Definition embedder.cc:2499
@ kFlutterLayerContentTypePlatformView
Indicates that the contents of this layer are determined by the embedder.
Definition embedder.h:2142
@ kFlutterLayerContentTypeBackingStore
Definition embedder.h:2140
@ kFlutterPlatformViewMutationTypeClipRoundedRect
Definition embedder.h:2044
@ kFlutterPlatformViewMutationTypeClipRect
Definition embedder.h:2041
@ kFlutterPlatformViewMutationTypeTransformation
Definition embedder.h:2047
@ kFlutterPlatformViewMutationTypeOpacity
Definition embedder.h:2038
@ kFlutterEngineDartObjectTypeString
Definition embedder.h:2374
@ kFlutterEngineDartObjectTypeBool
Definition embedder.h:2370
@ kFlutterEngineDartObjectTypeDouble
Definition embedder.h:2373
@ kFlutterEngineDartObjectTypeInt32
Definition embedder.h:2371
@ kFlutterEngineDartObjectTypeBuffer
Definition embedder.h:2377
@ kFlutterEngineDartObjectTypeInt64
Definition embedder.h:2372
@ kFlutterEngineDartObjectTypeNull
Definition embedder.h:2369
FlutterEngineResult
Definition embedder.h:72
@ kFlutterEngineDisplaysUpdateTypeStartup
Definition embedder.h:2362
int64_t FlutterEngineDartPort
Definition embedder.h:2366
@ kFlutterOpenGLTargetTypeFramebuffer
Definition embedder.h:424
@ kFlutterOpenGLTargetTypeSurface
Definition embedder.h:427
@ kFlutterOpenGLTargetTypeTexture
Definition embedder.h:421
@ kFlutterBackingStoreTypeSoftware
Specified an software allocation for Flutter to render into using the CPU.
Definition embedder.h:2091
@ kFlutterBackingStoreTypeOpenGL
Definition embedder.h:2089
flutter::DlMatrix DlMatrixMake(const FlutterTransformation &xformation)
FlutterSize FlutterSizeMake(double width, double height)
FlutterRect FlutterRectMake(const SkRect &rect)
FlutterRect FlutterRectMakeLTRB(double l, double t, double r, double b)
FlutterPoint FlutterPointMake(double x, double y)
FlutterTransformation FlutterTransformationMake(const flutter::DlMatrix &matrix)
SkRect SkRectMake(const FlutterRect &rect)
flutter::EmbedderEngine * ToEmbedderEngine(const FlutterEngine &engine)
FlutterRoundedRect FlutterRoundedRectMake(const SkRRect &rect)
FlutterEngine engine
Definition main.cc:84
VkSurfaceKHR surface
Definition main.cc:65
const char * message
const FlutterLayer size_t layers_count
const FlutterLayer ** layers
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
uint32_t uint32_t * format
uint32_t * target
G_BEGIN_DECLS FlutterViewId view_id
FlutterDesktopBinaryReply callback
#define FML_CHECK(condition)
Definition logging.h:104
size_t length
FlTexture * texture
TEST_F(DisplayListTest, Defaults)
sk_sp< SkSurface > CreateRenderSurface(const FlutterLayer &layer, GrDirectContext *context)
bool MemsetPatternSetOrCheck(uint8_t *buffer, size_t size, MemsetPatternOp op)
Depending on the operation, either scribbles a known pattern into the buffer or checks if that patter...
Definition testing.cc:78
void FilterMutationsByType(const FlutterPlatformViewMutation **mutations, size_t count, FlutterPlatformViewMutationType type, const std::function< void(const FlutterPlatformViewMutation &mutation)> &handler)
bool ImageMatchesFixture(const std::string &fixture_file_name, const sk_sp< SkImage > &scene_image, int allowable_different_pixels)
void ConfigureBackingStore(FlutterBackingStore &backing_store, EmbedderTestContextType backend, bool opengl_framebuffer)
Configures per-backend properties for a given backing store.
INSTANTIATE_TEST_SUITE_P(EmbedderTestGlVk, EmbedderTestMultiBackend, ::testing::Values(EmbedderTestContextType::kOpenGLContext, EmbedderTestContextType::kVulkanContext))
TEST_P(DlGoldenTest, TextBlurMaskFilterRespectCTM)
std::string FixtureNameForBackend(EmbedderTestContextType backend, const std::string &name)
Prepends a prefix to the name which is unique to the test context type. This is useful for tests that...
EmbedderTestBackingStoreProducer::RenderTargetType GetRenderTargetFromBackend(EmbedderTestContextType backend, bool opengl_framebuffer)
Resolves a render target type for a given backend description. This is useful for tests that use Embe...
DlMatrix GetTotalMutationTransformationMatrix(const FlutterPlatformViewMutation **mutations, size_t count)
impeller::ISize32 DlISize
impeller::Degrees DlDegrees
DEF_SWITCHES_START aot vmservice shared library name
Definition switch_defs.h:27
int64_t FlutterViewId
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set profile Make the profiler discard new samples once the profiler sample buffer is full When this flag is not the profiler sample buffer is used as a ring buffer
Definition switch_defs.h:98
Dart_Handle ToDart(const T &object)
Dart_Handle DartInvoke(Dart_Handle closure, std::initializer_list< Dart_Handle > args)
std::vector< FlutterEngineDisplay > * displays
impeller::ShaderType type
int32_t height
int32_t width
FlutterBackingStoreType type
Specifies the type of backing store.
Definition embedder.h:2109
FlutterOpenGLBackingStore open_gl
The description of the OpenGL backing store.
Definition embedder.h:2115
FlutterSoftwareBackingStore software
The description of the software backing store.
Definition embedder.h:2117
size_t struct_size
The size of this struct. Must be sizeof(FlutterBackingStore).
Definition embedder.h:2103
size_t struct_size
The size of this struct. Must be sizeof(FlutterBackingStorePresentInfo).
Definition embedder.h:2159
A structure to represent a damage region.
Definition embedder.h:671
size_t num_rects
The number of rectangles within the damage region.
Definition embedder.h:675
FlutterRect * damage
The actual damage region(s) in question.
Definition embedder.h:677
FlutterEngineDartObjectType type
Definition embedder.h:2425
size_t struct_size
The size of this struct. Must be sizeof(FlutterEngineDisplay).
Definition embedder.h:2329
FlutterEngineDisplayId display_id
Definition embedder.h:2331
FlutterUIntSize size
The size of the surface that will be backed by the fbo.
Definition embedder.h:690
FlutterPoint offset
Definition embedder.h:2183
FlutterLayerContentType type
Definition embedder.h:2172
const FlutterBackingStore * backing_store
Definition embedder.h:2176
FlutterBackingStorePresentInfo * backing_store_present_info
Definition embedder.h:2189
const FlutterPlatformView * platform_view
Definition embedder.h:2179
size_t struct_size
This size of this struct. Must be sizeof(FlutterLayer).
Definition embedder.h:2169
FlutterSize size
The size of the layer (in physical pixels).
Definition embedder.h:2185
FlutterOpenGLTargetType type
Definition embedder.h:1952
UIntFrameInfoCallback fbo_with_frame_info_callback
Definition embedder.h:778
size_t struct_size
The size of this struct. Must be sizeof(FlutterPlatformView).
Definition embedder.h:2063
FlutterPlatformViewIdentifier identifier
Definition embedder.h:2067
FlutterTransformation transformation
Definition embedder.h:2057
FlutterPlatformViewMutationType type
The type of the mutation described by the subsequent union.
Definition embedder.h:2052
FlutterRoundedRect clip_rounded_rect
Definition embedder.h:2056
uint32_t fbo_id
Id of the fbo backing the surface that was presented.
Definition embedder.h:712
FlutterDamage frame_damage
Damage representing the area that the compositor needs to render.
Definition embedder.h:714
FlutterDamage buffer_damage
Damage used to set the buffer's damage region.
Definition embedder.h:716
A structure to represent a rectangle.
Definition embedder.h:648
double bottom
Definition embedder.h:652
double top
Definition embedder.h:650
double left
Definition embedder.h:649
double right
Definition embedder.h:651
A region represented by a collection of non-overlapping rectangles.
Definition embedder.h:2146
size_t struct_size
The size of this struct. Must be sizeof(FlutterRegion).
Definition embedder.h:2148
FlutterOpenGLRendererConfig open_gl
Definition embedder.h:1040
double height
Definition embedder.h:636
double width
Definition embedder.h:635
size_t row_bytes
The number of bytes in a single row of the allocation.
Definition embedder.h:1970
size_t height
The number of rows in the allocation.
Definition embedder.h:1972
uint32_t height
Definition embedder.h:644
uint32_t width
Definition embedder.h:643
size_t struct_size
The size of this struct. Must be sizeof(FlutterWindowMetricsEvent).
Definition embedder.h:1054
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition matrix.h:95
static Matrix MakeRotationZ(Radians r)
Definition matrix.h:223
static constexpr Matrix MakeScale(const Vector3 &s)
Definition matrix.h:104
static constexpr TRect MakeWH(Type width, Type height)
Definition rect.h:140
static constexpr TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition rect.h:136
constexpr TRect TransformAndClipBounds(const Matrix &transform) const
Creates a new bounding box that contains this transformed rectangle, clipped against the near clippin...
Definition rect.h:472
static constexpr TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition rect.h:129
#define CREATE_NATIVE_ENTRY(native_entry)
int64_t texture_id
const uintptr_t id