skip decimation for direct pairs and make it a toggle in settings
This commit is contained in:
@@ -22,10 +22,12 @@ public class PlayerPerformanceConfig : ILightlessConfiguration
|
|||||||
public int TextureDownscaleMaxDimension { get; set; } = 2048;
|
public int TextureDownscaleMaxDimension { get; set; } = 2048;
|
||||||
public bool OnlyDownscaleUncompressedTextures { get; set; } = true;
|
public bool OnlyDownscaleUncompressedTextures { get; set; } = true;
|
||||||
public bool KeepOriginalTextureFiles { get; set; } = false;
|
public bool KeepOriginalTextureFiles { get; set; } = false;
|
||||||
|
public bool SkipTextureDownscaleForPreferredPairs { get; set; } = true;
|
||||||
public bool EnableModelDecimation { get; set; } = false;
|
public bool EnableModelDecimation { get; set; } = false;
|
||||||
public int ModelDecimationTriangleThreshold { get; set; } = 50_000;
|
public int ModelDecimationTriangleThreshold { get; set; } = 50_000;
|
||||||
public double ModelDecimationTargetRatio { get; set; } = 0.8;
|
public double ModelDecimationTargetRatio { get; set; } = 0.8;
|
||||||
public bool KeepOriginalModelFiles { get; set; } = true;
|
public bool KeepOriginalModelFiles { get; set; } = true;
|
||||||
|
public bool SkipModelDecimationForPreferredPairs { get; set; } = true;
|
||||||
public bool ModelDecimationAllowBody { get; set; } = false;
|
public bool ModelDecimationAllowBody { get; set; } = false;
|
||||||
public bool ModelDecimationAllowFaceHead { get; set; } = false;
|
public bool ModelDecimationAllowFaceHead { get; set; } = false;
|
||||||
public bool ModelDecimationAllowTail { get; set; } = false;
|
public bool ModelDecimationAllowTail { get; set; } = false;
|
||||||
|
|||||||
@@ -146,7 +146,8 @@ public class PlayerDataFactory
|
|||||||
fragment.FileReplacements =
|
fragment.FileReplacements =
|
||||||
new HashSet<FileReplacement>(resolvedPaths.Select(c => new FileReplacement([.. c.Value], c.Key)), FileReplacementComparer.Instance)
|
new HashSet<FileReplacement>(resolvedPaths.Select(c => new FileReplacement([.. c.Value], c.Key)), FileReplacementComparer.Instance)
|
||||||
.Where(p => p.HasFileReplacement).ToHashSet();
|
.Where(p => p.HasFileReplacement).ToHashSet();
|
||||||
fragment.FileReplacements.RemoveWhere(c => c.GamePaths.Any(g => !CacheMonitor.AllowedFileExtensions.Any(e => g.EndsWith(e, StringComparison.OrdinalIgnoreCase))));
|
var allowedExtensions = CacheMonitor.AllowedFileExtensions;
|
||||||
|
fragment.FileReplacements.RemoveWhere(c => c.GamePaths.Any(g => !allowedExtensions.Any(e => g.EndsWith(e, StringComparison.OrdinalIgnoreCase))));
|
||||||
|
|
||||||
ct.ThrowIfCancellationRequested();
|
ct.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
@@ -194,7 +195,9 @@ public class PlayerDataFactory
|
|||||||
|
|
||||||
// get all remaining paths and resolve them
|
// get all remaining paths and resolve them
|
||||||
var transientPaths = ManageSemiTransientData(objectKind);
|
var transientPaths = ManageSemiTransientData(objectKind);
|
||||||
var resolvedTransientPaths = await GetFileReplacementsFromPaths(playerRelatedObject, transientPaths, new HashSet<string>(StringComparer.Ordinal)).ConfigureAwait(false);
|
var resolvedTransientPaths = transientPaths.Count == 0
|
||||||
|
? new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase).AsReadOnly()
|
||||||
|
: await GetFileReplacementsFromPaths(playerRelatedObject, transientPaths, new HashSet<string>(StringComparer.Ordinal)).ConfigureAwait(false);
|
||||||
|
|
||||||
if (logDebug)
|
if (logDebug)
|
||||||
{
|
{
|
||||||
@@ -377,7 +380,15 @@ public class PlayerDataFactory
|
|||||||
{
|
{
|
||||||
var forwardPaths = forwardResolve.ToArray();
|
var forwardPaths = forwardResolve.ToArray();
|
||||||
var reversePaths = reverseResolve.ToArray();
|
var reversePaths = reverseResolve.ToArray();
|
||||||
Dictionary<string, List<string>> resolvedPaths = new(StringComparer.Ordinal);
|
if (forwardPaths.Length == 0 && reversePaths.Length == 0)
|
||||||
|
{
|
||||||
|
return new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase).AsReadOnly();
|
||||||
|
}
|
||||||
|
|
||||||
|
var forwardPathsLower = forwardPaths.Length == 0 ? Array.Empty<string>() : forwardPaths.Select(p => p.ToLowerInvariant()).ToArray();
|
||||||
|
var reversePathsLower = reversePaths.Length == 0 ? Array.Empty<string>() : reversePaths.Select(p => p.ToLowerInvariant()).ToArray();
|
||||||
|
|
||||||
|
Dictionary<string, List<string>> resolvedPaths = new(forwardPaths.Length + reversePaths.Length, StringComparer.Ordinal);
|
||||||
if (handler.ObjectKind != ObjectKind.Player)
|
if (handler.ObjectKind != ObjectKind.Player)
|
||||||
{
|
{
|
||||||
var (objectIndex, forwardResolved, reverseResolved) = await _dalamudUtil.RunOnFrameworkThread(() =>
|
var (objectIndex, forwardResolved, reverseResolved) = await _dalamudUtil.RunOnFrameworkThread(() =>
|
||||||
@@ -415,24 +426,29 @@ public class PlayerDataFactory
|
|||||||
|
|
||||||
if (resolvedPaths.TryGetValue(filePath, out var list))
|
if (resolvedPaths.TryGetValue(filePath, out var list))
|
||||||
{
|
{
|
||||||
list.Add(forwardPaths[i].ToLowerInvariant());
|
list.Add(forwardPathsLower[i]);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
resolvedPaths[filePath] = [forwardPaths[i].ToLowerInvariant()];
|
resolvedPaths[filePath] = [forwardPathsLower[i]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < reversePaths.Length; i++)
|
for (int i = 0; i < reversePaths.Length; i++)
|
||||||
{
|
{
|
||||||
var filePath = reversePaths[i].ToLowerInvariant();
|
var filePath = reversePathsLower[i];
|
||||||
|
var reverseResolvedLower = new string[reverseResolved[i].Length];
|
||||||
|
for (var j = 0; j < reverseResolvedLower.Length; j++)
|
||||||
|
{
|
||||||
|
reverseResolvedLower[j] = reverseResolved[i][j].ToLowerInvariant();
|
||||||
|
}
|
||||||
if (resolvedPaths.TryGetValue(filePath, out var list))
|
if (resolvedPaths.TryGetValue(filePath, out var list))
|
||||||
{
|
{
|
||||||
list.AddRange(reverseResolved[i].Select(c => c.ToLowerInvariant()));
|
list.AddRange(reverseResolvedLower);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
resolvedPaths[filePath] = new List<string>(reverseResolved[i].Select(c => c.ToLowerInvariant()).ToList());
|
resolvedPaths[filePath] = new List<string>(reverseResolvedLower);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -446,24 +462,29 @@ public class PlayerDataFactory
|
|||||||
var filePath = forward[i].ToLowerInvariant();
|
var filePath = forward[i].ToLowerInvariant();
|
||||||
if (resolvedPaths.TryGetValue(filePath, out var list))
|
if (resolvedPaths.TryGetValue(filePath, out var list))
|
||||||
{
|
{
|
||||||
list.Add(forwardPaths[i].ToLowerInvariant());
|
list.Add(forwardPathsLower[i]);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
resolvedPaths[filePath] = [forwardPaths[i].ToLowerInvariant()];
|
resolvedPaths[filePath] = [forwardPathsLower[i]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < reversePaths.Length; i++)
|
for (int i = 0; i < reversePaths.Length; i++)
|
||||||
{
|
{
|
||||||
var filePath = reversePaths[i].ToLowerInvariant();
|
var filePath = reversePathsLower[i];
|
||||||
|
var reverseResolvedLower = new string[reverse[i].Length];
|
||||||
|
for (var j = 0; j < reverseResolvedLower.Length; j++)
|
||||||
|
{
|
||||||
|
reverseResolvedLower[j] = reverse[i][j].ToLowerInvariant();
|
||||||
|
}
|
||||||
if (resolvedPaths.TryGetValue(filePath, out var list))
|
if (resolvedPaths.TryGetValue(filePath, out var list))
|
||||||
{
|
{
|
||||||
list.AddRange(reverse[i].Select(c => c.ToLowerInvariant()));
|
list.AddRange(reverseResolvedLower);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
resolvedPaths[filePath] = new List<string>(reverse[i].Select(c => c.ToLowerInvariant()).ToList());
|
resolvedPaths[filePath] = new List<string>(reverseResolvedLower);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using LightlessSync.API.Data.Enum;
|
|||||||
using LightlessSync.API.Data.Extensions;
|
using LightlessSync.API.Data.Extensions;
|
||||||
using LightlessSync.FileCache;
|
using LightlessSync.FileCache;
|
||||||
using LightlessSync.Interop.Ipc;
|
using LightlessSync.Interop.Ipc;
|
||||||
|
using LightlessSync.LightlessConfiguration;
|
||||||
using LightlessSync.PlayerData.Factories;
|
using LightlessSync.PlayerData.Factories;
|
||||||
using LightlessSync.PlayerData.Handlers;
|
using LightlessSync.PlayerData.Handlers;
|
||||||
using LightlessSync.Services;
|
using LightlessSync.Services;
|
||||||
@@ -38,6 +39,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
private readonly ActorObjectService _actorObjectService;
|
private readonly ActorObjectService _actorObjectService;
|
||||||
private readonly FileDownloadManager _downloadManager;
|
private readonly FileDownloadManager _downloadManager;
|
||||||
private readonly FileCacheManager _fileDbManager;
|
private readonly FileCacheManager _fileDbManager;
|
||||||
|
private readonly PlayerPerformanceConfigService _playerPerformanceConfigService;
|
||||||
private readonly GameObjectHandlerFactory _gameObjectHandlerFactory;
|
private readonly GameObjectHandlerFactory _gameObjectHandlerFactory;
|
||||||
private readonly IpcManager _ipcManager;
|
private readonly IpcManager _ipcManager;
|
||||||
private readonly IHostApplicationLifetime _lifetime;
|
private readonly IHostApplicationLifetime _lifetime;
|
||||||
@@ -197,6 +199,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
ActorObjectService actorObjectService,
|
ActorObjectService actorObjectService,
|
||||||
IHostApplicationLifetime lifetime,
|
IHostApplicationLifetime lifetime,
|
||||||
FileCacheManager fileDbManager,
|
FileCacheManager fileDbManager,
|
||||||
|
PlayerPerformanceConfigService playerPerformanceConfigService,
|
||||||
PlayerPerformanceService playerPerformanceService,
|
PlayerPerformanceService playerPerformanceService,
|
||||||
PairProcessingLimiter pairProcessingLimiter,
|
PairProcessingLimiter pairProcessingLimiter,
|
||||||
ServerConfigurationManager serverConfigManager,
|
ServerConfigurationManager serverConfigManager,
|
||||||
@@ -217,6 +220,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
_actorObjectService = actorObjectService;
|
_actorObjectService = actorObjectService;
|
||||||
_lifetime = lifetime;
|
_lifetime = lifetime;
|
||||||
_fileDbManager = fileDbManager;
|
_fileDbManager = fileDbManager;
|
||||||
|
_playerPerformanceConfigService = playerPerformanceConfigService;
|
||||||
_playerPerformanceService = playerPerformanceService;
|
_playerPerformanceService = playerPerformanceService;
|
||||||
_pairProcessingLimiter = pairProcessingLimiter;
|
_pairProcessingLimiter = pairProcessingLimiter;
|
||||||
_serverConfigManager = serverConfigManager;
|
_serverConfigManager = serverConfigManager;
|
||||||
@@ -522,11 +526,31 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
return GetCurrentPairs().Any(predicate);
|
return GetCurrentPairs().Any(predicate);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ShouldSkipDownscale()
|
private bool IsPreferredDirectPair()
|
||||||
{
|
{
|
||||||
return GetCurrentPairs().Any(p => p.IsDirectlyPaired && p.SelfToOtherPermissions.IsSticky());
|
return GetCurrentPairs().Any(p => p.IsDirectlyPaired && p.SelfToOtherPermissions.IsSticky());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool ShouldSkipDownscale()
|
||||||
|
{
|
||||||
|
if (!_playerPerformanceConfigService.Current.SkipTextureDownscaleForPreferredPairs)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return IsPreferredDirectPair();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ShouldSkipDecimation()
|
||||||
|
{
|
||||||
|
if (!_playerPerformanceConfigService.Current.SkipModelDecimationForPreferredPairs)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return IsPreferredDirectPair();
|
||||||
|
}
|
||||||
|
|
||||||
private bool IsPaused()
|
private bool IsPaused()
|
||||||
{
|
{
|
||||||
var pairs = GetCurrentPairs();
|
var pairs = GetCurrentPairs();
|
||||||
@@ -1843,6 +1867,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
bool skipDownscaleForPair = ShouldSkipDownscale();
|
bool skipDownscaleForPair = ShouldSkipDownscale();
|
||||||
|
bool skipDecimationForPair = ShouldSkipDecimation();
|
||||||
var user = GetPrimaryUserData();
|
var user = GetPrimaryUserData();
|
||||||
Dictionary<(string GamePath, string? Hash), string> moddedPaths;
|
Dictionary<(string GamePath, string? Hash), string> moddedPaths;
|
||||||
List<FileReplacementData> missingReplacements = [];
|
List<FileReplacementData> missingReplacements = [];
|
||||||
@@ -1881,7 +1906,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
}
|
}
|
||||||
|
|
||||||
var handlerForDownload = _charaHandler;
|
var handlerForDownload = _charaHandler;
|
||||||
_pairDownloadTask = Task.Run(async () => await _downloadManager.DownloadFiles(handlerForDownload, toDownloadReplacements, downloadToken, skipDownscaleForPair).ConfigureAwait(false));
|
_pairDownloadTask = Task.Run(async () => await _downloadManager.DownloadFiles(handlerForDownload, toDownloadReplacements, downloadToken, skipDownscaleForPair, skipDecimationForPair).ConfigureAwait(false));
|
||||||
|
|
||||||
await _pairDownloadTask.ConfigureAwait(false);
|
await _pairDownloadTask.ConfigureAwait(false);
|
||||||
|
|
||||||
@@ -1904,7 +1929,10 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
{
|
{
|
||||||
await _textureDownscaleService.WaitForPendingJobsAsync(downloadedTextureHashes, downloadToken).ConfigureAwait(false);
|
await _textureDownscaleService.WaitForPendingJobsAsync(downloadedTextureHashes, downloadToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!skipDecimationForPair)
|
||||||
|
{
|
||||||
var downloadedModelHashes = toDownloadReplacements
|
var downloadedModelHashes = toDownloadReplacements
|
||||||
.Where(static replacement => replacement.GamePaths.Any(static path => path.EndsWith(".mdl", StringComparison.OrdinalIgnoreCase)))
|
.Where(static replacement => replacement.GamePaths.Any(static path => path.EndsWith(".mdl", StringComparison.OrdinalIgnoreCase)))
|
||||||
.Select(static replacement => replacement.Hash)
|
.Select(static replacement => replacement.Hash)
|
||||||
@@ -2388,6 +2416,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
ConcurrentDictionary<(string GamePath, string? Hash), string> outputDict = new();
|
ConcurrentDictionary<(string GamePath, string? Hash), string> outputDict = new();
|
||||||
bool hasMigrationChanges = false;
|
bool hasMigrationChanges = false;
|
||||||
bool skipDownscaleForPair = ShouldSkipDownscale();
|
bool skipDownscaleForPair = ShouldSkipDownscale();
|
||||||
|
bool skipDecimationForPair = ShouldSkipDecimation();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -2419,16 +2448,13 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
foreach (var gamePath in item.GamePaths)
|
foreach (var gamePath in item.GamePaths)
|
||||||
{
|
{
|
||||||
var preferredPath = fileCache.ResolvedFilepath;
|
var preferredPath = fileCache.ResolvedFilepath;
|
||||||
if (!skipDownscaleForPair)
|
if (!skipDownscaleForPair && gamePath.EndsWith(".tex", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
if (gamePath.EndsWith(".tex", StringComparison.OrdinalIgnoreCase))
|
preferredPath = _textureDownscaleService.GetPreferredPath(item.Hash, fileCache.ResolvedFilepath);
|
||||||
{
|
}
|
||||||
preferredPath = _textureDownscaleService.GetPreferredPath(item.Hash, fileCache.ResolvedFilepath);
|
else if (!skipDecimationForPair && gamePath.EndsWith(".mdl", StringComparison.OrdinalIgnoreCase))
|
||||||
}
|
{
|
||||||
else if (gamePath.EndsWith(".mdl", StringComparison.OrdinalIgnoreCase))
|
preferredPath = _modelDecimationService.GetPreferredPath(item.Hash, fileCache.ResolvedFilepath);
|
||||||
{
|
|
||||||
preferredPath = _modelDecimationService.GetPreferredPath(item.Hash, fileCache.ResolvedFilepath);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
outputDict[(gamePath, item.Hash)] = preferredPath;
|
outputDict[(gamePath, item.Hash)] = preferredPath;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using LightlessSync.FileCache;
|
using LightlessSync.FileCache;
|
||||||
using LightlessSync.Interop.Ipc;
|
using LightlessSync.Interop.Ipc;
|
||||||
|
using LightlessSync.LightlessConfiguration;
|
||||||
using LightlessSync.PlayerData.Factories;
|
using LightlessSync.PlayerData.Factories;
|
||||||
using LightlessSync.Services;
|
using LightlessSync.Services;
|
||||||
using LightlessSync.Services.ActorTracking;
|
using LightlessSync.Services.ActorTracking;
|
||||||
@@ -27,6 +28,7 @@ internal sealed class PairHandlerAdapterFactory : IPairHandlerAdapterFactory
|
|||||||
private readonly IServiceProvider _serviceProvider;
|
private readonly IServiceProvider _serviceProvider;
|
||||||
private readonly IHostApplicationLifetime _lifetime;
|
private readonly IHostApplicationLifetime _lifetime;
|
||||||
private readonly FileCacheManager _fileCacheManager;
|
private readonly FileCacheManager _fileCacheManager;
|
||||||
|
private readonly PlayerPerformanceConfigService _playerPerformanceConfigService;
|
||||||
private readonly PlayerPerformanceService _playerPerformanceService;
|
private readonly PlayerPerformanceService _playerPerformanceService;
|
||||||
private readonly PairProcessingLimiter _pairProcessingLimiter;
|
private readonly PairProcessingLimiter _pairProcessingLimiter;
|
||||||
private readonly ServerConfigurationManager _serverConfigManager;
|
private readonly ServerConfigurationManager _serverConfigManager;
|
||||||
@@ -49,6 +51,7 @@ internal sealed class PairHandlerAdapterFactory : IPairHandlerAdapterFactory
|
|||||||
IFramework framework,
|
IFramework framework,
|
||||||
IHostApplicationLifetime lifetime,
|
IHostApplicationLifetime lifetime,
|
||||||
FileCacheManager fileCacheManager,
|
FileCacheManager fileCacheManager,
|
||||||
|
PlayerPerformanceConfigService playerPerformanceConfigService,
|
||||||
PlayerPerformanceService playerPerformanceService,
|
PlayerPerformanceService playerPerformanceService,
|
||||||
PairProcessingLimiter pairProcessingLimiter,
|
PairProcessingLimiter pairProcessingLimiter,
|
||||||
ServerConfigurationManager serverConfigManager,
|
ServerConfigurationManager serverConfigManager,
|
||||||
@@ -69,6 +72,7 @@ internal sealed class PairHandlerAdapterFactory : IPairHandlerAdapterFactory
|
|||||||
_framework = framework;
|
_framework = framework;
|
||||||
_lifetime = lifetime;
|
_lifetime = lifetime;
|
||||||
_fileCacheManager = fileCacheManager;
|
_fileCacheManager = fileCacheManager;
|
||||||
|
_playerPerformanceConfigService = playerPerformanceConfigService;
|
||||||
_playerPerformanceService = playerPerformanceService;
|
_playerPerformanceService = playerPerformanceService;
|
||||||
_pairProcessingLimiter = pairProcessingLimiter;
|
_pairProcessingLimiter = pairProcessingLimiter;
|
||||||
_serverConfigManager = serverConfigManager;
|
_serverConfigManager = serverConfigManager;
|
||||||
@@ -98,6 +102,7 @@ internal sealed class PairHandlerAdapterFactory : IPairHandlerAdapterFactory
|
|||||||
actorObjectService,
|
actorObjectService,
|
||||||
_lifetime,
|
_lifetime,
|
||||||
_fileCacheManager,
|
_fileCacheManager,
|
||||||
|
_playerPerformanceConfigService,
|
||||||
_playerPerformanceService,
|
_playerPerformanceService,
|
||||||
_pairProcessingLimiter,
|
_pairProcessingLimiter,
|
||||||
_serverConfigManager,
|
_serverConfigManager,
|
||||||
|
|||||||
@@ -129,6 +129,8 @@ public class PlayerPerformanceService
|
|||||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
|
var skipDecimation = config.SkipModelDecimationForPreferredPairs && pairHandler.IsDirectlyPaired && pairHandler.HasStickyPermissions;
|
||||||
|
|
||||||
foreach (var hash in moddedModelHashes)
|
foreach (var hash in moddedModelHashes)
|
||||||
{
|
{
|
||||||
var tris = await _xivDataAnalyzer.GetTrianglesByHash(hash).ConfigureAwait(false);
|
var tris = await _xivDataAnalyzer.GetTrianglesByHash(hash).ConfigureAwait(false);
|
||||||
@@ -138,7 +140,12 @@ public class PlayerPerformanceService
|
|||||||
var fileEntry = _fileCacheManager.GetFileCacheByHash(hash);
|
var fileEntry = _fileCacheManager.GetFileCacheByHash(hash);
|
||||||
if (fileEntry != null)
|
if (fileEntry != null)
|
||||||
{
|
{
|
||||||
var preferredPath = _modelDecimationService.GetPreferredPath(hash, fileEntry.ResolvedFilepath);
|
var preferredPath = fileEntry.ResolvedFilepath;
|
||||||
|
if (!skipDecimation)
|
||||||
|
{
|
||||||
|
preferredPath = _modelDecimationService.GetPreferredPath(hash, fileEntry.ResolvedFilepath);
|
||||||
|
}
|
||||||
|
|
||||||
if (!string.Equals(preferredPath, fileEntry.ResolvedFilepath, StringComparison.OrdinalIgnoreCase))
|
if (!string.Equals(preferredPath, fileEntry.ResolvedFilepath, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
var decimatedTris = await _xivDataAnalyzer.GetEffectiveTrianglesByHash(hash, preferredPath).ConfigureAwait(false);
|
var decimatedTris = await _xivDataAnalyzer.GetEffectiveTrianglesByHash(hash, preferredPath).ConfigureAwait(false);
|
||||||
@@ -192,7 +199,9 @@ public class PlayerPerformanceService
|
|||||||
public bool ComputeAndAutoPauseOnVRAMUsageThresholds(IPairPerformanceSubject pairHandler, CharacterData charaData, List<DownloadFileTransfer> toDownloadFiles)
|
public bool ComputeAndAutoPauseOnVRAMUsageThresholds(IPairPerformanceSubject pairHandler, CharacterData charaData, List<DownloadFileTransfer> toDownloadFiles)
|
||||||
{
|
{
|
||||||
var config = _playerPerformanceConfigService.Current;
|
var config = _playerPerformanceConfigService.Current;
|
||||||
bool skipDownscale = pairHandler.IsDirectlyPaired && pairHandler.HasStickyPermissions;
|
bool skipDownscale = config.SkipTextureDownscaleForPreferredPairs
|
||||||
|
&& pairHandler.IsDirectlyPaired
|
||||||
|
&& pairHandler.HasStickyPermissions;
|
||||||
|
|
||||||
long vramUsage = 0;
|
long vramUsage = 0;
|
||||||
long effectiveVramUsage = 0;
|
long effectiveVramUsage = 0;
|
||||||
|
|||||||
@@ -3590,6 +3590,14 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
_uiShared.DrawNoteLine("! ", UIColors.Get("LightlessYellow"), new SeStringUtils.RichTextEntry("If disabled, saved + effective VRAM usage information will not work.", UIColors.Get("LightlessYellow")));
|
_uiShared.DrawNoteLine("! ", UIColors.Get("LightlessYellow"), new SeStringUtils.RichTextEntry("If disabled, saved + effective VRAM usage information will not work.", UIColors.Get("LightlessYellow")));
|
||||||
|
|
||||||
|
var skipPreferredDownscale = textureConfig.SkipTextureDownscaleForPreferredPairs;
|
||||||
|
if (ImGui.Checkbox("Skip downscale for preferred/direct pairs", ref skipPreferredDownscale))
|
||||||
|
{
|
||||||
|
textureConfig.SkipTextureDownscaleForPreferredPairs = skipPreferredDownscale;
|
||||||
|
_playerPerformanceConfigService.Save();
|
||||||
|
}
|
||||||
|
_uiShared.DrawHelpText("When enabled, textures for direct pairs with preferred permissions are left untouched.");
|
||||||
|
|
||||||
if (!textureConfig.EnableNonIndexTextureMipTrim && !textureConfig.EnableIndexTextureDownscale)
|
if (!textureConfig.EnableNonIndexTextureMipTrim && !textureConfig.EnableIndexTextureDownscale)
|
||||||
{
|
{
|
||||||
UiSharedService.ColorTextWrapped("Both trimming and downscale are disabled. Lightless will keep original textures regardless of size.", UIColors.Get("DimRed"));
|
UiSharedService.ColorTextWrapped("Both trimming and downscale are disabled. Lightless will keep original textures regardless of size.", UIColors.Get("DimRed"));
|
||||||
@@ -3649,6 +3657,14 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
_uiShared.DrawNoteLine("! ", UIColors.Get("LightlessYellow"), new SeStringUtils.RichTextEntry("If disabled, saved + effective triangle usage information will not work.", UIColors.Get("LightlessYellow")));
|
_uiShared.DrawNoteLine("! ", UIColors.Get("LightlessYellow"), new SeStringUtils.RichTextEntry("If disabled, saved + effective triangle usage information will not work.", UIColors.Get("LightlessYellow")));
|
||||||
|
|
||||||
|
var skipPreferredDecimation = performanceConfig.SkipModelDecimationForPreferredPairs;
|
||||||
|
if (ImGui.Checkbox("Skip decimation for preferred/direct pairs", ref skipPreferredDecimation))
|
||||||
|
{
|
||||||
|
performanceConfig.SkipModelDecimationForPreferredPairs = skipPreferredDecimation;
|
||||||
|
_playerPerformanceConfigService.Save();
|
||||||
|
}
|
||||||
|
_uiShared.DrawHelpText("When enabled, models for direct pairs with preferred permissions are left untouched.");
|
||||||
|
|
||||||
var triangleThreshold = performanceConfig.ModelDecimationTriangleThreshold;
|
var triangleThreshold = performanceConfig.ModelDecimationTriangleThreshold;
|
||||||
ImGui.SetNextItemWidth(300 * ImGuiHelpers.GlobalScale);
|
ImGui.SetNextItemWidth(300 * ImGuiHelpers.GlobalScale);
|
||||||
if (ImGui.SliderInt("Decimate models above", ref triangleThreshold, 10_000, 100_000))
|
if (ImGui.SliderInt("Decimate models above", ref triangleThreshold, 10_000, 100_000))
|
||||||
|
|||||||
@@ -89,12 +89,12 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
CurrentOwnerToken = null;
|
CurrentOwnerToken = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DownloadFiles(GameObjectHandler? gameObject, List<FileReplacementData> fileReplacementDto, CancellationToken ct, bool skipDownscale = false)
|
public async Task DownloadFiles(GameObjectHandler? gameObject, List<FileReplacementData> fileReplacementDto, CancellationToken ct, bool skipDownscale = false, bool skipDecimation = false)
|
||||||
{
|
{
|
||||||
Mediator.Publish(new HaltScanMessage(nameof(DownloadFiles)));
|
Mediator.Publish(new HaltScanMessage(nameof(DownloadFiles)));
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await DownloadFilesInternal(gameObject, fileReplacementDto, ct, skipDownscale).ConfigureAwait(false);
|
await DownloadFilesInternal(gameObject, fileReplacementDto, ct, skipDownscale, skipDecimation).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@@ -498,7 +498,8 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
IReadOnlyDictionary<string, long> rawSizeLookup,
|
IReadOnlyDictionary<string, long> rawSizeLookup,
|
||||||
string downloadLabel,
|
string downloadLabel,
|
||||||
CancellationToken ct,
|
CancellationToken ct,
|
||||||
bool skipDownscale)
|
bool skipDownscale,
|
||||||
|
bool skipDecimation)
|
||||||
{
|
{
|
||||||
SetStatus(downloadStatusKey, DownloadStatus.Decompressing);
|
SetStatus(downloadStatusKey, DownloadStatus.Decompressing);
|
||||||
MarkTransferredFiles(downloadStatusKey, 1);
|
MarkTransferredFiles(downloadStatusKey, 1);
|
||||||
@@ -552,7 +553,7 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
await _fileCompactor.WriteAllBytesAsync(filePath, decompressed, ct).ConfigureAwait(false);
|
await _fileCompactor.WriteAllBytesAsync(filePath, decompressed, ct).ConfigureAwait(false);
|
||||||
PersistFileToStorage(fileHash, filePath, repl.GamePath, skipDownscale);
|
PersistFileToStorage(fileHash, filePath, repl.GamePath, skipDownscale, skipDecimation);
|
||||||
}
|
}
|
||||||
catch (EndOfStreamException)
|
catch (EndOfStreamException)
|
||||||
{
|
{
|
||||||
@@ -638,7 +639,7 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
yield return items.GetRange(i, Math.Min(chunkSize, items.Count - i));
|
yield return items.GetRange(i, Math.Min(chunkSize, items.Count - i));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DownloadFilesInternal(GameObjectHandler? gameObjectHandler, List<FileReplacementData> fileReplacement, CancellationToken ct, bool skipDownscale)
|
private async Task DownloadFilesInternal(GameObjectHandler? gameObjectHandler, List<FileReplacementData> fileReplacement, CancellationToken ct, bool skipDownscale, bool skipDecimation)
|
||||||
{
|
{
|
||||||
var objectName = gameObjectHandler?.Name ?? "Unknown";
|
var objectName = gameObjectHandler?.Name ?? "Unknown";
|
||||||
|
|
||||||
@@ -742,13 +743,13 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
Task batchTask = batchChunks.Length == 0
|
Task batchTask = batchChunks.Length == 0
|
||||||
? Task.CompletedTask
|
? Task.CompletedTask
|
||||||
: Parallel.ForEachAsync(batchChunks, new ParallelOptions { MaxDegreeOfParallelism = workerDop, CancellationToken = ct },
|
: Parallel.ForEachAsync(batchChunks, new ParallelOptions { MaxDegreeOfParallelism = workerDop, CancellationToken = ct },
|
||||||
async (chunk, token) => await ProcessBatchChunkAsync(chunk, replacementLookup, rawSizeLookup, token, skipDownscale).ConfigureAwait(false));
|
async (chunk, token) => await ProcessBatchChunkAsync(chunk, replacementLookup, rawSizeLookup, token, skipDownscale, skipDecimation).ConfigureAwait(false));
|
||||||
|
|
||||||
// direct downloads
|
// direct downloads
|
||||||
Task directTask = directDownloads.Count == 0
|
Task directTask = directDownloads.Count == 0
|
||||||
? Task.CompletedTask
|
? Task.CompletedTask
|
||||||
: Parallel.ForEachAsync(directDownloads, new ParallelOptions { MaxDegreeOfParallelism = workerDop, CancellationToken = ct },
|
: Parallel.ForEachAsync(directDownloads, new ParallelOptions { MaxDegreeOfParallelism = workerDop, CancellationToken = ct },
|
||||||
async (d, token) => await ProcessDirectAsync(d, replacementLookup, rawSizeLookup, token, skipDownscale).ConfigureAwait(false));
|
async (d, token) => await ProcessDirectAsync(d, replacementLookup, rawSizeLookup, token, skipDownscale, skipDecimation).ConfigureAwait(false));
|
||||||
|
|
||||||
await Task.WhenAll(batchTask, directTask).ConfigureAwait(false);
|
await Task.WhenAll(batchTask, directTask).ConfigureAwait(false);
|
||||||
|
|
||||||
@@ -761,7 +762,8 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
Dictionary<string, (string Extension, string GamePath)> replacementLookup,
|
Dictionary<string, (string Extension, string GamePath)> replacementLookup,
|
||||||
IReadOnlyDictionary<string, long> rawSizeLookup,
|
IReadOnlyDictionary<string, long> rawSizeLookup,
|
||||||
CancellationToken ct,
|
CancellationToken ct,
|
||||||
bool skipDownscale)
|
bool skipDownscale,
|
||||||
|
bool skipDecimation)
|
||||||
{
|
{
|
||||||
var statusKey = chunk.StatusKey;
|
var statusKey = chunk.StatusKey;
|
||||||
|
|
||||||
@@ -795,7 +797,7 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await DecompressBlockFileAsync(statusKey, blockFile, replacementLookup, rawSizeLookup, fi.Name, ct, skipDownscale).ConfigureAwait(false);
|
await DecompressBlockFileAsync(statusKey, blockFile, replacementLookup, rawSizeLookup, fi.Name, ct, skipDownscale, skipDecimation).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
@@ -817,7 +819,8 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
Dictionary<string, (string Extension, string GamePath)> replacementLookup,
|
Dictionary<string, (string Extension, string GamePath)> replacementLookup,
|
||||||
IReadOnlyDictionary<string, long> rawSizeLookup,
|
IReadOnlyDictionary<string, long> rawSizeLookup,
|
||||||
CancellationToken ct,
|
CancellationToken ct,
|
||||||
bool skipDownscale)
|
bool skipDownscale,
|
||||||
|
bool skipDecimation)
|
||||||
{
|
{
|
||||||
var progress = CreateInlineProgress(bytes =>
|
var progress = CreateInlineProgress(bytes =>
|
||||||
{
|
{
|
||||||
@@ -827,7 +830,7 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
if (!ShouldUseDirectDownloads() || string.IsNullOrEmpty(directDownload.DirectDownloadUrl))
|
if (!ShouldUseDirectDownloads() || string.IsNullOrEmpty(directDownload.DirectDownloadUrl))
|
||||||
{
|
{
|
||||||
await ProcessDirectAsQueuedFallbackAsync(directDownload, replacementLookup, rawSizeLookup, progress, ct, skipDownscale).ConfigureAwait(false);
|
await ProcessDirectAsQueuedFallbackAsync(directDownload, replacementLookup, rawSizeLookup, progress, ct, skipDownscale, skipDecimation).ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -875,7 +878,7 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
await _fileCompactor.WriteAllBytesAsync(finalFilename, decompressedBytes, ct).ConfigureAwait(false);
|
await _fileCompactor.WriteAllBytesAsync(finalFilename, decompressedBytes, ct).ConfigureAwait(false);
|
||||||
PersistFileToStorage(directDownload.Hash, finalFilename, repl.GamePath, skipDownscale);
|
PersistFileToStorage(directDownload.Hash, finalFilename, repl.GamePath, skipDownscale, skipDecimation);
|
||||||
|
|
||||||
MarkTransferredFiles(directDownload.DirectDownloadUrl!, 1);
|
MarkTransferredFiles(directDownload.DirectDownloadUrl!, 1);
|
||||||
SetStatus(directDownload.DirectDownloadUrl!, DownloadStatus.Completed);
|
SetStatus(directDownload.DirectDownloadUrl!, DownloadStatus.Completed);
|
||||||
@@ -902,7 +905,7 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await ProcessDirectAsQueuedFallbackAsync(directDownload, replacementLookup, rawSizeLookup, progress, ct, skipDownscale).ConfigureAwait(false);
|
await ProcessDirectAsQueuedFallbackAsync(directDownload, replacementLookup, rawSizeLookup, progress, ct, skipDownscale, skipDecimation).ConfigureAwait(false);
|
||||||
|
|
||||||
if (!expectedDirectDownloadFailure && failureCount >= 3 && !_disableDirectDownloads)
|
if (!expectedDirectDownloadFailure && failureCount >= 3 && !_disableDirectDownloads)
|
||||||
{
|
{
|
||||||
@@ -932,7 +935,8 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
IReadOnlyDictionary<string, long> rawSizeLookup,
|
IReadOnlyDictionary<string, long> rawSizeLookup,
|
||||||
IProgress<long> progress,
|
IProgress<long> progress,
|
||||||
CancellationToken ct,
|
CancellationToken ct,
|
||||||
bool skipDownscale)
|
bool skipDownscale,
|
||||||
|
bool skipDecimation)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(directDownload.DirectDownloadUrl))
|
if (string.IsNullOrEmpty(directDownload.DirectDownloadUrl))
|
||||||
throw new InvalidOperationException("Direct download fallback requested without a direct download URL.");
|
throw new InvalidOperationException("Direct download fallback requested without a direct download URL.");
|
||||||
@@ -957,7 +961,7 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
if (!File.Exists(blockFile))
|
if (!File.Exists(blockFile))
|
||||||
throw new FileNotFoundException("Block file missing after direct download fallback.", blockFile);
|
throw new FileNotFoundException("Block file missing after direct download fallback.", blockFile);
|
||||||
|
|
||||||
await DecompressBlockFileAsync(statusKey, blockFile, replacementLookup, rawSizeLookup, $"fallback-{directDownload.Hash}", ct, skipDownscale)
|
await DecompressBlockFileAsync(statusKey, blockFile, replacementLookup, rawSizeLookup, $"fallback-{directDownload.Hash}", ct, skipDownscale, skipDecimation)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@@ -986,7 +990,7 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
return await response.Content.ReadFromJsonAsync<List<DownloadFileDto>>(cancellationToken: ct).ConfigureAwait(false) ?? [];
|
return await response.Content.ReadFromJsonAsync<List<DownloadFileDto>>(cancellationToken: ct).ConfigureAwait(false) ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PersistFileToStorage(string fileHash, string filePath, string gamePath, bool skipDownscale)
|
private void PersistFileToStorage(string fileHash, string filePath, string gamePath, bool skipDownscale, bool skipDecimation)
|
||||||
{
|
{
|
||||||
var fi = new FileInfo(filePath);
|
var fi = new FileInfo(filePath);
|
||||||
|
|
||||||
@@ -1014,7 +1018,7 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
() => _textureMetadataHelper.DetermineMapKind(gamePath, filePath));
|
() => _textureMetadataHelper.DetermineMapKind(gamePath, filePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!skipDownscale && _modelDecimationService.ShouldScheduleDecimation(fileHash, filePath, gamePath))
|
if (!skipDecimation && _modelDecimationService.ShouldScheduleDecimation(fileHash, filePath, gamePath))
|
||||||
{
|
{
|
||||||
_modelDecimationService.ScheduleDecimation(fileHash, filePath, gamePath);
|
_modelDecimationService.ScheduleDecimation(fileHash, filePath, gamePath);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user