Объект приложения и DirectX
универсальная платформа Windows (UWP) с играми DirectX не используют многие элементы пользовательского интерфейса и объекты пользовательского интерфейса Windows. Скорее, поскольку они выполняются на более низком уровне в стеке среда выполнения Windows, они должны взаимодействовать с платформой пользовательского интерфейса более фундаментальным образом: путем доступа к объекту приложения и взаимодействия напрямую. Узнайте, когда и как происходит эта взаимодействие, а также как вы, разработчик DirectX, можете эффективно использовать эту модель в разработке приложения UWP.
Ознакомьтесь с глоссарием графики Direct3D для получения сведений о незнакомых терминах или понятиях, которые возникают при чтении.
Важные основные пространства имен пользовательского интерфейса
Сначала обратите внимание на среда выполнения Windows пространства имен, которые необходимо включить (с использованием) в приложение UWP. Мы смотрим на подробности немного.
- Windows.ApplicationModel.Core
- Windows.ApplicationModel.Activation
- Windows.UI.Core
- Windows.System
- Windows.Foundation
Примечание.
Если вы не разрабатываете приложение UWP, используйте компоненты пользовательского интерфейса, предоставляемые в библиотеках и пространствах имен javaScript или XAML, а не типы, предоставляемые в этих пространствах имен.
Объект приложения среда выполнения Windows
В приложении UWP вы хотите получить окно и поставщик представлений, из которого можно получить представление и к которому можно подключить цепочку буферов (буферы отображения). Вы также можете подключить это представление к событиям для конкретного окна для запущенного приложения. Чтобы получить родительское окно для объекта приложения, определенного типом CoreWindow, создайте тип, реализующий IFrameworkViewSource. Пример кода C++/WinRT, показывающий, как реализовать IFrameworkViewSource, см. в статье "Композиция машинного взаимодействия с DirectX и Direct2D".
Ниже приведен базовый набор шагов для получения окна с помощью основной платформы пользовательского интерфейса.
Создайте тип, реализующий IFrameworkView. Это ваше представление.
В этом типе определите следующее:
- Метод Initialize, который принимает экземпляр CoreApplicationView в качестве параметра. Экземпляр этого типа можно получить, вызвав CoreApplication.CreateNewView. Объект приложения вызывает его при запуске приложения.
- Метод SetWindow, который принимает экземпляр CoreWindow в качестве параметра. Экземпляр этого типа можно получить, доступ к свойству CoreWindow в новом экземпляре CoreApplicationView.
- Метод Load, который принимает строку для точки входа в качестве единственного параметра. Объект приложения предоставляет строку точки входа при вызове этого метода. Здесь вы настроили ресурсы. Здесь вы создаете ресурсы устройства. Объект приложения вызывает его при запуске приложения.
- Метод Run, который активирует объект CoreWindow и запускает диспетчер событий окна. Объект приложения вызывает его при запуске процесса приложения.
- Метод Uninitialize, который очищает ресурсы, настроенные в вызове Load. Объект приложения вызывает этот метод при закрытии приложения.
Создайте тип, реализующий IFrameworkViewSource. Это ваш поставщик представлений.
В этом типе определите следующее:
- Метод с именем CreateView , который возвращает экземпляр реализации IFrameworkView , как было создано на шаге 1.
Передайте экземпляр поставщика представления в CoreApplication.Run из main.
Учитывая эти основы, давайте рассмотрим дополнительные варианты, которые вы должны расширить этот подход.
Основные типы пользовательского интерфейса
Ниже приведены другие основные типы пользовательского интерфейса в среда выполнения Windows, которые могут оказаться полезными.
- Windows.ApplicationModel.Core.CoreApplicationView
- Windows.UI.Core.CoreWindow
- Windows.UI.Core.CoreDispatcher
Эти типы можно использовать для доступа к представлению приложения, в частности, биты, нарисующие содержимое родительского окна приложения, и обрабатывать события, запущенные для этого окна. Процесс окна приложения — это однопоточная квартира (ASTA), которая изолирована и обрабатывает все обратные вызовы.
Представление вашего приложения создается поставщиком представлений для окна приложения, и в большинстве случаев реализуется определенным пакетом платформы или самой системой, поэтому вам не нужно реализовать его самостоятельно. Для DirectX необходимо реализовать поставщик тонкого представления, как описано ранее. Между следующими компонентами и поведением существует определенная связь 1–1:
- Представление приложения, представленное типом CoreApplicationView и определяющим методы для обновления окна.
- AsTA, указание которого определяет поведение потоков приложения. Вы не можете создавать экземпляры типов с атрибутами COM STA в ASTA.
- Поставщик представлений, который приложение получает из системы или которое вы реализуете.
- Родительское окно, представленное типом CoreWindow .
- Подключение ко всем событиям активации. Оба представления и окна имеют отдельные события активации.
В итоге объект приложения предоставляет фабрику поставщиков представлений. Он создает поставщик представлений и создает родительское окно для приложения. Поставщик представлений определяет представление приложения для родительского окна приложения. Теперь рассмотрим особенности представления и родительского окна.
Поведение и свойства CoreApplicationView
CoreApplicationView представляет текущее представление приложения. Одноэлементное приложение создает представление приложения во время инициализации, но представление остается неактивным до его активации. Чтобы получить представление CoreWindow, можно получить доступ к свойству CoreApplicationView.CoreWindow, и вы можете обрабатывать события активации и деактивации для представления, регистрируя делегаты в событии CoreApplicationView.Activated.
Поведение и свойства CoreWindow
Родительское окно, которое является экземпляром CoreWindow , создается и передается поставщику представлений при инициализации объекта приложения. Если у приложения есть окно для отображения, он отображает его; в противном случае он просто инициализирует представление.
CoreWindow предоставляет ряд событий, относящихся к входным и базовым поведениям окон. Эти события можно обрабатывать, регистрируя собственные делегаты.
Вы также можете получить диспетчер событий окна для окна, получив доступ к свойству CoreWindow.Dispatcher, который предоставляет экземпляр CoreDispatcher.
Поведение и свойства CoreDispatcher
Вы можете определить поведение потоковой отправки событий для окна с типом CoreDispatcher . В этом типе существует один особенно важный метод: метод CoreDispatcher.ProcessEvents , который запускает обработку событий окна. Вызов этого метода с неправильным параметром для приложения может привести ко всем типам непредвиденных действий обработки событий.
Параметр CoreProcessEventsOption | Description |
---|---|
CoreProcessEventsOption.ProcessOneAndAllPending | Отправка всех доступных событий в очереди. Если события не ожидаются, дождитесь следующего нового события. |
CoreProcessEventsOption.ProcessOneIfPresent | Отправка одного события, если оно ожидается в очереди. Если события не ожидаются, не подождите, пока новое событие не будет поднято, но вместо этого немедленно вернитесь. |
CoreProcessEventsOption.ProcessUntilQuit | Дождитесь новых событий и перенаправите все доступные события. Продолжайте это поведение, пока окно не будет закрыто или приложение вызывает метод Close в экземпляре CoreWindow. |
CoreProcessEventsOption.ProcessAllIfPresent | Отправка всех доступных событий в очереди. Если события не ожидаются, вернитесь немедленно. |
UWP с помощью DirectX должен использовать параметр CoreProcessEventsOption.ProcessAllIfPresent , чтобы предотвратить блокировку поведения, которое может прервать обновления графики.
Рекомендации по ASTA для разработчиков DirectX
Объект приложения, определяющий представление времени выполнения приложения и приложения DirectX, использует модель потоковой работы, называемую однопоточной квартирой приложения (ASTA) для размещения представлений пользовательского интерфейса приложения. Если вы разрабатываете приложение UWP и DirectX, вы знакомы со свойствами ASTA, так как все потоки, которые вы отправляете из приложения UWP и DirectX, должны использовать API Windows::System::Threading или использовать CoreWindow::CoreDispatcher. (Вы можете получить Объект CoreWindow для ASTA путем вызова CoreWindow::GetForCurrentThread из приложения.)
Самое главное, что вы должны знать, как разработчик приложения UWP DirectX, заключается в том, что необходимо включить поток приложения для отправки потоков MTA, установив Platform::MTAThread на main().
[Platform::MTAThread]
int main(Platform::Array<Platform::String^>^)
{
auto myDXAppSource = ref new MyDXAppSource(); // your view provider factory
CoreApplication::Run(myDXAppSource);
return 0;
}
Когда объект приложения для приложения UWP DirectX активируется, он создает ASTA, который будет использоваться для представления пользовательского интерфейса. Новый поток ASTA вызывается в фабрике поставщиков представлений, чтобы создать поставщика представлений для объекта приложения, и в результате код поставщика представления будет выполняться в этом потоке ASTA.
Кроме того, любой поток, который вы отключаете из ASTA, должен находиться в MTA. Помните, что все потоки MTA, которые вы отключаете, по-прежнему могут создавать проблемы повторного входа и привести к взаимоблокировке.
Если вы переносите существующий код для запуска в потоке ASTA, имейте в виду следующее:
Подождите примитивы, такие как CoWaitForMultipleObjects, ведут себя по-разному в ASTA, чем в STA.
Модальный цикл вызова COM работает по-разному в ASTA. Вы больше не можете получать несвязанные вызовы во время выполнения исходящего звонка. Например, следующее поведение создаст взаимоблокировку из ASTA (и немедленно завершит работу приложения):
- ASTA вызывает объект MTA и передает указатель интерфейса P1.
- Позже ASTA вызывает тот же объект MTA. Объект MTA вызывает P1, прежде чем он возвращается в ASTA.
- P1 не может ввести ASTA, так как он заблокирован при выполнении несвязанного вызова. Однако поток MTA заблокирован, так как он пытается вызвать P1.
Это можно устранить, выполнив следующие действия:
- Использование асинхронного шаблона, определенного в библиотеке параллельных шаблонов (PPLTasks.h)
- Вызов CoreDispatcher::P rocessEvents из ASTA приложения (основной поток приложения) как можно скорее, чтобы разрешить произвольные вызовы.
Тем не более чем вы не можете полагаться на немедленную доставку несвязанных вызовов в ASTA вашего приложения. Дополнительные сведения о асинхронных вызовах см . в статье "Асинхронное программирование" в C++.
В целом при разработке приложения UWP используйте CoreDispatcher для приложения CoreWindow и CoreDispatcher::P rocessEvents, чтобы обрабатывать все потоки пользовательского интерфейса, а не пытаться самостоятельно создавать потоки MTA и управлять ими. Если вам нужен отдельный поток, который нельзя обрабатывать с помощью CoreDispatcher, используйте асинхронные шаблоны и следуйте инструкциям, упомянутым ранее, чтобы избежать проблем повторного входа.