Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
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// Qt-Security score:significant reason:default
4
6
7#include <QtWaylandClient/private/qwaylanddisplay_p.h>
8#include <QtWaylandClient/private/qwaylandwindow_p.h>
9#include <QtWaylandClient/private/qwaylandsubsurface_p.h>
10#include <QtWaylandClient/private/qwaylandabstractdecoration_p.h>
11#include <QtWaylandClient/private/qwaylandintegration_p.h>
13
14#include <QDebug>
15#include <QtGui/private/qeglconvenience_p.h>
16#include <QtGui/private/qopenglcontext_p.h>
17#include <QtOpenGL/private/qopengltexturecache_p.h>
18#include <QtGui/private/qguiapplication_p.h>
19#include <QtGui/private/qeglpbuffer_p.h>
20
21
22#include <qpa/qplatformopenglcontext.h>
23#include <QtGui/QSurfaceFormat>
24#include <QtOpenGL/QOpenGLShaderProgram>
25#include <QtGui/QOpenGLFunctions>
26#include <QOpenGLBuffer>
27
28#include <QtCore/qmutex.h>
29
30#include <dlfcn.h>
31
32// Constants from EGL_KHR_create_context
33#ifndef EGL_CONTEXT_MINOR_VERSION_KHR
34#define EGL_CONTEXT_MINOR_VERSION_KHR 0x30FB
35#endif
36#ifndef EGL_CONTEXT_FLAGS_KHR
37#define EGL_CONTEXT_FLAGS_KHR 0x30FC
38#endif
39#ifndef EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR
40#define EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR 0x30FD
41#endif
42#ifndef EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR
43#define EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR 0x00000001
44#endif
45#ifndef EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR
46#define EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR 0x00000002
47#endif
48#ifndef EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR
49#define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR 0x00000001
50#endif
51#ifndef EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR
52#define EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR 0x00000002
53#endif
54#ifndef EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR
55#define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR 0x31BD
56#endif
57#ifndef EGL_LOSE_CONTEXT_ON_RESET_KHR
58#define EGL_LOSE_CONTEXT_ON_RESET_KHR 0x31BF
59#endif
60#ifndef EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR
61#define EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR 0x00000004
62#endif
63
64// Constants from EGL_EXT_create_context_robustness
65#ifndef EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT
66#define EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT 0x30BF
67#endif
68#ifndef EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT
69#define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT 0x3138
70#endif
71#ifndef EGL_LOSE_CONTEXT_ON_RESET_EXT
72#define EGL_LOSE_CONTEXT_ON_RESET_EXT 0x31BF
73#endif
74
75// Constants for OpenGL which are not available in the ES headers.
76#ifndef GL_CONTEXT_FLAGS
77#define GL_CONTEXT_FLAGS 0x821E
78#endif
79#ifndef GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT
80#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x0001
81#endif
82#ifndef GL_CONTEXT_FLAG_DEBUG_BIT
83#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002
84#endif
85#ifndef GL_CONTEXT_PROFILE_MASK
86#define GL_CONTEXT_PROFILE_MASK 0x9126
87#endif
88#ifndef GL_CONTEXT_CORE_PROFILE_BIT
89#define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001
90#endif
91#ifndef GL_CONTEXT_COMPATIBILITY_PROFILE_BIT
92#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002
93#endif
94
95// Constants from EGL_NV_robustness_video_memory_purge
96#ifndef EGL_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV
97#define EGL_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV 0x334C
98#endif
99
100QT_BEGIN_NAMESPACE
101
102namespace QtWaylandClient {
103
105{
106public:
107 DecorationsBlitter(QWaylandGLContext *context)
108 : m_context(context)
109 {
110 initializeOpenGLFunctions();
111 m_blitProgram = new QOpenGLShaderProgram();
112 m_blitProgram->addShaderFromSourceCode(QOpenGLShader::Vertex, "attribute vec4 position;\n\
113 attribute vec4 texCoords;\n\
114 varying vec2 outTexCoords;\n\
115 void main()\n\
116 {\n\
117 gl_Position = position;\n\
118 outTexCoords = texCoords.xy;\n\
119 }");
120 m_blitProgram->addShaderFromSourceCode(QOpenGLShader::Fragment, "varying highp vec2 outTexCoords;\n\
121 uniform sampler2D texture;\n\
122 uniform lowp float forceOpaque;\n\
123 void main()\n\
124 {\n\
125 lowp vec4 c = texture2D(texture, outTexCoords);\n\
126 c.a = mix(c.a, 1.0, forceOpaque);\n\
127 gl_FragColor = c;\n\
128 }");
129
130 m_blitProgram->bindAttributeLocation("position", 0);
131 m_blitProgram->bindAttributeLocation("texCoords", 1);
132
133 if (!m_blitProgram->link()) {
134 qDebug() << "Shader Program link failed.";
135 qDebug() << m_blitProgram->log();
136 }
137
138 m_blitProgram->bind();
139 m_blitProgram->enableAttributeArray(0);
140 m_blitProgram->enableAttributeArray(1);
141
142 // Default: keep sampled alpha.
143 m_blitProgram->setUniformValue("forceOpaque", 0.0f);
144
145 glDisable(GL_DEPTH_TEST);
146 glDisable(GL_BLEND);
147 glDisable(GL_CULL_FACE);
148 glDisable(GL_SCISSOR_TEST);
149 glDepthMask(GL_FALSE);
150 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
151
152 m_buffer.create();
153 m_buffer.bind();
154
155 static const GLfloat squareVertices[] = {
156 -1.f, -1.f,
157 1.0f, -1.f,
158 -1.f, 1.0f,
159 1.0f, 1.0f
160 };
161 static const GLfloat inverseSquareVertices[] = {
162 -1.f, 1.f,
163 1.f, 1.f,
164 -1.f, -1.f,
165 1.f, -1.f
166 };
167 static const GLfloat textureVertices[] = {
168 0.0f, 0.0f,
169 1.0f, 0.0f,
170 0.0f, 1.0f,
171 1.0f, 1.0f,
172 };
173
175 m_inverseSquareVerticesOffset = sizeof(squareVertices);
176 m_textureVerticesOffset = sizeof(squareVertices) + sizeof(textureVertices);
177
178 m_buffer.allocate(sizeof(squareVertices) + sizeof(inverseSquareVertices) + sizeof(textureVertices));
179 m_buffer.write(m_squareVerticesOffset, squareVertices, sizeof(squareVertices));
180 m_buffer.write(m_inverseSquareVerticesOffset, inverseSquareVertices, sizeof(inverseSquareVertices));
181 m_buffer.write(m_textureVerticesOffset, textureVertices, sizeof(textureVertices));
182
183 m_blitProgram->setAttributeBuffer(1, GL_FLOAT, m_textureVerticesOffset, 2);
184
185 m_textureWrap = m_context->context()->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat) ? GL_REPEAT : GL_CLAMP_TO_EDGE;
186 }
188 {
189 delete m_blitProgram;
190 }
191 void blit(QWaylandEglWindow *window)
192 {
193 QOpenGLTextureCache *cache = QOpenGLTextureCache::cacheForContext(m_context->context());
194
195 QSize surfaceSize = window->surfaceSize();
196 qreal scale = window->scale() ;
197 glViewport(0, 0, surfaceSize.width() * scale, surfaceSize.height() * scale);
198
199 //Draw Decoration
200 if (auto *decoration = window->decoration()) {
201 m_blitProgram->setUniformValue("forceOpaque", 0.0f);
202 m_blitProgram->setAttributeBuffer(0, GL_FLOAT, m_inverseSquareVerticesOffset, 2);
203 QImage decorationImage = decoration->contentImage();
204 cache->bindTexture(m_context->context(), decorationImage);
205 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
206 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
207 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, m_textureWrap);
208 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, m_textureWrap);
209 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
210 }
211
212 //Draw Content
213 m_blitProgram->setAttributeBuffer(0, GL_FLOAT, m_squareVerticesOffset, 2);
214 glBindTexture(GL_TEXTURE_2D, window->contentTexture());
215 QRect r = window->contentsRect();
216 glViewport(r.x() * scale, r.y() * scale, r.width() * scale, r.height() * scale);
217
218 const bool opaqueWindow = window->window() && !window->window()->requestedFormat().hasAlpha();
219 m_blitProgram->setUniformValue("forceOpaque", opaqueWindow ? 1.0f : 0.0f);
220 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
221 }
222
223 QOpenGLShaderProgram *m_blitProgram = nullptr;
224 QWaylandGLContext *m_context = nullptr;
230};
231
237
242{
244 m_display, [this] { invalidateContext(); });
245
246 switch (format().renderableType()) {
249 break;
250#ifdef EGL_VERSION_1_4
253 break;
254#endif // EGL_VERSION_1_4
255 default:
257 break;
258 }
259
263 || !eglGetConfigAttrib(eglDisplay, eglConfig(), b, &b) || a > 0) {
265 }
266 {
267 bool ok;
268 int supportNonBlockingSwap = qEnvironmentVariableIntValue("QT_WAYLAND_FORCE_NONBLOCKING_SWAP_SUPPORT", &ok);
269 if (ok)
271 }
273 qCWarning(lcQpaWayland) << "Non-blocking swap buffers not supported."
274 << "Subsurface rendering can be affected."
275 << "It may also cause the event loop to freeze in some situations";
276 }
277}
278
291
300
302{
303 bool ok;
304 const int doneCurrentWorkAround = qEnvironmentVariableIntValue("QT_WAYLAND_ENABLE_DONECURRENT_WORKAROUND", &ok);
305 if (ok) {
308 qCDebug(lcQpaWayland) << "Enabling doneCurrent() workaround on request.";
309 else
310 qCDebug(lcQpaWayland) << "Disabling doneCurrent() workaround on request.";
311
312 } else {
313 // Note that even though there is an EGL context current here,
314 // QOpenGLContext and QOpenGLFunctions are not yet usable at this stage.
315 const char *renderer = reinterpret_cast<const char *>(glGetString(GL_RENDERER));
316 if (renderer && strstr(renderer, "Mali")) {
317 qCDebug(lcQpaWayland) << "Enabling doneCurrent() workaround for Mali GPU."
318 << "Set QT_WAYLAND_ENABLE_DONECURRENT_WORKAROUND=0 to disable.";
320 }
321 }
322
324}
325
334
336{
338
340 // Create an EGL context for the decorations blitter. By using a dedicated context we don't need to make sure to not
341 // change the context state and we also use OpenGL ES 2 API independently to what the app is using to draw.
343 EGLint flags = 0;
344
346 const bool haveResetOnVideoMemoryPurge = q_hasEglExtension(eglDisplay(), "EGL_NV_robustness_video_memory_purge");
347
355 }
356 } else if (format().renderableType() == QSurfaceFormat::OpenGLES) {
357 if (q_hasEglExtension(eglDisplay(), "EGL_EXT_create_context_robustness")) {
365 }
366 }
367 }
368 }
369
370 if (flags) {
373 }
374
376
380 qWarning("QWaylandGLContext: Failed to create the decorations EGLContext. Decorations will not be drawn.");
381 }
382}
383
385{
386 if (!m_currentWindow)
387 return;
388
391}
392
408
410{
411 if (!isValid()) {
412 return false;
413 }
414
416 m_currentWindow = nullptr;
418 }
419
420 // in QWaylandGLContext() we called eglBindAPI with the correct value. However,
421 // eglBindAPI's documentation says:
422 // "eglBindAPI defines the current rendering API for EGL in the thread it is called from"
423 // Since makeCurrent() can be called from a different thread than the one we created the
424 // context in make sure to call eglBindAPI in the correct thread.
425 if (eglQueryAPI() != m_api) {
427 }
428
429 m_currentWindow = static_cast<QWaylandEglWindow *>(surface);
430
433
434 if (!checkGraphicsReset())
435 return false;
436
439 qWarning("QWaylandGLContext::makeCurrent: eglError: %#x, this: %p \n", eglGetError(), this);
440 return false;
441 }
442 return true;
443 }
444
445 if (eglSurface == EGL_NO_SURFACE) {
448 }
449
451 qWarning("QWaylandGLContext::makeCurrent: eglError: %#x, this: %p \n", eglGetError(), this);
452 return false;
453 }
454
455 //### setCurrentContext will be called in QOpenGLContext::makeCurrent after this function
456 // returns, but that's too late, as we need a current context in order to bind the content FBO.
459
460 return true;
461}
462
468
470{
473 }
474
476
478
479 if (window->decoration()) {
482
483 // save the current EGL content and surface to set it again after the blitter is done
489
490 if (!m_blitter)
491 m_blitter = new DecorationsBlitter(this);
493
497 }
498
501 if (swapInterval == 0 && format().swapInterval() > 0) {
502 // Emulating a blocking swap
503 glFlush(); // Flush before waiting so we can swap more quickly when the frame event arrives
505 }
508 qCWarning(lcQpaWayland, "eglSwapBuffers failed with %#x, surface: %p", eglGetError(), eglSurface);
509}
510
512{
514 return static_cast<QWaylandEglWindow *>(surface)->contentFBO();
515 else
516 return 0;
517}
518
526
527}
528
529QT_END_NAMESPACE
DecorationsBlitter(QWaylandGLContext *context)
void blit(QWaylandEglWindow *window)
#define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR
#define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT
#define EGL_LOSE_CONTEXT_ON_RESET_EXT
#define EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR
#define EGL_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV
#define EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT
#define EGL_CONTEXT_FLAGS_KHR
#define EGL_LOSE_CONTEXT_ON_RESET_KHR