mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 15:50:16 +02:00
Better logic for choosing the display mode (#1039)
## Description This PR improves the logic used to choose a display mode when refresh rate and resolution switching are enabled. As before, matching the refresh rate is prioritized over picking the resolution. For example, with the display modes of 1080@30 & 720@60 for a 720@30 video, 1080@30 is used because the frame rate exactly matches. ### Related issues Fixes #1036 ### Testing Unit tests, added test cases for more situations ## Screenshots N/A ## AI or LLM usage None
This commit is contained in:
parent
354577e2e8
commit
014bed1bf3
2 changed files with 188 additions and 15 deletions
|
|
@ -158,33 +158,58 @@ class RefreshRateService
|
||||||
if (refreshRateSwitch) {
|
if (refreshRateSwitch) {
|
||||||
displayModes
|
displayModes
|
||||||
.filterNot { it.physicalHeight < streamHeight || it.physicalWidth < streamWidth }
|
.filterNot { it.physicalHeight < streamHeight || it.physicalWidth < streamWidth }
|
||||||
.filter {
|
.filter { frameRateMatches(it.refreshRateRounded, streamRate) }
|
||||||
it.refreshRateRounded % streamRate == 0 || // Exact multiple
|
|
||||||
it.refreshRateRounded == (streamRate * 2.5).roundToInt() // eg 24 & 60fps
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
displayModes
|
displayModes
|
||||||
.filterNot { it.physicalHeight < streamHeight || it.physicalWidth < streamWidth }
|
.filterNot { it.physicalHeight < streamHeight || it.physicalWidth < streamWidth }
|
||||||
}
|
}
|
||||||
// Timber.v("display modes candidates: %s", candidates.joinToString("\n"))
|
// Timber.v("display modes candidates: %s", candidates.joinToString("\n"))
|
||||||
return if (!resolutionSwitch) {
|
return if (!resolutionSwitch) {
|
||||||
candidates.maxByOrNull { it.physicalWidth * it.physicalHeight }
|
candidates
|
||||||
|
.filter { it.refreshRateRounded == streamRate }
|
||||||
|
.maxByOrNull { it.physicalWidth * it.physicalHeight }
|
||||||
|
?: candidates.maxByOrNull { it.physicalWidth * it.physicalHeight }
|
||||||
} else {
|
} else {
|
||||||
|
// Exact resolution & frame rate
|
||||||
candidates.firstOrNull {
|
candidates.firstOrNull {
|
||||||
it.physicalWidth == streamWidth &&
|
it.physicalWidth == streamWidth &&
|
||||||
it.physicalHeight == streamHeight &&
|
it.physicalHeight == streamHeight &&
|
||||||
it.refreshRateRounded == streamRate
|
it.refreshRateRounded == streamRate
|
||||||
}
|
}
|
||||||
?: candidates.firstOrNull {
|
// Next highest resolution, exact frame rate
|
||||||
it.physicalWidth == streamWidth &&
|
?: candidates.lastOrNull {
|
||||||
|
it.physicalWidth >= streamWidth &&
|
||||||
it.physicalHeight >= streamHeight &&
|
it.physicalHeight >= streamHeight &&
|
||||||
it.refreshRateRounded == streamRate
|
it.refreshRateRounded == streamRate
|
||||||
}
|
}
|
||||||
|
// Exact resolution, acceptable frame rate
|
||||||
|
?: candidates.lastOrNull {
|
||||||
|
it.physicalWidth == streamWidth &&
|
||||||
|
it.physicalHeight == streamHeight &&
|
||||||
|
frameRateMatches(it.refreshRateRounded, streamRate)
|
||||||
|
}
|
||||||
|
// Next highest resolution, acceptable frame rate
|
||||||
|
?: candidates.lastOrNull {
|
||||||
|
it.physicalWidth >= streamWidth &&
|
||||||
|
it.physicalHeight >= streamHeight &&
|
||||||
|
frameRateMatches(it.refreshRateRounded, streamRate)
|
||||||
|
}
|
||||||
|
// Fall back to largest resolution, exact frame rate
|
||||||
?: candidates
|
?: candidates
|
||||||
.filter { it.refreshRateRounded == streamRate }
|
.filter { it.refreshRateRounded == streamRate }
|
||||||
.maxByOrNull { it.physicalWidth * it.physicalHeight }
|
.maxByOrNull { it.physicalWidth * it.physicalHeight }
|
||||||
|
// Finally, just the highest resolution
|
||||||
|
?: candidates.maxByOrNull { it.physicalWidth * it.physicalHeight }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun frameRateMatches(
|
||||||
|
refreshRateRounded: Int,
|
||||||
|
streamRate: Int,
|
||||||
|
): Boolean {
|
||||||
|
return refreshRateRounded % streamRate == 0 || // Exact multiple
|
||||||
|
refreshRateRounded == (streamRate * 2.5).roundToInt() // eg 24 & 60fps
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,25 @@ class TestDisplayModeChoice {
|
||||||
val UHD_30 = DisplayMode(4, 3840, 2160, 30f)
|
val UHD_30 = DisplayMode(4, 3840, 2160, 30f)
|
||||||
val UHD_24 = DisplayMode(5, 3840, 2160, 24f)
|
val UHD_24 = DisplayMode(5, 3840, 2160, 24f)
|
||||||
|
|
||||||
val ALL_MODES = listOf(UHD_24, UHD_30, UHD_60, HD_24, HD_30, HD_60)
|
val HD720_60 = DisplayMode(6, 1280, 720, 60f)
|
||||||
|
val HD720_30 = DisplayMode(7, 1280, 720, 30f)
|
||||||
|
val HD720_24 = DisplayMode(8, 1280, 720, 24f)
|
||||||
|
|
||||||
|
val ALL_MODES =
|
||||||
|
listOf(
|
||||||
|
UHD_24,
|
||||||
|
UHD_30,
|
||||||
|
UHD_60,
|
||||||
|
HD_24,
|
||||||
|
HD_30,
|
||||||
|
HD_60,
|
||||||
|
HD720_60,
|
||||||
|
HD720_30,
|
||||||
|
HD720_24,
|
||||||
|
).sortedWith(
|
||||||
|
compareByDescending<DisplayMode>({ it.physicalWidth * it.physicalHeight })
|
||||||
|
.thenBy { it.refreshRateRounded },
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -32,7 +50,7 @@ class TestDisplayModeChoice {
|
||||||
refreshRateSwitch = true,
|
refreshRateSwitch = true,
|
||||||
resolutionSwitch = false,
|
resolutionSwitch = false,
|
||||||
)
|
)
|
||||||
Assert.assertEquals(3, result?.modeId)
|
Assert.assertEquals(UHD_60.modeId, result?.modeId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -49,7 +67,7 @@ class TestDisplayModeChoice {
|
||||||
refreshRateSwitch = true,
|
refreshRateSwitch = true,
|
||||||
resolutionSwitch = true,
|
resolutionSwitch = true,
|
||||||
)
|
)
|
||||||
Assert.assertEquals(0, result?.modeId)
|
Assert.assertEquals(HD_60.modeId, result?.modeId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -66,7 +84,7 @@ class TestDisplayModeChoice {
|
||||||
refreshRateSwitch = true,
|
refreshRateSwitch = true,
|
||||||
resolutionSwitch = false,
|
resolutionSwitch = false,
|
||||||
)
|
)
|
||||||
Assert.assertEquals(4, result?.modeId)
|
Assert.assertEquals(UHD_30.modeId, result?.modeId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -83,14 +101,14 @@ class TestDisplayModeChoice {
|
||||||
refreshRateSwitch = false,
|
refreshRateSwitch = false,
|
||||||
resolutionSwitch = true,
|
resolutionSwitch = true,
|
||||||
)
|
)
|
||||||
Assert.assertEquals(1, result?.modeId)
|
Assert.assertEquals(HD_30.modeId, result?.modeId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testFraction() {
|
fun testFraction() {
|
||||||
val streamWidth = 1920
|
val streamWidth = 1920
|
||||||
val streamHeight = 1080
|
val streamHeight = 1080
|
||||||
val streamRealFrameRate = 30f
|
val streamRealFrameRate = 29.970f
|
||||||
|
|
||||||
val displayModes =
|
val displayModes =
|
||||||
listOf(
|
listOf(
|
||||||
|
|
@ -104,7 +122,7 @@ class TestDisplayModeChoice {
|
||||||
displayModes = displayModes,
|
displayModes = displayModes,
|
||||||
streamWidth = streamWidth,
|
streamWidth = streamWidth,
|
||||||
streamHeight = streamHeight,
|
streamHeight = streamHeight,
|
||||||
targetFrameRate = 29.970f,
|
targetFrameRate = streamRealFrameRate,
|
||||||
refreshRateSwitch = true,
|
refreshRateSwitch = true,
|
||||||
resolutionSwitch = false,
|
resolutionSwitch = false,
|
||||||
)
|
)
|
||||||
|
|
@ -119,6 +137,136 @@ class TestDisplayModeChoice {
|
||||||
refreshRateSwitch = true,
|
refreshRateSwitch = true,
|
||||||
resolutionSwitch = false,
|
resolutionSwitch = false,
|
||||||
)
|
)
|
||||||
Assert.assertEquals(1, result2?.modeId)
|
Assert.assertEquals(HD_30.modeId, result2?.modeId)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun test(
|
||||||
|
expected: DisplayMode,
|
||||||
|
streamWidth: Int,
|
||||||
|
streamHeight: Int,
|
||||||
|
streamRealFrameRate: Float,
|
||||||
|
) {
|
||||||
|
val result =
|
||||||
|
RefreshRateService.findDisplayMode(
|
||||||
|
displayModes = ALL_MODES,
|
||||||
|
streamWidth = streamWidth,
|
||||||
|
streamHeight = streamHeight,
|
||||||
|
targetFrameRate = streamRealFrameRate,
|
||||||
|
refreshRateSwitch = false,
|
||||||
|
resolutionSwitch = true,
|
||||||
|
)
|
||||||
|
Assert.assertEquals(
|
||||||
|
"streamWidth=$streamWidth, streamHeight=$streamHeight, streamRealFrameRate=$streamRealFrameRate",
|
||||||
|
expected.modeId,
|
||||||
|
result?.modeId,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Test choose best resolution`() {
|
||||||
|
test(HD720_30, 1280, 720, 30f)
|
||||||
|
test(HD720_30, 1280, 548, 30f)
|
||||||
|
test(HD720_30, 640, 480, 30f)
|
||||||
|
test(HD720_30, 960, 720, 30f)
|
||||||
|
test(HD720_24, 960, 720, 24f)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Test 60fps for 24 without resolution switch`() {
|
||||||
|
val streamWidth = 1920
|
||||||
|
val streamHeight = 1080
|
||||||
|
val streamRealFrameRate = 24f
|
||||||
|
|
||||||
|
val displayModes =
|
||||||
|
listOf(
|
||||||
|
UHD_60,
|
||||||
|
HD_60,
|
||||||
|
HD_30,
|
||||||
|
)
|
||||||
|
|
||||||
|
val result =
|
||||||
|
RefreshRateService.findDisplayMode(
|
||||||
|
displayModes = displayModes,
|
||||||
|
streamWidth = streamWidth,
|
||||||
|
streamHeight = streamHeight,
|
||||||
|
targetFrameRate = streamRealFrameRate,
|
||||||
|
refreshRateSwitch = true,
|
||||||
|
resolutionSwitch = false,
|
||||||
|
)
|
||||||
|
Assert.assertEquals(UHD_60.modeId, result?.modeId)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Test 60fps for 24 with resolution switch`() {
|
||||||
|
val streamWidth = 1920
|
||||||
|
val streamHeight = 1080
|
||||||
|
val streamRealFrameRate = 24f
|
||||||
|
|
||||||
|
val displayModes =
|
||||||
|
listOf(
|
||||||
|
HD_60,
|
||||||
|
HD_30,
|
||||||
|
)
|
||||||
|
|
||||||
|
val result =
|
||||||
|
RefreshRateService.findDisplayMode(
|
||||||
|
displayModes = displayModes,
|
||||||
|
streamWidth = streamWidth,
|
||||||
|
streamHeight = streamHeight,
|
||||||
|
targetFrameRate = streamRealFrameRate,
|
||||||
|
refreshRateSwitch = true,
|
||||||
|
resolutionSwitch = true,
|
||||||
|
)
|
||||||
|
Assert.assertEquals(HD_60.modeId, result?.modeId)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Test prefer refresh rate over resolution`() {
|
||||||
|
val streamWidth = 1280
|
||||||
|
val streamHeight = 720
|
||||||
|
val streamRealFrameRate = 24f
|
||||||
|
|
||||||
|
// 720@60 is an acceptable refresh rate, but want to prioritize exact refresh rate
|
||||||
|
val displayModes =
|
||||||
|
listOf(
|
||||||
|
HD_24,
|
||||||
|
HD720_60,
|
||||||
|
)
|
||||||
|
|
||||||
|
val result =
|
||||||
|
RefreshRateService.findDisplayMode(
|
||||||
|
displayModes = displayModes,
|
||||||
|
streamWidth = streamWidth,
|
||||||
|
streamHeight = streamHeight,
|
||||||
|
targetFrameRate = streamRealFrameRate,
|
||||||
|
refreshRateSwitch = true,
|
||||||
|
resolutionSwitch = true,
|
||||||
|
)
|
||||||
|
Assert.assertEquals(HD_24.modeId, result?.modeId)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Test prefer refresh rate over resolution2`() {
|
||||||
|
val streamWidth = 1280
|
||||||
|
val streamHeight = 720
|
||||||
|
val streamRealFrameRate = 30f
|
||||||
|
|
||||||
|
// 720@60 is an acceptable refresh rate, but want to prioritize exact refresh rate
|
||||||
|
val displayModes =
|
||||||
|
listOf(
|
||||||
|
HD_30,
|
||||||
|
HD720_60,
|
||||||
|
)
|
||||||
|
|
||||||
|
val result =
|
||||||
|
RefreshRateService.findDisplayMode(
|
||||||
|
displayModes = displayModes,
|
||||||
|
streamWidth = streamWidth,
|
||||||
|
streamHeight = streamHeight,
|
||||||
|
targetFrameRate = streamRealFrameRate,
|
||||||
|
refreshRateSwitch = true,
|
||||||
|
resolutionSwitch = true,
|
||||||
|
)
|
||||||
|
Assert.assertEquals(HD_30.modeId, result?.modeId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue