/**************************************************************************** ** ** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the plugins of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 or (at your option) any later version ** approved by the KDE Free Qt Foundation. The licenses are as published by ** the Free Software Foundation and appearing in the file LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "vsp2hardwarelayerintegration.h" extern "C" { #define private priv #include #undef private } #include #include #include #include #include #include QT_BEGIN_NAMESPACE Vsp2Buffer::Vsp2Buffer(wl_kms_buffer *kmsBuffer) : dmabufFd(kmsBuffer->fd) , bytesPerLine(kmsBuffer->stride) , drmPixelFormat(kmsBuffer->format) , size(kmsBuffer->width, kmsBuffer->height) { } Vsp2Layer::Vsp2Layer(QWaylandQuickHardwareLayer *hwLayer, Vsp2HardwareLayerIntegration *integration) : m_hwLayer(hwLayer) { connect(hwLayer, &QWaylandQuickHardwareLayer::stackingLevelChanged, this, [integration](){ integration->recreateVspLayers(); }); connect(hwLayer->waylandItem(), &QWaylandQuickItem::surfaceChanged, this, &Vsp2Layer::handleSurfaceChanged); connect(hwLayer->waylandItem(), &QQuickItem::opacityChanged, this, &Vsp2Layer::updateOpacity); connect(hwLayer->waylandItem()->window(), &QQuickWindow::afterSynchronizing, this, &Vsp2Layer::updatePosition); hwLayer->disableSceneGraphPainting(); QWaylandViewPrivate::get(hwLayer->waylandItem()->view())->independentFrameCallback = true; handleSurfaceChanged(); } void Vsp2Layer::enableVspLayer() { auto *kmsBuffer = nextKmsBuffer(); if (!kmsBuffer) return; m_buffer = Vsp2Buffer(kmsBuffer); updatePosition(); auto *wlItem = m_hwLayer->waylandItem(); m_screen = wlItem->window()->screen(); m_layerIndex = QEglFSFunctions::vsp2AddLayer(m_screen, m_buffer.dmabufFd, m_buffer.size, m_position, m_buffer.drmPixelFormat, m_buffer.bytesPerLine); wlItem->surface()->frameStarted(); updateOpacity(); } void Vsp2Layer::disableVspLayer() { QEglFSFunctions::vsp2RemoveLayer(m_screen, m_layerIndex); m_layerIndex = -1; m_screen = nullptr; } void Vsp2Layer::handleBufferCommitted() { if (!isEnabled()) { enableVspLayer(); return; } auto *kmsBuffer = nextKmsBuffer(); Vsp2Buffer newBuffer(kmsBuffer); if (m_buffer.dmabufFd != -1) { bool formatChanged = false; formatChanged |= newBuffer.bytesPerLine != m_buffer.bytesPerLine; formatChanged |= newBuffer.size != m_buffer.size; formatChanged |= newBuffer.drmPixelFormat != m_buffer.drmPixelFormat; if (formatChanged) { qWarning() << "The VSP2 Wayland hardware layer integration doesn't support changing" << "surface formats, this will most likely fail"; } } m_buffer = newBuffer; auto *wlItem = m_hwLayer->waylandItem(); m_screen = wlItem->window()->screen(); QEglFSFunctions::vsp2SetLayerBuffer(m_screen, m_layerIndex, m_buffer.dmabufFd); wlItem->surface()->frameStarted(); } void Vsp2Layer::handleSurfaceChanged() { auto newSurface = m_hwLayer->waylandItem()->surface(); if (Q_UNLIKELY(newSurface == m_surface)) return; if (this->m_surface) disconnect(this->m_surface, &QWaylandSurface::redraw, this, &Vsp2Layer::handleBufferCommitted); if (newSurface) connect(newSurface, &QWaylandSurface::redraw, this, &Vsp2Layer::handleBufferCommitted, Qt::DirectConnection); this->m_surface = newSurface; } void Vsp2Layer::updatePosition() { QWaylandQuickItem *wlItem = m_hwLayer->waylandItem(); QRectF localGeometry(0, 0, wlItem->width(), wlItem->height()); auto lastMatrix = QWaylandQuickItemPrivate::get(wlItem)->lastMatrix; auto globalGeometry = lastMatrix.mapRect(localGeometry); if (m_buffer.size != globalGeometry.size().toSize()) { qWarning() << "wl_buffer size != WaylandQuickItem size and scaling has not been" << "implemented for the vsp2 hardware layer integration"; } m_position = globalGeometry.topLeft().toPoint(); if (isEnabled()) QEglFSFunctions::vsp2SetLayerPosition(m_screen, m_layerIndex, m_position); } void Vsp2Layer::updateOpacity() { if (isEnabled()) { qreal opacity = m_hwLayer->waylandItem()->opacity(); QEglFSFunctions::vsp2SetLayerAlpha(m_screen, m_layerIndex, opacity); } } wl_kms_buffer *Vsp2Layer::nextKmsBuffer() { Q_ASSERT(m_hwLayer && m_hwLayer->waylandItem()); QWaylandQuickItem *wlItem = m_hwLayer->waylandItem(); auto view = wlItem->view(); Q_ASSERT(view); view->advance(); auto wlBuffer = view->currentBuffer().wl_buffer(); if (!wlBuffer) return nullptr; struct wl_kms_buffer *kmsBuffer = wayland_kms_buffer_get(wlBuffer); if (!kmsBuffer) qWarning() << "Failed to get wl_kms_buffer for wl_buffer:" << wl_resource_get_id(wlBuffer); return kmsBuffer; } void Vsp2HardwareLayerIntegration::enableVspLayers() { for (auto &layer : qAsConst(m_layers)) { Q_ASSERT(!layer->isEnabled()); layer->enableVspLayer(); } } void Vsp2HardwareLayerIntegration::disableVspLayers() { for (auto it = m_layers.rbegin(); it != m_layers.rend(); ++it) { if ((*it)->isEnabled()) (*it)->disableVspLayer(); } } void Vsp2HardwareLayerIntegration::sortLayersByDepth() { std::sort(m_layers.begin(), m_layers.end(), [](auto &l1, auto &l2){ return l1->hwLayer()->stackingLevel() < l2->hwLayer()->stackingLevel(); }); } void Vsp2HardwareLayerIntegration::recreateVspLayers() { disableVspLayers(); sortLayersByDepth(); enableVspLayers(); } Vsp2HardwareLayerIntegration::Vsp2HardwareLayerIntegration() { if (QGuiApplication::platformName() != "eglfs") { qWarning() << "Vsp2 layers are currently only supported on the eglfs platform plugin" << "with the eglfs_kms_vsp2 device integration.\n" << "You need to set QT_QPA_PLATFORM=eglfs and QT_QPA_EGLFS_INTEGRATION=eglfs_kms_vsp2"; } static Vsp2HardwareLayerIntegration *s_instance = this; QEglFSFunctions::vsp2AddBlendListener(QGuiApplication::primaryScreen(), [](){ s_instance->sendFrameCallbacks(); }); } void Vsp2HardwareLayerIntegration::add(QWaylandQuickHardwareLayer *hwLayer) { disableVspLayers(); m_layers.append(QSharedPointer(new Vsp2Layer(hwLayer, this))); sortLayersByDepth(); enableVspLayers(); } void Vsp2HardwareLayerIntegration::remove(QWaylandQuickHardwareLayer *hwLayer) { disableVspLayers(); for (auto it = m_layers.begin(); it != m_layers.end(); ++it) { if ((*it)->hwLayer() == hwLayer) { m_layers.erase(it); break; } } enableVspLayers(); } void Vsp2HardwareLayerIntegration::sendFrameCallbacks() { for (auto &layer : qAsConst(m_layers)) { if (auto *surface = layer->hwLayer()->waylandItem()->surface()) surface->sendFrameCallbacks(); } } QT_END_NAMESPACE