Skip to content

Commit

Permalink
[Windows] Make the view own its EGL surface
Browse files Browse the repository at this point in the history
  • Loading branch information
loic-sharma committed Feb 7, 2024
1 parent 9576d97 commit 1c6acf3
Show file tree
Hide file tree
Showing 13 changed files with 329 additions and 237 deletions.
1 change: 1 addition & 0 deletions shell/platform/windows/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
49 changes: 26 additions & 23 deletions shell/platform/windows/compositor_opengl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

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

return ClearSurface();
return Clear(view);
}

// TODO: Support compositing layers and platform views.
Expand All @@ -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;
}

Expand All @@ -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;
}

Expand All @@ -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;
}

Expand All @@ -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;
}

Expand Down
2 changes: 1 addition & 1 deletion shell/platform/windows/compositor_opengl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
55 changes: 33 additions & 22 deletions shell/platform/windows/compositor_opengl_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -22,6 +24,7 @@ namespace flutter {
namespace testing {

namespace {
using ::testing::AnyNumber;
using ::testing::Return;

const unsigned char* MockGetString(GLenum name) {
Expand Down Expand Up @@ -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<egl::MockManager>();
render_context_ = std::make_unique<egl::MockContext>();
egl_manager_ = egl_manager.get();

if (add_surface) {
surface_ = std::make_unique<egl::MockWindowSurface>();
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()};

Expand All @@ -88,21 +89,32 @@ class CompositorOpenGLTest : public WindowsTest {
}

void UseEngineWithView(bool add_surface = true) {
UseHeadlessEngine(add_surface);
UseHeadlessEngine();

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

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

if (add_surface) {
auto surface = std::make_unique<egl::MockWindowSurface>();
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<FlutterWindowsEngine> engine_;
std::unique_ptr<FlutterWindowsView> view_;
std::unique_ptr<egl::MockWindowSurface> surface_;
std::unique_ptr<egl::MockContext> render_context_;
egl::MockWindowSurface* surface_;
egl::MockManager* egl_manager_;

FML_DISALLOW_COPY_AND_ASSIGN(CompositorOpenGLTest);
Expand All @@ -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));
}
Expand All @@ -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));
}

Expand All @@ -145,15 +155,15 @@ 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 = {};
layer.type = kFlutterLayerContentTypeBackingStore;
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));
Expand All @@ -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));
}
Expand All @@ -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 = {};
Expand All @@ -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));
Expand Down
50 changes: 7 additions & 43 deletions shell/platform/windows/egl/manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<WindowSurface> 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.
Expand All @@ -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<WindowSurface>(
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<WindowSurface>(display_, render_context_->GetHandle(),
surface, width, height);
}

bool Manager::HasContextCurrent() {
Expand Down Expand Up @@ -355,9 +323,5 @@ Context* Manager::resource_context() const {
return resource_context_.get();
}

WindowSurface* Manager::surface() const {
return surface_.get();
}

} // namespace egl
} // namespace flutter
Loading

0 comments on commit 1c6acf3

Please sign in to comment.