Виртуализация графического устройства в XNA 2.0
Автор: raxxla
Во второй версии XNA, графический девайс стал полностью виртуальным. Если кратко, то вам больше никогда не потребуется заботиться о многих вещах, о которых приходилось думать ранее.
Теперь по-порядку...
Как было раньше (XNA 1.0)
Когда ваша игра стартует, происходит вызов метода LoadGraphicsContent(true), который отвечает за загрузку контента и создание ваших ресурсов. (textures, vertex buffers, rendertargets, и т.д.)
Если во время игры происходит переход между оконным и полноэкранным режимами, а так же если было нажатие alt+tab и еще в ряде некоторых случаев - графическое устройство получает статус lost (потерянного) и нуждается в пересоздании. Использовать потерянный девайс разумеется нельзя и по этому, все вызовы к нему приостанавливаются и далее происходит создание нового устройства, по завершении которого некоторые типы ресурсов так же будут нуждаться в пересоздании.
— Большинство текстур, вершинных и индексных буферов, и т.д. остаются валидными.
— Все текстуры, буферы и др. ресурсы, созданные с использованием ResourceManagementMode.Manual - будут уничтожены
— Все рендер таргеты так же будут уничтожены.
Когда такое происходит, XNA вызывает метод LoadGraphicsContent(false), который должен пересоздать все ресурсы загружаемые в режиме ResourceManagementMode.Manual.
Многих такой подход вводил в заблуждение, так как не всегда можно было четко определить какой ресурс восстановит XNA, а какой нужно восстанавливать самому. Кроме этого, необходимо было заменить все ссылки на старые ресурсы - новыми, и если ваша игра использовала одни и те же ресурсы в разных местах, то процесс такой замены становился совсем не тривиальной задачей...
Гораздо более сложная проблема возникает когда пользователь перетаскивает окно игры с одного экрана на другой (когда два монитора в системе). В таких случаях уничтожаются вообще все ресурсы, даже те, которые были созданы в режиме ResourceManagementMode.Automatic. Кроме этого, сам графический девайс то же должен быть полностью пересоздан (создается новый экземпляр объекта GraphicsDevice) и все ваши ссылки на старый девайс становятся инвалидными. Получить ссылку на новый экземпляр девайса можно было с помощью интерфейса IGraphicsDeviceService или используя класс GraphicsDeviceManager. Тут смысл в том, что правильнее будет использовать не прямые ссылки на девайс а получать референс на него используя тот же менеджер, например.
Как работает теперь (XNA 2.0)
Во второй версии XNA, и сам графический девайс и все его ресурсы постоянно остаются валидными. Тут нет необходимости заботиться о восстановлении ссылок на мертвые ресурсы, плюс к этому, на объект класса GraphicsDevice можно ссылаться где угодно и сколько угодно. Метод LoadContent, который раньше был LoadGraphicsContent, теперь вызывается только один раз - в момент старта игры.
Разумеется все проблемы с ресурсами из первой версии XNA не исчезли сами по себе и во второй версии. Внутренняя логика работы с ресурсами была полностью переделана, теперь, если происходит сброс (потеря) устройства, XNA сама создаст новый ресурс и скопирует в него все данные из старого. После чего, у владельца этого ресурса будет заменена ссылка на него. (Все классы ресурсов в XNA являются обычными обертками и просто хранят ссылку на unsafe объект, именно эта ссылка и будет заменена)
Однако, тут есть один момент. Некоторые типы ресурсов просто технически невозможно восстановить автоматом. Это относиться к ресурсам, которые постоянно обновляются из клиентского кода или с помощью GPU.
1. DynamicVertexBuffer
2. DynamicIndexBuffer
3. RenderTarget*
4. ResolveTexture2D
Трудность автоматической реконструкции таких ресурсов в том, что они так же как и остальные ресурсы, создаются в памяти самой видеокарты, чтение из которой или очень затруднено по причинам производительности или вообще невозможно. И если со статическими данными все понятно (они создаются один раз и больше не меняются), то с динамическими, нельзя быть уверенным о свежести самих этих данных в момент сброса, так как логика их обновления определяется клиентским кодом.
Звучит это может и страшно, но пугаться все же не стоит. Единственное о чем придется заботиться, это о повторном наполнении контентом таких ресурсов, сами экземпляры классов останутся валидными. Вы проверяете состояние "потерянности" ресурса используя новое свойство IsContentLost, и дальше принимаете решение запускать ваши алгоритмы реинкарнации или нет. Оживлять вам придется только сам динамический контент ресурса, все остальное (его размеры, формат и др.) останутся прежними.
Приведем пример:
Если ваша игра использует рендер таргеты для отрисовки теней или может для пост эффектов, то об новом заполнении данными таких таргетов, в момент сброса устройства, не стоит заботиться вообще, так как их наполнение происходит и так каждый кадр.
В случае если обновление происходит реже одного раза за кадр, то новое свойство IsContentLost вам в помощь ... аминь
Как это работает на Xbox
На Xbox нет мультизадачной операционной системы и большинство ситуаций потери устройства просто не имеют место быть. Тут только одна задача может выполняться в один момент времени (не путать с потоками ею порожденными), соответственно ваша игра будет полноправным владельцем графического устройства и о его сбросах, вызванных внешними причинами, можно просто забыть.
Метод LoadGraphicsContent (или LoadContent во второй версии XNA ) вызывается только один раз, в момент старта. Так что при создании Xbox-овой версии XNA нам не пришлось заниматься проблемой виртуализации графического устройства...
Источник: Shawn Hargreaves Blog (один из разработчиков XNA).
25 мая 2008 (Обновление: 3 авг 2009)