DirectX 11.2 вошедший в выпуск Windows 8.1 насчитывает ряд интересных и нужных нововведений. В этом посте будет проведен краткий обзор основных новинок, а так же рассмотрены некоторые сценарии применения. Несмотря на то что нововведений не так много, некоторые из них будут весьма кстати при разработке приложений для мобильных устройств, и приложений для Windows Store.
Кратко о новинках.
Основная часть работ которые были проделаны с DirectX 11.2 связана в первую очередь с производительностью и эффективностью, и на прямую не коснется программистов. Ваши приложения будут быстрее работать и требовать меньше ресурсов. Тем не менее в Direct3D 11.2 API включено некоторое количество новых API:
- Поддержка аппаратных оверлеев: инструмент динамического масштабирования с интересными вариантами сценариев.
- Компиляция и линковка шейдеров HLSL в рантайме: возможность которая позволяет компоновать шейдеры во время выполнения, в том числе для приложений Windows Store.
- Отображаемые в память буфферы: Возможность которая позволяет убрать необходимость дополнительных операций копирования данных при обмене данными с GPU
- API снижения задержек ввода: Механизм который позволяет значительно снизить время задержки между пользовательским вводом и выводом результатов на экран.
- Тайловые ресурсы: Улучшение качества рендера с применением карт текстур.
Поддержка аппаратных оверлеев.
Одной из особенностей практически любого современного графического ускорителя является то что процедуры масштабирования графики являются очень дешевой операцией. В связи с этим возникает ряд сценариев которые интересно было бы использовать в случае если ощущается нехватка ресурсов или снижена скорость рендера.
Увеличить
Как уже понятно из картинки, аппаратный оверлей позволяет осуществлять рендер в буфер с низким разрешением, а затем увеличивать это изображение до необходимого размера и смешивать с дополнительными буферами через альфа-маску. Игра может отображать 3D сцену в первом оверлее с сниженным качеством, но при этом HUD или другие графические элементы приложения могут отображаться с высоким качеством.
При этом поддерживается два основных сценария применения аппаратных оверлеев — статический и динамический.
Статический оверлей.
Данный тип оверлея просто принимает уровень масштабирования при инициализации буфера и в дальнейшем не меняет своих значений. Для инициализации достаточно указать флаг DXGI_SCALING_STRETCH:
Автор статьи: Дмитрий Андреев.
DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0};
swapChainDesc.Width = screenWidth / 1.5f;
swapChainDesc.Height = screenHeight / 1.5f;
swapChainDesc.Scaling = DXGI_SCALING_STRETCH;
...
dxgiFactory->CreateSwapChainForCoreWindow(
m_d3dDevice.Get(),
reinterpret_cast<IUnknown*>(m_window.Get()),
&swapChainDesc,
nullptr,
&swapChain
);
Применимость этого метода ограничивается случаями, в которых вы уже знаете уровень масштабирования заранее.
Динамический оверлей.
Более интересный вариант, в котором уровень масштабирования может меняться «на лету», без повторной инициализации буферов (Swapchain). Вам достаточно вызвать функцию SetSourceSize перед каждым рендером:
DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0};
swapChainDesc.Width = screenWidth;
swapChainDesc.Height = screenHeight;
swapChainDesc.Scaling = DXGI_SCALING_STRETCH;
dxgiFactory->CreateSwapChainForCoreWindow( ... );
...
if (fps_low == true) {
swapChain->SetSourceSize(screenWidth * 0.8f, screenHeight * 0.8f);
}
// рендер.
...
swapChain->Present(1, 0);
Динамический оверлей позволяет, в зависимости от текущей нагрузки на аппаратные ресурсы, моментально менять качество картинки без ущерба к FPS. Порой даже 10% снижение разрешения конечного изображения может в разы ускорить процедуры рендера, что положительно скажется на динамичных нагруженных сценах. У игроков пропадет ощущение «тормозов» в случаях когда на экран выводится слишком много объектов.
Компиляция и линковка шейдеров.
Динамическая компиляция шейдеров — это весьма удобный инструмент оптимизации во время работы приложения. К сожалению, в Windows 8.0, для приложений Windows Store эта возможность была недоступна, и разработчикам необходимо было создавать бинарные блобы шейдеров заранее. С выходом Windows 8.1 эта возможность вернулась и для приложений Windows Store.
В дополнение к этому появилась опция компиляции шейдеров ‘lib_5_0', которая позволяет компоновать вычислительные блоки шейдеров и затем, во время исполнения программы, не компилировать шейдеры, а только собирать из готовых библиотек. Эта возможность позволяет значительно повысить время подключения шейдера и исключить дорогую операцию компиляции во время исполнения приложения.
Отображаемые в память буферы.
В Windows 8.0 обмен данными с GPU для вычислительных шейдеров требует использования вспомогательных буфферов. Это налагает некоторые издержки, и как раз таки для вычислительных шейдеров может стоить дорого.
Увеличить
Если вы используете Windows 8.1 и DirectX 11.2 у вас есть возможность убрать две вспомогательные операции воспользовавшись флагом CPU_ACCESS. Тогда картинка будет выглядеть следующим образом:
Увеличить
Тем самым можно достичь увеличения производительности для вычислительных шейдеров. Следует отметить что пока эта возможность работает только для буферов данных, но не для текстур (Texture1D/2D/3D). Во всяком случае у разработчика есть простой путь проверки и работы напрямую или же с помощью вспомогательного буфера:
D3D11_FEATURE_DATA_D3D11_OPTIONS1 featureOptions;
m_deviceResources->GetD3DDevice()->CheckFeatureSupport(
D3D11_FEATURE_D3D11_OPTIONS1,
&featureOptions,
sizeof(featureOptions)
);
...
If (featureOptions.MapDefaultBuffers) {
deviceContext->Map(defaultBuffer, ...);
} else {
deviceContext->CopyResource(stagingBuffer, defaultBuffer);
deviceContext->Map(stagingBuffer, ...);
}
API снижения задержек ввода
Время, между реакцией на ввод и реальным отображением результатов на экране, является критичным для многих приложений, а особенно игр. Если это время слишком велико, то у игрока появляется ощущение «тормозов» и дискомфорт. Оптимизация этого времени достаточно кропотливый процесс, но вместе с выходом DirectX 11.2 у программистов появился дополнительный механизм значительно облегчающий эту задачу. Появилось новое API IDXGISwapChain2::GetFrameLatencyWaitableObject которое позволяет получить WAIT HANDLE и в дальнейшем используя WaitForMultipleObjectEx дождаться самого удачного момента рендера:
DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0};
...
swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;
dxgiFactory->CreateSwapChainForCoreWindow( ... );
HANDLE frameLatencyWaitableObject = swapChain->GetFrameLatencyWaitableObject();
while (m_windowVisible)
{
WaitForSingleObjectEx(
frameLatencyWaitableObject,
INFINITE,
true
);
Render();
swapChain->Present(1, 0);
}
Например, результатом использования этого API может быть снижение времени задержки более чем вдвое на таких устройствах как Surface, с 46 миллисекунд до 20 миллисекунд.
Тайловые ресурсы
Увеличить
Современные игры требуют все большего и большего количества видео памяти, в том числе для текстур. От качества текстур и их разрешения напрямую зависит качество конечной картинки. Одним из методов оптимизации используемой видео памяти является механизм тайловых ресурсов Direct X 11.2 (Tiled resources). Чтобы понять о чем речь лучше посмотреть трехминутный ролик из пленарного доклада Build.
Ссылки и примеры
- Новое в C++/DirectX 11.2 разработке для Windows 8.1
- Тайловые ресурсы – доклад конфереции Build’13
- DirectX Foreground swapchain sample
- HLSL Shader Compiler sample
- DirectX latency sample
- Tiled resources sample