每次更新仅调用一次 Canvas onPaint?
Invoke Canvas onPaint exactly once per update?
又名: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()
,也一样。如果我真的在 canvas 每个回调上画一些东西,也是一样的。
所以我搬到了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()
,以证明我不受 i/o 约束。
如何让我的 canvas 重绘一次 "per frame",以便我可以半准确地测量 FPS?为什么 requestPaint()
实际上不起作用?为什么 requestAnimationFrame()
显然没用?
你的方法的问题是你正在请求 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。
Qt 5.9 之前有 bug with requestAnimationFrame()
。此错误已修复。
以下代码按预期和期望的方式工作,以保持 canvas 不断重绘:
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);
}
}
又名: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()
,也一样。如果我真的在 canvas 每个回调上画一些东西,也是一样的。
所以我搬到了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()
,以证明我不受 i/o 约束。
如何让我的 canvas 重绘一次 "per frame",以便我可以半准确地测量 FPS?为什么 requestPaint()
实际上不起作用?为什么 requestAnimationFrame()
显然没用?
你的方法的问题是你正在请求 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。
Qt 5.9 之前有 bug with requestAnimationFrame()
。此错误已修复。
以下代码按预期和期望的方式工作,以保持 canvas 不断重绘:
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);
}
}