2021-05-25 22:05:17 +00:00
|
|
|
/*
|
|
|
|
KWin - the KDE window manager
|
|
|
|
This file is part of the KDE project.
|
|
|
|
|
|
|
|
SPDX-FileCopyrightText: 2021 Xaver Hugl <xaver.hugl@gmail.com>
|
|
|
|
|
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "drm_pipeline.h"
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
#include "logging.h"
|
|
|
|
#include "drm_gpu.h"
|
|
|
|
#include "drm_object_connector.h"
|
|
|
|
#include "drm_object_crtc.h"
|
|
|
|
#include "drm_object_plane.h"
|
|
|
|
#include "drm_buffer.h"
|
|
|
|
#include "cursor.h"
|
|
|
|
#include "session.h"
|
2021-07-26 23:11:50 +00:00
|
|
|
#include "drm_output.h"
|
2021-05-25 22:05:17 +00:00
|
|
|
#include "drm_backend.h"
|
|
|
|
#include "egl_gbm_backend.h"
|
|
|
|
#include "drm_buffer_gbm.h"
|
|
|
|
|
2021-11-10 11:41:57 +00:00
|
|
|
#include <gbm.h>
|
2021-08-26 16:17:31 +00:00
|
|
|
#include <drm_fourcc.h>
|
|
|
|
|
2021-05-25 22:05:17 +00:00
|
|
|
namespace KWin
|
|
|
|
{
|
|
|
|
|
2021-09-28 08:29:56 +00:00
|
|
|
DrmPipeline::DrmPipeline(DrmConnector *conn)
|
2021-08-07 22:03:09 +00:00
|
|
|
: m_output(nullptr)
|
2021-05-25 22:05:17 +00:00
|
|
|
, m_connector(conn)
|
|
|
|
{
|
2021-08-07 18:06:40 +00:00
|
|
|
}
|
|
|
|
|
2021-09-28 08:29:56 +00:00
|
|
|
DrmPipeline::~DrmPipeline()
|
2021-05-25 22:05:17 +00:00
|
|
|
{
|
2021-10-08 08:52:01 +00:00
|
|
|
m_output = nullptr;
|
|
|
|
if (m_pageflipPending && m_current.crtc) {
|
|
|
|
pageFlipped({});
|
|
|
|
}
|
2021-05-25 22:05:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool DrmPipeline::present(const QSharedPointer<DrmBuffer> &buffer)
|
|
|
|
{
|
2021-09-28 08:29:56 +00:00
|
|
|
Q_ASSERT(pending.crtc);
|
2021-11-29 10:48:32 +00:00
|
|
|
Q_ASSERT(buffer);
|
2021-05-25 22:05:17 +00:00
|
|
|
m_primaryBuffer = buffer;
|
2021-11-10 11:41:57 +00:00
|
|
|
auto buf = dynamic_cast<DrmGbmBuffer*>(buffer.data());
|
2021-10-08 08:52:01 +00:00
|
|
|
// with direct scanout disallow modesets, calling presentFailed() and logging warnings
|
2021-11-10 11:41:57 +00:00
|
|
|
bool directScanout = buf && buf->clientBuffer();
|
2021-10-08 08:52:01 +00:00
|
|
|
if (gpu()->needsModeset()) {
|
|
|
|
if (directScanout) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
m_modesetPresentPending = true;
|
|
|
|
return gpu()->maybeModeset();
|
|
|
|
}
|
2021-09-28 08:29:56 +00:00
|
|
|
if (gpu()->atomicModeSetting()) {
|
|
|
|
if (!commitPipelines({this}, CommitMode::Commit)) {
|
2021-06-03 13:50:37 +00:00
|
|
|
// update properties and try again
|
2021-11-22 10:33:26 +00:00
|
|
|
m_connector->updateProperties();
|
|
|
|
if (pending.crtc) {
|
|
|
|
pending.crtc->updateProperties();
|
|
|
|
if (pending.crtc->primaryPlane()) {
|
|
|
|
pending.crtc->primaryPlane()->updateProperties();
|
|
|
|
}
|
2021-12-02 16:51:08 +00:00
|
|
|
if (pending.crtc->cursorPlane()) {
|
|
|
|
pending.crtc->cursorPlane()->updateProperties();
|
|
|
|
}
|
2021-11-22 10:33:26 +00:00
|
|
|
}
|
2021-09-28 08:29:56 +00:00
|
|
|
if (!commitPipelines({this}, CommitMode::Commit)) {
|
2021-10-08 08:52:01 +00:00
|
|
|
if (directScanout) {
|
|
|
|
return false;
|
|
|
|
}
|
2021-06-03 13:50:37 +00:00
|
|
|
qCWarning(KWIN_DRM) << "Atomic present failed!" << strerror(errno);
|
|
|
|
printDebugInfo();
|
2021-10-08 08:52:01 +00:00
|
|
|
if (m_output) {
|
|
|
|
m_output->presentFailed();
|
|
|
|
}
|
2021-06-03 13:50:37 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!presentLegacy()) {
|
|
|
|
qCWarning(KWIN_DRM) << "Present failed!" << strerror(errno);
|
2021-10-08 08:52:01 +00:00
|
|
|
if (m_output) {
|
|
|
|
m_output->presentFailed();
|
|
|
|
}
|
2021-06-03 13:50:37 +00:00
|
|
|
return false;
|
|
|
|
}
|
2021-05-25 22:05:17 +00:00
|
|
|
}
|
2021-06-03 13:50:37 +00:00
|
|
|
return true;
|
2021-05-25 22:05:17 +00:00
|
|
|
}
|
|
|
|
|
2021-10-08 09:22:02 +00:00
|
|
|
bool DrmPipeline::commitPipelines(const QVector<DrmPipeline*> &pipelines, CommitMode mode, const QVector<DrmObject*> &unusedObjects)
|
2021-05-25 22:05:17 +00:00
|
|
|
{
|
2021-08-07 18:06:40 +00:00
|
|
|
Q_ASSERT(!pipelines.isEmpty());
|
2021-09-28 08:29:56 +00:00
|
|
|
if (pipelines[0]->gpu()->atomicModeSetting()) {
|
2021-12-07 11:16:53 +00:00
|
|
|
return commitPipelinesAtomic(pipelines, mode, unusedObjects);
|
|
|
|
} else {
|
|
|
|
return commitPipelinesLegacy(pipelines, mode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DrmPipeline::commitPipelinesAtomic(const QVector<DrmPipeline*> &pipelines, CommitMode mode, const QVector<DrmObject*> &unusedObjects)
|
|
|
|
{
|
|
|
|
drmModeAtomicReq *req = drmModeAtomicAlloc();
|
|
|
|
if (!req) {
|
2021-11-18 11:20:58 +00:00
|
|
|
qCCritical(KWIN_DRM) << "Failed to allocate drmModeAtomicReq!" << strerror(errno);
|
2021-12-07 11:16:53 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
uint32_t flags = 0;
|
|
|
|
const auto &failed = [pipelines, req, &flags, unusedObjects](){
|
|
|
|
drmModeAtomicFree(req);
|
|
|
|
printFlags(flags);
|
2021-08-07 18:06:40 +00:00
|
|
|
for (const auto &pipeline : pipelines) {
|
2021-12-07 11:16:53 +00:00
|
|
|
pipeline->printDebugInfo();
|
|
|
|
pipeline->atomicCommitFailed();
|
2021-10-08 09:22:02 +00:00
|
|
|
}
|
2021-12-07 11:16:53 +00:00
|
|
|
for (const auto &obj : unusedObjects) {
|
|
|
|
printProps(obj, PrintMode::OnlyChanged);
|
|
|
|
obj->rollbackPending();
|
2021-10-08 08:52:01 +00:00
|
|
|
}
|
2021-12-07 11:16:53 +00:00
|
|
|
return false;
|
|
|
|
};
|
|
|
|
for (const auto &pipeline : pipelines) {
|
|
|
|
if (!pipeline->checkTestBuffer()) {
|
|
|
|
qCWarning(KWIN_DRM) << "Checking test buffer failed for" << mode;
|
2021-09-09 10:52:04 +00:00
|
|
|
return failed();
|
2021-05-25 22:05:17 +00:00
|
|
|
}
|
2021-12-07 11:16:53 +00:00
|
|
|
if (!pipeline->populateAtomicValues(req, flags)) {
|
|
|
|
qCWarning(KWIN_DRM) << "Populating atomic values failed for" << mode;
|
2021-09-09 10:52:04 +00:00
|
|
|
return failed();
|
|
|
|
}
|
2021-12-07 11:16:53 +00:00
|
|
|
}
|
|
|
|
for (const auto &unused : unusedObjects) {
|
|
|
|
unused->disable();
|
|
|
|
if (unused->needsModeset()) {
|
|
|
|
flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
|
2021-05-25 22:05:17 +00:00
|
|
|
}
|
2021-12-07 11:16:53 +00:00
|
|
|
if (!unused->atomicPopulate(req)) {
|
|
|
|
qCWarning(KWIN_DRM) << "Populating atomic values failed for unused resource" << unused;
|
|
|
|
return failed();
|
2021-10-08 09:22:02 +00:00
|
|
|
}
|
2021-12-07 11:16:53 +00:00
|
|
|
}
|
|
|
|
bool modeset = flags & DRM_MODE_ATOMIC_ALLOW_MODESET;
|
|
|
|
Q_ASSERT(!modeset || mode != CommitMode::Commit);
|
|
|
|
if (modeset) {
|
|
|
|
// The kernel fails commits with DRM_MODE_PAGE_FLIP_EVENT when a crtc is disabled in the commit
|
|
|
|
// and already was disabled before, to work around some quirks in old userspace.
|
|
|
|
// Instead of using DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_NONBLOCK, do the modeset in a blocking
|
|
|
|
// fashion without page flip events and directly call the pageFlipped method afterwards
|
|
|
|
flags = flags & (~DRM_MODE_PAGE_FLIP_EVENT);
|
2021-05-25 22:05:17 +00:00
|
|
|
} else {
|
2021-12-07 11:16:53 +00:00
|
|
|
flags |= DRM_MODE_ATOMIC_NONBLOCK;
|
|
|
|
}
|
|
|
|
if (drmModeAtomicCommit(pipelines[0]->gpu()->fd(), req, (flags & (~DRM_MODE_PAGE_FLIP_EVENT)) | DRM_MODE_ATOMIC_TEST_ONLY, nullptr) != 0) {
|
2021-11-18 11:20:58 +00:00
|
|
|
qCDebug(KWIN_DRM) << "Atomic test for" << mode << "failed!" << strerror(errno);
|
2021-12-07 11:16:53 +00:00
|
|
|
return failed();
|
|
|
|
}
|
|
|
|
if (mode != CommitMode::Test && drmModeAtomicCommit(pipelines[0]->gpu()->fd(), req, flags, nullptr) != 0) {
|
2021-11-18 11:20:58 +00:00
|
|
|
qCCritical(KWIN_DRM) << "Atomic commit failed! This should never happen!" << strerror(errno);
|
2021-12-07 11:16:53 +00:00
|
|
|
return failed();
|
|
|
|
}
|
|
|
|
for (const auto &pipeline : pipelines) {
|
2021-12-07 11:34:47 +00:00
|
|
|
pipeline->atomicCommitSuccessful(mode);
|
2021-12-07 11:16:53 +00:00
|
|
|
}
|
|
|
|
for (const auto &obj : unusedObjects) {
|
|
|
|
obj->commitPending();
|
|
|
|
if (mode != CommitMode::Test) {
|
|
|
|
obj->commit();
|
2021-09-28 08:29:56 +00:00
|
|
|
}
|
2021-05-25 22:05:17 +00:00
|
|
|
}
|
2021-12-07 11:16:53 +00:00
|
|
|
drmModeAtomicFree(req);
|
|
|
|
return true;
|
2021-05-25 22:05:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool DrmPipeline::populateAtomicValues(drmModeAtomicReq *req, uint32_t &flags)
|
|
|
|
{
|
2021-09-28 08:29:56 +00:00
|
|
|
if (needsModeset()) {
|
2021-12-14 14:37:23 +00:00
|
|
|
prepareAtomicModeset();
|
2021-05-25 22:05:17 +00:00
|
|
|
flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
|
2021-09-28 08:29:56 +00:00
|
|
|
}
|
2021-11-10 11:17:40 +00:00
|
|
|
if (activePending()) {
|
2021-09-28 08:29:56 +00:00
|
|
|
flags |= DRM_MODE_PAGE_FLIP_EVENT;
|
2021-05-25 22:05:17 +00:00
|
|
|
}
|
2021-09-28 08:29:56 +00:00
|
|
|
if (pending.crtc) {
|
2021-12-02 16:51:08 +00:00
|
|
|
pending.crtc->setPending(DrmCrtc::PropertyIndex::VrrEnabled, pending.syncMode == RenderLoopPrivate::SyncMode::Adaptive);
|
|
|
|
pending.crtc->setPending(DrmCrtc::PropertyIndex::Gamma_LUT, pending.gamma ? pending.gamma->blobId() : 0);
|
2021-09-28 08:30:57 +00:00
|
|
|
auto modeSize = m_connector->modes()[pending.modeIndex]->size();
|
2021-09-28 08:29:56 +00:00
|
|
|
pending.crtc->primaryPlane()->set(QPoint(0, 0), m_primaryBuffer ? m_primaryBuffer->size() : modeSize, QPoint(0, 0), modeSize);
|
|
|
|
pending.crtc->primaryPlane()->setBuffer(activePending() ? m_primaryBuffer.get() : nullptr);
|
2021-12-02 16:51:08 +00:00
|
|
|
|
|
|
|
if (pending.crtc->cursorPlane()) {
|
|
|
|
pending.crtc->cursorPlane()->set(QPoint(0, 0), gpu()->cursorSize(), pending.cursorPos, gpu()->cursorSize());
|
|
|
|
pending.crtc->cursorPlane()->setBuffer(activePending() ? pending.cursorBo.get() : nullptr);
|
|
|
|
pending.crtc->cursorPlane()->setPending(DrmPlane::PropertyIndex::CrtcId, (activePending() && pending.cursorBo) ? pending.crtc->id() : 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!m_connector->atomicPopulate(req)) {
|
|
|
|
return false;
|
2021-05-25 22:05:17 +00:00
|
|
|
}
|
2021-12-02 16:51:08 +00:00
|
|
|
if (pending.crtc) {
|
|
|
|
if (!pending.crtc->atomicPopulate(req)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!pending.crtc->primaryPlane()->atomicPopulate(req)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (pending.crtc->cursorPlane() && !pending.crtc->cursorPlane()->atomicPopulate(req)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
2021-05-25 22:05:17 +00:00
|
|
|
}
|
|
|
|
|
2021-12-14 14:37:23 +00:00
|
|
|
void DrmPipeline::prepareAtomicModeset()
|
|
|
|
{
|
|
|
|
if (!pending.crtc) {
|
|
|
|
m_connector->setPending(DrmConnector::PropertyIndex::CrtcId, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
auto mode = m_connector->modes()[pending.modeIndex];
|
|
|
|
|
|
|
|
m_connector->setPending(DrmConnector::PropertyIndex::CrtcId, activePending() ? pending.crtc->id() : 0);
|
|
|
|
if (const auto &prop = m_connector->getProp(DrmConnector::PropertyIndex::Broadcast_RGB)) {
|
|
|
|
prop->setEnum(pending.rgbRange);
|
|
|
|
}
|
|
|
|
|
|
|
|
pending.crtc->setPending(DrmCrtc::PropertyIndex::Active, activePending());
|
|
|
|
pending.crtc->setPending(DrmCrtc::PropertyIndex::ModeId, activePending() ? mode->blobId() : 0);
|
|
|
|
|
|
|
|
pending.crtc->primaryPlane()->setPending(DrmPlane::PropertyIndex::CrtcId, activePending() ? pending.crtc->id() : 0);
|
|
|
|
pending.crtc->primaryPlane()->setTransformation(DrmPlane::Transformation::Rotate0);
|
|
|
|
if (pending.crtc->cursorPlane()) {
|
|
|
|
pending.crtc->cursorPlane()->setTransformation(DrmPlane::Transformation::Rotate0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-07 11:16:53 +00:00
|
|
|
void DrmPipeline::atomicCommitFailed()
|
2021-05-25 22:05:17 +00:00
|
|
|
{
|
2021-12-07 11:16:53 +00:00
|
|
|
if (m_oldTestBuffer) {
|
|
|
|
m_primaryBuffer = m_oldTestBuffer;
|
|
|
|
m_oldTestBuffer = nullptr;
|
2021-05-25 22:05:17 +00:00
|
|
|
}
|
2021-12-07 11:16:53 +00:00
|
|
|
m_connector->rollbackPending();
|
|
|
|
if (pending.crtc) {
|
|
|
|
pending.crtc->rollbackPending();
|
|
|
|
pending.crtc->primaryPlane()->rollbackPending();
|
|
|
|
if (pending.crtc->cursorPlane()) {
|
|
|
|
pending.crtc->cursorPlane()->rollbackPending();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-07 11:34:47 +00:00
|
|
|
void DrmPipeline::atomicCommitSuccessful(CommitMode mode)
|
2021-12-07 11:16:53 +00:00
|
|
|
{
|
|
|
|
m_oldTestBuffer = nullptr;
|
|
|
|
m_connector->commitPending();
|
|
|
|
if (pending.crtc) {
|
|
|
|
pending.crtc->commitPending();
|
|
|
|
pending.crtc->primaryPlane()->commitPending();
|
|
|
|
if (pending.crtc->cursorPlane()) {
|
|
|
|
pending.crtc->cursorPlane()->commitPending();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (mode != CommitMode::Test) {
|
|
|
|
if (activePending()) {
|
|
|
|
m_pageflipPending = true;
|
|
|
|
}
|
|
|
|
m_connector->commit();
|
|
|
|
if (pending.crtc) {
|
|
|
|
pending.crtc->commit();
|
|
|
|
pending.crtc->primaryPlane()->setNext(m_primaryBuffer);
|
|
|
|
pending.crtc->primaryPlane()->commit();
|
|
|
|
if (pending.crtc->cursorPlane()) {
|
|
|
|
pending.crtc->cursorPlane()->setNext(pending.cursorBo);
|
|
|
|
pending.crtc->cursorPlane()->commit();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_current = pending;
|
|
|
|
if (mode == CommitMode::CommitModeset && activePending()) {
|
|
|
|
pageFlipped(std::chrono::steady_clock::now().time_since_epoch());
|
|
|
|
}
|
2021-05-25 22:05:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DrmPipeline::checkTestBuffer()
|
|
|
|
{
|
2021-09-28 08:29:56 +00:00
|
|
|
if (!pending.crtc || (m_primaryBuffer && m_primaryBuffer->size() == sourceSize())) {
|
2021-05-25 22:05:17 +00:00
|
|
|
return true;
|
|
|
|
}
|
2021-09-28 08:29:56 +00:00
|
|
|
auto backend = gpu()->eglBackend();
|
2021-09-16 11:17:42 +00:00
|
|
|
QSharedPointer<DrmBuffer> buffer;
|
2021-09-17 09:01:18 +00:00
|
|
|
// try to re-use buffers if possible.
|
|
|
|
const auto &checkBuffer = [this, backend, &buffer](const QSharedPointer<DrmBuffer> &buf){
|
2021-09-20 12:27:05 +00:00
|
|
|
const auto &mods = supportedModifiers(buf->format());
|
2021-11-10 13:47:13 +00:00
|
|
|
if (backend && buf->format() == backend->drmFormat(m_output)
|
2021-09-20 12:27:05 +00:00
|
|
|
&& (mods.isEmpty() || mods.contains(buf->modifier()))
|
2021-09-17 09:01:18 +00:00
|
|
|
&& buf->size() == sourceSize()) {
|
|
|
|
buffer = buf;
|
|
|
|
}
|
|
|
|
};
|
2021-09-28 08:29:56 +00:00
|
|
|
if (pending.crtc->primaryPlane() && pending.crtc->primaryPlane()->next()) {
|
|
|
|
checkBuffer(pending.crtc->primaryPlane()->next());
|
|
|
|
} else if (pending.crtc->primaryPlane() && pending.crtc->primaryPlane()->current()) {
|
|
|
|
checkBuffer(pending.crtc->primaryPlane()->current());
|
|
|
|
} else if (pending.crtc->next()) {
|
|
|
|
checkBuffer(pending.crtc->next());
|
|
|
|
} else if (pending.crtc->current()) {
|
|
|
|
checkBuffer(pending.crtc->current());
|
2021-09-17 09:01:18 +00:00
|
|
|
}
|
|
|
|
// if we don't have a fitting buffer already, get or create one
|
2021-11-10 11:41:57 +00:00
|
|
|
if (!buffer) {
|
|
|
|
if (backend && m_output) {
|
|
|
|
buffer = backend->renderTestFrame(m_output);
|
|
|
|
} else if (backend && gpu()->gbmDevice()) {
|
2021-12-02 16:51:08 +00:00
|
|
|
gbm_bo *bo = gbm_bo_create(gpu()->gbmDevice(), sourceSize().width(), sourceSize().height(), DRM_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
|
2021-11-10 11:41:57 +00:00
|
|
|
if (!bo) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
buffer = QSharedPointer<DrmGbmBuffer>::create(gpu(), bo, nullptr);
|
|
|
|
} else {
|
2021-12-02 16:51:08 +00:00
|
|
|
buffer = QSharedPointer<DrmDumbBuffer>::create(gpu(), sourceSize(), DRM_FORMAT_XRGB8888);
|
2021-05-25 22:05:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (buffer && buffer->bufferId()) {
|
|
|
|
m_oldTestBuffer = m_primaryBuffer;
|
|
|
|
m_primaryBuffer = buffer;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-08-02 09:03:07 +00:00
|
|
|
bool DrmPipeline::setCursor(const QSharedPointer<DrmDumbBuffer> &buffer, const QPoint &hotspot)
|
2021-05-25 22:05:17 +00:00
|
|
|
{
|
2021-12-07 11:34:47 +00:00
|
|
|
if (pending.cursorBo == buffer && pending.cursorHotspot == hotspot) {
|
|
|
|
return true;
|
|
|
|
}
|
2021-12-02 16:51:08 +00:00
|
|
|
bool result;
|
|
|
|
const bool visibleBefore = isCursorVisible();
|
2021-12-07 11:34:47 +00:00
|
|
|
pending.cursorBo = buffer;
|
|
|
|
pending.cursorHotspot = hotspot;
|
2021-12-02 16:51:08 +00:00
|
|
|
// explicitly check for the cursor plane and not for AMS, as we might not always have one
|
|
|
|
if (pending.crtc->cursorPlane()) {
|
|
|
|
result = commitPipelines({this}, CommitMode::Test);
|
|
|
|
} else {
|
2021-12-07 11:34:47 +00:00
|
|
|
result = setCursorLegacy();
|
2021-12-02 16:51:08 +00:00
|
|
|
}
|
2021-12-07 11:34:47 +00:00
|
|
|
if (result) {
|
|
|
|
m_next = pending;
|
|
|
|
if (m_output && (visibleBefore || isCursorVisible())) {
|
|
|
|
m_output->renderLoop()->scheduleRepaint();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
pending = m_next;
|
2021-12-02 16:51:08 +00:00
|
|
|
}
|
|
|
|
return result;
|
2021-05-25 22:05:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool DrmPipeline::moveCursor(QPoint pos)
|
|
|
|
{
|
2021-12-07 11:34:47 +00:00
|
|
|
if (pending.cursorPos == pos) {
|
|
|
|
return true;
|
|
|
|
}
|
2021-12-02 16:51:08 +00:00
|
|
|
const bool visibleBefore = isCursorVisible();
|
2021-12-09 19:28:00 +00:00
|
|
|
bool result;
|
|
|
|
pending.cursorPos = pos;
|
2021-12-02 16:51:08 +00:00
|
|
|
// explicitly check for the cursor plane and not for AMS, as we might not always have one
|
|
|
|
if (pending.crtc->cursorPlane()) {
|
|
|
|
result = commitPipelines({this}, CommitMode::Test);
|
|
|
|
} else {
|
2021-12-07 11:34:47 +00:00
|
|
|
result = moveCursorLegacy();
|
2021-12-02 16:51:08 +00:00
|
|
|
}
|
2021-12-07 11:34:47 +00:00
|
|
|
if (result) {
|
|
|
|
m_next = pending;
|
|
|
|
if (m_output && (visibleBefore || isCursorVisible())) {
|
|
|
|
m_output->renderLoop()->scheduleRepaint();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
pending = m_next;
|
2021-12-02 16:51:08 +00:00
|
|
|
}
|
|
|
|
return result;
|
2021-05-25 22:05:17 +00:00
|
|
|
}
|
|
|
|
|
2021-09-28 08:29:56 +00:00
|
|
|
void DrmPipeline::applyPendingChanges()
|
|
|
|
{
|
|
|
|
if (!pending.crtc) {
|
|
|
|
pending.active = false;
|
2021-05-25 22:05:17 +00:00
|
|
|
}
|
2021-09-28 08:29:56 +00:00
|
|
|
m_next = pending;
|
2021-05-25 22:05:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QSize DrmPipeline::sourceSize() const
|
|
|
|
{
|
2021-09-28 08:29:56 +00:00
|
|
|
auto mode = m_connector->modes()[pending.modeIndex];
|
|
|
|
if (pending.transformation & (DrmPlane::Transformation::Rotate90 | DrmPlane::Transformation::Rotate270)) {
|
2021-09-28 08:30:57 +00:00
|
|
|
return mode->size().transposed();
|
2021-05-25 22:05:17 +00:00
|
|
|
}
|
2021-09-28 08:30:57 +00:00
|
|
|
return mode->size();
|
2021-05-25 22:05:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool DrmPipeline::isCursorVisible() const
|
|
|
|
{
|
2021-12-02 16:51:08 +00:00
|
|
|
const QRect mode = QRect(QPoint(), m_connector->modes()[pending.modeIndex]->size());
|
2021-12-07 11:34:47 +00:00
|
|
|
return pending.cursorBo && QRect(pending.cursorPos, pending.cursorBo->size()).intersects(mode);
|
2021-05-25 22:05:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
DrmConnector *DrmPipeline::connector() const
|
|
|
|
{
|
|
|
|
return m_connector;
|
|
|
|
}
|
|
|
|
|
2021-09-28 08:29:56 +00:00
|
|
|
DrmGpu *DrmPipeline::gpu() const
|
2021-05-25 22:05:17 +00:00
|
|
|
{
|
2021-09-28 08:29:56 +00:00
|
|
|
return m_connector->gpu();
|
2021-05-25 22:05:17 +00:00
|
|
|
}
|
|
|
|
|
2021-10-08 08:52:01 +00:00
|
|
|
void DrmPipeline::pageFlipped(std::chrono::nanoseconds timestamp)
|
2021-05-25 22:05:17 +00:00
|
|
|
{
|
2021-09-28 08:29:56 +00:00
|
|
|
m_current.crtc->flipBuffer();
|
|
|
|
if (m_current.crtc->primaryPlane()) {
|
|
|
|
m_current.crtc->primaryPlane()->flipBuffer();
|
2021-05-25 22:05:17 +00:00
|
|
|
}
|
2021-12-02 16:51:08 +00:00
|
|
|
if (m_current.crtc->cursorPlane()) {
|
|
|
|
m_current.crtc->cursorPlane()->flipBuffer();
|
|
|
|
}
|
2021-10-08 08:52:01 +00:00
|
|
|
m_pageflipPending = false;
|
|
|
|
if (m_output) {
|
|
|
|
m_output->pageFlipped(timestamp);
|
|
|
|
}
|
2021-05-25 22:05:17 +00:00
|
|
|
}
|
|
|
|
|
2021-08-07 22:03:09 +00:00
|
|
|
void DrmPipeline::setOutput(DrmOutput *output)
|
2021-05-25 22:05:17 +00:00
|
|
|
{
|
2021-08-07 22:03:09 +00:00
|
|
|
m_output = output;
|
|
|
|
}
|
|
|
|
|
|
|
|
DrmOutput *DrmPipeline::output() const
|
|
|
|
{
|
|
|
|
return m_output;
|
2021-05-25 22:05:17 +00:00
|
|
|
}
|
|
|
|
|
2021-12-30 18:36:54 +00:00
|
|
|
static const QMap<uint32_t, QVector<uint64_t>> legacyFormats = {
|
|
|
|
{DRM_FORMAT_XRGB8888, {}},
|
|
|
|
{DRM_FORMAT_ARGB8888, {}}
|
|
|
|
};
|
|
|
|
|
2021-07-31 13:32:50 +00:00
|
|
|
bool DrmPipeline::isFormatSupported(uint32_t drmFormat) const
|
|
|
|
{
|
2021-11-10 15:18:53 +00:00
|
|
|
if (pending.crtc) {
|
|
|
|
if (pending.crtc->primaryPlane()) {
|
|
|
|
return pending.crtc->primaryPlane()->formats().contains(drmFormat);
|
|
|
|
} else {
|
2021-12-30 18:36:54 +00:00
|
|
|
return legacyFormats.keys().contains(drmFormat);
|
2021-11-10 15:18:53 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
2021-07-31 13:32:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QVector<uint64_t> DrmPipeline::supportedModifiers(uint32_t drmFormat) const
|
|
|
|
{
|
2021-11-10 15:18:53 +00:00
|
|
|
if (pending.crtc && pending.crtc->primaryPlane()) {
|
|
|
|
return pending.crtc->primaryPlane()->formats()[drmFormat];
|
|
|
|
} else {
|
|
|
|
return {};
|
|
|
|
}
|
2021-07-31 13:32:50 +00:00
|
|
|
}
|
|
|
|
|
2021-12-30 18:36:54 +00:00
|
|
|
QMap<uint32_t, QVector<uint64_t>> DrmPipeline::supportedFormats() const
|
|
|
|
{
|
|
|
|
if (pending.crtc) {
|
|
|
|
if (pending.crtc->primaryPlane()) {
|
|
|
|
return pending.crtc->primaryPlane()->formats();
|
|
|
|
} else {
|
|
|
|
return legacyFormats;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-28 08:29:56 +00:00
|
|
|
bool DrmPipeline::needsModeset() const
|
|
|
|
{
|
|
|
|
return pending.crtc != m_current.crtc
|
|
|
|
|| pending.active != m_current.active
|
|
|
|
|| pending.modeIndex != m_current.modeIndex
|
|
|
|
|| pending.rgbRange != m_current.rgbRange
|
2021-10-08 08:52:01 +00:00
|
|
|
|| pending.transformation != m_current.transformation
|
|
|
|
|| m_modesetPresentPending;
|
2021-09-28 08:29:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool DrmPipeline::activePending() const
|
|
|
|
{
|
|
|
|
return pending.crtc && pending.active;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DrmPipeline::revertPendingChanges()
|
|
|
|
{
|
|
|
|
pending = m_next;
|
|
|
|
}
|
|
|
|
|
2021-10-08 08:52:01 +00:00
|
|
|
bool DrmPipeline::pageflipPending() const
|
|
|
|
{
|
|
|
|
return m_pageflipPending;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DrmPipeline::modesetPresentPending() const
|
|
|
|
{
|
|
|
|
return m_modesetPresentPending;
|
|
|
|
}
|
|
|
|
|
2021-11-25 09:15:50 +00:00
|
|
|
void DrmPipeline::resetModesetPresentPending()
|
|
|
|
{
|
|
|
|
m_modesetPresentPending = false;
|
|
|
|
}
|
|
|
|
|
2021-10-08 08:52:01 +00:00
|
|
|
DrmCrtc *DrmPipeline::currentCrtc() const
|
|
|
|
{
|
|
|
|
return m_current.crtc;
|
|
|
|
}
|
|
|
|
|
2021-09-28 08:29:56 +00:00
|
|
|
DrmGammaRamp::DrmGammaRamp(DrmGpu *gpu, const GammaRamp &lut)
|
2021-10-22 10:17:59 +00:00
|
|
|
: m_gpu(gpu)
|
|
|
|
, m_lut(lut)
|
2021-09-28 08:29:56 +00:00
|
|
|
{
|
|
|
|
if (gpu->atomicModeSetting()) {
|
2021-10-22 10:17:59 +00:00
|
|
|
QVector<drm_color_lut> atomicLut(lut.size());
|
|
|
|
for (uint32_t i = 0; i < lut.size(); i++) {
|
2021-09-28 08:29:56 +00:00
|
|
|
atomicLut[i].red = lut.red()[i];
|
|
|
|
atomicLut[i].green = lut.green()[i];
|
|
|
|
atomicLut[i].blue = lut.blue()[i];
|
|
|
|
}
|
2021-10-22 10:17:59 +00:00
|
|
|
if (drmModeCreatePropertyBlob(gpu->fd(), atomicLut.data(), sizeof(drm_color_lut) * lut.size(), &m_blobId) != 0) {
|
|
|
|
qCWarning(KWIN_DRM) << "Failed to create gamma blob!" << strerror(errno);
|
|
|
|
}
|
2021-09-28 08:29:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DrmGammaRamp::~DrmGammaRamp()
|
|
|
|
{
|
2021-10-22 10:17:59 +00:00
|
|
|
if (m_blobId != 0) {
|
|
|
|
drmModeDestroyPropertyBlob(m_gpu->fd(), m_blobId);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t DrmGammaRamp::blobId() const
|
|
|
|
{
|
|
|
|
return m_blobId;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t DrmGammaRamp::size() const
|
|
|
|
{
|
|
|
|
return m_lut.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t *DrmGammaRamp::red() const
|
|
|
|
{
|
|
|
|
return const_cast<uint16_t*>(m_lut.red());
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t *DrmGammaRamp::green() const
|
|
|
|
{
|
|
|
|
return const_cast<uint16_t*>(m_lut.green());
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t *DrmGammaRamp::blue() const
|
|
|
|
{
|
|
|
|
return const_cast<uint16_t*>(m_lut.blue());
|
2021-09-28 08:29:56 +00:00
|
|
|
}
|
|
|
|
|
2021-10-20 08:43:35 +00:00
|
|
|
void DrmPipeline::printFlags(uint32_t flags)
|
|
|
|
{
|
|
|
|
if (flags == 0) {
|
2021-11-18 11:20:58 +00:00
|
|
|
qCDebug(KWIN_DRM) << "Flags: none";
|
2021-10-20 08:43:35 +00:00
|
|
|
} else {
|
2021-11-18 11:20:58 +00:00
|
|
|
qCDebug(KWIN_DRM) << "Flags:";
|
2021-10-20 08:43:35 +00:00
|
|
|
if (flags & DRM_MODE_PAGE_FLIP_EVENT) {
|
2021-11-18 11:20:58 +00:00
|
|
|
qCDebug(KWIN_DRM) << "\t DRM_MODE_PAGE_FLIP_EVENT";
|
2021-10-20 08:43:35 +00:00
|
|
|
}
|
|
|
|
if (flags & DRM_MODE_ATOMIC_ALLOW_MODESET) {
|
2021-11-18 11:20:58 +00:00
|
|
|
qCDebug(KWIN_DRM) << "\t DRM_MODE_ATOMIC_ALLOW_MODESET";
|
2021-10-20 08:43:35 +00:00
|
|
|
}
|
|
|
|
if (flags & DRM_MODE_PAGE_FLIP_ASYNC) {
|
2021-11-18 11:20:58 +00:00
|
|
|
qCDebug(KWIN_DRM) << "\t DRM_MODE_PAGE_FLIP_ASYNC";
|
2021-10-20 08:43:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DrmPipeline::printProps(DrmObject *object, PrintMode mode)
|
2021-05-25 22:05:17 +00:00
|
|
|
{
|
|
|
|
auto list = object->properties();
|
2021-10-20 08:43:35 +00:00
|
|
|
bool any = mode == PrintMode::All || std::any_of(list.constBegin(), list.constEnd(), [](const auto &prop){
|
|
|
|
return prop && !prop->isImmutable() && prop->needsCommit();
|
|
|
|
});
|
|
|
|
if (!any) {
|
|
|
|
return;
|
|
|
|
}
|
2021-11-18 11:20:58 +00:00
|
|
|
qCDebug(KWIN_DRM) << object->typeName() << object->id();
|
2021-05-25 22:05:17 +00:00
|
|
|
for (const auto &prop : list) {
|
|
|
|
if (prop) {
|
2021-09-16 10:37:44 +00:00
|
|
|
uint64_t current = prop->name().startsWith("SRC_") ? prop->current() >> 16 : prop->current();
|
2021-05-25 22:05:17 +00:00
|
|
|
if (prop->isImmutable() || !prop->needsCommit()) {
|
2021-10-20 08:43:35 +00:00
|
|
|
if (mode == PrintMode::All) {
|
2021-11-18 11:20:58 +00:00
|
|
|
qCDebug(KWIN_DRM).nospace() << "\t" << prop->name() << ": " << current;
|
2021-10-20 08:43:35 +00:00
|
|
|
}
|
2021-05-25 22:05:17 +00:00
|
|
|
} else {
|
2021-09-16 10:37:44 +00:00
|
|
|
uint64_t pending = prop->name().startsWith("SRC_") ? prop->pending() >> 16 : prop->pending();
|
2021-11-18 11:20:58 +00:00
|
|
|
qCDebug(KWIN_DRM).nospace() << "\t" << prop->name() << ": " << current << "->" << pending;
|
2021-05-25 22:05:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DrmPipeline::printDebugInfo() const
|
|
|
|
{
|
2021-11-18 11:20:58 +00:00
|
|
|
qCDebug(KWIN_DRM) << "Drm objects:";
|
2021-10-20 08:43:35 +00:00
|
|
|
printProps(m_connector, PrintMode::All);
|
2021-09-28 08:29:56 +00:00
|
|
|
if (pending.crtc) {
|
2021-10-20 08:43:35 +00:00
|
|
|
printProps(pending.crtc, PrintMode::All);
|
2021-09-28 08:29:56 +00:00
|
|
|
if (pending.crtc->primaryPlane()) {
|
2021-10-20 08:43:35 +00:00
|
|
|
printProps(pending.crtc->primaryPlane(), PrintMode::All);
|
2021-09-28 08:29:56 +00:00
|
|
|
}
|
2021-12-02 16:51:08 +00:00
|
|
|
if (pending.crtc->cursorPlane()) {
|
|
|
|
printProps(pending.crtc->cursorPlane(), PrintMode::All);
|
|
|
|
}
|
2021-05-25 22:05:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|