Account for root surface transformation on the surfaces managed by the external view embedder. (flutter/engine#11384)

The earlier design speculated that embedders could affect the same
transformations on the layers post engine compositor presentation but before
final composition.

However, the linked issue points out that this design is not suitable for use
with hardware overlay planes. When rendering to the same, to affect the
transformation before composition, embedders would have to render to an
off-screen render target and then apply the transformation before presentation.
This patch negates the need for that off-screen render pass.

To be clear, the previous architecture is still fully viable. Embedders still
have full control over layer transformations before composition. This is an
optimization for the hardware overlay planes use-case.

Fixes b/139758641
This commit is contained in:
Chinmay Garde 2019-09-17 15:16:59 -07:00 committed by GitHub
parent c8c68ac789
commit c8ecb03fa8
32 changed files with 1175 additions and 292 deletions

View File

@ -24,8 +24,6 @@ FILE: ../../../flutter/common/task_runners.cc
FILE: ../../../flutter/common/task_runners.h
FILE: ../../../flutter/flow/compositor_context.cc
FILE: ../../../flutter/flow/compositor_context.h
FILE: ../../../flutter/flow/debug_print.cc
FILE: ../../../flutter/flow/debug_print.h
FILE: ../../../flutter/flow/embedded_views.cc
FILE: ../../../flutter/flow/embedded_views.h
FILE: ../../../flutter/flow/instrumentation.cc
@ -849,10 +847,15 @@ 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_root_surface_xformation.png
FILE: ../../../flutter/shell/platform/embedder/fixtures/compositor_software.png
FILE: ../../../flutter/shell/platform/embedder/fixtures/compositor_with_platform_layer_on_bottom.png
FILE: ../../../flutter/shell/platform/embedder/fixtures/compositor_with_root_layer_only.png
FILE: ../../../flutter/shell/platform/embedder/fixtures/gradient.png
FILE: ../../../flutter/shell/platform/embedder/fixtures/gradient_xform.png
FILE: ../../../flutter/shell/platform/embedder/fixtures/main.dart
FILE: ../../../flutter/shell/platform/embedder/fixtures/scene_without_custom_compositor.png
FILE: ../../../flutter/shell/platform/embedder/fixtures/scene_without_custom_compositor_with_xform.png
FILE: ../../../flutter/shell/platform/embedder/platform_view_embedder.cc
FILE: ../../../flutter/shell/platform/embedder/platform_view_embedder.h
FILE: ../../../flutter/shell/platform/embedder/vsync_waiter_embedder.cc

View File

@ -12,8 +12,6 @@ source_set("flow") {
sources = [
"compositor_context.cc",
"compositor_context.h",
"debug_print.cc",
"debug_print.h",
"embedded_views.cc",
"embedded_views.h",
"instrumentation.cc",

View File

@ -1,84 +0,0 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/flow/debug_print.h"
#include <ostream>
#include "third_party/skia/include/core/SkString.h"
std::ostream& operator<<(std::ostream& os,
const flutter::MatrixDecomposition& m) {
if (!m.IsValid()) {
os << "Invalid Matrix!" << std::endl;
return os;
}
os << "Translation (x, y, z): " << m.translation() << std::endl;
os << "Scale (z, y, z): " << m.scale() << std::endl;
os << "Shear (zy, yz, zx): " << m.shear() << std::endl;
os << "Perspective (x, y, z, w): " << m.perspective() << std::endl;
os << "Rotation (x, y, z, w): " << m.rotation() << std::endl;
return os;
}
std::ostream& operator<<(std::ostream& os, const SkMatrix& m) {
SkString string;
string.printf("[%8.4f %8.4f %8.4f][%8.4f %8.4f %8.4f][%8.4f %8.4f %8.4f]",
m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
os << string.c_str();
return os;
}
std::ostream& operator<<(std::ostream& os, const SkMatrix44& m) {
os << m.get(0, 0) << ", " << m.get(0, 1) << ", " << m.get(0, 2) << ", "
<< m.get(0, 3) << std::endl;
os << m.get(1, 0) << ", " << m.get(1, 1) << ", " << m.get(1, 2) << ", "
<< m.get(1, 3) << std::endl;
os << m.get(2, 0) << ", " << m.get(2, 1) << ", " << m.get(2, 2) << ", "
<< m.get(2, 3) << std::endl;
os << m.get(3, 0) << ", " << m.get(3, 1) << ", " << m.get(3, 2) << ", "
<< m.get(3, 3);
return os;
}
std::ostream& operator<<(std::ostream& os, const SkVector3& v) {
os << v.x() << ", " << v.y() << ", " << v.z();
return os;
}
std::ostream& operator<<(std::ostream& os, const SkVector4& v) {
os << v.fData[0] << ", " << v.fData[1] << ", " << v.fData[2] << ", "
<< v.fData[3];
return os;
}
std::ostream& operator<<(std::ostream& os, const SkRect& r) {
os << "LTRB: " << r.fLeft << ", " << r.fTop << ", " << r.fRight << ", "
<< r.fBottom;
return os;
}
std::ostream& operator<<(std::ostream& os, const SkRRect& r) {
os << "LTRB: " << r.rect().fLeft << ", " << r.rect().fTop << ", "
<< r.rect().fRight << ", " << r.rect().fBottom;
return os;
}
std::ostream& operator<<(std::ostream& os, const SkPoint& r) {
os << "XY: " << r.fX << ", " << r.fY;
return os;
}
std::ostream& operator<<(std::ostream& os,
const flutter::PictureRasterCacheKey& k) {
os << "Picture: " << k.id() << " matrix: " << k.matrix();
return os;
}
std::ostream& operator<<(std::ostream& os, const SkISize& size) {
os << size.width() << ", " << size.height();
return os;
}

View File

@ -1,29 +0,0 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_FLOW_DEBUG_PRINT_H_
#define FLUTTER_FLOW_DEBUG_PRINT_H_
#include "flutter/flow/matrix_decomposition.h"
#include "flutter/flow/raster_cache_key.h"
#include "flutter/fml/macros.h"
#include "third_party/skia/include/core/SkMatrix.h"
#include "third_party/skia/include/core/SkMatrix44.h"
#include "third_party/skia/include/core/SkPoint3.h"
#include "third_party/skia/include/core/SkRRect.h"
#define DEF_PRINTER(x) std::ostream& operator<<(std::ostream&, const x&);
DEF_PRINTER(flutter::MatrixDecomposition);
DEF_PRINTER(flutter::PictureRasterCacheKey);
DEF_PRINTER(SkISize);
DEF_PRINTER(SkMatrix);
DEF_PRINTER(SkMatrix44);
DEF_PRINTER(SkPoint);
DEF_PRINTER(SkRect);
DEF_PRINTER(SkRRect);
DEF_PRINTER(SkVector3);
DEF_PRINTER(SkVector4);
#endif // FLUTTER_FLOW_DEBUG_PRINT_H_

View File

@ -19,7 +19,8 @@ class TestIOManager final : public IOManager {
public:
TestIOManager(fml::RefPtr<fml::TaskRunner> task_runner,
bool has_gpu_context = true)
: gl_context_(has_gpu_context ? gl_surface_.CreateGrContext() : nullptr),
: gl_surface_(SkISize::Make(1, 1)),
gl_context_(has_gpu_context ? gl_surface_.CreateGrContext() : nullptr),
weak_gl_context_factory_(
has_gpu_context ? std::make_unique<fml::WeakPtrFactory<GrContext>>(
gl_context_.get())

View File

@ -234,7 +234,8 @@ void ShellTest::AddNativeCallback(std::string name,
ShellTestPlatformView::ShellTestPlatformView(PlatformView::Delegate& delegate,
TaskRunners task_runners)
: PlatformView(delegate, std::move(task_runners)) {}
: PlatformView(delegate, std::move(task_runners)),
gl_surface_(SkISize::Make(800, 600)) {}
ShellTestPlatformView::~ShellTestPlatformView() = default;

View File

@ -85,9 +85,14 @@ test_fixtures("fixtures") {
dart_main = "fixtures/main.dart"
fixtures = [
"fixtures/compositor.png",
"fixtures/gradient.png",
"fixtures/gradient_xform.png",
"fixtures/compositor_software.png",
"fixtures/compositor_with_platform_layer_on_bottom.png",
"fixtures/compositor_with_root_layer_only.png",
"fixtures/compositor_root_surface_xformation.png",
"fixtures/scene_without_custom_compositor.png",
"fixtures/scene_without_custom_compositor_with_xform.png",
]
}
@ -119,6 +124,7 @@ if (current_toolchain == host_toolchain) {
"$flutter_root/runtime",
"$flutter_root/testing:dart",
"$flutter_root/testing:opengl",
"$flutter_root/testing:skia",
"//third_party/skia",
"//third_party/tonic",
]

View File

@ -171,6 +171,13 @@ InferOpenGLPlatformViewCreationCallback(
transformation.pers2 //
);
};
// If there is an external view embedder, ask it to apply the surface
// transformation to its surfaces as well.
if (external_view_embedder) {
external_view_embedder->SetSurfaceTransformationCallback(
gl_surface_transformation_callback);
}
}
flutter::GPUSurfaceGLDelegate::GLProcResolver gl_proc_resolver = nullptr;
@ -296,13 +303,13 @@ static sk_sp<SkSurface> MakeSkSurfaceFromBackingStore(
SkSurfaceProps::InitType::kLegacyFontHost_InitType);
auto surface = SkSurface::MakeFromBackendTexture(
context, // context
backend_texture, // back-end texture
kTopLeft_GrSurfaceOrigin, // surface origin
1, // sample count
kN32_SkColorType, // color type
SkColorSpace::MakeSRGB(), // color space
&surface_properties, // surface properties
context, // context
backend_texture, // back-end texture
kBottomLeft_GrSurfaceOrigin, // surface origin
1, // sample count
kN32_SkColorType, // color type
SkColorSpace::MakeSRGB(), // color space
&surface_properties, // surface properties
static_cast<SkSurface::TextureReleaseProc>(
texture->destruction_callback), // release proc
texture->user_data // release context
@ -337,12 +344,12 @@ static sk_sp<SkSurface> MakeSkSurfaceFromBackingStore(
SkSurfaceProps::InitType::kLegacyFontHost_InitType);
auto surface = SkSurface::MakeFromBackendRenderTarget(
context, // context
backend_render_target, // backend render target
kTopLeft_GrSurfaceOrigin, // surface origin
kN32_SkColorType, // color type
SkColorSpace::MakeSRGB(), // color space
&surface_properties, // surface properties
context, // context
backend_render_target, // backend render target
kBottomLeft_GrSurfaceOrigin, // surface origin
kN32_SkColorType, // color type
SkColorSpace::MakeSRGB(), // color space
&surface_properties, // surface properties
static_cast<SkSurface::RenderTargetReleaseProc>(
framebuffer->destruction_callback), // release proc
framebuffer->user_data // release context

View File

@ -282,6 +282,14 @@ typedef struct {
bool fbo_reset_after_present;
/// The transformation to apply to the render target before any rendering
/// operations. This callback is optional.
/// @attention When using a custom compositor, the layer offset and sizes
/// will be affected by this transformation. It will be
/// embedder responsibility to render contents at the
/// transformed offset and size. This is useful for embedders
/// that want to render transformed contents directly into
/// hardware overlay planes without having to apply extra
/// transformations to layer contents (which may necessitate
/// an expensive off-screen render pass).
TransformationCallback surface_transformation;
ProcResolver gl_proc_resolver;
/// When the embedder specifies that a texture has a frame available, the

View File

@ -21,6 +21,19 @@ EmbedderExternalViewEmbedder::EmbedderExternalViewEmbedder(
EmbedderExternalViewEmbedder::~EmbedderExternalViewEmbedder() = default;
void EmbedderExternalViewEmbedder::SetSurfaceTransformationCallback(
SurfaceTransformationCallback surface_transformation_callback) {
surface_transformation_callback_ = surface_transformation_callback;
}
SkMatrix EmbedderExternalViewEmbedder::GetSurfaceTransformation() const {
if (!surface_transformation_callback_) {
return SkMatrix{};
}
return surface_transformation_callback_();
}
void EmbedderExternalViewEmbedder::Reset() {
pending_recorders_.clear();
pending_canvas_spies_.clear();
@ -33,22 +46,34 @@ void EmbedderExternalViewEmbedder::CancelFrame() {
Reset();
}
static FlutterBackingStoreConfig MakeBackingStoreConfig(const SkISize& size) {
static FlutterBackingStoreConfig MakeBackingStoreConfig(
const SkISize& backing_store_size) {
FlutterBackingStoreConfig config = {};
config.struct_size = sizeof(config);
config.size.width = size.width();
config.size.height = size.height();
config.size.width = backing_store_size.width();
config.size.height = backing_store_size.height();
return config;
}
static SkISize TransformedSurfaceSize(const SkISize& size,
const SkMatrix& transformation) {
const auto source_rect = SkRect::MakeWH(size.width(), size.height());
const auto transformed_rect = transformation.mapRect(source_rect);
return SkISize::Make(transformed_rect.width(), transformed_rect.height());
}
// |ExternalViewEmbedder|
void EmbedderExternalViewEmbedder::BeginFrame(SkISize frame_size,
GrContext* context) {
Reset();
pending_frame_size_ = frame_size;
pending_surface_transformation_ = GetSurfaceTransformation();
const auto surface_size = TransformedSurfaceSize(
pending_frame_size_, pending_surface_transformation_);
// Decide if we want to discard the previous root render target.
if (root_render_target_) {
@ -61,7 +86,7 @@ void EmbedderExternalViewEmbedder::BeginFrame(SkISize frame_size,
} else {
auto last_surface_size =
SkISize::Make(surface->width(), surface->height());
if (pending_frame_size_ != last_surface_size) {
if (surface_size != last_surface_size) {
root_render_target_ = nullptr;
}
}
@ -72,7 +97,19 @@ void EmbedderExternalViewEmbedder::BeginFrame(SkISize frame_size,
// canvas.
if (!root_render_target_) {
root_render_target_ = create_render_target_callback_(
context, MakeBackingStoreConfig(pending_frame_size_));
context, MakeBackingStoreConfig(surface_size));
}
// Install the root surface transformation on the root canvas at the beginning
// of each frame.
if (root_render_target_) {
auto surface = root_render_target_->GetRenderSurface();
if (surface) {
auto canvas = surface->getCanvas();
if (canvas) {
canvas->setMatrix(pending_surface_transformation_);
}
}
}
}
@ -115,19 +152,26 @@ SkCanvas* EmbedderExternalViewEmbedder::CompositeEmbeddedView(int view_id) {
return found->second->GetSpyingCanvas();
}
static FlutterLayer MakeLayer(const SkISize& frame_size,
const FlutterBackingStore* store) {
static FlutterLayer MakeBackingStoreLayer(
const SkISize& frame_size,
const FlutterBackingStore* store,
const SkMatrix& surface_transformation) {
FlutterLayer layer = {};
layer.struct_size = sizeof(layer);
layer.type = kFlutterLayerContentTypeBackingStore;
layer.backing_store = store;
layer.offset.x = 0.0;
layer.offset.y = 0.0;
const auto layer_bounds =
SkRect::MakeWH(frame_size.width(), frame_size.height());
layer.size.width = frame_size.width();
layer.size.height = frame_size.height();
const auto transformed_layer_bounds =
surface_transformation.mapRect(layer_bounds);
layer.offset.x = transformed_layer_bounds.x();
layer.offset.y = transformed_layer_bounds.y();
layer.size.width = transformed_layer_bounds.width();
layer.size.height = transformed_layer_bounds.height();
return layer;
}
@ -143,19 +187,29 @@ static FlutterPlatformView MakePlatformView(
return view;
}
static FlutterLayer MakeLayer(const EmbeddedViewParams& params,
const FlutterPlatformView& platform_view) {
static FlutterLayer MakePlatformViewLayer(
const EmbeddedViewParams& params,
const FlutterPlatformView& platform_view,
const SkMatrix& surface_transformation) {
FlutterLayer layer = {};
layer.struct_size = sizeof(layer);
layer.type = kFlutterLayerContentTypePlatformView;
layer.platform_view = &platform_view;
layer.offset.x = params.offsetPixels.x();
layer.offset.y = params.offsetPixels.y();
const auto layer_bounds = SkRect::MakeXYWH(params.offsetPixels.x(), //
params.offsetPixels.y(), //
params.sizePoints.width(), //
params.sizePoints.height() //
);
layer.size.width = params.sizePoints.width();
layer.size.height = params.sizePoints.height();
const auto transformed_layer_bounds =
surface_transformation.mapRect(layer_bounds);
layer.offset.x = transformed_layer_bounds.x();
layer.offset.y = transformed_layer_bounds.y();
layer.size.width = transformed_layer_bounds.width();
layer.size.height = transformed_layer_bounds.height();
return layer;
}
@ -180,13 +234,14 @@ bool EmbedderExternalViewEmbedder::SubmitFrame(GrContext* context) {
{
// The root surface is expressed as a layer.
EmbeddedViewParams params;
params.offsetPixels = SkPoint::Make(0, 0);
params.sizePoints = pending_frame_size_;
presented_layers.push_back(
MakeLayer(pending_frame_size_, root_render_target_->GetBackingStore()));
presented_layers.push_back(MakeBackingStoreLayer(
pending_frame_size_, root_render_target_->GetBackingStore(),
pending_surface_transformation_));
}
const auto surface_size = TransformedSurfaceSize(
pending_frame_size_, pending_surface_transformation_);
for (const auto& view_id : composition_order_) {
FML_DCHECK(pending_recorders_.count(view_id) == 1);
FML_DCHECK(pending_canvas_spies_.count(view_id) == 1);
@ -208,15 +263,16 @@ bool EmbedderExternalViewEmbedder::SubmitFrame(GrContext* context) {
// struct. It is safe to deallocate when the embedder callback is done.
presented_platform_views[view_id] = MakePlatformView(view_id);
presented_layers.push_back(
MakeLayer(params, presented_platform_views.at(view_id)));
MakePlatformViewLayer(params, presented_platform_views.at(view_id),
pending_surface_transformation_));
if (!pending_canvas_spies_.at(view_id)->DidDrawIntoCanvas()) {
// Nothing was drawn into the overlay canvas, we don't need to composite
// it.
continue;
}
const auto backing_store_config =
MakeBackingStoreConfig(pending_frame_size_);
const auto backing_store_config = MakeBackingStoreConfig(surface_size);
RegistryKey registry_key(view_id, backing_store_config);
@ -249,13 +305,15 @@ bool EmbedderExternalViewEmbedder::SubmitFrame(GrContext* context) {
return false;
}
render_canvas->setMatrix(pending_surface_transformation_);
render_canvas->clear(SK_ColorTRANSPARENT);
render_canvas->drawPicture(picture);
render_canvas->flush();
// Indicate a layer for the backing store containing contents rendered by
// Flutter.
presented_layers.push_back(
MakeLayer(pending_frame_size_, render_target->GetBackingStore()));
presented_layers.push_back(MakeBackingStoreLayer(
pending_frame_size_, render_target->GetBackingStore(),
pending_surface_transformation_));
}
{

View File

@ -34,6 +34,7 @@ class EmbedderExternalViewEmbedder final : public ExternalViewEmbedder {
const FlutterBackingStoreConfig& config)>;
using PresentCallback =
std::function<bool(const std::vector<const FlutterLayer*>& layers)>;
using SurfaceTransformationCallback = std::function<SkMatrix(void)>;
//----------------------------------------------------------------------------
/// @brief Creates an external view embedder used by the generic embedder
@ -56,6 +57,17 @@ class EmbedderExternalViewEmbedder final : public ExternalViewEmbedder {
///
~EmbedderExternalViewEmbedder() override;
//----------------------------------------------------------------------------
/// @brief Sets the surface transformation callback used by the external
/// view embedder to ask the platform for the per frame root
/// surface transformation.
///
/// @param[in] surface_transformation_callback The surface transformation
/// callback
///
void SetSurfaceTransformationCallback(
SurfaceTransformationCallback surface_transformation_callback);
private:
// |ExternalViewEmbedder|
void CancelFrame() override;
@ -108,12 +120,14 @@ class EmbedderExternalViewEmbedder final : public ExternalViewEmbedder {
const CreateRenderTargetCallback create_render_target_callback_;
const PresentCallback present_callback_;
SurfaceTransformationCallback surface_transformation_callback_;
using Registry = std::unordered_map<RegistryKey,
std::shared_ptr<EmbedderRenderTarget>,
RegistryKey::Hash,
RegistryKey::Equal>;
SkISize pending_frame_size_ = SkISize::Make(0, 0);
SkMatrix pending_surface_transformation_;
std::map<ViewIdentifier, std::unique_ptr<SkPictureRecorder>>
pending_recorders_;
std::map<ViewIdentifier, std::unique_ptr<CanvasSpy>> pending_canvas_spies_;
@ -124,6 +138,8 @@ class EmbedderExternalViewEmbedder final : public ExternalViewEmbedder {
void Reset();
SkMatrix GetSurfaceTransformation() const;
FML_DISALLOW_COPY_AND_ASSIGN(EmbedderExternalViewEmbedder);
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

View File

@ -300,3 +300,107 @@ void can_composite_platform_views_with_platform_layer_on_bottom() {
signalNativeTest(); // Signal 1
window.scheduleFrame();
}
@pragma('vm:entry-point')
void can_render_scene_without_custom_compositor() {
window.onBeginFrame = (Duration duration) {
Color red = Color.fromARGB(127, 255, 0, 0);
Color green = Color.fromARGB(127, 0, 255, 0);
Color blue = Color.fromARGB(127, 0, 0, 255);
Color magenta = Color.fromARGB(127, 255, 0, 255);
Color gray = Color.fromARGB(127, 127, 127, 127);
Size size = Size(50.0, 150.0);
SceneBuilder builder = SceneBuilder();
builder.pushOffset(0.0, 0.0);
builder.addPicture(Offset(10.0, 10.0), CreateColoredBox(red, size)); // red - flutter
builder.addPicture(Offset(20.0, 20.0), CreateColoredBox(green, size)); // green - flutter
builder.addPicture(Offset(30.0, 30.0), CreateColoredBox(blue, size)); // blue - flutter
builder.addPicture(Offset(40.0, 40.0), CreateColoredBox(magenta, size)); // magenta - flutter
builder.addPicture(Offset(50.0, 50.0), CreateColoredBox(gray, size)); // gray - flutter
builder.pop();
window.render(builder.build());
};
window.scheduleFrame();
}
Picture CreateGradientBox(Size size) {
Paint paint = Paint();
List<Color> rainbow = [
Color.fromARGB(255, 255, 0, 0), // red
Color.fromARGB(255, 255, 165, 0), // orange
Color.fromARGB(255, 255, 255, 0), // yellow
Color.fromARGB(255, 0, 255, 0), // green
Color.fromARGB(255, 0, 0, 255), // blue
Color.fromARGB(255, 75, 0, 130), // indigo
Color.fromARGB(255, 238,130,238), // violet
];
List<double> stops = [
(1.0 / 7.0),
(2.0 / 7.0),
(3.0 / 7.0),
(4.0 / 7.0),
(5.0 / 7.0),
(6.0 / 7.0),
(7.0 / 7.0),
];
paint.shader = Gradient.linear(
Offset(0.0, 0.0),
Offset(size.width, size.height),
rainbow, stops);
PictureRecorder baseRecorder = PictureRecorder();
Canvas canvas = Canvas(baseRecorder);
canvas.drawRect(Rect.fromLTRB(0.0, 0.0, size.width, size.height), paint);
return baseRecorder.endRecording();
}
@pragma('vm:entry-point')
void render_gradient() {
window.onBeginFrame = (Duration duration) {
Size size = Size(800.0, 600.0);
SceneBuilder builder = SceneBuilder();
builder.pushOffset(0.0, 0.0);
builder.addPicture(Offset(0.0, 0.0), CreateGradientBox(size)); // gradient - flutter
builder.pop();
window.render(builder.build());
};
window.scheduleFrame();
}
@pragma('vm:entry-point')
void render_gradient_on_non_root_backing_store() {
window.onBeginFrame = (Duration duration) {
Size size = Size(800.0, 600.0);
Color red = Color.fromARGB(127, 255, 0, 0);
SceneBuilder builder = SceneBuilder();
builder.pushOffset(0.0, 0.0);
// Even though this is occluded, add something so it is not elided.
builder.addPicture(Offset(10.0, 10.0), CreateColoredBox(red, size)); // red - flutter
builder.addPlatformView(1, width: 100, height:200); // undefined - platform
builder.addPicture(Offset(0.0, 0.0), CreateGradientBox(size)); // gradient - flutter
builder.pop();
window.render(builder.build());
};
window.scheduleFrame();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

View File

@ -65,6 +65,7 @@ TEST_F(Embedder11yTest, A11yTreeIsConsistent) {
})));
EmbedderConfigBuilder builder(context);
builder.SetSoftwareRendererConfig();
builder.SetDartEntrypoint("a11y_main");
auto engine = builder.LaunchEngine();

View File

@ -247,4 +247,18 @@ inline FlutterSize FlutterSizeMake(double width, double height) {
return size;
}
inline FlutterTransformation FlutterTransformationMake(const SkMatrix& matrix) {
FlutterTransformation transformation = {};
transformation.scaleX = matrix[SkMatrix::kMScaleX];
transformation.skewX = matrix[SkMatrix::kMSkewX];
transformation.transX = matrix[SkMatrix::kMTransX];
transformation.skewY = matrix[SkMatrix::kMSkewY];
transformation.scaleY = matrix[SkMatrix::kMScaleY];
transformation.transY = matrix[SkMatrix::kMTransY];
transformation.pers0 = matrix[SkMatrix::kMPersp0];
transformation.pers1 = matrix[SkMatrix::kMPersp1];
transformation.pers2 = matrix[SkMatrix::kMPersp2];
return transformation;
}
#endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_TESTS_EMBEDDER_ASSERTIONS_H_

View File

@ -46,6 +46,12 @@ EmbedderConfigBuilder::EmbedderConfigBuilder(
return reinterpret_cast<EmbedderTestContext*>(context)->GLGetProcAddress(
name);
};
opengl_renderer_config_.fbo_reset_after_present = true;
opengl_renderer_config_.surface_transformation =
[](void* context) -> FlutterTransformation {
return reinterpret_cast<EmbedderTestContext*>(context)
->GetRootSurfaceTransformation();
};
software_renderer_config_.struct_size = sizeof(FlutterSoftwareRendererConfig);
software_renderer_config_.surface_present_callback =
@ -70,7 +76,6 @@ EmbedderConfigBuilder::EmbedderConfigBuilder(
AddCommandLineArgument("embedder_unittest");
if (preference == InitializationPreference::kInitialize) {
SetSoftwareRendererConfig();
SetAssetsPath();
SetSnapshots();
SetIsolateCreateCallbackHook();
@ -85,15 +90,20 @@ FlutterProjectArgs& EmbedderConfigBuilder::GetProjectArgs() {
return project_args_;
}
void EmbedderConfigBuilder::SetSoftwareRendererConfig() {
void EmbedderConfigBuilder::SetSoftwareRendererConfig(SkISize surface_size) {
renderer_config_.type = FlutterRendererType::kSoftware;
renderer_config_.software = software_renderer_config_;
// TODO(chinmaygarde): The compositor still uses a GL surface for operation.
// Once this is no longer the case, don't setup the GL surface when using the
// software renderer config.
context_.SetupOpenGLSurface(surface_size);
}
void EmbedderConfigBuilder::SetOpenGLRendererConfig() {
void EmbedderConfigBuilder::SetOpenGLRendererConfig(SkISize surface_size) {
renderer_config_.type = FlutterRendererType::kOpenGL;
renderer_config_.open_gl = opengl_renderer_config_;
context_.SetupOpenGLSurface();
context_.SetupOpenGLSurface(surface_size);
}
void EmbedderConfigBuilder::SetAssetsPath() {

View File

@ -43,9 +43,9 @@ class EmbedderConfigBuilder {
FlutterProjectArgs& GetProjectArgs();
void SetSoftwareRendererConfig();
void SetSoftwareRendererConfig(SkISize surface_size = SkISize::Make(1, 1));
void SetOpenGLRendererConfig();
void SetOpenGLRendererConfig(SkISize surface_size);
void SetAssetsPath();

View File

@ -11,8 +11,10 @@
namespace flutter {
namespace testing {
EmbedderTestCompositor::EmbedderTestCompositor(sk_sp<GrContext> context)
: context_(context) {
EmbedderTestCompositor::EmbedderTestCompositor(SkISize surface_size,
sk_sp<GrContext> context)
: surface_size_(surface_size), context_(context) {
FML_CHECK(!surface_size_.isEmpty()) << "Surface size must not be empty";
FML_CHECK(context_);
}
@ -61,8 +63,7 @@ bool EmbedderTestCompositor::UpdateOffscrenComposition(
size_t layers_count) {
last_composition_ = nullptr;
auto surface_size = SkISize::Make(800, 600);
const auto image_info = SkImageInfo::MakeN32Premul(surface_size);
const auto image_info = SkImageInfo::MakeN32Premul(surface_size_);
auto surface = type_ == RenderTargetType::kSoftwareBuffer
? SkSurface::MakeRaster(image_info)
@ -169,16 +170,16 @@ bool EmbedderTestCompositor::CreateFramebufferRenderSurface(
const auto image_info =
SkImageInfo::MakeN32Premul(config->size.width, config->size.height);
auto surface =
SkSurface::MakeRenderTarget(context_.get(), // context
SkBudgeted::kNo, // budgeted
image_info, // image info
1, // sample count
kTopLeft_GrSurfaceOrigin, // surface origin
nullptr, // surface properties
false // mipmaps
auto surface = SkSurface::MakeRenderTarget(
context_.get(), // context
SkBudgeted::kNo, // budgeted
image_info, // image info
1, // sample count
kBottomLeft_GrSurfaceOrigin, // surface origin
nullptr, // surface properties
false // mipmaps
);
);
if (!surface) {
FML_LOG(ERROR) << "Could not create render target for compositor layer.";
@ -219,16 +220,16 @@ bool EmbedderTestCompositor::CreateTextureRenderSurface(
const auto image_info =
SkImageInfo::MakeN32Premul(config->size.width, config->size.height);
auto surface =
SkSurface::MakeRenderTarget(context_.get(), // context
SkBudgeted::kNo, // budgeted
image_info, // image info
1, // sample count
kTopLeft_GrSurfaceOrigin, // surface origin
nullptr, // surface properties
false // mipmaps
auto surface = SkSurface::MakeRenderTarget(
context_.get(), // context
SkBudgeted::kNo, // budgeted
image_info, // image info
1, // sample count
kBottomLeft_GrSurfaceOrigin, // surface origin
nullptr, // surface properties
false // mipmaps
);
);
if (!surface) {
FML_LOG(ERROR) << "Could not create render target for compositor layer.";

View File

@ -22,7 +22,7 @@ class EmbedderTestCompositor {
kSoftwareBuffer,
};
EmbedderTestCompositor(sk_sp<GrContext> context);
EmbedderTestCompositor(SkISize surface_size, sk_sp<GrContext> context);
~EmbedderTestCompositor();
@ -59,6 +59,7 @@ class EmbedderTestCompositor {
size_t GetBackingStoresCount() const;
private:
const SkISize surface_size_;
sk_sp<GrContext> context_;
RenderTargetType type_ = RenderTargetType::kOpenGLFramebuffer;
PlatformViewRendererCallback platform_view_renderer_callback_;

View File

@ -5,6 +5,7 @@
#include "flutter/shell/platform/embedder/tests/embedder_test_context.h"
#include "flutter/runtime/dart_vm.h"
#include "flutter/shell/platform/embedder/tests/embedder_assertions.h"
#include "third_party/skia/include/core/SkSurface.h"
namespace flutter {
@ -59,6 +60,10 @@ const fml::Mapping* EmbedderTestContext::GetIsolateSnapshotInstructions()
return isolate_snapshot_instructions_.get();
}
void EmbedderTestContext::SetRootSurfaceTransformation(SkMatrix matrix) {
root_surface_transformation_ = matrix;
}
void EmbedderTestContext::AddIsolateCreateCallback(fml::closure closure) {
if (closure) {
isolate_create_callbacks_.push_back(closure);
@ -126,10 +131,9 @@ EmbedderTestContext::GetUpdateSemanticsCustomActionCallbackHook() {
};
}
void EmbedderTestContext::SetupOpenGLSurface() {
if (!gl_surface_) {
gl_surface_ = std::make_unique<TestGLSurface>();
}
void EmbedderTestContext::SetupOpenGLSurface(SkISize surface_size) {
FML_CHECK(!gl_surface_);
gl_surface_ = std::make_unique<TestGLSurface>(surface_size);
}
bool EmbedderTestContext::GLMakeCurrent() {
@ -143,16 +147,11 @@ bool EmbedderTestContext::GLClearCurrent() {
}
bool EmbedderTestContext::GLPresent() {
gl_surface_present_count_++;
FML_CHECK(gl_surface_) << "GL surface must be initialized.";
gl_surface_present_count_++;
if (next_scene_callback_) {
auto raster_snapshot = gl_surface_->GetRasterSurfaceSnapshot();
FML_CHECK(raster_snapshot);
auto callback = next_scene_callback_;
next_scene_callback_ = nullptr;
callback(std::move(raster_snapshot));
}
FireRootSurfacePresentCallbackIfPresent(
[&]() { return gl_surface_->GetRasterSurfaceSnapshot(); });
if (!gl_surface_->Present()) {
return false;
@ -176,18 +175,21 @@ void* EmbedderTestContext::GLGetProcAddress(const char* name) {
return gl_surface_->GetProcAddress(name);
}
FlutterTransformation EmbedderTestContext::GetRootSurfaceTransformation() {
return FlutterTransformationMake(root_surface_transformation_);
}
void EmbedderTestContext::SetupCompositor() {
if (compositor_) {
return;
}
SetupOpenGLSurface();
compositor_ =
std::make_unique<EmbedderTestCompositor>(gl_surface_->GetGrContext());
FML_CHECK(!compositor_) << "Already ssetup a compositor in this context.";
FML_CHECK(gl_surface_)
<< "Setup the GL surface before setting up a compositor.";
compositor_ = std::make_unique<EmbedderTestCompositor>(
gl_surface_->GetSurfaceSize(), gl_surface_->GetGrContext());
}
EmbedderTestCompositor& EmbedderTestContext::GetCompositor() {
FML_CHECK(compositor_)
<< "Accessed the compositor on a context where one was not setup. Used "
<< "Accessed the compositor on a context where one was not setup. Use "
"the config builder to setup a context with a custom compositor.";
return *compositor_;
}
@ -203,8 +205,10 @@ void EmbedderTestContext::SetNextSceneCallback(
bool EmbedderTestContext::SofwarePresent(sk_sp<SkImage> image) {
software_surface_present_count_++;
software_surface_ = std::move(image);
return software_surface_ != nullptr;
FireRootSurfacePresentCallbackIfPresent([image] { return image; });
return true;
}
size_t EmbedderTestContext::GetGLSurfacePresentCount() const {
@ -215,5 +219,15 @@ size_t EmbedderTestContext::GetSoftwareSurfacePresentCount() const {
return software_surface_present_count_;
}
void EmbedderTestContext::FireRootSurfacePresentCallbackIfPresent(
std::function<sk_sp<SkImage>(void)> image_callback) {
if (!next_scene_callback_) {
return;
}
auto callback = next_scene_callback_;
next_scene_callback_ = nullptr;
callback(image_callback());
}
} // namespace testing
} // namespace flutter

View File

@ -42,6 +42,8 @@ class EmbedderTestContext {
const fml::Mapping* GetIsolateSnapshotInstructions() const;
void SetRootSurfaceTransformation(SkMatrix matrix);
void AddIsolateCreateCallback(fml::closure closure);
void AddNativeCallback(const char* name, Dart_NativeFunction function);
@ -54,8 +56,6 @@ class EmbedderTestContext {
void SetPlatformMessageCallback(
std::function<void(const FlutterPlatformMessage*)> callback);
void SetupCompositor();
EmbedderTestCompositor& GetCompositor();
using NextSceneCallback = std::function<void(sk_sp<SkImage> image)>;
@ -80,9 +80,9 @@ class EmbedderTestContext {
SemanticsActionCallback update_semantics_custom_action_callback_;
std::function<void(const FlutterPlatformMessage*)> platform_message_callback_;
std::unique_ptr<TestGLSurface> gl_surface_;
sk_sp<SkImage> software_surface_;
std::unique_ptr<EmbedderTestCompositor> compositor_;
NextSceneCallback next_scene_callback_;
SkMatrix root_surface_transformation_;
size_t gl_surface_present_count_ = 0;
size_t software_surface_present_count_ = 0;
@ -94,11 +94,13 @@ class EmbedderTestContext {
static FlutterUpdateSemanticsCustomActionCallback
GetUpdateSemanticsCustomActionCallbackHook();
void SetupCompositor();
void FireIsolateCreateCallbacks();
void SetNativeResolver();
void SetupOpenGLSurface();
void SetupOpenGLSurface(SkISize surface_size);
bool GLMakeCurrent();
@ -112,10 +114,15 @@ class EmbedderTestContext {
void* GLGetProcAddress(const char* name);
FlutterTransformation GetRootSurfaceTransformation();
void PlatformMessageCallback(const FlutterPlatformMessage* message);
bool SofwarePresent(sk_sp<SkImage> image);
void FireRootSurfacePresentCallbackIfPresent(
std::function<sk_sp<SkImage>(void)> image_callback);
FML_DISALLOW_COPY_AND_ASSIGN(EmbedderTestContext);
};

View File

@ -19,6 +19,7 @@
#include "flutter/shell/platform/embedder/tests/embedder_assertions.h"
#include "flutter/shell/platform/embedder/tests/embedder_config_builder.h"
#include "flutter/shell/platform/embedder/tests/embedder_test.h"
#include "flutter/testing/assertions_skia.h"
#include "flutter/testing/testing.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "third_party/tonic/converter/dart_converter.h"
@ -41,6 +42,7 @@ TEST_F(EmbedderTest, CanLaunchAndShutdownWithValidProjectArgs) {
fml::AutoResetWaitableEvent latch;
context.AddIsolateCreateCallback([&latch]() { latch.Signal(); });
EmbedderConfigBuilder builder(context);
builder.SetSoftwareRendererConfig();
auto engine = builder.LaunchEngine();
ASSERT_TRUE(engine.is_valid());
// Wait for the root isolate to launch.
@ -50,6 +52,7 @@ TEST_F(EmbedderTest, CanLaunchAndShutdownWithValidProjectArgs) {
TEST_F(EmbedderTest, CanLaunchAndShutdownMultipleTimes) {
EmbedderConfigBuilder builder(GetEmbedderContext());
builder.SetSoftwareRendererConfig();
for (size_t i = 0; i < 3; ++i) {
auto engine = builder.LaunchEngine();
ASSERT_TRUE(engine.is_valid());
@ -65,6 +68,7 @@ TEST_F(EmbedderTest, CanInvokeCustomEntrypoint) {
};
context.AddNativeCallback("SayHiFromCustomEntrypoint", entrypoint);
EmbedderConfigBuilder builder(context);
builder.SetSoftwareRendererConfig();
builder.SetDartEntrypoint("customEntrypoint");
auto engine = builder.LaunchEngine();
latch.Wait();
@ -103,6 +107,7 @@ TEST_F(EmbedderTest, CanInvokeCustomEntrypointMacro) {
}));
EmbedderConfigBuilder builder(context);
builder.SetSoftwareRendererConfig();
builder.SetDartEntrypoint("customEntrypoint1");
auto engine = builder.LaunchEngine();
latch1.Wait();
@ -192,6 +197,7 @@ TEST_F(EmbedderTest, CanSpecifyCustomTaskRunner) {
EmbedderConfigBuilder builder(context);
const auto task_runner_description =
test_task_runner.GetFlutterTaskRunnerDescription();
builder.SetSoftwareRendererConfig();
builder.SetPlatformTaskRunner(&task_runner_description);
builder.SetDartEntrypoint("invokePlatformTaskRunner");
std::scoped_lock lock(engine_mutex);
@ -231,7 +237,7 @@ TEST(EmbedderTestNoFixture, CanGetCurrentTimeInNanoseconds) {
TEST_F(EmbedderTest, CanCreateOpenGLRenderingEngine) {
EmbedderConfigBuilder builder(GetEmbedderContext());
builder.SetOpenGLRendererConfig();
builder.SetOpenGLRendererConfig(SkISize::Make(1, 1));
auto engine = builder.LaunchEngine();
ASSERT_TRUE(engine.is_valid());
}
@ -246,6 +252,7 @@ TEST_F(EmbedderTest, IsolateServiceIdSent) {
thread.GetTaskRunner()->PostTask([&]() {
EmbedderConfigBuilder builder(context);
builder.SetSoftwareRendererConfig();
builder.SetDartEntrypoint("main");
builder.SetPlatformMessageCallback(
[&](const FlutterPlatformMessage* message) {
@ -281,6 +288,7 @@ TEST_F(EmbedderTest, IsolateServiceIdSent) {
TEST_F(EmbedderTest, CanCreateAndCollectCallbacks) {
auto& context = GetEmbedderContext();
EmbedderConfigBuilder builder(context);
builder.SetSoftwareRendererConfig();
builder.SetDartEntrypoint("platform_messages_response");
context.AddNativeCallback(
"SignalNativeTest",
@ -318,6 +326,7 @@ TEST_F(EmbedderTest, PlatformMessagesCanReceiveResponse) {
captures.thread_id = std::this_thread::get_id();
auto& context = GetEmbedderContext();
EmbedderConfigBuilder builder(context);
builder.SetSoftwareRendererConfig();
builder.SetDartEntrypoint("platform_messages_response");
fml::AutoResetWaitableEvent ready;
@ -373,7 +382,7 @@ TEST_F(EmbedderTest, PlatformMessagesCanReceiveResponse) {
TEST_F(EmbedderTest, PlatformMessagesCanBeSentWithoutResponseHandles) {
auto& context = GetEmbedderContext();
EmbedderConfigBuilder builder(context);
builder.SetSoftwareRendererConfig();
builder.SetDartEntrypoint("platform_messages_no_response");
const std::string message_data = "Hello but don't call me back.";
@ -418,7 +427,7 @@ TEST_F(EmbedderTest, PlatformMessagesCanBeSentWithoutResponseHandles) {
TEST_F(EmbedderTest, NullPlatformMessagesCanBeSent) {
auto& context = GetEmbedderContext();
EmbedderConfigBuilder builder(context);
builder.SetSoftwareRendererConfig();
builder.SetDartEntrypoint("null_platform_messages");
fml::AutoResetWaitableEvent ready, message;
@ -460,7 +469,7 @@ TEST_F(EmbedderTest, NullPlatformMessagesCanBeSent) {
TEST_F(EmbedderTest, InvalidPlatformMessages) {
auto& context = GetEmbedderContext();
EmbedderConfigBuilder builder(context);
builder.SetSoftwareRendererConfig();
auto engine = builder.LaunchEngine();
ASSERT_TRUE(engine.is_valid());
@ -484,7 +493,7 @@ TEST_F(EmbedderTest, InvalidPlatformMessages) {
TEST_F(EmbedderTest, VMShutsDownWhenNoEnginesInProcess) {
auto& context = GetEmbedderContext();
EmbedderConfigBuilder builder(context);
builder.SetSoftwareRendererConfig();
const auto launch_count = DartVM::GetVMLaunchCount();
{
@ -510,6 +519,7 @@ TEST_F(EmbedderTest, VMAndIsolateSnapshotSizesAreRedundantInAOTMode) {
}
auto& context = GetEmbedderContext();
EmbedderConfigBuilder builder(context);
builder.SetSoftwareRendererConfig();
// The fixture sets this up correctly. Intentionally mess up the args.
builder.GetProjectArgs().vm_snapshot_data_size = 0;
@ -530,6 +540,7 @@ TEST_F(EmbedderTest,
MustPreventEngineLaunchWhenRequiredCompositorArgsAreAbsent) {
auto& context = GetEmbedderContext();
EmbedderConfigBuilder builder(context);
builder.SetOpenGLRendererConfig(SkISize::Make(1, 1));
builder.SetCompositor();
builder.GetCompositor().create_backing_store_callback = nullptr;
builder.GetCompositor().collect_backing_store_callback = nullptr;
@ -545,7 +556,10 @@ TEST_F(EmbedderTest,
TEST_F(EmbedderTest, CompositorMustBeAbleToRenderToOpenGLFramebuffer) {
auto& context = GetEmbedderContext();
context.SetupCompositor();
EmbedderConfigBuilder builder(context);
builder.SetOpenGLRendererConfig(SkISize::Make(800, 600));
builder.SetCompositor();
builder.SetDartEntrypoint("can_composite_platform_views");
context.GetCompositor().SetRenderTargetType(
EmbedderTestCompositor::RenderTargetType::kOpenGLFramebuffer);
@ -607,10 +621,6 @@ TEST_F(EmbedderTest, CompositorMustBeAbleToRenderToOpenGLFramebuffer) {
latch.CountDown();
});
EmbedderConfigBuilder builder(context);
builder.SetOpenGLRendererConfig();
builder.SetCompositor();
builder.SetDartEntrypoint("can_composite_platform_views");
context.AddNativeCallback(
"SignalNativeTest",
CREATE_NATIVE_ENTRY(
@ -637,7 +647,10 @@ TEST_F(EmbedderTest, CompositorMustBeAbleToRenderToOpenGLFramebuffer) {
TEST_F(EmbedderTest, CompositorMustBeAbleToRenderToOpenGLTexture) {
auto& context = GetEmbedderContext();
context.SetupCompositor();
EmbedderConfigBuilder builder(context);
builder.SetOpenGLRendererConfig(SkISize::Make(800, 600));
builder.SetCompositor();
builder.SetDartEntrypoint("can_composite_platform_views");
context.GetCompositor().SetRenderTargetType(
EmbedderTestCompositor::RenderTargetType::kOpenGLTexture);
@ -699,10 +712,6 @@ TEST_F(EmbedderTest, CompositorMustBeAbleToRenderToOpenGLTexture) {
latch.CountDown();
});
EmbedderConfigBuilder builder(context);
builder.SetOpenGLRendererConfig();
builder.SetCompositor();
builder.SetDartEntrypoint("can_composite_platform_views");
context.AddNativeCallback(
"SignalNativeTest",
CREATE_NATIVE_ENTRY(
@ -729,7 +738,10 @@ TEST_F(EmbedderTest, CompositorMustBeAbleToRenderToOpenGLTexture) {
TEST_F(EmbedderTest, CompositorMustBeAbleToRenderToSoftwareBuffer) {
auto& context = GetEmbedderContext();
context.SetupCompositor();
EmbedderConfigBuilder builder(context);
builder.SetOpenGLRendererConfig(SkISize::Make(800, 600));
builder.SetCompositor();
builder.SetDartEntrypoint("can_composite_platform_views");
context.GetCompositor().SetRenderTargetType(
EmbedderTestCompositor::RenderTargetType::kSoftwareBuffer);
@ -791,10 +803,6 @@ TEST_F(EmbedderTest, CompositorMustBeAbleToRenderToSoftwareBuffer) {
latch.CountDown();
});
EmbedderConfigBuilder builder(context);
builder.SetOpenGLRendererConfig();
builder.SetCompositor();
builder.SetDartEntrypoint("can_composite_platform_views");
context.AddNativeCallback(
"SignalNativeTest",
CREATE_NATIVE_ENTRY(
@ -944,7 +952,10 @@ static bool ImageMatchesFixture(const std::string& fixture_file_name,
TEST_F(EmbedderTest, CompositorMustBeAbleToRenderKnownScene) {
auto& context = GetEmbedderContext();
context.SetupCompositor();
EmbedderConfigBuilder builder(context);
builder.SetOpenGLRendererConfig(SkISize::Make(800, 600));
builder.SetCompositor();
builder.SetDartEntrypoint("can_composite_platform_views_with_known_scene");
context.GetCompositor().SetRenderTargetType(
EmbedderTestCompositor::RenderTargetType::kOpenGLTexture);
@ -1083,10 +1094,6 @@ TEST_F(EmbedderTest, CompositorMustBeAbleToRenderKnownScene) {
return surface->makeImageSnapshot();
});
EmbedderConfigBuilder builder(context);
builder.SetOpenGLRendererConfig();
builder.SetCompositor();
builder.SetDartEntrypoint("can_composite_platform_views_with_known_scene");
context.AddNativeCallback(
"SignalNativeTest",
CREATE_NATIVE_ENTRY(
@ -1120,7 +1127,10 @@ TEST_F(EmbedderTest,
CompositorMustBeAbleToRenderKnownSceneWithSoftwareCompositor) {
auto& context = GetEmbedderContext();
context.SetupCompositor();
EmbedderConfigBuilder builder(context);
builder.SetSoftwareRendererConfig(SkISize::Make(800, 600));
builder.SetCompositor();
builder.SetDartEntrypoint("can_composite_platform_views_with_known_scene");
context.GetCompositor().SetRenderTargetType(
EmbedderTestCompositor::RenderTargetType::kSoftwareBuffer);
@ -1261,10 +1271,6 @@ TEST_F(EmbedderTest,
return surface->makeImageSnapshot();
});
EmbedderConfigBuilder builder(context);
builder.SetSoftwareRendererConfig();
builder.SetCompositor();
builder.SetDartEntrypoint("can_composite_platform_views_with_known_scene");
context.AddNativeCallback(
"SignalNativeTest",
CREATE_NATIVE_ENTRY(
@ -1297,6 +1303,12 @@ TEST_F(EmbedderTest,
TEST_F(EmbedderTest, CustomCompositorMustWorkWithCustomTaskRunner) {
auto& context = GetEmbedderContext();
EmbedderConfigBuilder builder(context);
builder.SetOpenGLRendererConfig(SkISize::Make(800, 600));
builder.SetCompositor();
builder.SetDartEntrypoint("can_composite_platform_views");
auto platform_task_runner = CreateNewThread("test_platform_thread");
static std::mutex engine_mutex;
UniqueEngine engine;
@ -1311,8 +1323,6 @@ TEST_F(EmbedderTest, CustomCompositorMustWorkWithCustomTaskRunner) {
ASSERT_EQ(FlutterEngineRunTask(engine.get(), &task), kSuccess);
});
context.SetupCompositor();
context.GetCompositor().SetRenderTargetType(
EmbedderTestCompositor::RenderTargetType::kOpenGLTexture);
@ -1376,12 +1386,8 @@ TEST_F(EmbedderTest, CustomCompositorMustWorkWithCustomTaskRunner) {
const auto task_runner_description =
test_task_runner.GetFlutterTaskRunnerDescription();
EmbedderConfigBuilder builder(context);
builder.SetPlatformTaskRunner(&task_runner_description);
builder.SetOpenGLRendererConfig();
builder.SetCompositor();
builder.SetDartEntrypoint("can_composite_platform_views");
context.AddNativeCallback(
"SignalNativeTest",
CREATE_NATIVE_ENTRY(
@ -1422,7 +1428,11 @@ TEST_F(EmbedderTest, CustomCompositorMustWorkWithCustomTaskRunner) {
TEST_F(EmbedderTest, CompositorMustBeAbleToRenderWithRootLayerOnly) {
auto& context = GetEmbedderContext();
context.SetupCompositor();
EmbedderConfigBuilder builder(context);
builder.SetOpenGLRendererConfig(SkISize::Make(800, 600));
builder.SetCompositor();
builder.SetDartEntrypoint(
"can_composite_platform_views_with_root_layer_only");
context.GetCompositor().SetRenderTargetType(
EmbedderTestCompositor::RenderTargetType::kOpenGLTexture);
@ -1459,11 +1469,6 @@ TEST_F(EmbedderTest, CompositorMustBeAbleToRenderWithRootLayerOnly) {
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(
@ -1493,7 +1498,11 @@ TEST_F(EmbedderTest, CompositorMustBeAbleToRenderWithRootLayerOnly) {
TEST_F(EmbedderTest, CompositorMustBeAbleToRenderWithPlatformLayerOnBottom) {
auto& context = GetEmbedderContext();
context.SetupCompositor();
EmbedderConfigBuilder builder(context);
builder.SetOpenGLRendererConfig(SkISize::Make(800, 600));
builder.SetCompositor();
builder.SetDartEntrypoint(
"can_composite_platform_views_with_platform_layer_on_bottom");
context.GetCompositor().SetRenderTargetType(
EmbedderTestCompositor::RenderTargetType::kOpenGLTexture);
@ -1572,11 +1581,6 @@ TEST_F(EmbedderTest, CompositorMustBeAbleToRenderWithPlatformLayerOnBottom) {
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(
@ -1601,5 +1605,644 @@ TEST_F(EmbedderTest, CompositorMustBeAbleToRenderWithPlatformLayerOnBottom) {
ASSERT_EQ(context.GetCompositor().GetBackingStoresCount(), 1u);
}
//------------------------------------------------------------------------------
/// Test the layer structure and pixels rendered when using a custom compositor
/// with a root surface transformation.
///
TEST_F(EmbedderTest,
CompositorMustBeAbleToRenderKnownSceneWithRootSurfaceTransformation) {
auto& context = GetEmbedderContext();
EmbedderConfigBuilder builder(context);
builder.SetOpenGLRendererConfig(SkISize::Make(600, 800));
builder.SetCompositor();
builder.SetDartEntrypoint("can_composite_platform_views_with_known_scene");
context.GetCompositor().SetRenderTargetType(
EmbedderTestCompositor::RenderTargetType::kOpenGLTexture);
// This must match the transformation provided in the
// |CanRenderGradientWithoutCompositorWithXform| test to ensure that
// transforms are consistent respected.
const auto root_surface_transformation =
SkMatrix().preTranslate(0, 800).preRotate(-90, 0, 0);
context.SetRootSurfaceTransformation(root_surface_transformation);
fml::CountDownLatch latch(6);
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, 5u);
// 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(600.0, 800.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(150.0, 50.0);
layer.offset = FlutterPointMake(20.0, 730.0);
ASSERT_EQ(*layers[1], layer);
}
// Layer 2
{
FlutterBackingStore backing_store = *layers[2]->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(600.0, 800.0);
layer.offset = FlutterPointMake(0.0, 0.0);
ASSERT_EQ(*layers[2], layer);
}
// Layer 3
{
FlutterPlatformView platform_view = {};
platform_view.struct_size = sizeof(platform_view);
platform_view.identifier = 2;
FlutterLayer layer = {};
layer.struct_size = sizeof(layer);
layer.type = kFlutterLayerContentTypePlatformView;
layer.platform_view = &platform_view;
layer.size = FlutterSizeMake(150.0, 50.0);
layer.offset = FlutterPointMake(40.0, 710.0);
ASSERT_EQ(*layers[3], layer);
}
// Layer 4
{
FlutterBackingStore backing_store = *layers[4]->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(600.0, 800.0);
layer.offset = FlutterPointMake(0.0, 0.0);
ASSERT_EQ(*layers[4], 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;
case 2: {
SkPaint paint;
// See dart test for total order.
paint.setColor(SK_ColorMAGENTA);
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();
});
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);
// Flutter still thinks it is 800 x 600. Only the root surface is rotated.
event.width = 800;
event.height = 600;
ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
kSuccess);
ASSERT_TRUE(engine.is_valid());
latch.Wait();
ASSERT_TRUE(ImageMatchesFixture("compositor_root_surface_xformation.png",
scene_image));
}
TEST_F(EmbedderTest, CanRenderSceneWithoutCustomCompositor) {
auto& context = GetEmbedderContext();
EmbedderConfigBuilder builder(context);
builder.SetDartEntrypoint("can_render_scene_without_custom_compositor");
builder.SetOpenGLRendererConfig(SkISize::Make(800, 600));
fml::CountDownLatch latch(1);
sk_sp<SkImage> renderered_scene;
context.SetNextSceneCallback([&](auto image) {
renderered_scene = std::move(image);
latch.CountDown();
});
auto engine = builder.LaunchEngine();
ASSERT_TRUE(engine.is_valid());
// 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);
latch.Wait();
ASSERT_NE(renderered_scene, nullptr);
ASSERT_TRUE(ImageMatchesFixture("scene_without_custom_compositor.png",
renderered_scene));
}
TEST_F(EmbedderTest, CanRenderSceneWithoutCustomCompositorWithTransformation) {
auto& context = GetEmbedderContext();
const auto root_surface_transformation =
SkMatrix().preTranslate(0, 800).preRotate(-90, 0, 0);
context.SetRootSurfaceTransformation(root_surface_transformation);
EmbedderConfigBuilder builder(context);
builder.SetDartEntrypoint("can_render_scene_without_custom_compositor");
builder.SetOpenGLRendererConfig(SkISize::Make(600, 800));
fml::CountDownLatch latch(1);
sk_sp<SkImage> renderered_scene;
context.SetNextSceneCallback([&](auto image) {
renderered_scene = std::move(image);
latch.CountDown();
});
auto engine = builder.LaunchEngine();
ASSERT_TRUE(engine.is_valid());
// Send a window metrics events so frames may be scheduled.
FlutterWindowMetricsEvent event = {};
event.struct_size = sizeof(event);
// Flutter still thinks it is 800 x 600.
event.width = 800;
event.height = 600;
ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
kSuccess);
latch.Wait();
ASSERT_NE(renderered_scene, nullptr);
ASSERT_TRUE(ImageMatchesFixture(
"scene_without_custom_compositor_with_xform.png", renderered_scene));
}
TEST_F(EmbedderTest, CanRenderGradientWithoutCompositor) {
auto& context = GetEmbedderContext();
EmbedderConfigBuilder builder(context);
builder.SetDartEntrypoint("render_gradient");
builder.SetOpenGLRendererConfig(SkISize::Make(800, 600));
fml::CountDownLatch latch(1);
sk_sp<SkImage> renderered_scene;
context.SetNextSceneCallback([&](auto image) {
renderered_scene = std::move(image);
latch.CountDown();
});
auto engine = builder.LaunchEngine();
ASSERT_TRUE(engine.is_valid());
// 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);
latch.Wait();
ASSERT_NE(renderered_scene, nullptr);
ASSERT_TRUE(ImageMatchesFixture("gradient.png", renderered_scene));
}
TEST_F(EmbedderTest, CanRenderGradientWithoutCompositorWithXform) {
auto& context = GetEmbedderContext();
const auto root_surface_transformation =
SkMatrix().preTranslate(0, 800).preRotate(-90, 0, 0);
context.SetRootSurfaceTransformation(root_surface_transformation);
EmbedderConfigBuilder builder(context);
const auto surface_size = SkISize::Make(600, 800);
builder.SetDartEntrypoint("render_gradient");
builder.SetOpenGLRendererConfig(surface_size);
fml::CountDownLatch latch(1);
sk_sp<SkImage> renderered_scene;
context.SetNextSceneCallback([&](auto image) {
renderered_scene = std::move(image);
latch.CountDown();
});
auto engine = builder.LaunchEngine();
ASSERT_TRUE(engine.is_valid());
// Send a window metrics events so frames may be scheduled.
FlutterWindowMetricsEvent event = {};
event.struct_size = sizeof(event);
// Flutter still thinks it is 800 x 600.
event.width = 800;
event.height = 600;
ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
kSuccess);
latch.Wait();
ASSERT_NE(renderered_scene, nullptr);
ASSERT_TRUE(ImageMatchesFixture("gradient_xform.png", renderered_scene));
}
TEST_F(EmbedderTest, CanRenderGradientWithCompositor) {
auto& context = GetEmbedderContext();
EmbedderConfigBuilder builder(context);
builder.SetDartEntrypoint("render_gradient");
builder.SetOpenGLRendererConfig(SkISize::Make(800, 600));
builder.SetCompositor();
fml::CountDownLatch latch(1);
sk_sp<SkImage> renderered_scene;
context.SetNextSceneCallback([&](auto image) {
renderered_scene = std::move(image);
latch.CountDown();
});
auto engine = builder.LaunchEngine();
ASSERT_TRUE(engine.is_valid());
// 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);
latch.Wait();
ASSERT_NE(renderered_scene, nullptr);
ASSERT_TRUE(ImageMatchesFixture("gradient.png", renderered_scene));
}
TEST_F(EmbedderTest, CanRenderGradientWithCompositorWithXform) {
auto& context = GetEmbedderContext();
// This must match the transformation provided in the
// |CanRenderGradientWithoutCompositorWithXform| test to ensure that
// transforms are consistent respected.
const auto root_surface_transformation =
SkMatrix().preTranslate(0, 800).preRotate(-90, 0, 0);
context.SetRootSurfaceTransformation(root_surface_transformation);
EmbedderConfigBuilder builder(context);
builder.SetDartEntrypoint("render_gradient");
builder.SetOpenGLRendererConfig(SkISize::Make(600, 800));
builder.SetCompositor();
fml::CountDownLatch latch(1);
sk_sp<SkImage> renderered_scene;
context.SetNextSceneCallback([&](auto image) {
renderered_scene = std::move(image);
latch.CountDown();
});
auto engine = builder.LaunchEngine();
ASSERT_TRUE(engine.is_valid());
// Send a window metrics events so frames may be scheduled.
FlutterWindowMetricsEvent event = {};
event.struct_size = sizeof(event);
// Flutter still thinks it is 800 x 600.
event.width = 800;
event.height = 600;
ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
kSuccess);
latch.Wait();
ASSERT_NE(renderered_scene, nullptr);
ASSERT_TRUE(ImageMatchesFixture("gradient_xform.png", renderered_scene));
}
TEST_F(EmbedderTest, CanRenderGradientWithCompositorOnNonRootLayer) {
auto& context = GetEmbedderContext();
EmbedderConfigBuilder builder(context);
builder.SetDartEntrypoint("render_gradient_on_non_root_backing_store");
builder.SetOpenGLRendererConfig(SkISize::Make(800, 600));
builder.SetCompositor();
fml::CountDownLatch latch(1);
context.GetCompositor().SetNextPresentCallback(
[&](const FlutterLayer** layers, size_t layers_count) {
ASSERT_EQ(layers_count, 3u);
// Layer Root
{
FlutterBackingStore backing_store = *layers[0]->backing_store;
backing_store.type = kFlutterBackingStoreTypeOpenGL;
backing_store.did_update = true;
backing_store.open_gl.type = kFlutterOpenGLTargetTypeFramebuffer;
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(100.0, 200.0);
layer.offset = FlutterPointMake(0.0, 0.0);
ASSERT_EQ(*layers[1], layer);
}
// Layer 2
{
FlutterBackingStore backing_store = *layers[2]->backing_store;
backing_store.type = kFlutterBackingStoreTypeOpenGL;
backing_store.did_update = true;
backing_store.open_gl.type = kFlutterOpenGLTargetTypeFramebuffer;
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[2], layer);
}
});
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: {
FML_CHECK(layer.size.width == 100);
FML_CHECK(layer.size.height == 200);
// This is occluded anyway. We just want to make sure we see this.
} break;
default:
// Asked to render an unknown platform view.
FML_CHECK(false)
<< "Test was asked to composite an unknown platform view.";
}
return surface->makeImageSnapshot();
});
sk_sp<SkImage> renderered_scene;
context.SetNextSceneCallback([&](auto image) {
renderered_scene = std::move(image);
latch.CountDown();
});
auto engine = builder.LaunchEngine();
ASSERT_TRUE(engine.is_valid());
// 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);
latch.Wait();
ASSERT_NE(renderered_scene, nullptr);
ASSERT_TRUE(ImageMatchesFixture("gradient.png", renderered_scene));
}
TEST_F(EmbedderTest, CanRenderGradientWithCompositorOnNonRootLayerWithXform) {
auto& context = GetEmbedderContext();
// This must match the transformation provided in the
// |CanRenderGradientWithoutCompositorWithXform| test to ensure that
// transforms are consistent respected.
const auto root_surface_transformation =
SkMatrix().preTranslate(0, 800).preRotate(-90, 0, 0);
context.SetRootSurfaceTransformation(root_surface_transformation);
EmbedderConfigBuilder builder(context);
builder.SetDartEntrypoint("render_gradient_on_non_root_backing_store");
builder.SetOpenGLRendererConfig(SkISize::Make(600, 800));
builder.SetCompositor();
fml::CountDownLatch latch(1);
context.GetCompositor().SetNextPresentCallback(
[&](const FlutterLayer** layers, size_t layers_count) {
ASSERT_EQ(layers_count, 3u);
// Layer Root
{
FlutterBackingStore backing_store = *layers[0]->backing_store;
backing_store.type = kFlutterBackingStoreTypeOpenGL;
backing_store.did_update = true;
backing_store.open_gl.type = kFlutterOpenGLTargetTypeFramebuffer;
FlutterLayer layer = {};
layer.struct_size = sizeof(layer);
layer.type = kFlutterLayerContentTypeBackingStore;
layer.backing_store = &backing_store;
layer.size = FlutterSizeMake(600.0, 800.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(200.0, 100.0);
layer.offset = FlutterPointMake(0.0, 700.0);
ASSERT_EQ(*layers[1], layer);
}
// Layer 2
{
FlutterBackingStore backing_store = *layers[2]->backing_store;
backing_store.type = kFlutterBackingStoreTypeOpenGL;
backing_store.did_update = true;
backing_store.open_gl.type = kFlutterOpenGLTargetTypeFramebuffer;
FlutterLayer layer = {};
layer.struct_size = sizeof(layer);
layer.type = kFlutterLayerContentTypeBackingStore;
layer.backing_store = &backing_store;
layer.size = FlutterSizeMake(600.0, 800.0);
layer.offset = FlutterPointMake(0.0, 0.0);
ASSERT_EQ(*layers[2], layer);
}
});
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: {
FML_CHECK(layer.size.width == 200);
FML_CHECK(layer.size.height == 100);
// This is occluded anyway. We just want to make sure we see this.
} break;
default:
// Asked to render an unknown platform view.
FML_CHECK(false)
<< "Test was asked to composite an unknown platform view.";
}
return surface->makeImageSnapshot();
});
sk_sp<SkImage> renderered_scene;
context.SetNextSceneCallback([&](auto image) {
renderered_scene = std::move(image);
latch.CountDown();
});
auto engine = builder.LaunchEngine();
ASSERT_TRUE(engine.is_valid());
// Send a window metrics events so frames may be scheduled.
FlutterWindowMetricsEvent event = {};
event.struct_size = sizeof(event);
// Flutter still thinks it is 800 x 600.
event.width = 800;
event.height = 600;
ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
kSuccess);
latch.Wait();
ASSERT_NE(renderered_scene, nullptr);
ASSERT_TRUE(ImageMatchesFixture("gradient_xform.png", renderered_scene));
}
} // namespace testing
} // namespace flutter

View File

@ -47,6 +47,19 @@ source_set("dart") {
]
}
source_set("skia") {
testonly = true
sources = [
"$flutter_root/testing/assertions_skia.h",
]
public_deps = [
":testing_lib",
"//third_party/skia",
]
}
if (current_toolchain == host_toolchain) {
source_set("opengl") {
testonly = true
@ -59,8 +72,8 @@ if (current_toolchain == host_toolchain) {
]
deps = [
":skia",
"$flutter_root/fml",
"//third_party/skia",
"//third_party/swiftshader_flutter:swiftshader",
]
}

View File

@ -0,0 +1,79 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_TESTING_ASSERTIONS_SKIA_H_
#define FLUTTER_TESTING_ASSERTIONS_SKIA_H_
#include <ostream>
#include "third_party/skia/include/core/SkMatrix.h"
#include "third_party/skia/include/core/SkMatrix44.h"
#include "third_party/skia/include/core/SkPoint3.h"
#include "third_party/skia/include/core/SkRRect.h"
//------------------------------------------------------------------------------
// Printing
//------------------------------------------------------------------------------
inline std::ostream& operator<<(std::ostream& os, const SkMatrix& m) {
os << std::endl;
os << "Scale X: " << m[SkMatrix::kMScaleX] << ", ";
os << "Skew X: " << m[SkMatrix::kMSkewX] << ", ";
os << "Trans X: " << m[SkMatrix::kMTransX] << std::endl;
os << "Skew Y: " << m[SkMatrix::kMSkewY] << ", ";
os << "Scale Y: " << m[SkMatrix::kMScaleY] << ", ";
os << "Trans Y: " << m[SkMatrix::kMTransY] << std::endl;
os << "Persp X: " << m[SkMatrix::kMPersp0] << ", ";
os << "Persp Y: " << m[SkMatrix::kMPersp1] << ", ";
os << "Persp Z: " << m[SkMatrix::kMPersp2];
os << std::endl;
return os;
}
inline std::ostream& operator<<(std::ostream& os, const SkMatrix44& m) {
os << m.get(0, 0) << ", " << m.get(0, 1) << ", " << m.get(0, 2) << ", "
<< m.get(0, 3) << std::endl;
os << m.get(1, 0) << ", " << m.get(1, 1) << ", " << m.get(1, 2) << ", "
<< m.get(1, 3) << std::endl;
os << m.get(2, 0) << ", " << m.get(2, 1) << ", " << m.get(2, 2) << ", "
<< m.get(2, 3) << std::endl;
os << m.get(3, 0) << ", " << m.get(3, 1) << ", " << m.get(3, 2) << ", "
<< m.get(3, 3);
return os;
}
inline std::ostream& operator<<(std::ostream& os, const SkVector3& v) {
os << v.x() << ", " << v.y() << ", " << v.z();
return os;
}
inline std::ostream& operator<<(std::ostream& os, const SkVector4& v) {
os << v.fData[0] << ", " << v.fData[1] << ", " << v.fData[2] << ", "
<< v.fData[3];
return os;
}
inline std::ostream& operator<<(std::ostream& os, const SkRect& r) {
os << "LTRB: " << r.fLeft << ", " << r.fTop << ", " << r.fRight << ", "
<< r.fBottom;
return os;
}
inline std::ostream& operator<<(std::ostream& os, const SkRRect& r) {
os << "LTRB: " << r.rect().fLeft << ", " << r.rect().fTop << ", "
<< r.rect().fRight << ", " << r.rect().fBottom;
return os;
}
inline std::ostream& operator<<(std::ostream& os, const SkPoint& r) {
os << "XY: " << r.fX << ", " << r.fY;
return os;
}
inline std::ostream& operator<<(std::ostream& os, const SkISize& size) {
os << size.width() << ", " << size.height();
return os;
}
#endif // FLUTTER_TESTING_ASSERTIONS_SKIA_H_

View File

@ -10,6 +10,7 @@
#include <sstream>
#include <string>
#include "flutter/fml/build_config.h"
#include "flutter/fml/logging.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "third_party/skia/include/gpu/gl/GrGLAssembleInterface.h"
@ -79,10 +80,8 @@ static std::string GetEGLError() {
return stream.str();
}
constexpr size_t kTestGLSurfaceWidth = 800;
constexpr size_t kTestGLSurfaceHeight = 600;
TestGLSurface::TestGLSurface() {
TestGLSurface::TestGLSurface(SkISize surface_size)
: surface_size_(surface_size) {
display_ = ::eglGetDisplay(EGL_DEFAULT_DISPLAY);
FML_CHECK(display_ != EGL_NO_DISPLAY);
@ -113,24 +112,31 @@ TestGLSurface::TestGLSurface() {
FML_CHECK(num_config == 1) << GetEGLError();
{
const EGLint surface_attributes[] = {
EGL_WIDTH, kTestGLSurfaceWidth, //
EGL_HEIGHT, kTestGLSurfaceHeight, //
const EGLint onscreen_surface_attributes[] = {
EGL_WIDTH, surface_size_.width(), //
EGL_HEIGHT, surface_size_.height(), //
EGL_NONE,
};
onscreen_surface_ =
::eglCreatePbufferSurface(display_, // display connection
config, // config
surface_attributes // surface attributes
);
onscreen_surface_ = ::eglCreatePbufferSurface(
display_, // display connection
config, // config
onscreen_surface_attributes // surface attributes
);
FML_CHECK(onscreen_surface_ != EGL_NO_SURFACE) << GetEGLError();
}
offscreen_surface_ =
::eglCreatePbufferSurface(display_, // display connection
config, // config
surface_attributes // surface attributes
);
{
const EGLint offscreen_surface_attributes[] = {
EGL_WIDTH, 1, //
EGL_HEIGHT, 1, //
EGL_NONE,
};
offscreen_surface_ = ::eglCreatePbufferSurface(
display_, // display connection
config, // config
offscreen_surface_attributes // surface attributes
);
FML_CHECK(offscreen_surface_ != EGL_NO_SURFACE) << GetEGLError();
}
@ -178,8 +184,8 @@ TestGLSurface::~TestGLSurface() {
FML_CHECK(result == EGL_TRUE);
}
SkISize TestGLSurface::GetSize() const {
return SkISize::Make(kTestGLSurfaceWidth, kTestGLSurfaceHeight);
const SkISize& TestGLSurface::GetSurfaceSize() const {
return surface_size_;
}
bool TestGLSurface::MakeCurrent() {
@ -289,25 +295,29 @@ sk_sp<GrContext> TestGLSurface::CreateGrContext() {
}
sk_sp<SkSurface> TestGLSurface::GetOnscreenSurface() {
FML_CHECK(::eglGetCurrentContext() != EGL_NO_CONTEXT);
GrGLFramebufferInfo framebuffer_info = {};
framebuffer_info.fFBOID = GetFramebuffer();
#if OS_MACOSX
framebuffer_info.fFormat = GR_GL_RGBA8;
const auto size = GetSize();
#else
framebuffer_info.fFormat = GR_GL_BGRA8;
#endif
GrBackendRenderTarget backend_render_target(
size.width(), // width
size.height(), // height
1, // sample count
8, // stencil bits
framebuffer_info // framebuffer info
surface_size_.width(), // width
surface_size_.height(), // height
1, // sample count
8, // stencil bits
framebuffer_info // framebuffer info
);
SkSurfaceProps surface_properties(
SkSurfaceProps::InitType::kLegacyFontHost_InitType);
auto surface = SkSurface::MakeFromBackendRenderTarget(
GetGrContext().get(), // context
GetGrContext().get(), // context
backend_render_target, // backend render target
kBottomLeft_GrSurfaceOrigin, // surface origin
kN32_SkColorType, // color type

View File

@ -15,11 +15,11 @@ namespace testing {
class TestGLSurface {
public:
TestGLSurface();
TestGLSurface(SkISize surface_size);
~TestGLSurface();
SkISize GetSize() const;
const SkISize& GetSurfaceSize() const;
bool MakeCurrent();
@ -50,6 +50,7 @@ class TestGLSurface {
using EGLContext = void*;
using EGLSurface = void*;
const SkISize surface_size_;
EGLDisplay display_;
EGLContext onscreen_context_;
EGLContext offscreen_context_;