Commit 9ffcb7d4 authored by Laszlo Agocs's avatar Laszlo Agocs

gl: Create a context automatically

...and allow importing an already existing QOpenGLContext via
QRhiGles2NativeHandles. This way the API is fully consistent with other
backends and typical applications become shorter due to not having to
create a context, having a convenience function to create the
QOffscreenSurface, and not having to worry about depth/stencil in
QSurfaceFormat.
parent f6fc566d
......@@ -63,7 +63,6 @@
#ifndef QT_NO_OPENGL
#include <QRhiGles2InitParams>
#include <QOpenGLContext>
#include <QOffscreenSurface>
#endif
......@@ -165,7 +164,6 @@ protected:
int m_elapsedCount;
#ifndef QT_NO_OPENGL
QOpenGLContext *m_context = nullptr;
QOffscreenSurface *m_fallbackSurface = nullptr;
#endif
};
......@@ -245,18 +243,10 @@ void Window::init()
{
#ifndef QT_NO_OPENGL
if (graphicsApi == OpenGL) {
m_context = new QOpenGLContext;
if (!m_context->create())
qFatal("Failed to get OpenGL context");
m_fallbackSurface = new QOffscreenSurface;
m_fallbackSurface->setFormat(m_context->format());
m_fallbackSurface->create();
m_fallbackSurface = QRhiGles2InitParams::newFallbackSurface();
QRhiGles2InitParams params;
params.context = m_context;
params.window = this;
params.fallbackSurface = m_fallbackSurface;
params.window = this;
m_r = QRhi::create(QRhi::OpenGLES2, &params);
}
#endif
......@@ -368,7 +358,6 @@ void Window::releaseResources()
delete m_r;
#ifndef QT_NO_OPENGL
delete m_context;
delete m_fallbackSurface;
#endif
}
......@@ -512,12 +501,6 @@ int main(int argc, char **argv)
if (cmdLineParser.isSet(mtlOption))
graphicsApi = Metal;
// OpenGL specifics.
QSurfaceFormat fmt;
fmt.setDepthBufferSize(24);
fmt.setStencilBufferSize(8);
QSurfaceFormat::setDefaultFormat(fmt);
// Vulkan setup.
#if QT_CONFIG(vulkan)
QVulkanInstance inst;
......
......@@ -65,7 +65,6 @@
#ifndef QT_NO_OPENGL
#include <QRhiGles2InitParams>
#include <QOpenGLContext>
#include <QOffscreenSurface>
#endif
......@@ -115,7 +114,6 @@ static struct {
#endif
QRhi *r = nullptr;
#ifndef QT_NO_OPENGL
QOpenGLContext *context = nullptr;
QOffscreenSurface *fallbackSurface = nullptr;
#endif
} r;
......@@ -124,18 +122,10 @@ void createRhi()
{
#ifndef QT_NO_OPENGL
if (graphicsApi == OpenGL) {
r.context = new QOpenGLContext;
if (!r.context->create())
qFatal("Failed to get OpenGL context");
r.fallbackSurface = new QOffscreenSurface;
r.fallbackSurface->setFormat(r.context->format());
r.fallbackSurface->create();
r.fallbackSurface = QRhiGles2InitParams::newFallbackSurface();
QRhiGles2InitParams params;
params.context = r.context;
//params.window = this;
params.fallbackSurface = r.fallbackSurface;
//params.window = this;
r.r = QRhi::create(QRhi::OpenGLES2, &params);
}
#endif
......@@ -172,7 +162,6 @@ void destroyRhi()
delete r.r;
#ifndef QT_NO_OPENGL
delete r.context;
delete r.fallbackSurface;
#endif
}
......
......@@ -68,7 +68,6 @@
#ifndef QT_NO_OPENGL
#include <QRhiGles2InitParams>
#include <QOpenGLContext>
#include <QOffscreenSurface>
#endif
......@@ -253,7 +252,6 @@ struct Renderer
Thread *thread;
QRhi *r = nullptr;
#ifndef QT_NO_OPENGL
QOpenGLContext *context = nullptr;
QOffscreenSurface *fallbackSurface = nullptr;
#endif
......@@ -311,11 +309,6 @@ void Thread::run()
sleeping = false;
}
}
#ifndef QT_NO_OPENGL
if (renderer->context)
renderer->context->moveToThread(qGuiApp->thread());
#endif
}
Renderer::Renderer(QWindow *w, const QColor &bgColor, int rotationAxis)
......@@ -326,17 +319,8 @@ Renderer::Renderer(QWindow *w, const QColor &bgColor, int rotationAxis)
thread = new Thread(this);
#ifndef QT_NO_OPENGL
if (graphicsApi == OpenGL) {
context = new QOpenGLContext;
if (!context->create())
qFatal("Failed to get OpenGL context");
fallbackSurface = new QOffscreenSurface;
fallbackSurface->setFormat(context->format());
fallbackSurface->create();
context->moveToThread(thread);
}
if (graphicsApi == OpenGL)
fallbackSurface = QRhiGles2InitParams::newFallbackSurface();
#endif
}
......@@ -347,7 +331,6 @@ Renderer::~Renderer()
delete thread;
#ifndef QT_NO_OPENGL
delete context;
delete fallbackSurface;
#endif
}
......@@ -362,9 +345,8 @@ void Renderer::createRhi()
#ifndef QT_NO_OPENGL
if (graphicsApi == OpenGL) {
QRhiGles2InitParams params;
params.context = context;
params.window = window;
params.fallbackSurface = fallbackSurface;
params.window = window;
r = QRhi::create(QRhi::OpenGLES2, &params);
}
#endif
......@@ -749,11 +731,6 @@ int main(int argc, char **argv)
qDebug("Selected graphics API is %s", qPrintable(graphicsApiName()));
qDebug("This is a multi-api example, use command line arguments to override:\n%s", qPrintable(cmdLineParser.helpText()));
QSurfaceFormat fmt;
fmt.setDepthBufferSize(24);
fmt.setStencilBufferSize(8);
QSurfaceFormat::setDefaultFormat(fmt);
#if QT_CONFIG(vulkan)
instance = new QVulkanInstance;
if (graphicsApi == Vulkan) {
......
......@@ -160,23 +160,11 @@ int main(int argc, char **argv)
QRhi *r = nullptr;
#ifndef QT_NO_OPENGL
QOpenGLContext context;
QOffscreenSurface offscreenSurface;
QScopedPointer<QOffscreenSurface> offscreenSurface;
if (graphicsApi == OpenGL) {
QSurfaceFormat fmt;
fmt.setDepthBufferSize(24);
fmt.setStencilBufferSize(8);
QSurfaceFormat::setDefaultFormat(fmt);
if (!context.create())
qFatal("Failed to get OpenGL context");
offscreenSurface.setFormat(context.format());
offscreenSurface.create();
offscreenSurface.reset(QRhiGles2InitParams::newFallbackSurface());
QRhiGles2InitParams params;
params.context = &context;
params.fallbackSurface = &offscreenSurface;
params.fallbackSurface = offscreenSurface.data();
r = QRhi::create(QRhi::OpenGLES2, &params);
}
#endif
......
......@@ -66,7 +66,6 @@
#ifndef QT_NO_OPENGL
#include <QRhiGles2InitParams>
#include <QOpenGLContext>
#include <QOffscreenSurface>
#endif
......@@ -162,7 +161,6 @@ protected:
int m_frameCount;
#ifndef QT_NO_OPENGL
QOpenGLContext *m_context = nullptr;
QOffscreenSurface *m_fallbackSurface = nullptr;
#endif
};
......@@ -247,18 +245,10 @@ void Window::init()
#ifndef QT_NO_OPENGL
if (graphicsApi == OpenGL) {
m_context = new QOpenGLContext;
if (!m_context->create())
qFatal("Failed to get OpenGL context");
m_fallbackSurface = new QOffscreenSurface;
m_fallbackSurface->setFormat(m_context->format());
m_fallbackSurface->create();
m_fallbackSurface = QRhiGles2InitParams::newFallbackSurface();
QRhiGles2InitParams params;
params.context = m_context;
params.window = this;
params.fallbackSurface = m_fallbackSurface;
params.window = this;
m_r = QRhi::create(QRhi::OpenGLES2, &params, rhiFlags);
}
#endif
......@@ -329,8 +319,6 @@ void Window::releaseResources()
m_r = nullptr;
#ifndef QT_NO_OPENGL
delete m_context;
m_context = nullptr;
delete m_fallbackSurface;
m_fallbackSurface = nullptr;
#endif
......
......@@ -2548,7 +2548,8 @@ QRhi *QRhi::create(Implementation impl, QRhiInitParams *params, Flags flags, QRh
#endif
case OpenGLES2:
#ifndef QT_NO_OPENGL
r->d = new QRhiGles2(static_cast<QRhiGles2InitParams *>(params));
r->d = new QRhiGles2(static_cast<QRhiGles2InitParams *>(params),
static_cast<QRhiGles2NativeHandles *>(importDevice));
break;
#else
qWarning("This build of Qt has no OpenGL support");
......
......@@ -63,51 +63,84 @@ QT_BEGIN_NAMESPACE
\inmodule QtRhi
\brief OpenGL specific initialization parameters.
An OpenGL-based QRhi needs an already initialized QOpenGLContext and
QOffscreenSurface. Additionally, while optional, it is recommended that the
QWindow the first QRhiSwapChain will target is passed in as well.
An OpenGL-based QRhi needs an already created QOffscreenSurface at minimum.
Additionally, while optional, it is recommended that the QWindow the first
QRhiSwapChain will target is passed in as well.
\badcode
QSurfaceFormat format;
format.setDepthBufferSize(24);
format.setStencilBufferSize(8);
QSurfaceFormat::setDefaultFormat(format);
context = new QOpenGLContext;
if (!context->create())
qFatal("Failed to get OpenGL context");
fallbackSurface = new QOffscreenSurface;
fallbackSurface->setFormat(context->format());
fallbackSurface->create();
fallbackSurface = QRhiGles2InitParams::newFallbackSurface();
QRhiGles2InitParams params;
params.context = context;
params.window = window;
params.fallbackSurface = fallbackSurface;
params.window = window;
rhi = QRhi::create(QRhi::OpenGLES2, &params);
\endcode
The example here shows the creation of the context and fallback surface as
well. Watch out for the fact that, unlike QOpenGLContext, a
QOffscreenSurface can only be created on the gui/main thread.
By default QRhi creates a QOpenGLContext on its own. The QSurfaceFormat for
this context is based on what was set via
QSurfaceFormat::setDefaultSurfaceFormat().
\note The depth and stencil buffer sizes are set automatically to 24 and 8
when no size was explicitly set for these buffers in the default surface
format.
The QRhi does not take ownership of any of the external objects.
This approach works well in most cases, included threaded scenarios, where
there is a dedicated QRhi for each rendering thread. As there will be a
QOpenGLContext for each QRhi, the OpenGL context requirements (a context
can only be current on one thread) are satisfied. The implicitly created
context is destroyed automatically together with the QRhi.
A QOffscreenSurface has to be specified in \l fallbackSurface. In order to
prevent mistakes in threaded situations, this is never created
automatically by the QRhi since, like QWindow, QOffscreenSurface can only
be created on the gui/main thread.
As a convenience, applications can use newFallbackSurface() which creates
and returns a QOffscreenSurface that is compatible with the QOpenGLContext
that is going to be created by the QRhi afterwards. Note that the ownership
of the returned QOffscreenSurface is transfered to the caller and the QRhi
will not destroy it.
\note QRhiSwapChain can only target QWindow instances that have their
surface type set to QSurface::OpenGLSurface.
\note \l window is optional. It is recommended to specify it whever
\note \l window is optional. It is recommended to specify it whenever
possible, in order to avoid problems on multi-adapter and multi-screen
systems. When \l window is not set, the very first
QOpenGLContext::makeCurrent() happens with \l fallbackSurface which may be
an invisible window on some platforms (for example, Windows) and that may
trigger unexpected problems in some cases.
\note A QRhi instance can be created and used on any thread but all usage
must be limited to that one single thread. When it comes to native objects,
such as OpenGL contexts, passed in in QRhiInitParams, it is up to the
application to ensure they are not misused by other threads.
\section2 Working with existing OpenGL contexts
When interoperating with another graphics engine, it may be necessary to
get a QRhi instance that uses the same OpenGL context. This can be achieved
by passing a pointer to a QRhiGles2NativeHandles to QRhi::create(). The
\l{QRhiGles2NativeHandles::context}{context} must be set to a non-null
value.
An alternative approach is to create a QOpenGLContext that
\l{QOpenGLContext::setShareContext()}{shares resources} with the other
engine's context and passing in that context via QRhiGles2NativeHandles.
The QRhi does not take ownership of the QOpenGLContext passed in via
QRhiGles2NativeHandles.
*/
/*!
\fn QOffscreenSurface *QRhiGles2InitParams::newFallbackSurface()
\return a new QOffscreenSurface that can be used with a QRhi by passing it
via a QRhiGles2InitParams.
The \l{QOffscreenSurface::format()}{format} is adjusted as appropriate in
order to avoid having problems afterwards due to an incompatible context
and surface.
\note This function must only be called on the gui/main thread.
\note It is the application's responsibility to destroy the returned
QOffscreenSurface on the gui/main thread once the associated QRhi has been
destroyed. The QRhi will not destroy the QOffscreenSurface.
*/
/*!
......@@ -122,12 +155,52 @@ QT_BEGIN_NAMESPACE
\brief Holds the OpenGL texture object that is backing a QRhiTexture instance.
*/
QRhiGles2::QRhiGles2(QRhiGles2InitParams *params)
static QSurfaceFormat qrhigles2_effectiveFormat()
{
QSurfaceFormat fmt = QSurfaceFormat::defaultFormat();
if (fmt.depthBufferSize() == -1)
fmt.setDepthBufferSize(24);
if (fmt.stencilBufferSize() == -1)
fmt.setStencilBufferSize(8);
return fmt;
}
QOffscreenSurface *QRhiGles2InitParams::newFallbackSurface()
{
QSurfaceFormat fmt = qrhigles2_effectiveFormat();
// To resolve all fields in the format as much as possible, create a context.
// This may be heavy, but allows avoiding BAD_MATCH on some systems.
QOpenGLContext tempContext;
tempContext.setFormat(fmt);
if (tempContext.create())
fmt = tempContext.format();
else
qWarning("QRhiGles2: Failed to create temporary context");
QOffscreenSurface *s = new QOffscreenSurface;
s->setFormat(fmt);
s->create();
return s;
}
QRhiGles2::QRhiGles2(QRhiGles2InitParams *params, QRhiGles2NativeHandles *importDevice)
: ofr(this)
{
ctx = params->context;
maybeWindow = params->window; // may be null
fallbackSurface = params->fallbackSurface;
maybeWindow = params->window; // may be null
importedContext = importDevice != nullptr;
if (importedContext) {
ctx = importDevice->context;
if (!ctx) {
qWarning("No OpenGL context given, cannot import");
importedContext = false;
}
}
}
bool QRhiGles2::ensureContext(QSurface *surface) const
......@@ -157,9 +230,19 @@ bool QRhiGles2::ensureContext(QSurface *surface) const
bool QRhiGles2::create(QRhi::Flags flags)
{
Q_UNUSED(flags);
Q_ASSERT(ctx);
Q_ASSERT(fallbackSurface);
if (!importedContext) {
ctx = new QOpenGLContext;
ctx->setFormat(qrhigles2_effectiveFormat());
if (!ctx->create()) {
qWarning("QRhiGles2: Failed to create context");
delete ctx;
ctx = nullptr;
return false;
}
}
if (!ensureContext(maybeWindow ? maybeWindow : fallbackSurface)) // see 'window' discussion in QRhiGles2InitParams comments
return false;
......@@ -196,6 +279,11 @@ void QRhiGles2::destroy()
executeDeferredReleases();
f = nullptr;
if (!importedContext) {
delete ctx;
ctx = nullptr;
}
}
// Strictly speaking this is not necessary since we could do the deletes in
......
......@@ -42,19 +42,20 @@
QT_BEGIN_NAMESPACE
class QOpenGLContext;
class QWindow;
class QOffscreenSurface;
class QWindow;
struct Q_RHI_EXPORT QRhiGles2InitParams : public QRhiInitParams
{
QOpenGLContext *context = nullptr;
QOffscreenSurface *fallbackSurface = nullptr;
QWindow *window = nullptr;
static QOffscreenSurface *newFallbackSurface();
};
struct Q_RHI_EXPORT QRhiGles2NativeHandles : public QRhiNativeHandles
{
QOpenGLContext *context;
QOpenGLContext *context = nullptr;
};
struct Q_RHI_EXPORT QRhiGles2TextureNativeHandles : public QRhiNativeHandles
......
......@@ -440,7 +440,7 @@ struct QGles2SwapChain : public QRhiSwapChain
class QRhiGles2 : public QRhiImplementation
{
public:
QRhiGles2(QRhiGles2InitParams *params);
QRhiGles2(QRhiGles2InitParams *params, QRhiGles2NativeHandles *importDevice = nullptr);
bool create(QRhi::Flags flags) override;
void destroy() override;
......@@ -523,6 +523,7 @@ public:
void setChangedUniforms(QGles2GraphicsPipeline *psD, QRhiShaderResourceBindings *srb, bool changedOnly);
QOpenGLContext *ctx = nullptr;
bool importedContext = false;
QWindow *maybeWindow = nullptr;
QSurface *fallbackSurface = nullptr;
mutable bool buffersSwapped = false;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment