1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Copyright (C) 2016 Pelagicore AG
5 ** Contact: https://www.qt.io/licensing/
6 **
7 ** This file is part of the plugins of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** Commercial License Usage
11 ** Licensees holding valid commercial Qt licenses may use this file in
12 ** accordance with the commercial license agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and The Qt Company. For licensing terms
15 ** and conditions see https://www.qt.io/terms-conditions. For further
16 ** information use the contact form at https://www.qt.io/contact-us.
17 **
18 ** GNU Lesser General Public License Usage
19 ** Alternatively, this file may be used under the terms of the GNU Lesser
20 ** General Public License version 3 as published by the Free Software
21 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
22 ** packaging of this file. Please review the following information to
23 ** ensure the GNU Lesser General Public License version 3 requirements
24 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25 **
26 ** GNU General Public License Usage
27 ** Alternatively, this file may be used under the terms of the GNU
28 ** General Public License version 2.0 or (at your option) the GNU General
29 ** Public license version 3 or any later version approved by the KDE Free
30 ** Qt Foundation. The licenses are as published by the Free Software
31 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32 ** included in the packaging of this file. Please review the following
33 ** information to ensure the GNU General Public License requirements will
34 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35 ** https://www.gnu.org/licenses/gpl-3.0.html.
36 **
37 ** $QT_END_LICENSE$
38 **
39 ****************************************************************************/
40 
41 #include "qeglfskmsegldeviceintegration.h"
42 #include "qeglfskmsegldevice.h"
43 #include "qeglfskmsegldevicescreen.h"
44 #include <QtEglSupport/private/qeglconvenience_p.h>
45 #include "private/qeglfswindow_p.h"
46 #include "private/qeglfscursor_p.h"
47 #include <QLoggingCategory>
48 #include <private/qmath_p.h>
49 
50 QT_BEGIN_NAMESPACE
51 
QEglFSKmsEglDeviceIntegration()52 QEglFSKmsEglDeviceIntegration::QEglFSKmsEglDeviceIntegration()
53     : m_egl_device(EGL_NO_DEVICE_EXT)
54     , m_funcs(nullptr)
55 {
56     qCDebug(qLcEglfsKmsDebug, "New DRM/KMS on EGLDevice integration created");
57 }
58 
surfaceFormatFor(const QSurfaceFormat & inputFormat) const59 QSurfaceFormat QEglFSKmsEglDeviceIntegration::surfaceFormatFor(const QSurfaceFormat &inputFormat) const
60 {
61     QSurfaceFormat format = QEglFSKmsIntegration::surfaceFormatFor(inputFormat);
62     format.setAlphaBufferSize(8);
63     return format;
64 }
65 
surfaceType() const66 EGLint QEglFSKmsEglDeviceIntegration::surfaceType() const
67 {
68     return EGL_STREAM_BIT_KHR;
69 }
70 
createDisplay(EGLNativeDisplayType nativeDisplay)71 EGLDisplay QEglFSKmsEglDeviceIntegration::createDisplay(EGLNativeDisplayType nativeDisplay)
72 {
73     qCDebug(qLcEglfsKmsDebug, "Creating display");
74 
75     EGLDisplay display;
76 
77     if (m_funcs->has_egl_platform_device) {
78         display = m_funcs->get_platform_display(EGL_PLATFORM_DEVICE_EXT, nativeDisplay, nullptr);
79     } else {
80         qWarning("EGL_EXT_platform_device not available, falling back to legacy path!");
81         display = eglGetDisplay(nativeDisplay);
82     }
83 
84     if (Q_UNLIKELY(display == EGL_NO_DISPLAY))
85         qFatal("Could not get EGL display");
86 
87     EGLint major, minor;
88     if (Q_UNLIKELY(!eglInitialize(display, &major, &minor)))
89         qFatal("Could not initialize egl display");
90 
91     if (Q_UNLIKELY(!eglBindAPI(EGL_OPENGL_ES_API)))
92         qFatal("Failed to bind EGL_OPENGL_ES_API\n");
93 
94     return display;
95 }
96 
supportsSurfacelessContexts() const97 bool QEglFSKmsEglDeviceIntegration::supportsSurfacelessContexts() const
98 {
99     // Returning false disables the usage of EGL_KHR_surfaceless_context even when the
100     // extension is available. This is just what we need since, at least with NVIDIA
101     // 352.00 making a null surface current with a context breaks.
102     return false;
103 }
104 
supportsPBuffers() const105 bool QEglFSKmsEglDeviceIntegration::supportsPBuffers() const
106 {
107     return true;
108 }
109 
110 class QEglFSKmsEglDeviceWindow : public QEglFSWindow
111 {
112 public:
QEglFSKmsEglDeviceWindow(QWindow * w,const QEglFSKmsEglDeviceIntegration * integration)113     QEglFSKmsEglDeviceWindow(QWindow *w, const QEglFSKmsEglDeviceIntegration *integration)
114         : QEglFSWindow(w)
115         , m_integration(integration)
116         , m_egl_stream(EGL_NO_STREAM_KHR)
117     { }
118 
~QEglFSKmsEglDeviceWindow()119     ~QEglFSKmsEglDeviceWindow() { destroy(); }
120 
121     void invalidateSurface() override;
122     void resetSurface() override;
123 
124     const QEglFSKmsEglDeviceIntegration *m_integration;
125     EGLStreamKHR m_egl_stream;
126     EGLint m_latency;
127 };
128 
invalidateSurface()129 void QEglFSKmsEglDeviceWindow::invalidateSurface()
130 {
131     QEglFSWindow::invalidateSurface();
132     m_integration->m_funcs->destroy_stream(screen()->display(), m_egl_stream);
133 }
134 
resetSurface()135 void QEglFSKmsEglDeviceWindow::resetSurface()
136 {
137     qCDebug(qLcEglfsKmsDebug, "Creating stream");
138 
139     EGLDisplay display = screen()->display();
140     EGLint streamAttribs[3];
141     int streamAttribCount = 0;
142     int fifoLength = qEnvironmentVariableIntValue("QT_QPA_EGLFS_STREAM_FIFO_LENGTH");
143     if (fifoLength > 0) {
144         streamAttribs[streamAttribCount++] = EGL_STREAM_FIFO_LENGTH_KHR;
145         streamAttribs[streamAttribCount++] = fifoLength;
146     }
147     streamAttribs[streamAttribCount++] = EGL_NONE;
148 
149     m_egl_stream = m_integration->m_funcs->create_stream(display, streamAttribs);
150     if (m_egl_stream == EGL_NO_STREAM_KHR) {
151         qWarning("resetSurface: Couldn't create EGLStream for native window");
152         return;
153     }
154 
155     qCDebug(qLcEglfsKmsDebug, "Created stream %p on display %p", m_egl_stream, display);
156 
157     EGLint count;
158     if (m_integration->m_funcs->query_stream(display, m_egl_stream, EGL_STREAM_FIFO_LENGTH_KHR, &count)) {
159         if (count > 0)
160             qCDebug(qLcEglfsKmsDebug, "Using EGLStream FIFO mode with %d frames", count);
161         else
162             qCDebug(qLcEglfsKmsDebug, "Using EGLStream mailbox mode");
163     } else {
164         qCDebug(qLcEglfsKmsDebug, "Could not query number of EGLStream FIFO frames");
165     }
166 
167     if (!m_integration->m_funcs->get_output_layers(display, nullptr, nullptr, 0, &count) || count == 0) {
168         qWarning("No output layers found");
169         return;
170     }
171 
172     qCDebug(qLcEglfsKmsDebug, "Output has %d layers", count);
173 
174     QVector<EGLOutputLayerEXT> layers;
175     layers.resize(count);
176     EGLint actualCount;
177     if (!m_integration->m_funcs->get_output_layers(display, nullptr, layers.data(), count, &actualCount)) {
178         qWarning("Failed to get layers");
179         return;
180     }
181 
182     QEglFSKmsEglDeviceScreen *cur_screen = static_cast<QEglFSKmsEglDeviceScreen *>(screen());
183     Q_ASSERT(cur_screen);
184     QKmsOutput &output(cur_screen->output());
185     const uint32_t wantedId = !output.wants_forced_plane ? output.crtc_id : output.forced_plane_id;
186     qCDebug(qLcEglfsKmsDebug, "Searching for id: %d", wantedId);
187 
188     EGLOutputLayerEXT layer = EGL_NO_OUTPUT_LAYER_EXT;
189     for (int i = 0; i < actualCount; ++i) {
190         EGLAttrib id;
191         if (m_integration->m_funcs->query_output_layer_attrib(display, layers[i], EGL_DRM_CRTC_EXT, &id)) {
192             qCDebug(qLcEglfsKmsDebug, "  [%d] layer %p - crtc %d", i, layers[i], (int) id);
193             if (id == EGLAttrib(wantedId))
194                 layer = layers[i];
195         } else if (m_integration->m_funcs->query_output_layer_attrib(display, layers[i], EGL_DRM_PLANE_EXT, &id)) {
196             qCDebug(qLcEglfsKmsDebug, "  [%d] layer %p - plane %d", i, layers[i], (int) id);
197             if (id == EGLAttrib(wantedId))
198                 layer = layers[i];
199         } else {
200             qCDebug(qLcEglfsKmsDebug, "  [%d] layer %p - unknown", i, layers[i]);
201         }
202     }
203 
204     QByteArray reqLayerIndex = qgetenv("QT_QPA_EGLFS_LAYER_INDEX");
205     if (!reqLayerIndex.isEmpty()) {
206         int idx = reqLayerIndex.toInt();
207         if (idx >= 0 && idx < layers.count()) {
208             qCDebug(qLcEglfsKmsDebug, "EGLOutput layer index override = %d", idx);
209             layer = layers[idx];
210         }
211     }
212 
213     if (layer == EGL_NO_OUTPUT_LAYER_EXT) {
214         qWarning("resetSurface: Couldn't get EGLOutputLayer for native window");
215         return;
216     }
217 
218     qCDebug(qLcEglfsKmsDebug, "Using layer %p", layer);
219 
220     if (!m_integration->m_funcs->stream_consumer_output(display, m_egl_stream, layer))
221         qWarning("resetSurface: Unable to connect stream");
222 
223     m_config = QEglFSDeviceIntegration::chooseConfig(display, m_integration->surfaceFormatFor(window()->requestedFormat()));
224     m_format = q_glFormatFromConfig(display, m_config);
225     qCDebug(qLcEglfsKmsDebug) << "Stream producer format is" << m_format;
226 
227     const int w = cur_screen->rawGeometry().width();
228     const int h = cur_screen->rawGeometry().height();
229     qCDebug(qLcEglfsKmsDebug, "Creating stream producer surface of size %dx%d", w, h);
230 
231     const EGLint stream_producer_attribs[] = {
232         EGL_WIDTH,  w,
233         EGL_HEIGHT, h,
234         EGL_NONE
235     };
236 
237     m_surface = m_integration->m_funcs->create_stream_producer_surface(display, m_config, m_egl_stream, stream_producer_attribs);
238     if (m_surface == EGL_NO_SURFACE)
239         return;
240 
241     qCDebug(qLcEglfsKmsDebug, "Created stream producer surface %p", m_surface);
242 }
243 
createWindow(QWindow * window) const244 QEglFSWindow *QEglFSKmsEglDeviceIntegration::createWindow(QWindow *window) const
245 {
246     QEglFSKmsEglDeviceWindow *eglWindow = new QEglFSKmsEglDeviceWindow(window, this);
247 
248     m_funcs->initialize(eglWindow->screen()->display());
249     if (Q_UNLIKELY(!(m_funcs->has_egl_output_base && m_funcs->has_egl_output_drm && m_funcs->has_egl_stream &&
250                      m_funcs->has_egl_stream_producer_eglsurface && m_funcs->has_egl_stream_consumer_egloutput)))
251         qFatal("Required extensions missing!");
252 
253     return eglWindow;
254 }
255 
createDevice()256 QKmsDevice *QEglFSKmsEglDeviceIntegration::createDevice()
257 {
258     if (Q_UNLIKELY(!query_egl_device()))
259         qFatal("Could not set up EGL device!");
260 
261     const char *deviceName = m_funcs->query_device_string(m_egl_device, EGL_DRM_DEVICE_FILE_EXT);
262     if (Q_UNLIKELY(!deviceName))
263         qFatal("Failed to query device name from EGLDevice");
264 
265     return new QEglFSKmsEglDevice(this, screenConfig(), QLatin1String(deviceName));
266 }
267 
query_egl_device()268 bool QEglFSKmsEglDeviceIntegration::query_egl_device()
269 {
270     m_funcs = new QEGLStreamConvenience;
271     if (Q_UNLIKELY(!m_funcs->has_egl_device_base))
272         qFatal("EGL_EXT_device_base missing");
273 
274     EGLint num_devices = 0;
275     if (m_funcs->query_devices(1, &m_egl_device, &num_devices) != EGL_TRUE) {
276         qWarning("eglQueryDevicesEXT failed: eglError: %x", eglGetError());
277         return false;
278     }
279 
280     qCDebug(qLcEglfsKmsDebug, "Found %d EGL devices", num_devices);
281 
282     if (num_devices < 1 || m_egl_device == EGL_NO_DEVICE_EXT) {
283         qWarning("eglQueryDevicesEXT could not find any EGL devices");
284         return false;
285     }
286 
287     return true;
288 }
289 
createCursor(QPlatformScreen * screen) const290 QPlatformCursor *QEglFSKmsEglDeviceIntegration::createCursor(QPlatformScreen *screen) const
291 {
292 #if QT_CONFIG(opengl)
293     if (screenConfig()->separateScreens())
294         return new QEglFSCursor(screen);
295 #else
296     Q_UNUSED(screen);
297 #endif
298     return nullptr;
299 }
300 
301 QT_END_NAMESPACE
302