[SOLVED] What is the best way to call a rendering function (and game logic) for a game?
-
Hi every one!
I have started on a small game and have a question about controlling the QCoreApplication::postEvent (and QEvent(QEvent::UpdateRequest I guess). My game runs at 60 frames per second on my desktop computer. It seems that Qt automatically runs the render function at the same frequency as my screen. But on my laptop where I have my graphicscard diseable by default and have to run my game through the "optirun" program (https://wiki.ubuntu.com/Bumblebee) to use it, the game runs at ~320 fps.Since I have placed my "game logic" in the render function this makes the game run much faster on the laptop, which is a problem. I understand that I could use a Qtimer to make the rendering function execute at a fixed frame rate, but then what happens if the rendering function is not completed before next rendering event occurs? Will it queue up and eventually crash my program?
Maybe I should move out the game logic into a separate function which is controlled by a Qtimer at a fixed frequency. But then I will have the same problem with event queueing up if the function is not finished before next event occurs? Also, if I choose to separate game logic and rendering, does Qt guarantee that render and the two are not executed simultaneously and create race conditions?
Also, I think is unnecessary that my laptop render 320 fps, since the screen is updated at 50 Hz. How do I solve this?
Summary:
Should have game logic inside the render function? If not, what about race conditions between logic and rendering functions/events?
Should I use my current method to execute the render function, or should I use a Qtimer? If I use the current method, how should I make my laptop to not render 320 fps? Maybe this have to do with my drivers and not Qt.
Does Qtimer queue up events if they are not finished before next events occurs?
I run Ubuntu 12.04 on my desktop, and 13.10 on my laptop. Both has Qt 5.2 installed.
Here is a simplified version of my code.
Main function:
@
int main(int argc, char **argv)
{
QGuiApplication app(argc, argv);OpenGLWindow window; return app.exec();
}
@OpenGLWindow class:
@
class OpenGLWindow : public QWindow, protected QOpenGLFunctions
{
Q_OBJECTbool m_update_pending; bool m_animating; QOpenGLContext *m_context; QOpenGLShaderProgram m_program; QOpenGLVertexArrayObject m_vao;
public:
explicit OpenGLWindow(QWindow *parent = 0)
: QWindow(parent)
, m_update_pending(false)
, m_animating(true)
, m_context(0)
{
setSurfaceType(OpenGLSurface);
QSurfaceFormat format;
format.setMajorVersion(3);
format.setMinorVersion(3);
format.setSamples(4);
format.setProfile(QSurfaceFormat::CoreProfile);
setFormat(format);
resize(640, 480);
show();
create();m_context = new QOpenGLContext(this); m_context->setFormat(format); m_context->create(); m_context->makeCurrent(this); initializeOpenGLFunctions(); m_vao.create(); m_vao.bind(); m_program.addShaderFromSourceFile(QOpenGLShader::Vertex, "simple.vert"); m_program.addShaderFromSourceFile(QOpenGLShader::Fragment, "simple.frag"); m_program.link(); glViewport(0, 0, width(), height()); } void render() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); m_program.bind(); // Move some objects HERE (game logic) // Draw some objects HERE } void setAnimating(bool animating) { m_animating = animating; if (animating) renderLater(); }
public slots:
void renderLater()
{
if (!m_update_pending)
{
m_update_pending = true;
QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest));
}
}void renderNow() { if (!isExposed()) return; m_context->makeCurrent(this); render(); m_context->swapBuffers(this); if (m_animating) renderLater(); }
protected:
bool event(QEvent *event)
{
switch (event->type()) {
case QEvent::UpdateRequest:
m_update_pending = false;
renderNow();
return true;
default:
return QWindow::event(event);
}
}void exposeEvent(QExposeEvent *event) { Q_UNUSED(event); if (isExposed()) renderNow(); }
};
@Thanks in advance!
-
Hi felet,
- separate them using two threads and use mutexes to avoid race conditions.
- yes, use qtimer.
- "In such a case of timeout overrun, Qt will emit activated() only once, even if multiple timeouts have expired, and then will resume the original interval.", or you may want to use QTimer's SingleShot function.
-
Thanks for answering! Have some new questions:
There is no way to force two different events (or the whole program) to always be executed by the very same thread?
How do I choose a good fps rate then? Can I ask the system of the frequency of the screen through Qt?
New question: I guess I should choose a time interval for the game logic function also, and make sure the interval does not exceeds due to a busy CPU. If the interval exceeds, I could use QElapsedTimer to compensate this. Does this make sense?
-
Hi felet,
the GUI events will always be executed in the GUI thread. Other events will appear in the thread in which the slot "lives", you should read the several QThread-related postings.
You can request the screen refresh rate by using http://qt-project.org/doc/qt-5.0/qtgui/qscreen.html#refreshRate-prop .