highly experimental runtime model decimation + file cache adjustment to clean up processed file copies

This commit is contained in:
2026-01-02 09:54:34 +09:00
parent 44bb53023e
commit aeed8503c2
46 changed files with 9990 additions and 84 deletions

View File

@@ -944,6 +944,7 @@ public class CompactUi : WindowMediatorSubscriberBase
VisiblePairSortMode.VramUsage => SortVisibleByMetric(entryList, e => e.LastAppliedApproximateVramBytes),
VisiblePairSortMode.EffectiveVramUsage => SortVisibleByMetric(entryList, e => e.LastAppliedApproximateEffectiveVramBytes),
VisiblePairSortMode.TriangleCount => SortVisibleByMetric(entryList, e => e.LastAppliedDataTris),
VisiblePairSortMode.EffectiveTriangleCount => SortVisibleByMetric(entryList, e => e.LastAppliedApproximateEffectiveTris),
VisiblePairSortMode.Alphabetical => [.. entryList.OrderBy(e => AlphabeticalSortKey(e), StringComparer.OrdinalIgnoreCase)],
VisiblePairSortMode.PreferredDirectPairs => SortVisibleByPreferred(entryList),
_ => SortEntries(entryList),

View File

@@ -326,6 +326,7 @@ public class DrawFolderTag : DrawFolderBase
VisiblePairSortMode.VramUsage => "VRAM usage (descending)",
VisiblePairSortMode.EffectiveVramUsage => "Effective VRAM usage (descending)",
VisiblePairSortMode.TriangleCount => "Triangle count (descending)",
VisiblePairSortMode.EffectiveTriangleCount => "Effective triangle count (descending)",
VisiblePairSortMode.PreferredDirectPairs => "Preferred permissions & Direct pairs",
_ => "Default",
};

View File

@@ -429,6 +429,7 @@ public class DrawUserPair
_pair.LastAppliedApproximateVRAMBytes,
_pair.LastAppliedApproximateEffectiveVRAMBytes,
_pair.LastAppliedDataTris,
_pair.LastAppliedApproximateEffectiveTris,
_pair.IsPaired,
groupDisplays is null ? ImmutableArray<string>.Empty : ImmutableArray.CreateRange(groupDisplays));
@@ -444,6 +445,8 @@ public class DrawUserPair
private static string BuildTooltip(in TooltipSnapshot snapshot)
{
var builder = new StringBuilder(256);
static string FormatTriangles(long count) =>
count > 1000 ? (count / 1000d).ToString("0.0'k'") : count.ToString();
if (snapshot.IsPaused)
{
@@ -510,9 +513,13 @@ public class DrawUserPair
{
builder.Append(Environment.NewLine);
builder.Append("Approx. Triangle Count (excl. Vanilla): ");
builder.Append(snapshot.LastAppliedDataTris > 1000
? (snapshot.LastAppliedDataTris / 1000d).ToString("0.0'k'")
: snapshot.LastAppliedDataTris);
builder.Append(FormatTriangles(snapshot.LastAppliedDataTris));
if (snapshot.LastAppliedApproximateEffectiveTris >= 0)
{
builder.Append(" (Effective: ");
builder.Append(FormatTriangles(snapshot.LastAppliedApproximateEffectiveTris));
builder.Append(')');
}
}
}
@@ -544,11 +551,12 @@ public class DrawUserPair
long LastAppliedApproximateVRAMBytes,
long LastAppliedApproximateEffectiveVRAMBytes,
long LastAppliedDataTris,
long LastAppliedApproximateEffectiveTris,
bool IsPaired,
ImmutableArray<string> GroupDisplays)
{
public static TooltipSnapshot Empty { get; } =
new(false, false, false, IndividualPairStatus.None, string.Empty, string.Empty, -1, -1, -1, -1, false, ImmutableArray<string>.Empty);
new(false, false, false, IndividualPairStatus.None, string.Empty, string.Empty, -1, -1, -1, -1, -1, false, ImmutableArray<string>.Empty);
}
private void DrawPairedClientMenu()

View File

@@ -217,6 +217,7 @@ public class DrawEntityFactory
entry.PairStatus,
handler?.LastAppliedDataBytes ?? -1,
handler?.LastAppliedDataTris ?? -1,
handler?.LastAppliedApproximateEffectiveTris ?? -1,
handler?.LastAppliedApproximateVRAMBytes ?? -1,
handler?.LastAppliedApproximateEffectiveVRAMBytes ?? -1,
handler);

View File

@@ -415,7 +415,9 @@ public class IdDisplayHandler
var vramBytes = pair.LastAppliedApproximateEffectiveVRAMBytes >= 0
? pair.LastAppliedApproximateEffectiveVRAMBytes
: pair.LastAppliedApproximateVRAMBytes;
var triangleCount = pair.LastAppliedDataTris;
var triangleCount = pair.LastAppliedApproximateEffectiveTris >= 0
? pair.LastAppliedApproximateEffectiveTris
: pair.LastAppliedDataTris;
if (vramBytes < 0 && triangleCount < 0)
{
return null;

View File

@@ -21,6 +21,7 @@ public sealed record PairUiEntry(
IndividualPairStatus? PairStatus,
long LastAppliedDataBytes,
long LastAppliedDataTris,
long LastAppliedApproximateEffectiveTris,
long LastAppliedApproximateVramBytes,
long LastAppliedApproximateEffectiveVramBytes,
IPairHandlerAdapter? Handler)

View File

@@ -7,4 +7,5 @@ public enum VisiblePairSortMode
EffectiveVramUsage = 2,
TriangleCount = 3,
PreferredDirectPairs = 4,
EffectiveTriangleCount = 5,
}

View File

@@ -574,6 +574,94 @@ public class SettingsUi : WindowMediatorSubscriberBase
}
}
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}");
}
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";
}
}
private void DrawThemeVectorRow(MainStyle.StyleVector2Option option)
{
ImGui.TableNextRow();
@@ -1495,6 +1583,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
DrawPairPropertyRow("Approx. VRAM", FormatBytes(pair.LastAppliedApproximateVRAMBytes));
DrawPairPropertyRow("Effective VRAM", FormatBytes(pair.LastAppliedApproximateEffectiveVRAMBytes));
DrawPairPropertyRow("Last Triangles", pair.LastAppliedDataTris < 0 ? "n/a" : pair.LastAppliedDataTris.ToString(CultureInfo.InvariantCulture));
DrawPairPropertyRow("Effective Triangles", pair.LastAppliedApproximateEffectiveTris < 0 ? "n/a" : pair.LastAppliedApproximateEffectiveTris.ToString(CultureInfo.InvariantCulture));
ImGui.EndTable();
}
@@ -3528,6 +3617,102 @@ public class SettingsUi : WindowMediatorSubscriberBase
ImGui.TreePop();
}
ImGui.Separator();
if (_uiShared.MediumTreeNode("Model Optimization", UIColors.Get("DimRed")))
{
_uiShared.MediumText("Warning", UIColors.Get("DimRed"));
_uiShared.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."));
_uiShared.DrawNoteLine("! ", UIColors.Get("DimRed"),
new SeStringUtils.RichTextEntry("this shit is placeholder still owo"));
var performanceConfig = _playerPerformanceConfigService.Current;
var enableDecimation = performanceConfig.EnableModelDecimation;
if (ImGui.Checkbox("Enable model decimation", ref enableDecimation))
{
performanceConfig.EnableModelDecimation = enableDecimation;
_playerPerformanceConfigService.Save();
}
_uiShared.DrawHelpText("When enabled, Lightless generates a decimated copy of given model after download.");
var triangleThreshold = performanceConfig.ModelDecimationTriangleThreshold;
ImGui.SetNextItemWidth(300 * ImGuiHelpers.GlobalScale);
if (ImGui.SliderInt("Decimate models above", ref triangleThreshold, 10_000, 100_000))
{
performanceConfig.ModelDecimationTriangleThreshold = Math.Clamp(triangleThreshold, 10_000, 100_000);
_playerPerformanceConfigService.Save();
}
ImGui.SameLine();
ImGui.Text("triangles");
_uiShared.DrawHelpText($"Models below this triangle count are left untouched.{UiSharedService.TooltipSeparator}Default: 50,000");
var targetPercent = (float)(performanceConfig.ModelDecimationTargetRatio * 100.0);
var clampedPercent = Math.Clamp(targetPercent, 70f, 99f);
if (Math.Abs(clampedPercent - targetPercent) > float.Epsilon)
{
performanceConfig.ModelDecimationTargetRatio = clampedPercent / 100.0;
_playerPerformanceConfigService.Save();
targetPercent = clampedPercent;
}
ImGui.SetNextItemWidth(300 * ImGuiHelpers.GlobalScale);
if (ImGui.SliderFloat("Target triangle ratio", ref targetPercent, 70f, 99f, "%.0f%%"))
{
performanceConfig.ModelDecimationTargetRatio = Math.Clamp(targetPercent / 100f, 0.7f, 0.99f);
_playerPerformanceConfigService.Save();
}
_uiShared.DrawHelpText($"Target ratio relative to original triangle count (70% keeps 70% of triangles).{UiSharedService.TooltipSeparator}Default: 70%");
ImGui.Dummy(new Vector2(5));
ImGui.TextUnformatted("Decimation targets");
_uiShared.DrawHelpText("Hair mods are always excluded from decimation.");
var allowBody = performanceConfig.ModelDecimationAllowBody;
if (ImGui.Checkbox("Body", ref allowBody))
{
performanceConfig.ModelDecimationAllowBody = allowBody;
_playerPerformanceConfigService.Save();
}
var allowFaceHead = performanceConfig.ModelDecimationAllowFaceHead;
if (ImGui.Checkbox("Face/head", ref allowFaceHead))
{
performanceConfig.ModelDecimationAllowFaceHead = allowFaceHead;
_playerPerformanceConfigService.Save();
}
var allowTail = performanceConfig.ModelDecimationAllowTail;
if (ImGui.Checkbox("Tails/Ears", ref allowTail))
{
performanceConfig.ModelDecimationAllowTail = allowTail;
_playerPerformanceConfigService.Save();
}
var allowClothing = performanceConfig.ModelDecimationAllowClothing;
if (ImGui.Checkbox("Clothing (body/legs/shoes/gloves/hats)", ref allowClothing))
{
performanceConfig.ModelDecimationAllowClothing = allowClothing;
_playerPerformanceConfigService.Save();
}
var allowAccessories = performanceConfig.ModelDecimationAllowAccessories;
if (ImGui.Checkbox("Accessories (earring/rings/bracelet/necklace)", ref allowAccessories))
{
performanceConfig.ModelDecimationAllowAccessories = allowAccessories;
_playerPerformanceConfigService.Save();
}
ImGui.Dummy(new Vector2(5));
DrawTriangleDecimationCounters();
ImGui.Dummy(new Vector2(5));
UiSharedService.ColoredSeparator(UIColors.Get("DimRed"), 1.5f);
ImGui.TreePop();
}
ImGui.Separator();
ImGui.Dummy(new Vector2(10));