From 64cb04a179de6bdf535a2fb07566c2c5427536cb Mon Sep 17 00:00:00 2001 From: andy840119 Date: Thu, 7 Oct 2021 00:39:58 +0900 Subject: [PATCH 1/5] add draggable circle at the front of content. --- .../Visual/BackgroundGridTestSample.cs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) 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, + }, } }); } From ebecbc6d90739044bb4965718ed7ac516886a61c Mon Sep 17 00:00:00 2001 From: andy840119 Date: Thu, 7 Oct 2021 00:42:10 +0900 Subject: [PATCH 2/5] to make sure that re-create framebuffer will clear exist frame buffer also. --- .../Graphics/MultiShaderBufferedDrawNodeSharedData.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Framework.Font/Graphics/MultiShaderBufferedDrawNodeSharedData.cs b/osu.Framework.Font/Graphics/MultiShaderBufferedDrawNodeSharedData.cs index ba432fc..3207e9a 100644 --- a/osu.Framework.Font/Graphics/MultiShaderBufferedDrawNodeSharedData.cs +++ b/osu.Framework.Font/Graphics/MultiShaderBufferedDrawNodeSharedData.cs @@ -26,7 +26,7 @@ public MultiShaderBufferedDrawNodeSharedData(RenderbufferInternalFormat[] format public void CreateDefaultFrameBuffers(IShader[] shaders) { - shaderBuffers.Clear(); + clearBuffers(); var filterMode = PixelSnapping ? All.Nearest : All.Linear; @@ -57,7 +57,11 @@ public FrameBuffer[] GetDrawFrameBuffers() protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); + clearBuffers(); + } + private void clearBuffers() + { // clear all frame in the dictionary. foreach (var shaderBuffer in shaderBuffers) { From a57139d17eece0349f4725bee9da93a6eb0196ef Mon Sep 17 00:00:00 2001 From: andy840119 Date: Thu, 7 Oct 2021 00:42:26 +0900 Subject: [PATCH 3/5] should only create shard data once because drawable node will create three times. --- osu.Framework.Font.Tests/Visual/Shaders/ShaderTestScene.cs | 6 ++++-- osu.Framework.Font/Graphics/Sprites/KaraokeSpriteText.cs | 3 +++ .../Graphics/Sprites/KaraokeSpriteText_DrawNode.cs | 3 +-- osu.Framework.Font/Graphics/Sprites/LyricSpriteText.cs | 3 +++ .../Graphics/Sprites/LyricSpriteText_DrawNode.cs | 3 +-- 5 files changed, 12 insertions(+), 6 deletions(-) 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/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 . From caa3433c0a2b2374fe51e2aca90441022e83e248 Mon Sep 17 00:00:00 2001 From: andy840119 Date: Mon, 11 Oct 2021 10:22:35 +0800 Subject: [PATCH 4/5] make list of frame buffer being readonly. also prevent duplicated destroy and create if shaders are the same. --- .../Graphics/MultiShaderBufferedDrawNode.cs | 25 ++----- .../MultiShaderBufferedDrawNodeSharedData.cs | 65 +++++++++++++++---- 2 files changed, 59 insertions(+), 31 deletions(-) diff --git a/osu.Framework.Font/Graphics/MultiShaderBufferedDrawNode.cs b/osu.Framework.Font/Graphics/MultiShaderBufferedDrawNode.cs index c03a794..03be370 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; @@ -92,6 +91,10 @@ protected override void DrawContents() { foreach (var frameBuffer in drawFrameBuffers) { + // got no idea why happened. + if (frameBuffer.Texture == null) + break; + DrawFrameBuffer(frameBuffer, DrawRectangle, Color4.White); } } @@ -111,8 +114,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 +152,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 3207e9a..a75db32 100644 --- a/osu.Framework.Font/Graphics/MultiShaderBufferedDrawNodeSharedData.cs +++ b/osu.Framework.Font/Graphics/MultiShaderBufferedDrawNodeSharedData.cs @@ -3,7 +3,10 @@ using System; using System.Collections.Generic; +using System.IO; 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,8 +17,6 @@ public class MultiShaderBufferedDrawNodeSharedData : BufferedDrawNodeSharedData { private readonly Dictionary shaderBuffers = new Dictionary(); - public IReadOnlyDictionary ShaderBuffers => shaderBuffers; - private readonly RenderbufferInternalFormat[] formats; public MultiShaderBufferedDrawNodeSharedData(RenderbufferInternalFormat[] formats = null, bool pixelSnapping = false) @@ -26,14 +27,56 @@ public MultiShaderBufferedDrawNodeSharedData(RenderbufferInternalFormat[] format public void CreateDefaultFrameBuffers(IShader[] shaders) { - clearBuffers(); + if (shaderBuffers.Keys.SequenceEqual(shaders)) + return; - var filterMode = PixelSnapping ? All.Nearest : All.Linear; + 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 DirectoryNotFoundException(); + + return shaderBuffers[fromShader]; + } + + public FrameBuffer GetTargetFrameBuffer(IShader shader) + { + if (!shaderBuffers.ContainsKey(shader)) + throw new DirectoryNotFoundException(); + + return shaderBuffers[shader]; } public void UpdateBuffer(IShader shader, FrameBuffer frameBuffer) @@ -45,7 +88,7 @@ public void UpdateBuffer(IShader shader, FrameBuffer frameBuffer) } public FrameBuffer[] GetDrawFrameBuffers() - => ShaderBuffers.Where(x => + => shaderBuffers.Where(x => { var shader = x.Key; if (shader is IStepShader stepShader) @@ -57,18 +100,16 @@ public FrameBuffer[] GetDrawFrameBuffers() protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); - clearBuffers(); + clearBuffers(shaderBuffers.Values.ToArray()); } - private void clearBuffers() + private void clearBuffers(FrameBuffer[] effectBuffers) { - // clear all frame in the dictionary. - foreach (var shaderBuffer in shaderBuffers) + // dispose all frame buffer in array. + foreach (var shaderBuffer in effectBuffers) { - shaderBuffer.Value.Dispose(); + shaderBuffer.Dispose(); } - - shaderBuffers.Clear(); } } } From f45d0b5903c6a1ea9eda86e25df8d3f9676eb8d0 Mon Sep 17 00:00:00 2001 From: andy840119 Date: Mon, 11 Oct 2021 10:48:55 +0800 Subject: [PATCH 5/5] should save shaders into shared data instead because frame buffer is highly related to shader. it might cause not found issue if changing frame buffer dictionary and drawing at the same time. --- .../Graphics/MultiShaderBufferedDrawNode.cs | 17 +++-------------- .../MultiShaderBufferedDrawNodeSharedData.cs | 17 ++++++++++++----- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/osu.Framework.Font/Graphics/MultiShaderBufferedDrawNode.cs b/osu.Framework.Font/Graphics/MultiShaderBufferedDrawNode.cs index 03be370..17e466f 100644 --- a/osu.Framework.Font/Graphics/MultiShaderBufferedDrawNode.cs +++ b/osu.Framework.Font/Graphics/MultiShaderBufferedDrawNode.cs @@ -20,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) @@ -33,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(); } @@ -91,10 +83,6 @@ protected override void DrawContents() { foreach (var frameBuffer in drawFrameBuffers) { - // got no idea why happened. - if (frameBuffer.Texture == null) - break; - DrawFrameBuffer(frameBuffer, DrawRectangle, Color4.White); } } @@ -107,6 +95,7 @@ protected override void DrawContents() private void drawFrameBuffer() { + var shaders = SharedData.Shaders; if (!shaders.Any()) return; diff --git a/osu.Framework.Font/Graphics/MultiShaderBufferedDrawNodeSharedData.cs b/osu.Framework.Font/Graphics/MultiShaderBufferedDrawNodeSharedData.cs index a75db32..2ab2e50 100644 --- a/osu.Framework.Font/Graphics/MultiShaderBufferedDrawNodeSharedData.cs +++ b/osu.Framework.Font/Graphics/MultiShaderBufferedDrawNodeSharedData.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Reflection; using osu.Framework.Graphics.OpenGL; @@ -17,6 +16,8 @@ public class MultiShaderBufferedDrawNodeSharedData : BufferedDrawNodeSharedData { private readonly Dictionary shaderBuffers = new Dictionary(); + public IShader[] Shaders => shaderBuffers.Keys.ToArray(); + private readonly RenderbufferInternalFormat[] formats; public MultiShaderBufferedDrawNodeSharedData(RenderbufferInternalFormat[] formats = null, bool pixelSnapping = false) @@ -25,11 +26,12 @@ public MultiShaderBufferedDrawNodeSharedData(RenderbufferInternalFormat[] format this.formats = formats; } - public void CreateDefaultFrameBuffers(IShader[] shaders) + public void UpdateFrameBuffers(IShader[] shaders) { if (shaderBuffers.Keys.SequenceEqual(shaders)) return; + // collect all frame buffer that needs to be disposed. var disposedFrameBuffer = shaderBuffers.Values.ToArray(); shaderBuffers.Clear(); @@ -66,7 +68,7 @@ public FrameBuffer GetSourceFrameBuffer(IShader shader) return CurrentEffectBuffer; if (!shaderBuffers.ContainsKey(fromShader)) - throw new DirectoryNotFoundException(); + throw new KeyNotFoundException(); return shaderBuffers[fromShader]; } @@ -74,7 +76,7 @@ public FrameBuffer GetSourceFrameBuffer(IShader shader) public FrameBuffer GetTargetFrameBuffer(IShader shader) { if (!shaderBuffers.ContainsKey(shader)) - throw new DirectoryNotFoundException(); + throw new KeyNotFoundException(); return shaderBuffers[shader]; } @@ -90,7 +92,12 @@ public void UpdateBuffer(IShader shader, FrameBuffer frameBuffer) public FrameBuffer[] GetDrawFrameBuffers() => 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;