Decompiled source of Valheim Universal Upscaler v1.0.1

BepInEx/plugins/ValheimUpscalerRecon/ValheimUpscaler.Recon.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ValheimUpscaler.Recon")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+a10b50c79e0bc65ddd7cab69f8374d7bea781499")]
[assembly: AssemblyProduct("ValheimUpscaler.Recon")]
[assembly: AssemblyTitle("ValheimUpscaler.Recon")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ValheimUpscaler.Inject
{
	internal static class BuildConfig
	{
		public const string Mode = "RELEASE";

		public const bool IsDev = false;

		public const bool DebugOverlay = false;

		public const bool DevHotkeys = false;

		public const bool VerboseGpuLogs = false;

		public const bool InternalResolutionOverride = true;
	}
	internal static class Log
	{
		internal static ManualLogSource Source;

		[Conditional("BUILD_DEV")]
		public static void Trace(string msg)
		{
		}

		[Conditional("BUILD_DEV")]
		public static void Debug(string msg)
		{
		}

		public static void Info(string msg)
		{
			ManualLogSource source = Source;
			if (source != null)
			{
				source.LogInfo((object)msg);
			}
		}

		public static void Warning(string msg)
		{
			ManualLogSource source = Source;
			if (source != null)
			{
				source.LogWarning((object)msg);
			}
		}

		public static void Error(string msg)
		{
			ManualLogSource source = Source;
			if (source != null)
			{
				source.LogError((object)msg);
			}
		}
	}
	internal static class HotkeyRegistry
	{
		internal readonly struct Hotkey
		{
			public readonly KeyCode Key;

			public readonly string Description;

			public readonly Action Action;

			public Hotkey(KeyCode key, string description, Action action)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				Key = key;
				Description = description;
				Action = action;
			}
		}

		private static readonly List<Hotkey> _hotkeys = new List<Hotkey>();

		public static IReadOnlyList<Hotkey> All => _hotkeys;

		public static void Register(KeyCode key, string description, Action action)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < _hotkeys.Count; i++)
			{
				if (_hotkeys[i].Key == key)
				{
					Log.Warning($"Hotkey {key} registered twice; keeping the first ('{_hotkeys[i].Description}').");
					return;
				}
			}
			_hotkeys.Add(new Hotkey(key, description, action));
		}

		public static void ProcessInput()
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < _hotkeys.Count; i++)
			{
				if (Input.GetKeyDown(_hotkeys[i].Key))
				{
					try
					{
						_hotkeys[i].Action();
					}
					catch (Exception arg)
					{
						Log.Error($"Hotkey {_hotkeys[i].Key} handler failed: {arg}");
					}
				}
			}
		}
	}
	public class ReflexPacer : MonoBehaviour
	{
		private void Update()
		{
			UpscalerRunner.CurrentFrameId = (ulong)Time.frameCount;
			UpscalerNative.EmitLatencyMarker(0, UpscalerRunner.CurrentFrameId);
		}

		private void LateUpdate()
		{
			UpscalerNative.EmitLatencyMarker(1, UpscalerRunner.CurrentFrameId);
		}
	}
	public class MipmapBiasManager : MonoBehaviour
	{
		private float _lastBias;

		private float _checkTimer;

		private void Update()
		{
			_checkTimer -= Time.deltaTime;
			if (!(_checkTimer > 0f))
			{
				_checkTimer = 5f;
				float num = Patches.CurrentQualityScale();
				float num2 = ((num < 1f) ? Mathf.Log(num, 2f) : 0f);
				if (num2 != 0f || _lastBias != 0f)
				{
					_lastBias = num2;
					ApplyBias(num2);
				}
			}
		}

		private void ApplyBias(float bias)
		{
			Texture2D[] array = Resources.FindObjectsOfTypeAll<Texture2D>();
			foreach (Texture2D val in array)
			{
				if ((Object)(object)val != (Object)null && ((Texture)val).mipMapBias != bias)
				{
					((Texture)val).mipMapBias = bias;
				}
			}
		}
	}
	public enum DlssQuality
	{
		Native,
		Quality,
		Balanced,
		Performance,
		UltraPerformance,
		Custom
	}
	public enum UpscalerBackend
	{
		Auto,
		DLSS,
		FSR31,
		XeSS
	}
	[BepInPlugin("dev.daniel.valheim.upscaler.inject", "Valheim Upscaler Injection", "0.3.1")]
	[BepInProcess("valheim.exe")]
	public class InjectPlugin : BaseUnityPlugin
	{
		public const string GUID = "dev.daniel.valheim.upscaler.inject";

		public const string NAME = "Valheim Upscaler Injection";

		public const string VERSION = "0.3.1";

		public static ConfigEntry<bool> ConfigEnableDLSS;

		public static ConfigEntry<bool> ConfigEnableFG;

		public static ConfigEntry<UpscalerBackend> ConfigBackend;

		public static ConfigEntry<DlssQuality> ConfigDLSSMode;

		public static ConfigEntry<float> ConfigCustomScale;

		public static ConfigEntry<bool> ConfigCustomResolutionPreset;

		private static bool _showMenu;

		private Rect _windowRect = new Rect(20f, 20f, 300f, 250f);

		private float _fgStatusPolledAt = -999f;

		private int _fgStatusCached = -1;

		private bool _fgSubmittingCached;

		public static bool IsMenuOpen()
		{
			return _showMenu;
		}

		private void ToggleMenu()
		{
			SetMenuOpen(!_showMenu);
		}

		private void SetMenuOpen(bool open)
		{
			if (_showMenu == open)
			{
				return;
			}
			_showMenu = open;
			if (_showMenu)
			{
				Cursor.lockState = (CursorLockMode)0;
				Cursor.visible = true;
				return;
			}
			bool flag = false;
			try
			{
				Type type = AccessTools.TypeByName("Menu");
				if (type != null)
				{
					MethodInfo methodInfo = AccessTools.Method(type, "IsVisible", (Type[])null, (Type[])null);
					if (methodInfo != null)
					{
						flag |= (bool)methodInfo.Invoke(null, null);
					}
				}
				Type type2 = AccessTools.TypeByName("InventoryGui");
				if (type2 != null)
				{
					FieldInfo fieldInfo = AccessTools.Field(type2, "m_instance");
					if (fieldInfo != null)
					{
						object value = fieldInfo.GetValue(null);
						if (value != null)
						{
							MethodInfo methodInfo2 = AccessTools.Method(type2, "IsVisible", (Type[])null, (Type[])null);
							if (methodInfo2 != null)
							{
								flag |= (bool)methodInfo2.Invoke(value, null);
							}
						}
					}
				}
			}
			catch (Exception ex)
			{
				Log.Warning("Error checking other menus: " + ex.Message);
			}
			if (!flag)
			{
				Cursor.lockState = (CursorLockMode)1;
				Cursor.visible = false;
			}
		}

		private void Awake()
		{
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Expected O, but got Unknown
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Invalid comparison between Unknown and I4
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Invalid comparison between Unknown and I4
			//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dc: Expected O, but got Unknown
			//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0202: Expected O, but got Unknown
			//IL_021c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0229: Expected O, but got Unknown
			//IL_0276: Unknown result type (might be due to invalid IL or missing references)
			//IL_0284: Expected O, but got Unknown
			//IL_02da: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e8: Expected O, but got Unknown
			//IL_03b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c5: Expected O, but got Unknown
			ConfigEnableDLSS = ((BaseUnityPlugin)this).Config.Bind<bool>("Upscaler", "Enable DLSS", true, "Enable DLSS/FSR upscaling");
			ConfigEnableFG = ((BaseUnityPlugin)this).Config.Bind<bool>("Upscaler", "Enable Frame Generation", false, "Experimental DLSS-G frame generation (direct NGX, NVIDIA only). Off by default - also needs OptiScaler's [FrameGen] Enabled=true and FGInput=dlssg/nukems set (Insert overlay or OptiScaler.ini).");
			ConfigBackend = ((BaseUnityPlugin)this).Config.Bind<UpscalerBackend>("Upscaler", "Preferred Upscaler", UpscalerBackend.Auto, "Which technology OptiScaler should output. Auto picks by GPU vendor (NVIDIA=DLSS, AMD=FSR 3.1, Intel=XeSS). Written to OptiScaler.ini; a change applies on the NEXT game launch.");
			ConfigDLSSMode = ((BaseUnityPlugin)this).Config.Bind<DlssQuality>("Upscaler", "Quality Mode", DlssQuality.Quality, "Resolution preset");
			ConfigCustomScale = ((BaseUnityPlugin)this).Config.Bind<float>("Upscaler", "Custom Scale", 0.67f, new ConfigDescription("Custom render scale (0.1 - 1.0)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 1f), Array.Empty<object>()));
			ConfigCustomResolutionPreset = ((BaseUnityPlugin)this).Config.Bind<bool>("Upscaler", "Custom Resolution Preset", false, "When enabled, the mod controls the game's internal rendering resolution (legacy mode). When disabled (default), the game's own resolution settings remain authoritative.");
			Log.Source = ((BaseUnityPlugin)this).Logger;
			Log.Info(string.Format("{0} {1} awake ({2} build). graphicsDeviceType={3}", "Valheim Upscaler Injection", "0.3.1", "RELEASE", SystemInfo.graphicsDeviceType));
			RegisterHotkeys();
			BackendSelector.Apply();
			if ((int)SystemInfo.graphicsDeviceType != 18 && (int)SystemInfo.graphicsDeviceType != 21)
			{
				Log.Warning("Not running under Direct3D12 or Vulkan - launch Valheim with -force-d3d12 (or -force-vulkan), or the native upscaler can't work.");
			}
			try
			{
				Type type = AccessTools.TypeByName("FrameBufferScaler");
				if (type == null)
				{
					Log.Error("FrameBufferScaler type NOT found - wrong game/version? Aborting patches.");
					return;
				}
				MethodInfo methodInfo = AccessTools.Method(type, "OnBufferCreated", (Type[])null, (Type[])null);
				MethodInfo methodInfo2 = AccessTools.Method(type, "OnBufferDestroyed", (Type[])null, (Type[])null);
				if (methodInfo == null || methodInfo2 == null)
				{
					Log.Error($"Method lookup failed (OnBufferCreated={methodInfo != null}, OnBufferDestroyed={methodInfo2 != null}). " + "Valheim may have renamed them - re-run ReconPlugin to get current names.");
					return;
				}
				Harmony val = new Harmony("dev.daniel.valheim.upscaler.inject");
				val.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(Patches), "Created", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				val.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(Patches), "Destroyed", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				MethodInfo methodInfo3 = AccessTools.Method(AccessTools.TypeByName("UpscaledFrameBuffer"), "UpdateCurrentRenderScale", (Type[])null, (Type[])null) ?? AccessTools.Method(type, "UpdateCurrentRenderScale", (Type[])null, (Type[])null);
				if (methodInfo3 != null)
				{
					val.Patch((MethodBase)methodInfo3, new HarmonyMethod(AccessTools.Method(typeof(Patches), "UpdateCurrentRenderScalePrefix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
				else
				{
					Log.Warning("UpdateCurrentRenderScale NOT found on UpscaledFrameBuffer or FrameBufferScaler - quality modes won't drive render scale (game slider stays in control). Re-run ReconPlugin.");
				}
				Type type2 = AccessTools.TypeByName("Menu");
				if (type2 != null)
				{
					MethodInfo methodInfo4 = AccessTools.Method(type2, "IsVisible", (Type[])null, (Type[])null);
					if (methodInfo4 != null)
					{
						val.Patch((MethodBase)methodInfo4, new HarmonyMethod(AccessTools.Method(typeof(Patches), "MenuIsVisiblePrefix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					}
					else
					{
						Log.Warning("Menu.IsVisible method NOT found!");
					}
				}
				else
				{
					Log.Warning("Menu type NOT found!");
				}
				MethodInfo methodInfo5 = AccessTools.Method(typeof(Graphics), "DrawMeshInstanced", new Type[9]
				{
					typeof(Mesh),
					typeof(int),
					typeof(Material),
					typeof(Matrix4x4[]),
					typeof(int),
					typeof(MaterialPropertyBlock),
					typeof(ShadowCastingMode),
					typeof(bool),
					typeof(int)
				}, (Type[])null);
				if (methodInfo5 != null)
				{
					val.Patch((MethodBase)methodInfo5, new HarmonyMethod(AccessTools.Method(typeof(Patches), "DrawMeshInstancedPrefix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
				else
				{
					Log.Warning("Graphics.DrawMeshInstanced method NOT found!");
				}
				Log.Info("Harmony patches applied to FrameBufferScaler.OnBufferCreated/OnBufferDestroyed. Lower render scale below 100% in-world to trigger OnBufferCreated.");
			}
			catch (Exception ex)
			{
				Log.Error("Harmony patch FAILED: " + ex);
			}
			UpscalerRunner.Init();
		}

		private void RegisterHotkeys()
		{
			HotkeyRegistry.Register((KeyCode)284, "Toggle the Upscaler Menu", ToggleMenu);
			HotkeyRegistry.Register((KeyCode)288, "Toggle upscaling (A/B vs vanilla)", InjectState.ToggleDisplay);
			HotkeyRegistry.Register((KeyCode)292, "Toggle DLSS-G frame generation (experimental)", ToggleFrameGeneration);
			HotkeyRegistry.Register((KeyCode)283, "Toggle the Upscaler Menu (custom resolution preset only)", delegate
			{
				if (ConfigCustomResolutionPreset.Value)
				{
					ToggleMenu();
				}
			});
		}

		private void ToggleFrameGeneration()
		{
			if (GpuInfo.Vendor != GpuVendor.Nvidia)
			{
				Log.Info($"Frame Generation (DLSS-G) unavailable: NVIDIA-only feature (GPU vendor: {GpuInfo.Vendor}). " + "OptiScaler's own FG (OptiFG) remains available via its Insert overlay.");
				return;
			}
			ConfigEnableFG.Value = !ConfigEnableFG.Value;
			Log.Info("Frame Generation (DLSS-G) " + (ConfigEnableFG.Value ? "ON" : "OFF") + " (experimental; also needs OptiScaler's FrameGen Enabled + FGInput=dlssg/nukems)");
		}

		private void Update()
		{
			HotkeyRegistry.ProcessInput();
			if (_showMenu)
			{
				Cursor.lockState = (CursorLockMode)0;
				Cursor.visible = true;
			}
		}

		private void OnGUI()
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Expected O, but got Unknown
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			if (_showMenu)
			{
				float num = Mathf.Max(1f, (float)Screen.height / 1080f);
				GUI.matrix = Matrix4x4.Scale(new Vector3(num, num, 1f));
				_windowRect = GUILayout.Window(98765, _windowRect, new WindowFunction(DrawMenu), "Upscaler Menu", Array.Empty<GUILayoutOption>());
			}
		}

		private void DrawMenu(int windowID)
		{
			//IL_042e: Unknown result type (might be due to invalid IL or missing references)
			bool flag = GUILayout.Toggle(ConfigEnableDLSS.Value, "Enable Upscaling (F7)", Array.Empty<GUILayoutOption>());
			if (flag != ConfigEnableDLSS.Value)
			{
				ConfigEnableDLSS.Value = flag;
				InjectState.SetDisplay(InjectState.Enabled ? InjectState.Output : InjectState.LowResInput);
				Log.Info("Injection " + (InjectState.Enabled ? "ON" : "OFF (vanilla)"));
			}
			bool flag2 = GUILayout.Toggle(ConfigCustomResolutionPreset.Value, "Custom Resolution Preset (Legacy Mode)", Array.Empty<GUILayoutOption>());
			if (flag2 != ConfigCustomResolutionPreset.Value)
			{
				ConfigCustomResolutionPreset.Value = flag2;
			}
			GUILayout.Space(10f);
			GUILayout.Label($"GPU: {GpuInfo.Vendor}   Backend: {BackendSelector.SelectedBackend}", Array.Empty<GUILayoutOption>());
			GUILayout.Label("Preferred Upscaler (applies next launch):", Array.Empty<GUILayoutOption>());
			foreach (UpscalerBackend value in Enum.GetValues(typeof(UpscalerBackend)))
			{
				if (GUILayout.Toggle(ConfigBackend.Value == value, value.ToString(), Array.Empty<GUILayoutOption>()) && ConfigBackend.Value != value)
				{
					ConfigBackend.Value = value;
					BackendSelector.Apply();
				}
			}
			GUILayout.Space(10f);
			if (ConfigCustomResolutionPreset.Value)
			{
				GUILayout.Label("Quality Mode (Legacy Mode):", Array.Empty<GUILayoutOption>());
				foreach (DlssQuality value2 in Enum.GetValues(typeof(DlssQuality)))
				{
					if (GUILayout.Toggle(ConfigDLSSMode.Value == value2, value2.ToString(), Array.Empty<GUILayoutOption>()) && ConfigDLSSMode.Value != value2)
					{
						ConfigDLSSMode.Value = value2;
					}
				}
				if (ConfigDLSSMode.Value == DlssQuality.Custom)
				{
					GUILayout.Space(10f);
					GUILayout.Label($"Custom Scale: {ConfigCustomScale.Value:F2}", Array.Empty<GUILayoutOption>());
					float num = GUILayout.HorizontalSlider(ConfigCustomScale.Value, 0.1f, 1f, Array.Empty<GUILayoutOption>());
					if (Mathf.Abs(num - ConfigCustomScale.Value) > 0.01f)
					{
						ConfigCustomScale.Value = num;
					}
				}
			}
			else
			{
				GUILayout.Label("Quality Mode: (Controlled by Game Resolution/Slider)", Array.Empty<GUILayoutOption>());
			}
			GUILayout.Space(10f);
			if (GpuInfo.Vendor == GpuVendor.Nvidia)
			{
				bool flag3 = GUILayout.Toggle(ConfigEnableFG.Value, "Enable Frame Generation (F11)", Array.Empty<GUILayoutOption>());
				if (flag3 != ConfigEnableFG.Value)
				{
					ConfigEnableFG.Value = flag3;
					Log.Info("Frame Generation (DLSS-G) " + (ConfigEnableFG.Value ? "ON" : "OFF") + " (experimental; also needs OptiScaler's FrameGen Enabled + FGInput=dlssg/nukems)");
				}
				if (Time.unscaledTime - _fgStatusPolledAt > 1f)
				{
					_fgStatusPolledAt = Time.unscaledTime;
					_fgStatusCached = UpscalerNative.DlssGStatus();
					_fgSubmittingCached = UpscalerNative.IsFgWritingOutput();
				}
				string text = ((_fgStatusCached == 0) ? "NVIDIA DLSS-G (Native)" : ((_fgStatusCached == -1) ? "OptiScaler / Custom" : $"Error (Status: {_fgStatusCached})"));
				GUILayout.Label("Current FG Backend: " + text, Array.Empty<GUILayoutOption>());
				GUILayout.Label($"FG status: avail={UpscalerNative.FgAvailable} submitting={_fgSubmittingCached}", Array.Empty<GUILayoutOption>());
				GUILayout.Label("Needs OptiScaler's FrameGen Enabled + FGInput=dlssg/nukems (Insert overlay).", Array.Empty<GUILayoutOption>());
			}
			else
			{
				GUILayout.Label($"Frame Generation: unavailable on {GpuInfo.Vendor} GPUs (DLSS-G is NVIDIA-only).", Array.Empty<GUILayoutOption>());
				GUILayout.Label("OptiScaler's own FG (OptiFG) is available via its Insert overlay.", Array.Empty<GUILayoutOption>());
			}
			GUILayout.Space(10f);
			GUILayout.Label("Hotkeys:", Array.Empty<GUILayoutOption>());
			IReadOnlyList<HotkeyRegistry.Hotkey> all = HotkeyRegistry.All;
			for (int i = 0; i < all.Count; i++)
			{
				GUILayout.Label($"  {all[i].Key}: {all[i].Description}", Array.Empty<GUILayoutOption>());
			}
			GUI.DragWindow();
		}

		private void OnDestroy()
		{
			UpscalerRunner.Dispose();
		}
	}
	internal static class Diag
	{
		[Conditional("BUILD_DEV")]
		public static void Eval(string status)
		{
		}

		[Conditional("FEATURE_ENABLE_DEBUG_OVERLAY")]
		public static void Heartbeat()
		{
		}

		[Conditional("FEATURE_ENABLE_DEBUG_OVERLAY")]
		public static void DumpStatus()
		{
		}
	}
	internal static class BackendSelector
	{
		public static bool OptiScalerPresent { get; private set; }

		public static string SelectedBackend { get; private set; } = "(not applied)";

		private static string GameDir => Path.GetDirectoryName(Application.dataPath);

		public static void Apply()
		{
			GpuInfo.Detect();
			string gameDir = GameDir;
			OptiScalerPresent = File.Exists(Path.Combine(gameDir, "OptiScaler.ini")) || File.Exists(Path.Combine(gameDir, "OptiScaler.dll")) || File.Exists(Path.Combine(gameDir, "dxgi.dll")) || File.Exists(Path.Combine(gameDir, "winmm.dll")) || File.Exists(Path.Combine(gameDir, "version.dll")) || File.Exists(Path.Combine(gameDir, "nvngx.dll"));
			UpscalerBackend upscalerBackend = InjectPlugin.ConfigBackend?.Value ?? UpscalerBackend.Auto;
			UpscalerBackend upscalerBackend2 = upscalerBackend;
			if (upscalerBackend == UpscalerBackend.Auto)
			{
				upscalerBackend2 = GpuInfo.Vendor switch
				{
					GpuVendor.Nvidia => UpscalerBackend.DLSS, 
					GpuVendor.Amd => UpscalerBackend.FSR31, 
					GpuVendor.Intel => UpscalerBackend.XeSS, 
					_ => (!OptiScalerPresent) ? UpscalerBackend.DLSS : UpscalerBackend.FSR31, 
				};
			}
			Log.Info($"GPU: {GpuInfo.Vendor} (PCI 0x{GpuInfo.VendorId:X4}) '{SystemInfo.graphicsDeviceName}'. " + string.Format("Backend: {0}{1}; ", upscalerBackend2, (upscalerBackend == UpscalerBackend.Auto) ? " (auto)" : " (user override)") + "OptiScaler " + (OptiScalerPresent ? "present" : "absent") + ".");
			if (GpuInfo.Vendor != GpuVendor.Nvidia && !OptiScalerPresent)
			{
				SelectedBackend = "none (bilinear)";
				UpscalerRunner.NativeEvalEnabled = false;
				Log.Warning("Non-NVIDIA GPU without OptiScaler: no upscaler backend can run. Install OptiScaler (as dxgi.dll next to valheim.exe) to get " + ((GpuInfo.Vendor == GpuVendor.Intel) ? "XeSS" : "FSR") + "; staying on bilinear scaling.");
			}
			else if (!OptiScalerPresent)
			{
				SelectedBackend = "DLSS (driver NGX)";
			}
			else
			{
				string value = (SelectedBackend = upscalerBackend2 switch
				{
					UpscalerBackend.XeSS => "xess", 
					UpscalerBackend.DLSS => "dlss", 
					_ => "fsr31", 
				});
				TryWriteOptiScalerIni(gameDir, value);
			}
		}

		private static void TryWriteOptiScalerIni(string dir, string value)
		{
			try
			{
				string path = Path.Combine(dir, "OptiScaler.ini");
				object obj = (File.Exists(path) ? ((object)File.ReadAllLines(path)) : ((object)new string[0]));
				List<string> list = new List<string>(((Array)obj).Length + 2);
				bool flag = false;
				bool flag2 = false;
				bool flag3 = false;
				int num = -1;
				string[] array = (string[])obj;
				for (int i = 0; i < array.Length; i++)
				{
					string item;
					string text = (item = array[i]).Trim();
					if (text.StartsWith("["))
					{
						flag = text.Equals("[Upscalers]", StringComparison.OrdinalIgnoreCase);
						if (flag && num < 0)
						{
							num = list.Count;
						}
					}
					else if (flag && !flag2)
					{
						int num2 = text.IndexOf('=');
						if (num2 > 0 && text.Substring(0, num2).Trim().Equals("Dx12Upscaler", StringComparison.OrdinalIgnoreCase))
						{
							flag2 = true;
							if (!text.Substring(num2 + 1).Trim().Equals(value, StringComparison.OrdinalIgnoreCase))
							{
								flag3 = true;
								item = "Dx12Upscaler=" + value;
							}
						}
					}
					list.Add(item);
				}
				if (!flag2)
				{
					flag3 = true;
					if (num >= 0)
					{
						list.Insert(num + 1, "Dx12Upscaler=" + value);
					}
					else
					{
						list.Add("[Upscalers]");
						list.Add("Dx12Upscaler=" + value);
					}
				}
				if (flag3)
				{
					File.WriteAllLines(path, list.ToArray());
					Log.Info("OptiScaler.ini updated: Dx12Upscaler=" + value + " (applies on the next launch).");
				}
			}
			catch (Exception ex)
			{
				Log.Warning("Could not update OptiScaler.ini (set Dx12Upscaler manually or via the Insert overlay): " + ex.Message);
			}
		}
	}
	internal static class Patches
	{
		private static IResolutionManager _lastManager;

		private static bool _isDrawingMask;

		public static void Created(object __instance, RenderTexture texture)
		{
			try
			{
				InjectState.OnBufferCreated(__instance, texture);
			}
			catch (Exception ex)
			{
				Log.Error("OnBufferCreated hook error: " + ex);
			}
		}

		public static void Destroyed()
		{
			try
			{
				InjectState.OnBufferDestroyed();
			}
			catch (Exception ex)
			{
				Log.Error("OnBufferDestroyed hook error: " + ex);
			}
		}

		internal static float CurrentQualityScale()
		{
			return ResolutionManagerProvider.Current.GetQualityScale();
		}

		public static bool UpdateCurrentRenderScalePrefix(object __instance)
		{
			IResolutionManager current = ResolutionManagerProvider.Current;
			if (_lastManager != null && _lastManager != current && _lastManager is F2ControlledResolutionManager f2ControlledResolutionManager)
			{
				f2ControlledResolutionManager.Restore(__instance);
			}
			_lastManager = current;
			return current.UpdateRenderScale(__instance);
		}

		public static bool MenuIsVisiblePrefix(ref bool __result)
		{
			if (InjectPlugin.IsMenuOpen())
			{
				__result = true;
				return false;
			}
			return true;
		}

		public static void DrawMeshInstancedPrefix(Mesh mesh, int submeshIndex, Material material, Matrix4x4[] matrices, int count, MaterialPropertyBlock properties, ShadowCastingMode castShadows, bool receiveShadows, int layer)
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			if (_isDrawingMask || !InjectState.Enabled || !UpscalerRunner.NativeEvalEnabled || !UpscalerNative.Available || !((Object)(object)InjectState.MaskCamera != (Object)null) || !((Object)(object)InjectState.MaskMaterial != (Object)null) || layer != InjectState.GrassLayer || InjectState.GrassLayer < 0)
			{
				return;
			}
			_isDrawingMask = true;
			try
			{
				Graphics.DrawMeshInstanced(mesh, submeshIndex, InjectState.MaskMaterial, matrices, count, properties, castShadows, receiveShadows, layer, InjectState.MaskCamera);
			}
			catch (Exception)
			{
			}
			finally
			{
				_isDrawingMask = false;
			}
		}
	}
	internal static class InjectState
	{
		public static bool FlipOutput = false;

		internal static bool ForceResetSubmit = false;

		internal static RenderTexture LowResInput;

		internal static RenderTexture Output;

		public static RenderTexture ReactiveMask;

		public static RenderTexture FgInterpOutput;

		public static Camera MaskCamera;

		public static Material MaskMaterial;

		public static int GrassLayer = -1;

		private static bool _disabledAA;

		private static object _aaModel;

		private static bool _originalAAEnabled;

		private static object _rawImage;

		private static PropertyInfo _texProp;

		private static PropertyInfo _uvRectProp;

		private static bool _flipped;

		private static UpscaleDispatch _dispatch;

		private static bool _warnedNoMaskShader;

		public static bool Enabled => InjectPlugin.ConfigEnableDLSS?.Value ?? true;

		public static void OnBufferCreated(object frameBufferScaler, RenderTexture lowRes)
		{
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			LowResInput = lowRes;
			ForceResetSubmit = true;
			UpscalerRunner.InvalidateTextureCache();
			_rawImage = AccessTools.Field(frameBufferScaler.GetType(), "m_rawImage")?.GetValue(frameBufferScaler);
			if (_rawImage == null)
			{
				Log.Warning("m_rawImage is NULL on FrameBufferScaler; cannot redirect display.");
				return;
			}
			if (_texProp == null)
			{
				_texProp = AccessTools.Property(_rawImage.GetType(), "texture");
			}
			if (_texProp == null)
			{
				Log.Warning("RawImage.texture property not found; cannot redirect display.");
				return;
			}
			if (_uvRectProp == null)
			{
				_uvRectProp = AccessTools.Property(_rawImage.GetType(), "uvRect");
			}
			EnsureOutput();
			EnsureReactiveMask(((Texture)lowRes).width, ((Texture)lowRes).height);
			EnsureMaskMaterial();
			GrassLayer = LayerMask.NameToLayer("InstanceRenderer");
			AttachDispatch(lowRes);
			SetDisplay(Enabled ? Output : LowResInput);
			DisableConflictingAA(ResolveMainCamera(lowRes));
			Log.Info($"OnBufferCreated: lowRes {((Texture)lowRes).width}x{((Texture)lowRes).height} {lowRes.format} -> output {((Texture)Output).width}x{((Texture)Output).height}; " + $"RawImage redirected (enabled={Enabled}). Dispatch attached={(Object)(object)_dispatch != (Object)null}.");
		}

		public static void OnBufferDestroyed()
		{
			RestoreConflictingAA();
			SetOutputFlip(flip: false);
			if ((Object)(object)LowResInput != (Object)null)
			{
				SetDisplay(LowResInput);
			}
			UpscalerRunner.InvalidateTextureCache();
			ReleaseOutput();
			ReleaseReactiveMask();
			ReleaseFgInterpOutput();
			if ((Object)(object)MaskCamera != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)MaskCamera).gameObject);
				MaskCamera = null;
			}
			if ((Object)(object)_dispatch != (Object)null)
			{
				Object.DestroyImmediate((Object)(object)_dispatch);
				_dispatch = null;
			}
			LowResInput = null;
			Log.Info("OnBufferDestroyed: restored vanilla RawImage texture, released output and reactive mask resources.");
		}

		private static void EnsureOutput()
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Expected O, but got Unknown
			int width = Screen.width;
			int height = Screen.height;
			if (!((Object)(object)Output != (Object)null) || ((Texture)Output).width != width || ((Texture)Output).height != height)
			{
				ReleaseOutput();
				Output = new RenderTexture(width, height, 0, (RenderTextureFormat)2, (RenderTextureReadWrite)1)
				{
					name = "Upscaler_Output",
					filterMode = (FilterMode)1,
					enableRandomWrite = true
				};
				if (!Output.Create())
				{
					Log.Error("Output RenderTexture.Create() FAILED (UAV ARGBHalf unsupported?).");
				}
				Graphics.Blit((Texture)(object)Texture2D.blackTexture, Output);
				UpscalerRunner.InvalidateTextureCache();
			}
		}

		public static void EnsureFgInterpOutput()
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Expected O, but got Unknown
			int width = Screen.width;
			int height = Screen.height;
			if (!((Object)(object)FgInterpOutput != (Object)null) || ((Texture)FgInterpOutput).width != width || ((Texture)FgInterpOutput).height != height)
			{
				ReleaseFgInterpOutput();
				FgInterpOutput = new RenderTexture(width, height, 0, (RenderTextureFormat)2, (RenderTextureReadWrite)1)
				{
					name = "Upscaler_FgInterpOutput",
					enableRandomWrite = true
				};
				if (!FgInterpOutput.Create())
				{
					Log.Error("FgInterpOutput RenderTexture.Create() FAILED (UAV ARGBHalf unsupported?).");
				}
				Graphics.Blit((Texture)(object)Texture2D.blackTexture, FgInterpOutput);
				UpscalerRunner.InvalidateTextureCache();
			}
		}

		private static void ReleaseFgInterpOutput()
		{
			if (!((Object)(object)FgInterpOutput == (Object)null))
			{
				FgInterpOutput.Release();
				Object.Destroy((Object)(object)FgInterpOutput);
				FgInterpOutput = null;
			}
		}

		public static void EnsureReactiveMask(int w, int h)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			if (!((Object)(object)ReactiveMask != (Object)null) || ((Texture)ReactiveMask).width != w || ((Texture)ReactiveMask).height != h)
			{
				ReleaseReactiveMask();
				ReactiveMask = new RenderTexture(w, h, 0, (RenderTextureFormat)0, (RenderTextureReadWrite)1)
				{
					name = "Upscaler_ReactiveMask",
					filterMode = (FilterMode)1
				};
				if (!ReactiveMask.Create())
				{
					Log.Error("ReactiveMask RenderTexture.Create() FAILED.");
				}
				UpscalerRunner.InvalidateTextureCache();
			}
		}

		private static void ReleaseReactiveMask()
		{
			if (!((Object)(object)ReactiveMask == (Object)null))
			{
				ReactiveMask.Release();
				Object.Destroy((Object)(object)ReactiveMask);
				ReactiveMask = null;
			}
		}

		private static void EnsureMaskMaterial()
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Expected O, but got Unknown
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)MaskMaterial != (Object)null)
			{
				return;
			}
			string[] array = new string[4] { "Unlit/Color", "Sprites/Default", "UI/Default", "Hidden/Internal-Colored" };
			for (int i = 0; i < array.Length; i++)
			{
				Shader val = Shader.Find(array[i]);
				if (!((Object)(object)val == (Object)null))
				{
					MaskMaterial = new Material(val);
					MaskMaterial.enableInstancing = true;
					MaskMaterial.color = Color.white;
					return;
				}
			}
			if (!_warnedNoMaskShader)
			{
				_warnedNoMaskShader = true;
				Log.Warning("No usable mask shader in this build; reactive mask disabled (upscaling unaffected).");
			}
		}

		internal static void DisableConflictingAA(Camera cam)
		{
			if (_disabledAA || (Object)(object)cam == (Object)null)
			{
				return;
			}
			try
			{
				Type type = AccessTools.TypeByName("PostProcessingBehaviour");
				if (type == null)
				{
					return;
				}
				Component component = ((Component)cam).GetComponent(type);
				if ((Object)(object)component == (Object)null)
				{
					return;
				}
				object obj = AccessTools.Field(type, "profile")?.GetValue(component);
				if (obj == null)
				{
					return;
				}
				_aaModel = AccessTools.Field(obj.GetType(), "antialiasing")?.GetValue(obj);
				if (_aaModel == null)
				{
					return;
				}
				FieldInfo fieldInfo = AccessTools.Field(_aaModel.GetType(), "m_Enabled") ?? AccessTools.Field(_aaModel.GetType(), "enabled");
				if (fieldInfo != null && fieldInfo.FieldType == typeof(bool))
				{
					_originalAAEnabled = (bool)fieldInfo.GetValue(_aaModel);
					if (_originalAAEnabled)
					{
						fieldInfo.SetValue(_aaModel, false);
						_disabledAA = true;
						Log.Info("Disabled PPv1 antialiasing (TAA/FXAA). DLSS provides temporal AA — double accumulation and pre-blurring eliminated.");
					}
				}
			}
			catch (Exception ex)
			{
				Log.Warning("Failed to disable conflicting AA: " + ex.Message);
			}
		}

		internal static void RestoreConflictingAA()
		{
			if (!_disabledAA || _aaModel == null)
			{
				return;
			}
			try
			{
				(AccessTools.Field(_aaModel.GetType(), "m_Enabled") ?? AccessTools.Field(_aaModel.GetType(), "enabled"))?.SetValue(_aaModel, _originalAAEnabled);
				_disabledAA = false;
				Log.Info("Restored PPv1 antialiasing (was " + (_originalAAEnabled ? "enabled" : "disabled") + ").");
			}
			catch (Exception ex)
			{
				Log.Warning("Failed to restore AA: " + ex.Message);
			}
		}

		private static void ReleaseOutput()
		{
			if (!((Object)(object)Output == (Object)null))
			{
				Output.Release();
				Object.Destroy((Object)(object)Output);
				Output = null;
			}
		}

		private static void AttachDispatch(RenderTexture lowRes)
		{
			Camera val = ResolveMainCamera(lowRes);
			if ((Object)(object)val == (Object)null)
			{
				Log.Warning("Main camera NOT found (no camera targets the low-res RT, GameCamera null). Dispatch not attached.");
				return;
			}
			UpscaleDispatch component = ((Component)val).GetComponent<UpscaleDispatch>();
			if ((Object)(object)component != (Object)null)
			{
				Object.DestroyImmediate((Object)(object)component);
			}
			_dispatch = ((Component)val).gameObject.AddComponent<UpscaleDispatch>();
			if ((Object)(object)((Component)val).GetComponent<ReflexPacer>() == (Object)null)
			{
				((Component)val).gameObject.AddComponent<ReflexPacer>();
			}
			if ((Object)(object)((Component)val).GetComponent<MipmapBiasManager>() == (Object)null)
			{
				((Component)val).gameObject.AddComponent<MipmapBiasManager>();
			}
			Type type = AccessTools.TypeByName("PostProcessingBehaviour");
			if (type != null)
			{
				Component component2 = ((Component)val).GetComponent(type);
				_ = (Object)(object)((component2 is MonoBehaviour) ? component2 : null) != (Object)null;
			}
			EnsureMaskCamera(val);
		}

		private static void EnsureMaskCamera(Camera mainCam)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)MaskCamera != (Object)null))
			{
				GameObject val = new GameObject("Upscaler_MaskCamera");
				val.transform.SetParent(((Component)mainCam).transform, false);
				MaskCamera = val.AddComponent<Camera>();
				((Behaviour)MaskCamera).enabled = false;
				if ((Object)(object)ReactiveMask != (Object)null && (Object)(object)LowResInput != (Object)null)
				{
					MaskCamera.SetTargetBuffers(ReactiveMask.colorBuffer, LowResInput.depthBuffer);
				}
				else
				{
					MaskCamera.targetTexture = ReactiveMask;
				}
				MaskCamera.cullingMask = ((GrassLayer >= 0) ? (1 << GrassLayer) : 0);
			}
		}

		private static Camera ResolveMainCamera(RenderTexture lowRes)
		{
			Camera[] allCameras = Camera.allCameras;
			foreach (Camera val in allCameras)
			{
				if ((Object)(object)val != (Object)null && (Object)(object)val.targetTexture == (Object)(object)lowRes)
				{
					return val;
				}
			}
			Type type = AccessTools.TypeByName("GameCamera");
			if (type != null)
			{
				object obj = AccessTools.Field(type, "m_instance")?.GetValue(null) ?? AccessTools.Property(type, "instance")?.GetValue(null);
				if (obj != null)
				{
					object? obj2 = AccessTools.Field(type, "m_camera")?.GetValue(obj);
					Camera val2 = (Camera)((obj2 is Camera) ? obj2 : null);
					if (val2 != null)
					{
						return val2;
					}
				}
			}
			return Camera.main;
		}

		internal static void SetDisplay(RenderTexture tex)
		{
			if (_rawImage != null && _texProp != null)
			{
				_texProp.SetValue(_rawImage, tex, null);
			}
		}

		internal static void SetOutputFlip(bool flip)
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			if (_rawImage != null && !(_uvRectProp == null) && flip != _flipped)
			{
				_flipped = flip;
				_uvRectProp.SetValue(_rawImage, (object)(flip ? new Rect(0f, 1f, 1f, -1f) : new Rect(0f, 0f, 1f, 1f)), null);
			}
		}

		internal static void ToggleDisplay()
		{
			if (InjectPlugin.ConfigEnableDLSS != null)
			{
				InjectPlugin.ConfigEnableDLSS.Value = !InjectPlugin.ConfigEnableDLSS.Value;
			}
			SetDisplay(Enabled ? Output : LowResInput);
			Log.Info("Injection " + (Enabled ? "ON" : "OFF (vanilla)"));
		}
	}
	public class UpscaleDispatch : MonoBehaviour
	{
		private Camera _cam;

		private Matrix4x4 _baseProj;

		private bool _jittered;

		private bool _loggedOnce;

		private bool _depthModeForced;

		private DepthTextureMode _depthModeBeforeForce;

		private void Awake()
		{
			_cam = ((Component)this).GetComponent<Camera>();
		}

		private void OnPreCull()
		{
			_jittered = false;
			bool upscalerDepthMode = InjectState.Enabled && (Object)(object)InjectState.LowResInput != (Object)null && UpscalerRunner.ForceMotionVectors;
			SetUpscalerDepthMode(upscalerDepthMode);
			if (InjectState.Enabled && !((Object)(object)InjectState.LowResInput == (Object)null))
			{
				UpscalerRunner.ApplyJitter(_cam, ((Texture)InjectState.LowResInput).width, ((Texture)InjectState.LowResInput).height, ref _baseProj, ref _jittered);
			}
		}

		private void OnPostRender()
		{
			if (_jittered)
			{
				_cam.ResetProjectionMatrix();
				_jittered = false;
			}
		}

		private void OnDisable()
		{
			SetUpscalerDepthMode(enabled: false);
			if (_jittered && (Object)(object)_cam != (Object)null)
			{
				_cam.ResetProjectionMatrix();
				_jittered = false;
			}
		}

		private void SetUpscalerDepthMode(bool enabled)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_cam == (Object)null)
			{
				return;
			}
			if (enabled)
			{
				if (!_depthModeForced)
				{
					_depthModeBeforeForce = _cam.depthTextureMode;
					_depthModeForced = true;
				}
				_cam.depthTextureMode = (DepthTextureMode)(_depthModeBeforeForce | 5);
			}
			else if (_depthModeForced)
			{
				DepthTextureMode val = _cam.depthTextureMode;
				if ((_depthModeBeforeForce & 1) == 0)
				{
					val = (DepthTextureMode)(val & -2);
				}
				if ((_depthModeBeforeForce & 4) == 0)
				{
					val = (DepthTextureMode)(val & -5);
				}
				_cam.depthTextureMode = (DepthTextureMode)(val | (_depthModeBeforeForce & 5));
				_depthModeForced = false;
			}
		}

		private void OnRenderImage(RenderTexture src, RenderTexture dst)
		{
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			if (!_loggedOnce)
			{
				_loggedOnce = true;
				Shader.GetGlobalTexture("_CameraDepthTexture");
				Shader.GetGlobalTexture("_CameraMotionVectorsTexture");
			}
			if (!InjectState.Enabled)
			{
				InjectState.SetOutputFlip(flip: false);
				Graphics.Blit((Texture)(object)src, dst);
				return;
			}
			bool flag = !InjectState.ForceResetSubmit && UpscalerRunner.NativeEvalEnabled && UpscalerNative.IsWritingOutput();
			bool num = UpscalerRunner.NativeEvalEnabled && UpscalerNative.Available;
			if ((Object)(object)InjectState.Output != (Object)null && !flag)
			{
				Graphics.Blit((Texture)(object)src, InjectState.Output);
			}
			if (UpscalerRunner.FgEnabled && (Object)(object)InjectState.FgInterpOutput == (Object)null)
			{
				InjectState.EnsureFgInterpOutput();
			}
			if (num && (Object)(object)InjectState.MaskCamera != (Object)null && (Object)(object)InjectState.ReactiveMask != (Object)null && (Object)(object)InjectState.LowResInput != (Object)null && InjectState.GrassLayer >= 0 && (Object)(object)InjectState.MaskMaterial != (Object)null)
			{
				Camera maskCamera = InjectState.MaskCamera;
				maskCamera.CopyFrom(_cam);
				maskCamera.depthTextureMode = (DepthTextureMode)0;
				maskCamera.renderingPath = (RenderingPath)1;
				maskCamera.allowHDR = false;
				maskCamera.allowMSAA = false;
				maskCamera.useOcclusionCulling = false;
				maskCamera.cullingMask = 1 << InjectState.GrassLayer;
				maskCamera.SetTargetBuffers(InjectState.ReactiveMask.colorBuffer, InjectState.LowResInput.depthBuffer);
				maskCamera.clearFlags = (CameraClearFlags)4;
				RenderTexture active = RenderTexture.active;
				Graphics.SetRenderTarget(InjectState.ReactiveMask.colorBuffer, InjectState.LowResInput.depthBuffer);
				GL.Clear(false, true, Color.clear);
				RenderTexture.active = active;
				maskCamera.RenderWithShader(InjectState.MaskMaterial.shader, "");
			}
			Matrix4x4 nonJitteredProj = (UpscalerRunner.JitterEnabled ? _cam.nonJitteredProjectionMatrix : _cam.projectionMatrix);
			bool num2 = UpscalerRunner.TryEvaluate(src, InjectState.Output, _cam, nonJitteredProj);
			InjectState.ForceResetSubmit = false;
			if (!num2 && flag && (Object)(object)InjectState.Output != (Object)null)
			{
				Graphics.Blit((Texture)(object)src, InjectState.Output);
			}
			flag = num2 && UpscalerRunner.NativeEvalEnabled && UpscalerNative.IsWritingOutput();
			InjectState.SetOutputFlip(InjectState.FlipOutput && flag);
			Graphics.Blit((Texture)(object)src, dst);
		}
	}
	public interface IResolutionManager
	{
		bool UpdateRenderScale(object upscaledFrameBuffer);

		float GetQualityScale();
	}
	public class GameControlledResolutionManager : IResolutionManager
	{
		public bool UpdateRenderScale(object upscaledFrameBuffer)
		{
			return true;
		}

		public float GetQualityScale()
		{
			if (!InjectState.Enabled)
			{
				return 1f;
			}
			if ((Object)(object)InjectState.LowResInput != (Object)null && (Object)(object)InjectState.Output != (Object)null && ((Texture)InjectState.Output).width > 0)
			{
				return (float)((Texture)InjectState.LowResInput).width / (float)((Texture)InjectState.Output).width;
			}
			return 1f;
		}
	}
	public class F2ControlledResolutionManager : IResolutionManager
	{
		private FieldInfo _ufbTargetVertical;

		private FieldInfo _ufbAutoTarget;

		private bool _ufbLookupFailed;

		private bool _overrideActive;

		private uint _savedTargetVertical;

		private bool _savedAutoTarget;

		public bool UpdateRenderScale(object upscaledFrameBuffer)
		{
			try
			{
				if (_ufbLookupFailed)
				{
					return true;
				}
				if (_ufbTargetVertical == null || _ufbAutoTarget == null)
				{
					Type type = upscaledFrameBuffer.GetType();
					_ufbTargetVertical = AccessTools.Field(type, "m_targetResolutionVertical");
					_ufbAutoTarget = AccessTools.Field(type, "m_autoTargetResolution");
					if (_ufbTargetVertical == null || _ufbAutoTarget == null)
					{
						_ufbLookupFailed = true;
						Log.Error("UpscaledFrameBuffer fields renamed (re-run ReconPlugin); leaving vanilla render-scale logic active.");
						return true;
					}
				}
				if (InjectState.Enabled)
				{
					if (!_overrideActive)
					{
						_overrideActive = true;
						_savedTargetVertical = (uint)_ufbTargetVertical.GetValue(null);
						_savedAutoTarget = (bool)_ufbAutoTarget.GetValue(null);
					}
					uint num = (uint)Mathf.Clamp(Mathf.RoundToInt((float)Screen.height * GetQualityScale()), 8, Screen.height - 1);
					_ufbAutoTarget.SetValue(null, false);
					_ufbTargetVertical.SetValue(null, num);
					return false;
				}
				Restore(upscaledFrameBuffer);
			}
			catch (Exception ex)
			{
				Log.Error("Error overriding UpscaledFrameBuffer resolution: " + ex);
			}
			return true;
		}

		public void Restore(object upscaledFrameBuffer)
		{
			if (_overrideActive)
			{
				_overrideActive = false;
				if (_ufbAutoTarget != null)
				{
					_ufbAutoTarget.SetValue(null, _savedAutoTarget);
				}
				if (_ufbTargetVertical != null)
				{
					_ufbTargetVertical.SetValue(null, _savedTargetVertical);
				}
			}
		}

		public float GetQualityScale()
		{
			if (!InjectState.Enabled)
			{
				return 1f;
			}
			return InjectPlugin.ConfigDLSSMode.Value switch
			{
				DlssQuality.Native => 1f, 
				DlssQuality.Quality => 0.67f, 
				DlssQuality.Balanced => 0.58f, 
				DlssQuality.Performance => 0.5f, 
				DlssQuality.UltraPerformance => 0.33f, 
				DlssQuality.Custom => InjectPlugin.ConfigCustomScale.Value, 
				_ => 1f, 
			};
		}
	}
	public static class ResolutionManagerProvider
	{
		private static readonly IResolutionManager _gameControlled = new GameControlledResolutionManager();

		private static readonly IResolutionManager _f2Controlled = new F2ControlledResolutionManager();

		public static IResolutionManager Current
		{
			get
			{
				if (InjectPlugin.ConfigCustomResolutionPreset != null && InjectPlugin.ConfigCustomResolutionPreset.Value)
				{
					return _f2Controlled;
				}
				return _gameControlled;
			}
		}

		public static IResolutionManager F2Controlled => _f2Controlled;
	}
	public enum GpuVendor
	{
		Unknown,
		Nvidia,
		Amd,
		Intel
	}
	internal static class GpuInfo
	{
		private static bool _detected;

		public static GpuVendor Vendor { get; private set; }

		public static uint VendorId { get; private set; }

		public static void Detect()
		{
			if (_detected)
			{
				return;
			}
			_detected = true;
			VendorId = (uint)SystemInfo.graphicsDeviceVendorID;
			switch (VendorId)
			{
			case 4318u:
				Vendor = GpuVendor.Nvidia;
				return;
			case 4098u:
			case 4130u:
				Vendor = GpuVendor.Amd;
				return;
			case 32902u:
			case 32903u:
				Vendor = GpuVendor.Intel;
				return;
			}
			string text = (SystemInfo.graphicsDeviceVendor + " " + SystemInfo.graphicsDeviceName).ToLowerInvariant();
			if (text.Contains("nvidia") || text.Contains("geforce"))
			{
				Vendor = GpuVendor.Nvidia;
			}
			else if (text.Contains("amd") || text.Contains("radeon") || text.Contains("ati "))
			{
				Vendor = GpuVendor.Amd;
			}
			else if (text.Contains("intel") || text.Contains("arc"))
			{
				Vendor = GpuVendor.Intel;
			}
			else
			{
				Vendor = GpuVendor.Unknown;
			}
		}
	}
	internal struct UpscalerFrameParams
	{
		public IntPtr Color;

		public IntPtr Depth;

		public IntPtr MotionVectors;

		public IntPtr Output;

		public IntPtr ReactiveMask;

		public int RenderWidth;

		public int RenderHeight;

		public int DisplayWidth;

		public int DisplayHeight;

		public float JitterX;

		public float JitterY;

		public float MvScaleX;

		public float MvScaleY;

		public int Reset;

		public int Flags;

		public float FrameDeltaMs;

		public IntPtr FgOutputInterp;

		public int FgEnabled;

		public int FgNotRenderingGameFrames;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
		public float[] CamViewToClip;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
		public float[] ClipToPrevClip;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
		public float[] PrevClipToClip;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
		public float[] CamPos;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
		public float[] CamUp;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
		public float[] CamRight;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
		public float[] CamFwd;

		public float CamNear;

		public float CamFar;

		public float CamFovRad;

		public float CamAspect;

		public int CamOrtho;
	}
	internal static class UpscalerNative
	{
		private const string DLL = "ValheimUpscalerNative";

		private static IntPtr _eventFunc;

		public static bool Available { get; private set; }

		public static IntPtr EventFunc => _eventFunc;

		public static bool FgAvailable { get; private set; }

		[DllImport("ValheimUpscalerNative", CallingConvention = CallingConvention.Cdecl)]
		private static extern int VUP_Init();

		[DllImport("ValheimUpscalerNative", CallingConvention = CallingConvention.Cdecl)]
		private static extern IntPtr VUP_GetRenderEventFunc();

		[DllImport("ValheimUpscalerNative", CallingConvention = CallingConvention.Cdecl)]
		private static extern int VUP_IsAvailable();

		[DllImport("ValheimUpscalerNative", CallingConvention = CallingConvention.Cdecl)]
		private static extern int VUP_IsSubmitting();

		[DllImport("ValheimUpscalerNative", CallingConvention = CallingConvention.Cdecl)]
		private static extern void VUP_Shutdown();

		[DllImport("ValheimUpscalerNative", CallingConvention = CallingConvention.Cdecl)]
		private static extern int VUP_IsFgAvailable();

		[DllImport("ValheimUpscalerNative", CallingConvention = CallingConvention.Cdecl)]
		private static extern int VUP_IsFgSubmitting();

		[DllImport("ValheimUpscalerNative", CallingConvention = CallingConvention.Cdecl)]
		private static extern int VUP_GetFrameParamsSize();

		[DllImport("ValheimUpscalerNative", CallingConvention = CallingConvention.Cdecl)]
		private static extern int VUP_GetDlssGStatus();

		[DllImport("ValheimUpscalerNative", CallingConvention = CallingConvention.Cdecl)]
		private static extern int VUP_GetGpuVendor();

		[DllImport("ValheimUpscalerNative", CallingConvention = CallingConvention.Cdecl)]
		private static extern int VUP_HasUnityInterfaces();

		[DllImport("ValheimUpscalerNative", CallingConvention = CallingConvention.Cdecl)]
		private static extern void VUP_EmitLatencyMarker(int markerType, ulong frameId);

		public static int NativeGpuVendor()
		{
			try
			{
				return Available ? VUP_GetGpuVendor() : 0;
			}
			catch
			{
				return 0;
			}
		}

		public static bool IsWritingOutput()
		{
			try
			{
				return Available && VUP_IsSubmitting() != 0;
			}
			catch
			{
				return false;
			}
		}

		public static bool IsFgWritingOutput()
		{
			try
			{
				return Available && VUP_IsFgSubmitting() != 0;
			}
			catch
			{
				return false;
			}
		}

		public static int DlssGStatus()
		{
			try
			{
				return Available ? VUP_GetDlssGStatus() : (-1);
			}
			catch
			{
				return -1;
			}
		}

		public static bool TryLoad()
		{
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Invalid comparison between Unknown and I4
			try
			{
				int num = VUP_Init();
				_eventFunc = VUP_GetRenderEventFunc();
				Available = num != 0 && _eventFunc != IntPtr.Zero;
				int num2 = VUP_IsAvailable();
				if (Available && num2 == 0)
				{
					Log.Info("Native DLL loaded; backend not confirmed yet (device/NGX init happens lazily on the first dispatched frame). Will dispatch.");
				}
				if (Available)
				{
					int num3 = VUP_GetFrameParamsSize();
					int num4 = Marshal.SizeOf(typeof(UpscalerFrameParams));
					if (num3 != num4)
					{
						Log.Error($"UpscalerFrameParams SIZE MISMATCH: native={num3} managed={num4} " + "- struct layout drifted between Upscaler.cs and upscaler_native.cpp. Fix before relying on output.");
					}
					else
					{
						Log.Info($"UpscalerFrameParams size OK ({num4} bytes, native/managed match).");
					}
				}
				try
				{
					FgAvailable = VUP_IsFgAvailable() != 0;
				}
				catch
				{
					FgAvailable = false;
				}
				if (Available && (int)SystemInfo.graphicsDeviceType == 21)
				{
					try
					{
						if (VUP_HasUnityInterfaces() == 0)
						{
							Log.Warning("Vulkan renderer, but UnityPluginLoad never ran - deploy ValheimUpscalerNative.dll into valheim_Data\\Plugins\\x86_64\\ (NOT next to valheim.exe). Native dispatch disabled; bilinear fallback.");
							Available = false;
						}
					}
					catch (EntryPointNotFoundException)
					{
					}
				}
			}
			catch (DllNotFoundException)
			{
				Available = false;
			}
			catch (EntryPointNotFoundException ex3)
			{
				Log.Warning("Native export missing: " + ex3.Message);
				Available = false;
			}
			catch (Exception ex4)
			{
				Log.Warning("Native bridge load error: " + ex4.Message);
				Available = false;
			}
			return Available;
		}

		public static void Shutdown()
		{
			try
			{
				if (Available)
				{
					VUP_Shutdown();
				}
			}
			catch
			{
			}
		}

		public static void EmitLatencyMarker(int type, ulong frameId)
		{
			try
			{
				if (Available)
				{
					VUP_EmitLatencyMarker(type, frameId);
				}
			}
			catch
			{
			}
		}
	}
	internal static class UpscalerRunner
	{
		private struct CachedNativePtr
		{
			public int W;

			public int H;

			public IntPtr Ptr;
		}

		public static ulong CurrentFrameId;

		public static bool JitterEnabled = true;

		public static bool NativeEvalEnabled = true;

		public static bool ForceMotionVectors = true;

		public static float CurrentJitterX;

		public static float CurrentJitterY;

		private static int _jitterSignMode = 3;

		public static int JitterReportSignX = -1;

		public static int JitterReportSignY = -1;

		private static int _mvSignMode = 3;

		public static int MvSignX = -1;

		public static int MvSignY = -1;

		private static int _jitterIndex;

		private const int kParamsRing = 4;

		private static readonly IntPtr[] _paramsRing = new IntPtr[4];

		private static int _paramsSlot;

		private static CommandBuffer _cmd;

		private static int _lastRW;

		private static int _lastRH;

		private static bool _warnedNoDepthMv;

		private static int _lastSceneIdx = -1;

		private static Vector3 _lastCamPos;

		private static bool _forceReset;

		private static readonly Dictionary<Texture, CachedNativePtr> _ptrCache = new Dictionary<Texture, CachedNativePtr>();

		private static readonly float[] _camViewToClip = new float[16];

		private static readonly float[] _clipToPrevClip = new float[16];

		private static readonly float[] _prevClipToClip = new float[16];

		private static readonly float[] _camPosArr = new float[3];

		private static readonly float[] _camUpArr = new float[3];

		private static readonly float[] _camRightArr = new float[3];

		private static readonly float[] _camFwdArr = new float[3];

		private static Matrix4x4 _prevViewProj = Matrix4x4.identity;

		private static bool _havePrevViewProj;

		public static bool FgEnabled
		{
			get
			{
				if (GpuInfo.Vendor == GpuVendor.Nvidia)
				{
					return InjectPlugin.ConfigEnableFG?.Value ?? false;
				}
				return false;
			}
		}

		public static int JitterSignMode => _jitterSignMode;

		public static string JitterSignLabel => string.Format("mode {0} (X{1},Y{2})", _jitterSignMode, (JitterReportSignX > 0) ? "+" : "-", (JitterReportSignY > 0) ? "+" : "-");

		public static string MvSignLabel => string.Format("mode {0} (X{1},Y{2})", _mvSignMode, (MvSignX > 0) ? "+" : "-", (MvSignY > 0) ? "+" : "-");

		public static void CycleJitterSign()
		{
			_jitterSignMode = (_jitterSignMode + 1) % 4;
			JitterReportSignX = (((_jitterSignMode & 1) == 0) ? 1 : (-1));
			JitterReportSignY = (((_jitterSignMode & 2) == 0) ? 1 : (-1));
		}

		public static void CycleMvSign()
		{
			_mvSignMode = (_mvSignMode + 1) % 4;
			MvSignX = (((_mvSignMode & 1) == 0) ? 1 : (-1));
			MvSignY = (((_mvSignMode & 2) == 0) ? 1 : (-1));
		}

		private static void FlattenRowMajor(Matrix4x4 m, float[] dst)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			Vector4 row = ((Matrix4x4)(ref m)).GetRow(0);
			Vector4 row2 = ((Matrix4x4)(ref m)).GetRow(1);
			Vector4 row3 = ((Matrix4x4)(ref m)).GetRow(2);
			Vector4 row4 = ((Matrix4x4)(ref m)).GetRow(3);
			dst[0] = row.x;
			dst[1] = row.y;
			dst[2] = row.z;
			dst[3] = row.w;
			dst[4] = row2.x;
			dst[5] = row2.y;
			dst[6] = row2.z;
			dst[7] = row2.w;
			dst[8] = row3.x;
			dst[9] = row3.y;
			dst[10] = row3.z;
			dst[11] = row3.w;
			dst[12] = row4.x;
			dst[13] = row4.y;
			dst[14] = row4.z;
			dst[15] = row4.w;
		}

		public static void Init()
		{
			for (int i = 0; i < 4; i++)
			{
				_paramsRing[i] = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(UpscalerFrameParams)));
			}
			if (UpscalerNative.TryLoad())
			{
				Log.Info("Upscaler native bridge active. Choose the backend (DLSS/FSR/XeSS) in the OptiScaler overlay (Insert).");
			}
			else
			{
				Log.Warning("Upscaler native bridge unavailable - using bilinear fallback. Build ValheimUpscalerNative.dll, then drop OptiScaler (as dxgi.dll) next to valheim.exe.");
			}
		}

		public static void ApplyJitter(Camera cam, int renderW, int renderH, ref Matrix4x4 baseProj, ref bool jittered)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			if (!JitterEnabled)
			{
				CurrentJitterX = 0f;
				CurrentJitterY = 0f;
				return;
			}
			cam.ResetProjectionMatrix();
			baseProj = cam.projectionMatrix;
			cam.nonJitteredProjectionMatrix = baseProj;
			int num = Mathf.Clamp(Mathf.RoundToInt(8f * Mathf.Pow((float)Screen.width / (float)Mathf.Max(1, renderW), 2f)), 8, 64);
			_jitterIndex = (_jitterIndex + 1) % num;
			float num2 = Halton(_jitterIndex + 1, 2) - 0.5f;
			float num3 = Halton(_jitterIndex + 1, 3) - 0.5f;
			CurrentJitterX = (float)JitterReportSignX * num2;
			CurrentJitterY = (float)JitterReportSignY * num3;
			Matrix4x4 projectionMatrix = baseProj;
			projectionMatrix.m02 += 2f * num2 / (float)renderW;
			projectionMatrix.m12 += 2f * num3 / (float)renderH;
			cam.projectionMatrix = projectionMatrix;
			jittered = true;
		}

		public static bool TryEvaluate(RenderTexture src, RenderTexture output, Camera cam, Matrix4x4 nonJitteredProj)
		{
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_0234: Unknown result type (might be due to invalid IL or missing references)
			//IL_0243: Unknown result type (might be due to invalid IL or missing references)
			//IL_0252: Unknown result type (might be due to invalid IL or missing references)
			//IL_0254: Unknown result type (might be due to invalid IL or missing references)
			//IL_0265: Unknown result type (might be due to invalid IL or missing references)
			//IL_026a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0272: Unknown result type (might be due to invalid IL or missing references)
			//IL_0277: Unknown result type (might be due to invalid IL or missing references)
			//IL_027f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0284: Unknown result type (might be due to invalid IL or missing references)
			//IL_028c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0291: Unknown result type (might be due to invalid IL or missing references)
			//IL_0299: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02df: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0309: Unknown result type (might be due to invalid IL or missing references)
			//IL_0317: Unknown result type (might be due to invalid IL or missing references)
			//IL_0325: Unknown result type (might be due to invalid IL or missing references)
			//IL_0333: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0203: Unknown result type (might be due to invalid IL or missing references)
			//IL_0208: Unknown result type (might be due to invalid IL or missing references)
			//IL_0217: Unknown result type (might be due to invalid IL or missing references)
			//IL_021e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0223: Unknown result type (might be due to invalid IL or missing references)
			//IL_0524: Unknown result type (might be due to invalid IL or missing references)
			//IL_0529: Unknown result type (might be due to invalid IL or missing references)
			//IL_0539: Expected O, but got Unknown
			if ((Object)(object)output == (Object)null)
			{
				return false;
			}
			if (!NativeEvalEnabled)
			{
				return false;
			}
			if (!UpscalerNative.Available)
			{
				return false;
			}
			Texture globalTexture = Shader.GetGlobalTexture("_CameraDepthTexture");
			Texture globalTexture2 = Shader.GetGlobalTexture("_CameraMotionVectorsTexture");
			if ((Object)(object)globalTexture == (Object)null || (Object)(object)globalTexture2 == (Object)null)
			{
				if (!_warnedNoDepthMv)
				{
					_warnedNoDepthMv = true;
					Log.Warning("Depth/MV not reachable as Textures; upscaler disabled this session (bilinear fallback).");
				}
				return false;
			}
			int width = ((Texture)src).width;
			int height = ((Texture)src).height;
			IntPtr cachedNativePtr = GetCachedNativePtr((Texture)(object)src);
			IntPtr cachedNativePtr2 = GetCachedNativePtr(globalTexture);
			IntPtr cachedNativePtr3 = GetCachedNativePtr(globalTexture2);
			IntPtr cachedNativePtr4 = GetCachedNativePtr((Texture)(object)output);
			if (cachedNativePtr == IntPtr.Zero || cachedNativePtr2 == IntPtr.Zero || cachedNativePtr3 == IntPtr.Zero || cachedNativePtr4 == IntPtr.Zero)
			{
				return false;
			}
			bool num = width != _lastRW || height != _lastRH;
			Scene activeScene = SceneManager.GetActiveScene();
			int buildIndex = ((Scene)(ref activeScene)).buildIndex;
			bool flag = _lastSceneIdx >= 0 && buildIndex != _lastSceneIdx;
			Vector3 val = (((Object)(object)cam != (Object)null) ? ((Component)cam).transform.position : Vector3.zero);
			bool flag2 = _lastSceneIdx >= 0 && Vector3.SqrMagnitude(val - _lastCamPos) > 2500f;
			int num2 = ((num || flag || flag2 || _forceReset) ? 1 : 0);
			_lastRW = width;
			_lastRH = height;
			_lastSceneIdx = buildIndex;
			_lastCamPos = val;
			_forceReset = false;
			IntPtr cachedNativePtr5 = GetCachedNativePtr((Texture)(object)InjectState.ReactiveMask);
			IntPtr cachedNativePtr6 = GetCachedNativePtr((Texture)(object)InjectState.FgInterpOutput);
			int fgEnabled = ((FgEnabled && cachedNativePtr6 != IntPtr.Zero) ? 1 : 0);
			int fgNotRenderingGameFrames = ((num2 != 0 || Time.timeScale == 0f || !InjectState.Enabled) ? 1 : 0);
			if ((Object)(object)cam != (Object)null)
			{
				FlattenRowMajor(nonJitteredProj, _camViewToClip);
				Matrix4x4 val2 = nonJitteredProj * cam.worldToCameraMatrix;
				if (_havePrevViewProj && num2 == 0)
				{
					FlattenRowMajor(_prevViewProj * ((Matrix4x4)(ref val2)).inverse, _clipToPrevClip);
					FlattenRowMajor(val2 * ((Matrix4x4)(ref _prevViewProj)).inverse, _prevClipToClip);
				}
				else
				{
					FlattenRowMajor(Matrix4x4.identity, _clipToPrevClip);
					FlattenRowMajor(Matrix4x4.identity, _prevClipToClip);
				}
				_prevViewProj = val2;
				_havePrevViewProj = true;
				Vector3 position = ((Component)cam).transform.position;
				Vector3 up = ((Component)cam).transform.up;
				Vector3 right = ((Component)cam).transform.right;
				Vector3 forward = ((Component)cam).transform.forward;
				_camPosArr[0] = position.x;
				_camPosArr[1] = position.y;
				_camPosArr[2] = position.z;
				_camUpArr[0] = up.x;
				_camUpArr[1] = up.y;
				_camUpArr[2] = up.z;
				_camRightArr[0] = right.x;
				_camRightArr[1] = right.y;
				_camRightArr[2] = right.z;
				_camFwdArr[0] = forward.x;
				_camFwdArr[1] = forward.y;
				_camFwdArr[2] = forward.z;
			}
			UpscalerFrameParams structure = new UpscalerFrameParams
			{
				Color = cachedNativePtr,
				Depth = cachedNativePtr2,
				MotionVectors = cachedNativePtr3,
				Output = cachedNativePtr4,
				ReactiveMask = cachedNativePtr5,
				RenderWidth = width,
				RenderHeight = height,
				DisplayWidth = ((Texture)output).width,
				DisplayHeight = ((Texture)output).height,
				JitterX = CurrentJitterX,
				JitterY = CurrentJitterY,
				MvScaleX = MvSignX * width,
				MvScaleY = MvSignY * height,
				Reset = num2,
				Flags = 0,
				FrameDeltaMs = Time.unscaledDeltaTime * 1000f,
				FgOutputInterp = cachedNativePtr6,
				FgEnabled = fgEnabled,
				FgNotRenderingGameFrames = fgNotRenderingGameFrames,
				CamViewToClip = _camViewToClip,
				ClipToPrevClip = _clipToPrevClip,
				PrevClipToClip = _prevClipToClip,
				CamPos = _camPosArr,
				CamUp = _camUpArr,
				CamRight = _camRightArr,
				CamFwd = _camFwdArr,
				CamNear = (((Object)(object)cam != (Object)null) ? cam.nearClipPlane : 0.1f),
				CamFar = (((Object)(object)cam != (Object)null) ? cam.farClipPlane : 1000f),
				CamFovRad = (((Object)(object)cam != (Object)null) ? (cam.fieldOfView * (MathF.PI / 180f)) : 0f),
				CamAspect = (((Object)(object)cam != (Object)null) ? cam.aspect : 1f),
				CamOrtho = (((Object)(object)cam != (Object)null && cam.orthographic) ? 1 : 0)
			};
			_paramsSlot = (_paramsSlot + 1) % 4;
			IntPtr intPtr = _paramsRing[_paramsSlot];
			Marshal.StructureToPtr(structure, intPtr, fDeleteOld: false);
			if (_cmd == null)
			{
				_cmd = new CommandBuffer
				{
					name = "ValheimUpscaler"
				};
			}
			_cmd.Clear();
			_cmd.IssuePluginEventAndData(UpscalerNative.EventFunc, 1, intPtr);
			Graphics.ExecuteCommandBuffer(_cmd);
			return true;
		}

		public static void InvalidateTextureCache()
		{
			_ptrCache.Clear();
		}

		private static IntPtr GetCachedNativePtr(Texture tex)
		{
			if ((Object)(object)tex == (Object)null)
			{
				return IntPtr.Zero;
			}
			if (_ptrCache.TryGetValue(tex, out var value) && value.W == tex.width && value.H == tex.height && value.Ptr != IntPtr.Zero)
			{
				return value.Ptr;
			}
			if (_ptrCache.Count > 64)
			{
				_ptrCache.Clear();
			}
			IntPtr nativeTexturePtr = tex.GetNativeTexturePtr();
			_ptrCache[tex] = new CachedNativePtr
			{
				W = tex.width,
				H = tex.height,
				Ptr = nativeTexturePtr
			};
			return nativeTexturePtr;
		}

		private static float Halton(int index, int b)
		{
			float num = 0f;
			float num2 = 1f;
			for (int num3 = index; num3 > 0; num3 /= b)
			{
				num2 /= (float)b;
				num += num2 * (float)(num3 % b);
			}
			return num;
		}

		public static void Dispose()
		{
			UpscalerNative.Shutdown();
			if (_cmd != null)
			{
				_cmd.Release();
				_cmd = null;
			}
			for (int i = 0; i < 4; i++)
			{
				if (_paramsRing[i] != IntPtr.Zero)
				{
					Marshal.FreeHGlobal(_paramsRing[i]);
					_paramsRing[i] = IntPtr.Zero;
				}
			}
		}
	}
}