Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquick3d.cpp
Go to the documentation of this file.
1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qquick3d.h"
5
6#if QT_CONFIG(opengl)
7# include <QtGui/qopenglcontext.h>
8#endif
9#include <QtQuick/qquickwindow.h>
10
11#include <QtCore/qloggingcategory.h>
12
13Q_STATIC_LOGGING_CATEGORY(lcQuick3D, "qt.quick3d.general")
14
15/*!
16 \class QQuick3D
17 \inmodule QtQuick3D
18 \since 5.15
19 \brief Helper class for selecting correct surface format.
20
21 When using Qt Quick 3D with OpenGL it is necessary to take extra steps to
22 define what kind of \l {QSurfaceFormat}{surface format} is used when
23 initializing Qt Quick. This is because by the time Qt Quick is aware that
24 3D content is being used, the OpenGL context and window surface has already
25 been initialized. So this helper class is provided to request the ideal
26 surface format from Qt Quick 3D so that it can be set as the default surface
27 for Qt Quick before initialization.
28
29 If this helper is run when using any other rendering backends than OpenGL
30 then it just returns a copy of the current default QSurfaceFormat with the
31 requested samples.
32
33 If this helper is run when using the OpenGL rendering backend, then it will
34 test for sufficiently modern versions of OpenGL and support for
35 multisampling if requested. Normally Qt Quick will request an OpenGL 2.0 or
36 OpenGL ES 2.0 context, which would limit the features available when using
37 Qt Quick 3D, so an extra step is needed to request a more capable context.
38
39 The correct usage pattern is to call \l QSurfaceFormat::setDefaultFormat
40 to set the \l QSurfaceFormat returned by \l QQuick3D::idealSurfaceFormat.
41 It is important that this method is called after \l QGuiApplication is
42 constructed, but before the Qt Quick application content is loaded. This
43 code snippet shows the correct usage pattern:
44 \code
45 #include <QGuiApplication>
46 #include <QQmlApplicationEngine>
47
48 #include <QtGui>
49 #include <QtQuick3D/qquick3d.h>
50
51 int main(int argc, char *argv[])
52 {
53 QGuiApplication app(argc, argv);
54
55 QSurfaceFormat::setDefaultFormat(QQuick3D::idealSurfaceFormat(4));
56
57 QQmlApplicationEngine engine;
58 engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
59 if (engine.rootObjects().isEmpty())
60 return -1;
61
62 return app.exec();
63 }
64 \endcode
65
66 \sa {Qt Quick 3D Graphics Requirements} {OpenGL specifics}
67*/
68
69QT_BEGIN_NAMESPACE
70#if QT_CONFIG(opengl)
71static QSurfaceFormat findIdealGLVersion(int samples)
72{
73 QSurfaceFormat fmt;
74 int defaultSamples = fmt.samples();
75 const bool multisampling = samples > 1;
76 fmt.setProfile(QSurfaceFormat::CoreProfile);
77
78 // Proper case: Try 4.3 core (so we get compute shaders for instance)
79 fmt.setVersion(4, 3);
80 fmt.setSamples(multisampling ? samples : defaultSamples);
81 QOpenGLContext ctx;
82 ctx.setFormat(fmt);
83 if (ctx.create() && ctx.format().version() >= qMakePair(4, 3)) {
84 qCDebug(lcQuick3D, "Requesting OpenGL 4.3 core context succeeded");
85 return ctx.format();
86 }
87 if (multisampling) {
88 // try without multisampling
89 fmt.setSamples(defaultSamples);
90 ctx.setFormat(fmt);
91 if (ctx.create() && ctx.format().version() >= qMakePair(4, 3)) {
92 qCDebug(lcQuick3D, "Requesting OpenGL 4.3 core context succeeded without multisampling");
93 return ctx.format();
94 }
95 }
96
97 // Fallback, but still good, case: Stick with 3.3 (the only thing we lose is compute for HDR mipmaps)
98 fmt.setVersion(3, 3);
99 fmt.setSamples(multisampling ? samples : defaultSamples);
100 ctx.setFormat(fmt);
101 if (ctx.create() && ctx.format().version() >= qMakePair(3, 3)) {
102 qCDebug(lcQuick3D, "Requesting OpenGL 3.3 core context succeeded");
103 return ctx.format();
104 }
105 if (multisampling) {
106 // try without multisampling
107 fmt.setSamples(defaultSamples);
108 ctx.setFormat(fmt);
109 if (ctx.create() && ctx.format().version() >= qMakePair(3, 3)) {
110 qCDebug(lcQuick3D, "Requesting OpenGL 3.3 core context succeeded without multisampling");
111 return ctx.format();
112 }
113 }
114
115 // If all else fails, try 3.0. This may have some issues but most things should work.
116 fmt.setVersion(3, 0);
117 // the modern core-compat. concept as we know it is only there since 3.2
118 fmt.setProfile(QSurfaceFormat::NoProfile);
119 fmt.setSamples(multisampling ? samples : defaultSamples);
120 ctx.setFormat(fmt);
121 if (ctx.create() && ctx.format().version() >= qMakePair(3, 0)) {
122 qCDebug(lcQuick3D, "Requesting OpenGL 3.0 context succeeded");
123 return ctx.format();
124 }
125 if (multisampling) {
126 fmt.setSamples(defaultSamples);
127 ctx.setFormat(fmt);
128 if (ctx.create() && ctx.format().version() >= qMakePair(3, 0)) {
129 qCDebug(lcQuick3D, "Requesting OpenGL 3.0 context succeeded without multisampling");
130 return ctx.format();
131 }
132 }
133
134 qCWarning(lcQuick3D, "Unable to find ideal GL version.");
135 return fmt;
136}
137
138static QSurfaceFormat findIdealGLESVersion(int samples)
139{
140 QSurfaceFormat fmt;
141 int defaultSamples = fmt.samples();
142 const bool multisampling = samples > 1;
143
144 // Proper case: Try 3.1 (so we get compute shaders)
145 fmt.setVersion(3, 1);
146 fmt.setRenderableType(QSurfaceFormat::OpenGLES);
147 fmt.setSamples(multisampling ? samples : defaultSamples);
148 QOpenGLContext ctx;
149 ctx.setFormat(fmt);
150
151 // Now, it's important to check the format with the actual version (parsed
152 // back from GL_VERSION) since some implementations are broken and succeed
153 // the 3.1 context request even though they only support and return a 3.0
154 // context. This is against the spec since 3.0 is obviously not backwards
155 // compatible with 3.1, but hey...
156 qCDebug(lcQuick3D, "Testing OpenGL ES 3.1");
157 if (ctx.create() && ctx.format().version() >= qMakePair(3, 1)) {
158 qCDebug(lcQuick3D, "Requesting OpenGL ES 3.1 context succeeded");
159 return ctx.format();
160 }
161 if (multisampling) {
162 fmt.setSamples(defaultSamples);
163 ctx.setFormat(fmt);
164 if (ctx.create() && ctx.format().version() >= qMakePair(3, 1)) {
165 qCDebug(lcQuick3D, "Requesting OpenGL ES 3.1 context succeeded without multisampling");
166 return ctx.format();
167 }
168 }
169
170 // Fallback, but still good, case: OpenGL ES 3.0
171 fmt.setVersion(3, 0);
172 fmt.setSamples(multisampling ? samples : defaultSamples);
173 ctx.setFormat(fmt);
174 qCDebug(lcQuick3D, "Testing OpenGL ES 3.0");
175 if (ctx.create() && ctx.format().version() >= qMakePair(3, 0)) {
176 qCDebug(lcQuick3D, "Requesting OpenGL ES 3.0 context succeeded");
177 return ctx.format();
178 }
179 if (multisampling) {
180 fmt.setSamples(defaultSamples);
181 ctx.setFormat(fmt);
182 if (ctx.create() && ctx.format().version() >= qMakePair(3, 0)) {
183 qCDebug(lcQuick3D, "Requesting OpenGL ES 3.0 context succeeded without multisampling");
184 return ctx.format();
185 }
186 }
187
188 // If all else fails, try 2.0 but that's going to lose a bunch of features.
189#ifdef Q_OS_WASM
190 qCWarning(lcQuick3D, "OpenGL ES 3.0 / WebGL 2 is required on WebAssembly.");
191#endif
192 fmt.setVersion(2, 0);
193 fmt.setSamples(multisampling ? samples : defaultSamples);
194 ctx.setFormat(fmt);
195 qCDebug(lcQuick3D, "Testing OpenGL ES 2.0");
196 if (ctx.create()) {
197 qCDebug(lcQuick3D, "Requesting OpenGL ES 2.0 context succeeded");
198 return fmt;
199 }
200 if (multisampling) {
201 fmt.setSamples(defaultSamples);
202 ctx.setFormat(fmt);
203 if (ctx.create()) {
204 qCDebug(lcQuick3D, "Requesting OpenGL ES 2.0 context succeeded without multisampling");
205 return fmt;
206 }
207 }
208
209 qCWarning(lcQuick3D, "Unable to find ideal GLES version.");
210 return fmt;
211}
212#endif // #if QT_CONFIG(opengl)
213/*!
214 Returns an ideal surface format for the platform. Optionally, \a samples can be specified
215 to select the number of multisamples for antialiasing.
216*/
217QSurfaceFormat QQuick3D::idealSurfaceFormat(int samples)
218{
219 if (QQuickWindow::graphicsApi() != QSGRendererInterface::OpenGLRhi) {
220 QSurfaceFormat fmt = QSurfaceFormat::defaultFormat();
221 fmt.setSamples(samples);
222 return fmt;
223 }
224#if QT_CONFIG(opengl)
225 static const QSurfaceFormat f = [samples] {
226 QSurfaceFormat fmt;
227 if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) { // works in dynamic gl builds too because there's a qguiapp already
228 fmt = findIdealGLVersion(samples);
229 } else {
230 fmt = findIdealGLESVersion(samples);
231 }
232 fmt.setDepthBufferSize(24);
233 fmt.setStencilBufferSize(8);
234 // Ignore MSAA here as that is a per-layer setting.
235 return fmt;
236 }();
237#else
238 // It really shouldn't be possible to get but if we do
239 // but at least return something if we do.
240 QSurfaceFormat f = QSurfaceFormat::defaultFormat();
241#endif //#if QT_CONFIG(opengl)
242 return f;
243}
244
245QT_END_NAMESPACE
Q_STATIC_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core")