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("QSG_FIXED_ANIMATION_STEP") && qgetenv("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(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("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::beginNextFrame(QSGRenderer *renderer,
345 RenderPassCallback mainPassRecordingStart,
346 RenderPassCallback mainPassRecordingEnd,
347 void *callbackUserData)
348{
349 Q_UNUSED(renderer);
350 Q_UNUSED(mainPassRecordingStart);
351 Q_UNUSED(mainPassRecordingEnd);
352 Q_UNUSED(callbackUserData);
353}
354
355void QSGRenderContext::endNextFrame(QSGRenderer *renderer)
356{
357 Q_UNUSED(renderer);
358}
359
360void QSGRenderContext::beginNextRhiFrame(QSGRenderer *renderer,
361 QRhiRenderTarget *rt, QRhiRenderPassDescriptor *rp, QRhiCommandBuffer *cb,
362 RenderPassCallback mainPassRecordingStart,
363 RenderPassCallback mainPassRecordingEnd,
364 void *callbackUserData)
365{
366 Q_UNUSED(renderer);
367 Q_UNUSED(rt);
368 Q_UNUSED(rp);
369 Q_UNUSED(cb);
370 Q_UNUSED(mainPassRecordingStart);
371 Q_UNUSED(mainPassRecordingEnd);
372 Q_UNUSED(callbackUserData);
373}
374
375void QSGRenderContext::renderNextRhiFrame(QSGRenderer *renderer)
376{
377 Q_UNUSED(renderer);
378}
379
380void QSGRenderContext::endNextRhiFrame(QSGRenderer *renderer)
381{
382 Q_UNUSED(renderer);
383}
384
385void QSGRenderContext::endSync()
386{
387 qDeleteAll(m_texturesToDelete);
388 m_texturesToDelete.clear();
389}
390
391/*!
392 Factory function for scene graph backends of the distance-field glyph cache.
393 */
394QSGDistanceFieldGlyphCache *QSGRenderContext::distanceFieldGlyphCache(const QRawFont &)
395{
396 return nullptr;
397}
398
399
400void QSGRenderContext::registerFontengineForCleanup(QFontEngine *engine)
401{
402 engine->ref.ref();
403 m_fontEnginesToClean << engine;
404}
405
406QRhi *QSGRenderContext::rhi() const
407{
408 return nullptr;
409}
410
411/*!
412 Factory function for the scene graph renderers.
413
414 The renderers are used for the toplevel renderer and once for every
415 QQuickShaderEffectSource used in the QML scene.
416 */
417
418QSGTexture *QSGRenderContext::textureForFactory(QQuickTextureFactory *factory, QQuickWindow *window)
419{
420 if (!factory)
421 return nullptr;
422
423 m_mutex.lock();
424 QSGTexture *texture = m_textures.value(factory);
425 m_mutex.unlock();
426
427 if (!texture) {
428 texture = factory->createTexture(window);
429
430 m_mutex.lock();
431 m_textures.insert(factory, texture);
432 m_mutex.unlock();
433
434 connect(factory, SIGNAL(destroyed(QObject*)), this, SLOT(textureFactoryDestroyed(QObject*)), Qt::DirectConnection);
435 }
436 return texture;
437}
438
439void QSGRenderContext::textureFactoryDestroyed(QObject *o)
440{
441 m_mutex.lock();
442 m_texturesToDelete << m_textures.take(o);
443 m_mutex.unlock();
444}
445
446/*!
447 Return the texture corresponding to a texture factory.
448
449 This may optionally manipulate the texture in some way; for example by returning
450 an atlased texture.
451
452 This function is not a replacement for textureForFactory; both should be used
453 for a single texture (this might atlas, while the other might cache).
454*/
455QSGTexture *QSGRenderContext::compressedTextureForFactory(const QSGCompressedTextureFactory *) const
456{
457 return nullptr;
458}
459
460#include "qsgcontext.moc"
461#include "moc_qsgcontext_p.cpp"
462
463QT_END_NAMESPACE
464