PipeWire: Make sure thumbnails properly use dmabuf again

There were some changes in KWin that only enabled exchanging streams
(thumbnails in our case) in case it's properly supported. We need to
explicitly request it now, otherwise we get memfd which is much slower.
wilder-5.24
Aleix Pol 5 years ago committed by Aleix Pol Gonzalez
parent eeae8cd993
commit 5dfcc03744
  1. 152
      libtaskmanager/declarative/pipewiresourcestream.cpp

@ -1,6 +1,6 @@
/*
SPDX-FileCopyrightText: 2018-2020 Red Hat Inc
SPDX-FileCopyrightText: 2020 Aleix Pol Gonzalez <aleixpol@kde.org>
SPDX-FileCopyrightText: 2020-2021 Aleix Pol Gonzalez <aleixpol@kde.org>
SPDX-FileContributor: Jan Grulich <jgrulich@redhat.com>
SPDX-License-Identifier: LGPL-2.0-or-later
@ -17,12 +17,88 @@
#include <sys/mman.h>
#include <unistd.h>
#include <QGuiApplication>
#include <QLoggingCategory>
#include <QOpenGLTexture>
#include <QSocketNotifier>
#include <QVersionNumber>
#include <qpa/qplatformnativeinterface.h>
#include <KLocalizedString>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <QtPlatformHeaders/QEGLNativeContext>
#undef Status
static uint32_t SpaPixelFormatToDrmFormat(uint32_t spa_format)
{
switch (spa_format) {
case SPA_VIDEO_FORMAT_RGBA:
return DRM_FORMAT_ABGR8888;
case SPA_VIDEO_FORMAT_RGBx:
return DRM_FORMAT_XBGR8888;
case SPA_VIDEO_FORMAT_BGRA:
return DRM_FORMAT_ARGB8888;
case SPA_VIDEO_FORMAT_BGRx:
return DRM_FORMAT_XRGB8888;
default:
return DRM_FORMAT_INVALID;
}
}
static std::vector<uint64_t> queryDmaBufModifiers(EGLDisplay display, uint32_t format)
{
static auto eglQueryDmaBufModifiersEXT = (PFNEGLQUERYDMABUFMODIFIERSEXTPROC)eglGetProcAddress("eglQueryDmaBufModifiersEXT");
static auto eglQueryDmaBufFormatsEXT = (PFNEGLQUERYDMABUFFORMATSEXTPROC)eglGetProcAddress("eglQueryDmaBufFormatsEXT");
if (!eglQueryDmaBufFormatsEXT || !eglQueryDmaBufModifiersEXT) {
return {};
}
uint32_t drm_format = SpaPixelFormatToDrmFormat(format);
if (drm_format == DRM_FORMAT_INVALID) {
qCDebug(PIPEWIRE_LOGGING) << "Failed to find matching DRM format." << format;
return {DRM_FORMAT_MOD_INVALID};
}
EGLint count = 0;
EGLBoolean success = eglQueryDmaBufFormatsEXT(display, 0, nullptr, &count);
if (!success || count == 0) {
qCWarning(PIPEWIRE_LOGGING) << "Failed to query DMA-BUF format count.";
return {DRM_FORMAT_MOD_INVALID};
}
std::vector<uint32_t> formats(count);
if (!eglQueryDmaBufFormatsEXT(display, count, reinterpret_cast<EGLint *>(formats.data()), &count)) {
if (!success)
qCWarning(PIPEWIRE_LOGGING) << "Failed to query DMA-BUF formats.";
return {DRM_FORMAT_MOD_INVALID};
}
if (std::find(formats.begin(), formats.end(), drm_format) == formats.end()) {
qCDebug(PIPEWIRE_LOGGING) << "Format " << drm_format << " not supported for modifiers.";
return {DRM_FORMAT_MOD_INVALID};
}
success = eglQueryDmaBufModifiersEXT(display, drm_format, 0, nullptr, nullptr, &count);
if (!success || count == 0) {
if (!success)
qCWarning(PIPEWIRE_LOGGING) << "Failed to query DMA-BUF modifier count.";
return {DRM_FORMAT_MOD_INVALID};
}
std::vector<uint64_t> modifiers(count);
if (!eglQueryDmaBufModifiersEXT(display, drm_format, count, modifiers.data(), nullptr, &count)) {
qCWarning(PIPEWIRE_LOGGING) << "Failed to query DMA-BUF modifiers.";
}
// Support modifier-less buffers
modifiers.push_back(DRM_FORMAT_MOD_INVALID);
return modifiers;
}
void PipeWireSourceStream::onStreamStateChanged(void *data, pw_stream_state old, pw_stream_state state, const char *error_message)
{
PipeWireSourceStream *pw = static_cast<PipeWireSourceStream *>(data);
@ -48,6 +124,42 @@ void PipeWireSourceStream::onStreamStateChanged(void *data, pw_stream_state old,
}
}
static spa_pod *buildFormat(spa_pod_builder *builder, spa_video_format format, const std::vector<uint64_t> &modifiers = {})
{
spa_pod_frame f[2];
const spa_rectangle pw_min_screen_bounds{1, 1};
const spa_rectangle pw_max_screen_bounds{UINT32_MAX, UINT32_MAX};
spa_pod_builder_push_object(builder, &f[0], SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat);
spa_pod_builder_add(builder, SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), 0);
spa_pod_builder_add(builder, SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), 0);
spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_format, SPA_POD_Id(format), 0);
if (modifiers.size()) {
auto pw_version = QVersionNumber::fromString(pw_get_library_version());
// SPA_POD_PROP_FLAG_DONT_FIXATE can be used with PipeWire >= 0.3.33
if (pw_version.majorVersion() >= 0 && pw_version.minorVersion() >= 3 && pw_version.microVersion() >= 33) {
spa_pod_builder_prop(builder, SPA_FORMAT_VIDEO_modifier, SPA_POD_PROP_FLAG_MANDATORY | SPA_POD_PROP_FLAG_DONT_FIXATE);
} else {
spa_pod_builder_prop(builder, SPA_FORMAT_VIDEO_modifier, SPA_POD_PROP_FLAG_MANDATORY);
}
spa_pod_builder_push_choice(builder, &f[1], SPA_CHOICE_Enum, 0);
// mofifiers from the array
for (auto it = modifiers.begin(); it != modifiers.end(); it++) {
spa_pod_builder_long(builder, *it);
if (it == modifiers.begin()) {
spa_pod_builder_long(builder, *it);
}
}
spa_pod_builder_pop(builder, &f[1]);
}
spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_size, SPA_POD_CHOICE_RANGE_Rectangle(&pw_min_screen_bounds, &pw_min_screen_bounds, &pw_max_screen_bounds), 0);
return static_cast<spa_pod *>(spa_pod_builder_pop(builder, &f[0]));
}
void PipeWireSourceStream::onStreamParamChanged(void *data, uint32_t id, const struct spa_pod *format)
{
if (!format || id != SPA_PARAM_Format) {
@ -67,6 +179,10 @@ void PipeWireSourceStream::onStreamParamChanged(void *data, uint32_t id, const s
uint8_t paramsBuffer[1024];
spa_pod_builder pod_builder = SPA_POD_BUILDER_INIT(paramsBuffer, sizeof(paramsBuffer));
const auto bufferTypes = spa_pod_find_prop(format, nullptr, SPA_FORMAT_VIDEO_modifier)
? (1 << SPA_DATA_DmaBuf) | (1 << SPA_DATA_MemFd) | (1 << SPA_DATA_MemPtr)
: (1 << SPA_DATA_MemFd) | (1 << SPA_DATA_MemPtr);
const spa_pod *param = (spa_pod *)spa_pod_builder_add_object(&pod_builder,
SPA_TYPE_OBJECT_ParamBuffers,
SPA_PARAM_Buffers,
@ -81,7 +197,7 @@ void PipeWireSourceStream::onStreamParamChanged(void *data, uint32_t id, const s
SPA_PARAM_BUFFERS_align,
SPA_POD_Int(16),
SPA_PARAM_BUFFERS_dataType,
SPA_POD_Int((1 << SPA_DATA_MemPtr) | (1 << SPA_DATA_MemFd) | (1 << SPA_DATA_DmaBuf)));
SPA_POD_CHOICE_FLAGS_Int(bufferTypes));
pw_stream_update_params(pw->pwStream, &param, 1);
}
@ -138,29 +254,19 @@ bool PipeWireSourceStream::createStream(uint nodeid)
uint8_t buffer[1024];
spa_pod_builder podBuilder = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
spa_fraction minFramerate = SPA_FRACTION(1, 1);
spa_fraction maxFramerate = SPA_FRACTION(25, 1);
const spa_pod *param = (spa_pod *)spa_pod_builder_add_object(&podBuilder,
SPA_TYPE_OBJECT_Format,
SPA_PARAM_EnumFormat,
SPA_FORMAT_mediaType,
SPA_POD_Id(SPA_MEDIA_TYPE_video),
SPA_FORMAT_mediaSubtype,
SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
SPA_FORMAT_VIDEO_format,
SPA_POD_CHOICE_ENUM_Id(6,
SPA_VIDEO_FORMAT_RGBx,
SPA_VIDEO_FORMAT_RGBA,
SPA_VIDEO_FORMAT_BGRx,
SPA_VIDEO_FORMAT_BGRA,
SPA_VIDEO_FORMAT_RGB,
SPA_VIDEO_FORMAT_BGR),
SPA_FORMAT_VIDEO_maxFramerate,
SPA_POD_CHOICE_RANGE_Fraction(&maxFramerate, &minFramerate, &maxFramerate));
const QVector<spa_video_format> formats =
{SPA_VIDEO_FORMAT_RGBx, SPA_VIDEO_FORMAT_RGBA, SPA_VIDEO_FORMAT_BGRx, SPA_VIDEO_FORMAT_BGRA, SPA_VIDEO_FORMAT_RGB, SPA_VIDEO_FORMAT_BGR};
QVector<const spa_pod *> params;
params.reserve(formats.size() * 2);
const EGLDisplay display = static_cast<EGLDisplay>(QGuiApplication::platformNativeInterface()->nativeResourceForIntegration("egldisplay"));
for (spa_video_format format : formats) {
params += buildFormat(&podBuilder, format, queryDmaBufModifiers(display, format));
params += buildFormat(&podBuilder, format, {});
}
pw_stream_flags s = (pw_stream_flags)(PW_STREAM_FLAG_DONT_RECONNECT | PW_STREAM_FLAG_AUTOCONNECT);
if (pw_stream_connect(pwStream, PW_DIRECTION_INPUT, pwNodeId, s, &param, 1) != 0) {
if (pw_stream_connect(pwStream, PW_DIRECTION_INPUT, pwNodeId, s, params.data(), params.size()) != 0) {
qCWarning(PIPEWIRE_LOGGING) << "Could not connect to stream";
pw_stream_destroy(pwStream);
return false;

Loading…
Cancel
Save