Приложение MFC D3D: руководство по Direct3D часть I - Структуры перечисления

ОГЛАВЛЕНИЕ

Структуры перечисления

Теперь, когда перечисление имеет набор ограничений приложения, разберемся в структурах и методах его реализации. Чтобы хранить информацию об адаптерах, устройствах для каждого адаптера, настройках и возможностях каждого устройства, класс перечисления реализует серию иерархических структур. Посмотрите снова на объявление класса CXD3DEnum. Последний член класса – массив структур AdapterInfo. Здесь приведено объявление AdapterInfo:

//----------------------------------------------------------
// AdapterInfo: информация об адаптере дисплея, и typedef
// для их массива
//----------------------------------------------------------
struct AdapterInfo
{
     int                    AdapterOrdinal;
     D3DADAPTER_IDENTIFIER9 AdapterIdentifier;
     D3DDISPLAYMODEARRAY    DisplayModes;
     DeviceInfoArray        DeviceInfos;
};
typedef CTArray<AdapterInfo> AdapterInfoArray;

AdapterInfo хранит порядковый номер адаптера (0 для основного или стандартного адаптера дисплея), структура D3DADAPTER_IDENTIFIER9 , массив DisplayModes адаптера и массив DeviceInfos. Прямо над объявлением вы найдете оператор typedef CTArray<D3DDISPLAYMODE>D3DDISPLAYMODEARRAY;. По сети, AdapterInfo хранит информацию об адаптере, режимах отображения, которые он может обработать, и списке устройств, которые он может предоставить, инкапсулированную последним массивом.

//----------------------------------------------------------
// DeviceInfo: информация об устройстве D3D; используются массивы их,
// поэтому typedef
//----------------------------------------------------------
struct DeviceInfo
{
    int              AdapterOrdinal;
    D3DDEVTYPE       DevType;
    D3DCAPS9         Caps;  
    DeviceComboArray DeviceCombos;
};
typedef CTArray<DeviceInfo> DeviceInfoArray;

Структура DeviceInfo наследует порядковый номер адаптера от своего родителя, AdapterInfo. Она хранит тип устройства - HAL, образцовое или программное – как определено перечислением D3DDEVTYPE, а также структурой возможностей устройства D3DCAPS9 и массивом DeviceCombos.

//----------------------------------------------------------
// класс DeviceCombo: комбинация формата адаптера и
// формата буфера невидимых поверхностей, совместимых с конкретным
// устройством D3D и приложением. Также будут использоваться их массивы,
// поэтому typedef.
//----------------------------------------------------------
struct DeviceCombo
{
    int        AdapterOrdinal;
    D3DDEVTYPE DevType;  
    D3DFORMAT  DisplayFormat;
    D3DFORMAT  BackBufferFormat;  
    bool       Windowed;
    DWORDARRAY VPTypes;
    DWORDARRAY DSFormats;
    DWORDARRAY MSTypes;
    DWORDARRAY MSQualityLevels;
    DSMSConflictArray DSMSConflicts;
    DWORDARRAY PresentIntervals;
};
typedef CTArray<DeviceCombo> DeviceComboArray;

Структура DeviceCombo наследует порядковый номер адаптера и тип устройства от своего родителя DeviceInfo. Она также хранит формат отображения и формат буфера невидимых поверхностей. Комбинация обоих форматов дает структуре ее имя. Кроме того, она хранит флаг оконный/полноэкранный и списки DWORD (двойное слово) типов VP, форматы глубины/трафарета, типы множественной выборки и уровни качества множественной выборки. Она также следит за тем, какие форматы глубины/трафарета несовместимы с какими типами множественной выборки в массиве DSMSConflict и, наконец, список DWORD интервалов воспроизведения. Последние два пункта будут рассмотрены позже.

Пошаговый разбор перечисления

Класс перечисления продолжает заполнять эти структуры, проверяя возможности на соответствие ограничениям приложения и запрашивая устройство Direct3D насчет поддержки. Используйте следующую схему как справку, если вы заблудились. Только помните, что члены DeviceCombo тоже являются списками, не развернутыми для наглядности.

Enumeration
|
+-- AdapterInfos[0]
| |
| +-- DisplayModes[0]
| +-- DisplayModes[1]
| ...
| |
| +-- DeviceInfos[0]
| | |
| | +-- DeviceCombos[0]
| | | |
| | | +-- VPTypes
| | | +-- DSFormats
| | | +-- MSTypes
| | | +-- MSQualityLevels
| | | +-- DSMSConflicts
| | | +-- PresentIntervals
| | +-- DeviceCombos[1]
| | ...
| +-- DeviceInfos[1]
| ...
+-- AdapterInfos[1]
...

Перечисление адаптеров

Оно начинается с функции CXD3DEnum::Enumerate, принимающей указатель на интерфейс Direct3D. Она будет хранить локальную ссылку на него в своем объекте m_pd3d, использовать его для получения количества адаптеров и обходить адаптеры, чтобы сохранить идентификаторы в массиве AdapterInfos. После того, как функция сохранит идентификатор адаптера, она использует методы Direct3D GetAdapterModeCount и EnumAdapterModes, чтобы извлечь и сохранить режимы отображения. Оба метода принимают формат отображения – улучшение версии Direct3D 9.0, – поэтому им передаются каждый из наших определенных для приложения форматов отображения. Перечисленные режимы возвращаются в структуре Direct3D D3DDISPLAYMODE:

typedef struct _D3DDISPLAYMODE 
{
    UINT Width;
    UINT Height;
    UINT RefreshRate;
    D3DFORMAT Format;
}
D3DDISPLAYMODE;

Сразу можно проверить размеры режима отображения, глубину бита цвета и глубину альфа-бита на совместимость с приложением. Снова: получаем идентификатор адаптера, обходим определенные для приложения форматы отображения и перечисляем режимы отображения для каждого формата. Доступный режим отображения, соответствующий требованиям приложения, добавляется в список DisplayModes. Снова взгляните на схему для лучшего понимания. Некоторые режимы отображения могут не попасть в список, так как формат отображения вообще не поддерживается (и GetAdapterModeCount возвращает 0), или так как он не прошел тесты, например, полноэкранный режим 320x200, когда AppMinFullscreenWidth равняется 640. Однако, когда режим попадает в список, формат отображения добавляется во временный список, позже используемый для перечисления устройств в каждом адаптере.

После обхода всех адаптеров и сбора всех возможных режимов отображения, подходящих для приложения, мы сортируем режимы отображения, чтобы наименьший и наибыстрейший всплыли вверху списка. Класс CTArray реализует сортировку с помощью qsort, принимая функцию сортировки обратного вызова. Чтобы отсортировать режимы отображения, перечисление использует функцию SortModesCallback вверху файла CXD3DEnum.cpp. На данном этапе Enumerate идентифицировал адаптер дисплея и перечислил все режимы отображения, которые он может обработать, и которые допустимы для приложения. Функция передает адаптер и временную подгруппу форматов отображения функции EnumerateDevices.

Перечисление устройств

Это процесс заполнения списка DeviceInfo для переданного адаптера. Существует не более трех DeviceInfos для каждого адаптера, а именно – HAL, образцовое и программное устройство. Мы спрашиваем Direct3D о поддержке каждого типа и сохраняем его возможности (если поддерживается) в члене Caps, с единственным вызовом IDirect3D9::GetDeviceCaps. Типы устройств, не пережившие вызов, пропускаются. Те, которые обращаются к их соответствующему перечислению DeviceCombos, снова используют переданный список форматов отображения.

Перечисление DeviceCombo

Мы обращаемся к EnumerateDeviceCombos для конкретного устройства, поддерживающего каждый из переданных форматов отображения. Скорее всего, это будет подгруппа AppDisplayFormats. Мы начнем с обхода этих форматов и извлечем каждый из них по очереди. Теперь мы обходим допустимые для приложения форматы буфера невидимых поверхностей, извлекаем один, пропускаем его, если он не соответствует глубине альфа-бита, и проверяем, могут ли форматы отображения и буфера невидимых поверхностей использоваться одновременно в устройстве в оконном и в полноэкранном режимах (третий внутренний цикл). Поддержка выявляется с помощью вызова API

IDirect3D9::CheckDeviceType.

Комбинация форматов, переживающая вызов, возвращает поддерживаемый системой DeviceCombo, но ее все еще нужно проверить по отношению к другим ограничениям приложения на совместимость, а именно: тип VP, формат глубины/трафарета, тип множественной выборки (и уровни качества), конфликт между последними двумя позициями, и интервал воспроизведения. Каждое из этих ограничений соответствует спискам, хранимым DeviceCombo, и каждое имеет свою собственную функцию перечисления.

Перечисление типов VP

Устройства HAL могут поддерживать три разных типа VP: программный, аппаратный и смешанный, как описано ранее. Чтобы обнаружить поддержку аппаратной VP, среда разработки проверяет Caps.DevCaps на наличие флага D3DDEVCAPS_HWTRANSFORMANDLIGHT. Если флаг установлен, устройство поддерживает ее и, следовательно, поддерживает смешанный тип VP, хотя последнее будет использоваться, только если флаг AppUsesMixedVP установлен. Функция перечисления типов VP также проверяет возможности на наличие типа чистого устройства, устанавливая тип VP в чистую аппаратную VP, чтобы среда разработки могла воспользоваться ее преимуществами и создать чистое устройство. Если аппаратные трансформации и освещение не поддерживаются, среда разработки возвращается назад к программной VP, которая всегда доступна.