Merge pull request #26273 from 78andyp/blurayfixes2
[xbmc.git] / xbmc / windowing / gbm / drm / DRMAtomic.cpp
blob70ae92e9482dc179a8fa2d334294bb3ebf4cc82d
1 /*
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.
7 */
9 #include "DRMAtomic.h"
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"
19 #include <errno.h>
20 #include <string.h>
22 #include <drm_fourcc.h>
23 #include <drm_mode.h>
24 #include <unistd.h>
26 using namespace KODI::WINDOWING::GBM;
28 namespace
31 const auto SETTING_VIDEOSCREEN_HW_SCALING_FILTER = "videoscreen.hwscalingfilter";
33 uint32_t GetScalingFactor(uint32_t srcWidth,
34 uint32_t srcHeight,
35 uint32_t destWidth,
36 uint32_t destHeight)
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;
42 return factor_W;
45 } // namespace
47 bool CDRMAtomic::SetScalingFilter(CDRMObject* object, const char* name, const char* type)
49 std::optional<uint64_t> scalingFilter = m_gui_plane->GetPropertyValue(name, type);
50 if (!scalingFilter)
51 return false;
53 if (!AddProperty(object, name, scalingFilter.value()))
54 return false;
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));
61 return true;
64 void CDRMAtomic::DrmAtomicCommit(int fb_id, int flags, bool rendered, bool videoLayer)
66 uint32_t blob_id;
68 if (flags & DRM_MODE_ATOMIC_ALLOW_MODESET)
70 if (!AddProperty(m_connector, "CRTC_ID", m_crtc->GetCrtcId()))
71 return;
73 if (drmModeCreatePropertyBlob(m_fd, m_mode, sizeof(*m_mode), &blob_id) != 0)
74 return;
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))
80 return;
82 if (!AddProperty(m_orig_crtc, "ACTIVE", 0))
83 return;
86 if (!AddProperty(m_crtc, "MODE_ID", blob_id))
87 return;
89 if (!AddProperty(m_crtc, "ACTIVE", m_active ? 1 : 0))
90 return;
93 if (rendered)
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())
105 // {
106 // SetScalingFilter(m_gui_plane, "SCALING_FILTER", "Nearest Neighbor");
107 // }
108 // else
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);
131 if (ret < 0)
133 CLog::Log(LOGERROR,
134 "CDRMAtomic::{} - test commit failed: ({}) - falling back to last successful atomic "
135 "request",
136 __FUNCTION__, strerror(errno));
138 auto oldRequest = m_atomicRequestQueue.front().get();
139 CDRMAtomicRequest::LogAtomicDiff(m_req, oldRequest);
140 m_req = oldRequest;
142 // update the old atomic request with the new fb id to avoid tearing
143 if (rendered)
144 AddProperty(m_gui_plane, "FB_ID", fb_id);
147 ret = drmModeAtomicCommit(m_fd, m_req->Get(), flags, nullptr);
148 if (ret < 0)
150 CLog::Log(LOGERROR, "CDRMAtomic::{} - atomic commit failed: {}", __FUNCTION__,
151 strerror(errno));
152 m_atomicRequestQueue.pop_back();
154 else if (m_atomicRequestQueue.size() > 1)
156 m_atomicRequestQueue.pop_front();
159 if (m_inFenceFd != -1)
161 close(m_inFenceFd);
162 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__,
169 strerror(errno));
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;
179 uint32_t flags = 0;
181 if (rendered)
183 if (videoLayer)
184 m_gui_plane->SetFormat(CDRMUtils::FourCCWithAlpha(m_gui_plane->GetFormat()));
185 else
186 m_gui_plane->SetFormat(CDRMUtils::FourCCWithoutAlpha(m_gui_plane->GetFormat()));
188 drm_fb = CDRMUtils::DrmFbGetFromBo(bo);
189 if (!drm_fb)
191 CLog::Log(LOGERROR, "CDRMAtomic::{} - Failed to get a new FBO", __FUNCTION__);
192 return;
195 if (async && !m_need_modeset)
196 flags |= DRM_MODE_ATOMIC_NONBLOCK;
199 if (m_need_modeset)
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))
212 return false;
214 auto ret = drmSetClientCap(m_fd, DRM_CLIENT_CAP_ATOMIC, 1);
215 if (ret)
217 CLog::Log(LOGERROR, "CDRMAtomic::{} - no atomic modesetting support: {}", __FUNCTION__,
218 strerror(errno));
219 return false;
222 m_atomicRequestQueue.emplace_back(std::make_unique<CDRMAtomicRequest>());
223 m_req = m_atomicRequestQueue.back().get();
225 if (!CDRMUtils::InitDrm())
226 return false;
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"))
238 // {
239 // const std::shared_ptr<CSettings> settings =
240 // CServiceBroker::GetSettingsComponent()->GetSettings();
241 // settings->GetSetting(SETTING_VIDEOSCREEN_HW_SCALING_FILTER)->SetVisible(true);
242 // }
244 return 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;
256 return true;
259 bool CDRMAtomic::SetActive(bool active)
261 m_need_modeset = true;
262 m_active = active;
264 return 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))
277 return true;
279 return false;
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);
289 if (propertyId == 0)
290 return false;
292 int ret = drmModeAtomicAddProperty(m_atomicRequest.get(), object->GetId(), propertyId, value);
293 if (ret < 0)
294 return false;
296 m_atomicRequestItems[object][propertyId] = value;
297 return true;
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;
320 else
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)
340 std::string message;
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);