Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
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// Qt-Security score:significant reason:default
6
9#include <qeglfskmshelpers_p.h>
10
11#include <QtCore/QLoggingCategory>
12#include <QtGui/private/qguiapplication_p.h>
13
14#include <drm_fourcc.h>
15#include <xf86drm.h>
16#include <fcntl.h>
17
18//TODO move to qlinuxmediadevice?
19#include <cstdlib> //this needs to go before mediactl/mediactl.h because it uses size_t without including it
20extern "C" {
21#include <mediactl/mediactl.h>
22#include <mediactl/v4l2subdev.h> //needed in header for default arguments
23}
24
26
28
29static inline uint32_t drmFormatToGbmFormat(uint32_t drmFormat)
30{
31 Q_ASSERT(DRM_FORMAT_XRGB8888 == GBM_FORMAT_XRGB8888);
32 return drmFormat;
33}
34
35static inline uint32_t gbmFormatToDrmFormat(uint32_t gbmFormat) //TODO: is this needed?
36{
37 Q_ASSERT(DRM_FORMAT_XRGB8888 == GBM_FORMAT_XRGB8888);
38 return gbmFormat;
39}
40
41void QEglFSKmsVsp2Screen::dmaBufferDestroyedHandler(gbm_bo *gbmBo, void *data)
42{
43 Q_UNUSED(gbmBo); //TODO: do we need to do something with it after all?
44 auto dmabuf = static_cast<DmaBuffer *>(data);
45 //TODO: need some extra cleanup here?
46 delete dmabuf;
47}
48
49QEglFSKmsVsp2Screen::DmaBuffer *QEglFSKmsVsp2Screen::dmaBufferForGbmBuffer(gbm_bo *gbmBo)
50{
51 auto existingBuffer = static_cast<DmaBuffer *>(gbm_bo_get_user_data(gbmBo));
52 if (existingBuffer)
53 return existingBuffer;
54
55 uint32_t handle = gbm_bo_get_handle(gbmBo).u32;
56 QScopedPointer<DmaBuffer> fb(new DmaBuffer);
57 int ret = drmPrimeHandleToFD(device()->fd(), handle, DRM_CLOEXEC, &fb->dmabufFd);
58 if (ret) {
59 qWarning("Failed to create dmabuf file descriptor for buffer with drmPrimeHandleToFd");
60 return nullptr;
61 }
62
63 gbm_bo_set_user_data(gbmBo, fb.data(), dmaBufferDestroyedHandler);
64 return fb.take();
65}
66
67QEglFSKmsVsp2Screen::QEglFSKmsVsp2Screen(QEglFSKmsDevice *device, const QKmsOutput &output)
69 , m_blender(new Blender(this))
70{
71}
72
74{
75 if (!m_gbmSurface) {
76 uint32_t gbmFormat = drmFormatToGbmFormat(m_output.drm_format);
77 qCDebug(qLcEglfsKmsDebug, "Creating gbm_surface for screen %s with format 0x%x", qPrintable(name()), gbmFormat);
78 Q_ASSERT(rawGeometry().isValid());
79 m_gbmSurface = gbm_surface_create(static_cast<QEglFSKmsVsp2Device *>(device())->gbmDevice(),
80 uint(rawGeometry().width()),
81 uint(rawGeometry().height()),
82 gbmFormat,
83 GBM_BO_USE_RENDERING);
84 }
85
86 if (!m_blendDevice)
88
89 if (m_frameBuffers[m_backFb].dmabufFd == -1)
91
92 return m_gbmSurface; // not owned, gets destroyed in QEglFSKmsGbmIntegration::destroyNativeWindow()
93}
94
96{
97 m_gbmSurface = nullptr;
98}
99
101{
102 for (auto &fb : m_frameBuffers)
103 initDumbFrameBuffer(fb);
104}
105
107{
108 qCDebug(qLcEglfsKmsDebug, "Initializing Vsp2 hardware");
109 m_blendDevice.reset(new QVsp2BlendingDevice(rawGeometry().size()));
110
111 // Enable input for main buffer drawn by the compositor (always on)
113}
114
116{
117 const QSize screenSize = rawGeometry().size();
118 const uint bytesPerLine = uint(screenSize.width()) * 4; //TODO: is this ok?
119 bool formatSet = m_blendDevice->enableInput(m_qtLayer, QRect(QPoint(), screenSize), m_output.drm_format, bytesPerLine);
120 if (!formatSet) {
121 const uint32_t fallbackFormat = DRM_FORMAT_ARGB8888;
122 qWarning() << "Failed to set format" << q_fourccToString(m_output.drm_format)
123 << "falling back to" << q_fourccToString(fallbackFormat);
124 formatSet = m_blendDevice->enableInput(m_qtLayer, QRect(QPoint(), screenSize), fallbackFormat, bytesPerLine);
125 if (!formatSet)
126 qFatal("Failed to set vsp2 blending format");
127 }
128}
129
130int QEglFSKmsVsp2Screen::addLayer(int dmabufFd, const QSize &size, const QPoint &position, uint drmPixelFormat, uint bytesPerLine)
131{
132 int index = m_blendDevice->enableInput(QRect(position, size), drmPixelFormat, bytesPerLine);
133 if (index != -1) {
134 m_blendDevice->setInputBuffer(index, dmabufFd);
135 int id = index; //TODO: maybe make id something independent of layer index?
136 qCDebug(qLcEglfsKmsDebug) << "Enabled extra layer for vsp input" << index;
137 return id;
138 }
139 qWarning() << "Failed to add layer";
140 return -1;
141}
142
143void QEglFSKmsVsp2Screen::setLayerBuffer(int id, int dmabufFd)
144{
145 int layerIndex = id;
146 m_blendDevice->setInputBuffer(layerIndex, dmabufFd);
147 if (!m_blendScheduled) {
148 m_blendScheduled = true;
149 QCoreApplication::postEvent(m_blender.data(), new QEvent(QEvent::User));
150 }
151}
152
153void QEglFSKmsVsp2Screen::setLayerPosition(int id, const QPoint &position)
154{
155 int layerIndex = id;
156 m_blendDevice->setInputPosition(layerIndex, position);
157}
158
159void QEglFSKmsVsp2Screen::setLayerAlpha(int id, qreal alpha)
160{
161 int layerIndex = id;
162 m_blendDevice->setInputAlpha(layerIndex, alpha);
163}
164
166{
167 int layerIndex = id;
168 m_blendDevice->disableInput(layerIndex);
169 return true;
170}
171
172void QEglFSKmsVsp2Screen::addBlendListener(void (*callback)())
173{
174 m_blendFinishedCallbacks.append(callback);
175}
176
178{
179 if (!m_gbmSurface) {
180 qWarning("Cannot sync before platform init!");
181 return;
182 }
183
184 if (!m_blendScheduled && !m_nextGbmBo) {
185 m_nextGbmBo = gbm_surface_lock_front_buffer(m_gbmSurface);
186
187 if (!m_nextGbmBo) {
188 qWarning("Could not lock GBM surface front buffer!");
189 return;
190 }
191
192 m_blendScheduled = true;
193 QCoreApplication::postEvent(m_blender.data(), new QEvent(QEvent::User));
194 }
195}
196
197void QEglFSKmsVsp2Screen::ensureModeSet()
198{
199 const int driFd = device()->fd();
200 QKmsOutput &op(output());
201 if (!op.mode_set) {
202 int ret = drmModeSetCrtc(driFd,
203 op.crtc_id,
204 m_frameBuffers[m_backFb].drmBufferId,
205 0, 0,
206 &op.connector_id, 1,
207 &op.modes[op.mode]);
208
209 if (ret == -1) {
210 qErrnoWarning(errno, "Could not set DRM mode!");
211 } else {
212 op.mode_set = true;
213 setPowerState(PowerStateOn);
214 }
215 }
216}
217
218void QEglFSKmsVsp2Screen::doDrmFlip()
219{
220 QKmsOutput &op(output());
221 const int driFd = device()->fd();
222
223 int ret = drmModePageFlip(driFd,
224 op.crtc_id,
225 m_frameBuffers[m_backFb].drmBufferId,
226 0,
227 this);
228
229 if (ret)
230 qErrnoWarning("Could not queue DRM page flip on screen %s", qPrintable(name()));
231
232 m_backFb = (m_backFb + 1) % 2;
233}
234
236{
237 m_blendScheduled = false;
238 if (!m_nextGbmBo && !m_blendDevice->isDirty())
239 return;
240
241 FrameBuffer &backBuffer = m_frameBuffers[m_backFb];
242 if (backBuffer.dmabufFd == -1)
244
245 if (m_nextGbmBo) {
246 Q_ASSERT(m_nextGbmBo != m_currentGbmBo);
247 int compositorBackBufferDmaFd = dmaBufferForGbmBuffer(m_nextGbmBo)->dmabufFd;
248 m_blendDevice->setInputBuffer(m_qtLayer, compositorBackBufferDmaFd);
249
250 if (m_currentGbmBo)
251 gbm_surface_release_buffer(m_gbmSurface, m_currentGbmBo);
252 m_currentGbmBo = m_nextGbmBo;
253 m_nextGbmBo = nullptr;
254 }
255
256 ensureModeSet();
257
258 if (!m_blendDevice)
260
261 if (!m_blendDevice->isDirty())
262 return;
263
264 const int driFd = device()->fd();
265 drmVBlank vBlank;
266 vBlank.request.type = static_cast<drmVBlankSeqType>(DRM_VBLANK_RELATIVE | DRM_VBLANK_SECONDARY); //TODO: make secondary configurable (or automatic)
267 vBlank.request.sequence = 1;
268 vBlank.request.signal = 0;
269 drmWaitVBlank(driFd, &vBlank);
270
271 if (!m_blendDevice->blend(backBuffer.dmabufFd)) {
272 qWarning() << "Vsp2: Blending failed";
273
274 // For some reason, a failed blend may often mess up the qt layer, so reinitialize it here
275 m_blendDevice->disableInput(m_qtLayer);
277 }
278
279 for (auto cb : m_blendFinishedCallbacks)
280 cb();
281
282 doDrmFlip();
283}
284
285void QEglFSKmsVsp2Screen::initDumbFrameBuffer(FrameBuffer &fb)
286{
287 QKmsOutput &op(output());
288 const uint32_t width = op.modes[op.mode].hdisplay;
289 const uint32_t height = op.modes[op.mode].vdisplay;
290
291 Q_ASSERT(fb.dmabufFd == -1);
292 const uint32_t dumbBufferFlags = 0; //TODO: do we want some flags? What's possible?
293 const uint32_t bpp = 32;
294
295 drm_mode_create_dumb creq = {
296 height,
297 width,
298 bpp,
299 dumbBufferFlags,
300 0, 0, 0 //return values
301 };
302
303 const int driFd = device()->fd();
304 if (drmIoctl(driFd, DRM_IOCTL_MODE_CREATE_DUMB, &creq) == -1)
305 qFatal("Failed to create dumb buffer: %s", strerror(errno));
306
307// uint32_t handles[4] = { gbm_bo_get_handle(bo).u32 };
308// uint32_t strides[4] = { gbm_bo_get_stride(bo) };
309// uint32_t offsets[4] = { 0 };
310// uint32_t pixelFormat = gbmFormatToDrmFormat(gbm_bo_get_format(bo));
311
312 //TODO: support additional planes
313 uint32_t gbmBoHandles[4] = { creq.handle, 0, 0, 0 };
314 uint32_t strides[4] = { creq.pitch, 0, 0, 0 };
315 uint32_t offsets[4] = { 0 };
316 uint32_t pixelFormat = DRM_FORMAT_ARGB8888; //TODO: support other formats?
317 uint32_t drmFlags = 0;
318
319 qCDebug(qLcEglfsKmsDebug) << "Adding FB" << QSize(width, height)
320 << ", DRM format" << q_fourccToString(pixelFormat);
321 int ret = drmModeAddFB2(driFd, width, height, pixelFormat,
322 gbmBoHandles, strides, offsets, &fb.drmBufferId, drmFlags);
323 if (ret)
324 qFatal("drmModeAddFB2 failed: %s", strerror(errno));
325
326 drmPrimeHandleToFD(driFd, gbmBoHandles[0], DRM_CLOEXEC, &fb.dmabufFd);
327}
328
329bool QEglFSKmsVsp2Screen::Blender::event(QEvent *event)
330{
331 switch (event->type()) {
332 case QEvent::User:
333 m_screen->blendAndFlipDrm();
334 return true;
335 default:
336 return QObject::event(event);
337 }
338}
339
340QT_END_NAMESPACE
gbm_device * gbmDevice() const
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)
static QT_BEGIN_NAMESPACE uint32_t drmFormatToGbmFormat(uint32_t drmFormat)
static uint32_t gbmFormatToDrmFormat(uint32_t gbmFormat)
QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(lcQIORing)