Merge branch 'veldrid-pipelines' into deferred-renderer-2

This commit is contained in:
Dan Balasescu
2024-02-28 01:01:33 +09:00
14 changed files with 150 additions and 116 deletions

View File

@@ -11,15 +11,27 @@ namespace osu.Framework.Benchmarks
{
private readonly BindableInt source1 = new BindableInt();
private readonly BindableInt source2 = new BindableInt();
private readonly AggregateBindable<int> boundAggregate = new AggregateBindable<int>(((i, j) => i + j));
private readonly AggregateBindable<int> aggregate = new AggregateBindable<int>(((i, j) => i + j));
[GlobalSetup]
public void GlobalSetup()
{
boundAggregate.AddSource(source1);
boundAggregate.AddSource(source2);
}
[Benchmark]
public void AggregateRecalculation()
public void AddRemoveSource()
{
var aggregate = new AggregateBindable<int>(((i, j) => i + j));
aggregate.AddSource(source1);
aggregate.AddSource(source2);
aggregate.RemoveAllSources();
}
[Benchmark]
public void SetValue()
{
for (int i = 0; i < 100; i++)
{
source1.Value = i;

View File

@@ -82,7 +82,7 @@ namespace osu.Framework.Tests.Visual.Graphics
Size = new Vector2(circle_radius),
RelativePositionAxes = Axes.Both,
Position = new Vector2(xPos, yPos),
Current = { Value = 1 }
Progress = 1
});
circlesContainerBuffered.Add(new CircularProgress
@@ -91,7 +91,7 @@ namespace osu.Framework.Tests.Visual.Graphics
Size = new Vector2(circle_radius),
RelativePositionAxes = Axes.Both,
Position = new Vector2(xPos, yPos),
Current = { Value = 1 }
Progress = 1
});
}
}

View File

@@ -17,7 +17,7 @@ namespace osu.Framework.Tests.Visual.Performance
base.Update();
foreach (var p in Flow.OfType<CircularProgress>())
p.Current.Value = (p.Current.Value + (Time.Elapsed * RNG.NextSingle()) / 1000) % 1;
p.Progress = (p.Progress + (Time.Elapsed * RNG.NextSingle()) / 1000) % 1;
}
}
}

View File

@@ -145,23 +145,23 @@ namespace osu.Framework.Tests.Visual.UserInterface
switch (rotateMode)
{
case 0:
clock.Current.Value = Time.Current % (period * 2) / period - 1;
clock.Progress = Time.Current % (period * 2) / period - 1;
break;
case 1:
clock.Current.Value = Time.Current % period / period;
clock.Progress = Time.Current % period / period;
break;
case 2:
clock.Current.Value = Time.Current % period / period - 1;
clock.Progress = Time.Current % period / period - 1;
break;
case 3:
clock.Current.Value = Time.Current % transition_period / transition_period / 5 - 0.1f;
clock.Progress = Time.Current % transition_period / transition_period / 5 - 0.1f;
break;
case 4:
clock.Current.Value = (Time.Current % transition_period / transition_period / 5 - 0.1f + 2) % 2 - 1;
clock.Progress = (Time.Current % transition_period / transition_period / 5 - 0.1f + 2) % 2 - 1;
break;
}
}
@@ -245,19 +245,19 @@ namespace osu.Framework.Tests.Visual.UserInterface
switch (tf)
{
case 0:
clock.FillTo(0).Then().FillTo(1, 1000).Loop();
clock.ProgressTo(0).Then().ProgressTo(1, 1000).Loop();
break;
case 1:
clock.FillTo(1).Then().FillTo(0, 1000).Loop();
clock.ProgressTo(1).Then().ProgressTo(0, 1000).Loop();
break;
case 2:
clock.FillTo(0, 1000).Then().FillTo(1, 1000).Loop();
clock.ProgressTo(0, 1000).Then().ProgressTo(1, 1000).Loop();
break;
case 3:
clock.FillTo(0).Then().FillTo(1, 1000, Easing.InOutQuart).Loop();
clock.ProgressTo(0).Then().ProgressTo(1, 1000, Easing.InOutQuart).Loop();
break;
}
}

View File

@@ -5,7 +5,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace osu.Framework.Bindables
{
@@ -52,7 +51,7 @@ namespace osu.Framework.Bindables
return;
var boundCopy = bindable.GetBoundCopy();
sourceMapping.Add(new WeakRefPair(new WeakReference<IBindable<T>>(bindable), boundCopy));
sourceMapping.Add(new WeakRefPair(bindable.GetWeakReference(), boundCopy));
boundCopy.BindValueChanged(recalculateAggregate, true);
}
}
@@ -65,20 +64,26 @@ namespace osu.Framework.Bindables
{
lock (sourceMapping)
{
var weak = findExistingPair(bindable);
if (weak != null)
if (findExistingPair(bindable) is WeakRefPair pair)
{
weak.BoundCopy.UnbindAll();
sourceMapping.Remove(weak);
pair.BoundCopy.UnbindAll();
sourceMapping.Remove(pair);
}
recalculateAggregate();
}
}
private WeakRefPair findExistingPair(IBindable<T> bindable) =>
sourceMapping.FirstOrDefault(p => p.WeakReference.TryGetTarget(out var target) && target == bindable);
private WeakRefPair? findExistingPair(IBindable<T> bindable)
{
foreach (var p in sourceMapping)
{
if (p.WeakReference.TryGetTarget(out var target) && target == bindable)
return p;
}
return null;
}
private void recalculateAggregate(ValueChangedEvent<T> obj = null)
{
@@ -112,16 +117,6 @@ namespace osu.Framework.Bindables
}
}
private class WeakRefPair
{
public readonly WeakReference<IBindable<T>> WeakReference;
public readonly IBindable<T> BoundCopy;
public WeakRefPair(WeakReference<IBindable<T>> weakReference, IBindable<T> boundCopy)
{
WeakReference = weakReference;
BoundCopy = boundCopy;
}
}
private readonly record struct WeakRefPair(WeakReference<Bindable<T>> WeakReference, IBindable<T> BoundCopy);
}
}

View File

@@ -440,6 +440,8 @@ namespace osu.Framework.Bindables
IBindable IBindable.GetBoundCopy() => GetBoundCopy();
WeakReference<Bindable<T>> IBindable<T>.GetWeakReference() => weakReference;
IBindable<T> IBindable<T>.GetBoundCopy() => GetBoundCopy();
/// <inheritdoc cref="IBindable{T}.GetBoundCopy"/>

View File

@@ -111,5 +111,10 @@ namespace osu.Framework.Bindables
/// <inheritdoc cref="IBindable.GetBoundCopy"/>
IBindable<T> GetBoundCopy();
/// <summary>
/// Retrieves a weak reference to this bindable.
/// </summary>
internal WeakReference<Bindable<T>> GetWeakReference();
}
}

View File

@@ -148,8 +148,8 @@ namespace osu.Framework.Graphics.Cursor
progress.FadeColour(Color4.SkyBlue)
.TransformTo(nameof(progress.InnerRadius), 0.2f)
.TransformTo(nameof(progress.InnerRadius), 0.3f, 150, Easing.OutQuint)
.TransformBindableTo(progress.Current, 0)
.TransformBindableTo(progress.Current, 1, duration / 3 * 2);
.ProgressTo(0)
.ProgressTo(1, duration / 3 * 2);
using (BeginDelayedSequence(duration / 3 * 2))
{
@@ -165,7 +165,7 @@ namespace osu.Framework.Graphics.Cursor
{
this.FadeOut(400, Easing.OutQuint);
progress.TransformBindableTo(progress.Current, 0, 400, Easing.OutQuint)
progress.ProgressTo(0, 400, Easing.OutQuint)
.TransformTo(nameof(progress.InnerRadius), 0.2f, 50, Easing.OutQuint);
}
}

View File

@@ -233,17 +233,8 @@ namespace osu.Framework.Graphics.OpenGL
protected override void SetFrameBufferImplementation(IFrameBuffer? frameBuffer) =>
GL.BindFramebuffer(FramebufferTarget.Framebuffer, ((GLFrameBuffer?)frameBuffer)?.FrameBuffer ?? backbufferFramebuffer);
/// <summary>
/// Deletes a frame buffer.
/// </summary>
/// <param name="frameBuffer">The frame buffer to delete.</param>
public void DeleteFrameBuffer(IFrameBuffer frameBuffer)
{
while (FrameBuffer == frameBuffer)
UnbindFrameBuffer(frameBuffer);
ScheduleDisposal(GL.DeleteFramebuffer, ((GLFrameBuffer)frameBuffer).FrameBuffer);
}
protected override void DeleteFrameBufferImplementation(IFrameBuffer frameBuffer)
=> GL.DeleteFramebuffer(((GLFrameBuffer)frameBuffer).FrameBuffer);
protected override void ClearImplementation(ClearInfo clearInfo)
{

View File

@@ -3,6 +3,7 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@@ -39,6 +40,8 @@ namespace osu.Framework.Graphics.Performance
private bool initialised;
private readonly List<FrameStatisticsDisplay> frameDisplays = new List<FrameStatisticsDisplay>();
public FrameStatisticsMode State
{
get => state;
@@ -77,22 +80,27 @@ namespace osu.Framework.Graphics.Performance
// for some reason PerformanceOverlay has 0 width despite using AutoSizeAxes, and it doesn't look simple to fix.
// let's just work around it and consider frame statistics display dimensions for receiving input events.
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Children.OfType<FrameStatisticsDisplay>().Any(d => d.ReceivePositionalInputAt(screenSpacePos));
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos)
{
foreach (var display in frameDisplays)
{
if (display.ReceivePositionalInputAt(screenSpacePos))
return true;
}
return false;
}
protected override bool OnKeyDown(KeyDownEvent e)
{
switch (e.Key)
{
case Key.ControlLeft:
foreach (var display in Children.OfType<FrameStatisticsDisplay>())
display.Expanded = true;
applyToDisplays(static d => d.Expanded = true);
break;
case Key.ShiftLeft:
foreach (var display in Children.OfType<FrameStatisticsDisplay>())
display.Running = false;
applyToDisplays(static d => d.Running = false);
break;
}
@@ -104,15 +112,11 @@ namespace osu.Framework.Graphics.Performance
switch (e.Key)
{
case Key.ControlLeft:
foreach (var display in Children.OfType<FrameStatisticsDisplay>())
display.Expanded = false;
applyToDisplays(static d => d.Expanded = false);
break;
case Key.ShiftLeft:
foreach (var display in Children.OfType<FrameStatisticsDisplay>())
display.Running = true;
applyToDisplays(static d => d.Running = true);
break;
}
@@ -124,15 +128,11 @@ namespace osu.Framework.Graphics.Performance
switch (e.Touch.Source)
{
case TouchSource.Touch1:
foreach (var display in Children.OfType<FrameStatisticsDisplay>())
display.Expanded = true;
applyToDisplays(static d => d.Expanded = true);
break;
case TouchSource.Touch2:
foreach (var display in Children.OfType<FrameStatisticsDisplay>())
display.Running = false;
applyToDisplays(static d => d.Running = false);
break;
}
@@ -144,15 +144,11 @@ namespace osu.Framework.Graphics.Performance
switch (e.Touch.Source)
{
case TouchSource.Touch1:
foreach (var display in Children.OfType<FrameStatisticsDisplay>())
display.Expanded = false;
applyToDisplays(static d => d.Expanded = false);
break;
case TouchSource.Touch2:
foreach (var display in Children.OfType<FrameStatisticsDisplay>())
display.Running = true;
applyToDisplays(static d => d.Running = true);
break;
}
@@ -188,12 +184,15 @@ namespace osu.Framework.Graphics.Performance
foreach (GameThread t in host.Threads)
{
Add(new FrameStatisticsDisplay(t, uploadPool)
var display = new FrameStatisticsDisplay(t, uploadPool)
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
State = state
});
};
Add(display);
frameDisplays.Add(display);
}
}
@@ -201,12 +200,18 @@ namespace osu.Framework.Graphics.Performance
break;
}
foreach (FrameStatisticsDisplay d in Children.OfType<FrameStatisticsDisplay>())
d.State = state;
foreach (var display in frameDisplays)
display.State = state;
StateChanged?.Invoke(State);
}
private void applyToDisplays(Predicate<FrameStatisticsDisplay> predicate)
{
foreach (var display in frameDisplays)
predicate.Invoke(display);
}
private void updateInfoText()
{
if (infoText == null)

View File

@@ -966,6 +966,20 @@ namespace osu.Framework.Graphics.Rendering
/// <param name="frameBuffer">The framebuffer to use, or null to use the backbuffer (i.e. main framebuffer).</param>
protected abstract void SetFrameBufferImplementation(IFrameBuffer? frameBuffer);
/// <summary>
/// Deletes a frame buffer.
/// </summary>
/// <param name="frameBuffer">The frame buffer to delete.</param>
public void DeleteFrameBuffer(IFrameBuffer frameBuffer)
{
while (FrameBuffer == frameBuffer)
UnbindFrameBuffer(frameBuffer);
ScheduleDisposal(DeleteFrameBufferImplementation, frameBuffer);
}
protected abstract void DeleteFrameBufferImplementation(IFrameBuffer frameBuffer);
#endregion
public void DrawVertices(PrimitiveTopology topology, int vertexStart, int verticesCount)

View File

@@ -6,7 +6,6 @@
using System;
using System.Runtime.InteropServices;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics.Rendering;
using osu.Framework.Graphics.Shaders;
using osu.Framework.Graphics.Shaders.Types;
@@ -15,14 +14,26 @@ using osu.Framework.Graphics.Transforms;
namespace osu.Framework.Graphics.UserInterface
{
public partial class CircularProgress : Sprite, IHasCurrentValue<double>
public partial class CircularProgress : Sprite
{
private readonly BindableWithCurrent<double> current = new BindableWithCurrent<double>();
private double progress;
public Bindable<double> Current
public double Progress
{
get => current.Current;
set => current.Current = value;
get => progress;
set
{
if (!double.IsFinite(value))
throw new ArgumentException($"{nameof(Progress)} must be finite, but is {value}.");
if (progress == value)
return;
progress = value;
if (IsLoaded)
Invalidate(Invalidation.DrawNode);
}
}
[BackgroundDependencyLoader]
@@ -32,27 +43,14 @@ namespace osu.Framework.Graphics.UserInterface
TextureShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, "CircularProgress");
}
protected override void LoadComplete()
{
base.LoadComplete();
Current.BindValueChanged(c =>
{
if (!double.IsFinite(c.NewValue))
throw new ArgumentException($"{nameof(Current)} must be finite, but is {c.NewValue}.");
Invalidate(Invalidation.DrawNode);
}, true);
}
protected override DrawNode CreateDrawNode() => new CircularProgressDrawNode(this);
public TransformSequence<CircularProgress> FillTo(double newValue, double duration = 0, Easing easing = Easing.None)
=> FillTo(newValue, duration, new DefaultEasingFunction(easing));
public TransformSequence<CircularProgress> ProgressTo(double newValue, double duration = 0, Easing easing = Easing.None)
=> ProgressTo(newValue, duration, new DefaultEasingFunction(easing));
public TransformSequence<CircularProgress> FillTo<TEasing>(double newValue, double duration, in TEasing easing)
public TransformSequence<CircularProgress> ProgressTo<TEasing>(double newValue, double duration, in TEasing easing)
where TEasing : IEasingFunction
=> this.TransformBindableTo(Current, newValue, duration, easing);
=> this.TransformTo(nameof(Progress), newValue, duration, easing);
private float innerRadius = 1;
@@ -108,7 +106,7 @@ namespace osu.Framework.Graphics.UserInterface
base.ApplyState();
InnerRadius = Source.innerRadius;
Progress = Math.Abs((float)Source.current.Value);
Progress = Math.Abs((float)Source.progress);
RoundedCaps = Source.roundedCaps;
// smoothstep looks too sharp with 1px, let's give it a bit more
@@ -162,11 +160,11 @@ namespace osu.Framework.Graphics.UserInterface
public static class CircularProgressTransformSequenceExtensions
{
public static TransformSequence<CircularProgress> FillTo(this TransformSequence<CircularProgress> t, double newValue, double duration = 0, Easing easing = Easing.None)
=> t.FillTo(newValue, duration, new DefaultEasingFunction(easing));
public static TransformSequence<CircularProgress> ProgressTo(this TransformSequence<CircularProgress> t, double newValue, double duration = 0, Easing easing = Easing.None)
=> t.ProgressTo(newValue, duration, new DefaultEasingFunction(easing));
public static TransformSequence<CircularProgress> FillTo<TEasing>(this TransformSequence<CircularProgress> t, double newValue, double duration, TEasing easing)
public static TransformSequence<CircularProgress> ProgressTo<TEasing>(this TransformSequence<CircularProgress> t, double newValue, double duration, TEasing easing)
where TEasing : IEasingFunction
=> t.Append(cp => cp.FillTo(newValue, duration, easing));
=> t.Append(cp => cp.ProgressTo(newValue, duration, easing));
}
}

View File

@@ -22,8 +22,9 @@ namespace osu.Framework.Graphics.Veldrid.Buffers
private readonly VeldridRenderer renderer;
private readonly PixelFormat? depthFormat;
private readonly VeldridTexture colourTarget;
private readonly bool externalColourTarget;
private readonly int mipLevel;
private Texture? depthTarget;
private Vector2 size = Vector2.One;
@@ -63,11 +64,23 @@ namespace osu.Framework.Graphics.Veldrid.Buffers
recreateResources();
}
internal VeldridFrameBuffer(VeldridRenderer renderer, VeldridTexture colourTarget, int mipLevel)
{
this.renderer = renderer;
this.colourTarget = colourTarget;
this.mipLevel = mipLevel;
Texture = renderer.CreateTexture(colourTarget);
externalColourTarget = true;
recreateResources();
}
[MemberNotNull(nameof(Framebuffer))]
private void recreateResources()
{
// The texture is created once and resized internally, so it should not be deleted.
deleteResources(false);
DeleteResources(false);
if (depthFormat is PixelFormat depth)
{
@@ -77,7 +90,7 @@ namespace osu.Framework.Graphics.Veldrid.Buffers
FramebufferDescription description = new FramebufferDescription
{
ColorTargets = new[] { new FramebufferAttachmentDescription(colourTarget.GetResourceList().Single().Texture, 0) },
ColorTargets = new[] { new FramebufferAttachmentDescription(colourTarget.GetResourceList().Single().Texture, 0, (uint)mipLevel) },
DepthTarget = depthTarget == null ? null : new FramebufferAttachmentDescription(depthTarget, 0)
};
@@ -95,9 +108,9 @@ namespace osu.Framework.Graphics.Veldrid.Buffers
/// Deletes the resources of this frame buffer.
/// </summary>
/// <param name="deleteTexture">Whether the texture should also be deleted.</param>
private void deleteResources(bool deleteTexture)
public void DeleteResources(bool deleteTexture)
{
if (deleteTexture)
if (deleteTexture && !externalColourTarget)
colourTarget.Dispose();
if (Framebuffer.IsNotNull())
@@ -127,11 +140,7 @@ namespace osu.Framework.Graphics.Veldrid.Buffers
if (isDisposed)
return;
while (renderer.IsFrameBufferBound(this))
Unbind();
deleteResources(true);
renderer.DeleteFrameBuffer(this);
isDisposed = true;
}

View File

@@ -156,6 +156,9 @@ namespace osu.Framework.Graphics.Veldrid
protected override void SetFrameBufferImplementation(IFrameBuffer? frameBuffer)
=> veldridDevice.Graphics.SetFrameBuffer((VeldridFrameBuffer?)frameBuffer);
protected override void DeleteFrameBufferImplementation(IFrameBuffer frameBuffer)
=> ((VeldridFrameBuffer)frameBuffer).DeleteResources(true);
public override void DrawVerticesImplementation(PrimitiveTopology topology, int vertexStart, int verticesCount)
{
// normally we would flush/submit all texture upload commands at the end of the frame, since no actual rendering by the GPU will happen until then,