Atomically call graphics resource destroy function

pull/53/head
cosmonaut 2023-12-08 16:07:38 -08:00
parent 528fb7ac7c
commit 450b08cbd8
4 changed files with 16 additions and 72 deletions

View File

@ -235,8 +235,6 @@ namespace MoonWorks
Draw(alpha); Draw(alpha);
accumulatedDrawTime -= FramerateCapTimeSpan; accumulatedDrawTime -= FramerateCapTimeSpan;
GraphicsDevice.FlushEmergencyDisposalQueue();
} }
} }

View File

@ -355,21 +355,6 @@ namespace MoonWorks.Graphics
} }
} }
ConcurrentQueue<GraphicsResourceDisposalHandle> emergencyDisposalQueue = new ConcurrentQueue<GraphicsResourceDisposalHandle>();
internal void RegisterForEmergencyDisposal(GraphicsResourceDisposalHandle handle)
{
emergencyDisposalQueue.Enqueue(handle);
}
internal void FlushEmergencyDisposalQueue()
{
while (emergencyDisposalQueue.TryDequeue(out var handle))
{
handle.Dispose(this);
}
}
protected virtual void Dispose(bool disposing) protected virtual void Dispose(bool disposing)
{ {
if (!IsDisposed) if (!IsDisposed)
@ -387,11 +372,9 @@ namespace MoonWorks.Graphics
} }
resources.Clear(); resources.Clear();
} }
Refresh.Refresh_DestroyDevice(Handle);
} }
FlushEmergencyDisposalQueue(); Refresh.Refresh_DestroyDevice(Handle);
IsDisposed = true; IsDisposed = true;
} }

View File

@ -1,18 +1,20 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading;
namespace MoonWorks.Graphics namespace MoonWorks.Graphics
{ {
// TODO: give this a Name property for debugging use
public abstract class GraphicsResource : IDisposable public abstract class GraphicsResource : IDisposable
{ {
public GraphicsDevice Device { get; } public GraphicsDevice Device { get; }
public IntPtr Handle { get; internal set; } public IntPtr Handle { get => handle; internal set => handle = value; }
private nint handle;
public bool IsDisposed { get; private set; } public bool IsDisposed { get; private set; }
protected abstract Action<IntPtr, IntPtr> QueueDestroyFunction { get; } protected abstract Action<IntPtr, IntPtr> QueueDestroyFunction { get; }
private GCHandle SelfReference; private GCHandle SelfReference;
protected GraphicsResource(GraphicsDevice device) protected GraphicsResource(GraphicsDevice device)
{ {
Device = device; Device = device;
@ -21,26 +23,21 @@ namespace MoonWorks.Graphics
Device.AddResourceReference(SelfReference); Device.AddResourceReference(SelfReference);
} }
private GraphicsResourceDisposalHandle CreateDisposalHandle()
{
return new GraphicsResourceDisposalHandle
{
QueueDestroyAction = QueueDestroyFunction,
ResourceHandle = Handle
};
}
protected void Dispose(bool disposing) protected void Dispose(bool disposing)
{ {
if (!IsDisposed) if (!IsDisposed)
{ {
if (Handle != IntPtr.Zero) if (disposing)
{ {
QueueDestroyFunction(Device.Handle, Handle);
Device.RemoveResourceReference(SelfReference); Device.RemoveResourceReference(SelfReference);
SelfReference.Free(); SelfReference.Free();
}
Handle = IntPtr.Zero; // Atomically call destroy function in case this is called from the finalizer thread
var toDispose = Interlocked.Exchange(ref handle, IntPtr.Zero);
if (toDispose != IntPtr.Zero)
{
QueueDestroyFunction(Device.Handle, toDispose);
} }
IsDisposed = true; IsDisposed = true;
@ -50,20 +47,12 @@ namespace MoonWorks.Graphics
~GraphicsResource() ~GraphicsResource()
{ {
#if DEBUG #if DEBUG
// If the graphics device associated with this resource was already disposed, we assume // If you see this log message, you leaked a graphics resource without disposing it!
// that your game is in the middle of shutting down. // We'll try to clean it up for you but you really should fix this.
if (!IsDisposed && Device != null && !Device.IsDisposed) Logger.LogWarn($"A resource of type {GetType().Name} was not Disposed.");
{
// If you see this log message, you leaked a graphics resource without disposing it!
// This means your game may eventually run out of native memory for mysterious reasons.
Logger.LogWarn($"A resource of type {GetType().Name} was not Disposed.");
}
#endif #endif
// While we only log in debug builds, in both debug and release builds we want to free Dispose(false);
// any native resources associated with this object at the earliest opportunity.
// This will at least prevent you from running out of memory rapidly.
Device.RegisterForEmergencyDisposal(CreateDisposalHandle());
} }
public void Dispose() public void Dispose()

View File

@ -1,26 +0,0 @@
using System;
namespace MoonWorks.Graphics
{
// This allows us to defer native disposal calls from the finalizer thread.
internal struct GraphicsResourceDisposalHandle
{
internal Action<IntPtr, IntPtr> QueueDestroyAction;
internal IntPtr ResourceHandle;
public void Dispose(GraphicsDevice device)
{
if (device == null)
{
throw new ArgumentNullException(nameof(device));
}
if (QueueDestroyAction == null)
{
return;
}
QueueDestroyAction(device.Handle, ResourceHandle);
}
}
}