1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQuick module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include <QtQuick/private/qsgcontext_p.h>
41#include <QtQuick/private/qsgtexture_p.h>
42#include <QtQuick/private/qquickpixmapcache_p.h>
43#include <QtQuick/private/qsgadaptationlayer_p.h>
44
45#include <QGuiApplication>
46#include <QScreen>
47#include <QQuickWindow>
48
49#include <private/qqmlglobal_p.h>
50
51#include <QtQuick/private/qsgtexture_p.h>
52#include <QtGui/private/qguiapplication_p.h>
53#include <QtCore/private/qabstractanimation_p.h>
54
55#include <private/qobject_p.h>
56#include <qmutex.h>
57
58/*
59 Comments about this class from Gunnar:
60
61 The QSGContext class is right now two things.. The first is the
62 adaptation layer and central storage ground for all the things
63 in the scene graph, like textures and materials. This part really
64 belongs inside the scene graph coreapi.
65
66 The other part is the QML adaptation classes, like how to implement
67 rectangle nodes. This is not part of the scene graph core API, but
68 more part of the QML adaptation of scene graph.
69
70 If we ever move the scene graph core API into its own thing, this class
71 needs to be split in two. Right now its one because we're lazy when it comes
72 to defining plugin interfaces..
73*/
74
75QT_BEGIN_NAMESPACE
76
77// Used for very high-level info about the renderering and gl context
78// Includes GL_VERSION, type of render loop, atlas size, etc.
79Q_LOGGING_CATEGORY(QSG_LOG_INFO, "qt.scenegraph.general")
80
81// Used to debug the renderloop logic. Primarily useful for platform integrators
82// and when investigating the render loop logic.
83Q_LOGGING_CATEGORY(QSG_LOG_RENDERLOOP, "qt.scenegraph.renderloop")
84
85
86// GLSL shader compilation
87Q_LOGGING_CATEGORY(QSG_LOG_TIME_COMPILATION, "qt.scenegraph.time.compilation")
88
89// polish, animations, sync, render and swap in the render loop
90Q_LOGGING_CATEGORY(QSG_LOG_TIME_RENDERLOOP, "qt.scenegraph.time.renderloop")
91
92// Texture uploads and swizzling
93Q_LOGGING_CATEGORY(QSG_LOG_TIME_TEXTURE, "qt.scenegraph.time.texture")
94
95// Glyph preparation (only for distance fields atm)
96Q_LOGGING_CATEGORY(QSG_LOG_TIME_GLYPH, "qt.scenegraph.time.glyph")
97
98// Timing inside the renderer base class
99Q_LOGGING_CATEGORY(QSG_LOG_TIME_RENDERER, "qt.scenegraph.time.renderer")
100
101bool qsg_useConsistentTiming()
102{
103 int use = -1;
104 if (use < 0) {
105 use = !qEnvironmentVariableIsEmpty(varName: "QSG_FIXED_ANIMATION_STEP") && qgetenv(varName: "QSG_FIXED_ANIMATION_STEP") != "no"
106 ? 1 : 0;
107 qCDebug(QSG_LOG_INFO, "Using %s", bool(use) ? "fixed animation steps" : "sg animation driver");
108 }
109 return bool(use);
110}
111
112class QSGAnimationDriver : public QAnimationDriver
113{
114 Q_OBJECT
115public:
116 enum Mode {
117 VSyncMode,
118 TimerMode
119 };
120
121 QSGAnimationDriver(QObject *parent)
122 : QAnimationDriver(parent)
123 , m_time(0)
124 , m_vsync(0)
125 , m_mode(VSyncMode)
126 , m_lag(0)
127 , m_bad(0)
128 , m_good(0)
129 {
130 QScreen *screen = QGuiApplication::primaryScreen();
131 if (screen && !qsg_useConsistentTiming()) {
132 m_vsync = 1000.0 / screen->refreshRate();
133 if (m_vsync <= 0)
134 m_mode = TimerMode;
135 } else {
136 m_mode = TimerMode;
137 if (qsg_useConsistentTiming())
138 QUnifiedTimer::instance(create: true)->setConsistentTiming(true);
139 }
140 if (m_mode == VSyncMode)
141 qCDebug(QSG_LOG_INFO, "Animation Driver: using vsync: %.2f ms", m_vsync);
142 else
143 qCDebug(QSG_LOG_INFO, "Animation Driver: using walltime");
144 }
145
146 void start() override
147 {
148 m_time = 0;
149 m_timer.start();
150 m_wallTime.restart();
151 QAnimationDriver::start();
152 }
153
154 qint64 elapsed() const override
155 {
156 return m_mode == VSyncMode
157 ? qint64(m_time)
158 : qint64(m_time) + m_wallTime.elapsed();
159 }
160
161 void advance() override
162 {
163 qint64 delta = m_timer.restart();
164
165 if (m_mode == VSyncMode) {
166 // If a frame is skipped, either because rendering was slow or because
167 // the QML was slow, we accept it and continue advancing with a single
168 // vsync tick. The reason for this is that by the time we notice this
169 // on the GUI thread, the temporal distortion has already gone to screen
170 // and by catching up, we will introduce a second distortion which will
171 // worse. We accept that the animation time falls behind wall time because
172 // it comes out looking better.
173 // Only when multiple bad frames are hit in a row, do we consider
174 // switching. A few really bad frames and we switch right away. For frames
175 // just above the vsync delta, we tolerate a bit more since a buffered
176 // driver can have vsync deltas on the form: 4, 21, 21, 2, 23, 16, and
177 // still manage to put the frames to screen at 16 ms intervals. In addition
178 // to that, we tolerate a 25% margin of error on the value of m_vsync
179 // reported from the system as this value is often not precise.
180
181 m_time += m_vsync;
182
183 if (delta > m_vsync * 1.25) {
184 m_lag += (delta / m_vsync);
185 m_bad++;
186 // We tolerate one bad frame without resorting to timer based. This is
187 // done to cope with a slow loader frame followed by smooth animation.
188 // However, on the second frame with massive lag, we switch.
189 if (m_lag > 10 && m_bad > 2) {
190 m_mode = TimerMode;
191 qCDebug(QSG_LOG_INFO, "animation driver switched to timer mode");
192 m_wallTime.restart();
193 }
194 } else {
195 m_lag = 0;
196 m_bad = 0;
197 }
198
199 } else {
200 if (delta < 1.25 * m_vsync) {
201 ++m_good;
202 } else {
203 m_good = 0;
204 }
205
206 // We've been solid for a while, switch back to vsync mode. Tolerance
207 // for switching back is lower than switching to timer mode, as we
208 // want to stay in vsync mode as much as possible.
209 if (m_good > 10 && !qsg_useConsistentTiming()) {
210 m_time = elapsed();
211 m_mode = VSyncMode;
212 m_bad = 0;
213 m_lag = 0;
214 qCDebug(QSG_LOG_INFO, "animation driver switched to vsync mode");
215 }
216 }
217
218 advanceAnimation();
219 }
220
221 double m_time;
222 double m_vsync;
223 Mode m_mode;
224 QElapsedTimer m_timer;
225 QElapsedTimer m_wallTime;
226 double m_lag;
227 int m_bad;
228 int m_good;
229};
230
231/*!
232 \class QSGContext
233
234 \brief The QSGContext holds the scene graph entry points for one QML engine.
235
236 The context is not ready for use until it has a QOpenGLContext. Once that happens,
237 the scene graph population can start.
238
239 \internal
240 */
241
242QSGContext::QSGContext(QObject *parent) :
243 QObject(parent)
244{
245}
246
247QSGContext::~QSGContext()
248{
249}
250
251void QSGContext::renderContextInitialized(QSGRenderContext *)
252{
253}
254
255void QSGContext::renderContextInvalidated(QSGRenderContext *)
256{
257}
258
259
260/*!
261 Convenience factory function for creating a colored rectangle with the given geometry.
262 */
263QSGInternalRectangleNode *QSGContext::createInternalRectangleNode(const QRectF &rect, const QColor &c)
264{
265 QSGInternalRectangleNode *node = createInternalRectangleNode();
266 node->setRect(rect);
267 node->setColor(c);
268 node->update();
269 return node;
270}
271
272/*!
273 Creates a new shader effect helper instance. This function is called on the
274 GUI thread, unlike the others. This is necessary in order to provide
275 adaptable, backend-specific shader effect functionality to the GUI thread too.
276 */
277QSGGuiThreadShaderEffectManager *QSGContext::createGuiThreadShaderEffectManager()
278{
279 return nullptr;
280}
281
282/*!
283 Creates a new shader effect node. The default of returning nullptr is
284 valid as long as the backend does not claim SupportsShaderEffectNode or
285 ignoring ShaderEffect elements is acceptable.
286 */
287QSGShaderEffectNode *QSGContext::createShaderEffectNode(QSGRenderContext *, QSGGuiThreadShaderEffectManager *)
288{
289 return nullptr;
290}
291
292/*!
293 Creates a new animation driver.
294 */
295QAnimationDriver *QSGContext::createAnimationDriver(QObject *parent)
296{
297 return new QSGAnimationDriver(parent);
298}
299
300QSize QSGContext::minimumFBOSize() const
301{
302 return QSize(1, 1);
303}
304
305/*!
306 Returns a pointer to the (presumably) global renderer interface.
307
308 \note This function may be called on the GUI thread in order to get access
309 to QSGRendererInterface::graphicsApi() and other getters.
310
311 \note it is expected that the simple queries (graphicsApi, shaderType,
312 etc.) are available regardless of the render context validity (i.e.
313 scenegraph status). This does not apply to engine-specific getters like
314 getResource(). In the end this means that this function must always return
315 a valid object in subclasses, even when renderContext->isValid() is false.
316 The typical pattern is to implement the QSGRendererInterface in the
317 QSGContext or QSGRenderContext subclass itself, whichever is more suitable.
318 */
319QSGRendererInterface *QSGContext::rendererInterface(QSGRenderContext *renderContext)
320{
321 Q_UNUSED(renderContext);
322 qWarning(msg: "QSGRendererInterface not implemented");
323 return nullptr;
324}
325
326QSGRenderContext::QSGRenderContext(QSGContext *context)
327 : m_sg(context)
328{
329}
330
331QSGRenderContext::~QSGRenderContext()
332{
333}
334
335void QSGRenderContext::initialize(const InitParams *params)
336{
337 Q_UNUSED(params);
338}
339
340void QSGRenderContext::invalidate()
341{
342}
343
344void QSGRenderContext::prepareSync(qreal devicePixelRatio, QRhiCommandBuffer *cb)
345{
346 Q_UNUSED(devicePixelRatio);
347 Q_UNUSED(cb);
348}
349
350void QSGRenderContext::beginNextFrame(QSGRenderer *renderer,
351 RenderPassCallback mainPassRecordingStart,
352 RenderPassCallback mainPassRecordingEnd,
353 void *callbackUserData)
354{
355 Q_UNUSED(renderer);
356 Q_UNUSED(mainPassRecordingStart);
357 Q_UNUSED(mainPassRecordingEnd);
358 Q_UNUSED(callbackUserData);
359}
360
361void QSGRenderContext::endNextFrame(QSGRenderer *renderer)
362{
363 Q_UNUSED(renderer);
364}
365
366void QSGRenderContext::beginNextRhiFrame(QSGRenderer *renderer,
367 QRhiRenderTarget *rt, QRhiRenderPassDescriptor *rp, QRhiCommandBuffer *cb,
368 RenderPassCallback mainPassRecordingStart,
369 RenderPassCallback mainPassRecordingEnd,
370 void *callbackUserData)
371{
372 Q_UNUSED(renderer);
373 Q_UNUSED(rt);
374 Q_UNUSED(rp);
375 Q_UNUSED(cb);
376 Q_UNUSED(mainPassRecordingStart);
377 Q_UNUSED(mainPassRecordingEnd);
378 Q_UNUSED(callbackUserData);
379}
380
381void QSGRenderContext::renderNextRhiFrame(QSGRenderer *renderer)
382{
383 Q_UNUSED(renderer);
384}
385
386void QSGRenderContext::endNextRhiFrame(QSGRenderer *renderer)
387{
388 Q_UNUSED(renderer);
389}
390
391void QSGRenderContext::endSync()
392{
393 qDeleteAll(c: m_texturesToDelete);
394 m_texturesToDelete.clear();
395}
396
397/*!
398 Do necessary preprocessing before the frame
399*/
400void QSGRenderContext::preprocess()
401{
402}
403
404/*!
405 Factory function for scene graph backends of the distance-field glyph cache.
406 */
407QSGDistanceFieldGlyphCache *QSGRenderContext::distanceFieldGlyphCache(const QRawFont &)
408{
409 return nullptr;
410}
411
412
413void QSGRenderContext::registerFontengineForCleanup(QFontEngine *engine)
414{
415 engine->ref.ref();
416 m_fontEnginesToClean << engine;
417}
418
419QRhi *QSGRenderContext::rhi() const
420{
421 return nullptr;
422}
423
424/*!
425 Factory function for the scene graph renderers.
426
427 The renderers are used for the toplevel renderer and once for every
428 QQuickShaderEffectSource used in the QML scene.
429 */
430
431QSGTexture *QSGRenderContext::textureForFactory(QQuickTextureFactory *factory, QQuickWindow *window)
432{
433 if (!factory)
434 return nullptr;
435
436 m_mutex.lock();
437 QSGTexture *texture = m_textures.value(akey: factory);
438 m_mutex.unlock();
439
440 if (!texture) {
441 texture = factory->createTexture(window);
442
443 m_mutex.lock();
444 m_textures.insert(akey: factory, avalue: texture);
445 m_mutex.unlock();
446
447 connect(sender: factory, SIGNAL(destroyed(QObject*)), receiver: this, SLOT(textureFactoryDestroyed(QObject*)), Qt::DirectConnection);
448 }
449 return texture;
450}
451
452void QSGRenderContext::textureFactoryDestroyed(QObject *o)
453{
454 m_mutex.lock();
455 m_texturesToDelete << m_textures.take(akey: o);
456 m_mutex.unlock();
457}
458
459/*!
460 Return the texture corresponding to a texture factory.
461
462 This may optionally manipulate the texture in some way; for example by returning
463 an atlased texture.
464
465 This function is not a replacement for textureForFactory; both should be used
466 for a single texture (this might atlas, while the other might cache).
467*/
468QSGTexture *QSGRenderContext::compressedTextureForFactory(const QSGCompressedTextureFactory *) const
469{
470 return nullptr;
471}
472
473#include "qsgcontext.moc"
474#include "moc_qsgcontext_p.cpp"
475
476QT_END_NAMESPACE
477

source code of qtdeclarative/src/quick/scenegraph/qsgcontext.cpp