flutter_scene assumes render-to-texture content is stored top-down, but the OpenGL ES backend stores it bottom-up, so scenes render upside down on OpenGL ES (e.g. desktop Linux, headless CI).
lib/src/render/y_flip.dart works around this. It probes the backend's render-to-texture Y orientation once at runtime (renders a known pattern, reads it back), and when the backend stores bottom-up it negates gl_Position.y in the offscreen passes, inverts cull winding, and flips the present blit. It is a no-op (byte-identical output) on backends that already store top-down.
This is a temporary workaround. Remove it once the OpenGL ES backend stores render-to-texture top-down by default (tracked upstream at flutter/flutter#186556). At that point backendFlipsRenderTargetY should always be false and the entire y_flip.dart mechanism, plus its call sites in the encoders, materials, full-screen passes, and the present blit, can be deleted.
Fix at the same time: the probe reads back asynchronously, so the first frame on a flip backend uses the default (no flip) before correcting.
flutter_scene assumes render-to-texture content is stored top-down, but the OpenGL ES backend stores it bottom-up, so scenes render upside down on OpenGL ES (e.g. desktop Linux, headless CI).
lib/src/render/y_flip.dartworks around this. It probes the backend's render-to-texture Y orientation once at runtime (renders a known pattern, reads it back), and when the backend stores bottom-up it negatesgl_Position.yin the offscreen passes, inverts cull winding, and flips the present blit. It is a no-op (byte-identical output) on backends that already store top-down.This is a temporary workaround. Remove it once the OpenGL ES backend stores render-to-texture top-down by default (tracked upstream at flutter/flutter#186556). At that point
backendFlipsRenderTargetYshould always be false and the entirey_flip.dartmechanism, plus its call sites in the encoders, materials, full-screen passes, and the present blit, can be deleted.Fix at the same time: the probe reads back asynchronously, so the first frame on a flip backend uses the default (no flip) before correcting.