My game window has manual resizing allowed, which means it can be resized like any other normal window, by dragging its edges. The game also makes use of a RenderTarget2D rt2d
, to which the main Render Target is set in the main Draw method: GraphicsDevice.SetRenderTarget(rt2d)
, but it is reset back to null
(the default render target) in the end of the main Draw method, which makes it a little bit confusing: is this really the source of the problem, resizing the game window right between the moment Render Target is set to rt2d
, and not reset back to default? Right now it looks like it.
The code within the main Draw method is supposed to always reset the main Render Target back to null
, so there are no expected cases when this normally wouldn't happen.
Still, the result of resizing the game window sometimes causes GraphicsDevice.isDisposed
return true
, and then the game throws System.ObjectDisposedException
at the first SpriteBatch.End()
. I've found posts about this error going back to the first days of XNA, but without a good explanation (and also without mentioning changing Render Target, so it may have been the source of the problem for those posters too).
Right now I'm able to trigger this error by calling this method a few times:
graphics.PreferredBackBufferWidth = graphics.PreferredBackBufferWidth;
graphics.PreferredBackBufferHeight = graphics.PreferredBackBufferHeight;
graphics.ApplyChanges();
…With the following lines in the main Draw method:
RenderTarget2D rt2d = new RenderTarget2D(GraphicsDevice,
graphics.PreferredBackBufferWidth,
graphics.PreferredBackBufferHeight);
GraphicsDevice.SetRenderTarget(rt2d);
sb.Begin();
// main draw method here, it's pretty big, so it might be taking long
// enough to process to actually resize before resetting render target
sb.End();
GraphicsDevice.SetRenderTarget(null);
sb.Begin();
// draw the whole rt2d to the screen
sb.End();
My guess is that I should be aborting the frame draw and resetting the render target if the resizing happens before the Render Target is reset, but I'm still not sure this is exactly what is causing this.
UPD: There are Window.ClientSizeChanged
and graphics.PreparingDeviceSettings
events, but even when they fire, defaulting the render target doesn't seem to help.
I guess this is not "timeout between resizing the client area and new graphics settings applying" or whatever. This is most likely caused by non-default render target.
And it's probably not that the render target size becomes different from new screen size, because this also throws exception when changing graphics device dimensions to the exact same values.
UPD2: I just tried making fullscreen toggling a pending operation, making the F11 set isFullscreenTogglePending
to true and checking it in the beginning of the main Update
method, and it didn't help at all. Then I figured out that previously fullscreen mode was also being toggled from the main Update method, only not at the very beginning, but halfway through the input update method, so it doesn't really matter where in the main Update
method it is run, it still causes this error. Interestingly, the GraphicsDevice.isDisposed
is false when the exception is thrown.
This is the exception message:
System.ObjectDisposedException occurred
Message=Cannot access a disposed object.
Object name: 'GraphicsDevice'.
Source=Microsoft.Xna.Framework
ObjectName=GraphicsDevice
StackTrace:
at Microsoft.Xna.Framework.Helpers.CheckDisposed(Object obj, IntPtr pComPtr)
at Microsoft.Xna.Framework.Graphics.BlendState.Apply(GraphicsDevice device)
at Microsoft.Xna.Framework.Graphics.GraphicsDevice.set_BlendState(BlendState value)
at Microsoft.Xna.Framework.Graphics.SpriteBatch.SetRenderState()
at Microsoft.Xna.Framework.Graphics.SpriteBatch.End()
at secret_project.Game1.Draw(GameTime gameTime) in P:\msvs projects\secret_project\Game1.cs:line 3310
InnerException:
It's at the spriteBatch.End()
in the main Draw call.
How do I prevent this error?
Possibly related questions:
Update()
getting called every frame the mouse button is held down, made the window resizable, and put the rest inDraw()
. No matter what I do, it doesn't crash. Are you sure it's the GraphicsDevice that's disposed? Errors that happen inside Begin() and End() may not show up until End(), perhaps something you are trying to draw got disposed. At a quick glance, xboxforums.create.msdn.com/forums/p/86840/536058.aspx looks like it might be relevant. – Kelbee