mirror of
https://github.com/ppy/osu.git
synced 2024-11-06 02:37:25 +08:00
233 lines
9.0 KiB
C#
233 lines
9.0 KiB
C#
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
// See the LICENCE file in the repository root for full licence text.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using osu.Framework.Allocation;
|
|
using osu.Framework.Audio;
|
|
using osu.Framework.Audio.Sample;
|
|
using osu.Framework.Bindables;
|
|
using osu.Framework.Extensions.ObjectExtensions;
|
|
using osu.Framework.Graphics.Rendering;
|
|
using osu.Framework.Graphics.Shaders;
|
|
using osu.Framework.Graphics.Textures;
|
|
using osu.Framework.IO.Stores;
|
|
using osu.Framework.Platform;
|
|
using osu.Game.Rulesets.Configuration;
|
|
|
|
namespace osu.Game.Rulesets.UI
|
|
{
|
|
public class DrawableRulesetDependencies : DependencyContainer, IDisposable
|
|
{
|
|
/// <summary>
|
|
/// The texture store to be used for the ruleset.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Reads textures from the "Textures" folder in ruleset resources.
|
|
/// If not available locally, lookups will fallback to the global texture store.
|
|
/// </remarks>
|
|
public TextureStore TextureStore { get; }
|
|
|
|
/// <summary>
|
|
/// The sample store to be used for the ruleset.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Reads samples from the "Samples" folder in ruleset resources.
|
|
/// If not available locally, lookups will fallback to the global sample store.
|
|
/// </remarks>
|
|
public ISampleStore SampleStore { get; }
|
|
|
|
/// <summary>
|
|
/// The shader manager to be used for the ruleset.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Reads shaders from the "Shaders" folder in ruleset resources.
|
|
/// If not available locally, lookups will fallback to the global shader manager.
|
|
/// </remarks>
|
|
public ShaderManager ShaderManager { get; }
|
|
|
|
/// <summary>
|
|
/// The ruleset config manager. May be null if ruleset does not expose a configuration manager.
|
|
/// </summary>
|
|
public IRulesetConfigManager? RulesetConfigManager { get; }
|
|
|
|
public DrawableRulesetDependencies(Ruleset ruleset, IReadOnlyDependencyContainer parent)
|
|
: base(parent)
|
|
{
|
|
var resources = ruleset.CreateResourceStore();
|
|
|
|
var host = parent.Get<GameHost>();
|
|
|
|
TextureStore = new TextureStore(host.Renderer, parent.Get<GameHost>().CreateTextureLoaderStore(new NamespacedResourceStore<byte[]>(resources, @"Textures")));
|
|
CacheAs(TextureStore = new FallbackTextureStore(host.Renderer, TextureStore, parent.Get<TextureStore>()));
|
|
|
|
SampleStore = parent.Get<AudioManager>().GetSampleStore(new NamespacedResourceStore<byte[]>(resources, @"Samples"));
|
|
SampleStore.PlaybackConcurrency = OsuGameBase.SAMPLE_CONCURRENCY;
|
|
CacheAs(SampleStore = new FallbackSampleStore(SampleStore, parent.Get<ISampleStore>()));
|
|
|
|
CacheAs(ShaderManager = new RulesetShaderManager(host.Renderer, new NamespacedResourceStore<byte[]>(resources, @"Shaders"), parent.Get<ShaderManager>()));
|
|
|
|
RulesetConfigManager = parent.Get<IRulesetConfigCache>().GetConfigFor(ruleset);
|
|
if (RulesetConfigManager != null)
|
|
Cache(RulesetConfigManager);
|
|
}
|
|
|
|
#region Disposal
|
|
|
|
~DrawableRulesetDependencies()
|
|
{
|
|
// required to potentially clean up sample store from audio hierarchy.
|
|
Dispose(false);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
Dispose(true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
private bool isDisposed;
|
|
|
|
protected void Dispose(bool disposing)
|
|
{
|
|
if (isDisposed)
|
|
return;
|
|
|
|
isDisposed = true;
|
|
|
|
if (ShaderManager.IsNotNull()) SampleStore.Dispose();
|
|
if (TextureStore.IsNotNull()) TextureStore.Dispose();
|
|
if (ShaderManager.IsNotNull()) ShaderManager.Dispose();
|
|
}
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// A sample store which adds a fallback source and prevents disposal of the fallback source.
|
|
/// </summary>
|
|
private class FallbackSampleStore : ISampleStore
|
|
{
|
|
private readonly ISampleStore primary;
|
|
private readonly ISampleStore fallback;
|
|
|
|
public FallbackSampleStore(ISampleStore primary, ISampleStore fallback)
|
|
{
|
|
this.primary = primary;
|
|
this.fallback = fallback;
|
|
}
|
|
|
|
public Sample Get(string name) => primary.Get(name) ?? fallback.Get(name);
|
|
|
|
public async Task<Sample> GetAsync(string name, CancellationToken cancellationToken = default)
|
|
{
|
|
return await primary.GetAsync(name, cancellationToken).ConfigureAwait(false)
|
|
?? await fallback.GetAsync(name, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
public Stream GetStream(string name) => primary.GetStream(name) ?? fallback.GetStream(name);
|
|
|
|
public IEnumerable<string> GetAvailableResources() => throw new NotSupportedException();
|
|
|
|
public void AddAdjustment(AdjustableProperty type, IBindable<double> adjustBindable) => throw new NotSupportedException();
|
|
|
|
public void RemoveAdjustment(AdjustableProperty type, IBindable<double> adjustBindable) => throw new NotSupportedException();
|
|
|
|
public void RemoveAllAdjustments(AdjustableProperty type) => throw new NotSupportedException();
|
|
|
|
public void BindAdjustments(IAggregateAudioAdjustment component) => throw new NotImplementedException();
|
|
|
|
public void UnbindAdjustments(IAggregateAudioAdjustment component) => throw new NotImplementedException();
|
|
|
|
public BindableNumber<double> Volume => throw new NotSupportedException();
|
|
|
|
public BindableNumber<double> Balance => throw new NotSupportedException();
|
|
|
|
public BindableNumber<double> Frequency => throw new NotSupportedException();
|
|
|
|
public BindableNumber<double> Tempo => throw new NotSupportedException();
|
|
|
|
public IBindable<double> AggregateVolume => throw new NotSupportedException();
|
|
|
|
public IBindable<double> AggregateBalance => throw new NotSupportedException();
|
|
|
|
public IBindable<double> AggregateFrequency => throw new NotSupportedException();
|
|
|
|
public IBindable<double> AggregateTempo => throw new NotSupportedException();
|
|
|
|
public int PlaybackConcurrency
|
|
{
|
|
get => throw new NotSupportedException();
|
|
set => throw new NotSupportedException();
|
|
}
|
|
|
|
public void AddExtension(string extension) => throw new NotSupportedException();
|
|
|
|
public void Dispose()
|
|
{
|
|
if (primary.IsNotNull()) primary.Dispose();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// A texture store which adds a fallback source and prevents disposal of the fallback source.
|
|
/// </summary>
|
|
private class FallbackTextureStore : TextureStore
|
|
{
|
|
private readonly TextureStore primary;
|
|
private readonly TextureStore fallback;
|
|
|
|
public FallbackTextureStore(IRenderer renderer, TextureStore primary, TextureStore fallback)
|
|
: base(renderer)
|
|
{
|
|
this.primary = primary;
|
|
this.fallback = fallback;
|
|
}
|
|
|
|
public override Texture Get(string name, WrapMode wrapModeS, WrapMode wrapModeT)
|
|
=> primary.Get(name, wrapModeS, wrapModeT) ?? fallback.Get(name, wrapModeS, wrapModeT);
|
|
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
base.Dispose(disposing);
|
|
if (primary.IsNotNull()) primary.Dispose();
|
|
}
|
|
}
|
|
|
|
private class RulesetShaderManager : ShaderManager
|
|
{
|
|
private readonly ShaderManager parent;
|
|
|
|
public RulesetShaderManager(IRenderer renderer, NamespacedResourceStore<byte[]> rulesetResources, ShaderManager parent)
|
|
: base(renderer, rulesetResources)
|
|
{
|
|
this.parent = parent;
|
|
}
|
|
|
|
// When the debugger is attached, exceptions are expensive.
|
|
// Manually work around this by caching failed lookups and falling back straight to parent.
|
|
private readonly HashSet<(string, string)> failedLookups = new HashSet<(string, string)>();
|
|
|
|
public override IShader Load(string vertex, string fragment)
|
|
{
|
|
if (!failedLookups.Contains((vertex, fragment)))
|
|
{
|
|
try
|
|
{
|
|
return base.Load(vertex, fragment);
|
|
}
|
|
catch
|
|
{
|
|
// Shader lookup is very non-standard. Rather than returning null on missing shaders, exceptions are thrown.
|
|
failedLookups.Add((vertex, fragment));
|
|
}
|
|
}
|
|
|
|
return parent.Load(vertex, fragment);
|
|
}
|
|
}
|
|
}
|
|
}
|