Skip to content

[Impeller] Higher energy consumption observed vs Skia during frequent vector primitive rendering. #164607

Open

Description

@LennoxGong

Steps to reproduce

Redraw 100 circles every 1 second using flutter
Test the GPU Utilization / Power consumption.

Power Comsumption data:
Java Android skia+vulkan : 324mA
Flutter impeller+vulkan : 371mA (+15%)

Test Group:
Android Skia using Vulkan(Switching at the framework level)
Flutter Skia
Flutter Impeller+Vulkan
Flutter Impeller+OpenGLES

It is expected that the GPU usage of Impeller will increase when using Vulkan, but compared with the lower CPU usage, excessive GPU usage will lead to an increase in the overall power consumption of the application.
The result of the power consumption test is about 15% higher overall.

Image

In Qualcomm Platform, we use SnapdragonProfiler to investigate the GPU Utilization.

Java Android with Skia (Vulkan)
Image

Flutter with impeller (Vulkan)
Image

Flutter with skia (OpenGLES)
Image

Flutter with impeller (OpenGLES)
Image

I created a issue last year about power consumption, but the data was for a relatively complex user interface.

see #156681

This test is only for simple graphics drawing, the data will be relatively more accurate.

Code sample

Code sample
      
import 'dart:async';

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(

        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Pure Flutter Memory'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});
  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: null,
      body: Padding(padding: EdgeInsets.only(top: 35),
      child: CircleWidget(),),
    );
  }
}

class CircleWidget extends StatefulWidget {
  const CircleWidget({Key? key}) : super(key: key);

  @override
  State<CircleWidget> createState() => _CircleWidgetState();
}

class _CircleWidgetState extends State<CircleWidget> {
    final _paint = Paint()
    ..color = Colors.blue
    ..style = PaintingStyle.fill;
    Timer? _timer;

  @override
  void initState() {
    super.initState();
    _timer = Timer.periodic(Duration(seconds: 1), (timer) {
      setState(() {});
    });
  }

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      width: 400,
      height: 400,
      child: CustomPaint(
        painter: CirclePainter(_paint),
      ),
    );
  }
}

class CirclePainter extends CustomPainter {

  final Paint _paint;

  CirclePainter(this._paint);

  @override
  void paint(Canvas canvas, Size size) {
    double radius = 1.45;
    double spacing = 0.3;
    double startX = spacing;
    double startY = spacing;
    double currentX = startX;
    double currentY = startY;
    for (int i = 0; i < 10; i++) {
      for (int j = 0; j < 10; j++) {
        canvas.drawCircle(Offset(currentX, currentY), radius, _paint);
        currentX += radius * 2 + spacing;
      }
      currentX = startX;
      currentY += radius * 2 + spacing;
    }
  }

  @override
  bool shouldRepaint(CirclePainter oldDelegate) {
    return true;
  }
}

    

What target platforms are you seeing this bug on?

Android

OS/Browser name and version | Device information

Redmi Note 14 Pro+ (Snapdragon 7s Gen3)(Android V)

Does the problem occur on emulator/simulator as well as on physical devices?

Unknown

Logs

Logs
[Paste your logs here]

Flutter Doctor output

Doctor output
[!] Flutter (Channel [user-branch], 3.29.0, on Ubuntu 24.04.2 LTS
    6.11.0-17-generic, locale zh_CN.UTF-8) [256ms]
    ! Flutter version 3.29.0 on channel [user-branch] at
      /home/mi/workspace/devkit/flutter
      Currently on an unknown channel. Run `flutter channel` to switch to an
      official channel.
      If that doesn't fix the issue, reinstall Flutter by following instructions
      at https://flutter.dev/setup.
    ! Upstream repository unknown source is not a standard remote.
      Set environment variable "FLUTTER_GIT_URL" to unknown source to dismiss
      this error.
    • Framework revision 35c388afb5 (3 周前), 2025-02-10 12:48:41 -0800
    • Engine revision 010c8a806b
    • Dart version 3.7.0 (build 3.7.0-323.0.dev)
    • DevTools version 2.42.0
    • If those were intentional, you can disregard the above warnings; however
      it is recommended to use "git" directly to perform update checks and
      upgrades.

[✓] Android toolchain - develop for Android devices (Android SDK version 35.0.0)
    [2.3s]
    • Android SDK at /home/mi/Android/Sdk
    • Platform android-35, build-tools 35.0.0
    • ANDROID_HOME = /home/mi/Android/Sdk
    • Java binary at:
      /home/mi/.local/share/JetBrains/Toolbox/apps/android-studio/jbr/bin/java
      This is the JDK bundled with the latest Android Studio installation on
      this machine.
      To manually set the JDK path, use: `flutter config
      --jdk-dir="path/to/jdk"`.
    • Java version OpenJDK Runtime Environment (build
      17.0.11+0-17.0.11b1207.24-11852314)
    • All Android licenses accepted.

[✓] Chrome - develop for the web [104ms]
    • Chrome at google-chrome

[✗] Linux toolchain - develop for Linux desktop [361ms]
    • Ubuntu clang version 18.1.3 (1ubuntu1)
    • cmake version 3.31.6
    ✗ ninja is required for Linux development.
      It is likely available from your distribution (e.g.: apt install
      ninja-build), or can be downloaded from
      https://github.com/ninja-build/ninja/releases
    • pkg-config version 1.8.1
    ✗ GTK 3.0 development libraries are required for Linux development.
      They are likely available from your distribution (e.g.: apt install
      libgtk-3-dev)

[✓] Android Studio (version 2024.2) [102ms]
    • Android Studio at
      /home/mi/.local/share/JetBrains/Toolbox/apps/android-studio
    • Flutter plugin version 81.1.3
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build
      17.0.11+0-17.0.11b1207.24-11852314)

[✓] IntelliJ IDEA Ultimate Edition (version 2024.3) [19ms]
    • IntelliJ at
      /home/mi/.local/share/JetBrains/Toolbox/apps/intellij-idea-ultimate
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart

[✓] VS Code (version 1.97.2) [18ms]
    • VS Code at /usr/share/code
    • Flutter extension version 3.106.0

[!] Proxy Configuration [47ms]
    • HTTP_PROXY is set
    • NO_PROXY is localhost,127.0.0.0/8,::1
    • NO_PROXY contains localhost
    ! NO_PROXY does not contain 127.0.0.1
    • NO_PROXY contains ::1

[✓] Connected device (2 available) [160ms]
    • Linux (desktop) • linux  • linux-x64      • Ubuntu 24.04.2 LTS
      6.11.0-17-generic
    • Chrome (web)    • chrome • web-javascript • Google Chrome 133.0.6943.141

[✓] Network resources [1,050ms]
    • All expected network resources are available.

Activity

  1. added
    in triagePresently being triaged by the triage team
    and removed on Mar 5, 2025
  2. darshankawar commented on Mar 5, 2025

    @darshankawar
    Member

    @FridayAutum
    Did you still use this device to monitor the power consumption ? Also, I am wondering how is this issue different that the linked one ?

  3. added
    waiting for responseThe Flutter team cannot make further progress on this issue until the original reporter responds
    on Mar 5, 2025
  4. LennoxGong commented on Mar 5, 2025

    @LennoxGong
    Author

    Yes. However, in the test data this time, we also tested the GPU usage. The main difference is that this test is based on the most basic graphics drawing test benchmark, without complex component construction. At the same time, we found that the number of instruction events generated by using Impeller Vulkan is much higher than that of using Skia Vulkan, which was not included in last year's test.

  5. removed
    waiting for responseThe Flutter team cannot make further progress on this issue until the original reporter responds
    on Mar 5, 2025
  6. jonahwilliams commented on Mar 5, 2025

    @jonahwilliams
  7. jonahwilliams commented on Mar 5, 2025

    @jonahwilliams
  8. added
    e: impellerImpeller rendering backend issues and features requests
    on Mar 5, 2025
  9. jonahwilliams commented on Mar 5, 2025

    @jonahwilliams
    Contributor

    oh wait no, I've misunderstood - these are separate paths. Its slightly the opposite problem - we don't make any attempt to batch up discrete calls. :)

    I'm not sure if we should, batching can be counter productive when it fails

  10. jonahwilliams commented on Mar 6, 2025

    @jonahwilliams
    Contributor

    So we can file this into problems we can more easily solve once display list is impeller only, though my personal thoughts are applications like this are kind of "benchmark bait" and I don't optimizations for them generalize very well.

  11. jonahwilliams commented on Mar 6, 2025

    @jonahwilliams
    Contributor

    Because this is a very simple example, you can get better performance just by switching to drawPoints.

    import 'dart:async';
    import 'dart:ui';
    
    import 'package:flutter/material.dart';
    
    void main() {
      runApp(const MyApp());
    }
    
    class MyApp extends StatelessWidget {
      const MyApp({super.key});
    
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
    
            colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
            useMaterial3: true,
          ),
          home: const MyHomePage(title: 'Pure Flutter Memory'),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      const MyHomePage({super.key, required this.title});
      final String title;
    
      @override
      State<MyHomePage> createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: null,
          body: Padding(padding: EdgeInsets.only(top: 35),
          child: CircleWidget(),),
        );
      }
    }
    
    class CircleWidget extends StatefulWidget {
      const CircleWidget({Key? key}) : super(key: key);
    
      @override
      State<CircleWidget> createState() => _CircleWidgetState();
    }
    
    class _CircleWidgetState extends State<CircleWidget> {
        final _paint = Paint()
        ..color = Colors.blue
        ..style = PaintingStyle.fill;
        Timer? _timer;
    
      @override
      void initState() {
        super.initState();
        _timer = Timer.periodic(Duration(milliseconds: 16), (timer) {
          setState(() {});
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return SizedBox(
          width: 400,
          height: 400,
          child: CustomPaint(
            painter: CirclePainter(_paint),
          ),
        );
      }
    }
    
    class CirclePainter extends CustomPainter {
    
      final Paint _paint;
    
      CirclePainter(this._paint);
    
      @override
      void paint(Canvas canvas, Size size) {
        final double radius = 1.45;
        double spacing = 0.3;
        double startX = spacing;
        double startY = spacing;
        double currentX = startX;
        double currentY = startY;
        var offsets = <Offset>[];
        for (int i = 0; i < 100; i++) {
          for (int j = 0; j < 100; j++) {
            offsets.add(Offset(currentX, currentY));
            //canvas.drawCircle(Offset(currentX, currentY), radius, _paint);
            currentX += radius * 2 + spacing;
          }
          currentX = startX;
          currentY += radius * 2 + spacing;
        }
        Paint paint = Paint.from(_paint);
        canvas.drawPoints(PointMode.points, offsets, paint..strokeWidth = radius);
      }
    
      @override
      bool shouldRepaint(CirclePainter oldDelegate) {
        return true;
      }
    }
    
  12. LennoxGong commented on Mar 7, 2025

    @LennoxGong
    Author

    It appears that this way of coding does yield better performance. However, based on our comprehensive tests of all relevant Canvas APIs, even when it comes to a simple line - drawing operation, Impeller Vulkan lags behind Skia Vulkan and Impeller OpenGLES in terms of GPU usage, power consumption, and the number of rendering instructions. This situation could pose a challenge for many application developers when trying to persuade their supervisors with power - consumption - related data. 😢

  13. jonahwilliams commented on Mar 7, 2025

    @jonahwilliams
    Contributor

    long term I would love to get Impeller vulkan closer to native android performance, but If your goal is lower power today you should be using that and not Flutter.

  14. jonahwilliams commented on Mar 7, 2025

    @jonahwilliams
    Contributor

    I don't want to discourage you too much, I really do want to dig into these issues you've filled more (especially if there are hardware specific issues here) but we're just very short on time these days.

  15. LennoxGong commented on Mar 7, 2025

    @LennoxGong
    Author

    I totally understand. At the same time, we also found that the problem that extends from this issue may inevitably lead to frame drops when launching more complex pages (possibly because the GPU frequency may not be raised at this time, but the tasks have been piled up).I may add this here or raise a new issue after I have done a detailed research recently. Thank you for your answer. : )

  16. jonahwilliams commented on Mar 7, 2025

    @jonahwilliams
    Contributor

    Please keep filing bugs :) I very much appreciate you putting Impeller through its paces.

  17. jonahwilliams commented on Mar 9, 2025

    @jonahwilliams
    Contributor

    https://github.com/flutter/flutter/tree/master/dev/benchmarks/macrobenchmarks contains an SVG example that has both animating and non-animating states. See "Path Tessellation" . This would probably be a more interesting case to investigate, as I suspect that masking approachs will be much slower with Skia there. That said, I could also imagine GPU utiliation remaining higher with Impeller (which is somewhat expected, given that we are trying to utilize the GPU over the CPU where possible)

  18. added
    P3Issues that are less important to the Flutter project
    on Mar 10, 2025
  19. LennoxGong commented on Mar 12, 2025

    @LennoxGong
    Author

    I was recently studying the Skia source code and found that when Skia handles canvas ops, "It will attempt to concatenate the given chain onto its own and merge ops across the chains" .This mechanism can reduce the performance pressure and power consumption at runtime to a certain extent.But I'm not sure if Impeller exists or will consider designing a similar mechanism for optimization.
    see(https://github.com/google/skia/blob/main/src/gpu/ganesh/ops/OpsTask.cpp#L291)

  20. jonahwilliams commented on Mar 12, 2025

    @jonahwilliams
    Contributor

    This is essentially what drawPoints does. So you could test this theory versus skia by changing the app source code to use a single drawPoints call to see if that changes the numbers. I'd expect lower CPU usage but similar or the same GPU utilization.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Issues that are less important to the Flutter projecte: impellerImpeller rendering backend issues and features requeststeam-engineOwned by Engine teamtriaged-engineTriaged by Engine team

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions