mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[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:
parent
f3eca332f3
commit
dd93de6fb1
@ -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)
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user