[DRM plugin] Remember static kernel objects, amplify use of DrmCrtc

To get an image from KWin to the screen in the DRM pipeline we combine a CRTC,
an encoder and a connector. These objects are static in the sense, that they
represent real hardware on the graphics card, which doesn't change in a
session. See here for more details:
https://01.org/linuxgraphics/gfx-docs/drm/gpu/drm-kms.html

Until now we used DrmOutput as the main representation for such an active
rendering pipeline. I.e. it gets created and destroyed on hot plug events of
displays. On the other side we had no fixed representation of the static kernel
objects throughout the lifetime of KWin. This has several disadvantages:
* We always need to query all available static objects on an hot plug event.
* We can't manipulate the frame buffer of a CRTC after an output has been
  disconnected
* Adding functionality for driving multiple displays on a single CRTC (i.e.
  cloning) would be difficult
* We can't destroy the last frame buffer on display disconnect because the CRTC
  still accesses it and have therefore a memory leak on every display disconnect

This patch  solves these issues by storing representations of all available CRTC
and Connector objects in DrmBackend on init via DrmCrtc and DrmConnector
instances. On an hotplug event these vectors are looped for a fitting CRTC and
Connector combinations. Buffer handling is moved to the respective CRTC
instance. All changes in overview:
* Query all available CRTCs and Connectors and save for subsequent hotplug
  events
* Fix logic errors in `queryResources()`
* Move framebuffers, buffer flip and blank logic in DrmCrtc
* Remove `restoreSaved()`. It isn't necessary and is dangerous if the old
  framebuffer was deleted in the meantime. Also could reveal sensitive user
  info from old session.

Test Plan:
Login, logout, VT switching, connect and disconnect external monitor, energy
saving mode.

Reviewers: #kwin

Subscribers: kwin, #kwin

Tags: #kwin

Differential Revision: https://phabricator.kde.org/D5118
This commit is contained in:
Roman Gilg 2017-05-09 20:02:49 +02:00
parent 750843061c
commit a0571ccf84
11 changed files with 262 additions and 213 deletions

View file

@ -89,8 +89,10 @@ DrmBackend::~DrmBackend()
while (m_pageFlipsPending != 0) {
QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents);
}
qDeleteAll(m_planes);
qDeleteAll(m_outputs);
qDeleteAll(m_planes);
qDeleteAll(m_crtcs);
qDeleteAll(m_connectors);
delete m_cursor[0];
delete m_cursor[1];
close(m_fd);
@ -170,7 +172,7 @@ void DrmBackend::reactivate()
for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) {
DrmOutput *o = *it;
o->pageFlipped();
o->blank();
o->m_crtc->blank();
o->showCursor(c);
o->moveCursor(cp);
}
@ -196,7 +198,6 @@ void DrmBackend::deactivate()
for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) {
DrmOutput *o = *it;
o->hideCursor();
o->restoreSaved();
}
m_active = false;
}
@ -288,7 +289,35 @@ void DrmBackend::openDrm()
}
}
ScopedDrmPointer<_drmModeRes, &drmModeFreeResources> resources(drmModeGetResources(m_fd));
drmModeRes *res = resources.data();
if (!resources) {
qCWarning(KWIN_DRM) << "drmModeGetResources failed";
return;
}
for (int i = 0; i < res->count_connectors; ++i) {
m_connectors << new DrmConnector(res->connectors[i], m_fd);
}
for (int i = 0; i < res->count_crtcs; ++i) {
m_crtcs << new DrmCrtc(res->crtcs[i], m_fd, i);
}
if (m_atomicModeSetting) {
auto tryInit = [] (DrmObject *o) -> bool {
if (o->init()) {
return false;
} else {
delete o;
return true;
}
};
m_connectors.erase(std::remove_if(m_connectors.begin(), m_connectors.end(), tryInit), m_connectors.end());
m_crtcs.erase(std::remove_if(m_crtcs.begin(), m_crtcs.end(), tryInit), m_crtcs.end());
}
queryResources();
if (m_outputs.isEmpty()) {
qCWarning(KWIN_DRM) << "No outputs, cannot render, will terminate now";
emit initFailed();
@ -331,6 +360,7 @@ void DrmBackend::queryResources()
if (m_fd < 0) {
return;
}
ScopedDrmPointer<_drmModeRes, &drmModeFreeResources> resources(drmModeGetResources(m_fd));
if (!resources) {
qCWarning(KWIN_DRM) << "drmModeGetResources failed";
@ -338,74 +368,21 @@ void DrmBackend::queryResources()
}
QVector<DrmOutput*> connectedOutputs;
for (int i = 0; i < resources->count_connectors; ++i) {
const auto id = resources->connectors[i];
ScopedDrmPointer<_drmModeConnector, &drmModeFreeConnector> connector(drmModeGetConnector(m_fd, id));
if (!connector) {
QVector<DrmConnector*> pendingConnectors;
// split up connected connectors in already or not yet assigned ones
for (DrmConnector *con : qAsConst(m_connectors)) {
if (!con->isConnected()) {
continue;
}
if (connector->connection != DRM_MODE_CONNECTED) {
continue;
}
if (connector->count_modes == 0) {
continue;
}
if (DrmOutput *o = findOutput(connector->connector_id)) {
if (DrmOutput *o = findOutput(con->id())) {
connectedOutputs << o;
continue;
}
bool crtcFound = false;
const quint32 crtcId = findCrtc(resources.data(), connector.data(), &crtcFound);
if (!crtcFound) {
continue;
}
ScopedDrmPointer<_drmModeCrtc, &drmModeFreeCrtc> crtc(drmModeGetCrtc(m_fd, crtcId));
if (!crtc) {
continue;
}
DrmOutput *drmOutput = new DrmOutput(this);
connect(drmOutput, &DrmOutput::dpmsChanged, this, &DrmBackend::outputDpmsChanged);
drmOutput->m_crtcId = crtcId;
drmOutput->m_connector = connector->connector_id;
if (m_atomicModeSetting) {
drmOutput->m_crtc = new DrmCrtc(crtcId, m_fd);
if (drmOutput->m_crtc->init()) {
drmOutput->m_crtc->setOutput(drmOutput);
} else {
qCWarning(KWIN_DRM) << "Crtc object failed, skipping output on connector" << connector->connector_id;
delete drmOutput->m_crtc;
delete drmOutput;
continue;
}
drmOutput->m_conn = new DrmConnector(connector->connector_id, m_fd);
if (drmOutput->m_conn->init()) {
drmOutput->m_conn->setOutput(drmOutput);
} else {
qCWarning(KWIN_DRM) << "Connector object failed, skipping output on connector" << connector->connector_id;
delete drmOutput->m_conn;
delete drmOutput;
continue;
}
}
if (crtc->mode_valid) {
drmOutput->m_mode = crtc->mode;
} else {
drmOutput->m_mode = connector->modes[0];
pendingConnectors << con;
}
qCDebug(KWIN_DRM) << "For new output use mode " << drmOutput->m_mode.name;
if (!drmOutput->init(connector.data())) {
qCWarning(KWIN_DRM) << "Failed to create output for connector " << connector->connector_id;
delete drmOutput;
continue;
}
qCDebug(KWIN_DRM) << "Found new output with uuid" << drmOutput->uuid();
connectedOutputs << drmOutput;
}
std::sort(connectedOutputs.begin(), connectedOutputs.end(), [] (DrmOutput *a, DrmOutput *b) { return a->m_connector < b->m_connector; });
// check for outputs which got removed
auto it = m_outputs.begin();
while (it != m_outputs.end()) {
@ -418,11 +395,80 @@ void DrmBackend::queryResources()
emit outputRemoved(removed);
delete removed;
}
for (auto it = connectedOutputs.constBegin(); it != connectedOutputs.constEnd(); ++it) {
if (!m_outputs.contains(*it)) {
emit outputAdded(*it);
// now check new connections
for (DrmConnector *con : qAsConst(pendingConnectors)) {
ScopedDrmPointer<_drmModeConnector, &drmModeFreeConnector> connector(drmModeGetConnector(m_fd, con->id()));
if (!connector) {
continue;
}
if (connector->count_modes == 0) {
continue;
}
bool outputDone = false;
QVector<uint32_t> encoders = con->encoders();
for (auto encId : qAsConst(encoders)) {
ScopedDrmPointer<_drmModeEncoder, &drmModeFreeEncoder> encoder(drmModeGetEncoder(m_fd, encId));
if (!encoder) {
continue;
}
for (DrmCrtc *crtc : qAsConst(m_crtcs)) {
if (!(encoder->possible_crtcs & (1 << crtc->resIndex()))) {
continue;
}
// check if crtc isn't used yet -- currently we don't allow multiple outputs on one crtc (cloned mode)
auto it = std::find_if(connectedOutputs.constBegin(), connectedOutputs.constEnd(),
[crtc] (DrmOutput *o) {
return o->m_crtc == crtc;
}
);
if (it != connectedOutputs.constEnd()) {
continue;
}
// we found a suitable encoder+crtc
// TODO: we could avoid these lib drm calls if we store all struct data in DrmCrtc and DrmConnector in the beginning
ScopedDrmPointer<_drmModeCrtc, &drmModeFreeCrtc> modeCrtc(drmModeGetCrtc(m_fd, crtc->id()));
if (!modeCrtc) {
continue;
}
DrmOutput *output = new DrmOutput(this);
con->setOutput(output);
output->m_conn = con;
crtc->setOutput(output);
output->m_crtc = crtc;
connect(output, &DrmOutput::dpmsChanged, this, &DrmBackend::outputDpmsChanged);
if (modeCrtc->mode_valid) {
output->m_mode = modeCrtc->mode;
} else {
output->m_mode = connector->modes[0];
}
qCDebug(KWIN_DRM) << "For new output use mode " << output->m_mode.name;
if (!output->init(connector.data())) {
qCWarning(KWIN_DRM) << "Failed to create output for connector " << con->id();
con->setOutput(nullptr);
crtc->setOutput(nullptr);
delete output;
continue;
}
qCDebug(KWIN_DRM) << "Found new output with uuid" << output->uuid();
connectedOutputs << output;
emit outputAdded(output);
outputDone = true;
break;
}
if (outputDone) {
break;
}
}
}
std::sort(connectedOutputs.begin(), connectedOutputs.end(), [] (DrmOutput *a, DrmOutput *b) { return a->m_conn->id() < b->m_conn->id(); });
m_outputs = connectedOutputs;
readOutputsConfiguration();
if (!m_outputs.isEmpty()) {
@ -484,7 +530,7 @@ void DrmBackend::configurationChangeRequested(KWayland::Server::OutputConfigurat
DrmOutput *DrmBackend::findOutput(quint32 connector)
{
auto it = std::find_if(m_outputs.constBegin(), m_outputs.constEnd(), [connector] (DrmOutput *o) {
return o->m_connector == connector;
return o->m_conn->id() == connector;
});
if (it != m_outputs.constEnd()) {
return *it;
@ -503,51 +549,6 @@ DrmOutput *DrmBackend::findOutput(const QByteArray &uuid)
return nullptr;
}
quint32 DrmBackend::findCrtc(drmModeRes *res, drmModeConnector *connector, bool *ok)
{
if (ok) {
*ok = false;
}
ScopedDrmPointer<_drmModeEncoder, &drmModeFreeEncoder> encoder(drmModeGetEncoder(m_fd, connector->encoder_id));
if (encoder) {
if (!crtcIsUsed(encoder->crtc_id)) {
if (ok) {
*ok = true;
}
return encoder->crtc_id;
}
}
// let's iterate over all encoders to find a suitable crtc
for (int i = 0; i < connector->count_encoders; ++i) {
ScopedDrmPointer<_drmModeEncoder, &drmModeFreeEncoder> encoder(drmModeGetEncoder(m_fd, connector->encoders[i]));
if (!encoder) {
continue;
}
for (int j = 0; j < res->count_crtcs; ++j) {
if (!(encoder->possible_crtcs & (1 << j))) {
continue;
}
if (!crtcIsUsed(res->crtcs[j])) {
if (ok) {
*ok = true;
}
return res->crtcs[j];
}
}
}
return 0;
}
bool DrmBackend::crtcIsUsed(quint32 crtc)
{
auto it = std::find_if(m_outputs.constBegin(), m_outputs.constEnd(),
[crtc] (DrmOutput *o) {
return o->m_crtcId == crtc;
}
);
return it != m_outputs.constEnd();
}
void DrmBackend::present(DrmBuffer *buffer, DrmOutput *output)
{
if (output->present(buffer)) {

View file

@ -55,6 +55,8 @@ class UdevMonitor;
class DrmOutput;
class DrmPlane;
class DrmCrtc;
class DrmConnector;
class KWIN_EXPORT DrmBackend : public Platform
@ -128,8 +130,6 @@ private:
void updateCursor();
void moveCursor();
void initCursor();
quint32 findCrtc(drmModeRes *res, drmModeConnector *connector, bool *ok = nullptr);
bool crtcIsUsed(quint32 crtc);
void outputDpmsChanged();
void readOutputsConfiguration();
QByteArray generateOutputConfigurationUuid() const;
@ -139,6 +139,11 @@ private:
QScopedPointer<UdevMonitor> m_udevMonitor;
int m_fd = -1;
int m_drmId = 0;
// all crtcs
QVector<DrmCrtc*> m_crtcs;
// all connectors
QVector<DrmConnector*> m_connectors;
// currently active output pipelines (planes + crtc + encoder + connector)
QVector<DrmOutput*> m_outputs;
DrmBuffer *m_cursor[2];
bool m_atomicModeSetting = false;

View file

@ -93,14 +93,14 @@ DrmBuffer::DrmBuffer(DrmBackend *backend, gbm_surface *surface)
DrmBuffer::~DrmBuffer()
{
if (m_bufferId) {
drmModeRmFB(m_backend->fd(), m_bufferId);
}
m_backend->bufferDestroyed(this);
delete m_image;
if (m_memory) {
munmap(m_memory, m_bufferSize);
}
if (m_bufferId) {
drmModeRmFB(m_backend->fd(), m_bufferId);
}
if (m_handle) {
drm_mode_destroy_dumb destroyArgs;
destroyArgs.handle = m_handle;

View file

@ -61,7 +61,6 @@ public:
bool deleteAfterPageFlip() const {
return m_deleteAfterPageFlip;
}
void releaseGbm();
private:

View file

@ -18,6 +18,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "drm_object_connector.h"
#include "drm_pointer.h"
#include "logging.h"
namespace KWin
@ -26,7 +27,13 @@ namespace KWin
DrmConnector::DrmConnector(uint32_t connector_id, int fd)
: DrmObject(connector_id, fd)
{
ScopedDrmPointer<_drmModeConnector, &drmModeFreeConnector> con(drmModeGetConnector(fd, connector_id));
if (!con) {
return;
}
for (int i = 0; i < con->count_encoders; ++i) {
m_encoders << con->encoders[i];
}
}
DrmConnector::~DrmConnector() = default;
@ -61,4 +68,13 @@ bool DrmConnector::initProps()
return true;
}
bool DrmConnector::isConnected()
{
ScopedDrmPointer<_drmModeConnector, &drmModeFreeConnector> con(drmModeGetConnector(m_fd, m_id));
if (!con) {
return false;
}
return con->connection == DRM_MODE_CONNECTED;
}
}

View file

@ -38,8 +38,17 @@ public:
CrtcId = 0,
Count
};
QVector<uint32_t> encoders() {
return m_encoders;
}
bool initProps();
bool isConnected();
private:
QVector<uint32_t> m_encoders;
};
}

View file

@ -18,21 +18,27 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "drm_object_crtc.h"
#include "drm_backend.h"
#include "drm_output.h"
#include "drm_buffer.h"
#include "logging.h"
namespace KWin
{
DrmCrtc::DrmCrtc(uint32_t crtc_id, int fd)
: DrmObject(crtc_id, fd)
DrmCrtc::DrmCrtc(uint32_t crtc_id, int fd, int resIndex)
: DrmObject(crtc_id, fd),
m_resIndex(resIndex)
{
}
DrmCrtc::~DrmCrtc() = default;
DrmCrtc::~DrmCrtc()
{
}
bool DrmCrtc::init()
{
qCDebug(KWIN_DRM) << "Creating CRTC" << m_id;
qCDebug(KWIN_DRM) << "Atomic init for CRTC:" << resIndex() << "id:" << m_id;
if (!initProps()) {
return false;
@ -61,4 +67,41 @@ bool DrmCrtc::initProps()
return true;
}
void DrmCrtc::flipBuffer()
{
if (m_currentBuffer && m_currentBuffer->deleteAfterPageFlip() && m_currentBuffer != m_nextBuffer) {
delete m_currentBuffer;
}
m_currentBuffer = m_nextBuffer;
m_nextBuffer = nullptr;
delete m_blackBuffer;
m_blackBuffer = nullptr;
}
bool DrmCrtc::blank()
{
if (!m_blackBuffer) {
DrmBuffer *blackBuffer = m_output->m_backend->createBuffer(m_output->pixelSize());
if (!blackBuffer->map()) {
delete blackBuffer;
return false;
}
blackBuffer->image()->fill(Qt::black);
m_blackBuffer = blackBuffer;
}
// TODO: Do this atomically
if (m_output->setModeLegacy(m_blackBuffer)) {
if (m_currentBuffer && m_currentBuffer->deleteAfterPageFlip()) {
delete m_currentBuffer;
delete m_nextBuffer;
}
m_currentBuffer = nullptr;
m_nextBuffer = nullptr;
return true;
}
return false;
}
}

View file

@ -25,12 +25,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
namespace KWin
{
class DrmBackend;
class DrmBuffer;
class DrmCrtc : public DrmObject
{
public:
DrmCrtc(uint32_t crtc_id, int fd);
DrmCrtc(uint32_t crtc_id, int fd, int resIndex);
virtual ~DrmCrtc();
@ -43,6 +44,31 @@ public:
};
bool initProps();
int resIndex() const {
return m_resIndex;
}
DrmBuffer *current() {
return m_currentBuffer;
}
DrmBuffer *next() {
return m_nextBuffer;
}
void setNext(DrmBuffer *buffer) {
m_nextBuffer = buffer;
}
void flipBuffer();
bool blank();
private:
DrmBackend *m_backend;
int m_resIndex;
DrmBuffer *m_currentBuffer = nullptr;
DrmBuffer *m_nextBuffer = nullptr;
DrmBuffer *m_blackBuffer = nullptr;
};
}

View file

@ -62,54 +62,38 @@ DrmOutput::DrmOutput(DrmBackend *backend)
DrmOutput::~DrmOutput()
{
hideCursor();
cleanupBlackBuffer();
delete m_crtc;
delete m_conn;
m_crtc->blank();
delete m_waylandOutput.data();
delete m_waylandOutputDevice.data();
}
void DrmOutput::cleanup()
void DrmOutput::releaseGbm()
{
if (m_currentBuffer) {
m_currentBuffer->releaseGbm();
}
if (m_nextBuffer) {
m_nextBuffer->releaseGbm();
if (DrmBuffer *b = m_crtc->current()) {
b->releaseGbm();
}
if (m_primaryPlane) {
if (m_primaryPlane->current()) {
m_primaryPlane->current()->releaseGbm();
}
if (m_primaryPlane->next()) {
m_primaryPlane->next()->releaseGbm();
}
}
}
void DrmOutput::hideCursor()
{
drmModeSetCursor(m_backend->fd(), m_crtcId, 0, 0, 0);
}
void DrmOutput::restoreSaved()
{
if (!m_savedCrtc.isNull()) {
drmModeSetCrtc(m_backend->fd(), m_savedCrtc->crtc_id, m_savedCrtc->buffer_id,
m_savedCrtc->x, m_savedCrtc->y, &m_connector, 1, &m_savedCrtc->mode);
}
drmModeSetCursor(m_backend->fd(), m_crtc->id(), 0, 0, 0);
}
void DrmOutput::showCursor(DrmBuffer *c)
{
const QSize &s = c->size();
drmModeSetCursor(m_backend->fd(), m_crtcId, c->handle(), s.width(), s.height());
drmModeSetCursor(m_backend->fd(), m_crtc->id(), c->handle(), s.width(), s.height());
}
void DrmOutput::moveCursor(const QPoint &globalPos)
{
const QPoint p = (globalPos - m_globalPos) * m_scale;
drmModeMoveCursor(m_backend->fd(), m_crtcId, p.x(), p.y());
drmModeMoveCursor(m_backend->fd(), m_crtc->id(), p.x(), p.y());
}
QSize DrmOutput::pixelSize() const
@ -193,8 +177,7 @@ bool DrmOutput::init(drmModeConnector *connector)
return false;
}
}
m_savedCrtc.reset(drmModeGetCrtc(m_backend->fd(), m_crtcId));
if (!blank()) {
if (!m_crtc->blank()) {
return false;
}
setDpms(DpmsMode::On);
@ -312,7 +295,7 @@ bool DrmOutput::init(drmModeConnector *connector)
void DrmOutput::initUuid()
{
QCryptographicHash hash(QCryptographicHash::Md5);
hash.addData(QByteArray::number(m_connector));
hash.addData(QByteArray::number(m_conn->id()));
hash.addData(m_edid.eisaId);
hash.addData(m_edid.monitorName);
hash.addData(m_edid.serialNumber);
@ -513,12 +496,12 @@ bool DrmOutput::initPrimaryPlane()
if (m_primaryPlane) { // Output already has a primary plane
continue;
}
if (!p->isCrtcSupported(m_crtcId)) {
if (!p->isCrtcSupported(m_crtc->id())) {
continue;
}
p->setOutput(this);
m_primaryPlane = p;
qCDebug(KWIN_DRM) << "Initialized primary plane" << p->id() << "on CRTC" << m_crtcId;
qCDebug(KWIN_DRM) << "Initialized primary plane" << p->id() << "on CRTC" << m_crtc->id();
return true;
}
qCCritical(KWIN_DRM) << "Failed to initialize primary plane.";
@ -541,12 +524,12 @@ bool DrmOutput::initCursorPlane() // TODO: Add call in init (but needs lay
if (m_cursorPlane) { // Output already has a cursor plane
continue;
}
if (!p->isCrtcSupported(m_crtcId)) {
if (!p->isCrtcSupported(m_crtc->id())) {
continue;
}
p->setOutput(this);
m_cursorPlane = p;
qCDebug(KWIN_DRM) << "Initialized cursor plane" << p->id() << "on CRTC" << m_crtcId;
qCDebug(KWIN_DRM) << "Initialized cursor plane" << p->id() << "on CRTC" << m_crtc->id();
return true;
}
return false;
@ -580,17 +563,17 @@ void DrmOutput::setDpms(DrmOutput::DpmsMode mode)
drmModeAtomicReq *req = drmModeAtomicAlloc();
if (atomicReqModesetPopulate(req, mode == DpmsMode::On) == DrmObject::AtomicReturn::Error) {
qCWarning(KWIN_DRM) << "Failed to populate atomic request for output" << m_crtcId;
qCWarning(KWIN_DRM) << "Failed to populate atomic request for output" << m_crtc->id();
return;
}
if (drmModeAtomicCommit(m_backend->fd(), req, DRM_MODE_ATOMIC_ALLOW_MODESET, this)) {
qCWarning(KWIN_DRM) << "Failed to commit atomic request for output" << m_crtcId;
qCWarning(KWIN_DRM) << "Failed to commit atomic request for output" << m_crtc->id();
} else {
qCDebug(KWIN_DRM) << "DPMS set for output" << m_crtcId;
qCDebug(KWIN_DRM) << "DPMS set for output" << m_crtc->id();
}
drmModeAtomicFree(req);
} else {
if (drmModeConnectorSetProperty(m_backend->fd(), m_connector, m_dpms->prop_id, uint64_t(mode)) < 0) {
if (drmModeConnectorSetProperty(m_backend->fd(), m_conn->id(), m_dpms->prop_id, uint64_t(mode)) < 0) {
qCWarning(KWIN_DRM) << "Setting DPMS failed";
return;
}
@ -605,7 +588,7 @@ void DrmOutput::setDpms(DrmOutput::DpmsMode mode)
m_backend->outputWentOff();
} else {
m_backend->checkOutputsAreOn();
blank();
m_crtc->blank();
if (Compositor *compositor = Compositor::self()) {
compositor->addRepaintFull();
}
@ -698,6 +681,9 @@ bool DrmOutput::commitChanges()
void DrmOutput::pageFlipped()
{
if (!m_crtc) {
return;
}
if (m_backend->atomicModeSetting()){
foreach (DrmPlane *p, m_planesFlipList) {
pageFlippedBufferRemover(p->current(), p->next());
@ -707,18 +693,15 @@ void DrmOutput::pageFlipped()
m_planesFlipList.clear();
} else {
if (!m_nextBuffer) {
if (!m_crtc->next()) {
// on manual vt switch
if (m_currentBuffer) {
m_currentBuffer->releaseGbm();
if (DrmBuffer *b = m_crtc->current()) {
b->releaseGbm();
}
return;
}
pageFlippedBufferRemover(m_currentBuffer, m_nextBuffer);
m_currentBuffer = m_nextBuffer;
m_nextBuffer = nullptr;
m_crtc->flipBuffer();
}
cleanupBlackBuffer();
}
void DrmOutput::pageFlippedBufferRemover(DrmBuffer *oldbuffer, DrmBuffer *newbuffer)
@ -728,28 +711,6 @@ void DrmOutput::pageFlippedBufferRemover(DrmBuffer *oldbuffer, DrmBuffer *newbuf
}
}
void DrmOutput::cleanupBlackBuffer()
{
if (m_blackBuffer) {
delete m_blackBuffer;
m_blackBuffer = nullptr;
}
}
bool DrmOutput::blank()
{
if (!m_blackBuffer) {
m_blackBuffer = m_backend->createBuffer(pixelSize());
if (!m_blackBuffer->map()) {
cleanupBlackBuffer();
return false;
}
m_blackBuffer->image()->fill(Qt::black);
}
// TODO: Do this atomically
return setModeLegacy(m_blackBuffer);
}
bool DrmOutput::present(DrmBuffer *buffer)
{
if (!buffer || buffer->bufferId() == 0) {
@ -868,11 +829,11 @@ bool DrmOutput::presentAtomically(DrmBuffer *buffer)
bool DrmOutput::presentLegacy(DrmBuffer *buffer)
{
if (m_nextBuffer) {
if (m_crtc->next()) {
return false;
}
if (!LogindIntegration::self()->isActiveSession()) {
m_nextBuffer = buffer;
m_crtc->setNext(buffer);
return false;
}
if (m_dpmsMode != DpmsMode::On) {
@ -885,9 +846,9 @@ bool DrmOutput::presentLegacy(DrmBuffer *buffer)
return false;
}
int errno_save = 0;
const bool ok = drmModePageFlip(m_backend->fd(), m_crtcId, buffer->bufferId(), DRM_MODE_PAGE_FLIP_EVENT, this) == 0;
const bool ok = drmModePageFlip(m_backend->fd(), m_crtc->id(), buffer->bufferId(), DRM_MODE_PAGE_FLIP_EVENT, this) == 0;
if (ok) {
m_nextBuffer = buffer;
m_crtc->setNext(buffer);
} else {
errno_save = errno;
qCWarning(KWIN_DRM) << "Page flip failed:" << strerror(errno);
@ -898,7 +859,8 @@ bool DrmOutput::presentLegacy(DrmBuffer *buffer)
bool DrmOutput::setModeLegacy(DrmBuffer *buffer)
{
if (drmModeSetCrtc(m_backend->fd(), m_crtcId, buffer->bufferId(), 0, 0, &m_connector, 1, &m_mode) == 0) {
uint32_t connId = m_conn->id();
if (drmModeSetCrtc(m_backend->fd(), m_crtc->id(), buffer->bufferId(), 0, 0, &connId, 1, &m_mode) == 0) {
m_lastStride = buffer->stride();
m_lastGbm = buffer->isGbm();
return true;

View file

@ -61,15 +61,13 @@ public:
QSize physicalSize;
};
virtual ~DrmOutput();
void cleanup();
void releaseGbm();
void showCursor(DrmBuffer *buffer);
void hideCursor();
void moveCursor(const QPoint &globalPos);
bool init(drmModeConnector *connector);
bool present(DrmBuffer *buffer);
void pageFlipped();
void restoreSaved();
bool blank();
/**
* This sets the changes and tests them against the DRM output
@ -108,8 +106,9 @@ Q_SIGNALS:
private:
friend class DrmBackend;
friend class DrmCrtc; // TODO: For use of setModeLegacy. Remove later when we allow multiple connectors per crtc
// and save the connector ids in the DrmCrtc instance.
DrmOutput(DrmBackend *backend);
void cleanupBlackBuffer();
bool presentAtomically(DrmBuffer *buffer);
bool presentLegacy(DrmBuffer *buffer);
bool setModeLegacy(DrmBuffer *buffer);
@ -126,23 +125,14 @@ private:
DrmObject::AtomicReturn atomicReqModesetPopulate(drmModeAtomicReq *req, bool enable);
DrmBackend *m_backend;
DrmConnector *m_conn = nullptr;
DrmCrtc *m_crtc = nullptr;
QPoint m_globalPos;
qreal m_scale = 1;
quint32 m_crtcId = 0;
quint32 m_connector = 0;
quint32 m_lastStride = 0;
bool m_lastGbm = false;
drmModeModeInfo m_mode;
DrmBuffer *m_currentBuffer = nullptr;
DrmBuffer *m_nextBuffer = nullptr;
DrmBuffer *m_blackBuffer = nullptr;
struct CrtcCleanup {
static void inline cleanup(_drmModeCrtc *ptr) {
drmModeFreeCrtc(ptr); // TODO: Atomically? See compositor-drm.c l.3670
}
};
Edid m_edid;
QScopedPointer<_drmModeCrtc, CrtcCleanup> m_savedCrtc;
QPointer<KWayland::Server::OutputInterface> m_waylandOutput;
QPointer<KWayland::Server::OutputDeviceInterface> m_waylandOutputDevice;
QPointer<KWayland::Server::OutputChangeSet> m_changeset;
@ -150,8 +140,6 @@ private:
DpmsMode m_dpmsMode = DpmsMode::On;
QByteArray m_uuid;
DrmConnector *m_conn = nullptr;
DrmCrtc *m_crtc = nullptr;
uint32_t m_blobId = 0;
DrmPlane* m_primaryPlane = nullptr;
DrmPlane* m_cursorPlane = nullptr;

View file

@ -74,7 +74,7 @@ void EglGbmBackend::cleanupSurfaces()
void EglGbmBackend::cleanupOutput(const Output &o)
{
o.output->cleanup();
o.output->releaseGbm();
if (o.eglSurface != EGL_NO_SURFACE) {
eglDestroySurface(eglDisplay(), o.eglSurface);