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
231private:
233};
234
240
245{
247 m_display, [this] { invalidateContext(); });
248
249 switch (format().renderableType()) {
252 break;
253#ifdef EGL_VERSION_1_4
256 break;
257#endif // EGL_VERSION_1_4
258 default:
260 break;
261 }
262
266 || !eglGetConfigAttrib(eglDisplay, eglConfig(), b, &b) || a > 0) {
268 }
269 {
270 bool ok;
271 int supportNonBlockingSwap = qEnvironmentVariableIntValue("QT_WAYLAND_FORCE_NONBLOCKING_SWAP_SUPPORT", &ok);
272 if (ok)
274 }
276 qCWarning(lcQpaWayland) << "Non-blocking swap buffers not supported."
277 << "Subsurface rendering can be affected."
278 << "It may also cause the event loop to freeze in some situations";
279 }
280}
281
294
303
305{
306 bool ok;
307 const int doneCurrentWorkAround = qEnvironmentVariableIntValue("QT_WAYLAND_ENABLE_DONECURRENT_WORKAROUND", &ok);
308 if (ok) {
311 qCDebug(lcQpaWayland) << "Enabling doneCurrent() workaround on request.";
312 else
313 qCDebug(lcQpaWayland) << "Disabling doneCurrent() workaround on request.";
314
315 } else {
316 // Note that even though there is an EGL context current here,
317 // QOpenGLContext and QOpenGLFunctions are not yet usable at this stage.
318 const char *renderer = reinterpret_cast<const char *>(glGetString(GL_RENDERER));
319 if (renderer && strstr(renderer, "Mali")) {
320 qCDebug(lcQpaWayland) << "Enabling doneCurrent() workaround for Mali GPU."
321 << "Set QT_WAYLAND_ENABLE_DONECURRENT_WORKAROUND=0 to disable.";
323 }
324 }
325
327}
328
337
339{
341
343 // Create an EGL context for the decorations blitter. By using a dedicated context we don't need to make sure to not
344 // change the context state and we also use OpenGL ES 2 API independently to what the app is using to draw.
346 EGLint flags = 0;
347
349 const bool haveResetOnVideoMemoryPurge = q_hasEglExtension(eglDisplay(), "EGL_NV_robustness_video_memory_purge");
350
358 }
359 } else if (format().renderableType() == QSurfaceFormat::OpenGLES) {
360 if (q_hasEglExtension(eglDisplay(), "EGL_EXT_create_context_robustness")) {
368 }
369 }
370 }
371 }
372
373 if (flags) {
376 }
377
379
383 qWarning("QWaylandGLContext: Failed to create the decorations EGLContext. Decorations will not be drawn.");
384 }
385}
386
388{
389 if (!m_currentWindow)
390 return;
391
394}
395
411
413{
414 if (!isValid()) {
415 return false;
416 }
417
419 m_currentWindow = nullptr;
421 }
422
423 // in QWaylandGLContext() we called eglBindAPI with the correct value. However,
424 // eglBindAPI's documentation says:
425 // "eglBindAPI defines the current rendering API for EGL in the thread it is called from"
426 // Since makeCurrent() can be called from a different thread than the one we created the
427 // context in make sure to call eglBindAPI in the correct thread.
428 if (eglQueryAPI() != m_api) {
430 }
431
432 m_currentWindow = static_cast<QWaylandEglWindow *>(surface);
433
434 if (!checkGraphicsReset())
435 return false;
436
438
441
442 if (eglSurface == EGL_NO_SURFACE) {
445 }
446
448 qWarning("QWaylandGLContext::makeCurrent: eglError: %#x, this: %p \n", eglGetError(), this);
449 return false;
450 }
451 // Nvidia requires that the surface is current and on the render thread at the time of doing the actual resizing
453
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
457 // FBO.
460 }
461
462 return true;
463}
464
470
472{
475 }
476
478
480
481 if (window->decoration()) {
484
485 // save the current EGL content and surface to set it again after the blitter is done
491
492 if (!m_blitter)
493 m_blitter = new DecorationsBlitter(this);
495
499 }
500
503 if (swapInterval == 0 && format().swapInterval() > 0) {
504 // Emulating a blocking swap
505 glFlush(); // Flush before waiting so we can swap more quickly when the frame event arrives
507 }
510 qCWarning(lcQpaWayland, "eglSwapBuffers failed with %#x, surface: %p", eglGetError(), eglSurface);
511}
512
514{
516 return static_cast<QWaylandEglWindow *>(surface)->contentFBO();
517 else
518 return 0;
519}
520
528
529}
530
531QT_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