[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:
parent
750843061c
commit
a0571ccf84
11 changed files with 262 additions and 213 deletions
|
@ -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)) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -61,7 +61,6 @@ public:
|
|||
bool deleteAfterPageFlip() const {
|
||||
return m_deleteAfterPageFlip;
|
||||
}
|
||||
|
||||
void releaseGbm();
|
||||
|
||||
private:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue