Add a DrmOutput class to be used by the DrmBackend

The DrmOutput corresponds to one output we perform mode setting on.
This implies that page flip is now performed on the DrmOutput. As our
compositor cannot drive multiple outputs yet, we only use the first one
and call into bufferSwapComplete and aboutToBufferSwap when either all
page flips are completed or respectivly the first one is performed.

Setting cursor is also handled on each DrmOutput.

When the DrmOutput is initialized it is blanked with a black buffer which
gets destroyed once the first page flip event has occurred (meanig the
buffer is no longer needed). Before setting the black buffer we store the
current buffer which will be set back on tear down. Because of that
tearing down the DrmBackend needs to wait till all page flips ended.
icc-effect-5.14.5
Martin Gräßlin 2015-04-14 11:25:16 +02:00
parent 38b676d809
commit 2f312f35c9
2 changed files with 175 additions and 43 deletions

View File

@ -67,7 +67,11 @@ DrmBackend::DrmBackend(QObject *parent)
DrmBackend::~DrmBackend()
{
if (m_fd >= 0) {
hideCursor();
// wait for pageflips
while (m_pageFlipsPending != 0) {
QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents);
}
qDeleteAll(m_outputs);
delete m_cursor[0];
delete m_cursor[1];
close(m_fd);
@ -99,15 +103,16 @@ void DrmBackend::pageFlipHandler(int fd, unsigned int frame, unsigned int sec, u
Q_UNUSED(frame)
Q_UNUSED(sec)
Q_UNUSED(usec)
DrmBuffer *buffer = reinterpret_cast<DrmBuffer*>(data);
buffer->m_backend->m_pageFlipPending = false;
#if HAVE_GBM
if (buffer->m_bo) {
gbm_surface_release_buffer(buffer->m_surface, buffer->m_bo);
buffer->m_bo = nullptr;
auto output = reinterpret_cast<DrmOutput*>(data);
output->pageFlipped();
output->m_backend->m_pageFlipsPending--;
if (output->m_backend->m_pageFlipsPending == 0) {
// TODO: improve, this currently means we wait for all page flips or all outputs.
// It would be better to driver the repaint per output
if (Compositor::self()) {
Compositor::self()->bufferSwapComplete();
}
}
#endif
Compositor::self()->bufferSwapComplete();
}
void DrmBackend::openDrm()
@ -178,31 +183,28 @@ void DrmBackend::queryResources()
if (!crtc) {
continue;
}
m_resolution = QSize(crtc->mode.hdisplay, crtc->mode.vdisplay);
m_crtcId = encoder->crtc_id;
m_connector = connector->connector_id;
m_mode = crtc->mode;
// TODO: improve
DrmBuffer *b = new DrmBuffer(this, m_resolution);
b->map();
b->image()->fill(Qt::black);
drmModeSetCrtc(m_fd, m_crtcId, b->m_bufferId, 0, 0, &m_connector, 1, &m_mode);
// for the moment only one crtc
break;
DrmOutput *drmOutput = new DrmOutput(this);
drmOutput->m_crtcId = encoder->crtc_id;
drmOutput->m_mode = crtc->mode;
drmOutput->m_connector = connector->connector_id;
drmOutput->init();
m_outputs << drmOutput;
}
// TODO: install global space
}
void DrmBackend::present(DrmBuffer *buffer)
{
if (!buffer || buffer->m_bufferId == 0) {
if (m_outputs.isEmpty()) {
return;
}
if (m_pageFlipPending) {
return;
// TODO: correct output
if (m_outputs.first()->present(buffer)) {
m_pageFlipsPending++;
if (m_pageFlipsPending == 1 && Compositor::self()) {
Compositor::self()->aboutToSwapBuffers();
}
}
m_pageFlipPending = true;
Compositor::self()->aboutToSwapBuffers();
drmModePageFlip(m_fd, m_crtcId, buffer->m_bufferId, DRM_MODE_PAGE_FLIP_EVENT, buffer);
}
void DrmBackend::installCursorFromServer()
@ -245,7 +247,9 @@ void DrmBackend::setCursor()
{
DrmBuffer *c = m_cursor[m_cursorIndex];
m_cursorIndex = (m_cursorIndex + 1) % 2;
drmModeSetCursor(m_fd, m_crtcId, c->m_handle, c->m_size.width(), c->m_size.height());
for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) {
(*it)->showCursor(c);
}
}
void DrmBackend::updateCursor()
@ -268,13 +272,25 @@ void DrmBackend::updateCursor()
void DrmBackend::hideCursor()
{
drmModeSetCursor(m_fd, m_crtcId, 0, 0, 0);
for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) {
(*it)->hideCursor();
}
}
void DrmBackend::moveCursor()
{
const QPoint p = Cursor::pos() - softwareCursorHotspot();
drmModeMoveCursor(m_fd, m_crtcId, p.x(), p.y());
for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) {
(*it)->moveCursor(p);
}
}
QSize DrmBackend::size() const
{
if (m_outputs.isEmpty()) {
return QSize();
}
return m_outputs.first()->size();
}
Screens *DrmBackend::createScreens(QObject *parent)
@ -307,6 +323,79 @@ DrmBuffer *DrmBackend::createBuffer(gbm_surface *surface)
#endif
}
DrmOutput::DrmOutput(DrmBackend *backend)
: m_backend(backend)
{
}
DrmOutput::~DrmOutput()
{
hideCursor();
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);
}
cleanupBlackBuffer();
}
void DrmOutput::hideCursor()
{
drmModeSetCursor(m_backend->fd(), m_crtcId, 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());
}
void DrmOutput::moveCursor(const QPoint &globalPos)
{
const QPoint p = globalPos - m_globalPos;
drmModeMoveCursor(m_backend->fd(), m_crtcId, p.x(), p.y());
}
QSize DrmOutput::size() const
{
return QSize(m_mode.hdisplay, m_mode.vdisplay);
}
bool DrmOutput::present(DrmBuffer *buffer)
{
if (!buffer || buffer->bufferId() == 0) {
return false;
}
if (m_currentBuffer) {
return false;
}
m_currentBuffer = buffer;
return drmModePageFlip(m_backend->fd(), m_crtcId, buffer->bufferId(), DRM_MODE_PAGE_FLIP_EVENT, this) == 0;
}
void DrmOutput::pageFlipped()
{
m_currentBuffer->releaseGbm();
m_currentBuffer = nullptr;
cleanupBlackBuffer();
}
void DrmOutput::cleanupBlackBuffer()
{
if (m_blackBuffer) {
delete m_blackBuffer;
m_blackBuffer = nullptr;
}
}
void DrmOutput::init()
{
m_savedCrtc.reset(drmModeGetCrtc(m_backend->fd(), m_crtcId));
m_blackBuffer = m_backend->createBuffer(size());
m_blackBuffer->map();
m_blackBuffer->image()->fill(Qt::black);
drmModeSetCrtc(m_backend->fd(), m_crtcId, m_blackBuffer->bufferId(), 0, 0, &m_connector, 1, &m_mode);
}
DrmBuffer::DrmBuffer(DrmBackend *backend, const QSize &size)
: m_backend(backend)
, m_size(size)
@ -368,11 +457,7 @@ DrmBuffer::~DrmBuffer()
destroyArgs.handle = m_handle;
drmIoctl(m_backend->fd(), DRM_IOCTL_MODE_DESTROY_DUMB, &destroyArgs);
}
#if HAVE_GBM
if (m_bo) {
gbm_surface_release_buffer(m_surface, m_bo);
}
#endif
releaseGbm();
}
bool DrmBuffer::map(QImage::Format format)
@ -395,4 +480,14 @@ bool DrmBuffer::map(QImage::Format format)
return !m_image->isNull();
}
void DrmBuffer::releaseGbm()
{
#if HAVE_GBM
if (m_bo) {
gbm_surface_release_buffer(m_surface, m_bo);
m_bo = nullptr;
}
#endif
}
}

View File

@ -34,6 +34,7 @@ namespace KWin
class Udev;
class DrmBuffer;
class DrmOutput;
class KWIN_EXPORT DrmBackend : public AbstractBackend
{
@ -53,10 +54,7 @@ public:
DrmBuffer *createBuffer(gbm_surface *surface);
void present(DrmBuffer *buffer);
QSize size() const {
// TODO: this is wrong
return m_resolution;
}
QSize size() const;
int fd() const {
return m_fd;
}
@ -76,14 +74,43 @@ private:
QScopedPointer<Udev> m_udev;
int m_fd = -1;
int m_drmId = 0;
// TODO: this is wrong
QSize m_resolution;
QVector<DrmOutput*> m_outputs;
DrmBuffer *m_cursor[2];
int m_cursorIndex = 0;
int m_pageFlipsPending = 0;
};
class DrmOutput
{
public:
virtual ~DrmOutput();
void showCursor(DrmBuffer *buffer);
void hideCursor();
void moveCursor(const QPoint &globalPos);
bool present(DrmBuffer *buffer);
void pageFlipped();
void init();
QSize size() const;
private:
friend class DrmBackend;
DrmOutput(DrmBackend *backend);
void cleanupBlackBuffer();
DrmBackend *m_backend;
QPoint m_globalPos;
quint32 m_crtcId = 0;
quint32 m_connector = 0;
drmModeModeInfo m_mode;
bool m_pageFlipPending = false;
DrmBuffer *m_cursor[2];
int m_cursorIndex = 0;
DrmBuffer *m_currentBuffer = nullptr;
DrmBuffer *m_blackBuffer = nullptr;
struct CrtcCleanup {
static void inline cleanup(_drmModeCrtc *ptr) {
drmModeFreeCrtc(ptr);
}
};
QScopedPointer<_drmModeCrtc, CrtcCleanup> m_savedCrtc;
};
class DrmBuffer
@ -95,6 +122,16 @@ public:
QImage *image() const {
return m_image;
}
quint32 handle() const {
return m_handle;
}
const QSize &size() const {
return m_size;
}
quint32 bufferId() const {
return m_bufferId;
}
void releaseGbm();
private:
friend class DrmBackend;