diff options
Diffstat (limited to 'src/quick/scenegraph/qsgdefaultrendercontext.cpp')
-rw-r--r-- | src/quick/scenegraph/qsgdefaultrendercontext.cpp | 360 |
1 files changed, 157 insertions, 203 deletions
diff --git a/src/quick/scenegraph/qsgdefaultrendercontext.cpp b/src/quick/scenegraph/qsgdefaultrendercontext.cpp index 73b79c6300..1b0753e9ae 100644 --- a/src/quick/scenegraph/qsgdefaultrendercontext.cpp +++ b/src/quick/scenegraph/qsgdefaultrendercontext.cpp @@ -1,121 +1,98 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 #include "qsgdefaultrendercontext_p.h" +#include "qsgcurveglyphatlas_p.h" #include <QtGui/QGuiApplication> -#include <QtGui/QOpenGLFramebufferObject> #include <QtQuick/private/qsgbatchrenderer_p.h> #include <QtQuick/private/qsgrenderer_p.h> -#include <QtQuick/private/qsgatlastexture_p.h> +#include <QtQuick/private/qsgrhiatlastexture_p.h> +#include <QtQuick/private/qsgrhidistancefieldglyphcache_p.h> +#include <QtQuick/private/qsgmaterialshader_p.h> + #include <QtQuick/private/qsgcompressedtexture_p.h> -#include <QtQuick/private/qsgdefaultdistancefieldglyphcache_p.h> -QT_BEGIN_NAMESPACE +#include <QtQuick/qsgrendererinterface.h> +#include <QtQuick/qquickgraphicsconfiguration.h> -#define QSG_RENDERCONTEXT_PROPERTY "_q_sgrendercontext" +QT_BEGIN_NAMESPACE QSGDefaultRenderContext::QSGDefaultRenderContext(QSGContext *context) : QSGRenderContext(context) - , m_gl(nullptr) - , m_depthStencilManager(nullptr) + , m_rhi(nullptr) , m_maxTextureSize(0) - , m_brokenIBOs(false) - , m_serializedRender(false) - , m_attachToGLContext(true) - , m_atlasManager(nullptr) + , m_rhiAtlasManager(nullptr) + , m_currentFrameCommandBuffer(nullptr) + , m_currentFrameRenderPass(nullptr) + , m_useDepthBufferFor2D(true) + , m_glyphCacheResourceUpdates(nullptr) { - } /*! Initializes the scene graph render context with the GL context \a context. This also emits the ready() signal so that the QML graph can start building scene graph nodes. */ -void QSGDefaultRenderContext::initialize(void *context) +void QSGDefaultRenderContext::initialize(const QSGRenderContext::InitParams *params) { if (!m_sg) return; - QOpenGLContext *openglContext = static_cast<QOpenGLContext *>(context); + const InitParams *initParams = static_cast<const InitParams *>(params); + if (initParams->sType != INIT_PARAMS_MAGIC) + qFatal("QSGDefaultRenderContext: Invalid parameters passed to initialize()"); - QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); - funcs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureSize); + m_initParams = *initParams; - // Sanity check the surface format, in case it was overridden by the application - QSurfaceFormat requested = m_sg->defaultSurfaceFormat(); - QSurfaceFormat actual = openglContext->format(); - if (requested.depthBufferSize() > 0 && actual.depthBufferSize() <= 0) - qWarning("QSGContext::initialize: depth buffer support missing, expect rendering errors"); - if (requested.stencilBufferSize() > 0 && actual.stencilBufferSize() <= 0) - qWarning("QSGContext::initialize: stencil buffer support missing, expect rendering errors"); + m_rhi = m_initParams.rhi; + m_maxTextureSize = m_rhi->resourceLimit(QRhi::TextureSizeMax); + if (!m_rhiAtlasManager) + m_rhiAtlasManager = new QSGRhiAtlasTexture::Manager(this, m_initParams.initialSurfacePixelSize, m_initParams.maybeSurface); - if (!m_atlasManager) - m_atlasManager = new QSGAtlasTexture::Manager(); + m_glyphCacheResourceUpdates = nullptr; - Q_ASSERT_X(!m_gl, "QSGRenderContext::initialize", "already initialized!"); - m_gl = openglContext; - if (m_attachToGLContext) { - Q_ASSERT(!openglContext->property(QSG_RENDERCONTEXT_PROPERTY).isValid()); - openglContext->setProperty(QSG_RENDERCONTEXT_PROPERTY, QVariant::fromValue(this)); - } m_sg->renderContextInitialized(this); -#ifdef Q_OS_LINUX - const char *vendor = (const char *) funcs->glGetString(GL_VENDOR); - if (vendor && strstr(vendor, "nouveau")) - m_brokenIBOs = true; - const char *renderer = (const char *) funcs->glGetString(GL_RENDERER); - if (renderer && strstr(renderer, "llvmpipe")) - m_serializedRender = true; - if (vendor && renderer && strstr(vendor, "Hisilicon Technologies") && strstr(renderer, "Immersion.16")) - m_brokenIBOs = true; -#endif - emit initialized(); } +void QSGDefaultRenderContext::invalidateGlyphCaches() +{ + { + auto it = m_glyphCaches.begin(); + while (it != m_glyphCaches.end()) { + if (!(*it)->isActive()) { + delete *it; + it = m_glyphCaches.erase(it); + } else { + ++it; + } + } + } + + qDeleteAll(m_curveGlyphAtlases); + m_curveGlyphAtlases.clear(); + + { + auto it = m_fontEnginesToClean.begin(); + while (it != m_fontEnginesToClean.end()) { + if (it.value() == 0) { + it.key()->clearGlyphCache(this); + if (!it.key()->ref.deref()) + delete it.key(); + it = m_fontEnginesToClean.erase(it); + } else { + ++it; + } + } + } +} void QSGDefaultRenderContext::invalidate() { - if (!m_gl) + if (!m_rhi) return; qDeleteAll(m_texturesToDelete); @@ -138,84 +115,77 @@ void QSGDefaultRenderContext::invalidate() deferred deleted last. Another alternative would be to use a QPointer in - QSGAtlasTexture::Texture, but this seemed simpler. + QSGOpenGLAtlasTexture::Texture, but this seemed simpler. */ - m_atlasManager->invalidate(); - m_atlasManager->deleteLater(); - m_atlasManager = nullptr; + if (m_rhiAtlasManager) { + m_rhiAtlasManager->invalidate(); + m_rhiAtlasManager->deleteLater(); + m_rhiAtlasManager = nullptr; + } // The following piece of code will read/write to the font engine's caches, // potentially from different threads. However, this is safe because this // code is only called from QQuickWindow's shutdown which is called // only when the GUI is blocked, and multiple threads will call it in // sequence. (see qsgdefaultglyphnode_p.cpp's init()) - for (QSet<QFontEngine *>::const_iterator it = m_fontEnginesToClean.constBegin(), - end = m_fontEnginesToClean.constEnd(); it != end; ++it) { - (*it)->clearGlyphCache(m_gl); - if (!(*it)->ref.deref()) - delete *it; + for (auto it = m_fontEnginesToClean.constBegin(); it != m_fontEnginesToClean.constEnd(); ++it) { + it.key()->clearGlyphCache(this); + if (!it.key()->ref.deref()) + delete it.key(); } m_fontEnginesToClean.clear(); - delete m_depthStencilManager; - m_depthStencilManager = nullptr; + qDeleteAll(m_curveGlyphAtlases); + m_curveGlyphAtlases.clear(); qDeleteAll(m_glyphCaches); m_glyphCaches.clear(); - if (m_gl->property(QSG_RENDERCONTEXT_PROPERTY) == QVariant::fromValue(this)) - m_gl->setProperty(QSG_RENDERCONTEXT_PROPERTY, QVariant()); - m_gl = nullptr; + resetGlyphCacheResources(); + + m_rhi = nullptr; if (m_sg) m_sg->renderContextInvalidated(this); + emit invalidated(); } -static QBasicMutex qsg_framerender_mutex; - -void QSGDefaultRenderContext::renderNextFrame(QSGRenderer *renderer, uint fboId) +void QSGDefaultRenderContext::prepareSync(qreal devicePixelRatio, + QRhiCommandBuffer *cb, + const QQuickGraphicsConfiguration &config) { - if (m_serializedRender) - qsg_framerender_mutex.lock(); + m_currentDevicePixelRatio = devicePixelRatio; + m_useDepthBufferFor2D = config.isDepthBufferEnabledFor2D(); - renderer->renderScene(fboId); + // we store the command buffer already here, in case there is something in + // an updatePaintNode() implementation that leads to needing it (for + // example, an updateTexture() call on a QSGRhiLayer) + m_currentFrameCommandBuffer = cb; +} + +void QSGDefaultRenderContext::beginNextFrame(QSGRenderer *renderer, const QSGRenderTarget &renderTarget, + RenderPassCallback mainPassRecordingStart, + RenderPassCallback mainPassRecordingEnd, + void *callbackUserData) +{ + renderer->setRenderTarget(renderTarget); + renderer->setRenderPassRecordingCallbacks(mainPassRecordingStart, mainPassRecordingEnd, callbackUserData); - if (m_serializedRender) - qsg_framerender_mutex.unlock(); + m_currentFrameCommandBuffer = renderTarget.cb; // usually the same as what was passed to prepareSync() but cannot count on that having been called + m_currentFrameRenderPass = renderTarget.rpDesc; } -/*! - Returns a shared pointer to a depth stencil buffer that can be used with \a fbo. -*/ -QSharedPointer<QSGDepthStencilBuffer> QSGDefaultRenderContext::depthStencilBufferForFbo(QOpenGLFramebufferObject *fbo) +void QSGDefaultRenderContext::renderNextFrame(QSGRenderer *renderer) { - if (!m_gl) - return QSharedPointer<QSGDepthStencilBuffer>(); - QSGDepthStencilBufferManager *manager = depthStencilBufferManager(); - QSGDepthStencilBuffer::Format format; - format.size = fbo->size(); - format.samples = fbo->format().samples(); - format.attachments = QSGDepthStencilBuffer::DepthAttachment | QSGDepthStencilBuffer::StencilAttachment; - QSharedPointer<QSGDepthStencilBuffer> buffer = manager->bufferForFormat(format); - if (buffer.isNull()) { - buffer = QSharedPointer<QSGDepthStencilBuffer>(new QSGDefaultDepthStencilBuffer(m_gl, format)); - manager->insertBuffer(buffer); - } - return buffer; + renderer->renderScene(); } -/*! - Returns a pointer to the context's depth/stencil buffer manager. This is useful for custom - implementations of \l depthStencilBufferForFbo(). -*/ -QSGDepthStencilBufferManager *QSGDefaultRenderContext::depthStencilBufferManager() +void QSGDefaultRenderContext::endNextFrame(QSGRenderer *renderer) { - if (!m_gl) - return nullptr; - if (!m_depthStencilManager) - m_depthStencilManager = new QSGDepthStencilBufferManager(m_gl); - return m_depthStencilManager; + Q_UNUSED(renderer); + m_currentFrameCommandBuffer = nullptr; + m_currentFrameRenderPass = nullptr; } QSGTexture *QSGDefaultRenderContext::createTexture(const QImage &image, uint flags) const @@ -226,13 +196,15 @@ QSGTexture *QSGDefaultRenderContext::createTexture(const QImage &image, uint fla // The atlas implementation is only supported from the render thread and // does not support mipmaps. - if (!mipmap && atlas && openglContext() && QThread::currentThread() == openglContext()->thread()) { - QSGTexture *t = m_atlasManager->create(image, alpha); - if (t) - return t; + if (m_rhi) { + if (!mipmap && atlas && QThread::currentThread() == m_rhi->thread()) { + QSGTexture *t = m_rhiAtlasManager->create(image, alpha); + if (t) + return t; + } } - QSGPlainTexture *texture = new QSGPlainTexture(); + QSGPlainTexture *texture = new QSGPlainTexture; texture->setImage(image); if (texture->hasAlphaChannel() && !alpha) texture->setHasAlphaChannel(false); @@ -240,106 +212,88 @@ QSGTexture *QSGDefaultRenderContext::createTexture(const QImage &image, uint fla return texture; } -QSGRenderer *QSGDefaultRenderContext::createRenderer() +QSGRenderer *QSGDefaultRenderContext::createRenderer(QSGRendererInterface::RenderMode renderMode) { - return new QSGBatchRenderer::Renderer(this); + return new QSGBatchRenderer::Renderer(this, renderMode); } QSGTexture *QSGDefaultRenderContext::compressedTextureForFactory(const QSGCompressedTextureFactory *factory) const { - // The atlas implementation is only supported from the render thread - if (openglContext() && QThread::currentThread() == openglContext()->thread()) - return m_atlasManager->create(factory); + // This is only used for atlasing compressed textures. Returning null implies no atlas. + + if (m_rhi && QThread::currentThread() == m_rhi->thread()) + return m_rhiAtlasManager->create(factory); + return nullptr; } -/*! - Compile \a shader, optionally using \a vertexCode and \a fragmentCode as - replacement for the source code supplied by \a shader. - - If \a vertexCode or \a fragmentCode is supplied, the caller is responsible - for setting up attribute bindings. +void QSGDefaultRenderContext::initializeRhiShader(QSGMaterialShader *shader, QShader::Variant shaderVariant) +{ + QSGMaterialShaderPrivate::get(shader)->prepare(shaderVariant); +} - \a material is supplied in case the implementation needs to take the - material flags into account. - */ -void QSGDefaultRenderContext::compileShader(QSGMaterialShader *shader, QSGMaterial *material, const char *vertexCode, const char *fragmentCode) +void QSGDefaultRenderContext::preprocess() { - Q_UNUSED(material); - if (vertexCode || fragmentCode) { - Q_ASSERT_X((material->flags() & QSGMaterial::CustomCompileStep) == 0, - "QSGRenderContext::compile()", - "materials with custom compile step cannot have modified vertex or fragment code"); - QOpenGLShaderProgram *p = shader->program(); - p->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, vertexCode ? vertexCode : shader->vertexShader()); - p->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, fragmentCode ? fragmentCode : shader->fragmentShader()); - p->link(); - if (!p->isLinked()) - qWarning() << "shader compilation failed:" << endl << p->log(); - } else { - shader->compile(); + for (auto it = m_glyphCaches.begin(); it != m_glyphCaches.end(); ++it) { + it.value()->processPendingGlyphs(); + it.value()->update(); } } -QString QSGDefaultRenderContext::fontKey(const QRawFont &font) +QSGCurveGlyphAtlas *QSGDefaultRenderContext::curveGlyphAtlas(const QRawFont &font) { - QFontEngine *fe = QRawFontPrivate::get(font)->fontEngine; - if (!fe->faceId().filename.isEmpty()) { - QByteArray keyName = fe->faceId().filename; - if (font.style() != QFont::StyleNormal) - keyName += QByteArray(" I"); - if (font.weight() != QFont::Normal) - keyName += ' ' + QByteArray::number(font.weight()); - keyName += QByteArray(" DF"); - return QString::fromUtf8(keyName); - } else { - return QString::fromLatin1("%1_%2_%3_%4") - .arg(font.familyName()) - .arg(font.styleName()) - .arg(font.weight()) - .arg(font.style()); + FontKey key = FontKey(font, 0); + QSGCurveGlyphAtlas *atlas = m_curveGlyphAtlases.value(key, nullptr); + if (atlas == nullptr) { + atlas = new QSGCurveGlyphAtlas(font); + m_curveGlyphAtlases.insert(key, atlas); } + + return atlas; } -void QSGDefaultRenderContext::initializeShader(QSGMaterialShader *shader) +QSGDistanceFieldGlyphCache *QSGDefaultRenderContext::distanceFieldGlyphCache(const QRawFont &font, int renderTypeQuality) { - shader->program()->bind(); - shader->initialize(); + FontKey key(font, renderTypeQuality); + QSGDistanceFieldGlyphCache *cache = m_glyphCaches.value(key, 0); + if (!cache && font.isValid()) { + cache = new QSGRhiDistanceFieldGlyphCache(this, font, renderTypeQuality); + m_glyphCaches.insert(key, cache); + } + + return cache; } -void QSGDefaultRenderContext::setAttachToGraphicsContext(bool attach) +QRhiResourceUpdateBatch *QSGDefaultRenderContext::maybeGlyphCacheResourceUpdates() { - Q_ASSERT(!isValid()); - m_attachToGLContext = attach; + return m_glyphCacheResourceUpdates; } -QSGDefaultRenderContext *QSGDefaultRenderContext::from(QOpenGLContext *context) +QRhiResourceUpdateBatch *QSGDefaultRenderContext::glyphCacheResourceUpdates() { - return qobject_cast<QSGDefaultRenderContext *>(context->property(QSG_RENDERCONTEXT_PROPERTY).value<QObject *>()); + if (!m_glyphCacheResourceUpdates) + m_glyphCacheResourceUpdates = m_rhi->nextResourceUpdateBatch(); + + return m_glyphCacheResourceUpdates; } -bool QSGDefaultRenderContext::separateIndexBuffer() const +void QSGDefaultRenderContext::deferredReleaseGlyphCacheTexture(QRhiTexture *texture) { - // WebGL: A given WebGLBuffer object may only be bound to one of - // the ARRAY_BUFFER or ELEMENT_ARRAY_BUFFER target in its - // lifetime. An attempt to bind a buffer object to the other - // target will generate an INVALID_OPERATION error, and the - // current binding will remain untouched. - static const bool isWebGL = (qGuiApp->platformName().compare(QLatin1String("webgl")) == 0 - || qGuiApp->platformName().compare(QLatin1String("wasm")) == 0); - return isWebGL; + if (texture) + m_pendingGlyphCacheTextures.insert(texture); } -QSGDistanceFieldGlyphCache *QSGDefaultRenderContext::distanceFieldGlyphCache(const QRawFont &font) +void QSGDefaultRenderContext::resetGlyphCacheResources() { - QString key = fontKey(font); - QSGDistanceFieldGlyphCache *cache = m_glyphCaches.value(key, 0); - if (!cache) { - cache = new QSGDefaultDistanceFieldGlyphCache(openglContext(), font); - m_glyphCaches.insert(key, cache); + if (m_glyphCacheResourceUpdates) { + m_glyphCacheResourceUpdates->release(); + m_glyphCacheResourceUpdates = nullptr; } - return cache; + for (QRhiTexture *t : std::as_const(m_pendingGlyphCacheTextures)) + t->deleteLater(); // the QRhiTexture object stays valid for the current frame + + m_pendingGlyphCacheTextures.clear(); } QT_END_NAMESPACE |