Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qwaylandglcontext.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 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
5
6#include <QtWaylandClient/private/qwaylanddisplay_p.h>
7#include <QtWaylandClient/private/qwaylandwindow_p.h>
8#include <QtWaylandClient/private/qwaylandsubsurface_p.h>
9#include <QtWaylandClient/private/qwaylandabstractdecoration_p.h>
10#include <QtWaylandClient/private/qwaylandintegration_p.h>
11#include "qwaylandeglwindow_p.h"
12
13#include <QDebug>
14#include <QtGui/private/qeglconvenience_p.h>
15#include <QtGui/private/qopenglcontext_p.h>
16#include <QtOpenGL/private/qopengltexturecache_p.h>
17#include <QtGui/private/qguiapplication_p.h>
18
19#include <qpa/qplatformopenglcontext.h>
20#include <QtGui/QSurfaceFormat>
21#include <QtOpenGL/QOpenGLShaderProgram>
22#include <QtGui/QOpenGLFunctions>
23#include <QOpenGLBuffer>
24
25#include <QtCore/qmutex.h>
26
27#include <dlfcn.h>
28
29// Constants from EGL_KHR_create_context
30#ifndef EGL_CONTEXT_MINOR_VERSION_KHR
31#define EGL_CONTEXT_MINOR_VERSION_KHR 0x30FB
32#endif
33#ifndef EGL_CONTEXT_FLAGS_KHR
34#define EGL_CONTEXT_FLAGS_KHR 0x30FC
35#endif
36#ifndef EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR
37#define EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR 0x30FD
38#endif
39#ifndef EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR
40#define EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR 0x00000001
41#endif
42#ifndef EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR
43#define EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR 0x00000002
44#endif
45#ifndef EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR
46#define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR 0x00000001
47#endif
48#ifndef EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR
49#define EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR 0x00000002
50#endif
51
52// Constants for OpenGL which are not available in the ES headers.
53#ifndef GL_CONTEXT_FLAGS
54#define GL_CONTEXT_FLAGS 0x821E
55#endif
56#ifndef GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT
57#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x0001
58#endif
59#ifndef GL_CONTEXT_FLAG_DEBUG_BIT
60#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002
61#endif
62#ifndef GL_CONTEXT_PROFILE_MASK
63#define GL_CONTEXT_PROFILE_MASK 0x9126
64#endif
65#ifndef GL_CONTEXT_CORE_PROFILE_BIT
66#define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001
67#endif
68#ifndef GL_CONTEXT_COMPATIBILITY_PROFILE_BIT
69#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002
70#endif
71
73
74namespace QtWaylandClient {
75
77{
78public:
81 {
85 attribute vec4 texCoords;\n\
86 varying vec2 outTexCoords;\n\
87 void main()\n\
88 {\n\
89 gl_Position = position;\n\
90 outTexCoords = texCoords.xy;\n\
91 }");
92 m_blitProgram->addShaderFromSourceCode(QOpenGLShader::Fragment, "varying highp vec2 outTexCoords;\n\
93 uniform sampler2D texture;\n\
94 void main()\n\
95 {\n\
96 gl_FragColor = texture2D(texture, outTexCoords);\n\
97 }");
98
100 m_blitProgram->bindAttributeLocation("texCoords", 1);
101
102 if (!m_blitProgram->link()) {
103 qDebug() << "Shader Program link failed.";
104 qDebug() << m_blitProgram->log();
105 }
106
110
111 glDisable(GL_DEPTH_TEST);
112 glDisable(GL_BLEND);
113 glDisable(GL_CULL_FACE);
114 glDisable(GL_SCISSOR_TEST);
115 glDepthMask(GL_FALSE);
116 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
117
119 m_buffer.bind();
120
121 static const GLfloat squareVertices[] = {
122 -1.f, -1.f,
123 1.0f, -1.f,
124 -1.f, 1.0f,
125 1.0f, 1.0f
126 };
127 static const GLfloat inverseSquareVertices[] = {
128 -1.f, 1.f,
129 1.f, 1.f,
130 -1.f, -1.f,
131 1.f, -1.f
132 };
133 static const GLfloat textureVertices[] = {
134 0.0f, 0.0f,
135 1.0f, 0.0f,
136 0.0f, 1.0f,
137 1.0f, 1.0f,
138 };
139
141 m_inverseSquareVerticesOffset = sizeof(squareVertices);
142 m_textureVerticesOffset = sizeof(squareVertices) + sizeof(textureVertices);
143
144 m_buffer.allocate(sizeof(squareVertices) + sizeof(inverseSquareVertices) + sizeof(textureVertices));
145 m_buffer.write(m_squareVerticesOffset, squareVertices, sizeof(squareVertices));
146 m_buffer.write(m_inverseSquareVerticesOffset, inverseSquareVertices, sizeof(inverseSquareVertices));
147 m_buffer.write(m_textureVerticesOffset, textureVertices, sizeof(textureVertices));
148
150
152 }
154 {
155 delete m_blitProgram;
156 }
158 {
160
161 QSize surfaceSize = window->surfaceSize();
162 qreal scale = window->scale() ;
163 glViewport(0, 0, surfaceSize.width() * scale, surfaceSize.height() * scale);
164
165 //Draw Decoration
166 if (auto *decoration = window->decoration()) {
168 QImage decorationImage = decoration->contentImage();
169 cache->bindTexture(m_context->context(), decorationImage);
170 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
171 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
172 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, m_textureWrap);
173 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, m_textureWrap);
174 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
175 }
176
177 //Draw Content
179 glBindTexture(GL_TEXTURE_2D, window->contentTexture());
180 QRect r = window->contentsRect();
181 glViewport(r.x() * scale, r.y() * scale, r.width() * scale, r.height() * scale);
182 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
183 }
184
192};
193
196 : QEGLPlatformContext(fmt, share, eglDisplay), m_display(display)
197{
198 m_reconnectionWatcher = QObject::connect(m_display, &QWaylandDisplay::connected,
199 m_display, [this] { invalidateContext(); });
200
201 switch (format().renderableType()) {
203 m_api = EGL_OPENVG_API;
204 break;
205#ifdef EGL_VERSION_1_4
207 m_api = EGL_OPENGL_API;
208 break;
209#endif // EGL_VERSION_1_4
210 default:
211 m_api = EGL_OPENGL_ES_API;
212 break;
213 }
214
215 // Create an EGL context for the decorations blitter. By using a dedicated context we don't need to make sure to not
216 // change the context state and we also use OpenGL ES 2 API independently to what the app is using to draw.
217 QList<EGLint> eglDecorationsContextAttrs = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
218 m_decorationsContext = eglCreateContext(eglDisplay, eglConfig(), eglContext(),
219 eglDecorationsContextAttrs.constData());
220 if (m_decorationsContext == EGL_NO_CONTEXT)
221 qWarning("QWaylandGLContext: Failed to create the decorations EGLContext. Decorations will not be drawn.");
222
223 EGLint a = EGL_MIN_SWAP_INTERVAL;
224 EGLint b = EGL_MAX_SWAP_INTERVAL;
225 if (!eglGetConfigAttrib(eglDisplay, eglConfig(), a, &a)
226 || !eglGetConfigAttrib(eglDisplay, eglConfig(), b, &b) || a > 0) {
227 m_supportNonBlockingSwap = false;
228 }
229 {
230 bool ok;
231 int supportNonBlockingSwap = qEnvironmentVariableIntValue("QT_WAYLAND_FORCE_NONBLOCKING_SWAP_SUPPORT", &ok);
232 if (ok)
233 m_supportNonBlockingSwap = supportNonBlockingSwap != 0;
234 }
235 if (!m_supportNonBlockingSwap) {
236 qWarning(lcQpaWayland) << "Non-blocking swap buffers not supported."
237 << "Subsurface rendering can be affected."
238 << "It may also cause the event loop to freeze in some situations";
239 }
240}
241
243{
244 m_wlSurface = m_display->createSurface(nullptr);
245 m_eglWindow = wl_egl_window_create(m_wlSurface, 1, 1);
246#if QT_CONFIG(egl_extension_platform_wayland)
247 EGLSurface eglSurface =
248 eglCreatePlatformWindowSurface(eglDisplay(), eglConfig(), m_eglWindow, nullptr);
249#else
250 EGLSurface eglSurface = eglCreateWindowSurface(eglDisplay(), eglConfig(), m_eglWindow, nullptr);
251#endif
252 return eglSurface;
253}
254
256{
257 eglDestroySurface(eglDisplay(), eglSurface);
258 wl_egl_window_destroy(m_eglWindow);
259 m_eglWindow = nullptr;
260 wl_surface_destroy(m_wlSurface);
261 m_wlSurface = nullptr;
262}
263
265{
266 QObject::disconnect(m_reconnectionWatcher);
267 delete m_blitter;
268 m_blitter = nullptr;
269 if (m_decorationsContext != EGL_NO_CONTEXT)
270 eglDestroyContext(eglDisplay(), m_decorationsContext);
271}
272
274{
275 Q_ASSERT(m_currentWindow != nullptr);
276 if (m_supportNonBlockingSwap)
277 m_currentWindow->beginFrame();
278}
279
281{
282 Q_ASSERT(m_currentWindow != nullptr);
283 if (m_supportNonBlockingSwap)
284 m_currentWindow->endFrame();
285}
286
288{
289 if (!isValid()) {
290 return false;
291 }
292
293 // in QWaylandGLContext() we called eglBindAPI with the correct value. However,
294 // eglBindAPI's documentation says:
295 // "eglBindAPI defines the current rendering API for EGL in the thread it is called from"
296 // Since makeCurrent() can be called from a different thread than the one we created the
297 // context in make sure to call eglBindAPI in the correct thread.
298 if (eglQueryAPI() != m_api) {
299 eglBindAPI(m_api);
300 }
301
302 m_currentWindow = static_cast<QWaylandEglWindow *>(surface);
303 EGLSurface eglSurface = m_currentWindow->eglSurface();
304
305 if (!m_currentWindow->needToUpdateContentFBO() && (eglSurface != EGL_NO_SURFACE)) {
306 if (!eglMakeCurrent(eglDisplay(), eglSurface, eglSurface, eglContext())) {
307 qWarning("QWaylandGLContext::makeCurrent: eglError: %#x, this: %p \n", eglGetError(), this);
308 return false;
309 }
310 return true;
311 }
312
313 if (m_currentWindow->isExposed())
314 m_currentWindow->setCanResize(false);
315
316 if (eglSurface == EGL_NO_SURFACE) {
317 m_currentWindow->updateSurface(true);
318 eglSurface = m_currentWindow->eglSurface();
319 }
320
321 if (!eglMakeCurrent(eglDisplay(), eglSurface, eglSurface, eglContext())) {
322 qWarning("QWaylandGLContext::makeCurrent: eglError: %#x, this: %p \n", eglGetError(), this);
323 m_currentWindow->setCanResize(true);
324 return false;
325 }
326
327 //### setCurrentContext will be called in QOpenGLContext::makeCurrent after this function
328 // returns, but that's too late, as we need a current context in order to bind the content FBO.
330 m_currentWindow->bindContentFBO();
331
332 return true;
333}
334
336{
337 eglMakeCurrent(eglDisplay(), EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
338}
339
341{
342 QWaylandEglWindow *window = static_cast<QWaylandEglWindow *>(surface);
343
344 EGLSurface eglSurface = window->eglSurface();
345
346 if (window->decoration()) {
347 if (m_api != EGL_OPENGL_ES_API)
348 eglBindAPI(EGL_OPENGL_ES_API);
349
350 // save the current EGL content and surface to set it again after the blitter is done
351 EGLDisplay currentDisplay = eglGetCurrentDisplay();
352 EGLContext currentContext = eglGetCurrentContext();
353 EGLSurface currentSurfaceDraw = eglGetCurrentSurface(EGL_DRAW);
354 EGLSurface currentSurfaceRead = eglGetCurrentSurface(EGL_READ);
355 eglMakeCurrent(eglDisplay(), eglSurface, eglSurface, m_decorationsContext);
356
357 if (!m_blitter)
358 m_blitter = new DecorationsBlitter(this);
359 m_blitter->blit(window);
360
361 if (m_api != EGL_OPENGL_ES_API)
362 eglBindAPI(m_api);
363 eglMakeCurrent(currentDisplay, currentSurfaceDraw, currentSurfaceRead, currentContext);
364 }
365
366 int swapInterval = m_supportNonBlockingSwap ? 0 : format().swapInterval();
367 eglSwapInterval(eglDisplay(), swapInterval);
368 if (swapInterval == 0 && format().swapInterval() > 0) {
369 // Emulating a blocking swap
370 glFlush(); // Flush before waiting so we can swap more quickly when the frame event arrives
371 window->waitForFrameSync(100);
372 }
373 window->handleUpdate();
374 if (!eglSwapBuffers(eglDisplay(), eglSurface))
375 qCWarning(lcQpaWayland, "eglSwapBuffers failed with %#x, surface: %p", eglGetError(), eglSurface);
376
377 window->setCanResize(true);
378}
379
381{
382 return static_cast<QWaylandEglWindow *>(surface)->contentFBO();
383}
384
385QFunctionPointer QWaylandGLContext::getProcAddress(const char *procName)
386{
387 QFunctionPointer proc = (QFunctionPointer) eglGetProcAddress(procName);
388 if (!proc)
389 proc = (QFunctionPointer) dlsym(RTLD_DEFAULT, procName);
390 return proc;
391}
392
394{
395 return static_cast<QWaylandEglWindow *>(surface)->eglSurface();
396}
397
398}
399
An EGL context implementation.
EGLConfig eglConfig() const
EGLDisplay eglDisplay() const
QSurfaceFormat format() const override
EGLContext eglContext() const
virtual void invalidateContext() override
bool isValid() const override
\inmodule QtGui
Definition qimage.h:37
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member)
\threadsafe
Definition qobject.cpp:3236
The QOpenGLBuffer class provides functions for creating and managing OpenGL buffer objects.
bool bind()
Binds the buffer associated with this object to the current OpenGL context.
void write(int offset, const void *data, int count)
Replaces the count bytes of this buffer starting at offset with the contents of data.
bool create()
Creates the buffer object in the OpenGL server.
void allocate(const void *data, int count)
Allocates count bytes of space to the buffer, initialized to the contents of data.
static QOpenGLContext * setCurrentContext(QOpenGLContext *context)
QOpenGLFunctions * functions() const
Get the QOpenGLFunctions instance for this context.
The QOpenGLFunctions class provides cross-platform access to the OpenGL ES 2.0 API.
void glViewport(GLint x, GLint y, GLsizei width, GLsizei height)
Convenience function that calls glViewport(x, y, width, height).
void glDrawArrays(GLenum mode, GLint first, GLsizei count)
Convenience function that calls glDrawArrays(mode, first, count).
void glTexParameteri(GLenum target, GLenum pname, GLint param)
Convenience function that calls glTexParameteri(target, pname, param).
void glColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha)
Convenience function that calls glColorMask(red, green, blue, alpha).
void glBindTexture(GLenum target, GLuint texture)
Convenience function that calls glBindTexture(target, texture).
void glDepthMask(GLboolean flag)
Convenience function that calls glDepthMask(flag).
void glDisable(GLenum cap)
Convenience function that calls glDisable(cap).
void initializeOpenGLFunctions()
Initializes OpenGL function resolution for the current context.
bool hasOpenGLFeature(QOpenGLFunctions::OpenGLFeature feature) const
Returns true if feature is present on this system's OpenGL implementation; false otherwise.
The QOpenGLShaderProgram class allows OpenGL shader programs to be linked and used.
void bindAttributeLocation(const char *name, int location)
Binds the attribute name to the specified location.
bool addShaderFromSourceCode(QOpenGLShader::ShaderType type, const char *source)
Compiles source as a shader of the specified type and adds it to this shader program.
void enableAttributeArray(int location)
Enables the vertex array at location in this shader program so that the value set by setAttributeArra...
virtual bool link()
Links together the shaders that were added to this program with addShader().
QString log() const
Returns the errors and warnings that occurred during the last link() or addShader() with explicitly s...
bool bind()
Binds this shader program to the active QOpenGLContext and makes it the current shader program.
void setAttributeBuffer(int location, GLenum type, int offset, int tupleSize, int stride=0)
Sets an array of vertex values on the attribute at location in this shader program,...
static QOpenGLTextureCache * cacheForContext(QOpenGLContext *context)
The QPlatformOpenGLContext class provides an abstraction for native GL contexts.
QOpenGLContext * context() const
The QPlatformSurface class provides an abstraction for a surface.
\inmodule QtCore\reentrant
Definition qrect.h:30
\inmodule QtCore
Definition qsize.h:25
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:133
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:130
The QSurfaceFormat class represents the format of a QSurface. \inmodule QtGui.
int swapInterval() const
Returns the swap interval.
RenderableType renderableType() const
Gets the renderable type.
DecorationsBlitter(QWaylandGLContext *context)
void blit(QWaylandEglWindow *window)
struct wl_surface * createSurface(void *handle)
QFunctionPointer getProcAddress(const char *procName) override
Reimplement in subclass to allow dynamic querying of OpenGL symbols.
void swapBuffers(QPlatformSurface *surface) override
Reimplement in subclass to native swap buffers calls.
EGLSurface eglSurfaceForPlatformSurface(QPlatformSurface *surface) override
QWaylandGLContext(EGLDisplay eglDisplay, QWaylandDisplay *display, const QSurfaceFormat &format, QPlatformOpenGLContext *share)
EGLSurface createTemporaryOffscreenSurface() override
void endFrame() override
Called when the RHI ends rendering a in the context.
void beginFrame() override
Called when the RHI begins rendering a new frame in the context.
void destroyTemporaryOffscreenSurface(EGLSurface surface) override
GLuint defaultFramebufferObject(QPlatformSurface *surface) const override
Reimplement in subclass if your platform uses framebuffer objects for surfaces.
bool makeCurrent(QPlatformSurface *surface) override
bool isExposed() const override
Returns if this window is exposed in the windowing system.
QCache< int, Employee > cache
[0]
struct wl_display * display
Definition linuxdmabuf.h:41
Combined button and popup list for selecting options.
static void * context
typedef EGLSurface(EGLAPIENTRYP PFNEGLCREATESTREAMPRODUCERSURFACEKHRPROC)(EGLDisplay dpy
typedef EGLDisplay(EGLAPIENTRYP PFNEGLGETPLATFORMDISPLAYEXTPROC)(EGLenum platform
#define qDebug
[1]
Definition qlogging.h:164
#define qWarning
Definition qlogging.h:166
#define qCWarning(category,...)
GLboolean GLboolean GLboolean b
typedef GLfloat(GL_APIENTRYP PFNGLGETPATHLENGTHNVPROC)(GLuint path
GLboolean GLboolean GLboolean GLboolean a
[7]
GLboolean r
[2]
GLenum GLenum GLenum GLenum GLenum scale
#define GL_CLAMP_TO_EDGE
Definition qopenglext.h:100
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define GLuint
#define GL_FLOAT
Q_CORE_EXPORT int qEnvironmentVariableIntValue(const char *varName, bool *ok=nullptr) noexcept
double qreal
Definition qtypes.h:187
QVideoFrameFormat::PixelFormat fmt
aWidget window() -> setWindowTitle("New Window Title")
[2]