mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 23:51:21 +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) {
|
||||
displayModes
|
||||
.filterNot { it.physicalHeight < streamHeight || it.physicalWidth < streamWidth }
|
||||
.filter {
|
||||
it.refreshRateRounded % streamRate == 0 || // Exact multiple
|
||||
it.refreshRateRounded == (streamRate * 2.5).roundToInt() // eg 24 & 60fps
|
||||
}
|
||||
.filter { frameRateMatches(it.refreshRateRounded, streamRate) }
|
||||
} else {
|
||||
displayModes
|
||||
.filterNot { it.physicalHeight < streamHeight || it.physicalWidth < streamWidth }
|
||||
}
|
||||
// Timber.v("display modes candidates: %s", candidates.joinToString("\n"))
|
||||
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 {
|
||||
// Exact resolution & frame rate
|
||||
candidates.firstOrNull {
|
||||
it.physicalWidth == streamWidth &&
|
||||
it.physicalHeight == streamHeight &&
|
||||
it.refreshRateRounded == streamRate
|
||||
}
|
||||
?: candidates.firstOrNull {
|
||||
it.physicalWidth == streamWidth &&
|
||||
// Next highest resolution, exact frame rate
|
||||
?: candidates.lastOrNull {
|
||||
it.physicalWidth >= streamWidth &&
|
||||
it.physicalHeight >= streamHeight &&
|
||||
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
|
||||
.filter { it.refreshRateRounded == streamRate }
|
||||
.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_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
|
||||
|
|
@ -32,7 +50,7 @@ class TestDisplayModeChoice {
|
|||
refreshRateSwitch = true,
|
||||
resolutionSwitch = false,
|
||||
)
|
||||
Assert.assertEquals(3, result?.modeId)
|
||||
Assert.assertEquals(UHD_60.modeId, result?.modeId)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -49,7 +67,7 @@ class TestDisplayModeChoice {
|
|||
refreshRateSwitch = true,
|
||||
resolutionSwitch = true,
|
||||
)
|
||||
Assert.assertEquals(0, result?.modeId)
|
||||
Assert.assertEquals(HD_60.modeId, result?.modeId)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -66,7 +84,7 @@ class TestDisplayModeChoice {
|
|||
refreshRateSwitch = true,
|
||||
resolutionSwitch = false,
|
||||
)
|
||||
Assert.assertEquals(4, result?.modeId)
|
||||
Assert.assertEquals(UHD_30.modeId, result?.modeId)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -83,14 +101,14 @@ class TestDisplayModeChoice {
|
|||
refreshRateSwitch = false,
|
||||
resolutionSwitch = true,
|
||||
)
|
||||
Assert.assertEquals(1, result?.modeId)
|
||||
Assert.assertEquals(HD_30.modeId, result?.modeId)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testFraction() {
|
||||
val streamWidth = 1920
|
||||
val streamHeight = 1080
|
||||
val streamRealFrameRate = 30f
|
||||
val streamRealFrameRate = 29.970f
|
||||
|
||||
val displayModes =
|
||||
listOf(
|
||||
|
|
@ -104,7 +122,7 @@ class TestDisplayModeChoice {
|
|||
displayModes = displayModes,
|
||||
streamWidth = streamWidth,
|
||||
streamHeight = streamHeight,
|
||||
targetFrameRate = 29.970f,
|
||||
targetFrameRate = streamRealFrameRate,
|
||||
refreshRateSwitch = true,
|
||||
resolutionSwitch = false,
|
||||
)
|
||||
|
|
@ -119,6 +137,136 @@ class TestDisplayModeChoice {
|
|||
refreshRateSwitch = true,
|
||||
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