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 | |
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 | |
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 | */ |
104 | QOffscreenSurface::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 | */ |
123 | QOffscreenSurface::~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 | */ |
133 | QOffscreenSurface::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 | */ |
151 | void 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 | */ |
187 | void 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 | */ |
210 | bool 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 | */ |
225 | void 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 | */ |
241 | QSurfaceFormat 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 | */ |
256 | QSurfaceFormat 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 | */ |
269 | QSize 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 | */ |
280 | QScreen *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 | */ |
293 | void 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 | */ |
319 | void 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 | */ |
339 | QPlatformOffscreenSurface *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 | */ |
363 | QPlatformSurface *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 | |
372 | QT_END_NAMESPACE |
373 | |