Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hot reload resources #5443

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 20 additions & 7 deletions Robust.Client/Audio/AudioManager.Public.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,19 @@ public void SetRotation(Angle angle)
AL.Listener(ALListenerfv.Orientation, ref at, ref up);
}

void IAudioInternal.Remove(AudioStream stream)
{
if (stream.ClydeHandle == null)
return;

if (!_audioSampleBuffers.Remove(stream.BufferId))
{
return;
}

AL.DeleteBuffer(stream.BufferId);
}

/// <inheritdoc/>
public AudioStream LoadAudioOggVorbis(Stream stream, string? name = null)
{
Expand Down Expand Up @@ -120,9 +133,9 @@ public AudioStream LoadAudioOggVorbis(Stream stream, string? name = null)
_checkAlError();

var handle = new ClydeHandle(_audioSampleBuffers.Count);
_audioSampleBuffers.Add(new LoadedAudioSample(buffer));
_audioSampleBuffers.Add(buffer, new LoadedAudioSample(buffer));
var length = TimeSpan.FromSeconds(vorbis.TotalSamples / (double) vorbis.SampleRate);
return new AudioStream(handle, length, (int) vorbis.Channels, name, vorbis.Title, vorbis.Artist);
return new AudioStream(buffer, handle, length, (int) vorbis.Channels, name, vorbis.Title, vorbis.Artist);
}

/// <inheritdoc/>
Expand Down Expand Up @@ -179,9 +192,9 @@ public AudioStream LoadAudioWav(Stream stream, string? name = null)
_checkAlError();

var handle = new ClydeHandle(_audioSampleBuffers.Count);
_audioSampleBuffers.Add(new LoadedAudioSample(buffer));
_audioSampleBuffers.Add(buffer, new LoadedAudioSample(buffer));
var length = TimeSpan.FromSeconds(wav.Data.Length / (double) wav.BlockAlign / wav.SampleRate);
return new AudioStream(handle, length, wav.NumChannels, name);
return new AudioStream(buffer, handle, length, wav.NumChannels, name);
}

/// <inheritdoc/>
Expand Down Expand Up @@ -210,8 +223,8 @@ public AudioStream LoadAudioRaw(ReadOnlySpan<short> samples, int channels, int s

var handle = new ClydeHandle(_audioSampleBuffers.Count);
var length = TimeSpan.FromSeconds((double) samples.Length / channels / sampleRate);
_audioSampleBuffers.Add(new LoadedAudioSample(buffer));
return new AudioStream(handle, length, channels, name);
_audioSampleBuffers.Add(buffer, new LoadedAudioSample(buffer));
return new AudioStream(buffer, handle, length, channels, name);
}

public void SetMasterGain(float newGain)
Expand Down Expand Up @@ -293,7 +306,7 @@ internal void RemoveBufferedAudioSource(int handle)

// ReSharper disable once PossibleInvalidOperationException
// TODO: This really shouldn't be indexing based on the ClydeHandle...
AL.Source(source, ALSourcei.Buffer, _audioSampleBuffers[(int) stream.ClydeHandle!.Value].BufferHandle);
AL.Source(source, ALSourcei.Buffer, _audioSampleBuffers[stream.BufferId].BufferHandle);

var audioSource = new AudioSource(this, source, stream);
_audioSources.Add(source, new WeakReference<BaseAudioSource>(audioSource));
Expand Down
21 changes: 20 additions & 1 deletion Robust.Client/Audio/AudioManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using OpenTK.Audio.OpenAL;
using OpenTK.Audio.OpenAL.Extensions.Creative.EFX;
using Robust.Client.Audio.Sources;
using Robust.Client.ResourceManagement;
using Robust.Shared;
using Robust.Shared.Audio;
using Robust.Shared.Configuration;
Expand All @@ -17,13 +18,15 @@ internal sealed partial class AudioManager : IAudioInternal
{
[Shared.IoC.Dependency] private readonly IConfigurationManager _cfg = default!;
[Shared.IoC.Dependency] private readonly ILogManager _logMan = default!;
[Shared.IoC.Dependency] private readonly IReloadManager _reload = default!;
[Shared.IoC.Dependency] private readonly IResourceCache _cache = default!;

private Thread? _gameThread;

private ALDevice _openALDevice;
private ALContext _openALContext;

private readonly List<LoadedAudioSample> _audioSampleBuffers = new();
private readonly Dictionary<int, LoadedAudioSample> _audioSampleBuffers = new();

private readonly Dictionary<int, WeakReference<BaseAudioSource>> _audioSources =
new();
Expand Down Expand Up @@ -116,6 +119,22 @@ private void InitializeAudio()
IsEfxSupported = HasAlDeviceExtension("ALC_EXT_EFX");

_cfg.OnValueChanged(CVars.AudioMasterVolume, SetMasterGain, true);

_reload.Register("/Audio", "*.ogg");
_reload.Register("/Audio", "*.wav");

_reload.OnChanged += OnReload;
}

private void OnReload(ResPath args)
{
if (args.Extension != "ogg" &&
args.Extension != "wav")
{
return;
}

_cache.ReloadResource<AudioResource>(args);
}

internal bool IsMainThread()
Expand Down
8 changes: 7 additions & 1 deletion Robust.Client/Audio/AudioStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,21 @@ namespace Robust.Client.Audio;
/// </summary>
public sealed class AudioStream
{
/// <summary>
/// Buffer ID for this audio in AL.
/// </summary>
internal int BufferId { get; }

public TimeSpan Length { get; }
internal IClydeHandle? ClydeHandle { get; }
public string? Name { get; }
public string? Title { get; }
public string? Artist { get; }
public int ChannelCount { get; }

internal AudioStream(IClydeHandle? handle, TimeSpan length, int channelCount, string? name = null, string? title = null, string? artist = null)
internal AudioStream(int bufferId, IClydeHandle? handle, TimeSpan length, int channelCount, string? name = null, string? title = null, string? artist = null)
{
BufferId = bufferId;
ClydeHandle = handle;
Length = length;
ChannelCount = channelCount;
Expand Down
13 changes: 10 additions & 3 deletions Robust.Client/Audio/HeadlessAudioManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ namespace Robust.Client.Audio;
/// </summary>
internal sealed class HeadlessAudioManager : IAudioInternal
{
private int _audioBuffer;

/// <inheritdoc />
public void InitializePostWindowing()
{
Expand Down Expand Up @@ -65,6 +67,11 @@ public void SetAttenuation(Attenuation attenuation)
{
}

/// <inheritdoc />
public void Remove(AudioStream stream)
{
}

/// <inheritdoc />
public void StopAllAudio()
{
Expand Down Expand Up @@ -101,11 +108,11 @@ public AudioStream LoadAudioWav(Stream stream, string? name = null)
public AudioStream LoadAudioRaw(ReadOnlySpan<short> samples, int channels, int sampleRate, string? name = null)
{
var length = TimeSpan.FromSeconds((double) samples.Length / channels / sampleRate);
return new AudioStream(null, length, channels, name);
return new AudioStream(_audioBuffer++, null, length, channels, name);
}

private static AudioStream AudioStreamFromMetadata(AudioMetadata metadata, string? name)
private AudioStream AudioStreamFromMetadata(AudioMetadata metadata, string? name)
{
return new AudioStream(null, metadata.Length, metadata.ChannelCount, name, metadata.Title, metadata.Artist);
return new AudioStream(_audioBuffer++, null, metadata.Length, metadata.ChannelCount, name, metadata.Title, metadata.Artist);
}
}
2 changes: 2 additions & 0 deletions Robust.Client/Audio/IAudioInternal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ internal interface IAudioInternal : IAudioManager

void SetAttenuation(Attenuation attenuation);

void Remove(AudioStream stream);

/// <summary>
/// Stops all audio from playing.
/// </summary>
Expand Down
3 changes: 1 addition & 2 deletions Robust.Client/Audio/IAudioManager.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.IO;
using Robust.Client.Audio.Sources;
using Robust.Shared.Audio.Sources;

namespace Robust.Client.Audio;
Expand All @@ -11,7 +10,7 @@ namespace Robust.Client.Audio;
public interface IAudioManager
{
IAudioSource? CreateAudioSource(AudioStream stream);

AudioStream LoadAudioOggVorbis(Stream stream, string? name = null);

AudioStream LoadAudioWav(Stream stream, string? name = null);
Expand Down
10 changes: 5 additions & 5 deletions Robust.Client/Audio/Sources/AudioSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ internal sealed class AudioSource : BaseAudioSource
/// <summary>
/// Underlying stream to the audio.
/// </summary>
private readonly AudioStream _sourceStream;
internal readonly AudioStream SourceStream;

#if DEBUG
private bool _didPositionWarning;
#endif

public AudioSource(AudioManager master, int sourceHandle, AudioStream sourceStream) : base(master, sourceHandle)
{
_sourceStream = sourceStream;
SourceStream = sourceStream;
}

/// <inheritdoc />
Expand All @@ -47,13 +47,13 @@ public override Vector2 Position
#if DEBUG
// OpenAL doesn't seem to want to play stereo positionally.
// Log a warning if people try to.
if (_sourceStream.ChannelCount > 1 && !_didPositionWarning)
if (SourceStream.ChannelCount > 1 && !_didPositionWarning)
{
_didPositionWarning = true;
Master.OpenALSawmill.Warning("Attempting to set position on audio source with multiple audio channels! Stream: '{0}'. Make sure the audio is MONO, not stereo.",
_sourceStream.Name);
SourceStream.Name);
// warning isn't enough, people just ignore it :(
DebugTools.Assert(false, $"Attempting to set position on audio source with multiple audio channels! Stream: '{_sourceStream.Name}'. Make sure the audio is MONO, not stereo.");
DebugTools.Assert(false, $"Attempting to set position on audio source with multiple audio channels! Stream: '{SourceStream.Name}'. Make sure the audio is MONO, not stereo.");
}
#endif

Expand Down
2 changes: 2 additions & 0 deletions Robust.Client/ClientIoC.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
using Robust.Shared.Serialization;
using Robust.Shared.Timing;
using Robust.Shared.Upload;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;

namespace Robust.Client
Expand Down Expand Up @@ -101,6 +102,7 @@ public static void RegisterIoC(GameController.DisplayMode mode, IDependencyColle
deps.Register<ProfViewManager>();
deps.Register<IGamePrototypeLoadManager, GamePrototypeLoadManager>();
deps.Register<NetworkResourceManager>();
deps.Register<IReloadManager, ReloadManager>();

switch (mode)
{
Expand Down
2 changes: 2 additions & 0 deletions Robust.Client/GameController/GameController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ internal sealed partial class GameController : IGameControllerInternal
[Dependency] private readonly IReplayPlaybackManager _replayPlayback = default!;
[Dependency] private readonly IReplayRecordingManagerInternal _replayRecording = default!;
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
[Dependency] private readonly IReloadManager _reload = default!;

private IWebViewManagerHook? _webViewHook;

Expand Down Expand Up @@ -171,6 +172,7 @@ internal bool StartupContinue(DisplayMode displayMode)
// before prototype load.
ProgramShared.FinishCheckBadFileExtensions(checkBadExtensions);

_reload.Initialize();
_reflectionManager.Initialize();
_prototypeManager.Initialize();
_prototypeManager.LoadDefaultPrototypes();
Expand Down
47 changes: 47 additions & 0 deletions Robust.Client/Graphics/Clyde/Clyde.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Robust.Client.Map;
using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface;
using Robust.Client.Utility;
using Robust.Shared;
using Robust.Shared.Configuration;
using Robust.Shared.ContentPack;
Expand All @@ -21,7 +22,9 @@
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Profiling;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using TextureWrapMode = Robust.Shared.Graphics.TextureWrapMode;

namespace Robust.Client.Graphics.Clyde
Expand All @@ -47,6 +50,8 @@ internal sealed partial class Clyde : IClydeInternal, IPostInjectInit, IEntityEv
[Dependency] private readonly ILocalizationManager _loc = default!;
[Dependency] private readonly IInputManager _inputManager = default!;
[Dependency] private readonly ClientEntityManager _entityManager = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly IReloadManager _reloads = default!;

private GLUniformBuffer<ProjViewMatrices> ProjViewUBO = default!;
private GLUniformBuffer<UniformConstants> UniformConstantsUBO = default!;
Expand Down Expand Up @@ -98,6 +103,16 @@ public bool InitializePreWindowing()
_sawmillOgl = _logManager.GetSawmill("clyde.ogl");
_sawmillWin = _logManager.GetSawmill("clyde.win");

_reloads.Register("/Shaders", "*.swsl");
_reloads.Register("/Textures/Shaders", "*.swsl");
_reloads.Register("/Textures", "*.jpg");
_reloads.Register("/Textures", "*.jpeg");
_reloads.Register("/Textures", "*.png");
_reloads.Register("/Textures", "*.webp");

_reloads.OnChanged += OnChange;
_proto.PrototypesReloaded += OnProtoReload;

_cfg.OnValueChanged(CVars.DisplayOGLCheckErrors, b => _checkGLErrors = b, true);
_cfg.OnValueChanged(CVars.DisplayVSync, VSyncChanged, true);
_cfg.OnValueChanged(CVars.DisplayWindowMode, WindowModeChanged, true);
Expand All @@ -121,6 +136,38 @@ public bool InitializePreWindowing()
return InitWindowing();
}

private void OnProtoReload(PrototypesReloadedEventArgs obj)
{
if (!obj.WasModified<ShaderPrototype>())
return;

foreach (var shader in obj.ByType[typeof(ShaderPrototype)].Modified.Keys)
{
_resourceCache.ReloadResource<ShaderSourceResource>(shader);
}
}

private void OnChange(ResPath obj)
{
if ((obj.TryRelativeTo(new ResPath("/Shaders"), out _) || obj.TryRelativeTo(new ResPath("/Textures/Shaders"), out _)) && obj.Extension == "swsl")
{
_resourceCache.ReloadResource<ShaderSourceResource>(obj);
}

if (obj.TryRelativeTo(new ResPath("/Textures"), out _) && !obj.TryRelativeTo(new ResPath("/Textures/Tiles"), out _))
{
if (obj.Extension == "jpg" || obj.Extension == "jpeg" || obj.Extension == "webp")
{
_resourceCache.ReloadResource<TextureResource>(obj);
}

if (obj.Extension == "png")
{
_resourceCache.ReloadResource<TextureResource>(obj);
}
}
}

public bool InitializePostWindowing()
{
_gameThread = Thread.CurrentThread;
Expand Down
Loading
Loading