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 | |
75 | QT_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. |
79 | Q_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. |
83 | Q_LOGGING_CATEGORY(QSG_LOG_RENDERLOOP, "qt.scenegraph.renderloop" ) |
84 | |
85 | |
86 | // GLSL shader compilation |
87 | Q_LOGGING_CATEGORY(QSG_LOG_TIME_COMPILATION, "qt.scenegraph.time.compilation" ) |
88 | |
89 | // polish, animations, sync, render and swap in the render loop |
90 | Q_LOGGING_CATEGORY(QSG_LOG_TIME_RENDERLOOP, "qt.scenegraph.time.renderloop" ) |
91 | |
92 | // Texture uploads and swizzling |
93 | Q_LOGGING_CATEGORY(QSG_LOG_TIME_TEXTURE, "qt.scenegraph.time.texture" ) |
94 | |
95 | // Glyph preparation (only for distance fields atm) |
96 | Q_LOGGING_CATEGORY(QSG_LOG_TIME_GLYPH, "qt.scenegraph.time.glyph" ) |
97 | |
98 | // Timing inside the renderer base class |
99 | Q_LOGGING_CATEGORY(QSG_LOG_TIME_RENDERER, "qt.scenegraph.time.renderer" ) |
100 | |
101 | bool 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 | |
112 | class QSGAnimationDriver : public QAnimationDriver |
113 | { |
114 | Q_OBJECT |
115 | public: |
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 | |
242 | QSGContext::QSGContext(QObject *parent) : |
243 | QObject(parent) |
244 | { |
245 | } |
246 | |
247 | QSGContext::~QSGContext() |
248 | { |
249 | } |
250 | |
251 | void QSGContext::renderContextInitialized(QSGRenderContext *) |
252 | { |
253 | } |
254 | |
255 | void QSGContext::renderContextInvalidated(QSGRenderContext *) |
256 | { |
257 | } |
258 | |
259 | |
260 | /*! |
261 | Convenience factory function for creating a colored rectangle with the given geometry. |
262 | */ |
263 | QSGInternalRectangleNode *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 | */ |
277 | QSGGuiThreadShaderEffectManager *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 | */ |
287 | QSGShaderEffectNode *QSGContext::createShaderEffectNode(QSGRenderContext *, QSGGuiThreadShaderEffectManager *) |
288 | { |
289 | return nullptr; |
290 | } |
291 | |
292 | /*! |
293 | Creates a new animation driver. |
294 | */ |
295 | QAnimationDriver *QSGContext::createAnimationDriver(QObject *parent) |
296 | { |
297 | return new QSGAnimationDriver(parent); |
298 | } |
299 | |
300 | QSize 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 | */ |
319 | QSGRendererInterface *QSGContext::rendererInterface(QSGRenderContext *renderContext) |
320 | { |
321 | Q_UNUSED(renderContext); |
322 | qWarning(msg: "QSGRendererInterface not implemented" ); |
323 | return nullptr; |
324 | } |
325 | |
326 | QSGRenderContext::QSGRenderContext(QSGContext *context) |
327 | : m_sg(context) |
328 | { |
329 | } |
330 | |
331 | QSGRenderContext::~QSGRenderContext() |
332 | { |
333 | } |
334 | |
335 | void QSGRenderContext::initialize(const InitParams *params) |
336 | { |
337 | Q_UNUSED(params); |
338 | } |
339 | |
340 | void QSGRenderContext::invalidate() |
341 | { |
342 | } |
343 | |
344 | void QSGRenderContext::prepareSync(qreal devicePixelRatio, QRhiCommandBuffer *cb) |
345 | { |
346 | Q_UNUSED(devicePixelRatio); |
347 | Q_UNUSED(cb); |
348 | } |
349 | |
350 | void 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 | |
361 | void QSGRenderContext::endNextFrame(QSGRenderer *renderer) |
362 | { |
363 | Q_UNUSED(renderer); |
364 | } |
365 | |
366 | void 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 | |
381 | void QSGRenderContext::renderNextRhiFrame(QSGRenderer *renderer) |
382 | { |
383 | Q_UNUSED(renderer); |
384 | } |
385 | |
386 | void QSGRenderContext::endNextRhiFrame(QSGRenderer *renderer) |
387 | { |
388 | Q_UNUSED(renderer); |
389 | } |
390 | |
391 | void QSGRenderContext::endSync() |
392 | { |
393 | qDeleteAll(c: m_texturesToDelete); |
394 | m_texturesToDelete.clear(); |
395 | } |
396 | |
397 | /*! |
398 | Do necessary preprocessing before the frame |
399 | */ |
400 | void QSGRenderContext::preprocess() |
401 | { |
402 | } |
403 | |
404 | /*! |
405 | Factory function for scene graph backends of the distance-field glyph cache. |
406 | */ |
407 | QSGDistanceFieldGlyphCache *QSGRenderContext::distanceFieldGlyphCache(const QRawFont &) |
408 | { |
409 | return nullptr; |
410 | } |
411 | |
412 | |
413 | void QSGRenderContext::registerFontengineForCleanup(QFontEngine *engine) |
414 | { |
415 | engine->ref.ref(); |
416 | m_fontEnginesToClean << engine; |
417 | } |
418 | |
419 | QRhi *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 | |
431 | QSGTexture *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 | |
452 | void 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 | */ |
468 | QSGTexture *QSGRenderContext::compressedTextureForFactory(const QSGCompressedTextureFactory *) const |
469 | { |
470 | return nullptr; |
471 | } |
472 | |
473 | #include "qsgcontext.moc" |
474 | #include "moc_qsgcontext_p.cpp" |
475 | |
476 | QT_END_NAMESPACE |
477 | |