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 "qsgabstractsoftwarerenderer_p.h"
41
42#include "qsgsoftwarerenderablenodeupdater_p.h"
43#include "qsgsoftwarerenderlistbuilder_p.h"
44#include "qsgsoftwarecontext_p.h"
45#include "qsgsoftwarerenderablenode_p.h"
46
47#include <QtCore/QLoggingCategory>
48#include <QtGui/QWindow>
49#include <QtQuick/QSGSimpleRectNode>
50
51Q_LOGGING_CATEGORY(lc2DRender, "qt.scenegraph.softwarecontext.abstractrenderer")
52
53QT_BEGIN_NAMESPACE
54
55QSGAbstractSoftwareRenderer::QSGAbstractSoftwareRenderer(QSGRenderContext *context)
56 : QSGRenderer(context)
57 , m_background(new QSGSimpleRectNode)
58 , m_nodeUpdater(new QSGSoftwareRenderableNodeUpdater(this))
59{
60 // Setup special background node
61 auto backgroundRenderable = new QSGSoftwareRenderableNode(QSGSoftwareRenderableNode::SimpleRect, m_background);
62 addNodeMapping(node: m_background, renderableNode: backgroundRenderable);
63}
64
65QSGAbstractSoftwareRenderer::~QSGAbstractSoftwareRenderer()
66{
67 // Cleanup RenderableNodes
68 delete m_background;
69
70 qDeleteAll(c: m_nodes);
71
72 delete m_nodeUpdater;
73}
74
75QSGSoftwareRenderableNode *QSGAbstractSoftwareRenderer::renderableNode(QSGNode *node) const
76{
77 return m_nodes.value(akey: node, adefaultValue: nullptr);
78}
79
80void QSGAbstractSoftwareRenderer::addNodeMapping(QSGNode *node, QSGSoftwareRenderableNode *renderableNode)
81{
82 m_nodes.insert(akey: node, avalue: renderableNode);
83}
84
85void QSGAbstractSoftwareRenderer::appendRenderableNode(QSGSoftwareRenderableNode *node)
86{
87 m_renderableNodes.append(t: node);
88}
89
90void QSGAbstractSoftwareRenderer::nodeChanged(QSGNode *node, QSGNode::DirtyState state)
91{
92 if (state & QSGNode::DirtyGeometry) {
93 nodeGeometryUpdated(node);
94 }
95 if (state & QSGNode::DirtyMaterial) {
96 nodeMaterialUpdated(node);
97 }
98 if (state & QSGNode::DirtyMatrix) {
99 nodeMatrixUpdated(node);
100 }
101 if (state & QSGNode::DirtyNodeAdded) {
102 nodeAdded(node);
103 }
104 if (state & QSGNode::DirtyNodeRemoved) {
105 nodeRemoved(node);
106 }
107 if (state & QSGNode::DirtyOpacity) {
108 nodeOpacityUpdated(node);
109 }
110 if (state & QSGNode::DirtySubtreeBlocked) {
111 m_nodeUpdater->updateNodes(node);
112 }
113 if (state & QSGNode::DirtyForceUpdate) {
114 m_nodeUpdater->updateNodes(node);
115 }
116 QSGRenderer::nodeChanged(node, state);
117}
118
119QRegion QSGAbstractSoftwareRenderer::renderNodes(QPainter *painter)
120{
121 QRegion dirtyRegion;
122 // If there are no nodes, do nothing
123 if (m_renderableNodes.isEmpty())
124 return dirtyRegion;
125
126 auto iterator = m_renderableNodes.begin();
127 // First node is the background and needs to painted without blending
128 auto backgroundNode = *iterator;
129 dirtyRegion += backgroundNode->renderNode(painter, /*force opaque painting*/ forceOpaquePainting: true);
130 iterator++;
131
132 for (; iterator != m_renderableNodes.end(); ++iterator) {
133 auto node = *iterator;
134 dirtyRegion += node->renderNode(painter);
135 }
136
137 return dirtyRegion;
138}
139
140void QSGAbstractSoftwareRenderer::buildRenderList()
141{
142 // Clear the previous renderlist
143 m_renderableNodes.clear();
144 // Add the background renderable (always first)
145 m_renderableNodes.append(t: renderableNode(node: m_background));
146 // Build the renderlist
147 QSGSoftwareRenderListBuilder(this).visitChildren(node: rootNode());
148}
149
150QRegion QSGAbstractSoftwareRenderer::optimizeRenderList()
151{
152 // Iterate through the renderlist from front to back
153 // Objective is to update the dirty status and rects.
154 for (auto i = m_renderableNodes.rbegin(); i != m_renderableNodes.rend(); ++i) {
155 auto node = *i;
156 if (!m_dirtyRegion.isEmpty()) {
157 // See if the current dirty regions apply to the current node
158 node->addDirtyRegion(dirtyRegion: m_dirtyRegion, forceDirty: true);
159 }
160
161 if (!m_obscuredRegion.isEmpty()) {
162 // Don't try to paint things that are covered by opaque objects
163 node->subtractDirtyRegion(dirtyRegion: m_obscuredRegion);
164 }
165
166 // Keep up with obscured regions
167 if (node->isOpaque()) {
168 m_obscuredRegion += node->boundingRectMin();
169 }
170
171 if (node->isDirty()) {
172 // Don't paint things outside of the rendering area
173 if (!m_background->rect().toRect().contains(r: node->boundingRectMax(), /*proper*/ true)) {
174 // Some part(s) of node is(are) outside of the rendering area
175 QRegion renderArea(m_background->rect().toRect());
176 QRegion outsideRegions = node->dirtyRegion().subtracted(r: renderArea);
177 if (!outsideRegions.isEmpty())
178 node->subtractDirtyRegion(dirtyRegion: outsideRegions);
179 }
180
181 // Get the dirty region's to pass to the next nodes
182 if (node->isOpaque()) {
183 // if isOpaque, subtract node's dirty rect from m_dirtyRegion
184 m_dirtyRegion -= node->boundingRectMin();
185 } else {
186 // if isAlpha, add node's dirty rect to m_dirtyRegion
187 m_dirtyRegion += node->dirtyRegion();
188 }
189 // if previousDirtyRegion has content outside of boundingRect add to m_dirtyRegion
190 QRegion prevDirty = node->previousDirtyRegion();
191 if (!prevDirty.isNull())
192 m_dirtyRegion += prevDirty;
193 }
194 }
195
196 if (m_obscuredRegion.contains(r: m_background->rect().toAlignedRect())) {
197 m_isOpaque = true;
198 } else {
199 m_isOpaque = false;
200 }
201
202 // Empty dirtyRegion (for second pass)
203 m_dirtyRegion = QRegion();
204 m_obscuredRegion = QRegion();
205
206 // Iterate through the renderlist from back to front
207 // Objective is to make sure all non-opaque items are painted when an item under them is dirty
208 for (auto j = m_renderableNodes.begin(); j != m_renderableNodes.end(); ++j) {
209 auto node = *j;
210
211 if (!node->isOpaque() && !m_dirtyRegion.isEmpty()) {
212 // Only blended nodes need to be updated
213 node->addDirtyRegion(dirtyRegion: m_dirtyRegion, forceDirty: true);
214 }
215
216 m_dirtyRegion += node->dirtyRegion();
217 }
218
219 QRegion updateRegion = m_dirtyRegion;
220
221 // Empty dirtyRegion
222 m_dirtyRegion = QRegion();
223 m_obscuredRegion = QRegion();
224
225 return updateRegion;
226}
227
228void QSGAbstractSoftwareRenderer::setBackgroundColor(const QColor &color)
229{
230 if (m_background->color() == color)
231 return;
232 m_background->setColor(color);
233 renderableNode(node: m_background)->markMaterialDirty();
234}
235
236void QSGAbstractSoftwareRenderer::setBackgroundRect(const QRect &rect, qreal devicePixelRatio)
237{
238 if (m_background->rect().toRect() == rect && m_devicePixelRatio == devicePixelRatio)
239 return;
240 m_background->setRect(rect);
241 m_devicePixelRatio = devicePixelRatio;
242 renderableNode(node: m_background)->markGeometryDirty();
243 // Invalidate the whole scene when the background is resized
244 markDirty();
245}
246
247QColor QSGAbstractSoftwareRenderer::backgroundColor()
248{
249 return m_background->color();
250}
251
252QRect QSGAbstractSoftwareRenderer::backgroundRect()
253{
254 return m_background->rect().toRect();
255}
256
257void QSGAbstractSoftwareRenderer::nodeAdded(QSGNode *node)
258{
259 qCDebug(lc2DRender, "nodeAdded %p", (void*)node);
260
261 m_nodeUpdater->updateNodes(node);
262}
263
264void QSGAbstractSoftwareRenderer::nodeRemoved(QSGNode *node)
265{
266 qCDebug(lc2DRender, "nodeRemoved %p", (void*)node);
267
268 auto renderable = renderableNode(node);
269 // remove mapping
270 if (renderable != nullptr) {
271 // Need to mark this region dirty in the other nodes
272 QRegion dirtyRegion = renderable->previousDirtyRegion(wasRemoved: true);
273 if (dirtyRegion.isEmpty())
274 dirtyRegion = renderable->boundingRectMax();
275 m_dirtyRegion += dirtyRegion;
276 m_nodes.remove(akey: node);
277 delete renderable;
278 }
279
280 // Remove all children nodes as well
281 for (QSGNode *child = node->firstChild(); child; child = child->nextSibling()) {
282 nodeRemoved(node: child);
283 }
284
285 m_nodeUpdater->updateNodes(node, isNodeRemoved: true);
286}
287
288void QSGAbstractSoftwareRenderer::nodeGeometryUpdated(QSGNode *node)
289{
290 qCDebug(lc2DRender, "nodeGeometryUpdated");
291
292 // Mark node as dirty
293 auto renderable = renderableNode(node);
294 if (renderable != nullptr) {
295 renderable->markGeometryDirty();
296 } else {
297 m_nodeUpdater->updateNodes(node);
298 }
299}
300
301void QSGAbstractSoftwareRenderer::nodeMaterialUpdated(QSGNode *node)
302{
303 qCDebug(lc2DRender, "nodeMaterialUpdated");
304
305 // Mark node as dirty
306 auto renderable = renderableNode(node);
307 if (renderable != nullptr) {
308 renderable->markMaterialDirty();
309 } else {
310 m_nodeUpdater->updateNodes(node);
311 }
312}
313
314void QSGAbstractSoftwareRenderer::nodeMatrixUpdated(QSGNode *node)
315{
316 qCDebug(lc2DRender, "nodeMaterialUpdated");
317
318 // Update children nodes
319 m_nodeUpdater->updateNodes(node);
320}
321
322void QSGAbstractSoftwareRenderer::nodeOpacityUpdated(QSGNode *node)
323{
324 qCDebug(lc2DRender, "nodeOpacityUpdated");
325
326 // Update children nodes
327 m_nodeUpdater->updateNodes(node);
328}
329
330void QSGAbstractSoftwareRenderer::markDirty()
331{
332 m_dirtyRegion = QRegion(m_background->rect().toRect());
333}
334
335QT_END_NAMESPACE
336

source code of qtdeclarative/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp