plugins/screencast: Preserve modifier order

The SPA_FORMAT_VIDEO_modifier property has the following format:

    preferred_value,alternative1,alternative2,...

The preferred value is usually the same as the first alternative value.
It can also happen that the modifier list the compositor has supplied is
not good either and it contains duplicate entries, specifically
`DRM_FORMAT_MOD_INVALID` to indicate that modifier-less buffers are
supported.

In order to deal with that, the screencast plugin removes the
duplicates by sorting the modifier list and then using std::unique().

That is not good, there is no written rule that the order of the
modifiers passed to the graphics buffer allocator matters but it
typically does, some drivers in mesa assume that the modifiers are
sorted in the preference order. The graphics buffer allocator might be
also very lazy and just look at the first supplied modifier instead of
wisely choosing the best modifier.

This change makes the ScreenCastStream remove the duplicate modifiers
in a more conservative fashion preserving the relative order of the
modifiers. It also removes an extra `{DRM_FORMAT_MOD_INVALID}` because
this kind of thing should be dictated by the render backend.
This commit is contained in:
Vlad Zahorodnii 2024-06-10 23:27:12 +03:00
parent 6c69f0a4d1
commit 64b02cf464

View file

@ -205,16 +205,19 @@ void ScreenCastStream::onStreamParamChanged(uint32_t id, const struct spa_pod *f
spa_format_video_raw_parse(format, &m_videoFormat);
auto modifierProperty = spa_pod_find_prop(format, nullptr, SPA_FORMAT_VIDEO_modifier);
QList<uint64_t> receivedModifiers;
if (modifierProperty) {
const struct spa_pod *modifierPod = &modifierProperty->value;
const uint32_t valueCount = SPA_POD_CHOICE_N_VALUES(&modifierProperty->value);
const uint64_t *values = (uint64_t *)SPA_POD_CHOICE_VALUES(&modifierProperty->value); // values[0] is the preferred choice
uint32_t modifiersCount = SPA_POD_CHOICE_N_VALUES(modifierPod);
uint64_t *modifiers = (uint64_t *)SPA_POD_CHOICE_VALUES(modifierPod);
receivedModifiers = QList<uint64_t>(modifiers, modifiers + modifiersCount);
// Remove duplicates
std::sort(receivedModifiers.begin(), receivedModifiers.end());
receivedModifiers.erase(std::unique(receivedModifiers.begin(), receivedModifiers.end()), receivedModifiers.end());
// Note that we don't need to search for duplicates in values[1...] but we do that anyway as a
// sanity check because the DRM modifier negotiation logic can be easily tripped by bad input.
QList<uint64_t> receivedModifiers;
receivedModifiers.reserve(valueCount);
for (uint32_t i = 0; i < valueCount; ++i) {
if (!receivedModifiers.contains(values[i])) {
receivedModifiers.append(values[i]);
}
}
if (!m_dmabufParams || m_dmabufParams->width != m_resolution.width() || m_dmabufParams->height != m_resolution.height() || !receivedModifiers.contains(m_dmabufParams->modifier)) {
if (modifierProperty->flags & SPA_POD_PROP_FLAG_DONT_FIXATE) {
@ -387,8 +390,6 @@ bool ScreenCastStream::createStream()
} else {
m_drmFormat = itModifiers.key();
m_modifiers = *itModifiers;
// Also support modifier-less DmaBufs
m_modifiers += DRM_FORMAT_MOD_INVALID;
}
m_hasDmaBuf = testCreateDmaBuf(m_resolution, m_drmFormat, {DRM_FORMAT_MOD_INVALID}).has_value();