Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qlinuxfbdrmscreen.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4// Experimental DRM dumb buffer backend.
5//
6// TODO:
7// Multiscreen: QWindow-QScreen(-output) association. Needs some reorg (device cannot be owned by screen)
8// Find card via devicediscovery like in eglfs_kms.
9// Mode restore like QEglFSKmsInterruptHandler.
10// grabWindow
11
12#include "qlinuxfbdrmscreen.h"
13#include <QLoggingCategory>
14#include <QGuiApplication>
15#include <QPainter>
16#include <QtFbSupport/private/qfbcursor_p.h>
17#include <QtFbSupport/private/qfbwindow_p.h>
18#include <QtKmsSupport/private/qkmsdevice_p.h>
19#include <QtCore/private/qcore_unix_p.h>
20#include <sys/mman.h>
21
23
24Q_LOGGING_CATEGORY(qLcFbDrm, "qt.qpa.fb")
25
26static const int BUFFER_COUNT = 2;
27
29{
30public:
31 struct Framebuffer {
32 Framebuffer() : handle(0), pitch(0), size(0), fb(0), p(MAP_FAILED) { }
33 uint32_t handle;
34 uint32_t pitch;
35 uint64_t size;
36 uint32_t fb;
37 void *p;
39 };
40
41 struct Output {
42 Output() : backFb(0), flipped(false) { }
46 int backFb;
47 bool flipped;
48 QSize currentRes() const {
49 const drmModeModeInfo &modeInfo(kmsOutput.modes[kmsOutput.mode]);
50 return QSize(modeInfo.hdisplay, modeInfo.vdisplay);
51 }
52 };
53
54 QLinuxFbDevice(QKmsScreenConfig *screenConfig);
55
56 bool open() override;
57 void close() override;
58
59 void createFramebuffers();
60 void destroyFramebuffers();
61 void setMode();
62
63 void swapBuffers(Output *output);
64
65 int outputCount() const { return m_outputs.size(); }
66 Output *output(int idx) { return &m_outputs[idx]; }
67
68private:
69 void *nativeDisplay() const override;
70 QPlatformScreen *createScreen(const QKmsOutput &output) override;
71 void registerScreen(QPlatformScreen *screen,
72 bool isPrimary,
73 const QPoint &virtualPos,
74 const QList<QPlatformScreen *> &virtualSiblings) override;
75
76 bool createFramebuffer(Output *output, int bufferIdx);
77 void destroyFramebuffer(Output *output, int bufferIdx);
78
79 static void pageFlipHandler(int fd, unsigned int sequence,
80 unsigned int tv_sec, unsigned int tv_usec, void *user_data);
81
82 QList<Output> m_outputs;
83};
84
86 : QKmsDevice(screenConfig, QStringLiteral("/dev/dri/card0"))
87{
88}
89
91{
92 int fd = qt_safe_open(devicePath().toLocal8Bit().constData(), O_RDWR | O_CLOEXEC);
93 if (fd == -1) {
94 qErrnoWarning("Could not open DRM device %s", qPrintable(devicePath()));
95 return false;
96 }
97
98 uint64_t hasDumbBuf = 0;
99 if (drmGetCap(fd, DRM_CAP_DUMB_BUFFER, &hasDumbBuf) == -1 || !hasDumbBuf) {
100 qWarning("Dumb buffers not supported");
102 return false;
103 }
104
105 setFd(fd);
106
107 qCDebug(qLcFbDrm, "DRM device %s opened", qPrintable(devicePath()));
108
109 return true;
110}
111
113{
114 for (Output &output : m_outputs)
115 output.kmsOutput.cleanup(this); // restore mode
116
117 m_outputs.clear();
118
119 if (fd() != -1) {
120 qCDebug(qLcFbDrm, "Closing DRM device");
121 qt_safe_close(fd());
122 setFd(-1);
123 }
124}
125
127{
128 Q_UNREACHABLE_RETURN(nullptr);
129}
130
132{
133 qCDebug(qLcFbDrm, "Got a new output: %s", qPrintable(output.name));
134 Output o;
135 o.kmsOutput = output;
136 m_outputs.append(o);
137 return nullptr; // no platformscreen, we are not a platform plugin
138}
139
141 bool isPrimary,
142 const QPoint &virtualPos,
143 const QList<QPlatformScreen *> &virtualSiblings)
144{
146 Q_UNUSED(isPrimary);
147 Q_UNUSED(virtualPos);
148 Q_UNUSED(virtualSiblings);
149 Q_UNREACHABLE();
150}
151
152static uint32_t bppForDrmFormat(uint32_t drmFormat)
153{
154 switch (drmFormat) {
156 case DRM_FORMAT_BGR565:
157 return 16;
158 default:
159 return 32;
160 }
161}
162
163static int depthForDrmFormat(uint32_t drmFormat)
164{
165 switch (drmFormat) {
167 case DRM_FORMAT_BGR565:
168 return 16;
169 case DRM_FORMAT_XRGB8888:
170 case DRM_FORMAT_XBGR8888:
171 return 24;
172 case DRM_FORMAT_XRGB2101010:
173 case DRM_FORMAT_XBGR2101010:
174 return 30;
175 default:
176 return 32;
177 }
178}
179
180static QImage::Format formatForDrmFormat(uint32_t drmFormat)
181{
182 switch (drmFormat) {
183 case DRM_FORMAT_XRGB8888:
184 case DRM_FORMAT_XBGR8888:
186 case DRM_FORMAT_ARGB8888:
190 case DRM_FORMAT_BGR565:
192 case DRM_FORMAT_XRGB2101010:
193 case DRM_FORMAT_XBGR2101010:
195 case DRM_FORMAT_ARGB2101010:
196 case DRM_FORMAT_ABGR2101010:
198 default:
200 }
201}
202
203bool QLinuxFbDevice::createFramebuffer(QLinuxFbDevice::Output *output, int bufferIdx)
204{
205 const QSize size = output->currentRes();
206 const uint32_t w = size.width();
207 const uint32_t h = size.height();
208 const uint32_t bpp = bppForDrmFormat(output->kmsOutput.drm_format);
209 drm_mode_create_dumb creq = {
210 h,
211 w,
212 bpp,
213 0, 0, 0, 0
214 };
215 if (drmIoctl(fd(), DRM_IOCTL_MODE_CREATE_DUMB, &creq) == -1) {
216 qErrnoWarning(errno, "Failed to create dumb buffer");
217 return false;
218 }
219
220 Framebuffer &fb(output->fb[bufferIdx]);
221 fb.handle = creq.handle;
222 fb.pitch = creq.pitch;
223 fb.size = creq.size;
224 qCDebug(qLcFbDrm, "Got a dumb buffer for size %dx%d and bpp %u: handle %u, pitch %u, size %u",
225 w, h, bpp, fb.handle, fb.pitch, (uint) fb.size);
226
227 uint32_t handles[4] = { fb.handle };
228 uint32_t strides[4] = { fb.pitch };
229 uint32_t offsets[4] = { 0 };
230
231 if (drmModeAddFB2(fd(), w, h, output->kmsOutput.drm_format,
232 handles, strides, offsets, &fb.fb, 0) == -1) {
233 qErrnoWarning(errno, "Failed to add FB");
234 return false;
235 }
236
237 drm_mode_map_dumb mreq = {
238 fb.handle,
239 0, 0
240 };
241 if (drmIoctl(fd(), DRM_IOCTL_MODE_MAP_DUMB, &mreq) == -1) {
242 qErrnoWarning(errno, "Failed to map dumb buffer");
243 return false;
244 }
245 fb.p = mmap(nullptr, fb.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd(), mreq.offset);
246 if (fb.p == MAP_FAILED) {
247 qErrnoWarning(errno, "Failed to mmap dumb buffer");
248 return false;
249 }
250
251 qCDebug(qLcFbDrm, "FB is %u (DRM format 0x%x), mapped at %p", fb.fb, output->kmsOutput.drm_format, fb.p);
252 memset(fb.p, 0, fb.size);
253
254 fb.wrapper = QImage(static_cast<uchar *>(fb.p), w, h, fb.pitch, formatForDrmFormat(output->kmsOutput.drm_format));
255
256 return true;
257}
258
260{
261 for (Output &output : m_outputs) {
262 for (int i = 0; i < BUFFER_COUNT; ++i) {
263 if (!createFramebuffer(&output, i))
264 return;
265 }
266 output.backFb = 0;
267 output.flipped = false;
268 }
269}
270
271void QLinuxFbDevice::destroyFramebuffer(QLinuxFbDevice::Output *output, int bufferIdx)
272{
273 Framebuffer &fb(output->fb[bufferIdx]);
274 if (fb.p != MAP_FAILED)
275 munmap(fb.p, fb.size);
276 if (fb.fb) {
277 if (drmModeRmFB(fd(), fb.fb) == -1)
278 qErrnoWarning("Failed to remove fb");
279 }
280 if (fb.handle) {
281 drm_mode_destroy_dumb dreq = { fb.handle };
282 if (drmIoctl(fd(), DRM_IOCTL_MODE_DESTROY_DUMB, &dreq) == -1)
283 qErrnoWarning(errno, "Failed to destroy dumb buffer %u", fb.handle);
284 }
285 fb = Framebuffer();
286}
287
289{
290 for (Output &output : m_outputs) {
291 for (int i = 0; i < BUFFER_COUNT; ++i)
292 destroyFramebuffer(&output, i);
293 }
294}
295
297{
298 for (Output &output : m_outputs) {
299 drmModeModeInfo &modeInfo(output.kmsOutput.modes[output.kmsOutput.mode]);
300 if (drmModeSetCrtc(fd(), output.kmsOutput.crtc_id, output.fb[0].fb, 0, 0,
301 &output.kmsOutput.connector_id, 1, &modeInfo) == -1) {
302 qErrnoWarning(errno, "Failed to set mode");
303 return;
304 }
305
306 output.kmsOutput.mode_set = true; // have cleanup() to restore the mode
307 output.kmsOutput.setPowerState(this, QPlatformScreen::PowerStateOn);
308 }
309}
310
311void QLinuxFbDevice::pageFlipHandler(int fd, unsigned int sequence,
312 unsigned int tv_sec, unsigned int tv_usec,
313 void *user_data)
314{
315 Q_UNUSED(fd);
316 Q_UNUSED(sequence);
317 Q_UNUSED(tv_sec);
318 Q_UNUSED(tv_usec);
319
320 Output *output = static_cast<Output *>(user_data);
321 output->backFb = (output->backFb + 1) % BUFFER_COUNT;
322}
323
325{
326 Framebuffer &fb(output->fb[output->backFb]);
327 if (drmModePageFlip(fd(), output->kmsOutput.crtc_id, fb.fb, DRM_MODE_PAGE_FLIP_EVENT, output) == -1) {
328 qErrnoWarning(errno, "Page flip failed");
329 return;
330 }
331
332 const int fbIdx = output->backFb;
333 while (output->backFb == fbIdx) {
334 drmEventContext drmEvent;
335 memset(&drmEvent, 0, sizeof(drmEvent));
336 drmEvent.version = 2;
337 drmEvent.vblank_handler = nullptr;
338 drmEvent.page_flip_handler = pageFlipHandler;
339 // Blocks until there is something to read on the drm fd
340 // and calls back pageFlipHandler once the flip completes.
341 drmHandleEvent(fd(), &drmEvent);
342 }
343}
344
346 : m_screenConfig(nullptr),
347 m_device(nullptr)
348{
349 Q_UNUSED(args);
350}
351
353{
354 if (m_device) {
355 m_device->destroyFramebuffers();
356 m_device->close();
357 delete m_device;
358 }
359 delete m_screenConfig;
360}
361
363{
364 m_screenConfig = new QKmsScreenConfig;
365 m_screenConfig->loadConfig();
366 m_device = new QLinuxFbDevice(m_screenConfig);
367 if (!m_device->open())
368 return false;
369
370 // Discover outputs. Calls back Device::createScreen().
371 m_device->createScreens();
372 // Now off to dumb buffer specifics.
373 m_device->createFramebuffers();
374 // Do the modesetting.
375 m_device->setMode();
376
377 QLinuxFbDevice::Output *output(m_device->output(0));
378
379 mGeometry = QRect(QPoint(0, 0), output->currentRes());
380 mDepth = depthForDrmFormat(output->kmsOutput.drm_format);
381 mFormat = formatForDrmFormat(output->kmsOutput.drm_format);
382 mPhysicalSize = output->kmsOutput.physical_size;
383 qCDebug(qLcFbDrm) << mGeometry << mPhysicalSize << mDepth << mFormat;
384
386
387 mCursor = new QFbCursor(this);
388
389 return true;
390}
391
393{
394 const QRegion dirty = QFbScreen::doRedraw();
395 if (dirty.isEmpty())
396 return dirty;
397
398 QLinuxFbDevice::Output *output(m_device->output(0));
399
400 for (int i = 0; i < BUFFER_COUNT; ++i)
401 output->dirty[i] += dirty;
402
403 if (output->fb[output->backFb].wrapper.isNull())
404 return dirty;
405
406 QPainter pntr(&output->fb[output->backFb].wrapper);
407 // Image has alpha but no need for blending at this stage.
408 // Do not waste time with the default SourceOver.
409 pntr.setCompositionMode(QPainter::CompositionMode_Source);
410 for (const QRect &rect : std::as_const(output->dirty[output->backFb]))
411 pntr.drawImage(rect, mScreenImage, rect);
412 pntr.end();
413
414 output->dirty[output->backFb] = QRegion();
415
416 m_device->swapBuffers(output);
417
418 return dirty;
419}
420
421QPixmap QLinuxFbDrmScreen::grabWindow(WId wid, int x, int y, int width, int height) const
422{
423 Q_UNUSED(wid);
424 Q_UNUSED(x);
425 Q_UNUSED(y);
428
429 return QPixmap();
430}
431
433
434#include "moc_qlinuxfbdrmscreen.cpp"
QImage mScreenImage
Definition qfbscreen_p.h:89
void initializeCompositor()
Definition qfbscreen.cpp:32
virtual QRegion doRedraw()
QFbCursor * mCursor
Definition qfbscreen_p.h:84
QRect mGeometry
Definition qfbscreen_p.h:85
QImage::Format mFormat
Definition qfbscreen_p.h:87
QSizeF mPhysicalSize
Definition qfbscreen_p.h:88
\inmodule QtGui
Definition qimage.h:37
Format
The following image formats are available in Qt.
Definition qimage.h:41
@ Format_RGB30
Definition qimage.h:63
@ Format_RGB32
Definition qimage.h:46
@ Format_A2RGB30_Premultiplied
Definition qimage.h:64
@ Format_RGB16
Definition qimage.h:49
@ Format_ARGB32
Definition qimage.h:47
int fd() const
void setFd(int fd)
void createScreens()
QString devicePath() const
virtual void loadConfig()
QPlatformScreen * createScreen(const QKmsOutput &output) override
Output * output(int idx)
int outputCount() const
QLinuxFbDevice(QKmsScreenConfig *screenConfig)
void close() override
bool open() override
void * nativeDisplay() const override
void swapBuffers(Output *output)
void registerScreen(QPlatformScreen *screen, bool isPrimary, const QPoint &virtualPos, const QList< QPlatformScreen * > &virtualSiblings) override
bool initialize() override
QPixmap grabWindow(WId wid, int x, int y, int width, int height) const override
This function is called when Qt needs to be able to grab the content of a window.
QRegion doRedraw() override
QLinuxFbDrmScreen(const QStringList &args)
The QPainter class performs low-level painting on widgets and other paint devices.
Definition qpainter.h:46
@ CompositionMode_Source
Definition qpainter.h:101
Returns a copy of the pixmap that is transformed using the given transformation transform and transfo...
Definition qpixmap.h:27
The QPlatformScreen class provides an abstraction for visual displays.
\inmodule QtCore\reentrant
Definition qpoint.h:25
\inmodule QtCore\reentrant
Definition qrect.h:30
The QRegion class specifies a clip region for a painter.
Definition qregion.h:27
bool isEmpty() const
Returns true if the region is empty; otherwise returns false.
\inmodule QtCore
Definition qsize.h:25
\inmodule QtCore
rect
[4]
void qErrnoWarning(const char *msg,...)
Combined button and popup list for selecting options.
static int qt_safe_open(const char *pathname, int flags, mode_t mode=0777)
static int qt_safe_close(int fd)
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void * user_data
static QT_BEGIN_NAMESPACE void pageFlipHandler(int fd, unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec, void *user_data)
#define DRM_FORMAT_RGB565
#define DRM_FORMAT_ABGR8888
static uint32_t bppForDrmFormat(uint32_t drmFormat)
static QImage::Format formatForDrmFormat(uint32_t drmFormat)
static int depthForDrmFormat(uint32_t drmFormat)
static QT_BEGIN_NAMESPACE const int BUFFER_COUNT
#define qWarning
Definition qlogging.h:166
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
GLuint64 GLenum void * handle
GLint GLint GLint GLint GLint x
[0]
GLfloat GLfloat GLfloat w
[0]
GLint GLsizei GLsizei height
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLint GLsizei width
GLuint64 GLenum GLint fd
GLint y
GLfloat GLfloat GLfloat GLfloat h
GLuint GLsizei const GLuint const GLintptr * offsets
GLfloat GLfloat p
[1]
GLsizei const GLuint const GLintptr const GLsizei * strides
#define MAP_FAILED
#define qPrintable(string)
Definition qstring.h:1531
static char * toLocal8Bit(char *out, QStringView in, QStringConverter::State *state)
#define QStringLiteral(str)
QScreen * screen
[1]
Definition main.cpp:29
#define Q_UNUSED(x)
unsigned char uchar
Definition qtypes.h:32
unsigned int uint
Definition qtypes.h:34
QT_BEGIN_NAMESPACE typedef uchar * output
file open(QIODevice::ReadOnly)
QObject::connect nullptr
QJSValueList args
QList< drmModeModeInfo > modes