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