Skip to content

Commit

Permalink
Merge pull request #65 from andy840119/fix-performance-issue
Browse files Browse the repository at this point in the history
Fix performance issue
  • Loading branch information
andy840119 authored Oct 11, 2021
2 parents 0d0fec3 + f45d0b5 commit b5771d2
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 48 deletions.
17 changes: 15 additions & 2 deletions osu.Framework.Font.Tests/Visual/BackgroundGridTestSample.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ public abstract class BackgroundGridTestSample : TestScene
[Resolved]
private ShaderManager shaderManager { get; set; }

protected override Container<Drawable> Content { get; } = new Container { RelativeSizeAxes = Axes.Both };
private readonly Container content;

protected override Container<Drawable> Content => content;

protected BackgroundGridTestSample()
{
Expand Down Expand Up @@ -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,
},
}
});
}
Expand Down
6 changes: 4 additions & 2 deletions osu.Framework.Font.Tests/Visual/Shaders/ShaderTestScene.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ protected class TestShaderContainer : Container, IMultiShaderBufferedDrawable

private readonly List<IShader> shaders = new();

// todo: should have a better way to let user able to customize formats?
private readonly MultiShaderBufferedDrawNodeSharedData sharedData = new();

public IReadOnlyList<IShader> Shaders
{
get => shaders;
Expand All @@ -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);

/// <summary>
/// <see cref="BufferedDrawNode"/> to apply <see cref="IShader"/>.
Expand Down
34 changes: 5 additions & 29 deletions osu.Framework.Font/Graphics/MultiShaderBufferedDrawNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using osu.Framework.Graphics.Colour;
Expand All @@ -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)
Expand All @@ -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();
}
Expand Down Expand Up @@ -104,15 +95,16 @@ protected override void DrawContents()

private void drawFrameBuffer()
{
var shaders = SharedData.Shaders;
if (!shaders.Any())
return;

GLWrapper.SetBlend(BlendingParameters.None);

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)
{
Expand Down Expand Up @@ -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];
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -14,7 +16,7 @@ public class MultiShaderBufferedDrawNodeSharedData : BufferedDrawNodeSharedData
{
private readonly Dictionary<IShader, FrameBuffer> shaderBuffers = new Dictionary<IShader, FrameBuffer>();

public IReadOnlyDictionary<IShader, FrameBuffer> ShaderBuffers => shaderBuffers;
public IShader[] Shaders => shaderBuffers.Keys.ToArray();

private readonly RenderbufferInternalFormat[] formats;

Expand All @@ -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)
Expand All @@ -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;

Expand All @@ -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();
}
}
}
3 changes: 3 additions & 0 deletions osu.Framework.Font/Graphics/Sprites/KaraokeSpriteText.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ public class KaraokeSpriteText : KaraokeSpriteText<LyricSpriteText>
private readonly MaskingContainer<T> 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; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);

/// <summary>
/// <see cref="BufferedDrawNode"/> to apply <see cref="IShader"/>.
Expand Down
3 changes: 3 additions & 0 deletions osu.Framework.Font/Graphics/Sprites/LyricSpriteText.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);

/// <summary>
/// <see cref="BufferedDrawNode"/> to apply <see cref="IShader"/>.
Expand Down

0 comments on commit b5771d2

Please sign in to comment.