Skip to content

Commit 862d7af

Browse files
committed
PowerControl: Install custom resolutions (EDID) (experimental feature)
1 parent fd93c05 commit 862d7af

File tree

7 files changed

+131
-0
lines changed

7 files changed

+131
-0
lines changed

PowerControl/Controller.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,34 @@ public Controller()
6969
rootMenu.VisibleChanged += delegate { updateOSD(); };
7070
contextMenu.Items.Add(new ToolStripSeparator());
7171

72+
if (Settings.Default.EnableExperimentalFeatures)
73+
{
74+
var installEDIDItem = contextMenu.Items.Add("Install &Resolutions");
75+
installEDIDItem.Click += delegate { Helpers.AMD.EDID.SetEDID(Resources.CRU_SteamDeck); };
76+
var replaceEDIDItem = contextMenu.Items.Add("Replace &Resolutions");
77+
replaceEDIDItem.Click += delegate { Helpers.AMD.EDID.SetEDID(new byte[0]); Helpers.AMD.EDID.SetEDID(Resources.CRU_SteamDeck); };
78+
var uninstallEDIDItem = contextMenu.Items.Add("Revert &Resolutions");
79+
uninstallEDIDItem.Click += delegate { Helpers.AMD.EDID.SetEDID(new byte[0]); };
80+
contextMenu.Opening += delegate
81+
{
82+
if (ExternalHelpers.DisplayConfig.IsInternalConnected == true)
83+
{
84+
var edid = Helpers.AMD.EDID.GetEDID() ?? new byte[0];
85+
var edidInstalled = Resources.CRU_SteamDeck.SequenceEqual(edid);
86+
installEDIDItem.Visible = edid.Length <= 128;
87+
replaceEDIDItem.Visible = !edidInstalled && edid.Length > 128;
88+
uninstallEDIDItem.Visible = edid.Length > 128;
89+
}
90+
else
91+
{
92+
installEDIDItem.Visible = false;
93+
replaceEDIDItem.Visible = false;
94+
uninstallEDIDItem.Visible = false;
95+
}
96+
};
97+
contextMenu.Items.Add(new ToolStripSeparator());
98+
}
99+
72100
if (startupManager.IsAvailable)
73101
{
74102
var startupItem = new ToolStripMenuItem("Run On Startup");

PowerControl/Helpers/AMD/ADL.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,30 @@ internal class ADLDisplayInfoArray
143143
}
144144
#endregion ADLDisplayInfo
145145

146+
[StructLayout(LayoutKind.Sequential)]
147+
internal struct ADLDisplayEDIDData
148+
{
149+
internal int iSize;
150+
internal int iFlag;
151+
internal int iEDIDSize;
152+
internal int iBlockIndex;
153+
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int)ADL.ADL_MAX_EDIDDATA_SIZE)]
154+
internal byte[] cEDIDData;
155+
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
156+
internal int[] iReserved;
157+
};
158+
159+
[StructLayout(LayoutKind.Sequential)]
160+
internal struct ADLDisplayEDIDDataX2
161+
{
162+
internal int iSize;
163+
internal int iFlag;
164+
internal int iEDIDSize;
165+
internal int iIgnored;
166+
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int)2 * ADL.ADL_MAX_EDIDDATA_SIZE)]
167+
internal byte[] cEDIDData;
168+
};
169+
146170
#region Radeon Image Sharpening
147171
[StructLayout(LayoutKind.Sequential)]
148172
internal struct ADL_RIS_SETTINGS
@@ -192,6 +216,8 @@ internal static class ADL
192216
internal const int ADL_MAX_GLSYNC_PORT_LEDS = 8;
193217
/// <summary> Maximum number of ADLMOdes for the adapter </summary>
194218
internal const int ADL_MAX_NUM_DISPLAYMODES = 1024;
219+
internal const int ADL_MAX_EDIDDATA_SIZE = 256;
220+
internal const int ADL_MAX_EDID_EXTENSION_BLOCKS = 3;
195221

196222
internal const int ADL_DISPLAY_DISPLAYINFO_DISPLAYCONNECTED = 0x00000001;
197223

@@ -265,6 +291,12 @@ internal static class ADL
265291

266292
[DllImport(Atiadlxx_FileName)]
267293
internal static extern int ADL2_RIS_Settings_Set(IntPtr context, int adapterIndex, ADL_RIS_SETTINGS settings, ADL_RIS_NOTFICATION_REASON reason);
294+
295+
[DllImport(Atiadlxx_FileName)]
296+
internal static extern int ADL2_Display_EdidData_Get(IntPtr context, int adapterIndex, int displayIndex, ref ADLDisplayEDIDData edidData);
297+
298+
[DllImport(Atiadlxx_FileName)]
299+
internal static extern int ADL2_Display_EdidData_Set(IntPtr context, int adapterIndex, int displayIndex, ref ADLDisplayEDIDDataX2 edidData);
268300
#endregion DLLImport
269301

270302
#region ADL_Main_Memory_Alloc

PowerControl/Helpers/AMD/EDID.cs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
using System.Linq;
2+
using System.Runtime.InteropServices;
3+
4+
namespace PowerControl.Helpers.AMD
5+
{
6+
internal class EDID
7+
{
8+
internal static byte[]? GetEDID(int displayIndex = ADL.ADL_DEFAULT_DISPLAY)
9+
{
10+
return ADLContext.WithSafe((context) =>
11+
{
12+
byte[] edid = new byte[0];
13+
14+
for (int block = 0; block < ADL.ADL_MAX_EDID_EXTENSION_BLOCKS; ++block)
15+
{
16+
ADLDisplayEDIDData displayEdidData = new ADLDisplayEDIDData()
17+
{
18+
iSize = Marshal.SizeOf<ADLDisplayEDIDData>(),
19+
iBlockIndex = block,
20+
};
21+
22+
int res = ADL.ADL2_Display_EdidData_Get(context.Context, ADL.ADL_DEFAULT_ADAPTER, ADL.ADL_DEFAULT_DISPLAY, ref displayEdidData);
23+
if (res != 0)
24+
break;
25+
26+
var blockBytes = displayEdidData.cEDIDData.Take(displayEdidData.iEDIDSize);
27+
edid = edid.Concat(blockBytes).ToArray();
28+
}
29+
30+
return edid;
31+
});
32+
}
33+
34+
internal static bool? SetEDID(byte[] value, int displayIndex = ADL.ADL_DEFAULT_DISPLAY)
35+
{
36+
return ADLContext.WithSafe((context) =>
37+
{
38+
var bytes = new byte[ADL.ADL_MAX_EDIDDATA_SIZE * 2];
39+
value.CopyTo(bytes, 0);
40+
41+
var blockData = new ADLDisplayEDIDDataX2()
42+
{
43+
// TODO: Hack to send a full EDID at once
44+
iSize = Marshal.SizeOf<ADLDisplayEDIDData>(),
45+
cEDIDData = bytes,
46+
iEDIDSize = value.Length,
47+
};
48+
49+
int res = ADL.ADL2_Display_EdidData_Set(context.Context,
50+
ADL.ADL_DEFAULT_ADAPTER, ADL.ADL_DEFAULT_DISPLAY,
51+
ref blockData);
52+
53+
return res == 0;
54+
});
55+
}
56+
}
57+
}

PowerControl/Resources.Designer.cs

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

PowerControl/Resources.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,9 @@
118118
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
119119
</resheader>
120120
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
121+
<data name="CRU_SteamDeck" type="System.Resources.ResXFileRef, System.Windows.Forms">
122+
<value>Resources\CRU_SteamDeck.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
123+
</data>
121124
<data name="traffic-light-outline_light" type="System.Resources.ResXFileRef, System.Windows.Forms">
122125
<value>Resources\traffic-light-outline_light.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
123126
</data>
512 Bytes
Binary file not shown.

RELEASE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
## 0.6.x
1111

12+
- PowerControl: Install custom resolutions (EDID) (experimental feature)
1213
- SteamController: Add `X360: No Touchpads` profile
1314
- All: Show `Missing RTSS` button to install RTSS
1415
- PowerControl: Retain FPS Limit (proportion) on refresh rate change

0 commit comments

Comments
 (0)