From 32653e7af59255596a724d4d501c8875be782341 Mon Sep 17 00:00:00 2001 From: Christopher Whitley Date: Thu, 30 May 2024 00:55:51 -0400 Subject: [PATCH] Implements standard IDispose pattern --- .../ParticleBuffer.cs | 53 ++++++++++--------- .../ParticleEffect.cs | 44 ++++++++++++++- .../ParticleEmitter.cs | 42 +++++++++++++-- .../ParticleExtensions.cs | 12 ++++- 4 files changed, 121 insertions(+), 30 deletions(-) diff --git a/source/MonoGame.Extended.Particles/ParticleBuffer.cs b/source/MonoGame.Extended.Particles/ParticleBuffer.cs index 2e410fa83..e361b8b7c 100644 --- a/source/MonoGame.Extended.Particles/ParticleBuffer.cs +++ b/source/MonoGame.Extended.Particles/ParticleBuffer.cs @@ -11,7 +11,6 @@ public class ParticleBuffer : IDisposable // points to the first memory pos after the buffer protected readonly unsafe Particle* BufferEnd; - private bool _disposed; // points to the particle after the last active particle. protected unsafe Particle* Tail; @@ -44,16 +43,28 @@ public unsafe ParticleBuffer(int size) // total size of active particles public int ActiveSizeInBytes => Particle.SizeInBytes*Count; + /// + /// Gets a value that indicates whether this instance of the class has been + /// disposed. + /// + public bool IsDisposed { get; set; } + public void Dispose() { - if (!_disposed) + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if(IsDisposed) { - Marshal.FreeHGlobal(_nativePointer); - _disposed = true; - GC.RemoveMemoryPressure(SizeInBytes); + return; } - GC.SuppressFinalize(this); + Marshal.FreeHGlobal(_nativePointer); + GC.RemoveMemoryPressure(SizeInBytes); + IsDisposed = true; } /// @@ -62,6 +73,8 @@ public void Dispose() /// public unsafe ParticleIterator Release(int releaseQuantity) { + ThrowIfDisposed(); + var numToRelease = Math.Min(releaseQuantity, Available); var prevCount = Count; @@ -75,6 +88,8 @@ public unsafe ParticleIterator Release(int releaseQuantity) public unsafe void Reclaim(int number) { + ThrowIfDisposed(); + Count -= number; Head += number; @@ -82,27 +97,17 @@ public unsafe void Reclaim(int number) Head -= Size + 1; } - //public void CopyTo(IntPtr destination) - //{ - // memcpy(destination, _nativePointer, ActiveSizeInBytes); - //} - - //public void CopyToReverse(IntPtr destination) - //{ - // var offset = 0; - // for (var i = ActiveSizeInBytes - Particle.SizeInBytes; i >= 0; i -= Particle.SizeInBytes) - // { - // memcpy(IntPtr.Add(destination, offset), IntPtr.Add(_nativePointer, i), Particle.SizeInBytes); - // offset += Particle.SizeInBytes; - // } - //} - - //[DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)] - //public static extern void memcpy(IntPtr dest, IntPtr src, int count); + private void ThrowIfDisposed() + { + if(IsDisposed) + { + throw new ObjectDisposedException(nameof(ParticleBuffer)); + } + } ~ParticleBuffer() { - Dispose(); + Dispose(false); } public class ParticleIterator diff --git a/source/MonoGame.Extended.Particles/ParticleEffect.cs b/source/MonoGame.Extended.Particles/ParticleEffect.cs index ce6c78205..89069e579 100644 --- a/source/MonoGame.Extended.Particles/ParticleEffect.cs +++ b/source/MonoGame.Extended.Particles/ParticleEffect.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; +using System.Runtime.CompilerServices; using System.Text.Json; using Microsoft.Xna.Framework; using MonoGame.Extended.Particles.Serialization; @@ -17,16 +19,42 @@ public ParticleEffect(string name = null) Emitters = new List(); } + ~ParticleEffect() => Dispose(false); + public void Dispose() { - foreach (var emitter in Emitters) - emitter.Dispose(); + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if(IsDisposed) + { + return; + } + + if(disposing) + { + foreach(var emitter in Emitters) + { + emitter.Dispose(); + } + } + + IsDisposed = true; } public string Name { get; set; } public List Emitters { get; set; } public int ActiveParticles => Emitters.Sum(t => t.ActiveParticles); + /// + /// Gets a value that indicates whether this instance of the class has been + /// disposed. + /// + public bool IsDisposed { get; private set; } + public void FastForward(Vector2 position, float seconds, float triggerPeriod) { var time = 0f; @@ -54,6 +82,8 @@ public static ParticleEffect FromStream(ITextureRegionService textureRegionServi public void Update(float elapsedSeconds) { + ThrowIfDisposed(); + for (var i = 0; i < Emitters.Count; i++) Emitters[i].Update(elapsedSeconds, Position); } @@ -65,16 +95,26 @@ public void Trigger() public void Trigger(Vector2 position, float layerDepth = 0) { + ThrowIfDisposed(); for (var i = 0; i < Emitters.Count; i++) Emitters[i].Trigger(position, layerDepth); } public void Trigger(LineSegment line, float layerDepth = 0) { + ThrowIfDisposed(); for (var i = 0; i < Emitters.Count; i++) Emitters[i].Trigger(line, layerDepth); } + private void ThrowIfDisposed() + { + if(IsDisposed) + { + throw new ObjectDisposedException(nameof(ParticleBuffer)); + } + } + public override string ToString() { return Name; diff --git a/source/MonoGame.Extended.Particles/ParticleEmitter.cs b/source/MonoGame.Extended.Particles/ParticleEmitter.cs index 1a6273b2c..65ac488e5 100644 --- a/source/MonoGame.Extended.Particles/ParticleEmitter.cs +++ b/source/MonoGame.Extended.Particles/ParticleEmitter.cs @@ -39,13 +39,25 @@ public ParticleEmitter(TextureRegion2D textureRegion, int capacity, TimeSpan lif public void Dispose() { - Buffer.Dispose(); + Dispose(true); GC.SuppressFinalize(this); } + protected virtual void Dispose(bool disposing) + { + if(IsDisposed) + { + return; + } + + Buffer.Dispose(); + Buffer = null; + IsDisposed = true; + } + ~ParticleEmitter() { - Dispose(); + Dispose(false); } public string Name { get; set; } @@ -57,6 +69,12 @@ public void Dispose() public ParticleReleaseParameters Parameters { get; set; } public TextureRegion2D TextureRegion { get; set; } + /// + /// Gets a value that indicates whether this instance of the class has been + /// disposed. + /// + public bool IsDisposed { get; private set;} + [EditorBrowsable(EditorBrowsableState.Never)] public ParticleModifierExecutionStrategy ModifierExecutionStrategy { get; set; } @@ -64,9 +82,15 @@ public void Dispose() public int Capacity { - get { return Buffer.Size; } + get + { + ThrowIfDisposed(); + return Buffer.Size; + } set { + ThrowIfDisposed(); + var oldBuffer = Buffer; oldBuffer.Dispose(); Buffer = new ParticleBuffer(value); @@ -125,6 +149,8 @@ private void ReclaimExpiredParticles() public bool Update(float elapsedSeconds, Vector2 position = default(Vector2)) { + ThrowIfDisposed(); + _totalSeconds += elapsedSeconds; if (_autoTrigger) @@ -176,6 +202,8 @@ public void Trigger(LineSegment line, float layerDepth = 0) private void Release(Vector2 position, int numToRelease, float layerDepth) { + ThrowIfDisposed(); + var iterator = Buffer.Release(numToRelease); while (iterator.HasNext) @@ -214,6 +242,14 @@ private void Release(Vector2 position, int numToRelease, float layerDepth) } } + private void ThrowIfDisposed() + { + if(IsDisposed) + { + throw new ObjectDisposedException(nameof(ParticleBuffer)); + } + } + public override string ToString() { return Name; diff --git a/source/MonoGame.Extended.Particles/ParticleExtensions.cs b/source/MonoGame.Extended.Particles/ParticleExtensions.cs index 7566e8d01..c86edaef2 100644 --- a/source/MonoGame.Extended.Particles/ParticleExtensions.cs +++ b/source/MonoGame.Extended.Particles/ParticleExtensions.cs @@ -8,12 +8,22 @@ public static class ParticleExtensions { public static void Draw(this SpriteBatch spriteBatch, ParticleEffect effect) { + if(effect.IsDisposed) + { + return; + } + for (var i = 0; i < effect.Emitters.Count; i++) UnsafeDraw(spriteBatch, effect.Emitters[i]); } public static void Draw(this SpriteBatch spriteBatch, ParticleEmitter emitter) { + if(emitter.IsDisposed) + { + return; + } + UnsafeDraw(spriteBatch, emitter); } @@ -46,4 +56,4 @@ private static unsafe void UnsafeDraw(SpriteBatch spriteBatch, ParticleEmitter e } } } -} \ No newline at end of file +}