首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >dart/flutter: CustomPaint更新率低于ValueNotifier的值更新率

dart/flutter: CustomPaint更新率低于ValueNotifier的值更新率
EN

Stack Overflow用户
提问于 2020-07-12 16:59:24
回答 1查看 516关注 0票数 0

我使用dart FFI从本地检索数据,并使用flutter CustomPaint显示数据。

我使用ValueNotifier来控制CustomPaint重绘。

代码:以一定的速率轮询数据

使用状态类,我定期从本机端轮询数据,并将其分配给ValueNotifier

代码语言:javascript
运行
复制
class _ColorViewState extends State<ColorView> {
  ValueNotifier<NativeColor> _notifier;
  Timer _pollTimer;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();

    ffiInit();

    // initialize notifier
    _notifier = ValueNotifier<NativeColor>(ffiGetColor().ref);

    _pollTimer = Timer.periodic(Duration(milliseconds: 16), _pollColor);
  }

  _pollColor(Timer t) {

    setState(() {
      print('polling ...');
      _notifier.value = ffiGetColor().ref;
      print('polled: ${_notifier.value.r}, ${_notifier.value.g}, ${_notifier.value.b}');
    });
  }

  ....

}

请注意,我以大约60fps的速率进行轮询。

我将通知程序绑定到CustomPaint重绘

代码语言:javascript
运行
复制
  @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.all(10),
      width: double.infinity,
      height: double.infinity,
      color: widget.clrBackground,
      child: ClipRect(
        child: CustomPaint(
          painter: _ColorViewPainter(
              context: context,
              notifier: _notifier,
              clrBackground: Color.fromARGB(255, 255, 0, 255)
          )
        )
      )
    );
  }

代码:反应性地重新绘制CustomPaint

然后,将CustomPaint的重新绘制绑定到ValueNotifier,我用检索到的颜色绘制屏幕。

代码语言:javascript
运行
复制
class _ColorViewPainter extends CustomPainter {
  ValueNotifier<NativeColor> notifier;
  BuildContext context;
  Color clrBackground;

  _ColorViewPainter({this.context, this.notifier, this.clrBackground})
    : super(repaint: notifier) {
  }

  @override
  bool shouldRepaint(_ColorViewPainter old) {
    print('should repaint');
    return true;
  }

  @override
  void paint(Canvas canvas, Size size) {
    print("paint: start");
    final r = notifier.value.r;
    final g = notifier.value.g;
    final b = notifier.value.b;
    print("color: $r, $g, $b");
    final paint = Paint()
        ..strokeJoin = StrokeJoin.round
        ..strokeWidth = 1.0
        ..color = Color.fromARGB(255, r, g, b)
        ..style = PaintingStyle.fill;

    final width = size.width;
    final height = size.height;
    final content = Offset(0.0, 0.0) & Size(width, height);
    canvas.drawRect(content, paint);
    print("paint: end");
  }

}

然后我注意到,从视觉上看,颜色更新的速度比轮询更低。这是通过同时查看我的日志和电话屏幕来观察到的,尽管重新绘制可以正常工作。

问题

我应该如何实现感知到的同步更新?

我还应该补充说,本机后端模拟以1秒的间隔在红/绿/蓝之间切换颜色。

由于轮询的频率要高得多,我希望在大约1秒的间隔内看到相当稳定的颜色变化。但现在颜色会以较长的间隔变化。有时很少调用repaint,这可能需要几秒钟,同时轮询返回非常稳定的数据更新。

更新

根据我的测试,我应该保留setState,否则重绘就会停止。另外,通过将数据更新切换到dart land,我发现一切都按预期工作。因此,它必须是本机或FFI接口中的某项内容。以下是修改后的dart代码,它在不涉及FFI时按预期工作。

基本上,我使用一个常量颜色集合并对其进行迭代。

代码语言:javascript
运行
复制
class _ColorViewState extends State<ColorView> {
  ValueNotifier<NativeColor> _notifier;
  Timer _pollTimer;
  var _colors;
  int _step = 0;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();

    ffiInit();

    // constant colour collection
    _colors = [
      [255, 0, 0],
      [0, 255, 0],
      [0, 0, 255]
    ];

    _notifier = ValueNotifier<NativeColor>(ffiGetColor().ref);

    _pollTimer = Timer.periodic(Duration(milliseconds: 1000), _pollColor);
  }

  _pollColor(Timer t) {

    setState(() {
      print('polling ...');

//      _notifier.value = ffiGetColor().ref;

      _notifier.value.r = _colors[_step][0];
      _notifier.value.g = _colors[_step][1];
      _notifier.value.b = _colors[_step][2];
      print('polled: ${_notifier.value.r}, ${_notifier.value.g}, ${_notifier.value.b}');

      if (++_step >= _colors.length) {
        _step = 0;
      }

    });
  }

在本机方面,我有一个生产者/消费者线程模型。制片人正以固定的速度在色彩集合中循环。每当生产者发出pinged命令时,消费者就会得到它。

代码语言:javascript
运行
复制
#include <cstdlib>
#include <ctime>
#include <chrono>
#include <condition_variable>
#include <mutex>
#include <thread>

#ifdef __cplusplus
    #define EXTERNC extern "C" __attribute__((visibility("default"))) __attribute__((used))
#else
    #define EXTERNC
#endif  // #ifdef __cplusplus

struct NativeColor {
    int r;
    int g;
    int b;
};

NativeColor* gpColor = nullptr;
NativeColor gWorker = {255, 0, 255};
// producer / consumer thread tools
std::thread gThread;
std::mutex gMutex;
std::condition_variable gConVar;

int gColors[][3] = {
    {255, 0, 0},
    {0, 255, 0},
    {0, 0, 255}
};
int gCounter = 0;
int gCounterPrev = 0;

EXTERNC void ffiinit() {
    if(!gpColor) {
        gpColor = (struct NativeColor*)malloc(sizeof(struct NativeColor));
    }

    if(!gThread.joinable()) {
        gThread = std::thread([&]() {
            while(true) {
                std::this_thread::sleep_for (std::chrono::seconds(1));
                std::unique_lock<std::mutex> lock(gMutex);
                gWorker.r = gColors[gCounter][0];
                gWorker.g = gColors[gCounter][1];
                gWorker.b = gColors[gCounter][2];
                if(++gCounter == 3) {
                    gCounter = 0;
                    gCounterPrev = gCounter;
                }
                lock.unlock();
                gConVar.notify_one();
            }
        });
    }
}

EXTERNC struct NativeColor* ffiproduce() {
    // get yellow
    gpColor->r = 255;
    gpColor->g = 255;
    gpColor->b = 255;

    std::unique_lock<std::mutex> lock(gMutex);
    gConVar.wait(lock, [&]{
        return gCounter > gCounterPrev;
        //return true;
    });
    *gpColor = gWorker;
    gCounterPrev = gCounter;
    lock.unlock();
    return gpColor;
}

ffiproduce()绑定到dart端的ffiGetColor()函数。所以我假设这个消费者函数在主线程中工作。

所以我的一个想法是,也许C++端的线程协调影响了flutter通过CustomPaint渲染的方式。

但我不知道如何证明这一点。

EN

回答 1

Stack Overflow用户

发布于 2020-07-12 18:35:47

我在本机上尝试了一下睡眠功能,取得了一些进展。

以下是我的发现:

  • I曾经使用1秒的间隔从本机生产者线程中大量提取数据,并在本机端从主线程中使用这些数据。消费者必须等待生产者ping。在这种情况下,flutter渲染线程似乎处于停顿状态。
  • 通过将睡眠时间一直减少到20毫秒以下,渲染开始按预期工作。
  • 即使在20毫秒睡眠的情况下,渲染也会出现问题。

因此,我相信我的数据生成模型必须适应flutter,以确保轮询不会阻塞,或者应该以flutter的首选速率(例如,60fps )提供。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62859088

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档