0.4.2.10 Formal Release

This commit is contained in:
ResetXPDR 2024-04-02 07:50:19 +11:00
parent 91c808b782
commit 2375fdb80f
19 changed files with 1244 additions and 413 deletions

View File

@ -136,6 +136,11 @@ namespace Installer
string path = Parameters.msExeSteam;
if (!File.Exists(path))
path = Parameters.msExeStore;
if (!File.Exists(path))
{
MessageBox.Show($"Required EXE.xml file not found for AutoStartExe. See readme for resolution.", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
return false;
}
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(File.ReadAllText(path));

View File

@ -21,7 +21,7 @@ namespace Installer
InitializeComponent();
string assemblyVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString();
assemblyVersion = assemblyVersion.Substring(0, assemblyVersion.LastIndexOf('.'));
assemblyVersion = assemblyVersion.Substring(0, assemblyVersion.LastIndexOf('.')) + "";
Title += " (" + assemblyVersion + ")";
if (Directory.Exists(Parameters.appDir))

View File

@ -49,5 +49,5 @@ using System.Windows;
// Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden,
// indem Sie "*" wie unten gezeigt eingeben:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.4.1.0")]
[assembly: AssemblyFileVersion("0.4.1.0")]
[assembly: AssemblyVersion("0.4.2.0")]
[assembly: AssemblyFileVersion("0.4.2.0")]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 401 KiB

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

View File

@ -5,6 +5,7 @@ using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
@ -14,12 +15,14 @@ namespace MSFS2020_AutoFPS
{
private ServiceModel Model;
private ServiceController Controller;
protected int Interval = 1000;
private TaskbarIcon notifyIcon;
public static new App Current => Application.Current as App;
public static string ConfigFile = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\MSFS2020_AutoFPS\MSFS2020_AutoFPS.config";
public static string AppDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\MSFS2020_AutoFPS\bin";
public static string MSFSDefaultsFile = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\MSFS2020_AutoFPS\MSFSDefaults.config";
protected override void OnStartup(StartupEventArgs e)
{
@ -45,6 +48,13 @@ namespace MSFS2020_AutoFPS
Application.Current.Shutdown();
return;
}
if (Process.GetProcessesByName("SmoothFlight").Length > 0)
{
MessageBox.Show("SmoothFlight is already running!", "Critical Error", MessageBoxButton.OK, MessageBoxImage.Error);
Application.Current.Shutdown();
return;
}
Directory.SetCurrentDirectory(AppDir);
if (!File.Exists(ConfigFile))
@ -86,6 +96,7 @@ namespace MSFS2020_AutoFPS
if (Model != null)
{
Model.CancellationRequested = true;
Thread.Sleep(Interval); // Ensure Runtick finishes its last MSFS settings changes
if (Model.DefaultSettingsRead && Model.IsSessionRunning)
{
Logger.Log(LogLevel.Information, "App:OnExit", $"Resetting LODs to {Model.DefaultTLOD} / {Model.DefaultOLOD} and VR {Model.DefaultTLOD_VR} / {Model.DefaultOLOD_VR}");
@ -93,9 +104,18 @@ namespace MSFS2020_AutoFPS
Model.MemoryAccess.SetTLOD_VR(Model.DefaultTLOD_VR);
Model.MemoryAccess.SetOLOD_PC(Model.DefaultOLOD);
Model.MemoryAccess.SetOLOD_VR(Model.DefaultOLOD_VR);
Logger.Log(LogLevel.Information, "App:OnExit", $"Resetting cloud quality to {Model.DefaultCloudQ} / VR {Model.DefaultCloudQ_VR}");
Logger.Log(LogLevel.Information, "App:OnExit", $"Resetting cloud quality to {Model.CloudQualityText(Model.DefaultCloudQ)} / VR {Model.CloudQualityText(Model.DefaultCloudQ_VR)}");
Model.MemoryAccess.SetCloudQ(Model.DefaultCloudQ);
Model.MemoryAccess.SetCloudQ_VR(Model.DefaultCloudQ_VR);
if (Model.MemoryAccess.GetTLOD_PC() == Model.DefaultTLOD) // As long as one setting restoration stuck
{
Model.ConfigurationFile.RemoveSetting("defaultTLOD");
Model.ConfigurationFile.RemoveSetting("defaultTLOD_VR");
Model.ConfigurationFile.RemoveSetting("defaultOLOD");
Model.ConfigurationFile.RemoveSetting("defaultOLOD_VR");
Model.ConfigurationFile.RemoveSetting("defaultCloudQ");
Model.ConfigurationFile.RemoveSetting("defaultCloudQ_VR");
}
}
}
notifyIcon?.Dispose();

View File

@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.IO;
using System.Security.RightsManagement;
using System.Xml;
namespace MSFS2020_AutoFPS
@ -34,6 +35,11 @@ namespace MSFS2020_AutoFPS
xmlDoc.Save(App.ConfigFile);
}
public bool SettingExists(string key)
{
if (appSettings.ContainsKey(key)) return true;
else return false;
}
public string GetSetting(string key, string defaultValue = "")
{
if (appSettings.ContainsKey(key))
@ -58,6 +64,19 @@ namespace MSFS2020_AutoFPS
}
}
public void RemoveSetting(string key)
{
if (appSettings.ContainsKey(key))
{
XmlNode nodeToRemove = xmlDoc.SelectSingleNode($"//add[@key='{key}']");
if (nodeToRemove != null)
{
nodeToRemove.ParentNode.RemoveChild(nodeToRemove);
appSettings.Remove(key);
SaveConfiguration();
}
}
}
public void SetSetting(string key, string value)
{
if (appSettings.ContainsKey(key))

Binary file not shown.

View File

@ -0,0 +1,189 @@
/*
Here is a short overview of all fields accessible via the shared memory.
You can get an updated list if you call ListAllData(true) and ListAllSensors(true).
# DATA FIELDS #
[0]FillratePixel: 13.2
[1]SubvendorID: 1002
[2]GPURevision:
[3]MemType: GDDR3
[4]MemBusWidth: 256
[5]VendorID: 1002
[6]MultiGPUName: ATI CrossFire
[7]BusInterface: PCI-E x16 @ x16
[8]BIOSVersion: VER010.075.000.002.027526
[9]ClockShader:
[10]MultiGPU0: Enabled (2 GPUs) (unsure on Vista64)
[11]ShaderModel: 4.1
[12]ClockMem: 900
[13]DriverVersion: atiumdag 7.14.10.0590 / Vista64
[14]Vendor: ATI
[15]DeviceID: 950F
[16]ProcessSize: 55
[17]FillrateTexel: 13.2
[18]Subvendor: ATI
[19]NumShadersUnified: 320
[20]NumROPs: 16
[21]DieSize: 190
[22]ClockGPUDefault: 823
[23]MemSize: 512
[24]NumShadersVertex:
[25]DirectXSupport: 10.1
[26]CardName: ATI Radeon HD 3870 X2
[27]ClockShaderDefault:
[28]ClockMemDefault: 900
[29]MemBandwidth: 57.6
[30]ClockGPU: 823
[31]SubsysID: 2042
[32]GPUName: R680
# SENSOR FIELDS #
[0]GPU Core Clock: 823 MHz
[1]GPU Memory Clock: 900 MHz
[2]GPU Temperature: 56 °C
[3]Fan Speed: 40 %%
[4]GPU Load: 0 %%
[5]VDDC Current: 4,51612903225806 A
[6]VDDC Slave #1 Temperature: 60 °C
[7]VDDC Slave #2 Temperature: 60 °C
[8]VDDC: 1,3125 V
*/
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace MSFS2020_AutoFPS
{
class GpuzWrapper
{
[DllImport(@"GpuzShMem.x64.dll", SetLastError = true)]
public static extern int InitGpuzShMem();
[DllImport(@"GpuzShMem.x64.dll", SetLastError = true)]
public static extern int RemGpuzShMem();
[DllImport(@"GpuzShMem.x64.dll", SetLastError = true)]
public static extern IntPtr GetSensorName(int index);
[DllImport(@"GpuzShMem.x64.dll", SetLastError = true)]
public static extern double GetSensorValue(int index);
[DllImport(@"GpuzShMem.x64.dll", SetLastError = true)]
public static extern IntPtr GetSensorUnit(int index);
[DllImport(@"GpuzShMem.x64.dll", SetLastError = true)]
public static extern IntPtr GetDataKey(int index);
[DllImport(@"GpuzShMem.x64.dll", SetLastError = true)]
public static extern IntPtr GetDataValue(int index);
/// <summary>
/// Opens the shared memory interface for reading. Don't forget to close it if you don't need it anymore!
/// </summary>
/// <exception cref="Exception">If the shared memory could not be opened.</exception>
public bool Open()
{
if (InitGpuzShMem() != 0)
{
return false;
}
return true;
}
/// <summary>
/// Closes the shared memory interface.
/// </summary>
public void Close()
{
RemGpuzShMem();
}
/// <summary>
/// Gets the name of the specified sensor field (eg. "GPU Core Clock", "Fan Speed", ...).
/// </summary>
/// <param name="index">Index of sensor field needed.</param>
/// <returns>Name of the sensor field.</returns>
public string SensorName(int index)
{
return Marshal.PtrToStringUni(GetSensorName(index));
}
/// <summary>
/// Gets the value of the specified sensor field (eg. 900.0, 56.0, ...).
/// </summary>
/// <param name="index">Index of sensor field needed.</param>
/// <returns>Value of the sensor field.</returns>
public double SensorValue(int index)
{
return GetSensorValue(index);
}
/// <summary>
/// Gets the unit of the specified sensor field (e.g. "MHz", "°C", ...).
/// </summary>
/// <param name="index">Index of sensor field needed.</param>
/// <returns>Unit of the sensor field.</returns>
public string SensorUnit(int index)
{
return Marshal.PtrToStringUni(GetSensorUnit(index));
}
/// <summary>
/// Gets the key (=name) of the specified data field (eg. "FillratePixel", "Vendor", ...).
/// </summary>
/// <param name="index">Index of data field needed.</param>
/// <returns>Key of the data field.</returns>
public string DataKey(int index)
{
return Marshal.PtrToStringUni(GetDataKey(index));
}
/// <summary>
/// Gets the value of the specified data field (eg. "13.2", "ATI", ...).
/// </summary>
/// <param name="index">Index of data field needed.</param>
/// <returns>Value of the data field.</returns>
public string DataValue(int index)
{
return Marshal.PtrToStringUni(GetDataValue(index));
}
/// <summary>
/// Returns a list of all sensor fields available.
/// </summary>
/// <returns>A formated string of all sensor names, values and units, each triple a line.</returns>
public string ListAllSensors()
{
String s, res = String.Empty;
for (int i = 0; (s = SensorName(i)) != String.Empty; i++)
res += "[" + i + "]" + s + ": " + SensorValue(i) + " " + SensorUnit(i) + "\n";
return res;
}
/// <summary>
/// Returns a list of all data fields available.
/// </summary>
/// <returns>A formated string of all data keys and values, each pair a line.</returns>
public string ListAllData()
{
String s, res = String.Empty;
for (int i = 0; (s = DataKey(i)) != String.Empty; i++)
res += "[" + i + "]" + s + ": " + DataValue(i) + "\n";
return res;
}
}
}

View File

@ -1,10 +1,14 @@
using System;
using Microsoft.FlightSimulator.SimConnect;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Eventing.Reader;
using System.Drawing.Printing;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Media.Media3D;
@ -18,9 +22,32 @@ namespace MSFS2020_AutoFPS
private int[] verticalStats = new int[5];
private float[] verticalStatsVS = new float[5];
private int verticalIndex = 0;
private int groundSpeed = 0;
private float vs;
private const float CloudRecoveryExtraTolerance = 0.15f;
private const float MinTLODExtraActiveTolerance = 0.15f;
private const float AutoTargetFPSRecalTolerance = 0.20f;
private const float VFRMinAltTLODLocks = 100.0f;
private bool AutoFPSInFlightPhase = false;
private bool MinTLODExtraActiveForDescentPrimed = false;
private int CloudRecoveryExtraNonExpert = 0;
private int CloudRecoveryTickMark = 0;
private int TotalTicks = 0;
private float altAboveGnd = 0;
private float groundSpeed = 0;
private float altAboveGndLast = 0;
private float groundSpeedLast = 0;
private float TLODStep;
private float newTLOD;
private float TLODMinAltBand;
private float MinTLODNoExtra;
private float MinTLODExtraAmount;
private float AltTLODBase;
private const float FPSPriorityBaseAlt = 1000.0f;
GpuzWrapper gpuz = new GpuzWrapper();
int gpuzGPULoadSensorIndex;
public LODController(ServiceModel model)
{
Model = model;
@ -32,6 +59,23 @@ namespace MSFS2020_AutoFPS
SimConnect.SubscribeSimVar("SIM ON GROUND", "Bool");
SimConnect.SubscribeSimVar("GROUND VELOCITY", "knots");
GetMSFSState();
if (gpuz.Open())
{
Logger.Log(LogLevel.Information, "LODController:LODController", "GPU-Z companion app running");
for (gpuzGPULoadSensorIndex = 0; gpuz.SensorName(gpuzGPULoadSensorIndex) != String.Empty && gpuz.SensorName(gpuzGPULoadSensorIndex) != "GPU Load"; gpuzGPULoadSensorIndex++) ;
if (gpuz.SensorName(gpuzGPULoadSensorIndex) != "GPU Load")
{
Logger.Log(LogLevel.Information, "LODController:LODController", "GPU-Z GPU Load sensor not found");
gpuzGPULoadSensorIndex = -1;
}
else Logger.Log(LogLevel.Information, "LODController:LODController", "GPU-Z GPU Load sensor found");
}
else
{
Logger.Log(LogLevel.Information, "LODController:LODController", "GPU-Z companion app not running");
gpuzGPULoadSensorIndex = -1;
}
SetActiveLODs();
}
private void UpdateVariables()
@ -52,126 +96,172 @@ namespace MSFS2020_AutoFPS
Model.VerticalTrend = VerticalAverage();
Model.altAboveGnd = (int)SimConnect.ReadSimVar("PLANE ALT ABOVE GROUND", "feet");
Model.altAboveGnd = (int)(altAboveGnd = SimConnect.ReadSimVar("PLANE ALT ABOVE GROUND", "feet"));
if (Model.altAboveGnd == 0 && !Model.OnGround)
Model.altAboveGnd = (int)SimConnect.ReadSimVar("PLANE ALT ABOVE GROUND MINUS CG", "feet");
groundSpeed = (int)SimConnect.ReadSimVar("GROUND VELOCITY", "knots");
Model.groundSpeed = (int)(groundSpeed = SimConnect.ReadSimVar("GROUND VELOCITY", "knots"));
GetMSFSState();
if (gpuzGPULoadSensorIndex >= 0) Model.gpuUsage = (float)gpuz.SensorValue(gpuzGPULoadSensorIndex);
else Model.gpuUsage = -1;
if (groundSpeed > 1 && altAboveGnd == altAboveGndLast && groundSpeed == groundSpeedLast) Model.SimPaused = true;
else Model.SimPaused = false;
altAboveGndLast = altAboveGnd;
groundSpeedLast = groundSpeed;
if ((!Model.ActiveWindowMSFS && ((Model.UseExpertOptions && Model.PauseMSFSFocusLost) || (!Model.UseExpertOptions && Model.FgModeEnabled))) || Model.SimPaused) Model.AppPaused = true;
else Model.AppPaused = false;
SetActiveLODs();
}
public void RunTick()
{
UpdateVariables();
float TLODStep;
bool TLODMinGndLanding = true;
bool DecCloudQ = true;
float FPSTolerance = 5.0f;
float MinTLOD;
float MaxTLOD;
float newTLOD;
float CloudRecoveryTLOD = Model.DefaultTLOD - 1;
float OLODAtBase = Model.DefaultOLOD;
float AltOLODBase = 2000;
float OLODAtTop = Math.Max(Model.DefaultOLOD * 0.2f, 10.0f);
float AltOLODTop = 10000;
float TLODMinAltBand;
float AltTLODBase = 1000;
float AvgDescentRate = 2000;
const float FPSPriorityBaseAlt = 1000;
if (Model.UseExpertOptions)
{
FPSTolerance = (float)Model.FPSTolerance;
TLODMinGndLanding = Model.TLODMinGndLanding;
MinTLOD = Model.MinTLOD;
MaxTLOD = Model.MaxTLOD;
DecCloudQ = Model.DecCloudQ;
CloudRecoveryTLOD = Model.CloudRecoveryTLOD;
if (Model.TLODMinGndLanding)
{
AltTLODBase = Model.AltTLODBase;
AvgDescentRate = Model.AvgDescentRate;
}
}
if (Model.UseExpertOptions) AltTLODBase = Model.AltTLODBase[Model.activeProfile];
else
{
if (Model.VrModeActive)
AltTLODBase = Model.AltTLODBase[(int)ServiceModel.appProfiles.NonExpert] = Model.FlightTypeIFR ? 1000.0f : 100.0f;
Model.CloudRecoveryTLOD[Model.activeProfile] = (float)Math.Min(Math.Round(2 * (Model.activeMinTLOD + Model.activeMaxTLOD) / 5), Model.activeMinTLOD + 50.0f) + CloudRecoveryExtraNonExpert;
}
TLODStep = Math.Max(2.0f, Model.FPSTolerance[Model.activeProfile]);
TLODMinAltBand = Model.AvgDescentRate[Model.activeProfile] / 60 * ((Model.activeMaxTLOD - MinTLODNoExtra) / TLODStep);
if (Model.altAboveGnd >= AltTLODBase + TLODMinAltBand) Model.IsAppPriorityFPS = true;
else Model.IsAppPriorityFPS = false;
if (Model.altAboveGnd >= AltTLODBase + TLODMinAltBand)
{
AutoFPSInFlightPhase = true;
if (!Model.UseExpertOptions || Model.MinTLODExtra[Model.activeProfile])
{
MinTLOD = Math.Max(Model.DefaultTLOD_VR * 0.5f, 10.0f);
MaxTLOD = Model.DefaultTLOD_VR * 2.0f;
if (!MinTLODExtraActiveForDescentPrimed && Model.tlod > Model.MinTLOD[Model.activeProfile] + MinTLODExtraAmount)
MinTLODExtraActiveForDescentPrimed = true;
Model.MinTLODExtraActive = false;
}
}
else if ((!Model.UseExpertOptions || Model.MinTLODExtra[Model.activeProfile]) && MinTLODExtraActiveForDescentPrimed && Model.tlod > Model.MinTLOD[Model.activeProfile] + MinTLODExtraAmount)
{
if (Model.TLODAutoMethod[Model.activeProfile] != 2)
{
Model.MinTLODExtraActive = true;
SetActiveLODs();
}
MinTLODExtraActiveForDescentPrimed = false;
}
if (Model.AutoTargetFPS && Model.OnGround && Model.groundSpeed == 0 && AutoFPSInFlightPhase)
{
Model.FPSSettleCounter = ServiceModel.FPSSettleSeconds;
AutoFPSInFlightPhase = false;
Model.ForceAutoFPSCal = true;
Model.ResetCloudsTLOD();
Logger.Log(LogLevel.Information, "LODController:RunTick", "Recalibrating Auto Target FPS on arrival at new location");
}
if (!Model.AppPaused && (Model.FPSSettleCounter == 0 || Model.TLODAutoMethod[Model.activeProfile] == 2))
{
float deltaFPS = GetAverageFPS() - Model.TargetFPS;
if (Model.AutoTargetFPS && !(Model.FgModeEnabled && !Model.ActiveWindowMSFS) && (Model.ForceAutoFPSCal || (!(Model.FgModeEnabled && !Model.ActiveWindowMSFS) && ((deltaFPS <= -Model.TargetFPS * AutoTargetFPSRecalTolerance && (!Model.DecCloudQ[Model.activeProfile] || Model.DecCloudQActive) && Model.tlod == Model.activeMinTLOD)))))
{
if (!Model.ForceAutoFPSCal && (!Model.UseExpertOptions || Model.MinTLODExtra[Model.activeProfile]) && Model.MinTLODExtraActive) Model.MinTLODExtraActive = false;
else
{
Model.TargetFPS = (int)(GetAverageFPS() * ((Model.FlightTypeIFR ? 0.95f : 0.90f) - Math.Min(0.1f, Model.altAboveGnd / 30000)));
Model.UpdateTargetFPS = true;
Model.ForceAutoFPSCal = false;
if (deltaFPS <= -Model.TargetFPS * AutoTargetFPSRecalTolerance && (!Model.DecCloudQ[Model.activeProfile] || Model.DecCloudQActive) && Model.tlod == Model.activeMinTLOD) Logger.Log(LogLevel.Information, "LODController:UpdateVariables", "FPS too " + (Model.tlod == Model.activeMinTLOD ? "low" : "high") + " for auto target FPS settings. Auto Target FPS updated to " + $"{Model.TargetFPS}");
else
{
deltaFPS = GetAverageFPS() - Model.TargetFPS;
Logger.Log(LogLevel.Information, "LODController:UpdateVariables", "Auto Target FPS set to " + $"{Model.TargetFPS}");
}
}
}
if (Model.TLODAutoMethod[Model.activeProfile] == 2)
{
if (Model.altAboveGnd < Model.AltTLODBase[Model.activeProfile]) newTLOD = Model.MinTLOD[Model.activeProfile];
else if (Model.altAboveGnd > Model.AltTLODTop[Model.activeProfile]) newTLOD = Model.MaxTLOD[Model.activeProfile];
else
{
newTLOD = Model.MinTLOD[Model.activeProfile] + (Model.MaxTLOD[Model.activeProfile] - Model.MinTLOD[Model.activeProfile]) * (Model.altAboveGnd - Model.AltTLODBase[Model.activeProfile]) / (Model.AltTLODTop[Model.activeProfile] - Model.AltTLODBase[Model.activeProfile]);
if (Math.Abs(newTLOD - Model.tlod) < 5) newTLOD = Model.tlod;
else if (Math.Abs(newTLOD - Model.tlod) > 10) newTLOD = Model.tlod + Math.Sign(newTLOD - Model.tlod) * 10;
else newTLOD = Model.tlod + Math.Sign(newTLOD - Model.tlod) * 5;
}
}
else if ((!Model.UseExpertOptions || Model.MinTLODExtra[Model.activeProfile]) && !(Model.FgModeEnabled && !Model.ActiveWindowMSFS) && !Model.DecCloudQActive && !Model.MinTLODExtraActive && Model.OnGround && deltaFPS >= Model.TargetFPS * MinTLODExtraActiveTolerance)
{
Model.MinTLODExtraActive = true;
SetActiveLODs();
newTLOD = Model.activeMinTLOD;
if (newTLOD != Model.tlod)
{
Model.MemoryAccess.SetTLOD(newTLOD);
Model.tlod = newTLOD;
Model.tlod_step = true;
Model.FPSSettleCounter = ServiceModel.FPSSettleSeconds * 2;
}
}
else
{
MinTLOD = Math.Max(Model.DefaultTLOD * 0.5f, 10.0f);
MaxTLOD = Model.DefaultTLOD * 2.0f;
if (Model.TLODAutoMethod[Model.activeProfile] == 0) newTLOD = Model.tlod + Math.Sign(deltaFPS) * (Math.Min((float)Math.Pow(Math.Abs(deltaFPS) / (GetAverageFPS() > 1 ? GetAverageFPS() : 1) * TLODStep * 10, 1.4f), TLODStep * 2));
else newTLOD = Model.tlod + Math.Sign(deltaFPS) * TLODStep * (Math.Abs(deltaFPS) >= Model.TargetFPS * 2 * Model.FPSTolerance[Model.activeProfile] / 100 ? 2 : 1) * (Model.altAboveGnd < FPSPriorityBaseAlt && !Model.OnGround ? (float)Model.altAboveGnd / FPSPriorityBaseAlt : 1);
if (Model.altAboveGnd < AltTLODBase) newTLOD = Model.activeMinTLOD;
else newTLOD = Math.Min(Model.activeMinTLOD + (Model.activeMaxTLOD - MinTLODNoExtra) * (Model.altAboveGnd - AltTLODBase) / TLODMinAltBand, newTLOD);
}
}
if (Model.CustomAutoOLOD && Model.UseExpertOptions)
{
OLODAtBase = Model.OLODAtBase;
AltOLODBase = Model.AltOLODBase;
OLODAtTop = Model.OLODAtTop;
AltOLODTop = Model.AltOLODTop;
}
TLODStep = Math.Max(2.0f, Model.FPSTolerance);
TLODMinAltBand = AvgDescentRate / 60 * ((MaxTLOD - MinTLOD) / TLODStep);
if (!TLODMinGndLanding || Model.altAboveGnd >= AltTLODBase + TLODMinAltBand) Model.IsAppPriorityFPS = true;
else Model.IsAppPriorityFPS = false;
if (!(!Model.ActiveWindowMSFS && (Model.UseExpertOptions && Model.PauseMSFSFocusLost)) && Model.FPSSettleCounter == 0)
{
float deltaFPS = GetAverageFPS() - Model.TargetFPS;
if (Math.Abs(deltaFPS) >= Model.TargetFPS * FPSTolerance / 100 || !Model.IsAppPriorityFPS)
newTLOD = (float)Math.Round(Math.Min(Model.activeMaxTLOD, Math.Max(Model.activeMinTLOD, newTLOD)));
if (Math.Abs(Model.tlod - newTLOD) >= 1)
{
newTLOD = Model.tlod + Math.Sign(deltaFPS) * (Model.OnGround && groundSpeed > 1 ? 2 : TLODStep * (Math.Abs(deltaFPS) >= Model.TargetFPS * 2 * FPSTolerance / 100 && (groundSpeed < 1 || !Model.OnGround) ? 2 : 1) * (Model.altAboveGnd < FPSPriorityBaseAlt && !Model.OnGround ? (float)Model.altAboveGnd / FPSPriorityBaseAlt : 1));
if (!Model.IsAppPriorityFPS)
{
if (Model.altAboveGnd < AltTLODBase) newTLOD = MinTLOD;
else newTLOD = Math.Min(MinTLOD + (MaxTLOD - MinTLOD) * (Model.altAboveGnd - AltTLODBase) / TLODMinAltBand, newTLOD);
}
newTLOD = (float)Math.Round(Math.Min(MaxTLOD, Math.Max(MinTLOD, newTLOD)));
if (Math.Abs(Model.tlod - newTLOD) >= 1)
{
Model.MemoryAccess.SetTLOD(newTLOD);
Model.tlod_step = true;
}
else Model.tlod_step = false;
if (DecCloudQ && !Model.DecCloudQActive && newTLOD == MinTLOD && (!TLODMinGndLanding || (TLODMinGndLanding && deltaFPS <= -Model.TargetFPS * FPSTolerance / 100)))
{
if (Model.VrModeActive && Model.DefaultCloudQ_VR >= 1)
{
Model.MemoryAccess.SetCloudQ_VR(Model.DefaultCloudQ_VR - 1);
Model.DecCloudQActive = true;
}
if (!Model.VrModeActive && Model.DefaultCloudQ >= 1)
{
Model.MemoryAccess.SetCloudQ(Model.DefaultCloudQ - 1);
Model.DecCloudQActive = true;
}
}
if (DecCloudQ && Model.DecCloudQActive && ((TLODMinGndLanding && deltaFPS >= Model.TargetFPS * 0.15f) || (newTLOD >= CloudRecoveryTLOD && (!TLODMinGndLanding || (TLODMinGndLanding && deltaFPS >= Model.TargetFPS * FPSTolerance / 100)))))
{
if (Model.VrModeActive) Model.MemoryAccess.SetCloudQ_VR(Model.DefaultCloudQ_VR);
else Model.MemoryAccess.SetCloudQ(Model.DefaultCloudQ);
Model.DecCloudQActive = false;
}
Model.MemoryAccess.SetTLOD(newTLOD);
Model.tlod = newTLOD;
Model.tlod_step = true;
}
else Model.tlod_step = false;
if (Model.CustomAutoOLOD && Model.UseExpertOptions)
if ((!Model.UseExpertOptions || Model.MinTLODExtra[Model.activeProfile]) && Model.MinTLODExtraActive && !Model.OnGround && deltaFPS <= -Model.TargetFPS * Model.FPSTolerance[Model.activeProfile] / 100)
Model.MinTLODExtraActive = false;
if (Model.TLODAutoMethod[Model.activeProfile] != 2 && Model.DecCloudQ[Model.activeProfile] && !Model.DecCloudQActive && Model.tlod == Model.activeMinTLOD && deltaFPS <= -Model.TargetFPS * Model.FPSTolerance[Model.activeProfile] / 100)
{
if (Model.VrModeActive && Model.DefaultCloudQ_VR >= 1)
{
Model.MemoryAccess.SetCloudQ_VR(Model.DefaultCloudQ_VR - 1);
Model.DecCloudQActive = true;
}
if (!Model.VrModeActive && Model.DefaultCloudQ >= 1)
{
Model.MemoryAccess.SetCloudQ(Model.DefaultCloudQ - 1);
Model.DecCloudQActive = true;
}
CloudRecoveryTickMark = TotalTicks;
}
if (Model.DecCloudQ[Model.activeProfile] && Model.DecCloudQActive && ((deltaFPS >= Model.TargetFPS * CloudRecoveryExtraTolerance) || (newTLOD >= (Model.UseExpertOptions && Model.CloudRecoveryPlus[Model.activeProfile] ? Model.activeMinTLOD + Model.CloudRecoveryTLOD[Model.activeProfile] : Model.CloudRecoveryTLOD[Model.activeProfile]) && deltaFPS >= 0)))
{
Model.ResetCloudsTLOD(false);
if ((!Model.UseExpertOptions || Model.MinTLODExtra[Model.activeProfile]) && !Model.MinTLODExtraActive && Model.OnGround) Model.FPSSettleCounter = ServiceModel.FPSSettleSeconds;
if (!Model.UseExpertOptions && Model.altAboveGnd > AltTLODBase && TotalTicks < CloudRecoveryTickMark + 30) CloudRecoveryExtraNonExpert += 20;
}
if (!Model.UseExpertOptions || Model.CustomAutoOLOD[Model.activeProfile])
{
float newOLOD;
if (Model.altAboveGnd < AltOLODBase) newOLOD = OLODAtBase;
else if (Model.altAboveGnd > AltOLODTop) newOLOD = OLODAtTop;
if (Model.altAboveGnd < Model.AltOLODBase[Model.activeProfile]) newOLOD = Model.activeOLODAtBase;
else if (Model.altAboveGnd > Model.AltOLODTop[Model.activeProfile]) newOLOD = Model.activeOLODAtTop;
else
{
newOLOD = OLODAtBase + (OLODAtTop - OLODAtBase) * (Model.altAboveGnd - AltOLODBase) / (AltOLODTop - AltOLODBase);
if (Math.Abs(newOLOD - Model.olod) > FPSTolerance) newOLOD = newOLOD + Math.Sign(newOLOD - Model.olod) * FPSTolerance;
newOLOD = Model.activeOLODAtBase + (Model.activeOLODAtTop - Model.activeOLODAtBase) * (Model.altAboveGnd - Model.AltOLODBase[Model.activeProfile]) / (Model.AltOLODTop[Model.activeProfile] - Model.AltOLODBase[Model.activeProfile]);
if (Math.Abs(newOLOD - Model.olod) < 5) newOLOD = Model.olod;
else if (Math.Abs(newOLOD - Model.olod) > 10) newOLOD = Model.olod + Math.Sign(newOLOD - Model.olod) * 10;
else newOLOD = Model.olod + Math.Sign(newOLOD - Model.olod) * 5;
}
if (newOLOD != Model.olod && Math.Abs(newOLOD - Model.olod) >= 1)
{
Model.MemoryAccess.SetOLOD(newOLOD);
Model.olod = newOLOD;
Model.olod_step = true;
}
else Model.olod_step = false;
@ -186,7 +276,8 @@ namespace MSFS2020_AutoFPS
else Model.olod_step = false;
}
}
else if (--Model.FPSSettleCounter < 0) Model.FPSSettleCounter = 0;
else if (!Model.AppPaused && Model.FPSSettleCounter != 0) --Model.FPSSettleCounter;
TotalTicks++;
}
public int VerticalAverage()
{
@ -197,6 +288,29 @@ namespace MSFS2020_AutoFPS
return verticalStatsVS.Average();
}
public void SetActiveLODs()
{
if (Model.UseExpertOptions)
{
MinTLODNoExtra = Model.MinTLOD[Model.activeProfile];
MinTLODExtraAmount = (float)Math.Min(50.0f, 0.5 * (Model.MaxTLOD[Model.activeProfile] - MinTLODNoExtra));
Model.activeMinTLOD = MinTLODNoExtra + (Model.MinTLODExtraActive && Model.TLODAutoMethod[Model.activeProfile] != 2 ? MinTLODExtraAmount : 0);
Model.activeMaxTLOD = Model.MaxTLOD[Model.activeProfile];
Model.activeOLODAtBase = Model.OLODAtBase[Model.activeProfile];
Model.activeOLODAtTop = Model.OLODAtTop[Model.activeProfile];
}
else
{
float defaultTLOD = Model.VrModeActive ? Model.DefaultTLOD_VR : Model.DefaultTLOD;
float defaultOLOD = Model.VrModeActive ? Model.DefaultOLOD_VR : Model.DefaultOLOD;
MinTLODNoExtra = Math.Max(defaultTLOD * (Model.FlightTypeIFR ? 0.5f : 1.0f), 10.0f);
Model.activeMaxTLOD = Model.MaxTLOD[(int)ServiceModel.appProfiles.NonExpert] = defaultTLOD * (Model.FlightTypeIFR ? 2.0f : 3.0f);
MinTLODExtraAmount = (float)Math.Min(50.0f, 0.5 * (Model.activeMaxTLOD - MinTLODNoExtra));
Model.activeMinTLOD = Model.MinTLOD[(int)ServiceModel.appProfiles.NonExpert] = MinTLODNoExtra + (Model.MinTLODExtraActive ? MinTLODExtraAmount : 0);
Model.activeOLODAtBase = Model.OLODAtBase[(int)ServiceModel.appProfiles.NonExpert] = Model.FlightTypeIFR ? defaultOLOD : defaultOLOD * 1.5f;
Model.activeOLODAtTop = Model.OLODAtTop[(int)ServiceModel.appProfiles.NonExpert] = Math.Max(Model.activeOLODAtBase * 0.1f, 10.0f);
}
}
public float GetAverageFPS()
{
if (Model.FgModeEnabled)
@ -212,28 +326,30 @@ namespace MSFS2020_AutoFPS
Model.cloudQ_VR = Model.MemoryAccess.GetCloudQ_VR();
Model.VrModeActive = Model.MemoryAccess.IsVrModeActive();
Model.FgModeEnabled = Model.MemoryAccess.IsFgModeEnabled();
if (Model.ActiveWindowMSFS != Model.MemoryAccess.IsActiveWindowMSFS() && Model.UseExpertOptions && Model.PauseMSFSFocusLost) Model.FPSSettleCounter = ServiceModel.FPSSettleSeconds;
if (!Model.ActiveWindowMSFS && Model.MemoryAccess.IsActiveWindowMSFS() && (Model.FgModeEnabled || (Model.UseExpertOptions && Model.PauseMSFSFocusLost))) Model.FPSSettleCounter = ServiceModel.FPSSettleSeconds;
Model.ActiveWindowMSFS = Model.MemoryAccess.IsActiveWindowMSFS();
string ActiveGraphicsMode = Model.ActiveGraphicsMode;
if (Model.VrModeActive)
{
Model.ActiveGraphicsMode = "VR";
Model.TargetFPS = Model.TargetFPS_VR;
if (!Model.AutoTargetFPS) Model.TargetFPS = Model.FlightTypeIFR ? Model.TargetFPS_VR : Model.TargetFPS_VR_VFR;
}
else if (Model.FgModeEnabled)
{
Model.ActiveGraphicsMode = "FG";
Model.TargetFPS = Model.TargetFPS_FG;
if (!Model.AutoTargetFPS) Model.TargetFPS = Model.FlightTypeIFR ? Model.TargetFPS_FG : Model.TargetFPS_FG_VFR;
}
else
{
Model.ActiveGraphicsMode = "PC";
Model.TargetFPS = Model.TargetFPS_PC;
if (!Model.AutoTargetFPS) Model.TargetFPS = Model.FlightTypeIFR ? Model.TargetFPS_PC : Model.TargetFPS_PC_VFR;
}
if (Model.ActiveGraphicsMode != ActiveGraphicsMode)
{
Model.FPSSettleCounter = ServiceModel.FPSSettleSeconds;
Model.ActiveGraphicsModeChanged = true;
if (Model.AutoTargetFPS) Model.ForceAutoFPSCal = true;
Model.ResetCloudsTLOD();
}
}
}

View File

@ -5,26 +5,4 @@
<add key="logFilePath" value="MSFS2020_AutoFPS.log" />
<add key="logLevel" value="Debug" />
<add key="waitForConnect" value="true" />
<add key="openWindow" value="true" />
<add key="simBinary" value="FlightSimulator" />
<add key="simModule" value="WwiseLibPCx64P.dll" />
<add key="offsetModuleBase" value="0x004B2368" />
<add key="offsetPointerMain" value="0x3D0" />
<add key="offsetPointerTlod" value="0xC" />
<add key="offsetPointerTlodVr" value="0x114" />
<add key="offsetPointerOlod" value="0xC" />
<add key="offsetPointerCloudQ" value="0x44" />
<add key="offsetPointerCloudQVr" value="0x108" />
<add key="offsetPointerVrMode" value="0x1C" />
<add key="offsetPointerFgMode" value="0x4A" />
<add key="simMinLod" value="10" />
<add key="useExpertOptions" value="false" />
<add key="PauseMSFSFocusLost" value="false" />
<add key="testLogSimValues" value="false" />
<add key="FpsTolerance" value="5" />
<add key="DecCloudQ" value="true" />
<add key="TLODMinGndLanding" value="true" />
<add key="CloudRecoveryTLOD" value="100" />
<add key="minTLod" value="50" />
<add key="maxTLod" value="200" />
</appSettings>

View File

@ -8,12 +8,12 @@
<StartupObject>MSFS2020_AutoFPS.App</StartupObject>
<ApplicationIcon>icon.ico</ApplicationIcon>
<PlatformTarget>x64</PlatformTarget>
<Authors>Fragtality</Authors>
<Authors>ResetXPDR</Authors>
<Description>Another Adapative LOD Implementation based on muumimorko and Fratality
Work</Description>
<Copyright>Copyright © 2024</Copyright>
<AllowUnsafeBlocks>False</AllowUnsafeBlocks>
<Version>0.4.1</Version>
<Version>0.4.2</Version>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
@ -44,6 +44,9 @@ Work</Description>
</ItemGroup>
<ItemGroup>
<Reference Include="GpuzShMem.x64">
<HintPath>GpuzShMem.x64.dll</HintPath>
</Reference>
<Reference Include="Microsoft.FlightSimulator.SimConnect">
<HintPath>Microsoft.FlightSimulator.SimConnect.dll</HintPath>
</Reference>
@ -56,6 +59,9 @@ Work</Description>
<None Update="Microsoft.FlightSimulator.SimConnect.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="README.md">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="SimConnect.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>

View File

@ -5,7 +5,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MSFS2020_AutoFPS"
mc:Ignorable="d"
Name ="AutoFPS" Title="MSFS2020_AutoFPS" Height="580" Width="402" SizeToContent="WidthAndHeight" ResizeMode="NoResize" IsVisibleChanged="Window_IsVisibleChanged" Closing="Window_Closing" Topmost="True">
Name ="AutoFPS" Title="MSFS2020_AutoFPS" Height="618" Width="402" SizeToContent="WidthAndHeight" ResizeMode="CanMinimize" IsVisibleChanged="Window_IsVisibleChanged" Closing="Window_Closing" Topmost="True" Background="#F9FFFFFF">
<Window.Resources>
<Style TargetType="GroupBox">
<Setter Property="HeaderTemplate">
@ -34,7 +34,7 @@
</Grid.RowDefinitions>
<StackPanel Orientation="Vertical" Grid.ColumnSpan="4" Margin="0,0,0,17" Grid.RowSpan="2">
<GroupBox Grid.Row="0" Grid.Column="0" BorderBrush="DarkGray" BorderThickness="1" Margin="10,8,10,8">
<GroupBox Grid.Row="0" Grid.Column="0" BorderThickness="1" Margin="10,8,10,8" BorderBrush="DarkGray">
<GroupBox.Header>Connection Status</GroupBox.Header>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<Label Name="lblConnStatMSFS" Height="40" VerticalContentAlignment="Center" VerticalAlignment="Center" TextBlock.Foreground="Red" Padding="8,0,16,0">MSFS</Label>
@ -43,7 +43,7 @@
</StackPanel>
</GroupBox>
<GroupBox Grid.Row="1" Grid.Column="0" BorderBrush="DarkGray" BorderThickness="1" Margin="10,14,10,0">
<GroupBox Grid.Row="1" Grid.Column="0" BorderThickness="1" Margin="10,0,10,0" BorderBrush="DarkGray">
<GroupBox.Header>Sim Values</GroupBox.Header>
<Grid>
<Grid.ColumnDefinitions>
@ -56,7 +56,7 @@
<RowDefinition MinHeight="32" Height="*"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Grid.Column="0" Orientation="Horizontal" VerticalAlignment="Center">
<Label MinWidth="60" VerticalContentAlignment="Center">ØFPS:</Label>
<Label MinWidth="60" VerticalContentAlignment="Center">FPS:</Label>
<Label Name="lblSimFPS" MinWidth="64" VerticalContentAlignment="Center">n/a</Label>
</StackPanel>
<StackPanel Grid.Row="0" Grid.Column="1" Orientation="Horizontal" VerticalAlignment="Center">
@ -84,42 +84,42 @@
</GroupBox>
</StackPanel>
<StackPanel Orientation="Vertical" Grid.Row="2" HorizontalAlignment="Left" Width="402" Grid.ColumnSpan="4" Margin="1,0,0,22">
<GroupBox BorderBrush="DarkGray" BorderThickness="1" Margin="10,8,10,8">
<StackPanel Orientation="Vertical" Grid.Row="1" HorizontalAlignment="Left" Width="402" Grid.ColumnSpan="4" Margin="1,75,0,22" Grid.RowSpan="2">
<GroupBox BorderThickness="1" Margin="10,8,10,8" BorderBrush="DarkGray">
<GroupBox.Header>General</GroupBox.Header>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="369*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="6*"/>
<RowDefinition Height="29*"/>
<RowDefinition Height="29*"/>
<RowDefinition Height="33.96"/>
<RowDefinition MinHeight="32" Height="*"/>
<RowDefinition MinHeight="32" Height="*"/>
<RowDefinition MinHeight="32" Height="*"/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Margin="0,0,0,34" Grid.RowSpan="2"/>
<StackPanel Grid.Row="1" Grid.Column="0" Orientation="Horizontal" Margin="0,8,0,0" Grid.ColumnSpan="2">
<StackPanel Grid.Row="0" Grid.Column="0" Orientation="Horizontal" Margin="0,8,0,0" Grid.ColumnSpan="2">
<Label Name="lblTargetFPS" Content="Target PC FPS" MinWidth="120"/>
<TextBox x:Name="txtTargetFPS" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Height="24" MaxHeight="24" MinHeight="24" Width="42" LostFocus="TextBox_LostFocus" KeyUp="TextBox_KeyUp"/>
<TextBox x:Name="txtTargetFPS" ToolTip ="Achievable for your system" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Height="24" MaxHeight="24" MinHeight="24" Width="42" LostFocus="TextBox_LostFocus" KeyUp="TextBox_KeyUp"/>
<CheckBox x:Name="chkOnTop" VerticalContentAlignment="Center" Width="76" Height="24" MaxHeight="24" MinHeight="24" Click="chkOnTop_Click" Content="On top" Margin="13,0,0,0"/>
<CheckBox x:Name="chkTestLogSimValues" VerticalContentAlignment="Center" Width="102" Height="24" MaxHeight="24" MinHeight="24" Click="chkTestLogSimValues_Click" Content="Log Sim Values"/>
<CheckBox x:Name="chkAutoTargetFPS" VerticalContentAlignment="Center" ToolTip ="Recommended for VFR flights" Height="24" MaxHeight="24" MinHeight="24" Click="chkAutoTargetFPS_Click" Content="Auto Target FPS" IsChecked="False"/>
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="0" Orientation="Horizontal" Margin="0,8,0,0">
<CheckBox Name="chkUseExpertOptions" VerticalContentAlignment="Center" Width="170" Height="24" MaxHeight="24" MinHeight="24" Click="chkUseExpertOptions_Click" Margin="5,0,0,0">Use Expert Options</CheckBox>
<Label Content="Flight Type"/>
<RadioButton x:Name="optVFRFlight" Margin="5" ToolTip ="General Aviation flights in regional areas" Click="chkFlightType_Click" GroupName="FlightType" Content="VFR" IsChecked="False" />
<RadioButton x:Name="optIFRFlight" ToolTip ="Airliner flights in urban areas" Margin="5" Click="chkFlightType_Click" GroupName="FlightType" Content="IFR" IsChecked="True" />
</StackPanel>
<StackPanel Grid.Row="2" Grid.Column="0" Orientation="Horizontal" Margin="0,8,0,0">
<CheckBox Name="chkUseExpertOptions" VerticalContentAlignment="Center" Width="170" Height="24" MaxHeight="24" MinHeight="24" Click="chkUseExpertOptions_Click" Margin="5,0,0,0">Use Expert Options</CheckBox>
<CheckBox x:Name="chkOpenWindow" VerticalContentAlignment="Center" Click="chkOpenWindow_Click" Content="Open Window on App Start" Height="15" Width="220"/>
</StackPanel>
<StackPanel Grid.Row="3" Grid.Column="0" Orientation="Horizontal" Margin="0,8,0,0">
<Label Name="lblStatusMessage" MinWidth="50" VerticalContentAlignment="Center" Content=""/>
<TextBlock x:Name="lblappUrl" VerticalAlignment="Center"> <Hyperlink NavigateUri="https://github.com/ResetXPDR/MSFS_AutoLOD/releases/latest" RequestNavigate="Hyperlink_RequestNavigate">
here</Hyperlink>
</TextBlock>
</StackPanel>
</Grid>
</GroupBox>
</StackPanel>
<StackPanel Name="stkpnlMSFSSettings" Orientation="Vertical" Grid.Column="0" Grid.Row="2" Grid.ColumnSpan="4" Margin="0,122,0,0" Grid.RowSpan="2">
<GroupBox BorderBrush="DarkGray" BorderThickness="1" Margin="10,8,10,8">
<StackPanel Name="stkpnlExpertSettings" Orientation="Vertical" Grid.Column="0" Grid.Row="2" Grid.ColumnSpan="4" Margin="0,120,0,0" Grid.RowSpan="2">
<GroupBox BorderThickness="1" Margin="10,0,10,8" BorderBrush="DarkGray">
<GroupBox.Header>Expert Options</GroupBox.Header>
<Grid Margin="0,2,0,2">
<Grid.ColumnDefinitions>
@ -132,52 +132,61 @@
</Grid.RowDefinitions>
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal" Margin="0,8,0,0">
<Label Content="FPS Tolerance" MinWidth="95"/>
<TextBox x:Name="txtFPSTolerance" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Height="24" MaxHeight="24" MinHeight="24" Width="42" LostFocus="TextBox_LostFocus" KeyUp="TextBox_KeyUp" Margin="5,0,0,0"/>
<Label Content="%"/>
<CheckBox x:Name="chkTLODMinGndLanding" Margin="15,0,0,0" VerticalContentAlignment="Center" Click="chkTLODMinGndLanding_Click" Width="192" Content="TLOD Min on Ground/Landing" Checked="chkTLODMinGndLanding_Checked" Padding="4,0,0,0"/>
</StackPanel>
<StackPanel Name="stkpnlTLODMinOptions" Orientation="Vertical" Grid.Column="0" Grid.Row="2" Grid.ColumnSpan="1" Margin="0,0,0,0" Grid.RowSpan="2">
<StackPanel Orientation="Horizontal" Margin="0,4,0,0">
<Label Content="Alt TLOD Base" MinWidth="100"/>
<TextBox Name="txtAltTLODBase" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Height="24" MaxHeight="24" MinHeight="24" Width="42" LostFocus="TextBox_LostFocus" KeyUp="TextBox_KeyUp" Margin="0,0,0,0"/>
<Label Content="ft" Margin="0,0,12,0"/>
<Label Content="Avg Descent Rate" MinWidth="100"/>
<TextBox x:Name="txtAvgDescentRate" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Height="24" MaxHeight="24" MinHeight="24" Width="42" LostFocus="TextBox_LostFocus" KeyUp="TextBox_KeyUp" Margin="0,0,0,0"/>
<Label Content="fpm"/>
</StackPanel>
<Label Content="TLOD Automation Method" MinWidth="177"/>
<ComboBox Name="cbTLODAutoMethod" MinWidth ="103" SelectionChanged="cbTLODAutoMethod_SelectionChange" SelectedIndex="0" >
<ComboBoxItem Content="FPS Sensitivity" IsSelected="True"/>
<ComboBoxItem Content="FPS Tolerance"/>
<ComboBoxItem Content="Auto TLOD"/>
</ComboBox>
<TextBox x:Name="txtFPSTolerance" ToolTip ="TLOD responsiveness to FPS variances" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Height="24" MaxHeight="24" MinHeight="24" Width="42" LostFocus="TextBox_LostFocus" KeyUp="TextBox_KeyUp" Margin="5,0,0,0"/>
<Label Name="lblFPSTolPercent" Content="%" MinWidth="20"></Label>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4,0,0">
<Label Content="TLOD Minimum" MinWidth="100"/>
<Label Name="lblTLODMin" Content="TLOD Min +" MinWidth="85"/>
<CheckBox x:Name="chkMinTLODExtra" VerticalContentAlignment="Center" ToolTip="+ up to 50 more TLOD Min with good performance conditions" Click="chkMinTLODExtra_Click" Checked="chkMinTLODExtra_Checked" Margin="0,0,7,0">
</CheckBox>
<TextBox Name="txtMinTLod" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Height="24" MaxHeight="24" MinHeight="24" Width="42" LostFocus="TextBox_LostFocus" KeyUp="TextBox_KeyUp" Margin="0,0,30,0"/>
<Label Content="TLOD Maximum" MinWidth="100"/>
<Label Name="lblTLODMax" Content="TLOD Max" MinWidth="100"/>
<TextBox x:Name="txtMaxTLod" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Height="24" MaxHeight="24" MinHeight="24" Width="42" LostFocus="TextBox_LostFocus" KeyUp="TextBox_KeyUp" Margin="5,0,0,0"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,2,0,0">
<CheckBox x:Name="chkDecCloudQ" VerticalContentAlignment="Center" Click="chkDecCloudQ_Click" Width="152" Content="Decrease Cloud Quality" Checked="chkDecCloudQ_Checked" Padding="4,0,0,0"/>
<StackPanel Name="stkpnlFlightTypeIFROptions" Orientation="Vertical" Grid.Column="0" Grid.Row="2" Grid.ColumnSpan="1" Margin="0,0,0,0" Grid.RowSpan="2">
<StackPanel Orientation="Horizontal" Margin="0,4,0,0">
<Label Content="Alt TLOD Base" MinWidth="108"/>
<TextBox Name="txtAltTLODBase" ToolTip ="TLOD Min will be locked below this altitude" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Height="24" MaxHeight="24" MinHeight="24" Width="42" LostFocus="TextBox_LostFocus" KeyUp="TextBox_KeyUp" Margin="0,0,0,0"/>
<Label Content="ft" Margin="0,0,12,0"/>
<Label Name="lblAltTLOD1" Content="Avg Descent Rate" MinWidth="103"/>
<TextBox x:Name="txtAvgDescentRate" ToolTip ="Determines what altitude TLOD will start reducing towards TLOD Min" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Height="24" MaxHeight="24" MinHeight="24" Width="42" LostFocus="TextBox_LostFocus" KeyUp="TextBox_KeyUp" Margin="2,0,0,0"/>
<Label Name="lblAltTLOD2" Content="fpm"/>
</StackPanel>
</StackPanel>
<StackPanel Name="stkpnlCloudQualityOptions" Orientation="Horizontal" Margin="0,2,0,0">
<CheckBox x:Name="chkDecCloudQ" ToolTip ="Reduce cloud quality by one level if TLOD Min is not enough to achieve target FPS" VerticalContentAlignment="Center" Click="chkDecCloudQ_Click" Width="152" Content="Decrease Cloud Quality" Checked="chkDecCloudQ_Checked" Padding="4,0,0,0"/>
<Label Name= "lblCloudRecoveryTLOD" >Cloud Recovery TLOD</Label>
<TextBox Name="txtCloudRecoveryTLOD" Margin="1,0,0,0" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Height="24" MaxHeight="24" MinHeight="24" Width="42" LostFocus="TextBox_LostFocus" KeyUp="TextBox_KeyUp"></TextBox>
<TextBox Name="txtCloudRecoveryTLOD" ToolTip ="Minimum TLOD required to cancel cloud quality reduction" Margin="9,0,0,0" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Height="24" MaxHeight="24" MinHeight="24" Width="42" LostFocus="TextBox_LostFocus" KeyUp="TextBox_KeyUp"></TextBox>
<Label Name="lblCloudPlus" Content="+" />
<CheckBox x:Name="chkCloudRecoveryPlus" VerticalContentAlignment="Center" ToolTip="+ for relative rather than absolute cloud recovery TLOD " Click="chkCloudRecoveryPlus_Click" Width="33" Padding="4,0,0,0" Checked="chkCloudRecoveryPlus_Checked">
</CheckBox>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,2,0,0">
<CheckBox x:Name="chkCustomAutoOLOD" VerticalContentAlignment="Center" Click="chkCustomAutoOLOD_Click" Width="177" Content="Auto OLOD" Padding="4,0,0,0" Checked="chkCustomAutoOLOD_Checked"/>
<CheckBox x:Name="chkPauseMSFSFocusLost" VerticalContentAlignment="Center" Click="chkPauseMSFSFocusLost_Click" Width="185" Content="Pause when MSFS loses focus" Checked="chkPauseMSFSFocusLost_Checked" Padding="4,0,0,0"/>
<CheckBox x:Name="chkCustomAutoOLOD" ToolTip ="Adjusts OLOD based on an altitude band" VerticalContentAlignment="Center" Click="chkCustomAutoOLOD_Click" Width="140" Content="Auto OLOD" Padding="4,0,0,0" Checked="chkCustomAutoOLOD_Checked"/>
<CheckBox x:Name="chkPauseMSFSFocusLost" ToolTip ="Use if FG Active/Inactive transitions cause TLOD hunting" VerticalContentAlignment="Center" Click="chkPauseMSFSFocusLost_Click" Width="185" Content="Pause when MSFS loses focus" Checked="chkPauseMSFSFocusLost_Checked" Padding="4,0,0,0" Margin="15,0,0,0"/>
</StackPanel>
<StackPanel Name="stkpnlCustomAutoOLOD" Orientation="Vertical" Grid.Column="0" Grid.Row="2" Grid.ColumnSpan="1" Margin="0,0,0,0" Grid.RowSpan="2">
<StackPanel Orientation="Horizontal" Margin="0,4,0,0">
<Label Content="OLOD @ Base" MinWidth="100"/>
<TextBox Name="txtOLODAtBase" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Height="24" MaxHeight="24" MinHeight="24" Width="42" LostFocus="TextBox_LostFocus" KeyUp="TextBox_KeyUp" Margin="0,0,30,0"/>
<Label Content="OLOD @ Top" MinWidth="100"/>
<TextBox x:Name="txtOLODAtTop" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Height="24" MaxHeight="24" MinHeight="24" Width="42" LostFocus="TextBox_LostFocus" KeyUp="TextBox_KeyUp" Margin="5,0,0,0"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4,0,0">
<Label Content="Alt OLOD Base" MinWidth="100"/>
<TextBox Name="txtAltOLODBase" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Height="24" MaxHeight="24" MinHeight="24" Width="42" LostFocus="TextBox_LostFocus" KeyUp="TextBox_KeyUp" Margin="0,0,0,0"/>
<Label Content="ft" Margin="0,0,12,0"/>
<Label Content="Alt OLOD Top" MinWidth="100"/>
<TextBox x:Name="txtAltOLODTop" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Height="24" MaxHeight="24" MinHeight="24" Width="42" LostFocus="TextBox_LostFocus" KeyUp="TextBox_KeyUp" Margin="5,0,0,0"/>
<Label Content="ft"/>
</StackPanel>
<Label Content="OLOD @ Base" MinWidth="108"/>
<TextBox Name="txtOLODAtBase" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Height="24" MaxHeight="24" MinHeight="24" Width="42" LostFocus="TextBox_LostFocus" KeyUp="TextBox_KeyUp" Margin="0,0,30,0"/>
<Label Content="OLOD @ Top" MinWidth="100"/>
<TextBox x:Name="txtOLODAtTop" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Height="24" MaxHeight="24" MinHeight="24" Width="42" LostFocus="TextBox_LostFocus" KeyUp="TextBox_KeyUp" Margin="5,0,0,0"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4,0,0">
<Label Content="Alt OLOD Base" MinWidth="108"/>
<TextBox Name="txtAltOLODBase" ToolTip ="OLOD @ Base will be locked below this altitude" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Height="24" MaxHeight="24" MinHeight="24" Width="42" LostFocus="TextBox_LostFocus" KeyUp="TextBox_KeyUp" Margin="0,0,0,0"/>
<Label Content="ft" Margin="0,0,12,0"/>
<Label Content="Alt OLOD Top" MinWidth="100"/>
<TextBox x:Name="txtAltOLODTop" ToolTip ="OLOD @ top will be locked above this altitude" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Height="24" MaxHeight="24" MinHeight="24" Width="42" LostFocus="TextBox_LostFocus" KeyUp="TextBox_KeyUp" Margin="5,0,0,0"/>
<Label Content="ft"/>
</StackPanel>
</StackPanel>

View File

@ -29,21 +29,25 @@ namespace MSFS2020_AutoFPS
private int logTimer = 0;
private int logTimerInterval = 8;
private float altAboveGndLast = -1;
private float tlodLast = -1;
private float olodLast = -1;
private bool decCloudQActiveLast = true;
private bool IsAppPriorityFPSLast = false;
private bool LoadSettingsRecursed = false;
public MainWindow(NotifyIconViewModel notifyModel, ServiceModel serviceModel)
{
InitializeComponent();
this.notifyModel = notifyModel;
this.serviceModel = serviceModel;
string assemblyVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString();
assemblyVersion = assemblyVersion[0..assemblyVersion.LastIndexOf('.')];
Title += " (" + assemblyVersion + (ServiceModel.TestVersion ? "-test" : "")+ ")";
string assemblyVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString();
assemblyVersion = assemblyVersion[0..assemblyVersion.LastIndexOf('.')] + ".10";
Title += " (" + assemblyVersion + (ServiceModel.TestVersion ? "-test" : "") + ")";
if (serviceModel.UseExpertOptions) stkpnlMSFSSettings.Visibility = Visibility.Visible;
else stkpnlMSFSSettings.Visibility = Visibility.Collapsed;
if (ServiceModel.TestVersion) chkTestLogSimValues.Visibility = Visibility.Visible;
else chkTestLogSimValues.Visibility = Visibility.Hidden;
if (serviceModel.UseExpertOptions) stkpnlExpertSettings.Visibility = Visibility.Visible;
else stkpnlExpertSettings.Visibility = Visibility.Collapsed;
timer = new DispatcherTimer
{
@ -51,6 +55,8 @@ namespace MSFS2020_AutoFPS
};
timer.Tick += OnTick;
this.Resources["CustomLabelColor"] = new SolidColorBrush(Colors.Aqua);
string latestAppVersionStr = GetFinalRedirect("https://github.com/ResetXPDR/MSFS2020_AutoFPS/releases/latest");
lblappUrl.Visibility = Visibility.Hidden;
if (int.TryParse(assemblyVersion.Replace(".", ""), CultureInfo.InvariantCulture, out int currentAppVersion) && latestAppVersionStr != null && latestAppVersionStr.Length > 50)
@ -64,6 +70,11 @@ namespace MSFS2020_AutoFPS
lblStatusMessage.Foreground = new SolidColorBrush(Colors.Green);
lblappUrl.Visibility = Visibility.Visible;
}
else if (ServiceModel.TestVersion)
{
lblStatusMessage.Content = "Test version installed";
lblStatusMessage.Foreground = new SolidColorBrush(Colors.Green);
}
else
{
lblStatusMessage.Content = "Latest app version is installed";
@ -71,7 +82,7 @@ namespace MSFS2020_AutoFPS
}
}
}
if (ServiceModel.TestVersion)
else if (ServiceModel.TestVersion)
{
lblStatusMessage.Content = "Test version installed";
lblStatusMessage.Foreground = new SolidColorBrush(Colors.Green);
@ -140,35 +151,98 @@ namespace MSFS2020_AutoFPS
}
protected void LoadSettings()
{
chkOpenWindow.IsChecked = serviceModel.OpenWindow;
if (serviceModel.RememberWindowPos)
{
Top = serviceModel.windowTop;
Left = serviceModel.windowLeft;
}
if (serviceModel.TLODAutoMethod[serviceModel.activeProfile] == 2)
{
txtFPSTolerance.Visibility = Visibility.Hidden;
lblTLODMin.Content = "TLOD @ Base";
lblTLODMax.Content = "TLOD @ Top";
lblAltTLOD1.Content = "Alt TLOD Top";
lblAltTLOD2.Content = "ft";
txtAvgDescentRate.ToolTip = "TLOD Max will be locked above this altitude";
txtAvgDescentRate.Text = Convert.ToString(serviceModel.AltTLODTop[serviceModel.activeProfile], CultureInfo.CurrentUICulture);
chkMinTLODExtra.Visibility = Visibility.Hidden;
stkpnlCloudQualityOptions.Visibility = Visibility.Collapsed;
serviceModel.AutoTargetFPS = false;
chkAutoTargetFPS.Visibility = Visibility.Hidden;
lblTargetFPS.Visibility= Visibility.Hidden;
txtTargetFPS.Visibility = Visibility.Hidden;
chkPauseMSFSFocusLost.Visibility = Visibility.Hidden;
serviceModel.PauseMSFSFocusLost = false;
}
else
{
txtFPSTolerance.Visibility = Visibility.Visible;
lblTLODMin.Content = "TLOD Min +";
lblTLODMax.Content = "TLOD Max";
lblAltTLOD1.Content = "Avg Descent Rate";
lblAltTLOD2.Content = "fpm";
txtAvgDescentRate.ToolTip = "Determines what altitude TLOD will start reducing towards TLOD Min";
txtAvgDescentRate.Text = Convert.ToString(serviceModel.AvgDescentRate[serviceModel.activeProfile], CultureInfo.CurrentUICulture);
chkMinTLODExtra.Visibility = Visibility.Visible;
chkMinTLODExtra.IsChecked = serviceModel.MinTLODExtra[serviceModel.activeProfile];
stkpnlCloudQualityOptions.Visibility = Visibility.Visible;
serviceModel.AutoTargetFPS = Convert.ToBoolean(serviceModel.ConfigurationFile.GetSetting("AutoTargetFPS", "false"));
chkAutoTargetFPS.Visibility = Visibility.Visible;
lblTargetFPS.Visibility = Visibility.Visible;
txtTargetFPS.Visibility = Visibility.Visible;
chkPauseMSFSFocusLost.Visibility = Visibility.Visible;
serviceModel.PauseMSFSFocusLost = Convert.ToBoolean(serviceModel.ConfigurationFile.GetSetting("PauseMSFSFocusLost", "false"));
}
chkUseExpertOptions.IsChecked = serviceModel.UseExpertOptions;
chkCustomAutoOLOD.IsChecked = serviceModel.CustomAutoOLOD;
if (serviceModel.CustomAutoOLOD && serviceModel.UseExpertOptions) stkpnlCustomAutoOLOD.Visibility = Visibility.Visible;
else stkpnlCustomAutoOLOD.Visibility = Visibility.Collapsed;
chkTestLogSimValues.IsChecked = serviceModel.TestLogSimValues;
if (serviceModel.FlightTypeIFR) optIFRFlight.IsChecked = true;
else optVFRFlight.IsChecked= true;
chkAutoTargetFPS.IsChecked = serviceModel.AutoTargetFPS;
if (serviceModel.AutoTargetFPS || serviceModel.TLODAutoMethod[serviceModel.activeProfile] == 2) txtTargetFPS.IsEnabled = false;
else txtTargetFPS.IsEnabled = true;
if (serviceModel.OnTop) AutoFPS.Topmost = true;
else AutoFPS.Topmost = false;
chkOnTop.IsChecked = serviceModel.OnTop;
if (serviceModel.ActiveGraphicsMode == "VR") txtTargetFPS.Text = Convert.ToString(serviceModel.TargetFPS_VR, CultureInfo.CurrentUICulture);
else if (serviceModel.ActiveGraphicsMode == "FG") txtTargetFPS.Text = Convert.ToString(serviceModel.TargetFPS_FG, CultureInfo.CurrentUICulture);
else txtTargetFPS.Text = Convert.ToString(serviceModel.TargetFPS_PC, CultureInfo.CurrentUICulture);
if (!serviceModel.UseExpertOptions) serviceModel.activeProfile = (int)ServiceModel.appProfiles.NonExpert;
else if (serviceModel.FlightTypeIFR) serviceModel.activeProfile = (int)ServiceModel.appProfiles.IFR_Expert;
else serviceModel.activeProfile = (int)ServiceModel.appProfiles.VFR_Expert;
if (serviceModel.AutoTargetFPS && serviceModel.ForceAutoFPSCal) txtTargetFPS.Text = "auto";
else if (serviceModel.AutoTargetFPS) txtTargetFPS.Text = Convert.ToString(serviceModel.TargetFPS, CultureInfo.CurrentUICulture);
else if (serviceModel.ActiveGraphicsMode == "VR") txtTargetFPS.Text = Convert.ToString(serviceModel.FlightTypeIFR ? serviceModel.TargetFPS_VR : serviceModel.TargetFPS_VR_VFR, CultureInfo.CurrentUICulture);
else if (serviceModel.ActiveGraphicsMode == "FG") txtTargetFPS.Text = Convert.ToString(serviceModel.FlightTypeIFR ? serviceModel.TargetFPS_FG : serviceModel.TargetFPS_FG_VFR, CultureInfo.CurrentUICulture);
else txtTargetFPS.Text = Convert.ToString(serviceModel.FlightTypeIFR ? serviceModel.TargetFPS_PC : serviceModel.TargetFPS_PC_VFR, CultureInfo.CurrentUICulture);
serviceModel.ActiveGraphicsModeChanged = false;
txtFPSTolerance.Text = Convert.ToString(serviceModel.FPSTolerance, CultureInfo.CurrentUICulture);
txtMinTLod.Text = Convert.ToString(serviceModel.MinTLOD, CultureInfo.CurrentUICulture);
txtMaxTLod.Text = Convert.ToString(serviceModel.MaxTLOD, CultureInfo.CurrentUICulture);
txtOLODAtBase.Text = Convert.ToString(serviceModel.OLODAtBase, CultureInfo.CurrentUICulture);
txtOLODAtTop.Text = Convert.ToString(serviceModel.OLODAtTop, CultureInfo.CurrentUICulture);
txtAltOLODBase.Text = Convert.ToString(serviceModel.AltOLODBase, CultureInfo.CurrentUICulture);
txtAltOLODTop.Text = Convert.ToString(serviceModel.AltOLODTop, CultureInfo.CurrentUICulture);
txtAltTLODBase.Text = Convert.ToString(serviceModel.AltTLODBase, CultureInfo.CurrentUICulture);
txtAvgDescentRate.Text = Convert.ToString(serviceModel.AvgDescentRate, CultureInfo.CurrentUICulture);
chkDecCloudQ.IsChecked = serviceModel.DecCloudQ;
txtFPSTolerance.Text = Convert.ToString(serviceModel.FPSTolerance[serviceModel.activeProfile], CultureInfo.CurrentUICulture);
cbTLODAutoMethod.SelectedIndex = serviceModel.TLODAutoMethod[serviceModel.activeProfile];
if (serviceModel.TLODAutoMethod[serviceModel.activeProfile] == 1) lblFPSTolPercent.Visibility = Visibility.Visible;
else lblFPSTolPercent.Visibility= Visibility.Hidden;
txtMinTLod.Text = Convert.ToString(serviceModel.MinTLOD[serviceModel.activeProfile], CultureInfo.CurrentUICulture);
txtMaxTLod.Text = Convert.ToString(serviceModel.MaxTLOD[serviceModel.activeProfile], CultureInfo.CurrentUICulture);
chkCustomAutoOLOD.IsChecked = serviceModel.CustomAutoOLOD[serviceModel.activeProfile];
if (serviceModel.CustomAutoOLOD[serviceModel.activeProfile] && serviceModel.UseExpertOptions) stkpnlCustomAutoOLOD.Visibility = Visibility.Visible;
else stkpnlCustomAutoOLOD.Visibility = Visibility.Collapsed;
txtOLODAtBase.Text = Convert.ToString(serviceModel.OLODAtBase[serviceModel.activeProfile], CultureInfo.CurrentUICulture);
txtOLODAtTop.Text = Convert.ToString(serviceModel.OLODAtTop[serviceModel.activeProfile], CultureInfo.CurrentUICulture);
txtAltOLODBase.Text = Convert.ToString(serviceModel.AltOLODBase[serviceModel.activeProfile], CultureInfo.CurrentUICulture);
txtAltOLODTop.Text = Convert.ToString(serviceModel.AltOLODTop[serviceModel.activeProfile], CultureInfo.CurrentUICulture);
txtAltTLODBase.Text = Convert.ToString(serviceModel.AltTLODBase[serviceModel.activeProfile], CultureInfo.CurrentUICulture);
chkDecCloudQ.IsChecked = serviceModel.DecCloudQ[serviceModel.activeProfile];
chkPauseMSFSFocusLost.IsChecked = serviceModel.PauseMSFSFocusLost;
chkTLODMinGndLanding.IsChecked = serviceModel.TLODMinGndLanding;
if (serviceModel.TLODMinGndLanding && serviceModel.UseExpertOptions) stkpnlTLODMinOptions.Visibility = Visibility.Visible;
else stkpnlTLODMinOptions.Visibility = Visibility.Collapsed;
txtCloudRecoveryTLOD.Text = Convert.ToString(serviceModel.CloudRecoveryTLOD, CultureInfo.CurrentUICulture);
if (ServiceModel.TestVersion && serviceModel.TestLogSimValues) Logger.Log(LogLevel.Information, "MainWindow:LoadSettings", $"Expert: {serviceModel.UseExpertOptions} Mode: {serviceModel.ActiveGraphicsMode} Target: {txtTargetFPS.Text} Tol: {txtFPSTolerance.Text} TMin: {txtMinTLod.Text} TMax: {txtMaxTLod.Text} CloudQ: {serviceModel.DecCloudQ} CRecovT: {txtCloudRecoveryTLOD.Text} Pause: {serviceModel.PauseMSFSFocusLost} TMinGL: {serviceModel.TLODMinGndLanding} TLODBAlt: {serviceModel.AltTLODBase} MaxDescRate {serviceModel.AvgDescentRate} CustomOLOD: {serviceModel.CustomAutoOLOD} OLODB: {serviceModel.OLODAtBase} OLODT: {serviceModel.OLODAtTop} OLODBAlt: {serviceModel.AltOLODBase} OLODTAlt: {serviceModel.AltOLODTop}");
txtCloudRecoveryTLOD.Text = Convert.ToString(serviceModel.CloudRecoveryTLOD[serviceModel.activeProfile], CultureInfo.CurrentUICulture);
chkCloudRecoveryPlus.IsChecked = serviceModel.CloudRecoveryPlus[serviceModel.activeProfile];
if (serviceModel.UseExpertOptions && !LoadSettingsRecursed && serviceModel.CloudRecoveryPlus[serviceModel.activeProfile] && serviceModel.MinTLOD[serviceModel.activeProfile] + serviceModel.CloudRecoveryTLOD[serviceModel.activeProfile] > serviceModel.MaxTLOD[serviceModel.activeProfile])
{
serviceModel.SetSetting(serviceModel.activeProfile == (int)ServiceModel.appProfiles.IFR_Expert ? "CloudRecoveryTLOD" : "CloudRecoveryTLOD_VFR", Convert.ToString((int)(2 * (serviceModel.MaxTLOD[serviceModel.activeProfile] - serviceModel.MinTLOD[serviceModel.activeProfile]) / 5), CultureInfo.InvariantCulture));
LoadSettingsRecursed = true;
LoadSettings();
}
else if (serviceModel.UseExpertOptions && !LoadSettingsRecursed && !serviceModel.CloudRecoveryPlus[serviceModel.activeProfile] && (serviceModel.CloudRecoveryTLOD[serviceModel.activeProfile] <= serviceModel.MinTLOD[serviceModel.activeProfile] || serviceModel.CloudRecoveryTLOD[serviceModel.activeProfile] >= serviceModel.MaxTLOD[serviceModel.activeProfile]))
{
serviceModel.SetSetting(serviceModel.activeProfile == (int)ServiceModel.appProfiles.IFR_Expert ? "CloudRecoveryTLOD" : "CloudRecoveryTLOD_VFR", Convert.ToString((int)(serviceModel.MinTLOD[serviceModel.activeProfile] + 2 * (float)(serviceModel.MaxTLOD[serviceModel.activeProfile] - serviceModel.MinTLOD[serviceModel.activeProfile]) / 5), CultureInfo.InvariantCulture));
LoadSettingsRecursed = true;
LoadSettings();
}
if (ServiceModel.TestVersion || serviceModel.LogSimValues) Logger.Log(LogLevel.Information, "MainWindow:LoadSettings", $"Expert: {serviceModel.UseExpertOptions} Mode: {serviceModel.ActiveGraphicsMode} ATgtFPS: {serviceModel.AutoTargetFPS} FltType: {(serviceModel.FlightTypeIFR ? "IFR" : "VFR")} TgtFPS: {txtTargetFPS.Text} TLODAMtd: {serviceModel.TLODAutoMethod[serviceModel.activeProfile]} Tol: {txtFPSTolerance.Text}" + (!serviceModel.UseExpertOptions && serviceModel.MemoryAccess == null ? "" : $" TMin: {txtMinTLod.Text} TMax: {txtMaxTLod.Text} OLODB: {serviceModel.OLODAtBase[serviceModel.activeProfile]} OLODT: {serviceModel.OLODAtTop[serviceModel.activeProfile]} OLODBAlt: {serviceModel.AltOLODBase[serviceModel.activeProfile]}") +$" TMinEx: {(!serviceModel.UseExpertOptions || serviceModel.MinTLODExtra[serviceModel.activeProfile] ? "true" : "false")} CloudQ: {serviceModel.DecCloudQ[serviceModel.activeProfile]} CRecovT: {txtCloudRecoveryTLOD.Text} Pause: {serviceModel.PauseMSFSFocusLost} TLODBAlt: {serviceModel.AltTLODBase[serviceModel.activeProfile]} MaxDesRate {serviceModel.AvgDescentRate[serviceModel.activeProfile]} CustomOLOD: {serviceModel.CustomAutoOLOD[serviceModel.activeProfile]} OLODTAlt: {serviceModel.AltOLODTop[serviceModel.activeProfile]}");
}
protected void UpdateStatus()
@ -189,15 +263,6 @@ namespace MSFS2020_AutoFPS
lblConnStatSession.Foreground = new SolidColorBrush(Colors.Red);
}
protected string CloudQualityLabel(int CloudQuality)
{
if (CloudQuality == 0) return "Low";
else if (CloudQuality == 1) return "Medium";
else if (CloudQuality == 2) return "High";
else if (CloudQuality == 3) return "Ultra";
else return "n/a";
}
protected float GetAverageFPS()
{
if (serviceModel.MemoryAccess != null && serviceModel.FgModeEnabled && serviceModel.ActiveWindowMSFS)
@ -214,92 +279,70 @@ namespace MSFS2020_AutoFPS
lblSimFPS.Content = "n/a";
lblSimFPS.Foreground = new SolidColorBrush(Colors.Black);
}
if (serviceModel.MemoryAccess != null)
{
lblappUrl.Visibility = Visibility.Hidden;
lblStatusMessage.Foreground = new SolidColorBrush(Colors.Black);
lblSimTLOD.Content = serviceModel.tlod.ToString("F0");
lblSimOLOD.Content = serviceModel.olod.ToString("F0");
if (serviceModel.MemoryAccess.MemoryWritesAllowed())
lblStatusMessage.Content = serviceModel.MemoryAccess.IsDX12() ? "DX12" : " DX11";
if (serviceModel.VrModeActive)
{
lblStatusMessage.Content = serviceModel.MemoryAccess.IsDX12() ? "DX12" : " DX11";
if (serviceModel.VrModeActive)
{
lblSimCloudQs.Content = CloudQualityLabel(serviceModel.cloudQ_VR);
lblStatusMessage.Content += " | VR Mode";
}
else
{
lblSimCloudQs.Content = CloudQualityLabel(serviceModel.cloudQ);
lblStatusMessage.Content += (serviceModel.FgModeEnabled ? (serviceModel.ActiveWindowMSFS ? " | FG Active" : " | FG Inactive") : " | PC Mode");
}
if (!serviceModel.ActiveWindowMSFS && serviceModel.UseExpertOptions && serviceModel.PauseMSFSFocusLost) lblStatusMessage.Content += " | Auto PAUSED";
else if (serviceModel.FPSSettleCounter > 0) lblStatusMessage.Content += " | FPS Settling for " + serviceModel.FPSSettleCounter.ToString("F0") + " second" + (serviceModel.FPSSettleCounter != 1 ? "s" : "");
else lblStatusMessage.Content += serviceModel.IsAppPriorityFPS ? " | FPS priority" : " | TLOD Min priority";
lblSimCloudQs.Content = serviceModel.CloudQualityText(serviceModel.cloudQ_VR);
lblStatusMessage.Content += " | VR Mode";
}
else
{
lblStatusMessage.Content = "MSFS compatibility test failed - Read Only mode";
lblStatusMessage.Foreground = new SolidColorBrush(Colors.Red);
lblSimCloudQs.Content = serviceModel.CloudQualityText(serviceModel.cloudQ);
lblStatusMessage.Content += (serviceModel.FgModeEnabled ? (serviceModel.ActiveWindowMSFS ? " | FG Active" : " | FG Inactive") : " | PC Mode");
}
if (serviceModel.IsSessionRunning)
{
bool TLODMinGndLanding;
float MinTLOD = serviceModel.MinTLOD;
float MaxTLOD = serviceModel.MaxTLOD;
float TargetFPS = (serviceModel.FgModeEnabled && !serviceModel.ActiveWindowMSFS ? serviceModel.TargetFPS / 2 : serviceModel.TargetFPS);
if (serviceModel.UseExpertOptions)
{
TLODMinGndLanding = serviceModel.TLODMinGndLanding;
MinTLOD = serviceModel.MinTLOD;
MaxTLOD = serviceModel.MaxTLOD;
}
else
{
TLODMinGndLanding = true;
if (serviceModel.VrModeActive)
{
MinTLOD = Math.Max(serviceModel.DefaultTLOD_VR * 0.5f, 10);
MaxTLOD = serviceModel.DefaultTLOD_VR * 2.0f;
}
else
{
MinTLOD = Math.Max(serviceModel.DefaultTLOD * 0.5f, 10);
MaxTLOD = serviceModel.DefaultTLOD * 2.0f;
}
}
if (serviceModel.TLODAutoMethod[serviceModel.activeProfile] != 2 && serviceModel.FPSSettleCounter > 0) lblStatusMessage.Content += " | FPS Settling " + serviceModel.FPSSettleCounter.ToString("F0") + "s";
else if (serviceModel.TLODAutoMethod[serviceModel.activeProfile] == 2) lblStatusMessage.Content += " | Auto TLOD";
else lblStatusMessage.Content += serviceModel.IsAppPriorityFPS ? " | FPS Priority" : " | TLOD" + ((!serviceModel.UseExpertOptions || serviceModel.MinTLODExtra[serviceModel.activeProfile]) && serviceModel.MinTLODExtraActive ? "+ " : " ") + serviceModel.activeMinTLOD.ToString("F0") + " Priority";
if (serviceModel.gpuUsage >= 0) lblStatusMessage.Content += " | GPU " + serviceModel.gpuUsage.ToString("F0") + "%";
if (serviceModel.AppPaused) lblStatusMessage.Content += " | Auto PAUSED";
lblTargetFPS.Content = "Target " + serviceModel.ActiveGraphicsMode + (serviceModel.ActiveGraphicsMode == "FG" ? " Active" : "") + " FPS";
if (serviceModel.ActiveGraphicsModeChanged) LoadSettings();
float ToleranceFPS = TargetFPS * (serviceModel.UseExpertOptions ? serviceModel.FPSTolerance : 5.0f) / 100.0f;
if (TLODMinGndLanding)
{
if (GetAverageFPS() < TargetFPS - ToleranceFPS) lblSimFPS.Foreground = new SolidColorBrush(Colors.Red);
else if (GetAverageFPS() > TargetFPS + ToleranceFPS) lblSimFPS.Foreground = new SolidColorBrush(Colors.Green);
else lblSimFPS.Foreground = new SolidColorBrush(Colors.Black);
}
else
{
if (GetAverageFPS() < TargetFPS - ToleranceFPS && serviceModel.tlod == MinTLOD) lblSimFPS.Foreground = new SolidColorBrush(Colors.Red);
else if (Math.Abs(GetAverageFPS() - TargetFPS) <= ToleranceFPS) lblSimFPS.Foreground = new SolidColorBrush(Colors.Green);
else lblSimFPS.Foreground = new SolidColorBrush(Colors.Black);
}
if (serviceModel.tlod == MinTLOD && (!TLODMinGndLanding || GetAverageFPS() < TargetFPS)) lblSimTLOD.Foreground = new SolidColorBrush(Colors.Red);
else if ((!TLODMinGndLanding && serviceModel.tlod == MaxTLOD) || (TLODMinGndLanding && serviceModel.tlod == MinTLOD && GetAverageFPS() > TargetFPS)) lblSimTLOD.Foreground = new SolidColorBrush(Colors.Green);
else if (serviceModel.tlod_step) lblSimTLOD.Foreground = new SolidColorBrush(Colors.Orange);
float ToleranceFPS = TargetFPS * serviceModel.FPSTolerance[serviceModel.activeProfile] / 100.0f;
if (serviceModel.TLODAutoMethod[serviceModel.activeProfile] == 2) lblSimFPS.Foreground = new SolidColorBrush(Colors.Black);
else if (GetAverageFPS() < TargetFPS - ToleranceFPS) lblSimFPS.Foreground = new SolidColorBrush(Colors.Red);
else if (GetAverageFPS() > TargetFPS + ToleranceFPS) lblSimFPS.Foreground = new SolidColorBrush(Colors.Green);
else lblSimFPS.Foreground = new SolidColorBrush(Colors.Black);
if ((serviceModel.tlod == serviceModel.activeMinTLOD || serviceModel.tlod == serviceModel.activeMaxTLOD) && (GetAverageFPS() > TargetFPS || serviceModel.TLODAutoMethod[serviceModel.activeProfile] == 2)) lblSimTLOD.Foreground = new SolidColorBrush(Colors.Green);
else if (serviceModel.tlod == serviceModel.activeMinTLOD && GetAverageFPS() < TargetFPS) lblSimTLOD.Foreground = new SolidColorBrush(Colors.Red);
else if (serviceModel.tlod_step && !serviceModel.AppPaused) lblSimTLOD.Foreground = new SolidColorBrush(Colors.Orange);
else lblSimTLOD.Foreground = new SolidColorBrush(Colors.Black);
if (serviceModel.DecCloudQ && serviceModel.DecCloudQActive) lblSimCloudQs.Foreground = new SolidColorBrush(Colors.Red);
if (serviceModel.DecCloudQ[serviceModel.activeProfile] && serviceModel.DecCloudQActive) lblSimCloudQs.Foreground = new SolidColorBrush(Colors.Red);
else lblSimCloudQs.Foreground = new SolidColorBrush(Colors.Black);
if (serviceModel.CustomAutoOLOD && serviceModel.UseExpertOptions && (serviceModel.olod == serviceModel.OLODAtBase || serviceModel.olod == serviceModel.OLODAtTop)) lblSimOLOD.Foreground = new SolidColorBrush(Colors.Green);
else if (serviceModel.olod_step) lblSimOLOD.Foreground = new SolidColorBrush(Colors.Orange);
else lblSimOLOD.Foreground= new SolidColorBrush(Colors.Black);
if ((!serviceModel.UseExpertOptions || serviceModel.CustomAutoOLOD[serviceModel.activeProfile]) && (serviceModel.olod == serviceModel.activeOLODAtBase || serviceModel.olod == serviceModel.activeOLODAtTop)) lblSimOLOD.Foreground = new SolidColorBrush(Colors.Green);
else if (serviceModel.olod_step && !serviceModel.AppPaused) lblSimOLOD.Foreground = new SolidColorBrush(Colors.Orange);
else lblSimOLOD.Foreground = new SolidColorBrush(Colors.Black);
if (ServiceModel.TestVersion && serviceModel.TestLogSimValues && logTimer == 0 && serviceModel.FPSSettleCounter == 0 && !(!serviceModel.ActiveWindowMSFS && serviceModel.UseExpertOptions && serviceModel.PauseMSFSFocusLost))
if (serviceModel.AutoTargetFPS && serviceModel.UpdateTargetFPS)
{
Logger.Log(LogLevel.Information, "MainWindow:UpdateLiveValues", $"FPS: {lblSimFPS.Content} TLOD: {lblSimTLOD.Content} OLOD: {lblSimOLOD.Content} AGL: {lblPlaneAGL.Content} FPM: {lblPlaneVS.Content} Clouds: {lblSimCloudQs.Content}");
txtTargetFPS.Text = Convert.ToString(serviceModel.TargetFPS, CultureInfo.CurrentUICulture);
serviceModel.UpdateTargetFPS = false;
}
if ((ServiceModel.TestVersion || serviceModel.LogSimValues) && logTimer == 0 && serviceModel.FPSSettleCounter == 0 && (Math.Round(serviceModel.altAboveGnd) != Math.Round(altAboveGndLast) || Math.Round(serviceModel.tlod) != Math.Round(tlodLast) || Math.Round(serviceModel.olod) != Math.Round(olodLast) || decCloudQActiveLast != serviceModel.DecCloudQActive || IsAppPriorityFPSLast != serviceModel.IsAppPriorityFPS) && !(!serviceModel.ActiveWindowMSFS && serviceModel.UseExpertOptions && serviceModel.PauseMSFSFocusLost))
{
Logger.Log(LogLevel.Information, "MainWindow:UpdateLiveValues", $"FPS: {lblSimFPS.Content}" + (serviceModel.FgModeEnabled ? " FGAct: " + serviceModel.ActiveWindowMSFS : "") + $" Pri: {(serviceModel.IsAppPriorityFPS ? "FPS" : "TLOD")} TLOD: {lblSimTLOD.Content} OLOD: {lblSimOLOD.Content} AGL: {lblPlaneAGL.Content} FPM: {lblPlaneVS.Content} Clouds: {lblSimCloudQs.Content}" + (serviceModel.gpuUsage >= 0 ? $" GPU: {serviceModel.gpuUsage}%" : ""));
logTimer = logTimerInterval;
altAboveGndLast = serviceModel.altAboveGnd;
tlodLast = serviceModel.tlod;
olodLast = serviceModel.olod;
decCloudQActiveLast = serviceModel.DecCloudQActive;
IsAppPriorityFPSLast = serviceModel.IsAppPriorityFPS;
}
else if (--logTimer < 0) logTimer = 0;
}
@ -334,8 +377,40 @@ namespace MSFS2020_AutoFPS
protected void OnTick(object sender, EventArgs e)
{
if (serviceModel.RememberWindowPos && !serviceModel.VrModeActive)
{
if ((int)Top != serviceModel.windowTop)
{
serviceModel.windowTop = (int)Top;
serviceModel.SetSetting("windowTop", serviceModel.windowTop.ToString().ToLower());
}
if ((int)Left != serviceModel.windowLeft)
{
serviceModel.windowLeft = (int)Left;
serviceModel.SetSetting("windowLeft", serviceModel.windowLeft.ToString().ToLower());
}
}
UpdateStatus();
UpdateLiveValues();
if (serviceModel.AppEnabled) UpdateLiveValues();
else
{
lblStatusMessage.Content = "MSFS compatibility test failed - app disabled. See readme to resolve.";
lblStatusMessage.Foreground = new SolidColorBrush(Colors.Red);
lblSimTLOD.Content = "n/a";
lblSimTLOD.Foreground = new SolidColorBrush(Colors.Red);
lblSimOLOD.Content = "n/a";
lblSimOLOD.Foreground = new SolidColorBrush(Colors.Red);
lblSimCloudQs.Content = "n/a";
lblSimCloudQs.Foreground = new SolidColorBrush(Colors.Red);
lblSimFPS.Content = "n/a";
lblSimFPS.Foreground = new SolidColorBrush(Colors.Red);
lblPlaneAGL.Content = "n/a";
lblPlaneAGL.Foreground = new SolidColorBrush(Colors.Red);
lblPlaneVS.Content = "n/a";
lblPlaneVS.Foreground = new SolidColorBrush(Colors.Red);
}
UpdateAircraftValues();
}
@ -361,9 +436,21 @@ namespace MSFS2020_AutoFPS
Hide();
}
private void chkTestLogSimValues_Click(object sender, RoutedEventArgs e)
private void chkAutoTargetFPS_Click(object sender, RoutedEventArgs e)
{
serviceModel.SetSetting("testLogSimValues", chkTestLogSimValues.IsChecked.ToString().ToLower());
serviceModel.SetSetting("AutoTargetFPS", chkAutoTargetFPS.IsChecked.ToString().ToLower());
if ((bool)chkAutoTargetFPS.IsChecked)
{
serviceModel.ForceAutoFPSCal = true;
serviceModel.FPSSettleCounter = ServiceModel.FPSSettleSeconds;
}
else
{
if (serviceModel.VrModeActive) serviceModel.TargetFPS = serviceModel.FlightTypeIFR ? serviceModel.TargetFPS_VR : serviceModel.TargetFPS_VR_VFR;
else if (serviceModel.FgModeEnabled) serviceModel.TargetFPS = serviceModel.FlightTypeIFR ? serviceModel.TargetFPS_FG : serviceModel.TargetFPS_FG_VFR;
else serviceModel.TargetFPS = serviceModel.FlightTypeIFR ? serviceModel.TargetFPS_PC : serviceModel.TargetFPS_PC_VFR;
}
serviceModel.ResetCloudsTLOD();
LoadSettings();
}
private void chkOnTop_Click(object sender, RoutedEventArgs e)
@ -371,38 +458,52 @@ namespace MSFS2020_AutoFPS
serviceModel.SetSetting("OnTop", chkOnTop.IsChecked.ToString().ToLower());
LoadSettings();
}
private void chkFlightType_Click(object sender, RoutedEventArgs e)
{
serviceModel.SetSetting("FlightTypeIFR", optIFRFlight.IsChecked.ToString().ToLower());
serviceModel.ResetCloudsTLOD();
LoadSettings();
}
private void chkUseExpertOptions_Click(object sender, RoutedEventArgs e)
{
serviceModel.SetSetting("useExpertOptions", chkUseExpertOptions.IsChecked.ToString().ToLower());
serviceModel.ResetCloudsTLOD();
LoadSettings();
if (serviceModel.UseExpertOptions) stkpnlMSFSSettings.Visibility = Visibility.Visible;
else stkpnlMSFSSettings.Visibility = Visibility.Collapsed;
if (serviceModel.UseExpertOptions) stkpnlExpertSettings.Visibility = Visibility.Visible;
else stkpnlExpertSettings.Visibility = Visibility.Collapsed;
}
private void cbTLODAutoMethod_SelectionChange(object sender, EventArgs e)
{
if (serviceModel != null)
{
serviceModel.SetSetting(serviceModel.activeProfile == (int)ServiceModel.appProfiles.IFR_Expert ? "TLODAutoMethod" : "TLODAutoMethod_VFR", cbTLODAutoMethod.SelectedIndex.ToString());
serviceModel.TLODAutoMethod[serviceModel.activeProfile] = cbTLODAutoMethod.SelectedIndex;
serviceModel.ResetCloudsTLOD();
LoadSettings();
}
}
private void chkCustomAutoOLOD_Click(object sender, RoutedEventArgs e)
{
serviceModel.SetSetting("customAutoOLOD", chkCustomAutoOLOD.IsChecked.ToString().ToLower());
serviceModel.SetSetting(serviceModel.activeProfile == (int)ServiceModel.appProfiles.IFR_Expert ? "customAutoOLOD" : "customAutoOLOD_VFR", chkCustomAutoOLOD.IsChecked.ToString().ToLower());
LoadSettings();
}
private void chkOpenWindow_Click(object sender, RoutedEventArgs e)
private void chkCloudRecoveryPlus_Click(object sender, RoutedEventArgs e)
{
serviceModel.SetSetting("openWindow", chkOpenWindow.IsChecked.ToString().ToLower());
serviceModel.SetSetting(serviceModel.activeProfile == (int)ServiceModel.appProfiles.IFR_Expert ? "CloudRecoveryPlus" : "CloudRecoveryPlus_VFR", chkCloudRecoveryPlus.IsChecked.ToString().ToLower());
if (((bool)chkCloudRecoveryPlus.IsChecked && (serviceModel.CloudRecoveryTLOD[serviceModel.activeProfile] < 5 || serviceModel.CloudRecoveryTLOD[serviceModel.activeProfile] > serviceModel.MaxTLOD[serviceModel.activeProfile] - serviceModel.MinTLOD[serviceModel.activeProfile] - 5)) || (!(bool)chkCloudRecoveryPlus.IsChecked && (serviceModel.CloudRecoveryTLOD[serviceModel.activeProfile] < serviceModel.MinTLOD[serviceModel.activeProfile] + 5 || serviceModel.CloudRecoveryTLOD[serviceModel.activeProfile] > serviceModel.MaxTLOD[serviceModel.activeProfile] - 5))) serviceModel.SetSetting(serviceModel.activeProfile == (int)ServiceModel.appProfiles.IFR_Expert ? "CloudRecoveryTLOD" : "CloudRecoveryTLOD_VFR", Convert.ToString(Math.Round(((bool)chkCloudRecoveryPlus.IsChecked ? 0 : serviceModel.MinTLOD[serviceModel.activeProfile]) + 2 * (serviceModel.MaxTLOD[serviceModel.activeProfile] - serviceModel.MinTLOD[serviceModel.activeProfile]) / 5), CultureInfo.InvariantCulture));
serviceModel.ResetCloudsTLOD();
LoadSettings();
}
private void chkTLODMinGndLanding_Click(object sender, RoutedEventArgs e)
private void chkMinTLODExtra_Click(object sender, RoutedEventArgs e)
{
serviceModel.SetSetting("TLODMinGndLanding", chkTLODMinGndLanding.IsChecked.ToString().ToLower());
serviceModel.SetSetting(serviceModel.activeProfile == (int)ServiceModel.appProfiles.IFR_Expert ? "MinTLODExtra" : "MinTLODExtra_VFR", chkMinTLODExtra.IsChecked.ToString().ToLower());
serviceModel.ResetCloudsTLOD();
LoadSettings();
if (serviceModel.MemoryAccess != null)
{
if (serviceModel.VrModeActive) serviceModel.MemoryAccess.SetCloudQ_VR(serviceModel.DefaultCloudQ_VR);
else serviceModel.MemoryAccess.SetCloudQ(serviceModel.DefaultCloudQ);
serviceModel.DecCloudQActive = false;
}
}
private void chkDecCloudQ_Click(object sender, RoutedEventArgs e)
{
serviceModel.SetSetting("DecCloudQ", chkDecCloudQ.IsChecked.ToString().ToLower());
serviceModel.SetSetting(serviceModel.activeProfile == (int)ServiceModel.appProfiles.IFR_Expert ? "DecCloudQ" : "DecCloudQ_VFR", chkDecCloudQ.IsChecked.ToString().ToLower());
LoadSettings();
chkCloudRecoveryTLOD_WindowVisibility();
@ -489,12 +590,13 @@ namespace MSFS2020_AutoFPS
{
case "targetFps":
if (iValue < 10 || iValue > 200) iValue = serviceModel.TargetFPS;
if (serviceModel.ActiveGraphicsMode == "VR") key = "targetFpsVR";
else if (serviceModel.ActiveGraphicsMode == "FG") key = "targetFpsFG";
else key = "targetFpsPC";
if (serviceModel.ActiveGraphicsMode == "VR") key = serviceModel.FlightTypeIFR ? "targetFpsVR" : "targetFpsVRVFR";
else if (serviceModel.ActiveGraphicsMode == "FG") key = serviceModel.FlightTypeIFR ? "targetFpsFG" : "targetFpsFGVFR";
else key = serviceModel.FlightTypeIFR ? "targetFpsPC" : "targetFpsPCVFR";
break;
case "FpsTolerance":
if (iValue > 20) iValue = serviceModel.FPSTolerance;
if (iValue > 20) iValue = serviceModel.FPSTolerance[serviceModel.activeProfile];
if (serviceModel.activeProfile != (int)ServiceModel.appProfiles.IFR_Expert) key += "_VFR";
break;
default:
break;
@ -509,37 +611,44 @@ namespace MSFS2020_AutoFPS
switch (key)
{
case "minTLod":
if (fValue < 10 || fValue > ServiceModel.TLODMinLockAlt || fValue > serviceModel.MaxTLOD - 10) fValue = serviceModel.MinTLOD;
if (serviceModel.CloudRecoveryTLOD < fValue + 10) serviceModel.SetSetting("CloudRecoveryTLOD", Convert.ToString(Math.Round(2 * (fValue + serviceModel.MaxTLOD) / 5), CultureInfo.InvariantCulture));
if (fValue < 10 || fValue > serviceModel.MaxTLOD[serviceModel.activeProfile] - 10) fValue = serviceModel.MinTLOD[serviceModel.activeProfile];
if (serviceModel.CloudRecoveryPlus[serviceModel.activeProfile] ? serviceModel.CloudRecoveryTLOD[serviceModel.activeProfile] + fValue > serviceModel.MaxTLOD[serviceModel.activeProfile] - 10 : serviceModel.CloudRecoveryTLOD[serviceModel.activeProfile] < fValue + 10) serviceModel.SetSetting(serviceModel.activeProfile == (int)ServiceModel.appProfiles.IFR_Expert ? "CloudRecoveryTLOD" : "CloudRecoveryTLOD_VFR", Convert.ToString(Math.Round((serviceModel.CloudRecoveryPlus[serviceModel.activeProfile] ? 0 : serviceModel.MinTLOD[serviceModel.activeProfile]) + 2 * (serviceModel.MaxTLOD[serviceModel.activeProfile] - fValue) / 5), CultureInfo.InvariantCulture));
break;
case "maxTLod":
if (fValue < serviceModel.MinTLOD + 10 || fValue > 1000) fValue = serviceModel.MaxTLOD;
if (serviceModel.CloudRecoveryTLOD > fValue - 10) serviceModel.SetSetting("CloudRecoveryTLOD", Convert.ToString(Math.Round(2 * (fValue + serviceModel.MinTLOD) / 5), CultureInfo.InvariantCulture));
if (fValue < serviceModel.MinTLOD[serviceModel.activeProfile] + 10 || fValue > 1000) fValue = serviceModel.MaxTLOD[serviceModel.activeProfile];
if ((serviceModel.CloudRecoveryPlus[serviceModel.activeProfile] ? serviceModel.MinTLOD[serviceModel.activeProfile] : 0) + serviceModel.CloudRecoveryTLOD[serviceModel.activeProfile] > fValue - 10) serviceModel.SetSetting(serviceModel.activeProfile == (int)ServiceModel.appProfiles.IFR_Expert ? "CloudRecoveryTLOD" : "CloudRecoveryTLOD_VFR", Convert.ToString(Math.Round((serviceModel.CloudRecoveryPlus[serviceModel.activeProfile] ? 0 : serviceModel.MinTLOD[serviceModel.activeProfile]) + 2 * (fValue - serviceModel.MinTLOD[serviceModel.activeProfile]) / 5), CultureInfo.InvariantCulture));
break;
case "CloudRecoveryTLOD":
if (fValue < serviceModel.MinTLOD + 5 || fValue > serviceModel.MaxTLOD - 5) fValue = serviceModel.CloudRecoveryTLOD;
if (serviceModel.CloudRecoveryPlus[serviceModel.activeProfile] && (fValue < 5 || fValue > serviceModel.MaxTLOD[serviceModel.activeProfile] - serviceModel.MinTLOD[serviceModel.activeProfile] - 5)) fValue = serviceModel.CloudRecoveryTLOD[serviceModel.activeProfile];
else if (!serviceModel.CloudRecoveryPlus[serviceModel.activeProfile] && (fValue < serviceModel.MinTLOD[serviceModel.activeProfile] + 5 || fValue > serviceModel.MaxTLOD[serviceModel.activeProfile] - 5)) fValue = serviceModel.CloudRecoveryTLOD[serviceModel.activeProfile];
break;
case "OLODAtBase":
if (fValue < 10 || fValue > 1000) fValue = serviceModel.OLODAtBase;
if (fValue < 10 || fValue > 1000) fValue = serviceModel.OLODAtBase[serviceModel.activeProfile];
break;
case "OLODAtTop":
if (fValue < 10 || fValue > 1000) fValue = serviceModel.OLODAtTop;
if (fValue < 10 || fValue > 1000) fValue = serviceModel.OLODAtTop[serviceModel.activeProfile];
break;
case "AltOLODBase":
if (fValue < 1000 || fValue >= 100000 || fValue > serviceModel.AltOLODTop - 1) fValue = serviceModel.AltOLODBase;
if (fValue < 1000 || fValue >= 100000 || fValue > serviceModel.AltOLODTop[serviceModel.activeProfile] - 1) fValue = serviceModel.AltOLODBase[serviceModel.activeProfile];
break;
case "AltOLODTop":
if (fValue < 2000 || fValue > 100000 || fValue < serviceModel.AltOLODBase + 1) fValue = serviceModel.AltOLODTop;
if (fValue < 2000 || fValue > 100000 || fValue < serviceModel.AltOLODBase[serviceModel.activeProfile] + 1) fValue = serviceModel.AltOLODTop[serviceModel.activeProfile];
break;
case "AltTLODBase":
if (fValue < 0 || fValue >= 100000) fValue = serviceModel.AltTLODBase;
if (fValue < 0 || fValue >= 100000) fValue = serviceModel.AltTLODBase[serviceModel.activeProfile];
break;
case "AvgDescentRate":
if (fValue < 200 || fValue > 10000) fValue = serviceModel.AvgDescentRate;
if (serviceModel.TLODAutoMethod[serviceModel.activeProfile] == 2)
{
key = "AltTLODTop";
if (fValue < 1000 || fValue > 100000 || fValue < serviceModel.AltTLODBase[serviceModel.activeProfile] + 1) fValue = serviceModel.AltTLODTop[serviceModel.activeProfile];
}
if (fValue < 200 || fValue > 10000) fValue = serviceModel.AvgDescentRate[serviceModel.activeProfile];
break;
default:
break;
}
if (serviceModel.activeProfile != (int)ServiceModel.appProfiles.IFR_Expert) key += "_VFR";
serviceModel.SetSetting(key, Convert.ToString(fValue, CultureInfo.InvariantCulture));
}
@ -565,11 +674,7 @@ namespace MSFS2020_AutoFPS
{
}
private void chkTLODMinGndLanding_Checked(object sender, RoutedEventArgs e)
{
}
private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e)
{
try
@ -587,21 +692,33 @@ namespace MSFS2020_AutoFPS
}
private void chkCloudRecoveryTLOD_WindowVisibility()
{
if (serviceModel.DecCloudQ)
if (serviceModel.DecCloudQ[serviceModel.activeProfile])
{
lblCloudRecoveryTLOD.Visibility = Visibility.Visible;
txtCloudRecoveryTLOD.Visibility = Visibility.Visible;
lblCloudPlus.Visibility = Visibility.Visible;
chkCloudRecoveryPlus.Visibility = Visibility.Visible;
}
else
{
lblCloudRecoveryTLOD.Visibility = Visibility.Hidden;
txtCloudRecoveryTLOD.Visibility = Visibility.Hidden;
lblCloudPlus.Visibility = Visibility.Hidden;
chkCloudRecoveryPlus.Visibility = Visibility.Hidden;
}
}
private void chkCustomAutoOLOD_Checked(object sender, RoutedEventArgs e)
{
}
private void chkCloudRecoveryPlus_Checked(object sender, RoutedEventArgs e)
{
}
private void chkMinTLODExtra_Checked(object sender, RoutedEventArgs e)
{
}
}
}

View File

@ -110,8 +110,8 @@ namespace MSFS2020_AutoFPS
Logger.Log(LogLevel.Debug, "MemoryManager:BoundaryTest", $"TLOD VR: {GetTLOD_VR()}");
Logger.Log(LogLevel.Debug, "MemoryManager:BoundaryTest", $"OLOD PC: {GetOLOD_PC()}");
Logger.Log(LogLevel.Debug, "MemoryManager:BoundaryTest", $"OLOD VR: {GetOLOD_VR()}");
Logger.Log(LogLevel.Debug, "MemoryManager:BoundaryTest", $"Cloud Quality PC: {GetCloudQ_PC()}");
Logger.Log(LogLevel.Debug, "MemoryManager:BoundaryTest", $"Cloud Quality VR: {GetCloudQ_VR()}");
Logger.Log(LogLevel.Debug, "MemoryManager:BoundaryTest", $"Cloud Quality PC: {Model.CloudQualityText(GetCloudQ_PC())}");
Logger.Log(LogLevel.Debug, "MemoryManager:BoundaryTest", $"Cloud Quality VR: {Model.CloudQualityText(GetCloudQ_VR())}");
Logger.Log(LogLevel.Debug, "MemoryManager:BoundaryTest", $"VR Mode: {MemoryInterface.ReadMemory<int>(addrVrMode)}");
Logger.Log(LogLevel.Debug, "MemoryManager:BoundaryTest", $"Ansio Filter: {MemoryInterface.ReadMemory<int>(addrTLOD + offsetPointerAnsioFilter)}");
Logger.Log(LogLevel.Debug, "MemoryManager:BoundaryTest", $"Water Waves: {MemoryInterface.ReadMemory<int>(addrTLOD + offsetWaterWaves)}");

223
MSFS2020_AutoFPS/README.md Normal file
View File

@ -0,0 +1,223 @@
# MSFS2020_AutoFPS
Based on muumimorko's idea and code in MSFS_AdaptiveLOD, as further developed by Fragtality in DynamicLOD and myself in DynamicLOD_ResetEdition.<br/><br/>
This app aims to improve the MSFS user experience by automatically changing key MSFS settings that impact MSFS performance and smoothness the most. It has an easy to use UI and provides features such as:<br/>
- Automatic TLOD adjustment when in the air to either achieve and maintain a target FPS or to an altitude schedule, the latter as an expert option,
- Improved target FPS tracking for all modes by having much smaller TLOD changes the closer you are to your target FPS, giving more consistent FPS for a better flight experience.
- A choice between VFR (GA) and IFR (Airliner) flight types, which defaults to settings suitable to each flight type and is fully customisable in Expert mode.
- Auto raising and lowering of the minimum TLOD option, depending on low altitude performance being either very favourable or poor respectively,
- Auto target FPS option, which is useful if you don't know what target FPS to choose or if your flight types are so varied that a single target FPS value is not always appropriate,
- Cloud quality decrease option for when FPS can't be achieved at the lowest desired TLOD,
- Automatic OLOD adjustment option based on an automatic or user-definable OLOD range and altitude band (AGL),
- Simultaneous PC, FG and VR mode compatibilty including correct FG FPS display and separate FPS targets for each mode,
- A greatly simplified non-expert default UI option that uses pre-defined settings for an automated experience suited to most user scenarios,
- Auto detection and protection from known similar apps already running or incompatibilities with newer MSFS versions, and
- Auto restoration of original MSFS settings changed by the app, recently enhanced to withstand MSFS CTDs.<br><br>
**Really, really important:**
- Do not even mention, let alone try to discuss, this app on the MSFS official forums, even in personal messages, as they have taken the view that this app modifies licenced data, regardless of how harmless the way in which the app does it, and is therefore a violation of their Terms of Service and Code of Conduct for that website. If you do so, your post/personal message will be flagged by moderators and you may get banned from the MSFS official forums. You have been warned!
- Notwithstanding, there is a new MSFS wishlist item requesting simconnect variables access to MSFS settings, which would allow me to make this app legitimate in MS/Abobo's eyes and expand the range of possibilities of what this app could do in future. Please vote for it [here](https://forums.flightsimulator.com/t/expose-tlod-olod-clouds-etc-via-simconnect-l-vars/634075).
Important:<br/>
- This app directly accesses active MSFS memory locations while MSFS is running to read and set TLOD and cloud quality settings on the fly at a maximum rate of one read and, if required, change per setting per second. The app will first verify that the MSFS memory locations being used are still valid and if not, likely because of an MSFS version change, will attempt to find where they have been relocated. If it does find the new memory locations and they pass validation tests, the app will update itself automatically and will function as normal. If it can't find or validate MSFS memory locations at any time when starting up, the app will self-restrict to read only mode to prevent the app making changes to unknown MSFS memory locations.
- As such, I believe the app to be robust in its interaction with validated MSFS memory locations and to be responsible in disabling itself if it can't guarantee that. Nonetheless, this app is offered as is and no responsibility will be taken for unintended negative side effects. Use at your own risk!<br/><br/>
## FAQ
I am new to this app/MSFS, or I don't care for all this technical jargon. What is the simplest way to use this app to make my MSFS experience better?
- Start the app before you load your flight,
- Leave Use Expert Settings unchecked,
- Pick what type of flight you are doing via the radio buttons ie. either VFR (GA aircraft) or IFR (airliners),
- Enter a realistic target FPS (or click on auto target FPS for the app to pick it for you),
- Click back on MSFS and wait until any FPS settle timer has finished (20 seconds max), then
- Go fly!
I am getting major stuttering, freezes or CTDs in MSFS using this app. What can I do to stop them?
- By far the most common reason is users have enabled expert settings and have modified the default settings to be way beyond what their system is capable of, even without running the app.
- As such, the first step to resolve is to restore the app's default settings, which you can do by using the installer to uninstall (remove option) and reinstall, which will recreate your config file.
- Rerun the app and try non-expert mode with IFR flight type and Auto Target FPS checked.
- If this doesn't resolve it, try enabling expert options and reducing the FPS Sensitivity setting to 2, to allow smaller TLOD changes.
- If still not resolved, try the FPS Tolerance mode, which was the automation method in the original release version that had larger TLOD changes but they occurred less often, with a setting of 5.
- Finally, if still not resolved, raise an issue here on github and I will do my best to help you, provided you have completed all of the aforementioned steps first.
My default MSFS TLOD, OLOD and/or cloud settings are messed up and each time I try to change them back they get messed up again. How do I fix this?
- You are likely trying to change these default MSFS settings while the app is still running and you are in an active flight, where the app will override any such changes you try to make.
- Either exit the app completely from the System Tray or be in the MSFS main menu (ie. NOT in a flight), then you can go to the MSFS settings screen and change your default MSFS settings to what you want and the app will restore these upon exiting.
Which app should I use? DynamicLOD_ResetEdition or MSFS2020_AutoFPS?:
- Essentially both apps are intended to give you better overall performance but with different priorities to achieve it that result in a slightly different experience. They both allow a lower TLOD down low and on the ground, when your viewing distance reduced anyway so the visual impact is minimal, and a higher TLOD when at higher altitude and not in close proximity to complex scenery or traffic. They also adjust OLOD and Cloud Quality but TLOD is usually the most important determiner of performance at these two extremes.
- Where they differ is that DynamicLOD provides user set tables for LOD changes at specific altitudes, giving the user precise control over when and where these changes take place such that they can optimise them to their particular flight activity they normally do, and can set a specific profile for each one. The price of such precise control is that the user must be intimately familiar with LODs to be able to tune a variety of settings in the app for the best outcome and this can be a bit daunting for more casual and non-technical users.
- Alternatively, AutoFPS seeks to automate these changes as much as possible based on a target FPS and a minimum and maximum LOD range within which to automatically adjust. This results in a much simpler and generally similarly acceptable user experience compared to DynamicLOD. Nonetheless, the automation algorithm does require FPS headroom to function correctly, so can conflict in cases where an FPS cap is being used, such as with Vsync or motion reprojection in VR. Additionally, AutoFPS tends to make constant small changes to TLOD, much more than DynamicLOD does, and this can induce stuttering on older hardware as it struggles to manage even small scenery changes. In these cases, the user would be better off using DynamicLOD in a more manually tuned approach.
- Both apps can be installed concurrently, but only one can be running at a time.
How does this app work for Frame Generation (FG) users?
- The app does detect correct FG FPS when FG is enabled in MSFS, however FG is only active when MSFS is the focused window and becomes inactive when not, through your graphics driver not this app.
- To see correct FG FPS, use the app's "On Top" option to overlay this app over MSFS and give MSFS the focus.
- If FG is being incorrectly reported as enabled by the app, the likely reason is that either the FG mod had been installed and removed or you have disabled Hardware Accelerated Graphics Scheduling under Windows settings and the now the now greyed out MSFS FG setting may show that it is off but it is still set to on internally to MSFS. To fix, change the DLSSG line in your UserCfg.opt file to be DLSSG 0.
Why am I getting a dangerous/Unsafe program warning when trying to download or install?
- This app is unsigned because I am a hobbyist and the cost of obtaining certification is prohibitive to me, so you may get a warning message of a potentially dangerous app when you download it in a web browser like Chrome or from your antivirus program, including Windows Defender.
- You can either trust this download, based on feedback you can easily find on Avsim and Youtube, make an exception in your browser and/or antivirus program for the download then run a virus scan and malware scan before you install just be sure, or just not install and use this app.<br/><br/>
## Requirements
The Installer will install the following Software:
- .NET 7 Desktop Runtime (x64)
- MobiFlight Event/WASM Module
<br/>
[Download here](https://github.com/ResetXPDR/MSFS2020_AutoFPS/releases/latest)
(Under Assests, the MSFS2020_AutoFPS-Installer-vXYZ.exe File)
<br/><br/>
## Installation / Update / Uninstall
Basically: Just run the Installer to either install, update or uninstall.<br/>
Some Notes:
- MSFS2020_AutoFPS has to be stopped before installing.
- If the MobiFlight Module is not installed or outdated, MSFS also has to be stopped.
- If you have duplicate MobiFlight Modules installed, in either your official or community folders, the app may display 0 value Sim Values and otherwise not function. Remove the duplicate versions, rerun the app installer and it should now work.
- Do not run the Installer as Admin!
- If the installer will not run at all, Windows SmartScreen is potentially blocking it because the app is so new. The solution to try is:
- Right-click on the Installer and select properties
- Check the option "Unblock"
- Click on Apply and Ok to save the change
- Then try to install it again
- If you wish to retain your settings for an update version, do NOT uninstall first, as that deletes all app files, including the config file. Just run the installer, select update and your settings will be retained.
- For Auto-Start either your FSUIPC7.ini or EXE.xml (MSFS) is modified. The Installer does not create a Backup.
- The app may be blocked by Windows Security or your AV-Scanner, if so try to unblock or set an exception (for the whole Folder)
- The Installation-Location is fixed to %appdata%\MSFS2020_AutoFPS (your Users AppData\Roaming Folder) and can't be changed.
- Binary in %appdata%\MSFS2020_AutoFPS\bin
- Logs in %appdata%\MSFS2020_AutoFPS\log
- Config: %appdata%\MSFS2020_AutoFPS\MSFS2020_AutoFPS.config
- If after installing and running the app your simconnect always stays red, try downloading and installing a Microsoft official version of “Microsoft Visual C++ 2015 - 2022 Redistributable”, which may be missing from your Windows installation.
- If you get an "MSFS compatibility test failed - app disabled." message there are three possible causes:
- There is an issue with permissions and you may need to run the app as Administrator. This is by far the most likely cause and resolution.
- You may have changed MSFS settings in your usercfg.opt file beyond what is possible to set in the MSFS settings menu. To rectify, go into MSFS settings at the main menu and reset to default (F12) the graphics settings for both PC and VR mode, then make all changes to MSFS within the MSFS settings menu.
- A new version of MSFS has come out that has a different memory map to what the app expects, which has happened only once since MSFS 2020 was released, and the app can't auto adjust to the new memory location for MSFS settings. If so, I will likely be already aware of it and working on a solution, but if you may be one of the first to encounter it (eg. on an MSFS beta) then please do let me know.
- If you get a message when starting the app that you need to install .NET desktop runtime, manually download it from [here](https://dotnet.microsoft.com/en-us/download/dotnet/7.0).
- If you get an error message saying "Required EXE.xml file not found for AutoStartExe" when trying to install with the autostart option for MSFS, it usually means that your MSFS installation is missing the required EXE.xml file in which to place the autostart entry. To resolve, you need to go to your MSFS root user directory (MS Store Version: "C:\Users\YOUR_USERNAME\AppData\Local\Packages\Microsoft. FlightSimulator_8wekyb3d8bbwe\LocalCache\ or Steam Version: "C:\Users\YOUR_USERNAME\AppData\Roaming\Microsoft Flight Simulator\") and manually create an EXE.xml file and save it there. You can use the following EXE.xml template, inserting your Windows username where shown:
```
<?xml version="1.0" encoding="Windows-1252"?>
<SimBase.Document Type="Launch" version="1,0">
<Descr>Launch</Descr>
<Filename>EXE.xml</Filename>
<Disabled>False</Disabled>
<Launch.ManualLoad>False</Launch.ManualLoad>
<Launch.Addon>
<Disabled>False</Disabled>
<ManualLoad>False</ManualLoad>
<Name>MSFS2020_AutoFPS</Name>
<Path>C:\Users\<username>\AppData\Roaming\MSFS2020_AutoFPS\bin\MSFS2020_AutoFPS.exe</Path>
</Launch.Addon>
</SimBase.Document>
```
- To uninstall, ensure you have completely exited the app (ie. it is not hiding still running in your SysTray), run the installer and select remove on the first window. This will remove all traces of the app, including the desktop icon, MSFS or FSUIPC autostart entries if you used them, and the entire app folder, including your configuration file.
<br/><br/>
## Usage / Configuration
- General
- Starting manually: anytime, but preferably before MSFS or in the Main Menu. The app will stop itself when MSFS closes.
- Closing the Window does not close the app, use the Context Menu of the SysTray Icon.
- Clicking on the SysTray Icon opens the Window (again).
- If you wish to have the app window always open to the SysTray, close the app and manually change the openWindow key state in the config file to false.
- The app's window position will be remembered between sessions, except movements to it made while in VR due to window restoration issues. If there are issues with the window not displaying correctly on startup, as can happen when auto-starting the app through MSFS of FSUIPC, either don't use auto-start or manually disable this feature in the config file by setting the RememberWindowPos line to be false.
- Runnning as Admin NOT usually required (BUT: It is required to be run under the same User/Elevation as MSFS).
- Do not change TLOD, OLOD and Cloud Quality MSFS settings manually while in a flight with this app running as it will conflict with what the app is managing and they will not restore to what you set when you exit your flight. If you wish to change the defaults for these MSFS settings, you must do so either without this app running or, if it is, only while you are in the MSFS main menu (ie not in a flight).
- If you wish to activate additional logging of settings changes and sim values, as currently happens automatically in test versions, you need to manually edit your config file and add a LogSimValues key, if it doesn't already exist, and set its value to true ie. ```<add key="LogSimValues" value="true" />```
- Connection Status
- Red values indicate not connected, green is connected.
- Sim Values
- Will not show valid values unless all three connections are green. n/a means not available right now.
- Green means the sim value is at or better than target value being sought, red means at lowest level or worse than target value being sought, orange means TLOD or OLOD is auto adjusting, black is shown otherwise.
- FPS shows the FPS for the current graphics mode averaged over 5 seconds which will smooth out any transient FPS spikes experienced when panning or loading new scenery or objects so that automated MSFS setting changes are minimised.
- General
- Status Message - Displays key system messages, such as:
- Before loading a flight - whether a newer version of the app is available to download and install,
- Loading in to a flight - whether MSFS memory integrity test have failed, and
- Flight is loaded
- Shows detected DX version, Graphics Mode (PC, FG, or VR), app pause, FPS settling time, and/or app priority mode as applicable.
- The FPS settling timer runs for up to 20 seconds to allow FPS to settle between pausing/unpausing, auto target FPS calibration, TLOD Min + transitions and VR/PC/FG mode transitions. This allows the FPS to stabilise before engaging automatic functions and should lead to much smaller TLOD changes when seeking the target FPS on such transitions.
- App priority shows whether FPS or TLOD are the current automation priority. A + next to TLOD indicates that TLOD Min + has been activated and that a higher TLOD Min should be expected.
- Bonus GPU load display if the optional GPU-Z companion app, downloadable separately [here](https://www.techpowerup.com/download/techpowerup-gpu-z/), is installed and detected running when starting any flight session.
- Auto pause will activate if in flight and either MSFS is in active pause or the MSFS settings menu is being accessed.
- Target FPS - The most important setting in this app.
- Set it to what FPS you want the app to target while running, noting that this value should be at the mid to lower end of what your system is capable of otherwise the app will be unlikely to achieve it.
- There is a setting for each graphics mode (PC, FG and VR) and each flight mode (VFR and IFR), which you can only change while in those mode pairs. This is particularly useful if regularly switching between FG mode and VR mode in your flights as the FG FPS target can be significantly higher than the one for VR.
- If using FG, the target FPS you set is your desired FG Active FPS, not the FG Inactive FPS you see when this app has the focus instead of MSFS.
- If you use an FPS cap, or Vsync for the same purpose, you will need to set your target FPS to be a few FPS lower than that cap. This allows the automated TLOD increase logic to function properly because it needs FPS to get above the target FPS to activate an increase in TLOD. If doing so causes unacceptable tearing of the image on your monitor, or breaks motion reprojection if you use it with VR, then this app likely isn't suitable for you.
- Auto Target FPS
- When checked, a target FPS will automatically be calculated, following any initial FPS settling, when stationary on the ground or any time you are in the air.
- Automatically recalulated if performance conditions are too low for the calculated target FPS, on the ground after arriving at a new destination, if you change graphics mode or if you uncheck then check the option again for a quick recalibration.
- With IFR it will range from 95% of your current average FPS on the ground to 85% at or above 3000 ft, the latter being lower to give head room for Max TLOD.
- With VFR it will be 5% less than each of the IFR percentages respectively to better suit the greater performance expectation with VFR flights.
- On Top
- Allows the app to overlay your MSFS session if desired, with MSFS having the focus.
- Mainly useful for adjusting settings and seeing the outcome over the top of your flight as it progresses.
- Should also satisfy single monitor users utilising the FG capability of MSFS as they now see the true FG FPS the app is reading when MSFS has the focus.
- Flight type - VFR or IFR
- In non-expert mode, VFR will use higher minimum and maximum TLODs and a lower TLOD base altitude than IFR to account for the greater performance expectation that GA flights in rural areas will have.
- Expert mode will default to similar settings differences, however the settings for each flight type are fully customisable and will save to and restore from separate profiles for VFR and IFR.
- On the ground, TLOD will be locked to either a pre-determined (non-expert) or user-selectable (expert) TLOD Min.
- Once in the air and above either a pre-determined (non-expert) or user-selectable (expert) TLOD base altitude, TLOD will be allowed to change to the lower of either the schedule based on your TLODs, FPS sensitivity/tolerance and average descent rate settings or what your current performance dictates.
- Once above a calculated altitude band above the the TLOD base altitude, the app priority will change from TLOD to FPS.
- On descent your TLOD will progressively work its way down to TLOD Min by the TLOD base altitude.
- Use Expert Options - When disabled allows the app to use default settings in conjuction with your chosen target FPS that should produce good automated FPS tracking, provided you have set a realistic FPS target within your system's performance capability. When enabled, the UI expands to show additional MSFS settings to adjust. If you do not understand these settings and their impact on MSFS performance and graphics quality, it is strongly recommended that you do not use these expert options and you should uncheck this option. When Use Expert Setting is unchecked, the following internal settings are used by the app:
- Auto Target FPS - user selectable
- FPS Sensitivity - 5%
- VFR or IFR flight type - user selectable
- Alt TLOD Base - VFR 100 ft, IFR 1000 ft
- Avg Descent Rate - VFR 1000 fpm, IFR 2000 fpm
- TLOD Minimum - VFR 100% of your current MSFS TLOD setting, IFR 50%
- TLOD Maximum - VFR 300% of your current MSFS TLOD setting, IFR 200%
- TLOD Min + - enabled
- Decrease Cloud Quality - enabled
- Cloud Recovery TLOD
- 2/5 between TLOD Minimum and TLOD Maximum or + 50 over TLOD Min, whichever is lower.
- If excessive changing of cloud quality levels are detected, the app will automatically increase its calculated cloud recovery TLOD.
- Auto OLOD - enabled and VFR 150% of your current MSFS OLOD setting, IFR 100%
- Pause when MSFS loses focus - disabled, unless using FG then enabled
- Expert Settings
- FPS Automation Method - FPS Sensitivity generally gives better results and hence is the default. Use FPS Tolerance if you experience stuttering issues.
- FPS Sensitivity (v0.4.2 and later) - smaller changes more often.
- Determines how sensitive the app will be to the variance between your current and target FPS.
- Also determines the largest TLOD step size you will see, being double the FPS sensitivity number.
- The lower the setting the smaller the changes will be, which is useful if you are experiencing stuttering with the default value of 5. Vice versa for higher settings.
- FPS Tolerance (all versions except 0.4.2) - larger changes less often.
- Determines how much variance from your target FPS must occur before the app will adjust MSFS settings to achieve the target FPS and what nominal magnitude those changes will be.
- The lower the setting, the more reactive the app will be, the more MSFS settings changes will occur and the changes will be smaller.
- TLOD changes are allowable on the ground in this mode with the VFR flight type, so be aware of potential ground texture tearing/flashing issues because of it.
- Auto TLOD - functions the same way that Auto OLOD does ie. independent of FPS.
- TLOD will adjust based on an altitude band with a base and top level and with TLOD values defined for each of these altitudes.
- The app will set TLOD @ Base at or below the Alt TLOD Base (AGL), set the TLOD @ Top at or above Alt TLOD Top (AGL) and interpolate in between.
- As this function completely ignores FPS, all FPS-related settings are removed from the UI when using this method. These are Target FPS, Auto Target FPS, TLOD Min + and Pause on MSFS losing Focus.
- FPS settling time is also disabled as it does not apply to this automation method.
- Pause when MSFS loses focus
- Will stop LODs and, if applicable, cloud quality from changing while you are focused on another app and not MSFS.
- Particularly useful for when using FG as the FG active and inactive frame rate can vary quite considerably and because FG is not always an exact doubling of non-FG FPS.
- TLOD Min with optional +
- Sets the minimum TLOD the automation algorithm will use.
- When + is checked and your system is achieving 15% or greater FPS than your target FPS, then your TLOD Min will increase by 50 - giving you additional graphics quality.
- TLOD Min + will only activate on the ground or when descending and transitioning from FPS to TLOD priority mode. Once activated on the ground, it will remain set so as not to tempt ground texture corruption occurring. On descent, if minimum performance can not be maintained for TLOD Min +, it will self-cancel before landing without any sudden TLOD changes.
- TLOD Max - Sets the maximum TLOD the automation algorithm will use.
- Alt TLOD Base - Altitude (AGL) at or below which TLOD will be at TLOD Min.
- Avg Descent Rate- Used in combination with FPS sensitivity to determine the altitude band in which TLOD will be interpolated between TLOD Min at the Alt TLOD base starting point and the lower of TLOD Max and the maximum TLOD your system can achieve while achieving at least your desired FPS target at a calculated top altitude.
- This band ensures that, if you descend at your set Avg Descent Rate or less, that the app can decrement TLOD from TLOD Max to TLOD Min by the Alt TLOD Base without exceeding the LOD Step rate associated with the FPS sensitivity level you have set.
- Decrease Cloud Quality - When enabled, will reduce cloud quality by one level if TLOD has already auto reduced to TLOD Min and FPS is still below target FPS by more than the FPS tolerance.
- Cloud Recovery TLOD with optional +
- The TLOD level required to cancel an active cloud quality reduction state and restore cloud quality back to its initial higher quality level.
- Provides a TLOD buffer to account for the increased TLOD achieved by reducing cloud quality and will minimise the chance that cloud quality will constantly change down and up.
- Ideally set to 50 TLOD or more above TLOD Min provided that the aforementioned conditions can be met.
- When + is checked, Cloud Recovery TLOD becomes relative to TLOD Min instead of absolute.
- Auto OLOD
- When enabled, four user definable parameters relating to this feature will be revealed on the UI.
- Rather than the automation being FPS based, which would cause contention with TLOD changes at the same time, OLOD will adjust based on an altitude band with a base and top level and with OLOD values defined for each of these altitudes.
- The app will set OLOD @ Base at or below the Alt OLOD Base (AGL), set the OLOD @ Top at or above Alt OLOD Top (AGL) and interpolate in between. Note that OLOD @ Base can be higher, lower or the same value as the OLOD @ Top, depending on whether you want OLOD to decrease, increase or stay the same respectively as you ascend.
<br/><br/>

View File

@ -1,4 +1,5 @@
using System;
using System.Diagnostics.Eventing.Reader;
using System.Threading;
namespace MSFS2020_AutoFPS
@ -17,8 +18,8 @@ namespace MSFS2020_AutoFPS
{
try
{
Logger.Log(LogLevel.Information, "ServiceController:Run", $"Service starting ...");
while (!Model.CancellationRequested)
Logger.Log(LogLevel.Information, "ServiceController:Run", $"Service starting ...");
while (!Model.CancellationRequested && Model.AppEnabled)
{
if (Wait())
{
@ -40,7 +41,8 @@ namespace MSFS2020_AutoFPS
}
}
}
if (!Model.AppEnabled) Logger.Log(LogLevel.Critical, "ServiceController:Run", "MSFS compatibility test failed - app disabled.");
IPCManager.CloseSafe();
}
catch (Exception ex)
@ -91,51 +93,93 @@ namespace MSFS2020_AutoFPS
{
Model.MemoryAccess = new MemoryManager(Model);
var lodController = new LODController(Model);
Logger.Log(LogLevel.Information, "ServiceController:ServiceLoop", "Starting Service Loop");
Model.DefaultTLOD = Model.MemoryAccess.GetTLOD_PC();
Model.DefaultTLOD_VR = Model.MemoryAccess.GetTLOD_VR();
Model.DefaultOLOD = Model.MemoryAccess.GetOLOD_PC();
Model.DefaultOLOD_VR = Model.MemoryAccess.GetOLOD_VR();
Logger.Log(LogLevel.Information, "ServiceController:ServiceLoop", $"Initial LODs PC {Model.DefaultTLOD} / {Model.DefaultOLOD} and VR {Model.DefaultTLOD_VR} / {Model.DefaultOLOD_VR}");
Model.DefaultCloudQ = Model.MemoryAccess.GetCloudQ_PC();
Model.DefaultCloudQ_VR = Model.MemoryAccess.GetCloudQ_VR();
Logger.Log(LogLevel.Information, "ServiceController:ServiceLoop", $"Initial cloud quality PC {Model.DefaultCloudQ} / VR {Model.DefaultCloudQ_VR}");
if (!Model.UseExpertOptions || Model.TLODMinGndLanding)
bool NormalStartup = true;
if (Model.MemoryAccess.MemoryWritesAllowed())
{
Model.MemoryAccess.SetTLOD(Model.MinTLOD);
Logger.Log(LogLevel.Information, "ServiceController:ServiceLoop", $"Setting TLOD Min on ground " + $"{Model.MinTLOD}");
}
if (Model.CustomAutoOLOD && Model.UseExpertOptions)
{
Model.MemoryAccess.SetOLOD(Model.OLODAtBase);
Logger.Log(LogLevel.Information, "ServiceController:ServiceLoop", $"Setting OLOD @ Base on ground " + $"{Model.OLODAtBase}");
}
Model.DefaultSettingsRead = true;
while (!Model.CancellationRequested && IPCManager.IsSimRunning() && IPCManager.IsCamReady())
{
try
Logger.Log(LogLevel.Information, "ServiceController:ServiceLoop", "Starting Service Loop");
if (Model.ConfigurationFile.SettingExists("defaultTLOD"))
{
lodController.RunTick();
Logger.Log(LogLevel.Information, "ServiceController:ServiceLoop", "MSFS or MSFS2020_AutoFPS did not exit properly last session. Getting default MSFS settings from MSFS2020_AutoFPS config file.");
NormalStartup = false;
}
catch (Exception ex)
else Logger.Log(LogLevel.Information, "ServiceController:ServiceLoop", "Normal startup detected. Getting default MSFS settings from MSFS.");
Model.DefaultTLOD = Convert.ToSingle(Model.ConfigurationFile.GetSetting("defaultTLOD", Model.MemoryAccess.GetTLOD_PC().ToString("F0")));
Model.DefaultTLOD_VR = Convert.ToSingle(Model.ConfigurationFile.GetSetting("defaultTLOD_VR", Model.MemoryAccess.GetTLOD_VR().ToString("F0")));
Model.DefaultOLOD = Convert.ToSingle(Model.ConfigurationFile.GetSetting("defaultOLOD", Model.MemoryAccess.GetOLOD_PC().ToString("F0")));
Model.DefaultOLOD_VR = Convert.ToSingle(Model.ConfigurationFile.GetSetting("defaultOLOD_VR", Model.MemoryAccess.GetOLOD_VR().ToString("F0")));
Logger.Log(LogLevel.Information, "ServiceController:ServiceLoop", $"Initial LODs PC {Model.DefaultTLOD} / {Model.DefaultOLOD} and VR {Model.DefaultTLOD_VR} / {Model.DefaultOLOD_VR}");
Model.DefaultCloudQ = Convert.ToInt32(Model.ConfigurationFile.GetSetting("defaultCloudQ", Model.MemoryAccess.GetCloudQ_PC().ToString("F0")));
Model.DefaultCloudQ_VR = Convert.ToInt32(Model.ConfigurationFile.GetSetting("defaultCloudQ_VR", Model.MemoryAccess.GetCloudQ_VR().ToString("F0")));
Logger.Log(LogLevel.Information, "ServiceController:ServiceLoop", $"Initial cloud quality PC {Model.CloudQualityText(Model.DefaultCloudQ)} / VR {Model.CloudQualityText(Model.DefaultCloudQ_VR)}");
if (!Model.UseExpertOptions)
{
Logger.Log(LogLevel.Critical, "ServiceController:ServiceLoop", $"Critical Exception during ServiceLoop() {ex.GetType()} {ex.Message} {ex.Source}");
if (Model.VrModeActive)
{
float MinTLOD = Math.Max(Model.DefaultTLOD_VR * 0.5f, 10.0f);
Model.MemoryAccess.SetTLOD(MinTLOD);
Logger.Log(LogLevel.Information, "ServiceController:ServiceLoop", $"Setting TLOD Min on ground " + $"{MinTLOD}");
}
else
{
float MinTLOD = Math.Max(Model.DefaultTLOD * 0.5f, 10.0f);
Model.MemoryAccess.SetTLOD(MinTLOD);
Logger.Log(LogLevel.Information, "ServiceController:ServiceLoop", $"Setting TLOD Min on ground " + $"{MinTLOD}");
}
}
Thread.Sleep(Interval);
}
Logger.Log(LogLevel.Information, "ServiceController:ServiceLoop", "ServiceLoop ended");
else
{
Model.MemoryAccess.SetTLOD(Model.MinTLOD[Model.activeProfile]);
Logger.Log(LogLevel.Information, "ServiceController:ServiceLoop", $"Setting TLOD Min on ground " + $"{Model.MinTLOD[Model.activeProfile]}");
}
if (Model.CustomAutoOLOD[Model.activeProfile] && Model.UseExpertOptions)
{
Model.MemoryAccess.SetOLOD(Model.OLODAtBase[Model.activeProfile]);
Logger.Log(LogLevel.Information, "ServiceController:ServiceLoop", $"Setting OLOD @ Base on ground " + $"{Model.OLODAtBase[Model.activeProfile]}");
}
Model.FPSSettleCounter = ServiceModel.FPSSettleSeconds * 2;
Model.MinTLODExtraActive = false;
Model.DecCloudQActive = false;
Model.DefaultSettingsRead = true;
if (!NormalStartup) Model.ResetCloudsTLOD(false);
while (!Model.CancellationRequested && IPCManager.IsSimRunning() && IPCManager.IsCamReady())
{
try
{
lodController.RunTick();
}
catch (Exception ex)
{
Logger.Log(LogLevel.Critical, "ServiceController:ServiceLoop", $"Critical Exception during ServiceLoop() {ex.GetType()} {ex.Message} {ex.Source}");
}
Thread.Sleep(Interval);
}
Logger.Log(LogLevel.Information, "ServiceController:ServiceLoop", "ServiceLoop ended");
if (true && IPCManager.IsSimRunning())
{
Logger.Log(LogLevel.Information, "ServiceController:ServiceLoop", $"Sim still running, resetting LODs to {Model.DefaultTLOD} / {Model.DefaultOLOD} and VR {Model.DefaultTLOD_VR} / {Model.DefaultOLOD_VR}");
Model.MemoryAccess.SetTLOD_PC(Model.DefaultTLOD);
Model.MemoryAccess.SetTLOD_VR(Model.DefaultTLOD_VR);
Model.MemoryAccess.SetOLOD_PC(Model.DefaultOLOD);
Model.MemoryAccess.SetOLOD_VR(Model.DefaultOLOD_VR);
Logger.Log(LogLevel.Information, "ServiceController:ServiceLoop", $"Sim still running, resetting cloud quality to {Model.DefaultCloudQ} / VR {Model.DefaultCloudQ_VR}");
Model.MemoryAccess.SetCloudQ(Model.DefaultCloudQ);
Model.MemoryAccess.SetCloudQ_VR(Model.DefaultCloudQ_VR);
if (true && IPCManager.IsSimRunning())
{
Logger.Log(LogLevel.Information, "ServiceController:ServiceLoop", $"Sim still running, resetting LODs to {Model.DefaultTLOD} / {Model.DefaultOLOD} and VR {Model.DefaultTLOD_VR} / {Model.DefaultOLOD_VR}");
Model.MemoryAccess.SetTLOD_PC(Model.DefaultTLOD);
Model.MemoryAccess.SetTLOD_VR(Model.DefaultTLOD_VR);
Model.MemoryAccess.SetOLOD_PC(Model.DefaultOLOD);
Model.MemoryAccess.SetOLOD_VR(Model.DefaultOLOD_VR);
Logger.Log(LogLevel.Information, "ServiceController:ServiceLoop", $"Sim still running, resetting cloud quality to {Model.CloudQualityText(Model.DefaultCloudQ)} / VR {Model.CloudQualityText(Model.DefaultCloudQ_VR)}");
Model.MemoryAccess.SetCloudQ(Model.DefaultCloudQ);
Model.MemoryAccess.SetCloudQ_VR(Model.DefaultCloudQ_VR);
if (Model.MemoryAccess.GetTLOD_PC() == Model.DefaultTLOD) // As long as one setting restoration stuck
{
Model.ConfigurationFile.RemoveSetting("defaultTLOD");
Model.ConfigurationFile.RemoveSetting("defaultTLOD_VR");
Model.ConfigurationFile.RemoveSetting("defaultOLOD");
Model.ConfigurationFile.RemoveSetting("defaultOLOD_VR");
Model.ConfigurationFile.RemoveSetting("defaultCloudQ");
Model.ConfigurationFile.RemoveSetting("defaultCloudQ_VR");
Logger.Log(LogLevel.Information, "ServiceController:ServiceLoop", "Default MSFS settings reset successful. Removed back up default MSFS settings from MSFS2020_AutoFPS config file.");
}
else Logger.Log(LogLevel.Information, "ServiceController:ServiceLoop", "Default MSFS settings reset failed. Retained back up default MSFS settings in MSFS2020_AutoFPS config file.");
}
}
else Model.AppEnabled = false;
Model.IsSessionRunning = false;

View File

@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Windows;
using System.Windows.Input;
namespace MSFS2020_AutoFPS
{
@ -12,6 +14,7 @@ namespace MSFS2020_AutoFPS
public int ConfigVersion { get; set; }
public bool ServiceExited { get; set; } = false;
public bool CancellationRequested { get; set; } = false;
public bool AppEnabled { get; set; } = true;
public bool IsSimRunning { get; set; } = false;
public bool IsSessionRunning { get; set; } = false;
@ -19,6 +22,8 @@ namespace MSFS2020_AutoFPS
public MemoryManager MemoryAccess { get; set; } = null;
public int VerticalTrend { get; set; }
public bool OnGround { get; set; } = true;
public int groundSpeed { get; set; } = 0;
public float tlod { get; set; } = 0;
public float olod { get; set; } = 0;
public float altAboveGnd { get; set; } = 0;
@ -27,30 +32,53 @@ namespace MSFS2020_AutoFPS
public bool VrModeActive { get; set; }
public bool ActiveWindowMSFS { get; set; }
public int FPSSettleCounter { get; set; } = FPSSettleSeconds;
public const int FPSSettleSeconds = 10;
public int FPSSettleCounter { get; set; } = FPSSettleSeconds * 2;
public bool AutoTargetFPS { get; set; } = false;
public bool UpdateTargetFPS { get; set; } = false;
public bool ForceAutoFPSCal { get; set; } = true;
public string ActiveGraphicsMode { get; set; } = "PC";
public bool ActiveGraphicsModeChanged { get; set; } = false;
public bool FgModeEnabled { get; set; }
public bool UseExpertOptions { get; set; }
public bool TestLogSimValues { get; set; }
public bool FlightTypeIFR { get; set; }
public bool LogSimValues { get; set; }
public bool IsAppPriorityFPS { get; set; } = true;
public int TargetFPS { get; set; }
public int TargetFPS_PC { get; set; }
public int TargetFPS_VR { get; set; }
public int TargetFPS_FG { get; set; }
public int FPSTolerance { get; set; }
public int CloudRecoveryTLOD { get; set; }
public bool DecCloudQActive { get; set; }
public bool PauseMSFSFocusLost { get; set; } = true;
public bool TLODMinGndLanding { get; set; }
public float MinTLOD { get; set; }
public float MaxTLOD { get; set; }
public float OLODAtBase { get; set; } = 100;
public float AltOLODBase { get; set; } = 2000;
public float OLODAtTop { get; set; } = 20;
public float AltOLODTop { get; set; } = 10000;
public float AltTLODBase { get; set; } = 1000;
public float AvgDescentRate { get; set; } = 2000;
public int TargetFPS_PC_VFR { get; set; }
public int TargetFPS_VR_VFR { get; set; }
public int TargetFPS_FG_VFR { get; set; }
public bool PauseMSFSFocusLost { get; set; }
public enum appProfiles
{
NonExpert,
IFR_Expert,
VFR_Expert
}
public int[] TLODAutoMethod { get; set; } = new int [3];
public int[] FPSTolerance { get; set; } = new int[3];
public float[] MinTLOD { get; set; } = new float[3];
public bool[] MinTLODExtra { get; set; } = new bool[3];
public float[] MaxTLOD { get; set; } = new float[3];
public float[] AltTLODBase { get; set; } = new float[3];
public float[] AltTLODTop { get; set; } = new float[3];
public float[] AvgDescentRate { get; set; } = new float [3];
public bool[] DecCloudQ { get; set; } = new bool[3];
public float[] CloudRecoveryTLOD { get; set; } = new float[3];
public bool[] CloudRecoveryPlus { get; set; } = new bool[3];
public bool[] CustomAutoOLOD { get; set; } = new bool[3];
public float[] OLODAtBase { get; set; } = new float[3];
public float[] AltOLODBase { get; set; } = new float[3];
public float[] OLODAtTop { get; set; } = new float[3];
public float[] AltOLODTop { get; set; } = new float[3];
public int activeProfile { get; set; }
public float SimMinLOD { get; set; }
public float DefaultTLOD { get; set; } = 100;
public float DefaultTLOD_VR { get; set; } = 100;
@ -59,19 +87,24 @@ namespace MSFS2020_AutoFPS
public int DefaultCloudQ { get; set; } = 2;
public int DefaultCloudQ_VR { get; set; } = 2;
public bool DefaultSettingsRead { get; set; } = false;
public int LodStepMaxInc { get; set; }
public int LodStepMaxDec { get; set; }
public bool tlod_step { get; set; } = false;
public bool olod_step { get; set; } = false;
public bool CustomAutoOLOD { get; set; } = false;
public bool DecCloudQActive { get; set; }
public bool MinTLODExtraActive { get; set; }
public float activeMinTLOD { get; set; }
public float activeMaxTLOD { get; set; }
public float activeOLODAtBase { get; set; }
public float activeOLODAtTop { get; set; }
public string LogLevel { get; set; }
public static int MfLvarsPerFrame { get; set; }
public bool WaitForConnect { get; set; }
public bool OpenWindow { get; set; }
public int windowTop { get; set; }
public int windowLeft { get; set; }
public bool RememberWindowPos { get; set; }
public bool OnTop { get; set; }
public bool DecCloudQ { get; set; }
public bool LodStepMax { get; set; }
public string SimBinary { get; set; }
public string SimModule { get; set; }
public long OffsetModuleBase { get; set; }
@ -83,17 +116,28 @@ namespace MSFS2020_AutoFPS
public long OffsetPointerCloudQVr { get; set; }
public long OffsetPointerVrMode { get; set; }
public long OffsetPointerFgMode { get; set; }
public bool SimPaused { get; set; } = false;
public bool AppPaused { get; set; } = false;
public float gpuUsage { get; set; } = -1;
public float TLODMinTriggerAlt { get; set; } = 2000;
public const int FPSSettleSeconds = 6;
public const float TLODMinLockAlt = 2000;
public const bool TestVersion = false;
protected ConfigurationFile ConfigurationFile = new();
public ConfigurationFile ConfigurationFile = new();
public ServiceModel()
{
TLODAutoMethod[(int)appProfiles.NonExpert] = 0;
FPSTolerance[(int)appProfiles.NonExpert] = 5;
MinTLODExtra[(int)appProfiles.NonExpert] = true;
AvgDescentRate[(int)appProfiles.NonExpert] = 2000;
DecCloudQ[(int)appProfiles.NonExpert] = true;
CloudRecoveryTLOD[(int)appProfiles.NonExpert] = 100;
CloudRecoveryPlus[(int)appProfiles.NonExpert] = false;
CustomAutoOLOD[(int)appProfiles.NonExpert] = false;
AltOLODBase[(int)appProfiles.NonExpert] = 2000;
AltOLODTop[(int)appProfiles.NonExpert] = 10000;
LoadConfiguration();
}
@ -104,33 +148,31 @@ namespace MSFS2020_AutoFPS
LogLevel = Convert.ToString(ConfigurationFile.GetSetting("logLevel", "Debug"));
MfLvarsPerFrame = Convert.ToInt32(ConfigurationFile.GetSetting("mfLvarPerFrame", "15"));
ConfigVersion = Convert.ToInt32(ConfigurationFile.GetSetting("ConfigVersion", "1"));
WaitForConnect = Convert.ToBoolean(ConfigurationFile.GetSetting("waitForConnect", "true"));
OpenWindow = Convert.ToBoolean(ConfigurationFile.GetSetting("openWindow", "true"));
DecCloudQ = Convert.ToBoolean(ConfigurationFile.GetSetting("DecCloudQ", "true"));
TLODMinGndLanding = Convert.ToBoolean(ConfigurationFile.GetSetting("TLODMinGndLanding", "true"));
RememberWindowPos = Convert.ToBoolean(ConfigurationFile.GetSetting("RememberWindowPos", "true"));
windowTop = Convert.ToInt32(ConfigurationFile.GetSetting("windowTop", "50"));
windowLeft = Convert.ToInt32(ConfigurationFile.GetSetting("windowLeft", "50"));
WaitForConnect = Convert.ToBoolean(ConfigurationFile.GetSetting("waitForConnect", "true"));
FlightTypeIFR = Convert.ToBoolean(ConfigurationFile.GetSetting("FlightTypeIFR", "true"));
SimBinary = Convert.ToString(ConfigurationFile.GetSetting("simBinary", "FlightSimulator"));
SimModule = Convert.ToString(ConfigurationFile.GetSetting("simModule", "WwiseLibPCx64P.dll"));
UseExpertOptions = Convert.ToBoolean(ConfigurationFile.GetSetting("useExpertOptions", "false"));
TestLogSimValues = Convert.ToBoolean(ConfigurationFile.GetSetting("testLogSimValues", "false"));
LogSimValues = Convert.ToBoolean(ConfigurationFile.GetSetting("LogSimValues", "false"));
AutoTargetFPS = Convert.ToBoolean(ConfigurationFile.GetSetting("AutoTargetFPS", "false"));
OnTop = Convert.ToBoolean(ConfigurationFile.GetSetting("OnTop", "false"));
PauseMSFSFocusLost = Convert.ToBoolean(ConfigurationFile.GetSetting("PauseMSFSFocusLost", "false"));
TargetFPS_PC = Convert.ToInt32(ConfigurationFile.GetSetting("targetFpsPC", "40"));
TargetFPS_VR = Convert.ToInt32(ConfigurationFile.GetSetting("targetFpsVR", "40"));
TargetFPS_FG = Convert.ToInt32(ConfigurationFile.GetSetting("targetFpsFG", "40"));
if (ActiveGraphicsMode == "VR") TargetFPS = TargetFPS_VR;
else if (ActiveGraphicsMode == "FG") TargetFPS = TargetFPS_FG;
else TargetFPS = TargetFPS_PC;
FPSTolerance = Convert.ToInt32(ConfigurationFile.GetSetting("FpsTolerance", "5"));
CloudRecoveryTLOD = Convert.ToInt32(ConfigurationFile.GetSetting("CloudRecoveryTLOD", "100"));
MinTLOD = Convert.ToSingle(ConfigurationFile.GetSetting("minTLod", "50"), new RealInvariantFormat(ConfigurationFile.GetSetting("minTLod", "50")));
MaxTLOD = Convert.ToSingle(ConfigurationFile.GetSetting("maxTLod", "200"), new RealInvariantFormat(ConfigurationFile.GetSetting("maxTLod", "200")));
OLODAtBase = Convert.ToSingle(ConfigurationFile.GetSetting("OLODAtBase", "100"), new RealInvariantFormat(ConfigurationFile.GetSetting("OLODAtBase", "100")));
OLODAtTop = Convert.ToSingle(ConfigurationFile.GetSetting("OLODAtTop", "20"), new RealInvariantFormat(ConfigurationFile.GetSetting("OLODAtTop", "20")));
AltOLODBase = Convert.ToSingle(ConfigurationFile.GetSetting("AltOLODBase", "2000"), new RealInvariantFormat(ConfigurationFile.GetSetting("AltOLODBase", "2000")));
AltOLODTop = Convert.ToSingle(ConfigurationFile.GetSetting("AltOLODTop", "10000"), new RealInvariantFormat(ConfigurationFile.GetSetting("AltOLODTop", "10000")));
AltTLODBase = Convert.ToSingle(ConfigurationFile.GetSetting("AltTLODBase", "1000"), new RealInvariantFormat(ConfigurationFile.GetSetting("AltTLODBase", "1000")));
AvgDescentRate = Convert.ToSingle(ConfigurationFile.GetSetting("AvgDescentRate", "2000"), new RealInvariantFormat(ConfigurationFile.GetSetting("AvgDescentRate", "2000")));
CustomAutoOLOD = Convert.ToBoolean(ConfigurationFile.GetSetting("customAutoOLOD", "false"));
TargetFPS_PC_VFR = Convert.ToInt32(ConfigurationFile.GetSetting("targetFpsPCVFR", Convert.ToString(TargetFPS_PC, CultureInfo.InvariantCulture)));
TargetFPS_VR_VFR = Convert.ToInt32(ConfigurationFile.GetSetting("targetFpsVRVFR", Convert.ToString(TargetFPS_VR, CultureInfo.InvariantCulture)));
TargetFPS_FG_VFR = Convert.ToInt32(ConfigurationFile.GetSetting("targetFpsFGVFR", Convert.ToString(TargetFPS_FG, CultureInfo.InvariantCulture)));
if (!AutoTargetFPS)
{
if (ActiveGraphicsMode == "VR") TargetFPS = FlightTypeIFR ? TargetFPS_VR : TargetFPS_VR_VFR;
else if (ActiveGraphicsMode == "FG") TargetFPS = FlightTypeIFR ? TargetFPS_FG : TargetFPS_FG_VFR;
else TargetFPS = FlightTypeIFR ? TargetFPS_PC : TargetFPS_PC_VFR;
}
OffsetModuleBase = Convert.ToInt64(ConfigurationFile.GetSetting("offsetModuleBase", "0x004B2368"), 16);
OffsetPointerMain = Convert.ToInt64(ConfigurationFile.GetSetting("offsetPointerMain", "0x3D0"), 16);
OffsetPointerTlod = Convert.ToInt64(ConfigurationFile.GetSetting("offsetPointerTlod", "0xC"), 16);
@ -141,7 +183,44 @@ namespace MSFS2020_AutoFPS
OffsetPointerVrMode = Convert.ToInt64(ConfigurationFile.GetSetting("offsetPointerVrMode", "0x1C"), 16);
OffsetPointerFgMode = Convert.ToInt64(ConfigurationFile.GetSetting("offsetPointerFgMode", "0x4A"), 16);
SimMinLOD = Convert.ToSingle(ConfigurationFile.GetSetting("simMinLod", "10"), new RealInvariantFormat(ConfigurationFile.GetSetting("simMinLod", "10")));
if (!UseExpertOptions) activeProfile = (int)appProfiles.NonExpert;
else if (FlightTypeIFR) activeProfile = (int)appProfiles.IFR_Expert;
else activeProfile = (int)appProfiles.VFR_Expert;
TLODAutoMethod[(int)appProfiles.IFR_Expert] = Convert.ToInt32(ConfigurationFile.GetSetting("TLODAutoMethod", "0"));
FPSTolerance[(int)appProfiles.IFR_Expert] = Convert.ToInt32(ConfigurationFile.GetSetting("FpsTolerance", "5"));
MinTLOD[(int)appProfiles.IFR_Expert] = Convert.ToSingle(ConfigurationFile.GetSetting("minTLod", "50"));
MinTLODExtra[(int)appProfiles.IFR_Expert] = Convert.ToBoolean(ConfigurationFile.GetSetting("MinTLODExtra", "false"));
MaxTLOD[(int)appProfiles.IFR_Expert] = Convert.ToSingle(ConfigurationFile.GetSetting("maxTLod", "200"));
AltTLODBase[(int)appProfiles.IFR_Expert] = Convert.ToSingle(ConfigurationFile.GetSetting("AltTLODBase", "1000"));
AltTLODTop[(int)appProfiles.IFR_Expert] = Convert.ToSingle(ConfigurationFile.GetSetting("AltTLODTop", "5000"));
AvgDescentRate[(int)appProfiles.IFR_Expert] = Convert.ToSingle(ConfigurationFile.GetSetting("AvgDescentRate", "2000"));
DecCloudQ[(int)appProfiles.IFR_Expert] = Convert.ToBoolean(ConfigurationFile.GetSetting("DecCloudQ", "true"));
CloudRecoveryTLOD[(int)appProfiles.IFR_Expert] = Convert.ToSingle(ConfigurationFile.GetSetting("CloudRecoveryTLOD", "100"));
CloudRecoveryPlus[(int)appProfiles.IFR_Expert] = Convert.ToBoolean(ConfigurationFile.GetSetting("CloudRecoveryPlus", "false"));
CustomAutoOLOD[(int)appProfiles.IFR_Expert] = Convert.ToBoolean(ConfigurationFile.GetSetting("customAutoOLOD", "true"));
OLODAtBase[(int)appProfiles.IFR_Expert] = Convert.ToSingle(ConfigurationFile.GetSetting("OLODAtBase", "100"));
OLODAtTop[(int)appProfiles.IFR_Expert] = Convert.ToSingle(ConfigurationFile.GetSetting("OLODAtTop", "20"));
AltOLODBase[(int)appProfiles.IFR_Expert] = Convert.ToSingle(ConfigurationFile.GetSetting("AltOLODBase", "2000"));
AltOLODTop[(int)appProfiles.IFR_Expert] = Convert.ToSingle(ConfigurationFile.GetSetting("AltOLODTop", "10000"));
TLODAutoMethod[(int)appProfiles.VFR_Expert] = Convert.ToInt32(ConfigurationFile.GetSetting("TLODAutoMethod_VFR", TLODAutoMethod[(int)appProfiles.IFR_Expert].ToString()));
FPSTolerance[(int)appProfiles.VFR_Expert] = Convert.ToInt32(ConfigurationFile.GetSetting("FpsTolerance_VFR", FPSTolerance[(int)appProfiles.IFR_Expert].ToString()));
MinTLOD[(int)appProfiles.VFR_Expert] = Convert.ToSingle(ConfigurationFile.GetSetting("minTLod_VFR", (MinTLOD[(int)appProfiles.IFR_Expert] * 2).ToString()));
MinTLODExtra[(int)appProfiles.VFR_Expert] = Convert.ToBoolean(ConfigurationFile.GetSetting("MinTLODExtra_VFR", MinTLODExtra[(int)appProfiles.IFR_Expert].ToString()));
MaxTLOD[(int)appProfiles.VFR_Expert] = Convert.ToSingle(ConfigurationFile.GetSetting("maxTLod_VFR", (Math.Round(MaxTLOD[(int)appProfiles.IFR_Expert] * 1.5)).ToString()));
AltTLODBase[(int)appProfiles.VFR_Expert] = Convert.ToSingle(ConfigurationFile.GetSetting("AltTLODBase_VFR", "100"));
AltTLODTop[(int)appProfiles.VFR_Expert] = Convert.ToSingle(ConfigurationFile.GetSetting("AltTLODTop_VFR", "3000"));
AvgDescentRate[(int)appProfiles.VFR_Expert] = Convert.ToSingle(ConfigurationFile.GetSetting("AvgDescentRate_VFR", "1000"));
DecCloudQ[(int)appProfiles.VFR_Expert] = Convert.ToBoolean(ConfigurationFile.GetSetting("DecCloudQ_VFR", DecCloudQ[(int)appProfiles.IFR_Expert].ToString()));
CloudRecoveryTLOD[(int)appProfiles.VFR_Expert] = Convert.ToSingle(ConfigurationFile.GetSetting("CloudRecoveryTLOD_VFR", CloudRecoveryTLOD[(int)appProfiles.IFR_Expert].ToString()));
CloudRecoveryPlus[(int)appProfiles.VFR_Expert] = Convert.ToBoolean(ConfigurationFile.GetSetting("CloudRecoveryPlus_VFR", CloudRecoveryPlus[(int)appProfiles.IFR_Expert].ToString()));
CustomAutoOLOD[(int)appProfiles.VFR_Expert] = Convert.ToBoolean(ConfigurationFile.GetSetting("customAutoOLOD_VFR", CustomAutoOLOD[(int)appProfiles.IFR_Expert].ToString()));
OLODAtBase[(int)appProfiles.VFR_Expert] = Convert.ToSingle(ConfigurationFile.GetSetting("OLODAtBase_VFR", (Math.Round(OLODAtBase[(int)appProfiles.IFR_Expert] * 1.5)).ToString()));
OLODAtTop[(int)appProfiles.VFR_Expert] = Convert.ToSingle(ConfigurationFile.GetSetting("OLODAtTop_VFR", OLODAtTop[(int)appProfiles.IFR_Expert].ToString()));
AltOLODBase[(int)appProfiles.VFR_Expert] = Convert.ToSingle(ConfigurationFile.GetSetting("AltOLODBase_VFR", AltOLODBase[(int)appProfiles.IFR_Expert].ToString()));
AltOLODTop[(int)appProfiles.VFR_Expert] = Convert.ToSingle(ConfigurationFile.GetSetting("AltOLODTop_VFR", AltOLODTop[(int)appProfiles.IFR_Expert].ToString()));
if (ConfigVersion < BuildConfigVersion)
{
//CHANGE SETTINGS IF NEEDED, Example:
@ -162,6 +241,32 @@ namespace MSFS2020_AutoFPS
LoadConfiguration();
}
public void ResetCloudsTLOD(bool ResetTLOD = true)
{
MinTLODExtraActive = false;
if (MemoryAccess != null && DefaultSettingsRead)
{
if (DecCloudQActive)
{
if (VrModeActive) MemoryAccess.SetCloudQ_VR(DefaultCloudQ_VR);
else MemoryAccess.SetCloudQ(DefaultCloudQ);
DecCloudQActive = false;
}
if (ResetTLOD && OnGround)
{
if (UseExpertOptions) MemoryAccess.SetTLOD(MinTLOD[activeProfile]);
else MemoryAccess.SetTLOD(Math.Max((VrModeActive ? DefaultTLOD_VR : DefaultTLOD) * (FlightTypeIFR ? 0.5f : 1.0f), 10.0f));
}
}
}
public string CloudQualityText(int CloudQuality)
{
if (CloudQuality == 0) return "Low";
else if (CloudQuality == 1) return "Medium";
else if (CloudQuality == 2) return "High";
else if (CloudQuality == 3) return "Ultra";
else return "n/a";
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 401 KiB

After

Width:  |  Height:  |  Size: 88 KiB