Поделиться через


Объект приложения и DirectX

универсальная платформа Windows (UWP) с играми DirectX не используют многие элементы пользовательского интерфейса и объекты пользовательского интерфейса Windows. Скорее, поскольку они выполняются на более низком уровне в стеке среда выполнения Windows, они должны взаимодействовать с платформой пользовательского интерфейса более фундаментальным образом: путем доступа к объекту приложения и взаимодействия напрямую. Узнайте, когда и как происходит эта взаимодействие, а также как вы, разработчик DirectX, можете эффективно использовать эту модель в разработке приложения UWP.

Ознакомьтесь с глоссарием графики Direct3D для получения сведений о незнакомых терминах или понятиях, которые возникают при чтении.

Важные основные пространства имен пользовательского интерфейса

Сначала обратите внимание на среда выполнения Windows пространства имен, которые необходимо включить (с использованием) в приложение UWP. Мы смотрим на подробности немного.

Примечание.

Если вы не разрабатываете приложение UWP, используйте компоненты пользовательского интерфейса, предоставляемые в библиотеках и пространствах имен javaScript или XAML, а не типы, предоставляемые в этих пространствах имен.

Объект приложения среда выполнения Windows

В приложении UWP вы хотите получить окно и поставщик представлений, из которого можно получить представление и к которому можно подключить цепочку буферов (буферы отображения). Вы также можете подключить это представление к событиям для конкретного окна для запущенного приложения. Чтобы получить родительское окно для объекта приложения, определенного типом CoreWindow, создайте тип, реализующий IFrameworkViewSource. Пример кода C++/WinRT, показывающий, как реализовать IFrameworkViewSource, см. в статье "Композиция машинного взаимодействия с DirectX и Direct2D".

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

  1. Создайте тип, реализующий IFrameworkView. Это ваше представление.

    В этом типе определите следующее:

    • Метод Initialize, который принимает экземпляр CoreApplicationView в качестве параметра. Экземпляр этого типа можно получить, вызвав CoreApplication.CreateNewView. Объект приложения вызывает его при запуске приложения.
    • Метод SetWindow, который принимает экземпляр CoreWindow в качестве параметра. Экземпляр этого типа можно получить, доступ к свойству CoreWindow в новом экземпляре CoreApplicationView.
    • Метод Load, который принимает строку для точки входа в качестве единственного параметра. Объект приложения предоставляет строку точки входа при вызове этого метода. Здесь вы настроили ресурсы. Здесь вы создаете ресурсы устройства. Объект приложения вызывает его при запуске приложения.
    • Метод Run, который активирует объект CoreWindow и запускает диспетчер событий окна. Объект приложения вызывает его при запуске процесса приложения.
    • Метод Uninitialize, который очищает ресурсы, настроенные в вызове Load. Объект приложения вызывает этот метод при закрытии приложения.
  2. Создайте тип, реализующий IFrameworkViewSource. Это ваш поставщик представлений.

    В этом типе определите следующее:

    • Метод с именем CreateView , который возвращает экземпляр реализации IFrameworkView , как было создано на шаге 1.
  3. Передайте экземпляр поставщика представления в CoreApplication.Run из main.

Учитывая эти основы, давайте рассмотрим дополнительные варианты, которые вы должны расширить этот подход.

Основные типы пользовательского интерфейса

Ниже приведены другие основные типы пользовательского интерфейса в среда выполнения Windows, которые могут оказаться полезными.

Эти типы можно использовать для доступа к представлению приложения, в частности, биты, нарисующие содержимое родительского окна приложения, и обрабатывать события, запущенные для этого окна. Процесс окна приложения — это однопоточная квартира (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 (и немедленно завершит работу приложения):

    1. ASTA вызывает объект MTA и передает указатель интерфейса P1.
    2. Позже ASTA вызывает тот же объект MTA. Объект MTA вызывает P1, прежде чем он возвращается в ASTA.
    3. P1 не может ввести ASTA, так как он заблокирован при выполнении несвязанного вызова. Однако поток MTA заблокирован, так как он пытается вызвать P1.

    Это можно устранить, выполнив следующие действия:

    • Использование асинхронного шаблона, определенного в библиотеке параллельных шаблонов (PPLTasks.h)
    • Вызов CoreDispatcher::P rocessEvents из ASTA приложения (основной поток приложения) как можно скорее, чтобы разрешить произвольные вызовы.

    Тем не более чем вы не можете полагаться на немедленную доставку несвязанных вызовов в ASTA вашего приложения. Дополнительные сведения о асинхронных вызовах см . в статье "Асинхронное программирование" в C++.

В целом при разработке приложения UWP используйте CoreDispatcher для приложения CoreWindow и CoreDispatcher::P rocessEvents, чтобы обрабатывать все потоки пользовательского интерфейса, а не пытаться самостоятельно создавать потоки MTA и управлять ими. Если вам нужен отдельный поток, который нельзя обрабатывать с помощью CoreDispatcher, используйте асинхронные шаблоны и следуйте инструкциям, упомянутым ранее, чтобы избежать проблем повторного входа.