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
qeglfskmsegldeviceintegration.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2016 Pelagicore AG
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4// Qt-Security score:significant reason:default
5
9#include <QtGui/private/qeglconvenience_p.h>
10#include "private/qeglfswindow_p.h"
11#include "private/qeglfscursor_p.h"
12#include <QLoggingCategory>
13#include <private/qmath_p.h>
14
16
17QEglFSKmsEglDeviceIntegration::QEglFSKmsEglDeviceIntegration()
18 : m_egl_device(EGL_NO_DEVICE_EXT)
19 , m_funcs(nullptr)
20{
21 qCDebug(qLcEglfsKmsDebug, "New DRM/KMS on EGLDevice integration created");
22}
23
24QSurfaceFormat QEglFSKmsEglDeviceIntegration::surfaceFormatFor(const QSurfaceFormat &inputFormat) const
25{
26 QSurfaceFormat format = QEglFSKmsIntegration::surfaceFormatFor(inputFormat);
27 format.setAlphaBufferSize(8);
28 return format;
29}
30
31EGLint QEglFSKmsEglDeviceIntegration::surfaceType() const
32{
33 return EGL_STREAM_BIT_KHR;
34}
35
36EGLDisplay QEglFSKmsEglDeviceIntegration::createDisplay(EGLNativeDisplayType nativeDisplay)
37{
38 qCDebug(qLcEglfsKmsDebug, "Creating display");
39
40 EGLDisplay display;
41
42 EGLint egldevice_fd = device()->fd();
43
44 const EGLint attribs[] = { EGL_DRM_MASTER_FD_EXT, egldevice_fd, EGL_NONE };
45 if (m_funcs->has_egl_platform_device) {
46 display = m_funcs->get_platform_display(EGL_PLATFORM_DEVICE_EXT, nativeDisplay, attribs);
47 } else {
48 qWarning("EGL_EXT_platform_device not available, falling back to legacy path!");
49 display = eglGetDisplay(nativeDisplay);
50 }
51
52 if (Q_UNLIKELY(display == EGL_NO_DISPLAY))
53 qFatal("Could not get EGL display");
54
55 EGLint major, minor;
56 if (Q_UNLIKELY(!eglInitialize(display, &major, &minor)))
57 qFatal("Could not initialize egl display");
58
59 if (Q_UNLIKELY(!eglBindAPI(EGL_OPENGL_ES_API)))
60 qFatal("Failed to bind EGL_OPENGL_ES_API\n");
61
62 return display;
63}
64
65bool QEglFSKmsEglDeviceIntegration::supportsSurfacelessContexts() const
66{
67 // Returning false disables the usage of EGL_KHR_surfaceless_context even when the
68 // extension is available. This is just what we need since, at least with NVIDIA
69 // 352.00 making a null surface current with a context breaks.
70 return false;
71}
72
73bool QEglFSKmsEglDeviceIntegration::supportsPBuffers() const
74{
75 return true;
76}
77
79{
80public:
81 QEglFSKmsEglDeviceWindow(QWindow *w, const QEglFSKmsEglDeviceIntegration *integration)
83 , m_integration(integration)
85 { }
86
87 ~QEglFSKmsEglDeviceWindow() { destroy(); }
88
91
92 const QEglFSKmsEglDeviceIntegration *m_integration;
95};
96
98{
99 QEglFSWindow::invalidateSurface();
100 m_integration->m_funcs->destroy_stream(screen()->display(), m_egl_stream);
101}
102
104{
105 qCDebug(qLcEglfsKmsDebug, "Creating stream");
106
107 EGLDisplay display = screen()->display();
108 EGLint streamAttribs[3];
109 int streamAttribCount = 0;
110 int fifoLength = qEnvironmentVariableIntValue("QT_QPA_EGLFS_STREAM_FIFO_LENGTH");
111 if (fifoLength > 0) {
112 streamAttribs[streamAttribCount++] = EGL_STREAM_FIFO_LENGTH_KHR;
113 streamAttribs[streamAttribCount++] = fifoLength;
114 }
115 streamAttribs[streamAttribCount++] = EGL_NONE;
116
117 m_egl_stream = m_integration->m_funcs->create_stream(display, streamAttribs);
118 if (m_egl_stream == EGL_NO_STREAM_KHR) {
119 qWarning("resetSurface: Couldn't create EGLStream for native window");
120 return;
121 }
122
123 qCDebug(qLcEglfsKmsDebug, "Created stream %p on display %p", m_egl_stream, display);
124
125 EGLint count;
126 if (m_integration->m_funcs->query_stream(display, m_egl_stream, EGL_STREAM_FIFO_LENGTH_KHR, &count)) {
127 if (count > 0)
128 qCDebug(qLcEglfsKmsDebug, "Using EGLStream FIFO mode with %d frames", count);
129 else
130 qCDebug(qLcEglfsKmsDebug, "Using EGLStream mailbox mode");
131 } else {
132 qCDebug(qLcEglfsKmsDebug, "Could not query number of EGLStream FIFO frames");
133 }
134
135 if (!m_integration->m_funcs->get_output_layers(display, nullptr, nullptr, 0, &count) || count == 0) {
136 qWarning("No output layers found");
137 return;
138 }
139
140 qCDebug(qLcEglfsKmsDebug, "Output has %d layers", count);
141
142 QList<EGLOutputLayerEXT> layers;
143 layers.resize(count);
144 EGLint actualCount;
145 if (!m_integration->m_funcs->get_output_layers(display, nullptr, layers.data(), count, &actualCount)) {
146 qWarning("Failed to get layers");
147 return;
148 }
149
150 QEglFSKmsEglDeviceScreen *cur_screen = static_cast<QEglFSKmsEglDeviceScreen *>(screen());
151 Q_ASSERT(cur_screen);
152 QKmsOutput &output(cur_screen->output());
153 const uint32_t wantedId = !output.wants_forced_plane ? output.crtc_id : output.forced_plane_id;
154 qCDebug(qLcEglfsKmsDebug, "Searching for id: %d", wantedId);
155
156 EGLOutputLayerEXT layer = EGL_NO_OUTPUT_LAYER_EXT;
157 for (int i = 0; i < actualCount; ++i) {
158 EGLAttrib id;
159 if (m_integration->m_funcs->query_output_layer_attrib(display, layers[i], EGL_DRM_CRTC_EXT, &id)) {
160 qCDebug(qLcEglfsKmsDebug, " [%d] layer %p - crtc %d", i, layers[i], (int) id);
161 if (id == EGLAttrib(wantedId))
162 layer = layers[i];
163 } else if (m_integration->m_funcs->query_output_layer_attrib(display, layers[i], EGL_DRM_PLANE_EXT, &id)) {
164 qCDebug(qLcEglfsKmsDebug, " [%d] layer %p - plane %d", i, layers[i], (int) id);
165 if (id == EGLAttrib(wantedId))
166 layer = layers[i];
167 } else {
168 qCDebug(qLcEglfsKmsDebug, " [%d] layer %p - unknown", i, layers[i]);
169 }
170 }
171
172 QByteArray reqLayerIndex = qgetenv("QT_QPA_EGLFS_LAYER_INDEX");
173 if (!reqLayerIndex.isEmpty()) {
174 int idx = reqLayerIndex.toInt();
175 if (idx >= 0 && idx < layers.size()) {
176 qCDebug(qLcEglfsKmsDebug, "EGLOutput layer index override = %d", idx);
177 layer = layers[idx];
178 }
179 }
180
181 if (layer == EGL_NO_OUTPUT_LAYER_EXT) {
182 qWarning("resetSurface: Couldn't get EGLOutputLayer for native window");
183 return;
184 }
185
186 qCDebug(qLcEglfsKmsDebug, "Using layer %p", layer);
187
188 if (!m_integration->m_funcs->stream_consumer_output(display, m_egl_stream, layer))
189 qWarning("resetSurface: Unable to connect stream");
190
191 m_config = QEglFSDeviceIntegration::chooseConfig(display, m_integration->surfaceFormatFor(window()->requestedFormat()));
192 m_format = q_glFormatFromConfig(display, m_config);
193 qCDebug(qLcEglfsKmsDebug) << "Stream producer format is" << m_format;
194
195 const int w = cur_screen->rawGeometry().width();
196 const int h = cur_screen->rawGeometry().height();
197 qCDebug(qLcEglfsKmsDebug, "Creating stream producer surface of size %dx%d", w, h);
198
199 const EGLint stream_producer_attribs[] = {
200 EGL_WIDTH, w,
201 EGL_HEIGHT, h,
202 EGL_NONE
203 };
204
205 m_surface = m_integration->m_funcs->create_stream_producer_surface(display, m_config, m_egl_stream, stream_producer_attribs);
206 if (m_surface == EGL_NO_SURFACE)
207 return;
208
209 qCDebug(qLcEglfsKmsDebug, "Created stream producer surface %p", m_surface);
210}
211
212QEglFSWindow *QEglFSKmsEglDeviceIntegration::createWindow(QWindow *window) const
213{
214 QEglFSKmsEglDeviceWindow *eglWindow = new QEglFSKmsEglDeviceWindow(window, this);
215
216 m_funcs->initialize(eglWindow->screen()->display());
217 if (Q_UNLIKELY(!(m_funcs->has_egl_output_base && m_funcs->has_egl_output_drm
218 && m_funcs->has_egl_stream && m_funcs->has_egl_stream_producer_eglsurface
219 && m_funcs->has_egl_stream_consumer_egloutput))) {
220 qCDebug(qLcEglfsKmsDebug, "EGL_EXTENSIONS %s",
221 eglQueryString(eglWindow->screen()->display(), EGL_EXTENSIONS));
222 qFatal("Required extensions missing!");
223 }
224
225 return eglWindow;
226}
227
228QKmsDevice *QEglFSKmsEglDeviceIntegration::createDevice()
229{
230 if (Q_UNLIKELY(!query_egl_device()))
231 qFatal("Could not set up EGL device!");
232
233 QString path = screenConfig()->devicePath();
234 if (!path.isEmpty()) {
235 qCDebug(qLcEglfsKmsDebug) << "EGLDevice: Using DRM device" << path
236 << "specified in config file";
237 } else {
238 path = QLatin1StringView(
239 m_funcs->query_device_string(m_egl_device, EGL_DRM_DEVICE_FILE_EXT));
240 if (Q_UNLIKELY(path.isEmpty()))
241 qFatal("Failed to query device name from EGLDevice");
242 }
243
244 return new QEglFSKmsEglDevice(this, screenConfig(), path);
245}
246
247bool QEglFSKmsEglDeviceIntegration::query_egl_device()
248{
249 m_funcs = new QEGLStreamConvenience;
250 if (Q_UNLIKELY(!m_funcs->has_egl_device_base))
251 qFatal("EGL_EXT_device_base missing");
252
253 EGLint num_devices = 0;
254 if (m_funcs->query_devices(1, &m_egl_device, &num_devices) != EGL_TRUE) {
255 qWarning("eglQueryDevicesEXT failed: eglError: %x", eglGetError());
256 return false;
257 }
258
259 qCDebug(qLcEglfsKmsDebug, "Found %d EGL devices", num_devices);
260
261 if (num_devices < 1 || m_egl_device == EGL_NO_DEVICE_EXT) {
262 qWarning("eglQueryDevicesEXT could not find any EGL devices");
263 return false;
264 }
265
266 return true;
267}
268
269QPlatformCursor *QEglFSKmsEglDeviceIntegration::createCursor(QPlatformScreen *screen) const
270{
271#if QT_CONFIG(opengl)
272 if (screenConfig()->separateScreens())
273 return new QEglFSCursor(screen);
274#else
275 Q_UNUSED(screen);
276#endif
277 return nullptr;
278}
279
280QT_END_NAMESPACE
const QEglFSKmsEglDeviceIntegration * m_integration
void invalidateSurface() override
Invalidates the window's surface by releasing its surface buffers.
QEglFSKmsEglDeviceWindow(QWindow *w, const QEglFSKmsEglDeviceIntegration *integration)
Combined button and popup list for selecting options.