mirror of
https://github.com/SK-la/osu-framework.git
synced 2026-03-15 03:20:30 +00:00
Merge branch 'master' into update-veldrid-spirv-on-ios
This commit is contained in:
@@ -82,18 +82,18 @@ namespace osu.Framework.Android
|
||||
if (!url.CheckIsValidUrl())
|
||||
throw new ArgumentException("The provided URL must be one of either http://, https:// or mailto: protocols.", nameof(url));
|
||||
|
||||
using (var intent = new Intent(Intent.ActionView, Uri.Parse(url)))
|
||||
try
|
||||
{
|
||||
// Recommended way to open URLs on Android 11+
|
||||
// https://developer.android.com/training/package-visibility/use-cases#open-urls-browser-or-other-app
|
||||
try
|
||||
using (var intent = new Intent(Intent.ActionView, Uri.Parse(url)))
|
||||
{
|
||||
// Recommended way to open URLs on Android 11+
|
||||
// https://developer.android.com/training/package-visibility/use-cases#open-urls-browser-or-other-app
|
||||
gameView.Activity.StartActivity(intent);
|
||||
}
|
||||
catch (ActivityNotFoundException e)
|
||||
{
|
||||
Logger.Error(e, $"Failed to start intent: {intent}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error(ex, "Unable to open external link.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -692,7 +692,6 @@ namespace osu.Framework.Tests.Visual.Drawables
|
||||
public bool IsRunning => offsetClock.IsRunning;
|
||||
public double ElapsedFrameTime => (reversed ? -1 : 1) * trackingClock.ElapsedFrameTime;
|
||||
public double FramesPerSecond => trackingClock.FramesPerSecond;
|
||||
public FrameTimeInfo TimeInfo => new FrameTimeInfo { Current = CurrentTime, Elapsed = ElapsedFrameTime };
|
||||
|
||||
public void ProcessFrame()
|
||||
{
|
||||
|
||||
@@ -14,6 +14,7 @@ using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Framework.iOS.Graphics.Textures;
|
||||
using osu.Framework.iOS.Graphics.Video;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Framework.Platform.MacOS;
|
||||
using UIKit;
|
||||
@@ -58,15 +59,26 @@ namespace osu.Framework.iOS
|
||||
|
||||
public override void OpenUrlExternally(string url)
|
||||
{
|
||||
if (!url.CheckIsValidUrl())
|
||||
if (!url.CheckIsValidUrl()
|
||||
// App store links
|
||||
&& !url.StartsWith("itms-apps://", StringComparison.Ordinal)
|
||||
// Testflight links
|
||||
&& !url.StartsWith("itms-beta://", StringComparison.Ordinal))
|
||||
throw new ArgumentException("The provided URL must be one of either http://, https:// or mailto: protocols.", nameof(url));
|
||||
|
||||
UIApplication.SharedApplication.InvokeOnMainThread(() =>
|
||||
try
|
||||
{
|
||||
NSUrl nsurl = NSUrl.FromString(url).AsNonNull();
|
||||
if (UIApplication.SharedApplication.CanOpenUrl(nsurl))
|
||||
UIApplication.SharedApplication.OpenUrl(nsurl, new NSDictionary(), null);
|
||||
});
|
||||
UIApplication.SharedApplication.InvokeOnMainThread(() =>
|
||||
{
|
||||
NSUrl nsurl = NSUrl.FromString(url).AsNonNull();
|
||||
if (UIApplication.SharedApplication.CanOpenUrl(nsurl))
|
||||
UIApplication.SharedApplication.OpenUrl(nsurl, new NSDictionary(), null);
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error(ex, "Unable to open external link.");
|
||||
}
|
||||
}
|
||||
|
||||
public override IResourceStore<TextureUpload> CreateTextureLoaderStore(IResourceStore<byte[]> underlyingStore)
|
||||
|
||||
@@ -336,7 +336,7 @@ namespace osu.Framework.Graphics.Audio
|
||||
// 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.ScreenSpaceScissorArea) * DrawInfo.MatrixInverse).AABBFloat;
|
||||
RectangleF localMaskingRectangle = (Quad.FromRectangle(renderer.CurrentMaskingInfo.ScreenSpaceAABB) * DrawInfo.MatrixInverse).AABBFloat;
|
||||
|
||||
float separation = drawSize.X / (points.Count - 1);
|
||||
|
||||
|
||||
@@ -35,7 +35,6 @@ namespace osu.Framework.Graphics
|
||||
protected RectangleF DrawRectangle { get; private set; }
|
||||
|
||||
private Color4 backgroundColour;
|
||||
private RectangleF localDrawRectangle;
|
||||
private RectangleF screenSpaceDrawRectangle;
|
||||
private Vector2 frameBufferScale;
|
||||
private Vector2 frameBufferSize;
|
||||
@@ -53,7 +52,6 @@ namespace osu.Framework.Graphics
|
||||
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;
|
||||
@@ -157,12 +155,17 @@ namespace osu.Framework.Graphics
|
||||
|
||||
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
|
||||
{
|
||||
ScreenSpaceScissorArea = screenSpaceDrawRectangle,
|
||||
MaskingArea = localDrawRectangle,
|
||||
ToMaskingSpace = DrawInfo.MatrixInverse,
|
||||
ToScissorSpace = Matrix3.Identity,
|
||||
ScreenSpaceAABB = screenSpaceMaskingRect,
|
||||
MaskingRect = screenSpaceDrawRectangle,
|
||||
ToMaskingSpace = Matrix3.Identity,
|
||||
BlendRange = 1,
|
||||
AlphaExponent = 1,
|
||||
}, true);
|
||||
@@ -170,12 +173,14 @@ namespace osu.Framework.Graphics
|
||||
// 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();
|
||||
|
||||
@@ -266,7 +266,7 @@ namespace osu.Framework.Graphics.Containers
|
||||
private void load(ShaderManager shaders)
|
||||
{
|
||||
TextureShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE);
|
||||
blurShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2_NO_MASKING, FragmentShaderDescriptor.BLUR);
|
||||
blurShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.BLUR);
|
||||
}
|
||||
|
||||
protected override DrawNode CreateDrawNode() => new BufferedContainerDrawNode(this, sharedData);
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace osu.Framework.Graphics.Containers
|
||||
private MaskingInfo? maskingInfo;
|
||||
|
||||
/// <summary>
|
||||
/// The screen-space version of <see cref="MaskingInfo.MaskingArea"/>.
|
||||
/// The screen-space version of <see cref="MaskingInfo.MaskingRect"/>.
|
||||
/// Used as cache of screen-space masking quads computed in previous frames.
|
||||
/// Assign null to reset.
|
||||
/// </summary>
|
||||
@@ -92,11 +92,10 @@ namespace osu.Framework.Graphics.Containers
|
||||
? null
|
||||
: new MaskingInfo
|
||||
{
|
||||
ScreenSpaceScissorArea = Source.ScreenSpaceDrawQuad.AABBFloat,
|
||||
MaskingArea = Source.DrawRectangle.Normalize(),
|
||||
ScreenSpaceAABB = Source.ScreenSpaceDrawQuad.AABB,
|
||||
MaskingRect = Source.DrawRectangle.Normalize(),
|
||||
ConservativeScreenSpaceQuad = Quad.FromRectangle(shrunkDrawRectangle) * DrawInfo.Matrix,
|
||||
ToMaskingSpace = DrawInfo.MatrixInverse,
|
||||
ToScissorSpace = Matrix3.Identity,
|
||||
CornerRadius = Source.effectiveCornerRadius,
|
||||
CornerExponent = Source.CornerExponent,
|
||||
BorderThickness = Source.BorderThickness,
|
||||
@@ -122,13 +121,13 @@ namespace osu.Framework.Graphics.Containers
|
||||
if (maskingInfo == null || edgeEffect.Type == EdgeEffectType.None || edgeEffect.Radius <= 0.0f || edgeEffect.Colour.Alpha <= 0)
|
||||
return;
|
||||
|
||||
RectangleF effectRect = maskingInfo.Value.MaskingArea.Inflate(edgeEffect.Radius).Offset(edgeEffect.Offset);
|
||||
RectangleF effectRect = maskingInfo.Value.MaskingRect.Inflate(edgeEffect.Radius).Offset(edgeEffect.Offset);
|
||||
|
||||
screenSpaceMaskingQuad ??= Quad.FromRectangle(effectRect) * DrawInfo.Matrix;
|
||||
|
||||
MaskingInfo edgeEffectMaskingInfo = maskingInfo.Value;
|
||||
edgeEffectMaskingInfo.MaskingArea = effectRect;
|
||||
edgeEffectMaskingInfo.ScreenSpaceScissorArea = screenSpaceMaskingQuad.Value.AABBFloat;
|
||||
edgeEffectMaskingInfo.MaskingRect = effectRect;
|
||||
edgeEffectMaskingInfo.ScreenSpaceAABB = 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
|
||||
|
||||
@@ -283,7 +283,6 @@ namespace osu.Framework.Graphics.OpenGL
|
||||
var glShader = (GLShader)Shader!;
|
||||
|
||||
glShader.BindUniformBlock("g_GlobalUniforms", GlobalUniformBuffer!);
|
||||
glShader.BindUniformBlock("g_MaskingBuffer", ShaderMaskingStack!.CurrentBuffer);
|
||||
|
||||
int currentUniformBinding = 0;
|
||||
int currentStorageBinding = 0;
|
||||
|
||||
@@ -29,7 +29,6 @@ namespace osu.Framework.Graphics.Rendering.Dummy
|
||||
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;
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace osu.Framework.Graphics.Rendering
|
||||
SetBlendMask,
|
||||
SetDepthInfo,
|
||||
SetFrameBuffer,
|
||||
SetMasking,
|
||||
SetProjection,
|
||||
SetScissor,
|
||||
SetShader,
|
||||
|
||||
@@ -16,9 +16,23 @@ namespace osu.Framework.Graphics.Rendering
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,8 +98,6 @@ namespace osu.Framework.Graphics.Rendering
|
||||
/// </summary>
|
||||
ref readonly MaskingInfo CurrentMaskingInfo { get; }
|
||||
|
||||
int CurrentMaskingIndex { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The current viewport.
|
||||
/// </summary>
|
||||
@@ -278,6 +276,17 @@ namespace osu.Framework.Graphics.Rendering
|
||||
/// </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>
|
||||
|
||||
@@ -10,34 +10,18 @@ namespace osu.Framework.Graphics.Rendering
|
||||
{
|
||||
public struct MaskingInfo : IEquatable<MaskingInfo>
|
||||
{
|
||||
/// <summary>
|
||||
/// A rectangle that defines the scissor area in screen-space coordinates.
|
||||
/// </summary>
|
||||
public RectangleF ScreenSpaceScissorArea;
|
||||
public RectangleI ScreenSpaceAABB;
|
||||
public RectangleF MaskingRect;
|
||||
|
||||
/// <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>
|
||||
/// A matrix that converts from vertex coordinates to the space of <see cref="MaskingArea"/>.
|
||||
/// 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.
|
||||
/// </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;
|
||||
|
||||
@@ -55,11 +39,10 @@ namespace osu.Framework.Graphics.Rendering
|
||||
public readonly bool Equals(MaskingInfo other) => this == other;
|
||||
|
||||
public static bool operator ==(in MaskingInfo left, in MaskingInfo right) =>
|
||||
left.ScreenSpaceScissorArea == right.ScreenSpaceScissorArea &&
|
||||
left.MaskingArea == right.MaskingArea &&
|
||||
left.ScreenSpaceAABB == right.ScreenSpaceAABB &&
|
||||
left.MaskingRect == right.MaskingRect &&
|
||||
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 &&
|
||||
|
||||
@@ -59,7 +59,6 @@ namespace osu.Framework.Graphics.Rendering
|
||||
public ulong FrameIndex { get; private set; }
|
||||
|
||||
public ref readonly MaskingInfo CurrentMaskingInfo => ref currentMaskingInfo;
|
||||
public int CurrentMaskingIndex => ShaderMaskingStack?.CurrentOffset ?? 0;
|
||||
|
||||
public RectangleI Viewport { get; private set; }
|
||||
public RectangleI Scissor { get; private set; }
|
||||
@@ -131,8 +130,6 @@ namespace osu.Framework.Graphics.Rendering
|
||||
private readonly LockedWeakList<Texture> allTextures = new LockedWeakList<Texture>();
|
||||
|
||||
protected IUniformBuffer<GlobalUniformData>? GlobalUniformBuffer { get; private set; }
|
||||
protected ShaderStorageBufferObjectStack<ShaderMaskingInfo>? ShaderMaskingStack { get; private set; }
|
||||
|
||||
private IVertexBatch<TexturedVertex2D>? defaultQuadBatch;
|
||||
private IVertexBatch? currentActiveBatch;
|
||||
private MaskingInfo currentMaskingInfo;
|
||||
@@ -199,10 +196,6 @@ namespace osu.Framework.Graphics.Rendering
|
||||
IsUvOriginTopLeft = IsUvOriginTopLeft
|
||||
};
|
||||
|
||||
// 60 elements keeps the total data length under 16KiB (16320).
|
||||
ShaderMaskingStack ??= new ShaderStorageBufferObjectStack<ShaderMaskingInfo>(this, 60, 8192);
|
||||
ShaderMaskingStack.Clear();
|
||||
|
||||
Debug.Assert(defaultQuadBatch != null);
|
||||
|
||||
FrameIndex++;
|
||||
@@ -255,12 +248,12 @@ namespace osu.Framework.Graphics.Rendering
|
||||
PushScissorState(true);
|
||||
PushViewport(new RectangleI(0, 0, (int)windowSize.X, (int)windowSize.Y));
|
||||
PushScissor(new RectangleI(0, 0, (int)windowSize.X, (int)windowSize.Y));
|
||||
PushScissorOffset(Vector2I.Zero);
|
||||
PushMaskingInfo(new MaskingInfo
|
||||
{
|
||||
ScreenSpaceScissorArea = new RectangleI(0, 0, (int)windowSize.X, (int)windowSize.Y),
|
||||
MaskingArea = new RectangleF(0, 0, windowSize.X, windowSize.Y),
|
||||
ScreenSpaceAABB = new RectangleI(0, 0, (int)windowSize.X, (int)windowSize.Y),
|
||||
MaskingRect = new RectangleF(0, 0, windowSize.X, windowSize.Y),
|
||||
ToMaskingSpace = Matrix3.Identity,
|
||||
ToScissorSpace = Matrix3.Identity,
|
||||
BlendRange = 1,
|
||||
AlphaExponent = 1,
|
||||
CornerExponent = 2.5f,
|
||||
@@ -496,6 +489,12 @@ namespace osu.Framework.Graphics.Rendering
|
||||
setScissorState(enabled);
|
||||
}
|
||||
|
||||
public void PushScissorOffset(Vector2I offset)
|
||||
{
|
||||
scissorOffsetStack.Push(offset);
|
||||
setScissorOffset(offset);
|
||||
}
|
||||
|
||||
public void PopScissor()
|
||||
{
|
||||
Trace.Assert(scissorRectStack.Count > 1);
|
||||
@@ -512,6 +511,14 @@ namespace osu.Framework.Graphics.Rendering
|
||||
setScissorState(scissorStateStack.Peek());
|
||||
}
|
||||
|
||||
public void PopScissorOffset()
|
||||
{
|
||||
Trace.Assert(scissorOffsetStack.Count > 1);
|
||||
|
||||
scissorOffsetStack.Pop();
|
||||
setScissorOffset(scissorOffsetStack.Peek());
|
||||
}
|
||||
|
||||
private void setScissor(RectangleI scissor)
|
||||
{
|
||||
if (scissor.Width < 0)
|
||||
@@ -552,6 +559,15 @@ namespace osu.Framework.Graphics.Rendering
|
||||
ScissorState = enabled;
|
||||
}
|
||||
|
||||
private void setScissorOffset(Vector2I offset)
|
||||
{
|
||||
if (ScissorOffset == offset)
|
||||
return;
|
||||
|
||||
FlushCurrentBatch(FlushBatchSource.SetScissor);
|
||||
ScissorOffset = offset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the graphics device with a new scissor rectangle.
|
||||
/// </summary>
|
||||
@@ -616,74 +632,71 @@ namespace osu.Framework.Graphics.Rendering
|
||||
if (CurrentMaskingInfo == maskingInfo)
|
||||
return;
|
||||
|
||||
FlushCurrentBatch(FlushBatchSource.SetMasking);
|
||||
|
||||
GlobalUniformBuffer!.Data = GlobalUniformBuffer.Data with
|
||||
{
|
||||
IsMasking = IsMaskingActive,
|
||||
MaskingRect = new Vector4(
|
||||
maskingInfo.MaskingRect.Left,
|
||||
maskingInfo.MaskingRect.Top,
|
||||
maskingInfo.MaskingRect.Right,
|
||||
maskingInfo.MaskingRect.Bottom),
|
||||
ToMaskingSpace = maskingInfo.ToMaskingSpace,
|
||||
CornerRadius = maskingInfo.CornerRadius,
|
||||
CornerExponent = maskingInfo.CornerExponent,
|
||||
BorderThickness = maskingInfo.BorderThickness / maskingInfo.BlendRange,
|
||||
BorderColour = maskingInfo.BorderThickness > 0
|
||||
? new Matrix4(
|
||||
// TopLeft
|
||||
maskingInfo.BorderColour.TopLeft.SRGB.R,
|
||||
maskingInfo.BorderColour.TopLeft.SRGB.G,
|
||||
maskingInfo.BorderColour.TopLeft.SRGB.B,
|
||||
maskingInfo.BorderColour.TopLeft.SRGB.A,
|
||||
// BottomLeft
|
||||
maskingInfo.BorderColour.BottomLeft.SRGB.R,
|
||||
maskingInfo.BorderColour.BottomLeft.SRGB.G,
|
||||
maskingInfo.BorderColour.BottomLeft.SRGB.B,
|
||||
maskingInfo.BorderColour.BottomLeft.SRGB.A,
|
||||
// TopRight
|
||||
maskingInfo.BorderColour.TopRight.SRGB.R,
|
||||
maskingInfo.BorderColour.TopRight.SRGB.G,
|
||||
maskingInfo.BorderColour.TopRight.SRGB.B,
|
||||
maskingInfo.BorderColour.TopRight.SRGB.A,
|
||||
// BottomRight
|
||||
maskingInfo.BorderColour.BottomRight.SRGB.R,
|
||||
maskingInfo.BorderColour.BottomRight.SRGB.G,
|
||||
maskingInfo.BorderColour.BottomRight.SRGB.B,
|
||||
maskingInfo.BorderColour.BottomRight.SRGB.A)
|
||||
: GlobalUniformBuffer.Data.BorderColour,
|
||||
MaskingBlendRange = maskingInfo.BlendRange,
|
||||
AlphaExponent = maskingInfo.AlphaExponent,
|
||||
EdgeOffset = maskingInfo.EdgeOffset,
|
||||
DiscardInner = maskingInfo.Hollow,
|
||||
InnerCornerRadius = maskingInfo.Hollow
|
||||
? maskingInfo.HollowCornerRadius
|
||||
: GlobalUniformBuffer.Data.InnerCornerRadius
|
||||
};
|
||||
|
||||
if (isPushing)
|
||||
{
|
||||
RectangleF scissorRect = maskingInfo.ScreenSpaceScissorArea;
|
||||
// When drawing to a viewport that doesn't match the projection size (e.g. via framebuffers), the resultant image will be scaled
|
||||
Vector2 projectionScale = new Vector2(ProjectionMatrix.Row0.X / 2, -ProjectionMatrix.Row1.Y / 2);
|
||||
Vector2 viewportScale = Vector2.Multiply(Viewport.Size, projectionScale);
|
||||
|
||||
if (!overwritePreviousScissor)
|
||||
{
|
||||
Vector4 currentSmiScissorRectangle = ShaderMaskingStack!.CurrentBuffer[ShaderMaskingStack.CurrentOffset].ScissorRect;
|
||||
RectangleF currentScissorRectangle = RectangleF.FromLTRB(
|
||||
currentSmiScissorRectangle.X,
|
||||
currentSmiScissorRectangle.Y,
|
||||
currentSmiScissorRectangle.Z,
|
||||
currentSmiScissorRectangle.W);
|
||||
Vector2 location = (maskingInfo.ScreenSpaceAABB.Location - ScissorOffset) * viewportScale;
|
||||
Vector2 size = maskingInfo.ScreenSpaceAABB.Size * viewportScale;
|
||||
|
||||
scissorRect = RectangleF.Intersect(currentScissorRectangle, scissorRect);
|
||||
}
|
||||
RectangleI actualRect = new RectangleI(
|
||||
(int)Math.Floor(location.X),
|
||||
(int)Math.Floor(location.Y),
|
||||
(int)Math.Ceiling(size.X),
|
||||
(int)Math.Ceiling(size.Y));
|
||||
|
||||
ShaderMaskingStack!.Push(new ShaderMaskingInfo
|
||||
{
|
||||
IsMasking = IsMaskingActive,
|
||||
MaskingRect = new Vector4(
|
||||
maskingInfo.MaskingArea.Left,
|
||||
maskingInfo.MaskingArea.Top,
|
||||
maskingInfo.MaskingArea.Right,
|
||||
maskingInfo.MaskingArea.Bottom),
|
||||
ScissorRect = new Vector4(
|
||||
scissorRect.Left,
|
||||
scissorRect.Top,
|
||||
scissorRect.Right,
|
||||
scissorRect.Bottom),
|
||||
ToMaskingSpace = new Matrix4(maskingInfo.ToMaskingSpace),
|
||||
ToScissorSpace = new Matrix4(maskingInfo.ToScissorSpace),
|
||||
CornerRadius = maskingInfo.CornerRadius,
|
||||
CornerExponent = maskingInfo.CornerExponent,
|
||||
BorderThickness = maskingInfo.BorderThickness / maskingInfo.BlendRange,
|
||||
BorderColour = maskingInfo.BorderThickness > 0
|
||||
? new Matrix4(
|
||||
// TopLeft
|
||||
maskingInfo.BorderColour.TopLeft.SRGB.R,
|
||||
maskingInfo.BorderColour.TopLeft.SRGB.G,
|
||||
maskingInfo.BorderColour.TopLeft.SRGB.B,
|
||||
maskingInfo.BorderColour.TopLeft.SRGB.A,
|
||||
// BottomLeft
|
||||
maskingInfo.BorderColour.BottomLeft.SRGB.R,
|
||||
maskingInfo.BorderColour.BottomLeft.SRGB.G,
|
||||
maskingInfo.BorderColour.BottomLeft.SRGB.B,
|
||||
maskingInfo.BorderColour.BottomLeft.SRGB.A,
|
||||
// TopRight
|
||||
maskingInfo.BorderColour.TopRight.SRGB.R,
|
||||
maskingInfo.BorderColour.TopRight.SRGB.G,
|
||||
maskingInfo.BorderColour.TopRight.SRGB.B,
|
||||
maskingInfo.BorderColour.TopRight.SRGB.A,
|
||||
// BottomRight
|
||||
maskingInfo.BorderColour.BottomRight.SRGB.R,
|
||||
maskingInfo.BorderColour.BottomRight.SRGB.G,
|
||||
maskingInfo.BorderColour.BottomRight.SRGB.B,
|
||||
maskingInfo.BorderColour.BottomRight.SRGB.A)
|
||||
: ShaderMaskingStack.CurrentBuffer[ShaderMaskingStack.CurrentOffset].BorderColour,
|
||||
MaskingBlendRange = maskingInfo.BlendRange,
|
||||
AlphaExponent = maskingInfo.AlphaExponent,
|
||||
EdgeOffset = maskingInfo.EdgeOffset,
|
||||
DiscardInner = maskingInfo.Hollow,
|
||||
InnerCornerRadius = maskingInfo.Hollow
|
||||
? maskingInfo.HollowCornerRadius
|
||||
: ShaderMaskingStack.CurrentBuffer[ShaderMaskingStack.CurrentOffset].InnerCornerRadius,
|
||||
});
|
||||
PushScissor(overwritePreviousScissor ? actualRect : RectangleI.Intersect(scissorRectStack.Peek(), actualRect));
|
||||
}
|
||||
else
|
||||
ShaderMaskingStack!.Pop();
|
||||
PopScissor();
|
||||
|
||||
currentMaskingInfo = maskingInfo;
|
||||
}
|
||||
|
||||
@@ -263,14 +263,9 @@ namespace osu.Framework.Graphics.Rendering
|
||||
public static void PushLocalMatrix(this IRenderer renderer, Matrix4 matrix)
|
||||
{
|
||||
var currentMasking = renderer.CurrentMaskingInfo;
|
||||
|
||||
// Normally, ToMaskingSpace is used to convert from screen-space coordinates to local coordinates in the masking-space.
|
||||
// But if a local matrix is pushed, then vertices will instead be provided in local-space, such that:
|
||||
// 1. To convert to masking-space we need to first convert to screen-space.
|
||||
// 2. To convert to scissor-space we need to convert to screen-space.
|
||||
// normally toMaskingSpace is fed vertices already in screen space coordinates,
|
||||
// but since we are modifying the matrix the vertices are in local space
|
||||
currentMasking.ToMaskingSpace = new Matrix3(matrix) * currentMasking.ToMaskingSpace;
|
||||
currentMasking.ToScissorSpace = new Matrix3(matrix);
|
||||
|
||||
renderer.PushMaskingInfo(currentMasking, true);
|
||||
renderer.PushProjectionMatrix(matrix * renderer.ProjectionMatrix);
|
||||
}
|
||||
@@ -279,13 +274,10 @@ namespace osu.Framework.Graphics.Rendering
|
||||
public static void PushLocalMatrix(this IRenderer renderer, Matrix3 matrix)
|
||||
{
|
||||
var currentMasking = renderer.CurrentMaskingInfo;
|
||||
|
||||
// Normally, ToMaskingSpace is used to convert from screen-space coordinates to local coordinates in the masking-space.
|
||||
// But if a local matrix is pushed, then vertices will instead be provided in local-space, such that:
|
||||
// 1. To convert to masking-space we need to first convert to screen-space.
|
||||
// 2. To convert to scissor-space we need to convert to screen-space.
|
||||
// normally toMaskingSpace is fed vertices already in screen space coordinates,
|
||||
// but since we are modifying the matrix the vertices are in local space
|
||||
currentMasking.ToMaskingSpace = matrix * currentMasking.ToMaskingSpace;
|
||||
currentMasking.ToScissorSpace = matrix;
|
||||
renderer.PushMaskingInfo(currentMasking, true);
|
||||
|
||||
// this makes sure it also works for 3D vertices like the ones path uses
|
||||
Matrix4 mat = new Matrix4(matrix);
|
||||
@@ -293,8 +285,6 @@ namespace osu.Framework.Graphics.Rendering
|
||||
mat.Row2.X = 0;
|
||||
mat.Row3.Y = mat.Row2.Y;
|
||||
mat.Row2.Y = 0;
|
||||
|
||||
renderer.PushMaskingInfo(currentMasking, true);
|
||||
renderer.PushProjectionMatrix(mat * renderer.ProjectionMatrix);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
// 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 System.Runtime.InteropServices;
|
||||
using osu.Framework.Graphics.Shaders.Types;
|
||||
|
||||
namespace osu.Framework.Graphics.Rendering
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public record struct ShaderMaskingInfo
|
||||
{
|
||||
public UniformMatrix4 ToMaskingSpace;
|
||||
public UniformMatrix4 ToScissorSpace;
|
||||
|
||||
public UniformBool IsMasking;
|
||||
public UniformFloat CornerRadius;
|
||||
public UniformFloat CornerExponent;
|
||||
public UniformFloat BorderThickness;
|
||||
|
||||
public UniformVector4 MaskingRect;
|
||||
public UniformVector4 ScissorRect;
|
||||
|
||||
public UniformMatrix4 BorderColour;
|
||||
public UniformFloat MaskingBlendRange;
|
||||
public UniformFloat AlphaExponent;
|
||||
public UniformVector2 EdgeOffset;
|
||||
|
||||
public UniformBool DiscardInner;
|
||||
public UniformFloat InnerCornerRadius;
|
||||
private readonly UniformPadding8 pad1;
|
||||
}
|
||||
}
|
||||
@@ -30,9 +30,6 @@ namespace osu.Framework.Graphics.Rendering.Vertices
|
||||
[VertexMember(1, VertexAttribPointerType.Float)]
|
||||
private readonly float backbufferDrawDepth;
|
||||
|
||||
[VertexMember(1, VertexAttribPointerType.Int)]
|
||||
private readonly int maskingIndex;
|
||||
|
||||
[Obsolete("Initialise this type with an IRenderer instead", true)]
|
||||
public TexturedVertex2D()
|
||||
{
|
||||
@@ -43,7 +40,6 @@ namespace osu.Framework.Graphics.Rendering.Vertices
|
||||
{
|
||||
this = default; // explicitly initialise all members to default values
|
||||
backbufferDrawDepth = renderer.BackbufferDrawDepth;
|
||||
maskingIndex = renderer.CurrentMaskingIndex;
|
||||
}
|
||||
|
||||
public readonly bool Equals(TexturedVertex2D other) =>
|
||||
@@ -52,7 +48,6 @@ namespace osu.Framework.Graphics.Rendering.Vertices
|
||||
&& Colour.Equals(other.Colour)
|
||||
&& TextureRect.Equals(other.TextureRect)
|
||||
&& BlendRange.Equals(other.BlendRange)
|
||||
&& backbufferDrawDepth == other.backbufferDrawDepth
|
||||
&& maskingIndex == other.maskingIndex;
|
||||
&& backbufferDrawDepth == other.backbufferDrawDepth;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,13 +21,6 @@ namespace osu.Framework.Graphics.Rendering.Vertices
|
||||
[VertexMember(2, VertexAttribPointerType.Float)]
|
||||
public Vector2 TexturePosition;
|
||||
|
||||
[VertexMember(1, VertexAttribPointerType.Int)]
|
||||
private readonly int maskingIndex;
|
||||
|
||||
public readonly bool Equals(TexturedVertex3D other)
|
||||
=> Position.Equals(other.Position)
|
||||
&& TexturePosition.Equals(other.TexturePosition)
|
||||
&& Colour.Equals(other.Colour)
|
||||
&& maskingIndex == other.maskingIndex;
|
||||
public readonly bool Equals(TexturedVertex3D other) => Position.Equals(other.Position) && TexturePosition.Equals(other.TexturePosition) && Colour.Equals(other.Colour);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,7 +149,6 @@ namespace osu.Framework.Graphics.Shaders
|
||||
public static class VertexShaderDescriptor
|
||||
{
|
||||
public const string TEXTURE_2 = "Texture2D";
|
||||
public const string TEXTURE_2_NO_MASKING = "Texture2D_NoMasking";
|
||||
public const string TEXTURE_3 = "Texture3D";
|
||||
public const string POSITION = "Position";
|
||||
}
|
||||
|
||||
@@ -547,7 +547,6 @@ namespace osu.Framework.Graphics.Veldrid
|
||||
var veldridShader = (VeldridShader)Shader!;
|
||||
|
||||
veldridShader.BindUniformBlock("g_GlobalUniforms", GlobalUniformBuffer!);
|
||||
veldridShader.BindUniformBlock("g_MaskingBuffer", ShaderMaskingStack!.CurrentBuffer);
|
||||
|
||||
pipeline.PrimitiveTopology = type;
|
||||
Array.Resize(ref pipeline.ResourceLayouts, veldridShader.LayoutCount);
|
||||
|
||||
@@ -9,6 +9,7 @@ using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Logging;
|
||||
|
||||
namespace osu.Framework.Platform
|
||||
{
|
||||
@@ -81,7 +82,14 @@ namespace osu.Framework.Platform
|
||||
if (!url.CheckIsValidUrl())
|
||||
throw new ArgumentException("The provided URL must be one of either http://, https:// or mailto: protocols.", nameof(url));
|
||||
|
||||
openUsingShellExecute(url);
|
||||
try
|
||||
{
|
||||
openUsingShellExecute(url);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error(ex, "Unable to open external link.");
|
||||
}
|
||||
}
|
||||
|
||||
public override bool PresentFileExternally(string filename)
|
||||
|
||||
@@ -19,6 +19,19 @@ layout(std140, set = -1, binding = 0) uniform g_GlobalUniforms
|
||||
bool g_IsUvOriginTopLeft;
|
||||
|
||||
mat4 g_ProjMatrix;
|
||||
mat3 g_ToMaskingSpace;
|
||||
|
||||
bool g_IsMasking;
|
||||
highp float g_CornerRadius;
|
||||
highp float g_CornerExponent;
|
||||
highp vec4 g_MaskingRect;
|
||||
highp float g_BorderThickness;
|
||||
lowp mat4 g_BorderColour;
|
||||
mediump float g_MaskingBlendRange;
|
||||
lowp float g_AlphaExponent;
|
||||
highp vec2 g_EdgeOffset;
|
||||
bool g_DiscardInner;
|
||||
highp float g_InnerCornerRadius;
|
||||
|
||||
// 0 -> None
|
||||
// 1 -> ClampToEdge
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
#ifndef INTERNAL_MASKING_INFO_H
|
||||
#define INTERNAL_MASKING_INFO_H
|
||||
|
||||
#extension GL_ARB_shader_storage_buffer_object : enable
|
||||
|
||||
struct MaskingInfo
|
||||
{
|
||||
mat4 ToMaskingSpace;
|
||||
mat4 ToScissorSpace;
|
||||
|
||||
bool IsMasking;
|
||||
highp float CornerRadius;
|
||||
highp float CornerExponent;
|
||||
highp float BorderThickness;
|
||||
|
||||
highp vec4 MaskingRect;
|
||||
highp vec4 ScissorRect;
|
||||
|
||||
lowp mat4 BorderColour;
|
||||
mediump float MaskingBlendRange;
|
||||
lowp float AlphaExponent;
|
||||
highp vec2 EdgeOffset;
|
||||
|
||||
bool DiscardInner;
|
||||
highp float InnerCornerRadius;
|
||||
vec2 pad1;
|
||||
};
|
||||
|
||||
MaskingInfo g_MaskingInfo;
|
||||
|
||||
#ifndef OSU_GRAPHICS_NO_SSBO
|
||||
|
||||
layout(std140, set = -2, binding = 0) readonly buffer g_MaskingBuffer
|
||||
{
|
||||
MaskingInfo Data[];
|
||||
} MaskingBuffer;
|
||||
|
||||
#else // OSU_GRAPHICS_NO_SSBO
|
||||
|
||||
layout(std140, set = -2, binding = 0) uniform g_MaskingBuffer
|
||||
{
|
||||
MaskingInfo Data[60];
|
||||
} MaskingBuffer;
|
||||
|
||||
#endif // OSU_GRAPHICS_NO_SSBO
|
||||
|
||||
void InitMasking(int index)
|
||||
{
|
||||
g_MaskingInfo = MaskingBuffer.Data[index];
|
||||
}
|
||||
|
||||
#endif // INTERNAL_MASKING_INFO_H
|
||||
@@ -1,8 +1,6 @@
|
||||
#ifndef MASKING_H
|
||||
#define MASKING_H
|
||||
|
||||
#include "Internal/sh_MaskingInfo.h"
|
||||
|
||||
layout(location = 0) in highp vec2 v_MaskingPosition;
|
||||
layout(location = 1) in lowp vec4 v_Colour;
|
||||
|
||||
@@ -13,27 +11,14 @@ layout(location = 1) in lowp vec4 v_Colour;
|
||||
#endif
|
||||
|
||||
layout(location = 4) in mediump vec2 v_BlendRange;
|
||||
layout(location = 5) flat in int v_MaskingIndex;
|
||||
layout(location = 6) in highp vec2 v_ScissorPosition;
|
||||
|
||||
/// Positive if outside the rect, negative if inside the rect.
|
||||
highp float distanceFromScissorRect()
|
||||
{
|
||||
highp vec2 topLeftOffset = g_MaskingInfo.ScissorRect.xy - v_ScissorPosition;
|
||||
highp vec2 bottomRightOffset = v_ScissorPosition - g_MaskingInfo.ScissorRect.zw;
|
||||
|
||||
highp vec2 distanceFromShrunkRect = max(bottomRightOffset, topLeftOffset);
|
||||
|
||||
return max(distanceFromShrunkRect.x, distanceFromShrunkRect.y);
|
||||
}
|
||||
|
||||
highp float distanceFromRoundedRect(highp vec2 offset, highp float radius)
|
||||
{
|
||||
highp vec2 maskingPosition = v_MaskingPosition + offset;
|
||||
|
||||
// Compute offset distance from masking rect in masking space.
|
||||
highp vec2 topLeftOffset = g_MaskingInfo.MaskingRect.xy - maskingPosition;
|
||||
highp vec2 bottomRightOffset = maskingPosition - g_MaskingInfo.MaskingRect.zw;
|
||||
highp vec2 topLeftOffset = g_MaskingRect.xy - maskingPosition;
|
||||
highp vec2 bottomRightOffset = maskingPosition - g_MaskingRect.zw;
|
||||
|
||||
highp vec2 distanceFromShrunkRect = max(
|
||||
bottomRightOffset + vec2(radius),
|
||||
@@ -48,7 +33,7 @@ highp float distanceFromRoundedRect(highp vec2 offset, highp float radius)
|
||||
else
|
||||
{
|
||||
distanceFromShrunkRect = max(vec2(0.0), distanceFromShrunkRect);
|
||||
return pow(pow(distanceFromShrunkRect.x, g_MaskingInfo.CornerExponent) + pow(distanceFromShrunkRect.y, g_MaskingInfo.CornerExponent), 1.0 / g_MaskingInfo.CornerExponent);
|
||||
return pow(pow(distanceFromShrunkRect.x, g_CornerExponent) + pow(distanceFromShrunkRect.y, g_CornerExponent), 1.0 / g_CornerExponent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,53 +55,46 @@ highp float distanceFromDrawingRect(mediump vec2 texCoord)
|
||||
|
||||
lowp vec4 getBorderColour()
|
||||
{
|
||||
highp vec2 relativeTexCoord = v_MaskingPosition / (g_MaskingInfo.MaskingRect.zw - g_MaskingInfo.MaskingRect.xy);
|
||||
lowp vec4 top = mix(g_MaskingInfo.BorderColour[0], g_MaskingInfo.BorderColour[2], relativeTexCoord.x);
|
||||
lowp vec4 bottom = mix(g_MaskingInfo.BorderColour[1], g_MaskingInfo.BorderColour[3], relativeTexCoord.x);
|
||||
highp vec2 relativeTexCoord = v_MaskingPosition / (g_MaskingRect.zw - g_MaskingRect.xy);
|
||||
lowp vec4 top = mix(g_BorderColour[0], g_BorderColour[2], relativeTexCoord.x);
|
||||
lowp vec4 bottom = mix(g_BorderColour[1], g_BorderColour[3], relativeTexCoord.x);
|
||||
return mix(top, bottom, relativeTexCoord.y);
|
||||
}
|
||||
|
||||
lowp vec4 getRoundedColor(lowp vec4 texel, mediump vec2 texCoord)
|
||||
{
|
||||
InitMasking(v_MaskingIndex);
|
||||
|
||||
if (!g_MaskingInfo.IsMasking && v_BlendRange == vec2(0.0))
|
||||
if (!g_IsMasking && v_BlendRange == vec2(0.0))
|
||||
{
|
||||
return v_Colour * texel;
|
||||
}
|
||||
|
||||
if (distanceFromScissorRect() > 0)
|
||||
{
|
||||
discard;
|
||||
}
|
||||
|
||||
highp float dist = distanceFromRoundedRect(vec2(0.0), g_MaskingInfo.CornerRadius);
|
||||
highp float dist = distanceFromRoundedRect(vec2(0.0), g_CornerRadius);
|
||||
lowp float alphaFactor = 1.0;
|
||||
|
||||
// Discard inner pixels
|
||||
if (g_MaskingInfo.DiscardInner)
|
||||
if (g_DiscardInner)
|
||||
{
|
||||
highp float innerDist = (g_MaskingInfo.EdgeOffset == vec2(0.0) && g_MaskingInfo.InnerCornerRadius == g_MaskingInfo.CornerRadius) ?
|
||||
dist : distanceFromRoundedRect(g_MaskingInfo.EdgeOffset, g_MaskingInfo.InnerCornerRadius);
|
||||
highp float innerDist = (g_EdgeOffset == vec2(0.0) && g_InnerCornerRadius == g_CornerRadius) ?
|
||||
dist : distanceFromRoundedRect(g_EdgeOffset, g_InnerCornerRadius);
|
||||
|
||||
// v_BlendRange is set from outside in a hacky way to tell us the g_MaskingInfo.MaskingBlendRange used for the rounded
|
||||
// v_BlendRange is set from outside in a hacky way to tell us the g_MaskingBlendRange used for the rounded
|
||||
// corners of the edge effect container itself. We can then derive the alpha factor for smooth inner edge
|
||||
// effect from that.
|
||||
highp float innerBlendFactor = (g_MaskingInfo.InnerCornerRadius - g_MaskingInfo.MaskingBlendRange - innerDist) / v_BlendRange.x;
|
||||
highp float innerBlendFactor = (g_InnerCornerRadius - g_MaskingBlendRange - innerDist) / v_BlendRange.x;
|
||||
if (innerBlendFactor > 1.0)
|
||||
{
|
||||
return vec4(0.0);
|
||||
}
|
||||
|
||||
// We exponentiate our factor to exactly counteract the later exponentiation by g_MaskingInfo.AlphaExponent for a smoother inner border.
|
||||
alphaFactor = pow(min(1.0 - innerBlendFactor, 1.0), 1.0 / g_MaskingInfo.AlphaExponent);
|
||||
// We exponentiate our factor to exactly counteract the later exponentiation by g_AlphaExponent for a smoother inner border.
|
||||
alphaFactor = pow(min(1.0 - innerBlendFactor, 1.0), 1.0 / g_AlphaExponent);
|
||||
}
|
||||
|
||||
dist /= g_MaskingInfo.MaskingBlendRange;
|
||||
dist /= g_MaskingBlendRange;
|
||||
|
||||
// This correction is needed to avoid fading of the alpha value for radii below 1px.
|
||||
highp float radiusCorrection = g_MaskingInfo.CornerRadius <= 0.0 ? g_MaskingInfo.MaskingBlendRange : max(0.0, g_MaskingInfo.MaskingBlendRange - g_MaskingInfo.CornerRadius);
|
||||
highp float fadeStart = (g_MaskingInfo.CornerRadius + radiusCorrection) / g_MaskingInfo.MaskingBlendRange;
|
||||
highp float radiusCorrection = g_CornerRadius <= 0.0 ? g_MaskingBlendRange : max(0.0, g_MaskingBlendRange - g_CornerRadius);
|
||||
highp float fadeStart = (g_CornerRadius + radiusCorrection) / g_MaskingBlendRange;
|
||||
alphaFactor *= min(fadeStart - dist, 1.0);
|
||||
|
||||
if (v_BlendRange.x > 0.0 || v_BlendRange.y > 0.0)
|
||||
@@ -130,9 +108,9 @@ lowp vec4 getRoundedColor(lowp vec4 texel, mediump vec2 texCoord)
|
||||
}
|
||||
|
||||
// This ends up softening glow without negatively affecting edge smoothness much.
|
||||
alphaFactor = pow(alphaFactor, g_MaskingInfo.AlphaExponent);
|
||||
alphaFactor = pow(alphaFactor, g_AlphaExponent);
|
||||
|
||||
highp float borderStart = 1.0 + fadeStart - g_MaskingInfo.BorderThickness;
|
||||
highp float borderStart = 1.0 + fadeStart - g_BorderThickness;
|
||||
lowp float colourWeight = min(borderStart - dist, 1.0);
|
||||
|
||||
lowp vec4 contentColour = v_Colour * texel;
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#define TEXTURE2D_VS
|
||||
|
||||
#include "sh_Utils.h"
|
||||
#include "Internal/sh_MaskingInfo.h"
|
||||
|
||||
layout(location = 0) in highp vec2 m_Position;
|
||||
layout(location = 1) in lowp vec4 m_Colour;
|
||||
@@ -10,33 +9,23 @@ layout(location = 2) in highp vec2 m_TexCoord;
|
||||
layout(location = 3) in highp vec4 m_TexRect;
|
||||
layout(location = 4) in mediump vec2 m_BlendRange;
|
||||
layout(location = 5) in highp float m_BackbufferDrawDepth;
|
||||
layout(location = 6) in int m_MaskingIndex;
|
||||
|
||||
layout(location = 0) out highp vec2 v_MaskingPosition;
|
||||
layout(location = 1) out lowp vec4 v_Colour;
|
||||
layout(location = 2) out highp vec2 v_TexCoord;
|
||||
layout(location = 3) out highp vec4 v_TexRect;
|
||||
layout(location = 4) out mediump vec2 v_BlendRange;
|
||||
layout(location = 5) flat out int v_MaskingIndex;
|
||||
layout(location = 6) out highp vec2 v_ScissorPosition;
|
||||
|
||||
void main(void)
|
||||
{
|
||||
InitMasking(m_MaskingIndex);
|
||||
|
||||
// Transform from screen space to masking space.
|
||||
highp vec4 maskingPos = g_MaskingInfo.ToMaskingSpace * vec4(m_Position, 1.0, 0.0);
|
||||
highp vec3 maskingPos = g_ToMaskingSpace * vec3(m_Position, 1.0);
|
||||
v_MaskingPosition = maskingPos.xy / maskingPos.z;
|
||||
|
||||
// Transform from screen space to scissor space.
|
||||
highp vec4 scissorPos = g_MaskingInfo.ToScissorSpace * vec4(m_Position, 1.0, 0.0);
|
||||
v_ScissorPosition = scissorPos.xy / scissorPos.z;
|
||||
|
||||
v_Colour = m_Colour;
|
||||
v_TexCoord = m_TexCoord;
|
||||
v_TexRect = m_TexRect;
|
||||
v_BlendRange = m_BlendRange;
|
||||
v_MaskingIndex = m_MaskingIndex;
|
||||
|
||||
gl_Position = g_ProjMatrix * vec4(m_Position, 1.0, 1.0);
|
||||
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
#ifndef TEXTURE2D_NO_MASKING_VS
|
||||
#define TEXTURE2D_NO_MASKING_VS
|
||||
|
||||
#include "sh_Utils.h"
|
||||
|
||||
layout(location = 0) in highp vec2 m_Position;
|
||||
layout(location = 1) in lowp vec4 m_Colour;
|
||||
layout(location = 2) in highp vec2 m_TexCoord;
|
||||
layout(location = 3) in highp vec4 m_TexRect;
|
||||
layout(location = 4) in mediump vec2 m_BlendRange;
|
||||
layout(location = 5) in highp float m_BackbufferDrawDepth;
|
||||
|
||||
layout(location = 0) out highp vec2 v_MaskingPosition;
|
||||
layout(location = 1) out lowp vec4 v_Colour;
|
||||
layout(location = 2) out highp vec2 v_TexCoord;
|
||||
layout(location = 3) out highp vec4 v_TexRect;
|
||||
layout(location = 4) out mediump vec2 v_BlendRange;
|
||||
|
||||
void main(void)
|
||||
{
|
||||
v_Colour = m_Colour;
|
||||
v_TexCoord = m_TexCoord;
|
||||
v_TexRect = m_TexRect;
|
||||
v_BlendRange = m_BlendRange;
|
||||
|
||||
gl_Position = g_ProjMatrix * vec4(m_Position, 1.0, 1.0);
|
||||
|
||||
if (g_BackbufferDraw)
|
||||
gl_Position.z = m_BackbufferDrawDepth;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -2,39 +2,28 @@
|
||||
#define TEXTURE3D_VS
|
||||
|
||||
#include "sh_Utils.h"
|
||||
#include "Internal/sh_MaskingInfo.h"
|
||||
|
||||
layout(location = 0) in highp vec3 m_Position;
|
||||
layout(location = 1) in lowp vec4 m_Colour;
|
||||
layout(location = 2) in highp vec2 m_TexCoord;
|
||||
layout(location = 3) in int m_MaskingIndex;
|
||||
|
||||
layout(location = 0) out highp vec2 v_MaskingPosition;
|
||||
layout(location = 1) out lowp vec4 v_Colour;
|
||||
layout(location = 2) out highp vec2 v_TexCoord;
|
||||
layout(location = 3) out highp vec4 v_TexRect;
|
||||
layout(location = 4) out mediump vec2 v_BlendRange;
|
||||
layout(location = 5) flat out int v_MaskingIndex;
|
||||
layout(location = 6) out highp vec2 v_ScissorPosition;
|
||||
|
||||
void main(void)
|
||||
{
|
||||
InitMasking(m_MaskingIndex);
|
||||
|
||||
// Transform from screen space to masking space.
|
||||
highp vec4 maskingPos = g_MaskingInfo.ToMaskingSpace * vec4(m_Position.xy, 1.0, 0.0);
|
||||
// Transform to position to masking space.
|
||||
vec3 maskingPos = g_ToMaskingSpace * vec3(m_Position.xy, 1.0);
|
||||
v_MaskingPosition = maskingPos.xy / maskingPos.z;
|
||||
|
||||
// Transform from screen space to scissor space.
|
||||
highp vec4 scissorPos = g_MaskingInfo.ToScissorSpace * vec4(m_Position.xy, 1.0, 0.0);
|
||||
v_ScissorPosition = scissorPos.xy / scissorPos.z;
|
||||
|
||||
v_TexRect = vec4(0.0);
|
||||
v_BlendRange = vec2(0.0);
|
||||
|
||||
v_Colour = m_Colour;
|
||||
v_TexCoord = m_TexCoord;
|
||||
v_MaskingIndex = m_MaskingIndex;
|
||||
|
||||
gl_Position = g_ProjMatrix * vec4(m_Position, 1.0);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using osu.Framework.Extensions.TypeExtensions;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
namespace osu.Framework.Timing
|
||||
@@ -23,13 +24,11 @@ namespace osu.Framework.Timing
|
||||
public FramedClock(IClock? source = null, bool processSource = true)
|
||||
{
|
||||
this.processSource = processSource;
|
||||
Source = source ?? new StopwatchClock(true);
|
||||
|
||||
ChangeSource(Source);
|
||||
ChangeSource(source ?? new StopwatchClock(true));
|
||||
Debug.Assert(Source != null);
|
||||
}
|
||||
|
||||
public FrameTimeInfo TimeInfo => new FrameTimeInfo { Elapsed = ElapsedFrameTime, Current = CurrentTime };
|
||||
|
||||
private readonly double[] betweenFrameTimes = new double[128];
|
||||
|
||||
private long totalFramesProcessed;
|
||||
@@ -58,12 +57,10 @@ namespace osu.Framework.Timing
|
||||
|
||||
private const int fps_calculation_interval = 250;
|
||||
|
||||
public void ChangeSource(IClock? source)
|
||||
public void ChangeSource(IClock source)
|
||||
{
|
||||
if (source == null) return;
|
||||
|
||||
CurrentTime = LastFrameTime = source.CurrentTime;
|
||||
Source = source;
|
||||
CurrentTime = LastFrameTime = source.CurrentTime;
|
||||
}
|
||||
|
||||
public virtual void ProcessFrame()
|
||||
|
||||
@@ -20,7 +20,11 @@ namespace osu.Framework.Timing
|
||||
/// </summary>
|
||||
double FramesPerSecond { get; }
|
||||
|
||||
FrameTimeInfo TimeInfo { get; }
|
||||
FrameTimeInfo TimeInfo => new FrameTimeInfo
|
||||
{
|
||||
Elapsed = ElapsedFrameTime,
|
||||
Current = CurrentTime,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Processes one frame. Generally should be run once per update loop.
|
||||
|
||||
@@ -17,6 +17,6 @@ namespace osu.Framework.Timing
|
||||
/// Change the source clock.
|
||||
/// </summary>
|
||||
/// <param name="source">The new source clock.</param>
|
||||
void ChangeSource(IClock? source);
|
||||
void ChangeSource(IClock source);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,50 +7,27 @@ namespace osu.Framework.Timing
|
||||
{
|
||||
/// <summary>
|
||||
/// A clock which uses an internal stopwatch to interpolate (smooth out) a source.
|
||||
/// Note that this will NOT function unless a source has been set.
|
||||
/// </summary>
|
||||
public class InterpolatingFramedClock : IFrameBasedClock, ISourceChangeableClock
|
||||
{
|
||||
private readonly FramedClock clock = new FramedClock(new StopwatchClock(true));
|
||||
|
||||
public IClock? Source { get; private set; }
|
||||
|
||||
protected IFrameBasedClock? FramedSourceClock;
|
||||
protected double LastInterpolatedTime;
|
||||
protected double CurrentInterpolatedTime;
|
||||
|
||||
public FrameTimeInfo TimeInfo => new FrameTimeInfo { Elapsed = ElapsedFrameTime, Current = CurrentTime };
|
||||
|
||||
public double FramesPerSecond => 0;
|
||||
|
||||
public virtual void ChangeSource(IClock? source)
|
||||
{
|
||||
if (source != null)
|
||||
{
|
||||
Source = source;
|
||||
FramedSourceClock = Source as IFrameBasedClock ?? new FramedClock(Source);
|
||||
}
|
||||
|
||||
currentTime = 0;
|
||||
LastInterpolatedTime = 0;
|
||||
CurrentInterpolatedTime = 0;
|
||||
}
|
||||
|
||||
public InterpolatingFramedClock(IClock? source = null)
|
||||
{
|
||||
ChangeSource(source);
|
||||
}
|
||||
|
||||
public virtual double CurrentTime => currentTime;
|
||||
|
||||
private double currentTime;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of error that is allowed between the source and interpolated time before the interpolated time is ignored and the source time is used.
|
||||
/// </summary>
|
||||
public virtual double AllowableErrorMilliseconds => 1000.0 / 60 * 2 * Rate;
|
||||
|
||||
private bool sourceIsRunning;
|
||||
/// <summary>
|
||||
/// Whether interpolation was applied at the last processed frame.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If <see cref="Drift"/> becomes too high (as defined by <see cref="AllowableErrorMilliseconds"/>),
|
||||
/// interpolation will be bypassed in order to provide a more correct time value.
|
||||
/// </remarks>
|
||||
public bool IsInterpolating { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The drift in milliseconds between the source and interpolation at the last processed frame.
|
||||
/// </summary>
|
||||
public double Drift => CurrentTime - (FramedSourceClock?.CurrentTime ?? 0);
|
||||
|
||||
public virtual double Rate
|
||||
{
|
||||
@@ -60,38 +37,63 @@ namespace osu.Framework.Timing
|
||||
|
||||
public virtual bool IsRunning => sourceIsRunning;
|
||||
|
||||
public virtual double Drift => CurrentTime - (FramedSourceClock?.CurrentTime ?? 0);
|
||||
public virtual double ElapsedFrameTime => currentInterpolatedTime - lastInterpolatedTime;
|
||||
|
||||
public virtual double ElapsedFrameTime => CurrentInterpolatedTime - LastInterpolatedTime;
|
||||
public IClock? Source { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether time is being interpolated for the frame currently being processed.
|
||||
/// </summary>
|
||||
public bool IsInterpolating { get; private set; }
|
||||
public virtual double CurrentTime => currentTime;
|
||||
|
||||
protected IFrameBasedClock? FramedSourceClock;
|
||||
|
||||
private readonly FramedClock realtimeClock = new FramedClock(new StopwatchClock(true));
|
||||
|
||||
private double lastInterpolatedTime;
|
||||
|
||||
private double currentInterpolatedTime;
|
||||
|
||||
private bool sourceIsRunning;
|
||||
|
||||
private double currentTime;
|
||||
|
||||
public InterpolatingFramedClock(IClock? source = null)
|
||||
{
|
||||
ChangeSource(source);
|
||||
}
|
||||
|
||||
public virtual void ChangeSource(IClock? source)
|
||||
{
|
||||
Source = source ?? new StopwatchClock(true);
|
||||
|
||||
// We need a frame-based source to correctly process interpolation.
|
||||
// If the provided source is not already a framed clock, encapsulate it in one.
|
||||
FramedSourceClock = Source as IFrameBasedClock ?? new FramedClock(source);
|
||||
|
||||
resetInterpolation();
|
||||
}
|
||||
|
||||
public virtual void ProcessFrame()
|
||||
{
|
||||
if (FramedSourceClock == null) return;
|
||||
|
||||
clock.ProcessFrame();
|
||||
realtimeClock.ProcessFrame();
|
||||
FramedSourceClock.ProcessFrame();
|
||||
|
||||
sourceIsRunning = FramedSourceClock.IsRunning;
|
||||
|
||||
LastInterpolatedTime = currentTime;
|
||||
lastInterpolatedTime = currentTime;
|
||||
|
||||
if (FramedSourceClock.IsRunning)
|
||||
{
|
||||
if (FramedSourceClock.ElapsedFrameTime != 0)
|
||||
IsInterpolating = true;
|
||||
|
||||
CurrentInterpolatedTime += clock.ElapsedFrameTime * Rate;
|
||||
currentInterpolatedTime += realtimeClock.ElapsedFrameTime * Rate;
|
||||
|
||||
if (!IsInterpolating || Math.Abs(FramedSourceClock.CurrentTime - CurrentInterpolatedTime) > AllowableErrorMilliseconds)
|
||||
if (!IsInterpolating || Math.Abs(FramedSourceClock.CurrentTime - currentInterpolatedTime) > AllowableErrorMilliseconds)
|
||||
{
|
||||
// if we've exceeded the allowable error, we should use the source clock's time value.
|
||||
// seeking backwards should only be allowed if the source is explicitly doing that.
|
||||
CurrentInterpolatedTime = FramedSourceClock.ElapsedFrameTime < 0 ? FramedSourceClock.CurrentTime : Math.Max(LastInterpolatedTime, FramedSourceClock.CurrentTime);
|
||||
currentInterpolatedTime = FramedSourceClock.ElapsedFrameTime < 0 ? FramedSourceClock.CurrentTime : Math.Max(lastInterpolatedTime, FramedSourceClock.CurrentTime);
|
||||
|
||||
// once interpolation fails, we don't want to resume interpolating until the source clock starts to move again.
|
||||
IsInterpolating = false;
|
||||
@@ -99,14 +101,23 @@ namespace osu.Framework.Timing
|
||||
else
|
||||
{
|
||||
//if we differ from the elapsed time of the source, let's adjust for the difference.
|
||||
CurrentInterpolatedTime += (FramedSourceClock.CurrentTime - CurrentInterpolatedTime) / 8;
|
||||
currentInterpolatedTime += (FramedSourceClock.CurrentTime - currentInterpolatedTime) / 8;
|
||||
|
||||
// limit the direction of travel to avoid seeking against the flow.
|
||||
CurrentInterpolatedTime = Rate >= 0 ? Math.Max(LastInterpolatedTime, CurrentInterpolatedTime) : Math.Min(LastInterpolatedTime, CurrentInterpolatedTime);
|
||||
currentInterpolatedTime = Rate >= 0 ? Math.Max(lastInterpolatedTime, currentInterpolatedTime) : Math.Min(lastInterpolatedTime, currentInterpolatedTime);
|
||||
}
|
||||
}
|
||||
|
||||
currentTime = sourceIsRunning ? CurrentInterpolatedTime : FramedSourceClock.CurrentTime;
|
||||
currentTime = sourceIsRunning ? currentInterpolatedTime : FramedSourceClock.CurrentTime;
|
||||
}
|
||||
|
||||
private void resetInterpolation()
|
||||
{
|
||||
currentTime = 0;
|
||||
lastInterpolatedTime = 0;
|
||||
currentInterpolatedTime = 0;
|
||||
}
|
||||
|
||||
double IFrameBasedClock.FramesPerSecond => 0;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user