A game framework written with osu! in mind.
at master 101 lines 3.4 kB view raw
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 4using System; 5using System.Threading; 6using osu.Framework.Graphics.OpenGL.Textures; 7 8namespace osu.Framework.Graphics.Textures 9{ 10 /// <summary> 11 /// A texture which shares a common reference count with all other textures using the same <see cref="TextureGL"/>. 12 /// </summary> 13 internal class TextureWithRefCount : Texture 14 { 15 private readonly ReferenceCount count; 16 17 public TextureWithRefCount(TextureGL textureGl, ReferenceCount count) 18 : base(textureGl) 19 { 20 this.count = count; 21 22 count.Increment(); 23 } 24 25 public sealed override TextureGL TextureGL 26 { 27 get 28 { 29 if (!Available) 30 throw new InvalidOperationException($"Attempting to access a {nameof(TextureWithRefCount)}'s underlying texture after all references are lost."); 31 32 return base.TextureGL; 33 } 34 } 35 36 // The base property invokes the overridden TextureGL property, which will throw an exception if not available 37 // So this property is redirected to reference the intended member 38 public sealed override bool Available => base.TextureGL.Available; 39 40 ~TextureWithRefCount() 41 { 42 // Finalizer implemented here rather than Texture to avoid GC overhead. 43 Dispose(false); 44 } 45 46 public bool IsDisposed { get; private set; } 47 48 protected override void Dispose(bool isDisposing) 49 { 50 base.Dispose(isDisposing); 51 52 if (IsDisposed) 53 return; 54 55 IsDisposed = true; 56 57 count.Decrement(); 58 } 59 60 public class ReferenceCount 61 { 62 private readonly object lockObject; 63 private readonly Action onAllReferencesLost; 64 65 private int referenceCount; 66 67 /// <summary> 68 /// Creates a new <see cref="ReferenceCount"/>. 69 /// </summary> 70 /// <param name="lockObject">The <see cref="object"/> which locks will be taken out on.</param> 71 /// <param name="onAllReferencesLost">A delegate to invoke after all references have been lost.</param> 72 public ReferenceCount(object lockObject, Action onAllReferencesLost) 73 { 74 this.lockObject = lockObject; 75 this.onAllReferencesLost = onAllReferencesLost; 76 } 77 78 /// <summary> 79 /// Increments the reference count. 80 /// </summary> 81 public void Increment() 82 { 83 lock (lockObject) 84 Interlocked.Increment(ref referenceCount); 85 } 86 87 /// <summary> 88 /// Decrements the reference count, invoking <see cref="onAllReferencesLost"/> if there are no remaining references. 89 /// The delegate is invoked while a lock on the provided <see cref="lockObject"/> is held. 90 /// </summary> 91 public void Decrement() 92 { 93 lock (lockObject) 94 { 95 if (Interlocked.Decrement(ref referenceCount) == 0) 96 onAllReferencesLost?.Invoke(); 97 } 98 } 99 } 100 } 101}