1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtQuick module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qsgdefaultrendercontext_p.h"
41 
42 #include <QtGui/QGuiApplication>
43 #include <QtGui/QOpenGLFramebufferObject>
44 
45 #include <QtQuick/private/qsgbatchrenderer_p.h>
46 #include <QtQuick/private/qsgrenderer_p.h>
47 #include <QtQuick/private/qsgrhiatlastexture_p.h>
48 #include <QtQuick/private/qsgrhidistancefieldglyphcache_p.h>
49 #include <QtQuick/private/qsgmaterialrhishader_p.h>
50 
51 #include <QtQuick/private/qsgopenglatlastexture_p.h>
52 #include <QtQuick/private/qsgcompressedtexture_p.h>
53 #include <QtQuick/private/qsgopengldistancefieldglyphcache_p.h>
54 
55 QT_BEGIN_NAMESPACE
56 
57 #define QSG_RENDERCONTEXT_PROPERTY "_q_sgrendercontext"
58 
QSGDefaultRenderContext(QSGContext * context)59 QSGDefaultRenderContext::QSGDefaultRenderContext(QSGContext *context)
60     : QSGRenderContext(context)
61     , m_rhi(nullptr)
62     , m_gl(nullptr)
63     , m_depthStencilManager(nullptr)
64     , m_maxTextureSize(0)
65     , m_brokenIBOs(false)
66     , m_serializedRender(false)
67     , m_attachToGLContext(true)
68     , m_glAtlasManager(nullptr)
69     , m_rhiAtlasManager(nullptr)
70     , m_currentFrameCommandBuffer(nullptr)
71     , m_currentFrameRenderPass(nullptr)
72 {
73 }
74 
75 /*!
76     Initializes the scene graph render context with the GL context \a context. This also
77     emits the ready() signal so that the QML graph can start building scene graph nodes.
78  */
initialize(const QSGRenderContext::InitParams * params)79 void QSGDefaultRenderContext::initialize(const QSGRenderContext::InitParams *params)
80 {
81     if (!m_sg)
82         return;
83 
84     const InitParams *initParams = static_cast<const InitParams *>(params);
85     if (initParams->sType != INIT_PARAMS_MAGIC)
86         qFatal("QSGDefaultRenderContext: Invalid parameters passed to initialize()");
87 
88     m_initParams = *initParams;
89 
90     m_rhi = m_initParams.rhi;
91     if (m_rhi) {
92         m_maxTextureSize = m_rhi->resourceLimit(QRhi::TextureSizeMax);
93         if (!m_rhiAtlasManager)
94             m_rhiAtlasManager = new QSGRhiAtlasTexture::Manager(this, m_initParams.initialSurfacePixelSize, m_initParams.maybeSurface);
95     } else {
96         QOpenGLFunctions *funcs = m_rhi ? nullptr : QOpenGLContext::currentContext()->functions();
97         funcs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureSize);
98 
99         // Sanity check the surface format, in case it was overridden by the application
100         QSurfaceFormat requested = m_sg->defaultSurfaceFormat();
101         QSurfaceFormat actual = m_initParams.openGLContext->format();
102         if (requested.depthBufferSize() > 0 && actual.depthBufferSize() <= 0)
103             qWarning("QSGContext::initialize: depth buffer support missing, expect rendering errors");
104         if (requested.stencilBufferSize() > 0 && actual.stencilBufferSize() <= 0)
105             qWarning("QSGContext::initialize: stencil buffer support missing, expect rendering errors");
106 
107 #ifdef Q_OS_LINUX
108         const char *vendor = (const char *) funcs->glGetString(GL_VENDOR);
109         if (vendor && strstr(vendor, "nouveau"))
110             m_brokenIBOs = true;
111         const char *renderer = (const char *) funcs->glGetString(GL_RENDERER);
112         if (renderer && strstr(renderer, "llvmpipe"))
113             m_serializedRender = true;
114         if (vendor && renderer && strstr(vendor, "Hisilicon Technologies") && strstr(renderer, "Immersion.16"))
115             m_brokenIBOs = true;
116 #endif
117 
118         Q_ASSERT_X(!m_gl, "QSGRenderContext::initialize", "already initialized!");
119         m_gl = m_initParams.openGLContext;
120         if (m_attachToGLContext) {
121             Q_ASSERT(!m_gl->property(QSG_RENDERCONTEXT_PROPERTY).isValid());
122             m_gl->setProperty(QSG_RENDERCONTEXT_PROPERTY, QVariant::fromValue(this));
123         }
124 
125         if (!m_glAtlasManager)
126             m_glAtlasManager = new QSGOpenGLAtlasTexture::Manager(m_initParams.initialSurfacePixelSize);
127     }
128 
129     m_sg->renderContextInitialized(this);
130 
131     emit initialized();
132 }
133 
invalidate()134 void QSGDefaultRenderContext::invalidate()
135 {
136     if (!m_gl && !m_rhi)
137         return;
138 
139     qDeleteAll(m_texturesToDelete);
140     m_texturesToDelete.clear();
141 
142     qDeleteAll(m_textures);
143     m_textures.clear();
144 
145     /* The cleanup of the atlas textures is a bit intriguing.
146        As part of the cleanup in the threaded render loop, we
147        do:
148        1. call this function
149        2. call QCoreApp::sendPostedEvents() to immediately process
150           any pending deferred deletes.
151        3. delete the GL context.
152 
153        As textures need the atlas manager while cleaning up, the
154        manager needs to be cleaned up after the textures, so
155        we post a deleteLater here at the very bottom so it gets
156        deferred deleted last.
157 
158        Another alternative would be to use a QPointer in
159        QSGOpenGLAtlasTexture::Texture, but this seemed simpler.
160      */
161     if (m_glAtlasManager) {
162         m_glAtlasManager->invalidate();
163         m_glAtlasManager->deleteLater();
164         m_glAtlasManager = nullptr;
165     }
166     if (m_rhiAtlasManager) {
167         m_rhiAtlasManager->invalidate();
168         m_rhiAtlasManager->deleteLater();
169         m_rhiAtlasManager = nullptr;
170     }
171 
172     // The following piece of code will read/write to the font engine's caches,
173     // potentially from different threads. However, this is safe because this
174     // code is only called from QQuickWindow's shutdown which is called
175     // only when the GUI is blocked, and multiple threads will call it in
176     // sequence. (see qsgdefaultglyphnode_p.cpp's init())
177     for (QSet<QFontEngine *>::const_iterator it = m_fontEnginesToClean.constBegin(),
178          end = m_fontEnginesToClean.constEnd(); it != end; ++it) {
179         (*it)->clearGlyphCache(m_gl ? (void *) m_gl : (void *) m_rhi);
180         if (!(*it)->ref.deref())
181             delete *it;
182     }
183     m_fontEnginesToClean.clear();
184 
185     delete m_depthStencilManager;
186     m_depthStencilManager = nullptr;
187 
188     qDeleteAll(m_glyphCaches);
189     m_glyphCaches.clear();
190 
191     if (m_gl && m_gl->property(QSG_RENDERCONTEXT_PROPERTY) == QVariant::fromValue(this))
192         m_gl->setProperty(QSG_RENDERCONTEXT_PROPERTY, QVariant());
193 
194     m_gl = nullptr;
195     m_rhi = nullptr;
196 
197     if (m_sg)
198         m_sg->renderContextInvalidated(this);
199 
200     emit invalidated();
201 }
202 
prepareSync(qreal devicePixelRatio,QRhiCommandBuffer * cb)203 void QSGDefaultRenderContext::prepareSync(qreal devicePixelRatio, QRhiCommandBuffer *cb)
204 {
205     m_currentDevicePixelRatio = devicePixelRatio;
206 
207     // we store the command buffer already here, in case there is something in
208     // an updatePaintNode() implementation that leads to needing it (for
209     // example, an updateTexture() call on a QSGRhiLayer)
210     m_currentFrameCommandBuffer = cb;
211 }
212 
213 static QBasicMutex qsg_framerender_mutex;
214 
beginNextFrame(QSGRenderer * renderer,RenderPassCallback mainPassRecordingStart,RenderPassCallback mainPassRecordingEnd,void * callbackUserData)215 void QSGDefaultRenderContext::beginNextFrame(QSGRenderer *renderer,
216                                              RenderPassCallback mainPassRecordingStart,
217                                              RenderPassCallback mainPassRecordingEnd,
218                                              void *callbackUserData)
219 {
220     renderer->setRenderPassRecordingCallbacks(mainPassRecordingStart, mainPassRecordingEnd, callbackUserData);
221 }
222 
renderNextFrame(QSGRenderer * renderer,uint fboId)223 void QSGDefaultRenderContext::renderNextFrame(QSGRenderer *renderer, uint fboId)
224 {
225     if (m_serializedRender)
226         qsg_framerender_mutex.lock();
227 
228     renderer->renderScene(fboId);
229 
230     if (m_serializedRender)
231         qsg_framerender_mutex.unlock();
232 }
233 
endNextFrame(QSGRenderer * renderer)234 void QSGDefaultRenderContext::endNextFrame(QSGRenderer *renderer)
235 {
236     Q_UNUSED(renderer);
237 }
238 
beginNextRhiFrame(QSGRenderer * renderer,QRhiRenderTarget * rt,QRhiRenderPassDescriptor * rp,QRhiCommandBuffer * cb,RenderPassCallback mainPassRecordingStart,RenderPassCallback mainPassRecordingEnd,void * callbackUserData)239 void QSGDefaultRenderContext::beginNextRhiFrame(QSGRenderer *renderer, QRhiRenderTarget *rt, QRhiRenderPassDescriptor *rp,
240                                                 QRhiCommandBuffer *cb,
241                                                 RenderPassCallback mainPassRecordingStart,
242                                                 RenderPassCallback mainPassRecordingEnd,
243                                                 void *callbackUserData)
244 {
245     renderer->setRenderTarget(rt);
246     renderer->setRenderPassDescriptor(rp);
247     renderer->setCommandBuffer(cb);
248     renderer->setRenderPassRecordingCallbacks(mainPassRecordingStart, mainPassRecordingEnd, callbackUserData);
249 
250     m_currentFrameCommandBuffer = cb; // usually the same as what was passed to prepareSync() but cannot count on that having been called
251     m_currentFrameRenderPass = rp;
252 }
253 
renderNextRhiFrame(QSGRenderer * renderer)254 void QSGDefaultRenderContext::renderNextRhiFrame(QSGRenderer *renderer)
255 {
256     renderer->renderScene();
257 }
258 
endNextRhiFrame(QSGRenderer * renderer)259 void QSGDefaultRenderContext::endNextRhiFrame(QSGRenderer *renderer)
260 {
261     Q_UNUSED(renderer);
262     m_currentFrameCommandBuffer = nullptr;
263     m_currentFrameRenderPass = nullptr;
264 }
265 
266 /*!
267     Returns a shared pointer to a depth stencil buffer that can be used with \a fbo.
268 */
depthStencilBufferForFbo(QOpenGLFramebufferObject * fbo)269 QSharedPointer<QSGDepthStencilBuffer> QSGDefaultRenderContext::depthStencilBufferForFbo(QOpenGLFramebufferObject *fbo)
270 {
271     if (!m_gl)
272         return QSharedPointer<QSGDepthStencilBuffer>();
273     QSGDepthStencilBufferManager *manager = depthStencilBufferManager();
274     QSGDepthStencilBuffer::Format format;
275     format.size = fbo->size();
276     format.samples = fbo->format().samples();
277     format.attachments = QSGDepthStencilBuffer::DepthAttachment | QSGDepthStencilBuffer::StencilAttachment;
278     QSharedPointer<QSGDepthStencilBuffer> buffer = manager->bufferForFormat(format);
279     if (buffer.isNull()) {
280         buffer = QSharedPointer<QSGDepthStencilBuffer>(new QSGDefaultDepthStencilBuffer(m_gl, format));
281         manager->insertBuffer(buffer);
282     }
283     return buffer;
284 }
285 
286 /*!
287     Returns a pointer to the context's depth/stencil buffer manager. This is useful for custom
288     implementations of \l depthStencilBufferForFbo().
289 */
depthStencilBufferManager()290 QSGDepthStencilBufferManager *QSGDefaultRenderContext::depthStencilBufferManager()
291 {
292     if (!m_gl)
293         return nullptr;
294     if (!m_depthStencilManager)
295         m_depthStencilManager = new QSGDepthStencilBufferManager(m_gl);
296     return m_depthStencilManager;
297 }
298 
createTexture(const QImage & image,uint flags) const299 QSGTexture *QSGDefaultRenderContext::createTexture(const QImage &image, uint flags) const
300 {
301     bool atlas = flags & CreateTexture_Atlas;
302     bool mipmap = flags & CreateTexture_Mipmap;
303     bool alpha = flags & CreateTexture_Alpha;
304 
305     // The atlas implementation is only supported from the render thread and
306     // does not support mipmaps.
307     if (m_rhi) {
308         if (!mipmap && atlas && QThread::currentThread() == m_rhi->thread()) {
309             QSGTexture *t = m_rhiAtlasManager->create(image, alpha);
310             if (t)
311                 return t;
312         }
313     } else {
314         if (!mipmap && atlas && openglContext() && QThread::currentThread() == openglContext()->thread()) {
315             QSGTexture *t = m_glAtlasManager->create(image, alpha);
316             if (t)
317                 return t;
318         }
319     }
320 
321     QSGPlainTexture *texture = new QSGPlainTexture;
322     texture->setImage(image);
323     if (texture->hasAlphaChannel() && !alpha)
324         texture->setHasAlphaChannel(false);
325 
326     return texture;
327 }
328 
createRenderer()329 QSGRenderer *QSGDefaultRenderContext::createRenderer()
330 {
331     return new QSGBatchRenderer::Renderer(this);
332 }
333 
compressedTextureForFactory(const QSGCompressedTextureFactory * factory) const334 QSGTexture *QSGDefaultRenderContext::compressedTextureForFactory(const QSGCompressedTextureFactory *factory) const
335 {
336     // This is only used for atlasing compressed textures. Returning null implies no atlas.
337 
338     if (m_rhi) {
339         // ###
340     } else if (openglContext() && QThread::currentThread() == openglContext()->thread()) {
341         // The atlas implementation is only supported from the render thread
342         return m_glAtlasManager->create(factory);
343     }
344 
345     return nullptr;
346 }
347 
348 /*!
349     Compile \a shader, optionally using \a vertexCode and \a fragmentCode as
350     replacement for the source code supplied by \a shader.
351 
352     If \a vertexCode or \a fragmentCode is supplied, the caller is responsible
353     for setting up attribute bindings.
354 
355     \a material is supplied in case the implementation needs to take the
356     material flags into account.
357  */
compileShader(QSGMaterialShader * shader,QSGMaterial * material,const char * vertexCode,const char * fragmentCode)358 void QSGDefaultRenderContext::compileShader(QSGMaterialShader *shader, QSGMaterial *material, const char *vertexCode, const char *fragmentCode)
359 {
360     Q_UNUSED(material);
361     if (vertexCode || fragmentCode) {
362         Q_ASSERT_X((material->flags() & QSGMaterial::CustomCompileStep) == 0,
363                    "QSGRenderContext::compile()",
364                    "materials with custom compile step cannot have modified vertex or fragment code");
365         QOpenGLShaderProgram *p = shader->program();
366         p->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, vertexCode ? vertexCode : shader->vertexShader());
367         p->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, fragmentCode ? fragmentCode : shader->fragmentShader());
368         p->link();
369         if (!p->isLinked())
370             qWarning() << "shader compilation failed:" << Qt::endl << p->log();
371     } else {
372         shader->compile();
373     }
374 }
375 
fontKey(const QRawFont & font)376 QString QSGDefaultRenderContext::fontKey(const QRawFont &font)
377 {
378     QFontEngine *fe = QRawFontPrivate::get(font)->fontEngine;
379     if (!fe->faceId().filename.isEmpty()) {
380         QByteArray keyName = fe->faceId().filename + ' ' + QByteArray::number(fe->faceId().index);
381         if (font.style() != QFont::StyleNormal)
382             keyName += QByteArray(" I");
383         if (font.weight() != QFont::Normal)
384             keyName += ' ' + QByteArray::number(font.weight());
385         keyName += QByteArray(" DF");
386         return QString::fromUtf8(keyName);
387     } else {
388         return QString::fromLatin1("%1_%2_%3_%4")
389             .arg(font.familyName())
390             .arg(font.styleName())
391             .arg(font.weight())
392             .arg(font.style());
393     }
394 }
395 
initializeShader(QSGMaterialShader * shader)396 void QSGDefaultRenderContext::initializeShader(QSGMaterialShader *shader)
397 {
398     shader->program()->bind();
399     shader->initialize();
400 }
401 
initializeRhiShader(QSGMaterialRhiShader * shader,QShader::Variant shaderVariant)402 void QSGDefaultRenderContext::initializeRhiShader(QSGMaterialRhiShader *shader, QShader::Variant shaderVariant)
403 {
404     QSGMaterialRhiShaderPrivate::get(shader)->prepare(shaderVariant);
405 }
406 
setAttachToGraphicsContext(bool attach)407 void QSGDefaultRenderContext::setAttachToGraphicsContext(bool attach)
408 {
409     Q_ASSERT(!isValid());
410     m_attachToGLContext = attach;
411 }
412 
from(QOpenGLContext * context)413 QSGDefaultRenderContext *QSGDefaultRenderContext::from(QOpenGLContext *context)
414 {
415     return qobject_cast<QSGDefaultRenderContext *>(context->property(QSG_RENDERCONTEXT_PROPERTY).value<QObject *>());
416 }
417 
separateIndexBuffer() const418 bool QSGDefaultRenderContext::separateIndexBuffer() const
419 {
420     if (m_rhi)
421         return true;
422 
423     // WebGL: A given WebGLBuffer object may only be bound to one of
424     // the ARRAY_BUFFER or ELEMENT_ARRAY_BUFFER target in its
425     // lifetime. An attempt to bind a buffer object to the other
426     // target will generate an INVALID_OPERATION error, and the
427     // current binding will remain untouched.
428     static const bool isWebGL = (qGuiApp->platformName().compare(QLatin1String("webgl")) == 0
429                                   || qGuiApp->platformName().compare(QLatin1String("wasm")) == 0);
430     return isWebGL;
431 }
432 
preprocess()433 void QSGDefaultRenderContext::preprocess()
434 {
435     for (auto it = m_glyphCaches.begin(); it != m_glyphCaches.end(); ++it) {
436         it.value()->processPendingGlyphs();
437         it.value()->update();
438     }
439 }
440 
distanceFieldGlyphCache(const QRawFont & font)441 QSGDistanceFieldGlyphCache *QSGDefaultRenderContext::distanceFieldGlyphCache(const QRawFont &font)
442 {
443     QString key = fontKey(font);
444     QSGDistanceFieldGlyphCache *cache = m_glyphCaches.value(key, 0);
445     if (!cache) {
446         if (m_rhi)
447             cache = new QSGRhiDistanceFieldGlyphCache(m_rhi, font);
448         else
449             cache = new QSGOpenGLDistanceFieldGlyphCache(openglContext(), font);
450         m_glyphCaches.insert(key, cache);
451     }
452 
453     return cache;
454 }
455 
456 QT_END_NAMESPACE
457 
458 #include "moc_qsgdefaultrendercontext_p.cpp"
459