Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qsgabstractsoftwarerenderer.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
6
11
12#include <QtCore/QLoggingCategory>
13#include <QtGui/QWindow>
14#include <QtQuick/QSGSimpleRectNode>
15
16Q_STATIC_LOGGING_CATEGORY(lc2DRender, "qt.scenegraph.softwarecontext.abstractrenderer")
17
18QT_BEGIN_NAMESPACE
19
20QSGAbstractSoftwareRenderer::QSGAbstractSoftwareRenderer(QSGRenderContext *context)
21 : QSGRenderer(context)
22 , m_background(new QSGSimpleRectNode)
23 , m_nodeUpdater(new QSGSoftwareRenderableNodeUpdater(this))
24{
25 // Setup special background node
26 auto backgroundRenderable = new QSGSoftwareRenderableNode(QSGSoftwareRenderableNode::SimpleRect, m_background);
27 addNodeMapping(m_background, backgroundRenderable);
28}
29
30QSGAbstractSoftwareRenderer::~QSGAbstractSoftwareRenderer()
31{
32 // Cleanup RenderableNodes
33 delete m_background;
34
35 qDeleteAll(m_nodes);
36
37 delete m_nodeUpdater;
38}
39
40QSGSoftwareRenderableNode *QSGAbstractSoftwareRenderer::renderableNode(QSGNode *node) const
41{
42 return m_nodes.value(node, nullptr);
43}
44
45// Used by GammaRay
46const QList<QSGSoftwareRenderableNode*> &QSGAbstractSoftwareRenderer::renderableNodes() const
47{
48 return m_renderableNodes;
49}
50
51void QSGAbstractSoftwareRenderer::addNodeMapping(QSGNode *node, QSGSoftwareRenderableNode *renderableNode)
52{
53 m_nodes.insert(node, renderableNode);
54}
55
56void QSGAbstractSoftwareRenderer::appendRenderableNode(QSGSoftwareRenderableNode *node)
57{
58 m_renderableNodes.append(node);
59}
60
61void QSGAbstractSoftwareRenderer::nodeChanged(QSGNode *node, QSGNode::DirtyState state)
62{
63 if (state & QSGNode::DirtyGeometry) {
64 nodeGeometryUpdated(node);
65 }
66 if (state & QSGNode::DirtyMaterial) {
67 nodeMaterialUpdated(node);
68 }
69 if (state & QSGNode::DirtyMatrix) {
70 nodeMatrixUpdated(node);
71 }
72 if (state & QSGNode::DirtyNodeAdded) {
73 nodeAdded(node);
74 }
75 if (state & QSGNode::DirtyNodeRemoved) {
76 nodeRemoved(node);
77 }
78 if (state & QSGNode::DirtyOpacity) {
79 nodeOpacityUpdated(node);
80 }
81 if (state & QSGNode::DirtySubtreeBlocked) {
82 m_nodeUpdater->updateNodes(node);
83 }
84 if (state & QSGNode::DirtyForceUpdate) {
85 m_nodeUpdater->updateNodes(node);
86 }
87 QSGRenderer::nodeChanged(node, state);
88}
89
90QRegion QSGAbstractSoftwareRenderer::renderNodes(QPainter *painter)
91{
92 QRegion dirtyRegion;
93 // If there are no nodes, do nothing
94 if (m_renderableNodes.isEmpty())
95 return dirtyRegion;
96
97 auto iterator = m_renderableNodes.begin();
98 // First node is the background and needs to painted without blending
99 if (m_clearColorEnabled) {
100 auto backgroundNode = *iterator;
101 dirtyRegion += backgroundNode->renderNode(painter, /*force opaque painting*/ true);
102 }
103 iterator++;
104
105 for (; iterator != m_renderableNodes.end(); ++iterator) {
106 auto node = *iterator;
107 dirtyRegion += node->renderNode(painter);
108 }
109
110 return dirtyRegion;
111}
112
113void QSGAbstractSoftwareRenderer::buildRenderList()
114{
115 // Clear the previous renderlist
116 m_renderableNodes.clear();
117 // Add the background renderable (always first)
118 m_renderableNodes.append(renderableNode(m_background));
119 // Build the renderlist
120 QSGSoftwareRenderListBuilder(this).visitChildren(rootNode());
121}
122
123QRegion QSGAbstractSoftwareRenderer::optimizeRenderList()
124{
125 // Iterate through the renderlist from front to back
126 // Objective is to update the dirty status and rects.
127 for (auto i = m_renderableNodes.rbegin(); i != m_renderableNodes.rend(); ++i) {
128 auto node = *i;
129 // Track the original version of isDirty() as it can change if
130 // when we subtract dirty regions but still need to mark the previously
131 // dirty region as dirty.
132 const bool wasDirty = node->isDirty();
133 if (!m_dirtyRegion.isEmpty()) {
134 // See if the current dirty regions apply to the current node
135 node->addDirtyRegion(m_dirtyRegion, true);
136 }
137
138 if (!m_obscuredRegion.isEmpty()) {
139 // Don't try to paint things that are covered by opaque objects
140 node->subtractDirtyRegion(m_obscuredRegion);
141 }
142
143 // Keep up with obscured regions
144 if (node->isOpaque()) {
145 m_obscuredRegion += node->boundingRectMin();
146 }
147
148 if (node->isDirty()) {
149 // Don't paint things outside of the rendering area
150 if (!m_background->rect().toRect().contains(node->boundingRectMax(), /*proper*/ true)) {
151 // Some part(s) of node is(are) outside of the rendering area
152 QRegion renderArea(m_background->rect().toRect());
153 QRegion outsideRegions = node->dirtyRegion().subtracted(renderArea);
154 if (!outsideRegions.isEmpty())
155 node->subtractDirtyRegion(outsideRegions);
156 }
157
158 // Get the dirty region's to pass to the next nodes
159 if (node->isOpaque()) {
160 // if isOpaque, subtract node's dirty rect from m_dirtyRegion
161 // node->boundingRectMax() is the area that might have been changed
162 // node->boundingRectMin() is the area that will have been changed
163 // This adding and subtracting will make sure that the "maybe" area is
164 // also marked dirty for the next nodes to prevent artifacts
165 m_dirtyRegion += node->boundingRectMax();
166 m_dirtyRegion -= node->boundingRectMin();
167 } else {
168 // if isAlpha, add node's dirty rect to m_dirtyRegion
169 m_dirtyRegion += node->dirtyRegion();
170 }
171 }
172
173 if (wasDirty) {
174 // If this node started out dirty, make sure its previous region is
175 // added to the dirty region so that it gets cleared properly.
176 QRegion prevDirty = node->previousDirtyRegion();
177 if (!prevDirty.isNull())
178 m_dirtyRegion += prevDirty;
179 }
180 }
181
182 if (m_obscuredRegion.contains(m_background->rect().toAlignedRect())) {
183 m_isOpaque = true;
184 } else {
185 m_isOpaque = false;
186 }
187
188 // Empty dirtyRegion (for second pass)
189 m_dirtyRegion = QRegion();
190 m_obscuredRegion = QRegion();
191
192 // Iterate through the renderlist from back to front
193 // Objective is to make sure all non-opaque items are painted when an item under them is dirty
194 for (auto j = m_renderableNodes.begin(); j != m_renderableNodes.end(); ++j) {
195 auto node = *j;
196
197 if ((!node->isOpaque() || node->boundingRectMax() != node->boundingRectMin()) && !m_dirtyRegion.isEmpty()) {
198 // Blended nodes need to be updated
199 // QTBUG-113745: Also nodes with floating point boundary rectangles need to
200 // be updated. The reason is that m_obscuredRegion contains only the rounded
201 // down bounding rectangle (node->boundingRectMin()) and thus not the whole
202 // node. As a result up to 1 pixel would be overpainted when it should not.
203 node->addDirtyRegion(m_dirtyRegion, true);
204 }
205
206 m_dirtyRegion += node->dirtyRegion();
207 }
208
209 QRegion updateRegion = m_dirtyRegion;
210
211 // Empty dirtyRegion
212 m_dirtyRegion = QRegion();
213 m_obscuredRegion = QRegion();
214
215 return updateRegion;
216}
217
218void QSGAbstractSoftwareRenderer::setBackgroundColor(const QColor &color)
219{
220 if (m_background->color() == color)
221 return;
222 m_background->setColor(color);
223 renderableNode(m_background)->markMaterialDirty();
224}
225
226void QSGAbstractSoftwareRenderer::setBackgroundRect(const QRect &rect, qreal devicePixelRatio)
227{
228 if (m_background->rect().toRect() == rect && m_devicePixelRatio == devicePixelRatio)
229 return;
230 m_background->setRect(rect);
231 m_devicePixelRatio = devicePixelRatio;
232 renderableNode(m_background)->markGeometryDirty();
233 // Invalidate the whole scene when the background is resized
234 markDirty();
235}
236
237QColor QSGAbstractSoftwareRenderer::backgroundColor()
238{
239 return m_background->color();
240}
241
242QRect QSGAbstractSoftwareRenderer::backgroundRect()
243{
244 return m_background->rect().toRect();
245}
246
247void QSGAbstractSoftwareRenderer::nodeAdded(QSGNode *node)
248{
249 qCDebug(lc2DRender, "nodeAdded %p", (void*)node);
250
251 m_nodeUpdater->updateNodes(node);
252}
253
254void QSGAbstractSoftwareRenderer::nodeRemoved(QSGNode *node)
255{
256 qCDebug(lc2DRender, "nodeRemoved %p", (void*)node);
257
258 auto renderable = renderableNode(node);
259 // remove mapping
260 if (renderable != nullptr) {
261 // Need to mark this region dirty in the other nodes
262 QRegion dirtyRegion = renderable->previousDirtyRegion(true);
263 if (dirtyRegion.isEmpty())
264 dirtyRegion = renderable->boundingRectMax();
265 m_dirtyRegion += dirtyRegion;
266 m_nodes.remove(node);
267 delete renderable;
268 }
269
270 // Remove all children nodes as well
271 for (QSGNode *child = node->firstChild(); child; child = child->nextSibling()) {
272 nodeRemoved(child);
273 }
274
275 m_nodeUpdater->updateNodes(node, true);
276}
277
278void QSGAbstractSoftwareRenderer::nodeGeometryUpdated(QSGNode *node)
279{
280 qCDebug(lc2DRender, "nodeGeometryUpdated");
281
282 // Mark node as dirty
283 auto renderable = renderableNode(node);
284 if (renderable != nullptr) {
285 renderable->markGeometryDirty();
286 } else {
287 m_nodeUpdater->updateNodes(node);
288 }
289}
290
291void QSGAbstractSoftwareRenderer::nodeMaterialUpdated(QSGNode *node)
292{
293 qCDebug(lc2DRender, "nodeMaterialUpdated");
294
295 // Mark node as dirty
296 auto renderable = renderableNode(node);
297 if (renderable != nullptr) {
298 renderable->markMaterialDirty();
299 } else {
300 m_nodeUpdater->updateNodes(node);
301 }
302}
303
304void QSGAbstractSoftwareRenderer::nodeMatrixUpdated(QSGNode *node)
305{
306 qCDebug(lc2DRender, "nodeMaterialUpdated");
307
308 // Update children nodes
309 m_nodeUpdater->updateNodes(node);
310}
311
312void QSGAbstractSoftwareRenderer::nodeOpacityUpdated(QSGNode *node)
313{
314 qCDebug(lc2DRender, "nodeOpacityUpdated");
315
316 // Update children nodes
317 m_nodeUpdater->updateNodes(node);
318}
319
320void QSGAbstractSoftwareRenderer::markDirty()
321{
322 m_dirtyRegion = QRegion(m_background->rect().toRect());
323}
324
325void QSGAbstractSoftwareRenderer::setClearColorEnabled(bool enable)
326{
327 m_clearColorEnabled = enable;
328}
329
330bool QSGAbstractSoftwareRenderer::clearColorEnabled() const
331{
332 return m_clearColorEnabled;
333}
334
335QT_END_NAMESPACE
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)