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
qeglfskmsvsp2screen.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 The Qt Company Ltd.
2// Copyright (C) 2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
3// Copyright (C) 2016 Pelagicore AG
4// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
5
9
10#include <QtCore/QLoggingCategory>
11#include <QtGui/private/qguiapplication_p.h>
12
13#include <drm_fourcc.h>
14#include <xf86drm.h>
15#include <fcntl.h>
16
17//TODO move to qlinuxmediadevice?
18#include <cstdlib> //this needs to go before mediactl/mediactl.h because it uses size_t without including it
19extern "C" {
20#include <mediactl/mediactl.h>
21#include <mediactl/v4l2subdev.h> //needed in header for default arguments
22}
23
25
26Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug)
27
28static inline uint32_t drmFormatToGbmFormat(uint32_t drmFormat)
29{
30 Q_ASSERT(DRM_FORMAT_XRGB8888 == GBM_FORMAT_XRGB8888);
31 return drmFormat;
32}
33
34static inline uint32_t gbmFormatToDrmFormat(uint32_t gbmFormat) //TODO: is this needed?
35{
36 Q_ASSERT(DRM_FORMAT_XRGB8888 == GBM_FORMAT_XRGB8888);
37 return gbmFormat;
38}
39
40void QEglFSKmsVsp2Screen::dmaBufferDestroyedHandler(gbm_bo *gbmBo, void *data)
41{
42 Q_UNUSED(gbmBo); //TODO: do we need to do something with it after all?
43 auto dmabuf = static_cast<DmaBuffer *>(data);
44 //TODO: need some extra cleanup here?
45 delete dmabuf;
46}
47
48QEglFSKmsVsp2Screen::DmaBuffer *QEglFSKmsVsp2Screen::dmaBufferForGbmBuffer(gbm_bo *gbmBo)
49{
50 auto existingBuffer = static_cast<DmaBuffer *>(gbm_bo_get_user_data(gbmBo));
51 if (existingBuffer)
52 return existingBuffer;
53
54 uint32_t handle = gbm_bo_get_handle(gbmBo).u32;
55 QScopedPointer<DmaBuffer> fb(new DmaBuffer);
56 int ret = drmPrimeHandleToFD(device()->fd(), handle, DRM_CLOEXEC, &fb->dmabufFd);
57 if (ret) {
58 qWarning("Failed to create dmabuf file descriptor for buffer with drmPrimeHandleToFd");
59 return nullptr;
60 }
61
62 gbm_bo_set_user_data(gbmBo, fb.data(), dmaBufferDestroyedHandler);
63 return fb.take();
64}
65
71
73{
74 if (!m_gbmSurface) {
75 uint32_t gbmFormat = drmFormatToGbmFormat(m_output.drm_format);
76 qCDebug(qLcEglfsKmsDebug, "Creating gbm_surface for screen %s with format 0x%x", qPrintable(name()), gbmFormat);
77 Q_ASSERT(rawGeometry().isValid());
78 m_gbmSurface = gbm_surface_create(static_cast<QEglFSKmsVsp2Device *>(device())->gbmDevice(),
81 gbmFormat,
82 GBM_BO_USE_RENDERING);
83 }
84
85 if (!m_blendDevice)
86 initVsp2();
87
88 if (m_frameBuffers[m_backFb].dmabufFd == -1)
90
91 return m_gbmSurface; // not owned, gets destroyed in QEglFSKmsGbmIntegration::destroyNativeWindow()
92}
93
95{
96 m_gbmSurface = nullptr;
97}
98
100{
101 for (auto &fb : m_frameBuffers)
102 initDumbFrameBuffer(fb);
103}
104
106{
107 qCDebug(qLcEglfsKmsDebug, "Initializing Vsp2 hardware");
108 m_blendDevice.reset(new QVsp2BlendingDevice(rawGeometry().size()));
109
110 // Enable input for main buffer drawn by the compositor (always on)
111 initQtLayer();
112}
113
115{
116 const QSize screenSize = rawGeometry().size();
117 const uint bytesPerLine = uint(screenSize.width()) * 4; //TODO: is this ok?
118 bool formatSet = m_blendDevice->enableInput(m_qtLayer, QRect(QPoint(), screenSize), m_output.drm_format, bytesPerLine);
119 if (!formatSet) {
120 const uint32_t fallbackFormat = DRM_FORMAT_ARGB8888;
121 qWarning() << "Failed to set format" << q_fourccToString(m_output.drm_format)
122 << "falling back to" << q_fourccToString(fallbackFormat);
123 formatSet = m_blendDevice->enableInput(m_qtLayer, QRect(QPoint(), screenSize), fallbackFormat, bytesPerLine);
124 if (!formatSet)
125 qFatal("Failed to set vsp2 blending format");
126 }
127}
128
129int QEglFSKmsVsp2Screen::addLayer(int dmabufFd, const QSize &size, const QPoint &position, uint drmPixelFormat, uint bytesPerLine)
130{
131 int index = m_blendDevice->enableInput(QRect(position, size), drmPixelFormat, bytesPerLine);
132 if (index != -1) {
133 m_blendDevice->setInputBuffer(index, dmabufFd);
134 int id = index; //TODO: maybe make id something independent of layer index?
135 qCDebug(qLcEglfsKmsDebug) << "Enabled extra layer for vsp input" << index;
136 return id;
137 }
138 qWarning() << "Failed to add layer";
139 return -1;
140}
141
142void QEglFSKmsVsp2Screen::setLayerBuffer(int id, int dmabufFd)
143{
144 int layerIndex = id;
145 m_blendDevice->setInputBuffer(layerIndex, dmabufFd);
146 if (!m_blendScheduled) {
147 m_blendScheduled = true;
149 }
150}
151
153{
154 int layerIndex = id;
155 m_blendDevice->setInputPosition(layerIndex, position);
156}
157
159{
160 int layerIndex = id;
161 m_blendDevice->setInputAlpha(layerIndex, alpha);
162}
163
165{
166 int layerIndex = id;
167 m_blendDevice->disableInput(layerIndex);
168 return true;
169}
170
172{
173 m_blendFinishedCallbacks.append(callback);
174}
175
177{
178 if (!m_gbmSurface) {
179 qWarning("Cannot sync before platform init!");
180 return;
181 }
182
183 if (!m_blendScheduled && !m_nextGbmBo) {
184 m_nextGbmBo = gbm_surface_lock_front_buffer(m_gbmSurface);
185
186 if (!m_nextGbmBo) {
187 qWarning("Could not lock GBM surface front buffer!");
188 return;
189 }
190
191 m_blendScheduled = true;
193 }
194}
195
196void QEglFSKmsVsp2Screen::ensureModeSet()
197{
198 const int driFd = device()->fd();
199 QKmsOutput &op(output());
200 if (!op.mode_set) {
201 int ret = drmModeSetCrtc(driFd,
202 op.crtc_id,
203 m_frameBuffers[m_backFb].drmBufferId,
204 0, 0,
205 &op.connector_id, 1,
206 &op.modes[op.mode]);
207
208 if (ret == -1) {
209 qErrnoWarning(errno, "Could not set DRM mode!");
210 } else {
211 op.mode_set = true;
213 }
214 }
215}
216
217void QEglFSKmsVsp2Screen::doDrmFlip()
218{
219 QKmsOutput &op(output());
220 const int driFd = device()->fd();
221
222 int ret = drmModePageFlip(driFd,
223 op.crtc_id,
224 m_frameBuffers[m_backFb].drmBufferId,
225 0,
226 this);
227
228 if (ret)
229 qErrnoWarning("Could not queue DRM page flip on screen %s", qPrintable(name()));
230
231 m_backFb = (m_backFb + 1) % 2;
232}
233
235{
236 m_blendScheduled = false;
237 if (!m_nextGbmBo && !m_blendDevice->isDirty())
238 return;
239
240 FrameBuffer &backBuffer = m_frameBuffers[m_backFb];
241 if (backBuffer.dmabufFd == -1)
243
244 if (m_nextGbmBo) {
245 Q_ASSERT(m_nextGbmBo != m_currentGbmBo);
246 int compositorBackBufferDmaFd = dmaBufferForGbmBuffer(m_nextGbmBo)->dmabufFd;
247 m_blendDevice->setInputBuffer(m_qtLayer, compositorBackBufferDmaFd);
248
249 if (m_currentGbmBo)
250 gbm_surface_release_buffer(m_gbmSurface, m_currentGbmBo);
251 m_currentGbmBo = m_nextGbmBo;
252 m_nextGbmBo = nullptr;
253 }
254
255 ensureModeSet();
256
257 if (!m_blendDevice)
258 initVsp2();
259
260 if (!m_blendDevice->isDirty())
261 return;
262
263 const int driFd = device()->fd();
264 drmVBlank vBlank;
265 vBlank.request.type = static_cast<drmVBlankSeqType>(DRM_VBLANK_RELATIVE | DRM_VBLANK_SECONDARY); //TODO: make secondary configurable (or automatic)
266 vBlank.request.sequence = 1;
267 vBlank.request.signal = 0;
268 drmWaitVBlank(driFd, &vBlank);
269
270 if (!m_blendDevice->blend(backBuffer.dmabufFd)) {
271 qWarning() << "Vsp2: Blending failed";
272
273 // For some reason, a failed blend may often mess up the qt layer, so reinitialize it here
274 m_blendDevice->disableInput(m_qtLayer);
275 initQtLayer();
276 }
277
278 for (auto cb : m_blendFinishedCallbacks)
279 cb();
280
281 doDrmFlip();
282}
283
284void QEglFSKmsVsp2Screen::initDumbFrameBuffer(FrameBuffer &fb)
285{
286 QKmsOutput &op(output());
287 const uint32_t width = op.modes[op.mode].hdisplay;
288 const uint32_t height = op.modes[op.mode].vdisplay;
289
290 Q_ASSERT(fb.dmabufFd == -1);
291 const uint32_t dumbBufferFlags = 0; //TODO: do we want some flags? What's possible?
292 const uint32_t bpp = 32;
293
294 drm_mode_create_dumb creq = {
295 height,
296 width,
297 bpp,
298 dumbBufferFlags,
299 0, 0, 0 //return values
300 };
301
302 const int driFd = device()->fd();
303 if (drmIoctl(driFd, DRM_IOCTL_MODE_CREATE_DUMB, &creq) == -1)
304 qFatal("Failed to create dumb buffer: %s", strerror(errno));
305
306// uint32_t handles[4] = { gbm_bo_get_handle(bo).u32 };
307// uint32_t strides[4] = { gbm_bo_get_stride(bo) };
308// uint32_t offsets[4] = { 0 };
309// uint32_t pixelFormat = gbmFormatToDrmFormat(gbm_bo_get_format(bo));
310
311 //TODO: support additional planes
312 uint32_t gbmBoHandles[4] = { creq.handle, 0, 0, 0 };
313 uint32_t strides[4] = { creq.pitch, 0, 0, 0 };
314 uint32_t offsets[4] = { 0 };
315 uint32_t pixelFormat = DRM_FORMAT_ARGB8888; //TODO: support other formats?
316 uint32_t drmFlags = 0;
317
318 qCDebug(qLcEglfsKmsDebug) << "Adding FB" << QSize(width, height)
319 << ", DRM format" << q_fourccToString(pixelFormat);
320 int ret = drmModeAddFB2(driFd, width, height, pixelFormat,
321 gbmBoHandles, strides, offsets, &fb.drmBufferId, drmFlags);
322 if (ret)
323 qFatal("drmModeAddFB2 failed: %s", strerror(errno));
324
325 drmPrimeHandleToFD(driFd, gbmBoHandles[0], DRM_CLOEXEC, &fb.dmabufFd);
326}
327
328bool QEglFSKmsVsp2Screen::Blender::event(QEvent *event)
329{
330 switch (event->type()) {
331 case QEvent::User:
332 m_screen->blendAndFlipDrm();
333 return true;
334 default:
335 return QObject::event(event);
336 }
337}
338
IOBluetoothDevice * device
static void postEvent(QObject *receiver, QEvent *event, int priority=Qt::NormalEventPriority)
QString name() const override
QKmsOutput & output()
QEglFSKmsDevice * device() const
void setPowerState(QPlatformScreen::PowerState state) override
Sets the power state for this screen.
QRect rawGeometry() const override
bool removeLayer(int id) override
void setLayerPosition(int id, const QPoint &position) override
gbm_surface * createSurface()
void setLayerBuffer(int id, int dmabufFd) override
void addBlendListener(void(*callback)()) override
int addLayer(int dmabufFd, const QSize &size, const QPoint &position, uint drmPixelFormat, uint bytesPerLine) override
void setLayerAlpha(int id, qreal alpha) override
QEglFSKmsVsp2Screen(QEglFSKmsDevice *device, const QKmsOutput &output)
\inmodule QtCore
Definition qcoreevent.h:45
int fd() const
void append(parameter_type t)
Definition qlist.h:458
virtual bool event(QEvent *event)
This virtual function receives events to an object and should return true if the event e was recogniz...
Definition qobject.cpp:1389
\inmodule QtCore\reentrant
Definition qpoint.h:25
\inmodule QtCore\reentrant
Definition qrect.h:30
constexpr QSize size() const noexcept
Returns the size of the rectangle.
Definition qrect.h:242
T * data() const noexcept
Returns the value of the pointer referenced by this object.
void reset(T *other=nullptr) noexcept(noexcept(Cleanup::cleanup(std::declval< T * >())))
Deletes the existing object it is pointing to (if any), and sets its pointer to other.
\inmodule QtCore
Definition qsize.h:25
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:130
bool blend(int outputDmabufFd)
bool setInputBuffer(int index, int dmabufFd)
bool enableInput(int i, const QRect &bufferGeometry, uint drmFormat, uint bytesPerLine)
bool setInputPosition(int index, const QPoint &position)
bool setInputAlpha(int index, qreal alpha)
#define this
Definition dialogs.cpp:9
void qErrnoWarning(const char *msg,...)
Combined button and popup list for selecting options.
static uint32_t drmFormatToGbmFormat(uint32_t drmFormat)
QT_BEGIN_NAMESPACE QString q_fourccToString(uint code)
static QT_BEGIN_NAMESPACE uint32_t drmFormatToGbmFormat(uint32_t drmFormat)
static uint32_t gbmFormatToDrmFormat(uint32_t gbmFormat)
#define qWarning
Definition qlogging.h:166
#define qFatal
Definition qlogging.h:168
#define qCDebug(category,...)
#define Q_DECLARE_LOGGING_CATEGORY(name)
return ret
GLuint64 GLenum void * handle
GLint GLsizei GLsizei height
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLenum GLuint id
[7]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLint GLsizei width
GLuint64 GLenum GLint fd
struct _cl_event * event
GLuint GLsizei const GLuint const GLintptr * offsets
GLsizei const GLuint const GLintptr const GLsizei * strides
GLfloat GLfloat GLfloat alpha
Definition qopenglext.h:418
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
SSL_CTX int(* cb)(SSL *ssl, unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg)
#define qPrintable(string)
Definition qstring.h:1531
#define Q_UNUSED(x)
unsigned int uint
Definition qtypes.h:34
double qreal
Definition qtypes.h:187
QT_BEGIN_NAMESPACE typedef uchar * output
uint32_t drm_format