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
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>
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#include <QtGui/private/qeglpbuffer_p.h>
19
20
21#include <qpa/qplatformopenglcontext.h>
22#include <QtGui/QSurfaceFormat>
23#include <QtOpenGL/QOpenGLShaderProgram>
24#include <QtGui/QOpenGLFunctions>
25#include <QOpenGLBuffer>
26
27#include <QtCore/qmutex.h>
28
29#include <dlfcn.h>
30
31// Constants from EGL_KHR_create_context
32#ifndef EGL_CONTEXT_MINOR_VERSION_KHR
33#define EGL_CONTEXT_MINOR_VERSION_KHR 0x30FB
34#endif
35#ifndef EGL_CONTEXT_FLAGS_KHR
36#define EGL_CONTEXT_FLAGS_KHR 0x30FC
37#endif
38#ifndef EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR
39#define EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR 0x30FD
40#endif
41#ifndef EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR
42#define EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR 0x00000001
43#endif
44#ifndef EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR
45#define EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR 0x00000002
46#endif
47#ifndef EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR
48#define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR 0x00000001
49#endif
50#ifndef EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR
51#define EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR 0x00000002
52#endif
53#ifndef EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR
54#define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR 0x31BD
55#endif
56#ifndef EGL_LOSE_CONTEXT_ON_RESET_KHR
57#define EGL_LOSE_CONTEXT_ON_RESET_KHR 0x31BF
58#endif
59#ifndef EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR
60#define EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR 0x00000004
61#endif
62
63// Constants from EGL_EXT_create_context_robustness
64#ifndef EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT
65#define EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT 0x30BF
66#endif
67#ifndef EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT
68#define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT 0x3138
69#endif
70#ifndef EGL_LOSE_CONTEXT_ON_RESET_EXT
71#define EGL_LOSE_CONTEXT_ON_RESET_EXT 0x31BF
72#endif
73
74// Constants for OpenGL which are not available in the ES headers.
75#ifndef GL_CONTEXT_FLAGS
76#define GL_CONTEXT_FLAGS 0x821E
77#endif
78#ifndef GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT
79#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x0001
80#endif
81#ifndef GL_CONTEXT_FLAG_DEBUG_BIT
82#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002
83#endif
84#ifndef GL_CONTEXT_PROFILE_MASK
85#define GL_CONTEXT_PROFILE_MASK 0x9126
86#endif
87#ifndef GL_CONTEXT_CORE_PROFILE_BIT
88#define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001
89#endif
90#ifndef GL_CONTEXT_COMPATIBILITY_PROFILE_BIT
91#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002
92#endif
93
94// Constants from EGL_NV_robustness_video_memory_purge
95#ifndef EGL_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV
96#define EGL_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV 0x334C
97#endif
98
99QT_BEGIN_NAMESPACE
100
101namespace QtWaylandClient {
102
104{
105public:
106 DecorationsBlitter(QWaylandGLContext *context)
107 : m_context(context)
108 {
109 initializeOpenGLFunctions();
110 m_blitProgram = new QOpenGLShaderProgram();
111 m_blitProgram->addShaderFromSourceCode(QOpenGLShader::Vertex, "attribute vec4 position;\n\
112 attribute vec4 texCoords;\n\
113 varying vec2 outTexCoords;\n\
114 void main()\n\
115 {\n\
116 gl_Position = position;\n\
117 outTexCoords = texCoords.xy;\n\
118 }");
119 m_blitProgram->addShaderFromSourceCode(QOpenGLShader::Fragment, "varying highp vec2 outTexCoords;\n\
120 uniform sampler2D texture;\n\
121 void main()\n\
122 {\n\
123 gl_FragColor = texture2D(texture, outTexCoords);\n\
124 }");
125
126 m_blitProgram->bindAttributeLocation("position", 0);
127 m_blitProgram->bindAttributeLocation("texCoords", 1);
128
129 if (!m_blitProgram->link()) {
130 qDebug() << "Shader Program link failed.";
131 qDebug() << m_blitProgram->log();
132 }
133
134 m_blitProgram->bind();
135 m_blitProgram->enableAttributeArray(0);
136 m_blitProgram->enableAttributeArray(1);
137
138 glDisable(GL_DEPTH_TEST);
139 glDisable(GL_BLEND);
140 glDisable(GL_CULL_FACE);
141 glDisable(GL_SCISSOR_TEST);
142 glDepthMask(GL_FALSE);
143 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
144
145 m_buffer.create();
146 m_buffer.bind();
147
148 static const GLfloat squareVertices[] = {
149 -1.f, -1.f,
150 1.0f, -1.f,
151 -1.f, 1.0f,
152 1.0f, 1.0f
153 };
154 static const GLfloat inverseSquareVertices[] = {
155 -1.f, 1.f,
156 1.f, 1.f,
157 -1.f, -1.f,
158 1.f, -1.f
159 };
160 static const GLfloat textureVertices[] = {
161 0.0f, 0.0f,
162 1.0f, 0.0f,
163 0.0f, 1.0f,
164 1.0f, 1.0f,
165 };
166
168 m_inverseSquareVerticesOffset = sizeof(squareVertices);
169 m_textureVerticesOffset = sizeof(squareVertices) + sizeof(textureVertices);
170
171 m_buffer.allocate(sizeof(squareVertices) + sizeof(inverseSquareVertices) + sizeof(textureVertices));
172 m_buffer.write(m_squareVerticesOffset, squareVertices, sizeof(squareVertices));
173 m_buffer.write(m_inverseSquareVerticesOffset, inverseSquareVertices, sizeof(inverseSquareVertices));
174 m_buffer.write(m_textureVerticesOffset, textureVertices, sizeof(textureVertices));
175
176 m_blitProgram->setAttributeBuffer(1, GL_FLOAT, m_textureVerticesOffset, 2);
177
178 m_textureWrap = m_context->context()->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat) ? GL_REPEAT : GL_CLAMP_TO_EDGE;
179 }
181 {
182 delete m_blitProgram;
183 }
184 void blit(QWaylandEglWindow *window)
185 {
186 QOpenGLTextureCache *cache = QOpenGLTextureCache::cacheForContext(m_context->context());
187
188 QSize surfaceSize = window->surfaceSize();
189 qreal scale = window->scale() ;
190 glViewport(0, 0, surfaceSize.width() * scale, surfaceSize.height() * scale);
191
192 //Draw Decoration
193 if (auto *decoration = window->decoration()) {
194 m_blitProgram->setAttributeBuffer(0, GL_FLOAT, m_inverseSquareVerticesOffset, 2);
195 QImage decorationImage = decoration->contentImage();
196 cache->bindTexture(m_context->context(), decorationImage);
197 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
198 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
199 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, m_textureWrap);
200 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, m_textureWrap);
201 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
202 }
203
204 //Draw Content
205 m_blitProgram->setAttributeBuffer(0, GL_FLOAT, m_squareVerticesOffset, 2);
206 glBindTexture(GL_TEXTURE_2D, window->contentTexture());
207 QRect r = window->contentsRect();
208 glViewport(r.x() * scale, r.y() * scale, r.width() * scale, r.height() * scale);
209 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
210 }
211
212 QOpenGLShaderProgram *m_blitProgram = nullptr;
213 QWaylandGLContext *m_context = nullptr;
219};
220
226
231{
233 m_display, [this] { invalidateContext(); });
234
235 switch (format().renderableType()) {
238 break;
239#ifdef EGL_VERSION_1_4
242 break;
243#endif // EGL_VERSION_1_4
244 default:
246 break;
247 }
248
252 || !eglGetConfigAttrib(eglDisplay, eglConfig(), b, &b) || a > 0) {
254 }
255 {
256 bool ok;
257 int supportNonBlockingSwap = qEnvironmentVariableIntValue("QT_WAYLAND_FORCE_NONBLOCKING_SWAP_SUPPORT", &ok);
258 if (ok)
260 }
262 qCWarning(lcQpaWayland) << "Non-blocking swap buffers not supported."
263 << "Subsurface rendering can be affected."
264 << "It may also cause the event loop to freeze in some situations";
265 }
266}
267
280
289
291{
292 bool ok;
293 const int doneCurrentWorkAround = qEnvironmentVariableIntValue("QT_WAYLAND_ENABLE_DONECURRENT_WORKAROUND", &ok);
294 if (ok) {
297 qCDebug(lcQpaWayland) << "Enabling doneCurrent() workaround on request.";
298 else
299 qCDebug(lcQpaWayland) << "Disabling doneCurrent() workaround on request.";
300
301 } else {
302 // Note that even though there is an EGL context current here,
303 // QOpenGLContext and QOpenGLFunctions are not yet usable at this stage.
304 const char *renderer = reinterpret_cast<const char *>(glGetString(GL_RENDERER));
305 if (renderer && strstr(renderer, "Mali")) {
306 qCDebug(lcQpaWayland) << "Enabling doneCurrent() workaround for Mali GPU."
307 << "Set QT_WAYLAND_ENABLE_DONECURRENT_WORKAROUND=0 to disable.";
309 }
310 }
311
313}
314
323
325{
327
329 // Create an EGL context for the decorations blitter. By using a dedicated context we don't need to make sure to not
330 // change the context state and we also use OpenGL ES 2 API independently to what the app is using to draw.
332 EGLint flags = 0;
333
335 const bool haveResetOnVideoMemoryPurge = q_hasEglExtension(eglDisplay(), "EGL_NV_robustness_video_memory_purge");
336
344 }
345 } else if (format().renderableType() == QSurfaceFormat::OpenGLES) {
346 if (q_hasEglExtension(eglDisplay(), "EGL_EXT_create_context_robustness")) {
354 }
355 }
356 }
357 }
358
359 if (flags) {
362 }
363
365
369 qWarning("QWaylandGLContext: Failed to create the decorations EGLContext. Decorations will not be drawn.");
370 }
371}
372
374{
375 if (!m_currentWindow)
376 return;
377
380}
381
397
399{
400 if (!isValid()) {
401 return false;
402 }
403
405 m_currentWindow = nullptr;
407 }
408
409 // in QWaylandGLContext() we called eglBindAPI with the correct value. However,
410 // eglBindAPI's documentation says:
411 // "eglBindAPI defines the current rendering API for EGL in the thread it is called from"
412 // Since makeCurrent() can be called from a different thread than the one we created the
413 // context in make sure to call eglBindAPI in the correct thread.
414 if (eglQueryAPI() != m_api) {
416 }
417
418 m_currentWindow = static_cast<QWaylandEglWindow *>(surface);
419
422
423 if (!checkGraphicsReset())
424 return false;
425
428 qWarning("QWaylandGLContext::makeCurrent: eglError: %#x, this: %p \n", eglGetError(), this);
429 return false;
430 }
431 return true;
432 }
433
434 if (eglSurface == EGL_NO_SURFACE) {
437 }
438
440 qWarning("QWaylandGLContext::makeCurrent: eglError: %#x, this: %p \n", eglGetError(), this);
441 return false;
442 }
443
444 //### setCurrentContext will be called in QOpenGLContext::makeCurrent after this function
445 // returns, but that's too late, as we need a current context in order to bind the content FBO.
448
449 return true;
450}
451
457
459{
462 }
463
465
467
468 if (window->decoration()) {
471
472 // save the current EGL content and surface to set it again after the blitter is done
478
479 if (!m_blitter)
480 m_blitter = new DecorationsBlitter(this);
482
486 }
487
490 if (swapInterval == 0 && format().swapInterval() > 0) {
491 // Emulating a blocking swap
492 glFlush(); // Flush before waiting so we can swap more quickly when the frame event arrives
494 }
497 qCWarning(lcQpaWayland, "eglSwapBuffers failed with %#x, surface: %p", eglGetError(), eglSurface);
498}
499
501{
503 return static_cast<QWaylandEglWindow *>(surface)->contentFBO();
504 else
505 return 0;
506}
507
515
516}
517
518QT_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