Инициализация Direct3D9 в приложении

Инициализация Direct3D9

Лицензия Creative Commons
Это произведение доступно по лицензии Creative Commons «Attribution» («Атрибуция») 4.0 Всемирная.

Данная статья является попыткой составления общей схемы инициализации библиотеки Direct3D9 при написании приложений, использующих 3D-графику. Исходным материалом послужили результаты анализа справочной системы DirectX SDK9 и нескольких графических и игровых движков с открытым исходным кодом.

Для того, чтобы можно было работать с библиотекой DirectX9, а именно для использования ее части Direct3D9 для рендера графики, необходимо вначале создать два объекта. Первый объект, который нужно создать, это объект типа IDirect3D9 – главный интерфейс Direct3D9. Он представляет собой некий менеджер для создания всех остальных объектов Direct3D9 (устройств, текстур, вершинных и индексных буферов, шейдеров и пр.). Чтобы создать объект типа IDirect3D9, необходимо вызвать функцию Direct3DCreate9:

// Пример — создание объекта IDirect3D9
LPDIRECT3D9 g_pD3D = NULL;
if( 0 == (g_pD3D = Direct3DCreate9(D3D_SDK_VERSION)))
    return E_FAIL;

Второй объект, который необходимо создать — это устройство Direct3D9. Его назначение — предоставлять программисту возможности по созданию различных ресурсов и рендера объектов в зависимости от возможностей, которыми обладает видеокарта. Для создания объекта устройства, в DirectX9 SDK реализован метод интерфейса IDirect3D9 с именем CreateDevice. Рассмотрим его прототип:

HRESULT CreateDevice(UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow,
    DWORD BehaviorFlags,D3DPRESENT_PARAMETERS *pPresentationParameters,
    IDirect3DDevice9 **ppReturnedDeviceInterface);

Adapter это номер видеокарты в системе. Дело в том, что на компьютере пользователя может быть установлено более одной видеокарты. Типичный случай для начала 2000-х — наличие одной встроенной видеокарты и одной внешней.

DeviceType тип устройства. DirectX может эмулировать аппаратные возможности видеокарты в случае, если они не поддерживаются видеокартой. На практике используются два типа устройств. Устройство REF используется для разработки новейших эффектов и шейдеров в режиме эмуляции, если они не поддерживаются аппаратно видеокартой. Устройство HAL — это устройство Direct3D9, в котором задействованы те аппаратные возможности видеокарты, которые доступны на данной видеокарте. В итоговом приложении (игре, редакторе), которое будет запускаться конечным пользователем, устройство Direct3D9 должно быть всегда создано с типом HAL и никогда с типом REF! Устройство типа REF используется исключительно разработчиками движков на стадии отладки или эмуляции недоступных аппаратно возможностей видеокарт. В силу этого рендер на устройстве REF практически всегда будет очень медленным.

hFocusWindow идентификатор окна. Устройство Direct3D9 всегда должно быть связано с каким-либо окном.

BehaviorFlags в этом параметре обычно передают способ обработки вершин, будет ли рассчитываться трансформации вершин аппаратно на видеокарте или программно на CPU.

pPresentationParametersв этом параметре нужно передать структуру, которая описывает параметры представления устройства Direct3D9. Об этом чуть дальше.

ppReturnedDeviceInterface сюда нужно передать указатель на указатель интерфейса устройства, который будет хранить адрес созданного устройства Direct3D9.

Теперь рассмотрим все параметры по порядку более детально.

Номер видеокарты (видеоадаптера)

Если вас не интересует поддержка возможности выбора конкретного видеоадаптера, используйте в качестве номера видеоадаптера макрос D3DADAPTER_DEFAULT. Этот макрос всегда означает номер главного видеоадаптера в системе. Стало быть, если текущим видеоадаптером, используемым в системе, выставлена внешняя видеокарта, то D3DADAPTER_DEFAULT будет означать ее номер.

Для тех, кто хочет в своем коде обеспечить поддержку выбора пользователем определенной видеокарты, при помощи метода GetAdapterCount() интерфейса IDirect3D9 необходимо узнать число установленных в системе видеокарт, которые распознал DirectX. Номера видеоадаптеров находятся в интервале [0, число видеоадаптеров). То есть от нуля до числа видеоадаптеров минус единица (обратите внимание на круглую скобку, которая в теории множеств означает, что правая граница не включается в интервал). Левая граница 0 диапазона задается типом UINT, правая числом видеоадаптеров в системе.

Тип устройства

Как уже было сказано, если вас не волнует разработка новейших эффектов и задействование самых последних возможностей видеокарт, то можно всегда применять тип устройства HAL. В Direct3D9 тип устройства HAL задается значением D3DDEVTYPE_HAL, а тип устройства REF – значением D3DDEVTYPE_REF. Существуют и другие типы, информацию о которых можно узнать из справки, поставляемой вместе с SDK. Проще всего поступать так. Если вы пишете графический движок, используйте HAL. А когда вам нужно разработать какой-то шейдер и на вашей видеокарте не реализованы нужные возможности (которые есть в DirectX9), используйте REF устройство. Но после того как убедитесь, что шейдер «работает», измените тип устройства обратно на HAL. REF устройство очень медленно рисует графику и в режиме реального времени не применимо. Немного упрощенно, но в целом, я думаю, понятно.

Окно фокуса

Как говорится в SDK окно фокуса используется Direct3D9 для того, чтобы Direct3D9 был в курсе того, что окно, в которое происходит рендер, потеряло фокус. Окно теряет фокус, когда нажимается комбинация клавиш Alt+Tab или когда его сворачивают, нажимая на кнопку «-» (тире). Когда окно, в котором производится рендер, теряет свой фокус, Direct3D9 не может продолжать работать в прежнем режиме и должен перенастроиться. А чтобы Direct3D смог перенастроиться, ему надо сообщить, что окно потеряло фокус. Вот для этого и служит этот параметр. Далее еще вернемся к этому вопросу.

Обработка вершин

Существуют три флага для задания способа обработки вершин на устройстве типа HAL:

D3DCREATE_SOFTWARE_VERTEXPROCESSING

D3DCREATE_HARDWARE_VERTEXPROCESSING

D3DCREATE_MIXED_VERTEXPROCESSING

Флаг D3DCREATE_HARDWARE_VERTEXPROCESSING требует, чтобы создаваемое устройство выполняло операции трансформации и освещения вершин аппаратно. Такая возможность поддерживается по-разному у разных видеокарт.

Флаг D3DCREATE_SOFTWARE_VERTEXPROCESSING требует, чтобы обработка вершин выполнялась на CPU программно.

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

Обычный подход, который применяется для выбора обработки вершин — метод последовательных проб, иллюстрируемый примером ниже:

////
// LPDIRECT3D9 d3d; // создан ранее
// LPDIRECT3DDEVICE9 device; 
// HWND hwnd; // создано ранее
// D3DPRESENT_PARAMETERS presentPars; // заполнена ранее
D3DCAPS9 devcaps;
d3d->GetDeviceCaps( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &devcaps);
if ( (D3DDEVCAPS_HWTRANSFORMANDLIGHT & devcaps.DevCaps) != 0 ){
    HRESULT hr = d3d->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, 
    D3DCREATE_HARDWARE_VERTEXPROCESSING, &presentPars, &device);
    if (FAILED(hr)){
        hr = d3d->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, 
                D3DCREATE_MIXED_VERTEXPROCESSING, &presentPars, &device);
        if (FAILED(hr)){
            hr = d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, 
                    D3DCREATE_SOFTWARE_VERTEXPROCESSING, 
                    &presentPars, &device);
            if (FAILED(hr)) { // ошибка — невозможно создать устройство }
       }
    }
}
else{
    HRESULT hr = d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, 
                   D3DCREATE_SOFTWARE_VERTEXPROCESSING, 
                   &presentPars, &device);
    if (FAILED(hr)) { // ошибка — невозможно создать устройство }
}

В структуре D3DCAPS9 есть поле DevCaps. Если это поле содержит битовый флаг D3DDEVCAPS_HWTRANSFORMANDLIGHT, то трансформация и обработка вершин на устройстве Direct3D9 может выполняться аппаратно. Сама структура D3DCAPS9 перед проверкой ее полей должна быть заполнена при помощи метода GetDeviceCaps() главного интерфейса Direct3D9.

Параметры представления

Формат пикселя

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

  • D3DFMT_A2R10G10B10

  • D3DFMT_X8R8G8B8

  • D3DFMT_A8R8G8B8

  • D3DFMT_R5G6B5

  • D3DFMT_X1R5G5B5

  • D3DFMT_A1R5G5B5

Любой из этих режимов может быть использован для заднего буфера. Для пикселей экрана форматы D3DFMT_A8R8G8B8 и D3DFMT_A1R5G5B5 использоваться не могут. А формат D3DFMT_A2R10G10B10 может использоваться для пикселей экрана только в полноэкранном режиме.

Как правило, пользователь движка (программист, который использует движок) или конечный пользователь не имеет понятия о каких-то форматах, но имеет понятие или слышал о глубине цвета. Глубина цвета — это сколько бит используется для задания цветового значения пикселя. Бывают две глубины цвета: по 16 и 32 бит на пиксель. В связи с этим, сначала необходимо отобразить значение желаемой глубины цвета в формат пикселя согласно нижеследующей таблице:

Формат пикселя

Глубина цвета

D3DFMT_A2R10G10B10

32

D3DFMT_X8R8G8B8

32

D3DFMT_A8R8G8B8

32

D3DFMT_R5G6B5

16

D3DFMT_X1R5G5B5

16

D3DFMT_A1R5G5B5

16

Как выбрать теперь конкретный формат? Что тут можно посоветовать. Как правило, разработчики движков либо выбирают какой-то один наиболее универсальный формат на каждое значение глубины цвета так, чтобы на каждую компоненту цвета приходилось бы примерно одинаковое число бит, либо же используют какую-то стратегию последовательного перебора форматов от «лучшего» к «худшему» пока не будет найден подходящий поддерживаемый формат. Например, существует такая стратегия — попробовать найти режим дисплея сначала для формата с дополнительным полем «X», потом для формата с альфа-каналом и затем для формата с меньшим числом бит на альфа-канал или с отсутствием такового:

D3DFMT_X8R8G8B8,

D3DFMT_A8R8G8B8,

D3DFMT_A2B10G10R10,

D3DFMT_X1R5G5B5,

D3DFMT_A1R5G5B5,

D3DFMT_R5G6B5

То есть если какой-то формат поддерживается (об этом позже), то дальше останавливаем перебор и используем этот формат. Из анализа исходников открытых движков, могу сказать, что существует тенденция использования формата D3DFMT_X8R8G8B8 для глубины пикселя 32 бит и формата D3DFMT_R5G6B5 для глубины пикселя в 16 бит. Для формата пикселей дисплея (экрана) выбор формата становится проще в силу ранее сделанного замечания относительно форматов с альфа-каналом. Существует только одно правило: если для в качестве формата пикселя дисплея выбран определенный формат, то для заднего буфера нужно выбрать формат с таким же распределением бит по компонентам. Например, если для формата пикселей дисплея был выбран формат D3DFMT_X1R5G5B5, то для формата пикселей заднего буфера нужно выбрать либо формат D3DFMT_X1R5G5B5, либо формат D3DFMT_A1R5G5B5, но никак не формат D3DFMT_R5G6B5.

Проверка возможности использования устройства на видеокарте

Далее можно проверить, сможет ли устройство Direct3D9 работать на базе видеокарты пользователя. Для этого предназначен метод CheckDeviceType() главного интерфейса Direct3D9. Суть состоит в передаче в функцию номера адаптера, типа устройства, формата пикселей дисплея, формата заднего буфера и флага, который помечает будет ли приложение оконным или нет (в этом случае оно будет полноэкранным), и проверке возвращаемого функцией значения на успешность. То есть нужно, например, проверить, будет ли устройство Direct3D9 типа HAL работать на видеокарте, выставленной как основная в настройках Windows, с определенным форматом дисплея в полноэкранном режиме, ведь устройство типа HAL требует аппаратной растеризации. И нужно проверить, сможет ли текущая видеокарта работать в таком режиме. Ведь видеокарта может поддерживать определенный список видеорежимов дисплея для данного формата пикселей, но поддерживает ли она именно аппаратную растеризацию при данном формате пикселей — это вопрос. Также формат заднего буфера может отличаться от формата дисплея наличием альфа-канала. Совместимы ли формат пикселей дисплея и формат пикселей заднего буфера? Чтобы проверить это, вы должны вызвать примерно следующий код:

if(SUCCEEDED(pD3Device->CheckDeviceType(D3DADAPTER_DEFAULT, 
    D3DDEVTYPE_HAL, DisplayFormat, BackBufferFormat, FALSE)))
    return S_OK;

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

Формат буфера глубины

Так же как и для заднего буфера выбирается формат буфера глубины. Здесь имеет смысл следовать такой стратегии. Перебирать форматы в последовательности уменьшения числа бит на буфер трафарета. То есть если определенный формат поддерживается, останавливаем перебор и используем этот формат глубины. Вот последовательность форматов буфера глубины от «лучшего» к «худшему»: D3DFMT_D24S8, D3DFMT_D24X4S4, D3DFMT_D24X8, D3DFMT_D15S1, D3DFMT_D32, D3DFMT_D16. Причем, если у вас в качестве формата заднего буфера выбран формат с глубиной пикселя в 16 бит, то формат буфера глубины следует выбрать из урезанной последовательности D3DFMT_D15S1, D3DFMT_D16.

Выбор видеорежима и проверка поддержки форматов пикселей

Перед тем как создать устройство Direct3D9, необходимо определить видеорежим, в котором будет работать создаваемое устройство. Как это сделать? Для начала нужно определить какие вообще режимы дисплея доступны на видеокарте, для чего следует воспользоваться методами GetAdapterModeCount() и EnumAdapterModes() интерфейса IDirect3D9. Необходимо организовать перебор видеорежимов. Входными данными обычно выступают глубина цвета, ширина и высота заднего буфера (разрешение экрана) и флаг использования полноэкранного режима.

Функция GetAdapterModeCount() позволяет узнать число видеорежимов, поддерживаемых видеокартой, а функция EnumAdapterModes() позволяет получить информацию о конкретном видеорежиме по его индексу. Видеорежимы видеокарты в Direct3D9 индексируются от нуля до их общего числа минус единица.

Как уже было сказано выше в стратегии выбора «лучшего» формата вы по глубине цвета выбираете формат пикселя дисплея. Затем получаете число доступных видеорежимов для данного формата дисплея через функцию GetAdapterModeCount(). После чего перебираете видеорежимы при помощи функции EnumAdapterModes(), сравнивая ширину и высоту видеорежима на видеокарте со значениями, которые задает пользователь. Далее нужно проверить поддержку буфера глубины, для чего используется метод CheckDeviceFormat():

// Пример проверки формата буфера глубины

BOOL IsDepthFormatExisting( D3DFORMAT DepthFormat, D3DFORMAT AdapterFormat ) {
    HRESULT hr = pD3D->CheckDeviceFormat( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
        AdapterFormat, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, DepthFormat);
    return SUCCEEDED( hr );
}

Иногда выполняется более сложная проверка:

// Более сложная проверка поддержки буфера глубины и трафарета
BOOL IsDepthFormatOk(D3DFORMAT DepthFormat, D3DFORMAT AdapterFormat, 
    D3DFORMAT BackBufferFormat){
    // Verify that the depth format exists
    HRESULT hr = pD3D->CheckDeviceFormat(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
        AdapterFormat, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, DepthFormat);
    if(FAILED(hr)) return FALSE;
    // Verify that the depth format is compatible
    hr = pD3D->CheckDepthStencilMatch(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
        AdapterFormat, BackBufferFormat, DepthFormat);
    return SUCCEEDED(hr);
}

Насколько я смог понять из DX SDK функция CheckDeviceFormat() используется, чтобы проверить можно ли использовать заданный формат пикселя в качестве буфера глубины, текстуры или цели рендера на данной видеокарте и данном устройстве Direct3D9 при выбранном формате дисплея — грубо говоря, «совместимость формата и способа его использования». В нашем случае при помощи функции CheckDeviceFormat() проверяется «совместимость» формата буфера глубины и способа использования этого формата именно для буфера глубины. А функция CheckDepthStencilMatch() производит проверку совместимости формата буфера глубины и формата заднего буфера.

Мультисэмплинг

Чтобы добиться исчезновения эффекта лестницы, можно проверить поддержку так называемого мультисэмплинга. Проверка выполняется посредством метода CheckDeviceMultiSampleType() главного интерфейса Direct3D9 примерно так:

// пример проверки поддержки мультисэмплинга
if( SUCCEEDED(pD3D->CheckDeviceMultiSampleType( AdapterOrdinal, 
    DeviceType, BackBufferFormat, FALSE, D3DMULTISAMPLE_3_SAMPLES, 
    pQualityLevels ) ) &&
    SUCCEEDED(pD3D->CheckDeviceMultiSampleType( AdapterOrdinal, 
    DeviceType, DepthBufferFormat, FALSE, D3DMULTISAMPLE_3_SAMPLES, 
    pQualityLevels ) ) )
return S_OK;

Из SDK ясно только, что 5-й параметр MultiSampleType описывает технику мультисэмплинга. Обычно именно пользователь задает уровень мультисэмплинга в расширенных настройках видео. А выбор пользователя нужно отобразить на значение перечислимого типа D3DMULTISAMPLE_TYPE. Нужно производить проверку поддержки мультисэмплинга как для заднего буфера, так и для буфера глубины. В pQualityLevels нужно передать указатель на беззнаковое длинное целое число, в которое будет записано число уровней качества мультисэмплинга. Более подробно (например о качестве мультисэмплинга я ничего сказать не могу, поскольку не имел опыта работы с ним). В проанализированных движках особо не заморачивались с качеством мультисэмплинга и выставляли его в 0 в поле MultiSampleQuality структуры D3DPRESENT_PARAMETERS.

Цепочка буферов переключения

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

Частота обновления экрана обычно намного ниже (60Гц, 75 Гц и т. д.) по сравнению со скоростью визуализации графики, которую способна обеспечить видеокарта. Допустим, монитор успел обновить верхнюю половину экрана. Если обновить содержимое переднего буфера в этот момент, отрендерив новый кадр, то когда монитор закончит обновление экрана, в нижней половине экрана будет визуализировано содержание нового кадра. В результате в верхней половине будет прорисовано содержание старого кадра, а в нижней половине — нового кадра. Этот эффект носит название тиринга.

Монитор обычно обновляет экран, начиная прорисовку с левого верхнего угла, строчка за строчкой. После обновления последней строки экрана, монитор производит перекалибровку (возврат к левому верхнему углу экрана), которая носит название вертикальной синхронизации.

Чтобы избавиться от тиринга, придумали метод переключения буферов. Дело в том, что отрендерить сцену можно в любую поверхность (поверхность, грубо говоря, это двумерный массив пикселей), а не только в передний буфер. Любая такая поверхность напрямую никогда не отображается на экране, вследствие чего носит название заднего буфера. Использование задних буферов позволяет освобождает от необходимости синхронизации с вертикальной разверткой монитора, так как рендеринг в задний буфер может производиться независимо от обновления экрана в те моменты, когда, например, очередь сообщений окна приложения пуста. После того как визуализация в задний буфер будет закончена, можно поменять местами передний и задний буфер практически мгновенно с помощью обмена значений указателей на поверхности переднего и заднего буферов. Таким образом задний буфер становится передним, а прежний передний буфер — новым задним буфером. Этот эффект получил название флиппинг поверхностей или переключение буферов. Сам процесс переключения буферов называется в Direct3D9 презентацией (метод Present). Для переключения буферов вводится один или более задних буферов.

С устройством Direct3D9 создается и связывается по крайней мере одна так называемая цепочка переключений (swap chain), которая состоит из переднего и всех задних буферов. Число задних буферов в цепочке переключений задается в поле BackBufferCount структуры D3DPRESENT_PARAMETERS. Задание 0 в параметре числа задних буферов равносильно заданию 1 заднего буфера. Наиболее всего применяют следующие способы переключений буферов:

D3DSWAPEFFECT_FLIP — буферы переключаются в круговом порядке последовательно друг за другом. Этот тип сохраняет содержимое заднего буфера, для чего Direct3D9 использует скрытые дополнительные буферы. Обеспечивает гладкий переход между кадрами, когда включена синхронизация с вертикальной разверткой.

D3DSWAPEFFECT_COPY — используется семантика копирования самих пикселей из заднего буфера в передний, за счет чего и обеспечивается сохранность содержимого заднего буфера. Использование этого типа переключения буферов возможно только, если имеется всего один задний буфер. В оконном режиме возможен эффекта тиринга.
D3DSWAPEFFECT_DISCARD — не сохраняет содержимое заднего буфера в отличие от режимов переключения D3DSWAPEFFECT_FLIP и D3DSWAPEFFECT_COPY. Этот режим всегда должен использоваться при активированном мультисэмплинге. Как правило, визуализация с этим типом переключения буферов выполняется быстрее.
Вообщем, напрашивается следующий вывод: использовать D3DSWAPEFFECT_DISCARD всегда, когда это возможно.

Поле PresentationInterval структуры D3DPRESENT_PARAMETERS определяет максимальную скорость преключения буферов. Как правило используются значения D3DPRESENT_INTERVAL_ONE и D3DPRESENT_INTERVAL_IMMEDIATE. Значение D3DPRESENT_INTERVAL_ONE означает, что переключение буферов будет происходить один раз во время перекалибровки после вертикальной развертки, то есть с такой же частотой, с какой обновляется экран монитора. Как правило, это отображение выбора пользователем параметра «синхронизировать с вертикальной разверткой» в расширенных настройках видеопараметров. Если задано значение D3DPRESENT_INTERVAL_IMMEDIATE, то видеокарта не будет дожидаться наступления перекалибровки и переключение буферов будет произведено немедленно. Что лучше — пусть выбирает пользователь, для этого и вводятся расширенные настройки в меню. На производительность системы рендеринга влияет много факторов.

В терминологии программирования компьютерной графики существует понятие тройной буферизации — это когда используется 2 задних буфера. Сущность тройной буферизации описана в википедии. Вкратце, когда изображение в заднем буфере уже готово, но видеокарта ждет момента вертикальной синхронизации, можно рисовать следующий кадр в третий буфер. В связи с отсутствием опыта использования тройной буферизации не могу сказать об этом ничего более.

Частота монитора

если приложение будет работать в оконном режиме, то в поле FullScreen_RefreshRateInHz структуры D3DPRESENT_PARAMETERS необходимо задать 0, если в полноэкранном — значение берется из описания видеорежима, полученного при вызове функции EnumAdapterModes.

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

Дескриптор окна устройства

Это окно играет роль основного холста, на котором устройство Direct3D9 будет размещать результаты визуализации. Его дескриптор нужно указать в поле hDeviceWindow структуры D3DPRESENT_PARAMETERS. Для приложений, работающих в оконном режиме, можно задать здесь 0, в этом случае в качестве дескриптора окна будет использоваться окно под фокусом из параметров метода CreateDevice.