mirror of
https://github.com/ayufan/steam-deck-tools.git
synced 2026-01-12 03:30:35 +01:00
211 lines
9.1 KiB
C#
211 lines
9.1 KiB
C#
using System.Runtime.InteropServices;
|
|
|
|
namespace PowerControl.Helpers.AMD
|
|
{
|
|
internal class ModeTiming
|
|
{
|
|
public struct DetailedTiming
|
|
{
|
|
public ADLDetailedTiming Timing { get; set; }
|
|
public List<int> PixelClocks { get; } = new List<int>();
|
|
public List<int> RefreshRates { get; } = new List<int>();
|
|
|
|
public DetailedTiming(
|
|
short hDisplay, short hFront, short hSync, short hBack,
|
|
short vDisplay, short vFront, short vSync, short vBack,
|
|
short timingFlags = ADL.ADL_DL_TIMINGFLAG_H_SYNC_POLARITY | ADL.ADL_DL_TIMINGFLAG_V_SYNC_POLARITY,
|
|
short pixelClock = 0)
|
|
{
|
|
Timing = new ADLDetailedTiming()
|
|
{
|
|
iSize = 96,
|
|
sPixelClock = pixelClock,
|
|
sTimingFlags = timingFlags,
|
|
sHDisplay = hDisplay,
|
|
sHSyncStart = (short)(hDisplay + hFront),
|
|
sHSyncWidth = hSync,
|
|
sHTotal = (short)(hDisplay + hFront + hSync + hBack),
|
|
sVDisplay = vDisplay,
|
|
sVSyncStart = (short)(vDisplay + vFront),
|
|
sVSyncWidth = vSync,
|
|
sVTotal = (short)(vDisplay + vFront + vSync + vBack),
|
|
};
|
|
}
|
|
|
|
public IEnumerable<ADLDisplayModeInfo> EachModeInfo()
|
|
{
|
|
foreach (var refreshRate in RefreshRates)
|
|
{
|
|
var pixelClock = (short)Math.Ceiling((double)Timing.sHTotal * Timing.sVTotal * refreshRate / 10000);
|
|
|
|
ADLDisplayModeInfo modeInfo = new ADLDisplayModeInfo()
|
|
{
|
|
iPelsHeight = Timing.sVDisplay,
|
|
iPelsWidth = Timing.sHDisplay,
|
|
iPossibleStandard = 0,
|
|
iRefreshRate = (short)refreshRate,
|
|
iTimingStandard = ADL.ADL_DL_MODETIMING_STANDARD_CUSTOM,
|
|
sDetailedTiming = Timing,
|
|
};
|
|
|
|
modeInfo.sDetailedTiming.sPixelClock = (short)pixelClock;
|
|
|
|
yield return modeInfo;
|
|
}
|
|
}
|
|
};
|
|
|
|
public static readonly DetailedTiming[] AllTimings = new DetailedTiming[] {
|
|
new DetailedTiming(400, 32, 20, 20, 640, 16, 2, 26, ADL.ADL_DL_TIMINGFLAG_H_SYNC_POLARITY | ADL.ADL_DL_TIMINGFLAG_V_SYNC_POLARITY) {
|
|
RefreshRates = { 30, 35, 40, 45, 48, 50, 55, 60 }
|
|
},
|
|
new DetailedTiming(640, 32, 20, 20, 1024, 16, 2, 26, ADL.ADL_DL_TIMINGFLAG_H_SYNC_POLARITY | ADL.ADL_DL_TIMINGFLAG_V_SYNC_POLARITY) {
|
|
RefreshRates = { 30, 35, 40, 45, 48, 50, 55, 60 }
|
|
},
|
|
new DetailedTiming(540, 32, 20, 20, 960, 16, 2, 26, ADL.ADL_DL_TIMINGFLAG_H_SYNC_POLARITY | ADL.ADL_DL_TIMINGFLAG_V_SYNC_POLARITY) {
|
|
RefreshRates = { 30, 35, 40, 45, 48, 50, 55, 60 }
|
|
},
|
|
new DetailedTiming(544, 32, 20, 20, 960, 16, 2, 26, ADL.ADL_DL_TIMINGFLAG_H_SYNC_POLARITY | ADL.ADL_DL_TIMINGFLAG_V_SYNC_POLARITY) {
|
|
RefreshRates = { 30, 35, 40, 45, 48, 50, 55, 60 }
|
|
},
|
|
new DetailedTiming(600, 32, 20, 20, 800, 16, 2, 26, ADL.ADL_DL_TIMINGFLAG_H_SYNC_POLARITY | ADL.ADL_DL_TIMINGFLAG_V_SYNC_POLARITY) {
|
|
RefreshRates = { 30, 35, 40, 45, 48, 50, 55, 60 }
|
|
},
|
|
new DetailedTiming(600, 32, 20, 20, 960, 16, 2, 26, ADL.ADL_DL_TIMINGFLAG_H_SYNC_POLARITY | ADL.ADL_DL_TIMINGFLAG_V_SYNC_POLARITY) {
|
|
RefreshRates = { 30, 35, 40, 45, 48, 50, 55, 60 }
|
|
},
|
|
new DetailedTiming(680, 32, 20, 20, 1088, 16, 2, 26, ADL.ADL_DL_TIMINGFLAG_H_SYNC_POLARITY | ADL.ADL_DL_TIMINGFLAG_V_SYNC_POLARITY) {
|
|
RefreshRates = { 30, 35, 40, 45, 48, 50, 55, 60 }
|
|
},
|
|
new DetailedTiming(576, 32, 20, 20, 1024, 16, 2, 26, ADL.ADL_DL_TIMINGFLAG_H_SYNC_POLARITY | ADL.ADL_DL_TIMINGFLAG_V_SYNC_POLARITY) {
|
|
RefreshRates = { 30, 35, 40, 45, 48, 50, 55, 60 }
|
|
},
|
|
new DetailedTiming(768, 32, 20, 20, 1024, 16, 2, 26, ADL.ADL_DL_TIMINGFLAG_H_SYNC_POLARITY | ADL.ADL_DL_TIMINGFLAG_V_SYNC_POLARITY) {
|
|
RefreshRates = { 30, 35, 40, 45, 48, 50, 55, 60 }
|
|
},
|
|
new DetailedTiming(720, 32, 20, 20, 1152, 16, 2, 26, ADL.ADL_DL_TIMINGFLAG_H_SYNC_POLARITY | ADL.ADL_DL_TIMINGFLAG_V_SYNC_POLARITY) {
|
|
RefreshRates = { 30, 35, 40, 45, 48, 50, 55, 60 }
|
|
},
|
|
new DetailedTiming(720, 112, 20, 20, 1280, 16, 2, 26, ADL.ADL_DL_TIMINGFLAG_H_SYNC_POLARITY | ADL.ADL_DL_TIMINGFLAG_V_SYNC_POLARITY) {
|
|
RefreshRates = { 30, 35, 40, 45, 48, 50, 55, 60 }
|
|
},
|
|
new DetailedTiming(800, 32, 20, 20, 1280, 16, 2, 26, ADL.ADL_DL_TIMINGFLAG_H_SYNC_POLARITY | ADL.ADL_DL_TIMINGFLAG_V_SYNC_POLARITY) {
|
|
RefreshRates = { 30, 35, 40, 45, 48 }
|
|
},
|
|
new DetailedTiming(800, 56, 80, 136, 1280, 3, 10, 27, ADL.ADL_DL_TIMINGFLAG_V_SYNC_POLARITY) {
|
|
RefreshRates = { 50, 55 }
|
|
}
|
|
};
|
|
|
|
internal static IEnumerable<ADLDisplayModeInfo> AllDetailedTimings()
|
|
{
|
|
foreach (var timing in AllTimings)
|
|
{
|
|
foreach (var modeInfo in timing.EachModeInfo())
|
|
yield return modeInfo;
|
|
}
|
|
}
|
|
|
|
internal static IEnumerable<ADLDisplayModeInfo>? AllTimingOverrideList()
|
|
{
|
|
return Helpers.AMD.ADLContext.WithSafe((context) =>
|
|
{
|
|
int res = ADL.ADL2_Display_ModeTimingOverrideList_Get(context.Context,
|
|
Helpers.AMD.ADL.ADL_DEFAULT_ADAPTER, 0,
|
|
ADL.ADL_MAX_OVERRIDES, out var modes, out var modesCount);
|
|
if (res != 0)
|
|
return null;
|
|
|
|
return modes.ADLDisplayModeInfo.Take(modesCount);
|
|
});
|
|
}
|
|
|
|
internal static void InstallAll()
|
|
{
|
|
var modeInfos = AllDetailedTimings()?.ToArray();
|
|
if (modeInfos is null)
|
|
return;
|
|
|
|
ADLDisplayModeInfo lastModeInfo = modeInfos.LastOrDefault();
|
|
int count = 0;
|
|
|
|
foreach (var modeInfo in modeInfos)
|
|
{
|
|
SetTimingOverride(modeInfo, modeInfo.Equals(lastModeInfo) || (count++ % 10 == 9));
|
|
}
|
|
}
|
|
|
|
internal static void UninstallAll()
|
|
{
|
|
var modes = AllTimingOverrideList()?.ToArray();
|
|
if (modes is null)
|
|
return;
|
|
|
|
ADLDisplayModeInfo lastMode = modes.LastOrDefault();
|
|
|
|
foreach (var mode in modes)
|
|
{
|
|
var newMode = mode;
|
|
newMode.iTimingStandard = Helpers.AMD.ADL.ADL_DL_MODETIMING_STANDARD_DRIVER_DEFAULT;
|
|
RemoveTimingOverride(newMode, mode.Equals(lastMode));
|
|
}
|
|
}
|
|
|
|
internal static ADLDisplayModeInfo? FindTiming(ADLDisplayModeX2 displayMode)
|
|
{
|
|
foreach (var modeInfo in AllDetailedTimings())
|
|
{
|
|
if (modeInfo.iPelsWidth == displayMode.PelsWidth &&
|
|
modeInfo.iPelsHeight == displayMode.PelsHeight &&
|
|
modeInfo.iRefreshRate == displayMode.RefreshRate)
|
|
return modeInfo;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
internal static bool AddTiming(ADLDisplayModeX2 displayMode, bool removeOld = false)
|
|
{
|
|
if (removeOld)
|
|
{
|
|
var modes = AllTimingOverrideList()?.ToArray() ?? new ADLDisplayModeInfo[0];
|
|
foreach (var oldMode in modes)
|
|
RemoveTimingOverride(oldMode, false);
|
|
}
|
|
|
|
var timing = FindTiming(displayMode);
|
|
if (timing is not null)
|
|
return SetTimingOverride(timing.Value, true);
|
|
return false;
|
|
}
|
|
|
|
internal static bool RemoveTimingOverride(ADLDisplayModeInfo displayMode, bool forceUpdate = true)
|
|
{
|
|
displayMode.iTimingStandard = ADL.ADL_DL_MODETIMING_STANDARD_DRIVER_DEFAULT;
|
|
return SetTimingOverride(displayMode, forceUpdate);
|
|
}
|
|
|
|
internal static bool SetTimingOverride(ADLDisplayModeInfo displayMode, bool forceUpdate = true)
|
|
{
|
|
return Helpers.AMD.ADLContext.WithSafe((context) =>
|
|
{
|
|
int res = ADL.ADL2_Display_ModeTimingOverride_Set(
|
|
context.Context,
|
|
Helpers.AMD.ADL.ADL_DEFAULT_ADAPTER,
|
|
0,
|
|
ref displayMode,
|
|
forceUpdate ? 1 : 0
|
|
);
|
|
|
|
CommonHelpers.Log.TraceLine("{0}x{1}p{2} => PCLK: {3} => {4} (forceUpdate={5}, remove={6})",
|
|
displayMode.sDetailedTiming.sHDisplay, displayMode.sDetailedTiming.sVDisplay,
|
|
displayMode.iRefreshRate, displayMode.sDetailedTiming.sPixelClock,
|
|
res == 0 ? "OK" : "FAIL",
|
|
forceUpdate, displayMode.iTimingStandard == ADL.ADL_DL_MODETIMING_STANDARD_DRIVER_DEFAULT);
|
|
|
|
return res == 0;
|
|
});
|
|
}
|
|
}
|
|
}
|