1 /****************************************************************************
2 **
3 ** Copyright (C) 2018 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:GPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 or (at your option) any later version
20 ** approved by the KDE Free Qt Foundation. The licenses are as published by
21 ** the Free Software Foundation and appearing in the file LICENSE.GPL3
22 ** included in the packaging of this file. Please review the following
23 ** information to ensure the GNU General Public License requirements will
24 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25 **
26 ** $QT_END_LICENSE$
27 **
28 ****************************************************************************/
29 
30 #include "vsp2hardwarelayerintegration.h"
31 
32 extern "C" {
33 #define private priv
34 #include <wayland-kms.h>
35 #undef private
36 }
37 
38 #include <private/qwaylandquickhardwarelayer_p.h>
39 #include <private/qwaylandquickitem_p.h>
40 #include <private/qwaylandview_p.h>
41 #include <QWaylandQuickOutput>
42 #include <QQuickWindow>
43 
44 #include <QtPlatformHeaders/qeglfsfunctions.h>
45 
46 QT_BEGIN_NAMESPACE
47 
Vsp2Buffer(wl_kms_buffer * kmsBuffer)48 Vsp2Buffer::Vsp2Buffer(wl_kms_buffer *kmsBuffer)
49     : dmabufFd(kmsBuffer->fd)
50     , bytesPerLine(kmsBuffer->stride)
51     , drmPixelFormat(kmsBuffer->format)
52     , size(kmsBuffer->width, kmsBuffer->height)
53 {
54 }
55 
Vsp2Layer(QWaylandQuickHardwareLayer * hwLayer,Vsp2HardwareLayerIntegration * integration)56 Vsp2Layer::Vsp2Layer(QWaylandQuickHardwareLayer *hwLayer, Vsp2HardwareLayerIntegration *integration)
57     : m_hwLayer(hwLayer)
58 {
59     connect(hwLayer, &QWaylandQuickHardwareLayer::stackingLevelChanged, this, [integration](){
60         integration->recreateVspLayers();
61     });
62     connect(hwLayer->waylandItem(), &QWaylandQuickItem::surfaceChanged, this, &Vsp2Layer::handleSurfaceChanged);
63     connect(hwLayer->waylandItem(), &QQuickItem::opacityChanged, this, &Vsp2Layer::updateOpacity);
64     connect(hwLayer->waylandItem()->window(), &QQuickWindow::afterSynchronizing, this, &Vsp2Layer::updatePosition);
65     hwLayer->disableSceneGraphPainting();
66     QWaylandViewPrivate::get(hwLayer->waylandItem()->view())->independentFrameCallback = true;
67     handleSurfaceChanged();
68 }
69 
enableVspLayer()70 void Vsp2Layer::enableVspLayer()
71 {
72     auto *kmsBuffer = nextKmsBuffer();
73 
74     if (!kmsBuffer)
75         return;
76 
77     m_buffer = Vsp2Buffer(kmsBuffer);
78     updatePosition();
79     auto *wlItem = m_hwLayer->waylandItem();
80     m_screen = wlItem->window()->screen();
81     m_layerIndex = QEglFSFunctions::vsp2AddLayer(m_screen, m_buffer.dmabufFd, m_buffer.size, m_position, m_buffer.drmPixelFormat, m_buffer.bytesPerLine);
82     wlItem->surface()->frameStarted();
83     updateOpacity();
84 }
85 
disableVspLayer()86 void Vsp2Layer::disableVspLayer()
87 {
88     QEglFSFunctions::vsp2RemoveLayer(m_screen, m_layerIndex);
89     m_layerIndex = -1;
90     m_screen = nullptr;
91 }
92 
handleBufferCommitted()93 void Vsp2Layer::handleBufferCommitted()
94 {
95     if (!isEnabled()) {
96         enableVspLayer();
97         return;
98     }
99 
100     auto *kmsBuffer = nextKmsBuffer();
101 
102     Vsp2Buffer newBuffer(kmsBuffer);
103     if (m_buffer.dmabufFd != -1) {
104         bool formatChanged = false;
105         formatChanged |= newBuffer.bytesPerLine != m_buffer.bytesPerLine;
106         formatChanged |= newBuffer.size != m_buffer.size;
107         formatChanged |= newBuffer.drmPixelFormat != m_buffer.drmPixelFormat;
108         if (formatChanged) {
109             qWarning() << "The VSP2 Wayland hardware layer integration doesn't support changing"
110                        << "surface formats, this will most likely fail";
111         }
112     }
113 
114     m_buffer = newBuffer;
115     auto *wlItem = m_hwLayer->waylandItem();
116     m_screen = wlItem->window()->screen();
117     QEglFSFunctions::vsp2SetLayerBuffer(m_screen, m_layerIndex, m_buffer.dmabufFd);
118     wlItem->surface()->frameStarted();
119 }
120 
handleSurfaceChanged()121 void Vsp2Layer::handleSurfaceChanged()
122 {
123     auto newSurface = m_hwLayer->waylandItem()->surface();
124 
125     if (Q_UNLIKELY(newSurface == m_surface))
126         return;
127 
128     if (this->m_surface)
129         disconnect(this->m_surface, &QWaylandSurface::redraw, this, &Vsp2Layer::handleBufferCommitted);
130     if (newSurface)
131         connect(newSurface, &QWaylandSurface::redraw, this, &Vsp2Layer::handleBufferCommitted, Qt::DirectConnection);
132 
133     this->m_surface = newSurface;
134 }
135 
updatePosition()136 void Vsp2Layer::updatePosition()
137 {
138     QWaylandQuickItem *wlItem = m_hwLayer->waylandItem();
139     QRectF localGeometry(0, 0, wlItem->width(), wlItem->height());
140     auto lastMatrix = QWaylandQuickItemPrivate::get(wlItem)->lastMatrix;
141     auto globalGeometry = lastMatrix.mapRect(localGeometry);
142 
143     if (m_buffer.size != globalGeometry.size().toSize()) {
144         qWarning() << "wl_buffer size != WaylandQuickItem size and scaling has not been"
145                    << "implemented for the vsp2 hardware layer integration";
146     }
147 
148     m_position = globalGeometry.topLeft().toPoint();
149     if (isEnabled())
150         QEglFSFunctions::vsp2SetLayerPosition(m_screen, m_layerIndex, m_position);
151 }
152 
updateOpacity()153 void Vsp2Layer::updateOpacity()
154 {
155     if (isEnabled()) {
156         qreal opacity = m_hwLayer->waylandItem()->opacity();
157         QEglFSFunctions::vsp2SetLayerAlpha(m_screen, m_layerIndex, opacity);
158     }
159 }
160 
nextKmsBuffer()161 wl_kms_buffer *Vsp2Layer::nextKmsBuffer()
162 {
163     Q_ASSERT(m_hwLayer && m_hwLayer->waylandItem());
164     QWaylandQuickItem *wlItem = m_hwLayer->waylandItem();
165     auto view = wlItem->view();
166     Q_ASSERT(view);
167 
168     view->advance();
169     auto wlBuffer = view->currentBuffer().wl_buffer();
170 
171     if (!wlBuffer)
172         return nullptr;
173 
174     struct wl_kms_buffer *kmsBuffer = wayland_kms_buffer_get(wlBuffer);
175 
176     if (!kmsBuffer)
177         qWarning() << "Failed to get wl_kms_buffer for wl_buffer:" << wl_resource_get_id(wlBuffer);
178 
179     return kmsBuffer;
180 }
181 
enableVspLayers()182 void Vsp2HardwareLayerIntegration::enableVspLayers()
183 {
184     for (auto &layer : qAsConst(m_layers)) {
185         Q_ASSERT(!layer->isEnabled());
186         layer->enableVspLayer();
187     }
188 }
189 
disableVspLayers()190 void Vsp2HardwareLayerIntegration::disableVspLayers()
191 {
192     for (auto it = m_layers.rbegin(); it != m_layers.rend(); ++it) {
193         if ((*it)->isEnabled())
194             (*it)->disableVspLayer();
195     }
196 }
197 
sortLayersByDepth()198 void Vsp2HardwareLayerIntegration::sortLayersByDepth()
199 {
200     std::sort(m_layers.begin(), m_layers.end(), [](auto &l1, auto &l2){
201         return l1->hwLayer()->stackingLevel() < l2->hwLayer()->stackingLevel();
202     });
203 }
204 
recreateVspLayers()205 void Vsp2HardwareLayerIntegration::recreateVspLayers() {
206     disableVspLayers();
207     sortLayersByDepth();
208     enableVspLayers();
209 }
210 
Vsp2HardwareLayerIntegration()211 Vsp2HardwareLayerIntegration::Vsp2HardwareLayerIntegration()
212 {
213     if (QGuiApplication::platformName() != "eglfs") {
214         qWarning() << "Vsp2 layers are currently only supported on the eglfs platform plugin"
215                    << "with the eglfs_kms_vsp2 device integration.\n"
216                    << "You need to set QT_QPA_PLATFORM=eglfs and QT_QPA_EGLFS_INTEGRATION=eglfs_kms_vsp2";
217     }
218     static Vsp2HardwareLayerIntegration *s_instance = this;
219     QEglFSFunctions::vsp2AddBlendListener(QGuiApplication::primaryScreen(), [](){
220         s_instance->sendFrameCallbacks();
221     });
222 }
223 
add(QWaylandQuickHardwareLayer * hwLayer)224 void Vsp2HardwareLayerIntegration::add(QWaylandQuickHardwareLayer *hwLayer)
225 {
226     disableVspLayers();
227     m_layers.append(QSharedPointer<Vsp2Layer>(new Vsp2Layer(hwLayer, this)));
228     sortLayersByDepth();
229     enableVspLayers();
230 }
231 
remove(QWaylandQuickHardwareLayer * hwLayer)232 void Vsp2HardwareLayerIntegration::remove(QWaylandQuickHardwareLayer *hwLayer)
233 {
234     disableVspLayers();
235     for (auto it = m_layers.begin(); it != m_layers.end(); ++it) {
236         if ((*it)->hwLayer() == hwLayer) {
237             m_layers.erase(it);
238             break;
239         }
240     }
241     enableVspLayers();
242 }
243 
sendFrameCallbacks()244 void Vsp2HardwareLayerIntegration::sendFrameCallbacks()
245 {
246     for (auto &layer : qAsConst(m_layers)) {
247         if (auto *surface = layer->hwLayer()->waylandItem()->surface())
248             surface->sendFrameCallbacks();
249     }
250 }
251 
252 QT_END_NAMESPACE
253