Skip to content

Commit

Permalink
Merge pull request #5952 from smoogipoo/masking-ssbo-2
Browse files Browse the repository at this point in the history
Use SSBOs for bindless masking
  • Loading branch information
bdach committed Aug 21, 2023
2 parents a8a1f13 + be8894e commit 844a9ee
Show file tree
Hide file tree
Showing 27 changed files with 461 additions and 190 deletions.
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 = Source.DrawRectangle;
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
6 changes: 6 additions & 0 deletions osu.Framework/Graphics/IDrawable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ public interface IDrawable : ITransformable
/// </summary>
Vector2 DrawSize { get; }

/// <summary>
/// Absolutely sized rectangle for drawing in the <see cref="Parent"/>'s coordinate system.
/// Based on <see cref="DrawSize"/>.
/// </summary>
RectangleF DrawRectangle { get; }

/// <summary>
/// Contains a linear transformation, colour information, and blending information
/// of this drawable.
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
24 changes: 20 additions & 4 deletions 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));

// 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.
// Find the minimum uniform/buffer binding set across all shader codes. This will be a negative number (see sh_GlobalUniforms.h / sh_MaskingInfo.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 equal to the absolute value of the minimum set from above.
// After this transformation, blocks with negative sets will start from set 0, and all other user blocks begin after them.
// The reason for doing this is that uniform blocks must be consistent between the shader stages, so they can't be 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

0 comments on commit 844a9ee

Please sign in to comment.