Revision control

++ // Copyright (C) 2016 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++ #include "qsgsoftwarerenderer_p.h"
++
++ #include "qsgsoftwarerenderablenodeupdater_p.h"
++ #include "qsgsoftwarerenderlistbuilder_p.h"
++ #include "qsgsoftwarecontext_p.h"
++ #include "qsgsoftwarerenderablenode_p.h"
++
++ #include <QtGui/QPaintDevice>
++ #include <QtGui/QBackingStore>
++ #include <QElapsedTimer>
++
++ Q_STATIC_LOGGING_CATEGORY(lcRenderer, "qt.scenegraph.softwarecontext.renderer")
++
++ QT_BEGIN_NAMESPACE
++
++ QSGSoftwareRenderer::QSGSoftwareRenderer(QSGRenderContext *context)
++ : QSGAbstractSoftwareRenderer(context)
++ , m_paintDevice(nullptr)
++ , m_backingStore(nullptr)
++ {
++ // Check if the environment variable is set to force/disable partial updates
++ static bool partialUpdateOverrideSet = false;
++ static const int forcePartialUpdates = qEnvironmentVariableIntValue("QSG_SOFTWARE_RENDERER_FORCE_PARTIAL_UPDATES", &partialUpdateOverrideSet);
++ if (partialUpdateOverrideSet) {
++ if (forcePartialUpdates == 0)
++ m_partialUpdateMode = DisablePartialUpdate;
++ else
++ m_partialUpdateMode = ForcePartialUpdate;
++ }
++ }
++
++ QSGSoftwareRenderer::~QSGSoftwareRenderer()
++ {
++ }
++
++ void QSGSoftwareRenderer::setCurrentPaintDevice(QPaintDevice *device)
++ {
++ m_paintDevice = device;
++ m_backingStore = nullptr;
++ }
++
++ QPaintDevice *QSGSoftwareRenderer::currentPaintDevice() const
++ {
++ return m_paintDevice;
++ }
++
++ void QSGSoftwareRenderer::setBackingStore(QBackingStore *backingStore)
++ {
++ m_backingStore = backingStore;
++ m_paintDevice = nullptr;
++ }
++
++ QRegion QSGSoftwareRenderer::flushRegion() const
++ {
++ return m_flushRegion;
++ }
++
++ void QSGSoftwareRenderer::renderScene()
++ {
++ QSGRenderer::renderScene();
++ }
++
++ void QSGSoftwareRenderer::render()
++ {
++ if (!m_paintDevice && !m_backingStore && !m_rt.paintDevice)
++ return;
++
++ QPaintDevice *paintDevice = m_paintDevice ? m_paintDevice : m_rt.paintDevice;
++ QSize paintSize;
++ qreal paintDevicePixelRatio = 1.0;
++
++ if (paintDevice) {
++ paintSize = QSize(paintDevice->width(), paintDevice->height());
++ paintDevicePixelRatio = paintDevice->devicePixelRatio();
++ } else {
++ // For HiDPI QBackingStores, the paint device is valid between calls to
++ // beginPaint() and endPaint(). See: QTBUG-55875
++
++ m_backingStore->beginPaint(QRegion());
++
++ const QPaintDevice *backingStorePaintDevice = m_backingStore->paintDevice();
++ paintSize = QSize(backingStorePaintDevice->width(), backingStorePaintDevice->height());
++ paintDevicePixelRatio = backingStorePaintDevice->devicePixelRatio();
++
++ m_backingStore->endPaint();
++ }
++
++ QElapsedTimer renderTimer;
++
++ setBackgroundColor(clearColor());
++ setBackgroundRect(QRect(0, 0,
++ paintSize.width() / paintDevicePixelRatio,
++ paintSize.height() / paintDevicePixelRatio),
++ paintDevicePixelRatio);
++ // If paintDevicePixelRatio is not a whole number, opt to disable the partial update mechanism
++ const bool nonIntegerRatio = !qFuzzyIsNull(paintDevicePixelRatio - qFloor(paintDevicePixelRatio));
++ bool disablePartialUpdates = m_partialUpdateMode == DisablePartialUpdate || nonIntegerRatio;
++ if (m_partialUpdateMode == ForcePartialUpdate)
++ disablePartialUpdates = false;
++
++ // Force the whole render area to be marked as dirty, avoiding any partial update
++ if (disablePartialUpdates)
++ markDirty();
++
++ // Build Renderlist
++ // The renderlist is created by visiting each node in the tree and when a
++ // renderable node is reach, we find the coorosponding RenderableNode object
++ // and append it to the renderlist. At this point the RenderableNode object
++ // should not need any further updating so it is just a matter of appending
++ // RenderableNodes
++ renderTimer.start();
++ buildRenderList();
++ qint64 buildRenderListTime = renderTimer.restart();
++
++ // Optimize Renderlist
++ // This is a pass through the renderlist to determine what actually needs to
++ // be painted. Without this pass the renderlist will simply render each item
++ // from back to front, with a high potential for overdraw. It would also lead
++ // to the entire window being flushed every frame. The objective of the
++ // optimization pass is to only paint dirty nodes that are not occuluded. A
++ // side effect of this is that additional nodes may need to be marked dirty to
++ // force a repaint. It is also important that any item that needs to be
++ // repainted only paints what is needed, via the use of clip regions.
++ const QRegion updateRegion = optimizeRenderList();
++ qint64 optimizeRenderListTime = renderTimer.restart();
++
++ // If Rendering to a backingstore, prepare it to be updated
++ bool usingBackingStore = false;
++ if (!paintDevice) {
++ m_backingStore->beginPaint(updateRegion);
++ paintDevice = m_backingStore->paintDevice();
++ usingBackingStore = true;
++ }
++
++ QPainter painter(paintDevice);
++ painter.setRenderHint(QPainter::Antialiasing);
++ auto rc = static_cast<QSGSoftwareRenderContext *>(context());
++ QPainter *prevPainter = rc->m_activePainter;
++ rc->m_activePainter = &painter;
++
++ // Render the contents Renderlist
++ m_flushRegion = renderNodes(&painter);
++ qint64 renderTime = renderTimer.elapsed();
++
++ painter.end();
++ if (usingBackingStore)
++ m_backingStore->endPaint();
++
++ rc->m_activePainter = prevPainter;
++ qCDebug(lcRenderer) << "render" << m_flushRegion << buildRenderListTime << optimizeRenderListTime << renderTime;
++ }
++
++ QT_END_NAMESPACE