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 | |
4 | #include "qsgsoftwarerenderer_p.h" |
5 | |
6 | #include "qsgsoftwarerenderablenodeupdater_p.h" |
7 | #include "qsgsoftwarerenderlistbuilder_p.h" |
8 | #include "qsgsoftwarecontext_p.h" |
9 | #include "qsgsoftwarerenderablenode_p.h" |
10 | |
11 | #include <QtGui/QPaintDevice> |
12 | #include <QtGui/QBackingStore> |
13 | #include <QElapsedTimer> |
14 | |
15 | Q_LOGGING_CATEGORY(lcRenderer, "qt.scenegraph.softwarecontext.renderer" ) |
16 | |
17 | QT_BEGIN_NAMESPACE |
18 | |
19 | QSGSoftwareRenderer::QSGSoftwareRenderer(QSGRenderContext *context) |
20 | : QSGAbstractSoftwareRenderer(context) |
21 | , m_paintDevice(nullptr) |
22 | , m_backingStore(nullptr) |
23 | { |
24 | } |
25 | |
26 | QSGSoftwareRenderer::~QSGSoftwareRenderer() |
27 | { |
28 | } |
29 | |
30 | void QSGSoftwareRenderer::setCurrentPaintDevice(QPaintDevice *device) |
31 | { |
32 | m_paintDevice = device; |
33 | m_backingStore = nullptr; |
34 | } |
35 | |
36 | QPaintDevice *QSGSoftwareRenderer::currentPaintDevice() const |
37 | { |
38 | return m_paintDevice; |
39 | } |
40 | |
41 | void QSGSoftwareRenderer::setBackingStore(QBackingStore *backingStore) |
42 | { |
43 | m_backingStore = backingStore; |
44 | m_paintDevice = nullptr; |
45 | } |
46 | |
47 | QRegion QSGSoftwareRenderer::flushRegion() const |
48 | { |
49 | return m_flushRegion; |
50 | } |
51 | |
52 | void QSGSoftwareRenderer::renderScene() |
53 | { |
54 | QSGRenderer::renderScene(); |
55 | } |
56 | |
57 | void QSGSoftwareRenderer::render() |
58 | { |
59 | if (!m_paintDevice && !m_backingStore && !m_rt.paintDevice) |
60 | return; |
61 | |
62 | QPaintDevice *paintDevice = m_paintDevice ? m_paintDevice : m_rt.paintDevice; |
63 | QBackingStore *backingStore = nullptr; |
64 | // If no paint device and there is a backingstore, set the current paint device |
65 | if (!paintDevice && m_backingStore) { |
66 | // For HiDPI QBackingStores, the paint device is not valid |
67 | // until begin() has been called. See: QTBUG-55875 |
68 | m_backingStore->beginPaint(QRegion()); |
69 | paintDevice = m_backingStore->paintDevice(); |
70 | m_backingStore->endPaint(); |
71 | backingStore = m_backingStore; |
72 | } |
73 | |
74 | QElapsedTimer renderTimer; |
75 | |
76 | setBackgroundColor(clearColor()); |
77 | setBackgroundRect(rect: QRect(0, 0, |
78 | paintDevice->width() / paintDevice->devicePixelRatio(), |
79 | paintDevice->height() / paintDevice->devicePixelRatio()), |
80 | devicePixelRatio: paintDevice->devicePixelRatio()); |
81 | |
82 | // Build Renderlist |
83 | // The renderlist is created by visiting each node in the tree and when a |
84 | // renderable node is reach, we find the coorosponding RenderableNode object |
85 | // and append it to the renderlist. At this point the RenderableNode object |
86 | // should not need any further updating so it is just a matter of appending |
87 | // RenderableNodes |
88 | renderTimer.start(); |
89 | buildRenderList(); |
90 | qint64 buildRenderListTime = renderTimer.restart(); |
91 | |
92 | // Optimize Renderlist |
93 | // This is a pass through the renderlist to determine what actually needs to |
94 | // be painted. Without this pass the renderlist will simply render each item |
95 | // from back to front, with a high potential for overdraw. It would also lead |
96 | // to the entire window being flushed every frame. The objective of the |
97 | // optimization pass is to only paint dirty nodes that are not occuluded. A |
98 | // side effect of this is that additional nodes may need to be marked dirty to |
99 | // force a repaint. It is also important that any item that needs to be |
100 | // repainted only paints what is needed, via the use of clip regions. |
101 | const QRegion updateRegion = optimizeRenderList(); |
102 | qint64 optimizeRenderListTime = renderTimer.restart(); |
103 | |
104 | // If Rendering to a backingstore, prepare it to be updated |
105 | if (backingStore != nullptr) { |
106 | backingStore->beginPaint(updateRegion); |
107 | // It is possible that a QBackingStore's paintDevice() will change |
108 | // when begin() is called. |
109 | paintDevice = backingStore->paintDevice(); |
110 | } |
111 | |
112 | QPainter painter(paintDevice); |
113 | painter.setRenderHint(hint: QPainter::Antialiasing); |
114 | auto rc = static_cast<QSGSoftwareRenderContext *>(context()); |
115 | QPainter *prevPainter = rc->m_activePainter; |
116 | rc->m_activePainter = &painter; |
117 | |
118 | // Render the contents Renderlist |
119 | m_flushRegion = renderNodes(painter: &painter); |
120 | qint64 renderTime = renderTimer.elapsed(); |
121 | |
122 | painter.end(); |
123 | if (backingStore != nullptr) |
124 | backingStore->endPaint(); |
125 | |
126 | rc->m_activePainter = prevPainter; |
127 | qCDebug(lcRenderer) << "render" << m_flushRegion << buildRenderListTime << optimizeRenderListTime << renderTime; |
128 | } |
129 | |
130 | QT_END_NAMESPACE |
131 | |