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: |
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 | */ |
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(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 | */ |
157 | QOffscreenSurface::QOffscreenSurface(QScreen *targetScreen) |
158 | : QOffscreenSurface(targetScreen, nullptr) |
159 | { |
160 | } |
161 | |
162 | |
163 | /*! |
164 | Destroys the offscreen surface. |
165 | */ |
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 | */ |
176 | 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 | */ |
194 | void 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 | */ |
230 | void 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 | */ |
255 | 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 | */ |
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 | */ |
286 | 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 | */ |
301 | 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 | */ |
314 | 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 | */ |
325 | 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 | */ |
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(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 | |
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 | */ |
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 | */ |
404 | 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 | |
417 | 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 | */ |
428 | 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 | |