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 "qsgopenvgrenderloop_p.h"
41 #include "qsgopenvgcontext_p.h"
42 
43 #include <QtCore/QCoreApplication>
44 #include <QtCore/QElapsedTimer>
45 
46 #include <private/qquickanimatorcontroller_p.h>
47 #include <private/qquickwindow_p.h>
48 #include <private/qquickprofiler_p.h>
49 
50 #include <qtquick_tracepoints_p.h>
51 
52 #include "qopenvgcontext_p.h"
53 
54 QT_BEGIN_NAMESPACE
55 
QSGOpenVGRenderLoop()56 QSGOpenVGRenderLoop::QSGOpenVGRenderLoop()
57     : vg(nullptr)
58 {
59     sg = QSGContext::createDefaultContext();
60     rc = sg->createRenderContext();
61 }
62 
~QSGOpenVGRenderLoop()63 QSGOpenVGRenderLoop::~QSGOpenVGRenderLoop()
64 {
65     delete rc;
66     delete sg;
67 }
68 
show(QQuickWindow * window)69 void QSGOpenVGRenderLoop::show(QQuickWindow *window)
70 {
71     WindowData data;
72     data.updatePending = false;
73     data.grabOnly = false;
74     m_windows[window] = data;
75 
76     maybeUpdate(window);
77 }
78 
hide(QQuickWindow * window)79 void QSGOpenVGRenderLoop::hide(QQuickWindow *window)
80 {
81     QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
82     cd->fireAboutToStop();
83 }
84 
windowDestroyed(QQuickWindow * window)85 void QSGOpenVGRenderLoop::windowDestroyed(QQuickWindow *window)
86 {
87     m_windows.remove(window);
88     hide(window);
89 
90     QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
91     d->cleanupNodesOnShutdown();
92 
93     if (m_windows.size() == 0) {
94         rc->invalidate();
95         delete vg;
96         vg = nullptr;
97     } else if (vg && window == vg->window()) {
98         vg->doneCurrent();
99     }
100 
101     d->animationController.reset();
102 }
103 
exposureChanged(QQuickWindow * window)104 void QSGOpenVGRenderLoop::exposureChanged(QQuickWindow *window)
105 {
106     if (window->isExposed()) {
107         m_windows[window].updatePending = true;
108         renderWindow(window);
109     }
110 }
111 
grab(QQuickWindow * window)112 QImage QSGOpenVGRenderLoop::grab(QQuickWindow *window)
113 {
114     if (!m_windows.contains(window))
115         return QImage();
116 
117     m_windows[window].grabOnly = true;
118 
119     renderWindow(window);
120 
121     QImage grabbed = grabContent;
122     grabContent = QImage();
123     return grabbed;
124 }
125 
update(QQuickWindow * window)126 void QSGOpenVGRenderLoop::update(QQuickWindow *window)
127 {
128     maybeUpdate(window);
129 }
130 
handleUpdateRequest(QQuickWindow * window)131 void QSGOpenVGRenderLoop::handleUpdateRequest(QQuickWindow *window)
132 {
133     renderWindow(window);
134 }
135 
maybeUpdate(QQuickWindow * window)136 void QSGOpenVGRenderLoop::maybeUpdate(QQuickWindow *window)
137 {
138     if (!m_windows.contains(window))
139         return;
140 
141     m_windows[window].updatePending = true;
142     window->requestUpdate();
143 }
144 
animationDriver() const145 QAnimationDriver *QSGOpenVGRenderLoop::animationDriver() const
146 {
147     return nullptr;
148 }
149 
sceneGraphContext() const150 QSGContext *QSGOpenVGRenderLoop::sceneGraphContext() const
151 {
152     return sg;
153 }
154 
createRenderContext(QSGContext *) const155 QSGRenderContext *QSGOpenVGRenderLoop::createRenderContext(QSGContext *) const
156 {
157     return rc;
158 }
159 
releaseResources(QQuickWindow * window)160 void QSGOpenVGRenderLoop::releaseResources(QQuickWindow *window)
161 {
162     Q_UNUSED(window)
163 }
164 
windowSurfaceType() const165 QSurface::SurfaceType QSGOpenVGRenderLoop::windowSurfaceType() const
166 {
167     return QSurface::OpenVGSurface;
168 }
169 
renderWindow(QQuickWindow * window)170 void QSGOpenVGRenderLoop::renderWindow(QQuickWindow *window)
171 {
172     QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
173     if (!cd->isRenderable() || !m_windows.contains(window))
174         return;
175 
176     Q_TRACE_SCOPE(QSG_renderWindow);
177 
178     WindowData &data = const_cast<WindowData &>(m_windows[window]);
179 
180     if (vg == nullptr) {
181         vg = new QOpenVGContext(window);
182         vg->makeCurrent();
183         QSGOpenVGRenderContext::InitParams params;
184         params.context = vg;
185         cd->context->initialize(&params);
186     } else {
187         vg->makeCurrent();
188     }
189 
190     bool alsoSwap = data.updatePending;
191     data.updatePending = false;
192 
193     if (!data.grabOnly) {
194         cd->flushFrameSynchronousEvents();
195         // Event delivery/processing triggered the window to be deleted or stop rendering.
196         if (!m_windows.contains(window))
197             return;
198     }
199     QElapsedTimer renderTimer;
200     qint64 renderTime = 0, syncTime = 0, polishTime = 0;
201     bool profileFrames = QSG_OPENVG_LOG_TIME_RENDERLOOP().isDebugEnabled();
202     if (profileFrames)
203         renderTimer.start();
204     Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishFrame);
205     Q_TRACE(QSG_polishItems_entry);
206 
207     cd->polishItems();
208 
209     if (profileFrames)
210         polishTime = renderTimer.nsecsElapsed();
211     Q_TRACE(QSG_polishItems_exit);
212     Q_QUICK_SG_PROFILE_SWITCH(QQuickProfiler::SceneGraphPolishFrame,
213                               QQuickProfiler::SceneGraphRenderLoopFrame,
214                               QQuickProfiler::SceneGraphPolishPolish);
215     Q_TRACE(QSG_sync_entry);
216 
217     emit window->afterAnimating();
218 
219     cd->syncSceneGraph();
220     rc->endSync();
221 
222     if (profileFrames)
223         syncTime = renderTimer.nsecsElapsed();
224     Q_TRACE(QSG_sync_exit);
225     Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame,
226                               QQuickProfiler::SceneGraphRenderLoopSync);
227     Q_TRACE(QSG_render_entry);
228 
229     // setup coordinate system for window
230     vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
231     vgLoadIdentity();
232     vgTranslate(0.0f, window->size().height());
233     vgScale(1.0, -1.0);
234 
235     cd->renderSceneGraph(window->size());
236 
237     if (profileFrames)
238         renderTime = renderTimer.nsecsElapsed();
239     Q_TRACE(QSG_render_exit);
240     Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame,
241                               QQuickProfiler::SceneGraphRenderLoopRender);
242     Q_TRACE(QSG_swap_entry);
243 
244     if (data.grabOnly) {
245         grabContent = vg->readFramebuffer(window->size() * window->effectiveDevicePixelRatio());
246         data.grabOnly = false;
247     }
248 
249     if (alsoSwap && window->isVisible()) {
250         vg->swapBuffers();
251         cd->fireFrameSwapped();
252     }
253 
254     qint64 swapTime = 0;
255     if (profileFrames)
256         swapTime = renderTimer.nsecsElapsed();
257     Q_TRACE(QSG_swap_exit);
258     Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame,
259                            QQuickProfiler::SceneGraphRenderLoopSwap);
260 
261     if (QSG_OPENVG_LOG_TIME_RENDERLOOP().isDebugEnabled()) {
262         static QTime lastFrameTime = QTime::currentTime();
263         qCDebug(QSG_OPENVG_LOG_TIME_RENDERLOOP,
264                 "Frame rendered with 'basic' renderloop in %dms, polish=%d, sync=%d, render=%d, swap=%d, frameDelta=%d",
265                 int(swapTime / 1000000),
266                 int(polishTime / 1000000),
267                 int((syncTime - polishTime) / 1000000),
268                 int((renderTime - syncTime) / 1000000),
269                 int((swapTime - renderTime) / 10000000),
270                 int(lastFrameTime.msecsTo(QTime::currentTime())));
271         lastFrameTime = QTime::currentTime();
272     }
273 
274     // Might have been set during syncSceneGraph()
275     if (data.updatePending)
276         maybeUpdate(window);
277 }
278 
279 QT_END_NAMESPACE
280