summaryrefslogtreecommitdiffstats
path: root/src/gui/kernel/qoffscreensurface.cpp
blob: ed1b8d944aa3e291c54c04c8927523c85dddf335 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtGui module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL21$
** 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 Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qoffscreensurface.h"

#include "qguiapplication_p.h"
#include "qscreen.h"
#include "qplatformintegration.h"
#include "qplatformoffscreensurface.h"
#include "qwindow.h"
#include "qplatformwindow.h"

QT_BEGIN_NAMESPACE

/*!
    \class QOffscreenSurface
    \inmodule QtGui
    \since 5.1
    \brief The QOffscreenSurface class represents an offscreen surface in the underlying platform.

    QOffscreenSurface is intended to be used with QOpenGLContext to allow rendering with OpenGL in
    an arbitrary thread without the need to create a QWindow.

    Even though the surface is typically renderable, the surface's pixels are not accessible.
    QOffscreenSurface should only be used to create OpenGL resources such as textures
    or framebuffer objects.

    An application will typically use QOffscreenSurface to perform some time-consuming tasks in a
    separate thread in order to avoid stalling the main rendering thread. Resources created in the
    QOffscreenSurface's context can be shared with the main OpenGL context. Some common use cases
    are asynchronous texture uploads or rendering into a QOpenGLFramebufferObject.

    How the offscreen surface is implemented depends on the underlying platform, but it will
    typically use a pixel buffer (pbuffer). If the platform doesn't implement or support
    offscreen surfaces, QOffscreenSurface will use an invisible QWindow internally.

    \note Due to the fact that QOffscreenSurface is backed by a QWindow on some platforms,
    cross-platform applications must ensure that create() is only called on the main (GUI)
    thread. The QOffscreenSurface is then safe to be used with
    \l{QOpenGLContext::makeCurrent()}{makeCurrent()} on other threads, but the
    initialization and destruction must always happen on the main (GUI) thread.

    \note In order to create an offscreen surface that is guaranteed to be compatible with
    a given context and window, make sure to set the format to the context's or the
    window's actual format, that is, the QSurfaceFormat returned from
    QOpenGLContext::format() or QWindow::format() \e{after the context or window has been
    created}. Passing the format returned from QWindow::requestedFormat() to setFormat()
    may result in an incompatible offscreen surface since the underlying windowing system
    interface may offer a different set of configurations for window and pbuffer surfaces.

    \note Some platforms may utilize a surfaceless context extension (for example
    EGL_KHR_surfaceless_context) when available. In this case there will be no underlying
    native surface. For the use cases of QOffscreenSurface (rendering to FBOs, texture
    upload) this is not a problem.
*/
class Q_GUI_EXPORT QOffscreenSurfacePrivate : public QObjectPrivate
{
    Q_DECLARE_PUBLIC(QOffscreenSurface)

public:
    QOffscreenSurfacePrivate()
        : QObjectPrivate()
        , surfaceType(QSurface::OpenGLSurface)
        , platformOffscreenSurface(0)
        , offscreenWindow(0)
        , requestedFormat(QSurfaceFormat::defaultFormat())
        , screen(0)
        , size(1, 1)
    {
    }

    ~QOffscreenSurfacePrivate()
    {
    }

    QSurface::SurfaceType surfaceType;
    QPlatformOffscreenSurface *platformOffscreenSurface;
    QWindow *offscreenWindow;
    QSurfaceFormat requestedFormat;
    QScreen *screen;
    QSize size;
};


/*!
    Creates an offscreen surface for the \a targetScreen.

    The underlying platform surface is not created until create() is called.

    \sa setScreen(), create()
*/
QOffscreenSurface::QOffscreenSurface(QScreen *targetScreen)
    : QObject(*new QOffscreenSurfacePrivate(), 0)
    , QSurface(Offscreen)
{
    Q_D(QOffscreenSurface);
    d->screen = targetScreen;
    if (!d->screen)
        d->screen = QGuiApplication::primaryScreen();

    //if your applications aborts here, then chances are your creating a QOffscreenSurface before
    //the screen list is populated.
    Q_ASSERT(d->screen);

    connect(d->screen, SIGNAL(destroyed(QObject*)), this, SLOT(screenDestroyed(QObject*)));
}


/*!
    Destroys the offscreen surface.
*/
QOffscreenSurface::~QOffscreenSurface()
{
    destroy();
}

/*!
    Returns the surface type of the offscreen surface.

    The surface type of an offscreen surface is always QSurface::OpenGLSurface.
*/
QOffscreenSurface::SurfaceType QOffscreenSurface::surfaceType() const
{
    Q_D(const QOffscreenSurface);
    return d->surfaceType;
}

/*!
    Allocates the platform resources associated with the offscreen surface.

    It is at this point that the surface format set using setFormat() gets resolved
    into an actual native surface.

    Call destroy() to free the platform resources if necessary.

    \note Some platforms require this function to be called on the main (GUI) thread.

    \sa destroy()
*/
void QOffscreenSurface::create()
{
    Q_D(QOffscreenSurface);
    if (!d->platformOffscreenSurface && !d->offscreenWindow) {
        d->platformOffscreenSurface = QGuiApplicationPrivate::platformIntegration()->createPlatformOffscreenSurface(this);
        // No platform offscreen surface, fallback to an invisible window
        if (!d->platformOffscreenSurface) {
            if (QThread::currentThread() != qGuiApp->thread())
                qWarning("Attempting to create QWindow-based QOffscreenSurface outside the gui thread. Expect failures.");
            d->offscreenWindow = new QWindow(d->screen);
            // Remove this window from the global list since we do not want it to be destroyed when closing the app.
            // The QOffscreenSurface has to be usable even after exiting the event loop.
            QGuiApplicationPrivate::window_list.removeOne(d->offscreenWindow);
            d->offscreenWindow->setSurfaceType(QWindow::OpenGLSurface);
            d->offscreenWindow->setFormat(d->requestedFormat);
            d->offscreenWindow->setGeometry(0, 0, d->size.width(), d->size.height());
            d->offscreenWindow->create();
        }

        QPlatformSurfaceEvent e(QPlatformSurfaceEvent::SurfaceCreated);
        QGuiApplication::sendEvent(this, &e);
    }
}

/*!
    Releases the native platform resources associated with this offscreen surface.

    \sa create()
*/
void QOffscreenSurface::destroy()
{
    Q_D(QOffscreenSurface);

    QPlatformSurfaceEvent e(QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed);
    QGuiApplication::sendEvent(this, &e);

    delete d->platformOffscreenSurface;
    d->platformOffscreenSurface = 0;
    if (d->offscreenWindow) {
        d->offscreenWindow->destroy();
        delete d->offscreenWindow;
        d->offscreenWindow = 0;
    }
}

/*!
    Returns \c true if this offscreen surface is valid; otherwise returns \c false.

    The offscreen surface is valid if the platform resources have been successfuly allocated.

    \sa create()
*/
bool QOffscreenSurface::isValid() const
{
    Q_D(const QOffscreenSurface);
    return (d->platformOffscreenSurface && d->platformOffscreenSurface->isValid())
            || (d->offscreenWindow && d->offscreenWindow->handle());
}

/*!
    Sets the offscreen surface \a format.

    The surface format will be resolved in the create() function. Calling
    this function after create() will not re-resolve the surface format of the native surface.

    \sa create(), destroy()
*/
void QOffscreenSurface::setFormat(const QSurfaceFormat &format)
{
    Q_D(QOffscreenSurface);
    d->requestedFormat = format;
}

/*!
    Returns the requested surfaceformat of this offscreen surface.

    If the requested format was not supported by the platform implementation,
    the requestedFormat will differ from the actual offscreen surface format.

    This is the value set with setFormat().

    \sa setFormat(), format()
 */
QSurfaceFormat QOffscreenSurface::requestedFormat() const
{
    Q_D(const QOffscreenSurface);
    return d->requestedFormat;
}

/*!
    Returns the actual format of this offscreen surface.

    After the offscreen surface has been created, this function will return the actual
    surface format of the surface. It might differ from the requested format if the requested
    format could not be fulfilled by the platform.

    \sa create(), requestedFormat()
*/
QSurfaceFormat QOffscreenSurface::format() const
{
    Q_D(const QOffscreenSurface);
    if (d->platformOffscreenSurface)
        return d->platformOffscreenSurface->format();
    if (d->offscreenWindow)
        return d->offscreenWindow->format();
    return d->requestedFormat;
}

/*!
    Returns the size of the offscreen surface.
*/
QSize QOffscreenSurface::size() const
{
    Q_D(const QOffscreenSurface);
    return d->size;
}

/*!
    Returns the screen to which the offscreen surface is connected.

    \sa setScreen()
*/
QScreen *QOffscreenSurface::screen() const
{
    Q_D(const QOffscreenSurface);
    return d->screen;
}

/*!
    Sets the screen to which the offscreen surface is connected.

    If the offscreen surface has been created, it will be recreated on the \a newScreen.

    \sa screen()
*/
void QOffscreenSurface::setScreen(QScreen *newScreen)
{
    Q_D(QOffscreenSurface);
    if (!newScreen)
        newScreen = QGuiApplication::primaryScreen();
    if (newScreen != d->screen) {
        const bool wasCreated = d->platformOffscreenSurface != 0 || d->offscreenWindow != 0;
        if (wasCreated)
            destroy();
        if (d->screen)
            disconnect(d->screen, SIGNAL(destroyed(QObject*)), this, SLOT(screenDestroyed(QObject*)));
        d->screen = newScreen;
        if (newScreen) {
            connect(d->screen, SIGNAL(destroyed(QObject*)), this, SLOT(screenDestroyed(QObject*)));
            if (wasCreated)
                create();
        }
        emit screenChanged(newScreen);
    }
}

/*!
    Called when the offscreen surface's screen is destroyed.

    \internal
*/
void QOffscreenSurface::screenDestroyed(QObject *object)
{
    Q_D(QOffscreenSurface);
    if (object == static_cast<QObject *>(d->screen))
        setScreen(0);
}

/*!
    \fn QOffscreenSurface::screenChanged(QScreen *screen)

    This signal is emitted when an offscreen surface's \a screen changes, either
    by being set explicitly with setScreen(), or automatically when
    the window's screen is removed.
*/

/*!
    Returns the platform offscreen surface corresponding to the offscreen surface.

    \internal
*/
QPlatformOffscreenSurface *QOffscreenSurface::handle() const
{
    Q_D(const QOffscreenSurface);
    return d->platformOffscreenSurface;
}

/*!
    Returns the platform surface corresponding to the offscreen surface.

    \internal
*/
QPlatformSurface *QOffscreenSurface::surfaceHandle() const
{
    Q_D(const QOffscreenSurface);
    if (d->offscreenWindow)
        return d->offscreenWindow->handle();

    return d->platformOffscreenSurface;
}

QT_END_NAMESPACE