1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qoffscreensurface.h"
5
6#include "qguiapplication_p.h"
7#include "qscreen.h"
8#include "qplatformintegration.h"
9#include "qoffscreensurface_p.h"
10#include "qwindow.h"
11#include "qplatformwindow.h"
12
13#include <private/qwindow_p.h>
14
15QT_BEGIN_NAMESPACE
16
17using namespace Qt::StringLiterals;
18
19/*!
20 \class QOffscreenSurface
21 \inmodule QtGui
22 \since 5.1
23 \brief The QOffscreenSurface class represents an offscreen surface in the underlying platform.
24
25 QOffscreenSurface is intended to be used with QOpenGLContext to allow rendering with OpenGL in
26 an arbitrary thread without the need to create a QWindow.
27
28 Even though the surface is typically renderable, the surface's pixels are not accessible.
29 QOffscreenSurface should only be used to create OpenGL resources such as textures
30 or framebuffer objects.
31
32 An application will typically use QOffscreenSurface to perform some time-consuming tasks in a
33 separate thread in order to avoid stalling the main rendering thread. Resources created in the
34 QOffscreenSurface's context can be shared with the main OpenGL context. Some common use cases
35 are asynchronous texture uploads or rendering into a QOpenGLFramebufferObject.
36
37 How the offscreen surface is implemented depends on the underlying platform, but it will
38 typically use a pixel buffer (pbuffer). If the platform doesn't implement or support
39 offscreen surfaces, QOffscreenSurface will use an invisible QWindow internally.
40
41 \note Due to the fact that QOffscreenSurface is backed by a QWindow on some platforms,
42 cross-platform applications must ensure that create() is only called on the main (GUI)
43 thread. The QOffscreenSurface is then safe to be used with
44 \l{QOpenGLContext::makeCurrent()}{makeCurrent()} on other threads, but the
45 initialization and destruction must always happen on the main (GUI) thread.
46
47 \note In order to create an offscreen surface that is guaranteed to be compatible with
48 a given context and window, make sure to set the format to the context's or the
49 window's actual format, that is, the QSurfaceFormat returned from
50 QOpenGLContext::format() or QWindow::format() \e{after the context or window has been
51 created}. Passing the format returned from QWindow::requestedFormat() to setFormat()
52 may result in an incompatible offscreen surface since the underlying windowing system
53 interface may offer a different set of configurations for window and pbuffer surfaces.
54
55 \note Some platforms may utilize a surfaceless context extension (for example
56 EGL_KHR_surfaceless_context) when available. In this case there will be no underlying
57 native surface. For the use cases of QOffscreenSurface (rendering to FBOs, texture
58 upload) this is not a problem.
59*/
60
61/*!
62 \since 5.10
63
64 Creates an offscreen surface for the \a targetScreen with the given \a parent.
65
66 The underlying platform surface is not created until create() is called.
67
68 \sa setScreen(), create()
69*/
70QOffscreenSurface::QOffscreenSurface(QScreen *targetScreen, QObject *parent)
71 : QObject(*new QOffscreenSurfacePrivate(), parent)
72 , QSurface(Offscreen)
73{
74 Q_D(QOffscreenSurface);
75 d->screen = targetScreen;
76 if (!d->screen)
77 d->screen = QGuiApplication::primaryScreen();
78
79 //if your applications aborts here, then chances are your creating a QOffscreenSurface before
80 //the screen list is populated.
81 Q_ASSERT(d->screen);
82
83 connect(sender: d->screen, SIGNAL(destroyed(QObject*)), receiver: this, SLOT(screenDestroyed(QObject*)));
84}
85
86/*!
87 Destroys the offscreen surface.
88*/
89QOffscreenSurface::~QOffscreenSurface()
90{
91 destroy();
92}
93
94/*!
95 Returns the surface type of the offscreen surface.
96
97 The surface type of an offscreen surface is always QSurface::OpenGLSurface.
98*/
99QOffscreenSurface::SurfaceType QOffscreenSurface::surfaceType() const
100{
101 Q_D(const QOffscreenSurface);
102 return d->surfaceType;
103}
104
105/*!
106 Allocates the platform resources associated with the offscreen surface.
107
108 It is at this point that the surface format set using setFormat() gets resolved
109 into an actual native surface.
110
111 Call destroy() to free the platform resources if necessary.
112
113 \note Some platforms require this function to be called on the main (GUI) thread.
114
115 \sa destroy()
116*/
117void QOffscreenSurface::create()
118{
119 Q_D(QOffscreenSurface);
120 if (!d->platformOffscreenSurface && !d->offscreenWindow) {
121 d->platformOffscreenSurface = QGuiApplicationPrivate::platformIntegration()->createPlatformOffscreenSurface(surface: this);
122 // No platform offscreen surface, fallback to an invisible window
123 if (!d->platformOffscreenSurface) {
124 if (QThread::currentThread() != qGuiApp->thread())
125 qWarning(msg: "Attempting to create QWindow-based QOffscreenSurface outside the gui thread. Expect failures.");
126 d->offscreenWindow = new QWindow(d->screen);
127 // Make the window frameless to prevent Windows from enlarging it, should it
128 // violate the minimum title bar width on the platform.
129 d->offscreenWindow->setFlags(d->offscreenWindow->flags()
130 | Qt::CustomizeWindowHint | Qt::FramelessWindowHint);
131 d->offscreenWindow->setObjectName("QOffscreenSurface"_L1);
132 // Remove this window from the global list since we do not want it to be destroyed when closing the app.
133 // The QOffscreenSurface has to be usable even after exiting the event loop.
134 QGuiApplicationPrivate::window_list.removeOne(t: d->offscreenWindow);
135 d->offscreenWindow->setSurfaceType(QWindow::OpenGLSurface);
136 d->offscreenWindow->setFormat(d->requestedFormat);
137 // Prevent QPlatformWindow::initialGeometry() and platforms from setting a default geometry.
138 qt_window_private(window: d->offscreenWindow)->setAutomaticPositionAndResizeEnabled(false);
139 d->offscreenWindow->setGeometry(posx: 0, posy: 0, w: d->size.width(), h: d->size.height());
140 d->offscreenWindow->create();
141 }
142
143 QPlatformSurfaceEvent e(QPlatformSurfaceEvent::SurfaceCreated);
144 QGuiApplication::sendEvent(receiver: this, event: &e);
145 }
146}
147
148/*!
149 Releases the native platform resources associated with this offscreen surface.
150
151 \sa create()
152*/
153void QOffscreenSurface::destroy()
154{
155 Q_D(QOffscreenSurface);
156
157 QPlatformSurfaceEvent e(QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed);
158 QGuiApplication::sendEvent(receiver: this, event: &e);
159
160 delete d->platformOffscreenSurface;
161 d->platformOffscreenSurface = nullptr;
162 if (d->offscreenWindow) {
163 d->offscreenWindow->destroy();
164 delete d->offscreenWindow;
165 d->offscreenWindow = nullptr;
166 }
167}
168
169/*!
170 Returns \c true if this offscreen surface is valid; otherwise returns \c false.
171
172 The offscreen surface is valid if the platform resources have been successfully allocated.
173
174 \sa create()
175*/
176bool QOffscreenSurface::isValid() const
177{
178 Q_D(const QOffscreenSurface);
179 return (d->platformOffscreenSurface && d->platformOffscreenSurface->isValid())
180 || (d->offscreenWindow && d->offscreenWindow->handle());
181}
182
183/*!
184 Sets the offscreen surface \a format.
185
186 The surface format will be resolved in the create() function. Calling
187 this function after create() will not re-resolve the surface format of the native surface.
188
189 \sa create(), destroy()
190*/
191void QOffscreenSurface::setFormat(const QSurfaceFormat &format)
192{
193 Q_D(QOffscreenSurface);
194 d->requestedFormat = format;
195}
196
197/*!
198 Returns the requested surfaceformat of this offscreen surface.
199
200 If the requested format was not supported by the platform implementation,
201 the requestedFormat will differ from the actual offscreen surface format.
202
203 This is the value set with setFormat().
204
205 \sa setFormat(), format()
206 */
207QSurfaceFormat QOffscreenSurface::requestedFormat() const
208{
209 Q_D(const QOffscreenSurface);
210 return d->requestedFormat;
211}
212
213/*!
214 Returns the actual format of this offscreen surface.
215
216 After the offscreen surface has been created, this function will return the actual
217 surface format of the surface. It might differ from the requested format if the requested
218 format could not be fulfilled by the platform.
219
220 \sa create(), requestedFormat()
221*/
222QSurfaceFormat QOffscreenSurface::format() const
223{
224 Q_D(const QOffscreenSurface);
225 if (d->platformOffscreenSurface)
226 return d->platformOffscreenSurface->format();
227 if (d->offscreenWindow)
228 return d->offscreenWindow->format();
229 return d->requestedFormat;
230}
231
232/*!
233 Returns the size of the offscreen surface.
234*/
235QSize QOffscreenSurface::size() const
236{
237 Q_D(const QOffscreenSurface);
238 return d->size;
239}
240
241/*!
242 Returns the screen to which the offscreen surface is connected.
243
244 \sa setScreen()
245*/
246QScreen *QOffscreenSurface::screen() const
247{
248 Q_D(const QOffscreenSurface);
249 return d->screen;
250}
251
252/*!
253 Sets the screen to which the offscreen surface is connected.
254
255 If the offscreen surface has been created, it will be recreated on the \a newScreen.
256
257 \sa screen()
258*/
259void QOffscreenSurface::setScreen(QScreen *newScreen)
260{
261 Q_D(QOffscreenSurface);
262 if (!newScreen)
263 newScreen = QCoreApplication::instance() ? QGuiApplication::primaryScreen() : nullptr;
264 if (newScreen != d->screen) {
265 const bool wasCreated = d->platformOffscreenSurface != nullptr || d->offscreenWindow != nullptr;
266 if (wasCreated)
267 destroy();
268 if (d->screen)
269 disconnect(sender: d->screen, SIGNAL(destroyed(QObject*)), receiver: this, SLOT(screenDestroyed(QObject*)));
270 d->screen = newScreen;
271 if (newScreen) {
272 connect(sender: d->screen, SIGNAL(destroyed(QObject*)), receiver: this, SLOT(screenDestroyed(QObject*)));
273 if (wasCreated)
274 create();
275 }
276 emit screenChanged(screen: newScreen);
277 }
278}
279
280/*!
281 Called when the offscreen surface's screen is destroyed.
282
283 \internal
284*/
285void QOffscreenSurface::screenDestroyed(QObject *object)
286{
287 Q_D(QOffscreenSurface);
288 if (object == static_cast<QObject *>(d->screen))
289 setScreen(nullptr);
290}
291
292/*!
293 \fn QOffscreenSurface::screenChanged(QScreen *screen)
294
295 This signal is emitted when an offscreen surface's \a screen changes, either
296 by being set explicitly with setScreen(), or automatically when
297 the window's screen is removed.
298*/
299
300/*!
301 Returns the platform offscreen surface corresponding to the offscreen surface.
302
303 \internal
304*/
305QPlatformOffscreenSurface *QOffscreenSurface::handle() const
306{
307 Q_D(const QOffscreenSurface);
308 return d->platformOffscreenSurface;
309}
310
311/*!
312 \fn template <typename QNativeInterface> QNativeInterface *QOffscreenSurface::nativeInterface() const
313
314 Returns a native interface of the given type for the surface.
315
316 This function provides access to platform specific functionality
317 of QOffScreenSurface, as defined in the QNativeInterface namespace:
318
319 \annotatedlist native-interfaces-qoffscreensurface
320
321 If the requested interface is not available a \nullptr is returned.
322*/
323
324/*!
325 Returns the platform surface corresponding to the offscreen surface.
326
327 \internal
328*/
329QPlatformSurface *QOffscreenSurface::surfaceHandle() const
330{
331 Q_D(const QOffscreenSurface);
332 if (d->offscreenWindow)
333 return d->offscreenWindow->handle();
334
335 return d->platformOffscreenSurface;
336}
337
338using namespace QNativeInterface;
339
340void *QOffscreenSurface::resolveInterface(const char *name, int revision) const
341{
342 Q_UNUSED(name); Q_UNUSED(revision);
343
344 Q_D(const QOffscreenSurface);
345 Q_UNUSED(d);
346
347#if defined(Q_OS_ANDROID)
348 QT_NATIVE_INTERFACE_RETURN_IF(QAndroidOffscreenSurface, d->platformOffscreenSurface);
349#endif
350
351 return nullptr;
352}
353
354QT_END_NAMESPACE
355
356#include "moc_qoffscreensurface.cpp"
357

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