2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
11 #include "ServiceBroker.h"
12 #include "guilib/GUIComponent.h"
13 #include "guilib/GUIWindowManager.h"
14 #include "settings/Settings.h"
15 #include "settings/SettingsComponent.h"
16 #include "settings/lib/Setting.h"
17 #include "utils/log.h"
22 #include <drm_fourcc.h>
26 using namespace KODI::WINDOWING::GBM
;
31 const auto SETTING_VIDEOSCREEN_HW_SCALING_FILTER
= "videoscreen.hwscalingfilter";
33 uint32_t GetScalingFactor(uint32_t srcWidth
,
38 uint32_t factor_W
= destWidth
/ srcWidth
;
39 uint32_t factor_H
= destHeight
/ srcHeight
;
40 if (factor_W
!= factor_H
)
41 return (factor_W
< factor_H
) ? factor_W
: factor_H
;
47 bool CDRMAtomic::SetScalingFilter(CDRMObject
* object
, const char* name
, const char* type
)
49 std::optional
<uint64_t> scalingFilter
= m_gui_plane
->GetPropertyValue(name
, type
);
53 if (!AddProperty(object
, name
, scalingFilter
.value()))
56 uint32_t mar_scale_factor
=
57 GetScalingFactor(m_width
, m_height
, m_mode
->hdisplay
, m_mode
->vdisplay
);
58 AddProperty(object
, "CRTC_W", (mar_scale_factor
* m_width
));
59 AddProperty(object
, "CRTC_H", (mar_scale_factor
* m_height
));
64 void CDRMAtomic::DrmAtomicCommit(int fb_id
, int flags
, bool rendered
, bool videoLayer
)
68 if (flags
& DRM_MODE_ATOMIC_ALLOW_MODESET
)
70 if (!AddProperty(m_connector
, "CRTC_ID", m_crtc
->GetCrtcId()))
73 if (drmModeCreatePropertyBlob(m_fd
, m_mode
, sizeof(*m_mode
), &blob_id
) != 0)
76 if (m_active
&& m_orig_crtc
&& m_orig_crtc
->GetCrtcId() != m_crtc
->GetCrtcId())
78 // if using a different CRTC than the original, disable original to avoid EINVAL
79 if (!AddProperty(m_orig_crtc
, "MODE_ID", 0))
82 if (!AddProperty(m_orig_crtc
, "ACTIVE", 0))
86 if (!AddProperty(m_crtc
, "MODE_ID", blob_id
))
89 if (!AddProperty(m_crtc
, "ACTIVE", m_active
? 1 : 0))
95 AddProperty(m_gui_plane
, "FB_ID", fb_id
);
96 AddProperty(m_gui_plane
, "CRTC_ID", m_crtc
->GetCrtcId());
97 AddProperty(m_gui_plane
, "SRC_X", 0);
98 AddProperty(m_gui_plane
, "SRC_Y", 0);
99 AddProperty(m_gui_plane
, "SRC_W", m_width
<< 16);
100 AddProperty(m_gui_plane
, "SRC_H", m_height
<< 16);
101 AddProperty(m_gui_plane
, "CRTC_X", 0);
102 AddProperty(m_gui_plane
, "CRTC_Y", 0);
103 //! @todo: disabled until upstream kernel changes are merged
104 // if (DisplayHardwareScalingEnabled())
106 // SetScalingFilter(m_gui_plane, "SCALING_FILTER", "Nearest Neighbor");
110 AddProperty(m_gui_plane
, "CRTC_W", m_mode
->hdisplay
);
111 AddProperty(m_gui_plane
, "CRTC_H", m_mode
->vdisplay
);
114 if (m_inFenceFd
!= -1)
116 AddProperty(m_crtc
, "OUT_FENCE_PTR", reinterpret_cast<uint64_t>(&m_outFenceFd
));
117 AddProperty(m_gui_plane
, "IN_FENCE_FD", m_inFenceFd
);
120 else if (videoLayer
&& !CServiceBroker::GetGUI()->GetWindowManager().HasVisibleControls())
122 // disable gui plane when video layer is active and gui has no visible controls
123 AddProperty(m_gui_plane
, "FB_ID", 0);
124 AddProperty(m_gui_plane
, "CRTC_ID", 0);
127 if (CServiceBroker::GetLogging().CanLogComponent(LOGWINDOWING
))
128 m_req
->LogAtomicRequest();
130 auto ret
= drmModeAtomicCommit(m_fd
, m_req
->Get(), flags
| DRM_MODE_ATOMIC_TEST_ONLY
, nullptr);
134 "CDRMAtomic::{} - test commit failed: ({}) - falling back to last successful atomic "
136 __FUNCTION__
, strerror(errno
));
138 auto oldRequest
= m_atomicRequestQueue
.front().get();
139 CDRMAtomicRequest::LogAtomicDiff(m_req
, oldRequest
);
142 // update the old atomic request with the new fb id to avoid tearing
144 AddProperty(m_gui_plane
, "FB_ID", fb_id
);
147 ret
= drmModeAtomicCommit(m_fd
, m_req
->Get(), flags
, nullptr);
150 CLog::Log(LOGERROR
, "CDRMAtomic::{} - atomic commit failed: {}", __FUNCTION__
,
152 m_atomicRequestQueue
.pop_back();
154 else if (m_atomicRequestQueue
.size() > 1)
156 m_atomicRequestQueue
.pop_front();
159 if (m_inFenceFd
!= -1)
165 if (flags
& DRM_MODE_ATOMIC_ALLOW_MODESET
)
167 if (drmModeDestroyPropertyBlob(m_fd
, blob_id
) != 0)
168 CLog::Log(LOGERROR
, "CDRMAtomic::{} - failed to destroy property blob: {}", __FUNCTION__
,
172 m_atomicRequestQueue
.emplace_back(std::make_unique
<CDRMAtomicRequest
>());
173 m_req
= m_atomicRequestQueue
.back().get();
176 void CDRMAtomic::FlipPage(struct gbm_bo
* bo
, bool rendered
, bool videoLayer
, bool async
)
178 struct drm_fb
*drm_fb
= nullptr;
184 m_gui_plane
->SetFormat(CDRMUtils::FourCCWithAlpha(m_gui_plane
->GetFormat()));
186 m_gui_plane
->SetFormat(CDRMUtils::FourCCWithoutAlpha(m_gui_plane
->GetFormat()));
188 drm_fb
= CDRMUtils::DrmFbGetFromBo(bo
);
191 CLog::Log(LOGERROR
, "CDRMAtomic::{} - Failed to get a new FBO", __FUNCTION__
);
195 if (async
&& !m_need_modeset
)
196 flags
|= DRM_MODE_ATOMIC_NONBLOCK
;
201 flags
|= DRM_MODE_ATOMIC_ALLOW_MODESET
;
202 m_need_modeset
= false;
203 CLog::Log(LOGDEBUG
, "CDRMAtomic::{} - Execute modeset at next commit", __FUNCTION__
);
206 DrmAtomicCommit(!drm_fb
? 0 : drm_fb
->fb_id
, flags
, rendered
, videoLayer
);
209 bool CDRMAtomic::InitDrm()
211 if (!CDRMUtils::OpenDrm(true))
214 auto ret
= drmSetClientCap(m_fd
, DRM_CLIENT_CAP_ATOMIC
, 1);
217 CLog::Log(LOGERROR
, "CDRMAtomic::{} - no atomic modesetting support: {}", __FUNCTION__
,
222 m_atomicRequestQueue
.emplace_back(std::make_unique
<CDRMAtomicRequest
>());
223 m_req
= m_atomicRequestQueue
.back().get();
225 if (!CDRMUtils::InitDrm())
228 for (auto& plane
: m_planes
)
230 AddProperty(plane
.get(), "FB_ID", 0);
231 AddProperty(plane
.get(), "CRTC_ID", 0);
234 CLog::Log(LOGDEBUG
, "CDRMAtomic::{} - initialized atomic DRM", __FUNCTION__
);
236 //! @todo: disabled until upstream kernel changes are merged
237 // if (m_gui_plane->SupportsProperty("SCALING_FILTER"))
239 // const std::shared_ptr<CSettings> settings =
240 // CServiceBroker::GetSettingsComponent()->GetSettings();
241 // settings->GetSetting(SETTING_VIDEOSCREEN_HW_SCALING_FILTER)->SetVisible(true);
247 void CDRMAtomic::DestroyDrm()
249 CDRMUtils::DestroyDrm();
252 bool CDRMAtomic::SetVideoMode(const RESOLUTION_INFO
& res
, struct gbm_bo
*bo
)
254 m_need_modeset
= true;
259 bool CDRMAtomic::SetActive(bool active
)
261 m_need_modeset
= true;
267 bool CDRMAtomic::AddProperty(CDRMObject
* object
, const char* name
, uint64_t value
)
269 return m_req
->AddProperty(object
, name
, value
);
272 bool CDRMAtomic::DisplayHardwareScalingEnabled()
274 auto settings
= CServiceBroker::GetSettingsComponent()->GetSettings();
276 if (settings
&& settings
->GetBool(SETTING_VIDEOSCREEN_HW_SCALING_FILTER
))
282 CDRMAtomic::CDRMAtomicRequest::CDRMAtomicRequest() : m_atomicRequest(drmModeAtomicAlloc())
286 bool CDRMAtomic::CDRMAtomicRequest::AddProperty(CDRMObject
* object
, const char* name
, uint64_t value
)
288 uint32_t propertyId
= object
->GetPropertyId(name
);
292 int ret
= drmModeAtomicAddProperty(m_atomicRequest
.get(), object
->GetId(), propertyId
, value
);
296 m_atomicRequestItems
[object
][propertyId
] = value
;
300 void CDRMAtomic::CDRMAtomicRequest::LogAtomicDiff(CDRMAtomicRequest
* current
,
301 CDRMAtomicRequest
* old
)
303 std::map
<CDRMObject
*, std::map
<uint32_t, uint64_t>> atomicDiff
;
305 for (const auto& object
: current
->m_atomicRequestItems
)
307 auto sameObject
= old
->m_atomicRequestItems
.find(object
.first
);
308 if (sameObject
!= old
->m_atomicRequestItems
.end())
310 std::map
<uint32_t, uint64_t> propertyDiff
;
312 std::set_difference(current
->m_atomicRequestItems
[object
.first
].begin(),
313 current
->m_atomicRequestItems
[object
.first
].end(),
314 old
->m_atomicRequestItems
[object
.first
].begin(),
315 old
->m_atomicRequestItems
[object
.first
].end(),
316 std::inserter(propertyDiff
, propertyDiff
.begin()));
318 atomicDiff
[object
.first
] = propertyDiff
;
322 atomicDiff
[object
.first
] = current
->m_atomicRequestItems
[object
.first
];
326 CLog::Log(LOGDEBUG
, "CDRMAtomicRequest::{} - DRM Atomic Request Diff:", __FUNCTION__
);
328 LogAtomicRequest(LOGERROR
, atomicDiff
);
331 void CDRMAtomic::CDRMAtomicRequest::LogAtomicRequest()
333 CLog::Log(LOGDEBUG
, "CDRMAtomicRequest::{} - DRM Atomic Request:", __FUNCTION__
);
334 LogAtomicRequest(LOGDEBUG
, m_atomicRequestItems
);
337 void CDRMAtomic::CDRMAtomicRequest::LogAtomicRequest(
338 uint8_t logLevel
, std::map
<CDRMObject
*, std::map
<uint32_t, uint64_t>>& atomicRequestItems
)
341 for (const auto& object
: atomicRequestItems
)
343 message
.append("\nObject: " + object
.first
->GetTypeName() +
344 "\tID: " + std::to_string(object
.first
->GetId()));
345 for (const auto& property
: object
.second
)
346 message
.append("\n Property: " + object
.first
->GetPropertyName(property
.first
) +
347 "\tID: " + std::to_string(property
.first
) +
348 "\tValue: " + std::to_string(property
.second
));
351 CLog::Log(logLevel
, "{}", message
);
354 void CDRMAtomic::CDRMAtomicRequest::DrmModeAtomicReqDeleter::operator()(drmModeAtomicReqPtr p
) const
356 drmModeAtomicFree(p
);