kwin/src/backends/drm/drm_egl_layer.cpp
Xaver Hugl 1f92dc5e5d backends/drm: correct test buffer check
There can be situations where the surface fits but there's no test buffer yet
2022-10-03 10:44:51 +00:00

188 lines
5.7 KiB
C++

/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2022 Xaver Hugl <xaver.hugl@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "drm_egl_layer.h"
#include "drm_abstract_output.h"
#include "drm_backend.h"
#include "drm_buffer_gbm.h"
#include "drm_egl_backend.h"
#include "drm_gpu.h"
#include "drm_logging.h"
#include "drm_output.h"
#include "drm_pipeline.h"
#include "egl_dmabuf.h"
#include "surfaceitem_wayland.h"
#include "wayland/linuxdmabufv1clientbuffer.h"
#include "wayland/surface_interface.h"
#include <QRegion>
#include <drm_fourcc.h>
#include <errno.h>
#include <gbm.h>
#include <unistd.h>
namespace KWin
{
EglGbmLayer::EglGbmLayer(EglGbmBackend *eglBackend, DrmPipeline *pipeline)
: DrmPipelineLayer(pipeline)
, m_surface(pipeline->gpu(), eglBackend)
, m_dmabufFeedback(pipeline->gpu(), eglBackend)
{
}
std::optional<OutputLayerBeginFrameInfo> EglGbmLayer::beginFrame()
{
m_scanoutBuffer.reset();
m_dmabufFeedback.renderingSurface();
return m_surface.startRendering(m_pipeline->bufferSize(), m_pipeline->renderOrientation(), m_pipeline->bufferOrientation(), m_pipeline->formats());
}
void EglGbmLayer::aboutToStartPainting(const QRegion &damagedRegion)
{
m_surface.aboutToStartPainting(m_pipeline->output(), damagedRegion);
}
bool EglGbmLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion)
{
Q_UNUSED(renderedRegion)
const auto ret = m_surface.endRendering(m_pipeline->renderOrientation(), damagedRegion);
if (ret.has_value()) {
std::tie(m_currentBuffer, m_currentDamage) = ret.value();
return m_currentBuffer != nullptr;
} else {
return false;
}
}
QRegion EglGbmLayer::currentDamage() const
{
return m_currentDamage;
}
bool EglGbmLayer::checkTestBuffer()
{
if (!m_currentBuffer || !m_surface.doesSurfaceFit(m_pipeline->bufferSize(), m_pipeline->formats())) {
const auto buffer = m_surface.renderTestBuffer(m_pipeline->bufferSize(), m_pipeline->formats());
if (!buffer) {
return false;
} else {
m_currentBuffer = buffer;
}
}
return true;
}
std::shared_ptr<GLTexture> EglGbmLayer::texture() const
{
if (m_scanoutBuffer) {
return m_surface.eglBackend()->importBufferObjectAsTexture(static_cast<GbmBuffer *>(m_scanoutBuffer->buffer())->bo());
} else {
return m_surface.texture();
}
}
static DrmPlane::Transformations invertAndConvertTransform(Output::Transform transform)
{
DrmPlane::Transformations ret;
switch (transform) {
case Output::Transform::Normal:
case Output::Transform::Flipped:
ret = DrmPlane::Transformation::Rotate0;
break;
case Output::Transform::Rotated90:
case Output::Transform::Flipped90:
ret = DrmPlane::Transformation::Rotate270;
break;
case Output::Transform::Rotated180:
case Output::Transform::Flipped180:
ret = DrmPlane::Transformation::Rotate180;
break;
case Output::Transform::Rotated270:
case Output::Transform::Flipped270:
ret = DrmPlane::Transformation::Rotate90;
break;
}
if (transform == Output::Transform::Flipped || transform == Output::Transform::Flipped180) {
ret |= DrmPlane::Transformation::ReflectX;
} else if (transform == Output::Transform::Flipped90 || transform == Output::Transform::Flipped270) {
ret |= DrmPlane::Transformation::ReflectY;
}
return ret;
}
bool EglGbmLayer::scanout(SurfaceItem *surfaceItem)
{
static bool valid;
static const bool directScanoutDisabled = qEnvironmentVariableIntValue("KWIN_DRM_NO_DIRECT_SCANOUT", &valid) == 1 && valid;
if (directScanoutDisabled) {
return false;
}
SurfaceItemWayland *item = qobject_cast<SurfaceItemWayland *>(surfaceItem);
if (!item || !item->surface()) {
return false;
}
const auto surface = item->surface();
if (invertAndConvertTransform(surface->bufferTransform()) != m_pipeline->bufferOrientation()) {
return false;
}
const auto buffer = qobject_cast<KWaylandServer::LinuxDmaBufV1ClientBuffer *>(surface->buffer());
if (!buffer || buffer->size() != m_pipeline->bufferSize()) {
return false;
}
const auto formats = m_pipeline->formats();
if (!formats.contains(buffer->format())) {
m_dmabufFeedback.scanoutFailed(surface, formats);
return false;
}
if (buffer->attributes().modifier == DRM_FORMAT_MOD_INVALID && m_pipeline->gpu()->platform()->gpuCount() > 1) {
// importing a buffer from another GPU without an explicit modifier can mess up the buffer format
return false;
}
if (!formats[buffer->format()].contains(buffer->attributes().modifier)) {
return false;
}
const auto gbmBuffer = GbmBuffer::importBuffer(m_pipeline->gpu(), buffer);
if (!gbmBuffer) {
m_dmabufFeedback.scanoutFailed(surface, formats);
return false;
}
m_scanoutBuffer = DrmFramebuffer::createFramebuffer(gbmBuffer);
if (m_scanoutBuffer && m_pipeline->testScanout()) {
m_dmabufFeedback.scanoutSuccessful(surface);
m_currentBuffer = m_scanoutBuffer;
m_currentDamage = surfaceItem->damage();
surfaceItem->resetDamage();
return true;
} else {
m_dmabufFeedback.scanoutFailed(surface, formats);
m_scanoutBuffer.reset();
return false;
}
}
std::shared_ptr<DrmFramebuffer> EglGbmLayer::currentBuffer() const
{
return m_scanoutBuffer ? m_scanoutBuffer : m_currentBuffer;
}
bool EglGbmLayer::hasDirectScanoutBuffer() const
{
return m_scanoutBuffer != nullptr;
}
void EglGbmLayer::releaseBuffers()
{
m_currentBuffer.reset();
m_scanoutBuffer.reset();
m_surface.destroyResources();
}
}