Hi,
So here I have a DesktopGL project with MonoGame 3.6.0
In my project I use RenderTarget2D one can everywhere for various use.
I get some NullReferenceException here is the stacktrace:
NullReferenceException:
Module "Microsoft.Xna.Framework.Graphics.RenderTarget2D", line 0, col 0, in Dispose { <lambda> }
Void <Dispose>b__c()
Module "Microsoft.Xna.Framework.Threading+<>c__DisplayClass1", line 0, col 0, in BlockOnUIThread { <lambda> }
Void <BlockOnUIThread>b__0()
Module "Microsoft.Xna.Framework.Threading", line 42, col 0, in Run
Void Run()
Module "Microsoft.Xna.Framework.SdlGamePlatform", line 33, col 0, in RunLoop
Void RunLoop()
Module "Microsoft.Xna.Framework.Game", line 139, col 0, in Run
Void Run(Microsoft.Xna.Framework.GameRunBehavior)
File "Program.cs", line 439, col 21, in
Int32
I think it’s due to the GC trying to Dispose a RenderTarget2D, or something like that.
So I created a class to manage my RenderTarget2Ds.
Here is the class:
// ==========================================================================================================================================
// ♦ Buffer2D.cs
// ------------------------------------------------------------------------------------------------------------------------------------------
//
// ==========================================================================================================================================
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Drawing;
using Color = Microsoft.Xna.Framework.Color;
using Rectangle = Microsoft.Xna.Framework.Rectangle;
namespace Engine127 {
// ==========================================================================================================================================
// o class Buffer2D
// ==========================================================================================================================================
public class Buffer2D : IDisposable {
private RenderTarget2D _buffer;
private readonly RenderTargetUsage _usage;
//
// ------------------------------------------------------------------------------------------------------------------------------------------
// • Constructors
// ------------------------------------------------------------------------------------------------------------------------------------------
//
public Buffer2D(int width, int height, RenderTargetUsage usage = RenderTargetUsage.DiscardContents) {
this._usage = usage;
this._buffer = GetNewRenderTarget(width, height, usage);
}
//
// ------------------------------------------------------------------------------------------------------------------------------------------
// • Destructor
// ------------------------------------------------------------------------------------------------------------------------------------------
//
~Buffer2D() => Dispose(false);
//
// ------------------------------------------------------------------------------------------------------------------------------------------
// • GetRenderTarget : RenderTarget2D
// ------------------------------------------------------------------------------------------------------------------------------------------
//
private static RenderTarget2D GetNewRenderTarget(int width, int height, RenderTargetUsage usage) => new RenderTarget2D(GraphicsDevice, width, height, false, SurfaceFormat.Color, GraphicsDevice.PresentationParameters.DepthStencilFormat, GraphicsDevice.PresentationParameters.MultiSampleCount, usage);
//
// ------------------------------------------------------------------------------------------------------------------------------------------
// • Reset : void
// ------------------------------------------------------------------------------------------------------------------------------------------
//
public void Reset(int width, int height) {
DisposeTextureAsync(this._buffer); // Add the buffer in a List<Texture2D> so the Main thread can dispose it
this._buffer = GetNewRenderTarget(width, height, this._usage);
}
//
// ------------------------------------------------------------------------------------------------------------------------------------------
// • Draw : void
// ------------------------------------------------------------------------------------------------------------------------------------------
//
public void Draw(Rectangle past, Rectangle? copy = null, Color? color = null) {
CheckDisposed();
DrawOnScreen(this._buffer, past, copy ?? this._buffer.Bounds, color ?? Color.White);
}
//
// ------------------------------------------------------------------------------------------------------------------------------------------
// • SetAsRenderTarget : void
// ------------------------------------------------------------------------------------------------------------------------------------------
//
public void SetAsRenderTarget(Color? color = null) {
CheckDisposed();
GraphicsDevice.SetRenderTarget(this._buffer);
if (this._usage == RenderTargetUsage.DiscardContents) GraphicsDevice.Clear(color ?? Color.Transparent);
}
//
// ------------------------------------------------------------------------------------------------------------------------------------------
// • CheckDisposed : void
// ------------------------------------------------------------------------------------------------------------------------------------------
//
private void CheckDisposed() {
if (this._buffer?.IsDisposed ?? true) throw new AccessViolationException("Buffer2D disposed");
}
//
// ------------------------------------------------------------------------------------------------------------------------------------------
// • Properties
// ------------------------------------------------------------------------------------------------------------------------------------------
//
public int Width => this._buffer.Width;
public int Height => this._buffer.Height;
public Rectangle Bounds => this._buffer.Bounds;
//
// ------------------------------------------------------------------------------------------------------------------------------------------
// • Dispose : void
// ------------------------------------------------------------------------------------------------------------------------------------------
//
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
//
// ------------------------------------------------------------------------------------------------------------------------------------------
// • Dispose *internal*
// ------------------------------------------------------------------------------------------------------------------------------------------
//
private void Dispose(bool notCalledFromGc) {
if (!this._buffer?.IsDisposed ?? false) DisposeTextureAsync(this._buffer); // Add the buffer in a List<Texture2D> so the Main thread can dispose it
}
}
// --- END namespace ---
}
The main thread diposing:
lock (TexturesToDisposeLock) {
int dispNum = this.TexturesToDispose.Count;
while (dispNum > 0) {
Texture2D texture = this.TexturesToDispose.First();
this.TexturesToDispose.RemoveAt(0);
//
if (texture != null) {
try {
if (!texture.IsDisposed) texture.Dispose();
} catch (Exception exception) {
Log("Disposing error");
Log(exception.ToString());
}
}
//
dispNum--;
}
}
The problem is that I still get NullReferenceException type error by RenderTarget2D.Dispose …
An idea of where that might come from?