<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html> <html lang="en"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>threadrenderer.cpp Example File | Qt Quick 5.9</title> <link rel="stylesheet" type="text/css" href="style/offline-simple.css" /> <script type="text/javascript"> document.getElementsByTagName("link").item(0).setAttribute("href", "style/offline.css"); // loading style sheet breaks anchors that were jumped to before // so force jumping to anchor again setTimeout(function() { var anchor = location.hash; // need to jump to different anchor first (e.g. none) location.hash = "#"; setTimeout(function() { location.hash = anchor; }, 0); }, 0); </script> </head> <body> <div class="header" id="qtdocheader"> <div class="main"> <div class="main-rounded"> <div class="navigationbar"> <table><tr> <td >Qt 5.9</td><td ><a href="qtquick-index.html">Qt Quick</a></td><td ><a href="qtquick-scenegraph-textureinthread-example.html">Scene Graph - Rendering FBOs in a thread</a></td><td >threadrenderer.cpp Example File</td></tr></table><table class="buildversion"><tr> <td id="buildversion" width="100%" align="right">Qt 5.9.4 Reference Documentation</td> </tr></table> </div> </div> <div class="content"> <div class="line"> <div class="content mainContent"> <div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div> <h1 class="title">threadrenderer.cpp Example File</h1> <span class="subtitle">scenegraph/textureinthread/threadrenderer.cpp</span> <!-- $$$scenegraph/textureinthread/threadrenderer.cpp-description --> <div class="descr"> <a name="details"></a> <pre class="cpp"> <span class="comment">/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** BSD License Usage ** Alternatively, you may use this file under the terms of the BSD license ** as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of The Qt Company Ltd nor the names of its ** contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/</span> <span class="preprocessor">#include "threadrenderer.h"</span> <span class="preprocessor">#include "logorenderer.h"</span> <span class="preprocessor">#include <QtCore/QMutex></span> <span class="preprocessor">#include <QtCore/QThread></span> <span class="preprocessor">#include <QtGui/QOpenGLContext></span> <span class="preprocessor">#include <QtGui/QOpenGLFramebufferObject></span> <span class="preprocessor">#include <QtGui/QGuiApplication></span> <span class="preprocessor">#include <QtGui/QOffscreenSurface></span> <span class="preprocessor">#include <QtQuick/QQuickWindow></span> <span class="preprocessor">#include <qsgsimpletexturenode.h></span> <span class="type">QList</span><span class="operator"><</span><span class="type">QThread</span> <span class="operator">*</span><span class="operator">></span> ThreadRenderer<span class="operator">::</span>threads; <span class="comment">/* * The render thread shares a context with the scene graph and will * render into two separate FBOs, one to use for display and one * to use for rendering */</span> <span class="keyword">class</span> RenderThread : <span class="keyword">public</span> <span class="type">QThread</span> { Q_OBJECT <span class="keyword">public</span>: RenderThread(<span class="keyword">const</span> <span class="type">QSize</span> <span class="operator">&</span>size) : surface(<span class="number">0</span>) <span class="operator">,</span> context(<span class="number">0</span>) <span class="operator">,</span> m_renderFbo(<span class="number">0</span>) <span class="operator">,</span> m_displayFbo(<span class="number">0</span>) <span class="operator">,</span> m_logoRenderer(<span class="number">0</span>) <span class="operator">,</span> m_size(size) { ThreadRenderer<span class="operator">::</span>threads <span class="operator"><</span><span class="operator"><</span> <span class="keyword">this</span>; } <span class="type">QOffscreenSurface</span> <span class="operator">*</span>surface; <span class="type">QOpenGLContext</span> <span class="operator">*</span>context; <span class="keyword">public</span> <span class="keyword">slots</span>: <span class="type">void</span> renderNext() { context<span class="operator">-</span><span class="operator">></span>makeCurrent(surface); <span class="keyword">if</span> (<span class="operator">!</span>m_renderFbo) { <span class="comment">// Initialize the buffers and renderer</span> <span class="type">QOpenGLFramebufferObjectFormat</span> format; format<span class="operator">.</span>setAttachment(<span class="type">QOpenGLFramebufferObject</span><span class="operator">::</span>CombinedDepthStencil); m_renderFbo <span class="operator">=</span> <span class="keyword">new</span> <span class="type">QOpenGLFramebufferObject</span>(m_size<span class="operator">,</span> format); m_displayFbo <span class="operator">=</span> <span class="keyword">new</span> <span class="type">QOpenGLFramebufferObject</span>(m_size<span class="operator">,</span> format); m_logoRenderer <span class="operator">=</span> <span class="keyword">new</span> LogoRenderer(); m_logoRenderer<span class="operator">-</span><span class="operator">></span>initialize(); } m_renderFbo<span class="operator">-</span><span class="operator">></span>bind(); context<span class="operator">-</span><span class="operator">></span>functions()<span class="operator">-</span><span class="operator">></span>glViewport(<span class="number">0</span><span class="operator">,</span> <span class="number">0</span><span class="operator">,</span> m_size<span class="operator">.</span>width()<span class="operator">,</span> m_size<span class="operator">.</span>height()); m_logoRenderer<span class="operator">-</span><span class="operator">></span>render(); <span class="comment">// We need to flush the contents to the FBO before posting</span> <span class="comment">// the texture to the other thread, otherwise, we might</span> <span class="comment">// get unexpected results.</span> context<span class="operator">-</span><span class="operator">></span>functions()<span class="operator">-</span><span class="operator">></span>glFlush(); m_renderFbo<span class="operator">-</span><span class="operator">></span>bindDefault(); qSwap(m_renderFbo<span class="operator">,</span> m_displayFbo); <span class="keyword">emit</span> textureReady(m_displayFbo<span class="operator">-</span><span class="operator">></span>texture()<span class="operator">,</span> m_size); } <span class="type">void</span> shutDown() { context<span class="operator">-</span><span class="operator">></span>makeCurrent(surface); <span class="keyword">delete</span> m_renderFbo; <span class="keyword">delete</span> m_displayFbo; <span class="keyword">delete</span> m_logoRenderer; context<span class="operator">-</span><span class="operator">></span>doneCurrent(); <span class="keyword">delete</span> context; <span class="comment">// schedule this to be deleted only after we're done cleaning up</span> surface<span class="operator">-</span><span class="operator">></span>deleteLater(); <span class="comment">// Stop event processing, move the thread to GUI and make sure it is deleted.</span> exit(); moveToThread(<span class="type">QGuiApplication</span><span class="operator">::</span>instance()<span class="operator">-</span><span class="operator">></span>thread()); } <span class="keyword">signals</span>: <span class="type">void</span> textureReady(<span class="type">int</span> id<span class="operator">,</span> <span class="keyword">const</span> <span class="type">QSize</span> <span class="operator">&</span>size); <span class="keyword">private</span>: <span class="type">QOpenGLFramebufferObject</span> <span class="operator">*</span>m_renderFbo; <span class="type">QOpenGLFramebufferObject</span> <span class="operator">*</span>m_displayFbo; LogoRenderer <span class="operator">*</span>m_logoRenderer; <span class="type">QSize</span> m_size; }; <span class="keyword">class</span> TextureNode : <span class="keyword">public</span> <span class="type">QObject</span><span class="operator">,</span> <span class="keyword">public</span> <span class="type"><a href="qsgsimpletexturenode.html">QSGSimpleTextureNode</a></span> { Q_OBJECT <span class="keyword">public</span>: TextureNode(<span class="type"><a href="qquickwindow.html">QQuickWindow</a></span> <span class="operator">*</span>window) : m_id(<span class="number">0</span>) <span class="operator">,</span> m_size(<span class="number">0</span><span class="operator">,</span> <span class="number">0</span>) <span class="operator">,</span> m_texture(<span class="number">0</span>) <span class="operator">,</span> m_window(window) { <span class="comment">// Our texture node must have a texture, so use the default 0 texture.</span> m_texture <span class="operator">=</span> m_window<span class="operator">-</span><span class="operator">></span>createTextureFromId(<span class="number">0</span><span class="operator">,</span> <span class="type">QSize</span>(<span class="number">1</span><span class="operator">,</span> <span class="number">1</span>)); setTexture(m_texture); setFiltering(<span class="type"><a href="qsgtexture.html">QSGTexture</a></span><span class="operator">::</span>Linear); } <span class="operator">~</span>TextureNode() { <span class="keyword">delete</span> m_texture; } <span class="keyword">signals</span>: <span class="type">void</span> textureInUse(); <span class="type">void</span> pendingNewTexture(); <span class="keyword">public</span> <span class="keyword">slots</span>: <span class="comment">// This function gets called on the FBO rendering thread and will store the</span> <span class="comment">// texture id and size and schedule an update on the window.</span> <span class="type">void</span> newTexture(<span class="type">int</span> id<span class="operator">,</span> <span class="keyword">const</span> <span class="type">QSize</span> <span class="operator">&</span>size) { m_mutex<span class="operator">.</span>lock(); m_id <span class="operator">=</span> id; m_size <span class="operator">=</span> size; m_mutex<span class="operator">.</span>unlock(); <span class="comment">// We cannot call QQuickWindow::update directly here, as this is only allowed</span> <span class="comment">// from the rendering thread or GUI thread.</span> <span class="keyword">emit</span> pendingNewTexture(); } <span class="comment">// Before the scene graph starts to render, we update to the pending texture</span> <span class="type">void</span> prepareNode() { m_mutex<span class="operator">.</span>lock(); <span class="type">int</span> newId <span class="operator">=</span> m_id; <span class="type">QSize</span> size <span class="operator">=</span> m_size; m_id <span class="operator">=</span> <span class="number">0</span>; m_mutex<span class="operator">.</span>unlock(); <span class="keyword">if</span> (newId) { <span class="keyword">delete</span> m_texture; <span class="comment">// note: include QQuickWindow::TextureHasAlphaChannel if the rendered content</span> <span class="comment">// has alpha.</span> m_texture <span class="operator">=</span> m_window<span class="operator">-</span><span class="operator">></span>createTextureFromId(newId<span class="operator">,</span> size); setTexture(m_texture); markDirty(DirtyMaterial); <span class="comment">// This will notify the rendering thread that the texture is now being rendered</span> <span class="comment">// and it can start rendering to the other one.</span> <span class="keyword">emit</span> textureInUse(); } } <span class="keyword">private</span>: <span class="type">int</span> m_id; <span class="type">QSize</span> m_size; <span class="type">QMutex</span> m_mutex; <span class="type"><a href="qsgtexture.html">QSGTexture</a></span> <span class="operator">*</span>m_texture; <span class="type"><a href="qquickwindow.html">QQuickWindow</a></span> <span class="operator">*</span>m_window; }; ThreadRenderer<span class="operator">::</span>ThreadRenderer() : m_renderThread(<span class="number">0</span>) { setFlag(ItemHasContents<span class="operator">,</span> <span class="keyword">true</span>); m_renderThread <span class="operator">=</span> <span class="keyword">new</span> RenderThread(<span class="type">QSize</span>(<span class="number">512</span><span class="operator">,</span> <span class="number">512</span>)); } <span class="type">void</span> ThreadRenderer<span class="operator">::</span>ready() { m_renderThread<span class="operator">-</span><span class="operator">></span>surface <span class="operator">=</span> <span class="keyword">new</span> <span class="type">QOffscreenSurface</span>(); m_renderThread<span class="operator">-</span><span class="operator">></span>surface<span class="operator">-</span><span class="operator">></span>setFormat(m_renderThread<span class="operator">-</span><span class="operator">></span>context<span class="operator">-</span><span class="operator">></span>format()); m_renderThread<span class="operator">-</span><span class="operator">></span>surface<span class="operator">-</span><span class="operator">></span>create(); m_renderThread<span class="operator">-</span><span class="operator">></span>moveToThread(m_renderThread); connect(window()<span class="operator">,</span> <span class="operator">&</span><span class="type"><a href="qquickwindow.html">QQuickWindow</a></span><span class="operator">::</span>sceneGraphInvalidated<span class="operator">,</span> m_renderThread<span class="operator">,</span> <span class="operator">&</span>RenderThread<span class="operator">::</span>shutDown<span class="operator">,</span> <span class="type">Qt</span><span class="operator">::</span>QueuedConnection); m_renderThread<span class="operator">-</span><span class="operator">></span>start(); update(); } <span class="type"><a href="qsgnode.html">QSGNode</a></span> <span class="operator">*</span>ThreadRenderer<span class="operator">::</span>updatePaintNode(<span class="type"><a href="qsgnode.html">QSGNode</a></span> <span class="operator">*</span>oldNode<span class="operator">,</span> UpdatePaintNodeData <span class="operator">*</span>) { TextureNode <span class="operator">*</span>node <span class="operator">=</span> <span class="keyword">static_cast</span><span class="operator"><</span>TextureNode <span class="operator">*</span><span class="operator">></span>(oldNode); <span class="keyword">if</span> (<span class="operator">!</span>m_renderThread<span class="operator">-</span><span class="operator">></span>context) { <span class="type">QOpenGLContext</span> <span class="operator">*</span>current <span class="operator">=</span> window()<span class="operator">-</span><span class="operator">></span>openglContext(); <span class="comment">// Some GL implementations requres that the currently bound context is</span> <span class="comment">// made non-current before we set up sharing, so we doneCurrent here</span> <span class="comment">// and makeCurrent down below while setting up our own context.</span> current<span class="operator">-</span><span class="operator">></span>doneCurrent(); m_renderThread<span class="operator">-</span><span class="operator">></span>context <span class="operator">=</span> <span class="keyword">new</span> <span class="type">QOpenGLContext</span>(); m_renderThread<span class="operator">-</span><span class="operator">></span>context<span class="operator">-</span><span class="operator">></span>setFormat(current<span class="operator">-</span><span class="operator">></span>format()); m_renderThread<span class="operator">-</span><span class="operator">></span>context<span class="operator">-</span><span class="operator">></span>setShareContext(current); m_renderThread<span class="operator">-</span><span class="operator">></span>context<span class="operator">-</span><span class="operator">></span>create(); m_renderThread<span class="operator">-</span><span class="operator">></span>context<span class="operator">-</span><span class="operator">></span>moveToThread(m_renderThread); current<span class="operator">-</span><span class="operator">></span>makeCurrent(window()); <span class="type">QMetaObject</span><span class="operator">::</span>invokeMethod(<span class="keyword">this</span><span class="operator">,</span> <span class="string">"ready"</span>); <span class="keyword">return</span> <span class="number">0</span>; } <span class="keyword">if</span> (<span class="operator">!</span>node) { node <span class="operator">=</span> <span class="keyword">new</span> TextureNode(window()); <span class="comment">/* Set up connections to get the production of FBO textures in sync with vsync on the * rendering thread. * * When a new texture is ready on the rendering thread, we use a direct connection to * the texture node to let it know a new texture can be used. The node will then * emit pendingNewTexture which we bind to QQuickWindow::update to schedule a redraw. * * When the scene graph starts rendering the next frame, the prepareNode() function * is used to update the node with the new texture. Once it completes, it emits * textureInUse() which we connect to the FBO rendering thread's renderNext() to have * it start producing content into its current "back buffer". * * This FBO rendering pipeline is throttled by vsync on the scene graph rendering thread. */</span> connect(m_renderThread<span class="operator">,</span> <span class="operator">&</span>RenderThread<span class="operator">::</span>textureReady<span class="operator">,</span> node<span class="operator">,</span> <span class="operator">&</span>TextureNode<span class="operator">::</span>newTexture<span class="operator">,</span> <span class="type">Qt</span><span class="operator">::</span>DirectConnection); connect(node<span class="operator">,</span> <span class="operator">&</span>TextureNode<span class="operator">::</span>pendingNewTexture<span class="operator">,</span> window()<span class="operator">,</span> <span class="operator">&</span><span class="type"><a href="qquickwindow.html">QQuickWindow</a></span><span class="operator">::</span>update<span class="operator">,</span> <span class="type">Qt</span><span class="operator">::</span>QueuedConnection); connect(window()<span class="operator">,</span> <span class="operator">&</span><span class="type"><a href="qquickwindow.html">QQuickWindow</a></span><span class="operator">::</span>beforeRendering<span class="operator">,</span> node<span class="operator">,</span> <span class="operator">&</span>TextureNode<span class="operator">::</span>prepareNode<span class="operator">,</span> <span class="type">Qt</span><span class="operator">::</span>DirectConnection); connect(node<span class="operator">,</span> <span class="operator">&</span>TextureNode<span class="operator">::</span>textureInUse<span class="operator">,</span> m_renderThread<span class="operator">,</span> <span class="operator">&</span>RenderThread<span class="operator">::</span>renderNext<span class="operator">,</span> <span class="type">Qt</span><span class="operator">::</span>QueuedConnection); <span class="comment">// Get the production of FBO textures started..</span> <span class="type">QMetaObject</span><span class="operator">::</span>invokeMethod(m_renderThread<span class="operator">,</span> <span class="string">"renderNext"</span><span class="operator">,</span> <span class="type">Qt</span><span class="operator">::</span>QueuedConnection); } node<span class="operator">-</span><span class="operator">></span>setRect(boundingRect()); <span class="keyword">return</span> node; } <span class="preprocessor">#include "threadrenderer.moc"</span> </pre> </div> <!-- @@@scenegraph/textureinthread/threadrenderer.cpp --> </div> </div> </div> </div> </div> <div class="footer"> <p> <acronym title="Copyright">©</acronym> 2017 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners.<br> The documentation provided herein is licensed under the terms of the <a href="http://www.gnu.org/licenses/fdl.html">GNU Free Documentation License version 1.3</a> as published by the Free Software Foundation.<br> Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners. </p> </div> </body> </html>