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
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
72QT_BEGIN_NAMESPACE
73
74namespace QtWaylandClient {
75
77{
78public:
79 DecorationsBlitter(QWaylandGLContext *context)
80 : m_context(context)
81 {
82 initializeOpenGLFunctions();
83 m_blitProgram = new QOpenGLShaderProgram();
84 m_blitProgram->addShaderFromSourceCode(QOpenGLShader::Vertex, "attribute vec4 position;\n\
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
99 m_blitProgram->bindAttributeLocation("position", 0);
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
107 m_blitProgram->bind();
108 m_blitProgram->enableAttributeArray(0);
109 m_blitProgram->enableAttributeArray(1);
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
118 m_buffer.create();
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
149 m_blitProgram->setAttributeBuffer(1, GL_FLOAT, m_textureVerticesOffset, 2);
150
151 m_textureWrap = m_context->context()->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat) ? GL_REPEAT : GL_CLAMP_TO_EDGE;
152 }
154 {
155 delete m_blitProgram;
156 }
157 void blit(QWaylandEglWindow *window)
158 {
159 QOpenGLTextureCache *cache = QOpenGLTextureCache::cacheForContext(m_context->context());
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()) {
167 m_blitProgram->setAttributeBuffer(0, GL_FLOAT, m_inverseSquareVerticesOffset, 2);
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
178 m_blitProgram->setAttributeBuffer(0, GL_FLOAT, m_squareVerticesOffset, 2);
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
185 QOpenGLShaderProgram *m_blitProgram = nullptr;
186 QWaylandGLContext *m_context = nullptr;
191 int m_textureWrap;
192};
193
199{
201 m_display, [this] { invalidateContext(); });
202
203 switch (format().renderableType()) {
206 break;
207#ifdef EGL_VERSION_1_4
210 break;
211#endif // EGL_VERSION_1_4
212 default:
214 break;
215 }
216
218 // Create an EGL context for the decorations blitter. By using a dedicated context we are free to
219 // change its state and we also use OpenGL ES 2 API independently to what the app is using to draw.
224 qWarning("QWaylandGLContext: Failed to create the decorations EGLContext. Decorations will not be drawn.");
225 }
226
230 || !eglGetConfigAttrib(eglDisplay, eglConfig(), b, &b) || a > 0) {
232 }
233 {
234 bool ok;
235 int supportNonBlockingSwap = qEnvironmentVariableIntValue("QT_WAYLAND_FORCE_NONBLOCKING_SWAP_SUPPORT", &ok);
236 if (ok)
238 }
240 qCWarning(lcQpaWayland) << "Non-blocking swap buffers not supported."
241 << "Subsurface rendering can be affected."
242 << "It may also cause the event loop to freeze in some situations";
243 }
244}
245
247{
250#if QT_CONFIG(egl_extension_platform_wayland)
253#else
255#endif
256 return eglSurface;
257}
258
260{
263 m_eglWindow = nullptr;
265 m_wlSurface = nullptr;
266}
267
269{
270 bool ok;
271 const int doneCurrentWorkAround = qEnvironmentVariableIntValue("QT_WAYLAND_ENABLE_DONECURRENT_WORKAROUND", &ok);
272 if (ok) {
275 qCDebug(lcQpaWayland) << "Enabling doneCurrent() workaround on request.";
276 else
277 qCDebug(lcQpaWayland) << "Disabling doneCurrent() workaround on request.";
278
279 } else {
280 // Note that even though there is an EGL context current here,
281 // QOpenGLContext and QOpenGLFunctions are not yet usable at this stage.
282 const char *renderer = reinterpret_cast<const char *>(glGetString(GL_RENDERER));
283 if (renderer && strstr(renderer, "Mali")) {
284 qCDebug(lcQpaWayland) << "Enabling doneCurrent() workaround for Mali GPU."
285 << "Set QT_WAYLAND_ENABLE_DONECURRENT_WORKAROUND=0 to disable.";
287 }
288 }
289
291}
292
294{
296 delete m_blitter;
297 m_blitter = nullptr;
300}
301
303{
304 Q_ASSERT(m_currentWindow != nullptr);
307}
308
310{
311 Q_ASSERT(m_currentWindow != nullptr);
313 doneCurrent();
315 }
316
319}
320
322{
323 if (!isValid()) {
324 return false;
325 }
326
327 // in QWaylandGLContext() we called eglBindAPI with the correct value. However,
328 // eglBindAPI's documentation says:
329 // "eglBindAPI defines the current rendering API for EGL in the thread it is called from"
330 // Since makeCurrent() can be called from a different thread than the one we created the
331 // context in make sure to call eglBindAPI in the correct thread.
332 if (eglQueryAPI() != m_api) {
334 }
335
336 m_currentWindow = static_cast<QWaylandEglWindow *>(surface);
337
340
343 qWarning("QWaylandGLContext::makeCurrent: eglError: %#x, this: %p \n", eglGetError(), this);
344 return false;
345 }
346 return true;
347 }
348
349 if (eglSurface == EGL_NO_SURFACE) {
352 }
353
355 qWarning("QWaylandGLContext::makeCurrent: eglError: %#x, this: %p \n", eglGetError(), this);
356 return false;
357 }
358
359 //### setCurrentContext will be called in QOpenGLContext::makeCurrent after this function
360 // returns, but that's too late, as we need a current context in order to bind the content FBO.
363
364 return true;
365}
366
368{
370}
371
373{
375
377
378 if (window->decoration()) {
381
382 // save the current EGL content and surface to set it again after the blitter is done
388
389 if (!m_blitter)
390 m_blitter = new DecorationsBlitter(this);
392
396 }
397
400 if (swapInterval == 0 && format().swapInterval() > 0) {
401 // Emulating a blocking swap
402 glFlush(); // Flush before waiting so we can swap more quickly when the frame event arrives
404 }
407 qCWarning(lcQpaWayland, "eglSwapBuffers failed with %#x, surface: %p", eglGetError(), eglSurface);
408}
409
411{
412 return static_cast<QWaylandEglWindow *>(surface)->contentFBO();
413}
414
416{
418 if (!proc)
419 proc = (QFunctionPointer) dlsym(RTLD_DEFAULT, procName);
420 return proc;
421}
422
424{
425 return static_cast<QWaylandEglWindow *>(surface)->eglSurface();
426}
427
428}
429
430QT_END_NAMESPACE
DecorationsBlitter(QWaylandGLContext *context)
void blit(QWaylandEglWindow *window)