Подтвердить что ты не робот

Как предотвратить использование GraphicsDevice при применении новых настроек?

В моем игровом окне разрешено ручное изменение размеров, что означает, что его можно изменить как любое другое нормальное окно, перетаскивая его края. В игре также используется RenderTarget2D rt2d, для которой основная цель Render задана в основном методе Draw: GraphicsDevice.SetRenderTarget(rt2d), но она reset возвращается к null (целевой рендеринга по умолчанию) в конце основной метод Draw, что делает его несколько запутанным: действительно ли это источник проблемы, изменяя размер окна игры прямо между моментом, когда Render Target установлен на rt2d, а не reset обратно на значение по умолчанию? Сейчас это похоже.

Код в основном методе Draw должен всегда reset основной целевой рендеринга вернуться к null, поэтому ожидаемых случаев не будет, когда этого обычно не будет.

Тем не менее, результат изменения размера окна игры иногда вызывает GraphicsDevice.isDisposed return true, а затем игра бросает System.ObjectDisposedException на первый SpriteBatch.End(). Я нашел сообщения об этой ошибке, возвращаясь к первым дням XNA, но без хорошего объяснения (а также без упоминания об изменении Render Target, поэтому, возможно, это и послужило источником проблемы для этих плакатов).

Сейчас я могу вызвать эту ошибку, вызвав этот метод несколько раз:

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

Мое предположение заключается в том, что я должен отменить рисование кадра и сбросить цель рендеринга, если изменение размера произойдет до того, как цель визуализации будет reset, но я все еще не уверен, что это именно то, что вызывает это.

UPD. Есть события Window.ClientSizeChanged и graphics.PreparingDeviceSettings, но даже когда они срабатывают, по умолчанию цель рендеринга не помогает.

Я предполагаю, что это не "тайм-аут между изменением размера клиентской области и применением новых графических настроек" или что-то еще. Это, скорее всего, вызвано неиндексной целью рендеринга.

И, вероятно, это не так, что размер целевого объекта рендеринга отличается от нового размера экрана, поскольку это также вызывает исключение при изменении размеров графического устройства с такими же значениями.

UPD2. Я просто попробовал сделать полноэкранный режим переключением ожидающей операции, установив для параметра F11 значение isFullscreenTogglePending значение true и проверив его в начале основного метода 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: 

Он находится в SpriteBatch.End() в главном вызове Draw.

Как предотвратить эту ошибку?


Возможно, связанные вопросы:

4b9b3361

Ответ 1

Две вещи: 1. Я не знаком с задачами рендеринга... но, может быть, это поможет? Из MSDN:

"Рендеринг целей представляет собой линейную область памяти дисплея и обычно находится в памяти дисплея карты дисплея. Из-за этого объекты RenderTarget должны быть воссозданы, когда устройство reset."

2. Кроме того, у меня была аналогичная проблема в какой-то момент. Я удалял текстуру в конце призыва на ничью. Это будет работать нормально, если я не попытаюсь переместить окно. Время от времени возникает исключение ObjectDisposed (для текстуры), когда я пытался переместить окно игры. Моя лучшая догадка заключается в том, что поток обновлений и ничьей нити будут смещены, хотя бы на короткое время, и текстура будет вызвана снова, прежде чем у нее будет шанс на reset. Я никогда не нашел способ остановить эффект, кроме как просто убедиться, что объект не был удален, прежде чем пытаться рисовать.

Конечно, наши ситуации могут быть совершенно несвязаны, но в качестве возможного исправления просто добавьте флаг, который остановит любые призывы рисования, когда окно будет недавно изменено.

И если это не решит проблему, надеюсь, это поможет сузить проблему.

Ответ 2

Вы не должны создавать графические ресурсы внутри вызова Draw, как вы делали с RenderTarget2D. Прежде всего создание таких ресурсов происходит медленно и должно выполняться только один раз для GraphicsDevice. Только Set calls должны быть внутри метода Draw, поскольку настройка уже созданного ресурса происходит намного быстрее, поскольку они уже находятся в памяти графического устройства.

Что вы должны сделать - это переместить все создание графических ресурсов (включая RenderTarget2D) внутри вызова LoadContent и оставить только задание методов внутри Draw. Метод LoadContent вызывается всякий раз, когда GraphicsDevice воссоздается (например, при изменении размера окна просмотра). Таким образом, все ранее созданные ресурсы будут воссозданы.

Ответ 3

Я думаю, что ваше исключение приходит, потому что вы воссоздаете графическое устройство, когда метод draw активен. При запуске игры следует изменить настройки устройства только в методе обновления. Установите некоторую переменную как 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 "Подготовка к событию". Он будет запущен, когда графическое устройство будет reset или воссоздано.