每次更新时只调用一次Canvas onPaint?

时间:2016-09-06 15:57:23

标签: qt qml qt-quick

AKA:Canvas requestPaint()太慢了; requestAnimationFrame()太快

我正在尝试创建一个尽可能快地重新绘制的QML Canvas - 在主UI渲染循环中每次更新一次 - 以创建FPS计时器。

我最初写了这个简单的测试:

import QtQuick 2.7
import QtQuick.Window 2.2

Window {
    visible:true; width:100; height:100
    Canvas {
        anchors.fill:parent
        onPaint: console.log(+new Date)
    }
}

我只收到一次回调。所以我添加了requestPaint()

onPaint: {
    console.log(+new Date)
    requestPaint()
}

没有变化:我仍然只收到一个回调。如果我使用markDirty(),则相同。如果我在画布上每次回调都画了一些东西,那就相同了。

所以我搬到了requestAnimationFrame()

import QtQuick 2.7
import QtQuick.Window 2.2
Window {
    visible:true; width:100; height:100
    Canvas {
        anchors.fill:parent
        Component.onCompleted: crank()
        function crank(){
            console.log(+new Date)
            requestAnimationFrame(crank)
        }
    }
}

现在我得到回调,但太多了。平均而言,我每毫秒获得77次回调,在一毫秒内有多达127次回调。如此多的回调,应用程序中没有任何其他内容显示,甚至最初都没有。即使我删除了console.log(),也证明我没有受到限制。

如何让“我的画布”每次“每帧”重绘一次,以便我可以半精确地测量FPS?为什么requestPaint()实际上不起作用?为什么requestAnimationFrame()显然无用?

2 个答案:

答案 0 :(得分:2)

您的方法存在的问题是您要求onPaint绘画,这不起作用, 因为onPaint事件是从内部触发的 QQuickItem::polish()

void QQuickItem::polish()
{
    Q_D(QQuickItem);
    if (!d->polishScheduled) {
        d->polishScheduled = true;
        if (d->window) {
            QQuickWindowPrivate *p = QQuickWindowPrivate::get(d->window);
            bool maybeupdate = p->itemsToPolish.isEmpty();
            p->itemsToPolish.append(this);
            if (maybeupdate) d->window->maybeUpdate();
        }
    }
}

在此通话期间,d->polishScheduled设置为true,如果再次拨打requestPaint(),则不会发生任何操作。您需要异步触发它。例如,使用间隔为0的Timer

import QtQuick 2.0

Canvas {
  id: canvas
  width: 200
  height: 200
  property real angle
  property int fps

  Timer {
    id: repaintTimer
    running: false
    interval: 0
    onTriggered: {
      angle += 0.01
      canvas.requestPaint()
    }
  }

  Timer {
    interval: 1000
    running: true
    repeat: true
    onTriggered: {
      console.log(fps)
      fps = 0
    }
  }

  onPaint: {
    var ctx = getContext("2d")
    ctx.save()
    ctx.clearRect(0, 0, width, height)
    ctx.moveTo(100, 100)
    ctx.translate(100,100)
    ctx.rotate(angle)
    ctx.beginPath()
    ctx.lineTo(40, 10)
    ctx.lineTo(40, 40)
    ctx.lineTo(10, 40)
    ctx.lineTo(10, 10)
    ctx.closePath()
    ctx.stroke()
    ctx.restore()
    fps += 1
    repaintTimer.start()
  }
}

另一个Timer来记录fps。当我在qmlscene中运行此代码时,我得到60 fps。

答案 1 :(得分:1)

在Qt 5.9之前有一个bug with requestAnimationFrame()。这个错误已得到修复。

以下代码按预期工作,并希望不断重绘画布:

Canvas {
    width:100; height:100;
    property var ctx
    onAvailableChanged: if (available) ctx = getContext('2d');
    onPaint: {
        if (!ctx) return;
        ctx.clearRect(0, 0, width, height);
        // draw here
        requestAnimationFrame(paint);
    }
}