Repository navigation
[Windows] Use an unsized texture format for backing stores on OpenGL ES 2 - #192917
auto-submit[bot] merged 2 commits into
Conversation
There was a problem hiding this comment.
Code Review
This pull request updates the Windows OpenGL compositor to support OpenGL ES 2.0 by introducing an internal_format field that dynamically selects between sized and unsized formats based on the detected GLES version. Unit tests are also added to verify this behavior under GLES2 and GLES3 contexts. Feedback suggests adding a defensive null check for the GLES description to prevent potential crashes, and refactoring a static std::vector in the unit tests to comply with the Google C++ Style Guide's rules on trivial destructibility.
| const impeller::DescriptionGLES* description = gl_->GetDescription(); | ||
| bool is_gles2 = | ||
| description->IsES() && description->GetGlVersion().major_version < 3; | ||
| format_.internal_format = | ||
| is_gles2 ? format_.general_format : format_.sized_format; |
There was a problem hiding this comment.
The gl_->GetDescription() call may return nullptr if the OpenGL context or driver description is not available or failed to initialize. Dereferencing it directly without a null check can lead to a crash. Please add a defensive null check before accessing its members.
| const impeller::DescriptionGLES* description = gl_->GetDescription(); | |
| bool is_gles2 = | |
| description->IsES() && description->GetGlVersion().major_version < 3; | |
| format_.internal_format = | |
| is_gles2 ? format_.general_format : format_.sized_format; | |
| const impeller::DescriptionGLES* description = gl_->GetDescription(); | |
| bool is_gles2 = description && | |
| description->IsES() && | |
| description->GetGlVersion().major_version < 3; | |
| format_.internal_format = | |
| is_gles2 ? format_.general_format : format_.sized_format; |
There was a problem hiding this comment.
GetDescription() cannot return null at this point, so I'd rather not add the check.
Initialize bails out when the proc table is invalid, a few lines above:
flutter/engine/src/flutter/shell/platform/windows/compositor_opengl.cc
Lines 270 to 274 in aced6c4
gl_ = std::make_unique<impeller::ProcTableGLES>(resolver_);
if (!gl_->IsValid()) {
gl_.reset();
return false;
}ProcTableGLES creates its description first and only becomes valid at the very end of the constructor, after the description passed its own check:
flutter/engine/src/flutter/impeller/renderer/backend/gles/proc_table_gles.cc
Lines 118 to 122 in aced6c4
description_ = std::make_unique<DescriptionGLES>(*this);
if (!description_->IsValid()) {
return;
}
...
is_valid_ = true;The existing BGRA check right above the new code already dereferences it the same way:
if (gl_->GetDescription()->HasExtension("GL_EXT_texture_format_BGRA8888")) {| std::vector<GLint>& TexImage2DInternalFormats() { | ||
| static std::vector<GLint> internal_formats; | ||
| return internal_formats; | ||
| } |
There was a problem hiding this comment.
According to the Google C++ Style Guide, static variables must be trivially destructible. std::vector has a non-trivial destructor, which violates this rule. To comply with the style guide, allocate the vector on the heap and store a pointer to it.
| std::vector<GLint>& TexImage2DInternalFormats() { | |
| static std::vector<GLint> internal_formats; | |
| return internal_formats; | |
| } | |
| std::vector<GLint>& TexImage2DInternalFormats() { | |
| static std::vector<GLint>* internal_formats = new std::vector<GLint>(); | |
| return *internal_formats; | |
| } |
References
- According to the Google C++ Style Guide, static variables must be trivially destructible. Class templates like
std::vectorhave non-trivial destructors and are not allowed as static variables. (link)
There was a problem hiding this comment.
This follows the pattern already used by the Windows embedder tests, where a captureless mock function records into a function-local or file-level static std::vector:
static std::vector<int> key_event_logs;
static std::vector<UINT> received_messages;The mock has to be a plain function pointer for the ProcTableGLES resolver, so it cannot capture state, and the vector is cleared at the start of each test. I kept it consistent with the neighbouring tests
gaaclarke
left a comment
There was a problem hiding this comment.
Looks good to me, thanks for the quick PR. Switching context's and setting up the environment to develop this PR would have taken some time. I just have some cleanup notes.
|
|
||
| TEST_F(CompositorOpenGLTest, CreateBackingStoreGLES3Sized) { | ||
| UseHeadlessEngine(); | ||
| TexImage2DInternalFormats().clear(); |
There was a problem hiding this comment.
Let's put this in TearDown() to avoid leaks between tests
| return internal_formats; | ||
| } | ||
|
|
||
| void RecordTexImage2D(GLenum, |
There was a problem hiding this comment.
We have a mechanism to statically assert that this signature is correct, it's probably easiest just to copy it into this file. We should unify these mocks at some point.
| }; | ||
|
|
||
| // ANGLE returns these strings for an OpenGL ES 2.0 context | ||
| const unsigned char* MockGetStringGLES2(GLenum name) { |
There was a problem hiding this comment.
(same comment about static_assert's for these functions)
done |
|
@b-luk please be a second reviewer for this. |
|
autosubmit label was removed for flutter/flutter/192917, because - The status or check suite Dashboard Checks has failed. Please fix the issues identified (or deflake) before re-applying this label. |
|
Some of the CI failures seem apropos: I'll hit rerun to make sure we aren't experiencing a flake. @krll-kov let me know if you want me to take a stab at diagnosing it deeper. There isn't much in the logs to go on. |
|
, b> Some of the CI failures seem apropos:
Pr only touches windows files in shell/platform/windows, i'm not sure how that could affect android, but i'll try to execute that test locally |
Ah yea, I saw OpenGLES and Android and thought we triggered something. You're right. I'll keep poking at CI to try to get this past. |
just checked that shell/platform/windows/BUILD.gn starts with assert(is_win), so none of the changed files are compiled into the Android embedder at all. Looks like a capture flake or something that is not related to this pr |
|
Hi! Thanks for this fix, and for #193306. Could both be considered for a cherry-pick to the 3.47 stable branch? Impact: We ship a Windows point-of-sale app (MSIX, Microsoft Store) built with Flutter 3.47.1. Several of our customers run it on older hardware, and on those machines the window stays completely black. The app is running fine underneath: network calls succeed, navigation happens, logs are written. Nothing is drawn, so the app is unusable for these customers. Affected machine:
Intel publishes no newer driver for this GPU on Windows 10, so the customer cannot fix it on their side. Why both PRs: this machine is at feature level 10_0, so it needs this PR to get past the black window (#191978), and #193306 so that Impeller does not erase content drawn before a Current state: I checked 3.47.6 (latest stable) and 3.49.0-0.2.pre (latest beta), and neither contains this PR. Our only option today is to pin our Windows builds to 3.44.x, which also holds back the rest of the app. This PR is small and only touches the Windows embedder, so the risk to other platforms looks low. Happy to test a candidate build on the affected hardware if that helps. Thanks! |
…level 10_0 (flutter#194114) Adds a parameter (only used in tests) to `egl::Manager` to cap ANGLE's D3D11 feature level. Adds `compositor_opengl_angle_unittests.cc`, based on [`compositor_opengl_unittests.cc`](https://github.com/flutter/flutter/blob/97db29fcaa2aa757c8d86f06187b5268bf4eed4a/engine/src/flutter/shell/platform/windows/compositor_opengl_unittests.cc). Unlike the existing tests, the new tests exercise the compositor with real ANGLE instead of mocks. The new tests exercise the compositor's backing store creation and presenting. The tests run both at uncapped feature level and capped to 10_0. This emulates the case where flutter#191978 resulted in a black window. Verified locally that when flutter#192917 is reverted, the new tests fail when running at feature level 10_0 and pass when running without a feature level cap. Part of flutter#192899: - This PR exercises the OpenGL compositor and swapchain presenting codepath on 10_0, verifying that the backing store framebuffer is valid (GL_FRAMEBUFFER_COMPLETE) with no GL errors. This catches issues that occur at the compositor level, like flutter#191978. - This would not catch issues where the rendering behavior of Impeller is broken due to taking different code paths on 10_0 (e.g. flutter#192918 resulting in different BackdropFilter behavior because offscreen MSAA is unavailable with 10_0). - I intend to follow up with a PR that extends our existing [`engine_integration_golden_test.dart`](https://github.com/flutter/flutter/blob/38785ef8b19e2a1ee1303167074ec98f95f7cb5b/dev/integration_tests/engine_integration_golden_test/integration_test/engine_integration_golden_test.dart) devicelab golden test, which currently [runs on Windows](https://github.com/flutter/flutter/blob/38785ef8b19e2a1ee1303167074ec98f95f7cb5b/dev/devicelab/lib/tasks/integration_tests.dart#L242-L248), to also run with feature level 10_0. These golden tests would catch issues where our Impeller rendering is broken. Goldens do not catch compositor level issues because the golden functionality reads its images from Impeller, skipping the compositor portion of the rendering pipeline. - This PR, together with the anticipated follow up PR, will give us basic smoke testing coverage on 10_0 of the rendering pipeline from Flutter app code, through Impeller rendering, to presenting on the swapchain. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [AI contribution guidelines] and understand my responsibilities, or I am not using AI tools. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant in-code documentation (doc comments with `///`). - [x] If this PR introduces a new feature or capability, I created and linked a website documentation issue or PR in [flutter/website] (or verified none is needed). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. If this change needs to override an active code freeze, provide a comment explaining why. The code freeze workflow can be overridden by code reviewers. See pinned issues for any active code freezes with guidance. **Note**: The Flutter team is currently trialing the use of [Gemini Code Assist for GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code). Comments from the `gemini-code-assist` bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [AI contribution guidelines]: https://github.com/flutter/flutter/blob/main/CONTRIBUTING.ai.md [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [flutter/website]: https://github.com/flutter/website [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
|
Confirming that #192917 ("[Windows] Use an unsized texture format for backing stores on OpenGL ES 2") fixes this for us. We built our Windows app with Before this, 3.47.x opened black on the affected machine (Intel HD Graphics Thanks for the fix. Is there a plan to cherry-pick it to a 3.47.x stable hotfix? Until then we're shipping from beta. |
…level 10_0 (flutter#194114) Adds a parameter (only used in tests) to `egl::Manager` to cap ANGLE's D3D11 feature level. Adds `compositor_opengl_angle_unittests.cc`, based on [`compositor_opengl_unittests.cc`](https://github.com/flutter/flutter/blob/97db29fcaa2aa757c8d86f06187b5268bf4eed4a/engine/src/flutter/shell/platform/windows/compositor_opengl_unittests.cc). Unlike the existing tests, the new tests exercise the compositor with real ANGLE instead of mocks. The new tests exercise the compositor's backing store creation and presenting. The tests run both at uncapped feature level and capped to 10_0. This emulates the case where flutter#191978 resulted in a black window. Verified locally that when flutter#192917 is reverted, the new tests fail when running at feature level 10_0 and pass when running without a feature level cap. Part of flutter#192899: - This PR exercises the OpenGL compositor and swapchain presenting codepath on 10_0, verifying that the backing store framebuffer is valid (GL_FRAMEBUFFER_COMPLETE) with no GL errors. This catches issues that occur at the compositor level, like flutter#191978. - This would not catch issues where the rendering behavior of Impeller is broken due to taking different code paths on 10_0 (e.g. flutter#192918 resulting in different BackdropFilter behavior because offscreen MSAA is unavailable with 10_0). - I intend to follow up with a PR that extends our existing [`engine_integration_golden_test.dart`](https://github.com/flutter/flutter/blob/38785ef8b19e2a1ee1303167074ec98f95f7cb5b/dev/integration_tests/engine_integration_golden_test/integration_test/engine_integration_golden_test.dart) devicelab golden test, which currently [runs on Windows](https://github.com/flutter/flutter/blob/38785ef8b19e2a1ee1303167074ec98f95f7cb5b/dev/devicelab/lib/tasks/integration_tests.dart#L242-L248), to also run with feature level 10_0. These golden tests would catch issues where our Impeller rendering is broken. Goldens do not catch compositor level issues because the golden functionality reads its images from Impeller, skipping the compositor portion of the rendering pipeline. - This PR, together with the anticipated follow up PR, will give us basic smoke testing coverage on 10_0 of the rendering pipeline from Flutter app code, through Impeller rendering, to presenting on the swapchain. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [AI contribution guidelines] and understand my responsibilities, or I am not using AI tools. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant in-code documentation (doc comments with `///`). - [x] If this PR introduces a new feature or capability, I created and linked a website documentation issue or PR in [flutter/website] (or verified none is needed). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. If this change needs to override an active code freeze, provide a comment explaining why. The code freeze workflow can be overridden by code reviewers. See pinned issues for any active code freezes with guidance. **Note**: The Flutter team is currently trialing the use of [Gemini Code Assist for GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code). Comments from the `gemini-code-assist` bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [AI contribution guidelines]: https://github.com/flutter/flutter/blob/main/CONTRIBUTING.ai.md [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [flutter/website]: https://github.com/flutter/website [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
|
Dears, is it possible to cherrypick this fix to 3.47 versions? |
On GPUs limited to D3D11 feature level 10_0, ANGLE only creates an OpenGL ES 2.0 context.
CompositorOpenGL::CreateBackingStorepasses the sized format (GL_BGRA8_EXTorGL_RGBA8) as theinternalformatofglTexImage2D. ES 2.0 requiresinternalformatto equalformat, so the call fails withGL_INVALID_VALUE, the backing store framebuffer is incomplete and the window stays black with no error logged. Flutter 3.44.2 passed the unsized format there, the sized one came in with the MSAA backing store changes.This change picks the internal format once in
Initialize: the unsized general format on an ES 2 context, the sized format otherwise. The threeTexImage2Dcalls use it. The sized format is still reported to the engine throughFlutterOpenGLFramebuffer.target, and nothing changes on ES 3 contexts.Tests:
CreateBackingStoreGLES2Unsized: ES 2 with Skia,glTexImage2DgetsGL_BGRA_EXT, the engine still getsGL_BGRA8_EXT.CreateBackingStoreImpellerGLES2Unsized: ES 2 with Impeller,GL_BGRA_EXT.CreateBackingStoreGLES3Sized: ES 3 keepsGL_RGBA8.The two ES 2 tests fail without the change.
Manually verified with a dxcpl Feature level limit of 10_0, where the unpatched engine shows a black window: on 3.47.4 x64 a stock app renders with Skia and Impeller, and so does an app with gradients, shadows, a multiply blend and animation. An animated app runs at 120 FPS at 10_0 with both renderers, the same as the official engine without the limit. With Impeller, content drawn before a
BackdropFilteris still lost at 10_0; that is a separate Impeller bug. Onmain(arm64) the stock app renders with an ES 2 context, a complete framebuffer and no GL errors. Also verified in a production app on 3.47.4.Fixes #191978
Pre-launch Checklist
///).