diff --git a/osu.Framework/Graphics/Audio/WaveformGraph.cs b/osu.Framework/Graphics/Audio/WaveformGraph.cs index bbd819f2e89..c8a3f0849f3 100644 --- a/osu.Framework/Graphics/Audio/WaveformGraph.cs +++ b/osu.Framework/Graphics/Audio/WaveformGraph.cs @@ -14,7 +14,6 @@ using osu.Framework.Graphics.Rendering.Vertices; using osu.Framework.Graphics.Shaders; using osu.Framework.Graphics.Textures; -using osu.Framework.Layout; using osu.Framework.Logging; using osu.Framework.Utils; using osuTK; @@ -158,18 +157,15 @@ public Color4? HighColour } } - protected override bool OnInvalidate(Invalidation invalidation, InvalidationSource source) + private Vector2? lastGeneratedDrawSize; + + protected override void Update() { - bool result = base.OnInvalidate(invalidation, source); + base.Update(); - if ((invalidation & Invalidation.RequiredParentSizeToFit) > 0) - { - // We should regenerate when `Scale` changed, but not `Position`. - // Unfortunately both of these are grouped together in `MiscGeometry`. + // Can't use invalidation for this as RequiredParentSizeToFit is closest, but also triggers on DrawPosition changes. + if (lastGeneratedDrawSize != null && DrawSize != lastGeneratedDrawSize) queueRegeneration(); - } - - return result; } private CancellationTokenSource? cancelSource = new CancellationTokenSource(); @@ -189,6 +185,8 @@ private void queueRegeneration() => Scheduler.AddOnce(() => cancelGeneration(); + lastGeneratedDrawSize = DrawSize; + var originalWaveform = Waveform; if (originalWaveform == null) diff --git a/osu.Framework/Platform/GameHost.cs b/osu.Framework/Platform/GameHost.cs index 926a08dbd63..495ba30a6ef 100644 --- a/osu.Framework/Platform/GameHost.cs +++ b/osu.Framework/Platform/GameHost.cs @@ -53,6 +53,11 @@ public abstract class GameHost : IIpcHost, IDisposable { public IWindow Window { get; private set; } + /// + /// Whether needs to be non-null for startup to succeed. + /// + protected virtual bool RequireWindowExists => true; + public IRenderer Renderer { get; private set; } public string RendererInfo { get; private set; } @@ -724,6 +729,14 @@ public void Run(Game game) ChooseAndSetupRenderer(); + // Window creation may fail in the case of a catastrophic failure (ie. graphics driver or SDL2 level). + // In such cases, we want to throw here to immediately mark this renderer setup as failed. + if (RequireWindowExists && Window == null) + { + Logger.Log("Aborting startup as no window could be created."); + return; + } + initialiseInputHandlers(); // Prepare renderer (requires config). @@ -961,17 +974,20 @@ protected void SetupRendererAndWindow(IRenderer renderer, GraphicsSurfaceType su Renderer = renderer; Renderer.CacheStorage = CacheStorage.GetStorageForDirectory("shaders"); - // Prepare window - Window = CreateWindow(surfaceType); - - if (Window == null) - { - Logger.Log("🖼️ Renderer could not be initialised, no window exists."); - return; - } - try { + // Prepare window + Window = CreateWindow(surfaceType); + + if (Window == null) + { + // Can be null, usually via Headless execution. + if (!RequireWindowExists) + return; + + throw new InvalidOperationException("🖼️ Renderer could not be initialised as window creation failed."); + } + Window.SetupWindow(Config); Window.Create(); Window.Title = $@"osu!framework (running ""{Name}"")"; diff --git a/osu.Framework/Platform/HeadlessGameHost.cs b/osu.Framework/Platform/HeadlessGameHost.cs index bff5953f5cc..21bed6de1bb 100644 --- a/osu.Framework/Platform/HeadlessGameHost.cs +++ b/osu.Framework/Platform/HeadlessGameHost.cs @@ -47,6 +47,8 @@ public HeadlessGameHost(string gameName = null, HostOptions options = null, bool this.realtime = realtime; } + protected override bool RequireWindowExists => false; + protected override IWindow CreateWindow(GraphicsSurfaceType preferredSurface) => null; protected override Clipboard CreateClipboard() => new HeadlessClipboard();