1/****************************************************************************
2**
3** Copyright (C) 2020 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 "qoffscreensurface_p.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*/
94
95/*!
96 \since 5.10
97
98 Creates an offscreen surface for the \a targetScreen with the given \a parent.
99
100 The underlying platform surface is not created until create() is called.
101
102 \sa setScreen(), create()
103*/
104QOffscreenSurface::QOffscreenSurface(QScreen *targetScreen, QObject *parent)
105 : QObject(*new QOffscreenSurfacePrivate(), parent)
106 , QSurface(Offscreen)
107{
108 Q_D(QOffscreenSurface);
109 d->screen = targetScreen;
110 if (!d->screen)
111 d->screen = QGuiApplication::primaryScreen();
112
113 //if your applications aborts here, then chances are your creating a QOffscreenSurface before
114 //the screen list is populated.
115 Q_ASSERT(d->screen);
116
117 connect(d->screen, SIGNAL(destroyed(QObject*)), this, SLOT(screenDestroyed(QObject*)));
118}
119
120/*!
121 Destroys the offscreen surface.
122*/
123QOffscreenSurface::~QOffscreenSurface()
124{
125 destroy();
126}
127
128/*!
129 Returns the surface type of the offscreen surface.
130
131 The surface type of an offscreen surface is always QSurface::OpenGLSurface.
132*/
133QOffscreenSurface::SurfaceType QOffscreenSurface::surfaceType() const
134{
135 Q_D(const QOffscreenSurface);
136 return d->surfaceType;
137}
138
139/*!
140 Allocates the platform resources associated with the offscreen surface.
141
142 It is at this point that the surface format set using setFormat() gets resolved
143 into an actual native surface.
144
145 Call destroy() to free the platform resources if necessary.
146
147 \note Some platforms require this function to be called on the main (GUI) thread.
148
149 \sa destroy()
150*/
151void QOffscreenSurface::create()
152{
153 Q_D(QOffscreenSurface);
154 if (!d->platformOffscreenSurface && !d->offscreenWindow) {
155 d->platformOffscreenSurface = QGuiApplicationPrivate::platformIntegration()->createPlatformOffscreenSurface(this);
156 // No platform offscreen surface, fallback to an invisible window
157 if (!d->platformOffscreenSurface) {
158 if (QThread::currentThread() != qGuiApp->thread())
159 qWarning("Attempting to create QWindow-based QOffscreenSurface outside the gui thread. Expect failures.");
160 d->offscreenWindow = new QWindow(d->screen);
161 // Make the window frameless to prevent Windows from enlarging it, should it
162 // violate the minimum title bar width on the platform.
163 d->offscreenWindow->setFlags(d->offscreenWindow->flags()
164 | Qt::CustomizeWindowHint | Qt::FramelessWindowHint);
165 d->offscreenWindow->setObjectName(QLatin1String("QOffscreenSurface"));
166 // Remove this window from the global list since we do not want it to be destroyed when closing the app.
167 // The QOffscreenSurface has to be usable even after exiting the event loop.
168 QGuiApplicationPrivate::window_list.removeOne(d->offscreenWindow);
169 d->offscreenWindow->setSurfaceType(QWindow::OpenGLSurface);
170 d->offscreenWindow->setFormat(d->requestedFormat);
171 // Prevent QPlatformWindow::initialGeometry() and platforms from setting a default geometry.
172 qt_window_private(d->offscreenWindow)->setAutomaticPositionAndResizeEnabled(false);
173 d->offscreenWindow->setGeometry(0, 0, d->size.width(), d->size.height());
174 d->offscreenWindow->create();
175 }
176
177 QPlatformSurfaceEvent e(QPlatformSurfaceEvent::SurfaceCreated);
178 QGuiApplication::sendEvent(this, &e);
179 }
180}
181
182/*!
183 Releases the native platform resources associated with this offscreen surface.
184
185 \sa create()
186*/
187void QOffscreenSurface::destroy()
188{
189 Q_D(QOffscreenSurface);
190
191 QPlatformSurfaceEvent e(QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed);
192 QGuiApplication::sendEvent(this, &e);
193
194 delete d->platformOffscreenSurface;
195 d->platformOffscreenSurface = nullptr;
196 if (d->offscreenWindow) {
197 d->offscreenWindow->destroy();
198 delete d->offscreenWindow;
199 d->offscreenWindow = nullptr;
200 }
201}
202
203/*!
204 Returns \c true if this offscreen surface is valid; otherwise returns \c false.
205
206 The offscreen surface is valid if the platform resources have been successfuly allocated.
207
208 \sa create()
209*/
210bool QOffscreenSurface::isValid() const
211{
212 Q_D(const QOffscreenSurface);
213 return (d->platformOffscreenSurface && d->platformOffscreenSurface->isValid())
214 || (d->offscreenWindow && d->offscreenWindow->handle());
215}
216
217/*!
218 Sets the offscreen surface \a format.
219
220 The surface format will be resolved in the create() function. Calling
221 this function after create() will not re-resolve the surface format of the native surface.
222
223 \sa create(), destroy()
224*/
225void QOffscreenSurface::setFormat(const QSurfaceFormat &format)
226{
227 Q_D(QOffscreenSurface);
228 d->requestedFormat = format;
229}
230
231/*!
232 Returns the requested surfaceformat of this offscreen surface.
233
234 If the requested format was not supported by the platform implementation,
235 the requestedFormat will differ from the actual offscreen surface format.
236
237 This is the value set with setFormat().
238
239 \sa setFormat(), format()
240 */
241QSurfaceFormat QOffscreenSurface::requestedFormat() const
242{
243 Q_D(const QOffscreenSurface);
244 return d->requestedFormat;
245}
246
247/*!
248 Returns the actual format of this offscreen surface.
249
250 After the offscreen surface has been created, this function will return the actual
251 surface format of the surface. It might differ from the requested format if the requested
252 format could not be fulfilled by the platform.
253
254 \sa create(), requestedFormat()
255*/
256QSurfaceFormat QOffscreenSurface::format() const
257{
258 Q_D(const QOffscreenSurface);
259 if (d->platformOffscreenSurface)
260 return d->platformOffscreenSurface->format();
261 if (d->offscreenWindow)
262 return d->offscreenWindow->format();
263 return d->requestedFormat;
264}
265
266/*!
267 Returns the size of the offscreen surface.
268*/
269QSize QOffscreenSurface::size() const
270{
271 Q_D(const QOffscreenSurface);
272 return d->size;
273}
274
275/*!
276 Returns the screen to which the offscreen surface is connected.
277
278 \sa setScreen()
279*/
280QScreen *QOffscreenSurface::screen() const
281{
282 Q_D(const QOffscreenSurface);
283 return d->screen;
284}
285
286/*!
287 Sets the screen to which the offscreen surface is connected.
288
289 If the offscreen surface has been created, it will be recreated on the \a newScreen.
290
291 \sa screen()
292*/
293void QOffscreenSurface::setScreen(QScreen *newScreen)
294{
295 Q_D(QOffscreenSurface);
296 if (!newScreen)
297 newScreen = QCoreApplication::instance() ? QGuiApplication::primaryScreen() : nullptr;
298 if (newScreen != d->screen) {
299 const bool wasCreated = d->platformOffscreenSurface != nullptr || d->offscreenWindow != nullptr;
300 if (wasCreated)
301 destroy();
302 if (d->screen)
303 disconnect(d->screen, SIGNAL(destroyed(QObject*)), this, SLOT(screenDestroyed(QObject*)));
304 d->screen = newScreen;
305 if (newScreen) {
306 connect(d->screen, SIGNAL(destroyed(QObject*)), this, SLOT(screenDestroyed(QObject*)));
307 if (wasCreated)
308 create();
309 }
310 emit screenChanged(newScreen);
311 }
312}
313
314/*!
315 Called when the offscreen surface's screen is destroyed.
316
317 \internal
318*/
319void QOffscreenSurface::screenDestroyed(QObject *object)
320{
321 Q_D(QOffscreenSurface);
322 if (object == static_cast<QObject *>(d->screen))
323 setScreen(nullptr);
324}
325
326/*!
327 \fn QOffscreenSurface::screenChanged(QScreen *screen)
328
329 This signal is emitted when an offscreen surface's \a screen changes, either
330 by being set explicitly with setScreen(), or automatically when
331 the window's screen is removed.
332*/
333
334/*!
335 Returns the platform offscreen surface corresponding to the offscreen surface.
336
337 \internal
338*/
339QPlatformOffscreenSurface *QOffscreenSurface::handle() const
340{
341 Q_D(const QOffscreenSurface);
342 return d->platformOffscreenSurface;
343}
344
345/*!
346 \fn template <typename QNativeInterface> QNativeInterface *QOffscreenSurface::nativeInterface() const
347
348 Returns a native interface of the given type for the surface.
349
350 This function provides access to platform specific functionality
351 of QOffScreenSurface, as defined in the QNativeInterface namespace:
352
353 \annotatedlist native-interfaces-qoffscreensurface
354
355 If the requested interface is not available a \nullptr is returned.
356*/
357
358/*!
359 Returns the platform surface corresponding to the offscreen surface.
360
361 \internal
362*/
363QPlatformSurface *QOffscreenSurface::surfaceHandle() const
364{
365 Q_D(const QOffscreenSurface);
366 if (d->offscreenWindow)
367 return d->offscreenWindow->handle();
368
369 return d->platformOffscreenSurface;
370}
371
372QT_END_NAMESPACE
373