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

ОГЛАВЛЕНИЕ

Устройства Direct3D

Устройство Direct3D – компонент визуализации Direct3D; оно инкапсулирует и хранит состояние визуализации. Кроме того, устройство Direct3D выполняет операции трансформации и освещения, а также растеризует изображение на поверхности. Положение дел становится лучше и хуже одновременно, поэтому потерпите, пока я подробно разъясню данное определение.

Состояние визуализации – то, когда, где, как и какие 3D объекты отображаются. Трансформация и освещение (или TL) производят действия над вершинами объектов, выполняя все надлежащие расчеты 3D математики и цвета, соответственно. Растеризация – процесс превращения трансформированных и освещенных 3D вершин в нечто, что видеокарта ПК (и в итоге экран) может обработать, а именно 2D пиксели.

С точки зрения архитектуры, каждая операция образует отдельный модуль устройства: модуль трансформации, модуль освещения и модуль растеризации. Первые два модуля производят действия над вершинами, и поэтому они также называются обработка вершин, или VP для краткости. Вершина - это положение точки в пространстве, обычно описываемое ее декартовыми координатами x, y и z. Конвейеры программирования трехмерной графики используют вершины в качестве базовых единиц для создания треугольников или других многоугольников, буквально путем соединения точек. Надлежащее количество правильно ориентированных, окрашенных и освещенных треугольных граней создаёт иллюзию гладкости реального трехмерного объекта.

Если вы все еще здесь, вернемся на правильный путь. Устройства Direct3D могут поддерживать аппаратную VP, в зависимости от адаптера и драйвера дисплея. Это означает, что конкретный адаптер может иметь специализированное аппаратное обеспечение, которое может взять необработанные данные вершин и выполнить трансформацию и освещение, освободив Direct3D от таких задач. Проблема в том, что другой адаптер дисплея может не предоставлять такую функцию. Вообще, приложения должны предоставлять внутри одного устройства функциональность аппаратной и программной VP, чтобы использовать преимущества обеих или даже совмещать их. Однако они должны возвращаться обратно к программной VP (которая доступна практически всегда),  если аппаратная VP недоступна.

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

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

Между прочим, определение устройства в SDK заканчивается словом "поверхность", а не "экран" или "окно". Поверхность – определенное обобщение, иногда она может представлять лишь часть экрана или окна. Чтобы быть точным, в контексте Direct3D, поверхность представляет линейную область видеопамяти. На практике, это прямоугольная часть видеопамяти.
Перерыв закончился, переходим к типам устройств.

Типы устройств Direct3D

Приложения, использующие Direct3D, не обращаются к платам видеографики напрямую; они вызывают функции и методы Direct3D. Direct3D, в свою очередь, обращается к аппаратному обеспечению через абстрактный аппаратный слой (HAL). HAL - аппаратно-зависимый, специфичный для производителя интерфейс (встроенный в драйвер и поставляемый в виде DLL), который Direct3D использует для работы непосредственно с аппаратным обеспечением дисплея, ограждая приложения от специфичных для платы деталей реализации. Майкрософт  подтверждает, что если компьютер, на котором работает ваше приложение, поддерживает устройство типа HAL, оно достигнет наилучшей производительности путем его использования. Однако есть другие типы создаваемых устройств Direct3D.

Второй тип устройства, которое вы можете создать, - образцовое устройство, использующее специальные команды центрального процессора всегда, когда оно может. Виды специальных команд сильно зависят от аппаратного обеспечения. Они включают набор команд 3DNow! на некоторых процессорах AMD (американские микроустройства), набор команд MMX (расширения для мультимедиа), поддерживаемый многими процессорами Intel,  и набор команд SSE – потоковые SIMD (с одним потоком команд и многими потоками данных) расширения на некоторых процессорах Intel. Любой тип устройства Direct3D может использовать эти наборы команд для ускорения VP, но только образцовому устройству разрешено использовать эти наборы команд для растеризации всегда, когда оно может.

Приложения не могут полагаться на образцовые устройства (иначе называемые средства программной прорисовки),  они не могут работать на каждой машине пользователя из-за явных аппаратных несоответствий. Вот почему Майкрософт рекомендует использовать образцовые устройства только для тестирования функций или демонстрации. Фактически, часто бывает так, что Direct3D успешно создает образцовое устройство, но устройство вообще не способно строить изображение! Чтобы создать рабочее образцовое устройство, вам нужна новейшая графическая плата.

Третий тип устройства – программное устройство. Если ПК пользователя не обеспечивает никакого специального аппаратного ускорения для растеризации, ваше приложение может использовать программное устройство для его эмуляции. Оно также будет использовать специальные наборы команд при наличии возможности, но оно однозначно будет работать медленнее, чем устройство HAL. Программные устройства не общедоступны. Более того, они предназначены для использования разработчиками драйверов, опирающихся на средство разработки драйверов Direct3D (DDK). Даже не тратьте силы на их поиск, так как даже Майкрософт не может их предложить. Кроме того, они должны быть загружены приложением и зарегистрированы в качестве подключаемого модуля к интерфейсу Direct3D. В статье они более не упоминаются.

Наконец,  в довершение всего, хотя не признанно как тип, существует разновидность создаваемых устройств Direct3D, именуемых чистыми устройствами. Чистое устройство может повысить производительность, но требует аппаратной VP, не поддерживает запрос некоторых состояний Direct3D и не фильтрует никакие лишние изменения состояний. Это значит, что если вы имеете, скажем, 1000 изменений состояния визуализации на каждый кадр, для вас было бы лучше, если бы фильтрация избыточности осуществлялась автоматически нечистым устройством. Что такое изменения состояния визуализации? Все, что движется от одного кадра к следующему, но, что хуже, анимация не распространяется только на геометрию. Вы можете анимировать путем изменения цветов, текстур, визуальных эффектов.

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

Следует надеяться, что ваш компьютер поддерживает тип устройства HAL, инкапсулирующий характеристики аппаратуры,  который в большинстве случаев является лучшим вариантом. То есть, пока вы не убедитесь, что ваше приложение работает безотказно и в 3 раза быстрее на чистом аппаратном устройстве обработки вершин, или пока вы не станете серьезным конкурентом Id Software! Да, создатели Doom.

Вернемся к классу CXD3D...

Любопытные вещи начинают происходить в CXD3D::CreateD3D.

//----------------------------------------------------------
// CreateD3D(): если m_hWnd был инициализирован, он
// создает экземпляр объекта d3d, выбирает начальные установки d3d
// и инициализирует элементы d3d.
//----------------------------------------------------------
HRESULT CXD3D::CreateD3D()
{
    HRESULT hr;

    // проверяет на наличие окна, в котором формируется изображение
    if (m_hWndRender == NULL)
        return DisplayErrorMsg(D3DAPPERR_NOWINDOW, MSGERR_CANNOTCONTINUE);
 
    // создает экземпляр объекта D3D
    if ((m_pd3d = Direct3DCreate9(D3D_SDK_VERSION)) == NULL)
        return DisplayErrorMsg(D3DAPPERR_NODIRECT3D, MSGERR_CANNOTCONTINUE);

    // создает список адаптеров, режимов и устройств D3D
    if (FAILED(hr = Enumeration.Enumerate(m_pd3d)))
    {
        SAFE_RELEASE(m_pd3d);
       
        return DisplayErrorMsg(hr, MSGERR_CANNOTCONTINUE);
    }

    // использует окно устройства в качестве активного окна, если не задано иное
    if (m_hWndFocus == NULL)
        m_hWndFocus = m_hWndRender;

    // сохраняет несколько свойств окна в членах класса
    m_dwWindowStyle = GetWindowLong(m_hWndRender, GWL_STYLE);
   
    GetWindowRect(m_hWndRender, &m_rcWindow);
    GetClientRect(m_hWndRender, &m_rcClient);

    // выбирает лучшие настройки для формирования изображения
    if (FAILED(hr = ChooseInitialSettings()))
    {
        SAFE_RELEASE(m_pd3d);
   
        return DisplayErrorMsg(hr, MSGERR_CANNOTCONTINUE);
    }

    // инициализирует таймер
    DXUtil_Timer(TIMER_START);

    // инициализирует пользовательские элементы приложения (перед созданием устройства)
    if (FAILED(hr = OneTimeSceneInit()))
    {
        SAFE_RELEASE(m_pd3d);
       
        return DisplayErrorMsg(hr, MSGERR_CANNOTCONTINUE);
    }

    // инициализирует трехмерную среду, создавая устройство
    if (FAILED(hr = InitializeEnvironment()))
    {
        SAFE_RELEASE(m_pd3d);
       
        return DisplayErrorMsg(hr, MSGERR_CANNOTCONTINUE);
    }

    // D3D готово к работе, поэтому активизируем его
    Pause(false);
    return S_OK;
}

Первая вещь, которую CXD3D::CreateD3D проверяет, - ненулевой член m_hWndRender. Затем он создаст экземпляр объекта D3D с Direct3DCreate9(D3D_SDK_VERSION). При успехе инициализируется член-указатель объекта D3D на интерфейс COM IDirect3D9 , поэтому мы можем продолжать. Затем нужно создать список адаптеров дисплея, режимов и устройств, используя объект внутренних настроек «перечисление» CXD3D, объект класса CXD3DEnum.

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