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
@ -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
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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;
|
||||
}
|
||||
@ -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_
|
||||
@ -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())
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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",
|
||||
]
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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_));
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@ -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);
|
||||
};
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 2.6 KiB |
BIN
engine/src/flutter/shell/platform/embedder/fixtures/gradient.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 81 KiB |
@ -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();
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 9.3 KiB |
@ -65,6 +65,7 @@ TEST_F(Embedder11yTest, A11yTreeIsConsistent) {
|
||||
})));
|
||||
|
||||
EmbedderConfigBuilder builder(context);
|
||||
builder.SetSoftwareRendererConfig();
|
||||
builder.SetDartEntrypoint("a11y_main");
|
||||
|
||||
auto engine = builder.LaunchEngine();
|
||||
|
||||
@ -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_
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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.";
|
||||
|
||||
@ -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_;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
};
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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",
|
||||
]
|
||||
}
|
||||
|
||||
79
engine/src/flutter/testing/assertions_skia.h
Normal 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_
|
||||
@ -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
|
||||
|
||||
@ -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_;
|
||||
|
||||