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

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

主Draw方法中的代码应始终将主Render Target重置为null ,因此没有预期的情况,通常不会发生这种情况。

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

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

 graphics.PreferredBackBufferWidth = graphics.PreferredBackBufferWidth; graphics.PreferredBackBufferHeight = graphics.PreferredBackBufferHeight; graphics.ApplyChanges(); 

…在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(); 

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

UPD :有Window.ClientSizeChangedgraphics.PreparingDeviceSettings事件,但即使它们触发,默认渲染目标似乎没有帮助。

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

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

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


这是exception消息:

 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: 

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

如何防止此错误?


可能相关的问题:

  • 当我将XNA游戏窗口的垂直大小更改为最小值时,它会为spritebatch抛出ObjectDisposedException,为什么?
  • 如果出现问题,是否可以恢复GraphicsDevice?

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

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

除此之外,我曾经有过类似的问题。 我在绘制调用结束时处理了一个纹理。 除非我试图移动窗口,否则这样可以正常工作。 每隔一段时间,当我试图移动游戏窗口时,会发生ObjectDisposedexception(对于纹理)。 我对推理的最好猜测是更新线程和绘制线程将会错位,如果只是短时间,并且纹理将在它有机会重置之前再次调用。 我从来没有找到办法阻止这种效果,除了在尝试绘制之前确保没有丢弃对象。

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

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

您不应该像在RenderTarget2D那样在Draw调用中创建任何图形资源。 首先,创建这样的资源很慢,并且只应对GraphicsDevice一次。 只有Set调用应该在Draw方法内部,因为设置已经创建的资源要快得多,因为它们已经在图形设备内存中。

你应该怎么做 – 是在LoadContent调用中移动所有图形资源(包括RenderTarget2D )并在Draw只留下Set方法。 每当重新创建GraphicsDevice (例如,在调整视口大小时),都会调用LoadContent方法。 因此,所有以前创建的资源也将被重新创建。

我认为您的exception是因为您在绘制方法处于活动状态时重新创建图形设备。 您只应在游戏运行后更改更新方法中的设备设置。 如果要更改分辨率,请将某个变量(如bool)设置为true,在更新方法中检查该值并在其中应用新分辨率。

 public class Game1 : Microsoft.Xna.Framework.Game { protected override void Update(GameTime gameTime) { if(resolutionChanged) { graphics.PreferredBackBufferHeight = userRequestedHeight; graphics.PreferredBackBufferWidth = userRequestedWidth; graphics.ApplyChanges(); } // ... } // ... } 

此外,我还与OpenMinded合作:永远不要在每帧的基础上创建资源。 使用GraphicsDevicerManagers PreparingDeviceSettings事件。 重置或重新创建图形设备时会触发它。