remove aggressive memory optimizations option

This commit is contained in:
Jesse Plamondon-Willard 2022-05-04 21:02:41 -04:00
parent c1342bd4cd
commit 295ad29b8d
No known key found for this signature in database
GPG Key ID: CF8B1456B3E29F49
10 changed files with 27 additions and 106 deletions

View File

@ -3,8 +3,10 @@
# Release notes
## Upcoming release
* For players:
* Case-insensitive file paths (introduced in 3.14.0) are now disabled by default.
_You can enable them via `smapi-internal/config.json` if needed. These will be re-enabled in a later version after reworking them to reduce performance impact._
* Disabled case-insensitive file paths (introduced in 3.14.0) by default.
_You can enable them by editing `smapi-internal/config.json` if needed. They'll be re-enabled in a later version after they're reworked to reduce performance impact._
* Removed experimental 'aggressive memory optimizations' option.
_This was disabled by default and is no longer needed in most cases. Memory usage will be better reduced by reworked asset propagation in the upcoming SMAPI 4.0.0._
* Updated compatibility list.
## 3.14.0

View File

@ -32,9 +32,6 @@ namespace StardewModdingAPI.Framework
/// <summary>An asset key prefix for assets from SMAPI mod folders.</summary>
private readonly string ManagedPrefix = "SMAPI";
/// <summary>Whether to enable more aggressive memory optimizations.</summary>
private readonly bool AggressiveMemoryOptimizations;
/// <summary>Get a file path lookup for the given directory.</summary>
private readonly Func<string, IFilePathLookup> GetFilePathLookup;
@ -118,13 +115,11 @@ namespace StardewModdingAPI.Framework
/// <param name="jsonHelper">Encapsulates SMAPI's JSON file parsing.</param>
/// <param name="onLoadingFirstAsset">A callback to invoke the first time *any* game content manager loads an asset.</param>
/// <param name="onAssetLoaded">A callback to invoke when an asset is fully loaded.</param>
/// <param name="aggressiveMemoryOptimizations">Whether to enable more aggressive memory optimizations.</param>
/// <param name="getFilePathLookup">Get a file path lookup for the given directory.</param>
/// <param name="onAssetsInvalidated">A callback to invoke when any asset names have been invalidated from the cache.</param>
/// <param name="requestAssetOperations">Get the load/edit operations to apply to an asset by querying registered <see cref="IContentEvents.AssetRequested"/> event handlers.</param>
public ContentCoordinator(IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, IMonitor monitor, Reflector reflection, JsonHelper jsonHelper, Action onLoadingFirstAsset, Action<BaseContentManager, IAssetName> onAssetLoaded, bool aggressiveMemoryOptimizations, Func<string, IFilePathLookup> getFilePathLookup, Action<IList<IAssetName>> onAssetsInvalidated, Func<IAssetInfo, IList<AssetOperationGroup>> requestAssetOperations)
public ContentCoordinator(IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, IMonitor monitor, Reflector reflection, JsonHelper jsonHelper, Action onLoadingFirstAsset, Action<BaseContentManager, IAssetName> onAssetLoaded, Func<string, IFilePathLookup> getFilePathLookup, Action<IList<IAssetName>> onAssetsInvalidated, Func<IAssetInfo, IList<AssetOperationGroup>> requestAssetOperations)
{
this.AggressiveMemoryOptimizations = aggressiveMemoryOptimizations;
this.GetFilePathLookup = getFilePathLookup;
this.Monitor = monitor ?? throw new ArgumentNullException(nameof(monitor));
this.Reflection = reflection;
@ -145,26 +140,11 @@ namespace StardewModdingAPI.Framework
reflection: reflection,
onDisposing: this.OnDisposing,
onLoadingFirstAsset: onLoadingFirstAsset,
onAssetLoaded: onAssetLoaded,
aggressiveMemoryOptimizations: aggressiveMemoryOptimizations
onAssetLoaded: onAssetLoaded
)
);
var contentManagerForAssetPropagation = new GameContentManagerForAssetPropagation(
name: nameof(GameContentManagerForAssetPropagation),
serviceProvider: serviceProvider,
rootDirectory: rootDirectory,
currentCulture: currentCulture,
coordinator: this,
monitor: monitor,
reflection: reflection,
onDisposing: this.OnDisposing,
onLoadingFirstAsset: onLoadingFirstAsset,
onAssetLoaded: onAssetLoaded,
aggressiveMemoryOptimizations: aggressiveMemoryOptimizations
);
this.ContentManagers.Add(contentManagerForAssetPropagation);
this.VanillaContentManager = new LocalizedContentManager(serviceProvider, rootDirectory);
this.CoreAssets = new CoreAssetPropagator(this.MainContentManager, contentManagerForAssetPropagation, this.Monitor, reflection, aggressiveMemoryOptimizations, name => this.ParseAssetName(name, allowLocales: true));
this.CoreAssets = new CoreAssetPropagator(this.MainContentManager, this.Monitor, reflection, name => this.ParseAssetName(name, allowLocales: true));
this.LocaleCodes = new Lazy<Dictionary<string, LocalizedContentManager.LanguageCode>>(() => this.GetLocaleCodes(customLanguages: Enumerable.Empty<ModLanguage>()));
}
@ -184,8 +164,7 @@ namespace StardewModdingAPI.Framework
reflection: this.Reflection,
onDisposing: this.OnDisposing,
onLoadingFirstAsset: this.OnLoadingFirstAsset,
onAssetLoaded: this.OnAssetLoaded,
aggressiveMemoryOptimizations: this.AggressiveMemoryOptimizations
onAssetLoaded: this.OnAssetLoaded
);
this.ContentManagers.Add(manager);
return manager;
@ -213,7 +192,6 @@ namespace StardewModdingAPI.Framework
reflection: this.Reflection,
jsonHelper: this.JsonHelper,
onDisposing: this.OnDisposing,
aggressiveMemoryOptimizations: this.AggressiveMemoryOptimizations,
relativePathLookup: this.GetFilePathLookup(rootDirectory)
);
this.ContentManagers.Add(manager);

View File

@ -11,7 +11,6 @@ using StardewModdingAPI.Framework.Content;
using StardewModdingAPI.Framework.Exceptions;
using StardewModdingAPI.Framework.Reflection;
using StardewValley;
using xTile;
namespace StardewModdingAPI.Framework.ContentManagers
{
@ -33,9 +32,6 @@ namespace StardewModdingAPI.Framework.ContentManagers
/// <summary>Simplifies access to private code.</summary>
protected readonly Reflector Reflection;
/// <summary>Whether to enable more aggressive memory optimizations.</summary>
protected readonly bool AggressiveMemoryOptimizations;
/// <summary>Whether to automatically try resolving keys to a localized form if available.</summary>
protected bool TryLocalizeKeys = true;
@ -82,8 +78,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
/// <param name="reflection">Simplifies access to private code.</param>
/// <param name="onDisposing">A callback to invoke when the content manager is being disposed.</param>
/// <param name="isNamespaced">Whether this content manager handles managed asset keys (e.g. to load assets from a mod folder).</param>
/// <param name="aggressiveMemoryOptimizations">Whether to enable more aggressive memory optimizations.</param>
protected BaseContentManager(string name, IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, Action<BaseContentManager> onDisposing, bool isNamespaced, bool aggressiveMemoryOptimizations)
protected BaseContentManager(string name, IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, Action<BaseContentManager> onDisposing, bool isNamespaced)
: base(serviceProvider, rootDirectory, currentCulture)
{
// init
@ -95,7 +90,6 @@ namespace StardewModdingAPI.Framework.ContentManagers
this.Reflection = reflection;
this.OnDisposing = onDisposing;
this.IsNamespaced = isNamespaced;
this.AggressiveMemoryOptimizations = aggressiveMemoryOptimizations;
// get asset data
this.BaseDisposableReferences = reflection.GetField<List<IDisposable>?>(this, "disposableAssets").GetValue()
@ -231,14 +225,6 @@ namespace StardewModdingAPI.Framework.ContentManagers
removeAssets[baseAssetName] = asset;
remove = true;
}
// dispose if safe
if (remove && this.AggressiveMemoryOptimizations)
{
if (asset is Map map)
map.DisposeTileSheets(Game1.mapDisplayDevice);
}
return remove;
}, dispose);

View File

@ -51,9 +51,8 @@ namespace StardewModdingAPI.Framework.ContentManagers
/// <param name="onDisposing">A callback to invoke when the content manager is being disposed.</param>
/// <param name="onLoadingFirstAsset">A callback to invoke the first time *any* game content manager loads an asset.</param>
/// <param name="onAssetLoaded">A callback to invoke when an asset is fully loaded.</param>
/// <param name="aggressiveMemoryOptimizations">Whether to enable more aggressive memory optimizations.</param>
public GameContentManager(string name, IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, Action<BaseContentManager> onDisposing, Action onLoadingFirstAsset, Action<BaseContentManager, IAssetName> onAssetLoaded, bool aggressiveMemoryOptimizations)
: base(name, serviceProvider, rootDirectory, currentCulture, coordinator, monitor, reflection, onDisposing, isNamespaced: false, aggressiveMemoryOptimizations: aggressiveMemoryOptimizations)
public GameContentManager(string name, IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, Action<BaseContentManager> onDisposing, Action onLoadingFirstAsset, Action<BaseContentManager, IAssetName> onAssetLoaded)
: base(name, serviceProvider, rootDirectory, currentCulture, coordinator, monitor, reflection, onDisposing, isNamespaced: false)
{
this.OnLoadingFirstAsset = onLoadingFirstAsset;
this.OnAssetLoaded = onAssetLoaded;

View File

@ -21,8 +21,8 @@ namespace StardewModdingAPI.Framework.ContentManagers
** Public methods
*********/
/// <inheritdoc />
public GameContentManagerForAssetPropagation(string name, IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, Action<BaseContentManager> onDisposing, Action onLoadingFirstAsset, Action<BaseContentManager, IAssetName> onAssetLoaded, bool aggressiveMemoryOptimizations)
: base(name, serviceProvider, rootDirectory, currentCulture, coordinator, monitor, reflection, onDisposing, onLoadingFirstAsset, onAssetLoaded, aggressiveMemoryOptimizations) { }
public GameContentManagerForAssetPropagation(string name, IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, Action<BaseContentManager> onDisposing, Action onLoadingFirstAsset, Action<BaseContentManager, IAssetName> onAssetLoaded)
: base(name, serviceProvider, rootDirectory, currentCulture, coordinator, monitor, reflection, onDisposing, onLoadingFirstAsset, onAssetLoaded) { }
/// <inheritdoc />
public override T LoadExact<T>(IAssetName assetName, bool useCache)

View File

@ -55,10 +55,9 @@ namespace StardewModdingAPI.Framework.ContentManagers
/// <param name="reflection">Simplifies access to private code.</param>
/// <param name="jsonHelper">Encapsulates SMAPI's JSON file parsing.</param>
/// <param name="onDisposing">A callback to invoke when the content manager is being disposed.</param>
/// <param name="aggressiveMemoryOptimizations">Whether to enable more aggressive memory optimizations.</param>
/// <param name="relativePathLookup">A lookup for relative paths within the <paramref name="rootDirectory"/>.</param>
public ModContentManager(string name, IContentManager gameContentManager, IServiceProvider serviceProvider, string modName, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, JsonHelper jsonHelper, Action<BaseContentManager> onDisposing, bool aggressiveMemoryOptimizations, IFilePathLookup relativePathLookup)
: base(name, serviceProvider, rootDirectory, currentCulture, coordinator, monitor, reflection, onDisposing, isNamespaced: true, aggressiveMemoryOptimizations: aggressiveMemoryOptimizations)
public ModContentManager(string name, IContentManager gameContentManager, IServiceProvider serviceProvider, string modName, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, JsonHelper jsonHelper, Action<BaseContentManager> onDisposing, IFilePathLookup relativePathLookup)
: base(name, serviceProvider, rootDirectory, currentCulture, coordinator, monitor, reflection, onDisposing, isNamespaced: true)
{
this.GameContentManager = gameContentManager;
this.RelativePathLookup = relativePathLookup;

View File

@ -22,7 +22,6 @@ namespace StardewModdingAPI.Framework.Models
[nameof(VerboseLogging)] = false,
[nameof(LogNetworkTraffic)] = false,
[nameof(RewriteMods)] = true,
[nameof(AggressiveMemoryOptimizations)] = false,
[nameof(UsePintail)] = true,
[nameof(UseCaseInsensitivePaths)] = false
};
@ -63,9 +62,6 @@ namespace StardewModdingAPI.Framework.Models
/// <summary>Whether SMAPI should rewrite mods for compatibility.</summary>
public bool RewriteMods { get; }
/// <summary>Whether to enable more aggressive memory optimizations.</summary>
public bool AggressiveMemoryOptimizations { get; }
/// <summary>Whether to use the experimental Pintail API proxying library, instead of the original proxying built into SMAPI itself.</summary>
public bool UsePintail { get; }
@ -94,13 +90,12 @@ namespace StardewModdingAPI.Framework.Models
/// <param name="webApiBaseUrl">The base URL for SMAPI's web API, used to perform update checks.</param>
/// <param name="verboseLogging">Whether SMAPI should log more information about the game context.</param>
/// <param name="rewriteMods">Whether SMAPI should rewrite mods for compatibility.</param>
/// <param name="aggressiveMemoryOptimizations">Whether to enable more aggressive memory optimizations.</param>
/// <param name="usePintail">Whether to use the experimental Pintail API proxying library, instead of the original proxying built into SMAPI itself.</param>
/// <param name="useCaseInsensitivePaths">>Whether to make SMAPI file APIs case-insensitive, even on Linux.</param>
/// <param name="logNetworkTraffic">Whether SMAPI should log network traffic.</param>
/// <param name="consoleColors">The colors to use for text written to the SMAPI console.</param>
/// <param name="suppressUpdateChecks">The mod IDs SMAPI should ignore when performing update checks or validating update keys.</param>
public SConfig(bool developerMode, bool checkForUpdates, bool? paranoidWarnings, bool? useBetaChannel, string gitHubProjectName, string webApiBaseUrl, bool verboseLogging, bool? rewriteMods, bool? aggressiveMemoryOptimizations, bool? usePintail, bool? useCaseInsensitivePaths, bool logNetworkTraffic, ColorSchemeConfig consoleColors, string[]? suppressUpdateChecks)
public SConfig(bool developerMode, bool checkForUpdates, bool? paranoidWarnings, bool? useBetaChannel, string gitHubProjectName, string webApiBaseUrl, bool verboseLogging, bool? rewriteMods, bool? usePintail, bool? useCaseInsensitivePaths, bool logNetworkTraffic, ColorSchemeConfig consoleColors, string[]? suppressUpdateChecks)
{
this.DeveloperMode = developerMode;
this.CheckForUpdates = checkForUpdates;
@ -110,7 +105,6 @@ namespace StardewModdingAPI.Framework.Models
this.WebApiBaseUrl = webApiBaseUrl;
this.VerboseLogging = verboseLogging;
this.RewriteMods = rewriteMods ?? (bool)SConfig.DefaultValues[nameof(SConfig.RewriteMods)];
this.AggressiveMemoryOptimizations = aggressiveMemoryOptimizations ?? (bool)SConfig.DefaultValues[nameof(SConfig.AggressiveMemoryOptimizations)];
this.UsePintail = usePintail ?? (bool)SConfig.DefaultValues[nameof(SConfig.UsePintail)];
this.UseCaseInsensitivePaths = useCaseInsensitivePaths ?? (bool)SConfig.DefaultValues[nameof(SConfig.UseCaseInsensitivePaths)];
this.LogNetworkTraffic = logNetworkTraffic;

View File

@ -1253,7 +1253,6 @@ namespace StardewModdingAPI.Framework
onLoadingFirstAsset: this.InitializeBeforeFirstAssetLoaded,
onAssetLoaded: this.OnAssetLoaded,
onAssetsInvalidated: this.OnAssetsInvalidated,
aggressiveMemoryOptimizations: this.Settings.AggressiveMemoryOptimizations,
getFilePathLookup: this.GetFilePathLookup,
requestAssetOperations: this.RequestAssetOperations
);

View File

@ -5,7 +5,6 @@ using System.IO;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using StardewModdingAPI.Framework.ContentManagers;
using StardewModdingAPI.Framework.Reflection;
using StardewModdingAPI.Internal;
using StardewModdingAPI.Toolkit.Utilities;
@ -33,18 +32,12 @@ namespace StardewModdingAPI.Metadata
/// <summary>The main content manager through which to reload assets.</summary>
private readonly LocalizedContentManager MainContentManager;
/// <summary>An internal content manager used only for asset propagation. See remarks on <see cref="GameContentManagerForAssetPropagation"/>.</summary>
private readonly GameContentManagerForAssetPropagation DisposableContentManager;
/// <summary>Writes messages to the console.</summary>
private readonly IMonitor Monitor;
/// <summary>Simplifies access to private game code.</summary>
private readonly Reflector Reflection;
/// <summary>Whether to enable more aggressive memory optimizations.</summary>
private readonly bool AggressiveMemoryOptimizations;
/// <summary>Parse a raw asset name.</summary>
private readonly Func<string, IAssetName> ParseAssetName;
@ -67,18 +60,14 @@ namespace StardewModdingAPI.Metadata
*********/
/// <summary>Initialize the core asset data.</summary>
/// <param name="mainContent">The main content manager through which to reload assets.</param>
/// <param name="disposableContent">An internal content manager used only for asset propagation.</param>
/// <param name="monitor">Writes messages to the console.</param>
/// <param name="reflection">Simplifies access to private code.</param>
/// <param name="aggressiveMemoryOptimizations">Whether to enable more aggressive memory optimizations.</param>
/// <param name="parseAssetName">Parse a raw asset name.</param>
public CoreAssetPropagator(LocalizedContentManager mainContent, GameContentManagerForAssetPropagation disposableContent, IMonitor monitor, Reflector reflection, bool aggressiveMemoryOptimizations, Func<string, IAssetName> parseAssetName)
public CoreAssetPropagator(LocalizedContentManager mainContent, IMonitor monitor, Reflector reflection, Func<string, IAssetName> parseAssetName)
{
this.MainContentManager = mainContent;
this.DisposableContentManager = disposableContent;
this.Monitor = monitor;
this.Reflection = reflection;
this.AggressiveMemoryOptimizations = aggressiveMemoryOptimizations;
this.ParseAssetName = parseAssetName;
}
@ -230,7 +219,7 @@ namespace StardewModdingAPI.Metadata
** Buildings
****/
case "buildings/houses": // Farm
Farm.houseTextures = this.LoadAndDisposeIfNeeded(Farm.houseTextures, key);
Farm.houseTextures = this.LoadTexture(key);
return true;
case "buildings/houses_paintmask": // Farm
@ -247,7 +236,7 @@ namespace StardewModdingAPI.Metadata
** Content\Characters\Farmer
****/
case "characters/farmer/accessories": // Game1.LoadContent
FarmerRenderer.accessoriesTexture = this.LoadAndDisposeIfNeeded(FarmerRenderer.accessoriesTexture, key);
FarmerRenderer.accessoriesTexture = this.LoadTexture(key);
return true;
case "characters/farmer/farmer_base": // Farmer
@ -257,19 +246,19 @@ namespace StardewModdingAPI.Metadata
return !ignoreWorld && this.ReloadPlayerSprites(assetName);
case "characters/farmer/hairstyles": // Game1.LoadContent
FarmerRenderer.hairStylesTexture = this.LoadAndDisposeIfNeeded(FarmerRenderer.hairStylesTexture, key);
FarmerRenderer.hairStylesTexture = this.LoadTexture(key);
return true;
case "characters/farmer/hats": // Game1.LoadContent
FarmerRenderer.hatsTexture = this.LoadAndDisposeIfNeeded(FarmerRenderer.hatsTexture, key);
FarmerRenderer.hatsTexture = this.LoadTexture(key);
return true;
case "characters/farmer/pants": // Game1.LoadContent
FarmerRenderer.pantsTexture = this.LoadAndDisposeIfNeeded(FarmerRenderer.pantsTexture, key);
FarmerRenderer.pantsTexture = this.LoadTexture(key);
return true;
case "characters/farmer/shirts": // Game1.LoadContent
FarmerRenderer.shirtsTexture = this.LoadAndDisposeIfNeeded(FarmerRenderer.shirtsTexture, key);
FarmerRenderer.shirtsTexture = this.LoadTexture(key);
return true;
/****
@ -905,9 +894,6 @@ namespace StardewModdingAPI.Metadata
GameLocation location = locationInfo.Location;
Vector2? playerPos = Game1.player?.Position;
if (this.AggressiveMemoryOptimizations)
location.map.DisposeTileSheets(Game1.mapDisplayDevice);
// reload map
location.interiorDoors.Clear(); // prevent errors when doors try to update tiles which no longer exist
location.reloadMap();
@ -973,7 +959,7 @@ namespace StardewModdingAPI.Metadata
// update sprite
foreach (var target in characters)
{
target.Npc.Sprite.spriteTexture = this.LoadAndDisposeIfNeeded(target.Npc.Sprite.spriteTexture, target.AssetName.BaseName);
target.Npc.Sprite.spriteTexture = this.LoadTexture(target.AssetName.BaseName);
propagated[target.AssetName] = true;
}
}
@ -1012,7 +998,7 @@ namespace StardewModdingAPI.Metadata
// update portrait
foreach (var target in characters)
{
target.Npc.Portrait = this.LoadAndDisposeIfNeeded(target.Npc.Portrait, target.AssetName.BaseName);
target.Npc.Portrait = this.LoadTexture(target.AssetName.BaseName);
propagated[target.AssetName] = true;
}
}
@ -1284,25 +1270,10 @@ namespace StardewModdingAPI.Metadata
: Array.Empty<string>();
}
/// <summary>Load a texture, and dispose the old one if <see cref="AggressiveMemoryOptimizations"/> is enabled and it's different from the new instance.</summary>
/// <param name="oldTexture">The previous texture to dispose.</param>
/// <summary>Load a texture from the main content manager.</summary>
/// <param name="key">The asset key to load.</param>
private Texture2D LoadAndDisposeIfNeeded(Texture2D? oldTexture, string key)
private Texture2D LoadTexture(string key)
{
// if aggressive memory optimizations are enabled, load the asset from the disposable
// content manager and dispose the old instance if needed.
if (this.AggressiveMemoryOptimizations)
{
GameContentManagerForAssetPropagation content = this.DisposableContentManager;
Texture2D newTexture = content.Load<Texture2D>(key);
if (oldTexture?.IsDisposed == false && !object.ReferenceEquals(oldTexture, newTexture) && content.IsResponsibleFor(oldTexture))
oldTexture.Dispose();
return newTexture;
}
// else just (re)load it from the main content manager
return this.MainContentManager.Load<Texture2D>(key);
}

View File

@ -39,13 +39,6 @@ copy all the settings, or you may cause bugs due to overridden changes in future
*/
"RewriteMods": true,
/**
* Whether to enable more aggressive memory optimizations.
* If you get frequent 'OutOfMemoryException' errors, you can try enabling this to reduce their
* frequency. This may cause crashes for farmhands in multiplayer.
*/
"AggressiveMemoryOptimizations": false,
/**
* Whether to make SMAPI file APIs case-insensitive, even on Linux.
* This is experimental, and the initial implementation may impact load times.