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
51QT_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*/
94class Q_GUI_EXPORT QOffscreenSurfacePrivate : public QObjectPrivate
95{
96 Q_DECLARE_PUBLIC(QOffscreenSurface)
97
98public:
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
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*/
134QOffscreenSurface::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(sender: d->screen, SIGNAL(destroyed(QObject*)), receiver: 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*/
157QOffscreenSurface::QOffscreenSurface(QScreen *targetScreen)
158 : QOffscreenSurface(targetScreen, nullptr)
159{
160}
161
162
163/*!
164 Destroys the offscreen surface.
165*/
166QOffscreenSurface::~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*/
176QOffscreenSurface::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*/
194void QOffscreenSurface::create()
195{
196 Q_D(QOffscreenSurface);
197 if (!d->platformOffscreenSurface && !d->offscreenWindow) {
198 d->platformOffscreenSurface = QGuiApplicationPrivate::platformIntegration()->createPlatformOffscreenSurface(surface: this);
199 // No platform offscreen surface, fallback to an invisible window
200 if (!d->platformOffscreenSurface) {
201 if (QThread::currentThread() != qGuiApp->thread())
202 qWarning(msg: "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(t: 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(window: d->offscreenWindow)->setAutomaticPositionAndResizeEnabled(false);
216 d->offscreenWindow->setGeometry(posx: 0, posy: 0, w: d->size.width(), h: d->size.height());
217 d->offscreenWindow->create();
218 }
219
220 QPlatformSurfaceEvent e(QPlatformSurfaceEvent::SurfaceCreated);
221 QGuiApplication::sendEvent(receiver: this, event: &e);
222 }
223}
224
225/*!
226 Releases the native platform resources associated with this offscreen surface.
227
228 \sa create()
229*/
230void QOffscreenSurface::destroy()
231{
232 Q_D(QOffscreenSurface);
233
234 QPlatformSurfaceEvent e(QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed);
235 QGuiApplication::sendEvent(receiver: this, event: &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*/
255bool 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*/
270void 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 */
286QSurfaceFormat 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*/
301QSurfaceFormat 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*/
314QSize 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*/
325QScreen *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*/
338void 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(sender: d->screen, SIGNAL(destroyed(QObject*)), receiver: this, SLOT(screenDestroyed(QObject*)));
349 d->screen = newScreen;
350 if (newScreen) {
351 connect(sender: d->screen, SIGNAL(destroyed(QObject*)), receiver: this, SLOT(screenDestroyed(QObject*)));
352 if (wasCreated)
353 create();
354 }
355 emit screenChanged(screen: 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
373void 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*/
384void 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*/
404QPlatformOffscreenSurface *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
417void *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*/
428QPlatformSurface *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
437QT_END_NAMESPACE
438

source code of qtbase/src/gui/kernel/qoffscreensurface.cpp