Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Rendering;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.IO.Stores;
using osu.Framework.Platform;
using osu.Framework.Testing;
using osuTK;
using osuTK.Graphics;

namespace osu.Framework.Tests.Visual.Containers
{
public partial class TestSceneBufferedContainerPixelSnapping : TestScene
{
private Texture texture = null!;
private Container content = null!;
private BufferedContainer container = null!;
private bool pixelSnapping = true;
private bool useBufferContainer = true;
private Vector2 frameBufferScale = new Vector2(1);

[BackgroundDependencyLoader]
private void load(IRenderer renderer, GameHost host, Game game)
{
var textures = new TextureStore(renderer, host.CreateTextureLoaderStore(new NamespacedResourceStore<byte[]>(game.Resources, "Textures")), filteringMode: TextureFilteringMode.Nearest);

texture = textures.Get("sample-texture");

Child = content = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(100),
};

content.Loop(t => t.ResizeTo(new Vector2(300), 1000, Easing.InOutSine)
.Then()
.ResizeTo(new Vector2(100), 1000, Easing.InOutSine));

updateContent();
}

private void updateContent()
{
var drawable = new CircularContainer
{
RelativeSizeAxes = Axes.Both,
Masking = true,
Children = new Drawable[]
{
new Sprite
{
Texture = texture,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Scale = new Vector2(1.5f)
},
new Box
{
Size = new Vector2(80),
Colour = Color4.White,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
new SpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Text = "Text",
Colour = Color4.Black,
Font = new FontUsage(size: 35),
}
}
};

container = new BufferedContainer(pixelSnapping: pixelSnapping)
{
RelativeSizeAxes = Axes.Both,
FrameBufferScale = frameBufferScale,
};

if (useBufferContainer)
{
container.Child = drawable;
content.Child = container;
}
else
{
content.Child = drawable;
}
}

[SetUpSteps]
public void Setup()
{
AddToggleStep("buffered", value =>
{
useBufferContainer = value;
updateContent();
});
AddToggleStep("pixel snapping", value =>
{
pixelSnapping = value;
updateContent();
});
AddSliderStep("framebuffer scale", 0.25f, 2f, 1f, value =>
{
frameBufferScale = new Vector2(value);
updateContent();
});
}
}
}
18 changes: 10 additions & 8 deletions osu.Framework/Graphics/BufferedDrawNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public class BufferedDrawNode : TexturedShaderDrawNode
private Color4 backgroundColour;
private RectangleF screenSpaceDrawRectangle;
private Vector2 frameBufferScale;
private Vector2 frameBufferSize;
private Vector2I frameBufferSize;
private IDrawable rootNodeCached;

public BufferedDrawNode(IBufferedDrawable source, DrawNode child, BufferedDrawNodeSharedData sharedData)
Expand All @@ -52,13 +52,16 @@ public override void ApplyState()
base.ApplyState();

backgroundColour = Source.BackgroundColour;
screenSpaceDrawRectangle = Source.ScreenSpaceDrawQuad.AABBFloat;
DrawColourInfo = Source.FrameBufferDrawColour ?? new DrawColourInfo(Color4.White, base.DrawColourInfo.Blending);

var drawRect = Source.ScreenSpaceDrawQuad.AABBFloat;

frameBufferScale = Source.FrameBufferScale;
frameBufferSize = new Vector2I((int)MathF.Ceiling(drawRect.Width * frameBufferScale.X), (int)MathF.Ceiling(drawRect.Height * frameBufferScale.Y));

clipDrawRectangle();
screenSpaceDrawRectangle = new RectangleF((int)drawRect.X, (int)drawRect.Y, frameBufferSize.X / frameBufferScale.X, frameBufferSize.Y / frameBufferScale.Y);

frameBufferSize = new Vector2(MathF.Ceiling(screenSpaceDrawRectangle.Width * frameBufferScale.X), MathF.Ceiling(screenSpaceDrawRectangle.Height * frameBufferScale.Y));
clipDrawRectangle();

DrawRectangle = screenSpaceDrawRectangle;

Expand Down Expand Up @@ -158,8 +161,7 @@ protected ValueInvokeOnDisposal<IFrameBuffer> BindFrameBuffer(IFrameBuffer frame
// Disable masking for generating the frame buffer since masking will be re-applied
// when actually drawing later on anyways. This allows more information to be captured
// in the frame buffer and helps with cached buffers being re-used.
RectangleI screenSpaceMaskingRect = new RectangleI((int)Math.Floor(screenSpaceDrawRectangle.X), (int)Math.Floor(screenSpaceDrawRectangle.Y), (int)frameBufferSize.X + 1,
(int)frameBufferSize.Y + 1);
RectangleI screenSpaceMaskingRect = new RectangleI((int)Math.Floor(screenSpaceDrawRectangle.X), (int)Math.Floor(screenSpaceDrawRectangle.Y), frameBufferSize.X + 1, frameBufferSize.Y + 1);

renderer.PushMaskingInfo(new MaskingInfo
{
Expand All @@ -171,8 +173,8 @@ protected ValueInvokeOnDisposal<IFrameBuffer> BindFrameBuffer(IFrameBuffer frame
}, true);

// Match viewport to FrameBuffer such that we don't draw unnecessary pixels.
renderer.PushViewport(new RectangleI(0, 0, (int)frameBufferSize.X, (int)frameBufferSize.Y));
renderer.PushScissor(new RectangleI(0, 0, (int)frameBufferSize.X, (int)frameBufferSize.Y));
renderer.PushViewport(new RectangleI(0, 0, frameBufferSize.X, frameBufferSize.Y));
renderer.PushScissor(new RectangleI(0, 0, frameBufferSize.X, frameBufferSize.Y));
renderer.PushScissorOffset(screenSpaceMaskingRect.Location);

return new ValueInvokeOnDisposal<(BufferedDrawNode node, IRenderer renderer)>((this, renderer), static tup => tup.node.returnViewport(tup.renderer));
Expand Down
Loading