sigma update
This commit is contained in:
930
LightlessSync/UI/Components/OptimizationSettingsPanel.cs
Normal file
930
LightlessSync/UI/Components/OptimizationSettingsPanel.cs
Normal file
@@ -0,0 +1,930 @@
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Utility;
|
||||
using Dalamud.Interface.Utility.Raii;
|
||||
using LightlessSync.LightlessConfiguration;
|
||||
using LightlessSync.LightlessConfiguration.Configurations;
|
||||
using LightlessSync.PlayerData.Pairs;
|
||||
using LightlessSync.UI.Services;
|
||||
using LightlessSync.Utils;
|
||||
using System.Numerics;
|
||||
|
||||
namespace LightlessSync.UI.Components;
|
||||
|
||||
public enum OptimizationPanelSection
|
||||
{
|
||||
Texture,
|
||||
Model,
|
||||
}
|
||||
|
||||
public sealed class OptimizationSettingsPanel
|
||||
{
|
||||
private readonly UiSharedService _uiSharedService;
|
||||
private readonly PlayerPerformanceConfigService _performanceConfigService;
|
||||
private readonly PairUiService _pairUiService;
|
||||
|
||||
private const ImGuiTableFlags SettingsTableFlags = ImGuiTableFlags.SizingStretchProp
|
||||
| ImGuiTableFlags.NoBordersInBody
|
||||
| ImGuiTableFlags.PadOuterX;
|
||||
|
||||
public OptimizationSettingsPanel(
|
||||
UiSharedService uiSharedService,
|
||||
PlayerPerformanceConfigService performanceConfigService,
|
||||
PairUiService pairUiService)
|
||||
{
|
||||
_uiSharedService = uiSharedService;
|
||||
_performanceConfigService = performanceConfigService;
|
||||
_pairUiService = pairUiService;
|
||||
}
|
||||
|
||||
public void DrawSettingsTrees(
|
||||
string textureLabel,
|
||||
Vector4 textureColor,
|
||||
string modelLabel,
|
||||
Vector4 modelColor,
|
||||
Func<string, Vector4, bool> beginTree)
|
||||
{
|
||||
if (beginTree(textureLabel, textureColor))
|
||||
{
|
||||
DrawTextureSection(showTitle: false);
|
||||
UiSharedService.ColoredSeparator(textureColor, 1.5f);
|
||||
ImGui.TreePop();
|
||||
}
|
||||
|
||||
ImGui.Separator();
|
||||
|
||||
if (beginTree(modelLabel, modelColor))
|
||||
{
|
||||
DrawModelSection(showTitle: false);
|
||||
UiSharedService.ColoredSeparator(modelColor, 1.5f);
|
||||
ImGui.TreePop();
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawPopup(OptimizationPanelSection section)
|
||||
{
|
||||
switch (section)
|
||||
{
|
||||
case OptimizationPanelSection.Texture:
|
||||
DrawTextureSection(showTitle: false);
|
||||
break;
|
||||
case OptimizationPanelSection.Model:
|
||||
DrawModelSection(showTitle: false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawTextureSection(bool showTitle)
|
||||
{
|
||||
var scale = ImGuiHelpers.GlobalScale;
|
||||
DrawSectionIntro(
|
||||
FontAwesomeIcon.Images,
|
||||
UIColors.Get("LightlessYellow"),
|
||||
"Texture Optimization",
|
||||
"Reduce texture memory by trimming mip levels and downscaling oversized textures.",
|
||||
showTitle);
|
||||
|
||||
DrawCallout("texture-opt-warning", UIColors.Get("DimRed"), () =>
|
||||
{
|
||||
_uiSharedService.MediumText("Warning", UIColors.Get("DimRed"));
|
||||
_uiSharedService.DrawNoteLine("! ", UIColors.Get("DimRed"),
|
||||
new SeStringUtils.RichTextEntry("Texture compression and downscaling is potentially a "),
|
||||
new SeStringUtils.RichTextEntry("destructive", UIColors.Get("DimRed"), true),
|
||||
new SeStringUtils.RichTextEntry(" process and may cause broken or incorrect character appearances."));
|
||||
|
||||
_uiSharedService.DrawNoteLine("! ", UIColors.Get("DimRed"),
|
||||
new SeStringUtils.RichTextEntry("This feature is encouraged to help "),
|
||||
new SeStringUtils.RichTextEntry("lower-end systems with limited VRAM", UIColors.Get("LightlessYellow"), true),
|
||||
new SeStringUtils.RichTextEntry(" and for use in "),
|
||||
new SeStringUtils.RichTextEntry("performance-critical scenarios", UIColors.Get("LightlessYellow"), true),
|
||||
new SeStringUtils.RichTextEntry("."));
|
||||
|
||||
_uiSharedService.DrawNoteLine("! ", UIColors.Get("DimRed"),
|
||||
new SeStringUtils.RichTextEntry("Runtime downscaling "),
|
||||
new SeStringUtils.RichTextEntry("MAY", UIColors.Get("DimRed"), true),
|
||||
new SeStringUtils.RichTextEntry(" cause higher load on the system when processing downloads."));
|
||||
|
||||
_uiSharedService.DrawNoteLine("!!! ", UIColors.Get("DimRed"),
|
||||
new SeStringUtils.RichTextEntry("When enabled, we cannot provide support for appearance issues caused by this setting!", UIColors.Get("DimRed"), true));
|
||||
});
|
||||
|
||||
ImGui.Dummy(new Vector2(0f, 2f * scale));
|
||||
DrawGroupHeader("Core Controls", UIColors.Get("LightlessYellow"));
|
||||
|
||||
var textureConfig = _performanceConfigService.Current;
|
||||
using (ImRaii.PushStyle(ImGuiStyleVar.CellPadding, new Vector2(6f * scale, 2f * scale)))
|
||||
using (var table = ImRaii.Table("texture-opt-core", 3, SettingsTableFlags))
|
||||
{
|
||||
if (table)
|
||||
{
|
||||
ImGui.TableSetupColumn("Label", ImGuiTableColumnFlags.WidthFixed, 220f * scale);
|
||||
ImGui.TableSetupColumn("Control", ImGuiTableColumnFlags.WidthFixed, 180f * scale);
|
||||
ImGui.TableSetupColumn("Description", ImGuiTableColumnFlags.WidthStretch);
|
||||
|
||||
DrawControlRow("Trim mip levels", () =>
|
||||
{
|
||||
var trimNonIndex = textureConfig.EnableNonIndexTextureMipTrim;
|
||||
var accent = UIColors.Get("LightlessYellow");
|
||||
if (DrawAccentCheckbox("##texture-trim-mips", ref trimNonIndex, accent))
|
||||
{
|
||||
textureConfig.EnableNonIndexTextureMipTrim = trimNonIndex;
|
||||
_performanceConfigService.Save();
|
||||
}
|
||||
}, "Removes high-resolution mip levels from oversized non-index textures.", UIColors.Get("LightlessYellow"), UIColors.Get("LightlessYellow"));
|
||||
|
||||
DrawControlRow("Downscale index textures", () =>
|
||||
{
|
||||
var downscaleIndex = textureConfig.EnableIndexTextureDownscale;
|
||||
var accent = UIColors.Get("LightlessYellow");
|
||||
if (DrawAccentCheckbox("##texture-downscale-index", ref downscaleIndex, accent))
|
||||
{
|
||||
textureConfig.EnableIndexTextureDownscale = downscaleIndex;
|
||||
_performanceConfigService.Save();
|
||||
}
|
||||
}, "Downscales oversized index textures to the configured dimension.", UIColors.Get("LightlessYellow"), UIColors.Get("LightlessYellow"));
|
||||
|
||||
DrawControlRow("Max texture dimension", () =>
|
||||
{
|
||||
var dimensionOptions = new[] { 512, 1024, 2048, 4096 };
|
||||
var optionLabels = dimensionOptions.Select(static value => value.ToString()).ToArray();
|
||||
var currentDimension = textureConfig.TextureDownscaleMaxDimension;
|
||||
var selectedIndex = Array.IndexOf(dimensionOptions, currentDimension);
|
||||
if (selectedIndex < 0)
|
||||
{
|
||||
selectedIndex = Array.IndexOf(dimensionOptions, 2048);
|
||||
}
|
||||
|
||||
ImGui.SetNextItemWidth(-1f);
|
||||
if (ImGui.Combo("##texture-max-dimension", ref selectedIndex, optionLabels, optionLabels.Length))
|
||||
{
|
||||
textureConfig.TextureDownscaleMaxDimension = dimensionOptions[selectedIndex];
|
||||
_performanceConfigService.Save();
|
||||
}
|
||||
}, "Textures above this size are reduced to the limit. Default: 2048.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!textureConfig.EnableNonIndexTextureMipTrim
|
||||
&& !textureConfig.EnableIndexTextureDownscale
|
||||
&& !textureConfig.EnableUncompressedTextureCompression)
|
||||
{
|
||||
UiSharedService.ColorTextWrapped(
|
||||
"Texture trimming, downscale, and compression are disabled. Lightless will keep original textures regardless of size.",
|
||||
UIColors.Get("DimRed"));
|
||||
}
|
||||
|
||||
ImGui.Dummy(new Vector2(0f, 4f * scale));
|
||||
DrawGroupHeader("Behavior & Exceptions", UIColors.Get("LightlessYellow"));
|
||||
|
||||
using (ImRaii.PushStyle(ImGuiStyleVar.CellPadding, new Vector2(6f * scale, 2f * scale)))
|
||||
using (var table = ImRaii.Table("texture-opt-behavior", 3, SettingsTableFlags))
|
||||
{
|
||||
if (table)
|
||||
{
|
||||
ImGui.TableSetupColumn("Label", ImGuiTableColumnFlags.WidthFixed, 220f * scale);
|
||||
ImGui.TableSetupColumn("Control", ImGuiTableColumnFlags.WidthFixed, 180f * scale);
|
||||
ImGui.TableSetupColumn("Description", ImGuiTableColumnFlags.WidthStretch);
|
||||
|
||||
DrawControlRow("Only downscale uncompressed", () =>
|
||||
{
|
||||
var onlyUncompressed = textureConfig.OnlyDownscaleUncompressedTextures;
|
||||
if (ImGui.Checkbox("##texture-only-uncompressed", ref onlyUncompressed))
|
||||
{
|
||||
textureConfig.OnlyDownscaleUncompressedTextures = onlyUncompressed;
|
||||
_performanceConfigService.Save();
|
||||
}
|
||||
}, "When disabled, block-compressed textures can be downscaled too.");
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.Dummy(new Vector2(0f, 2f * scale));
|
||||
DrawTextureCompressionCard(textureConfig);
|
||||
|
||||
using (ImRaii.PushStyle(ImGuiStyleVar.CellPadding, new Vector2(6f * scale, 2f * scale)))
|
||||
using (var table = ImRaii.Table("texture-opt-behavior-extra", 3, SettingsTableFlags))
|
||||
{
|
||||
if (table)
|
||||
{
|
||||
ImGui.TableSetupColumn("Label", ImGuiTableColumnFlags.WidthFixed, 220f * scale);
|
||||
ImGui.TableSetupColumn("Control", ImGuiTableColumnFlags.WidthFixed, 180f * scale);
|
||||
ImGui.TableSetupColumn("Description", ImGuiTableColumnFlags.WidthStretch);
|
||||
|
||||
DrawControlRow("Keep original texture files", () =>
|
||||
{
|
||||
var keepOriginalTextures = textureConfig.KeepOriginalTextureFiles;
|
||||
if (ImGui.Checkbox("##texture-keep-original", ref keepOriginalTextures))
|
||||
{
|
||||
textureConfig.KeepOriginalTextureFiles = keepOriginalTextures;
|
||||
_performanceConfigService.Save();
|
||||
}
|
||||
}, "Keeps the original texture alongside the downscaled copy.");
|
||||
|
||||
DrawControlRow("Skip preferred/direct pairs", () =>
|
||||
{
|
||||
var skipPreferredDownscale = textureConfig.SkipTextureDownscaleForPreferredPairs;
|
||||
if (ImGui.Checkbox("##texture-skip-preferred", ref skipPreferredDownscale))
|
||||
{
|
||||
textureConfig.SkipTextureDownscaleForPreferredPairs = skipPreferredDownscale;
|
||||
_performanceConfigService.Save();
|
||||
}
|
||||
}, "Leaves textures untouched for preferred/direct pairs.");
|
||||
}
|
||||
}
|
||||
|
||||
UiSharedService.ColorTextWrapped(
|
||||
"Note: Disabling \"Keep original texture files\" prevents saved/effective VRAM usage information.",
|
||||
UIColors.Get("LightlessYellow"));
|
||||
|
||||
ImGui.Dummy(new Vector2(0f, 4f * scale));
|
||||
DrawSummaryPanel("Usage Summary", UIColors.Get("LightlessPurple"), DrawTextureDownscaleCounters);
|
||||
}
|
||||
|
||||
private void DrawModelSection(bool showTitle)
|
||||
{
|
||||
var scale = ImGuiHelpers.GlobalScale;
|
||||
DrawSectionIntro(
|
||||
FontAwesomeIcon.ProjectDiagram,
|
||||
UIColors.Get("LightlessOrange"),
|
||||
"Model Optimization",
|
||||
"Reduce triangle counts by decimating models above a threshold.",
|
||||
showTitle);
|
||||
|
||||
DrawCallout("model-opt-warning", UIColors.Get("DimRed"), () =>
|
||||
{
|
||||
_uiSharedService.MediumText("Warning", UIColors.Get("DimRed"));
|
||||
_uiSharedService.DrawNoteLine("! ", UIColors.Get("DimRed"),
|
||||
new SeStringUtils.RichTextEntry("Model decimation is a "),
|
||||
new SeStringUtils.RichTextEntry("destructive", UIColors.Get("DimRed"), true),
|
||||
new SeStringUtils.RichTextEntry(" process and may cause broken or incorrect character appearances."));
|
||||
|
||||
_uiSharedService.DrawNoteLine("! ", UIColors.Get("DimRed"),
|
||||
new SeStringUtils.RichTextEntry("This feature is encouraged to help "),
|
||||
new SeStringUtils.RichTextEntry("lower-end systems with limited VRAM", UIColors.Get("LightlessYellow"), true),
|
||||
new SeStringUtils.RichTextEntry(" and for use in "),
|
||||
new SeStringUtils.RichTextEntry("performance-critical scenarios", UIColors.Get("LightlessYellow"), true),
|
||||
new SeStringUtils.RichTextEntry("."));
|
||||
|
||||
_uiSharedService.DrawNoteLine("! ", UIColors.Get("DimRed"),
|
||||
new SeStringUtils.RichTextEntry("Runtime decimation "),
|
||||
new SeStringUtils.RichTextEntry("MAY", UIColors.Get("DimRed"), true),
|
||||
new SeStringUtils.RichTextEntry(" cause higher load on the system when processing downloads."));
|
||||
|
||||
_uiSharedService.DrawNoteLine("!!! ", UIColors.Get("DimRed"),
|
||||
new SeStringUtils.RichTextEntry("When enabled, we cannot provide support for appearance issues caused by this setting!", UIColors.Get("DimRed"), true));
|
||||
});
|
||||
|
||||
ImGui.Dummy(new Vector2(0f, 2f * scale));
|
||||
DrawCallout("model-opt-behavior", UIColors.Get("LightlessGreen"), () =>
|
||||
{
|
||||
_uiSharedService.DrawNoteLine("! ", UIColors.Get("LightlessGreen"),
|
||||
new SeStringUtils.RichTextEntry("Meshes above the "),
|
||||
new SeStringUtils.RichTextEntry("triangle threshold", UIColors.Get("LightlessGreen"), true),
|
||||
new SeStringUtils.RichTextEntry(" will be decimated to the "),
|
||||
new SeStringUtils.RichTextEntry("target ratio", UIColors.Get("LightlessGreen"), true),
|
||||
new SeStringUtils.RichTextEntry(". This can reduce quality or alter intended structure."));
|
||||
});
|
||||
|
||||
DrawGroupHeader("Core Controls", UIColors.Get("LightlessOrange"));
|
||||
var performanceConfig = _performanceConfigService.Current;
|
||||
using (ImRaii.PushStyle(ImGuiStyleVar.CellPadding, new Vector2(6f * scale, 2f * scale)))
|
||||
using (var table = ImRaii.Table("model-opt-core", 3, SettingsTableFlags))
|
||||
{
|
||||
if (table)
|
||||
{
|
||||
ImGui.TableSetupColumn("Label", ImGuiTableColumnFlags.WidthFixed, 220f * scale);
|
||||
ImGui.TableSetupColumn("Control", ImGuiTableColumnFlags.WidthFixed, 180f * scale);
|
||||
ImGui.TableSetupColumn("Description", ImGuiTableColumnFlags.WidthStretch);
|
||||
|
||||
DrawControlRow("Enable model decimation", () =>
|
||||
{
|
||||
var enableDecimation = performanceConfig.EnableModelDecimation;
|
||||
var accent = UIColors.Get("LightlessOrange");
|
||||
if (DrawAccentCheckbox("##enable-model-decimation", ref enableDecimation, accent))
|
||||
{
|
||||
performanceConfig.EnableModelDecimation = enableDecimation;
|
||||
_performanceConfigService.Save();
|
||||
}
|
||||
}, "Generates a decimated copy of models after download.", UIColors.Get("LightlessOrange"), UIColors.Get("LightlessOrange"));
|
||||
|
||||
DrawControlRow("Decimate above (triangles)", () =>
|
||||
{
|
||||
var triangleThreshold = performanceConfig.ModelDecimationTriangleThreshold;
|
||||
ImGui.SetNextItemWidth(-1f);
|
||||
if (ImGui.SliderInt("##model-decimation-threshold", ref triangleThreshold, 1_000, 100_000))
|
||||
{
|
||||
performanceConfig.ModelDecimationTriangleThreshold = Math.Clamp(triangleThreshold, 1_000, 100_000);
|
||||
_performanceConfigService.Save();
|
||||
}
|
||||
}, "Models below this triangle count are left untouched. Default: 15,000.");
|
||||
|
||||
DrawControlRow("Target triangle ratio", () =>
|
||||
{
|
||||
var targetPercent = (float)(performanceConfig.ModelDecimationTargetRatio * 100.0);
|
||||
var clampedPercent = Math.Clamp(targetPercent, 60f, 99f);
|
||||
if (Math.Abs(clampedPercent - targetPercent) > float.Epsilon)
|
||||
{
|
||||
performanceConfig.ModelDecimationTargetRatio = clampedPercent / 100.0;
|
||||
_performanceConfigService.Save();
|
||||
targetPercent = clampedPercent;
|
||||
}
|
||||
|
||||
ImGui.SetNextItemWidth(-1f);
|
||||
if (ImGui.SliderFloat("##model-decimation-target", ref targetPercent, 60f, 99f, "%.0f%%"))
|
||||
{
|
||||
performanceConfig.ModelDecimationTargetRatio = Math.Clamp(targetPercent / 100f, 0.6f, 0.99f);
|
||||
_performanceConfigService.Save();
|
||||
}
|
||||
}, "Ratio relative to original triangle count (80% keeps 80%). Default: 80%.");
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.Dummy(new Vector2(0f, 2f * scale));
|
||||
DrawGroupHeader("Behavior & Exceptions", UIColors.Get("LightlessOrange"));
|
||||
|
||||
using (ImRaii.PushStyle(ImGuiStyleVar.CellPadding, new Vector2(6f * scale, 2f * scale)))
|
||||
using (var table = ImRaii.Table("model-opt-behavior-table", 3, SettingsTableFlags))
|
||||
{
|
||||
if (table)
|
||||
{
|
||||
ImGui.TableSetupColumn("Label", ImGuiTableColumnFlags.WidthFixed, 220f * scale);
|
||||
ImGui.TableSetupColumn("Control", ImGuiTableColumnFlags.WidthFixed, 180f * scale);
|
||||
ImGui.TableSetupColumn("Description", ImGuiTableColumnFlags.WidthStretch);
|
||||
|
||||
DrawControlRow("Normalize tangents", () =>
|
||||
{
|
||||
var normalizeTangents = performanceConfig.ModelDecimationNormalizeTangents;
|
||||
if (ImGui.Checkbox("##model-normalize-tangents", ref normalizeTangents))
|
||||
{
|
||||
performanceConfig.ModelDecimationNormalizeTangents = normalizeTangents;
|
||||
_performanceConfigService.Save();
|
||||
}
|
||||
}, "Normalizes tangents to reduce shading artifacts.");
|
||||
|
||||
DrawControlRow("Keep original model files", () =>
|
||||
{
|
||||
var keepOriginalModels = performanceConfig.KeepOriginalModelFiles;
|
||||
if (ImGui.Checkbox("##model-keep-original", ref keepOriginalModels))
|
||||
{
|
||||
performanceConfig.KeepOriginalModelFiles = keepOriginalModels;
|
||||
_performanceConfigService.Save();
|
||||
}
|
||||
}, "Keeps the original model alongside the decimated copy.");
|
||||
|
||||
DrawControlRow("Skip preferred/direct pairs", () =>
|
||||
{
|
||||
var skipPreferredDecimation = performanceConfig.SkipModelDecimationForPreferredPairs;
|
||||
if (ImGui.Checkbox("##model-skip-preferred", ref skipPreferredDecimation))
|
||||
{
|
||||
performanceConfig.SkipModelDecimationForPreferredPairs = skipPreferredDecimation;
|
||||
_performanceConfigService.Save();
|
||||
}
|
||||
}, "Leaves models untouched for preferred/direct pairs.");
|
||||
}
|
||||
}
|
||||
|
||||
UiSharedService.ColorTextWrapped(
|
||||
"Note: Disabling \"Keep original model files\" prevents saved/effective triangle usage information.",
|
||||
UIColors.Get("LightlessYellow"));
|
||||
|
||||
ImGui.Dummy(new Vector2(0f, 2f * scale));
|
||||
DrawGroupHeader("Decimation Targets", UIColors.Get("LightlessGrey"), "Hair mods are always excluded from decimation.");
|
||||
|
||||
_uiSharedService.DrawNoteLine("! ", UIColors.Get("LightlessGreen"),
|
||||
new SeStringUtils.RichTextEntry("Automatic decimation will only target the selected "),
|
||||
new SeStringUtils.RichTextEntry("decimation targets", UIColors.Get("LightlessGreen"), true),
|
||||
new SeStringUtils.RichTextEntry("."));
|
||||
|
||||
_uiSharedService.DrawNoteLine("! ", UIColors.Get("LightlessYellow"),
|
||||
new SeStringUtils.RichTextEntry("It is advised to not decimate any body related meshes which includes: "),
|
||||
new SeStringUtils.RichTextEntry("facial mods + sculpts, chest, legs, hands and feet", UIColors.Get("LightlessYellow"), true),
|
||||
new SeStringUtils.RichTextEntry("."));
|
||||
|
||||
_uiSharedService.DrawNoteLine("!!! ", UIColors.Get("DimRed"),
|
||||
new SeStringUtils.RichTextEntry("Automatic decimation is not perfect and can cause meshes with bad topology to be worse.", UIColors.Get("DimRed"), true));
|
||||
|
||||
DrawTargetGrid(performanceConfig);
|
||||
|
||||
ImGui.Dummy(new Vector2(0f, 4f * scale));
|
||||
DrawSummaryPanel("Usage Summary", UIColors.Get("LightlessPurple"), DrawTriangleDecimationCounters);
|
||||
}
|
||||
|
||||
private void DrawTargetGrid(PlayerPerformanceConfig config)
|
||||
{
|
||||
var scale = ImGuiHelpers.GlobalScale;
|
||||
using (ImRaii.PushStyle(ImGuiStyleVar.CellPadding, new Vector2(6f * scale, 2f * scale)))
|
||||
using (var table = ImRaii.Table("model-opt-targets", 3, SettingsTableFlags))
|
||||
{
|
||||
if (!table)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui.TableSetupColumn("Label", ImGuiTableColumnFlags.WidthFixed, 220f * scale);
|
||||
ImGui.TableSetupColumn("Control", ImGuiTableColumnFlags.WidthFixed, 180f * scale);
|
||||
ImGui.TableSetupColumn("Description", ImGuiTableColumnFlags.WidthStretch);
|
||||
|
||||
DrawControlRow("Body", () =>
|
||||
{
|
||||
var allowBody = config.ModelDecimationAllowBody;
|
||||
if (ImGui.Checkbox("##model-target-body", ref allowBody))
|
||||
{
|
||||
config.ModelDecimationAllowBody = allowBody;
|
||||
_performanceConfigService.Save();
|
||||
}
|
||||
}, "Body meshes (torso, limbs).");
|
||||
|
||||
DrawControlRow("Face/head", () =>
|
||||
{
|
||||
var allowFaceHead = config.ModelDecimationAllowFaceHead;
|
||||
if (ImGui.Checkbox("##model-target-facehead", ref allowFaceHead))
|
||||
{
|
||||
config.ModelDecimationAllowFaceHead = allowFaceHead;
|
||||
_performanceConfigService.Save();
|
||||
}
|
||||
}, "Face and head meshes.");
|
||||
|
||||
DrawControlRow("Tails/Ears", () =>
|
||||
{
|
||||
var allowTail = config.ModelDecimationAllowTail;
|
||||
if (ImGui.Checkbox("##model-target-tail", ref allowTail))
|
||||
{
|
||||
config.ModelDecimationAllowTail = allowTail;
|
||||
_performanceConfigService.Save();
|
||||
}
|
||||
}, "Tail, ear, and similar appendages.");
|
||||
|
||||
DrawControlRow("Clothing", () =>
|
||||
{
|
||||
var allowClothing = config.ModelDecimationAllowClothing;
|
||||
if (ImGui.Checkbox("##model-target-clothing", ref allowClothing))
|
||||
{
|
||||
config.ModelDecimationAllowClothing = allowClothing;
|
||||
_performanceConfigService.Save();
|
||||
}
|
||||
}, "Outfits, shoes, gloves, hats.");
|
||||
|
||||
DrawControlRow("Accessories", () =>
|
||||
{
|
||||
var allowAccessories = config.ModelDecimationAllowAccessories;
|
||||
if (ImGui.Checkbox("##model-target-accessories", ref allowAccessories))
|
||||
{
|
||||
config.ModelDecimationAllowAccessories = allowAccessories;
|
||||
_performanceConfigService.Save();
|
||||
}
|
||||
}, "Jewelry and small add-ons.");
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawSectionIntro(FontAwesomeIcon icon, Vector4 color, string title, string subtitle, bool showTitle)
|
||||
{
|
||||
var scale = ImGuiHelpers.GlobalScale;
|
||||
if (showTitle)
|
||||
{
|
||||
using (_uiSharedService.MediumFont.Push())
|
||||
{
|
||||
_uiSharedService.IconText(icon, color);
|
||||
ImGui.SameLine(0f, 6f * scale);
|
||||
ImGui.TextColored(color, title);
|
||||
}
|
||||
|
||||
ImGui.TextColored(UIColors.Get("LightlessGrey"), subtitle);
|
||||
}
|
||||
else
|
||||
{
|
||||
_uiSharedService.IconText(icon, color);
|
||||
ImGui.SameLine(0f, 6f * scale);
|
||||
ImGui.TextColored(UIColors.Get("LightlessGrey"), subtitle);
|
||||
}
|
||||
|
||||
ImGui.Dummy(new Vector2(0f, 2f * scale));
|
||||
}
|
||||
|
||||
private void DrawGroupHeader(string title, Vector4 color, string? helpText = null)
|
||||
{
|
||||
using var font = _uiSharedService.MediumFont.Push();
|
||||
ImGui.TextColored(color, title);
|
||||
if (!string.IsNullOrWhiteSpace(helpText))
|
||||
{
|
||||
_uiSharedService.DrawHelpText(helpText);
|
||||
}
|
||||
UiSharedService.ColoredSeparator(color, 1.2f);
|
||||
}
|
||||
|
||||
private void DrawCallout(string id, Vector4 color, Action content)
|
||||
{
|
||||
var scale = ImGuiHelpers.GlobalScale;
|
||||
var bg = new Vector4(color.X, color.Y, color.Z, 0.08f);
|
||||
var border = new Vector4(color.X, color.Y, color.Z, 0.25f);
|
||||
DrawPanelBox(id, bg, border, 6f * scale, new Vector2(10f * scale, 6f * scale), content);
|
||||
}
|
||||
|
||||
private void DrawSummaryPanel(string title, Vector4 accent, Action content)
|
||||
{
|
||||
var scale = ImGuiHelpers.GlobalScale;
|
||||
var bg = new Vector4(accent.X, accent.Y, accent.Z, 0.06f);
|
||||
var border = new Vector4(accent.X, accent.Y, accent.Z, 0.2f);
|
||||
DrawPanelBox($"summary-{title}", bg, border, 6f * scale, new Vector2(10f * scale, 6f * scale), () =>
|
||||
{
|
||||
_uiSharedService.MediumText(title, accent);
|
||||
content();
|
||||
});
|
||||
}
|
||||
|
||||
private void DrawTextureCompressionCard(PlayerPerformanceConfig textureConfig)
|
||||
{
|
||||
var scale = ImGuiHelpers.GlobalScale;
|
||||
var baseColor = UIColors.Get("LightlessGrey");
|
||||
var bg = new Vector4(baseColor.X, baseColor.Y, baseColor.Z, 0.12f);
|
||||
var border = new Vector4(baseColor.X, baseColor.Y, baseColor.Z, 0.32f);
|
||||
|
||||
DrawPanelBox("texture-compression-card", bg, border, 6f * scale, new Vector2(10f * scale, 6f * scale), () =>
|
||||
{
|
||||
using (ImRaii.PushStyle(ImGuiStyleVar.CellPadding, new Vector2(6f * scale, 2f * scale)))
|
||||
using (var table = ImRaii.Table("texture-opt-compress-card", 2, SettingsTableFlags))
|
||||
{
|
||||
if (!table)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui.TableSetupColumn("Label", ImGuiTableColumnFlags.WidthFixed, 220f * scale);
|
||||
ImGui.TableSetupColumn("Control", ImGuiTableColumnFlags.WidthStretch);
|
||||
|
||||
DrawInlineDescriptionRow("Compress uncompressed textures", () =>
|
||||
{
|
||||
var autoCompress = textureConfig.EnableUncompressedTextureCompression;
|
||||
if (UiSharedService.CheckboxWithBorder("##texture-auto-compress", ref autoCompress, baseColor))
|
||||
{
|
||||
textureConfig.EnableUncompressedTextureCompression = autoCompress;
|
||||
_performanceConfigService.Save();
|
||||
}
|
||||
}, "Converts uncompressed textures to BC formats based on map type (heavy). Runs after downscale/mip trim.",
|
||||
drawLabelSuffix: () =>
|
||||
{
|
||||
_uiSharedService.IconText(FontAwesomeIcon.ExclamationTriangle, UIColors.Get("LightlessYellow"));
|
||||
UiSharedService.AttachToolTip("This feature can be demanding and will increase character load times.");
|
||||
});
|
||||
|
||||
DrawInlineDescriptionRow("Skip mipmaps for auto-compress", () =>
|
||||
{
|
||||
var skipMipMaps = textureConfig.SkipUncompressedTextureCompressionMipMaps;
|
||||
if (UiSharedService.CheckboxWithBorder("##texture-auto-compress-skip-mips", ref skipMipMaps, baseColor))
|
||||
{
|
||||
textureConfig.SkipUncompressedTextureCompressionMipMaps = skipMipMaps;
|
||||
_performanceConfigService.Save();
|
||||
}
|
||||
}, "Skips mipmap generation to speed up compression, but can cause shimmering.",
|
||||
disableControl: !textureConfig.EnableUncompressedTextureCompression);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void DrawInlineDescriptionRow(
|
||||
string label,
|
||||
Action drawControl,
|
||||
string description,
|
||||
Action? drawLabelSuffix = null,
|
||||
bool disableControl = false)
|
||||
{
|
||||
var scale = ImGuiHelpers.GlobalScale;
|
||||
ImGui.TableNextRow();
|
||||
ImGui.TableSetColumnIndex(0);
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.TextUnformatted(label);
|
||||
if (drawLabelSuffix != null)
|
||||
{
|
||||
ImGui.SameLine(0f, 4f * scale);
|
||||
drawLabelSuffix();
|
||||
}
|
||||
|
||||
ImGui.TableSetColumnIndex(1);
|
||||
using (ImRaii.Disabled(disableControl))
|
||||
{
|
||||
drawControl();
|
||||
}
|
||||
|
||||
ImGui.SameLine(0f, 8f * scale);
|
||||
using (ImRaii.PushColor(ImGuiCol.Text, UIColors.Get("LightlessGrey")))
|
||||
{
|
||||
ImGui.PushTextWrapPos(ImGui.GetCursorPos().X + ImGui.GetContentRegionAvail().X);
|
||||
ImGui.TextUnformatted(description);
|
||||
ImGui.PopTextWrapPos();
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawControlRow(string label, Action drawControl, string description, Vector4? labelColor = null, Vector4? cardAccent = null, Action? drawLabelSuffix = null)
|
||||
{
|
||||
var scale = ImGuiHelpers.GlobalScale;
|
||||
if (!cardAccent.HasValue)
|
||||
{
|
||||
ImGui.TableNextRow();
|
||||
ImGui.TableSetColumnIndex(0);
|
||||
ImGui.AlignTextToFramePadding();
|
||||
using var labelTint = ImRaii.PushColor(ImGuiCol.Text, labelColor ?? Vector4.Zero, labelColor.HasValue);
|
||||
ImGui.TextUnformatted(label);
|
||||
if (drawLabelSuffix != null)
|
||||
{
|
||||
ImGui.SameLine(0f, 4f * scale);
|
||||
drawLabelSuffix();
|
||||
}
|
||||
ImGui.TableSetColumnIndex(1);
|
||||
drawControl();
|
||||
ImGui.TableSetColumnIndex(2);
|
||||
using var color = ImRaii.PushColor(ImGuiCol.Text, UIColors.Get("LightlessGrey"));
|
||||
ImGui.TextWrapped(description);
|
||||
return;
|
||||
}
|
||||
|
||||
var padX = 6f * scale;
|
||||
var padY = 3f * scale;
|
||||
var rowGap = 4f * scale;
|
||||
var accent = cardAccent.Value;
|
||||
var drawList = ImGui.GetWindowDrawList();
|
||||
|
||||
ImGui.TableNextRow();
|
||||
ImGui.TableSetColumnIndex(0);
|
||||
var col0Start = ImGui.GetCursorScreenPos();
|
||||
ImGui.TableSetColumnIndex(1);
|
||||
var col1Start = ImGui.GetCursorScreenPos();
|
||||
ImGui.TableSetColumnIndex(2);
|
||||
var col2Start = ImGui.GetCursorScreenPos();
|
||||
var col2Width = ImGui.GetContentRegionAvail().X;
|
||||
|
||||
float descriptionHeight;
|
||||
using (ImRaii.PushColor(ImGuiCol.Text, new Vector4(0f, 0f, 0f, 0f)))
|
||||
{
|
||||
ImGui.SetCursorScreenPos(col2Start);
|
||||
ImGui.PushTextWrapPos(ImGui.GetCursorPos().X + col2Width);
|
||||
ImGui.TextUnformatted(description);
|
||||
ImGui.PopTextWrapPos();
|
||||
descriptionHeight = ImGui.GetItemRectSize().Y;
|
||||
}
|
||||
|
||||
var lineHeight = ImGui.GetTextLineHeight();
|
||||
var labelHeight = lineHeight;
|
||||
var controlHeight = ImGui.GetFrameHeight();
|
||||
var contentHeight = MathF.Max(labelHeight, MathF.Max(controlHeight, descriptionHeight));
|
||||
var lineCount = Math.Max(1, (int)MathF.Round(descriptionHeight / MathF.Max(1f, lineHeight)));
|
||||
var descOffset = lineCount > 1 ? lineHeight * 0.18f : 0f;
|
||||
var cardTop = col0Start.Y;
|
||||
var contentTop = cardTop + padY;
|
||||
var cardHeight = contentHeight + (padY * 2f);
|
||||
|
||||
var labelY = contentTop + (contentHeight - labelHeight) * 0.5f;
|
||||
var controlY = contentTop + (contentHeight - controlHeight) * 0.5f;
|
||||
var descY = contentTop + (contentHeight - descriptionHeight) * 0.5f - descOffset;
|
||||
|
||||
drawList.ChannelsSplit(2);
|
||||
drawList.ChannelsSetCurrent(1);
|
||||
|
||||
ImGui.TableSetColumnIndex(0);
|
||||
ImGui.SetCursorScreenPos(new Vector2(col0Start.X, labelY));
|
||||
using (ImRaii.PushColor(ImGuiCol.Text, labelColor ?? Vector4.Zero, labelColor.HasValue))
|
||||
{
|
||||
ImGui.TextUnformatted(label);
|
||||
if (drawLabelSuffix != null)
|
||||
{
|
||||
ImGui.SameLine(0f, 4f * scale);
|
||||
drawLabelSuffix();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.TableSetColumnIndex(1);
|
||||
ImGui.SetCursorScreenPos(new Vector2(col1Start.X, controlY));
|
||||
drawControl();
|
||||
|
||||
ImGui.TableSetColumnIndex(2);
|
||||
ImGui.SetCursorScreenPos(new Vector2(col2Start.X, descY));
|
||||
using (ImRaii.PushColor(ImGuiCol.Text, UIColors.Get("LightlessGrey")))
|
||||
{
|
||||
ImGui.PushTextWrapPos(ImGui.GetCursorPos().X + col2Width);
|
||||
ImGui.TextUnformatted(description);
|
||||
ImGui.PopTextWrapPos();
|
||||
}
|
||||
|
||||
var rectMin = new Vector2(col0Start.X - padX, cardTop);
|
||||
var rectMax = new Vector2(col2Start.X + col2Width + padX, cardTop + cardHeight);
|
||||
var fill = new Vector4(accent.X, accent.Y, accent.Z, 0.07f);
|
||||
var border = new Vector4(accent.X, accent.Y, accent.Z, 0.35f);
|
||||
var rounding = MathF.Max(5f, ImGui.GetStyle().FrameRounding) * scale;
|
||||
var borderThickness = MathF.Max(1f, ImGui.GetStyle().ChildBorderSize);
|
||||
var clipMin = drawList.GetClipRectMin();
|
||||
var clipMax = drawList.GetClipRectMax();
|
||||
clipMin.X = MathF.Min(clipMin.X, rectMin.X);
|
||||
clipMax.X = MathF.Max(clipMax.X, rectMax.X);
|
||||
|
||||
drawList.ChannelsSetCurrent(0);
|
||||
drawList.PushClipRect(clipMin, clipMax, false);
|
||||
drawList.AddRectFilled(rectMin, rectMax, UiSharedService.Color(fill), rounding);
|
||||
drawList.AddRect(rectMin, rectMax, UiSharedService.Color(border), rounding, ImDrawFlags.None, borderThickness);
|
||||
drawList.PopClipRect();
|
||||
drawList.ChannelsMerge();
|
||||
|
||||
ImGui.TableSetColumnIndex(2);
|
||||
ImGui.SetCursorScreenPos(new Vector2(col2Start.X, cardTop + cardHeight));
|
||||
ImGui.Dummy(new Vector2(0f, rowGap));
|
||||
}
|
||||
|
||||
private static bool DrawAccentCheckbox(string id, ref bool value, Vector4 accent)
|
||||
{
|
||||
var frame = new Vector4(accent.X, accent.Y, accent.Z, 0.14f);
|
||||
var frameHovered = new Vector4(accent.X, accent.Y, accent.Z, 0.22f);
|
||||
var frameActive = new Vector4(accent.X, accent.Y, accent.Z, 0.3f);
|
||||
bool changed;
|
||||
using (ImRaii.PushColor(ImGuiCol.CheckMark, accent))
|
||||
using (ImRaii.PushColor(ImGuiCol.FrameBg, frame))
|
||||
using (ImRaii.PushColor(ImGuiCol.FrameBgHovered, frameHovered))
|
||||
using (ImRaii.PushColor(ImGuiCol.FrameBgActive, frameActive))
|
||||
{
|
||||
changed = ImGui.Checkbox(id, ref value);
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
private static void DrawPanelBox(string id, Vector4 background, Vector4 border, float rounding, Vector2 padding, Action content)
|
||||
{
|
||||
using (ImRaii.PushId(id))
|
||||
{
|
||||
var startPos = ImGui.GetCursorScreenPos();
|
||||
var availableWidth = ImGui.GetContentRegionAvail().X;
|
||||
var drawList = ImGui.GetWindowDrawList();
|
||||
|
||||
drawList.ChannelsSplit(2);
|
||||
drawList.ChannelsSetCurrent(1);
|
||||
|
||||
using (ImRaii.Group())
|
||||
{
|
||||
ImGui.Dummy(new Vector2(0f, padding.Y));
|
||||
ImGui.Indent(padding.X);
|
||||
content();
|
||||
ImGui.Unindent(padding.X);
|
||||
ImGui.Dummy(new Vector2(0f, padding.Y));
|
||||
}
|
||||
|
||||
var rectMin = startPos;
|
||||
var rectMax = new Vector2(startPos.X + availableWidth, ImGui.GetItemRectMax().Y);
|
||||
var borderThickness = MathF.Max(1f, ImGui.GetStyle().ChildBorderSize);
|
||||
|
||||
drawList.ChannelsSetCurrent(0);
|
||||
drawList.AddRectFilled(rectMin, rectMax, UiSharedService.Color(background), rounding);
|
||||
drawList.AddRect(rectMin, rectMax, UiSharedService.Color(border), rounding, ImDrawFlags.None, borderThickness);
|
||||
drawList.ChannelsMerge();
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawTextureDownscaleCounters()
|
||||
{
|
||||
HashSet<Pair> trackedPairs = new();
|
||||
|
||||
var snapshot = _pairUiService.GetSnapshot();
|
||||
|
||||
foreach (var pair in snapshot.DirectPairs)
|
||||
{
|
||||
trackedPairs.Add(pair);
|
||||
}
|
||||
|
||||
foreach (var group in snapshot.GroupPairs.Values)
|
||||
{
|
||||
foreach (var pair in group)
|
||||
{
|
||||
trackedPairs.Add(pair);
|
||||
}
|
||||
}
|
||||
|
||||
long totalOriginalBytes = 0;
|
||||
long totalEffectiveBytes = 0;
|
||||
var hasData = false;
|
||||
|
||||
foreach (var pair in trackedPairs)
|
||||
{
|
||||
if (!pair.IsVisible)
|
||||
continue;
|
||||
|
||||
var original = pair.LastAppliedApproximateVRAMBytes;
|
||||
var effective = pair.LastAppliedApproximateEffectiveVRAMBytes;
|
||||
|
||||
if (original >= 0)
|
||||
{
|
||||
hasData = true;
|
||||
totalOriginalBytes += original;
|
||||
}
|
||||
|
||||
if (effective >= 0)
|
||||
{
|
||||
hasData = true;
|
||||
totalEffectiveBytes += effective;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasData)
|
||||
{
|
||||
ImGui.TextDisabled("VRAM usage has not been calculated yet.");
|
||||
return;
|
||||
}
|
||||
|
||||
var savedBytes = Math.Max(0L, totalOriginalBytes - totalEffectiveBytes);
|
||||
var originalText = UiSharedService.ByteToString(totalOriginalBytes, addSuffix: true);
|
||||
var effectiveText = UiSharedService.ByteToString(totalEffectiveBytes, addSuffix: true);
|
||||
var savedText = UiSharedService.ByteToString(savedBytes, addSuffix: true);
|
||||
|
||||
ImGui.TextUnformatted($"Total VRAM usage (original): {originalText}");
|
||||
ImGui.TextUnformatted($"Total VRAM usage (effective): {effectiveText}");
|
||||
|
||||
if (savedBytes > 0)
|
||||
{
|
||||
UiSharedService.ColorText($"VRAM saved by downscaling: {savedText}", UIColors.Get("LightlessGreen"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui.TextUnformatted($"VRAM saved by downscaling: {savedText}");
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawTriangleDecimationCounters()
|
||||
{
|
||||
HashSet<Pair> trackedPairs = new();
|
||||
|
||||
var snapshot = _pairUiService.GetSnapshot();
|
||||
|
||||
foreach (var pair in snapshot.DirectPairs)
|
||||
{
|
||||
trackedPairs.Add(pair);
|
||||
}
|
||||
|
||||
foreach (var group in snapshot.GroupPairs.Values)
|
||||
{
|
||||
foreach (var pair in group)
|
||||
{
|
||||
trackedPairs.Add(pair);
|
||||
}
|
||||
}
|
||||
|
||||
long totalOriginalTris = 0;
|
||||
long totalEffectiveTris = 0;
|
||||
var hasData = false;
|
||||
|
||||
foreach (var pair in trackedPairs)
|
||||
{
|
||||
if (!pair.IsVisible)
|
||||
continue;
|
||||
|
||||
var original = pair.LastAppliedDataTris;
|
||||
var effective = pair.LastAppliedApproximateEffectiveTris;
|
||||
|
||||
if (original >= 0)
|
||||
{
|
||||
hasData = true;
|
||||
totalOriginalTris += original;
|
||||
}
|
||||
|
||||
if (effective >= 0)
|
||||
{
|
||||
hasData = true;
|
||||
totalEffectiveTris += effective;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasData)
|
||||
{
|
||||
ImGui.TextDisabled("Triangle usage has not been calculated yet.");
|
||||
return;
|
||||
}
|
||||
|
||||
var savedTris = Math.Max(0L, totalOriginalTris - totalEffectiveTris);
|
||||
var originalText = FormatTriangleCount(totalOriginalTris);
|
||||
var effectiveText = FormatTriangleCount(totalEffectiveTris);
|
||||
var savedText = FormatTriangleCount(savedTris);
|
||||
|
||||
ImGui.TextUnformatted($"Total triangle usage (original): {originalText}");
|
||||
ImGui.TextUnformatted($"Total triangle usage (effective): {effectiveText}");
|
||||
|
||||
if (savedTris > 0)
|
||||
{
|
||||
UiSharedService.ColorText($"Triangles saved by decimation: {savedText}", UIColors.Get("LightlessGreen"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui.TextUnformatted($"Triangles saved by decimation: {savedText}");
|
||||
}
|
||||
}
|
||||
|
||||
private static string FormatTriangleCount(long triangleCount)
|
||||
{
|
||||
if (triangleCount < 0)
|
||||
{
|
||||
return "n/a";
|
||||
}
|
||||
|
||||
if (triangleCount >= 1_000_000)
|
||||
{
|
||||
return FormattableString.Invariant($"{triangleCount / 1_000_000d:0.#}m tris");
|
||||
}
|
||||
|
||||
if (triangleCount >= 1_000)
|
||||
{
|
||||
return FormattableString.Invariant($"{triangleCount / 1_000d:0.#}k tris");
|
||||
}
|
||||
|
||||
return $"{triangleCount} tris";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user