blob: 6a82562a0d26110f6d4378226bb626388db8d839 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Quick 3D.
**
** $QT_BEGIN_LICENSE:GPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 or (at your option) any later version
** approved by the KDE Free Qt Foundation. The licenses are as published by
** the Free Software Foundation and appearing in the file LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qquick3d.h"
#include <QtGui/qopenglcontext.h>
#include <QtGui/qopenglfunctions.h>
#include <QtGui/qoffscreensurface.h>
#include <QtCore/qstring.h>
/*!
\class QQuick3D
\inmodule QtQuick3D
\since 5.15
\brief Helper class for selecting correct surface format.
Lets you select a \l {QSurfaceFormat}{surface format} that is appropriate for the application.
Usage:
\code
QSurfaceFormat::setDefaultFormat(QQuick3D::idealSurfaceFormat(4));
\endcode
*/
QT_BEGIN_NAMESPACE
static QSurfaceFormat findIdealGLVersion(int samples)
{
QSurfaceFormat fmt;
int defaultSamples = fmt.samples();
const bool multisampling = samples > 1;
fmt.setProfile(QSurfaceFormat::CoreProfile);
// Advanced: Try 4.3 core (so we get compute shaders for instance)
fmt.setVersion(4, 3);
fmt.setSamples(multisampling ? samples : defaultSamples);
QOpenGLContext ctx;
ctx.setFormat(fmt);
if (ctx.create() && ctx.format().version() >= qMakePair(4, 3)) {
qDebug("Requesting OpenGL 4.3 core context succeeded");
return ctx.format();
}
if (multisampling) {
// try without multisampling
fmt.setSamples(defaultSamples);
ctx.setFormat(fmt);
if (ctx.create() && ctx.format().version() >= qMakePair(4, 3)) {
qDebug("Requesting OpenGL 4.3 core context succeeded without multisampling");
return ctx.format();
}
}
// Basic: Stick with 3.3 for now to keep less fortunate, Mesa-based systems happy
fmt.setVersion(3, 3);
fmt.setSamples(multisampling ? samples : defaultSamples);
ctx.setFormat(fmt);
if (ctx.create() && ctx.format().version() >= qMakePair(3, 3)) {
qDebug("Requesting OpenGL 3.3 core context succeeded");
return ctx.format();
}
if (multisampling) {
// try without multisampling
fmt.setSamples(defaultSamples);
ctx.setFormat(fmt);
if (ctx.create() && ctx.format().version() >= qMakePair(3, 3)) {
qDebug("Requesting OpenGL 3.3 core context succeeded without multisampling");
return ctx.format();
}
}
qDebug("Unable to find ideal GL version.");
return fmt;
}
static bool isBlackListedES3Driver(QOpenGLContext &ctx)
{
static bool hasBeenTested = false;
static bool result = false;
if (!hasBeenTested) {
QOffscreenSurface offscreenSurface;
offscreenSurface.setFormat(ctx.format());
offscreenSurface.create();
if (ctx.makeCurrent(&offscreenSurface)) {
auto glFunctions = ctx.functions();
QString vendorString = QString::fromLatin1(reinterpret_cast<const char *>(glFunctions->glGetString(GL_RENDERER)));
ctx.doneCurrent();
if (vendorString == QStringLiteral("PowerVR Rogue GE8300"))
result = true;
} else {
qWarning("Context created successfully but makeCurrent() failed - this is bad.");
}
hasBeenTested = true;
}
return result;
}
static QSurfaceFormat findIdealGLESVersion(int samples)
{
QSurfaceFormat fmt;
int defaultSamples = fmt.samples();
const bool multisampling = samples > 1;
// Advanced: Try 3.2
fmt.setVersion(3, 2);
fmt.setRenderableType(QSurfaceFormat::OpenGLES);
fmt.setSamples(multisampling ? samples : defaultSamples);
QOpenGLContext ctx;
ctx.setFormat(fmt);
qDebug("Testing OpenGL ES 3.2");
if (ctx.create() && ctx.format().version() >= qMakePair(3, 2)) {
qDebug("Requesting OpenGL ES 3.2 context succeeded");
return ctx.format();
}
if (multisampling) {
fmt.setSamples(defaultSamples);
ctx.setFormat(fmt);
if (ctx.create() && ctx.format().version() >= qMakePair(3, 2)) {
qDebug("Requesting OpenGL ES 3.2 context succeeded without multisampling");
return ctx.format();
}
}
// Advanced: Try 3.1 (so we get compute shaders for instance)
fmt.setVersion(3, 1);
fmt.setRenderableType(QSurfaceFormat::OpenGLES);
fmt.setSamples(multisampling ? samples : defaultSamples);
ctx.setFormat(fmt);
// Now, it's important to check the format with the actual version (parsed
// back from GL_VERSION) since some implementations, ANGLE for instance,
// are broken and succeed the 3.1 context request even though they only
// support and return a 3.0 context. This is against the spec since 3.0 is
// obviously not backwards compatible with 3.1, but hey...
qDebug("Testing OpenGL ES 3.1");
if (ctx.create() && ctx.format().version() >= qMakePair(3, 1)) {
qDebug("Requesting OpenGL ES 3.1 context succeeded");
return ctx.format();
}
if (multisampling) {
fmt.setSamples(defaultSamples);
ctx.setFormat(fmt);
if (ctx.create() && ctx.format().version() >= qMakePair(3, 1)) {
qDebug("Requesting OpenGL ES 3.1 context succeeded without multisampling");
return ctx.format();
}
}
// Basic: OpenGL ES 3.0 is a hard requirement at the moment since we can
// only generate 300 es shaders, uniform buffers are mandatory.
fmt.setVersion(3, 0);
fmt.setSamples(multisampling ? samples : defaultSamples);
ctx.setFormat(fmt);
qDebug("Testing OpenGL ES 3.0");
if (ctx.create() && ctx.format().version() >= qMakePair(3, 0) && !isBlackListedES3Driver(ctx)) {
qDebug("Requesting OpenGL ES 3.0 context succeeded");
return ctx.format();
}
if (multisampling) {
fmt.setSamples(defaultSamples);
ctx.setFormat(fmt);
if (ctx.create() && ctx.format().version() >= qMakePair(3, 0)
&& !isBlackListedES3Driver(ctx)) {
qDebug("Requesting OpenGL ES 3.0 context succeeded without multisampling");
return ctx.format();
}
}
fmt.setVersion(2, 0);
fmt.setSamples(multisampling ? samples : defaultSamples);
ctx.setFormat(fmt);
qDebug("Testing OpenGL ES 2.0");
if (ctx.create()) {
qDebug("Requesting OpenGL ES 2.0 context succeeded");
return fmt;
}
if (multisampling) {
fmt.setSamples(defaultSamples);
ctx.setFormat(fmt);
if (ctx.create()) {
qDebug("Requesting OpenGL ES 2.0 context succeeded without multisampling");
return fmt;
}
}
qDebug("Unable to find ideal GLES version.");
return fmt;
}
/*!
Returns an ideal surface format for the platform. Optionally, \a samples can be specified
to select the number of multisamples for antialiasing.
*/
QSurfaceFormat QQuick3D::idealSurfaceFormat(int samples)
{
static const QSurfaceFormat f = [samples] {
QSurfaceFormat fmt;
if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) { // works in dynamic gl builds too because there's a qguiapp already
fmt = findIdealGLVersion(samples);
} else {
fmt = findIdealGLESVersion(samples);
}
fmt.setDepthBufferSize(24);
fmt.setStencilBufferSize(8);
// Ignore MSAA here as that is a per-layer setting.
return fmt;
}();
return f;
}
QT_END_NAMESPACE