Flutter Interview Handbook

Graphics & Rendering Engines

100%

Graphics & Rendering Engines

Flutter’s UI framework draws every pixel on screen using a low-level graphics rendering engine. Historically, Flutter used Google’s Skia engine. In Flutter 3+, Flutter introduced Impeller, a custom-designed rendering engine built specifically for Flutter to eliminate runtime Shader Compilation Jank and leverage modern GPU APIs (Metal & Vulkan).


1. Skia vs. Impeller Architectural Matrix

FeatureLegacy Skia EngineModern Impeller Engine
Shader CompilationJust-In-Time (JIT) at runtimeAhead-Of-Time (AOT) at build time (impellerc)
Primary Graphics APIOpenGL ES / Legacy VulkanMetal (iOS/macOS) & Vulkan (Android)
Shader Jank⚠️ Frequent frame drops on first animation renderβœ… Zero Shader Compilation Jank
Command Buffer ModelSingle-threaded stateful modelMulti-threaded concurrent command encoding
Memory AllocationDynamic runtime GPU memory allocationsPre-allocated transient memory pools

2. Under The Hood: Shader Compilation Jank in Skia

What is a Shader?

A Shader is a small program written in GLSL (OpenGL Shading Language) or MSL that runs directly on the GPU to calculate pixel colors, gradients, blurs, clipping masks, and shadows.

Why Skia Suffered from Shader Jank

In Skia, shaders were compiled Just-In-Time (JIT) on the GPU thread the first time a specific visual effect appeared on screen:

[ Flutter Widget Render ] ──► First Time Blur/Shadow Appears ──► GPU Compiles GLSL Shader (JIT) 
                                                                           β”‚
                                                                 Takes 30ms - 100ms!
                                                                           β”‚
                                                                           β–Ό
                                                                Exceeds 16.6ms Budget ──► DROPPED FRAME! (Jank)
  1. When a user triggered a new screen animation (e.g. page transition with a blurred backdrop), Skia halted frame rendering to compile GLSL shaders.
  2. Shader compilation took 30ms to 100ms+, blowing past the 16.6ms (60 FPS) frame budget and causing noticeable UI stuttering.
  3. Subsequent renders reused the compiled shader, meaning the jank occurred only on the first invocation.

3. How Impeller Eliminates Shader Jank

Impeller solves shader jank by shifting shader compilation from runtime to build time (Ahead-Of-Time).

 [ Build Time (impellerc) ]                             [ Runtime Execution ]
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Shaders (.hlsl / .spirv) β”‚                         β”‚ Pre-compiled MSL / SPIR-V β”‚
β”‚            β”‚             β”‚                         β”‚ Shaders loaded instantly β”‚
β”‚     (impellerc AOT)      β”‚ ──────────────────────► β”‚                          β”‚
β”‚            β–Ό             β”‚                         β”‚ ZERO GPU compilation!    β”‚
β”‚ Pre-compiled MSL / SPIRV β”‚                         β”‚ Deterministic 120 FPS    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

1. AOT Shader Compilation (impellerc)

During flutter build, the Impeller offline compiler (impellerc) compiles all HLSL/SPIR-V shader source code into target platform binaries (Metal Shading Language on iOS; SPIR-V on Android). Shaders are bundled directly into the application executable.

2. Explicit State Management & Multi-threading

Impeller creates explicit, immutable GPU pipeline state objects (PSOs). Rendering command buffers are encoded concurrently across multiple threads before submission to Metal or Vulkan drivers.

3. Predictable Frame Cadence

Because zero shader compilation occurs at runtime, frame rendering time becomes completely deterministic, maintaining a stable 60 FPS or 120 FPS display rate.


4. Trade-offs & Production Considerations

  • Engine Availability: Impeller is enabled by default on iOS (Flutter 3.7+) and Android Vulkan (Flutter 3.16+). On older Android devices running legacy OpenGL ES drivers, Flutter falls back to Skia or Impeller’s OpenGL ES backend.
  • Binary Size vs Runtime Speed: Bundling pre-compiled AOT shaders via impellerc adds a minor overhead (~1-2MB) to the release binary size, trading negligible disk space for flawless 120 FPS UI performance.