[CP-stable][Windows] Use ANGLE blit extension on GLES 2.0 (#170924)

This pull request is created by [automatic cherry pick workflow](https://github.com/flutter/flutter/blob/main/docs/releases/Flutter-Cherrypick-Process.md#automatically-creates-a-cherry-pick-request)

### Issue Link:
https://github.com/flutter/flutter/issues/169178

### Changelog Description:

Fix Flutter Windows on devices that only support OpenGL ES 2, like computers with Intel graphics cards.

### Impact Description:

Flutter Windows 3.29.0 and higher crashed immediately on startup on computers using Intel HD Graphics graphics cards.

### Workaround:
None, other than downgrading your Flutter SDK to 3.27.4 or lower.

### Risk:
What is the risk level of this cherry-pick?

### Test Coverage:
Are you confident that your fix is well-tested by automated tests?

### Validation Steps:

This bug does not reproduce on Flutter's infrastructure. The fix was manually tested on several Windows devices, including affected devices that use Intel HD Graphics graphics cards.
This commit is contained in:
flutteractionsbot 2025-06-24 07:39:37 -07:00 committed by GitHub
parent f3eca332f3
commit dd93de6fb1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 119 additions and 13 deletions

View File

@ -31,6 +31,7 @@ INTERNAL NOTE
### [3.32.5](https://github.com/flutter/flutter/releases/tag/3.32.5)
- [flutter/170924](https://github.com/flutter/flutter/pull/170924) - Fix Flutter Windows on devices that only support OpenGL ES 2, like computers with Intel graphics cards.
- [flutter/170880](https://github.com/flutter/flutter/pull/170880) - Fixes unhandled exception on application shutdown in the debug adapter used by IDEs.
### [3.32.4](https://github.com/flutter/flutter/releases/tag/3.32.4)

View File

@ -141,6 +141,10 @@ ProcTableGLES::ProcTableGLES( // NOLINT(google-readability-function-size)
DiscardFramebufferEXT.Reset();
}
if (!description_->HasExtension("GL_ANGLE_framebuffer_blit")) {
BlitFramebufferANGLE.Reset();
}
capabilities_ = std::make_shared<CapabilitiesGLES>(*this);
is_valid_ = true;

View File

@ -266,7 +266,8 @@ void(glDepthRange)(GLdouble n, GLdouble f);
PROC(GetQueryObjectui64vEXT); \
PROC(BeginQueryEXT); \
PROC(EndQueryEXT); \
PROC(GetQueryObjectuivEXT);
PROC(GetQueryObjectuivEXT); \
PROC(BlitFramebufferANGLE);
enum class DebugResourceType {
kTexture,

View File

@ -20,6 +20,20 @@ struct FramebufferBackingStore {
uint32_t texture_id;
};
typedef const impeller::GLProc<decltype(glBlitFramebuffer)> BlitFramebufferProc;
const BlitFramebufferProc& GetBlitFramebufferProc(
const impeller::ProcTableGLES& gl) {
if (gl.BlitFramebuffer.IsAvailable()) {
return gl.BlitFramebuffer;
} else if (gl.BlitFramebufferANGLE.IsAvailable()) {
return gl.BlitFramebufferANGLE;
}
// CompositorOpenGL::Initialize verifies that a blit procedure is available.
FML_UNREACHABLE();
}
} // namespace
CompositorOpenGL::CompositorOpenGL(FlutterWindowsEngine* engine,
@ -160,16 +174,17 @@ bool CompositorOpenGL::Present(FlutterWindowsView* view,
gl_->BindFramebuffer(GL_READ_FRAMEBUFFER, source_id);
gl_->BindFramebuffer(GL_DRAW_FRAMEBUFFER, kWindowFrameBufferId);
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
auto blitFramebuffer = GetBlitFramebufferProc(*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
);
if (!surface->SwapBuffers()) {
@ -206,6 +221,12 @@ bool CompositorOpenGL::Initialize() {
format_.general_format = GL_RGBA;
}
if (!gl_->BlitFramebuffer.IsAvailable() &&
!gl_->BlitFramebufferANGLE.IsAvailable()) {
FML_LOG(ERROR) << "Unable to find OpenGL blit framebuffer procedure.";
return false;
}
is_initialized_ = true;
return true;
}

View File

@ -27,6 +27,14 @@ namespace {
using ::testing::AnyNumber;
using ::testing::Return;
void MockGetIntegerv(GLenum name, int* value) {
if (name == GL_NUM_EXTENSIONS) {
*value = 1;
} else {
*value = 0;
}
}
const unsigned char* MockGetString(GLenum name) {
switch (name) {
case GL_VERSION:
@ -37,8 +45,12 @@ const unsigned char* MockGetString(GLenum name) {
}
}
void MockGetIntegerv(GLenum name, int* value) {
*value = 0;
const unsigned char* MockGetStringi(GLenum name, int index) {
if (name == GL_EXTENSIONS) {
return reinterpret_cast<const unsigned char*>("GL_ANGLE_framebuffer_blit");
} else {
return reinterpret_cast<const unsigned char*>("");
}
}
GLenum MockGetError() {
@ -52,6 +64,8 @@ const impeller::ProcTableGLES::Resolver kMockResolver = [](const char* name) {
if (function_name == "glGetString") {
return reinterpret_cast<void*>(&MockGetString);
} else if (function_name == "glGetStringi") {
return reinterpret_cast<void*>(&MockGetStringi);
} else if (function_name == "glGetIntegerv") {
return reinterpret_cast<void*>(&MockGetIntegerv);
} else if (function_name == "glGetError") {
@ -163,6 +177,30 @@ TEST_F(CompositorOpenGLTest, InitializationFailure) {
EXPECT_FALSE(compositor.CreateBackingStore(config, &backing_store));
}
TEST_F(CompositorOpenGLTest, InitializationRequiresBlit) {
UseHeadlessEngine();
const impeller::ProcTableGLES::Resolver resolver = [](const char* name) {
std::string function_name{name};
if (function_name == "glBlitFramebuffer" ||
function_name == "glBlitFramebufferANGLE") {
return (void*)nullptr;
}
return kMockResolver(name);
};
auto compositor =
CompositorOpenGL{engine(), resolver, /*enable_impeller=*/false};
FlutterBackingStoreConfig config = {};
FlutterBackingStore backing_store = {};
EXPECT_CALL(*render_context(), MakeCurrent).WillOnce(Return(true));
ASSERT_FALSE(compositor.CreateBackingStore(config, &backing_store));
}
TEST_F(CompositorOpenGLTest, Present) {
UseEngineWithView();
@ -225,5 +263,46 @@ TEST_F(CompositorOpenGLTest, NoSurfaceIgnored) {
ASSERT_TRUE(compositor.CollectBackingStore(&backing_store));
}
TEST_F(CompositorOpenGLTest, PresentUsingANGLEBlitExtension) {
UseEngineWithView();
bool resolved_ANGLE_blit = false;
const impeller::ProcTableGLES::Resolver resolver =
[&resolved_ANGLE_blit](const char* name) {
std::string function_name{name};
if (function_name == "glBlitFramebuffer") {
return (void*)nullptr;
} else if (function_name == "glBlitFramebufferANGLE") {
resolved_ANGLE_blit = true;
return reinterpret_cast<void*>(&DoNothing);
}
return kMockResolver(name);
};
auto compositor =
CompositorOpenGL{engine(), resolver, /*enable_impeller=*/false};
FlutterBackingStoreConfig config = {};
FlutterBackingStore 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 = &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(view(), &layer_ptr, 1));
EXPECT_TRUE(resolved_ANGLE_blit);
ASSERT_TRUE(compositor.CollectBackingStore(&backing_store));
}
} // namespace testing
} // namespace flutter