File indexing completed on 2024-11-10 04:56:33
0001 /* 0002 KWin - the KDE window manager 0003 This file is part of the KDE project. 0004 0005 SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com> 0006 SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org> 0007 0008 SPDX-License-Identifier: GPL-2.0-or-later 0009 */ 0010 0011 #include "wayland_egl_backend.h" 0012 #include "core/gbmgraphicsbufferallocator.h" 0013 #include "opengl/eglswapchain.h" 0014 #include "opengl/glrendertimequery.h" 0015 #include "opengl/glutils.h" 0016 #include "platformsupport/scenes/opengl/basiceglsurfacetexture_wayland.h" 0017 #include "wayland_backend.h" 0018 #include "wayland_display.h" 0019 #include "wayland_logging.h" 0020 #include "wayland_output.h" 0021 0022 #include <KWayland/Client/surface.h> 0023 0024 #include <cmath> 0025 #include <drm_fourcc.h> 0026 #include <fcntl.h> 0027 #include <unistd.h> 0028 0029 namespace KWin 0030 { 0031 namespace Wayland 0032 { 0033 0034 static const bool bufferAgeEnabled = qEnvironmentVariable("KWIN_USE_BUFFER_AGE") != QStringLiteral("0"); 0035 0036 WaylandEglPrimaryLayer::WaylandEglPrimaryLayer(WaylandOutput *output, WaylandEglBackend *backend) 0037 : m_waylandOutput(output) 0038 , m_backend(backend) 0039 { 0040 } 0041 0042 WaylandEglPrimaryLayer::~WaylandEglPrimaryLayer() 0043 { 0044 } 0045 0046 GLFramebuffer *WaylandEglPrimaryLayer::fbo() const 0047 { 0048 return m_buffer->framebuffer(); 0049 } 0050 0051 std::shared_ptr<GLTexture> WaylandEglPrimaryLayer::texture() const 0052 { 0053 return m_buffer->texture(); 0054 } 0055 0056 std::optional<OutputLayerBeginFrameInfo> WaylandEglPrimaryLayer::beginFrame() 0057 { 0058 if (!m_backend->contextObject()->makeCurrent()) { 0059 qCCritical(KWIN_WAYLAND_BACKEND) << "Make Context Current failed"; 0060 return std::nullopt; 0061 } 0062 0063 const QSize nativeSize = m_waylandOutput->modeSize(); 0064 if (!m_swapchain || m_swapchain->size() != nativeSize) { 0065 const QHash<uint32_t, QList<uint64_t>> formatTable = m_backend->backend()->display()->linuxDmabuf()->formats(); 0066 uint32_t format = DRM_FORMAT_INVALID; 0067 QList<uint64_t> modifiers; 0068 for (const uint32_t &candidateFormat : {DRM_FORMAT_XRGB2101010, DRM_FORMAT_XRGB8888}) { 0069 auto it = formatTable.constFind(candidateFormat); 0070 if (it != formatTable.constEnd()) { 0071 format = it.key(); 0072 modifiers = it.value(); 0073 break; 0074 } 0075 } 0076 if (format == DRM_FORMAT_INVALID) { 0077 qCWarning(KWIN_WAYLAND_BACKEND) << "Could not find a suitable render format"; 0078 return std::nullopt; 0079 } 0080 m_swapchain = EglSwapchain::create(m_backend->graphicsBufferAllocator(), m_backend->contextObject(), nativeSize, format, modifiers); 0081 if (!m_swapchain) { 0082 return std::nullopt; 0083 } 0084 } 0085 0086 m_buffer = m_swapchain->acquire(); 0087 if (!m_buffer) { 0088 return std::nullopt; 0089 } 0090 0091 const QRegion repair = bufferAgeEnabled ? m_damageJournal.accumulate(m_buffer->age(), infiniteRegion()) : infiniteRegion(); 0092 if (!m_query) { 0093 m_query = std::make_unique<GLRenderTimeQuery>(); 0094 } 0095 m_query->begin(); 0096 return OutputLayerBeginFrameInfo{ 0097 .renderTarget = RenderTarget(m_buffer->framebuffer()), 0098 .repaint = repair, 0099 }; 0100 } 0101 0102 bool WaylandEglPrimaryLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) 0103 { 0104 m_query->end(); 0105 // Flush rendering commands to the dmabuf. 0106 glFlush(); 0107 0108 m_damageJournal.add(damagedRegion); 0109 return true; 0110 } 0111 0112 void WaylandEglPrimaryLayer::present() 0113 { 0114 wl_buffer *buffer = m_backend->backend()->importBuffer(m_buffer->buffer()); 0115 Q_ASSERT(buffer); 0116 0117 KWayland::Client::Surface *surface = m_waylandOutput->surface(); 0118 surface->attachBuffer(buffer); 0119 surface->damage(m_damageJournal.lastDamage()); 0120 surface->setScale(std::ceil(m_waylandOutput->scale())); 0121 surface->commit(); 0122 Q_EMIT m_waylandOutput->outputChange(m_damageJournal.lastDamage()); 0123 0124 m_swapchain->release(m_buffer); 0125 } 0126 0127 std::chrono::nanoseconds WaylandEglPrimaryLayer::queryRenderTime() const 0128 { 0129 m_backend->makeCurrent(); 0130 return m_query->result(); 0131 } 0132 0133 WaylandEglCursorLayer::WaylandEglCursorLayer(WaylandOutput *output, WaylandEglBackend *backend) 0134 : m_output(output) 0135 , m_backend(backend) 0136 { 0137 } 0138 0139 WaylandEglCursorLayer::~WaylandEglCursorLayer() 0140 { 0141 m_backend->contextObject()->makeCurrent(); 0142 } 0143 0144 std::optional<OutputLayerBeginFrameInfo> WaylandEglCursorLayer::beginFrame() 0145 { 0146 if (!m_backend->contextObject()->makeCurrent()) { 0147 qCCritical(KWIN_WAYLAND_BACKEND) << "Make Context Current failed"; 0148 return std::nullopt; 0149 } 0150 0151 const auto tmp = size().expandedTo(QSize(64, 64)); 0152 const QSize bufferSize(std::ceil(tmp.width()), std::ceil(tmp.height())); 0153 if (!m_swapchain || m_swapchain->size() != bufferSize) { 0154 const QHash<uint32_t, QList<uint64_t>> formatTable = m_backend->backend()->display()->linuxDmabuf()->formats(); 0155 uint32_t format = DRM_FORMAT_INVALID; 0156 QList<uint64_t> modifiers; 0157 for (const uint32_t &candidateFormat : {DRM_FORMAT_ARGB2101010, DRM_FORMAT_ARGB8888}) { 0158 auto it = formatTable.constFind(candidateFormat); 0159 if (it != formatTable.constEnd()) { 0160 format = it.key(); 0161 modifiers = it.value(); 0162 break; 0163 } 0164 } 0165 if (format == DRM_FORMAT_INVALID) { 0166 qCWarning(KWIN_WAYLAND_BACKEND) << "Could not find a suitable render format"; 0167 return std::nullopt; 0168 } 0169 m_swapchain = EglSwapchain::create(m_backend->graphicsBufferAllocator(), m_backend->contextObject(), bufferSize, format, modifiers); 0170 } 0171 0172 m_buffer = m_swapchain->acquire(); 0173 if (!m_query) { 0174 m_query = std::make_unique<GLRenderTimeQuery>(); 0175 } 0176 m_query->begin(); 0177 return OutputLayerBeginFrameInfo{ 0178 .renderTarget = RenderTarget(m_buffer->framebuffer()), 0179 .repaint = infiniteRegion(), 0180 }; 0181 } 0182 0183 bool WaylandEglCursorLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) 0184 { 0185 m_query->end(); 0186 // Flush rendering commands to the dmabuf. 0187 glFlush(); 0188 0189 wl_buffer *buffer = m_backend->backend()->importBuffer(m_buffer->buffer()); 0190 Q_ASSERT(buffer); 0191 0192 m_output->cursor()->update(buffer, scale(), hotspot().toPoint()); 0193 0194 m_swapchain->release(m_buffer); 0195 return true; 0196 } 0197 0198 std::chrono::nanoseconds WaylandEglCursorLayer::queryRenderTime() const 0199 { 0200 m_backend->makeCurrent(); 0201 return m_query->result(); 0202 } 0203 0204 WaylandEglBackend::WaylandEglBackend(WaylandBackend *b) 0205 : AbstractEglBackend() 0206 , m_backend(b) 0207 , m_allocator(std::make_unique<GbmGraphicsBufferAllocator>(b->gbmDevice())) 0208 { 0209 connect(m_backend, &WaylandBackend::outputAdded, this, &WaylandEglBackend::createEglWaylandOutput); 0210 connect(m_backend, &WaylandBackend::outputRemoved, this, [this](Output *output) { 0211 m_outputs.erase(output); 0212 }); 0213 0214 b->setEglBackend(this); 0215 } 0216 0217 WaylandEglBackend::~WaylandEglBackend() 0218 { 0219 cleanup(); 0220 } 0221 0222 WaylandBackend *WaylandEglBackend::backend() const 0223 { 0224 return m_backend; 0225 } 0226 0227 GraphicsBufferAllocator *WaylandEglBackend::graphicsBufferAllocator() const 0228 { 0229 return m_allocator.get(); 0230 } 0231 0232 void WaylandEglBackend::cleanupSurfaces() 0233 { 0234 m_outputs.clear(); 0235 } 0236 0237 bool WaylandEglBackend::createEglWaylandOutput(Output *waylandOutput) 0238 { 0239 m_outputs[waylandOutput] = Layers{ 0240 .primaryLayer = std::make_unique<WaylandEglPrimaryLayer>(static_cast<WaylandOutput *>(waylandOutput), this), 0241 .cursorLayer = std::make_unique<WaylandEglCursorLayer>(static_cast<WaylandOutput *>(waylandOutput), this), 0242 }; 0243 return true; 0244 } 0245 0246 bool WaylandEglBackend::initializeEgl() 0247 { 0248 initClientExtensions(); 0249 0250 if (!m_backend->sceneEglDisplayObject()) { 0251 for (const QByteArray &extension : {QByteArrayLiteral("EGL_EXT_platform_base"), QByteArrayLiteral("EGL_KHR_platform_gbm")}) { 0252 if (!hasClientExtension(extension)) { 0253 qCWarning(KWIN_WAYLAND_BACKEND) << extension << "client extension is not supported by the platform"; 0254 return false; 0255 } 0256 } 0257 0258 m_backend->setEglDisplay(EglDisplay::create(eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_KHR, m_backend->gbmDevice(), nullptr))); 0259 } 0260 0261 auto display = m_backend->sceneEglDisplayObject(); 0262 if (!display) { 0263 return false; 0264 } 0265 setEglDisplay(display); 0266 return true; 0267 } 0268 0269 void WaylandEglBackend::init() 0270 { 0271 if (!initializeEgl()) { 0272 setFailed("Could not initialize egl"); 0273 return; 0274 } 0275 if (!initRenderingContext()) { 0276 setFailed("Could not initialize rendering context"); 0277 return; 0278 } 0279 0280 initKWinGL(); 0281 initWayland(); 0282 } 0283 0284 bool WaylandEglBackend::initRenderingContext() 0285 { 0286 if (!createContext(EGL_NO_CONFIG_KHR)) { 0287 return false; 0288 } 0289 0290 auto waylandOutputs = m_backend->waylandOutputs(); 0291 0292 // we only allow to start with at least one output 0293 if (waylandOutputs.isEmpty()) { 0294 return false; 0295 } 0296 0297 for (auto *out : waylandOutputs) { 0298 if (!createEglWaylandOutput(out)) { 0299 return false; 0300 } 0301 } 0302 0303 if (m_outputs.empty()) { 0304 qCCritical(KWIN_WAYLAND_BACKEND) << "Create Window Surfaces failed"; 0305 return false; 0306 } 0307 0308 return makeCurrent(); 0309 } 0310 0311 std::pair<std::shared_ptr<KWin::GLTexture>, ColorDescription> WaylandEglBackend::textureForOutput(KWin::Output *output) const 0312 { 0313 return std::make_pair(m_outputs.at(output).primaryLayer->texture(), ColorDescription::sRGB); 0314 } 0315 0316 std::unique_ptr<SurfaceTexture> WaylandEglBackend::createSurfaceTextureWayland(SurfacePixmap *pixmap) 0317 { 0318 return std::make_unique<BasicEGLSurfaceTextureWayland>(this, pixmap); 0319 } 0320 0321 void WaylandEglBackend::present(Output *output, const std::shared_ptr<OutputFrame> &frame) 0322 { 0323 m_outputs[output].primaryLayer->present(); 0324 static_cast<WaylandOutput *>(output)->framePending(frame); 0325 } 0326 0327 OutputLayer *WaylandEglBackend::primaryLayer(Output *output) 0328 { 0329 return m_outputs[output].primaryLayer.get(); 0330 } 0331 0332 OutputLayer *WaylandEglBackend::cursorLayer(Output *output) 0333 { 0334 return m_outputs[output].cursorLayer.get(); 0335 } 0336 0337 } 0338 } 0339 0340 #include "moc_wayland_egl_backend.cpp"