diff --git a/osu.Framework.Font.Tests/Visual/BackgroundGridTestSample.cs b/osu.Framework.Font.Tests/Visual/BackgroundGridTestSample.cs index a66d320..4627fcb 100644 --- a/osu.Framework.Font.Tests/Visual/BackgroundGridTestSample.cs +++ b/osu.Framework.Font.Tests/Visual/BackgroundGridTestSample.cs @@ -25,7 +25,9 @@ public abstract class BackgroundGridTestSample : TestScene [Resolved] private ShaderManager shaderManager { get; set; } - protected override Container Content { get; } = new Container { RelativeSizeAxes = Axes.Both }; + private readonly Container content; + + protected override Container Content => content; protected BackgroundGridTestSample() { @@ -58,7 +60,18 @@ protected BackgroundGridTestSample() Origin = Anchor.Centre, Colour = Color4.Purple, }, - Content, + content = new Container + { + RelativeSizeAxes = Axes.Both, + }, + new DraggableCircle + { + Name = "Test background change object", + Size = new Vector2(GRID_SIZE), + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + Colour = Color4.Green, + }, } }); } diff --git a/osu.Framework.Font.Tests/Visual/Shaders/ShaderTestScene.cs b/osu.Framework.Font.Tests/Visual/Shaders/ShaderTestScene.cs index 261d57a..62f9c65 100644 --- a/osu.Framework.Font.Tests/Visual/Shaders/ShaderTestScene.cs +++ b/osu.Framework.Font.Tests/Visual/Shaders/ShaderTestScene.cs @@ -73,6 +73,9 @@ protected class TestShaderContainer : Container, IMultiShaderBufferedDrawable private readonly List shaders = new(); + // todo: should have a better way to let user able to customize formats? + private readonly MultiShaderBufferedDrawNodeSharedData sharedData = new(); + public IReadOnlyList Shaders { get => shaders; @@ -95,9 +98,8 @@ private void load(ShaderManager shaders) RoundedTextureShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED); } - // todo: should have a better way to let user able to customize formats? protected override DrawNode CreateDrawNode() - => new TestShaderContainerShaderEffectDrawNode(this, new MultiShaderBufferedDrawNodeSharedData()); + => new TestShaderContainerShaderEffectDrawNode(this, sharedData); /// /// to apply . diff --git a/osu.Framework.Font/Graphics/MultiShaderBufferedDrawNode.cs b/osu.Framework.Font/Graphics/MultiShaderBufferedDrawNode.cs index c03a794..17e466f 100644 --- a/osu.Framework.Font/Graphics/MultiShaderBufferedDrawNode.cs +++ b/osu.Framework.Font/Graphics/MultiShaderBufferedDrawNode.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Reflection; using osu.Framework.Graphics.Colour; @@ -21,8 +20,6 @@ public class MultiShaderBufferedDrawNode : BufferedDrawNode protected new MultiShaderBufferedDrawNodeSharedData SharedData => (MultiShaderBufferedDrawNodeSharedData)base.SharedData; - private IShader[] shaders = { }; - private readonly double loadTime; public MultiShaderBufferedDrawNode(IBufferedDrawable source, DrawNode child, MultiShaderBufferedDrawNodeSharedData sharedData) @@ -34,19 +31,13 @@ public MultiShaderBufferedDrawNode(IBufferedDrawable source, DrawNode child, Mul public override void ApplyState() { base.ApplyState(); - - if (shaders.SequenceEqual(Source.Shaders)) - return; - - // should clear buffer if property changed because might be shader amount changed. - shaders = Source.Shaders.ToArray(); - SharedData.CreateDefaultFrameBuffers(shaders); + SharedData.UpdateFrameBuffers(Source.Shaders.ToArray()); } protected override long GetDrawVersion() { // if contains shader that need to apply time, then need to force run populate contents in each frame. - if (ContainTimePropertyShader(shaders)) + if (ContainTimePropertyShader(SharedData.Shaders)) { ResetDrawVersion(); } @@ -104,6 +95,7 @@ protected override void DrawContents() private void drawFrameBuffer() { + var shaders = SharedData.Shaders; if (!shaders.Any()) return; @@ -111,8 +103,8 @@ private void drawFrameBuffer() foreach (var shader in shaders) { - var current = getSourceFrameBuffer(shader); - var target = SharedData.ShaderBuffers[shader]; + var current = SharedData.GetSourceFrameBuffer(shader); + var target = SharedData.GetTargetFrameBuffer(shader); if (shader is IStepShader stepShader) { @@ -149,22 +141,6 @@ void renderShader(IShader shader, FrameBuffer current, FrameBuffer target) shader.Unbind(); } } - - FrameBuffer getSourceFrameBuffer(IShader targetShader) - { - if (!(targetShader is IStepShader stepShader)) - return SharedData.CurrentEffectBuffer; - - var fromShader = stepShader.FromShader; - if (fromShader == null) - return SharedData.CurrentEffectBuffer; - - var shaderBuffers = SharedData.ShaderBuffers; - if (!shaderBuffers.ContainsKey(fromShader)) - throw new DirectoryNotFoundException("Frame buffer does not found in target shader."); - - return shaderBuffers[fromShader]; - } } } } diff --git a/osu.Framework.Font/Graphics/MultiShaderBufferedDrawNodeSharedData.cs b/osu.Framework.Font/Graphics/MultiShaderBufferedDrawNodeSharedData.cs index ba432fc..2ab2e50 100644 --- a/osu.Framework.Font/Graphics/MultiShaderBufferedDrawNodeSharedData.cs +++ b/osu.Framework.Font/Graphics/MultiShaderBufferedDrawNodeSharedData.cs @@ -4,6 +4,8 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; +using osu.Framework.Graphics.OpenGL; using osu.Framework.Graphics.OpenGL.Buffers; using osu.Framework.Graphics.Shaders; using osuTK.Graphics.ES30; @@ -14,7 +16,7 @@ public class MultiShaderBufferedDrawNodeSharedData : BufferedDrawNodeSharedData { private readonly Dictionary shaderBuffers = new Dictionary(); - public IReadOnlyDictionary ShaderBuffers => shaderBuffers; + public IShader[] Shaders => shaderBuffers.Keys.ToArray(); private readonly RenderbufferInternalFormat[] formats; @@ -24,16 +26,59 @@ public MultiShaderBufferedDrawNodeSharedData(RenderbufferInternalFormat[] format this.formats = formats; } - public void CreateDefaultFrameBuffers(IShader[] shaders) + public void UpdateFrameBuffers(IShader[] shaders) { - shaderBuffers.Clear(); + if (shaderBuffers.Keys.SequenceEqual(shaders)) + return; - var filterMode = PixelSnapping ? All.Nearest : All.Linear; + // collect all frame buffer that needs to be disposed. + var disposedFrameBuffer = shaderBuffers.Values.ToArray(); + shaderBuffers.Clear(); foreach (var shader in shaders) { + var filterMode = PixelSnapping ? All.Nearest : All.Linear; shaderBuffers.Add(shader, new FrameBuffer(formats, filterMode)); } + + clearBuffersWithSchedule(() => + { + clearBuffers(disposedFrameBuffer); + }); + + static void clearBuffersWithSchedule(Action action) + { + // should call GLWrapper.ScheduleDisposal(() => Dispose(true)); + Delegate act = new Action(() => action?.Invoke()); + var prop = typeof(GLWrapper).GetRuntimeMethods().FirstOrDefault(x => x.Name == "ScheduleDisposal"); + if (prop == null) + throw new NullReferenceException(); + + prop.Invoke(prop, new object[] { act }); + } + } + + public FrameBuffer GetSourceFrameBuffer(IShader shader) + { + if (!(shader is IStepShader stepShader)) + return CurrentEffectBuffer; + + var fromShader = stepShader.FromShader; + if (fromShader == null) + return CurrentEffectBuffer; + + if (!shaderBuffers.ContainsKey(fromShader)) + throw new KeyNotFoundException(); + + return shaderBuffers[fromShader]; + } + + public FrameBuffer GetTargetFrameBuffer(IShader shader) + { + if (!shaderBuffers.ContainsKey(shader)) + throw new KeyNotFoundException(); + + return shaderBuffers[shader]; } public void UpdateBuffer(IShader shader, FrameBuffer frameBuffer) @@ -45,9 +90,14 @@ public void UpdateBuffer(IShader shader, FrameBuffer frameBuffer) } public FrameBuffer[] GetDrawFrameBuffers() - => ShaderBuffers.Where(x => + => shaderBuffers.Where(x => { - var shader = x.Key; + var (shader, frameBuffer) = x; + + // should not render disposed or not created frame buffer. + if (frameBuffer.Texture == null) + return false; + if (shader is IStepShader stepShader) return stepShader.StepShaders.Any() && stepShader.Draw; @@ -57,14 +107,16 @@ public FrameBuffer[] GetDrawFrameBuffers() protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); + clearBuffers(shaderBuffers.Values.ToArray()); + } - // clear all frame in the dictionary. - foreach (var shaderBuffer in shaderBuffers) + private void clearBuffers(FrameBuffer[] effectBuffers) + { + // dispose all frame buffer in array. + foreach (var shaderBuffer in effectBuffers) { - shaderBuffer.Value.Dispose(); + shaderBuffer.Dispose(); } - - shaderBuffers.Clear(); } } } diff --git a/osu.Framework.Font/Graphics/Sprites/KaraokeSpriteText.cs b/osu.Framework.Font/Graphics/Sprites/KaraokeSpriteText.cs index 258109e..69daf32 100644 --- a/osu.Framework.Font/Graphics/Sprites/KaraokeSpriteText.cs +++ b/osu.Framework.Font/Graphics/Sprites/KaraokeSpriteText.cs @@ -26,6 +26,9 @@ public class KaraokeSpriteText : KaraokeSpriteText private readonly MaskingContainer backLyricTextContainer; private readonly T backLyricText; + // todo: should have a better way to let user able to customize formats? + private readonly MultiShaderBufferedDrawNodeSharedData sharedData = new MultiShaderBufferedDrawNodeSharedData(); + public IShader TextureShader { get; private set; } public IShader RoundedTextureShader { get; private set; } diff --git a/osu.Framework.Font/Graphics/Sprites/KaraokeSpriteText_DrawNode.cs b/osu.Framework.Font/Graphics/Sprites/KaraokeSpriteText_DrawNode.cs index 059377c..d183710 100644 --- a/osu.Framework.Font/Graphics/Sprites/KaraokeSpriteText_DrawNode.cs +++ b/osu.Framework.Font/Graphics/Sprites/KaraokeSpriteText_DrawNode.cs @@ -18,9 +18,8 @@ protected override Quad ComputeScreenSpaceDrawQuad() return ToScreenSpace(newRectangle); } - // todo: should have a better way to let user able to customize formats? protected override DrawNode CreateDrawNode() - => new KaraokeSpriteTextShaderEffectDrawNode(this, new MultiShaderBufferedDrawNodeSharedData()); + => new KaraokeSpriteTextShaderEffectDrawNode(this, sharedData); /// /// to apply . diff --git a/osu.Framework.Font/Graphics/Sprites/LyricSpriteText.cs b/osu.Framework.Font/Graphics/Sprites/LyricSpriteText.cs index c22c97a..1f3db02 100644 --- a/osu.Framework.Font/Graphics/Sprites/LyricSpriteText.cs +++ b/osu.Framework.Font/Graphics/Sprites/LyricSpriteText.cs @@ -27,6 +27,9 @@ public partial class LyricSpriteText : Drawable, IMultiShaderBufferedDrawable, I private const float default_text_size = 48; private static readonly char[] default_never_fixed_width_characters = { '.', ',', ':', ' ' }; + // todo: should have a better way to let user able to customize formats? + private readonly MultiShaderBufferedDrawNodeSharedData sharedData = new MultiShaderBufferedDrawNodeSharedData(); + [Resolved] private FontStore store { get; set; } diff --git a/osu.Framework.Font/Graphics/Sprites/LyricSpriteText_DrawNode.cs b/osu.Framework.Font/Graphics/Sprites/LyricSpriteText_DrawNode.cs index 8e5b6ef..a992610 100644 --- a/osu.Framework.Font/Graphics/Sprites/LyricSpriteText_DrawNode.cs +++ b/osu.Framework.Font/Graphics/Sprites/LyricSpriteText_DrawNode.cs @@ -21,9 +21,8 @@ protected override Quad ComputeScreenSpaceDrawQuad() return ToScreenSpace(newRectangle); } - // todo: should have a better way to let user able to customize formats? protected override DrawNode CreateDrawNode() - => new LyricSpriteTextShaderEffectDrawNode(this, new MultiShaderBufferedDrawNodeSharedData()); + => new LyricSpriteTextShaderEffectDrawNode(this, sharedData); /// /// to apply .