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.
9 #include "WinSystemGbm.h"
11 #include "GBMDPMSSupport.h"
12 #include "OptionalsReg.h"
13 #include "ServiceBroker.h"
14 #include "VideoSyncGbm.h"
15 #include "cores/VideoPlayer/Buffers/VideoBufferDRMPRIME.h"
16 #include "cores/VideoPlayer/VideoReferenceClock.h"
17 #include "drm/DRMAtomic.h"
18 #include "drm/DRMLegacy.h"
19 #include "drm/OffScreenModeSetting.h"
20 #include "messaging/ApplicationMessenger.h"
21 #include "settings/DisplaySettings.h"
22 #include "settings/Settings.h"
23 #include "settings/SettingsComponent.h"
24 #include "settings/lib/Setting.h"
25 #include "utils/DisplayInfo.h"
26 #include "utils/Map.h"
27 #include "utils/StringUtils.h"
28 #include "utils/log.h"
29 #include "windowing/GraphicContext.h"
34 #ifndef HAVE_HDR_OUTPUT_METADATA
35 // HDR structs is copied from linux include/linux/hdmi.h
36 struct hdr_metadata_infoframe
39 uint8_t metadata_type
;
43 } display_primaries
[3];
48 uint16_t max_display_mastering_luminance
;
49 uint16_t min_display_mastering_luminance
;
53 struct hdr_output_metadata
55 uint32_t metadata_type
;
58 struct hdr_metadata_infoframe hdmi_metadata_type1
;
63 using namespace KODI::WINDOWING::GBM
;
65 using namespace std::chrono_literals
;
70 // These map to the definitions in the linux kernel
71 // drivers/gpu/drm/drm_connector.c
73 constexpr auto ColorimetryMap
= make_map
<KODI::UTILS::Colorimetry
, std::string_view
>({
74 {KODI::UTILS::Colorimetry::DEFAULT
, "Default"},
75 {KODI::UTILS::Colorimetry::XVYCC_601
, "XVYCC_601"},
76 {KODI::UTILS::Colorimetry::XVYCC_709
, "XVYCC_709"},
77 {KODI::UTILS::Colorimetry::SYCC_601
, "SYCC_601"},
78 {KODI::UTILS::Colorimetry::OPYCC_601
, "opYCC_601"},
79 {KODI::UTILS::Colorimetry::OPRGB
, "opRGB"},
80 {KODI::UTILS::Colorimetry::BT2020_CYCC
, "BT2020_CYCC"},
81 {KODI::UTILS::Colorimetry::BT2020_YCC
, "BT2020_YCC"},
82 {KODI::UTILS::Colorimetry::BT2020_RGB
, "BT2020_RGB"},
83 {KODI::UTILS::Colorimetry::ST2113_RGB
, "Default"},
84 {KODI::UTILS::Colorimetry::ICTCP
, "Default"},
88 CWinSystemGbm::CWinSystemGbm() :
91 m_libinput(new CLibInputHandler
)
93 m_dpms
= std::make_shared
<CGBMDPMSSupport
>();
97 CWinSystemGbm::~CWinSystemGbm() = default;
99 bool CWinSystemGbm::InitWindowSystem()
101 const char* x11
= getenv("DISPLAY");
102 const char* wayland
= getenv("WAYLAND_DISPLAY");
105 CLog::Log(LOGDEBUG
, "CWinSystemGbm::{} - not allowed to run GBM under a window manager",
110 m_DRM
= std::make_shared
<CDRMAtomic
>();
112 if (!m_DRM
->InitDrm())
114 CLog::Log(LOGERROR
, "CWinSystemGbm::{} - failed to initialize Atomic DRM", __FUNCTION__
);
117 m_DRM
= std::make_shared
<CDRMLegacy
>();
119 if (!m_DRM
->InitDrm())
121 CLog::Log(LOGERROR
, "CWinSystemGbm::{} - failed to initialize Legacy DRM", __FUNCTION__
);
124 m_DRM
= std::make_shared
<COffScreenModeSetting
>();
125 if (!m_DRM
->InitDrm())
127 CLog::Log(LOGERROR
, "CWinSystemGbm::{} - failed to initialize off screen DRM",
135 CDRMConnector
* connector
= m_DRM
->GetConnector();
138 std::vector
<uint8_t> edid
= connector
->GetEDID();
141 m_info
= UTILS::CDisplayInfo::Create(edid
);
145 if (!m_GBM
->CreateDevice(m_DRM
->GetFileDescriptor()))
151 auto settingsComponent
= CServiceBroker::GetSettingsComponent();
152 if (!settingsComponent
)
155 auto settings
= settingsComponent
->GetSettings();
159 auto setting
= settings
->GetSetting(CSettings::SETTING_VIDEOSCREEN_LIMITEDRANGE
);
161 setting
->SetVisible(true);
163 setting
= settings
->GetSetting("videoscreen.limitguisize");
165 setting
->SetVisible(true);
167 CLog::Log(LOGDEBUG
, "CWinSystemGbm::{} - initialized DRM", __FUNCTION__
);
168 return CWinSystemBase::InitWindowSystem();
171 bool CWinSystemGbm::DestroyWindowSystem()
173 CLog::Log(LOGDEBUG
, "CWinSystemGbm::{} - deinitialized DRM", __FUNCTION__
);
180 void CWinSystemGbm::UpdateResolutions()
182 RESOLUTION_INFO current
= m_DRM
->GetCurrentMode();
184 auto resolutions
= m_DRM
->GetModes();
185 if (resolutions
.empty())
187 CDisplaySettings::GetInstance().ClearCustomResolutions();
189 CServiceBroker::GetWinSystem()->GetGfxContext().ResetOverscan(current
);
190 CDisplaySettings::GetInstance().AddResolutionInfo(current
);
191 CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP
) = current
;
193 CLog::Log(LOGINFO
, "Found resolution {}x{} with {}x{}{} @ {:f} Hz", current
.iWidth
,
194 current
.iHeight
, current
.iScreenWidth
, current
.iScreenHeight
,
195 current
.dwFlags
& D3DPRESENTFLAG_INTERLACED
? "i" : "", current
.fRefreshRate
);
199 CDisplaySettings::GetInstance().ClearCustomResolutions();
201 for (auto &res
: resolutions
)
203 CServiceBroker::GetWinSystem()->GetGfxContext().ResetOverscan(res
);
204 CDisplaySettings::GetInstance().AddResolutionInfo(res
);
206 if (current
.iScreenWidth
== res
.iScreenWidth
&&
207 current
.iScreenHeight
== res
.iScreenHeight
&&
208 current
.iWidth
== res
.iWidth
&&
209 current
.iHeight
== res
.iHeight
&&
210 current
.fRefreshRate
== res
.fRefreshRate
&&
211 current
.dwFlags
== res
.dwFlags
)
213 CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP
) = res
;
216 CLog::Log(LOGINFO
, "Found resolution {}x{} with {}x{}{} @ {:f} Hz", res
.iWidth
, res
.iHeight
,
217 res
.iScreenWidth
, res
.iScreenHeight
,
218 res
.dwFlags
& D3DPRESENTFLAG_INTERLACED
? "i" : "", res
.fRefreshRate
);
222 CDisplaySettings::GetInstance().ApplyCalibrations();
225 bool CWinSystemGbm::ResizeWindow(int newWidth
, int newHeight
, int newLeft
, int newTop
)
230 bool CWinSystemGbm::SetFullScreen(bool fullScreen
, RESOLUTION_INFO
& res
, bool blankOtherDisplays
)
232 // Notify other subsystems that we will change resolution
235 if(!m_DRM
->SetMode(res
))
237 CLog::Log(LOGERROR
, "CWinSystemGbm::{} - failed to set DRM mode", __FUNCTION__
);
241 struct gbm_bo
*bo
= nullptr;
243 if (!std::dynamic_pointer_cast
<CDRMAtomic
>(m_DRM
))
245 bo
= m_GBM
->GetDevice().GetSurface().LockFrontBuffer().Get();
248 auto result
= m_DRM
->SetVideoMode(res
, bo
);
251 std::chrono::milliseconds(CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
252 "videoscreen.delayrefreshchange") *
255 m_dispResetTimer
.Set(delay
);
260 bool CWinSystemGbm::DisplayHardwareScalingEnabled()
262 auto drmAtomic
= std::dynamic_pointer_cast
<CDRMAtomic
>(m_DRM
);
263 if (drmAtomic
&& drmAtomic
->DisplayHardwareScalingEnabled())
269 void CWinSystemGbm::UpdateDisplayHardwareScaling(const RESOLUTION_INFO
& resInfo
)
271 if (!DisplayHardwareScalingEnabled())
274 //! @todo The PR that made the res struct constant was abandoned due to drama.
275 // It should be const-corrected and changed here.
276 RESOLUTION_INFO
& resMutable
= const_cast<RESOLUTION_INFO
&>(resInfo
);
278 SetFullScreen(true, resMutable
, false);
281 void CWinSystemGbm::FlipPage(bool rendered
, bool videoLayer
, bool async
)
283 if (m_videoLayerBridge
&& !videoLayer
)
285 // disable video plane when video layer no longer is active
286 m_videoLayerBridge
->Disable();
289 struct gbm_bo
*bo
= nullptr;
293 bo
= m_GBM
->GetDevice().GetSurface().LockFrontBuffer().Get();
296 m_DRM
->FlipPage(bo
, rendered
, videoLayer
, async
);
298 if (m_videoLayerBridge
&& !videoLayer
)
300 // delete video layer bridge when video layer no longer is active
301 m_videoLayerBridge
.reset();
305 bool CWinSystemGbm::UseLimitedColor()
307 return CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_VIDEOSCREEN_LIMITEDRANGE
);
310 bool CWinSystemGbm::Hide()
312 bool ret
= m_DRM
->SetActive(false);
313 FlipPage(false, false, false);
317 bool CWinSystemGbm::Show(bool raise
)
319 bool ret
= m_DRM
->SetActive(true);
320 FlipPage(false, false, false);
324 void CWinSystemGbm::Register(IDispResource
*resource
)
326 std::unique_lock
<CCriticalSection
> lock(m_resourceSection
);
327 m_resources
.push_back(resource
);
330 void CWinSystemGbm::Unregister(IDispResource
*resource
)
332 std::unique_lock
<CCriticalSection
> lock(m_resourceSection
);
333 std::vector
<IDispResource
*>::iterator i
= find(m_resources
.begin(), m_resources
.end(), resource
);
334 if (i
!= m_resources
.end())
336 m_resources
.erase(i
);
340 void CWinSystemGbm::OnLostDevice()
342 CLog::Log(LOGDEBUG
, "{} - notify display change event", __FUNCTION__
);
345 std::unique_lock
<CCriticalSection
> lock(m_resourceSection
);
346 for (auto resource
: m_resources
)
347 resource
->OnLostDisplay();
350 std::unique_ptr
<CVideoSync
> CWinSystemGbm::GetVideoSync(CVideoReferenceClock
* clock
)
352 return std::make_unique
<CVideoSyncGbm
>(clock
);
355 std::vector
<std::string
> CWinSystemGbm::GetConnectedOutputs()
357 return m_DRM
->GetConnectedConnectorNames();
360 bool CWinSystemGbm::SetHDR(const VideoPicture
* videoPicture
)
362 auto settingsComponent
= CServiceBroker::GetSettingsComponent();
363 if (!settingsComponent
)
366 auto settings
= settingsComponent
->GetSettings();
370 if (!settings
->GetBool(SETTING_WINSYSTEM_IS_HDR_DISPLAY
))
373 auto drm
= std::dynamic_pointer_cast
<CDRMAtomic
>(m_DRM
);
377 auto connector
= drm
->GetConnector();
383 if (connector
->SupportsProperty("Colorspace"))
385 std::optional
<uint64_t> colorspace
= connector
->GetPropertyValue("Colorspace", "Default");
388 CLog::LogF(LOGDEBUG
, "setting connector colorspace to Default");
389 drm
->AddProperty(connector
, "Colorspace", colorspace
.value());
390 drm
->SetActive(true);
394 if (connector
->SupportsProperty("HDR_OUTPUT_METADATA"))
396 drm
->AddProperty(connector
, "HDR_OUTPUT_METADATA", 0);
397 drm
->SetActive(true);
400 drmModeDestroyPropertyBlob(drm
->GetFileDescriptor(), m_hdr_blob_id
);
407 KODI::UTILS::Colorimetry colorimetry
= DRMPRIME::GetColorimetry(*videoPicture
);
409 if (connector
->SupportsProperty("Colorspace") && m_info
&&
410 m_info
->SupportsColorimetry(colorimetry
))
412 std::optional
<uint64_t> colorspace
=
413 connector
->GetPropertyValue("Colorspace", ColorimetryMap
.at(colorimetry
));
416 CLog::LogF(LOGDEBUG
, "setting connector colorspace to {}", ColorimetryMap
.at(colorimetry
));
417 drm
->AddProperty(connector
, "Colorspace", colorspace
.value());
418 drm
->SetActive(true);
422 KODI::UTILS::Eotf eotf
= DRMPRIME::GetEOTF(*videoPicture
);
424 if (connector
->SupportsProperty("HDR_OUTPUT_METADATA") && m_info
&&
425 m_info
->SupportsHDRStaticMetadataType1() && m_info
->SupportsEOTF(eotf
))
427 hdr_output_metadata hdr_metadata
= {};
429 hdr_metadata
.metadata_type
= DRMPRIME::HDMI_STATIC_METADATA_TYPE1
;
430 hdr_metadata
.hdmi_metadata_type1
.eotf
= static_cast<uint8_t>(eotf
);
431 hdr_metadata
.hdmi_metadata_type1
.metadata_type
= DRMPRIME::HDMI_STATIC_METADATA_TYPE1
;
434 drmModeDestroyPropertyBlob(drm
->GetFileDescriptor(), m_hdr_blob_id
);
437 if (hdr_metadata
.hdmi_metadata_type1
.eotf
)
439 const AVMasteringDisplayMetadata
* mdmd
= DRMPRIME::GetMasteringDisplayMetadata(*videoPicture
);
440 if (mdmd
&& mdmd
->has_primaries
)
442 // Convert to unsigned 16-bit values in units of 0.00002,
443 // where 0x0000 represents zero and 0xC350 represents 1.0000
444 for (int i
= 0; i
< 3; i
++)
446 hdr_metadata
.hdmi_metadata_type1
.display_primaries
[i
].x
=
447 std::round(av_q2d(mdmd
->display_primaries
[i
][0]) * 50000.0);
448 hdr_metadata
.hdmi_metadata_type1
.display_primaries
[i
].y
=
449 std::round(av_q2d(mdmd
->display_primaries
[i
][1]) * 50000.0);
451 CLog::Log(LOGDEBUG
, LOGVIDEO
, "CWinSystemGbm::{} - display_primaries[{}].x: {}",
452 __FUNCTION__
, i
, hdr_metadata
.hdmi_metadata_type1
.display_primaries
[i
].x
);
453 CLog::Log(LOGDEBUG
, LOGVIDEO
, "CWinSystemGbm::{} - display_primaries[{}].y: {}",
454 __FUNCTION__
, i
, hdr_metadata
.hdmi_metadata_type1
.display_primaries
[i
].y
);
456 hdr_metadata
.hdmi_metadata_type1
.white_point
.x
=
457 std::round(av_q2d(mdmd
->white_point
[0]) * 50000.0);
458 hdr_metadata
.hdmi_metadata_type1
.white_point
.y
=
459 std::round(av_q2d(mdmd
->white_point
[1]) * 50000.0);
461 CLog::Log(LOGDEBUG
, LOGVIDEO
, "CWinSystemGbm::{} - white_point.x: {}", __FUNCTION__
,
462 hdr_metadata
.hdmi_metadata_type1
.white_point
.x
);
463 CLog::Log(LOGDEBUG
, LOGVIDEO
, "CWinSystemGbm::{} - white_point.y: {}", __FUNCTION__
,
464 hdr_metadata
.hdmi_metadata_type1
.white_point
.y
);
466 if (mdmd
&& mdmd
->has_luminance
)
468 // Convert to unsigned 16-bit value in units of 1 cd/m2,
469 // where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2
470 hdr_metadata
.hdmi_metadata_type1
.max_display_mastering_luminance
=
471 std::round(av_q2d(mdmd
->max_luminance
));
473 // Convert to unsigned 16-bit value in units of 0.0001 cd/m2,
474 // where 0x0001 represents 0.0001 cd/m2 and 0xFFFF represents 6.5535 cd/m2
475 hdr_metadata
.hdmi_metadata_type1
.min_display_mastering_luminance
=
476 std::round(av_q2d(mdmd
->min_luminance
) * 10000.0);
478 CLog::Log(LOGDEBUG
, LOGVIDEO
, "CWinSystemGbm::{} - max_display_mastering_luminance: {}",
479 __FUNCTION__
, hdr_metadata
.hdmi_metadata_type1
.max_display_mastering_luminance
);
480 CLog::Log(LOGDEBUG
, LOGVIDEO
, "CWinSystemGbm::{} - min_display_mastering_luminance: {}",
481 __FUNCTION__
, hdr_metadata
.hdmi_metadata_type1
.min_display_mastering_luminance
);
484 const AVContentLightMetadata
* clmd
= DRMPRIME::GetContentLightMetadata(*videoPicture
);
487 hdr_metadata
.hdmi_metadata_type1
.max_cll
= clmd
->MaxCLL
;
488 hdr_metadata
.hdmi_metadata_type1
.max_fall
= clmd
->MaxFALL
;
490 CLog::Log(LOGDEBUG
, LOGVIDEO
, "CWinSystemGbm::{} - max_cll: {}", __FUNCTION__
,
491 hdr_metadata
.hdmi_metadata_type1
.max_cll
);
492 CLog::Log(LOGDEBUG
, LOGVIDEO
, "CWinSystemGbm::{} - max_fall: {}", __FUNCTION__
,
493 hdr_metadata
.hdmi_metadata_type1
.max_fall
);
496 drmModeCreatePropertyBlob(drm
->GetFileDescriptor(), &hdr_metadata
, sizeof(hdr_metadata
),
500 drm
->AddProperty(connector
, "HDR_OUTPUT_METADATA", m_hdr_blob_id
);
501 drm
->SetActive(true);
504 return m_hdr_blob_id
!= 0;
507 bool CWinSystemGbm::IsHDRDisplay()
509 auto drm
= std::dynamic_pointer_cast
<CDRMAtomic
>(m_DRM
);
513 auto connector
= drm
->GetConnector();
517 return connector
->SupportsProperty("HDR_OUTPUT_METADATA") && m_info
&&
518 m_info
->SupportsHDRStaticMetadataType1();
521 CHDRCapabilities
CWinSystemGbm::GetDisplayHDRCapabilities() const
526 CHDRCapabilities caps
;
528 if (m_info
->SupportsEOTF(UTILS::Eotf::PQ
))
531 if (m_info
->SupportsEOTF(UTILS::Eotf::HLG
))