Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit a190775

Browse files
authored
[Windows] Make the view own its EGL surface (#50421)
This makes the view own its EGL surface. This will allow us to support multiple EGL surfaces once the engine supports having multiple views. Some notable changes: 1. EGL surface resizing logic is now entirely in `FlutterWindowsView`. Previously some resizing logic was in the `egl::Manager`, however, the view has to handle resizing failures so this unifies the logic in one place. 2. The `OnEmptyFrameGenerated` and `OnFrameGenerated` now return `false` (aka "don't present") if the surface is invalid. This simplifies the compositor as it no longer needs to check for invalid surfaces 3. This introduces a `ViewModifier` testing helper to allow overriding a view's surface. This isn't strictly necessary, tests can setup a surface by mocking several EGL methods and calling `FlutterWindowsView::CreateRenderSurface()`. However, this is verbose & heavily tied to implementation details. The `ViewModifier` avoids this boilerplate. 4. `CompositorOpenGL`'s initialization now makes the render context current without any render surfaces. Previously it also made the view's surface current, which was unnecessary. Part of flutter/flutter#137267 [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
1 parent 7d111c7 commit a190775

13 files changed

Lines changed: 329 additions & 237 deletions

shell/platform/windows/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ executable("flutter_windows_unittests") {
230230
"testing/test_keyboard.cc",
231231
"testing/test_keyboard.h",
232232
"testing/test_keyboard_unittests.cc",
233+
"testing/view_modifier.h",
233234
"testing/windows_test.cc",
234235
"testing/windows_test.h",
235236
"testing/windows_test_config_builder.cc",

shell/platform/windows/compositor_opengl.cc

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,8 @@ bool CompositorOpenGL::CollectBackingStore(const FlutterBackingStore* store) {
9393

9494
bool CompositorOpenGL::Present(const FlutterLayer** layers,
9595
size_t layers_count) {
96-
if (!engine_->view()) {
97-
return false;
98-
}
99-
100-
if (!engine_->egl_manager()->surface() ||
101-
!engine_->egl_manager()->surface()->IsValid()) {
96+
FlutterWindowsView* view = engine_->view();
97+
if (!view) {
10298
return false;
10399
}
104100

@@ -111,7 +107,7 @@ bool CompositorOpenGL::Present(const FlutterLayer** layers,
111107
return false;
112108
}
113109

114-
return ClearSurface();
110+
return Clear(view);
115111
}
116112

117113
// TODO: Support compositing layers and platform views.
@@ -129,11 +125,16 @@ bool CompositorOpenGL::Present(const FlutterLayer** layers,
129125

130126
// Check if this frame can be presented. This resizes the surface if a resize
131127
// is pending and |width| and |height| match the target size.
132-
if (!engine_->view()->OnFrameGenerated(width, height)) {
128+
if (!view->OnFrameGenerated(width, height)) {
133129
return false;
134130
}
135131

136-
if (!engine_->egl_manager()->surface()->MakeCurrent()) {
132+
// |OnFrameGenerated| should return false if the surface isn't valid.
133+
FML_DCHECK(view->surface() != nullptr);
134+
FML_DCHECK(view->surface()->IsValid());
135+
136+
egl::WindowSurface* surface = view->surface();
137+
if (!surface->MakeCurrent()) {
137138
return false;
138139
}
139140

@@ -159,11 +160,11 @@ bool CompositorOpenGL::Present(const FlutterLayer** layers,
159160
GL_NEAREST // filter
160161
);
161162

162-
if (!engine_->egl_manager()->surface()->SwapBuffers()) {
163+
if (!surface->SwapBuffers()) {
163164
return false;
164165
}
165166

166-
engine_->view()->OnFramePresented();
167+
view->OnFramePresented();
167168
return true;
168169
}
169170

@@ -175,12 +176,7 @@ bool CompositorOpenGL::Initialize() {
175176
return false;
176177
}
177178

178-
egl::Surface* surface = manager->surface();
179-
if (!surface || !surface->IsValid()) {
180-
return false;
181-
}
182-
183-
if (!surface->MakeCurrent()) {
179+
if (!manager->render_context()->MakeCurrent()) {
184180
return false;
185181
}
186182

@@ -195,24 +191,31 @@ bool CompositorOpenGL::Initialize() {
195191
return true;
196192
}
197193

198-
bool CompositorOpenGL::ClearSurface() {
194+
bool CompositorOpenGL::Clear(FlutterWindowsView* view) {
199195
FML_DCHECK(is_initialized_);
200196

201-
// Resize the surface if needed.
202-
engine_->view()->OnEmptyFrameGenerated();
197+
// Check if this frame can be presented. This resizes the surface if needed.
198+
if (!view->OnEmptyFrameGenerated()) {
199+
return false;
200+
}
201+
202+
// |OnEmptyFrameGenerated| should return false if the surface isn't valid.
203+
FML_DCHECK(view->surface() != nullptr);
204+
FML_DCHECK(view->surface()->IsValid());
203205

204-
if (!engine_->egl_manager()->surface()->MakeCurrent()) {
206+
egl::WindowSurface* surface = view->surface();
207+
if (!surface->MakeCurrent()) {
205208
return false;
206209
}
207210

208211
gl_->ClearColor(0.0f, 0.0f, 0.0f, 0.0f);
209212
gl_->Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
210213

211-
if (!engine_->egl_manager()->surface()->SwapBuffers()) {
214+
if (!surface->SwapBuffers()) {
212215
return false;
213216
}
214217

215-
engine_->view()->OnFramePresented();
218+
view->OnFramePresented();
216219
return true;
217220
}
218221

shell/platform/windows/compositor_opengl.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ class CompositorOpenGL : public Compositor {
5353
bool Initialize();
5454

5555
// Clear the view's surface and removes any previously presented layers.
56-
bool ClearSurface();
56+
bool Clear(FlutterWindowsView* view);
5757
};
5858

5959
} // namespace flutter

shell/platform/windows/compositor_opengl_unittests.cc

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@
99
#include "flutter/shell/platform/windows/compositor_opengl.h"
1010
#include "flutter/shell/platform/windows/egl/manager.h"
1111
#include "flutter/shell/platform/windows/flutter_windows_view.h"
12+
#include "flutter/shell/platform/windows/testing/egl/mock_context.h"
1213
#include "flutter/shell/platform/windows/testing/egl/mock_manager.h"
1314
#include "flutter/shell/platform/windows/testing/egl/mock_window_surface.h"
1415
#include "flutter/shell/platform/windows/testing/engine_modifier.h"
1516
#include "flutter/shell/platform/windows/testing/flutter_windows_engine_builder.h"
1617
#include "flutter/shell/platform/windows/testing/mock_window_binding_handler.h"
18+
#include "flutter/shell/platform/windows/testing/view_modifier.h"
1719
#include "flutter/shell/platform/windows/testing/windows_test.h"
1820
#include "gmock/gmock.h"
1921
#include "gtest/gtest.h"
@@ -22,6 +24,7 @@ namespace flutter {
2224
namespace testing {
2325

2426
namespace {
27+
using ::testing::AnyNumber;
2528
using ::testing::Return;
2629

2730
const unsigned char* MockGetString(GLenum name) {
@@ -66,19 +69,17 @@ class CompositorOpenGLTest : public WindowsTest {
6669
protected:
6770
FlutterWindowsEngine* engine() { return engine_.get(); }
6871
egl::MockManager* egl_manager() { return egl_manager_; }
69-
egl::MockWindowSurface* surface() { return surface_.get(); }
72+
egl::MockContext* render_context() { return render_context_.get(); }
73+
egl::MockWindowSurface* surface() { return surface_; }
7074

71-
void UseHeadlessEngine(bool add_surface = true) {
75+
void UseHeadlessEngine() {
7276
auto egl_manager = std::make_unique<egl::MockManager>();
77+
render_context_ = std::make_unique<egl::MockContext>();
7378
egl_manager_ = egl_manager.get();
7479

75-
if (add_surface) {
76-
surface_ = std::make_unique<egl::MockWindowSurface>();
77-
EXPECT_CALL(*egl_manager_, surface)
78-
.WillRepeatedly(Return(surface_.get()));
79-
} else {
80-
EXPECT_CALL(*egl_manager_, surface).WillRepeatedly(Return(nullptr));
81-
}
80+
EXPECT_CALL(*egl_manager_, render_context)
81+
.Times(AnyNumber())
82+
.WillRepeatedly(Return(render_context_.get()));
8283

8384
FlutterWindowsEngineBuilder builder{GetContext()};
8485

@@ -88,21 +89,32 @@ class CompositorOpenGLTest : public WindowsTest {
8889
}
8990

9091
void UseEngineWithView(bool add_surface = true) {
91-
UseHeadlessEngine(add_surface);
92+
UseHeadlessEngine();
9293

9394
auto window = std::make_unique<MockWindowBindingHandler>();
9495
EXPECT_CALL(*window.get(), SetView).Times(1);
9596
EXPECT_CALL(*window.get(), GetWindowHandle).WillRepeatedly(Return(nullptr));
9697

9798
view_ = std::make_unique<FlutterWindowsView>(std::move(window));
9899

100+
if (add_surface) {
101+
auto surface = std::make_unique<egl::MockWindowSurface>();
102+
surface_ = surface.get();
103+
104+
EXPECT_CALL(*surface_, Destroy).Times(AnyNumber());
105+
106+
ViewModifier modifier{view_.get()};
107+
modifier.SetSurface(std::move(surface));
108+
}
109+
99110
engine_->SetView(view_.get());
100111
}
101112

102113
private:
103114
std::unique_ptr<FlutterWindowsEngine> engine_;
104115
std::unique_ptr<FlutterWindowsView> view_;
105-
std::unique_ptr<egl::MockWindowSurface> surface_;
116+
std::unique_ptr<egl::MockContext> render_context_;
117+
egl::MockWindowSurface* surface_;
106118
egl::MockManager* egl_manager_;
107119

108120
FML_DISALLOW_COPY_AND_ASSIGN(CompositorOpenGLTest);
@@ -118,8 +130,7 @@ TEST_F(CompositorOpenGLTest, CreateBackingStore) {
118130
FlutterBackingStoreConfig config = {};
119131
FlutterBackingStore backing_store = {};
120132

121-
EXPECT_CALL(*surface(), IsValid).WillOnce(Return(true));
122-
EXPECT_CALL(*surface(), MakeCurrent).WillOnce(Return(true));
133+
EXPECT_CALL(*render_context(), MakeCurrent).WillOnce(Return(true));
123134
ASSERT_TRUE(compositor.CreateBackingStore(config, &backing_store));
124135
ASSERT_TRUE(compositor.CollectBackingStore(&backing_store));
125136
}
@@ -132,8 +143,7 @@ TEST_F(CompositorOpenGLTest, InitializationFailure) {
132143
FlutterBackingStoreConfig config = {};
133144
FlutterBackingStore backing_store = {};
134145

135-
EXPECT_CALL(*surface(), IsValid).WillOnce(Return(true));
136-
EXPECT_CALL(*surface(), MakeCurrent).WillOnce(Return(false));
146+
EXPECT_CALL(*render_context(), MakeCurrent).WillOnce(Return(false));
137147
EXPECT_FALSE(compositor.CreateBackingStore(config, &backing_store));
138148
}
139149

@@ -145,15 +155,15 @@ TEST_F(CompositorOpenGLTest, Present) {
145155
FlutterBackingStoreConfig config = {};
146156
FlutterBackingStore backing_store = {};
147157

148-
EXPECT_CALL(*surface(), IsValid).WillRepeatedly(Return(true));
149-
EXPECT_CALL(*surface(), MakeCurrent).WillOnce(Return(true));
158+
EXPECT_CALL(*render_context(), MakeCurrent).WillOnce(Return(true));
150159
ASSERT_TRUE(compositor.CreateBackingStore(config, &backing_store));
151160

152161
FlutterLayer layer = {};
153162
layer.type = kFlutterLayerContentTypeBackingStore;
154163
layer.backing_store = &backing_store;
155164
const FlutterLayer* layer_ptr = &layer;
156165

166+
EXPECT_CALL(*surface(), IsValid).WillRepeatedly(Return(true));
157167
EXPECT_CALL(*surface(), MakeCurrent).WillOnce(Return(true));
158168
EXPECT_CALL(*surface(), SwapBuffers).WillOnce(Return(true));
159169
EXPECT_TRUE(compositor.Present(&layer_ptr, 1));
@@ -168,8 +178,9 @@ TEST_F(CompositorOpenGLTest, PresentEmpty) {
168178

169179
// The context will be bound twice: first to initialize the compositor, second
170180
// to clear the surface.
181+
EXPECT_CALL(*render_context(), MakeCurrent).WillOnce(Return(true));
171182
EXPECT_CALL(*surface(), IsValid).WillRepeatedly(Return(true));
172-
EXPECT_CALL(*surface(), MakeCurrent).Times(2).WillRepeatedly(Return(true));
183+
EXPECT_CALL(*surface(), MakeCurrent).WillOnce(Return(true));
173184
EXPECT_CALL(*surface(), SwapBuffers).WillOnce(Return(true));
174185
EXPECT_TRUE(compositor.Present(nullptr, 0));
175186
}
@@ -182,8 +193,7 @@ TEST_F(CompositorOpenGLTest, HeadlessPresentIgnored) {
182193
FlutterBackingStoreConfig config = {};
183194
FlutterBackingStore backing_store = {};
184195

185-
EXPECT_CALL(*surface(), IsValid).WillOnce(Return(true));
186-
EXPECT_CALL(*surface(), MakeCurrent).WillOnce(Return(true));
196+
EXPECT_CALL(*render_context(), MakeCurrent).WillOnce(Return(true));
187197
ASSERT_TRUE(compositor.CreateBackingStore(config, &backing_store));
188198

189199
FlutterLayer layer = {};
@@ -204,11 +214,12 @@ TEST_F(CompositorOpenGLTest, NoSurfaceIgnored) {
204214
FlutterBackingStoreConfig config = {};
205215
FlutterBackingStore backing_store = {};
206216

207-
ASSERT_FALSE(compositor.CreateBackingStore(config, &backing_store));
217+
EXPECT_CALL(*render_context(), MakeCurrent).WillOnce(Return(true));
218+
ASSERT_TRUE(compositor.CreateBackingStore(config, &backing_store));
208219

209220
FlutterLayer layer = {};
210221
layer.type = kFlutterLayerContentTypeBackingStore;
211-
layer.backing_store = nullptr;
222+
layer.backing_store = &backing_store;
212223
const FlutterLayer* layer_ptr = &layer;
213224

214225
EXPECT_FALSE(compositor.Present(&layer_ptr, 1));

shell/platform/windows/egl/manager.cc

Lines changed: 7 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -263,11 +263,11 @@ bool Manager::IsValid() const {
263263
return is_valid_;
264264
}
265265

266-
bool Manager::CreateWindowSurface(HWND hwnd, size_t width, size_t height) {
267-
FML_DCHECK(surface_ == nullptr || !surface_->IsValid());
268-
266+
std::unique_ptr<WindowSurface> Manager::CreateWindowSurface(HWND hwnd,
267+
size_t width,
268+
size_t height) {
269269
if (!hwnd || !is_valid_) {
270-
return false;
270+
return nullptr;
271271
}
272272

273273
// 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) {
286286
surface_attributes);
287287
if (surface == EGL_NO_SURFACE) {
288288
LogEGLError("Surface creation failed.");
289-
return false;
289+
return nullptr;
290290
}
291291

292-
surface_ = std::make_unique<WindowSurface>(
293-
display_, render_context_->GetHandle(), surface, width, height);
294-
return true;
295-
}
296-
297-
void Manager::ResizeWindowSurface(HWND hwnd, size_t width, size_t height) {
298-
FML_CHECK(surface_ != nullptr);
299-
300-
auto const existing_width = surface_->width();
301-
auto const existing_height = surface_->height();
302-
auto const existing_vsync = surface_->vsync_enabled();
303-
304-
if (width != existing_width || height != existing_height) {
305-
// TODO: Destroying the surface and re-creating it is expensive.
306-
// Ideally this would use ANGLE's automatic surface sizing instead.
307-
// See: https://github.com/flutter/flutter/issues/79427
308-
if (!surface_->Destroy()) {
309-
FML_LOG(ERROR) << "Manager::ResizeSurface failed to destroy surface";
310-
return;
311-
}
312-
313-
if (!CreateWindowSurface(hwnd, width, height)) {
314-
FML_LOG(ERROR) << "Manager::ResizeSurface failed to create surface";
315-
return;
316-
}
317-
318-
if (!surface_->MakeCurrent() ||
319-
!surface_->SetVSyncEnabled(existing_vsync)) {
320-
// Surfaces block until the v-blank by default.
321-
// Failing to update the vsync might result in unnecessary blocking.
322-
// This regresses performance but not correctness.
323-
FML_LOG(ERROR) << "Manager::ResizeSurface failed to set vsync";
324-
}
325-
}
292+
return std::make_unique<WindowSurface>(display_, render_context_->GetHandle(),
293+
surface, width, height);
326294
}
327295

328296
bool Manager::HasContextCurrent() {
@@ -355,9 +323,5 @@ Context* Manager::resource_context() const {
355323
return resource_context_.get();
356324
}
357325

358-
WindowSurface* Manager::surface() const {
359-
return surface_.get();
360-
}
361-
362326
} // namespace egl
363327
} // namespace flutter

0 commit comments

Comments
 (0)