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