2020-08-02 22:22:19 +00:00
|
|
|
/*
|
|
|
|
KWin - the KDE window manager
|
|
|
|
This file is part of the KDE project.
|
2015-04-10 08:44:07 +00:00
|
|
|
|
2020-08-02 22:22:19 +00:00
|
|
|
SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
|
2015-04-10 08:44:07 +00:00
|
|
|
|
2020-08-02 22:22:19 +00:00
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
*/
|
2015-04-10 08:44:07 +00:00
|
|
|
#include "egl_gbm_backend.h"
|
2021-04-09 07:06:04 +00:00
|
|
|
#include "basiceglsurfacetexture_internal.h"
|
|
|
|
#include "basiceglsurfacetexture_wayland.h"
|
2015-04-10 08:44:07 +00:00
|
|
|
// kwin
|
|
|
|
#include "composite.h"
|
|
|
|
#include "drm_backend.h"
|
2021-04-28 19:12:11 +00:00
|
|
|
#include "drm_buffer_gbm.h"
|
2016-03-21 14:11:17 +00:00
|
|
|
#include "drm_output.h"
|
2017-10-05 16:58:57 +00:00
|
|
|
#include "gbm_surface.h"
|
2015-05-05 15:27:03 +00:00
|
|
|
#include "logging.h"
|
2015-04-10 08:44:07 +00:00
|
|
|
#include "options.h"
|
2020-11-29 11:24:38 +00:00
|
|
|
#include "renderloop_p.h"
|
2015-04-10 08:44:07 +00:00
|
|
|
#include "screens.h"
|
2021-02-04 08:40:20 +00:00
|
|
|
#include "surfaceitem_wayland.h"
|
2020-10-05 21:05:55 +00:00
|
|
|
#include "drm_gpu.h"
|
2021-02-02 13:26:43 +00:00
|
|
|
#include "linux_dmabuf.h"
|
2021-05-04 10:19:31 +00:00
|
|
|
#include "dumb_swapchain.h"
|
2021-06-20 15:36:15 +00:00
|
|
|
#include "kwineglutils_p.h"
|
2021-06-22 00:23:17 +00:00
|
|
|
#include "shadowbuffer.h"
|
2021-05-25 22:05:17 +00:00
|
|
|
#include "drm_pipeline.h"
|
2021-11-10 12:26:34 +00:00
|
|
|
#include "drm_abstract_output.h"
|
2015-04-10 08:44:07 +00:00
|
|
|
// kwin libs
|
|
|
|
#include <kwinglplatform.h>
|
2020-07-22 17:38:57 +00:00
|
|
|
#include <kwineglimagetexture.h>
|
2015-04-10 08:44:07 +00:00
|
|
|
// system
|
|
|
|
#include <gbm.h>
|
2020-11-28 17:53:41 +00:00
|
|
|
#include <unistd.h>
|
2020-11-29 18:31:49 +00:00
|
|
|
#include <errno.h>
|
2021-05-30 23:58:47 +00:00
|
|
|
#include <drm_fourcc.h>
|
2021-02-02 13:26:43 +00:00
|
|
|
// kwayland server
|
|
|
|
#include "KWaylandServer/surface_interface.h"
|
2021-07-20 19:37:03 +00:00
|
|
|
#include "KWaylandServer/linuxdmabufv1clientbuffer.h"
|
2021-05-30 23:58:47 +00:00
|
|
|
#include "KWaylandServer/clientconnection.h"
|
2015-04-10 08:44:07 +00:00
|
|
|
|
|
|
|
namespace KWin
|
|
|
|
{
|
|
|
|
|
2020-10-05 21:05:55 +00:00
|
|
|
EglGbmBackend::EglGbmBackend(DrmBackend *drmBackend, DrmGpu *gpu)
|
2021-11-24 08:16:47 +00:00
|
|
|
: AbstractEglBackend(gpu->deviceId())
|
|
|
|
, m_backend(drmBackend)
|
2021-11-10 12:26:34 +00:00
|
|
|
, m_gpu(gpu)
|
2015-04-10 08:44:07 +00:00
|
|
|
{
|
2021-11-10 12:26:34 +00:00
|
|
|
m_gpu->setEglBackend(this);
|
|
|
|
connect(m_gpu, &DrmGpu::outputEnabled, this, &EglGbmBackend::addOutput);
|
|
|
|
connect(m_gpu, &DrmGpu::outputDisabled, this, &EglGbmBackend::removeOutput);
|
|
|
|
setIsDirectRendering(true);
|
2015-04-10 08:44:07 +00:00
|
|
|
}
|
|
|
|
|
2021-04-19 14:23:46 +00:00
|
|
|
EglGbmBackend::~EglGbmBackend()
|
|
|
|
{
|
|
|
|
cleanup();
|
|
|
|
}
|
|
|
|
|
2015-04-17 13:48:55 +00:00
|
|
|
void EglGbmBackend::cleanupSurfaces()
|
|
|
|
{
|
2021-05-25 17:25:55 +00:00
|
|
|
// shadow buffer needs context current for destruction
|
|
|
|
makeCurrent();
|
2017-10-05 16:58:57 +00:00
|
|
|
m_outputs.clear();
|
2015-04-24 08:05:38 +00:00
|
|
|
}
|
|
|
|
|
2021-05-25 22:05:17 +00:00
|
|
|
void EglGbmBackend::cleanupRenderData(Output::RenderData &render)
|
|
|
|
{
|
|
|
|
render.gbmSurface = nullptr;
|
|
|
|
render.importSwapchain = nullptr;
|
|
|
|
if (render.shadowBuffer) {
|
|
|
|
makeContextCurrent(render);
|
|
|
|
render.shadowBuffer = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-10 08:44:07 +00:00
|
|
|
bool EglGbmBackend::initializeEgl()
|
|
|
|
{
|
|
|
|
initClientExtensions();
|
2020-10-16 13:11:23 +00:00
|
|
|
EGLDisplay display = m_gpu->eglDisplay();
|
2015-04-10 08:44:07 +00:00
|
|
|
|
|
|
|
// Use eglGetPlatformDisplayEXT() to get the display pointer
|
|
|
|
// if the implementation supports it.
|
2016-07-18 08:27:56 +00:00
|
|
|
if (display == EGL_NO_DISPLAY) {
|
check for EGL_KHR_platform_gbm extension as well
Summary:
some drivers, like Mali have EGL_KHR_platform_gbm
but not EGL_MESA_platform_gbm
Test Plan: pending a test on rock64 board
Reviewers: #kwin, #plasma, davidedmundson, graesslin
Reviewed By: #kwin, #plasma, graesslin
Subscribers: graesslin, garg, davidedmundson, plasma-devel, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D10346
2018-02-15 10:41:10 +00:00
|
|
|
const bool hasMesaGBM = hasClientExtension(QByteArrayLiteral("EGL_MESA_platform_gbm"));
|
|
|
|
const bool hasKHRGBM = hasClientExtension(QByteArrayLiteral("EGL_KHR_platform_gbm"));
|
|
|
|
const GLenum platform = hasMesaGBM ? EGL_PLATFORM_GBM_MESA : EGL_PLATFORM_GBM_KHR;
|
|
|
|
|
2016-07-18 08:27:56 +00:00
|
|
|
if (!hasClientExtension(QByteArrayLiteral("EGL_EXT_platform_base")) ||
|
check for EGL_KHR_platform_gbm extension as well
Summary:
some drivers, like Mali have EGL_KHR_platform_gbm
but not EGL_MESA_platform_gbm
Test Plan: pending a test on rock64 board
Reviewers: #kwin, #plasma, davidedmundson, graesslin
Reviewed By: #kwin, #plasma, graesslin
Subscribers: graesslin, garg, davidedmundson, plasma-devel, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D10346
2018-02-15 10:41:10 +00:00
|
|
|
(!hasMesaGBM && !hasKHRGBM)) {
|
2019-12-10 00:24:45 +00:00
|
|
|
setFailed("Missing one or more extensions between EGL_EXT_platform_base, "
|
|
|
|
"EGL_MESA_platform_gbm, EGL_KHR_platform_gbm");
|
2016-07-18 08:27:56 +00:00
|
|
|
return false;
|
|
|
|
}
|
2015-04-10 08:44:07 +00:00
|
|
|
|
2021-10-14 15:25:13 +00:00
|
|
|
if (!m_gpu->gbmDevice()) {
|
2016-07-18 08:27:56 +00:00
|
|
|
setFailed("Could not create gbm device");
|
|
|
|
return false;
|
|
|
|
}
|
2015-04-10 08:44:07 +00:00
|
|
|
|
2021-10-14 15:25:13 +00:00
|
|
|
display = eglGetPlatformDisplayEXT(platform, m_gpu->gbmDevice(), nullptr);
|
2020-10-16 13:11:23 +00:00
|
|
|
m_gpu->setEglDisplay(display);
|
2016-07-18 08:27:56 +00:00
|
|
|
}
|
2015-04-10 08:44:07 +00:00
|
|
|
|
2019-12-10 00:24:45 +00:00
|
|
|
if (display == EGL_NO_DISPLAY) {
|
2015-04-10 08:44:07 +00:00
|
|
|
return false;
|
2019-12-10 00:24:45 +00:00
|
|
|
}
|
2015-04-10 08:44:07 +00:00
|
|
|
setEglDisplay(display);
|
|
|
|
return initEglAPI();
|
|
|
|
}
|
|
|
|
|
|
|
|
void EglGbmBackend::init()
|
|
|
|
{
|
2015-11-25 12:09:28 +00:00
|
|
|
if (!initializeEgl()) {
|
|
|
|
setFailed("Could not initialize egl");
|
|
|
|
return;
|
|
|
|
}
|
2021-04-21 15:23:46 +00:00
|
|
|
|
2015-04-10 08:44:07 +00:00
|
|
|
if (!initRenderingContext()) {
|
|
|
|
setFailed("Could not initialize rendering context");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
initBufferAge();
|
2020-11-28 17:53:41 +00:00
|
|
|
// at the moment: no secondary GPU -> no OpenGL context!
|
|
|
|
if (isPrimary()) {
|
|
|
|
initKWinGL();
|
|
|
|
initWayland();
|
|
|
|
}
|
2015-04-10 08:44:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool EglGbmBackend::initRenderingContext()
|
|
|
|
{
|
2021-08-26 15:45:46 +00:00
|
|
|
if (!initBufferConfigs()) {
|
|
|
|
return false;
|
|
|
|
}
|
2021-04-20 10:13:58 +00:00
|
|
|
if (isPrimary()) {
|
2021-05-31 11:13:59 +00:00
|
|
|
if (!createContext() || !makeCurrent()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const auto outputs = m_gpu->outputs();
|
|
|
|
for (const auto &output : outputs) {
|
|
|
|
addOutput(output);
|
2020-11-28 17:53:41 +00:00
|
|
|
}
|
|
|
|
return true;
|
2015-04-10 08:44:07 +00:00
|
|
|
}
|
2021-07-31 13:32:50 +00:00
|
|
|
|
2021-08-24 20:55:42 +00:00
|
|
|
bool EglGbmBackend::resetOutput(Output &output)
|
2015-04-24 08:05:38 +00:00
|
|
|
{
|
2021-08-24 20:55:42 +00:00
|
|
|
const QSize size = output.output->sourceSize();
|
|
|
|
QVector<uint64_t> modifiers = output.output->supportedModifiers(m_gbmFormat);
|
2021-07-31 13:32:50 +00:00
|
|
|
|
|
|
|
QSharedPointer<GbmSurface> gbmSurface;
|
2021-11-29 16:32:05 +00:00
|
|
|
bool modifiersEnvSet = false;
|
|
|
|
static bool modifiersEnv = qEnvironmentVariableIntValue("KWIN_DRM_USE_MODIFIERS", &modifiersEnvSet) != 0;
|
|
|
|
static bool allowModifiers = (gpu()->isNVidia() && !modifiersEnvSet) || (modifiersEnvSet && modifiersEnv);
|
2021-08-25 13:03:04 +00:00
|
|
|
#if HAVE_GBM_BO_GET_FD_FOR_PLANE
|
2021-11-29 16:32:05 +00:00
|
|
|
if (!allowModifiers) {
|
2021-08-25 13:03:04 +00:00
|
|
|
#else
|
|
|
|
// modifiers have to be disabled with multi-gpu if gbm_bo_get_fd_for_plane is not available
|
2021-11-29 16:32:05 +00:00
|
|
|
if (!allowModifiers || output.output->gpu() != m_gpu) {
|
2021-08-25 13:03:04 +00:00
|
|
|
#endif
|
2021-07-31 13:32:50 +00:00
|
|
|
int flags = GBM_BO_USE_RENDERING;
|
2021-08-24 20:55:42 +00:00
|
|
|
if (output.output->gpu() == m_gpu) {
|
2021-07-31 13:32:50 +00:00
|
|
|
flags |= GBM_BO_USE_SCANOUT;
|
|
|
|
} else {
|
|
|
|
flags |= GBM_BO_USE_LINEAR;
|
|
|
|
}
|
|
|
|
gbmSurface = QSharedPointer<GbmSurface>::create(m_gpu, size, m_gbmFormat, flags);
|
2021-01-31 00:31:15 +00:00
|
|
|
} else {
|
2021-07-31 13:32:50 +00:00
|
|
|
gbmSurface = QSharedPointer<GbmSurface>::create(m_gpu, size, m_gbmFormat, modifiers);
|
|
|
|
if (!gbmSurface->isValid()) {
|
|
|
|
// the egl / gbm implementation may reject the modifier list from another gpu
|
|
|
|
// as a fallback use linear, to at least make CPU copy more efficient
|
|
|
|
modifiers = {DRM_FORMAT_MOD_LINEAR};
|
|
|
|
gbmSurface = QSharedPointer<GbmSurface>::create(m_gpu, size, m_gbmFormat, modifiers);
|
|
|
|
}
|
2021-01-31 00:31:15 +00:00
|
|
|
}
|
2021-07-31 13:32:50 +00:00
|
|
|
if (!gbmSurface->isValid()) {
|
2021-05-25 17:25:55 +00:00
|
|
|
qCCritical(KWIN_DRM) << "Creating GBM surface failed:" << strerror(errno);
|
2017-10-21 13:16:41 +00:00
|
|
|
return false;
|
2015-04-24 08:05:38 +00:00
|
|
|
}
|
2021-05-25 22:05:17 +00:00
|
|
|
cleanupRenderData(output.old);
|
|
|
|
output.old = output.current;
|
|
|
|
output.current = {};
|
|
|
|
output.current.gbmSurface = gbmSurface;
|
2020-01-02 14:55:09 +00:00
|
|
|
|
2021-09-10 17:12:27 +00:00
|
|
|
if (!output.output->needsSoftwareTransformation()) {
|
2021-05-25 22:05:17 +00:00
|
|
|
output.current.shadowBuffer = nullptr;
|
2021-06-22 00:23:17 +00:00
|
|
|
} else {
|
2021-05-25 22:05:17 +00:00
|
|
|
makeContextCurrent(output.current);
|
|
|
|
output.current.shadowBuffer = QSharedPointer<ShadowBuffer>::create(output.output->pixelSize());
|
|
|
|
if (!output.current.shadowBuffer->isComplete()) {
|
2021-06-22 00:23:17 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2017-10-21 13:16:41 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-07-26 23:11:50 +00:00
|
|
|
bool EglGbmBackend::addOutput(DrmAbstractOutput *drmOutput)
|
2017-10-21 13:16:41 +00:00
|
|
|
{
|
2021-08-24 20:55:42 +00:00
|
|
|
Output newOutput;
|
|
|
|
newOutput.output = drmOutput;
|
2021-10-19 20:59:44 +00:00
|
|
|
if (!isPrimary() && !renderingBackend()->addOutput(drmOutput)) {
|
|
|
|
return false;
|
2020-11-28 17:53:41 +00:00
|
|
|
}
|
2021-08-24 20:55:42 +00:00
|
|
|
m_outputs.insert(drmOutput, newOutput);
|
2021-05-23 17:45:05 +00:00
|
|
|
return true;
|
2020-11-28 17:53:41 +00:00
|
|
|
}
|
|
|
|
|
2021-07-26 23:11:50 +00:00
|
|
|
void EglGbmBackend::removeOutput(DrmAbstractOutput *drmOutput)
|
2020-11-28 17:53:41 +00:00
|
|
|
{
|
2021-08-24 20:55:42 +00:00
|
|
|
Q_ASSERT(m_outputs.contains(drmOutput));
|
2020-12-01 16:58:24 +00:00
|
|
|
if (isPrimary()) {
|
2021-05-25 17:25:55 +00:00
|
|
|
// shadow buffer needs context current for destruction
|
|
|
|
makeCurrent();
|
2020-12-01 16:58:24 +00:00
|
|
|
} else {
|
2021-08-24 20:55:42 +00:00
|
|
|
renderingBackend()->removeOutput(drmOutput);
|
2020-11-28 17:53:41 +00:00
|
|
|
}
|
2021-08-24 20:55:42 +00:00
|
|
|
m_outputs.remove(drmOutput);
|
2015-04-24 08:05:38 +00:00
|
|
|
}
|
|
|
|
|
2021-07-26 23:11:50 +00:00
|
|
|
bool EglGbmBackend::swapBuffers(DrmAbstractOutput *drmOutput, const QRegion &dirty)
|
2019-12-10 00:24:45 +00:00
|
|
|
{
|
2021-08-24 20:55:42 +00:00
|
|
|
Q_ASSERT(m_outputs.contains(drmOutput));
|
|
|
|
Output &output = m_outputs[drmOutput];
|
2021-07-13 11:04:43 +00:00
|
|
|
renderFramebufferToSurface(output);
|
|
|
|
if (output.current.gbmSurface->swapBuffers()) {
|
|
|
|
cleanupRenderData(output.old);
|
|
|
|
updateBufferAge(output, dirty);
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
2021-05-04 10:19:31 +00:00
|
|
|
}
|
|
|
|
|
2021-07-26 23:11:50 +00:00
|
|
|
bool EglGbmBackend::exportFramebuffer(DrmAbstractOutput *drmOutput, void *data, const QSize &size, uint32_t stride)
|
2021-05-04 10:19:31 +00:00
|
|
|
{
|
2021-08-24 20:55:42 +00:00
|
|
|
Q_ASSERT(m_outputs.contains(drmOutput));
|
|
|
|
auto bo = m_outputs[drmOutput].current.gbmSurface->currentBuffer();
|
2021-05-25 17:25:55 +00:00
|
|
|
if (!bo->map(GBM_BO_TRANSFER_READ)) {
|
2021-05-04 10:19:31 +00:00
|
|
|
return false;
|
|
|
|
}
|
2021-05-25 17:25:55 +00:00
|
|
|
if (stride != bo->stride()) {
|
2021-05-04 10:19:31 +00:00
|
|
|
// shouldn't happen if formats are the same
|
|
|
|
return false;
|
|
|
|
}
|
2021-05-25 17:25:55 +00:00
|
|
|
return memcpy(data, bo->mappedData(), size.height() * stride);
|
2021-05-04 10:19:31 +00:00
|
|
|
}
|
|
|
|
|
2021-08-24 22:45:43 +00:00
|
|
|
bool EglGbmBackend::exportFramebufferAsDmabuf(DrmAbstractOutput *drmOutput, int *fds, int *strides, int *offsets, uint32_t *num_fds, uint32_t *format, uint64_t *modifier)
|
2021-05-04 10:19:31 +00:00
|
|
|
{
|
2021-08-24 20:55:42 +00:00
|
|
|
Q_ASSERT(m_outputs.contains(drmOutput));
|
|
|
|
auto bo = m_outputs[drmOutput].current.gbmSurface->currentBuffer()->getBo();
|
2021-08-25 13:03:04 +00:00
|
|
|
#if HAVE_GBM_BO_GET_FD_FOR_PLANE
|
2021-08-24 22:45:43 +00:00
|
|
|
if (gbm_bo_get_handle_for_plane(bo, 0).s32 != -1) {
|
|
|
|
*num_fds = gbm_bo_get_plane_count(bo);
|
|
|
|
for (uint32_t i = 0; i < *num_fds; i++) {
|
|
|
|
fds[i] = gbm_bo_get_fd_for_plane(bo, i);
|
|
|
|
if (fds[i] < 0) {
|
|
|
|
qCWarning(KWIN_DRM) << "failed to export gbm_bo as dma-buf:" << strerror(errno);
|
|
|
|
for (uint32_t f = 0; f < i; f++) {
|
|
|
|
close(fds[f]);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
strides[i] = gbm_bo_get_stride_for_plane(bo, i);
|
|
|
|
offsets[i] = gbm_bo_get_offset(bo, i);
|
|
|
|
}
|
|
|
|
*modifier = gbm_bo_get_modifier(bo);
|
|
|
|
} else {
|
2021-08-25 13:03:04 +00:00
|
|
|
#endif
|
2021-08-24 22:45:43 +00:00
|
|
|
fds[0] = gbm_bo_get_fd(bo);
|
|
|
|
if (fds[0] < 0) {
|
|
|
|
qCWarning(KWIN_DRM) << "failed to export gbm_bo as dma-buf:" << strerror(errno);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
*num_fds = 1;
|
|
|
|
strides[0] = gbm_bo_get_stride(bo);
|
|
|
|
*modifier = DRM_FORMAT_MOD_INVALID;
|
2021-08-25 13:03:04 +00:00
|
|
|
#if HAVE_GBM_BO_GET_FD_FOR_PLANE
|
2020-11-28 17:53:41 +00:00
|
|
|
}
|
2021-08-25 13:03:04 +00:00
|
|
|
#endif
|
2021-05-25 17:25:55 +00:00
|
|
|
*format = gbm_bo_get_format(bo);
|
2021-08-24 22:45:43 +00:00
|
|
|
return true;
|
2020-11-28 17:53:41 +00:00
|
|
|
}
|
|
|
|
|
2021-07-13 11:04:43 +00:00
|
|
|
QSharedPointer<DrmBuffer> EglGbmBackend::importFramebuffer(Output &output, const QRegion &dirty) const
|
2021-05-04 10:19:31 +00:00
|
|
|
{
|
2021-07-13 11:04:43 +00:00
|
|
|
if (!renderingBackend()->swapBuffers(output.output, dirty)) {
|
2021-05-04 10:19:31 +00:00
|
|
|
qCWarning(KWIN_DRM) << "swapping buffers failed on output" << output.output;
|
2021-05-25 17:55:15 +00:00
|
|
|
return nullptr;
|
2021-05-04 10:19:31 +00:00
|
|
|
}
|
|
|
|
const auto size = output.output->modeSize();
|
2021-05-25 22:05:17 +00:00
|
|
|
if (output.current.importMode == ImportMode::Dmabuf) {
|
2021-08-24 22:45:43 +00:00
|
|
|
struct gbm_import_fd_modifier_data data;
|
|
|
|
data.width = size.width();
|
|
|
|
data.height = size.height();
|
|
|
|
if (renderingBackend()->exportFramebufferAsDmabuf(output.output, data.fds, data.strides, data.offsets, &data.num_fds, &data.format, &data.modifier)) {
|
|
|
|
gbm_bo *importedBuffer = nullptr;
|
|
|
|
if (data.modifier == DRM_FORMAT_MOD_INVALID) {
|
|
|
|
struct gbm_import_fd_data data1;
|
|
|
|
data1.fd = data.fds[0];
|
|
|
|
data1.width = size.width();
|
|
|
|
data1.height = size.height();
|
|
|
|
data1.stride = data.strides[0];
|
|
|
|
data1.format = data.format;
|
|
|
|
importedBuffer = gbm_bo_import(m_gpu->gbmDevice(), GBM_BO_IMPORT_FD, &data1, GBM_BO_USE_SCANOUT | GBM_BO_USE_LINEAR);
|
|
|
|
} else {
|
|
|
|
importedBuffer = gbm_bo_import(m_gpu->gbmDevice(), GBM_BO_IMPORT_FD_MODIFIER, &data, 0);
|
|
|
|
}
|
|
|
|
for (uint32_t i = 0; i < data.num_fds; i++) {
|
|
|
|
close(data.fds[i]);
|
|
|
|
}
|
2021-05-04 10:19:31 +00:00
|
|
|
if (importedBuffer) {
|
|
|
|
auto buffer = QSharedPointer<DrmGbmBuffer>::create(m_gpu, importedBuffer, nullptr);
|
|
|
|
if (buffer->bufferId() > 0) {
|
2021-05-25 17:55:15 +00:00
|
|
|
return buffer;
|
2021-05-04 10:19:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
qCDebug(KWIN_DRM) << "import with dmabuf failed! Switching to CPU import on output" << output.output;
|
2021-05-25 22:05:17 +00:00
|
|
|
output.current.importMode = ImportMode::DumbBuffer;
|
2021-05-04 10:19:31 +00:00
|
|
|
}
|
|
|
|
// ImportMode::DumbBuffer
|
2021-05-25 22:05:17 +00:00
|
|
|
if (!output.current.importSwapchain || output.current.importSwapchain->size() != size) {
|
2021-12-02 16:51:08 +00:00
|
|
|
output.current.importSwapchain = QSharedPointer<DumbSwapchain>::create(m_gpu, size, DRM_FORMAT_XRGB8888);
|
2021-05-25 22:05:17 +00:00
|
|
|
if (output.current.importSwapchain->isEmpty()) {
|
|
|
|
output.current.importSwapchain = nullptr;
|
2021-05-04 10:19:31 +00:00
|
|
|
}
|
|
|
|
}
|
2021-05-25 22:05:17 +00:00
|
|
|
if (output.current.importSwapchain) {
|
|
|
|
auto buffer = output.current.importSwapchain->acquireBuffer();
|
2021-05-04 10:19:31 +00:00
|
|
|
if (renderingBackend()->exportFramebuffer(output.output, buffer->data(), size, buffer->stride())) {
|
2021-05-25 17:55:15 +00:00
|
|
|
return buffer;
|
2021-05-04 10:19:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
qCWarning(KWIN_DRM) << "all imports failed on output" << output.output;
|
|
|
|
// TODO turn off output?
|
2021-05-25 17:55:15 +00:00
|
|
|
return nullptr;
|
2021-05-04 10:19:31 +00:00
|
|
|
}
|
|
|
|
|
2020-01-02 14:55:09 +00:00
|
|
|
void EglGbmBackend::renderFramebufferToSurface(Output &output)
|
|
|
|
{
|
2021-05-25 22:05:17 +00:00
|
|
|
if (!output.current.shadowBuffer) {
|
2020-01-02 14:55:09 +00:00
|
|
|
// No additional render target.
|
|
|
|
return;
|
|
|
|
}
|
2021-05-25 22:05:17 +00:00
|
|
|
makeContextCurrent(output.current);
|
|
|
|
output.current.shadowBuffer->render(output.output);
|
2020-01-02 14:55:09 +00:00
|
|
|
}
|
|
|
|
|
2021-05-25 22:05:17 +00:00
|
|
|
bool EglGbmBackend::makeContextCurrent(const Output::RenderData &render) const
|
2015-04-10 08:44:07 +00:00
|
|
|
{
|
2020-11-28 17:53:41 +00:00
|
|
|
Q_ASSERT(isPrimary());
|
2021-05-25 22:05:17 +00:00
|
|
|
const auto surface = render.gbmSurface;
|
2021-05-25 17:25:55 +00:00
|
|
|
if (!surface) {
|
2015-04-17 13:48:55 +00:00
|
|
|
return false;
|
|
|
|
}
|
2021-05-25 17:25:55 +00:00
|
|
|
if (eglMakeCurrent(eglDisplay(), surface->eglSurface(), surface->eglSurface(), context()) == EGL_FALSE) {
|
2021-06-20 15:36:15 +00:00
|
|
|
qCCritical(KWIN_DRM) << "eglMakeCurrent failed:" << getEglErrorString();
|
2015-04-10 08:44:07 +00:00
|
|
|
return false;
|
|
|
|
}
|
2021-10-27 09:45:06 +00:00
|
|
|
if (!GLPlatform::instance()->isGLES()) {
|
|
|
|
glDrawBuffer(GL_BACK);
|
|
|
|
}
|
2015-04-10 08:44:07 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool EglGbmBackend::initBufferConfigs()
|
|
|
|
{
|
|
|
|
const EGLint config_attribs[] = {
|
|
|
|
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
|
|
|
EGL_RED_SIZE, 1,
|
|
|
|
EGL_GREEN_SIZE, 1,
|
|
|
|
EGL_BLUE_SIZE, 1,
|
|
|
|
EGL_ALPHA_SIZE, 0,
|
2015-10-30 11:56:03 +00:00
|
|
|
EGL_RENDERABLE_TYPE, isOpenGLES() ? EGL_OPENGL_ES2_BIT : EGL_OPENGL_BIT,
|
2015-04-10 08:44:07 +00:00
|
|
|
EGL_CONFIG_CAVEAT, EGL_NONE,
|
|
|
|
EGL_NONE,
|
|
|
|
};
|
|
|
|
|
|
|
|
EGLint count;
|
|
|
|
EGLConfig configs[1024];
|
2019-12-10 00:24:45 +00:00
|
|
|
if (!eglChooseConfig(eglDisplay(), config_attribs, configs,
|
|
|
|
sizeof(configs) / sizeof(EGLConfig),
|
|
|
|
&count)) {
|
2021-06-20 15:36:15 +00:00
|
|
|
qCCritical(KWIN_DRM) << "eglChooseConfig failed:" << getEglErrorString();
|
2015-04-10 08:44:07 +00:00
|
|
|
return false;
|
|
|
|
}
|
drm backend: choose correct EGL config with mesa-18
Summary:
Do not blindly select first EGL config from returned list, but choose the one that matches GBM surfaces, that will be created later.
GBM surfaces are created with GBM_FORMAT_XRGB8888 format, so choose the config that matches it.
With wrong format EglGbmBackend::resetOutput() will later fail with error EGL_BAD_MATCH.
Test Plan: Compile, run startplasmacompositor. Verify that OpenGL compositing is used, either by kwin debug console, or by kwin support information.
Reviewers: graesslin, davidedmundson, #kwin, #plasma_on_wayland, bshah
Reviewed By: davidedmundson
Subscribers: zzag, kwin, #kwin
Tags: #kwin, #plasma_on_wayland
Differential Revision: https://phabricator.kde.org/D11758
2018-04-04 22:20:46 +00:00
|
|
|
|
|
|
|
qCDebug(KWIN_DRM) << "EGL buffer configs count:" << count;
|
|
|
|
|
2021-09-06 22:24:24 +00:00
|
|
|
uint32_t fallbackFormat = 0;
|
2021-07-31 13:32:50 +00:00
|
|
|
EGLConfig fallbackConfig = nullptr;
|
|
|
|
|
2019-12-10 00:24:45 +00:00
|
|
|
// Loop through all configs, choosing the first one that has suitable format.
|
drm backend: choose correct EGL config with mesa-18
Summary:
Do not blindly select first EGL config from returned list, but choose the one that matches GBM surfaces, that will be created later.
GBM surfaces are created with GBM_FORMAT_XRGB8888 format, so choose the config that matches it.
With wrong format EglGbmBackend::resetOutput() will later fail with error EGL_BAD_MATCH.
Test Plan: Compile, run startplasmacompositor. Verify that OpenGL compositing is used, either by kwin debug console, or by kwin support information.
Reviewers: graesslin, davidedmundson, #kwin, #plasma_on_wayland, bshah
Reviewed By: davidedmundson
Subscribers: zzag, kwin, #kwin
Tags: #kwin, #plasma_on_wayland
Differential Revision: https://phabricator.kde.org/D11758
2018-04-04 22:20:46 +00:00
|
|
|
for (EGLint i = 0; i < count; i++) {
|
|
|
|
EGLint gbmFormat;
|
|
|
|
eglGetConfigAttrib(eglDisplay(), configs[i], EGL_NATIVE_VISUAL_ID, &gbmFormat);
|
|
|
|
|
2021-08-22 20:25:03 +00:00
|
|
|
if (!m_gpu->isFormatSupported(gbmFormat)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-07-31 13:32:50 +00:00
|
|
|
// Query number of bits for color channel
|
|
|
|
EGLint blueSize, redSize, greenSize, alphaSize;
|
|
|
|
eglGetConfigAttrib(eglDisplay(), configs[i], EGL_RED_SIZE, &redSize);
|
|
|
|
eglGetConfigAttrib(eglDisplay(), configs[i], EGL_GREEN_SIZE, &greenSize);
|
|
|
|
eglGetConfigAttrib(eglDisplay(), configs[i], EGL_BLUE_SIZE, &blueSize);
|
|
|
|
eglGetConfigAttrib(eglDisplay(), configs[i], EGL_ALPHA_SIZE, &alphaSize);
|
|
|
|
|
|
|
|
// prefer XRGB8888 as it's most likely to be supported by secondary GPUs as well
|
2021-08-26 16:36:52 +00:00
|
|
|
if (gbmFormat == GBM_FORMAT_XRGB8888) {
|
2021-07-31 13:32:50 +00:00
|
|
|
m_gbmFormat = gbmFormat;
|
drm backend: choose correct EGL config with mesa-18
Summary:
Do not blindly select first EGL config from returned list, but choose the one that matches GBM surfaces, that will be created later.
GBM surfaces are created with GBM_FORMAT_XRGB8888 format, so choose the config that matches it.
With wrong format EglGbmBackend::resetOutput() will later fail with error EGL_BAD_MATCH.
Test Plan: Compile, run startplasmacompositor. Verify that OpenGL compositing is used, either by kwin debug console, or by kwin support information.
Reviewers: graesslin, davidedmundson, #kwin, #plasma_on_wayland, bshah
Reviewed By: davidedmundson
Subscribers: zzag, kwin, #kwin
Tags: #kwin, #plasma_on_wayland
Differential Revision: https://phabricator.kde.org/D11758
2018-04-04 22:20:46 +00:00
|
|
|
setConfig(configs[i]);
|
|
|
|
return true;
|
2021-07-31 13:32:50 +00:00
|
|
|
} else if (!fallbackConfig && blueSize >= 8 && redSize >= 8 && greenSize >= 8) {
|
|
|
|
fallbackFormat = gbmFormat;
|
|
|
|
fallbackConfig = configs[i];
|
drm backend: choose correct EGL config with mesa-18
Summary:
Do not blindly select first EGL config from returned list, but choose the one that matches GBM surfaces, that will be created later.
GBM surfaces are created with GBM_FORMAT_XRGB8888 format, so choose the config that matches it.
With wrong format EglGbmBackend::resetOutput() will later fail with error EGL_BAD_MATCH.
Test Plan: Compile, run startplasmacompositor. Verify that OpenGL compositing is used, either by kwin debug console, or by kwin support information.
Reviewers: graesslin, davidedmundson, #kwin, #plasma_on_wayland, bshah
Reviewed By: davidedmundson
Subscribers: zzag, kwin, #kwin
Tags: #kwin, #plasma_on_wayland
Differential Revision: https://phabricator.kde.org/D11758
2018-04-04 22:20:46 +00:00
|
|
|
}
|
2015-04-10 08:44:07 +00:00
|
|
|
}
|
|
|
|
|
2021-07-31 13:32:50 +00:00
|
|
|
if (fallbackConfig) {
|
|
|
|
m_gbmFormat = fallbackFormat;
|
|
|
|
setConfig(fallbackConfig);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-12-10 00:24:45 +00:00
|
|
|
qCCritical(KWIN_DRM) << "Choosing EGL config did not return a suitable config. There were"
|
2021-08-26 15:38:58 +00:00
|
|
|
<< count << "configs:";
|
|
|
|
for (EGLint i = 0; i < count; i++) {
|
|
|
|
EGLint gbmFormat, blueSize, redSize, greenSize, alphaSize;
|
|
|
|
eglGetConfigAttrib(eglDisplay(), configs[i], EGL_NATIVE_VISUAL_ID, &gbmFormat);
|
|
|
|
eglGetConfigAttrib(eglDisplay(), configs[i], EGL_RED_SIZE, &redSize);
|
|
|
|
eglGetConfigAttrib(eglDisplay(), configs[i], EGL_GREEN_SIZE, &greenSize);
|
|
|
|
eglGetConfigAttrib(eglDisplay(), configs[i], EGL_BLUE_SIZE, &blueSize);
|
|
|
|
eglGetConfigAttrib(eglDisplay(), configs[i], EGL_ALPHA_SIZE, &alphaSize);
|
|
|
|
gbm_format_name_desc name;
|
|
|
|
gbm_format_get_name(gbmFormat, &name);
|
|
|
|
qCCritical(KWIN_DRM, "EGL config %d has format %s with %d,%d,%d,%d bits for r,g,b,a", i, name.name, redSize, greenSize, blueSize, alphaSize);
|
|
|
|
}
|
drm backend: choose correct EGL config with mesa-18
Summary:
Do not blindly select first EGL config from returned list, but choose the one that matches GBM surfaces, that will be created later.
GBM surfaces are created with GBM_FORMAT_XRGB8888 format, so choose the config that matches it.
With wrong format EglGbmBackend::resetOutput() will later fail with error EGL_BAD_MATCH.
Test Plan: Compile, run startplasmacompositor. Verify that OpenGL compositing is used, either by kwin debug console, or by kwin support information.
Reviewers: graesslin, davidedmundson, #kwin, #plasma_on_wayland, bshah
Reviewed By: davidedmundson
Subscribers: zzag, kwin, #kwin
Tags: #kwin, #plasma_on_wayland
Differential Revision: https://phabricator.kde.org/D11758
2018-04-04 22:20:46 +00:00
|
|
|
return false;
|
2015-04-10 08:44:07 +00:00
|
|
|
}
|
|
|
|
|
2020-04-24 17:11:41 +00:00
|
|
|
static QVector<EGLint> regionToRects(const QRegion ®ion, AbstractWaylandOutput *output)
|
|
|
|
{
|
|
|
|
const int height = output->modeSize().height();
|
|
|
|
|
2020-10-13 20:57:15 +00:00
|
|
|
const QMatrix4x4 matrix = DrmOutput::logicalToNativeMatrix(output->geometry(),
|
|
|
|
output->scale(),
|
|
|
|
output->transform());
|
2020-04-24 17:11:41 +00:00
|
|
|
|
|
|
|
QVector<EGLint> rects;
|
|
|
|
rects.reserve(region.rectCount() * 4);
|
|
|
|
for (const QRect &_rect : region) {
|
|
|
|
const QRect rect = matrix.mapRect(_rect);
|
|
|
|
|
|
|
|
rects << rect.left();
|
|
|
|
rects << height - (rect.y() + rect.height());
|
|
|
|
rects << rect.width();
|
|
|
|
rects << rect.height();
|
|
|
|
}
|
|
|
|
return rects;
|
|
|
|
}
|
|
|
|
|
2021-08-24 20:55:42 +00:00
|
|
|
void EglGbmBackend::aboutToStartPainting(AbstractOutput *drmOutput, const QRegion &damagedRegion)
|
2020-04-24 17:11:41 +00:00
|
|
|
{
|
2021-08-24 20:55:42 +00:00
|
|
|
Q_ASSERT_X(drmOutput, "aboutToStartPainting", "not using per screen rendering");
|
|
|
|
Q_ASSERT(m_outputs.contains(drmOutput));
|
|
|
|
const Output &output = m_outputs[drmOutput];
|
2021-05-25 22:05:17 +00:00
|
|
|
if (output.current.bufferAge > 0 && !damagedRegion.isEmpty() && supportsPartialUpdate()) {
|
2020-04-24 17:11:41 +00:00
|
|
|
const QRegion region = damagedRegion & output.output->geometry();
|
|
|
|
|
|
|
|
QVector<EGLint> rects = regionToRects(region, output.output);
|
2021-05-25 22:05:17 +00:00
|
|
|
const bool correct = eglSetDamageRegionKHR(eglDisplay(), output.current.gbmSurface->eglSurface(),
|
2020-04-24 17:11:41 +00:00
|
|
|
rects.data(), rects.count()/4);
|
|
|
|
if (!correct) {
|
2021-06-20 15:36:15 +00:00
|
|
|
qCWarning(KWIN_DRM) << "eglSetDamageRegionKHR failed:" << getEglErrorString();
|
2020-04-24 17:11:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-20 14:58:58 +00:00
|
|
|
SurfaceTexture *EglGbmBackend::createSurfaceTextureInternal(SurfacePixmapInternal *pixmap)
|
2015-04-10 08:44:07 +00:00
|
|
|
{
|
2021-04-09 07:06:04 +00:00
|
|
|
return new BasicEGLSurfaceTextureInternal(this, pixmap);
|
|
|
|
}
|
|
|
|
|
2021-10-20 14:58:58 +00:00
|
|
|
SurfaceTexture *EglGbmBackend::createSurfaceTextureWayland(SurfacePixmapWayland *pixmap)
|
2021-04-09 07:06:04 +00:00
|
|
|
{
|
|
|
|
return new BasicEGLSurfaceTextureWayland(this, pixmap);
|
2015-04-10 08:44:07 +00:00
|
|
|
}
|
|
|
|
|
2019-12-10 00:24:45 +00:00
|
|
|
void EglGbmBackend::setViewport(const Output &output) const
|
|
|
|
{
|
2021-06-08 20:25:40 +00:00
|
|
|
const QSize size = output.output->pixelSize();
|
|
|
|
glViewport(0, 0, size.width(), size.height());
|
2019-12-10 00:24:45 +00:00
|
|
|
}
|
|
|
|
|
2021-08-24 20:55:42 +00:00
|
|
|
QRegion EglGbmBackend::beginFrame(AbstractOutput *drmOutput)
|
2015-04-23 07:55:49 +00:00
|
|
|
{
|
2021-08-24 20:55:42 +00:00
|
|
|
Q_ASSERT(m_outputs.contains(drmOutput));
|
|
|
|
Output &output = m_outputs[drmOutput];
|
2021-11-24 08:16:47 +00:00
|
|
|
if (output.scanoutSurface) {
|
2021-05-30 23:58:47 +00:00
|
|
|
qCDebug(KWIN_DRM) << "Direct scanout stopped on output" << output.output->name();
|
|
|
|
}
|
2021-11-24 08:16:47 +00:00
|
|
|
output.scanoutSurface = nullptr;
|
|
|
|
if (output.scanoutCandidate) {
|
|
|
|
output.oldScanoutCandidate = output.scanoutCandidate;
|
|
|
|
output.scanoutCandidate = nullptr;
|
|
|
|
} else if (output.oldScanoutCandidate && output.oldScanoutCandidate->dmabufFeedbackV1()) {
|
|
|
|
output.oldScanoutCandidate->dmabufFeedbackV1()->setTranches({});
|
|
|
|
}
|
2020-11-28 17:53:41 +00:00
|
|
|
if (isPrimary()) {
|
2021-02-02 15:03:43 +00:00
|
|
|
return prepareRenderingForOutput(output);
|
2020-11-28 17:53:41 +00:00
|
|
|
} else {
|
2021-11-10 12:26:34 +00:00
|
|
|
return renderingBackend()->beginFrame(output.output);
|
2020-11-28 17:53:41 +00:00
|
|
|
}
|
|
|
|
}
|
2019-12-10 00:24:45 +00:00
|
|
|
|
2021-07-26 23:11:50 +00:00
|
|
|
bool EglGbmBackend::doesRenderFit(DrmAbstractOutput *output, const Output::RenderData &render)
|
2020-11-28 17:53:41 +00:00
|
|
|
{
|
2021-05-25 22:05:17 +00:00
|
|
|
if (!render.gbmSurface) {
|
|
|
|
return false;
|
|
|
|
}
|
2021-07-26 23:11:50 +00:00
|
|
|
QSize surfaceSize = output->sourceSize();
|
2021-05-25 22:05:17 +00:00
|
|
|
if (surfaceSize != render.gbmSurface->size()) {
|
|
|
|
return false;
|
|
|
|
}
|
2021-09-10 17:12:27 +00:00
|
|
|
bool needsTexture = output->needsSoftwareTransformation();
|
2021-05-25 22:05:17 +00:00
|
|
|
if (needsTexture) {
|
2021-09-10 17:12:27 +00:00
|
|
|
return render.shadowBuffer && render.shadowBuffer->textureSize() == output->pixelSize();
|
2021-05-25 22:05:17 +00:00
|
|
|
} else {
|
|
|
|
return render.shadowBuffer == nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QRegion EglGbmBackend::prepareRenderingForOutput(Output &output)
|
|
|
|
{
|
|
|
|
// check if the current surface still fits
|
|
|
|
if (!doesRenderFit(output.output, output.current)) {
|
|
|
|
if (doesRenderFit(output.output, output.old)) {
|
|
|
|
cleanupRenderData(output.current);
|
|
|
|
output.current = output.old;
|
|
|
|
output.old = {};
|
|
|
|
} else {
|
2021-08-24 20:55:42 +00:00
|
|
|
resetOutput(output);
|
2021-05-25 22:05:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
makeContextCurrent(output.current);
|
|
|
|
if (output.current.shadowBuffer) {
|
|
|
|
output.current.shadowBuffer->bind();
|
2021-06-22 00:23:17 +00:00
|
|
|
}
|
2019-12-10 00:24:45 +00:00
|
|
|
setViewport(output);
|
|
|
|
|
2021-07-24 10:03:00 +00:00
|
|
|
const QRect geometry = output.output->geometry();
|
2015-04-17 13:48:55 +00:00
|
|
|
if (supportsBufferAge()) {
|
2021-07-24 10:03:00 +00:00
|
|
|
auto current = &output.current;
|
|
|
|
return current->damageJournal.accumulate(current->bufferAge, geometry);
|
2015-04-17 13:48:55 +00:00
|
|
|
}
|
2021-07-24 10:03:00 +00:00
|
|
|
|
|
|
|
return geometry;
|
2015-04-10 08:44:07 +00:00
|
|
|
}
|
|
|
|
|
2021-08-24 20:55:42 +00:00
|
|
|
QSharedPointer<DrmBuffer> EglGbmBackend::endFrameWithBuffer(AbstractOutput *drmOutput, const QRegion &dirty)
|
2021-05-25 22:05:17 +00:00
|
|
|
{
|
2021-08-24 20:55:42 +00:00
|
|
|
Q_ASSERT(m_outputs.contains(drmOutput));
|
|
|
|
Output &output = m_outputs[drmOutput];
|
2021-05-25 22:05:17 +00:00
|
|
|
if (isPrimary()) {
|
|
|
|
renderFramebufferToSurface(output);
|
2021-07-13 11:04:43 +00:00
|
|
|
auto buffer = output.current.gbmSurface->swapBuffersForDrm();
|
|
|
|
if (buffer) {
|
|
|
|
updateBufferAge(output, dirty);
|
|
|
|
}
|
|
|
|
return buffer;
|
2021-05-25 22:05:17 +00:00
|
|
|
} else {
|
2021-07-13 11:04:43 +00:00
|
|
|
return importFramebuffer(output, dirty);
|
2021-05-25 22:05:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-24 20:55:42 +00:00
|
|
|
void EglGbmBackend::endFrame(AbstractOutput *drmOutput, const QRegion &renderedRegion,
|
2020-11-09 14:31:26 +00:00
|
|
|
const QRegion &damagedRegion)
|
2015-04-10 08:44:07 +00:00
|
|
|
{
|
2021-08-24 20:55:42 +00:00
|
|
|
Q_ASSERT(m_outputs.contains(drmOutput));
|
2020-11-29 11:24:38 +00:00
|
|
|
Q_UNUSED(renderedRegion)
|
|
|
|
|
2021-08-24 20:55:42 +00:00
|
|
|
Output &output = m_outputs[drmOutput];
|
2021-05-25 22:05:17 +00:00
|
|
|
cleanupRenderData(output.old);
|
2020-11-29 11:24:38 +00:00
|
|
|
|
2021-04-06 08:53:26 +00:00
|
|
|
const QRegion dirty = damagedRegion.intersected(output.output->geometry());
|
2021-08-24 20:55:42 +00:00
|
|
|
QSharedPointer<DrmBuffer> buffer = endFrameWithBuffer(drmOutput, dirty);
|
2021-10-08 08:52:01 +00:00
|
|
|
output.output->present(buffer, dirty);
|
2021-07-13 11:04:43 +00:00
|
|
|
}
|
2015-04-10 08:44:07 +00:00
|
|
|
|
2021-07-13 11:04:43 +00:00
|
|
|
void EglGbmBackend::updateBufferAge(Output &output, const QRegion &dirty)
|
|
|
|
{
|
2020-10-29 21:33:06 +00:00
|
|
|
if (supportsBufferAge()) {
|
2021-05-25 22:05:17 +00:00
|
|
|
eglQuerySurface(eglDisplay(), output.current.gbmSurface->eglSurface(), EGL_BUFFER_AGE_EXT, &output.current.bufferAge);
|
2021-07-24 10:03:00 +00:00
|
|
|
output.current.damageJournal.add(dirty);
|
2015-04-23 07:55:49 +00:00
|
|
|
}
|
2015-04-10 08:44:07 +00:00
|
|
|
}
|
|
|
|
|
2021-08-24 20:55:42 +00:00
|
|
|
bool EglGbmBackend::scanout(AbstractOutput *drmOutput, SurfaceItem *surfaceItem)
|
2021-02-02 13:26:43 +00:00
|
|
|
{
|
2021-08-24 20:55:42 +00:00
|
|
|
Q_ASSERT(m_outputs.contains(drmOutput));
|
2021-02-04 08:40:20 +00:00
|
|
|
SurfaceItemWayland *item = qobject_cast<SurfaceItemWayland *>(surfaceItem);
|
|
|
|
if (!item) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
KWaylandServer::SurfaceInterface *surface = item->surface();
|
2021-07-20 19:37:03 +00:00
|
|
|
if (!surface) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto buffer = qobject_cast<KWaylandServer::LinuxDmaBufV1ClientBuffer *>(surface->buffer());
|
|
|
|
if (!buffer) {
|
2021-02-02 13:26:43 +00:00
|
|
|
return false;
|
|
|
|
}
|
2021-08-24 20:55:42 +00:00
|
|
|
Output &output = m_outputs[drmOutput];
|
2021-07-20 19:37:03 +00:00
|
|
|
if (buffer->size() != output.output->modeSize()) {
|
2021-02-02 13:26:43 +00:00
|
|
|
return false;
|
|
|
|
}
|
2021-11-24 08:16:47 +00:00
|
|
|
if (output.oldScanoutCandidate && output.oldScanoutCandidate != surface) {
|
|
|
|
output.oldScanoutCandidate->dmabufFeedbackV1()->setTranches({});
|
|
|
|
output.oldScanoutCandidate = nullptr;
|
|
|
|
}
|
|
|
|
output.scanoutCandidate = surface;
|
|
|
|
const auto &sendFeedback = [&output, this]() {
|
|
|
|
if (const auto &drmOutput = qobject_cast<DrmOutput *>(output.output); drmOutput && output.scanoutCandidate->dmabufFeedbackV1()) {
|
|
|
|
KWaylandServer::LinuxDmaBufV1Feedback::Tranche tranche;
|
|
|
|
tranche.device = m_gpu->deviceId();
|
|
|
|
tranche.flags = KWaylandServer::LinuxDmaBufV1Feedback::TrancheFlag::Scanout;
|
|
|
|
// atm no format changes are sent as those might require a modeset
|
|
|
|
// and thus require more elaborate feedback
|
|
|
|
const auto &mods = drmOutput->pipeline()->supportedModifiers(m_gbmFormat);
|
|
|
|
for (const auto &mod : mods) {
|
|
|
|
tranche.formatTable[m_gbmFormat] << mod;
|
|
|
|
}
|
|
|
|
if (tranche.formatTable.isEmpty()) {
|
|
|
|
output.scanoutCandidate->dmabufFeedbackV1()->setTranches({});
|
|
|
|
} else {
|
|
|
|
output.scanoutCandidate->dmabufFeedbackV1()->setTranches({tranche});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
if (!buffer->planes().count() || !output.output->isFormatSupported(buffer->format())) {
|
|
|
|
sendFeedback();
|
2021-02-02 13:26:43 +00:00
|
|
|
return false;
|
|
|
|
}
|
2021-07-31 13:32:50 +00:00
|
|
|
|
2021-02-02 13:26:43 +00:00
|
|
|
gbm_bo *importedBuffer;
|
2021-09-30 07:26:53 +00:00
|
|
|
const auto planes = buffer->planes();
|
|
|
|
if (planes.first().modifier != DRM_FORMAT_MOD_INVALID
|
|
|
|
|| planes.first().offset > 0
|
|
|
|
|| planes.count() > 1) {
|
|
|
|
if (!m_gpu->addFB2ModifiersSupported() || !output.output->supportedModifiers(buffer->format()).contains(planes.first().modifier)) {
|
2021-11-24 08:16:47 +00:00
|
|
|
sendFeedback();
|
2021-06-08 14:07:57 +00:00
|
|
|
return false;
|
|
|
|
}
|
2021-02-02 13:26:43 +00:00
|
|
|
gbm_import_fd_modifier_data data = {};
|
2021-07-20 19:37:03 +00:00
|
|
|
data.format = buffer->format();
|
|
|
|
data.width = (uint32_t) buffer->size().width();
|
|
|
|
data.height = (uint32_t) buffer->size().height();
|
2021-09-30 07:26:53 +00:00
|
|
|
data.num_fds = planes.count();
|
|
|
|
data.modifier = planes.first().modifier;
|
|
|
|
for (int i = 0; i < planes.count(); i++) {
|
|
|
|
data.fds[i] = planes[i].fd;
|
|
|
|
data.offsets[i] = planes[i].offset;
|
|
|
|
data.strides[i] = planes[i].stride;
|
2021-02-02 13:26:43 +00:00
|
|
|
}
|
|
|
|
importedBuffer = gbm_bo_import(m_gpu->gbmDevice(), GBM_BO_IMPORT_FD_MODIFIER, &data, GBM_BO_USE_SCANOUT);
|
|
|
|
} else {
|
2021-09-30 07:26:53 +00:00
|
|
|
auto plane = planes.first();
|
2021-02-02 13:26:43 +00:00
|
|
|
gbm_import_fd_data data = {};
|
|
|
|
data.fd = plane.fd;
|
2021-07-20 19:37:03 +00:00
|
|
|
data.width = (uint32_t) buffer->size().width();
|
|
|
|
data.height = (uint32_t) buffer->size().height();
|
2021-02-02 13:26:43 +00:00
|
|
|
data.stride = plane.stride;
|
2021-07-20 19:37:03 +00:00
|
|
|
data.format = buffer->format();
|
2021-02-02 13:26:43 +00:00
|
|
|
importedBuffer = gbm_bo_import(m_gpu->gbmDevice(), GBM_BO_IMPORT_FD, &data, GBM_BO_USE_SCANOUT);
|
|
|
|
}
|
|
|
|
if (!importedBuffer) {
|
2021-11-24 08:16:47 +00:00
|
|
|
sendFeedback();
|
2021-04-24 17:46:34 +00:00
|
|
|
if (errno != EINVAL) {
|
2021-06-20 15:36:15 +00:00
|
|
|
qCWarning(KWIN_DRM) << "Importing buffer for direct scanout failed:" << strerror(errno);
|
2021-04-24 17:46:34 +00:00
|
|
|
}
|
2021-02-02 13:26:43 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// damage tracking for screen casting
|
|
|
|
QRegion damage;
|
2021-11-24 08:16:47 +00:00
|
|
|
if (output.scanoutSurface == surface && buffer->size() == output.output->modeSize()) {
|
2021-02-04 08:40:20 +00:00
|
|
|
QRegion trackedDamage = surfaceItem->damage();
|
|
|
|
surfaceItem->resetDamage();
|
2021-02-02 13:26:43 +00:00
|
|
|
for (const auto &rect : trackedDamage) {
|
|
|
|
auto damageRect = QRect(rect);
|
|
|
|
damageRect.translate(output.output->geometry().topLeft());
|
|
|
|
damage |= damageRect;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
damage = output.output->geometry();
|
|
|
|
}
|
2021-05-25 17:55:15 +00:00
|
|
|
auto bo = QSharedPointer<DrmGbmBuffer>::create(m_gpu, importedBuffer, buffer);
|
2021-06-07 11:45:50 +00:00
|
|
|
// ensure that a context is current like with normal presentation
|
|
|
|
makeCurrent();
|
2021-05-25 17:55:15 +00:00
|
|
|
if (output.output->present(bo, damage)) {
|
2021-07-24 10:03:00 +00:00
|
|
|
output.current.damageJournal.clear();
|
2021-11-24 08:16:47 +00:00
|
|
|
if (output.scanoutSurface != surface) {
|
2021-05-30 23:58:47 +00:00
|
|
|
auto path = surface->client()->executablePath();
|
|
|
|
qCDebug(KWIN_DRM).nospace() << "Direct scanout starting on output " << output.output->name() << " for application \"" << path << "\"";
|
|
|
|
}
|
2021-11-24 08:16:47 +00:00
|
|
|
output.scanoutSurface = surface;
|
2021-05-30 23:58:47 +00:00
|
|
|
return true;
|
|
|
|
} else {
|
2021-11-24 08:16:47 +00:00
|
|
|
sendFeedback();
|
2021-05-30 23:58:47 +00:00
|
|
|
return false;
|
|
|
|
}
|
2021-02-02 13:26:43 +00:00
|
|
|
}
|
|
|
|
|
2021-07-26 23:11:50 +00:00
|
|
|
QSharedPointer<DrmBuffer> EglGbmBackend::renderTestFrame(DrmAbstractOutput *output)
|
2021-05-25 22:05:17 +00:00
|
|
|
{
|
2021-08-24 20:55:42 +00:00
|
|
|
beginFrame(output);
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
return endFrameWithBuffer(output, output->geometry());
|
2021-05-25 22:05:17 +00:00
|
|
|
}
|
|
|
|
|
2021-08-24 20:55:42 +00:00
|
|
|
QSharedPointer<GLTexture> EglGbmBackend::textureForOutput(AbstractOutput *output) const
|
2020-07-22 17:38:57 +00:00
|
|
|
{
|
2021-08-24 20:55:42 +00:00
|
|
|
Q_ASSERT(m_outputs.contains(output));
|
|
|
|
auto &renderOutput = m_outputs[output];
|
|
|
|
if (renderOutput.current.shadowBuffer) {
|
|
|
|
const auto glTexture = QSharedPointer<KWin::GLTexture>::create(renderOutput.current.shadowBuffer->texture(), GL_RGBA8, output->pixelSize());
|
2020-07-22 17:38:57 +00:00
|
|
|
glTexture->setYInverted(true);
|
|
|
|
return glTexture;
|
|
|
|
}
|
2021-08-24 20:55:42 +00:00
|
|
|
GbmBuffer *gbmBuffer = renderOutput.current.gbmSurface->currentBuffer().get();
|
2021-05-04 10:19:31 +00:00
|
|
|
if (!gbmBuffer) {
|
2021-05-25 17:55:15 +00:00
|
|
|
qCWarning(KWIN_DRM) << "Failed to record frame: No gbm buffer!";
|
2021-05-04 10:19:31 +00:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
EGLImageKHR image = eglCreateImageKHR(eglDisplay(), nullptr, EGL_NATIVE_PIXMAP_KHR, gbmBuffer->getBo(), nullptr);
|
2020-07-22 17:38:57 +00:00
|
|
|
if (image == EGL_NO_IMAGE_KHR) {
|
|
|
|
qCWarning(KWIN_DRM) << "Failed to record frame: Error creating EGLImageKHR - " << glGetError();
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2021-08-24 20:55:42 +00:00
|
|
|
return QSharedPointer<EGLImageTexture>::create(eglDisplay(), image, GL_RGBA8, static_cast<DrmAbstractOutput*>(output)->modeSize());
|
|
|
|
}
|
|
|
|
|
|
|
|
bool EglGbmBackend::directScanoutAllowed(AbstractOutput *output) const
|
|
|
|
{
|
2021-10-22 13:49:30 +00:00
|
|
|
return !m_backend->usesSoftwareCursor() && !output->directScanoutInhibited();
|
2020-07-22 17:38:57 +00:00
|
|
|
}
|
|
|
|
|
2021-08-24 20:55:42 +00:00
|
|
|
bool EglGbmBackend::hasOutput(AbstractOutput *output) const
|
2021-02-02 13:26:43 +00:00
|
|
|
{
|
2021-08-24 20:55:42 +00:00
|
|
|
return m_outputs.contains(output);
|
2021-02-02 13:26:43 +00:00
|
|
|
}
|
|
|
|
|
2021-09-17 09:01:18 +00:00
|
|
|
uint32_t EglGbmBackend::drmFormat() const
|
|
|
|
{
|
|
|
|
return m_gbmFormat;
|
|
|
|
}
|
|
|
|
|
2021-11-10 12:26:34 +00:00
|
|
|
DrmGpu *EglGbmBackend::gpu() const
|
|
|
|
{
|
|
|
|
return m_gpu;
|
|
|
|
}
|
|
|
|
|
|
|
|
EglGbmBackend *EglGbmBackend::renderingBackend()
|
|
|
|
{
|
|
|
|
return static_cast<EglGbmBackend*>(primaryBackend());
|
|
|
|
}
|
|
|
|
|
2019-12-10 00:24:45 +00:00
|
|
|
}
|