From c04dcc2e7d834218ef2d4194331e383402495ae1 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 10 Apr 2024 20:07:22 +0200 Subject: Adding upstream version 2:20.4+dfsg. Signed-off-by: Daniel Baumann --- xbmc/windowing/gbm/drm/DRMAtomic.cpp | 344 +++++++++++++++++++++++++++++++++++ 1 file changed, 344 insertions(+) create mode 100644 xbmc/windowing/gbm/drm/DRMAtomic.cpp (limited to 'xbmc/windowing/gbm/drm/DRMAtomic.cpp') diff --git a/xbmc/windowing/gbm/drm/DRMAtomic.cpp b/xbmc/windowing/gbm/drm/DRMAtomic.cpp new file mode 100644 index 0000000..5d61a69 --- /dev/null +++ b/xbmc/windowing/gbm/drm/DRMAtomic.cpp @@ -0,0 +1,344 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "DRMAtomic.h" + +#include "ServiceBroker.h" +#include "guilib/GUIComponent.h" +#include "guilib/GUIWindowManager.h" +#include "settings/Settings.h" +#include "settings/SettingsComponent.h" +#include "settings/lib/Setting.h" +#include "utils/log.h" + +#include +#include + +#include +#include +#include + +using namespace KODI::WINDOWING::GBM; + +namespace +{ + +const auto SETTING_VIDEOSCREEN_HW_SCALING_FILTER = "videoscreen.hwscalingfilter"; + +uint32_t GetScalingFactor(uint32_t srcWidth, + uint32_t srcHeight, + uint32_t destWidth, + uint32_t destHeight) +{ + uint32_t factor_W = destWidth / srcWidth; + uint32_t factor_H = destHeight / srcHeight; + if (factor_W != factor_H) + return (factor_W < factor_H) ? factor_W : factor_H; + return factor_W; +} + +} // namespace + +bool CDRMAtomic::SetScalingFilter(CDRMObject* object, const char* name, const char* type) +{ + bool result; + uint64_t value; + std::tie(result, value) = m_gui_plane->GetPropertyValue(name, type); + if (!result) + return false; + + if (!AddProperty(object, name, value)) + return false; + + uint32_t mar_scale_factor = + GetScalingFactor(m_width, m_height, m_mode->hdisplay, m_mode->vdisplay); + AddProperty(object, "CRTC_W", (mar_scale_factor * m_width)); + AddProperty(object, "CRTC_H", (mar_scale_factor * m_height)); + + return true; +} + +void CDRMAtomic::DrmAtomicCommit(int fb_id, int flags, bool rendered, bool videoLayer) +{ + uint32_t blob_id; + + if (flags & DRM_MODE_ATOMIC_ALLOW_MODESET) + { + if (!AddProperty(m_connector, "CRTC_ID", m_crtc->GetCrtcId())) + return; + + if (drmModeCreatePropertyBlob(m_fd, m_mode, sizeof(*m_mode), &blob_id) != 0) + return; + + if (m_active && m_orig_crtc && m_orig_crtc->GetCrtcId() != m_crtc->GetCrtcId()) + { + // if using a different CRTC than the original, disable original to avoid EINVAL + if (!AddProperty(m_orig_crtc, "MODE_ID", 0)) + return; + + if (!AddProperty(m_orig_crtc, "ACTIVE", 0)) + return; + } + + if (!AddProperty(m_crtc, "MODE_ID", blob_id)) + return; + + if (!AddProperty(m_crtc, "ACTIVE", m_active ? 1 : 0)) + return; + } + + if (rendered) + { + AddProperty(m_gui_plane, "FB_ID", fb_id); + AddProperty(m_gui_plane, "CRTC_ID", m_crtc->GetCrtcId()); + AddProperty(m_gui_plane, "SRC_X", 0); + AddProperty(m_gui_plane, "SRC_Y", 0); + AddProperty(m_gui_plane, "SRC_W", m_width << 16); + AddProperty(m_gui_plane, "SRC_H", m_height << 16); + AddProperty(m_gui_plane, "CRTC_X", 0); + AddProperty(m_gui_plane, "CRTC_Y", 0); + //! @todo: disabled until upstream kernel changes are merged + // if (DisplayHardwareScalingEnabled()) + // { + // SetScalingFilter(m_gui_plane, "SCALING_FILTER", "Nearest Neighbor"); + // } + // else + { + AddProperty(m_gui_plane, "CRTC_W", m_mode->hdisplay); + AddProperty(m_gui_plane, "CRTC_H", m_mode->vdisplay); + } + + } + else if (videoLayer && !CServiceBroker::GetGUI()->GetWindowManager().HasVisibleControls()) + { + // disable gui plane when video layer is active and gui has no visible controls + AddProperty(m_gui_plane, "FB_ID", 0); + AddProperty(m_gui_plane, "CRTC_ID", 0); + } + + if (CServiceBroker::GetLogging().CanLogComponent(LOGWINDOWING)) + m_req->LogAtomicRequest(); + + auto ret = drmModeAtomicCommit(m_fd, m_req->Get(), flags | DRM_MODE_ATOMIC_TEST_ONLY, nullptr); + if (ret < 0) + { + CLog::Log(LOGERROR, + "CDRMAtomic::{} - test commit failed: ({}) - falling back to last successful atomic " + "request", + __FUNCTION__, strerror(errno)); + + auto oldRequest = m_atomicRequestQueue.front().get(); + CDRMAtomicRequest::LogAtomicDiff(m_req, oldRequest); + m_req = oldRequest; + + // update the old atomic request with the new fb id to avoid tearing + if (rendered) + AddProperty(m_gui_plane, "FB_ID", fb_id); + } + + ret = drmModeAtomicCommit(m_fd, m_req->Get(), flags, nullptr); + if (ret < 0) + { + CLog::Log(LOGERROR, "CDRMAtomic::{} - atomic commit failed: {}", __FUNCTION__, + strerror(errno)); + } + + if (flags & DRM_MODE_ATOMIC_ALLOW_MODESET) + { + if (drmModeDestroyPropertyBlob(m_fd, blob_id) != 0) + CLog::Log(LOGERROR, "CDRMAtomic::{} - failed to destroy property blob: {}", __FUNCTION__, + strerror(errno)); + } + + if (m_atomicRequestQueue.size() > 1) + m_atomicRequestQueue.pop_back(); + + m_atomicRequestQueue.emplace_back(std::make_unique()); + m_req = m_atomicRequestQueue.back().get(); +} + +void CDRMAtomic::FlipPage(struct gbm_bo *bo, bool rendered, bool videoLayer) +{ + struct drm_fb *drm_fb = nullptr; + + if (rendered) + { + if (videoLayer) + m_gui_plane->SetFormat(CDRMUtils::FourCCWithAlpha(m_gui_plane->GetFormat())); + else + m_gui_plane->SetFormat(CDRMUtils::FourCCWithoutAlpha(m_gui_plane->GetFormat())); + + drm_fb = CDRMUtils::DrmFbGetFromBo(bo); + if (!drm_fb) + { + CLog::Log(LOGERROR, "CDRMAtomic::{} - Failed to get a new FBO", __FUNCTION__); + return; + } + } + + uint32_t flags = 0; + + if (m_need_modeset) + { + flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; + m_need_modeset = false; + CLog::Log(LOGDEBUG, "CDRMAtomic::{} - Execute modeset at next commit", __FUNCTION__); + } + + DrmAtomicCommit(!drm_fb ? 0 : drm_fb->fb_id, flags, rendered, videoLayer); +} + +bool CDRMAtomic::InitDrm() +{ + if (!CDRMUtils::OpenDrm(true)) + return false; + + auto ret = drmSetClientCap(m_fd, DRM_CLIENT_CAP_ATOMIC, 1); + if (ret) + { + CLog::Log(LOGERROR, "CDRMAtomic::{} - no atomic modesetting support: {}", __FUNCTION__, + strerror(errno)); + return false; + } + + m_atomicRequestQueue.emplace_back(std::make_unique()); + m_req = m_atomicRequestQueue.back().get(); + + if (!CDRMUtils::InitDrm()) + return false; + + for (auto& plane : m_planes) + { + AddProperty(plane.get(), "FB_ID", 0); + AddProperty(plane.get(), "CRTC_ID", 0); + } + + CLog::Log(LOGDEBUG, "CDRMAtomic::{} - initialized atomic DRM", __FUNCTION__); + + //! @todo: disabled until upstream kernel changes are merged + // if (m_gui_plane->SupportsProperty("SCALING_FILTER")) + // { + // const std::shared_ptr settings = + // CServiceBroker::GetSettingsComponent()->GetSettings(); + // settings->GetSetting(SETTING_VIDEOSCREEN_HW_SCALING_FILTER)->SetVisible(true); + // } + + return true; +} + +void CDRMAtomic::DestroyDrm() +{ + CDRMUtils::DestroyDrm(); +} + +bool CDRMAtomic::SetVideoMode(const RESOLUTION_INFO& res, struct gbm_bo *bo) +{ + m_need_modeset = true; + + return true; +} + +bool CDRMAtomic::SetActive(bool active) +{ + m_need_modeset = true; + m_active = active; + + return true; +} + +bool CDRMAtomic::AddProperty(CDRMObject* object, const char* name, uint64_t value) +{ + return m_req->AddProperty(object, name, value); +} + +bool CDRMAtomic::DisplayHardwareScalingEnabled() +{ + auto settings = CServiceBroker::GetSettingsComponent()->GetSettings(); + + if (settings && settings->GetBool(SETTING_VIDEOSCREEN_HW_SCALING_FILTER)) + return true; + + return false; +} + +CDRMAtomic::CDRMAtomicRequest::CDRMAtomicRequest() : m_atomicRequest(drmModeAtomicAlloc()) +{ +} + +bool CDRMAtomic::CDRMAtomicRequest::AddProperty(CDRMObject* object, const char* name, uint64_t value) +{ + uint32_t propertyId = object->GetPropertyId(name); + if (propertyId == 0) + return false; + + int ret = drmModeAtomicAddProperty(m_atomicRequest.get(), object->GetId(), propertyId, value); + if (ret < 0) + return false; + + m_atomicRequestItems[object][propertyId] = value; + return true; +} + +void CDRMAtomic::CDRMAtomicRequest::LogAtomicDiff(CDRMAtomicRequest* current, + CDRMAtomicRequest* old) +{ + std::map> atomicDiff; + + for (const auto& object : current->m_atomicRequestItems) + { + auto sameObject = old->m_atomicRequestItems.find(object.first); + if (sameObject != old->m_atomicRequestItems.end()) + { + std::map propertyDiff; + + std::set_difference(current->m_atomicRequestItems[object.first].begin(), + current->m_atomicRequestItems[object.first].end(), + old->m_atomicRequestItems[object.first].begin(), + old->m_atomicRequestItems[object.first].end(), + std::inserter(propertyDiff, propertyDiff.begin())); + + atomicDiff[object.first] = propertyDiff; + } + else + { + atomicDiff[object.first] = current->m_atomicRequestItems[object.first]; + } + } + + CLog::Log(LOGDEBUG, "CDRMAtomicRequest::{} - DRM Atomic Request Diff:", __FUNCTION__); + + LogAtomicRequest(LOGERROR, atomicDiff); +} + +void CDRMAtomic::CDRMAtomicRequest::LogAtomicRequest() +{ + CLog::Log(LOGDEBUG, "CDRMAtomicRequest::{} - DRM Atomic Request:", __FUNCTION__); + LogAtomicRequest(LOGDEBUG, m_atomicRequestItems); +} + +void CDRMAtomic::CDRMAtomicRequest::LogAtomicRequest( + uint8_t logLevel, std::map>& atomicRequestItems) +{ + std::string message; + for (const auto& object : atomicRequestItems) + { + message.append("\nObject: " + object.first->GetTypeName() + + "\tID: " + std::to_string(object.first->GetId())); + for (const auto& property : object.second) + message.append("\n Property: " + object.first->GetPropertyName(property.first) + + "\tID: " + std::to_string(property.first) + + "\tValue: " + std::to_string(property.second)); + } + + CLog::Log(logLevel, "{}", message); +} + +void CDRMAtomic::CDRMAtomicRequest::DrmModeAtomicReqDeleter::operator()(drmModeAtomicReqPtr p) const +{ + drmModeAtomicFree(p); +}; -- cgit v1.2.3