You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
245 lines
6.8 KiB
245 lines
6.8 KiB
/* |
|
KWin - the KDE window manager |
|
This file is part of the KDE project. |
|
|
|
SPDX-FileCopyrightText: 2023 Xaver Hugl <xaver.hugl@gmail.com> |
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later |
|
*/ |
|
#include "drm_commit.h" |
|
#include "drm_blob.h" |
|
#include "drm_buffer.h" |
|
#include "drm_connector.h" |
|
#include "drm_crtc.h" |
|
#include "drm_gpu.h" |
|
#include "drm_object.h" |
|
#include "drm_property.h" |
|
|
|
#include <QApplication> |
|
#include <QThread> |
|
|
|
namespace KWin |
|
{ |
|
|
|
DrmCommit::DrmCommit(DrmGpu *gpu) |
|
: m_gpu(gpu) |
|
{ |
|
} |
|
|
|
DrmCommit::~DrmCommit() |
|
{ |
|
Q_ASSERT(QThread::currentThread() == QApplication::instance()->thread()); |
|
} |
|
|
|
DrmGpu *DrmCommit::gpu() const |
|
{ |
|
return m_gpu; |
|
} |
|
|
|
DrmAtomicCommit::DrmAtomicCommit(const QList<DrmPipeline *> &pipelines) |
|
: DrmCommit(pipelines.front()->gpu()) |
|
, m_pipelines(pipelines) |
|
{ |
|
} |
|
|
|
void DrmAtomicCommit::addProperty(const DrmProperty &prop, uint64_t value) |
|
{ |
|
prop.checkValueInRange(value); |
|
m_properties[prop.drmObject()->id()][prop.propId()] = value; |
|
} |
|
|
|
void DrmAtomicCommit::addBlob(const DrmProperty &prop, const std::shared_ptr<DrmBlob> &blob) |
|
{ |
|
addProperty(prop, blob ? blob->blobId() : 0); |
|
m_blobs[&prop] = blob; |
|
} |
|
|
|
void DrmAtomicCommit::addBuffer(DrmPlane *plane, const std::shared_ptr<DrmFramebuffer> &buffer) |
|
{ |
|
addProperty(plane->fbId, buffer ? buffer->framebufferId() : 0); |
|
m_buffers[plane] = buffer; |
|
// atomic commits with IN_FENCE_FD fail with NVidia |
|
if (plane->inFenceFd.isValid() && !plane->gpu()->isNVidia()) { |
|
addProperty(plane->inFenceFd, buffer ? buffer->syncFd().get() : -1); |
|
} |
|
m_planes.emplace(plane); |
|
} |
|
|
|
void DrmAtomicCommit::setVrr(DrmCrtc *crtc, bool vrr) |
|
{ |
|
addProperty(crtc->vrrEnabled, vrr ? 1 : 0); |
|
m_vrr = vrr; |
|
} |
|
|
|
void DrmAtomicCommit::setPresentationMode(PresentationMode mode) |
|
{ |
|
m_mode = mode; |
|
} |
|
|
|
bool DrmAtomicCommit::test() |
|
{ |
|
return doCommit(DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_ATOMIC_NONBLOCK); |
|
} |
|
|
|
bool DrmAtomicCommit::testAllowModeset() |
|
{ |
|
return doCommit(DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_ATOMIC_ALLOW_MODESET); |
|
} |
|
|
|
bool DrmAtomicCommit::commit() |
|
{ |
|
return doCommit(DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT); |
|
} |
|
|
|
bool DrmAtomicCommit::commitModeset() |
|
{ |
|
m_modeset = true; |
|
return doCommit(DRM_MODE_ATOMIC_ALLOW_MODESET); |
|
} |
|
|
|
bool DrmAtomicCommit::doCommit(uint32_t flags) |
|
{ |
|
std::vector<uint32_t> objects; |
|
std::vector<uint32_t> propertyCounts; |
|
std::vector<uint32_t> propertyIds; |
|
std::vector<uint64_t> values; |
|
objects.reserve(m_properties.size()); |
|
propertyCounts.reserve(m_properties.size()); |
|
uint64_t totalPropertiesCount = 0; |
|
for (const auto &[object, properties] : m_properties) { |
|
objects.push_back(object); |
|
propertyCounts.push_back(properties.size()); |
|
totalPropertiesCount += properties.size(); |
|
} |
|
propertyIds.reserve(totalPropertiesCount); |
|
values.reserve(totalPropertiesCount); |
|
for (const auto &[object, properties] : m_properties) { |
|
for (const auto &[property, value] : properties) { |
|
propertyIds.push_back(property); |
|
values.push_back(value); |
|
} |
|
} |
|
drm_mode_atomic commitData{ |
|
.flags = flags, |
|
.count_objs = uint32_t(objects.size()), |
|
.objs_ptr = reinterpret_cast<uint64_t>(objects.data()), |
|
.count_props_ptr = reinterpret_cast<uint64_t>(propertyCounts.data()), |
|
.props_ptr = reinterpret_cast<uint64_t>(propertyIds.data()), |
|
.prop_values_ptr = reinterpret_cast<uint64_t>(values.data()), |
|
.reserved = 0, |
|
.user_data = reinterpret_cast<uint64_t>(this), |
|
}; |
|
return drmIoctl(m_gpu->fd(), DRM_IOCTL_MODE_ATOMIC, &commitData) == 0; |
|
} |
|
|
|
void DrmAtomicCommit::pageFlipped(std::chrono::nanoseconds timestamp) const |
|
{ |
|
Q_ASSERT(QThread::currentThread() == QApplication::instance()->thread()); |
|
for (const auto &[plane, buffer] : m_buffers) { |
|
plane->setCurrentBuffer(buffer); |
|
} |
|
DrmPipeline::PageflipType type = DrmPipeline::PageflipType::Normal; |
|
if (m_modeset) { |
|
type = DrmPipeline::PageflipType::Modeset; |
|
} else if (m_cursorOnly) { |
|
type = DrmPipeline::PageflipType::CursorOnly; |
|
} |
|
for (const auto pipeline : std::as_const(m_pipelines)) { |
|
pipeline->pageFlipped(timestamp, type, m_mode); |
|
} |
|
} |
|
|
|
bool DrmAtomicCommit::areBuffersReadable() const |
|
{ |
|
return std::ranges::all_of(m_buffers, [](const auto &pair) { |
|
const auto &[plane, buffer] = pair; |
|
return !buffer || buffer->isReadable(); |
|
}); |
|
} |
|
|
|
void DrmAtomicCommit::setDeadline(std::chrono::steady_clock::time_point deadline) |
|
{ |
|
for (const auto &[plane, buffer] : m_buffers) { |
|
if (buffer) { |
|
buffer->setDeadline(deadline); |
|
} |
|
} |
|
} |
|
|
|
std::optional<bool> DrmAtomicCommit::isVrr() const |
|
{ |
|
return m_vrr; |
|
} |
|
|
|
const std::unordered_set<DrmPlane *> &DrmAtomicCommit::modifiedPlanes() const |
|
{ |
|
return m_planes; |
|
} |
|
|
|
void DrmAtomicCommit::merge(DrmAtomicCommit *onTop) |
|
{ |
|
for (const auto &[obj, properties] : onTop->m_properties) { |
|
auto &ownProperties = m_properties[obj]; |
|
for (const auto &[prop, value] : properties) { |
|
ownProperties[prop] = value; |
|
} |
|
} |
|
for (const auto &[plane, buffer] : onTop->m_buffers) { |
|
m_buffers[plane] = buffer; |
|
m_planes.emplace(plane); |
|
} |
|
for (const auto &[prop, blob] : onTop->m_blobs) { |
|
m_blobs[prop] = blob; |
|
} |
|
if (onTop->m_vrr) { |
|
m_vrr = onTop->m_vrr; |
|
} |
|
m_cursorOnly &= onTop->isCursorOnly(); |
|
} |
|
|
|
void DrmAtomicCommit::setCursorOnly(bool cursor) |
|
{ |
|
m_cursorOnly = cursor; |
|
} |
|
|
|
bool DrmAtomicCommit::isCursorOnly() const |
|
{ |
|
return m_cursorOnly; |
|
} |
|
|
|
DrmLegacyCommit::DrmLegacyCommit(DrmPipeline *pipeline, const std::shared_ptr<DrmFramebuffer> &buffer) |
|
: DrmCommit(pipeline->gpu()) |
|
, m_pipeline(pipeline) |
|
, m_buffer(buffer) |
|
{ |
|
} |
|
|
|
bool DrmLegacyCommit::doModeset(DrmConnector *connector, DrmConnectorMode *mode) |
|
{ |
|
m_modeset = true; |
|
uint32_t connectorId = connector->id(); |
|
if (drmModeSetCrtc(gpu()->fd(), m_pipeline->crtc()->id(), m_buffer->framebufferId(), 0, 0, &connectorId, 1, mode->nativeMode()) == 0) { |
|
m_pipeline->crtc()->setCurrent(m_buffer); |
|
return true; |
|
} else { |
|
return false; |
|
} |
|
} |
|
|
|
bool DrmLegacyCommit::doPageflip(PresentationMode mode) |
|
{ |
|
m_mode = mode; |
|
uint32_t flags = DRM_MODE_PAGE_FLIP_EVENT; |
|
if (mode == PresentationMode::Async || mode == PresentationMode::AdaptiveAsync) { |
|
flags |= DRM_MODE_PAGE_FLIP_ASYNC; |
|
} |
|
return drmModePageFlip(gpu()->fd(), m_pipeline->crtc()->id(), m_buffer->framebufferId(), flags, this) == 0; |
|
} |
|
|
|
void DrmLegacyCommit::pageFlipped(std::chrono::nanoseconds timestamp) const |
|
{ |
|
Q_ASSERT(QThread::currentThread() == QApplication::instance()->thread()); |
|
m_pipeline->crtc()->setCurrent(m_buffer); |
|
m_pipeline->pageFlipped(timestamp, m_modeset ? DrmPipeline::PageflipType::Modeset : DrmPipeline::PageflipType::Normal, m_mode); |
|
} |
|
}
|
|
|