aboutsummaryrefslogtreecommitdiffstats
path: root/examples/quick/scenegraph
diff options
context:
space:
mode:
authorFrederik Gladhorn <frederik.gladhorn@digia.com>2014-07-02 09:58:13 +0200
committerFrederik Gladhorn <frederik.gladhorn@digia.com>2014-07-02 10:32:33 +0200
commit41e924eb6e1c690bc81d095fc5e8f57244aae964 (patch)
treeb459dfa150530443901be68471df88c69ca7a7d4 /examples/quick/scenegraph
parentd51afdb74fdcb9a5c7e1fdaa763325c78d794d06 (diff)
parent87a5889029aed8c53a4b02a42804d036614db36b (diff)
Merge remote-tracking branch 'origin/5.3' into dev
Conflicts: .qmake.conf examples/quick/scenegraph/openglunderqml/squircle.h src/quick/doc/src/qmltypereference.qdoc src/quick/scenegraph/qsgthreadedrenderloop.cpp Change-Id: Ife4f4b897044a7ffcd0710493c6aed1d87cf1ef9
Diffstat (limited to 'examples/quick/scenegraph')
-rw-r--r--examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc113
-rw-r--r--examples/quick/scenegraph/openglunderqml/squircle.cpp74
-rw-r--r--examples/quick/scenegraph/openglunderqml/squircle.h33
3 files changed, 116 insertions, 104 deletions
diff --git a/examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc b/examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc
index 1f87412aa4..de023ff95c 100644
--- a/examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc
+++ b/examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc
@@ -50,54 +50,40 @@
in the QML file and this value is used by the OpenGL shader
program that draws the squircles.
+ \snippet scenegraph/openglunderqml/squircle.h 2
+
+ First of all, we need an object we can expose to QML. This is a
+ subclass of QQuickItem so we can easily access \l QQuickItem::window().
+
\snippet scenegraph/openglunderqml/squircle.h 1
- First of all, we need a QObject with a slot to connect the signals
- to. We subclass QQuickItem in order to use the \l
- QQuickItem::window() which holds the window instance we want to
- connect to.
-
- We use two values of \c t. The variable \c m_t is the property
- value as it exists in the GUI thread. The \c m_thread_t value is a
- copy of \c m_t for use in the rendering thread. We need an
- explicit copy because the scene graph can render in one thread
- while updating properties on the GUI thread in preparation for the
- next frame. If we had used only one value, the animation could
- have updated the value to that of the next frame before we got a
- chance to render it.
-
- \note In this example, a wrong value for \c t will have minimal
- consequences, but we emphasize that rendering and GUI thread
- objects and values must stay separate to avoid race conditions,
- undesired behavior and in the worst case, crashes.
+ Then we need an object to take care of the rendering. This
+ instance needs to be separated from the QQuickItem because the
+ item lives in the GUI thread and the rendering potentially happens
+ on the render thread. Since we want to connect to \l
+ QQuickWindow::beforeRendering(), we make the renderer a QObject.
+ The renderer contains a copy of all the state it needs,
+ independent of the GUI thread.
+
+ \note Don't be tempted to merge the two objects into
+ one. QQuickItems may be deleted on the GUI thread while the render
+ thread is rendering.
Lets move on to the implementation.
\snippet scenegraph/openglunderqml/squircle.cpp 7
The constructor of the \c Squircle class simply initializes the
- values. The shader program will be initialized during rendering
- later.
-
- \snippet scenegraph/openglunderqml/squircle.cpp 8
-
- The property setter checks that the value has indeed changed
- before updating its internal variable. It then calls \l
- QQuickWindow::update() which will trigger another frame to be
- rendered. Note that the setter might be called during
- initialization, before the object has been entered into the scene
- and before it has a window.
+ values and connects to the window changed signal which we will use
+ to prepare our renderer.
\snippet scenegraph/openglunderqml/squircle.cpp 1
- \snippet scenegraph/openglunderqml/squircle.cpp 2
- For our paint function to be called, we need to connect to the
- window's signals. When Squircle object is populated into the
- scene, the windowChanged signal is emitted. In our handler,
- we connect \l QQuickWindow::beforeRendering() to
- \c paint() to do the rendering, and \l
- QQuickWindow::beforeSynchronizing() to \c sync() to copy the state
- of the \c t property for the upcoming frame.
+ Once we have a window, we attach to the \l
+ QQuickWindow::beforeSynchronizing() signal which we will use to
+ create the renderer and to copy state into it safely. We also
+ connect to the \l QQuickWindow::sceneGraphInvalidated() signal to
+ handle the cleanup of the renderer.
\note Since the Squircle object has affinity to the GUI thread and
the signals are emitted from the rendering thread, it is crucial
@@ -113,18 +99,35 @@
graph, we need to turn this clearing off. This means that we need
to clear ourselves in the \c paint() function.
- \snippet scenegraph/openglunderqml/squircle.cpp 4
+ \snippet scenegraph/openglunderqml/squircle.cpp 9
+
+ We use the \c sync() function to initialize the renderer and to
+ copy the state in our item into the renderer. When the renderer is
+ created, we also connect the \l QQuickWindow::beforeRendering() to
+ the renderer's \c paint() slot.
- The first thing we do in the \c paint() function is to
- initialize the shader program. By initializing the shader program
- here, we make sure that the OpenGL context is bound and that we
- are on the correct thread.
+ \note The \l QQuickWindow::beforeSynchronizing() signal is emitted
+ on the rendering thread while the GUI thread is blocked, so it is
+ safe to simply copy the value without any additional protection.
- We also connect to the QOpenGLContext::aboutToBeDestroyed()
- signal, so that we can clean up the shader program when the
- context is destroyed. Again, this is a \l Qt::DirectConnection as
- all rendering related operations must happen on the rendering
- thread.
+ \snippet scenegraph/openglunderqml/squircle.cpp 6
+
+ In the \c cleanup() function we delete the renderer which in turn
+ cleans up its own resources.
+
+ \snippet scenegraph/openglunderqml/squircle.cpp 8
+
+ When the value of \c t changes, we call \l QQuickWindow::update()
+ rather than \l QQuickItem::update() because the former will force
+ the entire window to be redrawn, even when the scene graph has not
+ changed since the last frame.
+
+ \snippet scenegraph/openglunderqml/squircle.cpp 4
+
+ In the SquircleRenderer's \c paint() function we start by
+ initializing the shader program. By initializing the shader
+ program here, we make sure that the OpenGL context is bound and
+ that we are on the correct thread.
\snippet scenegraph/openglunderqml/squircle.cpp 5
@@ -133,18 +136,10 @@
attributes we used so that the OpenGL context is in a "clean"
state for the scene graph to pick it up.
- \snippet scenegraph/openglunderqml/squircle.cpp 6
-
- In the \c cleanup() function we delete the program.
-
- \snippet scenegraph/openglunderqml/squircle.cpp 9
-
- We use the \c sync() function to copy the state of the
- object in the GUI thread into the rendering thread.
-
- The signal is emitted on the rendering thread while the GUI
- thread is blocked, so it is safe to simply copy the value without
- any additional protection.
+ \note If tracking the changes in the OpenGL context's state is not
+ feasible, one can use the function \l
+ QQuickWindow::resetOpenGLState() which will reset all state that
+ the scene graph relies on.
\snippet scenegraph/openglunderqml/main.cpp 1
diff --git a/examples/quick/scenegraph/openglunderqml/squircle.cpp b/examples/quick/scenegraph/openglunderqml/squircle.cpp
index 5be12e6b62..9f8a27ac12 100644
--- a/examples/quick/scenegraph/openglunderqml/squircle.cpp
+++ b/examples/quick/scenegraph/openglunderqml/squircle.cpp
@@ -47,9 +47,8 @@
//! [7]
Squircle::Squircle()
- : m_program(0)
- , m_t(0)
- , m_thread_t(0)
+ : m_t(0)
+ , m_renderer(0)
{
connect(this, SIGNAL(windowChanged(QQuickWindow*)), this, SLOT(handleWindowChanged(QQuickWindow*)));
}
@@ -71,24 +70,46 @@ void Squircle::setT(qreal t)
void Squircle::handleWindowChanged(QQuickWindow *win)
{
if (win) {
-//! [1]
- // Connect the beforeRendering signal to our paint function.
- // Since this call is executed on the rendering thread it must be
- // a Qt::DirectConnection
-//! [2]
- connect(win, SIGNAL(beforeRendering()), this, SLOT(paint()), Qt::DirectConnection);
connect(win, SIGNAL(beforeSynchronizing()), this, SLOT(sync()), Qt::DirectConnection);
-//! [2]
-
+ connect(win, SIGNAL(sceneGraphInvalidated()), this, SLOT(cleanup()), Qt::DirectConnection);
+//! [1]
// If we allow QML to do the clearing, they would clear what we paint
// and nothing would show.
//! [3]
win->setClearBeforeRendering(false);
}
}
+//! [3]
+
+//! [6]
+void Squircle::cleanup()
+{
+ if (m_renderer) {
+ delete m_renderer;
+ m_renderer = 0;
+ }
+}
-//! [3] //! [4]
-void Squircle::paint()
+SquircleRenderer::~SquircleRenderer()
+{
+ delete m_program;
+}
+//! [6]
+
+//! [9]
+void Squircle::sync()
+{
+ if (!m_renderer) {
+ m_renderer = new SquircleRenderer();
+ connect(window(), SIGNAL(beforeRendering()), m_renderer, SLOT(paint()), Qt::DirectConnection);
+ }
+ m_renderer->setViewportSize(window()->size() * window()->devicePixelRatio());
+ m_renderer->setT(m_t);
+}
+//! [9]
+
+//! [4]
+void SquircleRenderer::paint()
{
if (!m_program) {
initializeOpenGLFunctions();
@@ -114,8 +135,6 @@ void Squircle::paint()
m_program->bindAttributeLocation("vertices", 0);
m_program->link();
- connect(window()->openglContext(), SIGNAL(aboutToBeDestroyed()),
- this, SLOT(cleanup()), Qt::DirectConnection);
}
//! [4] //! [5]
m_program->bind();
@@ -129,12 +148,9 @@ void Squircle::paint()
1, 1
};
m_program->setAttributeArray(0, GL_FLOAT, values, 2);
- m_program->setUniformValue("t", (float) m_thread_t);
+ m_program->setUniformValue("t", (float) m_t);
- qreal ratio = window()->devicePixelRatio();
- int w = int(ratio * window()->width());
- int h = int(ratio * window()->height());
- glViewport(0, 0, w, h);
+ glViewport(0, 0, m_viewportSize.width(), m_viewportSize.height());
glDisable(GL_DEPTH_TEST);
@@ -150,21 +166,3 @@ void Squircle::paint()
m_program->release();
}
//! [5]
-
-//! [6]
-void Squircle::cleanup()
-{
- if (m_program) {
- delete m_program;
- m_program = 0;
- }
-}
-//! [6]
-
-//! [9]
-void Squircle::sync()
-{
- m_thread_t = m_t;
-}
-//! [9]
-
diff --git a/examples/quick/scenegraph/openglunderqml/squircle.h b/examples/quick/scenegraph/openglunderqml/squircle.h
index 203a174f3c..d557339155 100644
--- a/examples/quick/scenegraph/openglunderqml/squircle.h
+++ b/examples/quick/scenegraph/openglunderqml/squircle.h
@@ -46,11 +46,33 @@
#include <QtGui/QOpenGLShaderProgram>
#include <QtGui/QOpenGLFunctions>
+
+
//! [1]
-class Squircle : public QQuickItem, protected QOpenGLFunctions
+class SquircleRenderer : public QObject, protected QOpenGLFunctions
{
Q_OBJECT
+public:
+ SquircleRenderer() : m_t(0), m_program(0) { }
+ ~SquircleRenderer();
+ void setT(qreal t) { m_t = t; }
+ void setViewportSize(const QSize &size) { m_viewportSize = size; }
+
+public slots:
+ void paint();
+
+private:
+ QSize m_viewportSize;
+ qreal m_t;
+ QOpenGLShaderProgram *m_program;
+};
+//! [1]
+
+//! [2]
+class Squircle : public QQuickItem
+{
+ Q_OBJECT
Q_PROPERTY(qreal t READ t WRITE setT NOTIFY tChanged)
public:
@@ -63,19 +85,16 @@ signals:
void tChanged();
public slots:
- void paint();
- void cleanup();
void sync();
+ void cleanup();
private slots:
void handleWindowChanged(QQuickWindow *win);
private:
- QOpenGLShaderProgram *m_program;
-
qreal m_t;
- qreal m_thread_t;
+ SquircleRenderer *m_renderer;
};
-//! [1]
+//! [2]
#endif // SQUIRCLE_H