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__
,
154 if (m_inFenceFd
!= -1)
160 if (flags
& DRM_MODE_ATOMIC_ALLOW_MODESET
)
162 if (drmModeDestroyPropertyBlob(m_fd
, blob_id
) != 0)
163 CLog::Log(LOGERROR
, "CDRMAtomic::{} - failed to destroy property blob: {}", __FUNCTION__
,
167 if (m_atomicRequestQueue
.size() > 1)
168 m_atomicRequestQueue
.pop_back();
170 m_atomicRequestQueue
.emplace_back(std::make_unique
<CDRMAtomicRequest
>());
171 m_req
= m_atomicRequestQueue
.back().get();
174 void CDRMAtomic::FlipPage(struct gbm_bo
* bo
, bool rendered
, bool videoLayer
, bool async
)
176 struct drm_fb
*drm_fb
= nullptr;
182 m_gui_plane
->SetFormat(CDRMUtils::FourCCWithAlpha(m_gui_plane
->GetFormat()));
184 m_gui_plane
->SetFormat(CDRMUtils::FourCCWithoutAlpha(m_gui_plane
->GetFormat()));
186 drm_fb
= CDRMUtils::DrmFbGetFromBo(bo
);
189 CLog::Log(LOGERROR
, "CDRMAtomic::{} - Failed to get a new FBO", __FUNCTION__
);
193 if (async
&& !m_need_modeset
)
194 flags
|= DRM_MODE_ATOMIC_NONBLOCK
;
199 flags
|= DRM_MODE_ATOMIC_ALLOW_MODESET
;
200 m_need_modeset
= false;
201 CLog::Log(LOGDEBUG
, "CDRMAtomic::{} - Execute modeset at next commit", __FUNCTION__
);
204 DrmAtomicCommit(!drm_fb
? 0 : drm_fb
->fb_id
, flags
, rendered
, videoLayer
);
207 bool CDRMAtomic::InitDrm()
209 if (!CDRMUtils::OpenDrm(true))
212 auto ret
= drmSetClientCap(m_fd
, DRM_CLIENT_CAP_ATOMIC
, 1);
215 CLog::Log(LOGERROR
, "CDRMAtomic::{} - no atomic modesetting support: {}", __FUNCTION__
,
220 m_atomicRequestQueue
.emplace_back(std::make_unique
<CDRMAtomicRequest
>());
221 m_req
= m_atomicRequestQueue
.back().get();
223 if (!CDRMUtils::InitDrm())
226 for (auto& plane
: m_planes
)
228 AddProperty(plane
.get(), "FB_ID", 0);
229 AddProperty(plane
.get(), "CRTC_ID", 0);
232 CLog::Log(LOGDEBUG
, "CDRMAtomic::{} - initialized atomic DRM", __FUNCTION__
);
234 //! @todo: disabled until upstream kernel changes are merged
235 // if (m_gui_plane->SupportsProperty("SCALING_FILTER"))
237 // const std::shared_ptr<CSettings> settings =
238 // CServiceBroker::GetSettingsComponent()->GetSettings();
239 // settings->GetSetting(SETTING_VIDEOSCREEN_HW_SCALING_FILTER)->SetVisible(true);
245 void CDRMAtomic::DestroyDrm()
247 CDRMUtils::DestroyDrm();
250 bool CDRMAtomic::SetVideoMode(const RESOLUTION_INFO
& res
, struct gbm_bo
*bo
)
252 m_need_modeset
= true;
257 bool CDRMAtomic::SetActive(bool active
)
259 m_need_modeset
= true;
265 bool CDRMAtomic::AddProperty(CDRMObject
* object
, const char* name
, uint64_t value
)
267 return m_req
->AddProperty(object
, name
, value
);
270 bool CDRMAtomic::DisplayHardwareScalingEnabled()
272 auto settings
= CServiceBroker::GetSettingsComponent()->GetSettings();
274 if (settings
&& settings
->GetBool(SETTING_VIDEOSCREEN_HW_SCALING_FILTER
))
280 CDRMAtomic::CDRMAtomicRequest::CDRMAtomicRequest() : m_atomicRequest(drmModeAtomicAlloc())
284 bool CDRMAtomic::CDRMAtomicRequest::AddProperty(CDRMObject
* object
, const char* name
, uint64_t value
)
286 uint32_t propertyId
= object
->GetPropertyId(name
);
290 int ret
= drmModeAtomicAddProperty(m_atomicRequest
.get(), object
->GetId(), propertyId
, value
);
294 m_atomicRequestItems
[object
][propertyId
] = value
;
298 void CDRMAtomic::CDRMAtomicRequest::LogAtomicDiff(CDRMAtomicRequest
* current
,
299 CDRMAtomicRequest
* old
)
301 std::map
<CDRMObject
*, std::map
<uint32_t, uint64_t>> atomicDiff
;
303 for (const auto& object
: current
->m_atomicRequestItems
)
305 auto sameObject
= old
->m_atomicRequestItems
.find(object
.first
);
306 if (sameObject
!= old
->m_atomicRequestItems
.end())
308 std::map
<uint32_t, uint64_t> propertyDiff
;
310 std::set_difference(current
->m_atomicRequestItems
[object
.first
].begin(),
311 current
->m_atomicRequestItems
[object
.first
].end(),
312 old
->m_atomicRequestItems
[object
.first
].begin(),
313 old
->m_atomicRequestItems
[object
.first
].end(),
314 std::inserter(propertyDiff
, propertyDiff
.begin()));
316 atomicDiff
[object
.first
] = propertyDiff
;
320 atomicDiff
[object
.first
] = current
->m_atomicRequestItems
[object
.first
];
324 CLog::Log(LOGDEBUG
, "CDRMAtomicRequest::{} - DRM Atomic Request Diff:", __FUNCTION__
);
326 LogAtomicRequest(LOGERROR
, atomicDiff
);
329 void CDRMAtomic::CDRMAtomicRequest::LogAtomicRequest()
331 CLog::Log(LOGDEBUG
, "CDRMAtomicRequest::{} - DRM Atomic Request:", __FUNCTION__
);
332 LogAtomicRequest(LOGDEBUG
, m_atomicRequestItems
);
335 void CDRMAtomic::CDRMAtomicRequest::LogAtomicRequest(
336 uint8_t logLevel
, std::map
<CDRMObject
*, std::map
<uint32_t, uint64_t>>& atomicRequestItems
)
339 for (const auto& object
: atomicRequestItems
)
341 message
.append("\nObject: " + object
.first
->GetTypeName() +
342 "\tID: " + std::to_string(object
.first
->GetId()));
343 for (const auto& property
: object
.second
)
344 message
.append("\n Property: " + object
.first
->GetPropertyName(property
.first
) +
345 "\tID: " + std::to_string(property
.first
) +
346 "\tValue: " + std::to_string(property
.second
));
349 CLog::Log(logLevel
, "{}", message
);
352 void CDRMAtomic::CDRMAtomicRequest::DrmModeAtomicReqDeleter::operator()(drmModeAtomicReqPtr p
) const
354 drmModeAtomicFree(p
);