Цепочка переключения буферов - SIISII/DirectX GitHub Wiki

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

Как правило, цепочка состоит из двух или большего числа буферов; каждый буфер является поверхностью, т. е. хранилищем изображения (массива пикселей определённого формата). В определённых ситуациях возможно использование единственного буфера, а приложения, осуществляющие рендеринг без непрерывного взаимодействия с пользователем (например, видеоплееры), могут получить выгоду от использования большого числа буферов (до 16). В играх типичным является использование двух или трёх буферов, один из которых является первичным (front buffer), а остальные — вторичными (back buffers). В любой момент времени имеется только один первичный буфер, чьё содержимое отображается на экране (в оконном или полноэкранном режиме); остальные буферы являются вторичными и в них выполняется рендеринг следующих кадров.

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

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

Для создания цепочки буферов в зависимости от её особенностей можно использовать одну из следующих функций:

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

Метод IDXGIFactory2::CreateSwapChainForHwnd

HRESULT  IDXGIFactory2::CreateSwapChainForHwnd(
    IUnknown                              *pDevice,
    HWND                                   hWnd,
    const DXGI_SWAP_CHAIN_DESC1           *pDesc,
    const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *pFullscreenDesc,
    IDXGIOutput                           *pRestrictToOutput,
    IDXGISwapChain1                       **ppSwapChain);
  • pDevice — для DirectX 10 и 11 этот параметр указывает на интерфейс устройства Direct3D (см. ID3D10Device, ID3D10Device1, ID3D11Device, ID3D11Device1, ID3D11Device2, ID3D11Device3, ID3D11Device4 и ID3D11Device5), которое будет использоваться для рендеринга изображения в буферы создаваемой цепочки; для DirectX 12 здесь указывается интерфейс непосредственной очереди команд (см. ID3D12CommandQueue). В любом случае он не может быть нулевым;

  • hWnd — хэндл окна, к которому привязывается цепочка; не может быть нулевым. Не следует связывать с одним окном несколько цепочек;

  • pDesc — указатель на структуру типа DXGI_SWAP_CHAIN_DESC1, содержащую описание параметров создаваемой цепочки; не может быть нулевым (подробнее см. ниже);

  • pFullscreenDesc — указатель на структуру типа DXGI_SWAP_CHAIN_FULLSCREEN_DESC, если создаётся цепочка для полноэкранного режима, и нуль в противном случае. Создавать полноэкранную цепочку не рекомендуется, вместо этого следует создать оконную цепочку, а затем переключиться в полноэкранный режим;

  • pRestrictToOutput — указатель на интерфейс видеовыхода (IDXGIOutput или один из его потомков), если цепочка должна выводить изображение только через этот выход (при попытке перенести вывод изображения на другой выход будет показываться чёрный экран). Если ограничивать возможность вывода лишь определённым видеовыходом не нужна, этот параметр должен быть нулевым;

  • ppSwapChain — указатель на переменную, в которую будет помещён указатель на интерфейс IDXGISwapChain1 для работы с созданной цепочкой. Если требуется иная версия интерфейса, после создания цепочки он может быть получен из интерфейса IDXGISwapChain1 обычным вызовом метода QueryInterface.

Характеристики цепочки

Наиболее полно характеристики создаваемой цепочки описываются структурой типа DXGI_SWAP_CHAIN_DESC1, указатель на которую передаётся в функцию IDXGIFactory2::CreateSwapChainForHwnd и ряд других функций, содержит основные характеристики цепочки переключения буферов:

  • Width и Height — ширина и высота каждого буфера цепочки в пикселях; если любой из этих параметров равен нулю, при создании цепочки будут взяты размеры клиентской части окна, для работы с которым предназначена цепочка;

  • Formatформат пикселей, хранящихся в буферах цепочки. Допустимые форматы зависят от используемой версии Direct3D и должны выбираться с учётом обеспечения правильной цветопередачи. Если в поле SwapEffect задано значение DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, может использоваться лишь один из форматов DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_B8G8R8A8_UNORM или DXGI_FORMAT_R8G8B8A8_UNORM;

  • Stereo — равен true, если в полноэкранном режиме будет формироваться стереоизображение, при этом поле SwapEffect должно содержать значение DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; равен false для обычного режима;

  • SampleDesc — структура типа DXGI_SAMPLE_DESC, содержащая поля Count и Quality и задающая параметры мультисэмплинга для антиалиасинга. Мультисэмплинг может использоваться лишь для цепочки, чьё поле SwapEffect имеет значение DXGI_SWAP_EFFECT_DISCARD (что означает, что в DirectX 12 мультисэмплинг на уровне цепочки переключения буферов задаваться не может); в остальных случаях подполе Count должно быть равным единице, а Quality — нулю;

  • BufferUsage — маска, биты которой определяют вид использования поверхности, т. е. содержимого вторичных буферов в цепочке. При создании цепочки могут указываться лишь биты DXGI_USAGE_RENDER_TARGET_OUTPUT (цепочка принимает результаты рендеринга) и DXGI_USAGE_SHADER_INPUT (информация в буфере может считываться шейдерами), а также DXGI_USAGE_BACK_BUFFER, но последний не обязателен (он подразумевается по умолчанию);

  • BufferCount — количество буферов в цепочке. Если она может использоваться для полноэкранного режима, в это число следует включать и первичный буфер (в оконном режиме он в строгом смысле слова у цепочки отсутствует, поскольку реальным первичным буфером управляет оконная подсистема Windows, а изображение, сформированное приложением, тем или иным способом переносится в клиентскую область окна этого приложения);

  • Scaling — способ масштабирования изображения, если размеры буферов в цепочке не совпадают с фактическими размерами области вывода. Возможные значения:

    • DXGI_SCALING_NONE — масштабирование не выполняется; содержимое первичного буфера цепочки прижимается к верхнему и либо к левому, либо к правому краю области вывода (выбор левого или правого края зависит от того, был ли указан стиль WS_EX_LAYOUTRTL для окна, с которым связана цепочка), а если в области вывода осталось неиспользуемое пространство, оно закрашивается цветом фона (задаётся методом IDXGISwapChain1::SetBackgroundColor). Данное значение может использоваться лишь с «перекидной» (flip) моделью переключения буферов, задаваемой полем SwapEffect;

    • DXGI_SCALING_STRETCH — производится масштабирование содержимого буфера к размеру области вывода (независимо по горизонтали и по вертикали);

    • DXGI_SCALING_ASPECT_RATIO_STRETCH — производится масштабирование с сохранением пропорций; при необходимости изображение помещается в центре области вывода, а незанятые части закрашиваются (то ли чёрным цветом, то ли цветом фона). Это значение может указываться в Windows 10; в более ранних системах оно ведёт себя аналогично предыдущему значению;

  • SwapEffect — способ отображения сформированного изображения и что с ним происходит после отображения. Возможные значения:

    • DXGI_SWAP_EFFECT_DISCARD — отображение при вызове метода, подобного IDXGISwapChain1::Present1, осуществляется копированием сформированного изображения из вторичного буфера во временный буфер, после чего созданная копия поступает в распоряжение оконного менеджера рабочего стола (DWM, Desktop Window Manager) для фактического отображения. Это так называемая модель переключения буферов копированием битов — bitblt model. Исходное содержимое вторичного буфера после копирования теряется. Конкретный способ выполнения операции определяет драйвер. Этот способ может применяться с цепочкой, содержащей несколько вторичных буферов, хотя приложение имеет доступ на чтение и запись только к нулевому буферу. Он поддерживается в DirectX 10 и 11, но не в DirectX 12;

    • DXGI_SWAP_EFFECT_SEQUENTIAL — отображение осуществляется копированием, при этом содержимое вторичного буфера сохраняется. Этот способ подходит для последовательного отображения цепочки вторичных буферов. Он несовместим с мультисэмплингом (на уровне всего вторичного буфера; мультисэмплинг может выполняться и шейдерами в процессе рендеринга, на что никаких ограничений не накладывается) и поддерживается в DirectX 10 и 11, но не в DirectX 12;

    • DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL — все вторичные буферы цепочки используются совместно приложением и DWM, поэтому при передаче буфера на отображение DWM может обойтись без лишних операций копирования. Это так называемая «перекидная» модель переключения буферов — flip model, поскольку буферы цепочки «перекидываются» от приложения к DWM и обратно. Содержимое отображённого буфера сохраняется. Этот способ несовместим с мультисэмплингом и поддерживается, начиная с DirectX 11 в Windows 8 (в более ранних версиях системы он недоступен). Он требует наличия минимум двух буферов в цепочке, а после отображения буфера новый вторичный буфер перед началом его использования должен быть опять привязан к немедленному контексту D3D11. Кроме того, при переходе из полноэкранного режима в оконный или обратно необходимо изменять размеры буферов (возможно, даже в случае, если фактический размер в пикселях не изменяется — документация не даёт ясного ответа на этот вопрос).

      Хотя «перекидывание» буферов в общем случае существенно эффективней копирования, оно накладывает ряд ограничений на приложение. В частности, «перекидывание» не всегда совместимо с GDI и с другими «гетерогенными» способами формирования изображения (однако ограничений на совместную работу с Direct2D нет). Подробнее об особенностях этой модели сказано в DXGI flip model, For best performance, use DXGI flip model и Flip model, dirty rectangles, scrolled areas;

    • DXGI_SWAP_EFFECT_FLIP_DISCARD — отображение аналогично предыдущему случаю, однако содержимое отображённого вторичного буфера утрачивается. Этот способ несовместим с мультисэмплингом и частичным отображением и поддерживается, начиная с DirectX 11 в Windows 10. Если приложение всегда формирует изображение целиком и не нуждается в считывании ранее созданных кадров, этот способ является наиболее предпочтительным.

  • AlphaMode — задаёт способ смешивания изображения из буфера цепочки с остальными изображениями на экране, используя прозрачность. Возможные значения:

    • DXGI_ALPHA_MODE_UNSPECIFIED — прозрачность не определена;

    • DXGI_ALPHA_MODE_PREMULTIPLIED — значения цветовых каналов были предварительно умножены на значение альфа-канала. Например, если исходный цвет был равен [255, 0, 0, 153] (красный цвет с прозрачностью 40%, так как 153 = 255 * 0.6), то он будет сохранён в буфере как [153, 0, 0, 153]. Как правило, значения цветовых каналов в таком режиме не превосходят значения альфа-канала (неясно, как именно обрабатывается случай, когда значение цветового канала больше значения альфа-канала);

    • DXGI_ALPHA_MODE_STRAIGHT — предварительное умножение не выполняется, поэтому в буфере содержатся истинные значения цветовых каналов пикселей наряду с обычным значением альфа-канала (последний одинаков в этом и предыдущем режимах);

    • DXGI_ALPHA_MODE_IGNORE — прозрачность игнорируется. Неясно, распространяется ли действие этого значения на буферы цели рендеринга: согласно описанию Direct2D (куда переадресует читателя описание для Direct3D), альфа-канал цели рендеринга работает всегда.

  • Flags — битовая маска флагов, задающих определённые аспекты цепочки переключения буферов:

    • DXGI_SWAP_CHAIN_FLAG_NONPREROTATED — этот флаг запрещает автоматический поворот изображения во время его копирования в первичный буфер монитора. Он используется только с полноэкранным режимом теми приложениями, которые сами умеют обрабатывать поворот экрана;

    • DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH — этот флаг разрешает приложению изменять режимы вызовом метода IDXGISwapChain::ResizeTarget, т. е. даёт возможность изменять размеры клиентской области окна или разрешение монитора в полноэкранном режиме;

    • DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE — этот флаг разрешает совместную работу (с определёнными ограничениями) Direct3D 10 или 11 с GDI. Он не может указываться при использовании Direct3D 12;

    • DXGI_SWAP_CHAIN_FLAG_RESTRICTED_CONTENT — этот флаг разрешает создание цепочки переключения лишь в случае, если аппаратура и её драйвер поддерживают вывод защищённого контента, в противном случае попытка создания цепочки заканчивается неудачей. Этот флаг поддерживается, начиная с DirectX 11 в Windows 8;

    • DXGI_SWAP_CHAIN_FLAG_RESTRICT_SHARED_RESOURCE_DRIVER — этот флаг указывает, что совместно используемые ресурсы, создаваемые с цепочкой, должны быть защищены драйвером с помощью механизма ограничения доступа к совместно используемым ресурсам. Он поддерживается, начиная с DirectX 11 в Windows 8;

    • DXGI_SWAP_CHAIN_FLAG_DISPLAY_ONLY — этот флаг ограничивает вывод изображения только локальными дисплеями, запрещая удалённое отображение или использование API дублирования рабочего стола. Этот флаг используется для защиты контента и препятствует захвату изображения с экрана посредством специальных функций API; он поддерживается, начиная с DirectX 11 в Windows 8;

    • DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT — этот флаг указывается для создания объекта, который может использоваться, чтобы предотвратить начало рендеринга, если кадр всё ещё отображается. При его использовании необходимо устанавливать максимальную латентность цепочки переключения методом IDXGISwapChain2::SetMaximumFrameLatency, а не IDXGIDevice1::SetMaximumFrameLatency. В полноэкранном режиме этот флаг может использоваться только совместно с Direct3D 12. Он был введён в Windows 8.1;

    • DXGI_SWAP_CHAIN_FLAG_FOREGROUND_LAYER — этот флаг вызывает создание цепочки переключения для переднего плана, что возможно лишь для приложений Windows Store, где цепочка создаётся вызовом CreateSwapChainForCoreWindow, и лишь в случае, если многоплановый рендеринг поддерживается. Этот флаг был введён в Windows 8.1;

    • DXGI_SWAP_CHAIN_FLAG_FULLSCREEN_VIDEO — этот флаг устанавливается для создания цепочки, предназначенной для вывода полноэкранного видео; он появился в Windows 8.1;

    • DXGI_SWAP_CHAIN_FLAG_YUV_VIDEO — этот флаг устанавливается для создания цепочки, предназначенной для вывода видео цветового формата YUV; он появился в Windows 8.1;

    • DXGI_SWAP_CHAIN_FLAG_HW_PROTECTED — этот флаг разрешает создание цепочки лишь в случае, если все нижележащие ресурсы будут защищаться аппаратурой. Он может использоваться лишь в случае, если поле SwapEffect равно DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, и сам по себе не гарантирует защиту: некоторые реализации требуют предварительной явной инициализации компонентов DRM. Этот флаг появился в Windows 10;

    • DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING — этот флаг разрешает разрыв изображения. Он обязателен для правильной работы в оконном режиме на дисплеях с переменной частотой кадров, поддержка которых появилась в одном из обновлений Windows 10. Приложениям, использующим Direct3D 11 или 12, рекомендуется всегда указывать этот флаг, если метод IDXGIFactory5::CheckFeatureSupport подтверждает возможность его применения, а приложение при выводе буфера из цепочки использует нулевой интервал синхронизации (см. ниже). Этот флаг может использоваться лишь с «перекидной» моделью отображения буферов;

    • DXGI_SWAP_CHAIN_FLAG_RESTRICTED_TO_ALL_HOLOGRAPHIC_DISPLAYS — этот флаг в официальной документации приводится без каких-либо пояснений.

Переключение между оконным и полноэкранным режимами или изменение характеристик буферов цепочки

Чтобы разрешить пользователю переключаться между оконным и полноэкранным режимами нажатием Ctrl-Enter, следует использовать метод IDXGIFactory::MakeWindowAssociation, имеющий следующий прототип:

HRESULT  IDXGIFactory::MakeWindowAssociation(
    HWND  WindowHandle,
    UINT  Flags);

Здесь WindowHandle — хэндл окна, с которым связана цепочка переключения буферов, а Flags — набор флагов, управляющий действиями этого метода:

  • DXGI_MWA_NO_WINDOW_CHANGES — запрещает DXGI отслеживать очередь сообщений приложения, тем самым блокируя возможность реагировать на изменение режимов. Этот флаг необходимо указывать лишь в случае, если данный метод вызывается для запрета автоматической реакции DXGI на возможные изменения (предполагается, что все необходимые действие приложение будет выполнять самостоятельно);

  • DXGI_MWA_NO_ALT_ENTER — запрещает DXGI реагировать на нажатие Ctrl-Enter, т. е. автоматически переключаться между оконным и полноэкранным режимами;

  • DXGI_MWA_NO_PRINT_SCREEN — запрещает DXGI реагировать на нажатие PrintScreen, т. е. делать скриншоты.

Метод MakeWindowAssociation должен вызываться для той фабрики, которая создала данную цепочку. Получить интерфейс фабрики, имея лишь интерфейс цепочки, можно методом IDXGIObject::GetParent (цепочки наследуют все методы базового объекта DXGI).

В минимальном варианте вызова этого метода достаточно. При переключении в полноэкранный режим из оконного DXGI сама выбирает наименьшее из поддерживаемых разрешений, равное или большее размера текущего вторичного буфера. Однако данный способ может оказаться недостаточно эффективным: во-первых, может потребоваться растягивание формируемого изображения, а во-вторых, даже если растягивание не требуется (разрешение буферов в цепочке совпадает с разрешением монитора), формат буферов может оказаться неоптимальным для непосредственного вывода на монитор. Приложению рекомендуется обрабатывать сообщение WM_SIZE (DXGI всегда выдаёт его при переключении между режимами), в процессе чего вызывать метод IDXGISwapChain::ResizeBuffers, имеющий следующий прототип (набор его параметров является подмножеством набора, передаваемого методам, вызываемым для создания цепочки):

HRESULT  IDXGISwapChain::ResizeBuffers(
    UINT         BufferCount,
    UINT         Width,
    UINT         Height,
    DXGI_FORMAT  NewFormat,
    UINT         SwapChainFlags);

Если цепочка переключения создавалась, используя очередь команд Direct3D 12 в качестве входного устройства, вместо описанного выше метода следует вызывать метод IDXGISwapChain3::ResizeBuffers1:

HRESULT IDXGISwapChain3::ResizeBuffers1(
    UINT              BufferCount,
    UINT              Width,
    UINT              Height,
    DXGI_FORMAT       Format,
    UINT              SwapChainFlags,
    const UINT       *pCreationNodeMask,
    IUnknown* const  *ppPresentQueue);

Дальнейшие замечания, если не сказано иное, применимы к обоим методам, поэтому под ResizeBuffers следует иметь в виду и ResizeBuffers1.

Если используется «перекидная» модель вывода буфера, которая необходима в Direct3D 12 и является предпочтительной в Direct3D 11, приложение обязано вызвать ResizeBuffers, в противном случае отображение следующего кадра окажется невозможным. Вызов необходим и в случае, если потребовалось изменить размеры или другие характеристики буферов без переключения между режимами.

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

  • вызвать метод Release столько раз, сколько для данного буфера явным образом вызывался метод AddRef;

  • вызвать метод ID3D11DeviceContext::ClearState, чтобы отвязать все буферы и их представления от контекста устройства;

  • вызвать метод ID3D11DeviceContext::FinishCommandList, а затем Release для всех списков команд, формируемых для буферов.

Если цепочка переключения используется совместно с GDI, перед вызовом ResizeBuffers необходимо также освободить контекст устройства вызовом метода IDXGISurface1::ReleaseDC.

После изменения размера буферов требуется начать рендеринг заново, начиная с захвата буфера вызовом метода IDXGISwapChain::GetBuffer.

Если приложение хочет ограничить возможности изменения размера окна в оконном режиме только значениями, совпадающими с доступными для полноэкранного режима, оно может обрабатывать сообщение WM_SIZING, в котором сравнивать желаемый размер со списком возможных и при необходимости разрешать или запрещать изменение размеров. Найти наиболее подходящий полноэкранный режим для заданного размера окна можно вызовом метода IDXGIOutput::FindClosestMatchingMode, а найти, какой из выходов используется для отображения окна (а соответственно, какой будет использоваться для полноэкранного режима) — вызовом метода IDXGISwapChain::GetContainingOutput.

Когда приложение желает перейти из оконного режима в полноэкранный или обратно по своей инициативе, оно вызывает метод IDXGISwapChain::SetFullscreenState, имеющий следующий прототип:

HRESULT  IDXGISwapChain::SetFullscreenState(
    BOOL          Fullscreen,
    IDXGIOutput  *pTarget);

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

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

Ещё одним случаем принудительного переключения является отсутствие реакции приложения на сообщения и прекращение выдачи новых кадров; это отслеживается с помощью специального потока. В целях отладки может потребоваться запретить такое поведение, тогда следует присвоить ненулевое значение ключу реестра HKCU\Software\Microsoft\DXGI\DisableFullscreenWatchdog.

Для изменения разрешения монитора (или размеров окна) рекомендуется вызывать метод IDXGISwapChain::ResizeTarget (не может использоваться для цепочек, созданных методом IDXGIFactory2::CreateSwapChainForComposition, и в приложениях для Windows Store), которому передаются новые параметры цели рендеринга в виде структуры DXGI_MODE_DESC. Этот метод сам изменяет размер окна, избавляя приложение от необходимости вызывать функцию SetWindowPos, при этом приложение получит сообщение WM_SIZE, в котором оно должно переопределить размеры буферов цепочки, как описывалось выше.

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

Собственно уничтожение выполняется обычным освобождением интерфейса вызовом метода Release (уничтожение произойдёт, когда счётчик ссылок на объект достигнет нуля). Однако, если приложение находится в полноэкранном режиме, оно сначала должно перейти в оконный режим и лишь затем уничтожать цепочку.

Отображение буфера из цепочки

Для отображения вторичного буфера следует вызвать один из методов IDXGISwapChain::Present или IDXGISwapChain1::Present1. Документация рекомендует применять второй метод для любых приложений, использующих Direct3D 11.1 или старше, поскольку он предоставляет следующие дополнительные возможности:

  • перерисовку лишь определённых прямоугольных областей кадра, при этом содержимое остальных областей остаётся неизменным (эта возможность недоступна для цепочек переключения, использующих мультисэмплинг);

  • копирование из предыдущего кадра определённой прямоугольной области в новый кадр с возможностью смещения положения этой области на экране (в новом кадре; эта возможность недоступна при отображении копированием или при использовании цепочкой мультисэмплинга).

Эти возможности могут быть полезны при передаче изображения на удалённый рабочий стол и в других подобных случаях, когда полоса пропускания ограничена, а изображение меняется от кадра к кадру лишь частично. Однако основная масса игр целиком изменяет каждый кадр, поэтому для них перечисленные дополнительные возможности бесполезны и вполне разумно использовать метод IDXGISwapChain::Present (не потребуется передавать лишний параметр).

Метод Present имеет следующий прототип:

HRESULT  IDXGISwapChain::Present(
    UINT  SyncInterval,
    UINT  Flags);

Метод Present1 имеет следующий прототип:

HRESULT  IDXGISwapChain1::Present1(
    UINT                            SyncInterval,
    UINT                            Flags,
    const DXGI_PRESENT_PARAMETERS  *pPresentParameters);
  • SyncInterval задаёт синхронизацию отображения буфера с вертикальной развёрткой монитора. В случае отображения копированием его нулевое значение задаёт немедленное отображение, не дожидаясь вертикального гашения, а значения 1–4 — количество импульсов гашения, которых нужно дождаться перед выводом кадра.

    При «перекидном» отображении нуль означает, что время отображения предыдущего кадра надо отменить, а сам кадр — отбросить, поскольку для отображения поступил новый кадр; значения 1–4 означают предварительную синхронизацию с импульсами гашения. Например, предположим, что в очереди отображения присутствуют следующие кадры, начиная со самого старого и заканчивая последним сформированным и поставленным в очередь вызовом Present (в скобках указаны значения параметра SyncInterval для каждого кадра): A (3), B (0), C (0), D (1), E (0). Тогда DXGI, пропустив 3 импульса вертикального гашения, отобразит на один период развёртки кадр A (всего один период, поскольку у следующего кадра задано значение 0, т. е. немедленный вывод), затем, также на один период, отобразит кадр D (B и C будут отброшены, поскольку для них задано нулевое значение и в очереди уже присутствует следующий за ними кадр), а после него — кадр E, который будет отображаться до тех пор, пока не будет подготовлен и поставлен в очередь очередной кадр.

  • Flags — маска флагов, управляющая отображением буфера:

    • нулевое значение задаёт вывод кадров из всех буферов цепочки, начиная с текущего буфера (видимо, имеются в виду все буферы, кроме являющегося в настоящий момент первичным);

    • DXGI_PRESENT_DO_NOT_SEQUENCE — выдача текущего буфера, опираясь лишь на синхронизацию с вертикальной развёрткой, а не на обычный порядок буферов в цепочке. Если этот флаг установлен в самой первой операции отображения, т. е. когда текущего буфера ещё нет, отображение не производится;

    • DXGI_PRESENT_TEST — проверка состояния цепочки без вывода кадра; состояние отражается обычным кодом возврата. Этот флаг предназначен для использования при выходе из состояния бездействия; его нельзя применять для определения, когда переходить в бездействие, поскольку это может помешать цепочке выйти из полноэкранного режима;

    • DXGI_PRESENT_RESTART — ожидающие своего отображения кадры выбрасываются из очереди. Этот флаг можно использовать только с «перекидной» моделью вывода и полноэкранным режимом. Он может применяться для устранения «тормозов» при воспроизведении видео, а также при переходе в полноэкранный режим для отбрасывания уже поставленных в очередь, но ещё не отображённых кадров, сформированных для оконного режима, в котором они имели иной размер;

    • DXGI_PRESENT_DO_NOT_WAIT — если вызываемый поток блокирован, попытка выдачи кадра завершается ошибкой DXGI_ERROR_WAS_STILL_DRAWING вместо приостановки до разблокировки потока. Этот флаг появился в Windows 8;

    • DXGI_PRESENT_RESTRICT_TO_OUTPUT — указывает, что кадр должен выводиться лишь на определённый выход; если пользователь попробует переместить вывод на другой выход, кадр отображаться не будет. Этот флаг появился в Windows 8; его не следует использовать при выводе копированием, поскольку такой вывод может перестать работать в будущих версиях системы. Кроме того, его можно указывать лишь в случае, если цепочка создана с возможностью ограничения вывода;

    • DXGI_PRESENT_STEREO_PREFER_RIGHT — указывает, что, если вместо стереоизображения приходится выводить моно, следует предпочитать правый кадр. Этот флаг появился в Windows 8 и может применяться только со стереоцепочками;

    • DXGI_PRESENT_STEREO_TEMPORARY_MONO — левый буфер должен быть использован как единственный (проверить, поддерживается ли такая возможность, можно вызовом метода IDXGISwapChain1::IsTemporaryMonoSupported; попытка использовать этот флаг в случае, если он не поддерживается, приводит к ошибке). Этот флаг появился в Windows 8 и может применяться только со стереоцепочками. Он предназначен для временного подавления вывода стереоизображения (т. е. когда в данный момент его нет, но ожидается его появление; переключение между стерео- и моноцепочками в этом случае может оказаться нежелательным);

    • DXGI_PRESENT_USE_DURATION — этот флаг должен устанавливаться приложениями, использующими свою собственную продолжительность отображения (см. интерфейс IDXGISwapChainMedia). Этот флаг появился в Windows 8.1;

    • DXGI_PRESENT_ALLOW_TEARING — необходимо разрешить разрывы кадров для дисплеев с переменной частотой кадров. Этот флаг нужно использовать, если приложение работает в оконном режиме, параметр SyncInterval метода Present равен нулю, а цепочка создавалась с флагом DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING.

  • pPresentParameters — указатель на структуру типа DXGI_PRESENT_PARAMETERS, содержащую дополнительные параметры вывода отрендеренного кадра. В этой структуре имеются следующие поля:

    • DirtyRectsCount — количество прямоугольных областей во вторичном буфере, которые были обновлены и должны быть выведены. Если равно нулю, необходимо вывести весь буфер;

    • pDirtyRects — указатель на первый элемент массива, содержащего структуры типа RECT с координатами углов прямоугольника, подлежащего выводу. Перед вызовом Present1 приложение должно сформировать значение каждого пикселя в каждом из обновляемых прямоугольников; оно не должно надеяться на то, что значение пикселей с момента предыдущего отображения осталось в буфере неизменным. Это поле должно содержать nullptr, если поле DirtyRectsCount равно нулю;

    • pScrollRect — указатель на прямоугольник прокрутки, т. е. прямоугольный участок предыдущего кадра, содержимое которого должно быть скопировано в новый кадр. Этот прямоугольник задаёт также область текущего кадра, в которую будет осуществляться копирование. Если данный указатель равен nullptr, содержимое предыдущего кадра при отображении нового не используется;

    • pScrollOffset — указатель на описание смещения положения исходного прямоугольника (из предыдущего кадра) внутри нового кадра. Если равен nullptr, смещения нет.

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

Выводимый вторичный буфер (для Direct3D 10 и 11 это всегда буфер 0) отвязывается от графического конвейера, за исключением случая, когда был задан флаг DXGI_PRESENT_DO_NOT_SEQUENCE. Для рендеринга следующего кадра буфер необходимо опять привязать к конвейеру, причём рекомендуется делать это как можно позже, непосредственно перед тем, как будет запущен собственно рендеринг.

Если вызов метода вернул один из кодов DXGI_ERROR_DEVICE_REMOVED или D3DDDIERR_DEVICEREMOVED, это означает, что либо устройство было физически удалено, либо произошло обновление драйвера. В последнем случае для продолжения работы надо уничтожить и повторно создать все объекты, связанные с рендерингом.

Возврат кода DXGI_STATUS_OCCLUDED означает, что пользователь не видит сформированное изображение, поскольку оно перекрыто другим окном. В ответ приложение, как правило, должно приостановить рендеринг, чтобы не тратить напрасно ресурсы, и использовать флаг DXGI_PRESENT_TEST для проверки, не пора ли возобновить работу (об этом будет свидетельствовать возврат методом Present кода S_OK). Заметим, что при использовании «перекидной» модели данный код возвращён быть не может.

Ориентация экрана

Приложение и создаваемая им цепочка может не учитывать ориентацию экрана, на который осуществляется вывод; в этом случае DXGI при необходимости сама повернёт сформированное изображение в процессе его вывода. Однако в общем случае такой поворот требует дополнительных затрат, поэтому будет лучше, если приложение учитывает возможность поворота экрана и адаптируется к нему. Такая адаптация заключается как в изменении алгоритмов рендеринга (в обычных трёхмерных сценах достаточно поменять матрицу проекции), так и в характеристиках цепочки переключения буферов:

  • цепочка должна иметь размеры, соответствующие фактической ориентации дисплея (получить их для полноэкранного режима можно с помощью метода IDXGIOutput::GetDesc);

  • при создании или изменении параметров цепочки нужно задать флаг DXGI_SWAP_CHAIN_FLAG_NONPREROTATED, уведомляющий DXGI, что поворачивать изображение в кадре не требуется.

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

Методы, предоставляемые интерфейсами цепочки переключения буферов

Каждый последующий интерфейс цепочки наследует все методы предыдущей версии. Самый первый из интерфейсов является наследником интерфейса IDXGIDeviceSubObject, а тот, в свою очередь — наследником базового интерфейса объектов DXGI IDXGIObject.

IDXGISwapChain

Этот интерфейс доступен, начиная с Windows Vista.

IDXGISwapChain1

Этот интерфейс доступен, начиная с Windows 8; он поддерживается также в Windows 7 с Platform Update.

IDXGISwapChain2

Этот интерфейс доступен, начиная с Windows 8.1.

IDXGISwapChain3

Этот интерфейс доступен, начиная с Windows 10.

IDXGISwapChain4

Этот интерфейс доступен, начиная с Windows 10 (вероятно, с какого-то из ранних обновлений).

IDXGISwapChainMedia

Дополнительный интерфейс цепочки переключения, доступный, начиная с Windows 8.1. Он унаследован прямо от IUnknown и предназначен для обеспечения бесшовного изменения медиаприложениями частоты кадров.