From 13d9d79fe3c040c09acf89b4f0bff6cba1a24ce2 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Mon, 5 Jan 2026 20:37:52 -0600 Subject: [PATCH] Implement support for adjusting zpos to make planes visible --- app/streaming/video/ffmpeg-renderers/drm.cpp | 129 +++++++++++++++---- app/streaming/video/ffmpeg-renderers/drm.h | 1 + 2 files changed, 107 insertions(+), 23 deletions(-) diff --git a/app/streaming/video/ffmpeg-renderers/drm.cpp b/app/streaming/video/ffmpeg-renderers/drm.cpp index 004c7f85..87d2d5de 100644 --- a/app/streaming/video/ffmpeg-renderers/drm.cpp +++ b/app/streaming/video/ffmpeg-renderers/drm.cpp @@ -174,6 +174,14 @@ DrmRenderer::~DrmRenderer() if (auto prop = m_Connector.property("max bpc")) { m_PropSetter.set(*prop, prop->initialValue()); } + if (auto zpos = m_VideoPlane.property("zpos"); zpos && !zpos->isImmutable()) { + m_PropSetter.set(*zpos, zpos->initialValue()); + } + for (int i = 0; i < Overlay::OverlayMax; i++) { + if (auto zpos = m_OverlayPlanes[i].property("zpos"); zpos && !zpos->isImmutable()) { + m_PropSetter.set(*zpos, zpos->initialValue()); + } + } m_PropSetter.apply(); } @@ -350,6 +358,31 @@ void DrmRenderer::prepareToRender() } } + // Adjust zpos values if needed + if (auto zpos = m_VideoPlane.property("zpos"); zpos && m_VideoPlaneZpos != zpos->initialValue()) { + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, + "Moving video plane to zpos: %" PRIu64, + m_VideoPlaneZpos); + m_PropSetter.set(*zpos, m_VideoPlaneZpos, false); + } + for (int i = 0; i < Overlay::OverlayMax; i++) { + if (auto zpos = m_OverlayPlanes[i].property("zpos")) { + // This may result in multiple overlays having the same zpos, which + // means undefined ordering between the planes, but that's fine. + // The planes should never overlap anyway. + if (!zpos->isImmutable() && zpos->initialValue() <= m_VideoPlaneZpos) { + auto zposRange = zpos->range(); + uint64_t newZpos = std::clamp(m_VideoPlaneZpos + 1, zposRange.first, zposRange.second); + + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, + "Moving overlay plane %u to zpos: %" PRIu64, + i, + newZpos); + m_PropSetter.set(*zpos, newZpos, false); + } + } + } + m_PropSetter.apply(); // We've now changed state that must be restored @@ -557,24 +590,22 @@ bool DrmRenderer::initialize(PDECODER_PARAMETERS params) // Find the active plane (if any) on this CRTC with the highest zpos. // We'll need to use a plane with a equal or greater zpos to be visible. - uint64_t maxActiveZpos = qEnvironmentVariableIntValue("DRM_MIN_PLANE_ZPOS"); + std::set activePlanesZpos; for (uint32_t i = 0; i < planeRes->count_planes; i++) { drmModePlane* plane = drmModeGetPlane(m_DrmFd, planeRes->planes[i]); if (plane != nullptr) { if (plane->crtc_id == m_Crtc.objectId()) { - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, - "Plane %u is active on CRTC %u", - plane->plane_id, - plane->crtc_id); - // Don't consider cursor planes when searching for the highest active zpos DrmPropertyMap props { m_DrmFd, planeRes->planes[i], DRM_MODE_OBJECT_PLANE }; if (props.property("type")->initialValue() == DRM_PLANE_TYPE_PRIMARY || props.property("type")->initialValue() == DRM_PLANE_TYPE_OVERLAY) { + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, + "Plane %u is active on CRTC %u", + plane->plane_id, + plane->crtc_id); + if (auto zpos = props.property("zpos")) { - if (zpos->initialValue() > maxActiveZpos) { - maxActiveZpos = zpos->initialValue(); - } + activePlanesZpos.emplace(zpos->initialValue()); } } } @@ -614,7 +645,6 @@ bool DrmRenderer::initialize(PDECODER_PARAMETERS params) // FIXME: We should check the actual DRM format in a real AVFrame rather // than just assuming it will be a certain hardcoded type like NV12 based // on the chosen video format. - uint64_t videoPlaneZpos = 0; for (uint32_t i = 0; i < planeRes->count_planes && !m_VideoPlane.isValid(); i++) { drmModePlane* plane = drmModeGetPlane(m_DrmFd, planeRes->planes[i]); if (plane != nullptr) { @@ -652,19 +682,58 @@ bool DrmRenderer::initialize(PDECODER_PARAMETERS params) } // If this plane has a zpos property and it's lower (further from user) than - // the highest active plane we found, avoid this plane. It won't be visible. + // the highest active plane we found, avoid this plane unless we can adjust + // the zpos property to an acceptable value. // // Note: zpos is not a required property, but if any plane has it, all planes must. auto zpos = props.property("zpos"); - if (zpos && zpos->initialValue() < maxActiveZpos) { - drmModeFreePlane(plane); - continue; + if (zpos) { + // If the zpos property is immutable, then we're stuck with whatever it is + if (zpos->isImmutable()) { + if (!activePlanesZpos.empty() && zpos->initialValue() < *activePlanesZpos.crbegin()) { + // This plane is too low to be visible + drmModeFreePlane(plane); + continue; + } + else { + m_VideoPlaneZpos = zpos->initialValue(); + } + } + else { + auto zposRange = zpos->range(); + + uint64_t lowestAcceptableZpos; + + // No active planes, so we can use the minimum zpos + auto zposIt = activePlanesZpos.crbegin(); + if (zposIt == activePlanesZpos.crend()) { + lowestAcceptableZpos = zposRange.first; + } + else if (*zposIt == zpos->initialValue()) { + // The highest active zpos is our current plane, so try the next one + if (++zposIt == activePlanesZpos.crend()) { + // Our plane is the only active, so we can use the minimum zpos + lowestAcceptableZpos = zposRange.first; + } + else { + lowestAcceptableZpos = *zposIt + 1; + } + } + else { + // The highest active zpos is some other plane that isn't ours + lowestAcceptableZpos = *zposIt + 1; + } + + m_VideoPlaneZpos = std::clamp(lowestAcceptableZpos, zposRange.first, zposRange.second); + } + } + else { + m_VideoPlaneZpos = 0; } SDL_assert(!m_VideoPlane.isValid()); m_VideoPlane.load(m_DrmFd, plane->plane_id, DRM_MODE_OBJECT_PLANE); SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Selected plane %u for video", plane->plane_id); - videoPlaneZpos = zpos ? zpos->initialValue() : 0; drmModeFreePlane(plane); } } @@ -693,14 +762,6 @@ bool DrmRenderer::initialize(PDECODER_PARAMETERS params) drmModeFreePlane(plane); continue; } - // If this overlay plane has a zpos property and it's lower (further from user) than - // the video plane we selected, avoid this plane. It won't be visible on top. - // - // Note: zpos is not a required property, but if any plane has it, all planes must. - else if (auto zpos = props.property("zpos"); !zpos || zpos->initialValue() <= videoPlaneZpos) { - drmModeFreePlane(plane); - continue; - } // The overlay plane must support ARGB8888 bool foundFormat = false; @@ -715,6 +776,28 @@ bool DrmRenderer::initialize(PDECODER_PARAMETERS params) continue; } + // If this plane has a zpos property and it's lower (further from user) than + // the highest active plane we found, avoid this plane unless we can adjust + // the zpos property to an acceptable value. + // + // Note: zpos is not a required property, but if any plane has it, all planes must. + auto zpos = props.property("zpos"); + if (zpos) { + // If the zpos property is immutable, then we're stuck with whatever it is + if (zpos->isImmutable()) { + if (zpos->initialValue() <= m_VideoPlaneZpos) { + // This plane is too low to be visible + drmModeFreePlane(plane); + continue; + } + } + else if (zpos->range().second <= m_VideoPlaneZpos) { + // This plane cannot be raised high enough to be visible + drmModeFreePlane(plane); + continue; + } + } + // Allocate this overlay plane to the next unused overlay slot SDL_assert(!m_OverlayPlanes[overlayIndex].isValid()); m_OverlayPlanes[overlayIndex++].load(m_DrmFd, plane->plane_id, DRM_MODE_OBJECT_PLANE); diff --git a/app/streaming/video/ffmpeg-renderers/drm.h b/app/streaming/video/ffmpeg-renderers/drm.h index 3c37d4a2..9810cb51 100644 --- a/app/streaming/video/ffmpeg-renderers/drm.h +++ b/app/streaming/video/ffmpeg-renderers/drm.h @@ -651,6 +651,7 @@ private: DrmPropertyMap m_Connector; DrmPropertyMap m_Crtc; DrmPropertyMap m_VideoPlane; + uint64_t m_VideoPlaneZpos; DrmPropertyMap m_OverlayPlanes[Overlay::OverlayMax]; DrmPropertySetter m_PropSetter; SDL_Rect m_OverlayRects[Overlay::OverlayMax];