如何防止在应用新设置时处理GraphicsDevice?

use*_*322 11 c# xna objectdisposedexception

我的游戏窗口允许手动调整大小,这意味着它可以像任何其他普通窗口一样通过拖动其边缘来调整大小.游戏还使用RenderTarget2D rt2d了主渲染目标在主Draw方法中设置的a:,GraphicsDevice.SetRenderTarget(rt2d)但它null在主Draw方法的末尾被重置回(默认渲染目标),这使得它有点令人困惑:这是否真的是问题的根源,在Render Target设置为的时刻之前调整游戏窗口的大小rt2d,而不是重置为默认值?现在它看起来像.

主Draw方法中的代码应该始终将主渲染目标重置为null,因此没有预期的情况,这通常不会发生.

尽管如此,调整游戏窗口大小的结果有时会导致GraphicsDevice.isDisposed返回true,然后游戏会System.ObjectDisposedException在第一个时间内抛出SpriteBatch.End().我发现有关这个错误的帖子可以追溯到XNA的第一天,但​​是没有一个很好的解释(也没有提到改变渲染目标,所以它也可能是这些海报问题的根源).

现在,我可以通过调用此方法几次来触发此错误:

graphics.PreferredBackBufferWidth = graphics.PreferredBackBufferWidth;
graphics.PreferredBackBufferHeight = graphics.PreferredBackBufferHeight;
graphics.ApplyChanges();
Run Code Online (Sandbox Code Playgroud)

...在Draw主方法中使用以下行:

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();
Run Code Online (Sandbox Code Playgroud)

我的猜测是,如果在重置渲染目标之前调整大小,我应该中止帧绘制并重置渲染目标,但我仍然不确定这究竟是导致这种情况的原因.

UPD:有Window.ClientSizeChangedgraphics.PreparingDeviceSettings事件,但即使他们开火,默认渲染目标似乎没有帮助.

我想这不是"调整客户区域和应用新图形设置之间的超时"等等.这很可能是由非默认渲染目标引起的.

并且可能不是渲染目标大小与新屏幕大小不同,因为当将图形设备尺寸更改为完全相同的值时,这也会引发异常.

UPD2:我刚尝试全屏切换挂起的操作,将F11设置设置isFullscreenTogglePendingtrue并在main Update方法的开头检查它,它根本没有帮助.然后我发现以前的全屏模式也是从主Update方法中切换出来的,只是在开始时没有,而是在输入更新方法的中途,所以在主Update方法的哪个地方运行它并不重要,它仍然会导致此错误.有趣的GraphicsDevice.isDisposed是,抛出异常时它是错误的.


这是异常消息:

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: 
Run Code Online (Sandbox Code Playgroud)

它位于spriteBatch.End()主要的Draw调用中.

如何防止此错误?


可能相关的问题:

Col*_*ton 1

两件事:1.我不熟悉渲染目标......但这也许会有帮助?来自 MSDN:

“渲染目标代表显示内存的线性区域,通常驻留在显示卡的显示内存中。因此,重置设备时必须重新创建 RenderTarget 对象。”

2.除此之外,我曾经也遇到过类似的问题。我在绘制调用结束时处理了纹理。这会很好地工作,除非我尝试移动窗口。每隔一段时间,当我尝试移动游戏窗口时,就会发生 ObjectDispose 异常(对于纹理)。我对推理的最佳猜测是,更新线程和绘制线程会错位,即使只是短暂的一段时间,并且纹理会在有机会重置之前再次被调用。我从来没有找到一种方法来阻止这种效果,除了确保在尝试绘制之前没有处理该对象之外。

当然,我们的情况可能完全不相关,但作为一种可能的解决方案,只需添加一个标志,该标志将在窗口最近重新调整大小时停止任何绘制调用。

如果这不能解决问题,希望它能帮助缩小问题范围。