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

Use SSBOs for bindless masking #5952

Merged
merged 18 commits into from
Aug 21, 2023
Merged
Show file tree
Hide file tree
Changes from 15 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
94 changes: 94 additions & 0 deletions osu.Framework.Tests/Visual/Graphics/TestSceneScissor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// 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.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osuTK;
using osuTK.Graphics;

namespace osu.Framework.Tests.Visual.Graphics
{
public partial class TestSceneScissor : FrameworkTestScene
{
public TestSceneScissor()
{
Children = new Drawable[]
{
new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Size = new Vector2(0.5f),
Masking = true,
Child = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Size = new Vector2(2),
Masking = true,
Child = new Box
{
RelativeSizeAxes = Axes.Both
},
}
},
new Container
{
Name = "Overlays",
RelativeSizeAxes = Axes.Both,
Masking = true,
BorderColour = Color4.Red,
BorderThickness = 4,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true,
},
new SpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = "Invisible Area",
Colour = Color4.Red,
Font = FontUsage.Default.With(size: 36)
},
new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Size = new Vector2(0.5f),
Masking = true,
BorderColour = Color4.Green,
BorderThickness = 4,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
},
new SpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = "Visible Area",
Colour = Color4.Green,
Font = FontUsage.Default.With(size: 36)
}
}
}
}
}
};
}
}
}
2 changes: 1 addition & 1 deletion osu.Framework/Graphics/Audio/WaveformGraph.cs
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ public override void Draw(IRenderer renderer)
// We're dealing with a _large_ number of points, so we need to optimise the quadToDraw * drawInfo.Matrix multiplications below
// for points that are going to be masked out anyway. This allows for higher resolution graphs at larger scales with virtually no performance loss.
// Since the points are generated in the local coordinate space, we need to convert the screen space masking quad coordinates into the local coordinate space
RectangleF localMaskingRectangle = (Quad.FromRectangle(renderer.CurrentMaskingInfo.ScreenSpaceAABB) * DrawInfo.MatrixInverse).AABBFloat;
RectangleF localMaskingRectangle = (Quad.FromRectangle(renderer.CurrentMaskingInfo.ScreenSpaceScissorArea) * DrawInfo.MatrixInverse).AABBFloat;

float separation = drawSize.X / (points.Count - 1);

Expand Down
17 changes: 6 additions & 11 deletions osu.Framework/Graphics/BufferedDrawNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public class BufferedDrawNode : TexturedShaderDrawNode
protected RectangleF DrawRectangle { get; private set; }

private Color4 backgroundColour;
private RectangleF localDrawRectangle;
private RectangleF screenSpaceDrawRectangle;
private Vector2 frameBufferScale;
private Vector2 frameBufferSize;
Expand All @@ -52,6 +53,7 @@ public override void ApplyState()
base.ApplyState();

backgroundColour = Source.BackgroundColour;
localDrawRectangle = ((Drawable)Source).DrawRectangle;
bdach marked this conversation as resolved.
Show resolved Hide resolved
screenSpaceDrawRectangle = Source.ScreenSpaceDrawQuad.AABBFloat;
DrawColourInfo = Source.FrameBufferDrawColour ?? new DrawColourInfo(Color4.White, base.DrawColourInfo.Blending);
frameBufferScale = Source.FrameBufferScale;
Expand Down Expand Up @@ -155,32 +157,25 @@ protected IDisposable BindFrameBuffer(IFrameBuffer frameBuffer)

private IDisposable establishFrameBufferViewport(IRenderer renderer)
{
// 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);

renderer.PushMaskingInfo(new MaskingInfo
{
ScreenSpaceAABB = screenSpaceMaskingRect,
MaskingRect = screenSpaceDrawRectangle,
ToMaskingSpace = Matrix3.Identity,
ScreenSpaceScissorArea = screenSpaceDrawRectangle,
MaskingArea = localDrawRectangle,
ToMaskingSpace = DrawInfo.MatrixInverse,
ToScissorSpace = Matrix3.Identity,
BlendRange = 1,
AlphaExponent = 1,
}, 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.PushScissorOffset(screenSpaceMaskingRect.Location);

return new ValueInvokeOnDisposal<(BufferedDrawNode node, IRenderer renderer)>((this, renderer), tup => tup.node.returnViewport(tup.renderer));
}

private void returnViewport(IRenderer renderer)
{
renderer.PopScissorOffset();
renderer.PopViewport();
renderer.PopScissor();
renderer.PopMaskingInfo();
Expand Down
2 changes: 1 addition & 1 deletion osu.Framework/Graphics/Containers/BufferedContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ public BufferedContainer(RenderBufferFormat[] formats = null, bool pixelSnapping
private void load(ShaderManager shaders)
{
TextureShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE);
blurShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.BLUR);
blurShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2_NO_MASKING, FragmentShaderDescriptor.BLUR);
}

protected override DrawNode CreateDrawNode() => new BufferedContainerDrawNode(this, sharedData);
Expand Down
13 changes: 7 additions & 6 deletions osu.Framework/Graphics/Containers/CompositeDrawable_DrawNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ protected class CompositeDrawableDrawNode : DrawNode, ICompositeDrawNode
private MaskingInfo? maskingInfo;

/// <summary>
/// The screen-space version of <see cref="MaskingInfo.MaskingRect"/>.
/// The screen-space version of <see cref="MaskingInfo.MaskingArea"/>.
/// Used as cache of screen-space masking quads computed in previous frames.
/// Assign null to reset.
/// </summary>
Expand Down Expand Up @@ -92,10 +92,11 @@ public override void ApplyState()
? null
: new MaskingInfo
{
ScreenSpaceAABB = Source.ScreenSpaceDrawQuad.AABB,
MaskingRect = Source.DrawRectangle.Normalize(),
ScreenSpaceScissorArea = Source.ScreenSpaceDrawQuad.AABB,
MaskingArea = Source.DrawRectangle.Normalize(),
ConservativeScreenSpaceQuad = Quad.FromRectangle(shrunkDrawRectangle) * DrawInfo.Matrix,
ToMaskingSpace = DrawInfo.MatrixInverse,
ToScissorSpace = Matrix3.Identity,
CornerRadius = Source.effectiveCornerRadius,
CornerExponent = Source.CornerExponent,
BorderThickness = Source.BorderThickness,
Expand All @@ -121,13 +122,13 @@ private void drawEdgeEffect(IRenderer renderer)
if (maskingInfo == null || edgeEffect.Type == EdgeEffectType.None || edgeEffect.Radius <= 0.0f || edgeEffect.Colour.Alpha <= 0)
return;

RectangleF effectRect = maskingInfo.Value.MaskingRect.Inflate(edgeEffect.Radius).Offset(edgeEffect.Offset);
RectangleF effectRect = maskingInfo.Value.MaskingArea.Inflate(edgeEffect.Radius).Offset(edgeEffect.Offset);

screenSpaceMaskingQuad ??= Quad.FromRectangle(effectRect) * DrawInfo.Matrix;

MaskingInfo edgeEffectMaskingInfo = maskingInfo.Value;
edgeEffectMaskingInfo.MaskingRect = effectRect;
edgeEffectMaskingInfo.ScreenSpaceAABB = screenSpaceMaskingQuad.Value.AABB;
edgeEffectMaskingInfo.MaskingArea = effectRect;
edgeEffectMaskingInfo.ScreenSpaceScissorArea = screenSpaceMaskingQuad.Value.AABB;
edgeEffectMaskingInfo.CornerRadius = maskingInfo.Value.CornerRadius + edgeEffect.Radius + edgeEffect.Roundness;
edgeEffectMaskingInfo.BorderThickness = 0;
// HACK HACK HACK. We abuse blend range to give us the linear alpha gradient of
Expand Down
1 change: 1 addition & 0 deletions osu.Framework/Graphics/OpenGL/GLRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ public void DrawVertices(PrimitiveType type, int vertexStart, int verticesCount)
var glShader = (GLShader)Shader!;

glShader.BindUniformBlock("g_GlobalUniforms", GlobalUniformBuffer!);
glShader.BindUniformBlock("g_MaskingBuffer", ShaderMaskingStack!.CurrentBuffer);

int currentUniformBinding = 0;
int currentStorageBinding = 0;
Expand Down
18 changes: 17 additions & 1 deletion osu.Framework/Graphics/OpenGL/Shaders/GLShaderPart.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using osu.Framework.Graphics.Rendering;
using osu.Framework.Graphics.Shaders;
Expand Down Expand Up @@ -43,11 +44,26 @@ public GLShaderPart(GLRenderer renderer, string name, byte[]? data, ShaderType t
// Load the shader files.
shaderCodes.Add(loadFile(data, true));

// Find the minimum uniform/buffer binding set across all shader codes. This will be a negative number (see sh_GlobalUniforms.h).
int minSet = 0;

foreach (string code in shaderCodes)
{
minSet = Math.Min(minSet, uniform_pattern.Matches(code)
.Where(m => m.Success)
.Select(m => int.Parse(m.Groups[2].Value, CultureInfo.InvariantCulture))
.DefaultIfEmpty(0).Min());
}

// Increment the binding set of all uniform blocks.
// After this transformation, the g_GlobalUniforms block is placed in set 0 and all other user blocks begin from 1.
// The difference in implementation here (compared to above) is intentional, as uniform blocks must be consistent between the shader stages, so they can't be easily appended.
for (int i = 0; i < shaderCodes.Count; i++)
shaderCodes[i] = uniform_pattern.Replace(shaderCodes[i], match => $"{match.Groups[1].Value}set = {int.Parse(match.Groups[2].Value, CultureInfo.InvariantCulture) + 1}{match.Groups[3].Value}");
{
shaderCodes[i] = uniform_pattern.Replace(
shaderCodes[i],
match => $"{match.Groups[1].Value}set = {int.Parse(match.Groups[2].Value, CultureInfo.InvariantCulture) + Math.Abs(minSet)}{match.Groups[3].Value}");
}
}

private string loadFile(byte[]? bytes, bool mainFile)
Expand Down
1 change: 1 addition & 0 deletions osu.Framework/Graphics/Rendering/Dummy/DummyRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public sealed class DummyRenderer : IRenderer
public bool IsUvOriginTopLeft => true;
public bool IsClipSpaceYInverted => true;
public ref readonly MaskingInfo CurrentMaskingInfo => ref maskingInfo;
public int CurrentMaskingIndex => 0;
private readonly MaskingInfo maskingInfo;

public RectangleI Viewport => RectangleI.Empty;
Expand Down
1 change: 0 additions & 1 deletion osu.Framework/Graphics/Rendering/FlushBatchSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ public enum FlushBatchSource
SetBlendMask,
SetDepthInfo,
SetFrameBuffer,
SetMasking,
SetProjection,
SetScissor,
SetShader,
Expand Down
16 changes: 1 addition & 15 deletions osu.Framework/Graphics/Rendering/GlobalUniformData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,9 @@ public record struct GlobalUniformData
public UniformBool IsUvOriginTopLeft;

public UniformMatrix4 ProjMatrix;
public UniformMatrix3 ToMaskingSpace;
public UniformBool IsMasking;
public UniformFloat CornerRadius;
public UniformFloat CornerExponent;
private readonly UniformPadding4 pad2;

public UniformVector4 MaskingRect;
public UniformFloat BorderThickness;
private readonly UniformPadding12 pad3;

public UniformMatrix4 BorderColour;
public UniformFloat MaskingBlendRange;
public UniformFloat AlphaExponent;
public UniformVector2 EdgeOffset;
public UniformBool DiscardInner;
public UniformFloat InnerCornerRadius;
public UniformInt WrapModeS;
public UniformInt WrapModeT;
private readonly UniformPadding8 pad1;
}
}
13 changes: 2 additions & 11 deletions osu.Framework/Graphics/Rendering/IRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ public interface IRenderer
/// </summary>
ref readonly MaskingInfo CurrentMaskingInfo { get; }

int CurrentMaskingIndex { get; }

/// <summary>
/// The current viewport.
/// </summary>
Expand Down Expand Up @@ -276,17 +278,6 @@ public interface IRenderer
/// </summary>
void PopScissor();

/// <summary>
/// Applies a new scissor offset to the scissor rectangle.
/// </summary>
/// <param name="offset">The scissor offset.</param>
void PushScissorOffset(Vector2I offset);

/// <summary>
/// Restores the last scissor offset.
/// </summary>
void PopScissorOffset();

/// <summary>
/// Applies a new projection matrix.
/// </summary>
Expand Down
31 changes: 24 additions & 7 deletions osu.Framework/Graphics/Rendering/MaskingInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,34 @@ namespace osu.Framework.Graphics.Rendering
{
public struct MaskingInfo : IEquatable<MaskingInfo>
{
public RectangleI ScreenSpaceAABB;
public RectangleF MaskingRect;
/// <summary>
/// A rectangle that defines the scissor area in screen-space coordinates.
/// </summary>
public RectangleI ScreenSpaceScissorArea;

/// <summary>
/// A rectangle that defines the masking area in the local-space (i.e. <see cref="Drawable.DrawRectangle"/>) of the masking container.
/// </summary>
public RectangleF MaskingArea;

/// <summary>
/// A quad representing the internal "safe" (without borders, corners, and AA smoothening) area of the masking container.
/// </summary>
/// <remarks>
/// This is used to clip drawn polygons during the front-to-back pass such that only areas guaranteed to be visible are drawn.
/// </remarks>
public Quad ConservativeScreenSpaceQuad;

/// <summary>
/// This matrix transforms screen space coordinates to masking space (likely the parent
/// space of the container doing the masking).
/// It is used by a shader to determine which pixels to discard.
/// A matrix that converts from vertex coordinates to the space of <see cref="MaskingArea"/>.
/// </summary>
public Matrix3 ToMaskingSpace;

/// <summary>
/// A matrix that converts from vertex coordinates to the space of <see cref="ScreenSpaceScissorArea"/>.
/// </summary>
public Matrix3 ToScissorSpace;

public float CornerRadius;
public float CornerExponent;

Expand All @@ -39,10 +55,11 @@ public struct MaskingInfo : IEquatable<MaskingInfo>
public readonly bool Equals(MaskingInfo other) => this == other;

public static bool operator ==(in MaskingInfo left, in MaskingInfo right) =>
left.ScreenSpaceAABB == right.ScreenSpaceAABB &&
left.MaskingRect == right.MaskingRect &&
left.ScreenSpaceScissorArea == right.ScreenSpaceScissorArea &&
left.MaskingArea == right.MaskingArea &&
left.ConservativeScreenSpaceQuad.Equals(right.ConservativeScreenSpaceQuad) &&
left.ToMaskingSpace == right.ToMaskingSpace &&
left.ToScissorSpace == right.ToScissorSpace &&
left.CornerRadius == right.CornerRadius &&
left.CornerExponent == right.CornerExponent &&
left.BorderThickness == right.BorderThickness &&
Expand Down
Loading
Loading