-
Notifications
You must be signed in to change notification settings - Fork 421
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
Implement FastCircle
component
#6290
Merged
Merged
Changes from 13 commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
42c01f0
Implement FastCircle
EVAST9919 0a76264
Add antialiasing
EVAST9919 cd05147
Add TestSceneCircle
EVAST9919 8931d98
Make circle with uneven sides pill-shaped
EVAST9919 73237d3
Don't inherit Sprite
EVAST9919 a0a4ccc
Implement correct input handling
EVAST9919 f0d3350
Implement EdgeSmoothness property
EVAST9919 6dd915c
Add Circle to the text scene for comparison
EVAST9919 30dc846
Improve input test
EVAST9919 58593b4
Add nested masking test
EVAST9919 88f9416
Bind texture just in case
EVAST9919 382c49a
Merge branch 'master' into hacky-circle
EVAST9919 018fd4b
Schedule content loading in TestSceneFastCircle setup
EVAST9919 ac3a224
Add rotation test
EVAST9919 0554880
Merge branch 'master' into hacky-circle
EVAST9919 5f6716b
Add performance test scenes for alternating drawables
EVAST9919 8ecbafb
Merge branch 'master' into hacky-circle
EVAST9919 bcac4ac
Add shear and scale tests
EVAST9919 f77f946
Merge branch 'master' into hacky-circle
EVAST9919 0a75c42
Fix incorrect scale handling
EVAST9919 2937678
Add xmldoc
smoogipoo File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
146 changes: 146 additions & 0 deletions
146
osu.Framework.Tests/Visual/Drawables/TestSceneFastCircle.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence. | ||
// See the LICENCE file in the repository root for full licence text. | ||
|
||
using System; | ||
using NUnit.Framework; | ||
using osu.Framework.Graphics.Shapes; | ||
using osu.Framework.Graphics; | ||
using osu.Framework.Graphics.Containers; | ||
using osu.Framework.Graphics.Sprites; | ||
using osu.Framework.Input.Events; | ||
using osu.Framework.Testing; | ||
using osuTK; | ||
using osuTK.Input; | ||
|
||
namespace osu.Framework.Tests.Visual.Drawables | ||
{ | ||
public partial class TestSceneFastCircle : ManualInputManagerTestScene | ||
{ | ||
private TestCircle fastCircle = null!; | ||
private Circle circle = null!; | ||
private CircularContainer fastCircleMask = null!; | ||
private CircularContainer circleMask = null!; | ||
|
||
[SetUp] | ||
public void Setup() => Schedule(() => | ||
{ | ||
Child = new GridContainer | ||
{ | ||
RelativeSizeAxes = Axes.Both, | ||
RowDimensions = new[] | ||
{ | ||
new Dimension(GridSizeMode.Absolute, 100), | ||
new Dimension(), | ||
}, | ||
ColumnDimensions = new[] | ||
{ | ||
new Dimension(GridSizeMode.Relative, 0.5f), | ||
new Dimension() | ||
}, | ||
Content = new[] | ||
{ | ||
new Drawable[] | ||
{ | ||
new SpriteText | ||
{ | ||
Anchor = Anchor.Centre, | ||
Origin = Anchor.Centre, | ||
Text = "FastCircle" | ||
}, | ||
new SpriteText | ||
{ | ||
Anchor = Anchor.Centre, | ||
Origin = Anchor.Centre, | ||
Text = "Circle" | ||
} | ||
}, | ||
new Drawable[] | ||
{ | ||
fastCircleMask = new CircularContainer | ||
{ | ||
Anchor = Anchor.Centre, | ||
Origin = Anchor.TopRight, | ||
Size = new Vector2(200), | ||
Child = fastCircle = new TestCircle | ||
{ | ||
Anchor = Anchor.TopRight, | ||
Origin = Anchor.Centre, | ||
Size = new Vector2(200), | ||
Clicked = onClick | ||
} | ||
}, | ||
circleMask = new CircularContainer | ||
{ | ||
Anchor = Anchor.Centre, | ||
Origin = Anchor.TopRight, | ||
Size = new Vector2(200), | ||
Child = circle = new Circle | ||
{ | ||
Anchor = Anchor.TopRight, | ||
Origin = Anchor.Centre, | ||
Size = new Vector2(200) | ||
} | ||
}, | ||
} | ||
} | ||
}; | ||
}); | ||
|
||
[Test] | ||
public void TestInput() | ||
{ | ||
testInput(new Vector2(200, 100)); | ||
testInput(new Vector2(100, 200)); | ||
testInput(new Vector2(200, 200)); | ||
} | ||
|
||
[Test] | ||
public void TestSmoothness() | ||
{ | ||
AddStep("Change smoothness to 0", () => fastCircle.EdgeSmoothness = circle.MaskingSmoothness = 0); | ||
AddStep("Change smoothness to 1", () => fastCircle.EdgeSmoothness = circle.MaskingSmoothness = 1); | ||
AddStep("Change smoothness to 5", () => fastCircle.EdgeSmoothness = circle.MaskingSmoothness = 5); | ||
} | ||
|
||
[Test] | ||
public void TestNestedMasking() | ||
{ | ||
AddToggleStep("Toggle parent masking", m => fastCircleMask.Masking = circleMask.Masking = m); | ||
} | ||
|
||
private void testInput(Vector2 size) | ||
{ | ||
AddStep($"Resize to {size}", () => | ||
{ | ||
fastCircle.Size = circle.Size = size; | ||
}); | ||
AddStep("Click outside the corner", () => clickNearCorner(-Vector2.One)); | ||
AddAssert("input not received", () => clicked == false); | ||
AddStep("Click inside the corner", () => clickNearCorner(Vector2.One)); | ||
AddAssert("input received", () => clicked); | ||
} | ||
|
||
private void clickNearCorner(Vector2 offset) | ||
{ | ||
clicked = false; | ||
InputManager.MoveMouseTo(fastCircle.ToScreenSpace(new Vector2(fastCircle.Radius * (1f - MathF.Sqrt(0.5f))) + offset)); | ||
InputManager.Click(MouseButton.Left); | ||
} | ||
|
||
private bool clicked; | ||
|
||
private void onClick() => clicked = true; | ||
|
||
private partial class TestCircle : FastCircle | ||
{ | ||
public Action? Clicked; | ||
|
||
protected override bool OnClick(ClickEvent e) | ||
{ | ||
base.OnClick(e); | ||
Clicked?.Invoke(); | ||
return true; | ||
} | ||
} | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
osu.Framework.Tests/Visual/Performance/TestSceneFastCirclePerformance.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence. | ||
// See the LICENCE file in the repository root for full licence text. | ||
|
||
using osu.Framework.Graphics; | ||
using osu.Framework.Graphics.Shapes; | ||
|
||
namespace osu.Framework.Tests.Visual.Performance | ||
{ | ||
public sealed partial class TestSceneFastCirclePerformance : RepeatedDrawablePerformanceTestScene | ||
{ | ||
protected override Drawable CreateDrawable() => new FastCircle(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence. | ||
// See the LICENCE file in the repository root for full licence text. | ||
|
||
using System; | ||
using osu.Framework.Allocation; | ||
using osu.Framework.Graphics.Primitives; | ||
using osu.Framework.Graphics.Rendering; | ||
using osu.Framework.Graphics.Rendering.Vertices; | ||
using osu.Framework.Graphics.Shaders; | ||
using osuTK; | ||
|
||
namespace osu.Framework.Graphics.Shapes | ||
{ | ||
public partial class FastCircle : Drawable | ||
{ | ||
private float edgeSmoothness = 1f; | ||
|
||
public float EdgeSmoothness | ||
{ | ||
get => edgeSmoothness; | ||
set | ||
{ | ||
if (edgeSmoothness == value) | ||
return; | ||
|
||
edgeSmoothness = value; | ||
|
||
if (IsLoaded) | ||
Invalidate(Invalidation.DrawNode); | ||
} | ||
} | ||
|
||
private IShader shader = null!; | ||
|
||
[BackgroundDependencyLoader] | ||
private void load(ShaderManager shaders) | ||
{ | ||
shader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, "FastCircle"); | ||
} | ||
|
||
public float Radius => MathF.Min(DrawSize.X, DrawSize.Y) * 0.5f; | ||
|
||
public override bool Contains(Vector2 screenSpacePos) | ||
{ | ||
if (!base.Contains(screenSpacePos)) | ||
return false; | ||
|
||
float cRadius = Radius; | ||
return DrawRectangle.Shrink(cRadius).DistanceExponentiated(ToLocalSpace(screenSpacePos), 2f) <= cRadius * cRadius; | ||
} | ||
|
||
protected override DrawNode CreateDrawNode() => new FastCircleDrawNode(this); | ||
|
||
private class FastCircleDrawNode : DrawNode | ||
{ | ||
protected new FastCircle Source => (FastCircle)base.Source; | ||
|
||
public FastCircleDrawNode(FastCircle source) | ||
: base(source) | ||
{ | ||
} | ||
|
||
private Quad screenSpaceDrawQuad; | ||
private Vector4 drawRectangle; | ||
private Vector2 edgeSmoothness; | ||
private IShader shader = null!; | ||
|
||
public override void ApplyState() | ||
{ | ||
base.ApplyState(); | ||
|
||
screenSpaceDrawQuad = Source.ScreenSpaceDrawQuad; | ||
drawRectangle = new Vector4(0, 0, screenSpaceDrawQuad.Width, screenSpaceDrawQuad.Height); | ||
shader = Source.shader; | ||
edgeSmoothness = new Vector2(Source.edgeSmoothness); | ||
} | ||
|
||
protected override void Draw(IRenderer renderer) | ||
{ | ||
base.Draw(renderer); | ||
|
||
if (!renderer.BindTexture(renderer.WhitePixel)) | ||
return; | ||
|
||
shader.Bind(); | ||
|
||
var vertexAction = renderer.DefaultQuadBatch.AddAction; | ||
|
||
vertexAction(new TexturedVertex2D(renderer) | ||
{ | ||
Position = screenSpaceDrawQuad.BottomLeft, | ||
TexturePosition = new Vector2(0, screenSpaceDrawQuad.Height), | ||
TextureRect = drawRectangle, | ||
BlendRange = edgeSmoothness, | ||
Colour = DrawColourInfo.Colour.BottomLeft.SRGB, | ||
}); | ||
vertexAction(new TexturedVertex2D(renderer) | ||
{ | ||
Position = screenSpaceDrawQuad.BottomRight, | ||
TexturePosition = new Vector2(screenSpaceDrawQuad.Width, screenSpaceDrawQuad.Height), | ||
TextureRect = drawRectangle, | ||
BlendRange = edgeSmoothness, | ||
Colour = DrawColourInfo.Colour.BottomRight.SRGB, | ||
}); | ||
vertexAction(new TexturedVertex2D(renderer) | ||
{ | ||
Position = screenSpaceDrawQuad.TopRight, | ||
TexturePosition = new Vector2(screenSpaceDrawQuad.Width, 0), | ||
TextureRect = drawRectangle, | ||
BlendRange = edgeSmoothness, | ||
Colour = DrawColourInfo.Colour.TopRight.SRGB, | ||
}); | ||
vertexAction(new TexturedVertex2D(renderer) | ||
{ | ||
Position = screenSpaceDrawQuad.TopLeft, | ||
TexturePosition = new Vector2(0, 0), | ||
TextureRect = drawRectangle, | ||
BlendRange = edgeSmoothness, | ||
Colour = DrawColourInfo.Colour.TopLeft.SRGB, | ||
}); | ||
|
||
shader.Unbind(); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
#ifndef FAST_CIRCLE_FS | ||
#define FAST_CIRCLE_FS | ||
|
||
#undef HIGH_PRECISION_VERTEX | ||
#define HIGH_PRECISION_VERTEX | ||
|
||
#include "sh_Utils.h" | ||
#include "sh_Masking.h" | ||
|
||
layout(location = 2) in highp vec2 v_TexCoord; | ||
|
||
layout(location = 0) out vec4 o_Colour; | ||
|
||
void main(void) | ||
{ | ||
highp vec2 pixelPos = v_TexRect.zw * 0.5 - abs(v_TexCoord - v_TexRect.zw * 0.5); | ||
highp float radius = min(v_TexRect.z, v_TexRect.w) * 0.5; | ||
|
||
highp float dst = max(pixelPos.x, pixelPos.y) > radius ? radius - min(pixelPos.x, pixelPos.y) : distance(pixelPos, vec2(radius)); | ||
|
||
highp float alpha = v_BlendRange.x == 0.0 ? float(dst < radius) : (clamp(radius - dst, 0.0, v_BlendRange.x) / v_BlendRange.x); | ||
|
||
o_Colour = getRoundedColor(vec4(vec3(1.0), alpha), vec2(0.0)); | ||
} | ||
|
||
#endif |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure about the use of
v_TexRect
here.Texture.Crop()
)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's the thing: I'm abusing texture coordinates to pass screenspace size to the shader without uniforms (to compute antialiasing). This new circle isn't a sprite, so textures can't be set. (in my defence current circle isn't a sprite either)
Regarding rotation: I'll add test showing it's working.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To clarify: I'm not trying replicate
CircularContainer
, I'm simplifying one particular usage of it, which isCircularContainer
with a planeBox
with no texture inside (Circle
class) to avoid masking overhead in cases where multiple circes are being drawn consecutively (GPU side) and reducing total drawable count (CPU side)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a side note I can apply hacks explored in this pr to the
Triangles
shader since it's being widely used in the game to eliminate uniform buffer. Will pr after checking whether performance gain is worth doing so.