Add GPU scaling and Vivid Gaming selection

This commit is contained in:
Kamil Trzciński 2022-11-21 21:27:33 +01:00
parent ed6cc9ea14
commit af52e41aef
9 changed files with 765 additions and 50 deletions

View file

@ -0,0 +1,272 @@
#region Copyright
/*******************************************************************************
Copyright(c) 2008 - 2022 Advanced Micro Devices, Inc. All Rights Reserved.
Copyright (c) 2002 - 2006 ATI Technologies Inc. All Rights Reserved.
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDED BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
File: ADL.cs
Purpose: Implements ADL interface
Description: Implements some of the methods defined in ADL interface.
********************************************************************************/
#endregion Copyright
#region Using
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Threading;
using FARPROC = System.IntPtr;
using HMODULE = System.IntPtr;
#endregion Using
#region ATI.ADL
namespace PowerControl.Helpers.AMD
{
#region Export Struct
#region ADLAdapterInfo
/// <summary> ADLAdapterInfo Structure</summary>
[StructLayout(LayoutKind.Sequential)]
internal struct ADLAdapterInfo
{
/// <summary>The size of the structure</summary>
int Size;
/// <summary> Adapter Index</summary>
internal int AdapterIndex;
/// <summary> Adapter UDID</summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)ADL.ADL_MAX_PATH)]
internal string UDID;
/// <summary> Adapter Bus Number</summary>
internal int BusNumber;
/// <summary> Adapter Driver Number</summary>
internal int DriverNumber;
/// <summary> Adapter Function Number</summary>
internal int FunctionNumber;
/// <summary> Adapter Vendor ID</summary>
internal int VendorID;
/// <summary> Adapter Adapter name</summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)ADL.ADL_MAX_PATH)]
internal string AdapterName;
/// <summary> Adapter Display name</summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)ADL.ADL_MAX_PATH)]
internal string DisplayName;
/// <summary> Adapter Present status</summary>
internal int Present;
/// <summary> Adapter Exist status</summary>
internal int Exist;
/// <summary> Adapter Driver Path</summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)ADL.ADL_MAX_PATH)]
internal string DriverPath;
/// <summary> Adapter Driver Ext Path</summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)ADL.ADL_MAX_PATH)]
internal string DriverPathExt;
/// <summary> Adapter PNP String</summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)ADL.ADL_MAX_PATH)]
internal string PNPString;
/// <summary> OS Display Index</summary>
internal int OSDisplayIndex;
}
/// <summary> ADLAdapterInfo Array</summary>
[StructLayout(LayoutKind.Sequential)]
internal struct ADLAdapterInfoArray
{
/// <summary> ADLAdapterInfo Array </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int)ADL.ADL_MAX_ADAPTERS)]
internal ADLAdapterInfo[] ADLAdapterInfo;
}
#endregion ADLAdapterInfo
#region ADLDisplayInfo
/// <summary> ADLDisplayID Structure</summary>
[StructLayout(LayoutKind.Sequential)]
internal struct ADLDisplayID
{
/// <summary> Display Logical Index </summary>
internal int DisplayLogicalIndex;
/// <summary> Display Physical Index </summary>
internal int DisplayPhysicalIndex;
/// <summary> Adapter Logical Index </summary>
internal int DisplayLogicalAdapterIndex;
/// <summary> Adapter Physical Index </summary>
internal int DisplayPhysicalAdapterIndex;
}
/// <summary> ADLDisplayInfo Structure</summary>
[StructLayout(LayoutKind.Sequential)]
internal struct ADLDisplayInfo
{
/// <summary> Display Index </summary>
internal ADLDisplayID DisplayID;
/// <summary> Display Controller Index </summary>
internal int DisplayControllerIndex;
/// <summary> Display Name </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)ADL.ADL_MAX_PATH)]
internal string DisplayName;
/// <summary> Display Manufacturer Name </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)ADL.ADL_MAX_PATH)]
internal string DisplayManufacturerName;
/// <summary> Display Type : < The Display type. CRT, TV,CV,DFP are some of display types,</summary>
internal int DisplayType;
/// <summary> Display output type </summary>
internal int DisplayOutputType;
/// <summary> Connector type</summary>
internal int DisplayConnector;
///<summary> Indicating the display info bits' mask.<summary>
internal int DisplayInfoMask;
///<summary> Indicating the display info value.<summary>
internal int DisplayInfoValue;
}
/// <summary> ADLAdapterInfo Array</summary>
[StructLayout(LayoutKind.Sequential)]
internal class ADLDisplayInfoArray
{
/// <summary> ADLAdapterInfo Array </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (int)ADL.ADL_MAX_DISPLAYS)]
internal ADLDisplayInfo[] ADLAdapterInfo;
}
#endregion ADLDisplayInfo
#endregion Export Struct
#region ADL Class
/// <summary> ADL Class</summary>
internal static class ADL
{
internal const int ADL_DEFAULT_ADAPTER = 0;
internal const int ADL_DEFAULT_DISPLAY = 0;
#region Internal Constant
/// <summary> Define the maximum path</summary>
internal const int ADL_MAX_PATH = 256;
/// <summary> Define the maximum adapters</summary>
internal const int ADL_MAX_ADAPTERS = 40 /* 150 */;
/// <summary> Define the maximum displays</summary>
internal const int ADL_MAX_DISPLAYS = 40 /* 150 */;
/// <summary> Define the maximum device name length</summary>
internal const int ADL_MAX_DEVICENAME = 32;
/// <summary> Define the successful</summary>
internal const int ADL_SUCCESS = 0;
/// <summary> Define the failure</summary>
internal const int ADL_FAIL = -1;
/// <summary> Define the driver ok</summary>
internal const int ADL_DRIVER_OK = 0;
/// <summary> Maximum number of GL-Sync ports on the GL-Sync module </summary>
internal const int ADL_MAX_GLSYNC_PORTS = 8;
/// <summary> Maximum number of GL-Sync ports on the GL-Sync module </summary>
internal const int ADL_MAX_GLSYNC_PORT_LEDS = 8;
/// <summary> Maximum number of ADLMOdes for the adapter </summary>
internal const int ADL_MAX_NUM_DISPLAYMODES = 1024;
internal const int ADL_DISPLAY_DISPLAYINFO_DISPLAYCONNECTED = 0x00000001;
#endregion Internal Constant
#region Internal Constant
/// <summary> Atiadlxx_FileName </summary>
internal const string Atiadlxx_FileName = "atiadlxx.dll";
/// <summary> Kernel32_FileName </summary>
internal const string Kernel32_FileName = "kernel32.dll";
#endregion Internal Constant
#region Export Delegates
/// <summary> ADL Memory allocation function allows ADL to callback for memory allocation</summary>
/// <param name="size">input size</param>
/// <returns> retrun ADL Error Code</returns>
internal delegate IntPtr ADL_Main_Memory_Alloc(int size);
#endregion
#region DLLImport
[DllImport(Kernel32_FileName)]
internal static extern HMODULE GetModuleHandle (string moduleName);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL2_Main_Control_Create(ADL_Main_Memory_Alloc callback, int enumConnectedAdapters, out IntPtr context);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL2_Main_Control_Destroy (IntPtr context);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL2_Main_Control_IsFunctionValid (IntPtr context, HMODULE module, string procName);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL2_Adapter_NumberOfAdapters_Get (IntPtr context, out int numAdapters);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL2_Adapter_AdapterInfo_Get (IntPtr context, out ADLAdapterInfoArray info, int inputSize);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL2_Adapter_Active_Get(IntPtr context, int adapterIndex, out int status);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL2_Display_DisplayInfo_Get(IntPtr context, int adapterIndex, out int numDisplays, out IntPtr displayInfoArray, int forceDetect);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL2_DFP_GPUScalingEnable_Get(IntPtr context, int adapterIndex, int displayIndex, out int support, out int current, out int default_);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL2_DFP_GPUScalingEnable_Set(IntPtr context, int adapterIndex, int displayIndex, int current);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL2_Display_PreservedAspectRatio_Get(IntPtr context, int adapterIndex, int displayIndex, out int support, out int current, out int default_);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL2_Display_PreservedAspectRatio_Set(IntPtr context, int adapterIndex, int displayIndex, int current);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL2_Display_ImageExpansion_Get(IntPtr context, int adapterIndex, int displayIndex, out int support, out int current, out int default_);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL2_Display_ImageExpansion_Set(IntPtr context, int adapterIndex, int displayIndex, int current);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL2_Display_SCE_State_Get(IntPtr context, int adapterIndex, int displayIndex, out int current, out int support, out int default_);
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL2_Display_SCE_State_Set(IntPtr context, int adapterIndex, int displayIndex, int current);
#endregion DLLImport
#region ADL_Main_Memory_Alloc
/// <summary> Build in memory allocation function</summary>
/// <param name="size">input size</param>
/// <returns>return the memory buffer</returns>
internal static IntPtr ADL_Main_Memory_Alloc_(int size)
{
IntPtr result = Marshal.AllocCoTaskMem(size);
return result;
}
#endregion ADL_Main_Memory_Alloc
#region ADL_Main_Memory_Free
/// <summary> Build in memory free function</summary>
/// <param name="buffer">input buffer</param>
internal static void ADL_Main_Memory_Free(IntPtr buffer)
{
if (IntPtr.Zero != buffer)
{
Marshal.FreeCoTaskMem(buffer);
}
}
#endregion ADL_Main_Memory_Free
}
#endregion ADL Class
}
#endregion ATI.ADL

View file

@ -0,0 +1,156 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace PowerControl.Helpers.AMD
{
internal class ADLContext : IDisposable
{
public IntPtr Context { get; private set; }
public ADLContext()
{
IntPtr context = IntPtr.Zero;
WithSafe(() => ADL.ADL2_Main_Control_Create(ADL.ADL_Main_Memory_Alloc_, 1, out context), true);
Context = context;
}
public bool IsValid
{
get { return Context != IntPtr.Zero; }
}
public ADLAdapterInfo[]? AdapterInfos
{
get
{
int res = ADL.ADL2_Adapter_NumberOfAdapters_Get(Context, out var numAdapters);
if (res != 0)
return null;
res = ADL.ADL2_Adapter_AdapterInfo_Get(Context,
out var adapters,
Marshal.SizeOf<ADLAdapterInfoArray>());
if (res != 0)
return null;
return adapters.ADLAdapterInfo.Take(numAdapters).ToArray();
}
}
public IEnumerable<ADLDisplayInfo> DisplayInfos
{
get
{
foreach (var adapter in AdapterInfos)
{
if (adapter.Present == 0)
continue;
foreach (var display in GetDisplayInfos(adapter.AdapterIndex))
{
if ((display.DisplayInfoValue & ADL.ADL_DISPLAY_DISPLAYINFO_DISPLAYCONNECTED) != ADL.ADL_DISPLAY_DISPLAYINFO_DISPLAYCONNECTED)
continue;
yield return display;
}
}
}
}
public IEnumerable<ADLDisplayInfo> GetDisplayInfos(int adapterIndex)
{
int res = ADL.ADL2_Display_DisplayInfo_Get(Context, adapterIndex, out var numDisplays, out var displays, 0);
if (res != 0)
yield break;
try
{
int sizeOf = Marshal.SizeOf<ADLDisplayInfo>();
for (int i = 0; i < numDisplays; i++)
{
var display = Marshal.PtrToStructure<ADLDisplayInfo>(IntPtr.Add(displays, i * sizeOf));
// TODO: why even though we pass adapterIndex?
if (display.DisplayID.DisplayLogicalAdapterIndex != adapterIndex)
continue;
yield return display;
}
}
finally
{
ADL.ADL_Main_Memory_Free(displays);
}
}
~ADLContext()
{
Dispose();
}
public void Dispose()
{
GC.SuppressFinalize(this);
if (Context != IntPtr.Zero)
{
WithSafe(() => ADL.ADL2_Main_Control_Destroy(Context));
Context = IntPtr.Zero;
}
}
private static ADLContext? instance;
private static readonly Mutex mutex = new Mutex();
public static T? WithSafe<T>(Func<ADLContext, T?> func, bool useInstance = false)
{
if (useInstance)
{
if (!mutex.WaitOne(200))
return default;
try
{
if (instance == null)
instance = new ADLContext();
return instance.WithSafe(() => func(instance));
}
finally
{
mutex.ReleaseMutex();
}
}
else
{
using(var context = new ADLContext())
{
return context.WithSafe(() => func(context));
}
}
}
public T? WithSafe<T>(Func<T?> func, bool force = false)
{
if (!IsValid && !force)
return default;
try
{
return func();
}
catch (DllNotFoundException e) { TraceLine("ADL: Method not found: {0}", e); }
catch (EntryPointNotFoundException e) { TraceLine("ADL: Entry point not found: {0}", e); }
catch (Exception e) { TraceLine("ADL: Generic Exception: {0}", e); }
return default;
}
private static void TraceLine(string format, params object?[]? arg)
{
Trace.WriteLine(string.Format(format, arg));
}
}
}

View file

@ -1,44 +0,0 @@
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PowerControl.Helpers.GPU
{
internal class AMDAdrenaline
{
// TODO: This CLSID is likely to change over time and be broken
// pnputil /enum-devices /class Display
const string GPUDriverKey = "SYSTEM\\CurrentControlSet\\Control\\Class\\{4d36e968-e325-11ce-bfc1-08002be10318}\\0000";
const string DriverDesc = "DriverDesc";
const string ExpectedDriverDesc = "AMD Custom GPU 0405";
const string GPUScaling = "GPUScaling00";
internal static bool IsGPUScalingEnabled()
{
try
{
var registry = Registry.LocalMachine.OpenSubKey(GPUDriverKey);
if (registry == null)
return false;
var driverDesc = registry.GetValue(DriverDesc);
if (driverDesc is string && (string)driverDesc != ExpectedDriverDesc)
return false;
var scalingBytes = registry.GetValue(GPUScaling);
if (scalingBytes is not byte[])
return false;
var scaling = BitConverter.ToUInt32((byte[])scalingBytes);
return scaling == 1;
}
catch
{
return false;
}
}
}
}

View file

@ -0,0 +1,56 @@
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PowerControl.Helpers.AMD
{
internal class DCE
{
internal enum Mode
{
Normal = 1,
Vivid
}
internal static Mode? Current
{
get
{
return ADLContext.WithSafe((context) =>
{
int res = ADL.ADL2_Display_SCE_State_Get(
context.Context,
ADL.ADL_DEFAULT_ADAPTER,
ADL.ADL_DEFAULT_DISPLAY,
out var current, out var support, out _);
if (res == 0 && support == 1)
return current == 2 ? Mode.Vivid : Mode.Normal;
return (DCE.Mode?)null;
});
}
set
{
ADLContext.WithSafe((context) =>
{
if (value is null)
return false;
ADL.ADL2_Display_SCE_State_Set(
context.Context,
ADL.ADL_DEFAULT_ADAPTER,
ADL.ADL_DEFAULT_DISPLAY,
(int)value
);
return true;
});
}
}
}
}

View file

@ -0,0 +1,184 @@
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PowerControl.Helpers.AMD
{
internal class GPUScaling
{
public enum ScalingMode
{
AspectRatio,
FullPanel,
Centered
}
internal static bool SafeResolutionChange
{
get { return Enabled; }
}
internal static bool IsSupported
{
get
{
return ADLContext.WithSafe((context) =>
{
int res = ADL.ADL2_DFP_GPUScalingEnable_Get(
context.Context,
ADL.ADL_DEFAULT_ADAPTER,
ADL.ADL_DEFAULT_DISPLAY,
out var support, out _, out _);
if (res == 0 && support == 1)
return true;
return false;
});
}
}
internal static bool Enabled
{
get
{
return ADLContext.WithSafe((context) =>
{
int res = ADL.ADL2_DFP_GPUScalingEnable_Get(
context.Context,
ADL.ADL_DEFAULT_ADAPTER,
ADL.ADL_DEFAULT_DISPLAY,
out var support, out var current, out _);
if (res == 0 && support == 1 && current == 1)
return true;
return false;
});
}
set
{
ADLContext.WithSafe((context) =>
{
ADL.ADL2_DFP_GPUScalingEnable_Get(
context.Context,
ADL.ADL_DEFAULT_ADAPTER,
ADL.ADL_DEFAULT_DISPLAY,
out var _, out var current, out _);
if (current == (value ? 1 : 0))
return true;
ADL.ADL2_DFP_GPUScalingEnable_Set(
context.Context,
ADL.ADL_DEFAULT_ADAPTER,
ADL.ADL_DEFAULT_DISPLAY,
value ? 1 : 0
);
return true;
});
}
}
internal static ScalingMode? Mode
{
get
{
return ADLContext.WithSafe((context) =>
{
int resAR = ADL.ADL2_Display_PreservedAspectRatio_Get(
context.Context,
ADL.ADL_DEFAULT_ADAPTER,
ADL.ADL_DEFAULT_DISPLAY,
out var supportAR, out var ar, out _);
int resIE = ADL.ADL2_Display_ImageExpansion_Get(
context.Context,
ADL.ADL_DEFAULT_ADAPTER,
ADL.ADL_DEFAULT_DISPLAY,
out var supportIE, out var ie, out _);
if (resAR != 0 || resIE != 0)
return default(ScalingMode?);
TraceLine("GPUScaling: ar={0}, ie={1}",
supportAR > 0 ? ar : -1, supportIE > 0 ? ie : -1);
if (ar == 1)
return ScalingMode.AspectRatio;
else if (ie == 1)
return ScalingMode.FullPanel;
else if (ie == 0 && supportIE == 1)
return ScalingMode.Centered;
return default(ScalingMode?);
});
}
set
{
ADLContext.WithSafe((context) =>
{
int resGS = ADL.ADL2_DFP_GPUScalingEnable_Get(
context.Context,
ADL.ADL_DEFAULT_ADAPTER,
ADL.ADL_DEFAULT_DISPLAY,
out var _, out var current, out _);
if (current == 0)
{
resGS = ADL.ADL2_DFP_GPUScalingEnable_Set(
context.Context,
ADL.ADL_DEFAULT_ADAPTER,
ADL.ADL_DEFAULT_DISPLAY,
1
);
}
int resAR = -1;
int resIE = -1;
switch (value)
{
case ScalingMode.FullPanel:
resIE = ADL.ADL2_Display_ImageExpansion_Set(
context.Context,
ADL.ADL_DEFAULT_ADAPTER,
ADL.ADL_DEFAULT_DISPLAY,
1
);
break;
case ScalingMode.AspectRatio:
resAR = ADL.ADL2_Display_PreservedAspectRatio_Set(
context.Context,
ADL.ADL_DEFAULT_ADAPTER,
ADL.ADL_DEFAULT_DISPLAY,
1
);
break;
case ScalingMode.Centered:
resIE = ADL.ADL2_Display_ImageExpansion_Set(
context.Context,
ADL.ADL_DEFAULT_ADAPTER,
ADL.ADL_DEFAULT_DISPLAY,
0
);
break;
}
TraceLine("GPUScaling: mode={0} => resAR={1}, resIE={2}, resGS={3}",
value, resAR, resIE, resGS);
return true;
});
}
}
private static void TraceLine(string format, params object?[]? arg)
{
Trace.WriteLine(string.Format(format, arg));
}
}
}

View file

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PowerControl.Helpers.AMD
{
public class RadeonSoftware
{
public static void Kill()
{
foreach (var process in Process.GetProcessesByName("RadeonSoftware.exe"))
{
try { process.Kill(); }
catch { }
}
}
}
}

View file

@ -56,7 +56,7 @@ namespace PowerControl.Helpers
}
}
private static DEVMODE? CurrentDisplaySettings()
internal static DEVMODE? CurrentDisplaySettings()
{
DEVMODE dm = new DEVMODE();
if (EnumDisplaySettings(null, ENUM_CURRENT_SETTINGS, ref dm))
@ -64,6 +64,17 @@ namespace PowerControl.Helpers
return null;
}
public static bool SetDisplaySettings(DEVMODE? best)
{
if (best == null)
return false;
DEVMODE dm = best.Value;
var dispChange = ChangeDisplaySettingsEx(null, ref dm, IntPtr.Zero, ChangeDisplaySettingsFlags.CDS_NONE, IntPtr.Zero);
return dispChange == DISP_CHANGE.Successful;
}
public static DisplayResolution[] GetAllResolutions()
{
return FindAllDisplaySettings()
@ -174,7 +185,7 @@ namespace PowerControl.Helpers
}
[Flags()]
enum DM : int
internal enum DM : int
{
Orientation = 0x1,
PaperSize = 0x2,
@ -208,14 +219,14 @@ namespace PowerControl.Helpers
DisplayFixedOutput = 0x20000000
}
struct POINTL
internal struct POINTL
{
public Int32 x;
public Int32 y;
};
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
struct DEVMODE
internal struct DEVMODE
{
public const int CCHDEVICENAME = 32;
public const int CCHFORMNAME = 32;

View file

@ -1,7 +1,9 @@
using CommonHelpers;
using PowerControl.Helpers;
using PowerControl.Helpers.AMD;
using PowerControl.Helpers.GPU;
using System.Diagnostics;
using static PowerControl.Helpers.AMD.DCE;
namespace PowerControl
{
@ -45,12 +47,13 @@ namespace PowerControl
return Helpers.AudioManager.GetMasterVolume(5.0);
}
},
new Menu.MenuItemSeparator(),
new Menu.MenuItemWithOptions()
{
Name = "Resolution",
ApplyDelay = 1000,
ResetValue = () => {
if (!AMDAdrenaline.IsGPUScalingEnabled() && !Settings.Default.EnableExperimentalFeatures)
if (!GPUScaling.SafeResolutionChange && !Settings.Default.EnableExperimentalFeatures)
return null;
return DisplayResolutionController.GetAllResolutions().Last();
},
@ -63,7 +66,7 @@ namespace PowerControl
},
CurrentValue = delegate()
{
if (!AMDAdrenaline.IsGPUScalingEnabled() && !Settings.Default.EnableExperimentalFeatures)
if (!GPUScaling.SafeResolutionChange && !Settings.Default.EnableExperimentalFeatures)
return null;
return DisplayResolutionController.GetResolution();
},
@ -148,6 +151,61 @@ namespace PowerControl
}
},
new Menu.MenuItemWithOptions()
{
Name = "GPU Scaling",
ApplyDelay = 1000,
Options = Enum.GetValues<GPUScaling.ScalingMode>().Cast<object>().Prepend("Off").ToArray(),
CurrentValue = delegate()
{
if (!GPUScaling.IsSupported)
return null;
if (!GPUScaling.Enabled)
return "Off";
return GPUScaling.Mode;
},
ApplyValue = delegate(object selected)
{
if (!GPUScaling.IsSupported)
return null;
if (selected is GPUScaling.ScalingMode)
GPUScaling.Mode = (GPUScaling.ScalingMode)selected;
else
GPUScaling.Enabled = false;
// Since the RadeonSoftware will try to revert values
RadeonSoftware.Kill();
Root["Resolution"].Update();
Root["Refresh Rate"].Update();
Root["FPS Limit"].Update();
if (!GPUScaling.Enabled)
return "Off";
return GPUScaling.Mode;
}
},
new Menu.MenuItemWithOptions()
{
Name = "Colors",
ApplyDelay = 1000,
Options = Enum.GetValues<DCE.Mode>().Cast<object>().ToList(),
CurrentValue = delegate()
{
return DCE.Current;
},
ApplyValue = delegate(object selected)
{
if (DCE.Current is null)
return null;
DCE.Current = (DCE.Mode)selected;
RadeonSoftware.Kill();
return DCE.Current;
}
},
new Menu.MenuItemSeparator(),
new Menu.MenuItemWithOptions()
{
Name = "TDP",
Options = { "3W", "4W", "5W", "6W", "7W", "8W", "10W", "12W", "15W" },

View file

@ -8,5 +8,6 @@
- Show Full OSD if in PowerControl mode
- Highly risky: Allow to change CPU and GPU frequency (enable `EnableExperimentalFeatures` in `PowerControl.dll.config`)
- Show CPU/GPU frequency in Full overlay
- Allow to control GPU Scaling and Display Color Correction
If you found it useful buy me [Ko-fi](https://ko-fi.com/ayufan).