mirror of
https://github.com/SK-la/Ez2Lazer.git
synced 2026-03-13 11:20:28 +00:00
[内存调试]解决延迟追踪和虚拟音轨的内存泄露风险。
This commit is contained in:
@@ -95,5 +95,17 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
|
||||
// }
|
||||
|
||||
// private void invalidateLayout() => layout.Invalidate();
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
if (isDisposing)
|
||||
{
|
||||
noteSetName.UnbindBindings();
|
||||
hitPositonBindable.UnbindBindings();
|
||||
columnWidth.UnbindBindings();
|
||||
}
|
||||
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,5 +222,17 @@ namespace osu.Game.Rulesets.Mania.Skinning.EzStylePro
|
||||
TANOc2,
|
||||
TECHNIKA,
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
if (isDisposing)
|
||||
{
|
||||
stageName.UnbindBindings();
|
||||
hitPositonBindable.UnbindBindings();
|
||||
noteSize.UnbindBindings();
|
||||
}
|
||||
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,6 +217,11 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
NoteSizeBindable.ValueChanged -= onNoteSizeBindableChanged;
|
||||
ezConfig.OnNoteColourChanged -= onNoteColourChanged;
|
||||
}
|
||||
|
||||
NoteSetBindable.UnbindBindings();
|
||||
NoteSizeBindable.UnbindBindings();
|
||||
EzColumnColourBindable.UnbindBindings();
|
||||
hitModeBindable.UnbindBindings();
|
||||
}
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||
|
||||
@@ -161,13 +161,7 @@ namespace osu.Game.LAsEzExtensions.Audio
|
||||
beatmapTrackMuteAdjustment = null;
|
||||
mutedOriginalTrack = null;
|
||||
|
||||
// 停止并释放候选轨(若为独立实例)
|
||||
if (activeCandidateTrack != null)
|
||||
{
|
||||
activeCandidateTrack.Stop();
|
||||
activeCandidateTrack = null;
|
||||
ownsCandidateTrack = false;
|
||||
}
|
||||
releaseActiveCandidateTrack();
|
||||
|
||||
lastLoopStartGameplayTime = null;
|
||||
|
||||
@@ -181,6 +175,36 @@ namespace osu.Game.LAsEzExtensions.Audio
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
|
||||
private void releaseActiveCandidateTrack()
|
||||
{
|
||||
if (activeCandidateTrack == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
activeCandidateTrack.Stop();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log($"failed to stop active candidate track during release: {ex}");
|
||||
}
|
||||
|
||||
if (ownsCandidateTrack)
|
||||
{
|
||||
try
|
||||
{
|
||||
activeCandidateTrack.Dispose();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log($"failed to dispose owned candidate track: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
activeCandidateTrack = null;
|
||||
ownsCandidateTrack = false;
|
||||
}
|
||||
|
||||
protected override void UpdateAfterChildren()
|
||||
{
|
||||
base.UpdateAfterChildren();
|
||||
@@ -771,13 +795,7 @@ namespace osu.Game.LAsEzExtensions.Audio
|
||||
log($"restored candidate track.Looping to {prevCandidateLooping.Value}");
|
||||
}
|
||||
|
||||
if (ownsCandidateTrack)
|
||||
{
|
||||
// do any necessary cleanup for owned track instances if required
|
||||
}
|
||||
|
||||
activeCandidateTrack = null;
|
||||
ownsCandidateTrack = false;
|
||||
releaseActiveCandidateTrack();
|
||||
}
|
||||
|
||||
// 恢复之前添加的音量调整(如果存在)。
|
||||
|
||||
@@ -33,6 +33,7 @@ namespace osu.Game.LAsEzExtensions.Audio
|
||||
private readonly EzLatencyManager latencyManager;
|
||||
private Bindable<bool>? inputAudioLatencyConfigBindable;
|
||||
private Action<ValueChangedEvent<bool>>? inputAudioLatencyConfigHandler;
|
||||
private Action<ValueChangedEvent<bool>>? enabledChangedHandler;
|
||||
|
||||
/// <summary>
|
||||
/// Global instance for unified access
|
||||
@@ -53,6 +54,9 @@ namespace osu.Game.LAsEzExtensions.Audio
|
||||
Logger.Log("InputAudioLatencyTracker.Initialize called", LoggingTarget.Runtime, LogLevel.Debug);
|
||||
scoreProcessor = processor;
|
||||
|
||||
// Start each gameplay session from a clean latency dataset.
|
||||
latencyManager.ClearStatistics();
|
||||
|
||||
// 将 Ez2Setting 的启用状态绑定到 EzLatencyManager
|
||||
inputAudioLatencyConfigBindable = ezConfig.GetBindable<bool>(Ez2Setting.InputAudioLatencyTracker);
|
||||
// Avoid BindTo here to prevent repeated double-binding errors when Initialize is called multiple times.
|
||||
@@ -64,13 +68,15 @@ namespace osu.Game.LAsEzExtensions.Audio
|
||||
latencyManager.OnNewRecord += OnLatencyRecordGenerated;
|
||||
|
||||
// 绑定启用状态变化,控制生命周期
|
||||
latencyManager.Enabled.BindValueChanged(enabled =>
|
||||
enabledChangedHandler = enabled =>
|
||||
{
|
||||
if (enabled.NewValue)
|
||||
Start();
|
||||
else
|
||||
Stop();
|
||||
}, true);
|
||||
};
|
||||
|
||||
latencyManager.Enabled.BindValueChanged(enabledChangedHandler, true);
|
||||
}
|
||||
|
||||
private bool started;
|
||||
@@ -156,6 +162,8 @@ namespace osu.Game.LAsEzExtensions.Audio
|
||||
$"Latency analysis complete!\nInput→Judge: {stats.AvgInputToJudge:F1}ms\nInput→Audio: {stats.AvgInputToPlayback:F1}ms\nAudio→Judge: {stats.AvgPlaybackToJudge:F1}ms\nRecords: {stats.RecordCount}",
|
||||
Icon = FontAwesome.Solid.ChartLine,
|
||||
});
|
||||
|
||||
latencyManager.ClearStatistics();
|
||||
}
|
||||
|
||||
private void OnNewJudgement(JudgementResult result)
|
||||
@@ -185,11 +193,15 @@ namespace osu.Game.LAsEzExtensions.Audio
|
||||
|
||||
// 解绑事件
|
||||
latencyManager.OnNewRecord -= OnLatencyRecordGenerated;
|
||||
latencyManager.Dispose();
|
||||
|
||||
if (enabledChangedHandler != null)
|
||||
latencyManager.Enabled.ValueChanged -= enabledChangedHandler;
|
||||
|
||||
if (inputAudioLatencyConfigBindable != null && inputAudioLatencyConfigHandler != null)
|
||||
inputAudioLatencyConfigBindable.ValueChanged -= inputAudioLatencyConfigHandler;
|
||||
|
||||
latencyManager.ClearStatistics();
|
||||
|
||||
Instance = null;
|
||||
}
|
||||
|
||||
|
||||
@@ -181,7 +181,7 @@ namespace osu.Game.LAsEzExtensions
|
||||
noteSizeBindables[cacheKey] = source;
|
||||
}
|
||||
|
||||
return source.GetBoundCopy();
|
||||
return source;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -213,6 +213,8 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
protected GameplayClockContainer GameplayClockContainer { get; private set; }
|
||||
|
||||
protected InputAudioLatencyTracker LatencyTracker { get; private set; }
|
||||
|
||||
public DimmableStoryboard DimmableStoryboard { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -259,6 +261,13 @@ namespace osu.Game.Screens.Play
|
||||
gameActive.BindValueChanged(_ => updatePauseOnFocusLostState(), true);
|
||||
}
|
||||
|
||||
private void scheduleGameplayTextureCleanup()
|
||||
{
|
||||
var beatmapSkin = Beatmap.Value?.Skin as Skin;
|
||||
beatmapSkin?.RecycleTextures(disposeAtlas: true);
|
||||
beatmapSkin?.RecycleSamples();
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
@@ -278,7 +287,7 @@ namespace osu.Game.Screens.Play
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(OsuConfigManager config, OsuGameBase game, CancellationToken cancellationToken)
|
||||
private void load(OsuConfigManager config, OsuGameBase game, CancellationToken cancellationToken, Ez2ConfigManager ez2Config)
|
||||
{
|
||||
var gameplayMods = Mods.Value.Select(m => m.DeepClone()).ToArray();
|
||||
|
||||
@@ -317,14 +326,13 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
dependencies.CacheAs(ScoreProcessor);
|
||||
|
||||
// Initialize InputAudioLatencyTracker if available in DI
|
||||
// 初始化InputAudioLatencyTracker
|
||||
if (GlobalConfigStore.EzConfig.Get<bool>(Ez2Setting.InputAudioLatencyTracker))
|
||||
{
|
||||
var ezConfig = dependencies.Get<Ez2ConfigManager>();
|
||||
var tracker = new InputAudioLatencyTracker(ezConfig);
|
||||
dependencies.CacheAs(tracker);
|
||||
tracker.Initialize(ScoreProcessor);
|
||||
tracker.Start();
|
||||
LatencyTracker = new InputAudioLatencyTracker(ez2Config);
|
||||
dependencies.CacheAs(LatencyTracker);
|
||||
LatencyTracker.Initialize(ScoreProcessor);
|
||||
LatencyTracker.Start();
|
||||
}
|
||||
|
||||
HealthProcessor = gameplayMods.OfType<IApplicableHealthProcessor>().FirstOrDefault()?.CreateHealthProcessor(playableBeatmap.HitObjects[0].StartTime);
|
||||
@@ -792,6 +800,17 @@ namespace osu.Game.Screens.Play
|
||||
updateSampleDisabledState();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
if (isDisposing)
|
||||
{
|
||||
LatencyTracker?.Dispose();
|
||||
LatencyTracker = null;
|
||||
}
|
||||
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Seek to a specific time in gameplay.
|
||||
/// </summary>
|
||||
@@ -1281,6 +1300,9 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
fadeOut();
|
||||
|
||||
if (!isRestarting)
|
||||
scheduleGameplayTextureCleanup();
|
||||
|
||||
return base.OnExiting(e);
|
||||
}
|
||||
|
||||
|
||||
@@ -55,9 +55,6 @@ namespace osu.Game.Screens.Play
|
||||
[Resolved]
|
||||
private Ez2ConfigManager ezConfig { get; set; }
|
||||
|
||||
[CanBeNull]
|
||||
private InputAudioLatencyTracker latencyTracker;
|
||||
|
||||
private readonly object scoreSubmissionLock = new object();
|
||||
private TaskCompletionSource<bool> scoreSubmissionSource;
|
||||
|
||||
@@ -85,20 +82,6 @@ namespace osu.Game.Screens.Play
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
});
|
||||
|
||||
// 保存配置实例并初始化延迟追踪
|
||||
latencyTracker = new InputAudioLatencyTracker(ezConfig);
|
||||
latencyTracker?.Initialize(ScoreProcessor);
|
||||
|
||||
// Ensure tracker is started for testing scenarios in SubmittingPlayer
|
||||
try
|
||||
{
|
||||
latencyTracker?.Start();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Log($"InputAudioLatencyTracker failed to Start: {ex.Message}", level: LogLevel.Error);
|
||||
}
|
||||
}
|
||||
|
||||
protected override GameplayClockContainer CreateGameplayClockContainer(WorkingBeatmap beatmap, double gameplayStart) => new MasterGameplayClockContainer(beatmap, gameplayStart)
|
||||
@@ -284,7 +267,7 @@ namespace osu.Game.Screens.Play
|
||||
statics.SetValue(Static.LastLocalUserScore, Score?.ScoreInfo.DeepClone());
|
||||
|
||||
// 生成延迟报告
|
||||
latencyTracker?.GenerateLatencyReport();
|
||||
LatencyTracker?.GenerateLatencyReport();
|
||||
|
||||
return exiting;
|
||||
}
|
||||
@@ -348,13 +331,13 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
if (Ruleset.Value.OnlineID == 3 && !offsetManiaBindable.IsDefault)
|
||||
{
|
||||
Logger.Log($"[EzMania]Score submission blocked by offset settings.", Ez2ConfigManager.LOGGER_NAME, LogLevel.Important);
|
||||
Logger.Log("[EzMania]Score submission blocked by offset settings.", Ez2ConfigManager.LOGGER_NAME, LogLevel.Important);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
if (Ruleset.Value.OnlineID != 3 && !offsetNonStdBindable.IsDefault)
|
||||
{
|
||||
Logger.Log($"[EzNoMania]Score submission blocked by offset settings.", Ez2ConfigManager.LOGGER_NAME, LogLevel.Important);
|
||||
Logger.Log("[EzNoMania]Score submission blocked by offset settings.", Ez2ConfigManager.LOGGER_NAME, LogLevel.Important);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
@@ -166,6 +166,11 @@ namespace osu.Game.Skinning
|
||||
Samples = samples;
|
||||
}
|
||||
|
||||
public void RecycleTextures(bool disposeAtlas = false)
|
||||
{
|
||||
Textures?.ClearCache(disposeAtlas);
|
||||
}
|
||||
|
||||
protected virtual IResourceStore<TextureUpload> CreateTextureLoaderStore(IStorageResourceProvider resources, IResourceStore<byte[]> storage)
|
||||
=> new MaxDimensionLimitedTextureLoaderStore(resources.CreateTextureLoaderStore(storage));
|
||||
|
||||
|
||||
@@ -100,11 +100,6 @@ namespace osu.Game.Storyboards.Drawables
|
||||
if (clock != null)
|
||||
Clock = clock;
|
||||
|
||||
dependencies.CacheAs(typeof(TextureStore),
|
||||
new TextureStore(host.Renderer, host.CreateTextureLoaderStore(
|
||||
CreateResourceLookupStore()
|
||||
), false, scaleAdjust: 1));
|
||||
|
||||
foreach (var layer in Storyboard.Layers)
|
||||
{
|
||||
cancellationToken?.ThrowIfCancellationRequested();
|
||||
|
||||
Reference in New Issue
Block a user