Qt 6.x
The Qt SDK
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::reconnected, [this]() {
200 });
201
202 switch (format().renderableType()) {
204 m_api = EGL_OPENVG_API;
205 break;
206#ifdef EGL_VERSION_1_4
208 m_api = EGL_OPENGL_API;
209 break;
210#endif // EGL_VERSION_1_4
211 default:
212 m_api = EGL_OPENGL_ES_API;
213 break;
214 }
215
216 // Create an EGL context for the decorations blitter. By using a dedicated context we don't need to make sure to not
217 // change the context state and we also use OpenGL ES 2 API independently to what the app is using to draw.
218 QList<EGLint> eglDecorationsContextAttrs = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
219 m_decorationsContext = eglCreateContext(eglDisplay, eglConfig(), eglContext(),
220 eglDecorationsContextAttrs.constData());
221 if (m_decorationsContext == EGL_NO_CONTEXT)
222 qWarning("QWaylandGLContext: Failed to create the decorations EGLContext. Decorations will not be drawn.");
223
224 EGLint a = EGL_MIN_SWAP_INTERVAL;
225 EGLint b = EGL_MAX_SWAP_INTERVAL;
226 if (!eglGetConfigAttrib(eglDisplay, eglConfig(), a, &a)
227 || !eglGetConfigAttrib(eglDisplay, eglConfig(), b, &b) || a > 0) {
228 m_supportNonBlockingSwap = false;
229 }
230 {
231 bool ok;
232 int supportNonBlockingSwap = qEnvironmentVariableIntValue("QT_WAYLAND_FORCE_NONBLOCKING_SWAP_SUPPORT", &ok);
233 if (ok)
234 m_supportNonBlockingSwap = supportNonBlockingSwap != 0;
235 }
236 if (!m_supportNonBlockingSwap) {
237 qWarning(lcQpaWayland) << "Non-blocking swap buffers not supported."
238 << "Subsurface rendering can be affected."
239 << "It may also cause the event loop to freeze in some situations";
240 }
241}
242
244{
245 m_wlSurface = m_display->createSurface(nullptr);
246 m_eglWindow = wl_egl_window_create(m_wlSurface, 1, 1);
247#if QT_CONFIG(egl_extension_platform_wayland)
248 EGLSurface eglSurface =
249 eglCreatePlatformWindowSurface(eglDisplay(), eglConfig(), m_eglWindow, nullptr);
250#else
251 EGLSurface eglSurface = eglCreateWindowSurface(eglDisplay(), eglConfig(), m_eglWindow, nullptr);
252#endif
253 return eglSurface;
254}
255
257{
258 eglDestroySurface(eglDisplay(), eglSurface);
259 wl_egl_window_destroy(m_eglWindow);
260 m_eglWindow = nullptr;
261 wl_surface_destroy(m_wlSurface);
262 m_wlSurface = nullptr;
263}
264
266{
267 QObject::disconnect(m_reconnectionWatcher);
268 delete m_blitter;
269 m_blitter = nullptr;
270 if (m_decorationsContext != EGL_NO_CONTEXT)
271 eglDestroyContext(eglDisplay(), m_decorationsContext);
272}
273
275{
276 Q_ASSERT(m_currentWindow != nullptr);
277 m_currentWindow->beginFrame();
278}
279
281{
282 Q_ASSERT(m_currentWindow != nullptr);
283 m_currentWindow->endFrame();
284}
285
287{
288 if (!isValid()) {
289 return false;
290 }
291
292 // in QWaylandGLContext() we called eglBindAPI with the correct value. However,
293 // eglBindAPI's documentation says:
294 // "eglBindAPI defines the current rendering API for EGL in the thread it is called from"
295 // Since makeCurrent() can be called from a different thread than the one we created the
296 // context in make sure to call eglBindAPI in the correct thread.
297 if (eglQueryAPI() != m_api) {
298 eglBindAPI(m_api);
299 }
300
301 m_currentWindow = static_cast<QWaylandEglWindow *>(surface);
302 EGLSurface eglSurface = m_currentWindow->eglSurface();
303
304 if (!m_currentWindow->needToUpdateContentFBO() && (eglSurface != EGL_NO_SURFACE)) {
305 if (!eglMakeCurrent(eglDisplay(), eglSurface, eglSurface, eglContext())) {
306 qWarning("QWaylandGLContext::makeCurrent: eglError: %#x, this: %p \n", eglGetError(), this);
307 return false;
308 }
309 return true;
310 }
311
312 if (m_currentWindow->isExposed())
313 m_currentWindow->setCanResize(false);
314 if (m_decorationsContext != EGL_NO_CONTEXT && !m_currentWindow->decoration())
315 m_currentWindow->createDecoration();
316
317 if (eglSurface == EGL_NO_SURFACE) {
318 m_currentWindow->updateSurface(true);
319 eglSurface = m_currentWindow->eglSurface();
320 }
321
322 if (!eglMakeCurrent(eglDisplay(), eglSurface, eglSurface, eglContext())) {
323 qWarning("QWaylandGLContext::makeCurrent: eglError: %#x, this: %p \n", eglGetError(), this);
324 m_currentWindow->setCanResize(true);
325 return false;
326 }
327
328 //### setCurrentContext will be called in QOpenGLContext::makeCurrent after this function
329 // returns, but that's too late, as we need a current context in order to bind the content FBO.
331 m_currentWindow->bindContentFBO();
332
333 return true;
334}
335
337{
338 eglMakeCurrent(eglDisplay(), EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
339}
340
342{
343 QWaylandEglWindow *window = static_cast<QWaylandEglWindow *>(surface);
344
345 EGLSurface eglSurface = window->eglSurface();
346
347 if (window->decoration()) {
348 if (m_api != EGL_OPENGL_ES_API)
349 eglBindAPI(EGL_OPENGL_ES_API);
350
351 // save the current EGL content and surface to set it again after the blitter is done
352 EGLDisplay currentDisplay = eglGetCurrentDisplay();
353 EGLContext currentContext = eglGetCurrentContext();
354 EGLSurface currentSurfaceDraw = eglGetCurrentSurface(EGL_DRAW);
355 EGLSurface currentSurfaceRead = eglGetCurrentSurface(EGL_READ);
356 eglMakeCurrent(eglDisplay(), eglSurface, eglSurface, m_decorationsContext);
357
358 if (!m_blitter)
359 m_blitter = new DecorationsBlitter(this);
360 m_blitter->blit(window);
361
362 if (m_api != EGL_OPENGL_ES_API)
363 eglBindAPI(m_api);
364 eglMakeCurrent(currentDisplay, currentSurfaceDraw, currentSurfaceRead, currentContext);
365 }
366
367 int swapInterval = m_supportNonBlockingSwap ? 0 : format().swapInterval();
368 eglSwapInterval(eglDisplay(), swapInterval);
369 if (swapInterval == 0 && format().swapInterval() > 0) {
370 // Emulating a blocking swap
371 glFlush(); // Flush before waiting so we can swap more quickly when the frame event arrives
372 window->waitForFrameSync(100);
373 }
374 window->handleUpdate();
375 eglSwapBuffers(eglDisplay(), 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
Definition qlist.h:74
const_pointer constData() const noexcept
Definition qlist.h:416
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2823
static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member)
\threadsafe
Definition qobject.cpp:3099
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:132
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:129
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
QWaylandAbstractDecoration * decoration() const
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:160
#define qWarning
Definition qlogging.h:162
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:92
QVideoFrameFormat::PixelFormat fmt
aWidget window() -> setWindowTitle("New Window Title")
[2]