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 "qsgdefaultcontext_p.h"
41
42#include <QtQuick/private/qsgdefaultinternalrectanglenode_p.h>
43#include <QtQuick/private/qsgdefaultinternalimagenode_p.h>
44#include <QtQuick/private/qsgdefaultpainternode_p.h>
45#include <QtQuick/private/qsgdefaultglyphnode_p.h>
46#include <QtQuick/private/qsgdistancefieldglyphnode_p.h>
47#include <QtQuick/private/qsgdistancefieldglyphnode_p_p.h>
48#include <QtQuick/private/qsgopengllayer_p.h>
49#include <QtQuick/private/qsgrhisupport_p.h>
50#include <QtQuick/private/qsgrhilayer_p.h>
51#include <QtQuick/private/qsgdefaultrendercontext_p.h>
52#include <QtQuick/private/qsgdefaultrectanglenode_p.h>
53#include <QtQuick/private/qsgdefaultimagenode_p.h>
54#include <QtQuick/private/qsgdefaultninepatchnode_p.h>
55#if QT_CONFIG(quick_sprite)
56#include <QtQuick/private/qsgdefaultspritenode_p.h>
57#endif
58#include <QtQuick/private/qsgrhishadereffectnode_p.h>
59
60#include <QtGui/QOpenGLContext>
61#include <QtGui/QOpenGLFramebufferObject>
62
63#include <QtQuick/private/qquickwindow_p.h>
64
65#include <private/qqmlglobal_p.h>
66
67#include <algorithm>
68
69#include <QtGui/private/qrhi_p.h>
70#include <QtGui/private/qrhigles2_p.h>
71
72QT_BEGIN_NAMESPACE
73
74namespace QSGMultisampleAntialiasing {
75 class ImageNode : public QSGDefaultInternalImageNode {
76 public:
77 ImageNode(QSGDefaultRenderContext *rc) : QSGDefaultInternalImageNode(rc) { }
78 void setAntialiasing(bool) override { }
79 };
80
81 class RectangleNode : public QSGDefaultInternalRectangleNode {
82 public:
83 void setAntialiasing(bool) override { }
84 };
85}
86
87DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
88
89QSGDefaultContext::QSGDefaultContext(QObject *parent)
90 : QSGContext (parent)
91 , m_antialiasingMethod(QSGContext::UndecidedAntialiasing)
92 , m_distanceFieldDisabled(qmlDisableDistanceField())
93 , m_distanceFieldAntialiasing(QSGGlyphNode::HighQualitySubPixelAntialiasing)
94 , m_distanceFieldAntialiasingDecided(false)
95{
96 if (Q_UNLIKELY(!qEnvironmentVariableIsEmpty("QSG_DISTANCEFIELD_ANTIALIASING"))) {
97 const QByteArray mode = qgetenv("QSG_DISTANCEFIELD_ANTIALIASING");
98 m_distanceFieldAntialiasingDecided = true;
99 if (mode == "subpixel")
100 m_distanceFieldAntialiasing = QSGGlyphNode::HighQualitySubPixelAntialiasing;
101 else if (mode == "subpixel-lowq")
102 m_distanceFieldAntialiasing = QSGGlyphNode::LowQualitySubPixelAntialiasing;
103 else if (mode == "gray")
104 m_distanceFieldAntialiasing = QSGGlyphNode::GrayAntialiasing;
105 }
106
107 // Adds compatibility with Qt 5.3 and earlier's QSG_RENDER_TIMING
108 if (qEnvironmentVariableIsSet("QSG_RENDER_TIMING")) {
109 const_cast<QLoggingCategory &>(QSG_LOG_TIME_GLYPH()).setEnabled(QtDebugMsg, true);
110 const_cast<QLoggingCategory &>(QSG_LOG_TIME_TEXTURE()).setEnabled(QtDebugMsg, true);
111 const_cast<QLoggingCategory &>(QSG_LOG_TIME_RENDERER()).setEnabled(QtDebugMsg, true);
112 const_cast<QLoggingCategory &>(QSG_LOG_TIME_RENDERLOOP()).setEnabled(QtDebugMsg, true);
113 const_cast<QLoggingCategory &>(QSG_LOG_TIME_COMPILATION()).setEnabled(QtDebugMsg, true);
114 }
115}
116
117QSGDefaultContext::~QSGDefaultContext()
118{
119
120}
121
122void QSGDefaultContext::renderContextInitialized(QSGRenderContext *renderContext)
123{
124 m_mutex.lock();
125
126 auto rc = static_cast<const QSGDefaultRenderContext *>(renderContext);
127 if (m_antialiasingMethod == UndecidedAntialiasing) {
128 if (Q_UNLIKELY(qEnvironmentVariableIsSet("QSG_ANTIALIASING_METHOD"))) {
129 const QByteArray aaType = qgetenv("QSG_ANTIALIASING_METHOD");
130 if (aaType == "msaa")
131 m_antialiasingMethod = MsaaAntialiasing;
132 else if (aaType == "vertex")
133 m_antialiasingMethod = VertexAntialiasing;
134 }
135 if (m_antialiasingMethod == UndecidedAntialiasing)
136 m_antialiasingMethod = rc->msaaSampleCount() > 1 ? MsaaAntialiasing : VertexAntialiasing;
137 }
138
139 // With OpenGL ES, except for Angle on Windows, use GrayAntialiasing, unless
140 // some value had been requested explicitly. This could not be decided
141 // before without a context. Now the context is ready.
142 if (!m_distanceFieldAntialiasingDecided) {
143 m_distanceFieldAntialiasingDecided = true;
144#ifndef Q_OS_WIN32
145 if (rc->rhi()) {
146 if (rc->rhi()->backend() == QRhi::OpenGLES2
147 && static_cast<const QRhiGles2NativeHandles *>(rc->rhi()->nativeHandles())->context->isOpenGLES())
148 {
149 m_distanceFieldAntialiasing = QSGGlyphNode::GrayAntialiasing;
150 }
151 } else {
152 if (rc->openglContext()->isOpenGLES())
153 m_distanceFieldAntialiasing = QSGGlyphNode::GrayAntialiasing;
154 }
155#endif
156 }
157
158 static bool dumped = false;
159 if (!dumped && QSG_LOG_INFO().isDebugEnabled() && !rc->rhi()) {
160 dumped = true;
161 QSurfaceFormat format = rc->openglContext()->format();
162 QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
163 qCDebug(QSG_LOG_INFO, "R/G/B/A Buffers: %d %d %d %d", format.redBufferSize(),
164 format.greenBufferSize(), format.blueBufferSize(), format.alphaBufferSize());
165 qCDebug(QSG_LOG_INFO, "Depth Buffer: %d", format.depthBufferSize());
166 qCDebug(QSG_LOG_INFO, "Stencil Buffer: %d", format.stencilBufferSize());
167 qCDebug(QSG_LOG_INFO, "Samples: %d", format.samples());
168 qCDebug(QSG_LOG_INFO, "GL_VENDOR: %s", (const char*)funcs->glGetString(GL_VENDOR));
169 qCDebug(QSG_LOG_INFO, "GL_RENDERER: %s",
170 (const char*)funcs->glGetString(GL_RENDERER));
171 qCDebug(QSG_LOG_INFO, "GL_VERSION: %s", (const char*)funcs->glGetString(GL_VERSION));
172 QByteArrayList exts = rc->openglContext()->extensions().values();
173 std::sort(exts.begin(), exts.end());
174 qCDebug(QSG_LOG_INFO, "GL_EXTENSIONS: %s", exts.join(' ').constData());
175 qCDebug(QSG_LOG_INFO, "Max Texture Size: %d", rc->maxTextureSize());
176 qCDebug(QSG_LOG_INFO, "Debug context: %s",
177 format.testOption(QSurfaceFormat::DebugContext) ? "true" : "false");
178 }
179
180 m_mutex.unlock();
181}
182
183void QSGDefaultContext::renderContextInvalidated(QSGRenderContext *)
184{
185}
186
187QSGRenderContext *QSGDefaultContext::createRenderContext()
188{
189 return new QSGDefaultRenderContext(this);
190}
191
192QSGInternalRectangleNode *QSGDefaultContext::createInternalRectangleNode()
193{
194 return m_antialiasingMethod == MsaaAntialiasing
195 ? new QSGMultisampleAntialiasing::RectangleNode
196 : new QSGDefaultInternalRectangleNode;
197}
198
199QSGInternalImageNode *QSGDefaultContext::createInternalImageNode(QSGRenderContext *renderContext)
200{
201 return m_antialiasingMethod == MsaaAntialiasing
202 ? new QSGMultisampleAntialiasing::ImageNode(static_cast<QSGDefaultRenderContext *>(renderContext))
203 : new QSGDefaultInternalImageNode(static_cast<QSGDefaultRenderContext *>(renderContext));
204}
205
206QSGPainterNode *QSGDefaultContext::createPainterNode(QQuickPaintedItem *item)
207{
208 return new QSGDefaultPainterNode(item);
209}
210
211QSGGlyphNode *QSGDefaultContext::createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode)
212{
213 if (m_distanceFieldDisabled || preferNativeGlyphNode) {
214 return new QSGDefaultGlyphNode(rc);
215 } else {
216 QSGDistanceFieldGlyphNode *node = new QSGDistanceFieldGlyphNode(rc);
217 node->setPreferredAntialiasingMode(m_distanceFieldAntialiasing);
218 return node;
219 }
220}
221
222QSGLayer *QSGDefaultContext::createLayer(QSGRenderContext *renderContext)
223{
224 auto rc = static_cast<const QSGDefaultRenderContext *>(renderContext);
225 if (rc->rhi())
226 return new QSGRhiLayer(renderContext);
227 else
228 return new QSGOpenGLLayer(renderContext);
229}
230
231QSurfaceFormat QSGDefaultContext::defaultSurfaceFormat() const
232{
233 QSurfaceFormat format = QSurfaceFormat::defaultFormat();
234 static bool useDepth = qEnvironmentVariableIsEmpty("QSG_NO_DEPTH_BUFFER");
235 static bool useStencil = qEnvironmentVariableIsEmpty("QSG_NO_STENCIL_BUFFER");
236 static bool enableDebug = qEnvironmentVariableIsSet("QSG_OPENGL_DEBUG");
237 if (useDepth && format.depthBufferSize() == -1)
238 format.setDepthBufferSize(24);
239 else if (!useDepth)
240 format.setDepthBufferSize(0);
241 if (useStencil && format.stencilBufferSize() == -1)
242 format.setStencilBufferSize(8);
243 else if (!useStencil)
244 format.setStencilBufferSize(0);
245 if (enableDebug)
246 format.setOption(QSurfaceFormat::DebugContext);
247 if (QQuickWindow::hasDefaultAlphaBuffer())
248 format.setAlphaBufferSize(8);
249 format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
250 return format;
251}
252
253void QSGDefaultContext::setDistanceFieldEnabled(bool enabled)
254{
255 m_distanceFieldDisabled = !enabled;
256}
257
258bool QSGDefaultContext::isDistanceFieldEnabled() const
259{
260 return !m_distanceFieldDisabled;
261}
262
263QSGRendererInterface *QSGDefaultContext::rendererInterface(QSGRenderContext *renderContext)
264{
265 Q_UNUSED(renderContext);
266 return this;
267}
268
269QSGRectangleNode *QSGDefaultContext::createRectangleNode()
270{
271 return new QSGDefaultRectangleNode;
272}
273
274QSGImageNode *QSGDefaultContext::createImageNode()
275{
276 return new QSGDefaultImageNode;
277}
278
279QSGNinePatchNode *QSGDefaultContext::createNinePatchNode()
280{
281 return new QSGDefaultNinePatchNode;
282}
283
284#if QT_CONFIG(quick_sprite)
285QSGSpriteNode *QSGDefaultContext::createSpriteNode()
286{
287 return new QSGDefaultSpriteNode;
288}
289#endif
290
291QSGGuiThreadShaderEffectManager *QSGDefaultContext::createGuiThreadShaderEffectManager()
292{
293 if (QSGRhiSupport::instance()->isRhiEnabled())
294 return new QSGRhiGuiThreadShaderEffectManager;
295
296 return nullptr;
297}
298
299QSGShaderEffectNode *QSGDefaultContext::createShaderEffectNode(QSGRenderContext *renderContext,
300 QSGGuiThreadShaderEffectManager *mgr)
301{
302 if (QSGRhiSupport::instance()->isRhiEnabled()) {
303 return new QSGRhiShaderEffectNode(static_cast<QSGDefaultRenderContext *>(renderContext),
304 static_cast<QSGRhiGuiThreadShaderEffectManager *>(mgr));
305 }
306
307 return nullptr;
308}
309
310QSGRendererInterface::GraphicsApi QSGDefaultContext::graphicsApi() const
311{
312 return QSGRhiSupport::instance()->graphicsApi();
313}
314
315void *QSGDefaultContext::getResource(QQuickWindow *window, Resource resource) const
316{
317 if (!window)
318 return nullptr;
319
320 // Unlike the graphicsApi and shaderType and similar queries, getting a
321 // native resource is only possible when there is an initialized
322 // rendercontext, or rather, only within rendering a frame, as per
323 // QSGRendererInterface docs. This is good since getting some things is
324 // only possible within a beginFrame - endFrame with the RHI.
325
326 const QSGDefaultRenderContext *rc = static_cast<const QSGDefaultRenderContext *>(
327 QQuickWindowPrivate::get(window)->context);
328 QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
329
330 switch (resource) {
331 case OpenGLContextResource:
332 if (rhiSupport->graphicsApi() == OpenGL)
333 return rc->openglContext();
334 else
335 return const_cast<void *>(rhiSupport->rifResource(resource, rc));
336#if QT_CONFIG(vulkan)
337 case VulkanInstanceResource:
338 return window->vulkanInstance();
339#endif
340 default:
341 return const_cast<void *>(rhiSupport->rifResource(resource, rc));
342 }
343}
344
345QSGRendererInterface::ShaderType QSGDefaultContext::shaderType() const
346{
347 return QSGRhiSupport::instance()->isRhiEnabled() ? RhiShader : GLSL;
348}
349
350QSGRendererInterface::ShaderCompilationTypes QSGDefaultContext::shaderCompilationType() const
351{
352 return QSGRhiSupport::instance()->isRhiEnabled() ? OfflineCompilation : RuntimeCompilation;
353}
354
355QSGRendererInterface::ShaderSourceTypes QSGDefaultContext::shaderSourceType() const
356{
357 return QSGRhiSupport::instance()->isRhiEnabled() ? ShaderSourceFile : (ShaderSourceString | ShaderSourceFile);
358}
359
360QT_END_NAMESPACE
361
362static void initResources()
363{
364 Q_INIT_RESOURCE(scenegraph);
365}
366
367Q_CONSTRUCTOR_FUNCTION(initResources)
368