diff --git a/shell/platform/windows/BUILD.gn b/shell/platform/windows/BUILD.gn index 7a30f737b69b6..b33d4389bbd3a 100644 --- a/shell/platform/windows/BUILD.gn +++ b/shell/platform/windows/BUILD.gn @@ -230,6 +230,7 @@ executable("flutter_windows_unittests") { "testing/test_keyboard.cc", "testing/test_keyboard.h", "testing/test_keyboard_unittests.cc", + "testing/view_modifier.h", "testing/windows_test.cc", "testing/windows_test.h", "testing/windows_test_config_builder.cc", diff --git a/shell/platform/windows/compositor_opengl.cc b/shell/platform/windows/compositor_opengl.cc index b43f888d65191..0d6d8ed665dd3 100644 --- a/shell/platform/windows/compositor_opengl.cc +++ b/shell/platform/windows/compositor_opengl.cc @@ -93,12 +93,8 @@ bool CompositorOpenGL::CollectBackingStore(const FlutterBackingStore* store) { bool CompositorOpenGL::Present(const FlutterLayer** layers, size_t layers_count) { - if (!engine_->view()) { - return false; - } - - if (!engine_->egl_manager()->surface() || - !engine_->egl_manager()->surface()->IsValid()) { + FlutterWindowsView* view = engine_->view(); + if (!view) { return false; } @@ -111,7 +107,7 @@ bool CompositorOpenGL::Present(const FlutterLayer** layers, return false; } - return ClearSurface(); + return Clear(view); } // TODO: Support compositing layers and platform views. @@ -129,11 +125,16 @@ bool CompositorOpenGL::Present(const FlutterLayer** layers, // Check if this frame can be presented. This resizes the surface if a resize // is pending and |width| and |height| match the target size. - if (!engine_->view()->OnFrameGenerated(width, height)) { + if (!view->OnFrameGenerated(width, height)) { return false; } - if (!engine_->egl_manager()->surface()->MakeCurrent()) { + // |OnFrameGenerated| should return false if the surface isn't valid. + FML_DCHECK(view->surface() != nullptr); + FML_DCHECK(view->surface()->IsValid()); + + egl::WindowSurface* surface = view->surface(); + if (!surface->MakeCurrent()) { return false; } @@ -159,11 +160,11 @@ bool CompositorOpenGL::Present(const FlutterLayer** layers, GL_NEAREST // filter ); - if (!engine_->egl_manager()->surface()->SwapBuffers()) { + if (!surface->SwapBuffers()) { return false; } - engine_->view()->OnFramePresented(); + view->OnFramePresented(); return true; } @@ -175,12 +176,7 @@ bool CompositorOpenGL::Initialize() { return false; } - egl::Surface* surface = manager->surface(); - if (!surface || !surface->IsValid()) { - return false; - } - - if (!surface->MakeCurrent()) { + if (!manager->render_context()->MakeCurrent()) { return false; } @@ -195,24 +191,31 @@ bool CompositorOpenGL::Initialize() { return true; } -bool CompositorOpenGL::ClearSurface() { +bool CompositorOpenGL::Clear(FlutterWindowsView* view) { FML_DCHECK(is_initialized_); - // Resize the surface if needed. - engine_->view()->OnEmptyFrameGenerated(); + // Check if this frame can be presented. This resizes the surface if needed. + if (!view->OnEmptyFrameGenerated()) { + return false; + } + + // |OnEmptyFrameGenerated| should return false if the surface isn't valid. + FML_DCHECK(view->surface() != nullptr); + FML_DCHECK(view->surface()->IsValid()); - if (!engine_->egl_manager()->surface()->MakeCurrent()) { + egl::WindowSurface* surface = view->surface(); + if (!surface->MakeCurrent()) { return false; } gl_->ClearColor(0.0f, 0.0f, 0.0f, 0.0f); gl_->Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - if (!engine_->egl_manager()->surface()->SwapBuffers()) { + if (!surface->SwapBuffers()) { return false; } - engine_->view()->OnFramePresented(); + view->OnFramePresented(); return true; } diff --git a/shell/platform/windows/compositor_opengl.h b/shell/platform/windows/compositor_opengl.h index 06c22202332e2..e1ed15293f921 100644 --- a/shell/platform/windows/compositor_opengl.h +++ b/shell/platform/windows/compositor_opengl.h @@ -53,7 +53,7 @@ class CompositorOpenGL : public Compositor { bool Initialize(); // Clear the view's surface and removes any previously presented layers. - bool ClearSurface(); + bool Clear(FlutterWindowsView* view); }; } // namespace flutter diff --git a/shell/platform/windows/compositor_opengl_unittests.cc b/shell/platform/windows/compositor_opengl_unittests.cc index 08fd6dfaf4861..bfa307533e715 100644 --- a/shell/platform/windows/compositor_opengl_unittests.cc +++ b/shell/platform/windows/compositor_opengl_unittests.cc @@ -9,11 +9,13 @@ #include "flutter/shell/platform/windows/compositor_opengl.h" #include "flutter/shell/platform/windows/egl/manager.h" #include "flutter/shell/platform/windows/flutter_windows_view.h" +#include "flutter/shell/platform/windows/testing/egl/mock_context.h" #include "flutter/shell/platform/windows/testing/egl/mock_manager.h" #include "flutter/shell/platform/windows/testing/egl/mock_window_surface.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/view_modifier.h" #include "flutter/shell/platform/windows/testing/windows_test.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -22,6 +24,7 @@ namespace flutter { namespace testing { namespace { +using ::testing::AnyNumber; using ::testing::Return; const unsigned char* MockGetString(GLenum name) { @@ -66,19 +69,17 @@ class CompositorOpenGLTest : public WindowsTest { protected: FlutterWindowsEngine* engine() { return engine_.get(); } egl::MockManager* egl_manager() { return egl_manager_; } - egl::MockWindowSurface* surface() { return surface_.get(); } + egl::MockContext* render_context() { return render_context_.get(); } + egl::MockWindowSurface* surface() { return surface_; } - void UseHeadlessEngine(bool add_surface = true) { + void UseHeadlessEngine() { auto egl_manager = std::make_unique(); + render_context_ = std::make_unique(); egl_manager_ = egl_manager.get(); - if (add_surface) { - surface_ = std::make_unique(); - EXPECT_CALL(*egl_manager_, surface) - .WillRepeatedly(Return(surface_.get())); - } else { - EXPECT_CALL(*egl_manager_, surface).WillRepeatedly(Return(nullptr)); - } + EXPECT_CALL(*egl_manager_, render_context) + .Times(AnyNumber()) + .WillRepeatedly(Return(render_context_.get())); FlutterWindowsEngineBuilder builder{GetContext()}; @@ -88,7 +89,7 @@ class CompositorOpenGLTest : public WindowsTest { } void UseEngineWithView(bool add_surface = true) { - UseHeadlessEngine(add_surface); + UseHeadlessEngine(); auto window = std::make_unique(); EXPECT_CALL(*window.get(), SetView).Times(1); @@ -96,13 +97,24 @@ class CompositorOpenGLTest : public WindowsTest { view_ = std::make_unique(std::move(window)); + if (add_surface) { + auto surface = std::make_unique(); + surface_ = surface.get(); + + EXPECT_CALL(*surface_, Destroy).Times(AnyNumber()); + + ViewModifier modifier{view_.get()}; + modifier.SetSurface(std::move(surface)); + } + engine_->SetView(view_.get()); } private: std::unique_ptr engine_; std::unique_ptr view_; - std::unique_ptr surface_; + std::unique_ptr render_context_; + egl::MockWindowSurface* surface_; egl::MockManager* egl_manager_; FML_DISALLOW_COPY_AND_ASSIGN(CompositorOpenGLTest); @@ -118,8 +130,7 @@ TEST_F(CompositorOpenGLTest, CreateBackingStore) { FlutterBackingStoreConfig config = {}; FlutterBackingStore backing_store = {}; - EXPECT_CALL(*surface(), IsValid).WillOnce(Return(true)); - EXPECT_CALL(*surface(), MakeCurrent).WillOnce(Return(true)); + EXPECT_CALL(*render_context(), MakeCurrent).WillOnce(Return(true)); ASSERT_TRUE(compositor.CreateBackingStore(config, &backing_store)); ASSERT_TRUE(compositor.CollectBackingStore(&backing_store)); } @@ -132,8 +143,7 @@ TEST_F(CompositorOpenGLTest, InitializationFailure) { FlutterBackingStoreConfig config = {}; FlutterBackingStore backing_store = {}; - EXPECT_CALL(*surface(), IsValid).WillOnce(Return(true)); - EXPECT_CALL(*surface(), MakeCurrent).WillOnce(Return(false)); + EXPECT_CALL(*render_context(), MakeCurrent).WillOnce(Return(false)); EXPECT_FALSE(compositor.CreateBackingStore(config, &backing_store)); } @@ -145,8 +155,7 @@ TEST_F(CompositorOpenGLTest, Present) { FlutterBackingStoreConfig config = {}; FlutterBackingStore backing_store = {}; - EXPECT_CALL(*surface(), IsValid).WillRepeatedly(Return(true)); - EXPECT_CALL(*surface(), MakeCurrent).WillOnce(Return(true)); + EXPECT_CALL(*render_context(), MakeCurrent).WillOnce(Return(true)); ASSERT_TRUE(compositor.CreateBackingStore(config, &backing_store)); FlutterLayer layer = {}; @@ -154,6 +163,7 @@ TEST_F(CompositorOpenGLTest, Present) { layer.backing_store = &backing_store; const FlutterLayer* layer_ptr = &layer; + EXPECT_CALL(*surface(), IsValid).WillRepeatedly(Return(true)); EXPECT_CALL(*surface(), MakeCurrent).WillOnce(Return(true)); EXPECT_CALL(*surface(), SwapBuffers).WillOnce(Return(true)); EXPECT_TRUE(compositor.Present(&layer_ptr, 1)); @@ -168,8 +178,9 @@ TEST_F(CompositorOpenGLTest, PresentEmpty) { // The context will be bound twice: first to initialize the compositor, second // to clear the surface. + EXPECT_CALL(*render_context(), MakeCurrent).WillOnce(Return(true)); EXPECT_CALL(*surface(), IsValid).WillRepeatedly(Return(true)); - EXPECT_CALL(*surface(), MakeCurrent).Times(2).WillRepeatedly(Return(true)); + EXPECT_CALL(*surface(), MakeCurrent).WillOnce(Return(true)); EXPECT_CALL(*surface(), SwapBuffers).WillOnce(Return(true)); EXPECT_TRUE(compositor.Present(nullptr, 0)); } @@ -182,8 +193,7 @@ TEST_F(CompositorOpenGLTest, HeadlessPresentIgnored) { FlutterBackingStoreConfig config = {}; FlutterBackingStore backing_store = {}; - EXPECT_CALL(*surface(), IsValid).WillOnce(Return(true)); - EXPECT_CALL(*surface(), MakeCurrent).WillOnce(Return(true)); + EXPECT_CALL(*render_context(), MakeCurrent).WillOnce(Return(true)); ASSERT_TRUE(compositor.CreateBackingStore(config, &backing_store)); FlutterLayer layer = {}; @@ -204,11 +214,12 @@ TEST_F(CompositorOpenGLTest, NoSurfaceIgnored) { FlutterBackingStoreConfig config = {}; FlutterBackingStore backing_store = {}; - ASSERT_FALSE(compositor.CreateBackingStore(config, &backing_store)); + EXPECT_CALL(*render_context(), MakeCurrent).WillOnce(Return(true)); + ASSERT_TRUE(compositor.CreateBackingStore(config, &backing_store)); FlutterLayer layer = {}; layer.type = kFlutterLayerContentTypeBackingStore; - layer.backing_store = nullptr; + layer.backing_store = &backing_store; const FlutterLayer* layer_ptr = &layer; EXPECT_FALSE(compositor.Present(&layer_ptr, 1)); diff --git a/shell/platform/windows/egl/manager.cc b/shell/platform/windows/egl/manager.cc index 8c9cbcc321f55..2b827f863f803 100644 --- a/shell/platform/windows/egl/manager.cc +++ b/shell/platform/windows/egl/manager.cc @@ -263,11 +263,11 @@ bool Manager::IsValid() const { return is_valid_; } -bool Manager::CreateWindowSurface(HWND hwnd, size_t width, size_t height) { - FML_DCHECK(surface_ == nullptr || !surface_->IsValid()); - +std::unique_ptr Manager::CreateWindowSurface(HWND hwnd, + size_t width, + size_t height) { if (!hwnd || !is_valid_) { - return false; + return nullptr; } // Disable ANGLE's automatic surface resizing and provide an explicit size. @@ -286,43 +286,11 @@ bool Manager::CreateWindowSurface(HWND hwnd, size_t width, size_t height) { surface_attributes); if (surface == EGL_NO_SURFACE) { LogEGLError("Surface creation failed."); - return false; + return nullptr; } - surface_ = std::make_unique( - display_, render_context_->GetHandle(), surface, width, height); - return true; -} - -void Manager::ResizeWindowSurface(HWND hwnd, size_t width, size_t height) { - FML_CHECK(surface_ != nullptr); - - auto const existing_width = surface_->width(); - auto const existing_height = surface_->height(); - auto const existing_vsync = surface_->vsync_enabled(); - - if (width != existing_width || height != existing_height) { - // TODO: Destroying the surface and re-creating it is expensive. - // Ideally this would use ANGLE's automatic surface sizing instead. - // See: https://github.com/flutter/flutter/issues/79427 - if (!surface_->Destroy()) { - FML_LOG(ERROR) << "Manager::ResizeSurface failed to destroy surface"; - return; - } - - if (!CreateWindowSurface(hwnd, width, height)) { - FML_LOG(ERROR) << "Manager::ResizeSurface failed to create surface"; - return; - } - - if (!surface_->MakeCurrent() || - !surface_->SetVSyncEnabled(existing_vsync)) { - // Surfaces block until the v-blank by default. - // Failing to update the vsync might result in unnecessary blocking. - // This regresses performance but not correctness. - FML_LOG(ERROR) << "Manager::ResizeSurface failed to set vsync"; - } - } + return std::make_unique(display_, render_context_->GetHandle(), + surface, width, height); } bool Manager::HasContextCurrent() { @@ -355,9 +323,5 @@ Context* Manager::resource_context() const { return resource_context_.get(); } -WindowSurface* Manager::surface() const { - return surface_.get(); -} - } // namespace egl } // namespace flutter diff --git a/shell/platform/windows/egl/manager.h b/shell/platform/windows/egl/manager.h index 601b6bdf8e22f..4ef289549d9ef 100644 --- a/shell/platform/windows/egl/manager.h +++ b/shell/platform/windows/egl/manager.h @@ -37,22 +37,19 @@ class Manager { // Whether the manager is currently valid. bool IsValid() const; - // Creates an EGLSurface wrapper and backing DirectX 11 SwapChain - // associated with window, in the appropriate format for display. - // HWND is the window backing the surface. Width and height represent - // dimensions surface is created at. + // Creates an EGL surface that can be used to render a Flutter view into a + // win32 HWND. // - // After the surface is created, |SetVSyncEnabled| should be called on a - // thread that can bind the |render_context_|. - virtual bool CreateWindowSurface(HWND hwnd, size_t width, size_t height); - - // Resizes backing surface from current size to newly requested size - // based on width and height for the specific case when width and height do - // not match current surface dimensions. HWND is the window backing the - // surface. + // After the surface is created, |WindowSurface::SetVSyncEnabled| should be + // called on a thread that can make the surface current. // - // This binds |render_context_| to the current thread. - virtual void ResizeWindowSurface(HWND hwnd, size_t width, size_t height); + // HWND is the window backing the surface. Width and height are the surface's + // physical pixel dimensions. + // + // Returns nullptr on failure. + virtual std::unique_ptr CreateWindowSurface(HWND hwnd, + size_t width, + size_t height); // Check if the current thread has a context bound. bool HasContextCurrent(); @@ -74,9 +71,6 @@ class Manager { // Get the EGL context used for async texture uploads. virtual Context* resource_context() const; - // Get the EGL surface that backs the Flutter view. - virtual WindowSurface* surface() const; - protected: // Creates a new surface manager retaining reference to the passed-in target // for the lifetime of the manager. @@ -115,9 +109,6 @@ class Manager { // The EGL context used for async texture uploads. std::unique_ptr resource_context_; - // Th EGL surface used to render into the Flutter view. - std::unique_ptr surface_; - // The current D3D device. Microsoft::WRL::ComPtr resolved_device_ = nullptr; diff --git a/shell/platform/windows/egl/surface.h b/shell/platform/windows/egl/surface.h index 34cc96510c71e..6f1b107bd6852 100644 --- a/shell/platform/windows/egl/surface.h +++ b/shell/platform/windows/egl/surface.h @@ -19,7 +19,7 @@ class Surface { public: Surface(EGLDisplay display, EGLContext context, EGLSurface surface); - ~Surface(); + virtual ~Surface(); // Destroy the EGL surface and invalidate this object. // diff --git a/shell/platform/windows/flutter_windows_unittests.cc b/shell/platform/windows/flutter_windows_unittests.cc index 0b9b619b5957b..b21182e4c99d6 100644 --- a/shell/platform/windows/flutter_windows_unittests.cc +++ b/shell/platform/windows/flutter_windows_unittests.cc @@ -30,8 +30,9 @@ class HalfBrokenEGLManager : public egl::Manager { public: HalfBrokenEGLManager() : egl::Manager(/*enable_impeller = */ false) {} - bool CreateWindowSurface(HWND hwnd, size_t width, size_t height) override { - return false; + std::unique_ptr + CreateWindowSurface(HWND hwnd, size_t width, size_t height) override { + return nullptr; } }; diff --git a/shell/platform/windows/flutter_windows_view.cc b/shell/platform/windows/flutter_windows_view.cc index 7b3d97f87b06f..91e9306e682f6 100644 --- a/shell/platform/windows/flutter_windows_view.cc +++ b/shell/platform/windows/flutter_windows_view.cc @@ -40,15 +40,16 @@ bool SurfaceWillUpdate(size_t cur_width, /// Update the surface's swap interval to block until the v-blank iff /// the system compositor is disabled. -void UpdateVsync(const FlutterWindowsEngine& engine, bool needs_vsync) { +void UpdateVsync(const FlutterWindowsEngine& engine, + egl::WindowSurface* surface, + bool needs_vsync) { egl::Manager* egl_manager = engine.egl_manager(); if (!egl_manager) { return; } - auto update_vsync = [egl_manager, needs_vsync]() { - egl::WindowSurface* surface = egl_manager->surface(); - if (!surface) { + auto update_vsync = [egl_manager, surface, needs_vsync]() { + if (!surface || !surface->IsValid()) { return; } @@ -119,39 +120,52 @@ void FlutterWindowsView::SetEngine(FlutterWindowsEngine* engine) { binding_handler_->GetDpiScale()); } -void FlutterWindowsView::OnEmptyFrameGenerated() { +bool FlutterWindowsView::OnEmptyFrameGenerated() { // Called on the raster thread. std::unique_lock lock(resize_mutex_); + if (surface_ == nullptr || !surface_->IsValid()) { + return false; + } + if (resize_status_ != ResizeState::kResizeStarted) { - return; + return true; + } + + if (!ResizeRenderSurface(resize_target_height_, resize_target_width_)) { + return false; } // Platform thread is blocked for the entire duration until the - // resize_status_ is set to kDone. - engine_->egl_manager()->ResizeWindowSurface( - GetWindowHandle(), resize_target_width_, resize_target_height_); + // resize_status_ is set to kDone by |OnFramePresented|. resize_status_ = ResizeState::kFrameGenerated; + return true; } bool FlutterWindowsView::OnFrameGenerated(size_t width, size_t height) { // Called on the raster thread. std::unique_lock lock(resize_mutex_); + if (surface_ == nullptr || !surface_->IsValid()) { + return false; + } + if (resize_status_ != ResizeState::kResizeStarted) { return true; } - if (resize_target_width_ == width && resize_target_height_ == height) { - // Platform thread is blocked for the entire duration until the - // resize_status_ is set to kDone. - engine_->egl_manager()->ResizeWindowSurface(GetWindowHandle(), width, - height); - resize_status_ = ResizeState::kFrameGenerated; - return true; + if (resize_target_width_ != width || resize_target_height_ != height) { + return false; + } + + if (!ResizeRenderSurface(resize_target_width_, resize_target_height_)) { + return false; } - return false; + // Platform thread is blocked for the entire duration until the + // resize_status_ is set to kDone by |OnFramePresented|. + resize_status_ = ResizeState::kFrameGenerated; + return true; } void FlutterWindowsView::UpdateFlutterCursor(const std::string& cursor_name) { @@ -178,8 +192,7 @@ bool FlutterWindowsView::OnWindowSizeChanged(size_t width, size_t height) { return true; } - egl::WindowSurface* surface = engine_->egl_manager()->surface(); - if (!surface || !surface->IsValid()) { + if (!surface_ || !surface_->IsValid()) { SendWindowMetrics(width, height, binding_handler_->GetDpiScale()); return true; } @@ -187,7 +200,7 @@ bool FlutterWindowsView::OnWindowSizeChanged(size_t width, size_t height) { // We're using OpenGL rendering. Resizing the surface must happen on the // raster thread. bool surface_will_update = - SurfaceWillUpdate(surface->width(), surface->height(), width, height); + SurfaceWillUpdate(surface_->width(), surface_->height(), width, height); if (!surface_will_update) { SendWindowMetrics(width, height, binding_handler_->GetDpiScale()); return true; @@ -651,34 +664,66 @@ bool FlutterWindowsView::PresentSoftwareBitmap(const void* allocation, } void FlutterWindowsView::CreateRenderSurface() { + FML_DCHECK(surface_ == nullptr); + if (engine_ && engine_->egl_manager()) { PhysicalWindowBounds bounds = binding_handler_->GetPhysicalWindowBounds(); - engine_->egl_manager()->CreateWindowSurface(GetWindowHandle(), bounds.width, - bounds.height); + surface_ = engine_->egl_manager()->CreateWindowSurface( + GetWindowHandle(), bounds.width, bounds.height); - UpdateVsync(*engine_, NeedsVsync()); + UpdateVsync(*engine_, surface_.get(), NeedsVsync()); resize_target_width_ = bounds.width; resize_target_height_ = bounds.height; } } -void FlutterWindowsView::DestroyRenderSurface() { - if (!engine_) { - return; +bool FlutterWindowsView::ResizeRenderSurface(size_t width, size_t height) { + FML_DCHECK(surface_ != nullptr); + + // No-op if the surface is already the desired size. + if (width == surface_->width() && height == surface_->height()) { + return true; } - auto const manager = engine_->egl_manager(); - if (!manager) { - return; + auto const existing_vsync = surface_->vsync_enabled(); + + // TODO: Destroying the surface and re-creating it is expensive. + // Ideally this would use ANGLE's automatic surface sizing instead. + // See: https://github.com/flutter/flutter/issues/79427 + if (!surface_->Destroy()) { + FML_LOG(ERROR) << "View resize failed to destroy surface"; + return false; } - auto const surface = manager->surface(); - if (!surface) { - return; + std::unique_ptr resized_surface = + engine_->egl_manager()->CreateWindowSurface(GetWindowHandle(), width, + height); + if (!resized_surface) { + FML_LOG(ERROR) << "View resize failed to create surface"; + return false; } - surface->Destroy(); + if (!resized_surface->MakeCurrent() || + !resized_surface->SetVSyncEnabled(existing_vsync)) { + // Surfaces block until the v-blank by default. + // Failing to update the vsync might result in unnecessary blocking. + // This regresses performance but not correctness. + FML_LOG(ERROR) << "View resize failed to set vsync"; + } + + surface_ = std::move(resized_surface); + return true; +} + +void FlutterWindowsView::DestroyRenderSurface() { + if (surface_) { + surface_->Destroy(); + } +} + +egl::WindowSurface* FlutterWindowsView::surface() const { + return surface_.get(); } void FlutterWindowsView::OnHighContrastChanged() { @@ -689,7 +734,7 @@ HWND FlutterWindowsView::GetWindowHandle() const { return binding_handler_->GetWindowHandle(); } -FlutterWindowsEngine* FlutterWindowsView::GetEngine() { +FlutterWindowsEngine* FlutterWindowsView::GetEngine() const { return engine_; } @@ -736,7 +781,7 @@ void FlutterWindowsView::UpdateSemanticsEnabled(bool enabled) { } void FlutterWindowsView::OnDwmCompositionChanged() { - UpdateVsync(*engine_, NeedsVsync()); + UpdateVsync(*engine_, surface_.get(), NeedsVsync()); } void FlutterWindowsView::OnWindowStateEvent(HWND hwnd, WindowStateEvent event) { diff --git a/shell/platform/windows/flutter_windows_view.h b/shell/platform/windows/flutter_windows_view.h index ba6651f909cfd..62ccb70c3943f 100644 --- a/shell/platform/windows/flutter_windows_view.h +++ b/shell/platform/windows/flutter_windows_view.h @@ -52,11 +52,16 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate { // Destroys current rendering surface if one has been allocated. void DestroyRenderSurface(); + // Get the EGL surface that backs the Flutter view. + // + // This might be nullptr or an invalid surface. + egl::WindowSurface* surface() const; + // Return the currently configured HWND. virtual HWND GetWindowHandle() const; // Returns the engine backing this view. - FlutterWindowsEngine* GetEngine(); + FlutterWindowsEngine* GetEngine() const; // Tells the engine to generate a new frame void ForceRedraw(); @@ -79,16 +84,17 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate { void OnHighContrastChanged() override; // Called on the raster thread when |CompositorOpenGL| receives an empty - // frame. + // frame. Returns true if the frame can be presented. // - // This resizes the surface if a resize is pending. - void OnEmptyFrameGenerated(); + // This destroys and then re-creates the view's surface if a resize is + // pending. + bool OnEmptyFrameGenerated(); // Called on the raster thread when |CompositorOpenGL| receives a frame. // Returns true if the frame can be presented. // - // This resizes the surface if a resize is pending and |width| and - // |height| match the target size. + // This destroys and then re-creates the view's surface if a resize is pending + // and |width| and |height| match the target size. bool OnFrameGenerated(size_t width, size_t height); // Called on the raster thread after |CompositorOpenGL| presents a frame. @@ -226,6 +232,9 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate { CreateAccessibilityBridge(); private: + // Allows setting the surface in tests. + friend class ViewModifier; + // Struct holding the state of an individual pointer. The engine doesn't keep // track of which buttons have been pressed, so it's the embedding's // responsibility. @@ -267,6 +276,17 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate { kDone, }; + // Resize the surface to the desired size. + // + // If the dimensions have changed, this destroys the original surface and + // creates a new one. + // + // This must be run on the raster thread. This binds the surface to the + // current thread. + // + // Width and height are the surface's desired physical pixel dimensions. + bool ResizeRenderSurface(size_t width, size_t height); + // Sends a window metrics update to the Flutter engine using current window // dimensions in physical void SendWindowMetrics(size_t width, size_t height, double dpiscale) const; @@ -371,6 +391,12 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate { // Mocks win32 APIs. std::shared_ptr windows_proc_table_; + // The EGL surface backing the view. + // + // Null if using software rasterization, the surface hasn't been created yet, + // or if surface creation failed. + std::unique_ptr surface_ = nullptr; + // Keeps track of pointer states in relation to the window. std::unordered_map> pointer_states_; diff --git a/shell/platform/windows/flutter_windows_view_unittests.cc b/shell/platform/windows/flutter_windows_view_unittests.cc index eed65117b169b..92c0416ec69b2 100644 --- a/shell/platform/windows/flutter_windows_view_unittests.cc +++ b/shell/platform/windows/flutter_windows_view_unittests.cc @@ -25,6 +25,7 @@ #include "flutter/shell/platform/windows/testing/mock_window_binding_handler.h" #include "flutter/shell/platform/windows/testing/mock_windows_proc_table.h" #include "flutter/shell/platform/windows/testing/test_keyboard.h" +#include "flutter/shell/platform/windows/testing/view_modifier.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -237,18 +238,18 @@ TEST(FlutterWindowsViewTest, Shutdown) { auto egl_manager = std::make_unique(); auto surface = std::make_unique(); - EXPECT_CALL(*egl_manager.get(), surface).WillOnce(Return(surface.get())); - - EngineModifier modifier(engine.get()); FlutterWindowsView view(std::move(window_binding_handler)); + EngineModifier engine_modifier(engine.get()); + ViewModifier view_modifier(&view); // The engine must be stopped before the surface can be destroyed. InSequence s; EXPECT_CALL(*engine.get(), Stop).Times(1); EXPECT_CALL(*surface.get(), Destroy).Times(1); - modifier.SetEGLManager(std::move(egl_manager)); + engine_modifier.SetEGLManager(std::move(egl_manager)); view.SetEngine(engine.get()); + view_modifier.SetSurface(std::move(surface)); } TEST(FlutterWindowsViewTest, KeySequence) { @@ -814,30 +815,37 @@ TEST(FlutterWindowsViewTest, AccessibilityHitTesting) { TEST(FlutterWindowsViewTest, WindowResizeTests) { std::unique_ptr engine = GetTestEngine(); - EngineModifier modifier(engine.get()); + EngineModifier engine_modifier(engine.get()); auto window_binding_handler = std::make_unique>(); auto windows_proc_table = std::make_shared(); auto egl_manager = std::make_unique(); auto surface = std::make_unique(); + auto resized_surface = std::make_unique(); + auto resized_surface_ptr = resized_surface.get(); + + EXPECT_CALL(*surface.get(), IsValid).WillRepeatedly(Return(true)); + EXPECT_CALL(*surface.get(), Destroy).WillOnce(Return(true)); - EXPECT_CALL(*windows_proc_table.get(), DwmFlush).WillOnce(Return(S_OK)); - EXPECT_CALL(*egl_manager.get(), surface) - .WillRepeatedly(Return(surface.get())); EXPECT_CALL(*egl_manager.get(), - ResizeWindowSurface(_, /*width=*/500, /*height=*/500)) - .Times(1); - EXPECT_CALL(*surface.get(), IsValid).WillOnce(Return(true)); - EXPECT_CALL(*surface.get(), Destroy).Times(1); + CreateWindowSurface(_, /*width=*/500, /*height=*/500)) + .WillOnce(Return(std::move((resized_surface)))); + EXPECT_CALL(*resized_surface_ptr, MakeCurrent).WillOnce(Return(true)); + EXPECT_CALL(*resized_surface_ptr, SetVSyncEnabled).WillOnce(Return(true)); + EXPECT_CALL(*windows_proc_table.get(), DwmFlush).WillOnce(Return(S_OK)); + + EXPECT_CALL(*resized_surface_ptr, Destroy).WillOnce(Return(true)); FlutterWindowsView view(std::move(window_binding_handler), std::move(windows_proc_table)); - modifier.SetEGLManager(std::move(egl_manager)); + ViewModifier view_modifier(&view); + engine_modifier.SetEGLManager(std::move(egl_manager)); view.SetEngine(engine.get()); + view_modifier.SetSurface(std::move(surface)); fml::AutoResetWaitableEvent metrics_sent_latch; - modifier.embedder_api().SendWindowMetricsEvent = MOCK_ENGINE_PROC( + engine_modifier.embedder_api().SendWindowMetricsEvent = MOCK_ENGINE_PROC( SendWindowMetricsEvent, ([&metrics_sent_latch](auto engine, const FlutterWindowMetricsEvent* event) { @@ -865,30 +873,37 @@ TEST(FlutterWindowsViewTest, WindowResizeTests) { // Verify that an empty frame completes a view resize. TEST(FlutterWindowsViewTest, TestEmptyFrameResizes) { std::unique_ptr engine = GetTestEngine(); - EngineModifier modifier(engine.get()); + EngineModifier engine_modifier(engine.get()); auto window_binding_handler = std::make_unique>(); auto windows_proc_table = std::make_shared(); auto egl_manager = std::make_unique(); auto surface = std::make_unique(); + auto resized_surface = std::make_unique(); + auto resized_surface_ptr = resized_surface.get(); + + EXPECT_CALL(*surface.get(), IsValid).WillRepeatedly(Return(true)); + EXPECT_CALL(*surface.get(), Destroy).WillOnce(Return(true)); - EXPECT_CALL(*windows_proc_table.get(), DwmFlush).WillOnce(Return(S_OK)); - EXPECT_CALL(*egl_manager.get(), surface) - .WillRepeatedly(Return(surface.get())); EXPECT_CALL(*egl_manager.get(), - ResizeWindowSurface(_, /*width=*/500, /*height=*/500)) - .Times(1); - EXPECT_CALL(*surface.get(), IsValid).WillOnce(Return(true)); - EXPECT_CALL(*surface.get(), Destroy).Times(1); + CreateWindowSurface(_, /*width=*/500, /*height=*/500)) + .WillOnce(Return(std::move((resized_surface)))); + EXPECT_CALL(*resized_surface_ptr, MakeCurrent).WillOnce(Return(true)); + EXPECT_CALL(*resized_surface_ptr, SetVSyncEnabled).WillOnce(Return(true)); + EXPECT_CALL(*windows_proc_table.get(), DwmFlush).WillOnce(Return(S_OK)); + + EXPECT_CALL(*resized_surface_ptr, Destroy).WillOnce(Return(true)); FlutterWindowsView view(std::move(window_binding_handler), std::move(windows_proc_table)); - modifier.SetEGLManager(std::move(egl_manager)); + ViewModifier view_modifier(&view); + engine_modifier.SetEGLManager(std::move(egl_manager)); view.SetEngine(engine.get()); + view_modifier.SetSurface(std::move(surface)); fml::AutoResetWaitableEvent metrics_sent_latch; - modifier.embedder_api().SendWindowMetricsEvent = MOCK_ENGINE_PROC( + engine_modifier.embedder_api().SendWindowMetricsEvent = MOCK_ENGINE_PROC( SendWindowMetricsEvent, ([&metrics_sent_latch](auto engine, const FlutterWindowMetricsEvent* event) { @@ -918,7 +933,7 @@ TEST(FlutterWindowsViewTest, TestEmptyFrameResizes) { // https://github.com/flutter/flutter/issues/141855 TEST(FlutterWindowsViewTest, WindowResizeRace) { std::unique_ptr engine = GetTestEngine(); - EngineModifier modifier(engine.get()); + EngineModifier engine_modifier(engine.get()); auto window_binding_handler = std::make_unique>(); @@ -926,14 +941,14 @@ TEST(FlutterWindowsViewTest, WindowResizeRace) { auto egl_manager = std::make_unique(); auto surface = std::make_unique(); - EXPECT_CALL(*egl_manager.get(), surface) - .WillRepeatedly(Return(surface.get())); - EXPECT_CALL(*surface.get(), IsValid).WillOnce(Return(true)); - EXPECT_CALL(*surface.get(), Destroy).Times(1); + EXPECT_CALL(*surface.get(), IsValid).WillRepeatedly(Return(true)); + EXPECT_CALL(*surface.get(), Destroy).WillOnce(Return(true)); FlutterWindowsView view(std::move(window_binding_handler), std::move(windows_proc_table)); - modifier.SetEGLManager(std::move(egl_manager)); + ViewModifier view_modifier(&view); + engine_modifier.SetEGLManager(std::move(egl_manager)); + view_modifier.SetSurface(std::move(surface)); view.SetEngine(engine.get()); // Begin a frame. @@ -961,7 +976,7 @@ TEST(FlutterWindowsViewTest, WindowResizeRace) { // even though EGL initialized successfully. TEST(FlutterWindowsViewTest, WindowResizeInvalidSurface) { std::unique_ptr engine = GetTestEngine(); - EngineModifier modifier(engine.get()); + EngineModifier engine_modifier(engine.get()); auto window_binding_handler = std::make_unique>(); @@ -969,19 +984,19 @@ TEST(FlutterWindowsViewTest, WindowResizeInvalidSurface) { auto egl_manager = std::make_unique(); auto surface = std::make_unique(); - EXPECT_CALL(*egl_manager.get(), surface) - .WillRepeatedly(Return(surface.get())); - EXPECT_CALL(*egl_manager.get(), ResizeWindowSurface).Times(0); + EXPECT_CALL(*egl_manager.get(), CreateWindowSurface).Times(0); EXPECT_CALL(*surface.get(), IsValid).WillRepeatedly(Return(false)); EXPECT_CALL(*surface.get(), Destroy).WillOnce(Return(false)); FlutterWindowsView view(std::move(window_binding_handler), std::move(windows_proc_table)); - modifier.SetEGLManager(std::move(egl_manager)); + ViewModifier view_modifier(&view); + engine_modifier.SetEGLManager(std::move(egl_manager)); + view_modifier.SetSurface(std::move(surface)); view.SetEngine(engine.get()); auto metrics_sent = false; - modifier.embedder_api().SendWindowMetricsEvent = MOCK_ENGINE_PROC( + engine_modifier.embedder_api().SendWindowMetricsEvent = MOCK_ENGINE_PROC( SendWindowMetricsEvent, ([&metrics_sent](auto engine, const FlutterWindowMetricsEvent* event) { metrics_sent = true; @@ -1002,8 +1017,7 @@ TEST(FlutterWindowsViewTest, WindowResizeWithoutSurface) { auto windows_proc_table = std::make_shared(); auto egl_manager = std::make_unique(); - EXPECT_CALL(*egl_manager.get(), surface).WillRepeatedly(Return(nullptr)); - EXPECT_CALL(*egl_manager.get(), ResizeWindowSurface).Times(0); + EXPECT_CALL(*egl_manager.get(), CreateWindowSurface).Times(0); FlutterWindowsView view(std::move(window_binding_handler), std::move(windows_proc_table)); @@ -1377,6 +1391,7 @@ TEST(FlutterWindowsViewTest, DisablesVSyncAtStartup) { auto egl_manager = std::make_unique(); egl::MockContext render_context; auto surface = std::make_unique(); + auto surface_ptr = surface.get(); EXPECT_CALL(*engine.get(), running).WillRepeatedly(Return(false)); EXPECT_CALL(*engine.get(), PostRasterThreadTask).Times(0); @@ -1386,25 +1401,23 @@ TEST(FlutterWindowsViewTest, DisablesVSyncAtStartup) { EXPECT_CALL(*egl_manager.get(), render_context) .WillOnce(Return(&render_context)); - EXPECT_CALL(*egl_manager.get(), surface) - .WillRepeatedly(Return(surface.get())); + EXPECT_CALL(*surface_ptr, IsValid).WillOnce(Return(true)); - EngineModifier modifier(engine.get()); + EngineModifier engine_modifier(engine.get()); FlutterWindowsView view(std::move(window_binding_handler), std::move(windows_proc_table)); InSequence s; - EXPECT_CALL(*egl_manager.get(), CreateWindowSurface(_, _, _)) - .Times(1) - .WillOnce(Return(true)); - EXPECT_CALL(*surface.get(), MakeCurrent).WillOnce(Return(true)); - EXPECT_CALL(*surface.get(), SetVSyncEnabled(false)).WillOnce(Return(true)); + EXPECT_CALL(*egl_manager.get(), CreateWindowSurface) + .WillOnce(Return(std::move(surface))); + EXPECT_CALL(*surface_ptr, MakeCurrent).WillOnce(Return(true)); + EXPECT_CALL(*surface_ptr, SetVSyncEnabled(false)).WillOnce(Return(true)); EXPECT_CALL(render_context, ClearCurrent).WillOnce(Return(true)); EXPECT_CALL(*engine.get(), Stop).Times(1); - EXPECT_CALL(*surface.get(), Destroy).Times(1); + EXPECT_CALL(*surface_ptr, Destroy).Times(1); - modifier.SetEGLManager(std::move(egl_manager)); + engine_modifier.SetEGLManager(std::move(egl_manager)); view.SetEngine(engine.get()); view.CreateRenderSurface(); @@ -1420,6 +1433,7 @@ TEST(FlutterWindowsViewTest, EnablesVSyncAtStartup) { auto egl_manager = std::make_unique(); egl::MockContext render_context; auto surface = std::make_unique(); + auto surface_ptr = surface.get(); EXPECT_CALL(*engine.get(), running).WillRepeatedly(Return(false)); EXPECT_CALL(*engine.get(), PostRasterThreadTask).Times(0); @@ -1428,23 +1442,21 @@ TEST(FlutterWindowsViewTest, EnablesVSyncAtStartup) { EXPECT_CALL(*egl_manager.get(), render_context) .WillOnce(Return(&render_context)); - EXPECT_CALL(*egl_manager.get(), surface) - .WillRepeatedly(Return(surface.get())); + EXPECT_CALL(*surface_ptr, IsValid).WillOnce(Return(true)); EngineModifier modifier(engine.get()); FlutterWindowsView view(std::move(window_binding_handler), std::move(windows_proc_table)); InSequence s; - EXPECT_CALL(*egl_manager.get(), CreateWindowSurface(_, _, _)) - .Times(1) - .WillOnce(Return(true)); - EXPECT_CALL(*surface.get(), MakeCurrent).WillOnce(Return(true)); - EXPECT_CALL(*surface.get(), SetVSyncEnabled(true)).WillOnce(Return(true)); + EXPECT_CALL(*egl_manager.get(), CreateWindowSurface) + .WillOnce(Return(std::move(surface))); + EXPECT_CALL(*surface_ptr, MakeCurrent).WillOnce(Return(true)); + EXPECT_CALL(*surface_ptr, SetVSyncEnabled(true)).WillOnce(Return(true)); EXPECT_CALL(render_context, ClearCurrent).WillOnce(Return(true)); EXPECT_CALL(*engine.get(), Stop).Times(1); - EXPECT_CALL(*surface.get(), Destroy).Times(1); + EXPECT_CALL(*surface_ptr, Destroy).Times(1); modifier.SetEGLManager(std::move(egl_manager)); view.SetEngine(engine.get()); @@ -1462,6 +1474,7 @@ TEST(FlutterWindowsViewTest, DisablesVSyncAfterStartup) { auto egl_manager = std::make_unique(); egl::MockContext render_context; auto surface = std::make_unique(); + auto surface_ptr = surface.get(); EXPECT_CALL(*engine.get(), running).WillRepeatedly(Return(true)); EXPECT_CALL(*windows_proc_table.get(), DwmIsCompositionEnabled) @@ -1469,27 +1482,25 @@ TEST(FlutterWindowsViewTest, DisablesVSyncAfterStartup) { EXPECT_CALL(*egl_manager.get(), render_context) .WillOnce(Return(&render_context)); - EXPECT_CALL(*egl_manager.get(), surface) - .WillRepeatedly(Return(surface.get())); + EXPECT_CALL(*surface_ptr, IsValid).WillOnce(Return(true)); EngineModifier modifier(engine.get()); FlutterWindowsView view(std::move(window_binding_handler), std::move(windows_proc_table)); InSequence s; - EXPECT_CALL(*egl_manager.get(), CreateWindowSurface(_, _, _)) - .Times(1) - .WillOnce(Return(true)); + EXPECT_CALL(*egl_manager.get(), CreateWindowSurface) + .WillOnce(Return(std::move(surface))); EXPECT_CALL(*engine.get(), PostRasterThreadTask) .WillOnce([](fml::closure callback) { callback(); return true; }); - EXPECT_CALL(*surface.get(), MakeCurrent).WillOnce(Return(true)); - EXPECT_CALL(*surface.get(), SetVSyncEnabled(false)).WillOnce(Return(true)); + EXPECT_CALL(*surface_ptr, MakeCurrent).WillOnce(Return(true)); + EXPECT_CALL(*surface_ptr, SetVSyncEnabled(false)).WillOnce(Return(true)); EXPECT_CALL(render_context, ClearCurrent).WillOnce(Return(true)); EXPECT_CALL(*engine.get(), Stop).Times(1); - EXPECT_CALL(*surface.get(), Destroy).Times(1); + EXPECT_CALL(*surface_ptr, Destroy).Times(1); modifier.SetEGLManager(std::move(egl_manager)); view.SetEngine(engine.get()); @@ -1507,6 +1518,7 @@ TEST(FlutterWindowsViewTest, EnablesVSyncAfterStartup) { auto egl_manager = std::make_unique(); egl::MockContext render_context; auto surface = std::make_unique(); + auto surface_ptr = surface.get(); EXPECT_CALL(*engine.get(), running).WillRepeatedly(Return(true)); @@ -1515,28 +1527,26 @@ TEST(FlutterWindowsViewTest, EnablesVSyncAfterStartup) { EXPECT_CALL(*egl_manager.get(), render_context) .WillOnce(Return(&render_context)); - EXPECT_CALL(*egl_manager.get(), surface) - .WillRepeatedly(Return(surface.get())); + EXPECT_CALL(*surface_ptr, IsValid).WillOnce(Return(true)); EngineModifier modifier(engine.get()); FlutterWindowsView view(std::move(window_binding_handler), std::move(windows_proc_table)); InSequence s; - EXPECT_CALL(*egl_manager.get(), CreateWindowSurface(_, _, _)) - .Times(1) - .WillOnce(Return(true)); + EXPECT_CALL(*egl_manager.get(), CreateWindowSurface) + .WillOnce(Return(std::move(surface))); EXPECT_CALL(*engine.get(), PostRasterThreadTask) .WillOnce([](fml::closure callback) { callback(); return true; }); - EXPECT_CALL(*surface.get(), MakeCurrent).WillOnce(Return(true)); - EXPECT_CALL(*surface.get(), SetVSyncEnabled(true)).WillOnce(Return(true)); + EXPECT_CALL(*surface_ptr, MakeCurrent).WillOnce(Return(true)); + EXPECT_CALL(*surface_ptr, SetVSyncEnabled(true)).WillOnce(Return(true)); EXPECT_CALL(render_context, ClearCurrent).WillOnce(Return(true)); EXPECT_CALL(*engine.get(), Stop).Times(1); - EXPECT_CALL(*surface.get(), Destroy).Times(1); + EXPECT_CALL(*surface_ptr, Destroy).Times(1); modifier.SetEGLManager(std::move(egl_manager)); view.SetEngine(engine.get()); @@ -1555,6 +1565,7 @@ TEST(FlutterWindowsViewTest, UpdatesVSyncOnDwmUpdates) { auto egl_manager = std::make_unique(); egl::MockContext render_context; auto surface = std::make_unique(); + auto surface_ptr = surface.get(); EXPECT_CALL(*engine.get(), running).WillRepeatedly(Return(true)); @@ -1570,27 +1581,29 @@ TEST(FlutterWindowsViewTest, UpdatesVSyncOnDwmUpdates) { EXPECT_CALL(*egl_manager.get(), render_context) .WillRepeatedly(Return(&render_context)); - EXPECT_CALL(*egl_manager.get(), surface) - .WillRepeatedly(Return(surface.get())); - EngineModifier modifier(engine.get()); + EXPECT_CALL(*surface_ptr, IsValid).WillRepeatedly(Return(true)); + + EngineModifier engine_modifier(engine.get()); FlutterWindowsView view(std::move(window_binding_handler), std::move(windows_proc_table)); + ViewModifier view_modifier(&view); InSequence s; - EXPECT_CALL(*surface.get(), MakeCurrent).WillOnce(Return(true)); - EXPECT_CALL(*surface.get(), SetVSyncEnabled(true)).WillOnce(Return(true)); + EXPECT_CALL(*surface_ptr, MakeCurrent).WillOnce(Return(true)); + EXPECT_CALL(*surface_ptr, SetVSyncEnabled(true)).WillOnce(Return(true)); EXPECT_CALL(render_context, ClearCurrent).WillOnce(Return(true)); - EXPECT_CALL(*surface.get(), MakeCurrent).WillOnce(Return(true)); - EXPECT_CALL(*surface.get(), SetVSyncEnabled(false)).WillOnce(Return(true)); + EXPECT_CALL(*surface_ptr, MakeCurrent).WillOnce(Return(true)); + EXPECT_CALL(*surface_ptr, SetVSyncEnabled(false)).WillOnce(Return(true)); EXPECT_CALL(render_context, ClearCurrent).WillOnce(Return(true)); EXPECT_CALL(*engine.get(), Stop).Times(1); - EXPECT_CALL(*surface.get(), Destroy).Times(1); + EXPECT_CALL(*surface_ptr, Destroy).Times(1); - modifier.SetEGLManager(std::move(egl_manager)); + engine_modifier.SetEGLManager(std::move(egl_manager)); view.SetEngine(engine.get()); + view_modifier.SetSurface(std::move(surface)); view.GetEngine()->OnDwmCompositionChanged(); view.GetEngine()->OnDwmCompositionChanged(); diff --git a/shell/platform/windows/testing/egl/mock_manager.h b/shell/platform/windows/testing/egl/mock_manager.h index 640ad50361199..1bde275416e16 100644 --- a/shell/platform/windows/testing/egl/mock_manager.h +++ b/shell/platform/windows/testing/egl/mock_manager.h @@ -18,12 +18,13 @@ class MockManager : public flutter::egl::Manager { public: MockManager() : Manager(false) {} - MOCK_METHOD(bool, CreateWindowSurface, (HWND, size_t, size_t), (override)); - MOCK_METHOD(void, ResizeWindowSurface, (HWND, size_t, size_t), (override)); + MOCK_METHOD(std::unique_ptr, + CreateWindowSurface, + (HWND, size_t, size_t), + (override)); MOCK_METHOD(flutter::egl::Context*, render_context, (), (const, override)); MOCK_METHOD(flutter::egl::Context*, resource_context, (), (const, override)); - MOCK_METHOD(flutter::egl::WindowSurface*, surface, (), (const, override)); private: FML_DISALLOW_COPY_AND_ASSIGN(MockManager); diff --git a/shell/platform/windows/testing/view_modifier.h b/shell/platform/windows/testing/view_modifier.h new file mode 100644 index 0000000000000..f99f04fe90378 --- /dev/null +++ b/shell/platform/windows/testing/view_modifier.h @@ -0,0 +1,36 @@ +// 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_TESTING_VIEW_MODIFIER_H_ +#define FLUTTER_SHELL_PLATFORM_WINDOWS_TESTING_VIEW_MODIFIER_H_ + +#include "flutter/fml/macros.h" +#include "flutter/shell/platform/windows/egl/window_surface.h" +#include "flutter/shell/platform/windows/flutter_windows_view.h" + +namespace flutter { + +// A test utility class providing the ability to access and alter various +// private fields in a |FlutterWindowsView| instance. +class ViewModifier { + public: + explicit ViewModifier(FlutterWindowsView* view) : view_(view) {} + + // Override the EGL surface used by the view. + // + // Modifications are to the view, and will last for the lifetime of the + // view unless overwritten again. + void SetSurface(std::unique_ptr surface) { + view_->surface_ = std::move(surface); + } + + private: + FlutterWindowsView* view_; + + FML_DISALLOW_COPY_AND_ASSIGN(ViewModifier); +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_TESTING_VIEW_MODIFIER_H_