blob: a0abf69729cda206846847caa61f0ffdca5140fe [file] [log] [blame]
/****************************************************************************
**
** 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 <wayland-kms.h>
#undef private
}
#include <private/qwaylandquickhardwarelayer_p.h>
#include <private/qwaylandquickitem_p.h>
#include <private/qwaylandview_p.h>
#include <QWaylandQuickOutput>
#include <QQuickWindow>
#include <QtPlatformHeaders/qeglfsfunctions.h>
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<Vsp2Layer>(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