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
55QT_BEGIN_NAMESPACE
56
57#define QSG_RENDERCONTEXT_PROPERTY "_q_sgrendercontext"
58
59QSGDefaultRenderContext::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 */
79void 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(msg: "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(limit: 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, params: &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(msg: "QSGContext::initialize: depth buffer support missing, expect rendering errors");
104 if (requested.stencilBufferSize() > 0 && actual.stencilBufferSize() <= 0)
105 qWarning(msg: "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(haystack: vendor, needle: "nouveau"))
110 m_brokenIBOs = true;
111 const char *renderer = (const char *) funcs->glGetString(GL_RENDERER);
112 if (renderer && strstr(haystack: renderer, needle: "llvmpipe"))
113 m_serializedRender = true;
114 if (vendor && renderer && strstr(haystack: vendor, needle: "Hisilicon Technologies") && strstr(haystack: renderer, needle: "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, value: QVariant::fromValue(value: this));
123 }
124
125 if (!m_glAtlasManager)
126 m_glAtlasManager = new QSGOpenGLAtlasTexture::Manager(m_initParams.initialSurfacePixelSize);
127 }
128
129 m_sg->renderContextInitialized(renderContext: this);
130
131 emit initialized();
132}
133
134void QSGDefaultRenderContext::invalidate()
135{
136 if (!m_gl && !m_rhi)
137 return;
138
139 qDeleteAll(c: m_texturesToDelete);
140 m_texturesToDelete.clear();
141
142 qDeleteAll(c: 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(key: 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(c: m_glyphCaches);
189 m_glyphCaches.clear();
190
191 if (m_gl && m_gl->property(QSG_RENDERCONTEXT_PROPERTY) == QVariant::fromValue(value: this))
192 m_gl->setProperty(QSG_RENDERCONTEXT_PROPERTY, value: QVariant());
193
194 m_gl = nullptr;
195 m_rhi = nullptr;
196
197 if (m_sg)
198 m_sg->renderContextInvalidated(renderContext: this);
199
200 emit invalidated();
201}
202
203void 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
213static QBasicMutex qsg_framerender_mutex;
214
215void QSGDefaultRenderContext::beginNextFrame(QSGRenderer *renderer,
216 RenderPassCallback mainPassRecordingStart,
217 RenderPassCallback mainPassRecordingEnd,
218 void *callbackUserData)
219{
220 renderer->setRenderPassRecordingCallbacks(start: mainPassRecordingStart, end: mainPassRecordingEnd, userData: callbackUserData);
221}
222
223void 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
234void QSGDefaultRenderContext::endNextFrame(QSGRenderer *renderer)
235{
236 Q_UNUSED(renderer);
237}
238
239void 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(start: mainPassRecordingStart, end: mainPassRecordingEnd, userData: 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
254void QSGDefaultRenderContext::renderNextRhiFrame(QSGRenderer *renderer)
255{
256 renderer->renderScene();
257}
258
259void 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*/
269QSharedPointer<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(fmt: 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*/
290QSGDepthStencilBufferManager *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
299QSGTexture *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, hasAlphaChannel: 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, hasAlphaChannel: 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
329QSGRenderer *QSGDefaultRenderContext::createRenderer()
330{
331 return new QSGBatchRenderer::Renderer(this);
332}
333
334QSGTexture *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 */
358void 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(type: QOpenGLShader::Vertex, source: vertexCode ? vertexCode : shader->vertexShader());
367 p->addCacheableShaderFromSourceCode(type: QOpenGLShader::Fragment, source: 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
376QString 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(str: keyName);
387 } else {
388 return QString::fromLatin1(str: "%1_%2_%3_%4")
389 .arg(a: font.familyName())
390 .arg(a: font.styleName())
391 .arg(a: font.weight())
392 .arg(a: font.style());
393 }
394}
395
396void QSGDefaultRenderContext::initializeShader(QSGMaterialShader *shader)
397{
398 shader->program()->bind();
399 shader->initialize();
400}
401
402void QSGDefaultRenderContext::initializeRhiShader(QSGMaterialRhiShader *shader, QShader::Variant shaderVariant)
403{
404 QSGMaterialRhiShaderPrivate::get(s: shader)->prepare(vertexShaderVariant: shaderVariant);
405}
406
407void QSGDefaultRenderContext::setAttachToGraphicsContext(bool attach)
408{
409 Q_ASSERT(!isValid());
410 m_attachToGLContext = attach;
411}
412
413QSGDefaultRenderContext *QSGDefaultRenderContext::from(QOpenGLContext *context)
414{
415 return qobject_cast<QSGDefaultRenderContext *>(object: context->property(QSG_RENDERCONTEXT_PROPERTY).value<QObject *>());
416}
417
418bool 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(other: QLatin1String("webgl")) == 0
429 || qGuiApp->platformName().compare(other: QLatin1String("wasm")) == 0);
430 return isWebGL;
431}
432
433void 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
441QSGDistanceFieldGlyphCache *QSGDefaultRenderContext::distanceFieldGlyphCache(const QRawFont &font)
442{
443 QString key = fontKey(font);
444 QSGDistanceFieldGlyphCache *cache = m_glyphCaches.value(key, defaultValue: 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, value: cache);
451 }
452
453 return cache;
454}
455
456QT_END_NAMESPACE
457
458#include "moc_qsgdefaultrendercontext_p.cpp"
459

source code of qtdeclarative/src/quick/scenegraph/qsgdefaultrendercontext.cpp