KWin
Loading...
Searching...
No Matches
drm_egl_layer_surface.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: 2022 Xaver Hugl <xaver.hugl@gmail.com>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8*/
10
11#include "config-kwin.h"
14#include "core/iccprofile.h"
15#include "drm_egl_backend.h"
16#include "drm_gpu.h"
17#include "drm_logging.h"
18#include "icc_shader.h"
20#include "opengl/eglswapchain.h"
21#include "opengl/gllut.h"
25
26#include <drm_fourcc.h>
27#include <errno.h>
28#include <gbm.h>
29#include <unistd.h>
30
31namespace KWin
32{
33
34static const QList<uint64_t> linearModifier = {DRM_FORMAT_MOD_LINEAR};
35static const QList<uint64_t> implicitModifier = {DRM_FORMAT_MOD_INVALID};
36static const QList<uint32_t> cpuCopyFormats = {DRM_FORMAT_ARGB8888, DRM_FORMAT_XRGB8888};
37
38static const bool bufferAgeEnabled = qEnvironmentVariable("KWIN_USE_BUFFER_AGE") != QStringLiteral("0");
39
40static gbm_format_name_desc formatName(uint32_t format)
41{
42 gbm_format_name_desc ret;
43 gbm_format_get_name(format, &ret);
44 return ret;
45}
46
48 : m_gpu(gpu)
49 , m_eglBackend(eglBackend)
50 , m_requestedBufferTarget(target)
51 , m_formatOption(formatOption)
52{
53}
54
56
57EglGbmLayerSurface::Surface::~Surface()
58{
59 if (importContext) {
60 importContext->makeCurrent();
61 importGbmSwapchain.reset();
62 importedTextureCache.clear();
63 importContext.reset();
64 }
65 if (context) {
66 context->makeCurrent();
67 }
68}
69
71{
72 m_surface = {};
73 m_oldSurface = {};
74}
75
76std::optional<OutputLayerBeginFrameInfo> EglGbmLayerSurface::startRendering(const QSize &bufferSize, OutputTransform transformation, const QMap<uint32_t, QList<uint64_t>> &formats, const ColorDescription &colorDescription, const QVector3D &channelFactors, const std::shared_ptr<IccProfile> &iccProfile, bool enableColormanagement)
77{
78 if (!checkSurface(bufferSize, formats)) {
79 return std::nullopt;
80 }
81
82 if (!m_eglBackend->contextObject()->makeCurrent()) {
83 return std::nullopt;
84 }
85
86 auto slot = m_surface->gbmSwapchain->acquire();
87 if (!slot) {
88 return std::nullopt;
89 }
90
91 if (slot->framebuffer()->colorAttachment()->contentTransform() != transformation) {
92 m_surface->damageJournal.clear();
93 }
94 slot->framebuffer()->colorAttachment()->setContentTransform(transformation);
95 m_surface->currentSlot = slot;
96
97 if (m_surface->targetColorDescription != colorDescription || m_surface->channelFactors != channelFactors
98 || m_surface->colormanagementEnabled != enableColormanagement || m_surface->iccProfile != iccProfile) {
99 m_surface->damageJournal.clear();
100 m_surface->colormanagementEnabled = enableColormanagement;
101 m_surface->targetColorDescription = colorDescription;
102 m_surface->channelFactors = channelFactors;
103 m_surface->iccProfile = iccProfile;
104 if (iccProfile) {
105 if (!m_surface->iccShader) {
106 m_surface->iccShader = std::make_unique<IccShader>();
107 }
108 } else {
109 m_surface->iccShader.reset();
110 }
111 if (enableColormanagement) {
112 m_surface->intermediaryColorDescription = ColorDescription(colorDescription.colorimetry(), NamedTransferFunction::linear,
116 } else {
117 m_surface->intermediaryColorDescription = colorDescription;
118 }
119 }
120
121 const QRegion repaint = bufferAgeEnabled ? m_surface->damageJournal.accumulate(slot->age(), infiniteRegion()) : infiniteRegion();
122 if (enableColormanagement) {
123 if (!m_surface->shadowBuffer || m_surface->shadowTexture->size() != m_surface->gbmSwapchain->size()) {
124 m_surface->shadowTexture = GLTexture::allocate(GL_RGBA16F, m_surface->gbmSwapchain->size());
125 if (!m_surface->shadowTexture) {
126 return std::nullopt;
127 }
128 m_surface->shadowBuffer = std::make_unique<GLFramebuffer>(m_surface->shadowTexture.get());
129 }
130 m_surface->shadowTexture->setContentTransform(m_surface->currentSlot->framebuffer()->colorAttachment()->contentTransform());
131 m_surface->renderStart = std::chrono::steady_clock::now();
132 m_surface->timeQuery->begin();
134 .renderTarget = RenderTarget(m_surface->shadowBuffer.get(), m_surface->intermediaryColorDescription),
135 .repaint = repaint,
136 };
137 } else {
138 m_surface->shadowTexture.reset();
139 m_surface->shadowBuffer.reset();
140 m_surface->renderStart = std::chrono::steady_clock::now();
141 m_surface->timeQuery->begin();
143 .renderTarget = RenderTarget(m_surface->currentSlot->framebuffer()),
144 .repaint = repaint,
145 };
146 }
147}
148
149bool EglGbmLayerSurface::endRendering(const QRegion &damagedRegion)
150{
151 if (m_surface->colormanagementEnabled) {
152 GLFramebuffer *fbo = m_surface->currentSlot->framebuffer();
154 ShaderBinder binder = m_surface->iccShader ? ShaderBinder(m_surface->iccShader->shader()) : ShaderBinder(ShaderTrait::MapTexture | ShaderTrait::TransformColorspace);
155 if (m_surface->iccShader) {
156 m_surface->iccShader->setUniforms(m_surface->iccProfile, m_surface->intermediaryColorDescription.sdrBrightness(), m_surface->channelFactors);
157 } else {
158 QMatrix4x4 ctm;
159 ctm(0, 0) = m_surface->channelFactors.x();
160 ctm(1, 1) = m_surface->channelFactors.y();
161 ctm(2, 2) = m_surface->channelFactors.z();
163 binder.shader()->setUniform(GLShader::IntUniform::SourceNamedTransferFunction, int(m_surface->intermediaryColorDescription.transferFunction()));
164 binder.shader()->setUniform(GLShader::IntUniform::DestinationNamedTransferFunction, int(m_surface->targetColorDescription.transferFunction()));
165 binder.shader()->setUniform(GLShader::FloatUniform::SdrBrightness, m_surface->intermediaryColorDescription.sdrBrightness());
166 binder.shader()->setUniform(GLShader::FloatUniform::MaxHdrBrightness, m_surface->intermediaryColorDescription.maxHdrHighlightBrightness());
167 }
168 QMatrix4x4 mat;
169 mat.scale(1, -1);
170 mat *= fbo->colorAttachment()->contentTransform().toMatrix();
171 mat.scale(1, -1);
172 mat.ortho(QRectF(QPointF(), fbo->size()));
174 glDisable(GL_BLEND);
175 m_surface->shadowTexture->render(m_surface->gbmSwapchain->size());
177 }
178 m_surface->damageJournal.add(damagedRegion);
179 m_surface->gbmSwapchain->release(m_surface->currentSlot);
180 m_surface->timeQuery->end();
181 glFlush();
182 EGLNativeFence sourceFence(m_eglBackend->eglDisplayObject());
183 if (!sourceFence.isValid()) {
184 // llvmpipe doesn't do synchronization properly: https://gitlab.freedesktop.org/mesa/mesa/-/issues/9375
185 // and NVidia doesn't support implicit sync
186 glFinish();
187 }
188 const auto buffer = importBuffer(m_surface.get(), m_surface->currentSlot.get(), sourceFence.fileDescriptor());
189 m_surface->renderEnd = std::chrono::steady_clock::now();
190 if (buffer) {
191 m_surface->currentFramebuffer = buffer;
192 return true;
193 } else {
194 return false;
195 }
196}
197
198std::chrono::nanoseconds EglGbmLayerSurface::queryRenderTime() const
199{
200 if (!m_surface) {
201 return std::chrono::nanoseconds::zero();
202 }
203 const auto cpuTime = m_surface->renderEnd - m_surface->renderStart;
204 if (m_surface->timeQuery) {
205 m_eglBackend->makeCurrent();
206 auto gpuTime = m_surface->timeQuery->result();
207 if (m_surface->importTimeQuery && m_eglBackend->contextForGpu(m_gpu)->makeCurrent()) {
208 gpuTime += m_surface->importTimeQuery->result();
209 }
210 return std::max(gpuTime, cpuTime);
211 } else {
212 return cpuTime;
213 }
214}
215
217{
218 return m_eglBackend;
219}
220
221std::shared_ptr<DrmFramebuffer> EglGbmLayerSurface::currentBuffer() const
222{
223 return m_surface ? m_surface->currentFramebuffer : nullptr;
224}
225
227{
228 if (m_surface) {
229 return m_surface->shadowTexture ? m_surface->intermediaryColorDescription : m_surface->targetColorDescription;
230 } else {
232 }
233}
234
235bool EglGbmLayerSurface::doesSurfaceFit(const QSize &size, const QMap<uint32_t, QList<uint64_t>> &formats) const
236{
237 return doesSurfaceFit(m_surface.get(), size, formats);
238}
239
240std::shared_ptr<GLTexture> EglGbmLayerSurface::texture() const
241{
242 if (m_surface) {
243 return m_surface->shadowTexture ? m_surface->shadowTexture : m_surface->currentSlot->texture();
244 } else {
245 return nullptr;
246 }
247}
248
249std::shared_ptr<DrmFramebuffer> EglGbmLayerSurface::renderTestBuffer(const QSize &bufferSize, const QMap<uint32_t, QList<uint64_t>> &formats)
250{
251 if (checkSurface(bufferSize, formats)) {
252 return m_surface->currentFramebuffer;
253 } else {
254 return nullptr;
255 }
256}
257
259{
260 if (m_surface) {
261 m_surface->damageJournal.clear();
262 }
263}
264
265bool EglGbmLayerSurface::checkSurface(const QSize &size, const QMap<uint32_t, QList<uint64_t>> &formats)
266{
267 if (doesSurfaceFit(m_surface.get(), size, formats)) {
268 return true;
269 }
270 if (doesSurfaceFit(m_oldSurface.get(), size, formats)) {
271 m_surface = std::move(m_oldSurface);
272 return true;
273 }
274 if (auto newSurface = createSurface(size, formats)) {
275 m_oldSurface = std::move(m_surface);
276 if (m_oldSurface) {
277 m_oldSurface->damageJournal.clear(); // TODO: Use absolute frame sequence numbers for indexing the DamageJournal
278 }
279 m_surface = std::move(newSurface);
280 return true;
281 }
282 return false;
283}
284
285bool EglGbmLayerSurface::doesSurfaceFit(Surface *surface, const QSize &size, const QMap<uint32_t, QList<uint64_t>> &formats) const
286{
287 if (!surface || !surface->gbmSwapchain || surface->gbmSwapchain->size() != size) {
288 return false;
289 }
290 switch (surface->importMode) {
291 case MultiGpuImportMode::None:
292 case MultiGpuImportMode::Dmabuf:
293 case MultiGpuImportMode::LinearDmabuf: {
294 const auto format = surface->gbmSwapchain->format();
295 return formats.contains(format) && (surface->gbmSwapchain->modifier() == DRM_FORMAT_MOD_INVALID || formats[format].contains(surface->gbmSwapchain->modifier()));
296 }
297 case MultiGpuImportMode::DumbBuffer:
298 return formats.contains(surface->importDumbSwapchain->format());
299 case MultiGpuImportMode::Egl: {
300 const auto format = surface->importGbmSwapchain->format();
301 return formats.contains(format) && (surface->importGbmSwapchain->modifier() == DRM_FORMAT_MOD_INVALID || formats[format].contains(surface->importGbmSwapchain->modifier()));
302 }
303 }
304 Q_UNREACHABLE();
305}
306
307std::unique_ptr<EglGbmLayerSurface::Surface> EglGbmLayerSurface::createSurface(const QSize &size, const QMap<uint32_t, QList<uint64_t>> &formats) const
308{
309 QList<FormatInfo> preferredFormats;
310 QList<FormatInfo> fallbackFormats;
311 for (auto it = formats.begin(); it != formats.end(); it++) {
312 const auto format = FormatInfo::get(it.key());
313 if (format.has_value() && format->bitsPerColor >= 8) {
314 if (format->bitsPerPixel <= 32) {
315 preferredFormats.push_back(format.value());
316 } else {
317 fallbackFormats.push_back(format.value());
318 }
319 }
320 }
321
322 // special case: the cursor plane needs linear, but not all GPUs (NVidia) can render to linear
323 auto bufferTarget = m_requestedBufferTarget;
324 if (m_gpu == m_eglBackend->gpu()) {
325 const auto checkSurfaceNeedsLinear = [&formats](const FormatInfo &fmt) {
326 const auto &mods = formats[fmt.drmFormat];
327 return std::all_of(mods.cbegin(), mods.cend(), [](const auto &mod) {
328 return mod == DRM_FORMAT_MOD_LINEAR;
329 });
330 };
331 const bool needsLinear =
332 std::all_of(preferredFormats.cbegin(), preferredFormats.cend(), checkSurfaceNeedsLinear) && std::all_of(fallbackFormats.cbegin(), fallbackFormats.cend(), checkSurfaceNeedsLinear);
333 if (needsLinear) {
334 const auto renderFormats = m_eglBackend->eglDisplayObject()->allSupportedDrmFormats();
335 const auto checkFormatSupportsLinearRender = [&renderFormats](const auto &formatInfo) {
336 const auto it = renderFormats.constFind(formatInfo.drmFormat);
337 return it != renderFormats.cend() && it->nonExternalOnlyModifiers.contains(DRM_FORMAT_MOD_LINEAR);
338 };
339 const bool noLinearSupport =
340 std::none_of(preferredFormats.cbegin(), preferredFormats.cend(), checkFormatSupportsLinearRender) && std::none_of(fallbackFormats.cbegin(), fallbackFormats.cend(), checkFormatSupportsLinearRender);
341 if (noLinearSupport) {
342 bufferTarget = BufferTarget::Dumb;
343 }
344 }
345 }
346
347 const auto sort = [this](const auto &lhs, const auto &rhs) {
348 if (lhs.drmFormat == rhs.drmFormat) {
349 // prefer having an alpha channel
350 return lhs.alphaBits > rhs.alphaBits;
351 } else if (m_eglBackend->prefer10bpc() && ((lhs.bitsPerColor == 10) != (rhs.bitsPerColor == 10))) {
352 // prefer 10bpc / 30bpp formats
353 return lhs.bitsPerColor == 10;
354 } else {
355 // fallback: prefer formats with lower bandwidth requirements
356 return lhs.bitsPerPixel < rhs.bitsPerPixel;
357 }
358 };
359 const auto doTestFormats = [this, &size, &formats, bufferTarget](const QList<FormatInfo> &gbmFormats, MultiGpuImportMode importMode) -> std::unique_ptr<Surface> {
360 for (const auto &format : gbmFormats) {
361 if (m_formatOption == FormatOption::RequireAlpha && format.alphaBits == 0) {
362 continue;
363 }
364 auto surface = createSurface(size, format.drmFormat, formats[format.drmFormat], importMode, bufferTarget);
365 if (surface) {
366 return surface;
367 }
368 }
369 return nullptr;
370 };
371 const auto testFormats = [this, &sort, &doTestFormats](QList<FormatInfo> &formats) -> std::unique_ptr<Surface> {
372 std::sort(formats.begin(), formats.end(), sort);
373 if (m_gpu == m_eglBackend->gpu()) {
374 return doTestFormats(formats, MultiGpuImportMode::None);
375 }
376 if (auto surface = doTestFormats(formats, MultiGpuImportMode::Egl)) {
377 qCDebug(KWIN_DRM) << "chose egl import with format" << formatName(surface->gbmSwapchain->format()).name << "and modifier" << surface->gbmSwapchain->modifier();
378 return surface;
379 }
380 if (auto surface = doTestFormats(formats, MultiGpuImportMode::Dmabuf)) {
381 qCDebug(KWIN_DRM) << "chose dmabuf import with format" << formatName(surface->gbmSwapchain->format()).name << "and modifier" << surface->gbmSwapchain->modifier();
382 return surface;
383 }
384 if (auto surface = doTestFormats(formats, MultiGpuImportMode::LinearDmabuf)) {
385 qCDebug(KWIN_DRM) << "chose linear dmabuf import with format" << formatName(surface->gbmSwapchain->format()).name << "and modifier" << surface->gbmSwapchain->modifier();
386 return surface;
387 }
388 if (auto surface = doTestFormats(formats, MultiGpuImportMode::DumbBuffer)) {
389 qCDebug(KWIN_DRM) << "chose cpu import with format" << formatName(surface->gbmSwapchain->format()).name << "and modifier" << surface->gbmSwapchain->modifier();
390 return surface;
391 }
392 return nullptr;
393 };
394 if (auto ret = testFormats(preferredFormats)) {
395 return ret;
396 } else if (auto ret = testFormats(fallbackFormats)) {
397 return ret;
398 } else {
399 return nullptr;
400 }
401}
402
403static QList<uint64_t> filterModifiers(const QList<uint64_t> &one, const QList<uint64_t> &two)
404{
405 QList<uint64_t> ret = one;
406 ret.erase(std::remove_if(ret.begin(), ret.end(), [&two](uint64_t mod) {
407 return !two.contains(mod);
408 }),
409 ret.end());
410 return ret;
411}
412
413std::unique_ptr<EglGbmLayerSurface::Surface> EglGbmLayerSurface::createSurface(const QSize &size, uint32_t format, const QList<uint64_t> &modifiers, MultiGpuImportMode importMode, BufferTarget bufferTarget) const
414{
415 const bool cpuCopy = importMode == MultiGpuImportMode::DumbBuffer || bufferTarget == BufferTarget::Dumb;
416 QList<uint64_t> renderModifiers;
417 auto ret = std::make_unique<Surface>();
418 const auto drmFormat = m_eglBackend->eglDisplayObject()->allSupportedDrmFormats()[format];
419 if (importMode == MultiGpuImportMode::Egl) {
420 ret->importContext = m_eglBackend->contextForGpu(m_gpu);
421 if (!ret->importContext || ret->importContext->isSoftwareRenderer()) {
422 return nullptr;
423 }
424 const auto importDrmFormat = ret->importContext->displayObject()->allSupportedDrmFormats()[format];
425 renderModifiers = filterModifiers(importDrmFormat.allModifiers,
426 drmFormat.nonExternalOnlyModifiers);
427 // transferring non-linear buffers with implicit modifiers between GPUs is likely to yield wrong results
428 renderModifiers.removeAll(DRM_FORMAT_MOD_INVALID);
429 } else if (cpuCopy) {
430 if (!cpuCopyFormats.contains(format)) {
431 return nullptr;
432 }
433 renderModifiers = drmFormat.nonExternalOnlyModifiers;
434 } else {
435 renderModifiers = filterModifiers(modifiers, drmFormat.nonExternalOnlyModifiers);
436 }
437 if (renderModifiers.empty()) {
438 return nullptr;
439 }
440 ret->context = m_eglBackend->contextForGpu(m_eglBackend->gpu());
441 ret->bufferTarget = bufferTarget;
442 ret->importMode = importMode;
443 ret->gbmSwapchain = createGbmSwapchain(m_eglBackend->gpu(), m_eglBackend->contextObject(), size, format, renderModifiers, importMode, bufferTarget);
444 if (!ret->gbmSwapchain) {
445 return nullptr;
446 }
447 if (cpuCopy) {
448 ret->importDumbSwapchain = std::make_unique<QPainterSwapchain>(m_gpu->graphicsBufferAllocator(), size, format);
449 } else if (importMode == MultiGpuImportMode::Egl) {
450 ret->importGbmSwapchain = createGbmSwapchain(m_gpu, ret->importContext.get(), size, format, modifiers, MultiGpuImportMode::None, BufferTarget::Normal);
451 if (!ret->importGbmSwapchain) {
452 return nullptr;
453 }
454 ret->importTimeQuery = std::make_unique<GLRenderTimeQuery>();
455 }
456 ret->timeQuery = std::make_unique<GLRenderTimeQuery>();
457 if (!doRenderTestBuffer(ret.get())) {
458 return nullptr;
459 }
460 return ret;
461}
462
463std::shared_ptr<EglSwapchain> EglGbmLayerSurface::createGbmSwapchain(DrmGpu *gpu, EglContext *context, const QSize &size, uint32_t format, const QList<uint64_t> &modifiers, MultiGpuImportMode importMode, BufferTarget bufferTarget) const
464{
465 static bool modifiersEnvSet = false;
466 static const bool modifiersEnv = qEnvironmentVariableIntValue("KWIN_DRM_USE_MODIFIERS", &modifiersEnvSet) != 0;
467 bool allowModifiers = (m_gpu->addFB2ModifiersSupported() || importMode == MultiGpuImportMode::Egl || importMode == MultiGpuImportMode::DumbBuffer) && (!modifiersEnvSet || (modifiersEnvSet && modifiersEnv)) && modifiers != implicitModifier;
468#if !HAVE_GBM_BO_GET_FD_FOR_PLANE
469 allowModifiers &= m_gpu == gpu;
470#endif
471 const bool linearSupported = modifiers.contains(DRM_FORMAT_MOD_LINEAR);
472 const bool preferLinear = importMode == MultiGpuImportMode::DumbBuffer || bufferTarget == BufferTarget::Linear;
473 const bool forceLinear = importMode == MultiGpuImportMode::LinearDmabuf || (importMode != MultiGpuImportMode::None && importMode != MultiGpuImportMode::DumbBuffer && !allowModifiers);
474 if (forceLinear && !linearSupported) {
475 return nullptr;
476 }
477 if (linearSupported && (preferLinear || forceLinear)) {
478 if (const auto swapchain = EglSwapchain::create(gpu->graphicsBufferAllocator(), context, size, format, linearModifier)) {
479 return swapchain;
480 } else if (forceLinear) {
481 return nullptr;
482 }
483 }
484
485 if (allowModifiers) {
486 if (auto swapchain = EglSwapchain::create(gpu->graphicsBufferAllocator(), context, size, format, modifiers)) {
487 return swapchain;
488 }
489 }
490
491 return EglSwapchain::create(gpu->graphicsBufferAllocator(), context, size, format, implicitModifier);
492}
493
494std::shared_ptr<DrmFramebuffer> EglGbmLayerSurface::doRenderTestBuffer(Surface *surface) const
495{
496 auto slot = surface->gbmSwapchain->acquire();
497 if (!slot) {
498 return nullptr;
499 }
500 if (const auto ret = importBuffer(surface, slot.get(), FileDescriptor{})) {
501 surface->currentSlot = slot;
502 surface->currentFramebuffer = ret;
503 return ret;
504 } else {
505 return nullptr;
506 }
507}
508
509std::shared_ptr<DrmFramebuffer> EglGbmLayerSurface::importBuffer(Surface *surface, EglSwapchainSlot *slot, const FileDescriptor &readFence) const
510{
511 if (surface->bufferTarget == BufferTarget::Dumb || surface->importMode == MultiGpuImportMode::DumbBuffer) {
512 return importWithCpu(surface, slot);
513 } else if (surface->importMode == MultiGpuImportMode::Egl) {
514 return importWithEgl(surface, slot->buffer(), readFence);
515 } else {
516 const auto ret = m_gpu->importBuffer(slot->buffer(), readFence.duplicate());
517 if (!ret) {
518 qCWarning(KWIN_DRM, "Failed to create framebuffer: %s", strerror(errno));
519 }
520 return ret;
521 }
522}
523
524std::shared_ptr<DrmFramebuffer> EglGbmLayerSurface::importWithEgl(Surface *surface, GraphicsBuffer *sourceBuffer, const FileDescriptor &readFence) const
525{
526 Q_ASSERT(surface->importGbmSwapchain);
527
528 const auto display = m_eglBackend->displayForGpu(m_gpu);
529 // older versions of the NVidia proprietary driver support neither implicit sync nor EGL_ANDROID_native_fence_sync
530 if (!readFence.isValid() || !display->supportsNativeFence()) {
531 glFinish();
532 }
533
534 if (!surface->importContext->makeCurrent()) {
535 return nullptr;
536 }
537 surface->importTimeQuery->begin();
538
539 if (readFence.isValid()) {
540 const auto destinationFence = EGLNativeFence::importFence(surface->importContext->displayObject(), readFence.duplicate());
541 destinationFence.waitSync();
542 }
543
544 auto &sourceTexture = surface->importedTextureCache[sourceBuffer];
545 if (!sourceTexture) {
546 sourceTexture = surface->importContext->importDmaBufAsTexture(*sourceBuffer->dmabufAttributes());
547 }
548 if (!sourceTexture) {
549 qCWarning(KWIN_DRM, "failed to import the source texture!");
550 return nullptr;
551 }
552 auto slot = surface->importGbmSwapchain->acquire();
553 if (!slot) {
554 qCWarning(KWIN_DRM, "failed to import the local texture!");
555 return nullptr;
556 }
557
558 GLFramebuffer *fbo = slot->framebuffer();
559 glBindFramebuffer(GL_FRAMEBUFFER, fbo->handle());
560 glViewport(0, 0, fbo->size().width(), fbo->size().height());
561
562 const auto shader = surface->importContext->shaderManager()->pushShader(sourceTexture->target() == GL_TEXTURE_EXTERNAL_OES ? ShaderTrait::MapExternalTexture : ShaderTrait::MapTexture);
563 QMatrix4x4 mat;
564 mat.scale(1, -1);
565 mat.ortho(QRect(QPoint(), fbo->size()));
567
568 sourceTexture->bind();
569 sourceTexture->render(fbo->size());
570 sourceTexture->unbind();
571
572 glBindFramebuffer(GL_FRAMEBUFFER, 0);
573
574 surface->importContext->shaderManager()->popShader();
575 glFlush();
576 EGLNativeFence endFence(display);
577 if (!endFence.isValid()) {
578 glFinish();
579 }
580 surface->importGbmSwapchain->release(slot);
581 surface->importTimeQuery->end();
582
583 // restore the old context
584 m_eglBackend->makeCurrent();
585 return m_gpu->importBuffer(slot->buffer(), endFence.fileDescriptor().duplicate());
586}
587
588std::shared_ptr<DrmFramebuffer> EglGbmLayerSurface::importWithCpu(Surface *surface, EglSwapchainSlot *source) const
589{
590 Q_ASSERT(surface->importDumbSwapchain);
591 const auto slot = surface->importDumbSwapchain->acquire();
592 if (!slot) {
593 qCWarning(KWIN_DRM) << "EglGbmLayerSurface::importWithCpu: failed to get a target dumb buffer";
594 return nullptr;
595 }
596 const auto size = source->buffer()->size();
597 const qsizetype srcStride = 4 * size.width();
598 GLFramebuffer::pushFramebuffer(source->framebuffer());
599 QImage *const dst = slot->view()->image();
600 if (dst->bytesPerLine() == srcStride) {
601 glReadPixels(0, 0, dst->width(), dst->height(), GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, dst->bits());
602 } else {
603 // there's padding, need to copy line by line
604 if (surface->cpuCopyCache.size() != dst->size()) {
605 surface->cpuCopyCache = QImage(dst->size(), QImage::Format_RGBA8888);
606 }
607 glReadPixels(0, 0, dst->width(), dst->height(), GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, surface->cpuCopyCache.bits());
608 for (int i = 0; i < dst->height(); i++) {
609 std::memcpy(dst->scanLine(i), surface->cpuCopyCache.scanLine(i), srcStride);
610 }
611 }
613
614 const auto ret = m_gpu->importBuffer(slot->buffer(), FileDescriptor{});
615 if (!ret) {
616 qCWarning(KWIN_DRM, "Failed to create a framebuffer: %s", strerror(errno));
617 }
618 surface->importDumbSwapchain->release(slot);
619 return ret;
620}
621}
622
623#include "moc_drm_egl_layer_surface.cpp"
EglDisplay * eglDisplayObject() const
double minHdrBrightness() const
static const ColorDescription sRGB
Definition colorspace.h:132
double maxHdrHighlightBrightness() const
const Colorimetry & colorimetry() const
double maxFrameAverageBrightness() const
const Colorimetry & sdrColorimetry() const
double sdrBrightness() const
bool addFB2ModifiersSupported() const
Definition drm_gpu.cpp:683
std::shared_ptr< DrmFramebuffer > importBuffer(GraphicsBuffer *buffer, FileDescriptor &&explicitFence)
Definition drm_gpu.cpp:839
GraphicsBufferAllocator * graphicsBufferAllocator() const
Definition drm_gpu.cpp:834
static EGLNativeFence importFence(EglDisplay *display, FileDescriptor &&fd)
const FileDescriptor & fileDescriptor() const
bool makeCurrent(EGLSurface surface=EGL_NO_SURFACE) const
QHash< uint32_t, DrmFormatInfo > allSupportedDrmFormats() const
OpenGL Backend using Egl on a GBM surface.
EglDisplay * displayForGpu(DrmGpu *gpu)
bool prefer10bpc() const override
std::shared_ptr< EglContext > contextForGpu(DrmGpu *gpu)
std::optional< OutputLayerBeginFrameInfo > startRendering(const QSize &bufferSize, OutputTransform transformation, const QMap< uint32_t, QList< uint64_t > > &formats, const ColorDescription &colorDescription, const QVector3D &channelFactors, const std::shared_ptr< IccProfile > &iccProfile, bool enableColormanagement)
std::shared_ptr< GLTexture > texture() const
std::chrono::nanoseconds queryRenderTime() const
EglGbmBackend * eglBackend() const
EglGbmLayerSurface(DrmGpu *gpu, EglGbmBackend *eglBackend, BufferTarget target=BufferTarget::Normal, FormatOption formatOption=FormatOption::PreferAlpha)
const ColorDescription & colorDescription() const
bool endRendering(const QRegion &damagedRegion)
bool doesSurfaceFit(const QSize &size, const QMap< uint32_t, QList< uint64_t > > &formats) const
std::shared_ptr< DrmFramebuffer > currentBuffer() const
std::shared_ptr< DrmFramebuffer > renderTestBuffer(const QSize &bufferSize, const QMap< uint32_t, QList< uint64_t > > &formats)
static std::shared_ptr< EglSwapchain > create(GraphicsBufferAllocator *allocator, EglContext *context, const QSize &size, uint32_t format, const QList< uint64_t > &modifiers)
OpenGL framebuffer object.
QSize size() const
static GLFramebuffer * popFramebuffer()
static void pushFramebuffer(GLFramebuffer *fbo)
GLTexture * colorAttachment() const
bool setUniform(const char *name, float value)
Definition glshader.cpp:301
static std::unique_ptr< GLTexture > allocate(GLenum internalFormat, const QSize &size, int levels=1)
OutputTransform contentTransform() const
QMatrix4x4 toMatrix() const
Definition output.cpp:300
KWIN_EXPORT QRect infiniteRegion()
Definition globals.h:234
GLenum format
Definition gltexture.cpp:49
static std::optional< FormatInfo > get(uint32_t drmFormat)