我的公司有一个基于QML的应用程序,它使用基于OpenGL的自定义渲染插件(MyGame
)显示一些内容。这个插件有一些关键需求:
我们现在的代码尊重(1)和(2),但是失败了(3);该插件不会始终如一地进行直观更新。 (更新是不稳定的,估计为5-10Hz。)我相信我们基于QQuickFramebufferObject
创建的插件没有正确利用Qt / QML如何更新场景图。< / p>
如何重新构建我的插件,以便获得以上所有三个?
概述:
QQuickFramebufferObject
(MyPlugin
)和QQuickFramebufferObject::Renderer
(MyRenderer
)。MyRenderer::render()
后,它会调用MyGame::Redraw()
本身,然后调用update()
。MyGame::Redraw()
执行所需操作,并且在可以接受更改的正确位置,在timeToMakeChanges
上发出MyPlugin
QML信号。onTimeToMakeChanges
信号并调用插件上影响MyGame的方法。要解决低频视觉更新的问题,我发现如果我在我的插件上覆盖QML Canvas
并使用Timer
频繁重绘画布,我的插件就开始了在看起来大约60Hz的情况下进行视觉更新。显然,这是一个严重的黑客攻击。
以下是代码设置的摘要。请原谅丢失/错误的代码;我正在尝试将数千行胶水代码提炼到这个问题的基本要素。
MyPlugin.h
#include <QOpenGLFramebufferObject>
#include <QQuickFramebufferObject>
class MyPlugin : public QQuickFramebufferObject {
Q_OBJECT
public:
MyPlugin();
virtual ~MyPlugin();
virtual QQuickFramebufferObject::Renderer* createRenderer() const;
signals:
void timeToMakeChanges();
public slots:
void makeChanges(QVariant inValue);
void HandleWindowChanged(QQuickWindow *inWindow);
private:
MyGame* GetGame() { ... }
};
MyPlugin.cpp
#include "MyPlugin.h"
#include <MyGame.h>
// ******************************************************************
class MyRenderer:
public QObject,
public QQuickFramebufferObject::Renderer,
protected QOpenGLFunctions
{
Q_OBJECT
public:
virtual void render();
private:
static void RequestGameChanges();
};
void MyRenderer::render() {
if ( !m_Initialized ) {
QOpenGLFramebufferObject *theFbo = this->framebufferObject();
InitializeGl( theFbo ); // Not shown
m_MyGame = &MyGame::Create();
m_MyGame->RegisterCallback(
reinterpret_cast<qml_Function>(MyRenderer::RequestGameChanges)
);
m_Initialized = true;
}
m_MyGame->RestoreState();
m_MyGame->Redraw();
m_MyGame->SaveState();
m_PluginItem->window()->resetOpenGLState();
// Tell QML that we want to render again as soon as possible
update();
}
// This gets invoked in the middle of m_MyGame->Redraw()
void MyRenderer::RequestGameChanges() {
emit m_PluginItem->timeToMakeChanges();
}
// ******************************************************************
MyPlugin::MyPlugin() {
setMirrorVertically(true);
connect(
this, SIGNAL(windowChanged(QQuickWindow*)),
this, SLOT(HandleWindowChanged(QQuickWindow*))
);
}
void MyPlugin::HandleWindowChanged(QQuickWindow *inWindow) {
inWindow->setClearBeforeRendering(false);
}
void MyPlugin::makeChanges(QVariant inValue) {
MyGame *theGame = GetGame();
// Send the requested changes to theGame
}
QQuickFramebufferObject::Renderer* MyPlugin::createRenderer() const {
m_Renderer = new MyRenderer( *this );
}
MyApp.qml
import MyPlugin 1.0
Window {
MyPlugin {
property var queuedUpChanges: ([])
onSomeOtherSignal: queueUpChangesToMake();
onTimeToMakeChanges: makeChanges( queuedUpChanges );
}
Canvas { id:hack }
Timer {
interval:10; running:true; repeat:true
onTriggered: hack.changeWhatYouShow();
}
}
主要问题是 “如何修改我的代码,以便获得60Hz更新?” 但是,如QML所示,上面的设置要求我排队QML中的所有更改,以便能够在MyGame::Render()
的正确位置应用它们。
理想情况下,我更愿意在没有timeToMakeChanges
的情况下编写QML,例如:
MyPlugin {
onSomeOtherSignal: makeChanges( ... );
}
如果有办法实现这一目标(除了排队C ++中的更改) - 可能与synchronize()
相关的事情我很想知道它。
答案 0 :(得分:1)
我在QML中制作一个定时调用makeChanges
的计时器。但是将所有州存储在MyPlugin
中。然后,在Renderer::synchronize()
中,从MyPlugin
复制到MyRenderer
,以便MyGame
可以使用它。
(虽然,我不会在QML中做任何与游戏逻辑相关的计算)