0Pricing
Flutter Mobile Development · 강의

셰이더 예열 및 Impeller 마이그레이션

셰이더를 미리 컴파일하고 Impeller 렌더러를 도입해 최초 실행 시 셰이더 버벅거림을 제거합니다.

셰이더 예열 및 Impeller 마이그레이션은(는) CoddyKit의 무료 Flutter Mobile Development 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Flutter Mobile Development 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Flutter Mobile Development 강의에는 총 4개의 강의가 포함되어 있습니다.

이 강의의 일부는 아직 번역되지 않았으며 영어로 표시됩니다.

Why First-Run Jank Happens

The first time a Flutter app draws a particular effect, the GPU backend has to compile the underlying shader program on the device. With the legacy Skia backend this compilation happens lazily, right in the middle of the frame that needs it.

  • A shader compile can take tens of milliseconds.
  • That blows the 16ms budget of a 60fps frame, producing a visible stutter called shader jank.
  • It is worst on the very first run because nothing is cached yet.

Animations, page transitions, and BackdropFilter blurs are the usual culprits.

Where the Time Goes

A jank frame caused by shader compilation shows up clearly in DevTools' Performance view as a tall raster-thread bar with a ShaderCompilation event.

To reproduce and measure it reliably, run in profile mode (never debug mode, which is much slower and misleading):

  • Profile mode gives release-like performance with tracing hooks.
  • The DevTools timeline marks shader compile events so you can confirm the root cause before optimizing.
// Run the app in profile mode to capture realistic frame timings.
// flutter run --profile

// Then open DevTools > Performance and look for
// 'ShaderCompilation' events on the raster thread.
// flutter run --profile --trace-skia

The Skia Warm-Up Strategy

On the legacy Skia backend, the classic fix is shader warm-up: collect the shaders your app uses into a bundle, then precompile them at startup before the user interacts.

Flutter generates this bundle for you with the --cache-sksl flag, which records SkSL (Skia Shader Language) programs while you exercise the app:

// 1. Run in profile mode, capturing SkSL while you navigate every screen
//    and trigger every animation that might cause jank.
// flutter run --profile --cache-sksl --purge-persistent-cache

// 2. In the running app, press 'M' in the terminal to write the
//    captured shaders to a JSON file, e.g. flutter_01.sksl.json

Bundling the Captured Shaders

Once you have the captured .sksl.json file, you bundle it into the release build. Flutter precompiles those shaders during the engine warm-up phase, so they are ready before the first frame the user sees.

  • Capture on a physical device similar to your target hardware.
  • Re-capture whenever the UI changes significantly.
// Bundle the captured SkSL into a release build:
// flutter build apk --bundle-sksl-path flutter_01.sksl.json
// flutter build ios --bundle-sksl-path flutter_01.sksl.json

// The engine warms up these shaders at launch,
// eliminating compile stalls during animations.

Why Skia Warm-Up Is a Band-Aid

SkSL warm-up works, but it has real downsides that motivated a deeper fix:

  • The capture is device- and driver-specific; a bundle from one GPU may not cover another.
  • You must remember to re-capture after UI changes, or jank silently returns.
  • It only covers the shaders you happened to exercise during capture.

The Flutter team's permanent answer is a new rendering engine that does not compile shaders at runtime at all: Impeller.

How Impeller Eliminates the Problem

Impeller precompiles a small, fixed set of shaders at engine build time rather than at runtime. Instead of generating arbitrary shaders per draw call, it composes effects from these known-ahead-of-time programs.

  • No runtime shader compilation means no first-run shader jank by design.
  • It uses Metal on iOS and Vulkan on modern Android.
  • Because shaders are known ahead of time, --cache-sksl warm-up is unnecessary and unsupported with Impeller.

Impeller's Default Status

Impeller is now the default renderer on iOS and on modern Android (devices supporting Vulkan), as of recent stable Flutter releases. On older Android hardware without Vulkan, the engine falls back to an OpenGL backend automatically.

Most apps get the benefit with no code change. The migration work is about verifying visual correctness and handling the few edge cases where Impeller and Skia differ.

Toggling Impeller Per Platform

You control Impeller through native platform manifests, not Dart code. This lets you opt in, opt out, or compare against Skia during migration testing.

On iOS, set the flag in Info.plist; on Android, in AndroidManifest.xml:

<!-- ios/Runner/Info.plist -->
<key>FLTEnableImpeller</key>
<true/>

<!-- android/app/src/main/AndroidManifest.xml (inside <application>) -->
<meta-data
    android:name="io.flutter.embedding.android.EnableImpeller"
    android:value="true" />

Custom Shaders Still Need Warm-Up

If you ship your own GLSL fragment shaders via FragmentProgram, those are your code and are not part of Impeller's built-in set. Compiling or loading them on demand can still stall a frame.

The fix is to load and warm them up during app startup, before they are first used in an animation:

import 'package:flutter/material.dart';

class ShaderCache {
  static FragmentProgram? ripple;

  // Call during startup so the program is ready before first paint.
  static Future<void> warmUp() async {
    ripple = await FragmentProgram.fromAsset('shaders/ripple.frag');
  }
}

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await ShaderCache.warmUp();
  runApp(const MyApp());
}

Pre-Rendering Expensive Effects

Even with shaders precompiled, the very first build of an expensive widget can still cost more than later builds. A common technique is to render the heavy effect off-screen during a splash or warm-up frame so the work is done before the user navigates to it.

You can drive a one-frame warm-up render after the first frame is committed:

import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';

void scheduleWarmUp(VoidCallback warmUpExpensiveEffects) {
  // Runs once after the first frame is rendered,
  // so warm-up work does not block startup paint.
  SchedulerBinding.instance.addPostFrameCallback((_) {
    warmUpExpensiveEffects();
  });
}

Measuring the Win

Always confirm the improvement with data, not vibes. Compare the worst frame raster time before and after, on a real device, in profile mode.

You can compute simple statistics from captured frame timings to verify that the 99th-percentile frame now fits the budget:

void main() {
  // Raster times in milliseconds captured before the warm-up fix.
  final frames = <double>[8.1, 7.9, 42.6, 8.0, 9.3, 8.2, 7.7];

  frames.sort();
  final worst = frames.last;
  final p50 = frames[frames.length ~/ 2];
  const budget = 16.0; // 60fps frame budget

  print('p50: ${p50}ms  worst: ${worst}ms');
  print(worst > budget
      ? 'Jank present: worst frame exceeds ${budget}ms'
      : 'All frames within budget');
}

Quick Check

You migrate a C1-level app from Skia to Impeller to fix first-run shader jank. What happens to your existing SkSL warm-up bundle and why?

Recap

You now know how to eliminate first-run shader jank in Flutter:

  • Diagnose shader compilation stalls in DevTools' Performance view using profile mode.
  • Skia warm-up with --cache-sksl and --bundle-sksl-path precompiles captured SkSL, but is device-specific and brittle.
  • Impeller is the permanent fix: it precompiles a fixed shader set at build time, so there is no runtime compilation and no shader jank by design. It is the default on iOS and modern (Vulkan) Android.
  • Toggle Impeller via Info.plist and AndroidManifest.xml; drop your SkSL bundle once migrated.
  • Custom FragmentProgram shaders still need explicit startup warm-up.
  • Always measure worst-frame raster time on a real device to confirm the win.

자주 묻는 질문

“셰이더 예열 및 Impeller 마이그레이션” 강의는 무료인가요?

네 — “셰이더 예열 및 Impeller 마이그레이션” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Flutter Mobile Development 강의 전체를 잠금 해제할 수 있습니다. Flutter Mobile Development 강의에는 총 4개의 강의가 포함되어 있습니다.

“셰이더 예열 및 Impeller 마이그레이션”에서 뭘 배우나요?

셰이더를 미리 컴파일하고 Impeller 렌더러를 도입해 최초 실행 시 셰이더 버벅거림을 제거합니다. 브라우저에서 직접 실행하는 실습 코드로 Flutter Mobile Development을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Flutter Mobile Development을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Flutter Mobile Development은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.

“셰이더 예열 및 Impeller 마이그레이션” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Flutter Mobile Development 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Flutter Mobile Development 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 세 가지 트리: Widget, Element 및 RenderObject
  2. DevTools 타임라인으로 버벅거림 프로파일링
  3. RepaintBoundary, 상수 위젯 및 다시 빌드 가지치기
  4. 셰이더 예열 및 Impeller 마이그레이션
← Flutter Mobile Development(으)로 돌아가기