platforms/drm: delay presentation for modesets
Currently KWin is combining modesets with presentation, which causes problems when multiple monitors are used and crtcs need to be switched around, because taking away a CRTC from another output causes the driver to disable the other output. In order to avoid such problems, delay presentation until all pipelines are ready to present and then do a modeset with a single atomic commit. To process the resulting page flip events properly this commit also ports KWin to page_flip_handler2 and changes how the pageFlipped and notifyFrameFailed signals are processed.
This commit is contained in:
parent
e2a0863843
commit
a07aae8282
14 changed files with 209 additions and 52 deletions
|
@ -763,6 +763,9 @@ bool DrmBackend::applyOutputChanges(const WaylandOutputConfig &config)
|
|||
}
|
||||
};
|
||||
updateCursor();
|
||||
if (auto compositor = Compositor::self()) {
|
||||
compositor->addRepaintFull();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -83,6 +83,11 @@ bool GbmBuffer::map(uint32_t flags)
|
|||
return m_data;
|
||||
}
|
||||
|
||||
KWaylandServer::ClientBuffer *GbmBuffer::clientBuffer() const
|
||||
{
|
||||
return m_clientBuffer;
|
||||
}
|
||||
|
||||
|
||||
DrmGbmBuffer::DrmGbmBuffer(DrmGpu *gpu, GbmSurface *surface, gbm_bo *bo)
|
||||
: DrmBuffer(gpu, gbm_bo_get_format(bo), gbm_bo_get_modifier(bo)), GbmBuffer(surface, bo)
|
||||
|
|
|
@ -47,6 +47,7 @@ public:
|
|||
uint32_t stride() const {
|
||||
return m_stride;
|
||||
}
|
||||
KWaylandServer::ClientBuffer *clientBuffer() const;
|
||||
|
||||
protected:
|
||||
GbmSurface *m_surface = nullptr;
|
||||
|
|
|
@ -347,7 +347,30 @@ bool DrmGpu::checkCrtcAssignment(QVector<DrmConnector*> connectors, QVector<DrmC
|
|||
qCWarning(KWIN_DRM) << "disabling connector" << conn->modelName() << "without a crtc";
|
||||
conn->pipeline()->pending.crtc = nullptr;
|
||||
}
|
||||
return DrmPipeline::commitPipelines(m_pipelines, DrmPipeline::CommitMode::Test);
|
||||
// non-desktop outputs need to be tested when they would be enabled so that they can be used if leased
|
||||
QVector<DrmPipeline*> leasePipelines;
|
||||
for (const auto &output : qAsConst(m_leaseOutputs)) {
|
||||
if (!output->lease()) {
|
||||
output->pipeline()->pending.active = true;
|
||||
leasePipelines << output->pipeline();
|
||||
}
|
||||
}
|
||||
bool test = DrmPipeline::commitPipelines(m_pipelines, DrmPipeline::CommitMode::Test);
|
||||
if (!leasePipelines.isEmpty() && test) {
|
||||
// non-desktop outputs should be disabled for normal usage
|
||||
for (const auto &pipeline : qAsConst(leasePipelines)) {
|
||||
pipeline->pending.active = false;
|
||||
}
|
||||
bool ret = DrmPipeline::commitPipelines(m_pipelines, DrmPipeline::CommitMode::Test);
|
||||
if (ret) {
|
||||
for (const auto &pipeline : qAsConst(leasePipelines)) {
|
||||
pipeline->applyPendingChanges();
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
} else {
|
||||
return test;
|
||||
}
|
||||
}
|
||||
auto connector = connectors.takeFirst();
|
||||
auto pipeline = connector->pipeline();
|
||||
|
@ -424,7 +447,7 @@ void DrmGpu::waitIdle()
|
|||
m_socketNotifier->setEnabled(false);
|
||||
while (true) {
|
||||
const bool idle = std::all_of(m_drmOutputs.constBegin(), m_drmOutputs.constEnd(), [](DrmOutput *output){
|
||||
return !output->m_pageFlipPending;
|
||||
return !output->pipeline()->pageflipPending();
|
||||
});
|
||||
if (idle) {
|
||||
break;
|
||||
|
@ -471,10 +494,10 @@ static std::chrono::nanoseconds convertTimestamp(clockid_t sourceClock, clockid_
|
|||
return convertTimestamp(targetCurrentTime) - delta;
|
||||
}
|
||||
|
||||
static void pageFlipHandler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void *data)
|
||||
void DrmGpu::pageFlipHandler(int fd, unsigned int sequence, unsigned int sec, unsigned int usec, unsigned int crtc_id, void *user_data)
|
||||
{
|
||||
Q_UNUSED(fd)
|
||||
Q_UNUSED(frame)
|
||||
Q_UNUSED(sequence)
|
||||
Q_UNUSED(user_data)
|
||||
auto backend = dynamic_cast<DrmBackend*>(kwinApp()->platform());
|
||||
if (!backend) {
|
||||
return;
|
||||
|
@ -483,29 +506,28 @@ static void pageFlipHandler(int fd, unsigned int frame, unsigned int sec, unsign
|
|||
if (!gpu) {
|
||||
return;
|
||||
}
|
||||
auto output = static_cast<DrmOutput *>(data);
|
||||
if (!gpu->outputs().contains(output)) {
|
||||
// output already got deleted
|
||||
return;
|
||||
}
|
||||
|
||||
// The static_cast<> here are for a 32-bit environment where
|
||||
// sizeof(time_t) == sizeof(unsigned int) == 4 . Putting @p sec
|
||||
// into a time_t cuts off the most-significant bit (after the
|
||||
// year 2038), similarly long can't hold all the bits of an
|
||||
// unsigned multiplication.
|
||||
std::chrono::nanoseconds timestamp = convertTimestamp(output->gpu()->presentationClock(),
|
||||
CLOCK_MONOTONIC,
|
||||
std::chrono::nanoseconds timestamp = convertTimestamp(gpu->presentationClock(), CLOCK_MONOTONIC,
|
||||
{ static_cast<time_t>(sec), static_cast<long>(usec * 1000) });
|
||||
if (timestamp == std::chrono::nanoseconds::zero()) {
|
||||
qCDebug(KWIN_DRM, "Got invalid timestamp (sec: %u, usec: %u) on output %s",
|
||||
sec, usec, qPrintable(output->name()));
|
||||
qCDebug(KWIN_DRM, "Got invalid timestamp (sec: %u, usec: %u) on gpu %s",
|
||||
sec, usec, qPrintable(gpu->devNode()));
|
||||
timestamp = std::chrono::steady_clock::now().time_since_epoch();
|
||||
}
|
||||
|
||||
output->pageFlipped();
|
||||
RenderLoopPrivate *renderLoopPrivate = RenderLoopPrivate::get(output->renderLoop());
|
||||
renderLoopPrivate->notifyFrameCompleted(timestamp);
|
||||
const auto pipelines = gpu->pipelines();
|
||||
auto it = std::find_if(pipelines.begin(), pipelines.end(), [crtc_id](const auto &pipeline) {
|
||||
return pipeline->currentCrtc() && pipeline->currentCrtc()->id() == crtc_id;
|
||||
});
|
||||
if (it == pipelines.end()) {
|
||||
qCWarning(KWIN_DRM, "received invalid page flip event for crtc %u", crtc_id);
|
||||
} else {
|
||||
(*it)->pageFlipped(timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
void DrmGpu::dispatchEvents()
|
||||
|
@ -514,8 +536,8 @@ void DrmGpu::dispatchEvents()
|
|||
return;
|
||||
}
|
||||
drmEventContext context = {};
|
||||
context.version = 2;
|
||||
context.page_flip_handler = pageFlipHandler;
|
||||
context.version = 3;
|
||||
context.page_flip_handler2 = pageFlipHandler;
|
||||
drmHandleEvent(m_fd, &context);
|
||||
}
|
||||
|
||||
|
@ -707,4 +729,30 @@ bool DrmGpu::isNVidia() const
|
|||
return m_isNVidia;
|
||||
}
|
||||
|
||||
bool DrmGpu::needsModeset() const
|
||||
{
|
||||
return std::any_of(m_pipelines.constBegin(), m_pipelines.constEnd(), [](const auto &pipeline) {
|
||||
return pipeline->needsModeset();
|
||||
});
|
||||
}
|
||||
|
||||
bool DrmGpu::maybeModeset()
|
||||
{
|
||||
auto pipelines = m_pipelines;
|
||||
for (const auto &output : qAsConst(m_leaseOutputs)) {
|
||||
if (output->lease()) {
|
||||
pipelines.removeOne(output->pipeline());
|
||||
}
|
||||
}
|
||||
bool presentPendingForAll = std::all_of(pipelines.constBegin(), pipelines.constEnd(), [](const auto &pipeline) {
|
||||
return pipeline->modesetPresentPending() || !pipeline->pending.active;
|
||||
});
|
||||
if (presentPendingForAll) {
|
||||
return DrmPipeline::commitPipelines(pipelines, DrmPipeline::CommitMode::CommitModeset);
|
||||
} else {
|
||||
// commit only once all pipelines are ready for presentation
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -83,6 +83,9 @@ public:
|
|||
DrmVirtualOutput *createVirtualOutput(const QString &name, const QSize &size, double scale, VirtualOutputMode mode);
|
||||
void removeVirtualOutput(DrmVirtualOutput *output);
|
||||
|
||||
bool needsModeset() const;
|
||||
bool maybeModeset();
|
||||
|
||||
Q_SIGNALS:
|
||||
void outputAdded(DrmAbstractOutput *output);
|
||||
void outputRemoved(DrmAbstractOutput *output);
|
||||
|
@ -102,6 +105,8 @@ private:
|
|||
void handleLeaseRequest(KWaylandServer::DrmLeaseV1Interface *leaseRequest);
|
||||
void handleLeaseRevoked(KWaylandServer::DrmLeaseV1Interface *lease);
|
||||
|
||||
static void pageFlipHandler(int fd, unsigned int sequence, unsigned int sec, unsigned int usec, unsigned int crtc_id, void *user_data);
|
||||
|
||||
const int m_fd;
|
||||
const dev_t m_deviceId;
|
||||
const QString m_devNode;
|
||||
|
|
|
@ -306,6 +306,9 @@ bool DrmConnector::vrrCapable() const
|
|||
|
||||
bool DrmConnector::needsModeset() const
|
||||
{
|
||||
if (!gpu()->atomicModeSetting()) {
|
||||
return false;
|
||||
}
|
||||
if (getProp(PropertyIndex::CrtcId)->needsCommit()) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -54,6 +54,9 @@ drmModeModeInfo DrmCrtc::queryCurrentMode()
|
|||
|
||||
bool DrmCrtc::needsModeset() const
|
||||
{
|
||||
if (!gpu()->atomicModeSetting()) {
|
||||
return false;
|
||||
}
|
||||
return getProp(PropertyIndex::Active)->needsCommit()
|
||||
|| getProp(PropertyIndex::ModeId)->needsCommit();
|
||||
}
|
||||
|
|
|
@ -168,6 +168,9 @@ void DrmPlane::setBuffer(DrmBuffer *buffer)
|
|||
|
||||
bool DrmPlane::needsModeset() const
|
||||
{
|
||||
if (!gpu()->atomicModeSetting()) {
|
||||
return false;
|
||||
}
|
||||
auto rotation = getProp(PropertyIndex::Rotation);
|
||||
if (rotation && rotation->needsCommit()) {
|
||||
return true;
|
||||
|
|
|
@ -69,9 +69,6 @@ DrmOutput::DrmOutput(DrmPipeline *pipeline)
|
|||
|
||||
DrmOutput::~DrmOutput()
|
||||
{
|
||||
if (m_pageFlipPending) {
|
||||
pageFlipped();
|
||||
}
|
||||
m_pipeline->setOutput(nullptr);
|
||||
}
|
||||
|
||||
|
@ -264,7 +261,7 @@ bool DrmOutput::setDrmDpmsMode(DpmsMode mode)
|
|||
return true;
|
||||
}
|
||||
m_pipeline->pending.active = active;
|
||||
if (DrmPipeline::commitPipelines({m_pipeline}, active ? DrmPipeline::CommitMode::Test : DrmPipeline::CommitMode::Commit)) {
|
||||
if (DrmPipeline::commitPipelines({m_pipeline}, active ? DrmPipeline::CommitMode::Test : DrmPipeline::CommitMode::CommitModeset)) {
|
||||
m_pipeline->applyPendingChanges();
|
||||
setDpmsModeInternal(mode);
|
||||
if (active) {
|
||||
|
@ -370,13 +367,6 @@ bool DrmOutput::needsSoftwareTransformation() const
|
|||
return m_pipeline->pending.transformation != outputToPlaneTransform(transform());
|
||||
}
|
||||
|
||||
void DrmOutput::pageFlipped()
|
||||
{
|
||||
Q_ASSERT(m_pageFlipPending || !m_gpu->atomicModeSetting());
|
||||
m_pageFlipPending = false;
|
||||
m_pipeline->pageFlipped();
|
||||
}
|
||||
|
||||
bool DrmOutput::present(const QSharedPointer<DrmBuffer> &buffer, QRegion damagedRegion)
|
||||
{
|
||||
if (!buffer || buffer->bufferId() == 0) {
|
||||
|
@ -393,7 +383,6 @@ bool DrmOutput::present(const QSharedPointer<DrmBuffer> &buffer, QRegion damaged
|
|||
}
|
||||
}
|
||||
if (m_pipeline->present(buffer)) {
|
||||
m_pageFlipPending = true;
|
||||
Q_EMIT outputChange(damagedRegion);
|
||||
return true;
|
||||
} else {
|
||||
|
@ -494,4 +483,14 @@ void DrmOutput::revertQueuedChanges()
|
|||
m_pipeline->revertPendingChanges();
|
||||
}
|
||||
|
||||
void DrmOutput::pageFlipped(std::chrono::nanoseconds timestamp)
|
||||
{
|
||||
RenderLoopPrivate::get(m_renderLoop)->notifyFrameCompleted(timestamp);
|
||||
}
|
||||
|
||||
void DrmOutput::presentFailed()
|
||||
{
|
||||
RenderLoopPrivate::get(m_renderLoop)->notifyFrameFailed();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <QVector>
|
||||
#include <QSharedPointer>
|
||||
#include <xf86drmMode.h>
|
||||
#include <chrono>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
@ -47,7 +48,6 @@ public:
|
|||
bool moveCursor() override;
|
||||
|
||||
bool present(const QSharedPointer<DrmBuffer> &buffer, QRegion damagedRegion) override;
|
||||
void pageFlipped();
|
||||
|
||||
DrmConnector *connector() const;
|
||||
DrmPipeline *pipeline() const;
|
||||
|
@ -61,6 +61,9 @@ public:
|
|||
void applyQueuedChanges(const WaylandOutputConfig &config);
|
||||
void revertQueuedChanges();
|
||||
|
||||
void pageFlipped(std::chrono::nanoseconds timestamp);
|
||||
void presentFailed();
|
||||
|
||||
private:
|
||||
friend class DrmGpu;
|
||||
friend class DrmBackend;
|
||||
|
@ -84,7 +87,6 @@ private:
|
|||
DrmConnector *m_connector;
|
||||
|
||||
QSharedPointer<DrmDumbBuffer> m_cursor;
|
||||
bool m_pageFlipPending = false;
|
||||
QTimer m_turnOffTimer;
|
||||
};
|
||||
|
||||
|
|
|
@ -46,11 +46,21 @@ DrmPipeline::DrmPipeline(DrmConnector *conn)
|
|||
|
||||
DrmPipeline::~DrmPipeline()
|
||||
{
|
||||
m_output = nullptr;
|
||||
if (m_pageflipPending && m_current.crtc) {
|
||||
pageFlipped({});
|
||||
}
|
||||
}
|
||||
|
||||
bool DrmPipeline::present(const QSharedPointer<DrmBuffer> &buffer)
|
||||
{
|
||||
Q_ASSERT(pending.crtc);
|
||||
if (!buffer) {
|
||||
if (m_output) {
|
||||
m_output->presentFailed();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
m_primaryBuffer = buffer;
|
||||
if (gpu()->useEglStreams() && gpu()->eglBackend() != nullptr && gpu() == gpu()->platform()->primaryGpu()) {
|
||||
// EglStreamBackend queues normal page flips through EGL,
|
||||
|
@ -59,19 +69,42 @@ bool DrmPipeline::present(const QSharedPointer<DrmBuffer> &buffer)
|
|||
return true;
|
||||
}
|
||||
}
|
||||
bool directScanout = false;
|
||||
#if HAVE_GBM
|
||||
// with direct scanout disallow modesets, calling presentFailed() and logging warnings
|
||||
if (auto buf = dynamic_cast<DrmGbmBuffer*>(buffer.data()); buf && buf->clientBuffer()) {
|
||||
directScanout = true;
|
||||
}
|
||||
#endif
|
||||
if (gpu()->needsModeset()) {
|
||||
if (directScanout) {
|
||||
return false;
|
||||
}
|
||||
m_modesetPresentPending = true;
|
||||
return gpu()->maybeModeset();
|
||||
}
|
||||
if (gpu()->atomicModeSetting()) {
|
||||
if (!commitPipelines({this}, CommitMode::Commit)) {
|
||||
// update properties and try again
|
||||
updateProperties();
|
||||
if (!commitPipelines({this}, CommitMode::Commit)) {
|
||||
if (directScanout) {
|
||||
return false;
|
||||
}
|
||||
qCWarning(KWIN_DRM) << "Atomic present failed!" << strerror(errno);
|
||||
printDebugInfo();
|
||||
if (m_output) {
|
||||
m_output->presentFailed();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!presentLegacy()) {
|
||||
qCWarning(KWIN_DRM) << "Present failed!" << strerror(errno);
|
||||
if (m_output) {
|
||||
m_output->presentFailed();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -87,8 +120,8 @@ bool DrmPipeline::commitPipelines(const QVector<DrmPipeline*> &pipelines, Commit
|
|||
qCDebug(KWIN_DRM) << "Failed to allocate drmModeAtomicReq!" << strerror(errno);
|
||||
return false;
|
||||
}
|
||||
uint32_t flags = DRM_MODE_ATOMIC_NONBLOCK;
|
||||
const auto &failed = [pipelines, req](){
|
||||
uint32_t flags = 0;
|
||||
const auto &failed = [pipelines, req, mode](){
|
||||
drmModeAtomicFree(req);
|
||||
for (const auto &pipeline : pipelines) {
|
||||
pipeline->printDebugInfo();
|
||||
|
@ -101,6 +134,10 @@ bool DrmPipeline::commitPipelines(const QVector<DrmPipeline*> &pipelines, Commit
|
|||
pipeline->pending.crtc->rollbackPending();
|
||||
pipeline->pending.crtc->primaryPlane()->rollbackPending();
|
||||
}
|
||||
if (mode != CommitMode::Test && pipeline->activePending() && pipeline->output()) {
|
||||
pipeline->m_modesetPresentPending = false;
|
||||
pipeline->output()->presentFailed();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
@ -114,11 +151,22 @@ bool DrmPipeline::commitPipelines(const QVector<DrmPipeline*> &pipelines, Commit
|
|||
return failed();
|
||||
}
|
||||
}
|
||||
if (drmModeAtomicCommit(pipelines[0]->gpu()->fd(), req, (flags & (~DRM_MODE_PAGE_FLIP_EVENT)) | DRM_MODE_ATOMIC_TEST_ONLY, pipelines[0]->output()) != 0) {
|
||||
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);
|
||||
} else {
|
||||
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) {
|
||||
qCWarning(KWIN_DRM) << "Atomic test for" << mode << "failed!" << strerror(errno);
|
||||
return failed();
|
||||
}
|
||||
if (mode != CommitMode::Test && drmModeAtomicCommit(pipelines[0]->gpu()->fd(), req, flags, pipelines[0]->output()) != 0) {
|
||||
if (mode != CommitMode::Test && drmModeAtomicCommit(pipelines[0]->gpu()->fd(), req, flags, nullptr) != 0) {
|
||||
qCWarning(KWIN_DRM) << "Atomic commit failed! This should never happen!" << strerror(errno);
|
||||
return failed();
|
||||
}
|
||||
|
@ -130,6 +178,8 @@ bool DrmPipeline::commitPipelines(const QVector<DrmPipeline*> &pipelines, Commit
|
|||
pipeline->pending.crtc->primaryPlane()->commitPending();
|
||||
}
|
||||
if (mode != CommitMode::Test) {
|
||||
pipeline->m_modesetPresentPending = false;
|
||||
pipeline->m_pageflipPending = true;
|
||||
pipeline->m_connector->commit();
|
||||
if (pipeline->pending.crtc) {
|
||||
pipeline->pending.crtc->primaryPlane()->setNext(pipeline->m_primaryBuffer);
|
||||
|
@ -137,6 +187,9 @@ bool DrmPipeline::commitPipelines(const QVector<DrmPipeline*> &pipelines, Commit
|
|||
pipeline->pending.crtc->primaryPlane()->commit();
|
||||
}
|
||||
pipeline->m_current = pipeline->pending;
|
||||
if (modeset && pipeline->activePending()) {
|
||||
pipeline->pageFlipped(std::chrono::steady_clock::now().time_since_epoch());
|
||||
}
|
||||
}
|
||||
}
|
||||
drmModeAtomicFree(req);
|
||||
|
@ -152,11 +205,21 @@ bool DrmPipeline::commitPipelines(const QVector<DrmPipeline*> &pipelines, Commit
|
|||
if (failure) {
|
||||
// at least try to revert the config
|
||||
for (const auto &pipeline : pipelines) {
|
||||
pipeline->pending = pipeline->m_next;
|
||||
pipeline->revertPendingChanges();
|
||||
pipeline->applyPendingChangesLegacy();
|
||||
if (mode == CommitMode::CommitModeset && pipeline->output() && pipeline->activePending()) {
|
||||
pipeline->output()->presentFailed();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
for (const auto &pipeline : pipelines) {
|
||||
pipeline->applyPendingChanges();
|
||||
pipeline->m_current = pipeline->pending;
|
||||
if (mode == CommitMode::CommitModeset && mode != CommitMode::Test && pipeline->activePending()) {
|
||||
pipeline->pageFlipped(std::chrono::steady_clock::now().time_since_epoch());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -201,6 +264,7 @@ bool DrmPipeline::presentLegacy()
|
|||
qCWarning(KWIN_DRM) << "Page flip failed:" << strerror(errno) << m_primaryBuffer;
|
||||
return false;
|
||||
}
|
||||
m_pageflipPending = true;
|
||||
pending.crtc->setNext(m_primaryBuffer);
|
||||
return true;
|
||||
}
|
||||
|
@ -378,12 +442,16 @@ DrmGpu *DrmPipeline::gpu() const
|
|||
return m_connector->gpu();
|
||||
}
|
||||
|
||||
void DrmPipeline::pageFlipped()
|
||||
void DrmPipeline::pageFlipped(std::chrono::nanoseconds timestamp)
|
||||
{
|
||||
m_current.crtc->flipBuffer();
|
||||
if (m_current.crtc->primaryPlane()) {
|
||||
m_current.crtc->primaryPlane()->flipBuffer();
|
||||
}
|
||||
m_pageflipPending = false;
|
||||
if (m_output) {
|
||||
m_output->pageFlipped(timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
void DrmPipeline::setOutput(DrmOutput *output)
|
||||
|
@ -426,7 +494,8 @@ bool DrmPipeline::needsModeset() const
|
|||
|| pending.active != m_current.active
|
||||
|| pending.modeIndex != m_current.modeIndex
|
||||
|| pending.rgbRange != m_current.rgbRange
|
||||
|| pending.transformation != m_current.transformation;
|
||||
|| pending.transformation != m_current.transformation
|
||||
|| m_modesetPresentPending;
|
||||
}
|
||||
|
||||
bool DrmPipeline::activePending() const
|
||||
|
@ -439,6 +508,21 @@ void DrmPipeline::revertPendingChanges()
|
|||
pending = m_next;
|
||||
}
|
||||
|
||||
bool DrmPipeline::pageflipPending() const
|
||||
{
|
||||
return m_pageflipPending;
|
||||
}
|
||||
|
||||
bool DrmPipeline::modesetPresentPending() const
|
||||
{
|
||||
return m_modesetPresentPending;
|
||||
}
|
||||
|
||||
DrmCrtc *DrmPipeline::currentCrtc() const
|
||||
{
|
||||
return m_current.crtc;
|
||||
}
|
||||
|
||||
DrmGammaRamp::DrmGammaRamp(DrmGpu *gpu, const GammaRamp &lut)
|
||||
: lut(lut)
|
||||
, size(lut.size())
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <QSharedPointer>
|
||||
|
||||
#include <xf86drmMode.h>
|
||||
#include <chrono>
|
||||
|
||||
#include "drm_object_plane.h"
|
||||
#include "renderloop_p.h"
|
||||
|
@ -54,6 +55,7 @@ public:
|
|||
bool present(const QSharedPointer<DrmBuffer> &buffer);
|
||||
|
||||
bool needsModeset() const;
|
||||
bool needsCommit() const;
|
||||
void prepareModeset();
|
||||
void applyPendingChanges();
|
||||
void revertPendingChanges();
|
||||
|
@ -65,9 +67,12 @@ public:
|
|||
QPoint cursorPos() const;
|
||||
|
||||
DrmConnector *connector() const;
|
||||
DrmCrtc *currentCrtc() const;
|
||||
DrmGpu *gpu() const;
|
||||
|
||||
void pageFlipped();
|
||||
void pageFlipped(std::chrono::nanoseconds timestamp);
|
||||
bool pageflipPending() const;
|
||||
bool modesetPresentPending() const;
|
||||
void printDebugInfo() const;
|
||||
QSize sourceSize() const;
|
||||
void updateProperties();
|
||||
|
@ -92,7 +97,8 @@ public:
|
|||
|
||||
enum class CommitMode {
|
||||
Test,
|
||||
Commit
|
||||
Commit,
|
||||
CommitModeset
|
||||
};
|
||||
Q_ENUM(CommitMode);
|
||||
static bool commitPipelines(const QVector<DrmPipeline*> &pipelines, CommitMode mode);
|
||||
|
@ -111,6 +117,8 @@ private:
|
|||
|
||||
QSharedPointer<DrmBuffer> m_primaryBuffer;
|
||||
QSharedPointer<DrmBuffer> m_oldTestBuffer;
|
||||
bool m_pageflipPending = false;
|
||||
bool m_modesetPresentPending = false;
|
||||
|
||||
QMap<uint32_t, QVector<uint64_t>> m_formats;
|
||||
int m_lastFlags = 0;
|
||||
|
|
|
@ -581,11 +581,7 @@ void EglGbmBackend::endFrame(AbstractOutput *drmOutput, const QRegion &renderedR
|
|||
|
||||
const QRegion dirty = damagedRegion.intersected(output.output->geometry());
|
||||
QSharedPointer<DrmBuffer> buffer = endFrameWithBuffer(drmOutput, dirty);
|
||||
if (!buffer || !output.output->present(buffer, dirty)) {
|
||||
RenderLoopPrivate *renderLoopPrivate = RenderLoopPrivate::get(output.output->renderLoop());
|
||||
renderLoopPrivate->notifyFrameFailed();
|
||||
return;
|
||||
}
|
||||
output.output->present(buffer, dirty);
|
||||
}
|
||||
|
||||
void EglGbmBackend::updateBufferAge(Output &output, const QRegion &dirty)
|
||||
|
|
|
@ -78,10 +78,7 @@ void DrmQPainterBackend::endFrame(AbstractOutput *output, const QRegion &damage)
|
|||
QSharedPointer<DrmDumbBuffer> back = rendererOutput.swapchain->currentBuffer();
|
||||
rendererOutput.swapchain->releaseBuffer(back);
|
||||
|
||||
if (!drmOutput->present(back, drmOutput->geometry())) {
|
||||
RenderLoopPrivate *renderLoopPrivate = RenderLoopPrivate::get(drmOutput->renderLoop());
|
||||
renderLoopPrivate->notifyFrameFailed();
|
||||
}
|
||||
drmOutput->present(back, drmOutput->geometry());
|
||||
|
||||
rendererOutput.damageJournal.add(damage);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue