From dda3ab040e7e480eca4d2a9fce60017ffabe9b64 Mon Sep 17 00:00:00 2001 From: Loic Sharma Date: Mon, 4 Dec 2023 15:36:14 -0800 Subject: [PATCH 01/11] [Windows] Move to `FlutterCompositor` for rendering --- .../renderer/backend/gles/description_gles.cc | 4 + .../renderer/backend/gles/description_gles.h | 2 + .../linux/fl_backing_store_provider.cc | 3 +- shell/platform/windows/BUILD.gn | 9 ++ shell/platform/windows/compositor.h | 39 +++++ shell/platform/windows/compositor_opengl.cc | 142 ++++++++++++++++++ shell/platform/windows/compositor_opengl.h | 58 +++++++ .../windows/compositor_opengl_unittests.cc | 111 ++++++++++++++ shell/platform/windows/compositor_software.cc | 49 ++++++ shell/platform/windows/compositor_software.h | 35 +++++ .../windows/compositor_software_unittests.cc | 38 +++++ .../windows/flutter_windows_engine.cc | 71 ++++++--- .../platform/windows/flutter_windows_engine.h | 5 + 13 files changed, 541 insertions(+), 25 deletions(-) create mode 100644 shell/platform/windows/compositor.h create mode 100644 shell/platform/windows/compositor_opengl.cc create mode 100644 shell/platform/windows/compositor_opengl.h create mode 100644 shell/platform/windows/compositor_opengl_unittests.cc create mode 100644 shell/platform/windows/compositor_software.cc create mode 100644 shell/platform/windows/compositor_software.h create mode 100644 shell/platform/windows/compositor_software_unittests.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..18aac3c3cabb6 --- /dev/null +++ b/shell/platform/windows/compositor_opengl.cc @@ -0,0 +1,142 @@ +// 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; +}; + +} // 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 = nullptr; + 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) { + 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); + + 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; + } + + auto gl = std::make_unique(resolver_); + if (!gl->IsValid()) { + return false; + } + + // Based off Skia's logic: + // https://github.com/google/skia/blob/4738ed711e03212aceec3cd502a4adb545f38e63/src/gpu/ganesh/gl/GrGLCaps.cpp#L1963-L2116 + auto description = gl->GetDescription(); + if (description->HasExtension("GL_EXT_texture_format_BGRA8888")) { + format_ = GL_BGRA8_EXT; + } else if (description->HasExtension("GL_APPLE_texture_format_BGRA8888") && + description->GetGlVersion().IsAtLeast(impeller::Version(3, 0))) { + format_ = GL_BGRA8_EXT; + } else { + format_ = GL_RGBA8; + } + + gl_ = std::move(gl); + 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..08516fc498c10 --- /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_; + + // Table of resolved GLES functions. Null until the compositor is initialized. + std::unique_ptr gl_; + + // The OpenGL texture target format for backing stores. Invalid value until + // the compositor is initialized. + uint32_t format_; + + // 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..53e44b05183f3 --- /dev/null +++ b/shell/platform/windows/compositor_opengl_unittests.cc @@ -0,0 +1,111 @@ +// 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_opengl.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/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 (unsigned char*)"3.0"; + default: + return (unsigned char*)""; + } +} + +const unsigned char* MockGetStringi(GLenum name, GLuint index) { + return (unsigned char*)""; +} + +void MockGetIntegerv(GLenum name, int* value) { + *value = 0; +} + +GLenum MockGetError() { + return GL_NO_ERROR; +} + +void DoNothing() {} + +const impeller::ProcTableGLES::Resolver kMockResolver = [](const char* name) { + if (strcmp(name, "glGetString") == 0) { + return reinterpret_cast(&MockGetString); + } else if (strcmp(name, "glGetStringi") == 0) { + return reinterpret_cast(&MockGetStringi); + } else if (strcmp(name, "glGetIntegerv") == 0) { + return reinterpret_cast(&MockGetIntegerv); + } else if (strcmp(name, "glGetError") == 0) { + 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 CompositorOpenGLTest : public WindowsTest {}; + +} // namespace + +TEST_F(CompositorOpenGLTest, CreateBackingStore) { + auto surface_manager = std::make_unique(); + + EXPECT_CALL(*surface_manager.get(), MakeCurrent).WillOnce(Return(true)); + + auto engine = FlutterWindowsEngineBuilder{GetContext()}.Build(); + EngineModifier modifier(engine.get()); + modifier.SetSurfaceManager(surface_manager.release()); + + auto compositor = CompositorOpenGL{engine.get(), kMockResolver}; + + FlutterBackingStoreConfig config = {}; + FlutterBackingStore backing_store = {}; + + ASSERT_TRUE(compositor.CreateBackingStore(config, &backing_store)); + ASSERT_TRUE(compositor.CollectBackingStore(&backing_store)); +} + +TEST_F(CompositorOpenGLTest, InitializationFailure) { + auto surface_manager = std::make_unique(); + + EXPECT_CALL(*surface_manager.get(), MakeCurrent).WillOnce(Return(false)); + + auto engine = FlutterWindowsEngineBuilder{GetContext()}.Build(); + EngineModifier modifier(engine.get()); + modifier.SetSurfaceManager(surface_manager.release()); + + auto compositor = CompositorOpenGL{engine.get(), kMockResolver}; + + FlutterBackingStoreConfig config = {}; + FlutterBackingStore backing_store = {}; + + bool result = compositor.CreateBackingStore(config, &backing_store); + + EXPECT_FALSE(result); +} + +} // 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..adb3f209e21dc --- /dev/null +++ b/shell/platform/windows/compositor_software.cc @@ -0,0 +1,49 @@ +// 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) { + void* allocation = std::malloc(config.size.width * config.size.height * 4); + 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.destruction_callback = nullptr; + result->software.user_data = nullptr; + 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) { + FML_DCHECK(layers_count == 1); + FML_DCHECK(layers[0]->type == kFlutterLayerContentTypeBackingStore); + FML_DCHECK(layers[0]->backing_store->type == + kFlutterBackingStoreTypeSoftware); + + 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..7754ed5402e2c --- /dev/null +++ b/shell/platform/windows/compositor_software_unittests.cc @@ -0,0 +1,38 @@ +// 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/testing/engine_modifier.h" +#include "flutter/shell/platform/windows/testing/flutter_windows_engine_builder.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 CompositorSoftwareTest : public WindowsTest {}; + +} // namespace + +TEST_F(CompositorSoftwareTest, CreateBackingStore) { + auto engine = FlutterWindowsEngineBuilder{GetContext()}.Build(); + + auto compositor = CompositorSoftware{engine.get()}; + + FlutterBackingStoreConfig config = {}; + FlutterBackingStore backing_store = {}; + + ASSERT_TRUE(compositor.CreateBackingStore(config, &backing_store)); + 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_; From 782dc4d9f1253c685ca34c4fcbf677013345703d Mon Sep 17 00:00:00 2001 From: Loic Sharma Date: Fri, 8 Dec 2023 16:03:04 -0800 Subject: [PATCH 02/11] Remove unused mock --- shell/platform/windows/compositor_opengl_unittests.cc | 6 ------ 1 file changed, 6 deletions(-) diff --git a/shell/platform/windows/compositor_opengl_unittests.cc b/shell/platform/windows/compositor_opengl_unittests.cc index 53e44b05183f3..10439af631969 100644 --- a/shell/platform/windows/compositor_opengl_unittests.cc +++ b/shell/platform/windows/compositor_opengl_unittests.cc @@ -28,10 +28,6 @@ const unsigned char* MockGetString(GLenum name) { } } -const unsigned char* MockGetStringi(GLenum name, GLuint index) { - return (unsigned char*)""; -} - void MockGetIntegerv(GLenum name, int* value) { *value = 0; } @@ -45,8 +41,6 @@ void DoNothing() {} const impeller::ProcTableGLES::Resolver kMockResolver = [](const char* name) { if (strcmp(name, "glGetString") == 0) { return reinterpret_cast(&MockGetString); - } else if (strcmp(name, "glGetStringi") == 0) { - return reinterpret_cast(&MockGetStringi); } else if (strcmp(name, "glGetIntegerv") == 0) { return reinterpret_cast(&MockGetIntegerv); } else if (strcmp(name, "glGetError") == 0) { From 5611ca633818fd16f3243eedf03d3034374bfc15 Mon Sep 17 00:00:00 2001 From: Loic Sharma Date: Fri, 8 Dec 2023 16:14:12 -0800 Subject: [PATCH 03/11] Add new line --- shell/platform/windows/compositor_opengl.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/shell/platform/windows/compositor_opengl.cc b/shell/platform/windows/compositor_opengl.cc index 18aac3c3cabb6..9b3754dcf1263 100644 --- a/shell/platform/windows/compositor_opengl.cc +++ b/shell/platform/windows/compositor_opengl.cc @@ -36,6 +36,7 @@ bool CompositorOpenGL::CreateBackingStore( 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); From 6cc972efaeedbeb8c84d81d99f269a63c033c6b5 Mon Sep 17 00:00:00 2001 From: Loic Sharma Date: Fri, 8 Dec 2023 16:27:03 -0800 Subject: [PATCH 04/11] Clean --- shell/platform/windows/compositor_software_unittests.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/shell/platform/windows/compositor_software_unittests.cc b/shell/platform/windows/compositor_software_unittests.cc index 7754ed5402e2c..8958044b0ecee 100644 --- a/shell/platform/windows/compositor_software_unittests.cc +++ b/shell/platform/windows/compositor_software_unittests.cc @@ -9,14 +9,12 @@ #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/windows_test.h" -#include "gmock/gmock.h" #include "gtest/gtest.h" namespace flutter { namespace testing { namespace { -using ::testing::Return; class CompositorSoftwareTest : public WindowsTest {}; From 46d692cbfa03698545fb6282290b9ca954e2ec27 Mon Sep 17 00:00:00 2001 From: Loic Sharma Date: Fri, 8 Dec 2023 17:01:23 -0800 Subject: [PATCH 05/11] Update licenses --- ci/licenses_golden/excluded_files | 2 ++ ci/licenses_golden/licenses_flutter | 10 ++++++++++ 2 files changed, 12 insertions(+) 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 From 69b6586355b7b1e045a7c914994564b8a39c310e Mon Sep 17 00:00:00 2001 From: Loic Sharma Date: Fri, 8 Dec 2023 17:26:35 -0800 Subject: [PATCH 06/11] More tests --- shell/platform/windows/compositor_opengl.cc | 4 + .../windows/compositor_opengl_unittests.cc | 297 +++++++++++------- shell/platform/windows/compositor_software.cc | 4 + .../windows/compositor_software_unittests.cc | 165 +++++++--- shell/platform/windows/flutter_windows_view.h | 8 +- 5 files changed, 333 insertions(+), 145 deletions(-) diff --git a/shell/platform/windows/compositor_opengl.cc b/shell/platform/windows/compositor_opengl.cc index 9b3754dcf1263..0b3a5988d721e 100644 --- a/shell/platform/windows/compositor_opengl.cc +++ b/shell/platform/windows/compositor_opengl.cc @@ -82,6 +82,10 @@ bool CompositorOpenGL::Present(const FlutterLayer** layers, 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; diff --git a/shell/platform/windows/compositor_opengl_unittests.cc b/shell/platform/windows/compositor_opengl_unittests.cc index 10439af631969..03a9a195460a0 100644 --- a/shell/platform/windows/compositor_opengl_unittests.cc +++ b/shell/platform/windows/compositor_opengl_unittests.cc @@ -1,105 +1,192 @@ -// 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_opengl.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/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 (unsigned char*)"3.0"; - default: - return (unsigned char*)""; - } -} - -void MockGetIntegerv(GLenum name, int* value) { - *value = 0; -} - -GLenum MockGetError() { - return GL_NO_ERROR; -} - -void DoNothing() {} - -const impeller::ProcTableGLES::Resolver kMockResolver = [](const char* name) { - if (strcmp(name, "glGetString") == 0) { - return reinterpret_cast(&MockGetString); - } else if (strcmp(name, "glGetIntegerv") == 0) { - return reinterpret_cast(&MockGetIntegerv); - } else if (strcmp(name, "glGetError") == 0) { - 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 CompositorOpenGLTest : public WindowsTest {}; - -} // namespace - -TEST_F(CompositorOpenGLTest, CreateBackingStore) { - auto surface_manager = std::make_unique(); - - EXPECT_CALL(*surface_manager.get(), MakeCurrent).WillOnce(Return(true)); - - auto engine = FlutterWindowsEngineBuilder{GetContext()}.Build(); - EngineModifier modifier(engine.get()); - modifier.SetSurfaceManager(surface_manager.release()); - - auto compositor = CompositorOpenGL{engine.get(), kMockResolver}; - - FlutterBackingStoreConfig config = {}; - FlutterBackingStore backing_store = {}; - - ASSERT_TRUE(compositor.CreateBackingStore(config, &backing_store)); - ASSERT_TRUE(compositor.CollectBackingStore(&backing_store)); -} - -TEST_F(CompositorOpenGLTest, InitializationFailure) { - auto surface_manager = std::make_unique(); - - EXPECT_CALL(*surface_manager.get(), MakeCurrent).WillOnce(Return(false)); - - auto engine = FlutterWindowsEngineBuilder{GetContext()}.Build(); - EngineModifier modifier(engine.get()); - modifier.SetSurfaceManager(surface_manager.release()); - - auto compositor = CompositorOpenGL{engine.get(), kMockResolver}; - - FlutterBackingStoreConfig config = {}; - FlutterBackingStore backing_store = {}; - - bool result = compositor.CreateBackingStore(config, &backing_store); - - EXPECT_FALSE(result); -} - -} // namespace testing -} // namespace flutter +// 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 (unsigned char*)"3.0"; + default: + return (unsigned char*)""; + } +} + +void MockGetIntegerv(GLenum name, int* value) { + *value = 0; +} + +GLenum MockGetError() { + return GL_NO_ERROR; +} + +void DoNothing() {} + +const impeller::ProcTableGLES::Resolver kMockResolver = [](const char* name) { + if (strcmp(name, "glGetString") == 0) { + return reinterpret_cast(&MockGetString); + } else if (strcmp(name, "glGetIntegerv") == 0) { + return reinterpret_cast(&MockGetIntegerv); + } else if (strcmp(name, "glGetError") == 0) { + 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(surface_manager.release()); + } + + 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 index adb3f209e21dc..0e27bfe7d2752 100644 --- a/shell/platform/windows/compositor_software.cc +++ b/shell/platform/windows/compositor_software.cc @@ -40,6 +40,10 @@ bool CompositorSoftware::Present(const FlutterLayer** layers, 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( diff --git a/shell/platform/windows/compositor_software_unittests.cc b/shell/platform/windows/compositor_software_unittests.cc index 8958044b0ecee..e9a8860e03223 100644 --- a/shell/platform/windows/compositor_software_unittests.cc +++ b/shell/platform/windows/compositor_software_unittests.cc @@ -1,36 +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/testing/engine_modifier.h" -#include "flutter/shell/platform/windows/testing/flutter_windows_engine_builder.h" -#include "flutter/shell/platform/windows/testing/windows_test.h" -#include "gtest/gtest.h" - -namespace flutter { -namespace testing { - -namespace { - -class CompositorSoftwareTest : public WindowsTest {}; - -} // namespace - -TEST_F(CompositorSoftwareTest, CreateBackingStore) { - auto engine = FlutterWindowsEngineBuilder{GetContext()}.Build(); - - auto compositor = CompositorSoftware{engine.get()}; - - FlutterBackingStoreConfig config = {}; - FlutterBackingStore backing_store = {}; - - ASSERT_TRUE(compositor.CreateBackingStore(config, &backing_store)); - ASSERT_TRUE(compositor.CollectBackingStore(&backing_store)); -} - -} // namespace testing -} // namespace flutter +// 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_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(); From 6eb7fa0f2992a43844f5f32cc0c4a450f8c71297 Mon Sep 17 00:00:00 2001 From: Loic Sharma Date: Mon, 11 Dec 2023 13:30:06 -0800 Subject: [PATCH 07/11] Switch to calloc --- shell/platform/windows/compositor_software.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shell/platform/windows/compositor_software.cc b/shell/platform/windows/compositor_software.cc index 0e27bfe7d2752..e9927faf5c450 100644 --- a/shell/platform/windows/compositor_software.cc +++ b/shell/platform/windows/compositor_software.cc @@ -14,7 +14,8 @@ CompositorSoftware::CompositorSoftware(FlutterWindowsEngine* engine) bool CompositorSoftware::CreateBackingStore( const FlutterBackingStoreConfig& config, FlutterBackingStore* result) { - void* allocation = std::malloc(config.size.width * config.size.height * 4); + size_t size = config.size.width * config.size.height * 4; + void* allocation = std::calloc(size, sizeof(uint8_t)); if (!allocation) { return false; } From fcf036508ce037aec7c8b7c4aa4db1ca9fe4c5e8 Mon Sep 17 00:00:00 2001 From: Loic Sharma Date: Tue, 12 Dec 2023 13:54:51 -0800 Subject: [PATCH 08/11] Address some feedback --- shell/platform/windows/compositor_opengl.cc | 8 ++++---- shell/platform/windows/compositor_opengl.h | 6 +++--- .../platform/windows/compositor_opengl_unittests.cc | 12 +++++++----- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/shell/platform/windows/compositor_opengl.cc b/shell/platform/windows/compositor_opengl.cc index 0b3a5988d721e..07babf2973a2c 100644 --- a/shell/platform/windows/compositor_opengl.cc +++ b/shell/platform/windows/compositor_opengl.cc @@ -122,14 +122,15 @@ bool CompositorOpenGL::Initialize() { return false; } - auto gl = std::make_unique(resolver_); - if (!gl->IsValid()) { + gl_ = std::make_unique(resolver_); + if (!gl_->IsValid()) { + gl_.reset(); return false; } // Based off Skia's logic: // https://github.com/google/skia/blob/4738ed711e03212aceec3cd502a4adb545f38e63/src/gpu/ganesh/gl/GrGLCaps.cpp#L1963-L2116 - auto description = gl->GetDescription(); + auto description = gl_->GetDescription(); if (description->HasExtension("GL_EXT_texture_format_BGRA8888")) { format_ = GL_BGRA8_EXT; } else if (description->HasExtension("GL_APPLE_texture_format_BGRA8888") && @@ -139,7 +140,6 @@ bool CompositorOpenGL::Initialize() { format_ = GL_RGBA8; } - gl_ = std::move(gl); is_initialized_ = true; return true; } diff --git a/shell/platform/windows/compositor_opengl.h b/shell/platform/windows/compositor_opengl.h index 08516fc498c10..46b42eebf0eb2 100644 --- a/shell/platform/windows/compositor_opengl.h +++ b/shell/platform/windows/compositor_opengl.h @@ -40,14 +40,14 @@ class CompositorOpenGL : public Compositor { bool is_initialized_ = false; // Function used to resolve GLES functions. - impeller::ProcTableGLES::Resolver resolver_; + impeller::ProcTableGLES::Resolver resolver_ = nullptr; // Table of resolved GLES functions. Null until the compositor is initialized. - std::unique_ptr gl_; + std::unique_ptr gl_ = nullptr; // The OpenGL texture target format for backing stores. Invalid value until // the compositor is initialized. - uint32_t format_; + uint32_t format_ = 0; // Initialize the compositor. This must run on the raster thread. bool Initialize(); diff --git a/shell/platform/windows/compositor_opengl_unittests.cc b/shell/platform/windows/compositor_opengl_unittests.cc index 03a9a195460a0..9fd7fc1c6fe3e 100644 --- a/shell/platform/windows/compositor_opengl_unittests.cc +++ b/shell/platform/windows/compositor_opengl_unittests.cc @@ -26,9 +26,9 @@ const unsigned char* MockGetString(GLenum name) { switch (name) { case GL_VERSION: case GL_SHADING_LANGUAGE_VERSION: - return (unsigned char*)"3.0"; + return reinterpret_cast("3.0"); default: - return (unsigned char*)""; + return reinterpret_cast(""); } } @@ -43,11 +43,13 @@ GLenum MockGetError() { void DoNothing() {} const impeller::ProcTableGLES::Resolver kMockResolver = [](const char* name) { - if (strcmp(name, "glGetString") == 0) { + std::string function_name{name}; + + if (function_name == "glGetString") { return reinterpret_cast(&MockGetString); - } else if (strcmp(name, "glGetIntegerv") == 0) { + } else if (function_name == "glGetIntegerv") { return reinterpret_cast(&MockGetIntegerv); - } else if (strcmp(name, "glGetError") == 0) { + } else if (function_name == "glGetError") { return reinterpret_cast(&MockGetError); } else { return reinterpret_cast(&DoNothing); From 498825b0ac8ddf86c31f818ab994a8eebedb6eb5 Mon Sep 17 00:00:00 2001 From: Loic Sharma Date: Tue, 12 Dec 2023 14:43:58 -0800 Subject: [PATCH 09/11] Document why destruction_callback is empty --- shell/platform/windows/compositor_opengl.cc | 5 ++++- shell/platform/windows/compositor_software.cc | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/shell/platform/windows/compositor_opengl.cc b/shell/platform/windows/compositor_opengl.cc index 07babf2973a2c..ec3b77ed6fd48 100644 --- a/shell/platform/windows/compositor_opengl.cc +++ b/shell/platform/windows/compositor_opengl.cc @@ -54,7 +54,10 @@ bool CompositorOpenGL::CreateBackingStore( 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 = nullptr; + 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; } diff --git a/shell/platform/windows/compositor_software.cc b/shell/platform/windows/compositor_software.cc index e9927faf5c450..1c58fa9240731 100644 --- a/shell/platform/windows/compositor_software.cc +++ b/shell/platform/windows/compositor_software.cc @@ -24,8 +24,11 @@ bool CompositorSoftware::CreateBackingStore( result->software.allocation = allocation; result->software.height = config.size.height; result->software.row_bytes = config.size.width * 4; - result->software.destruction_callback = nullptr; 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; } From 215db1bdea98c03b856ab8530188bea8d63c543c Mon Sep 17 00:00:00 2001 From: Loic Sharma Date: Wed, 13 Dec 2023 09:55:40 -0800 Subject: [PATCH 10/11] Feedback --- shell/platform/windows/compositor_opengl.cc | 2 ++ shell/platform/windows/compositor_opengl_unittests.cc | 2 +- shell/platform/windows/compositor_software.cc | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/shell/platform/windows/compositor_opengl.cc b/shell/platform/windows/compositor_opengl.cc index ec3b77ed6fd48..6729e6197897f 100644 --- a/shell/platform/windows/compositor_opengl.cc +++ b/shell/platform/windows/compositor_opengl.cc @@ -78,6 +78,8 @@ bool CompositorOpenGL::CollectBackingStore(const FlutterBackingStore* store) { 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); diff --git a/shell/platform/windows/compositor_opengl_unittests.cc b/shell/platform/windows/compositor_opengl_unittests.cc index 9fd7fc1c6fe3e..c60d0c685e554 100644 --- a/shell/platform/windows/compositor_opengl_unittests.cc +++ b/shell/platform/windows/compositor_opengl_unittests.cc @@ -96,7 +96,7 @@ class CompositorOpenGLTest : public WindowsTest { engine_ = builder.Build(); EngineModifier modifier(engine_.get()); - modifier.SetSurfaceManager(surface_manager.release()); + modifier.SetSurfaceManager(std::move(surface_manager)); } void UseEngineWithView() { diff --git a/shell/platform/windows/compositor_software.cc b/shell/platform/windows/compositor_software.cc index 1c58fa9240731..06809e11b5ee2 100644 --- a/shell/platform/windows/compositor_software.cc +++ b/shell/platform/windows/compositor_software.cc @@ -39,6 +39,8 @@ bool CompositorSoftware::CollectBackingStore(const FlutterBackingStore* store) { 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 == From aea1725d81408ba1a6467f3b09b0632317e0dcbc Mon Sep 17 00:00:00 2001 From: Loic Sharma Date: Wed, 13 Dec 2023 10:24:24 -0800 Subject: [PATCH 11/11] Refactor --- shell/platform/windows/compositor_opengl.cc | 26 +++++++++++---------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/shell/platform/windows/compositor_opengl.cc b/shell/platform/windows/compositor_opengl.cc index 6729e6197897f..3a445c4ded63f 100644 --- a/shell/platform/windows/compositor_opengl.cc +++ b/shell/platform/windows/compositor_opengl.cc @@ -17,6 +17,19 @@ struct FramebufferBackingStore { 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, @@ -133,18 +146,7 @@ bool CompositorOpenGL::Initialize() { return false; } - // Based off Skia's logic: - // https://github.com/google/skia/blob/4738ed711e03212aceec3cd502a4adb545f38e63/src/gpu/ganesh/gl/GrGLCaps.cpp#L1963-L2116 - auto description = gl_->GetDescription(); - if (description->HasExtension("GL_EXT_texture_format_BGRA8888")) { - format_ = GL_BGRA8_EXT; - } else if (description->HasExtension("GL_APPLE_texture_format_BGRA8888") && - description->GetGlVersion().IsAtLeast(impeller::Version(3, 0))) { - format_ = GL_BGRA8_EXT; - } else { - format_ = GL_RGBA8; - } - + format_ = GetSupportedTextureFormat(gl_->GetDescription()); is_initialized_ = true; return true; }