Start Debugging

Migra una aplicación de escritorio Flutter para Windows o Linux a Impeller (Flutter 3.47)

Flutter 3.47 convierte a Impeller en el renderizador predeterminado en Windows y Linux. Qué cambia en realidad por debajo (sigue siendo OpenGL ES, no Vulkan), cómo hacer una prueba A/B contra Skia sobre un binario compilado, un interruptor de emergencia por máquina para compilaciones de release, y por qué tus pruebas golden no lo notarán.

Flutter 3.47.0 (estable desde el 2026-08-12, Dart 3.13) cambia las aplicaciones de escritorio de Windows y Linux de Skia a Impeller sin que modifiques una sola línea del código de tu runner. Para la mayoría de las aplicaciones la migración es cuestión de una tarde: actualizar, confirmar que el registro del motor dice Using the Impeller rendering backend (OpenGLESSDF), comparar capturas de pantalla y tiempos de fotograma contra una ejecución con --no-enable-impeller, y solo entonces decidir si publicas con Impeller o fijas Skia de forma temporal en windows/runner/main.cpp o linux/runner/my_application.cc. Lo que se rompe es sobre todo visual: la rasterización del texto (Impeller fuerza texto con campos de distancia con signo en escritorio, más una nueva corrección gamma), el antialiasing en GPU sin MSAA implícito, y algún que otro shader personalizado. Todo lo que sigue se comprobó contra el motor de Flutter 3.47.0 y el código fuente de flutter_tools.

Qué cambia realmente bajo tu aplicación

Lo primero que conviene saber es lo que no cambia: la API gráfica. El embedder de Windows sigue renderizando mediante OpenGL ES a través de ANGLE, que lo traduce a Direct3D 11. flutter_windows_engine.cc en 3.47.0 crea un egl::Manager y un CompositorOpenGL sin importar el renderizador, y el embedder de Linux solo conoce dos tipos de renderizador, opengl y software. No hay ruta de Vulkan en ninguno de los dos embedders de escritorio. Impeller en escritorio es el backend GLES de Impeller ejecutándose sobre el mismo contexto GL que usaba Skia antes. En macOS es el backend Metal de Impeller.

Lo que cambia es todo lo que está por encima de las llamadas GL:

Ese último punto es la razón por la que esto necesita una pasada de migración deliberada. Nada en tu diff le dice a los revisores que el renderizador cambió.

Qué se rompe

ÁreaCambio en 3.47Gravedad
Renderizado de textoGlifos SDF más corrección gamma; los bordes y el grosor de los glifos cambian ligeramentemedia
AntialiasingLas GPU de Windows sin MSAA implícito necesitan la ruta de MSAA fuera de pantalla (#190374, incluido por cherry-pick en 3.47)media
Capturas de pruebas de integraciónDiferencias de píxeles contra líneas base capturadas con Skiamedia
Fragment shaders personalizadosCompilados por impellerc para el destino GLES; los errores específicos del controlador aparecen de forma distintabaja a media
Golden de flutter testSin afectar por defecto (consulta los detalles a tener en cuenta)ninguna
Código del runnerSin cambio en la plantilla; para desactivarlo hace falta una edición manualbaja

Lista de verificación previa

Pasos de migración

  1. Captura una línea base con Skia en 3.44. Compila binarios de perfil y haz capturas de tus pantallas de estrés en cada máquina de destino:

    # Flutter 3.44.x
    flutter build windows --profile
    flutter build linux --profile

    Registra también los tiempos de fotograma. Basta con una traza de rendimiento de DevTools de los primeros 10 segundos tras el arranque y de tu desplazamiento más pesado. Verifica: tienes una traza y un conjunto de capturas por máquina.

  2. Actualiza a 3.47 y vuelve a compilar.

    flutter upgrade
    flutter --version   # expect Flutter 3.47.x, Dart 3.13.x
    flutter clean
    flutter build windows --profile

    Verifica: git status no muestra cambios en windows/runner/ ni en linux/runner/. Si los muestra, alguien ejecutó flutter create . y deberías revisar ese diff por separado.

  3. Confirma qué backend eligió el motor. Ejecuta la aplicación con flutter run -d windows (o -d linux) y busca la línea de arranque del motor:

    [IMPORTANT:flutter/shell/platform/embedder/embedder_surface_gl_impeller.cc(126)] Using the Impeller rendering backend (OpenGLESSDF).

    OpenGLESSDF significa Impeller con texto SDF, que es el resultado esperado en ambas plataformas. Si en cambio ves Could not create Impeller context., el contexto GL no pudo satisfacer a Impeller y tienes un problema de controlador que atender antes que cualquier otra cosa. Ten en cuenta que no hay un respaldo silencioso a Skia en la superficie del embedder: la superficie de Impeller simplemente queda inválida. Verifica: la línea aparece exactamente una vez por ventana.

  4. Haz A/B del mismo binario contra Skia. Con flutter run, el flag funciona en todas las plataformas de escritorio:

    flutter run -d windows --profile --no-enable-impeller

    Para un binario de depuración o de perfil ya compilado puedes cambiar el renderizador con las variables de entorno de switches del motor, que ambos embedders de escritorio leen mediante GetSwitchesFromEnvironment():

    # Flutter 3.47, Windows, debug or profile build only
    $env:FLUTTER_ENGINE_SWITCHES = "1"
    $env:FLUTTER_ENGINE_SWITCH_1 = "enable-impeller=false"
    .\build\windows\x64\runner\Profile\my_app.exe
    # Flutter 3.47, Linux, debug or profile build only
    FLUTTER_ENGINE_SWITCHES=1 FLUTTER_ENGINE_SWITCH_1=enable-impeller=false \
      ./build/linux/x64/profile/bundle/my_app

    Es la forma más rápida de entregarle a un tester una compilación y dos accesos directos. Verifica: la línea de registro de arranque desaparece cuando se define el switch, y vuelve cuando no.

  5. Compara capturas y trazas. Coloca lado a lado las capturas de 3.44 con Skia, las de 3.47 con Skia y las de 3.47 con Impeller. La comparación útil es 3.47 Skia contra 3.47 Impeller, porque aísla el renderizador de todos los demás cambios de la versión. Espera que el texto se vea ligeramente distinto en todas partes. Busca lo que está mal en lugar de lo que es diferente: glifos recortados, sombras ausentes, bordes dentados en rectángulos redondeados, regiones negras. Verifica: cada diferencia está aceptada o tiene un repro mínimo.

  6. Decide y, si hace falta, fija Skia en el runner. Si encontraste una regresión real, desactiva Impeller en la compilación desplegada. En Windows, en windows/runner/main.cpp:

    // Flutter 3.47, windows/runner/main.cpp
    flutter::DartProject project(L"data");
    project.set_impeller_switch(flutter::ImpellerSwitch::Disabled);

    En Linux, en linux/runner/my_application.cc, antes de fl_view_new(project):

    // Flutter 3.47, linux/runner/my_application.cc
    g_autoptr(FlDartProject) project = fl_dart_project_new();
    fl_dart_project_set_enable_impeller(project, FALSE);

    Verifica: vuelve a compilar, ejecuta y confirma que la línea Using the Impeller rendering backend ya no aparece.

  7. Reporta el bug el mismo día. La documentación de Impeller dice que la opción de desactivarlo se eliminará en una versión futura, como ocurrió en iOS. Abre un issue con el prefijo [Impeller] en el título, un repro mínimo, la GPU y la versión del controlador, capturas de pantalla y una traza de rendimiento comprimida en zip. Verifica: el enlace al issue está en un comentario junto a la línea que lo desactiva, para que quien la elimine más adelante sepa por qué está ahí.

Un interruptor de emergencia por máquina para compilaciones de release

El truco de FLUTTER_ENGINE_SWITCHES del paso 4 no funciona en compilaciones de release. engine_switches.cc envuelve toda la búsqueda en #ifndef FLUTTER_RELEASE, así que una aplicación distribuida la ignora. Si quieres publicar con Impeller pero conservar una vía de escape para el cliente cuya laptop de 2017 dibuja una ventana negra, lee tu propia variable de entorno en el runner:

// Flutter 3.47, windows/runner/main.cpp
#include <cwchar>

flutter::DartProject project(L"data");

wchar_t value[8];
DWORD length = ::GetEnvironmentVariableW(L"MYAPP_DISABLE_IMPELLER", value, 8);
if (length > 0 && length < 8 && std::wcscmp(value, L"1") == 0) {
  project.set_impeller_switch(flutter::ImpellerSwitch::Disabled);
}
// Flutter 3.47, linux/runner/my_application.cc
g_autoptr(FlDartProject) project = fl_dart_project_new();
if (g_strcmp0(g_getenv("MYAPP_DISABLE_IMPELLER"), "1") == 0) {
  fl_dart_project_set_enable_impeller(project, FALSE);
}

Soporte puede entonces indicarle al usuario afectado que defina una variable en lugar de esperar una nueva compilación. Un valor del registro o una línea en un archivo de configuración junto al ejecutable funciona igual si las variables de entorno resultan incómodas para tus usuarios. Trátalo como andamiaje temporal con la misma vida útil que la opción de desactivación del motor.

Verificación

Tras la migración, en cada máquina de tu matriz:

Plan de reversión

La reversión es barata y reversible en ambos sentidos. Puedes bajar a 3.44.x, que nunca activó Impeller de forma predeterminada en Windows ni Linux, o quedarte en 3.47 y añadir la desactivación en el runner del paso 6. La segunda opción es mejor: conservas todas las demás correcciones de 3.47 y puedes volver atrás borrando una sola línea. Simplemente no cuentes con que la opción de desactivación exista para siempre.

Detalles a tener en cuenta

Los switches de entorno prevalecen sobre el switch del proyecto en Windows. En el constructor de FlutterWindowsEngine primero se lee el ImpellerSwitch del proyecto, y luego el bucle sobre los switches de entorno lo sobrescribe. Un desarrollador con FLUTTER_ENGINE_SWITCH_1=enable-impeller=true olvidado en el perfil de su shell verá Impeller incluso en una rama que fijó Disabled. Revisa env antes de depurar cualquier otra cosa.

ImpellerSwitch::Default no es Enabled. Si quieres dejar Impeller activado pase lo que pase en una versión futura, establece ImpellerSwitch::Enabled de forma explícita. Default sigue al motor, que es exactamente lo que cambió bajo tus pies en 3.47.

Los golden de flutter test no ven Impeller. flutter_tester_device.dart lanza el shell de pruebas con --enable-software-rendering --skia-deterministic-rendering a menos que pases --enable-impeller. Tus golden de pruebas de widgets siguen pasando tras la actualización, lo cual no te dice nada sobre el renderizador de escritorio. Solo las pruebas de integración que ejecutan el .exe real o el bundle de Linux ejercitan Impeller.

La desactivación en Linux tiene que ocurrir antes de que exista la vista. fl_dart_project_set_enable_impeller establece un campo que FlEngine lee cuando arranca. Llámala justo después de fl_dart_project_new() y antes de fl_view_new(project), no más tarde en my_application_activate.

Las laptops con GPU híbrida eligen una GPU antes de que importe el renderizador. En Windows, DartProject::set_gpu_preference con flutter::GpuPreference::HighPerformancePreference o LowPowerPreference decide qué adaptador usa ANGLE. Si una regresión solo se reproduce en una laptop con GPU Intel y NVIDIA a la vez, prueba ambas preferencias antes de culpar a Impeller.

VM y sesiones remotas. Las máquinas sin soporte de MSAA implícito sufrían una pantalla negra en Windows al principio del ciclo de 3.47; #187288 y el respaldo de MSAA fuera de pantalla de #190374 lo resolvieron. Si ves una ventana negra en una VM, confirma que tienes el último parche de 3.47 antes de abrir un nuevo issue.

Shaders personalizados. Los shaders de FragmentProgram siguen funcionando, pero ahora los ejecuta el backend GLES de Impeller sobre el controlador que exponga ANGLE o Mesa. Vuelve a probar cada archivo .frag en tu GPU soportada más antigua y evita depender de un comportamiento de precisión que casualmente funcionaba con Skia.

La opción de desactivación tiene un plazo. Cada desactivación que añades es una deuda con una fecha límite que no controlas. Pon el enlace al issue junto a ella y revísala en cada actualización de Flutter.

Relacionado

Fuentes

Comments

Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.

< Volver