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 "Resolution.h"
11 #include "GraphicContext.h"
12 #include "ServiceBroker.h"
13 #include "settings/AdvancedSettings.h"
14 #include "settings/DisplaySettings.h"
15 #include "settings/Settings.h"
16 #include "settings/SettingsComponent.h"
17 #include "utils/MathUtils.h"
18 #include "utils/Variant.h"
19 #include "utils/log.h"
27 const char* SETTING_VIDEOSCREEN_WHITELIST_PULLDOWN
{"videoscreen.whitelistpulldown"};
28 const char* SETTING_VIDEOSCREEN_WHITELIST_DOUBLEREFRESHRATE
{
29 "videoscreen.whitelistdoublerefreshrate"};
33 EdgeInsets::EdgeInsets(float l
, float t
, float r
, float b
) : left(l
), top(t
), right(r
), bottom(b
)
37 RESOLUTION_INFO::RESOLUTION_INFO(int width
, int height
, float aspect
, const std::string
&mode
) :
44 iScreenHeight
= height
;
45 fPixelRatio
= aspect
? ((float)width
)/height
/ aspect
: 1.0f
;
48 dwFlags
= iSubtitles
= 0;
51 RESOLUTION_INFO::RESOLUTION_INFO(const RESOLUTION_INFO
& res
)
52 : Overscan(res
.Overscan
),
53 guiInsets(res
.guiInsets
),
55 strOutput(res
.strOutput
),
58 bFullScreen
= res
.bFullScreen
;
59 iWidth
= res
.iWidth
; iHeight
= res
.iHeight
;
60 iScreenWidth
= res
.iScreenWidth
; iScreenHeight
= res
.iScreenHeight
;
61 iSubtitles
= res
.iSubtitles
; dwFlags
= res
.dwFlags
;
62 fPixelRatio
= res
.fPixelRatio
; fRefreshRate
= res
.fRefreshRate
;
63 iBlanking
= res
.iBlanking
;
66 float RESOLUTION_INFO::DisplayRatio() const
68 return iWidth
* fPixelRatio
/ iHeight
;
71 RESOLUTION
CResolutionUtils::ChooseBestResolution(float fps
, int width
, int height
, bool is3D
)
73 RESOLUTION res
= CServiceBroker::GetWinSystem()->GetGfxContext().GetVideoResolution();
76 if (!FindResolutionFromOverride(fps
, width
, is3D
, res
, weight
, false)) //find a refreshrate from overrides
78 if (!FindResolutionFromOverride(fps
, width
, is3D
, res
, weight
, true)) //if that fails find it from a fallback
80 FindResolutionFromWhitelist(fps
, width
, height
, is3D
, res
); //find a refreshrate from whitelist
84 CLog::Log(LOGINFO
, "Display resolution ADJUST : {} ({}) (weight: {:.3f})",
85 CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo(res
).strMode
, res
, weight
);
89 void CResolutionUtils::FindResolutionFromWhitelist(float fps
, int width
, int height
, bool is3D
, RESOLUTION
&resolution
)
91 RESOLUTION_INFO curr
= CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo(resolution
);
93 "[WHITELIST] Searching the whitelist for: width: {}, height: {}, fps: {:0.3f}, 3D: {}",
94 width
, height
, fps
, is3D
? "true" : "false");
96 std::vector
<CVariant
> indexList
= CServiceBroker::GetSettingsComponent()->GetSettings()->GetList(CSettings::SETTING_VIDEOSCREEN_WHITELIST
);
98 bool noWhiteList
= indexList
.empty();
103 "[WHITELIST] Using the default whitelist because the user whitelist is empty");
104 std::vector
<RESOLUTION
> candidates
;
105 RESOLUTION_INFO info
;
106 std::string resString
;
107 CServiceBroker::GetWinSystem()->GetGfxContext().GetAllowedResolutions(candidates
);
108 for (const auto& c
: candidates
)
110 info
= CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo(c
);
111 if (info
.iScreenHeight
>= curr
.iScreenHeight
&& info
.iScreenWidth
>= curr
.iScreenWidth
&&
112 (info
.dwFlags
& D3DPRESENTFLAG_MODEMASK
) == (curr
.dwFlags
& D3DPRESENTFLAG_MODEMASK
))
114 // do not add half refreshrates (25, 29.97 by default) as kodi cannot cope with
115 // them on playback start. Especially interlaced content is not properly detected
116 // and this causes ugly double switching.
117 // This won't allow 25p / 30p playback on native refreshrate by default
118 if ((info
.fRefreshRate
> 30) || (MathUtils::FloatEquals(info
.fRefreshRate
, 24.0f
, 0.1f
)))
120 resString
= CDisplaySettings::GetInstance().GetStringFromRes(c
);
121 indexList
.emplace_back(resString
);
127 CLog::Log(LOGDEBUG
, "[WHITELIST] Searching for an exact resolution with an exact refresh rate");
129 unsigned int penalty
= std::numeric_limits
<unsigned int>::max();
132 for (const auto& mode
: indexList
)
134 auto i
= CDisplaySettings::GetInstance().GetResFromString(mode
.asString());
135 const RESOLUTION_INFO info
= CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo(i
);
137 // allow resolutions that are exact and have the correct refresh rate
138 // allow macroblock alignment / padding errors (e.g. 1080 mod16 == 8)
139 if (((height
== info
.iScreenHeight
&& width
<= info
.iScreenWidth
+ 8) ||
140 (width
== info
.iScreenWidth
&& height
<= info
.iScreenHeight
+ 8)) &&
141 (info
.dwFlags
& D3DPRESENTFLAG_MODEMASK
) == (curr
.dwFlags
& D3DPRESENTFLAG_MODEMASK
) &&
142 MathUtils::FloatEquals(info
.fRefreshRate
, fps
, 0.01f
))
145 "[WHITELIST] Matched an exact resolution with an exact refresh rate {} ({})",
147 unsigned int pen
= abs(info
.iScreenHeight
- height
) + abs(info
.iScreenWidth
- width
);
158 CLog::Log(LOGDEBUG
, "[WHITELIST] No match for an exact resolution with an exact refresh rate");
160 if (noWhiteList
|| CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
161 SETTING_VIDEOSCREEN_WHITELIST_DOUBLEREFRESHRATE
))
164 "[WHITELIST] Searching for an exact resolution with double the refresh rate");
166 for (const auto& mode
: indexList
)
168 auto i
= CDisplaySettings::GetInstance().GetResFromString(mode
.asString());
169 const RESOLUTION_INFO info
= CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo(i
);
171 // allow resolutions that are exact and have double the refresh rate
172 // allow macroblock alignment / padding errors (e.g. 1080 mod16 == 8)
173 if (((height
== info
.iScreenHeight
&& width
<= info
.iScreenWidth
+ 8) ||
174 (width
== info
.iScreenWidth
&& height
<= info
.iScreenHeight
+ 8)) &&
175 (info
.dwFlags
& D3DPRESENTFLAG_MODEMASK
) == (curr
.dwFlags
& D3DPRESENTFLAG_MODEMASK
) &&
176 MathUtils::FloatEquals(info
.fRefreshRate
, fps
* 2, 0.01f
))
179 "[WHITELIST] Matched an exact resolution with double the refresh rate {} ({})",
181 unsigned int pen
= abs(info
.iScreenHeight
- height
) + abs(info
.iScreenWidth
- width
);
194 "[WHITELIST] No match for an exact resolution with double the refresh rate");
199 if (noWhiteList
|| CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
200 SETTING_VIDEOSCREEN_WHITELIST_PULLDOWN
))
203 "[WHITELIST] Searching for an exact resolution with a 3:2 pulldown refresh rate");
205 for (const auto& mode
: indexList
)
207 auto i
= CDisplaySettings::GetInstance().GetResFromString(mode
.asString());
208 const RESOLUTION_INFO info
= CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo(i
);
210 // allow resolutions that are exact and have 2.5 times the refresh rate
211 // allow macroblock alignment / padding errors (e.g. 1080 mod16 == 8)
212 if (((height
== info
.iScreenHeight
&& width
<= info
.iScreenWidth
+ 8) ||
213 (width
== info
.iScreenWidth
&& height
<= info
.iScreenHeight
+ 8)) &&
214 (info
.dwFlags
& D3DPRESENTFLAG_MODEMASK
) == (curr
.dwFlags
& D3DPRESENTFLAG_MODEMASK
) &&
215 MathUtils::FloatEquals(info
.fRefreshRate
, fps
* 2.5f
, 0.01f
))
219 "[WHITELIST] Matched an exact resolution with a 3:2 pulldown refresh rate {} ({})",
221 unsigned int pen
= abs(info
.iScreenHeight
- height
) + abs(info
.iScreenWidth
- width
);
233 CLog::Log(LOGDEBUG
, "[WHITELIST] No match for a resolution with a 3:2 pulldown refresh rate");
237 CLog::Log(LOGDEBUG
, "[WHITELIST] Searching for a desktop resolution with an exact refresh rate");
239 const RESOLUTION_INFO desktop_info
= CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo(CDisplaySettings::GetInstance().GetCurrentResolution());
241 for (const auto& mode
: indexList
)
243 auto i
= CDisplaySettings::GetInstance().GetResFromString(mode
.asString());
244 const RESOLUTION_INFO info
= CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo(i
);
246 // allow resolutions that are desktop resolution but have the correct refresh rate
247 if (info
.iScreenWidth
== desktop_info
.iScreenWidth
&&
248 (info
.dwFlags
& D3DPRESENTFLAG_MODEMASK
) == (desktop_info
.dwFlags
& D3DPRESENTFLAG_MODEMASK
) &&
249 MathUtils::FloatEquals(info
.fRefreshRate
, fps
, 0.01f
))
252 "[WHITELIST] Matched a desktop resolution with an exact refresh rate {} ({})",
259 CLog::Log(LOGDEBUG
, "[WHITELIST] No match for a desktop resolution with an exact refresh rate");
261 if (noWhiteList
|| CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
262 SETTING_VIDEOSCREEN_WHITELIST_DOUBLEREFRESHRATE
))
265 "[WHITELIST] Searching for a desktop resolution with double the refresh rate");
267 for (const auto& mode
: indexList
)
269 auto i
= CDisplaySettings::GetInstance().GetResFromString(mode
.asString());
270 const RESOLUTION_INFO info
= CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo(i
);
272 // allow resolutions that are desktop resolution but have double the refresh rate
273 if (info
.iScreenWidth
== desktop_info
.iScreenWidth
&&
274 (info
.dwFlags
& D3DPRESENTFLAG_MODEMASK
) ==
275 (desktop_info
.dwFlags
& D3DPRESENTFLAG_MODEMASK
) &&
276 MathUtils::FloatEquals(info
.fRefreshRate
, fps
* 2, 0.01f
))
279 "[WHITELIST] Matched a desktop resolution with double the refresh rate {} ({})",
287 "[WHITELIST] No match for a desktop resolution with double the refresh rate");
290 if (noWhiteList
|| CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
291 SETTING_VIDEOSCREEN_WHITELIST_PULLDOWN
))
294 "[WHITELIST] Searching for a desktop resolution with a 3:2 pulldown refresh rate");
296 for (const auto& mode
: indexList
)
298 auto i
= CDisplaySettings::GetInstance().GetResFromString(mode
.asString());
299 const RESOLUTION_INFO info
= CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo(i
);
301 // allow resolutions that are desktop resolution but have 2.5 times the refresh rate
302 if (info
.iScreenWidth
== desktop_info
.iScreenWidth
&&
303 (info
.dwFlags
& D3DPRESENTFLAG_MODEMASK
) ==
304 (desktop_info
.dwFlags
& D3DPRESENTFLAG_MODEMASK
) &&
305 MathUtils::FloatEquals(info
.fRefreshRate
, fps
* 2.5f
, 0.01f
))
309 "[WHITELIST] Matched a desktop resolution with a 3:2 pulldown refresh rate {} ({})",
317 "[WHITELIST] No match for a desktop resolution with a 3:2 pulldown refresh rate");
320 CLog::Log(LOGDEBUG
, "[WHITELIST] No resolution matched");
323 bool CResolutionUtils::FindResolutionFromOverride(float fps
, int width
, bool is3D
, RESOLUTION
&resolution
, float& weight
, bool fallback
)
325 RESOLUTION_INFO curr
= CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo(resolution
);
327 //try to find a refreshrate from the override
328 for (int i
= 0; i
< (int)CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoAdjustRefreshOverrides
.size(); i
++)
330 RefreshOverride
& override
= CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoAdjustRefreshOverrides
[i
];
332 if (override
.fallback
!= fallback
)
335 //if we're checking for overrides, check if the fps matches
336 if (!fallback
&& (fps
< override
.fpsmin
|| fps
> override
.fpsmax
))
339 for (size_t j
= (int)RES_DESKTOP
; j
< CDisplaySettings::GetInstance().ResolutionInfoSize(); j
++)
341 RESOLUTION_INFO info
= CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo((RESOLUTION
)j
);
343 if (info
.iScreenWidth
== curr
.iScreenWidth
&&
344 info
.iScreenHeight
== curr
.iScreenHeight
&&
345 (info
.dwFlags
& D3DPRESENTFLAG_MODEMASK
) == (curr
.dwFlags
& D3DPRESENTFLAG_MODEMASK
))
347 if (info
.fRefreshRate
<= override
.refreshmax
&&
348 info
.fRefreshRate
>= override
.refreshmin
)
350 resolution
= (RESOLUTION
)j
;
356 "Found Resolution {} ({}) from fallback (refreshmin:{:.3f} refreshmax:{:.3f})",
357 info
.strMode
, resolution
, override
.refreshmin
, override
.refreshmax
);
362 "Found Resolution {} ({}) from override of fps {:.3f} (fpsmin:{:.3f} "
363 "fpsmax:{:.3f} refreshmin:{:.3f} refreshmax:{:.3f})",
364 info
.strMode
, resolution
, fps
, override
.fpsmin
, override
.fpsmax
,
365 override
.refreshmin
, override
.refreshmax
);
368 weight
= RefreshWeight(info
.fRefreshRate
, fps
);
370 return true; //fps and refresh match with this override, use this resolution
376 return false; //no override found
379 //distance of refresh to the closest multiple of fps (multiple is 1 or higher), as a multiplier of fps
380 float CResolutionUtils::RefreshWeight(float refresh
, float fps
)
382 float div
= refresh
/ fps
;
383 int round
= MathUtils::round_int(static_cast<double>(div
));
388 weight
= (fps
- refresh
) / fps
;
390 weight
= fabs(div
/ round
- 1.0f
);
392 // punish higher refreshrates and prefer better matching
393 // e.g. 30 fps content at 60 hz is better than
394 // 30 fps at 120 hz - as we sometimes don't know if
395 // the content is interlaced at the start, only
396 // punish when refreshrate > 60 hz to not have to switch
397 // twice for 30i content
398 if (refresh
> 60 && round
> 1)
399 weight
+= round
/ 10000.0f
;
404 bool CResolutionUtils::HasWhitelist()
406 std::vector
<CVariant
> indexList
= CServiceBroker::GetSettingsComponent()->GetSettings()->GetList(CSettings::SETTING_VIDEOSCREEN_WHITELIST
);
407 return !indexList
.empty();
410 void CResolutionUtils::PrintWhitelist()
413 auto indexList
= CServiceBroker::GetSettingsComponent()->GetSettings()->GetList(
414 CSettings::SETTING_VIDEOSCREEN_WHITELIST
);
415 if (!indexList
.empty())
417 for (const auto& mode
: indexList
)
419 auto i
= CDisplaySettings::GetInstance().GetResFromString(mode
.asString());
420 const RESOLUTION_INFO info
= CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo(i
);
421 modeStr
.append("\n" + info
.strMode
);
424 CLog::Log(LOGDEBUG
, "[WHITELIST] whitelisted modes:{}", modeStr
);
428 void CResolutionUtils::GetMaxAllowedScreenResolution(unsigned int& width
, unsigned int& height
)
430 if (!CServiceBroker::GetWinSystem()->GetGfxContext().IsFullScreenRoot())
433 std::vector
<RESOLUTION_INFO
> resList
;
435 auto indexList
= CServiceBroker::GetSettingsComponent()->GetSettings()->GetList(
436 CSettings::SETTING_VIDEOSCREEN_WHITELIST
);
438 unsigned int maxWidth
{0};
439 unsigned int maxHeight
{0};
441 if (!indexList
.empty())
443 for (const auto& mode
: indexList
)
445 RESOLUTION res
= CDisplaySettings::GetInstance().GetResFromString(mode
.asString());
446 RESOLUTION_INFO resInfo
{CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo(res
)};
447 if (static_cast<unsigned int>(resInfo
.iScreenWidth
) > maxWidth
&&
448 static_cast<unsigned int>(resInfo
.iScreenHeight
) > maxHeight
)
450 maxWidth
= static_cast<unsigned int>(resInfo
.iScreenWidth
);
451 maxHeight
= static_cast<unsigned int>(resInfo
.iScreenHeight
);
457 std::vector
<RESOLUTION
> resList
;
458 CServiceBroker::GetWinSystem()->GetGfxContext().GetAllowedResolutions(resList
);
460 for (const auto& res
: resList
)
462 RESOLUTION_INFO resInfo
{CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo(res
)};
463 if (static_cast<unsigned int>(resInfo
.iScreenWidth
) > maxWidth
&&
464 static_cast<unsigned int>(resInfo
.iScreenHeight
) > maxHeight
)
466 maxWidth
= static_cast<unsigned int>(resInfo
.iScreenWidth
);
467 maxHeight
= static_cast<unsigned int>(resInfo
.iScreenHeight
);