KWin
Loading...
Searching...
No Matches
eglplatformcontext.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
6 SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
7
8 SPDX-License-Identifier: GPL-2.0-or-later
9*/
10
11#include "eglplatformcontext.h"
12#include "core/outputbackend.h"
13#include "eglhelpers.h"
14#include "internalwindow.h"
15#include "offscreensurface.h"
16#include "opengl/eglcontext.h"
17#include "opengl/egldisplay.h"
18#include "opengl/glutils.h"
19#include "swapchain.h"
20#include "window.h"
21
22#include "logging.h"
23
24#include <QOpenGLContext>
25
26#include <private/qopenglcontext_p.h>
27
28namespace KWin
29{
30namespace QPA
31{
32
33EGLRenderTarget::EGLRenderTarget(GraphicsBuffer *buffer, std::unique_ptr<GLFramebuffer> fbo, std::shared_ptr<GLTexture> texture)
34 : buffer(buffer)
35 , fbo(std::move(fbo))
36 , texture(std::move(texture))
37{
38}
39
41{
42 fbo.reset();
43 texture.reset();
44}
45
46EGLPlatformContext::EGLPlatformContext(QOpenGLContext *context, EglDisplay *display)
47 : m_eglDisplay(display)
48{
49 create(context->format(), kwinApp()->outputBackend()->sceneEglGlobalShareContext());
50}
51
53{
54 if (!m_renderTargets.empty() || !m_zombieRenderTargets.empty()) {
55 m_eglContext->makeCurrent();
56 m_renderTargets.clear();
57 m_zombieRenderTargets.clear();
58 }
59}
60
61bool EGLPlatformContext::makeCurrent(QPlatformSurface *surface)
62{
63 const bool ok = m_eglContext->makeCurrent();
64 if (!ok) {
65 qCWarning(KWIN_QPA, "eglMakeCurrent failed: %x", eglGetError());
66 return false;
67 }
68
69 m_zombieRenderTargets.clear();
70
71 if (surface->surface()->surfaceClass() == QSurface::Window) {
72 // QOpenGLContextPrivate::setCurrentContext will be called after this
73 // method returns, but that's too late, as we need a current context in
74 // order to bind the content framebuffer object.
75 QOpenGLContextPrivate::setCurrentContext(context());
76
77 Window *window = static_cast<Window *>(surface);
78 Swapchain *swapchain = window->swapchain(m_eglDisplay->nonExternalOnlySupportedDrmFormats());
79
80 GraphicsBuffer *buffer = swapchain->acquire();
81 if (!buffer) {
82 return false;
83 }
84
85 auto it = m_renderTargets.find(buffer);
86 if (it != m_renderTargets.end()) {
87 m_current = it->second;
88 } else {
89 std::shared_ptr<GLTexture> texture = m_eglContext->importDmaBufAsTexture(*buffer->dmabufAttributes());
90 if (!texture) {
91 return false;
92 }
93
94 std::unique_ptr<GLFramebuffer> fbo = std::make_unique<GLFramebuffer>(texture.get(), GLFramebuffer::CombinedDepthStencil);
95 if (!fbo->valid()) {
96 return false;
97 }
98
99 auto target = std::make_shared<EGLRenderTarget>(buffer, std::move(fbo), std::move(texture));
100 m_renderTargets[buffer] = target;
101 QObject::connect(buffer, &QObject::destroyed, this, [this, buffer]() {
102 if (auto it = m_renderTargets.find(buffer); it != m_renderTargets.end()) {
103 m_zombieRenderTargets.push_back(std::move(it->second));
104 m_renderTargets.erase(it);
105 }
106 });
107
108 m_current = target;
109 }
110
111 glBindFramebuffer(GL_FRAMEBUFFER, m_current->fbo->handle());
112 }
113
114 return true;
115}
116
118{
119 m_eglContext->doneCurrent();
120}
121
123{
124 return m_eglContext != nullptr;
125}
126
128{
129 return false;
130}
131
132QSurfaceFormat EGLPlatformContext::format() const
133{
134 return m_format;
135}
136
137QFunctionPointer EGLPlatformContext::getProcAddress(const char *procName)
138{
139 return eglGetProcAddress(procName);
140}
141
142void EGLPlatformContext::swapBuffers(QPlatformSurface *surface)
143{
144 if (surface->surface()->surfaceClass() == QSurface::Window) {
145 Window *window = static_cast<Window *>(surface);
146 InternalWindow *internalWindow = window->internalWindow();
147 if (!internalWindow) {
148 return;
149 }
150
151 glFlush(); // We need to flush pending rendering commands manually
152
153 internalWindow->present(InternalWindowFrame{
154 .buffer = m_current->buffer,
155 .bufferDamage = QRect(QPoint(0, 0), m_current->buffer->size()),
156 .bufferOrigin = GraphicsBufferOrigin::BottomLeft,
157 });
158
159 m_current.reset();
160 }
161}
162
163GLuint EGLPlatformContext::defaultFramebufferObject(QPlatformSurface *surface) const
164{
165 if (surface->surface()->surfaceClass() == QSurface::Window) {
166 if (m_current) {
167 return m_current->fbo->handle();
168 }
169 qCDebug(KWIN_QPA) << "No default framebuffer object for internal window";
170 }
171
172 return 0;
173}
174
175void EGLPlatformContext::create(const QSurfaceFormat &format, ::EGLContext shareContext)
176{
177 if (!eglBindAPI(isOpenGLES() ? EGL_OPENGL_ES_API : EGL_OPENGL_API)) {
178 qCWarning(KWIN_QPA, "eglBindAPI failed: 0x%x", eglGetError());
179 return;
180 }
181
182 m_config = configFromFormat(m_eglDisplay, format);
183 if (m_config == EGL_NO_CONFIG_KHR) {
184 qCWarning(KWIN_QPA) << "Could not find suitable EGLConfig for" << format;
185 return;
186 }
187
188 m_format = formatFromConfig(m_eglDisplay, m_config);
189 m_eglContext = EglContext::create(m_eglDisplay, m_config, shareContext);
190 if (!m_eglContext) {
191 qCWarning(KWIN_QPA) << "Failed to create EGL context";
192 return;
193 }
194 updateFormatFromContext();
195}
196
197void EGLPlatformContext::updateFormatFromContext()
198{
199 const EGLSurface oldDrawSurface = eglGetCurrentSurface(EGL_DRAW);
200 const EGLSurface oldReadSurface = eglGetCurrentSurface(EGL_READ);
201 const ::EGLContext oldContext = eglGetCurrentContext();
202
203 m_eglContext->makeCurrent();
204
205 const char *version = reinterpret_cast<const char *>(glGetString(GL_VERSION));
206 int major, minor;
207 if (parseOpenGLVersion(version, major, minor)) {
208 m_format.setMajorVersion(major);
209 m_format.setMinorVersion(minor);
210 } else {
211 qCWarning(KWIN_QPA) << "Unrecognized OpenGL version:" << version;
212 }
213
214 GLint value;
215
216 if (m_format.version() >= qMakePair(3, 0)) {
217 glGetIntegerv(GL_CONTEXT_FLAGS, &value);
218 if (value & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT) {
219 m_format.setOption(QSurfaceFormat::DeprecatedFunctions);
220 }
221 if (value & GL_CONTEXT_FLAG_DEBUG_BIT) {
222 m_format.setOption(QSurfaceFormat::DebugContext);
223 }
224 } else {
225 m_format.setOption(QSurfaceFormat::DeprecatedFunctions);
226 }
227
228 if (m_format.version() >= qMakePair(3, 2)) {
229 glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &value);
230 if (value & GL_CONTEXT_CORE_PROFILE_BIT) {
231 m_format.setProfile(QSurfaceFormat::CoreProfile);
232 } else if (value & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) {
233 m_format.setProfile(QSurfaceFormat::CompatibilityProfile);
234 } else {
235 m_format.setProfile(QSurfaceFormat::NoProfile);
236 }
237 } else {
238 m_format.setProfile(QSurfaceFormat::NoProfile);
239 }
240
241 eglMakeCurrent(m_eglDisplay->handle(), oldDrawSurface, oldReadSurface, oldContext);
242}
243
244} // namespace QPA
245} // namespace KWin
static std::unique_ptr< EglContext > create(EglDisplay *display, EGLConfig config, ::EGLContext sharedContext)
QHash< uint32_t, QList< uint64_t > > nonExternalOnlySupportedDrmFormats() const
::EGLDisplay handle() const
virtual const DmaBufAttributes * dmabufAttributes() const
void present(const InternalWindowFrame &frame)
GLuint defaultFramebufferObject(QPlatformSurface *surface) const override
EGLPlatformContext(QOpenGLContext *context, EglDisplay *display)
QFunctionPointer getProcAddress(const char *procName) override
void swapBuffers(QPlatformSurface *surface) override
bool makeCurrent(QPlatformSurface *surface) override
QSurfaceFormat format() const override
std::unique_ptr< GLFramebuffer > fbo
EGLRenderTarget(GraphicsBuffer *buffer, std::unique_ptr< GLFramebuffer > fbo, std::shared_ptr< GLTexture > texture)
std::shared_ptr< GLTexture > texture
GraphicsBuffer * acquire()
Definition swapchain.cpp:33
InternalWindow * internalWindow() const
Definition window.cpp:135
Swapchain * swapchain(const QHash< uint32_t, QList< uint64_t > > &formats)
Definition window.cpp:45
QSurfaceFormat formatFromConfig(EglDisplay *display, EGLConfig config)
EGLConfig configFromFormat(EglDisplay *display, const QSurfaceFormat &surfaceFormat, EGLint surfaceType)
bool isOpenGLES()
constexpr int version
GLenum format
Definition gltexture.cpp:49