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

Fix performance issue #65

Merged
merged 5 commits into from
Oct 11, 2021
Merged
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
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