0efe79f80d
We were already using the 'native' nomenclature when referring to these kinds of APIs, e.g. when talking about native handles, or the existing QPlatformNativeInterface on a QPA level. Using 'native' for the user facing APIs also distinguishes them from the 'platform' backend layer in QPA and elsewhere. Change-Id: I0f3273265904f0f19c0b6d62471f8820d3c3232e Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io> Reviewed-by: Lars Knoll <lars.knoll@qt.io>
404 lines
14 KiB
C++
404 lines
14 KiB
C++
/****************************************************************************
|
|
**
|
|
** Copyright (C) 2016 The Qt Company Ltd.
|
|
** Contact: https://www.qt.io/licensing/
|
|
**
|
|
** This file is part of the examples of the Qt Toolkit.
|
|
**
|
|
** $QT_BEGIN_LICENSE:BSD$
|
|
** 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.
|
|
**
|
|
** BSD License Usage
|
|
** Alternatively, you may use this file under the terms of the BSD license
|
|
** as follows:
|
|
**
|
|
** "Redistribution and use in source and binary forms, with or without
|
|
** modification, are permitted provided that the following conditions are
|
|
** met:
|
|
** * Redistributions of source code must retain the above copyright
|
|
** notice, this list of conditions and the following disclaimer.
|
|
** * Redistributions in binary form must reproduce the above copyright
|
|
** notice, this list of conditions and the following disclaimer in
|
|
** the documentation and/or other materials provided with the
|
|
** distribution.
|
|
** * Neither the name of The Qt Company Ltd nor the names of its
|
|
** contributors may be used to endorse or promote products derived
|
|
** from this software without specific prior written permission.
|
|
**
|
|
**
|
|
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
|
**
|
|
** $QT_END_LICENSE$
|
|
**
|
|
****************************************************************************/
|
|
|
|
#include "widget.h"
|
|
#include "renderwindow.h"
|
|
#include <QVBoxLayout>
|
|
#include <QComboBox>
|
|
#include <QGroupBox>
|
|
#include <QRadioButton>
|
|
#include <QCheckBox>
|
|
#include <QHBoxLayout>
|
|
#include <QLabel>
|
|
#include <QPushButton>
|
|
#include <QTextEdit>
|
|
#include <QSplitter>
|
|
#include <QGuiApplication>
|
|
#include <QSurfaceFormat>
|
|
#include <QOpenGLContext>
|
|
#include <QOpenGLFunctions>
|
|
#include <QDebug>
|
|
#include <QTextStream>
|
|
|
|
struct Version {
|
|
const char *str;
|
|
int major;
|
|
int minor;
|
|
};
|
|
|
|
static struct Version versions[] = {
|
|
{ "1.0", 1, 0 },
|
|
{ "1.1", 1, 1 },
|
|
{ "1.2", 1, 2 },
|
|
{ "1.3", 1, 3 },
|
|
{ "1.4", 1, 4 },
|
|
{ "1.5", 1, 5 },
|
|
{ "2.0", 2, 0 },
|
|
{ "2.1", 2, 1 },
|
|
{ "3.0", 3, 0 },
|
|
{ "3.1", 3, 1 },
|
|
{ "3.2", 3, 2 },
|
|
{ "3.3", 3, 3 },
|
|
{ "4.0", 4, 0 },
|
|
{ "4.1", 4, 1 },
|
|
{ "4.2", 4, 2 },
|
|
{ "4.3", 4, 3 },
|
|
{ "4.4", 4, 4 },
|
|
{ "4.5", 4, 5 }
|
|
};
|
|
|
|
struct Profile {
|
|
const char *str;
|
|
QSurfaceFormat::OpenGLContextProfile profile;
|
|
};
|
|
|
|
static struct Profile profiles[] = {
|
|
{ "none", QSurfaceFormat::NoProfile },
|
|
{ "core", QSurfaceFormat::CoreProfile },
|
|
{ "compatibility", QSurfaceFormat::CompatibilityProfile }
|
|
};
|
|
|
|
struct Option {
|
|
const char *str;
|
|
QSurfaceFormat::FormatOption option;
|
|
};
|
|
|
|
static struct Option options[] = {
|
|
{ "deprecated functions (not forward compatible)", QSurfaceFormat::DeprecatedFunctions },
|
|
{ "debug context", QSurfaceFormat::DebugContext },
|
|
{ "stereo buffers", QSurfaceFormat::StereoBuffers },
|
|
// This is not a QSurfaceFormat option but is helpful to determine if the driver
|
|
// allows compiling old-style shaders with core profile.
|
|
{ "force version 110 shaders", QSurfaceFormat::FormatOption(0) }
|
|
};
|
|
|
|
struct Renderable {
|
|
const char *str;
|
|
QSurfaceFormat::RenderableType renderable;
|
|
};
|
|
|
|
static struct Renderable renderables[] = {
|
|
{ "default", QSurfaceFormat::DefaultRenderableType },
|
|
{ "OpenGL", QSurfaceFormat::OpenGL },
|
|
{ "OpenGL ES", QSurfaceFormat::OpenGLES }
|
|
};
|
|
|
|
void Widget::addVersions(QLayout *layout)
|
|
{
|
|
QHBoxLayout *hbox = new QHBoxLayout;
|
|
hbox->setSpacing(20);
|
|
QLabel *label = new QLabel(tr("Context &version: "));
|
|
hbox->addWidget(label);
|
|
m_version = new QComboBox;
|
|
m_version->setMinimumWidth(60);
|
|
label->setBuddy(m_version);
|
|
hbox->addWidget(m_version);
|
|
for (size_t i = 0; i < sizeof(versions) / sizeof(Version); ++i) {
|
|
m_version->addItem(QString::fromLatin1(versions[i].str));
|
|
if (versions[i].major == 2 && versions[i].minor == 0)
|
|
m_version->setCurrentIndex(m_version->count() - 1);
|
|
}
|
|
|
|
QPushButton *btn = new QPushButton(tr("Create context"));
|
|
connect(btn, &QPushButton::clicked, this, &Widget::start);
|
|
btn->setMinimumSize(120, 40);
|
|
hbox->addWidget(btn);
|
|
|
|
layout->addItem(hbox);
|
|
}
|
|
|
|
void Widget::addProfiles(QLayout *layout)
|
|
{
|
|
QGroupBox *groupBox = new QGroupBox(tr("Profile"));
|
|
QVBoxLayout *vbox = new QVBoxLayout;
|
|
for (size_t i = 0; i < sizeof(profiles) / sizeof(Profile); ++i)
|
|
vbox->addWidget(new QRadioButton(QString::fromLatin1(profiles[i].str)));
|
|
static_cast<QRadioButton *>(vbox->itemAt(0)->widget())->setChecked(true);
|
|
groupBox->setLayout(vbox);
|
|
layout->addWidget(groupBox);
|
|
m_profiles = vbox;
|
|
}
|
|
|
|
void Widget::addOptions(QLayout *layout)
|
|
{
|
|
QGroupBox *groupBox = new QGroupBox(tr("Options"));
|
|
QVBoxLayout *vbox = new QVBoxLayout;
|
|
for (size_t i = 0; i < sizeof(options) / sizeof(Option); ++i)
|
|
vbox->addWidget(new QCheckBox(QString::fromLatin1(options[i].str)));
|
|
groupBox->setLayout(vbox);
|
|
layout->addWidget(groupBox);
|
|
m_options = vbox;
|
|
}
|
|
|
|
void Widget::addRenderableTypes(QLayout *layout)
|
|
{
|
|
QGroupBox *groupBox = new QGroupBox(tr("Renderable type"));
|
|
QVBoxLayout *vbox = new QVBoxLayout;
|
|
for (size_t i = 0; i < sizeof(renderables) / sizeof(Renderable); ++i)
|
|
vbox->addWidget(new QRadioButton(QString::fromLatin1(renderables[i].str)));
|
|
static_cast<QRadioButton *>(vbox->itemAt(0)->widget())->setChecked(true);
|
|
groupBox->setLayout(vbox);
|
|
layout->addWidget(groupBox);
|
|
m_renderables = vbox;
|
|
}
|
|
|
|
void Widget::addRenderWindow()
|
|
{
|
|
m_renderWindowLayout->addWidget(m_renderWindowContainer);
|
|
}
|
|
|
|
static QWidget *widgetWithLayout(QLayout *layout)
|
|
{
|
|
QWidget *w = new QWidget;
|
|
w->setLayout(layout);
|
|
return w;
|
|
}
|
|
|
|
Widget::Widget(QWidget *parent)
|
|
: QWidget(parent)
|
|
{
|
|
QVBoxLayout *layout = new QVBoxLayout;
|
|
QSplitter *vsplit = new QSplitter(Qt::Vertical);
|
|
layout->addWidget(vsplit);
|
|
|
|
QSplitter *hsplit = new QSplitter;
|
|
|
|
QVBoxLayout *settingsLayout = new QVBoxLayout;
|
|
addVersions(settingsLayout);
|
|
addProfiles(settingsLayout);
|
|
addOptions(settingsLayout);
|
|
addRenderableTypes(settingsLayout);
|
|
hsplit->addWidget(widgetWithLayout(settingsLayout));
|
|
|
|
QVBoxLayout *outputLayout = new QVBoxLayout;
|
|
m_output = new QTextEdit;
|
|
m_output->setReadOnly(true);
|
|
outputLayout->addWidget(m_output);
|
|
m_extensions = new QTextEdit;
|
|
m_extensions->setReadOnly(true);
|
|
outputLayout->addWidget(m_extensions);
|
|
hsplit->addWidget(widgetWithLayout(outputLayout));
|
|
|
|
hsplit->setStretchFactor(0, 4);
|
|
hsplit->setStretchFactor(1, 6);
|
|
vsplit->addWidget(hsplit);
|
|
|
|
m_renderWindowLayout = new QVBoxLayout;
|
|
vsplit->addWidget(widgetWithLayout(m_renderWindowLayout));
|
|
vsplit->setStretchFactor(1, 5);
|
|
|
|
m_renderWindowContainer = new QWidget;
|
|
addRenderWindow();
|
|
|
|
QString description;
|
|
QTextStream str(&description);
|
|
str << "Qt " << QT_VERSION_STR << ' ' << QGuiApplication::platformName();
|
|
const char *openGlVariables[] =
|
|
{"QT_ANGLE_PLATFORM", "QT_OPENGL", "QT_OPENGL_BUGLIST", "QT_OPENGL_DLL"};
|
|
const size_t variableCount = sizeof(openGlVariables) / sizeof(openGlVariables[0]);
|
|
for (size_t v = 0; v < variableCount; ++v) {
|
|
if (qEnvironmentVariableIsSet(openGlVariables[v]))
|
|
str << ' ' << openGlVariables[v] << '=' << qgetenv(openGlVariables[v]);
|
|
}
|
|
if (QCoreApplication::testAttribute(Qt::AA_UseOpenGLES))
|
|
str << " Qt::AA_UseOpenGLES";
|
|
if (QCoreApplication::testAttribute(Qt::AA_UseSoftwareOpenGL))
|
|
str << " Qt::AA_UseSoftwareOpenGL";
|
|
if (QCoreApplication::testAttribute(Qt::AA_UseDesktopOpenGL))
|
|
str << " Qt::AA_UseDesktopOpenGL";
|
|
layout->addWidget(new QLabel(description));
|
|
|
|
setLayout(layout);
|
|
}
|
|
|
|
void Widget::start()
|
|
{
|
|
QSurfaceFormat fmt;
|
|
|
|
int idx = m_version->currentIndex();
|
|
if (idx < 0)
|
|
return;
|
|
fmt.setVersion(versions[idx].major, versions[idx].minor);
|
|
|
|
for (size_t i = 0; i < sizeof(profiles) / sizeof(Profile); ++i)
|
|
if (static_cast<QRadioButton *>(m_profiles->itemAt(int(i))->widget())->isChecked()) {
|
|
fmt.setProfile(profiles[i].profile);
|
|
break;
|
|
}
|
|
|
|
bool forceGLSL110 = false;
|
|
for (size_t i = 0; i < sizeof(options) / sizeof(Option); ++i)
|
|
if (static_cast<QCheckBox *>(m_options->itemAt(int(i))->widget())->isChecked()) {
|
|
if (options[i].option)
|
|
fmt.setOption(options[i].option);
|
|
else if (i == 3)
|
|
forceGLSL110 = true;
|
|
}
|
|
|
|
for (size_t i = 0; i < sizeof(renderables) / sizeof(Renderable); ++i)
|
|
if (static_cast<QRadioButton *>(m_renderables->itemAt(int(i))->widget())->isChecked()) {
|
|
fmt.setRenderableType(renderables[i].renderable);
|
|
break;
|
|
}
|
|
|
|
// The example rendering will need a depth buffer.
|
|
fmt.setDepthBufferSize(16);
|
|
|
|
m_output->clear();
|
|
m_extensions->clear();
|
|
qDebug() << "Requesting surface format" << fmt;
|
|
|
|
m_renderWindowLayout->removeWidget(m_renderWindowContainer);
|
|
delete m_renderWindowContainer;
|
|
|
|
RenderWindow *renderWindow = new RenderWindow(fmt);
|
|
if (!renderWindow->context()) {
|
|
m_output->append(tr("Failed to create context"));
|
|
delete renderWindow;
|
|
m_renderWindowContainer = new QWidget;
|
|
addRenderWindow();
|
|
return;
|
|
}
|
|
m_surface = renderWindow;
|
|
|
|
renderWindow->setForceGLSL110(forceGLSL110);
|
|
connect(renderWindow, &RenderWindow::ready, this, &Widget::renderWindowReady);
|
|
connect(renderWindow, &RenderWindow::error, this, &Widget::renderWindowError);
|
|
|
|
m_renderWindowContainer = QWidget::createWindowContainer(renderWindow);
|
|
addRenderWindow();
|
|
}
|
|
|
|
void Widget::printFormat(const QSurfaceFormat &format)
|
|
{
|
|
m_output->append(tr("OpenGL version: %1.%2").arg(format.majorVersion()).arg(format.minorVersion()));
|
|
|
|
for (size_t i = 0; i < sizeof(profiles) / sizeof(Profile); ++i)
|
|
if (profiles[i].profile == format.profile()) {
|
|
m_output->append(tr("Profile: %1").arg(QString::fromLatin1(profiles[i].str)));
|
|
break;
|
|
}
|
|
|
|
QString opts;
|
|
for (size_t i = 0; i < sizeof(options) / sizeof(Option); ++i)
|
|
if (format.testOption(options[i].option))
|
|
opts += QString::fromLatin1(options[i].str) + QLatin1Char(' ');
|
|
m_output->append(tr("Options: %1").arg(opts));
|
|
|
|
for (size_t i = 0; i < sizeof(renderables) / sizeof(Renderable); ++i)
|
|
if (renderables[i].renderable == format.renderableType()) {
|
|
m_output->append(tr("Renderable type: %1").arg(QString::fromLatin1(renderables[i].str)));
|
|
break;
|
|
}
|
|
|
|
m_output->append(tr("Depth buffer size: %1").arg(QString::number(format.depthBufferSize())));
|
|
m_output->append(tr("Stencil buffer size: %1").arg(QString::number(format.stencilBufferSize())));
|
|
m_output->append(tr("Samples: %1").arg(QString::number(format.samples())));
|
|
m_output->append(tr("Red buffer size: %1").arg(QString::number(format.redBufferSize())));
|
|
m_output->append(tr("Green buffer size: %1").arg(QString::number(format.greenBufferSize())));
|
|
m_output->append(tr("Blue buffer size: %1").arg(QString::number(format.blueBufferSize())));
|
|
m_output->append(tr("Alpha buffer size: %1").arg(QString::number(format.alphaBufferSize())));
|
|
m_output->append(tr("Swap interval: %1").arg(QString::number(format.swapInterval())));
|
|
}
|
|
|
|
void Widget::renderWindowReady()
|
|
{
|
|
QOpenGLContext *context = QOpenGLContext::currentContext();
|
|
Q_ASSERT(context);
|
|
|
|
QString vendor, renderer, version, glslVersion;
|
|
const GLubyte *p;
|
|
QOpenGLFunctions *f = context->functions();
|
|
if ((p = f->glGetString(GL_VENDOR)))
|
|
vendor = QString::fromLatin1(reinterpret_cast<const char *>(p));
|
|
if ((p = f->glGetString(GL_RENDERER)))
|
|
renderer = QString::fromLatin1(reinterpret_cast<const char *>(p));
|
|
if ((p = f->glGetString(GL_VERSION)))
|
|
version = QString::fromLatin1(reinterpret_cast<const char *>(p));
|
|
if ((p = f->glGetString(GL_SHADING_LANGUAGE_VERSION)))
|
|
glslVersion = QString::fromLatin1(reinterpret_cast<const char *>(p));
|
|
|
|
m_output->append(tr("*** Context information ***"));
|
|
m_output->append(tr("Vendor: %1").arg(vendor));
|
|
m_output->append(tr("Renderer: %1").arg(renderer));
|
|
m_output->append(tr("OpenGL version: %1").arg(version));
|
|
m_output->append(tr("GLSL version: %1").arg(glslVersion));
|
|
|
|
m_output->append(tr("\n*** QSurfaceFormat from context ***"));
|
|
printFormat(context->format());
|
|
|
|
m_output->append(tr("\n*** QSurfaceFormat from window surface ***"));
|
|
printFormat(m_surface->format());
|
|
|
|
m_output->append(tr("\n*** Qt build information ***"));
|
|
const char *gltype[] = { "Desktop", "GLES 2", "GLES 1" };
|
|
m_output->append(tr("Qt OpenGL configuration: %1")
|
|
.arg(QString::fromLatin1(gltype[QOpenGLContext::openGLModuleType()])));
|
|
#if defined(Q_OS_WIN)
|
|
using namespace QNativeInterface;
|
|
m_output->append(tr("Qt OpenGL library handle: %1")
|
|
.arg(QString::number(qintptr(QWGLContext::openGLModuleHandle()), 16)));
|
|
#endif
|
|
|
|
QList<QByteArray> extensionList = context->extensions().values();
|
|
std::sort(extensionList.begin(), extensionList.end());
|
|
m_extensions->append(tr("Found %1 extensions:").arg(extensionList.count()));
|
|
for (const QByteArray &ext : qAsConst(extensionList))
|
|
m_extensions->append(QString::fromLatin1(ext));
|
|
|
|
m_output->moveCursor(QTextCursor::Start);
|
|
m_extensions->moveCursor(QTextCursor::Start);
|
|
}
|
|
|
|
void Widget::renderWindowError(const QString &msg)
|
|
{
|
|
m_output->append(tr("An error has occurred:\n%1").arg(msg));
|
|
}
|