Impeller's OpenGL ES backend now stores render-to-texture content top-down, the same as the Metal and Vulkan backends. A fragment shader or Flutter GPU app that sampled a render-target texture on OpenGL ES and added a vertical flip to compensate for the old bottom-up orientation now renders that content upside down.
- Who is affected
- Applications and packages relying on the previous behaviour or signature. Code that uses FragmentShader.
- Practical impact
- Behaviour or API differences that can break builds or change runtime behaviour.
- What action should I take?
- Follow the official migration guide below, update affected call sites, run `dart fix --apply` where a fix is available, then run your tests.
Before and after (from the official guide)
| 1 | void main() { | |
| 2 | vec2 uv = FlutterFragCoord().xy / u_size; | |
| 3 | − | #ifdef IMPELLER_TARGET_OPENGLES |
| 4 | − | uv.y = 1.0 - uv.y; |
| 5 | − | #endif |
| 6 | fragColor = texture(u_texture, uv); | |
| 7 | } |
| 1 | void main() { | |
| 2 | vec2 uv = FlutterFragCoord().xy / u_size; | |
| 3 | fragColor = texture(u_texture, uv); | |
| 4 | } |
Raw before / after
void main() {
vec2 uv = FlutterFragCoord().xy / u_size;
#ifdef IMPELLER_TARGET_OPENGLES
uv.y = 1.0 - uv.y;
#endif
fragColor = texture(u_texture, uv);
}void main() {
vec2 uv = FlutterFragCoord().xy / u_size;
fragColor = texture(u_texture, uv);
}Official migration guide (excerpt, © The Flutter Authors, CC BY 3.0)
Shaders written for OpenGL ES often flipped the
Y coordinate inside an IMPELLER_TARGET_OPENGLES block to make
render-target textures match the other backends.
Render-target textures are now top-down on every backend,
so that flip is no longer needed.
Code before migration:
void main() {
vec2 uv = FlutterFragCoord().xy / u_size;
#ifdef IMPELLER_TARGET_OPENGLES
uv.y = 1.0 - uv.y;
#endif
fragColor = texture(u_texture, uv);
}If the shader only needs to run on a Flutter release that includes this change, remove the flip.
Code after migration:
void main() {
vec2 uv = FlutterFragCoord().xy / u_size;
fragColor = texture(u_texture, uv);
}To keep one source working across releases, guard the flip with the
IMPELLER_OPENGLES_UNFLIPPED_DEPRECATED macro.
That macro is only defined on releases that store render-target textures
top-down, so the flip still runs on older releases that need it.
void main() {
vec2 uv = FlutterFragCoord().xy / u_size;
#if defined(IMPELLER_TARGET_OPENGLES) && !defined(IMPELLER_OPENGLES_UNFLIPPED_DEPRECATED)
uv.y = 1.0 - uv.y;
#endif
fragColor = texture(u_texture, uv);
}AI_PROVIDER (for example to openai-compatible with a local Ollama or LM Studio endpoint) to enable grounded explanations. Everything else on this page works without an AI provider.Listed in the official breaking-changes index (docs.flutter.dev/release/breaking-changes).
An official announcement or migration guide documents an intended behaviour or API change. A verified source change (git diff) shows code changed but does not by itself prove a public API is breaking. AI interpretations are labelled and never used to classify a change.
- Source commit
- b3045439b7c0
- Source last updated
- Oct 21, 2026
- Last synchronized
- Oct 21, 2026
- Publication status
- published
Publication history
- Oct 21, 2026, 8:31 PM: new → published (ingestion) — Backed by an official breaking-change/migration guide.
AI_PROVIDER (for example to openai-compatible with a local Ollama or LM Studio endpoint) to enable grounded explanations. Everything else on this page works without an AI provider.