From 862d7afec507e443b9f8d372bfef81ccb4a4825d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Trzci=C5=84ski?= Date: Thu, 9 Feb 2023 12:18:29 +0100 Subject: [PATCH] PowerControl: Install custom resolutions (EDID) (experimental feature) --- PowerControl/Controller.cs | 28 +++++++++++ PowerControl/Helpers/AMD/ADL.cs | 32 +++++++++++++ PowerControl/Helpers/AMD/EDID.cs | 57 +++++++++++++++++++++++ PowerControl/Resources.Designer.cs | 10 ++++ PowerControl/Resources.resx | 3 ++ PowerControl/Resources/CRU_SteamDeck.bin | Bin 0 -> 512 bytes RELEASE.md | 1 + 7 files changed, 131 insertions(+) create mode 100644 PowerControl/Helpers/AMD/EDID.cs create mode 100644 PowerControl/Resources/CRU_SteamDeck.bin diff --git a/PowerControl/Controller.cs b/PowerControl/Controller.cs index ab42857..3446703 100644 --- a/PowerControl/Controller.cs +++ b/PowerControl/Controller.cs @@ -69,6 +69,34 @@ namespace PowerControl rootMenu.VisibleChanged += delegate { updateOSD(); }; contextMenu.Items.Add(new ToolStripSeparator()); + if (Settings.Default.EnableExperimentalFeatures) + { + var installEDIDItem = contextMenu.Items.Add("Install &Resolutions"); + installEDIDItem.Click += delegate { Helpers.AMD.EDID.SetEDID(Resources.CRU_SteamDeck); }; + var replaceEDIDItem = contextMenu.Items.Add("Replace &Resolutions"); + replaceEDIDItem.Click += delegate { Helpers.AMD.EDID.SetEDID(new byte[0]); Helpers.AMD.EDID.SetEDID(Resources.CRU_SteamDeck); }; + var uninstallEDIDItem = contextMenu.Items.Add("Revert &Resolutions"); + uninstallEDIDItem.Click += delegate { Helpers.AMD.EDID.SetEDID(new byte[0]); }; + contextMenu.Opening += delegate + { + if (ExternalHelpers.DisplayConfig.IsInternalConnected == true) + { + var edid = Helpers.AMD.EDID.GetEDID() ?? new byte[0]; + var edidInstalled = Resources.CRU_SteamDeck.SequenceEqual(edid); + installEDIDItem.Visible = edid.Length <= 128; + replaceEDIDItem.Visible = !edidInstalled && edid.Length > 128; + uninstallEDIDItem.Visible = edid.Length > 128; + } + else + { + installEDIDItem.Visible = false; + replaceEDIDItem.Visible = false; + uninstallEDIDItem.Visible = false; + } + }; + contextMenu.Items.Add(new ToolStripSeparator()); + } + if (startupManager.IsAvailable) { var startupItem = new ToolStripMenuItem("Run On Startup"); diff --git a/PowerControl/Helpers/AMD/ADL.cs b/PowerControl/Helpers/AMD/ADL.cs index 8f2b8a0..38f5db5 100644 --- a/PowerControl/Helpers/AMD/ADL.cs +++ b/PowerControl/Helpers/AMD/ADL.cs @@ -143,6 +143,30 @@ namespace PowerControl.Helpers.AMD } #endregion ADLDisplayInfo + [StructLayout(LayoutKind.Sequential)] + internal struct ADLDisplayEDIDData + { + internal int iSize; + internal int iFlag; + internal int iEDIDSize; + internal int iBlockIndex; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = (int)ADL.ADL_MAX_EDIDDATA_SIZE)] + internal byte[] cEDIDData; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + internal int[] iReserved; + }; + + [StructLayout(LayoutKind.Sequential)] + internal struct ADLDisplayEDIDDataX2 + { + internal int iSize; + internal int iFlag; + internal int iEDIDSize; + internal int iIgnored; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = (int)2 * ADL.ADL_MAX_EDIDDATA_SIZE)] + internal byte[] cEDIDData; + }; + #region Radeon Image Sharpening [StructLayout(LayoutKind.Sequential)] internal struct ADL_RIS_SETTINGS @@ -192,6 +216,8 @@ namespace PowerControl.Helpers.AMD internal const int ADL_MAX_GLSYNC_PORT_LEDS = 8; /// Maximum number of ADLMOdes for the adapter internal const int ADL_MAX_NUM_DISPLAYMODES = 1024; + internal const int ADL_MAX_EDIDDATA_SIZE = 256; + internal const int ADL_MAX_EDID_EXTENSION_BLOCKS = 3; internal const int ADL_DISPLAY_DISPLAYINFO_DISPLAYCONNECTED = 0x00000001; @@ -265,6 +291,12 @@ namespace PowerControl.Helpers.AMD [DllImport(Atiadlxx_FileName)] internal static extern int ADL2_RIS_Settings_Set(IntPtr context, int adapterIndex, ADL_RIS_SETTINGS settings, ADL_RIS_NOTFICATION_REASON reason); + + [DllImport(Atiadlxx_FileName)] + internal static extern int ADL2_Display_EdidData_Get(IntPtr context, int adapterIndex, int displayIndex, ref ADLDisplayEDIDData edidData); + + [DllImport(Atiadlxx_FileName)] + internal static extern int ADL2_Display_EdidData_Set(IntPtr context, int adapterIndex, int displayIndex, ref ADLDisplayEDIDDataX2 edidData); #endregion DLLImport #region ADL_Main_Memory_Alloc diff --git a/PowerControl/Helpers/AMD/EDID.cs b/PowerControl/Helpers/AMD/EDID.cs new file mode 100644 index 0000000..095684a --- /dev/null +++ b/PowerControl/Helpers/AMD/EDID.cs @@ -0,0 +1,57 @@ +using System.Linq; +using System.Runtime.InteropServices; + +namespace PowerControl.Helpers.AMD +{ + internal class EDID + { + internal static byte[]? GetEDID(int displayIndex = ADL.ADL_DEFAULT_DISPLAY) + { + return ADLContext.WithSafe((context) => + { + byte[] edid = new byte[0]; + + for (int block = 0; block < ADL.ADL_MAX_EDID_EXTENSION_BLOCKS; ++block) + { + ADLDisplayEDIDData displayEdidData = new ADLDisplayEDIDData() + { + iSize = Marshal.SizeOf(), + iBlockIndex = block, + }; + + int res = ADL.ADL2_Display_EdidData_Get(context.Context, ADL.ADL_DEFAULT_ADAPTER, ADL.ADL_DEFAULT_DISPLAY, ref displayEdidData); + if (res != 0) + break; + + var blockBytes = displayEdidData.cEDIDData.Take(displayEdidData.iEDIDSize); + edid = edid.Concat(blockBytes).ToArray(); + } + + return edid; + }); + } + + internal static bool? SetEDID(byte[] value, int displayIndex = ADL.ADL_DEFAULT_DISPLAY) + { + return ADLContext.WithSafe((context) => + { + var bytes = new byte[ADL.ADL_MAX_EDIDDATA_SIZE * 2]; + value.CopyTo(bytes, 0); + + var blockData = new ADLDisplayEDIDDataX2() + { + // TODO: Hack to send a full EDID at once + iSize = Marshal.SizeOf(), + cEDIDData = bytes, + iEDIDSize = value.Length, + }; + + int res = ADL.ADL2_Display_EdidData_Set(context.Context, + ADL.ADL_DEFAULT_ADAPTER, ADL.ADL_DEFAULT_DISPLAY, + ref blockData); + + return res == 0; + }); + } + } +} diff --git a/PowerControl/Resources.Designer.cs b/PowerControl/Resources.Designer.cs index 48a2d72..0648e80 100644 --- a/PowerControl/Resources.Designer.cs +++ b/PowerControl/Resources.Designer.cs @@ -60,6 +60,16 @@ namespace PowerControl { } } + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] CRU_SteamDeck { + get { + object obj = ResourceManager.GetObject("CRU_SteamDeck", resourceCulture); + return ((byte[])(obj)); + } + } + /// /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). /// diff --git a/PowerControl/Resources.resx b/PowerControl/Resources.resx index 716a078..fe2cdb2 100644 --- a/PowerControl/Resources.resx +++ b/PowerControl/Resources.resx @@ -118,6 +118,9 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + Resources\CRU_SteamDeck.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + Resources\traffic-light-outline_light.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a diff --git a/PowerControl/Resources/CRU_SteamDeck.bin b/PowerControl/Resources/CRU_SteamDeck.bin new file mode 100644 index 0000000000000000000000000000000000000000..575bc657d57859b045b2a96b4667007fcddc82c0 GIT binary patch literal 512 zcmZSh4+ad8(-;jH85kJkWEfeNa`9J)0oe$^$bbs@r4>937<2*@M3`7kH~{s@EfYl- z@fJZB;Sxd@VU}iMW??wVk1pQ9i!O43Kf*)dfQ|vk#%UjbE|W{=lYxu0gGIb~foc?> zYEFREu!}N)-01+)bpYrG204VEz^nrx`}w6W%uoROrU0mF3)nZ4*e7@hH0UUR^rb8S znRHpO!2_rUY|;X-NtOZ*aFGdM5fiZs9tsT*HCq%wwtW#^;i2FF5m_aG#s1kK`)_eP vfZJ99wymB|0q(aBu-_I-D+m}eXaraUSTTS@T&9B;-PK$|=ptZ0;v@6{cHL3| literal 0 HcmV?d00001 diff --git a/RELEASE.md b/RELEASE.md index 0c03f04..d1815e9 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -9,6 +9,7 @@ ## 0.6.x +- PowerControl: Install custom resolutions (EDID) (experimental feature) - SteamController: Add `X360: No Touchpads` profile - All: Show `Missing RTSS` button to install RTSS - PowerControl: Retain FPS Limit (proportion) on refresh rate change