diff --git a/ci/licenses_golden/excluded_files b/ci/licenses_golden/excluded_files index 51104ce3d8a54..d6ca34a8052a7 100644 --- a/ci/licenses_golden/excluded_files +++ b/ci/licenses_golden/excluded_files @@ -366,6 +366,8 @@ ../../../flutter/shell/platform/windows/client_wrapper/flutter_view_unittests.cc ../../../flutter/shell/platform/windows/client_wrapper/plugin_registrar_windows_unittests.cc ../../../flutter/shell/platform/windows/client_wrapper/testing +../../../flutter/shell/platform/windows/compositor_opengl_unittests.cc +../../../flutter/shell/platform/windows/compositor_software_unittests.cc ../../../flutter/shell/platform/windows/cursor_handler_unittests.cc ../../../flutter/shell/platform/windows/direct_manipulation_unittests.cc ../../../flutter/shell/platform/windows/dpi_utils_unittests.cc diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 5083bee902362..611f58c269881 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -7113,6 +7113,11 @@ ORIGIN: ../../../flutter/shell/platform/windows/client_wrapper/include/flutter/f ORIGIN: ../../../flutter/shell/platform/windows/client_wrapper/include/flutter/flutter_view.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/windows/client_wrapper/include/flutter/flutter_view_controller.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/windows/client_wrapper/include/flutter/plugin_registrar_windows.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/windows/compositor.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/windows/compositor_opengl.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/windows/compositor_opengl.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/windows/compositor_software.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/windows/compositor_software.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/windows/cursor_handler.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/windows/cursor_handler.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/windows/direct_manipulation.cc + ../../../flutter/LICENSE @@ -9942,6 +9947,11 @@ FILE: ../../../flutter/shell/platform/windows/client_wrapper/include/flutter/flu FILE: ../../../flutter/shell/platform/windows/client_wrapper/include/flutter/flutter_view.h FILE: ../../../flutter/shell/platform/windows/client_wrapper/include/flutter/flutter_view_controller.h FILE: ../../../flutter/shell/platform/windows/client_wrapper/include/flutter/plugin_registrar_windows.h +FILE: ../../../flutter/shell/platform/windows/compositor.h +FILE: ../../../flutter/shell/platform/windows/compositor_opengl.cc +FILE: ../../../flutter/shell/platform/windows/compositor_opengl.h +FILE: ../../../flutter/shell/platform/windows/compositor_software.cc +FILE: ../../../flutter/shell/platform/windows/compositor_software.h FILE: ../../../flutter/shell/platform/windows/cursor_handler.cc FILE: ../../../flutter/shell/platform/windows/cursor_handler.h FILE: ../../../flutter/shell/platform/windows/direct_manipulation.cc diff --git a/impeller/renderer/backend/gles/description_gles.cc b/impeller/renderer/backend/gles/description_gles.cc index a2142c1191444..40d583bca535d 100644 --- a/impeller/renderer/backend/gles/description_gles.cc +++ b/impeller/renderer/backend/gles/description_gles.cc @@ -156,6 +156,10 @@ std::string DescriptionGLES::GetString() const { return stream.str(); } +Version DescriptionGLES::GetGlVersion() const { + return gl_version_; +} + bool DescriptionGLES::IsES() const { return is_es_; } diff --git a/impeller/renderer/backend/gles/description_gles.h b/impeller/renderer/backend/gles/description_gles.h index dac13cab61422..818ea0d0cc358 100644 --- a/impeller/renderer/backend/gles/description_gles.h +++ b/impeller/renderer/backend/gles/description_gles.h @@ -27,6 +27,8 @@ class DescriptionGLES { std::string GetString() const; + Version GetGlVersion() const; + bool HasExtension(const std::string& ext) const; /// @brief Returns whether GLES includes the debug extension. diff --git a/shell/platform/linux/fl_backing_store_provider.cc b/shell/platform/linux/fl_backing_store_provider.cc index 643e9a343621e..f5f4bf7115ec8 100644 --- a/shell/platform/linux/fl_backing_store_provider.cc +++ b/shell/platform/linux/fl_backing_store_provider.cc @@ -82,7 +82,8 @@ uint32_t fl_backing_store_provider_get_gl_format(FlBackingStoreProvider* self) { // In Linux kN32_SkColorType is assumed to be kBGRA_8888_SkColorType. // So we must choose a valid gl format to be compatible with surface format // BGRA8. - // Following logics are copied from Skia GrGLCaps.cpp. + // Following logics are copied from Skia GrGLCaps.cpp: + // https://github.com/google/skia/blob/4738ed711e03212aceec3cd502a4adb545f38e63/src/gpu/ganesh/gl/GrGLCaps.cpp#L1963-L2116 if (epoxy_is_desktop_gl()) { // For OpenGL. diff --git a/shell/platform/windows/BUILD.gn b/shell/platform/windows/BUILD.gn index ecc9080965993..4bebc496bfd37 100644 --- a/shell/platform/windows/BUILD.gn +++ b/shell/platform/windows/BUILD.gn @@ -42,6 +42,11 @@ source_set("flutter_windows_source") { "accessibility_bridge_windows.h", "angle_surface_manager.cc", "angle_surface_manager.h", + "compositor.h", + "compositor_opengl.cc", + "compositor_opengl.h", + "compositor_software.cc", + "compositor_software.h", "cursor_handler.cc", "cursor_handler.h", "direct_manipulation.cc", @@ -134,6 +139,7 @@ source_set("flutter_windows_source") { deps = [ ":flutter_windows_headers", "//flutter/fml:fml", + "//flutter/impeller/renderer/backend/gles", "//flutter/shell/platform/common:common_cpp", "//flutter/shell/platform/common:common_cpp_input", "//flutter/shell/platform/common:common_cpp_switches", @@ -175,6 +181,8 @@ executable("flutter_windows_unittests") { # Common Windows test sources. sources = [ "accessibility_bridge_windows_unittests.cc", + "compositor_opengl_unittests.cc", + "compositor_software_unittests.cc", "cursor_handler_unittests.cc", "direct_manipulation_unittests.cc", "dpi_utils_unittests.cc", @@ -235,6 +243,7 @@ executable("flutter_windows_unittests") { ":flutter_windows_fixtures", ":flutter_windows_headers", ":flutter_windows_source", + "//flutter/impeller/renderer/backend/gles", "//flutter/shell/platform/common:common_cpp", "//flutter/shell/platform/common/client_wrapper:client_wrapper", "//flutter/shell/platform/embedder:embedder_as_internal_library", diff --git a/shell/platform/windows/compositor.h b/shell/platform/windows/compositor.h new file mode 100644 index 0000000000000..761e2da9c831e --- /dev/null +++ b/shell/platform/windows/compositor.h @@ -0,0 +1,39 @@ +// 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_SHELL_PLATFORM_WINDOWS_COMPOSITOR_H_ +#define FLUTTER_SHELL_PLATFORM_WINDOWS_COMPOSITOR_H_ + +#include "flutter/shell/platform/embedder/embedder.h" + +namespace flutter { + +// Enables the Flutter engine to render content on Windows. +// +// The engine uses this to: +// +// 1. Create backing stores used for rendering Flutter content +// 2. Composite and present Flutter content and platform views onto a view +// +// Platform views are not yet supported. +class Compositor { + public: + virtual ~Compositor() = default; + + // Create a backing store used for rendering Flutter content. + // + // The backing store's configuration is stored in |backing_store_out|. + virtual bool CreateBackingStore(const FlutterBackingStoreConfig& config, + FlutterBackingStore* backing_store_out) = 0; + + // Destroys a backing store and releases its resources. + virtual bool CollectBackingStore(const FlutterBackingStore* store) = 0; + + // Present Flutter content and platform views onto the view. + virtual bool Present(const FlutterLayer** layers, size_t layers_count) = 0; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_COMPOSITOR_H_ diff --git a/shell/platform/windows/compositor_opengl.cc b/shell/platform/windows/compositor_opengl.cc new file mode 100644 index 0000000000000..3a445c4ded63f --- /dev/null +++ b/shell/platform/windows/compositor_opengl.cc @@ -0,0 +1,154 @@ +// 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/shell/platform/windows/compositor_opengl.h" + +#include "GLES3/gl3.h" +#include "flutter/shell/platform/windows/flutter_windows_view.h" + +namespace flutter { + +namespace { + +// The metadata for an OpenGL framebuffer backing store. +struct FramebufferBackingStore { + uint32_t framebuffer_id; + uint32_t texture_id; +}; + +// Based off Skia's logic: +// https://github.com/google/skia/blob/4738ed711e03212aceec3cd502a4adb545f38e63/src/gpu/ganesh/gl/GrGLCaps.cpp#L1963-L2116 +int GetSupportedTextureFormat(const impeller::DescriptionGLES* description) { + if (description->HasExtension("GL_EXT_texture_format_BGRA8888")) { + return GL_BGRA8_EXT; + } else if (description->HasExtension("GL_APPLE_texture_format_BGRA8888") && + description->GetGlVersion().IsAtLeast(impeller::Version(3, 0))) { + return GL_BGRA8_EXT; + } else { + return GL_RGBA8; + } +} + +} // namespace + +CompositorOpenGL::CompositorOpenGL(FlutterWindowsEngine* engine, + impeller::ProcTableGLES::Resolver resolver) + : engine_(engine), resolver_(resolver) {} + +bool CompositorOpenGL::CreateBackingStore( + const FlutterBackingStoreConfig& config, + FlutterBackingStore* result) { + if (!is_initialized_ && !Initialize()) { + return false; + } + + auto store = std::make_unique(); + + gl_->GenTextures(1, &store->texture_id); + gl_->GenFramebuffers(1, &store->framebuffer_id); + + gl_->BindFramebuffer(GL_FRAMEBUFFER, store->framebuffer_id); + + gl_->BindTexture(GL_TEXTURE_2D, store->texture_id); + gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, config.size.width, + config.size.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + gl_->BindTexture(GL_TEXTURE_2D, 0); + + gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_2D, store->texture_id, 0); + + result->type = kFlutterBackingStoreTypeOpenGL; + result->open_gl.type = kFlutterOpenGLTargetTypeFramebuffer; + result->open_gl.framebuffer.name = store->framebuffer_id; + result->open_gl.framebuffer.target = format_; + result->open_gl.framebuffer.user_data = store.release(); + result->open_gl.framebuffer.destruction_callback = [](void* user_data) { + // Backing store destroyed in `CompositorOpenGL::CollectBackingStore`, set + // on FlutterCompositor.collect_backing_store_callback during engine start. + }; + return true; +} + +bool CompositorOpenGL::CollectBackingStore(const FlutterBackingStore* store) { + FML_DCHECK(is_initialized_); + FML_DCHECK(store->type == kFlutterBackingStoreTypeOpenGL); + FML_DCHECK(store->open_gl.type == kFlutterOpenGLTargetTypeFramebuffer); + + auto user_data = static_cast( + store->open_gl.framebuffer.user_data); + + gl_->DeleteFramebuffers(1, &user_data->framebuffer_id); + gl_->DeleteTextures(1, &user_data->texture_id); + + delete user_data; + return true; +} + +bool CompositorOpenGL::Present(const FlutterLayer** layers, + size_t layers_count) { + // TODO: Support compositing layers and platform views. + // See: https://github.com/flutter/flutter/issues/31713 + FML_DCHECK(is_initialized_); + FML_DCHECK(layers_count == 1); + FML_DCHECK(layers[0]->type == kFlutterLayerContentTypeBackingStore); + FML_DCHECK(layers[0]->backing_store->type == kFlutterBackingStoreTypeOpenGL); + FML_DCHECK(layers[0]->backing_store->open_gl.type == + kFlutterOpenGLTargetTypeFramebuffer); + + if (!engine_->view()) { + return false; + } + + auto width = layers[0]->size.width; + auto height = layers[0]->size.height; + + // Acquiring the view's framebuffer ID resizes its surface if necessary. + auto destination_id = engine_->view()->GetFrameBufferId(width, height); + auto source_id = layers[0]->backing_store->open_gl.framebuffer.name; + + if (!engine_->surface_manager()->MakeCurrent()) { + return false; + } + + gl_->BindFramebuffer(GL_READ_FRAMEBUFFER, source_id); + gl_->BindFramebuffer(GL_DRAW_FRAMEBUFFER, destination_id); + + gl_->BlitFramebuffer(0, // srcX0 + 0, // srcY0 + width, // srcX1 + height, // srcY1 + 0, // dstX0 + 0, // dstY0 + width, // dstX1 + height, // dstY1 + GL_COLOR_BUFFER_BIT, // mask + GL_NEAREST // filter + ); + + return engine_->view()->SwapBuffers(); +} + +bool CompositorOpenGL::Initialize() { + FML_DCHECK(!is_initialized_); + + if (!engine_->surface_manager()->MakeCurrent()) { + return false; + } + + gl_ = std::make_unique(resolver_); + if (!gl_->IsValid()) { + gl_.reset(); + return false; + } + + format_ = GetSupportedTextureFormat(gl_->GetDescription()); + is_initialized_ = true; + return true; +} + +} // namespace flutter diff --git a/shell/platform/windows/compositor_opengl.h b/shell/platform/windows/compositor_opengl.h new file mode 100644 index 0000000000000..46b42eebf0eb2 --- /dev/null +++ b/shell/platform/windows/compositor_opengl.h @@ -0,0 +1,58 @@ +// 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_SHELL_PLATFORM_WINDOWS_COMPOSITOR_OPENGL_H_ +#define FLUTTER_SHELL_PLATFORM_WINDOWS_COMPOSITOR_OPENGL_H_ + +#include + +#include "flutter/impeller/renderer/backend/gles/proc_table_gles.h" +#include "flutter/shell/platform/embedder/embedder.h" +#include "flutter/shell/platform/windows/compositor.h" +#include "flutter/shell/platform/windows/flutter_windows_engine.h" + +namespace flutter { + +// Enables the Flutter engine to render content on Windows using OpenGL. +class CompositorOpenGL : public Compositor { + public: + CompositorOpenGL(FlutterWindowsEngine* engine, + impeller::ProcTableGLES::Resolver resolver); + + /// |Compositor| + bool CreateBackingStore(const FlutterBackingStoreConfig& config, + FlutterBackingStore* result) override; + + /// |Compositor| + bool CollectBackingStore(const FlutterBackingStore* store) override; + + /// |Compositor| + bool Present(const FlutterLayer** layers, size_t layers_count) override; + + private: + // The Flutter engine that manages the views to render. + FlutterWindowsEngine* engine_; + + private: + // The compositor initializes itself lazily once |CreateBackingStore| is + // called. True if initialization completed successfully. + bool is_initialized_ = false; + + // Function used to resolve GLES functions. + impeller::ProcTableGLES::Resolver resolver_ = nullptr; + + // Table of resolved GLES functions. Null until the compositor is initialized. + std::unique_ptr gl_ = nullptr; + + // The OpenGL texture target format for backing stores. Invalid value until + // the compositor is initialized. + uint32_t format_ = 0; + + // Initialize the compositor. This must run on the raster thread. + bool Initialize(); +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_COMPOSITOR_OPENGL_H_ diff --git a/shell/platform/windows/compositor_opengl_unittests.cc b/shell/platform/windows/compositor_opengl_unittests.cc new file mode 100644 index 0000000000000..c60d0c685e554 --- /dev/null +++ b/shell/platform/windows/compositor_opengl_unittests.cc @@ -0,0 +1,194 @@ +// 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 +#include + +#include "flutter/impeller/renderer/backend/gles/gles.h" +#include "flutter/shell/platform/windows/angle_surface_manager.h" +#include "flutter/shell/platform/windows/compositor_opengl.h" +#include "flutter/shell/platform/windows/flutter_windows_view.h" +#include "flutter/shell/platform/windows/testing/engine_modifier.h" +#include "flutter/shell/platform/windows/testing/flutter_windows_engine_builder.h" +#include "flutter/shell/platform/windows/testing/mock_window_binding_handler.h" +#include "flutter/shell/platform/windows/testing/windows_test.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace flutter { +namespace testing { + +namespace { +using ::testing::Return; + +const unsigned char* MockGetString(GLenum name) { + switch (name) { + case GL_VERSION: + case GL_SHADING_LANGUAGE_VERSION: + return reinterpret_cast("3.0"); + default: + return reinterpret_cast(""); + } +} + +void MockGetIntegerv(GLenum name, int* value) { + *value = 0; +} + +GLenum MockGetError() { + return GL_NO_ERROR; +} + +void DoNothing() {} + +const impeller::ProcTableGLES::Resolver kMockResolver = [](const char* name) { + std::string function_name{name}; + + if (function_name == "glGetString") { + return reinterpret_cast(&MockGetString); + } else if (function_name == "glGetIntegerv") { + return reinterpret_cast(&MockGetIntegerv); + } else if (function_name == "glGetError") { + return reinterpret_cast(&MockGetError); + } else { + return reinterpret_cast(&DoNothing); + } +}; + +class MockAngleSurfaceManager : public AngleSurfaceManager { + public: + MockAngleSurfaceManager() : AngleSurfaceManager(false) {} + + MOCK_METHOD(bool, MakeCurrent, (), (override)); + + private: + FML_DISALLOW_COPY_AND_ASSIGN(MockAngleSurfaceManager); +}; + +class MockFlutterWindowsView : public FlutterWindowsView { + public: + MockFlutterWindowsView(std::unique_ptr window) + : FlutterWindowsView(std::move(window)) {} + virtual ~MockFlutterWindowsView() = default; + + MOCK_METHOD(bool, SwapBuffers, (), (override)); + + private: + FML_DISALLOW_COPY_AND_ASSIGN(MockFlutterWindowsView); +}; + +class CompositorOpenGLTest : public WindowsTest { + public: + CompositorOpenGLTest() = default; + virtual ~CompositorOpenGLTest() = default; + + protected: + FlutterWindowsEngine* engine() { return engine_.get(); } + MockFlutterWindowsView* view() { return view_.get(); } + MockAngleSurfaceManager* surface_manager() { return surface_manager_; } + + void UseHeadlessEngine() { + auto surface_manager = std::make_unique(); + surface_manager_ = surface_manager.get(); + + FlutterWindowsEngineBuilder builder{GetContext()}; + + engine_ = builder.Build(); + EngineModifier modifier(engine_.get()); + modifier.SetSurfaceManager(std::move(surface_manager)); + } + + void UseEngineWithView() { + UseHeadlessEngine(); + + auto window = std::make_unique(); + EXPECT_CALL(*window.get(), SetView).Times(1); + EXPECT_CALL(*window.get(), GetRenderTarget).WillRepeatedly(Return(nullptr)); + + view_ = std::make_unique(std::move(window)); + + engine_->SetView(view_.get()); + } + + private: + std::unique_ptr engine_; + std::unique_ptr view_; + MockAngleSurfaceManager* surface_manager_; + + FML_DISALLOW_COPY_AND_ASSIGN(CompositorOpenGLTest); +}; + +} // namespace + +TEST_F(CompositorOpenGLTest, CreateBackingStore) { + UseHeadlessEngine(); + + auto compositor = CompositorOpenGL{engine(), kMockResolver}; + + FlutterBackingStoreConfig config = {}; + FlutterBackingStore backing_store = {}; + + EXPECT_CALL(*surface_manager(), MakeCurrent).WillOnce(Return(true)); + ASSERT_TRUE(compositor.CreateBackingStore(config, &backing_store)); + ASSERT_TRUE(compositor.CollectBackingStore(&backing_store)); +} + +TEST_F(CompositorOpenGLTest, InitializationFailure) { + UseHeadlessEngine(); + + auto compositor = CompositorOpenGL{engine(), kMockResolver}; + + FlutterBackingStoreConfig config = {}; + FlutterBackingStore backing_store = {}; + + EXPECT_CALL(*surface_manager(), MakeCurrent).WillOnce(Return(false)); + EXPECT_FALSE(compositor.CreateBackingStore(config, &backing_store)); +} + +TEST_F(CompositorOpenGLTest, Present) { + UseEngineWithView(); + + auto compositor = CompositorOpenGL{engine(), kMockResolver}; + + FlutterBackingStoreConfig config = {}; + FlutterBackingStore backing_store = {}; + + EXPECT_CALL(*surface_manager(), MakeCurrent).WillOnce(Return(true)); + ASSERT_TRUE(compositor.CreateBackingStore(config, &backing_store)); + + FlutterLayer layer = {}; + layer.type = kFlutterLayerContentTypeBackingStore; + layer.backing_store = &backing_store; + const FlutterLayer* layer_ptr = &layer; + + EXPECT_CALL(*surface_manager(), MakeCurrent).WillOnce(Return(true)); + EXPECT_CALL(*view(), SwapBuffers).WillOnce(Return(true)); + EXPECT_TRUE(compositor.Present(&layer_ptr, 1)); + + ASSERT_TRUE(compositor.CollectBackingStore(&backing_store)); +} + +TEST_F(CompositorOpenGLTest, HeadlessPresentIgnored) { + UseHeadlessEngine(); + + auto compositor = CompositorOpenGL{engine(), kMockResolver}; + + FlutterBackingStoreConfig config = {}; + FlutterBackingStore backing_store = {}; + + EXPECT_CALL(*surface_manager(), MakeCurrent).WillOnce(Return(true)); + ASSERT_TRUE(compositor.CreateBackingStore(config, &backing_store)); + + FlutterLayer layer = {}; + layer.type = kFlutterLayerContentTypeBackingStore; + layer.backing_store = &backing_store; + const FlutterLayer* layer_ptr = &layer; + + EXPECT_FALSE(compositor.Present(&layer_ptr, 1)); + + ASSERT_TRUE(compositor.CollectBackingStore(&backing_store)); +} + +} // namespace testing +} // namespace flutter diff --git a/shell/platform/windows/compositor_software.cc b/shell/platform/windows/compositor_software.cc new file mode 100644 index 0000000000000..06809e11b5ee2 --- /dev/null +++ b/shell/platform/windows/compositor_software.cc @@ -0,0 +1,59 @@ +// 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/shell/platform/windows/compositor_software.h" + +#include "flutter/shell/platform/windows/flutter_windows_view.h" + +namespace flutter { + +CompositorSoftware::CompositorSoftware(FlutterWindowsEngine* engine) + : engine_(engine) {} + +bool CompositorSoftware::CreateBackingStore( + const FlutterBackingStoreConfig& config, + FlutterBackingStore* result) { + size_t size = config.size.width * config.size.height * 4; + void* allocation = std::calloc(size, sizeof(uint8_t)); + if (!allocation) { + return false; + } + + result->type = kFlutterBackingStoreTypeSoftware; + result->software.allocation = allocation; + result->software.height = config.size.height; + result->software.row_bytes = config.size.width * 4; + result->software.user_data = nullptr; + result->software.destruction_callback = [](void* user_data) { + // Backing store destroyed in `CompositorSoftware::CollectBackingStore`, set + // on FlutterCompositor.collect_backing_store_callback during engine start. + }; + return true; +} + +bool CompositorSoftware::CollectBackingStore(const FlutterBackingStore* store) { + std::free(const_cast(store->software.allocation)); + return true; +} + +bool CompositorSoftware::Present(const FlutterLayer** layers, + size_t layers_count) { + // TODO: Support compositing layers and platform views. + // See: https://github.com/flutter/flutter/issues/31713 + FML_DCHECK(layers_count == 1); + FML_DCHECK(layers[0]->type == kFlutterLayerContentTypeBackingStore); + FML_DCHECK(layers[0]->backing_store->type == + kFlutterBackingStoreTypeSoftware); + + if (!engine_->view()) { + return false; + } + + const auto& backing_store = layers[0]->backing_store->software; + + return engine_->view()->PresentSoftwareBitmap( + backing_store.allocation, backing_store.row_bytes, backing_store.height); +} + +} // namespace flutter diff --git a/shell/platform/windows/compositor_software.h b/shell/platform/windows/compositor_software.h new file mode 100644 index 0000000000000..c4e39111b3c1f --- /dev/null +++ b/shell/platform/windows/compositor_software.h @@ -0,0 +1,35 @@ +// 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_SHELL_PLATFORM_WINDOWS_COMPOSITOR_SOFTWARE_H_ +#define FLUTTER_SHELL_PLATFORM_WINDOWS_COMPOSITOR_SOFTWARE_H_ + +#include "flutter/shell/platform/embedder/embedder.h" +#include "flutter/shell/platform/windows/compositor.h" +#include "flutter/shell/platform/windows/flutter_windows_engine.h" + +namespace flutter { + +// Enables the Flutter engine to render content on Windows using software +// rasterization and bitmaps. +class CompositorSoftware : public Compositor { + public: + CompositorSoftware(FlutterWindowsEngine* engine); + + /// |Compositor| + bool CreateBackingStore(const FlutterBackingStoreConfig& config, + FlutterBackingStore* result) override; + /// |Compositor| + bool CollectBackingStore(const FlutterBackingStore* store) override; + + /// |Compositor| + bool Present(const FlutterLayer** layers, size_t layers_count) override; + + private: + FlutterWindowsEngine* engine_; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_COMPOSITOR_SOFTWARE_H_ diff --git a/shell/platform/windows/compositor_software_unittests.cc b/shell/platform/windows/compositor_software_unittests.cc new file mode 100644 index 0000000000000..e9a8860e03223 --- /dev/null +++ b/shell/platform/windows/compositor_software_unittests.cc @@ -0,0 +1,129 @@ +// 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 +#include + +#include "flutter/shell/platform/windows/compositor_software.h" +#include "flutter/shell/platform/windows/flutter_windows_view.h" +#include "flutter/shell/platform/windows/testing/engine_modifier.h" +#include "flutter/shell/platform/windows/testing/flutter_windows_engine_builder.h" +#include "flutter/shell/platform/windows/testing/mock_window_binding_handler.h" +#include "flutter/shell/platform/windows/testing/windows_test.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace flutter { +namespace testing { + +namespace { +using ::testing::Return; + +class MockFlutterWindowsView : public FlutterWindowsView { + public: + MockFlutterWindowsView(std::unique_ptr window) + : FlutterWindowsView(std::move(window)) {} + virtual ~MockFlutterWindowsView() = default; + + MOCK_METHOD(bool, + PresentSoftwareBitmap, + (const void* allocation, size_t row_bytes, size_t height), + (override)); + + private: + FML_DISALLOW_COPY_AND_ASSIGN(MockFlutterWindowsView); +}; + +class CompositorSoftwareTest : public WindowsTest { + public: + CompositorSoftwareTest() = default; + virtual ~CompositorSoftwareTest() = default; + + protected: + FlutterWindowsEngine* engine() { return engine_.get(); } + MockFlutterWindowsView* view() { return view_.get(); } + + void UseHeadlessEngine() { + FlutterWindowsEngineBuilder builder{GetContext()}; + + engine_ = builder.Build(); + } + + void UseEngineWithView() { + FlutterWindowsEngineBuilder builder{GetContext()}; + + auto window = std::make_unique(); + EXPECT_CALL(*window.get(), SetView).Times(1); + EXPECT_CALL(*window.get(), GetRenderTarget).WillRepeatedly(Return(nullptr)); + + engine_ = builder.Build(); + view_ = std::make_unique(std::move(window)); + + engine_->SetView(view_.get()); + } + + private: + std::unique_ptr engine_; + std::unique_ptr view_; + + FML_DISALLOW_COPY_AND_ASSIGN(CompositorSoftwareTest); +}; + +} // namespace + +TEST_F(CompositorSoftwareTest, CreateBackingStore) { + UseHeadlessEngine(); + + auto compositor = CompositorSoftware{engine()}; + + FlutterBackingStoreConfig config = {}; + FlutterBackingStore backing_store = {}; + + ASSERT_TRUE(compositor.CreateBackingStore(config, &backing_store)); + ASSERT_TRUE(compositor.CollectBackingStore(&backing_store)); +} + +TEST_F(CompositorSoftwareTest, Present) { + UseEngineWithView(); + + auto compositor = CompositorSoftware{engine()}; + + FlutterBackingStoreConfig config = {}; + FlutterBackingStore backing_store = {}; + + ASSERT_TRUE(compositor.CreateBackingStore(config, &backing_store)); + + FlutterLayer layer = {}; + layer.type = kFlutterLayerContentTypeBackingStore; + layer.backing_store = &backing_store; + const FlutterLayer* layer_ptr = &layer; + + EXPECT_CALL(*view(), PresentSoftwareBitmap).WillOnce(Return(true)); + EXPECT_TRUE(compositor.Present(&layer_ptr, 1)); + + ASSERT_TRUE(compositor.CollectBackingStore(&backing_store)); +} + +TEST_F(CompositorSoftwareTest, HeadlessPresentIgnored) { + UseHeadlessEngine(); + + auto compositor = CompositorSoftware{engine()}; + + FlutterBackingStoreConfig config = {}; + FlutterBackingStore backing_store = {}; + + ASSERT_TRUE(compositor.CreateBackingStore(config, &backing_store)); + + FlutterLayer layer = {}; + layer.type = kFlutterLayerContentTypeBackingStore; + layer.backing_store = &backing_store; + const FlutterLayer* layer_ptr = &layer; + + EXPECT_FALSE(compositor.Present(&layer_ptr, 1)); + + ASSERT_TRUE(compositor.CollectBackingStore(&backing_store)); +} + +} // namespace testing +} // namespace flutter diff --git a/shell/platform/windows/flutter_windows_engine.cc b/shell/platform/windows/flutter_windows_engine.cc index 8f093f046c644..3bf140c38b7cd 100644 --- a/shell/platform/windows/flutter_windows_engine.cc +++ b/shell/platform/windows/flutter_windows_engine.cc @@ -17,6 +17,8 @@ #include "flutter/shell/platform/common/path_utils.h" #include "flutter/shell/platform/embedder/embedder_struct_macros.h" #include "flutter/shell/platform/windows/accessibility_bridge_windows.h" +#include "flutter/shell/platform/windows/compositor_opengl.h" +#include "flutter/shell/platform/windows/compositor_software.h" #include "flutter/shell/platform/windows/flutter_windows_view.h" #include "flutter/shell/platform/windows/keyboard_key_channel_handler.h" #include "flutter/shell/platform/windows/system_utils.h" @@ -65,23 +67,11 @@ FlutterRendererConfig GetOpenGLRendererConfig() { } return host->surface_manager()->ClearContext(); }; - config.open_gl.present = [](void* user_data) -> bool { - auto host = static_cast(user_data); - if (!host->view()) { - return false; - } - return host->view()->SwapBuffers(); - }; + config.open_gl.present = [](void* user_data) -> bool { FML_UNREACHABLE(); }; config.open_gl.fbo_reset_after_present = true; config.open_gl.fbo_with_frame_info_callback = [](void* user_data, const FlutterFrameInfo* info) -> uint32_t { - auto host = static_cast(user_data); - if (host->view()) { - return host->view()->GetFrameBufferId(info->size.width, - info->size.height); - } else { - return kWindowFrameBufferID; - } + FML_UNREACHABLE(); }; config.open_gl.gl_proc_resolver = [](void* user_data, const char* what) -> void* { @@ -115,16 +105,12 @@ FlutterRendererConfig GetSoftwareRendererConfig() { FlutterRendererConfig config = {}; config.type = kSoftware; config.software.struct_size = sizeof(config.software); - config.software.surface_present_callback = [](void* user_data, - const void* allocation, - size_t row_bytes, - size_t height) { - auto host = static_cast(user_data); - if (!host->view()) { - return false; - } - return host->view()->PresentSoftwareBitmap(allocation, row_bytes, height); - }; + config.software.surface_present_callback = + [](void* user_data, const void* allocation, size_t row_bytes, + size_t height) { + FML_UNREACHABLE(); + return false; + }; return config; } @@ -394,6 +380,43 @@ bool FlutterWindowsEngine::Run(std::string_view entrypoint) { args.custom_task_runners = &custom_task_runners; + if (surface_manager_) { + auto resolver = [](const char* name) -> void* { + return reinterpret_cast(::eglGetProcAddress(name)); + }; + + compositor_ = std::make_unique(this, resolver); + } else { + compositor_ = std::make_unique(this); + } + + FlutterCompositor compositor = {}; + compositor.struct_size = sizeof(FlutterCompositor); + compositor.user_data = this; + compositor.create_backing_store_callback = + [](const FlutterBackingStoreConfig* config, + FlutterBackingStore* backing_store_out, void* user_data) -> bool { + auto host = static_cast(user_data); + + return host->compositor_->CreateBackingStore(*config, backing_store_out); + }; + + compositor.collect_backing_store_callback = + [](const FlutterBackingStore* backing_store, void* user_data) -> bool { + auto host = static_cast(user_data); + + return host->compositor_->CollectBackingStore(backing_store); + }; + + compositor.present_layers_callback = [](const FlutterLayer** layers, + size_t layers_count, + void* user_data) -> bool { + auto host = static_cast(user_data); + + return host->compositor_->Present(layers, layers_count); + }; + args.compositor = &compositor; + if (aot_data_) { args.aot_data = aot_data_.get(); } diff --git a/shell/platform/windows/flutter_windows_engine.h b/shell/platform/windows/flutter_windows_engine.h index 4406b4235b36f..9e60f860f6e3c 100644 --- a/shell/platform/windows/flutter_windows_engine.h +++ b/shell/platform/windows/flutter_windows_engine.h @@ -23,6 +23,7 @@ #include "flutter/shell/platform/embedder/embedder.h" #include "flutter/shell/platform/windows/accessibility_bridge_windows.h" #include "flutter/shell/platform/windows/angle_surface_manager.h" +#include "flutter/shell/platform/windows/compositor.h" #include "flutter/shell/platform/windows/cursor_handler.h" #include "flutter/shell/platform/windows/flutter_desktop_messenger.h" #include "flutter/shell/platform/windows/flutter_project_bundle.h" @@ -367,6 +368,10 @@ class FlutterWindowsEngine { // May be nullptr if ANGLE failed to initialize. std::unique_ptr surface_manager_; + // The compositor that creates backing stores for the engine to render into + // and then presents them onto views. + std::unique_ptr compositor_; + // The plugin registrar managing internal plugins. std::unique_ptr internal_plugin_registrar_; diff --git a/shell/platform/windows/flutter_windows_view.h b/shell/platform/windows/flutter_windows_view.h index 55125f8551fa2..2551fe8f0b3c7 100644 --- a/shell/platform/windows/flutter_windows_view.h +++ b/shell/platform/windows/flutter_windows_view.h @@ -71,12 +71,12 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate { // If the view is resizing, this returns false if the frame is not the target // size. Otherwise, it unblocks the platform thread and blocks the raster // thread until the v-blank. - bool SwapBuffers(); + virtual bool SwapBuffers(); // Callback for presenting a software bitmap. - bool PresentSoftwareBitmap(const void* allocation, - size_t row_bytes, - size_t height); + virtual bool PresentSoftwareBitmap(const void* allocation, + size_t row_bytes, + size_t height); // Send initial bounds to embedder. Must occur after engine has initialized. void SendInitialBounds();