1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qoffscreensurface.h"
41 
42 #include "qguiapplication_p.h"
43 #include "qscreen.h"
44 #include "qplatformintegration.h"
45 #include "qplatformoffscreensurface.h"
46 #include "qwindow.h"
47 #include "qplatformwindow.h"
48 
49 #include <private/qwindow_p.h>
50 
51 QT_BEGIN_NAMESPACE
52 
53 /*!
54     \class QOffscreenSurface
55     \inmodule QtGui
56     \since 5.1
57     \brief The QOffscreenSurface class represents an offscreen surface in the underlying platform.
58 
59     QOffscreenSurface is intended to be used with QOpenGLContext to allow rendering with OpenGL in
60     an arbitrary thread without the need to create a QWindow.
61 
62     Even though the surface is typically renderable, the surface's pixels are not accessible.
63     QOffscreenSurface should only be used to create OpenGL resources such as textures
64     or framebuffer objects.
65 
66     An application will typically use QOffscreenSurface to perform some time-consuming tasks in a
67     separate thread in order to avoid stalling the main rendering thread. Resources created in the
68     QOffscreenSurface's context can be shared with the main OpenGL context. Some common use cases
69     are asynchronous texture uploads or rendering into a QOpenGLFramebufferObject.
70 
71     How the offscreen surface is implemented depends on the underlying platform, but it will
72     typically use a pixel buffer (pbuffer). If the platform doesn't implement or support
73     offscreen surfaces, QOffscreenSurface will use an invisible QWindow internally.
74 
75     \note Due to the fact that QOffscreenSurface is backed by a QWindow on some platforms,
76     cross-platform applications must ensure that create() is only called on the main (GUI)
77     thread. The QOffscreenSurface is then safe to be used with
78     \l{QOpenGLContext::makeCurrent()}{makeCurrent()} on other threads, but the
79     initialization and destruction must always happen on the main (GUI) thread.
80 
81     \note In order to create an offscreen surface that is guaranteed to be compatible with
82     a given context and window, make sure to set the format to the context's or the
83     window's actual format, that is, the QSurfaceFormat returned from
84     QOpenGLContext::format() or QWindow::format() \e{after the context or window has been
85     created}. Passing the format returned from QWindow::requestedFormat() to setFormat()
86     may result in an incompatible offscreen surface since the underlying windowing system
87     interface may offer a different set of configurations for window and pbuffer surfaces.
88 
89     \note Some platforms may utilize a surfaceless context extension (for example
90     EGL_KHR_surfaceless_context) when available. In this case there will be no underlying
91     native surface. For the use cases of QOffscreenSurface (rendering to FBOs, texture
92     upload) this is not a problem.
93 */
94 class Q_GUI_EXPORT QOffscreenSurfacePrivate : public QObjectPrivate
95 {
96     Q_DECLARE_PUBLIC(QOffscreenSurface)
97 
98 public:
QOffscreenSurfacePrivate()99     QOffscreenSurfacePrivate()
100         : QObjectPrivate()
101         , surfaceType(QSurface::OpenGLSurface)
102         , platformOffscreenSurface(nullptr)
103         , offscreenWindow(nullptr)
104         , requestedFormat(QSurfaceFormat::defaultFormat())
105         , screen(nullptr)
106         , size(1, 1)
107         , nativeHandle(nullptr)
108     {
109     }
110 
~QOffscreenSurfacePrivate()111     ~QOffscreenSurfacePrivate()
112     {
113     }
114 
115     QSurface::SurfaceType surfaceType;
116     QPlatformOffscreenSurface *platformOffscreenSurface;
117     QWindow *offscreenWindow;
118     QSurfaceFormat requestedFormat;
119     QScreen *screen;
120     QSize size;
121     void *nativeHandle;
122 };
123 
124 
125 /*!
126     \since 5.10
127 
128     Creates an offscreen surface for the \a targetScreen with the given \a parent.
129 
130     The underlying platform surface is not created until create() is called.
131 
132     \sa setScreen(), create()
133 */
QOffscreenSurface(QScreen * targetScreen,QObject * parent)134 QOffscreenSurface::QOffscreenSurface(QScreen *targetScreen, QObject *parent)
135     : QObject(*new QOffscreenSurfacePrivate(), parent)
136     , QSurface(Offscreen)
137 {
138     Q_D(QOffscreenSurface);
139     d->screen = targetScreen;
140     if (!d->screen)
141         d->screen = QGuiApplication::primaryScreen();
142 
143     //if your applications aborts here, then chances are your creating a QOffscreenSurface before
144     //the screen list is populated.
145     Q_ASSERT(d->screen);
146 
147     connect(d->screen, SIGNAL(destroyed(QObject*)), this, SLOT(screenDestroyed(QObject*)));
148 }
149 
150 /*!
151     Creates an offscreen surface for the \a targetScreen.
152 
153     The underlying platform surface is not created until create() is called.
154 
155     \sa setScreen(), create()
156 */
QOffscreenSurface(QScreen * targetScreen)157 QOffscreenSurface::QOffscreenSurface(QScreen *targetScreen)
158     : QOffscreenSurface(targetScreen, nullptr)
159 {
160 }
161 
162 
163 /*!
164     Destroys the offscreen surface.
165 */
~QOffscreenSurface()166 QOffscreenSurface::~QOffscreenSurface()
167 {
168     destroy();
169 }
170 
171 /*!
172     Returns the surface type of the offscreen surface.
173 
174     The surface type of an offscreen surface is always QSurface::OpenGLSurface.
175 */
surfaceType() const176 QOffscreenSurface::SurfaceType QOffscreenSurface::surfaceType() const
177 {
178     Q_D(const QOffscreenSurface);
179     return d->surfaceType;
180 }
181 
182 /*!
183     Allocates the platform resources associated with the offscreen surface.
184 
185     It is at this point that the surface format set using setFormat() gets resolved
186     into an actual native surface.
187 
188     Call destroy() to free the platform resources if necessary.
189 
190     \note Some platforms require this function to be called on the main (GUI) thread.
191 
192     \sa destroy()
193 */
create()194 void QOffscreenSurface::create()
195 {
196     Q_D(QOffscreenSurface);
197     if (!d->platformOffscreenSurface && !d->offscreenWindow) {
198         d->platformOffscreenSurface = QGuiApplicationPrivate::platformIntegration()->createPlatformOffscreenSurface(this);
199         // No platform offscreen surface, fallback to an invisible window
200         if (!d->platformOffscreenSurface) {
201             if (QThread::currentThread() != qGuiApp->thread())
202                 qWarning("Attempting to create QWindow-based QOffscreenSurface outside the gui thread. Expect failures.");
203             d->offscreenWindow = new QWindow(d->screen);
204             // Make the window frameless to prevent Windows from enlarging it, should it
205             // violate the minimum title bar width on the platform.
206             d->offscreenWindow->setFlags(d->offscreenWindow->flags()
207                                          | Qt::CustomizeWindowHint | Qt::FramelessWindowHint);
208             d->offscreenWindow->setObjectName(QLatin1String("QOffscreenSurface"));
209             // Remove this window from the global list since we do not want it to be destroyed when closing the app.
210             // The QOffscreenSurface has to be usable even after exiting the event loop.
211             QGuiApplicationPrivate::window_list.removeOne(d->offscreenWindow);
212             d->offscreenWindow->setSurfaceType(QWindow::OpenGLSurface);
213             d->offscreenWindow->setFormat(d->requestedFormat);
214             // Prevent QPlatformWindow::initialGeometry() and platforms from setting a default geometry.
215             qt_window_private(d->offscreenWindow)->setAutomaticPositionAndResizeEnabled(false);
216             d->offscreenWindow->setGeometry(0, 0, d->size.width(), d->size.height());
217             d->offscreenWindow->create();
218         }
219 
220         QPlatformSurfaceEvent e(QPlatformSurfaceEvent::SurfaceCreated);
221         QGuiApplication::sendEvent(this, &e);
222     }
223 }
224 
225 /*!
226     Releases the native platform resources associated with this offscreen surface.
227 
228     \sa create()
229 */
destroy()230 void QOffscreenSurface::destroy()
231 {
232     Q_D(QOffscreenSurface);
233 
234     QPlatformSurfaceEvent e(QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed);
235     QGuiApplication::sendEvent(this, &e);
236 
237     delete d->platformOffscreenSurface;
238     d->platformOffscreenSurface = nullptr;
239     if (d->offscreenWindow) {
240         d->offscreenWindow->destroy();
241         delete d->offscreenWindow;
242         d->offscreenWindow = nullptr;
243     }
244 
245     d->nativeHandle = nullptr;
246 }
247 
248 /*!
249     Returns \c true if this offscreen surface is valid; otherwise returns \c false.
250 
251     The offscreen surface is valid if the platform resources have been successfuly allocated.
252 
253     \sa create()
254 */
isValid() const255 bool QOffscreenSurface::isValid() const
256 {
257     Q_D(const QOffscreenSurface);
258     return (d->platformOffscreenSurface && d->platformOffscreenSurface->isValid())
259             || (d->offscreenWindow && d->offscreenWindow->handle());
260 }
261 
262 /*!
263     Sets the offscreen surface \a format.
264 
265     The surface format will be resolved in the create() function. Calling
266     this function after create() will not re-resolve the surface format of the native surface.
267 
268     \sa create(), destroy()
269 */
setFormat(const QSurfaceFormat & format)270 void QOffscreenSurface::setFormat(const QSurfaceFormat &format)
271 {
272     Q_D(QOffscreenSurface);
273     d->requestedFormat = format;
274 }
275 
276 /*!
277     Returns the requested surfaceformat of this offscreen surface.
278 
279     If the requested format was not supported by the platform implementation,
280     the requestedFormat will differ from the actual offscreen surface format.
281 
282     This is the value set with setFormat().
283 
284     \sa setFormat(), format()
285  */
requestedFormat() const286 QSurfaceFormat QOffscreenSurface::requestedFormat() const
287 {
288     Q_D(const QOffscreenSurface);
289     return d->requestedFormat;
290 }
291 
292 /*!
293     Returns the actual format of this offscreen surface.
294 
295     After the offscreen surface has been created, this function will return the actual
296     surface format of the surface. It might differ from the requested format if the requested
297     format could not be fulfilled by the platform.
298 
299     \sa create(), requestedFormat()
300 */
format() const301 QSurfaceFormat QOffscreenSurface::format() const
302 {
303     Q_D(const QOffscreenSurface);
304     if (d->platformOffscreenSurface)
305         return d->platformOffscreenSurface->format();
306     if (d->offscreenWindow)
307         return d->offscreenWindow->format();
308     return d->requestedFormat;
309 }
310 
311 /*!
312     Returns the size of the offscreen surface.
313 */
size() const314 QSize QOffscreenSurface::size() const
315 {
316     Q_D(const QOffscreenSurface);
317     return d->size;
318 }
319 
320 /*!
321     Returns the screen to which the offscreen surface is connected.
322 
323     \sa setScreen()
324 */
screen() const325 QScreen *QOffscreenSurface::screen() const
326 {
327     Q_D(const QOffscreenSurface);
328     return d->screen;
329 }
330 
331 /*!
332     Sets the screen to which the offscreen surface is connected.
333 
334     If the offscreen surface has been created, it will be recreated on the \a newScreen.
335 
336     \sa screen()
337 */
setScreen(QScreen * newScreen)338 void QOffscreenSurface::setScreen(QScreen *newScreen)
339 {
340     Q_D(QOffscreenSurface);
341     if (!newScreen)
342         newScreen = QCoreApplication::instance() ? QGuiApplication::primaryScreen() : nullptr;
343     if (newScreen != d->screen) {
344         const bool wasCreated = d->platformOffscreenSurface != nullptr || d->offscreenWindow != nullptr;
345         if (wasCreated)
346             destroy();
347         if (d->screen)
348             disconnect(d->screen, SIGNAL(destroyed(QObject*)), this, SLOT(screenDestroyed(QObject*)));
349         d->screen = newScreen;
350         if (newScreen) {
351             connect(d->screen, SIGNAL(destroyed(QObject*)), this, SLOT(screenDestroyed(QObject*)));
352             if (wasCreated)
353                 create();
354         }
355         emit screenChanged(newScreen);
356     }
357 }
358 
359 /*!
360     Sets the native handle to which the offscreen surface is connected to \a handle.
361 
362     The native handle will be resolved in the create() function. Calling
363     this function after create() will not re-create a native surface.
364 
365     \note The interpretation of the native handle is platform specific.  Only
366     some platforms will support adopting native handles of offscreen surfaces
367     and platforms that do not implement this support will ignore the handle.
368 
369     \since 5.9
370     \sa nativeHandle()
371 */
372 
setNativeHandle(void * handle)373 void QOffscreenSurface::setNativeHandle(void *handle)
374 {
375     Q_D(QOffscreenSurface);
376     d->nativeHandle = handle;
377 }
378 
379 /*!
380     Called when the offscreen surface's screen is destroyed.
381 
382     \internal
383 */
screenDestroyed(QObject * object)384 void QOffscreenSurface::screenDestroyed(QObject *object)
385 {
386     Q_D(QOffscreenSurface);
387     if (object == static_cast<QObject *>(d->screen))
388         setScreen(nullptr);
389 }
390 
391 /*!
392     \fn QOffscreenSurface::screenChanged(QScreen *screen)
393 
394     This signal is emitted when an offscreen surface's \a screen changes, either
395     by being set explicitly with setScreen(), or automatically when
396     the window's screen is removed.
397 */
398 
399 /*!
400     Returns the platform offscreen surface corresponding to the offscreen surface.
401 
402     \internal
403 */
handle() const404 QPlatformOffscreenSurface *QOffscreenSurface::handle() const
405 {
406     Q_D(const QOffscreenSurface);
407     return d->platformOffscreenSurface;
408 }
409 
410 /*!
411     Returns an optional native handle to which the offscreen surface is connected.
412 
413     \since 5.9
414     \sa setNativeHandle()
415 */
416 
nativeHandle() const417 void *QOffscreenSurface::nativeHandle() const
418 {
419     Q_D(const QOffscreenSurface);
420     return d->nativeHandle;
421 }
422 
423 /*!
424     Returns the platform surface corresponding to the offscreen surface.
425 
426     \internal
427 */
surfaceHandle() const428 QPlatformSurface *QOffscreenSurface::surfaceHandle() const
429 {
430     Q_D(const QOffscreenSurface);
431     if (d->offscreenWindow)
432         return d->offscreenWindow->handle();
433 
434     return d->platformOffscreenSurface;
435 }
436 
437 QT_END_NAMESPACE
438