A game framework written with osu! in mind.
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'master' into avoid-audio-container-unnecessary-unbind-rebind

authored by

Dan Balasescu and committed by
GitHub
816c46f4 ad448200

+180 -136
+6 -2
osu.Framework.Benchmarks/BenchmarkBindableInstantiation.cs
··· 7 7 8 8 namespace osu.Framework.Benchmarks 9 9 { 10 + [MemoryDiagnoser] 10 11 public class BenchmarkBindableInstantiation 11 12 { 12 - [Benchmark(Baseline = true)] 13 - public Bindable<int> GetBoundCopyOld() => new BindableOld<int>().GetBoundCopy(); 13 + [Benchmark] 14 + public Bindable<int> Instantiate() => new Bindable<int>(); 14 15 15 16 [Benchmark] 16 17 public Bindable<int> GetBoundCopy() => new Bindable<int>().GetBoundCopy(); 18 + 19 + [Benchmark(Baseline = true)] 20 + public Bindable<int> GetBoundCopyOld() => new BindableOld<int>().GetBoundCopy(); 17 21 18 22 private class BindableOld<T> : Bindable<T> 19 23 {
+31
osu.Framework.Benchmarks/BenchmarkTransform.cs
··· 26 26 public Transform CreateSingleBlank() => new TestTransform(); 27 27 28 28 [Benchmark] 29 + public void CreateSequenceThenClearAfter() 30 + { 31 + target.FadeIn(1000, Easing.OutQuint) 32 + .Then().FadeOut(1000) 33 + .Then().FadeOut(1000) 34 + .Then().FadeOut(1000) 35 + .Then().FadeOut(1000) 36 + .Then().FadeOut(1000) 37 + .Then().FadeOut(1000) 38 + .Then().FadeOut(1000) 39 + .Then().FadeOut(1000) 40 + .Then().FadeOut(1000) 41 + .Then().FadeOut(1000); 42 + 43 + target.ClearTransformsAfter(5000); 44 + } 45 + 46 + [Benchmark] 47 + public void Expiry() 48 + { 49 + target.FadeIn(1000, Easing.OutQuint) 50 + .ScaleTo(2, 1000, Easing.OutQuint) 51 + .RotateTo(2, 1000, Easing.OutQuint); 52 + 53 + for (int i = 0; i < 1000; i++) 54 + target.Expire(); 55 + 56 + target.ClearTransforms(); 57 + } 58 + 59 + [Benchmark] 29 60 public void CreateSequenceWithDefaultEasing() 30 61 { 31 62 target.FadeIn(1000, Easing.OutQuint)
+5 -3
osu.Framework/Audio/AudioAdjustments.cs
··· 12 12 /// </summary> 13 13 public class AudioAdjustments : IAdjustableAudioComponent 14 14 { 15 + private static readonly AdjustableProperty[] all_adjustments = (AdjustableProperty[])Enum.GetValues(typeof(AdjustableProperty)); 16 + 15 17 /// <summary> 16 18 /// The volume of this component. 17 19 /// </summary> ··· 59 61 60 62 public AudioAdjustments() 61 63 { 62 - foreach (AdjustableProperty type in Enum.GetValues(typeof(AdjustableProperty))) 64 + foreach (AdjustableProperty type in all_adjustments) 63 65 { 64 66 var aggregate = getAggregate(type) = new AggregateBindable<double>(getAggregateFunction(type), getProperty(type).GetUnboundCopy()); 65 67 aggregate.AddSource(getProperty(type)); ··· 74 76 75 77 public void BindAdjustments(IAggregateAudioAdjustment component) 76 78 { 77 - foreach (AdjustableProperty type in Enum.GetValues(typeof(AdjustableProperty))) 79 + foreach (AdjustableProperty type in all_adjustments) 78 80 getAggregate(type).AddSource(component.GetAggregate(type)); 79 81 } 80 82 81 83 public void UnbindAdjustments(IAggregateAudioAdjustment component) 82 84 { 83 - foreach (AdjustableProperty type in Enum.GetValues(typeof(AdjustableProperty))) 85 + foreach (AdjustableProperty type in all_adjustments) 84 86 getAggregate(type).RemoveSource(component.GetAggregate(type)); 85 87 } 86 88
+3 -1
osu.Framework/Audio/BassAmplitudeProcessor.cs
··· 29 29 30 30 private float[]? frequencyData; 31 31 32 + private readonly float[] channelLevels = new float[2]; 33 + 32 34 public void Update() 33 35 { 34 36 if (channel.Handle == 0) ··· 36 38 37 39 bool active = channel.Mixer.ChannelIsActive(channel) == PlaybackState.Playing; 38 40 39 - float[] channelLevels = new float[2]; 40 41 channel.Mixer.ChannelGetLevel(channel, channelLevels, 1 / 60f, LevelRetrievalFlags.Stereo); 42 + 41 43 var leftChannel = active ? channelLevels[0] : -1; 42 44 var rightChannel = active ? channelLevels[1] : -1; 43 45
+2 -1
osu.Framework/Audio/Mixing/Bass/BassAudioMixer.cs
··· 12 12 using ManagedBass; 13 13 using ManagedBass.Mix; 14 14 using osu.Framework.Bindables; 15 + using osu.Framework.Extensions.EnumExtensions; 15 16 using osu.Framework.Extensions.ObjectExtensions; 16 17 using osu.Framework.Statistics; 17 18 ··· 140 141 141 142 // The channel is always in a playing state unless stopped or stalled as it's a decoding channel. Retrieve the true playing state from the mixer channel. 142 143 if (state == PlaybackState.Playing) 143 - state = BassMix.ChannelHasFlag(channel.Handle, BassFlags.MixerChanPause) ? PlaybackState.Paused : state; 144 + state = BassMix.ChannelFlags(channel.Handle, BassFlags.Default, BassFlags.Default).HasFlagFast(BassFlags.MixerChanPause) ? PlaybackState.Paused : state; 144 145 145 146 return state; 146 147 }
+2 -3
osu.Framework/Bindables/Bindable.cs
··· 7 7 using System.Linq; 8 8 using JetBrains.Annotations; 9 9 using Newtonsoft.Json; 10 - using osu.Framework.Caching; 11 10 using osu.Framework.Extensions.TypeExtensions; 12 11 using osu.Framework.IO.Serialization; 13 12 using osu.Framework.Lists; ··· 129 128 TriggerDefaultChange(previousValue, source ?? this, true, bypassChecks); 130 129 } 131 130 132 - private readonly Cached<WeakReference<Bindable<T>>> weakReferenceCache = new Cached<WeakReference<Bindable<T>>>(); 131 + private WeakReference<Bindable<T>> weakReferenceInstance; 133 132 134 - private WeakReference<Bindable<T>> weakReference => weakReferenceCache.IsValid ? weakReferenceCache.Value : weakReferenceCache.Value = new WeakReference<Bindable<T>>(this); 133 + private WeakReference<Bindable<T>> weakReference => weakReferenceInstance ??= new WeakReference<Bindable<T>>(this); 135 134 136 135 /// <summary> 137 136 /// Creates a new bindable instance. This is used for deserialization of bindables.
+3 -3
osu.Framework/Graphics/Containers/CompositeDrawable.cs
··· 1245 1245 if (!recursive || internalChildren.Count == 0) 1246 1246 return base.BeginAbsoluteSequence(newTransformStartTime, false); 1247 1247 1248 - List<IDisposable> disposalActions = new List<IDisposable>(internalChildren.Count + 1); 1248 + List<AbsoluteSequenceSender> disposalActions = new List<AbsoluteSequenceSender>(internalChildren.Count + 1); 1249 1249 1250 1250 base.CollectAbsoluteSequenceActionsFromSubTree(newTransformStartTime, disposalActions); 1251 1251 1252 1252 foreach (var c in internalChildren) 1253 1253 c.CollectAbsoluteSequenceActionsFromSubTree(newTransformStartTime, disposalActions); 1254 1254 1255 - return new ValueInvokeOnDisposal<List<IDisposable>>(disposalActions, actions => 1255 + return new ValueInvokeOnDisposal<List<AbsoluteSequenceSender>>(disposalActions, actions => 1256 1256 { 1257 1257 foreach (var a in actions) 1258 1258 a.Dispose(); 1259 1259 }); 1260 1260 } 1261 1261 1262 - internal override void CollectAbsoluteSequenceActionsFromSubTree(double newTransformStartTime, List<IDisposable> actions) 1262 + internal override void CollectAbsoluteSequenceActionsFromSubTree(double newTransformStartTime, List<AbsoluteSequenceSender> actions) 1263 1263 { 1264 1264 base.CollectAbsoluteSequenceActionsFromSubTree(newTransformStartTime, actions); 1265 1265
+1 -1
osu.Framework/Graphics/Containers/VisibilityContainer.cs
··· 45 45 } 46 46 47 47 /// <summary> 48 - /// Hide this container by setting its visibility to <see cref="Visibility.Visible"/>. 48 + /// Show this container by setting its visibility to <see cref="Visibility.Visible"/>. 49 49 /// </summary> 50 50 public override void Show() => State.Value = Visibility.Visible; 51 51
+5 -5
osu.Framework/Graphics/DrawNode.cs
··· 1 1 // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. 2 2 // See the LICENCE file in the repository root for full licence text. 3 3 4 - using osu.Framework.Graphics.OpenGL; 5 4 using System; 6 5 using System.Runtime.CompilerServices; 6 + using System.Threading; 7 7 using osu.Framework.Graphics.Batches; 8 8 using osu.Framework.Graphics.Colour; 9 + using osu.Framework.Graphics.OpenGL; 9 10 using osu.Framework.Graphics.OpenGL.Buffers; 10 11 using osu.Framework.Graphics.OpenGL.Textures; 11 12 using osu.Framework.Graphics.OpenGL.Vertices; 12 13 using osu.Framework.Graphics.Primitives; 13 14 using osu.Framework.Graphics.Textures; 14 - using osu.Framework.Threading; 15 15 using osu.Framework.Utils; 16 16 using osuTK; 17 17 ··· 45 45 /// </summary> 46 46 protected IDrawable Source { get; private set; } 47 47 48 - private readonly AtomicCounter referenceCount = new AtomicCounter(); 48 + private long referenceCount; 49 49 50 50 /// <summary> 51 51 /// The depth at which drawing should take place. ··· 282 282 /// <remarks> 283 283 /// All <see cref="DrawNode"/>s start with a reference count of 1. 284 284 /// </remarks> 285 - internal void Reference() => referenceCount.Increment(); 285 + internal void Reference() => Interlocked.Increment(ref referenceCount); 286 286 287 287 protected internal bool IsDisposed { get; private set; } 288 288 289 289 public void Dispose() 290 290 { 291 - if (referenceCount.Decrement() != 0) 291 + if (Interlocked.Decrement(ref referenceCount) != 0) 292 292 return; 293 293 294 294 GLWrapper.ScheduleDisposal(() => Dispose(true));
+2 -2
osu.Framework/Graphics/Drawable.cs
··· 151 151 { 152 152 a(target); 153 153 } 154 - catch 154 + catch (Exception e) 155 155 { 156 - // Execution should continue regardless of whether an unbind failed 156 + Logger.Error(e, $"Failed to unbind a local bindable in {type.ReadableName()}"); 157 157 } 158 158 } 159 159 };
+11
osu.Framework/Graphics/Transforms/ITransformSequence.cs
··· 1 + // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. 2 + // See the LICENCE file in the repository root for full licence text. 3 + 4 + namespace osu.Framework.Graphics.Transforms 5 + { 6 + public interface ITransformSequence 7 + { 8 + internal void TransformAborted(); 9 + internal void TransformCompleted(); 10 + } 11 + }
+34 -19
osu.Framework/Graphics/Transforms/TargetGroupingTransformTracker.cs
··· 17 17 /// <summary> 18 18 /// A list of <see cref="Transform"/>s associated with the <see cref="TargetGrouping"/>. 19 19 /// </summary> 20 - public IEnumerable<Transform> Transforms => transforms; 20 + public IReadOnlyList<Transform> Transforms => transforms; 21 21 22 22 /// <summary> 23 23 /// The member this instance is tracking. ··· 28 28 29 29 private readonly Transformable transformable; 30 30 31 - private readonly Queue<Action> removalActions = new Queue<Action>(); 31 + private readonly Queue<(ITransformSequence sequence, Action<ITransformSequence> action)> removalActions = new Queue<(ITransformSequence, Action<ITransformSequence>)>(); 32 32 33 33 /// <summary> 34 34 /// Used to assign a monotonically increasing ID to <see cref="Transform"/>s as they are added. This member is ··· 134 134 flushAppliedCache = true; 135 135 i--; 136 136 137 - if (u.OnAbort != null) 138 - removalActions.Enqueue(u.OnAbort); 137 + if (u.AbortTargetSequence != null) 138 + removalActions.Enqueue((u.AbortTargetSequence, s => s.TransformAborted())); 139 139 } 140 140 else 141 141 u.AppliedToEnd = true; ··· 184 184 transforms.Add(t); 185 185 flushAppliedCache = true; 186 186 } 187 - else if (t.OnComplete != null) 188 - removalActions.Enqueue(t.OnComplete); 187 + else if (t.CompletionTargetSequence != null) 188 + removalActions.Enqueue((t.CompletionTargetSequence, s => s.TransformCompleted())); 189 189 } 190 190 } 191 191 ··· 233 233 if (t.TargetMember == transform.TargetMember) 234 234 { 235 235 transforms.RemoveAt(i--); 236 - if (t.OnAbort != null) 237 - removalActions.Enqueue(t.OnAbort); 236 + if (t.AbortTargetSequence != null) 237 + removalActions.Enqueue((t.AbortTargetSequence, s => s.TransformAborted())); 238 238 } 239 239 } 240 240 ··· 263 263 { 264 264 resetLastAppliedCache(); 265 265 266 - Transform[] toAbort; 267 - 268 266 if (targetMember == null) 269 267 { 270 - toAbort = transforms.Where(t => t.StartTime >= time).ToArray(); 271 - transforms.RemoveAll(t => t.StartTime >= time); 268 + for (int i = 0; i < transforms.Count; i++) 269 + { 270 + var t = transforms[i]; 271 + 272 + if (t.StartTime >= time) 273 + { 274 + transforms.RemoveAt(i--); 275 + if (t.AbortTargetSequence != null) 276 + removalActions.Enqueue((t.AbortTargetSequence, s => s.TransformAborted())); 277 + } 278 + } 272 279 } 273 280 else 274 281 { 275 - toAbort = transforms.Where(t => t.TargetMember == targetMember && t.StartTime >= time).ToArray(); 276 - transforms.RemoveAll(t => t.TargetMember == targetMember && t.StartTime >= time); 282 + for (int i = 0; i < transforms.Count; i++) 283 + { 284 + var t = transforms[i]; 285 + 286 + if (t.TargetMember == targetMember && t.StartTime >= time) 287 + { 288 + transforms.RemoveAt(i--); 289 + if (t.AbortTargetSequence != null) 290 + removalActions.Enqueue((t.AbortTargetSequence, s => s.TransformAborted())); 291 + } 292 + } 277 293 } 278 294 279 - foreach (var t in toAbort) 280 - t.OnAbort?.Invoke(); 295 + invokePendingRemovalActions(); 281 296 } 282 297 283 298 /// <summary> ··· 310 325 } 311 326 312 327 t.Apply(t.EndTime); 313 - t.OnComplete?.Invoke(); 328 + t.TriggerComplete(); 314 329 } 315 330 } 316 331 317 332 private void invokePendingRemovalActions() 318 333 { 319 - while (removalActions.TryDequeue(out var action)) 320 - action(); 334 + while (removalActions.TryDequeue(out var item)) 335 + item.action(item.sequence); 321 336 } 322 337 323 338 /// <summary>
+6 -2
osu.Framework/Graphics/Transforms/Transform.cs
··· 54 54 55 55 internal bool HasStartValue; 56 56 57 - public Action OnComplete; 57 + internal ITransformSequence CompletionTargetSequence; 58 58 59 - public Action OnAbort; 59 + internal ITransformSequence AbortTargetSequence; 60 60 61 61 public Transform Clone() => (Transform)MemberwiseClone(); 62 62 ··· 77 77 return compare; 78 78 } 79 79 } 80 + 81 + internal void TriggerComplete() => CompletionTargetSequence?.TransformCompleted(); 82 + 83 + internal void TriggerAbort() => AbortTargetSequence?.TransformAborted(); 80 84 } 81 85 82 86 public abstract class Transform<TValue> : Transform
+37 -37
osu.Framework/Graphics/Transforms/TransformSequence.cs
··· 15 15 /// <typeparam name="T"> 16 16 /// The type of the <see cref="ITransformable"/> the <see cref="Transform"/>s in this sequence operate upon. 17 17 /// </typeparam> 18 - public class TransformSequence<T> where T : class, ITransformable 18 + public class TransformSequence<T> : ITransformSequence where T : class, ITransformable 19 19 { 20 20 /// <summary> 21 21 /// A delegate that generates a new <see cref="TransformSequence{T}"/> on a given <paramref name="origin"/>. ··· 57 57 // As soon as we have an infinitely looping transform, 58 58 // completion no longer makes sense. 59 59 if (last != null) 60 - last.OnComplete = null; 60 + last.CompletionTargetSequence = null; 61 61 62 62 last = null; 63 63 lastEndTime = double.PositiveInfinity; ··· 81 81 82 82 transforms.Add(transform); 83 83 84 - transform.OnComplete = null; 85 - transform.OnAbort = onTransformAborted; 84 + transform.CompletionTargetSequence = this; 85 + transform.AbortTargetSequence = this; 86 86 87 87 if (transform.IsLooping) 88 88 onLoopingTransform(); ··· 91 91 if (last == null || transform.EndTime > lastEndTime) 92 92 { 93 93 if (last != null) 94 - last.OnComplete = null; 94 + last.CompletionTargetSequence = null; 95 95 96 96 last = transform; 97 - last.OnComplete = onTransformsComplete; 97 + last.CompletionTargetSequence = this; 98 98 lastEndTime = last.EndTime; 99 99 hasCompleted = false; 100 100 } ··· 175 175 return this; 176 176 } 177 177 178 - private void onTransformAborted() 179 - { 180 - if (transforms.Count == 0) 181 - return; 182 - 183 - // No need for OnAbort events to trigger anymore, since 184 - // we are already aware of the abortion. 185 - foreach (var t in transforms) 186 - { 187 - t.OnAbort = null; 188 - t.OnComplete = null; 189 - 190 - if (!t.HasStartValue) 191 - t.TargetTransformable.RemoveTransform(t); 192 - } 193 - 194 - transforms.Clear(); 195 - last = null; 196 - 197 - onAbort?.Invoke(); 198 - } 199 - 200 - private void onTransformsComplete() 201 - { 202 - hasCompleted = true; 203 - onComplete?.Invoke(); 204 - } 205 - 206 178 private void subscribeComplete(Action func) 207 179 { 208 180 if (onComplete != null) ··· 342 314 343 315 foreach (var t in transforms) 344 316 { 345 - Action tmpOnAbort = t.OnAbort; 346 - t.OnAbort = null; 317 + var tmpOnAbort = t.AbortTargetSequence; 318 + t.AbortTargetSequence = null; 347 319 t.TargetTransformable.RemoveTransform(t); 348 - t.OnAbort = tmpOnAbort; 320 + t.AbortTargetSequence = tmpOnAbort; 349 321 350 322 // Update start and end times such that no transformations need to be instantly 351 323 // looped right after they're added. This is required so that transforms can be ··· 457 429 if (hasEnd) 458 430 OnComplete(function); 459 431 OnAbort(function); 432 + } 433 + 434 + void ITransformSequence.TransformAborted() 435 + { 436 + if (transforms.Count == 0) 437 + return; 438 + 439 + // No need for OnAbort events to trigger anymore, since 440 + // we are already aware of the abortion. 441 + foreach (var t in transforms) 442 + { 443 + t.AbortTargetSequence = null; 444 + t.CompletionTargetSequence = null; 445 + 446 + if (!t.HasStartValue) 447 + t.TargetTransformable.RemoveTransform(t); 448 + } 449 + 450 + transforms.Clear(); 451 + last = null; 452 + 453 + onAbort?.Invoke(); 454 + } 455 + 456 + void ITransformSequence.TransformCompleted() 457 + { 458 + hasCompleted = true; 459 + onComplete?.Invoke(); 460 460 } 461 461 } 462 462 }
+31 -22
osu.Framework/Graphics/Transforms/Transformable.cs
··· 61 61 //expiry should happen either at the end of the last transform or using the current sequence delay (whichever is highest). 62 62 double max = TransformStartTime; 63 63 64 - foreach (Transform t in Transforms) 64 + foreach (var tracker in targetGroupingTrackers) 65 65 { 66 - if (t.EndTime > max) 67 - max = t.EndTime + 1; //adding 1ms here ensures we can expire on the current frame without issue. 66 + for (int i = 0; i < tracker.Transforms.Count; i++) 67 + { 68 + var t = tracker.Transforms[i]; 69 + if (t.EndTime > max) 70 + max = t.EndTime + 1; //adding 1ms here ensures we can expire on the current frame without issue. 71 + } 68 72 } 69 73 70 74 return max; ··· 142 146 143 147 getTrackerForGrouping(toRemove.TargetGrouping, false)?.RemoveTransform(toRemove); 144 148 145 - toRemove.OnAbort?.Invoke(); 149 + toRemove.TriggerAbort(); 146 150 } 147 151 148 152 /// <summary> ··· 294 298 return createAbsoluteSequenceAction(newTransformStartTime); 295 299 } 296 300 297 - internal virtual void CollectAbsoluteSequenceActionsFromSubTree(double newTransformStartTime, List<IDisposable> actions) 301 + internal virtual void CollectAbsoluteSequenceActionsFromSubTree(double newTransformStartTime, List<AbsoluteSequenceSender> actions) 298 302 { 299 303 actions.Add(createAbsoluteSequenceAction(newTransformStartTime)); 300 304 } 301 305 302 - private IDisposable createAbsoluteSequenceAction(double newTransformStartTime) 306 + private AbsoluteSequenceSender createAbsoluteSequenceAction(double newTransformStartTime) 303 307 { 304 308 double oldTransformDelay = TransformDelay; 305 309 double newTransformDelay = TransformDelay = newTransformStartTime - (Clock?.CurrentTime ?? 0); 306 310 307 - return new ValueInvokeOnDisposal<AbsoluteSequenceSender>(new AbsoluteSequenceSender(this, oldTransformDelay, newTransformDelay), sender => 308 - { 309 - if (!Precision.AlmostEquals(sender.NewTransformDelay, sender.Transformable.TransformDelay)) 310 - { 311 - throw new InvalidOperationException( 312 - $"{nameof(sender.Transformable.TransformStartTime)} at the end of absolute sequence is not the same as at the beginning, but should be. " + 313 - $"(begin={sender.NewTransformDelay} end={sender.Transformable.TransformDelay})"); 314 - } 315 - 316 - sender.Transformable.TransformDelay = sender.OldTransformDelay; 317 - }); 311 + return new AbsoluteSequenceSender(this, oldTransformDelay, newTransformDelay); 318 312 } 319 313 320 314 /// An ad-hoc struct used as a closure environment in <see cref="BeginAbsoluteSequence" />. 321 - private readonly struct AbsoluteSequenceSender 315 + internal readonly struct AbsoluteSequenceSender : IDisposable 322 316 { 323 - public readonly Transformable Transformable; 317 + public readonly Transformable Sender; 318 + 324 319 public readonly double OldTransformDelay; 325 320 public readonly double NewTransformDelay; 326 321 327 - public AbsoluteSequenceSender(Transformable transformable, double oldTransformDelay, double newTransformDelay) 322 + public AbsoluteSequenceSender(Transformable sender, double oldTransformDelay, double newTransformDelay) 328 323 { 329 - Transformable = transformable; 330 324 OldTransformDelay = oldTransformDelay; 331 325 NewTransformDelay = newTransformDelay; 326 + 327 + Sender = sender; 328 + } 329 + 330 + public void Dispose() 331 + { 332 + if (!Precision.AlmostEquals(NewTransformDelay, Sender.TransformDelay)) 333 + { 334 + throw new InvalidOperationException( 335 + $"{nameof(Sender.TransformStartTime)} at the end of absolute sequence is not the same as at the beginning, but should be. " + 336 + $"(begin={NewTransformDelay} end={Sender.TransformDelay})"); 337 + } 338 + 339 + Sender.TransformDelay = OldTransformDelay; 332 340 } 333 341 } 334 342 ··· 364 372 } 365 373 366 374 transform.Apply(transform.EndTime); 367 - transform.OnComplete?.Invoke(); 375 + transform.TriggerComplete(); 376 + 368 377 return; 369 378 } 370 379
+1 -9
osu.Framework/Lists/SortedList.cs
··· 94 94 95 95 public virtual void RemoveAt(int index) => list.RemoveAt(index); 96 96 97 - public int RemoveAll(Predicate<T> match) 98 - { 99 - List<T> found = (List<T>)FindAll(match); 100 - 101 - foreach (var i in found) 102 - Remove(i); 103 - 104 - return found.Count; 105 - } 97 + public int RemoveAll(Predicate<T> match) => list.RemoveAll(match); 106 98 107 99 public virtual void Clear() => list.Clear(); 108 100
-26
osu.Framework/Threading/AtomicCounter.cs
··· 1 - // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. 2 - // See the LICENCE file in the repository root for full licence text. 3 - 4 - using System.Threading; 5 - 6 - namespace osu.Framework.Threading 7 - { 8 - public class AtomicCounter 9 - { 10 - private long count; 11 - 12 - public long Increment() => Interlocked.Increment(ref count); 13 - 14 - public long Decrement() => Interlocked.Decrement(ref count); 15 - 16 - public long Add(long value) => Interlocked.Add(ref count, value); 17 - 18 - public long Reset() => Interlocked.Exchange(ref count, 0); 19 - 20 - public long Value 21 - { 22 - set => Interlocked.Exchange(ref count, value); 23 - get => Interlocked.Read(ref count); 24 - } 25 - } 26 - }