Explain clearly on Skia vs Impeller rendering engine in flutter with some proper examples

#flutter#skia#impeller#rendering#performance#gpu#shader#engine

Answer

Overview

Flutter uses a rendering engine to draw every pixel on screen. Unlike native Android/iOS which use platform UI components, Flutter paints everything itself using its own engine.

  • Skia — Flutter's original rendering engine (used from Flutter 1.0 until Flutter 3.x)
  • Impeller — Flutter's new rendering engine (default since Flutter 3.16 on iOS, gradually replacing Skia on Android)

What is Skia?

Skia is an open-source 2D graphics library developed by Google. It's used by Chrome, Android, Firefox, and many other products.

text
Flutter Widget Tree
Render Objects (layout + paint commands)
┌─────────────────────────────────┐
│           SKIA ENGINE            │
│                                  │
│  1. Receives paint commands      │
│  2. Compiles GLSL shaders        │
│     at RUNTIME (first use)       │
│  3. Rasterizes to pixels         │
│  4. Sends to GPU                 │
└─────────────────────────────────┘
    Screen

Skia's Problem — Runtime Shader Compilation (Jank)

dart
// When you first show a complex animation:
AnimatedContainer(
  duration: Duration(milliseconds: 300),
  decoration: BoxDecoration(
    borderRadius: BorderRadius.circular(20),
    gradient: LinearGradient(colors: [Colors.blue, Colors.purple]),
    boxShadow: [BoxShadow(blurRadius: 10, color: Colors.black26)],
  ),
  child: child,
)

// Skia behavior:
// Frame 1: "I need a shader for rounded corners + gradient + shadow"
//          → Compiles GLSL shader on CPU (takes 50-200ms) ← JANK!
// Frame 2: Shader cached → smooth 60fps
// Frame 3: Smooth
//
// The FIRST time any visual effect is rendered,
// Skia must compile a shader → causes dropped frames

This is called "shader compilation jank" or "first-run jank" — the #1 performance complaint with Flutter.


What is Impeller?

Impeller is Flutter's purpose-built rendering engine designed to eliminate shader compilation jank entirely.

text
Flutter Widget Tree
Render Objects (layout + paint commands)
┌─────────────────────────────────┐
│         IMPELLER ENGINE          │
│                                  │
│  1. Receives paint commands      │
│  2. ALL shaders PRE-COMPILED     │
│     at BUILD TIME (AOT)          │
│  3. Rasterizes to pixels         │
│  4. Sends to GPU via Metal/Vulkan│
└─────────────────────────────────┘
    Screen

Impeller's Solution — Pre-compiled Shaders

dart
// Same complex animation as before:
AnimatedContainer(
  duration: Duration(milliseconds: 300),
  decoration: BoxDecoration(
    borderRadius: BorderRadius.circular(20),
    gradient: LinearGradient(colors: [Colors.blue, Colors.purple]),
    boxShadow: [BoxShadow(blurRadius: 10, color: Colors.black26)],
  ),
  child: child,
)

// Impeller behavior:
// Frame 1: Shader already compiled → smooth 60fps ← NO JANK!
// Frame 2: Smooth
// Frame 3: Smooth
//
// ALL shaders are pre-compiled when you run flutter build
// Nothing is compiled at runtime → zero shader jank

How Skia Causes Jank — Visual Timeline

text
Skia (First Run):
 Frame 1  │████████████████████░░░░░░│  Shader compiling (JANK - 200ms)
 Frame 2  │████░░░░░░░░░░░░░░░░░░░░░│  Cached - fast (16ms)
 Frame 3  │████░░░░░░░░░░░░░░░░░░░░░│  Cached - fast (16ms)
 Frame 4  │████████████████░░░░░░░░░│  NEW effect → compile again (JANK)
 Frame 5  │████░░░░░░░░░░░░░░░░░░░░░│  Cached - fast (16ms)

Impeller (First Run):
 Frame 1  │████░░░░░░░░░░░░░░░░░░░░░│  Pre-compiled - fast (16ms)
 Frame 2  │████░░░░░░░░░░░░░░░░░░░░░│  Fast (16ms)
 Frame 3  │████░░░░░░░░░░░░░░░░░░░░░│  Fast (16ms)
 Frame 4  │████░░░░░░░░░░░░░░░░░░░░░│  Fast (16ms)
 Frame 5  │████░░░░░░░░░░░░░░░░░░░░░│  Fast (16ms)

 █ = Frame time    ░ = Idle time    │ = 16ms budget (60fps)

Key Differences

FeatureSkiaImpeller
Shader compilationRuntime (causes jank)Build time (pre-compiled, zero jank)
First frameSlow (compiling shaders)Fast (shaders ready)
Graphics APIOpenGL ESMetal (iOS) / Vulkan (Android)
Designed forGeneral 2D graphics (Chrome, Android, etc.)Flutter specifically
Animation smoothnessJank on first run of effectsConsistently smooth
Text renderingMature, well-testedImproved (uses own text renderer)
GPU utilizationLess efficient (OpenGL overhead)More efficient (modern GPU APIs)
Memory usageHigher (shader cache grows)Lower (fixed shader set)
Platform supportAll platformsiOS (default), Android (default since 3.22)
MaturityBattle-tested (10+ years)Newer (still evolving)

Graphics API Difference

text
Skia's Approach:
 Paint Commands → Skia → OpenGL ES → GPU
                   Old API (2003)
                   Higher overhead
                   More CPU work

Impeller's Approach:
 Paint Commands → Impeller → Metal (iOS)  → GPU
                           → Vulkan (Android) → GPU
                        Modern APIs (2014+)
                        Lower overhead
                        Direct GPU access

Metal (Apple) and Vulkan (Google/Khronos) are modern graphics APIs that give more direct control over the GPU with less driver overhead than OpenGL.


When You'll Notice the Difference

Scenarios Where Skia Had Jank

dart
// 1. Complex page transitions
Navigator.push(context, MaterialPageRoute(
  builder: (_) => ComplexPage(),  // First transition → jank with Skia
));

// 2. First time showing a custom painter
CustomPaint(
  painter: ChartPainter(),  // First render → shader compile with Skia
)

// 3. Animated blur/shadow effects
BackdropFilter(
  filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),  // Expensive shader
  child: child,
)

// 4. Complex gradient animations
AnimatedBuilder(
  animation: controller,
  builder: (_, __) => Container(
    decoration: BoxDecoration(
      gradient: SweepGradient(
        colors: [Colors.red, Colors.blue, Colors.green],
        transform: GradientRotation(controller.value * 2 * pi),
      ),
    ),
  ),
)

// With Impeller: ALL of these are smooth from Frame 1

How to Check Which Engine You're Using

dart
import 'dart:ui' as ui;

void main() {
  // Check if Impeller is active
  print('Impeller enabled: Check flutter run logs');
  runApp(MyApp());
}
bash
# Check in terminal when running
flutter run --verbose 2>&1 | grep -i impeller
# Output: "Impeller rendering is enabled"

# Force Impeller ON (if not default)
flutter run --enable-impeller

# Force Impeller OFF (fallback to Skia)
flutter run --no-enable-impeller

Skia's Workaround — SkSL Warmup (Before Impeller)

Before Impeller existed, Flutter provided a workaround for shader jank:

bash
# Step 1: Record shaders during test run
flutter run --profile --cache-sksl
# Use app, trigger all animations, navigate all pages
# Press 'M' to export SkSL shaders

# Step 2: Build with pre-cached shaders
flutter build apk --bundle-sksl-path flutter_01.sksl.json

This was a manual, tedious process — Impeller eliminates this entirely by pre-compiling ALL shaders at build time automatically.


Current Status (Flutter 3.22+)

PlatformDefault EngineStatus
iOSImpellerDefault since Flutter 3.16, stable
AndroidImpellerDefault since Flutter 3.22, stable
macOSImpellerDefault since Flutter 3.22
WebSkia (CanvasKit)Impeller not available yet
WindowsSkiaImpeller in development
LinuxSkiaImpeller in development

Performance Comparison

MetricSkiaImpellerImprovement
Worst frame time50-200ms (shader compile)< 16ms3-12x better
99th percentile frame25-40ms8-12ms2-4x better
Average frame8-12ms6-10msSlightly better
First animation frameJank (noticeable)SmoothMajor improvement
Memory (shaders)Grows over timeFixed at buildMore predictable
App startupFaster (no shader pre-load)Slightly slower (loads pre-compiled shaders)Trade-off

Key Takeaway: Skia compiles shaders at runtime (causing jank the first time any visual effect renders). Impeller pre-compiles all shaders at build time, eliminating jank entirely. Impeller also uses modern GPU APIs (Metal/Vulkan) instead of OpenGL for better performance. For new Flutter projects, Impeller is the default — you don't need to do anything special to use it.

Learn more at Flutter Impeller and Flutter Rendering Architecture.