Skip to content

Commit

Permalink
When using a custom compositor, ensure the root canvas is flushed. (f…
Browse files Browse the repository at this point in the history
…lutter#11310)

The root canvas is managed by the external view embedder when using a custom
compositor. Due to this, frame submission on the surface will not end up
flushing the same (because the surface doesn’t have it to begin with). Fixed
with tests.
  • Loading branch information
chinmaygarde authored Aug 21, 2019
1 parent a79a31a commit 9b1f6d3
Show file tree
Hide file tree
Showing 6 changed files with 294 additions and 1 deletion.
1 change: 1 addition & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -827,6 +827,7 @@ FILE: ../../../flutter/shell/platform/embedder/embedder_task_runner.h
FILE: ../../../flutter/shell/platform/embedder/embedder_thread_host.cc
FILE: ../../../flutter/shell/platform/embedder/embedder_thread_host.h
FILE: ../../../flutter/shell/platform/embedder/fixtures/compositor.png
FILE: ../../../flutter/shell/platform/embedder/fixtures/compositor_with_root_layer_only.png
FILE: ../../../flutter/shell/platform/embedder/fixtures/main.dart
FILE: ../../../flutter/shell/platform/embedder/platform_view_embedder.cc
FILE: ../../../flutter/shell/platform/embedder/platform_view_embedder.h
Expand Down
5 changes: 4 additions & 1 deletion shell/platform/embedder/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,10 @@ config("embedder_prefix_config") {

test_fixtures("fixtures") {
dart_main = "fixtures/main.dart"
fixtures = [ "fixtures/compositor.png" ]
fixtures = [
"fixtures/compositor.png",
"fixtures/compositor_with_root_layer_only.png",
]
}

if (current_toolchain == host_toolchain) {
Expand Down
4 changes: 4 additions & 0 deletions shell/platform/embedder/embedder_external_view_embedder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,10 @@ bool EmbedderExternalViewEmbedder::SubmitFrame(GrContext* context) {
return false;
}

if (auto root_canvas = root_render_target_->GetRenderSurface()->getCanvas()) {
root_canvas->flush();
}

{
// The root surface is expressed as a layer.
EmbeddedViewParams params;
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
46 changes: 46 additions & 0 deletions shell/platform/embedder/fixtures/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -254,3 +254,49 @@ void can_composite_platform_views_with_known_scene() {
window.scheduleFrame();
}

@pragma('vm:entry-point')
void can_composite_platform_views_with_root_layer_only() {
window.onBeginFrame = (Duration duration) {
Color red = Color.fromARGB(127, 255, 0, 0);
Size size = Size(50.0, 150.0);

SceneBuilder builder = SceneBuilder();
builder.pushOffset(0.0, 0.0);

// 10 (Index 0)
builder.addPicture(Offset(10.0, 10.0), CreateColoredBox(red, size)); // red - flutter
builder.pop();

window.render(builder.build());

signalNativeTest(); // Signal 2
};
signalNativeTest(); // Signal 1
window.scheduleFrame();
}

@pragma('vm:entry-point')
void can_composite_platform_views_with_platform_layer_on_bottom() {
window.onBeginFrame = (Duration duration) {
Color red = Color.fromARGB(127, 255, 0, 0);
Size size = Size(50.0, 150.0);

SceneBuilder builder = SceneBuilder();
builder.pushOffset(0.0, 0.0);

// 10 (Index 0)
builder.addPicture(Offset(10.0, 10.0), CreateColoredBox(red, size)); // red - flutter

builder.pushOffset(20.0, 20.0);
// 20 (Index 1)
builder.addPlatformView(1, width: size.width, height:size.height); // green - platform
builder.pop();
builder.pop();

window.render(builder.build());

signalNativeTest(); // Signal 2
};
signalNativeTest(); // Signal 1
window.scheduleFrame();
}
239 changes: 239 additions & 0 deletions shell/platform/embedder/tests/embedder_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1200,5 +1200,244 @@ TEST_F(EmbedderTest, CustomCompositorMustWorkWithCustomTaskRunner) {
sync_latch.Wait();
}

//------------------------------------------------------------------------------
/// Test the layer structure and pixels rendered when using a custom compositor
/// and a single layer.
///
TEST_F(EmbedderTest, CompositorMustBeAbleToRenderWithRootLayerOnly) {
auto& context = GetEmbedderContext();

context.SetupCompositor();

context.GetCompositor().SetRenderTargetType(
EmbedderTestCompositor::RenderTargetType::kOpenGLTexture);

fml::CountDownLatch latch(4);

sk_sp<SkImage> scene_image;
context.SetNextSceneCallback([&](sk_sp<SkImage> scene) {
scene_image = std::move(scene);
latch.CountDown();
});

context.GetCompositor().SetNextPresentCallback(
[&](const FlutterLayer** layers, size_t layers_count) {
ASSERT_EQ(layers_count, 1u);

// Layer Root
{
FlutterBackingStore backing_store = *layers[0]->backing_store;
backing_store.type = kFlutterBackingStoreTypeOpenGL;
backing_store.did_update = true;
backing_store.open_gl.type = kFlutterOpenGLTargetTypeTexture;

FlutterLayer layer = {};
layer.struct_size = sizeof(layer);
layer.type = kFlutterLayerContentTypeBackingStore;
layer.backing_store = &backing_store;
layer.size = FlutterSizeMake(800.0, 600.0);
layer.offset = FlutterPointMake(0.0, 0.0);

ASSERT_EQ(*layers[0], layer);
}

latch.CountDown();
});

EmbedderConfigBuilder builder(context);
builder.SetOpenGLRendererConfig();
builder.SetCompositor();
builder.SetDartEntrypoint(
"can_composite_platform_views_with_root_layer_only");
context.AddNativeCallback(
"SignalNativeTest",
CREATE_NATIVE_ENTRY(
[&latch](Dart_NativeArguments args) { latch.CountDown(); }));

auto engine = builder.LaunchEngine();

// Send a window metrics events so frames may be scheduled.
FlutterWindowMetricsEvent event = {};
event.struct_size = sizeof(event);
event.width = 800;
event.height = 600;
ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
kSuccess);
ASSERT_TRUE(engine.is_valid());

latch.Wait();

// Render the scene and assert that it matches expectation.
ASSERT_TRUE(scene_image);

fml::FileMapping fixture_image_mapping(
OpenFixture("compositor_with_root_layer_only.png"));
ASSERT_GE(fixture_image_mapping.GetSize(), 0u);
auto encoded_image = SkData::MakeWithoutCopy(
fixture_image_mapping.GetMapping(), fixture_image_mapping.GetSize());
auto fixture_image =
SkImage::MakeFromEncoded(std::move(encoded_image))->makeRasterImage();
ASSERT_TRUE(fixture_image);
auto scene_image_subset = scene_image->makeSubset(
SkIRect::MakeWH(fixture_image->width(), fixture_image->height()));
ASSERT_TRUE(scene_image_subset);
const auto images_are_same =
RasterImagesAreSame(scene_image_subset, fixture_image);
if (!images_are_same) {
auto fixtures_fd = OpenFixturesDirectory();
ASSERT_TRUE(WriteImageToDisk(fixtures_fd, "actual_with_root_layer_only.png",
scene_image_subset));
ASSERT_TRUE(WriteImageToDisk(
fixtures_fd, "expectation_with_root_layer_only.png", fixture_image));
FML_LOG(ERROR) << "Test compositor did not generated expected images. Got: "
"'actual_with_root_layer_only.png' Expected: "
"'compositor_with_root_layer_only.png' in Directory: "
<< GetFixturesPath();
}
ASSERT_TRUE(images_are_same);
}

//------------------------------------------------------------------------------
/// Test the layer structure and pixels rendered when using a custom compositor
/// and ensure that a redundant layer is not added.
///
TEST_F(EmbedderTest,
DISABLED_CompositorMustBeAbleToRenderWithPlatformLayerOnBottom) {
auto& context = GetEmbedderContext();

context.SetupCompositor();

context.GetCompositor().SetRenderTargetType(
EmbedderTestCompositor::RenderTargetType::kOpenGLTexture);

fml::CountDownLatch latch(4);

sk_sp<SkImage> scene_image;
context.SetNextSceneCallback([&](sk_sp<SkImage> scene) {
scene_image = std::move(scene);
latch.CountDown();
});

context.GetCompositor().SetNextPresentCallback(
[&](const FlutterLayer** layers, size_t layers_count) {
ASSERT_EQ(layers_count, 2u);

// Layer Root
{
FlutterBackingStore backing_store = *layers[0]->backing_store;
backing_store.type = kFlutterBackingStoreTypeOpenGL;
backing_store.did_update = true;
backing_store.open_gl.type = kFlutterOpenGLTargetTypeTexture;

FlutterLayer layer = {};
layer.struct_size = sizeof(layer);
layer.type = kFlutterLayerContentTypeBackingStore;
layer.backing_store = &backing_store;
layer.size = FlutterSizeMake(800.0, 600.0);
layer.offset = FlutterPointMake(0.0, 0.0);

ASSERT_EQ(*layers[0], layer);
}

// Layer 1
{
FlutterPlatformView platform_view = {};
platform_view.struct_size = sizeof(platform_view);
platform_view.identifier = 1;

FlutterLayer layer = {};
layer.struct_size = sizeof(layer);
layer.type = kFlutterLayerContentTypePlatformView;
layer.platform_view = &platform_view;
layer.size = FlutterSizeMake(50.0, 150.0);
layer.offset = FlutterPointMake(20.0, 20.0);

ASSERT_EQ(*layers[1], layer);
}

latch.CountDown();
});

context.GetCompositor().SetPlatformViewRendererCallback(
[&](const FlutterLayer& layer, GrContext* context) -> sk_sp<SkImage> {
auto surface = CreateRenderSurface(layer, context);
auto canvas = surface->getCanvas();
FML_CHECK(canvas != nullptr);

switch (layer.platform_view->identifier) {
case 1: {
SkPaint paint;
// See dart test for total order.
paint.setColor(SK_ColorGREEN);
paint.setAlpha(127);
const auto& rect =
SkRect::MakeWH(layer.size.width, layer.size.height);
canvas->drawRect(rect, paint);
latch.CountDown();
} break;
default:
// Asked to render an unknown platform view.
FML_CHECK(false)
<< "Test was asked to composite an unknown platform view.";
}

return surface->makeImageSnapshot();
});

EmbedderConfigBuilder builder(context);
builder.SetOpenGLRendererConfig();
builder.SetCompositor();
builder.SetDartEntrypoint(
"can_composite_platform_views_with_platform_layer_on_bottom");
context.AddNativeCallback(
"SignalNativeTest",
CREATE_NATIVE_ENTRY(
[&latch](Dart_NativeArguments args) { latch.CountDown(); }));

auto engine = builder.LaunchEngine();

// Send a window metrics events so frames may be scheduled.
FlutterWindowMetricsEvent event = {};
event.struct_size = sizeof(event);
event.width = 800;
event.height = 600;
ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
kSuccess);
ASSERT_TRUE(engine.is_valid());

latch.Wait();

// Render the scene and assert that it matches expectation.
ASSERT_TRUE(scene_image);
fml::FileMapping fixture_image_mapping(
OpenFixture("compositor_with_platform_layer_on_bottom.png"));
ASSERT_GE(fixture_image_mapping.GetSize(), 0u);
auto encoded_image = SkData::MakeWithoutCopy(
fixture_image_mapping.GetMapping(), fixture_image_mapping.GetSize());
auto fixture_image =
SkImage::MakeFromEncoded(std::move(encoded_image))->makeRasterImage();
ASSERT_TRUE(fixture_image);
auto scene_image_subset = scene_image->makeSubset(
SkIRect::MakeWH(fixture_image->width(), fixture_image->height()));
ASSERT_TRUE(scene_image_subset);
const auto images_are_same =
RasterImagesAreSame(scene_image_subset, fixture_image);
if (!images_are_same) {
auto fixtures_fd = OpenFixturesDirectory();
ASSERT_TRUE(WriteImageToDisk(fixtures_fd,
"actual_with_platform_layer_on_bottom.png",
scene_image_subset));
ASSERT_TRUE(WriteImageToDisk(
fixtures_fd, "expectation_with_platform_layer_on_bottom.png",
fixture_image));
FML_LOG(ERROR) << "Test compositor did not generated expected images. Got: "
"'actual_with_platform_layer_on_bottom.png' Expected: "
"'expectation_with_patform_layer_on_bottom.png' in "
"Directory: "
<< GetFixturesPath();
}
ASSERT_TRUE(images_are_same);
}

} // namespace testing
} // namespace flutter

0 comments on commit 9b1f6d3

Please sign in to comment.