Обзор DXGI

Инфраструктура графики Microsoft DirectX (DXGI) распознает, что некоторые части графики развиваются медленнее, чем другие. Основной целью DXGI является управление низкоуровневыми задачами, которые могут быть независимы от среды выполнения графики DirectX. DXGI предоставляет общую платформу для будущих графических компонентов; Первым компонентом, используюющим преимущества DXGI, является Microsoft Direct3D 10.

В предыдущих версиях Direct3D в среду выполнения Direct3D были включены низкоуровневые задачи, такие как перечисление аппаратных устройств, представление отображаемых кадров на выход, управление гаммой и управление полноэкранным переходом. Эти задачи теперь реализованы в DXGI.

DXGI предназначен для взаимодействия с драйвером режима ядра и системным оборудованием, как показано на следующей схеме.

Схема взаимодействия между приложениями, dxgi, драйверами и оборудованием

Приложение может напрямую обращаться к DXGI или вызывать API Direct3D в D3D11_1.h, D3D11.h, D3D10_1.h или D3D10.h, которые обрабатывают взаимодействие с DXGI. Вы можете работать с DXGI напрямую, если приложению необходимо перечислить устройства или управлять представлением данных в выходных данных.

В этом разделе содержатся следующие подразделы.

Чтобы узнать, какие форматы поддерживаются оборудованием Direct3D 11, выполните следующие действия:

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

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

схема компьютера с двумя видеоадаптеры и тремя мониторами

При перечислении этих элементов оборудования DXGI создает интерфейс IDXGIOutput1 для каждого вывода (или монитора) и интерфейс IDXGIAdapter2 для каждого видео карта (даже если это видео карта встроенный в системную плату). Перечисление выполняется с помощью вызова интерфейса IDXGIFactoryIDXGIFactory::EnumAdapters для возврата набора интерфейсов IDXGIAdapter , представляющих видеоустройство.

В DXGI 1.1 добавлен интерфейс IDXGIFactory1 . IDXGIFactory1::EnumAdapters1 возвращает набор интерфейсов IDXGIAdapter1 , представляющих видеоустройство.

Если вы хотите выбрать определенные возможности видеоустройства при использовании API Direct3D, рекомендуется итеративно вызывать функцию D3D11CreateDevice или D3D11CreateDeviceAndSwapChain с каждым дескриптором адаптера и возможным уровнем функций оборудования. Эта функция выполняется успешно, если уровень компонентов поддерживается указанным адаптером.

Новые сведения о перечислении адаптеров для Windows 8

Начиная с Windows 8, адаптер с именем Microsoft Basic Render Driver всегда присутствует. Этот адаптер имеет значение VendorId 0x1414 и DeviceID 0x8c. Этот адаптер также имеет значение DXGI_ADAPTER_FLAG_SOFTWARE , заданное в элементе Flags структуры DXGI_ADAPTER_DESC2 . Этот адаптер является устройством только для отрисовки, которое не имеет выходных данных. DXGI никогда не возвращает DXGI_ERROR_DEVICE_REMOVED для этого адаптера.

Если драйвер дисплея компьютера не работает или отключен, основной адаптер компьютера (NULL) также может называться Microsoft Basic Render Driver. Но у этого адаптера есть выходные данные и не задано значение DXGI_ADAPTER_FLAG_SOFTWARE . Операционная система и приложения используют этот адаптер по умолчанию. Если драйвер дисплея установлен или включен, приложения могут получать DXGI_ERROR_DEVICE_REMOVED для этого адаптера, а затем повторно перечислять адаптеры.

Если основным видеоадаптером компьютера является microsoft Basic Display Adapter (АДАПТЕР WARP ), этот компьютер также имеет второй адаптер. Этот второй адаптер является устройством только для отрисовки, которое не имеет отображаемых выходных данных и для которого DXGI никогда не возвращает DXGI_ERROR_DEVICE_REMOVED.

Если вы хотите использовать WARP для отрисовки, вычислений или других длительных задач, рекомендуется использовать устройство только для отрисовки. Вы можете получить указатель на устройство, доступное только для отрисовки, вызвав метод IDXGIFactory1::EnumAdapters1 . Устройство, доступное только для отрисовки, также создается при указании D3D_DRIVER_TYPE_WARP в параметре DriverTypeD3D11CreateDevice, так как устройство WARP также использует адаптер WARP только для отрисовки.

Уровень представления

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

Иллюстрация цепочки буферов

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

Иллюстрация вложенной системы отображения

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

Цепочка буферов настроена для рисования в полноэкранном или оконном режиме. Это избавляет от необходимости знать, является ли вывод оконным или полноэкранным. Цепочка буферов в полноэкранном режиме может оптимизировать производительность, переключив разрешение экрана.

Создание цепочки буферов

Различия между Direct3D 9 и Direct3D 10: Direct3D 10 является первым графическим компонентом, который использует DXGI. DXGI имеет несколько различных вариантов поведения цепочки буферов.
  • В DXGI цепочка буферов привязывается к окну при создании цепочки буферов. Это изменение повышает производительность и экономит память. Предыдущие версии Direct3D позволяли цепочке буферов изменять окно, к которому привязана цепочка буферов.
  • В DXGI цепочка буферов при создании привязывается к устройству отрисовки. Объект устройства, возвращаемый функцией создания устройства Direct3D, реализует интерфейс IUnknown . Вы можете вызвать QueryInterface для запроса соответствующего интерфейса IDXGIDevice2 устройства. Для изменения устройства отрисовки требуется повторно создать цепочку буферов.
  • В DXGI доступны эффекты переключения DXGI_SWAP_EFFECT_DISCARD и DXGI_SWAP_EFFECT_SEQUENTIAL. Начиная с Windows 8 также доступен эффект переключения DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL. В следующей таблице показано сопоставление Direct3D 9 с определениями эффекта переключения DXGI.

    Эффект переключения D3D9 Эффект переключения DXGI
    D3DSWAPEFFECT_DISCARD DXGI_SWAP_EFFECT_DISCARD
    D3DSWAPEFFECT_COPY DXGI_SWAP_EFFECT_SEQUENTIAL с 1 буфером
    D3DSWAPEFFECT_FLIP DXGI_SWAP_EFFECT_SEQUENTIAL с 2 или более буферами
    D3DSWAPEFFECT_FLIPEX DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL с 2 или более буферами

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

После создания цепочки буферов обычно требуется преобразовать в нее изображения. Ниже приведен фрагмент кода, который настраивает контекст Direct3D для отрисовки в цепочку буферов. Этот код извлекает буфер из цепочки буферов, создает представление render-target-view из этого буфера, а затем задает его на устройстве:

ID3D11Resource * pBB;
ThrowFailure( pSwapChain->GetBuffer(0, __uuidof(pBB),    
  reinterpret_cast<void**>(&pBB)), "Couldn't get back buffer");
ID3D11RenderTargetView * pView;
ThrowFailure( pD3D11Device->CreateRenderTargetView(pBB, NULL, &pView), 
  "Couldn't create view" );
pD3D11DeviceContext->OMSetRenderTargets(1, &pView, 0);
        

После отрисовки кадра в буфер цепочки буферов вызовите IDXGISwapChain1::P resent1. Затем приложение может перейти на отрисовку следующего изображения.

Уход и кормление цепочки буферов

После отрисовки изображения вызовите МЕТОД IDXGISwapChain1::P resent1 и отрисуйте следующее изображение. Это степень вашей ответственности.

Если вы ранее называли IDXGIFactory::MakeWindowAssociation, пользователь может нажать сочетание клавиш Alt-Enter, и DXGI переключит приложение из оконного режима в полноэкранный режим. Рекомендуется использовать IDXGIFactory::MakeWindowAssociation, так как настоятельно необходим стандартный механизм управления для пользователя.

Хотя вам не нужно писать больше кода, чем было описано, несколько простых шагов могут повысить скорость реагирования приложения. Наиболее важным аспектом является изменение размера буферов цепочки буферов в ответ на изменение размера окна вывода. Естественно, лучший маршрут приложения — реагировать на WM_SIZE и вызывать IDXGISwapChain::ResizeBuffers, передав размер, содержащийся в параметрах сообщения. Это поведение, очевидно, заставляет приложение хорошо реагировать на пользователя, когда он перетаскивает границы окна, но это также именно то, что обеспечивает плавный переход в полноэкранный режим. Окно будет получать WM_SIZE сообщение всякий раз, когда происходит такой переход, и вызов IDXGISwapChain::ResizeBuffers является шансом цепочки буферов повторно выделить хранилище буферов для оптимального представления. Именно поэтому приложение должно освободить все ссылки на существующие буферы перед вызовом IDXGISwapChain::ResizeBuffers.

Сбой вызова IDXGISwapChain::ResizeBuffers в ответ на переключение в полноэкранный режим (естественно, в ответ на WM_SIZE) может исключить оптимизацию перелистывания, при этом DXGI может просто поменять отображаемый буфер, а не копировать данные на весь экран.

IDXGISwapChain1::P resent1 сообщит, если окно вывода полностью заключено через DXGI_STATUS_OCCLUDED. В этом случае рекомендуется, чтобы приложение перешел в режим ожидания (путем вызова IDXGISwapChain1::P resent1 с DXGI_PRESENT_TEST), так как ресурсы, используемые для отрисовки кадра, будут потрачены впустую. Использование DXGI_PRESENT_TEST предотвратит представление любых данных при выполнении проверка окклюзии. Как только IDXGISwapChain1::P resent1 вернет S_OK, следует выйти из режима ожидания; Не используйте код возврата для переключения в резервный режим, так как это может оставить цепочку буферов не в состоянии отказаться от полноэкранного режима.

Среда выполнения Direct3D 11.1, которая доступна, начиная с Windows 8, предоставляет цепочку буферов с использованием модели переворачивания (т. е. цепочку буферов, которая содержит значение DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, заданное в элементе SwapEffectDXGI_SWAP_CHAIN_DESC или DXGI_SWAP_CHAIN_DESC1). При представлении кадров в выходные данные с помощью цепочки буферов модели переворачивания DXGI отменяет привязку обратного буфера из всех расположений состояния конвейера, таких как целевой объект отрисовки вывода и слияния, которые записывают данные в задний буфер 0. Поэтому рекомендуется вызывать ID3D11DeviceContext::OMSetRenderTargets непосредственно перед отрисовкой в задний буфер. Например, не вызывайте OMSetRenderTargets , а затем выполняйте работу вычислительного шейдера, которая не приводит к отрисовке ресурса. Дополнительные сведения о цепочках буферов модели переворачивания и их преимуществах см. в разделе DXGI Flip Model.

Примечание

В Direct3D 10 и Direct3D 11 не нужно вызывать IDXGISwapChain::GetBuffer , чтобы получить обратно буфер 0 после вызова IDXGISwapChain1::P resent1 , так как для удобства удостоверения задних буферов изменяются. В Direct3D 12 этого не происходит, и приложение должно вручную отслеживать индексы буфера.

Обработка изменения размера окна

Для обработки изменения размера окна можно использовать метод IDXGISwapChain::ResizeBuffers . Перед вызовом ResizeBuffers необходимо освободить все оставшиеся ссылки на буферы цепочки буферов. Объект, который обычно содержит ссылку на буфер цепочки буферов, является представлением объекта render-target.

В следующем примере кода показано, как вызывать ResizeBuffers из обработчика WindowProc для WM_SIZE сообщений:

    case WM_SIZE:
        if (g_pSwapChain)
        {
            g_pd3dDeviceContext->OMSetRenderTargets(0, 0, 0);

            // Release all outstanding references to the swap chain's buffers.
            g_pRenderTargetView->Release();

            HRESULT hr;
            // Preserve the existing buffer count and format.
            // Automatically choose the width and height to match the client rect for HWNDs.
            hr = g_pSwapChain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, 0);
                                            
            // Perform error handling here!

            // Get buffer and create a render-target-view.
            ID3D11Texture2D* pBuffer;
            hr = g_pSwapChain->GetBuffer(0, __uuidof( ID3D11Texture2D),
                                         (void**) &pBuffer );
            // Perform error handling here!

            hr = g_pd3dDevice->CreateRenderTargetView(pBuffer, NULL,
                                                     &g_pRenderTargetView);
            // Perform error handling here!
            pBuffer->Release();

            g_pd3dDeviceContext->OMSetRenderTargets(1, &g_pRenderTargetView, NULL );

            // Set up the viewport.
            D3D11_VIEWPORT vp;
            vp.Width = width;
            vp.Height = height;
            vp.MinDepth = 0.0f;
            vp.MaxDepth = 1.0f;
            vp.TopLeftX = 0;
            vp.TopLeftY = 0;
            g_pd3dDeviceContext->RSSetViewports( 1, &vp );
        }
        return 1;

Выбор выходных данных и размера DXGI

По умолчанию DXGI выбирает выходные данные, содержащие большую часть клиентской области окна. Это единственный вариант, доступный DXGI при переходе в полноэкранный режим в ответ на alt-ввод. Если приложение само по себе переходит в полноэкранный режим, оно может вызвать IDXGISwapChain::SetFullscreenState и передать явное значение IDXGIOutput1 (или NULL, если приложение радо разрешить DXGI решить).

Чтобы изменить размер выходных данных в полноэкранном или оконном режиме, рекомендуется вызвать IDXGISwapChain::ResizeTarget, так как этот метод также изменяет размер целевого окна. Так как размер целевого окна изменен, операционная система отправляет WM_SIZE, и код в ответ будет вызывать IDXGISwapChain::ResizeBuffers . Таким образом, это пустая трата усилий, чтобы изменить размер буферов, а затем изменить размер целевого объекта.

Отладка в полноэкранном режиме

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

Если переключение режима разрешено, цепочка буферов будет отказаться от полноэкранного режима всякий раз, когда окно вывода будет заключено другим окном. Проверка окклюзии выполняется во время IDXGISwapChain1::P resent1 или отдельным потоком, целью которого является watch, чтобы проверить, не отвечает ли приложение (и больше не вызывает IDXGISwapChain1::P resent1). Чтобы отключить возможность отдельного потока вызывать переключение, задайте для следующего раздела реестра любое ненулевое значение.

HKCU\Software\Microsoft\DXGI\DisableFullscreenWatchdog

Уничтожение цепочки буферов

Вы не можете освободить цепочку буферов в полноэкранном режиме, так как это может привести к состязанию потоков (что приведет к тому, что DXGI вызовет исключение, не являющееся непрерывным). Перед выпуском цепочки буферов сначала переключитесь в оконный режим (с помощью IDXGISwapChain::SetFullscreenState( FALSE, NULL )), а затем вызовите IUnknown::Release.

Использование повернутого монитора

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

  • Используйте DXGI_SWAP_CHAIN_FLAG_NONPREROTATED. Это уведомляет DXGI о том, что приложение создаст повернутое изображение (например, путем изменения матрицы проекции). Следует отметить, что этот флаг действителен только в полноэкранном режиме.
  • Выделите каждый буфер цепочки буферов в повернутом размере. При необходимости используйте IDXGIOutput::GetDesc , чтобы получить эти значения.

Выполняя ротацию в приложении, DXGI просто выполняет копирование, а не копирование и поворот.

Среда выполнения Direct3D 11.1, доступная начиная с Windows 8, предоставляет цепочку буферов модели переворачивания (т. е. цепочку буферов, которая содержит значение DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, заданное в элементе SwapEffectDXGI_SWAP_CHAIN_DESC1). Чтобы максимально повысить эффективность оптимизации презентации, доступных с помощью цепочки буферов модели переворачивания, рекомендуется настроить ориентацию содержимого приложений в соответствии с конкретными выходными данными, на которых находится содержимое, когда это содержимое полностью занимает выходные данные. Дополнительные сведения о цепочках буферов модели переворачивания и их преимуществах см. в разделе DXGI Flip Model.

Переключение режимов

Цепочка буферов DXGI может изменить режим отображения выходных данных при выполнении полноэкранного перехода. Чтобы включить автоматическое изменение режима отображения, необходимо указать DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH в описании цепочки буферов. Если режим отображения изменится автоматически, DXGI выберет самый скромный режим (размер и разрешение не изменятся, но глубина цвета может измениться). Изменение размера буферов цепочки буферов не вызовет переключение режима. Цепочка буферов дает неявное обещание, что если выбрать задний буфер, который точно соответствует режиму отображения, поддерживаемму целевыми выходными данными, он переключится в этот режим отображения при входе в полноэкранный режим на этом выходе. Следовательно, вы выбираете режим отображения, выбрав размер и формат заднего буфера.

Советы по повышению производительности в полноэкранном режиме

При вызове IDXGISwapChain1::P resent1 в полноэкранном приложении цепочка буферов переворачивает содержимое обратного буфера в передний буфер. Для этого необходимо, чтобы цепочка буферов была создана с использованием перечисленного режима отображения (указанного в DXGI_SWAP_CHAIN_DESC1). Если не удается перечислить режимы отображения или неправильно указать режим отображения в описании, цепочка буферов может выполнять передачу битового блока (bitblt). Bitblt вызывает дополнительную растягивающую копию, а также некоторое увеличение использования видеопамять, и его трудно обнаружить. Чтобы избежать этой проблемы, перечислите режимы отображения и правильно инициализируйте описание цепочки буферов перед созданием цепочки буферов. Это обеспечит максимальную производительность при перелистывание в полноэкранном режиме и избежать дополнительных затрат на память.

Рекомендации по многопотоковой работе

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

  • Поток отрисовки не является потоком потока потока сообщений.
  • Поток, выполняющий API DXGI, не является потоком, создающим окно.

Будьте осторожны, чтобы поток конвейера сообщений никогда не ждал в потоке отрисовки при использовании полноэкранных цепочек буферов. Например, вызов IDXGISwapChain1::P resent1 (из потока отрисовки) может привести к тому, что поток отрисовки будет ожидать потока потоков сообщений. При изменении режима этот сценарий возможен, если Present1 вызывает ::SetWindowPos() или ::SetWindowStyle() и любой из этих методов вызывает ::SendMessage(). В этом сценарии, если поток потока потоков сообщений имеет критически важный раздел, охраняющий его, или поток отрисовки заблокирован, то два потока будут взаимоблокированы.

Дополнительные сведения об использовании DXGI с несколькими потоками см. в разделе Многопоточность и DXGI.

Ответы DXGI от DLLMain

Так как функция DllMain не может гарантировать порядок, в котором она загружает и выгружает библиотеки DLL, мы рекомендуем не вызывать функции и методы Direct3D или DXGI, включая функции или методы, создающие или освобождающие объекты. Если функция DllMain вашего приложения вызывает определенный компонент, этот компонент может вызвать другую библиотеку DLL, которая отсутствует в операционной системе, что приводит к сбою операционной системы. Direct3D и DXGI могут загружать набор библиотек DLL, обычно набор драйверов, который отличается от компьютера к компьютеру. Таким образом, даже если ваше приложение не завершает работу на компьютерах разработки и тестирования, когда его функция DllMain вызывает функции или методы Direct3D или DXGI, оно может завершиться сбоем при запуске на другом компьютере.

Чтобы предотвратить создание приложения, которое может привести к аварийному завершению работы операционной системы, DXGI предоставляет следующие ответы в указанных ситуациях:

  • Если функция DllMain вашего приложения освобождает последнюю ссылку на фабрику DXGI, DXGI создает исключение.
  • Если функция DllMain приложения создает фабрику DXGI, DXGI возвращает код ошибки.

Изменения DXGI 1.1

Мы добавили следующие функции в DXGI 1.1.

  • Поддержка синхронизированных общих поверхностей

    Синхронизированные общие поверхности для Direct3D 10.1 и Direct3D 11 обеспечивают эффективное совместное использование поверхностей чтения и записи между несколькими устройствами Direct3D (возможно совместное использование между устройствами Direct3D 10 и Direct3D 11). См . статьи IDXGIKeyedMutex::AcquireSync и IDXGIKeyedMutex::ReleaseSync.

  • Поддержка высокого цвета

    Поддерживает формат DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM.

  • IDXGIDevice1::SetMaximumFrameLatency и IDXGIDevice1::GetMaximumFrameLatency

  • IDXGIFactory1::EnumAdapters1 перечисляет локальные адаптеры без подключенных мониторов или выходных данных, а также адаптеры с подключенными выходами. Первым возвращенным адаптером будет локальный адаптер, на котором отображается основной рабочий стол.

  • Поддержка формата BGRA

    DXGI_FORMAT_B8G8R8A8_UNORM и DXGI_FORMAT_B8G8R8A8_UNORM_SRGB см. в разделах IDXGISurface1::GetDC и IDXGISurface1::ReleaseDC.

Изменения DXGI 1.2

Мы добавили следующие функции в DXGI 1.2.

  • Стерео цепочка буферов
  • Цепочка буферов модели flip
  • Оптимизированная презентация (прокрутка, грязное прямоугольники и поворот)
  • Улучшены общие ресурсы и синхронизация
  • Дублирование рабочего стола
  • Оптимизированное использование видеопамять
  • Поддержка форматов 16 бит на пиксель (bpp) (DXGI_FORMAT_B5G6R5_UNORM, DXGI_FORMAT_B5G5R5A1_UNORM, DXGI_FORMAT_B4G4R4A4_UNORM)
  • API отладки

Дополнительные сведения о DXGI 1.2 см. в разделе Улучшения DXGI 1.2.

Руководство по программированию для DXGI