Flutter Moments Performance Test

May 26, 2026 ยท View on GitHub

๐Ÿ“Š Project Status

Flutter 3.19 Flutter 3.27 Flutter 3.29 API Level Dart Platform FVM Load Types

A WeChat Moments-style Flutter application for performance and power consumption testing, supporting Flutter 3.19, 3.27, and 3.29 version comparison with 13 load types for comprehensive performance testing.

ไธญๆ–‡ๆ–‡ๆกฃ

Quick Start

# Build APKs (requires FVM)
./build_release.sh

# Install to device
./install_apks.sh

# Quick launch (interactive menu)
./quick_launch.sh

Flutter Version Differences

This project includes 3 Flutter versions, each with significant architectural changes:

Version Comparison

FeatureFlutter 3.19Flutter 3.27Flutter 3.29
Rendering EngineSkiaImpellerImpeller
Main Thread MergerNoNoYes
UI ThreadSeparate 1.ui threadSeparate 1.ui threadMerged into main thread
GPU SubmissionqueueBuffer/dequeueBufferQueueSubmitQueueSubmit
Dart Version3.3.03.6.03.7.0

Detailed Description

Flutter 3.19 - Skia Renderer

  • Rendering Engine: Uses traditional Skia renderer
  • Thread Model: Flutter UI thread (1.ui) is separate from Android main thread
  • GPU Communication: Communicates with SurfaceFlinger via queueBuffer/dequeueBuffer

Flutter 3.27 - Impeller Renderer

  • Rendering Engine: Impeller becomes the default renderer
  • Thread Model: Flutter UI thread (1.ui) remains separate from Android main thread
  • GPU Communication: Switches to QueueSubmit API, reducing GPU communication latency

Flutter 3.29 - Main Thread Merger

  • Rendering Engine: Impeller renderer (further optimized)
  • Thread Model: Flutter UI thread merged with Android main thread
  • Impact: No separate 1.ui thread, all UI operations execute on main thread

Perfetto Trace Analysis

Thread Structure Differences

Flutter 3.19 / 3.27 (Non-Merged Mode)

In Perfetto, you can see the following threads:

Thread NameDescription
mainAndroid main thread (Java/Kotlin)
1.uiFlutter UI thread (Dart) running independently
1.rasterFlutter rasterization thread
1.ioFlutter IO thread
gpu_completionGPU completion thread

Trace Characteristics:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ main (Android)                                              โ”‚
โ”‚   โ””โ”€ Activity lifecycle, JNI calls                         โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ 1.ui (Flutter UI Thread)                                   โ”‚
โ”‚   โ””โ”€ Dart execution, Layout, Paint                         โ”‚
โ”‚   โ””โ”€ BuildFrame                                            โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ 1.raster (Raster Thread)                                  โ”‚
โ”‚   โ””โ”€ Skia/Impeller rasterization                           โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Flutter 3.29 (Main Thread Merged Mode)

In Perfetto, you can see the following threads:

Thread NameDescription
mainAndroid main thread + Flutter UI thread (merged)
1.rasterFlutter rasterization thread
1.ioFlutter IO thread
gpu_completionGPU completion thread

Trace Characteristics:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ main (Android + Flutter UI Merged)                         โ”‚
โ”‚   โ”œโ”€ Activity lifecycle, JNI calls                         โ”‚
โ”‚   โ”œโ”€ Dart execution, Layout, Paint (directly on main)      โ”‚
โ”‚   โ””โ”€ BuildFrame                                            โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ 1.raster (Raster Thread)                                  โ”‚
โ”‚   โ””โ”€ Impeller rasterization                               โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

GPU Communication Differences

Skia (3.19) - SurfaceView Mode

Architecture: Dual Pipeline (ๅŒ็ฎก้“ๅนถ่กŒ)

SurfaceView creates two independent rendering pipelines:

  • Pipeline A (Flutter): Renders actual content
  • Pipeline B (Android App): Renders window chrome (Status Bar, Nav Bar) + defines SurfaceView position

Phase 1: Production (Flutter Raster Thread) - Independent Path

Vsync Signal
    โ†“
[1.raster] LayerTree rasterization โ†’ GraphicBuffer
    โ†“
    BufferQueue::queueBuffer()
    (Flutter's ANativeWindow maps directly to a SurfaceFlinger Layer)
    โ†“
    *** Direct Submission ***
    (No Android Main Thread or RenderThread involvement)
    โ†“
    Shared Memory โ†’ SurfaceFlinger receives "Frame Available" directly

Phase 2: Hole Punching (Android RenderThread)

Vsync-App Signal (Parallel, non-blocking)
    โ†“
[RenderThread] Draw App Window UI
    โ†“
    At SurfaceView region: Draw transparent pixels
    (This creates a "hole" in the app window)
    โ†“
    Z-Order: SurfaceView (Z=-1) behind App Window (Z=0)
    โ†“
    BufferQueue::queueBuffer() (App Window with transparent hole)

Phase 3: System Composition (SurfaceFlinger & HWC - ZERO-COPY)

[SurfaceFlinger]
    โ†“
    Collect multiple layers in one Vsync period:
    โ”œโ”€ App Window Buffer (with transparent hole at SurfaceView position)
    โ””โ”€ Flutter Surface Buffer (actual content)
    โ†“
    โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—
    โ•‘  *** ZERO-COPY / HARDWARE OVERLAY ***                                   โ•‘
    โ•‘  SF โ†’ HWC: "Set Layer 1 (App Window, Z=0)"                               โ•‘
    โ•‘         "Set Layer 2 (SurfaceView, Z=-1)"                              โ•‘
    โ•‘  HWC: Hardware Overlay composition                                        โ•‘
    โ•‘  NO GPU synthesis - Direct Display Processor scanout                      โ•‘
    โ•‘  Cost: Zero copy, GPU may stay idle                                     โ•‘
    โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

Sequence Diagram:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Vsync   โ”‚    โ”‚ Flutter  โ”‚    โ”‚Buffer    โ”‚    โ”‚ Android  โ”‚    โ”‚ Surface  โ”‚    โ”‚ Display โ”‚
โ”‚ Signal  โ”‚    โ”‚ Raster   โ”‚    โ”‚ Queue    โ”‚    โ”‚ Render   โ”‚    โ”‚Flinger   โ”‚    โ”‚  (HWC)  โ”‚
โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”˜
     โ”‚              โ”‚               โ”‚              โ”‚              โ”‚              โ”‚
     โ”‚ Vsync        โ”‚               โ”‚              โ”‚ Vsync        โ”‚              โ”‚
     โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€>โ”‚               โ”‚              โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€>โ”‚              โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚              โ”‚              โ”‚
     โ”‚ *** Pipeline A: Flutter Content *** โ”‚              โ”‚              โ”‚              โ”‚
     โ”‚              โ”‚ Rasterize      โ”‚              โ”‚              โ”‚              โ”‚
     โ”‚              โ”‚ to GraphicBuf โ”‚              โ”‚              โ”‚              โ”‚
     โ”‚              โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€>โ”‚              โ”‚              โ”‚              โ”‚
     โ”‚              โ”‚               โ”‚ queueBuffer()โ”‚              โ”‚              โ”‚
     โ”‚              โ”‚               โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€>โ”‚              โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚ (Direct to SF)โ”‚              โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚              โ”‚              โ”‚
     โ”‚ *** Pipeline B: Window Hole Punch *** (Parallel) โ”‚              โ”‚              โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚ Draw App UI  โ”‚              โ”‚
     โ”‚              โ”‚               โ”‚              โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€>              โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚ Draw transparentโ”‚              โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚ at SurfaceViewโ”‚              โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚              โ”‚              โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚ queueBuffer()โ”‚              โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€>โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚              โ”‚              โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚              โ”‚ acquireBuf()โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚              โ”‚<โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚              โ”‚ acquireBuf()โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚              โ”‚<โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚              โ”‚              โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚              โ”‚ *** HWC Overlay***
     โ”‚              โ”‚               โ”‚              โ”‚              โ”‚ Layer 1: App โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚              โ”‚ Layer 2: Flutter
     โ”‚              โ”‚               โ”‚              โ”‚              โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€>โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚              โ”‚   Scanout    โ”‚

Key Differences from TextureView:

  • No GPU Copy: Flutter content bypasses App's RenderThread entirely
  • Independent Layer: Flutter Surface is a separate SurfaceFlinger layer
  • Hole Punching: App window has transparent region revealing Flutter beneath
  • Hardware Overlay: HWC combines layers without GPU synthesis

Trace Characteristics:

  • queueBuffer on 1.raster thread โ†’ Flutter content to SF (direct)
  • queueBuffer on RenderThread โ†’ App window with transparent hole
  • dequeueBuffer in SurfaceFlinger โ†’ acquires both buffers
  • BLASTBufferQueue_* symbols visible
  • No updateTexImage or GPU copy operations

TextureView Mode (All Flutter Versions)

TextureView mode has significant overhead due to the GPU copy process. Here's the complete flow:

Phase 1: Production (Flutter Raster Thread)

Vsync Signal
    โ†“
[1.raster] Skia/Impeller rasterization โ†’ GraphicBuffer
    โ†“
    BufferQueue::queueBuffer()
    (Buffer enters QUEUED state in SurfaceTexture's BufferQueue)
    โ†“
    onFrameAvailable() callback โ†’ Main Thread Handler
    (Only marks TextureView as dirty, no immediate draw)

Phase 2: Scheduling (Android Main Thread - Next Vsync)

Vsync-App Signal (T+16.6ms)
    โ†“
[Main Thread] performTraversals()
    โ”œโ”€ Measure
    โ”œโ”€ Layout
    โ””โ”€ Draw
        โ””โ”€ TextureView.draw() โ†’ Creates DisplayList RenderNode
            (Command: "RenderThread, draw SurfaceTexture content at these coordinates")
    โ†“
    SyncFrame โ†’ Send DisplayList to RenderThread

Phase 3: Composition & GPU Copy (Android RenderThread - THE PERFORMANCE HOTSPOT)

[RenderThread]
    โ†“
    BufferQueue::acquireBuffer() (Lock latest available frame)
    โ†“
    SurfaceTexture.updateTexImage()
    (Bind GraphicBuffer as GL_TEXTURE_EXTERNAL_OES)
    โ†“
    โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—
    โ•‘  *** CRITICAL PERFORMANCE POINT ***                                         โ•‘
    โ•‘  drawTexture() โ†’ GPU Fragment Shader                                        โ•‘
    โ•‘  Input:  Flutter OES texture                                                 โ•‘
    โ•‘  Output: App window FrameBuffer                                             โ•‘
    โ•‘  Process: GPU samples from OES texture โ†’ Color conversion (YUVโ†’RGB)        โ•‘
    โ•‘           โ†’ Writes to window Buffer                                         โ•‘
    โ•‘  Cost: GPU ALU + Memory Bandwidth ("Extra GPU Copy")                        โ•‘
    โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

Phase 4: System Composition (SurfaceFlinger)

[RenderThread] queueBuffer() (Complete App Window)
    โ†“
[SurfaceFlinger]
    โ†“
    BufferQueueConsumer::acquireBuffer() (App window as Layer)
    โ†“
    [SF Main Thread] Layer composition (includes TextureView layer)
    โ†“
    [HWComposer / WHC] Present to Display

Sequence Diagram:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Vsync   โ”‚    โ”‚ Flutter  โ”‚    โ”‚Buffer    โ”‚    โ”‚ Android  โ”‚    โ”‚ Android  โ”‚    โ”‚ Surface โ”‚    โ”‚ Display  โ”‚
โ”‚ Signal  โ”‚    โ”‚ Raster   โ”‚    โ”‚ Queue    โ”‚    โ”‚ Main     โ”‚    โ”‚ Render   โ”‚    โ”‚Flinger  โ”‚    โ”‚          โ”‚
โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”˜
     โ”‚              โ”‚               โ”‚              โ”‚              โ”‚              โ”‚              โ”‚
     โ”‚ Vsync-App    โ”‚               โ”‚              โ”‚              โ”‚              โ”‚              โ”‚
     โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€>โ”‚               โ”‚              โ”‚              โ”‚              โ”‚              โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚ Vsync        โ”‚              โ”‚              โ”‚
     โ”‚              โ”‚               โ”‚              โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€>โ”‚              โ”‚              โ”‚
     โ”‚              โ”‚ Rasterize     โ”‚              โ”‚              โ”‚              โ”‚              โ”‚
     โ”‚              โ”‚ to GraphicBuf โ”‚              โ”‚              โ”‚              โ”‚              โ”‚
     โ”‚              โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€>โ”‚              โ”‚              โ”‚              โ”‚              โ”‚
     โ”‚              โ”‚               โ”‚ queueBuffer()โ”‚              โ”‚              โ”‚              โ”‚
     โ”‚              โ”‚               โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€>โ”‚              โ”‚              โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚ (dirty flag)  โ”‚              โ”‚              โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚<โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚              โ”‚              โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚ onFrameAvail  โ”‚              โ”‚              โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚              โ”‚              โ”‚              โ”‚
     โ”‚     Next Vsync (T+16.6ms)    โ”‚              โ”‚              โ”‚              โ”‚              โ”‚
     โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€>โ”‚              โ”‚              โ”‚              โ”‚              โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚              โ”‚              โ”‚              โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚performTrav.  โ”‚              โ”‚              โ”‚
     โ”‚              โ”‚               โ”‚              โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€>โ”‚              โ”‚              โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚              โ”‚              โ”‚              โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚ Build        โ”‚              โ”‚              โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚ DisplayList  โ”‚              โ”‚              โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€>โ”‚              โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚              โ”‚              โ”‚              โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚              โ”‚ acquireBuf()โ”‚              โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚              โ”‚<โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚              โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚              โ”‚              โ”‚              โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚              โ”‚ updateTexImageโ”‚              โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚              โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€>              โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚              โ”‚              โ”‚              โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚              โ”‚ *** GPU COPY ***โ”‚              โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚              โ”‚ OES โ†’ WindowBufโ”‚              โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚              โ”‚              โ”‚              โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚              โ”‚ queueBuffer()โ”‚              โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚              โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€>โ”‚              โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚              โ”‚              โ”‚              โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚              โ”‚              โ”‚ acquireBuf()โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚              โ”‚              โ”‚<โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚              โ”‚              โ”‚              โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚              โ”‚              โ”‚ HWC Compose โ”‚
     โ”‚              โ”‚               โ”‚              โ”‚              โ”‚              โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€>โ”‚

Trace Characteristics:

  • onFrameAvailable on Main Thread โ†’ callback from BufferQueue
  • performTraversals on Main Thread โ†’ View system traversal
  • updateTexImage on RenderThread โ†’ Bind Buffer as OES texture
  • drawTexture / drawRenderNode on RenderThread โ†’ GPU Copy operation
  • queueBuffer twice: once for Flutter content (SurfaceTexture), once for App window (BLASTBufferQueue)

Performance Impact:

  • Extra GPU Copy: Fragment Shader samples from OES texture and writes to window FrameBuffer
  • Memory Bandwidth: Each frame consumes additional GPU bandwidth for texture sampling
  • Vsync Delay: Flutter content may be one frame behind (produced at T, consumed at T+16.6ms)

Impeller (3.27/3.29) - SurfaceView Mode

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Flutter App Process                                                         โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ [1.ui/main]     Dart code โ†’ Impeller Layer tree                           โ”‚
โ”‚                      โ†“                                                      โ”‚
โ”‚ [1.raster]      Impeller rendering โ†’ GPU commands (Vulkan/Metal)          โ”‚
โ”‚                      โ†“                                                      โ”‚
โ”‚                  AHardwareQueue_submit() / QueueSubmit()                  โ”‚
โ”‚                      โ””โ”€ Direct GPU command submission                    โ”‚
โ”‚                      โ””โ”€ No intermediate buffer queue                     โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                       โ†“ GPU commands
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ GPU (Vulkan/Metal)                                                          โ”‚
โ”‚                      โ†“                                                      โ”‚
โ”‚ [HW Composer / WHC] Direct scanout or composition                         โ”‚
โ”‚                      โ†“                                                      โ”‚
โ”‚ Display                                                                    โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Trace Characteristics:

  • AHardwareQueue_submit or vkQueuePresentKHR on 1.raster thread
  • No queueBuffer/dequeueBuffer for rendering (reduced latency)
  • Key difference: Impeller bypasses traditional BufferQueue, submits directly to GPU

Impeller (3.27/3.29) - TextureView Mode

Same flow as Skia TextureView - TextureView still requires SurfaceTexture path:

[1.ui/main] โ†’ Impeller rendering โ†’ GPU texture upload โ†’ [JNIOnload] updateTexImage()
                            โ†“
                   Then View system โ†’ Window BLASTBufferQueue โ†’ SF โ†’ Display

Note: TextureView mode cannot benefit from Impeller's direct GPU submission due to OpenGL texture requirement. Still goes through full View composition path.

Summary Table

ModeFlutter VersionBuffer FlowKey ThreadsOverhead
SurfaceView3.19 (Skia)1.raster โ†’ BufferQueue โ†’ SF โ†’ Display1.raster, SF MainMedium
SurfaceView3.27/3.29 (Impeller)1.raster โ†’ GPU (direct) โ†’ Display1.raster, GPULow
TextureViewAll1.raster โ†’ GPU Texture โ†’ JNIOnload โ†’ View System โ†’ Window BufferQueue โ†’ SF1.raster, JNIOnload, MainHigh

Performance Analysis Key Metrics

When analyzing in Perfetto, focus on these slices:

Metric3.19/3.27 (Non-Merged)3.29 (Merged)
Frame Build TimeIn BuildFrame slice on 1.ui threadIn BuildFrame slice on main thread
JNI Overheadmain โ†” 1.ui inter-thread communicationNo cross-thread communication
GPU Submission1.ui โ†’ commands โ†’ 1.raster โ†’ SurfaceFlingermain โ†’ commands โ†’ 1.raster โ†’ SurfaceFlinger
Buffer ExchangequeueBuffer/dequeueBuffer on 1.raster (SurfaceView)QueueSubmit on 1.raster (SurfaceView)
Frame IntervalMore stable frame intervalPotentially tighter frame interval

Note: For TextureView mode, updateTexImage is called on JNIOnload thread regardless of Flutter version.

Trace Characteristics Under Load Testing

Build Load (Widget.build() computation)

  • 3.19/3.27: BuildFrame slice on 1.ui thread becomes longer
  • 3.29: BuildFrame slice on main thread becomes longer

Paint Load (CustomPainter GPU drawing)

  • All versions: 1.raster thread activity increases
  • 3.27/3.29: More efficient GPU command submission

PostFrame Load (inter-frame computation)

  • 3.19/3.27: May affect next frame's 1.ui thread scheduling
  • 3.29: Directly affects main thread, may compete with other main thread operations

APK Version Reference

App NameFlutter VersionRender ModePackage Name
Flu-V319-Surface3.19 (Skia)SurfaceViewcom.example.friendscircle.v19
Flu-V319-Texture3.19 (Skia)TextureViewcom.example.friendscircle.v19.textureview
Flu-V327-Surface3.27 (Impeller)SurfaceViewcom.example.friendscircle.v27
Flu-V327-Texture3.27 (Impeller)TextureViewcom.example.friendscircle.v27.textureview
Flu-V329-Surface3.29 (Impeller+Merged)SurfaceViewcom.example.friendscircle.v29
Flu-V329-Texture3.29 (Impeller+Merged)TextureViewcom.example.friendscircle.v29.textureview

Load Types

Each app supports 13 load types:

CategoryLoad TypesDescription
BaselineMinimalNo extra computation, performance baseline
Build (In-Frame)Light / Medium / HeavyCPU computation in Widget.build() phase
Paint (In-Frame GPU)Light / Medium / HeavyGPU drawing in CustomPainter.paint() phase
PostFrame (Between-Frames)Light / Medium / HeavyCPU computation after frame rendering
Mixed (Combined)Light / Medium / HeavyBuild + PostFrame combined load

ADB Quick Launch

# Format
adb shell am start -n <package_name>/.MainActivity -e "load" "<load_type>"

# Example: Launch 3.27 SurfaceView + Build Heavy
adb shell am start -n com.example.friendscircle.v27/.MainActivity -e "load" "build_heavy"

# Example: Launch 3.29 TextureView + Paint Heavy
adb shell am start -n com.example.friendscircle.v29.textureview/.MainActivity -e "load" "paint_heavy"

Project Structure

FriendsCircle_Flutter/
โ”œโ”€โ”€ shared/                # Single source of truth for all Dart code and assets
โ”‚   โ”œโ”€โ”€ lib/               # 18 Dart files (3,749 LOC), parameterized via --dart-define
โ”‚   โ”‚   โ”œโ”€โ”€ main.dart
โ”‚   โ”‚   โ”œโ”€โ”€ constants.dart
โ”‚   โ”‚   โ”œโ”€โ”€ screens/
โ”‚   โ”‚   โ”œโ”€โ”€ data/
โ”‚   โ”‚   โ”œโ”€โ”€ models/
โ”‚   โ”‚   โ”œโ”€โ”€ utils/
โ”‚   โ”‚   โ””โ”€โ”€ widgets/
โ”‚   โ””โ”€โ”€ assets/            # Shared avatars and images
โ”œโ”€โ”€ 3.19_SurfaceView/      # Flutter 3.19 + SurfaceView (Skia)
โ”œโ”€โ”€ 3.19_TextureView/      # Flutter 3.19 + TextureView (Skia)
โ”œโ”€โ”€ 3.27_SurfaceView/      # Flutter 3.27 + SurfaceView (Impeller)
โ”œโ”€โ”€ 3.27_TextureView/      # Flutter 3.27 + TextureView (Impeller)
โ”œโ”€โ”€ 3.29_SurfaceView/      # Flutter 3.29 + SurfaceView (Impeller + Main Thread Merger)
โ”œโ”€โ”€ 3.29_TextureView/      # Flutter 3.29 + TextureView (Impeller + Main Thread Merger)
โ”œโ”€โ”€ build_release.sh       # One-click build script
โ”œโ”€โ”€ install_apks.sh        # Batch install script
โ”œโ”€โ”€ quick_launch.sh        # Quick launch script
โ”œโ”€โ”€ check_env.sh           # Environment verification script
โ”œโ”€โ”€ .github/workflows/     # GitHub Actions CI/CD
โ””โ”€โ”€ apk-release/           # APK output directory

Architecture: Single Source with Build-Time Configuration

All 6 variants share the same Dart source code via symlinks:

  • Each variant's lib/ and assets/ are symlinks to ../shared/lib and ../shared/assets
  • Each variant retains its own pubspec.yaml (Flutter version constraint) and android/ (namespace, applicationId)
  • Runtime differences are parameterized via --dart-define compile-time constants:
ConstantDescriptionExample
FLUTTER_VERSIONFlutter version label3.19, 3.27, 3.29
RENDER_MODERendering surface typeSurfaceView, TextureView
PACKAGE_NAMEAndroid application IDcom.example.friendscircle.v27

Build Instructions

Using FVM to Manage Multiple Flutter Versions

# Install FVM
brew install fvm

# Install Flutter versions
fvm install 3.19.0
fvm install 3.27.0
fvm install 3.29.0

# Each project is configured with the corresponding Flutter version
# Simply run the build script
./build_release.sh

Manual Build (Single Variant)

cd 3.27_SurfaceView
fvm flutter build apk --release \
    --dart-define=FLUTTER_VERSION=3.27 \
    --dart-define=RENDER_MODE=SurfaceView \
    --dart-define=PACKAGE_NAME=com.example.friendscircle.v27

Load Parameter Configuration

Design references HighPerformanceFriendsCircle native project. Gradual progression with probability-based triggering, frame interval control, and scroll awareness.

Build Load Iterations (In-Frame, ratio 1:4:10)

LevelIterations
Light2,000
Medium8,000
Heavy20,000

Paint Load Shape Count (Flutter-only)

LevelShape CountPath PointsShadowBlur
Light5010โŒโŒ
Medium20050โœ…โŒ
Heavy800200โœ…โœ…

PostFrame Load Iterations (Between-Frame, ratio 1:3:6)

LevelIterations
Light5,000
Medium15,000
Heavy30,000

Mixed Load Combination (Build + PostFrame = In-Frame)

LevelBuild PartPostFrame PartTotal = In-Frame
Light1,0001,0002,000
Medium4,0004,0008,000
Heavy10,00010,00020,000

Trigger Probability (aligned with native)

LevelProbabilityMultiplier
Light32%1.0x
Medium48%1.5x
Heavy72%2.25x
Mixed Boostร—1.20-

Frame Interval (aligned with native 3~5 frame interval)

LevelMin FramesMax Frames
All levels35

Scroll Awareness

All synthetic load types are gated by scroll phase:

  • Press-drag/manual drag interval: no Build, Paint, PostFrame, or Mixed load is executed.
  • Ballistic/inertial interval after finger release: load execution is enabled with the probability and frame-interval controls above.
  • Idle interval: load execution stops immediately.

License

MIT License