Сравнение кода EGL с DXGI и Direct3D

Важные API

Графический интерфейс DirectX (DXGI) и несколько API Direct3D служат той же ролью, что и EGL. Этот раздел поможет вам понять DXGI и Direct3D 11 с точки зрения EGL.

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

Обратите внимание , что это руководство основано на открытой спецификации Группы Хроноса для EGL 1.4, приведенной здесь: Хронос Native Platform Graphics Interface (EGL версии 1.4 – 6 апреля 2011 г.) [PDF]. Различия в синтаксисе, относящиеся к другим платформам и языкам разработки, не рассматриваются в этом руководстве.

 

Как сравниваются DXGI и Direct3D?

Большое преимущество EGL над DXGI и Direct3D заключается в том, что относительно просто начать рисование на поверхности окна. Это связано с тем, что OpenGL ES 2.0 (и, следовательно, EGL) — это спецификация, реализованная несколькими поставщиками платформ, в то время как DXGI и Direct3D являются одной ссылкой на то, что драйверы поставщиков оборудования должны соответствовать требованиям. Это означает, что корпорация Майкрософт должна реализовать набор API,которые позволяют максимально широкому набору функций поставщика, а не сосредоточиться на функциональном подмножестве, предлагаемом определенным поставщиком, или объединить команды установки конкретного поставщика в более простые API. С другой стороны, Direct3D предоставляет единый набор API, охватывающий очень широкий спектр графических аппаратных платформ и уровней функций, и обеспечивает большую гибкость для разработчиков, опытных с платформой.

Например, EGL, DXGI и Direct3D предоставляют API для следующих действий:

  • Получение и чтение и запись в буфер кадра (называется "цепочкой буферов" в DXGI).
  • Связывание буфера кадра с окном пользовательского интерфейса.
  • Получение и настройка контекстов отрисовки, в которых необходимо нарисовать.
  • Выдача команд в графический конвейер для определенного контекста отрисовки.
  • Создание ресурсов шейдера и управление ими и связывание их с содержимым отрисовки.
  • Отрисовка для определенных целевых объектов отрисовки (например, текстур).
  • Обновление области отображения окна с результатами отрисовки с помощью графических ресурсов.

Чтобы просмотреть базовый процесс Direct3D для настройки графического конвейера, ознакомьтесь с шаблоном приложения DirectX 11 (универсальная версия Windows) в Microsoft Visual Studio 2015. Базовый класс отрисовки в нем предоставляет хороший базовый план для настройки графической инфраструктуры Direct3D 11 и настройки базовых ресурсов на нем, а также поддержки функций приложения универсальная платформа Windows (UWP), таких как поворот экрана.

EGL имеет очень мало API относительно Direct3D 11, и навигация по последнему может быть проблемой, если вы не знакомы с именованием и jargon конкретной платформы. Вот простой обзор, который поможет вам получить ориентацию.

Сначала просмотрите базовый объект EGL для сопоставления интерфейса Direct3D:

Абстракция EGL Аналогичное представление Direct3D
EGLDisplay В Direct3D (для приложений UWP) дескриптор отображения получается через API Windows::UI::CoreWindow (или интерфейс ICoreWindowInterop, предоставляющий HWND). Адаптер и конфигурация оборудования задаются с помощью интерфейсов COM IDXGIAdapter и IDXGIDevice1 соответственно.
EGLSurface В Direct3D буферы и другие ресурсы окна (видимые или внеэкранные) создаются и настраиваются определенными интерфейсами DXGI, включая IDXGIFactory2 (реализация шаблона фабрики, используемая для получения ресурсов DXGI, таких какIDXGISwapChain1 (буферы отображения). Идентификатор ID3D11Device1, представляющий графическое устройство и его ресурсы, получается с помощью D3D11Device::CreateDevice. Для целевых объектов отрисовки используйте интерфейс ID3D11RenderTargetView .
EGLContext В Direct3D вы настраиваете и выдаете команды в графическом конвейере с интерфейсом ID3D11DeviceContext1 .
EGLConfig В Direct3D 11 вы создаете и настраиваете графические ресурсы, такие как буферы, текстуры, наборы элементов и шейдеры с методами в интерфейсе ID3D11Device1 .

 

Теперь вот самый простой процесс настройки простого графического отображения, ресурсов и контекста в DXGI и Direct3D для приложения UWP.

  1. Получите дескриптор объекта CoreWindow для основного потока пользовательского интерфейса приложения, вызвав CoreWindow::GetForCurrentThread.
  2. Для приложений UWP получите цепочку буферов из IDXGIAdapter2 с IDXGIFactory2::CreateSwapChainForCoreWindow и передайте ее ссылку CoreWindow, полученную на шаге 1. Вы получите экземпляр IDXGISwapChain1. Область действия объекта отрисовщика и его потока отрисовки.
  3. Получение экземпляров ID3D11Device1 и ID3D11DeviceContext1 путем вызова метода D3D11Device::CreateDevice. Также обведите их область для объекта отрисовщика.
  4. Создайте шейдеры, текстуры и другие ресурсы с помощью методов в объекте ID3D11Device1 отрисовщика.
  5. Определите буферы, шейдеры запуска и управляйте этапами конвейера с помощью методов в объекте ID3D11DeviceContext1 средства отрисовки.
  6. Когда конвейер выполнен и кадр рисуется в буфер обратной части, доведите его на экран с идентификатором IDXGISwapChain1::P resent1.

Дополнительные сведения об этом процессе см. в статье "Начало работы с графикой DirectX". В остальной части этой статьи рассматриваются многие распространенные шаги по настройке и управлению графическим конвейером.

Обратите внимание, что классические приложения Windows имеют разные API для получения цепочки буферов Direct3D, например D3D11Device::CreateDeviceAndSwapChain, и не используйте объект CoreWindow.

 

Получение окна для отображения

В этом примере eglGetDisplay передает HWND для ресурса окна, относяющегося к платформе Microsoft Windows. Другие платформы, такие как iOS Apple (Какао) и Android Google, имеют разные дескриптора или ссылки на ресурсы окна, и могут иметь другой синтаксис вызова в целом. После получения дисплея вы инициализируете его, задайте предпочтительную конфигурацию и создадите поверхность с буфером задней части, в который можно нарисовать.

Получение дисплея и его настройка с помощью EGL..

// Obtain an EGL display object.
EGLDisplay display = eglGetDisplay(GetDC(hWnd));
if (display == EGL_NO_DISPLAY)
{
  return EGL_FALSE;
}

// Initialize the display
if (!eglInitialize(display, &majorVersion, &minorVersion))
{
  return EGL_FALSE;
}

// Obtain the display configs
if (!eglGetConfigs(display, NULL, 0, &numConfigs))
{
  return EGL_FALSE;
}

// Choose the display config
if (!eglChooseConfig(display, attribList, &config, 1, &numConfigs))
{
  return EGL_FALSE;
}

// Create a surface
surface = eglCreateWindowSurface(display, config, (EGLNativeWindowType)hWnd, NULL);
if (surface == EGL_NO_SURFACE)
{
  return EGL_FALSE;
}

В Direct3D основное окно приложения UWP представлено объектом CoreWindow, который можно получить из объекта приложения, вызвав CoreWindow::GetForCurrentThread в рамках процесса инициализации поставщика представления, созданного для Direct3D. (Если вы используете взаимодействие Direct3D-XAML, используйте поставщик представлений платформы XAML.) Процесс создания поставщика представления Direct3D рассматривается в статье "Настройка приложения для отображения представления".

Получение CoreWindow для Direct3D.

CoreWindow::GetForCurrentThread();

После получения ссылки CoreWindow окно должно быть активировано, которое выполняет метод Run основного объекта и начинает обработку событий окна. После этого создайте идентификатор ID3D11Device1 и ID3D11DeviceContext1 и используйте их для получения базового объекта IDXGIDevice1 и IDXGIAdapter, чтобы получить объект IDXGIFactory2 для создания ресурса цепочки буферов на основе конфигурации DXGI_SWAP_CHAIN_DESC1.

Настройка и настройка цепочки буферов DXGI в CoreWindow для Direct3D.

// Called when the CoreWindow object is created (or re-created).
void SimpleDirect3DApp::SetWindow(CoreWindow^ window)
{
  // Register event handlers with the CoreWindow object.
  // ...

  // Obtain your ID3D11Device1 and ID3D11DeviceContext1 objects
  // In this example, m_d3dDevice contains the scoped ID3D11Device1 object
  // ...

  ComPtr<IDXGIDevice1>  dxgiDevice;
  // Get the underlying DXGI device of the Direct3D device.
  m_d3dDevice.As(&dxgiDevice);

  ComPtr<IDXGIAdapter> dxgiAdapter;
  dxgiDevice->GetAdapter(&dxgiAdapter);

  ComPtr<IDXGIFactory2> dxgiFactory;
  dxgiAdapter->GetParent(
    __uuidof(IDXGIFactory2), 
    &dxgiFactory);

  DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0};
  swapChainDesc.Width = static_cast<UINT>(m_d3dRenderTargetSize.Width); // Match the size of the window.
  swapChainDesc.Height = static_cast<UINT>(m_d3dRenderTargetSize.Height);
  swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // This is the most common swap chain format.
  swapChainDesc.Stereo = false;
  swapChainDesc.SampleDesc.Count = 1; // Don't use multi-sampling.
  swapChainDesc.SampleDesc.Quality = 0;
  swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
  swapChainDesc.BufferCount = 2; // Use double-buffering to minimize latency.
  swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // All UWP apps must use this SwapEffect.
  swapChainDesc.Flags = 0;

  // ...

  Windows::UI::Core::CoreWindow^ window = m_window.Get();
  dxgiFactory->CreateSwapChainForCoreWindow(
    m_d3dDevice.Get(),
    reinterpret_cast<IUnknown*>(window),
    &swapChainDesc,
    nullptr, // Allow on all displays.
    &m_swapChainCoreWindow);
}

Вызовите метод IDXGISwapChain1::P resent1 после подготовки кадра для отображения.

Обратите внимание, что в Direct3D 11 нет абстракции, идентичной EGLSurface. (Существует IDXGISurface1, но используется по-другому.) Ближайшее концептуальное приближение — это объект ID3D11RenderTargetView, который мы используем для назначения текстуры (ID3D11Texture2D) в качестве обратного буфера, в который будет рисовать конвейер шейдеров.

Настройка резервного буфера для цепочки буферов в Direct3D 11

ComPtr<ID3D11RenderTargetView>    m_d3dRenderTargetViewWin; // scoped to renderer object

// ...

ComPtr<ID3D11Texture2D> backBuffer2;
    
m_swapChainCoreWindow->GetBuffer(0, IID_PPV_ARGS(&backBuffer2));

m_d3dDevice->CreateRenderTargetView(
  backBuffer2.Get(),
  nullptr,
    &m_d3dRenderTargetViewWin);

Рекомендуется вызывать этот код всякий раз, когда окно создается или изменяет размер. Во время отрисовки задайте целевое представление отрисовки с идентификатором ID3D11DeviceContext1::OMSetRenderTargets перед настройкой других подресурсов, таких как буферы вершин или шейдеры.

// Set the render target for the draw operation.
m_d3dContext->OMSetRenderTargets(
        1,
        d3dRenderTargetView.GetAddressOf(),
        nullptr);

Создание контекста отрисовки

В EGL 1.4 "display" представляет набор ресурсов окна. Как правило, вы настраиваете "surface" для отображения, предоставляя набор атрибутов отображаемого объекта и получая поверхность в обратном виде. Вы создаете контекст для отображения содержимого поверхности путем создания этого контекста и привязки его к поверхности и отображению.

Поток вызовов обычно выглядит примерно так:

  • Вызовите eglGetDisplay с дескриптором к ресурсу отображения или окна и получите отображаемый объект.
  • Инициализировать отображение с помощью eglInitialize.
  • Получите доступную конфигурацию отображения и выберите ее с помощью eglGetConfigs и eglChooseConfig.
  • Создайте поверхность окна с помощью eglCreateWindowSurface.
  • Создайте контекст отображения для рисования с помощью eglCreateContext.
  • Привязка контекста отображения к экрану и поверхности с помощью eglMakeCurrent.

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

Получение контекста отрисовки с помощью EGL 1.4

// Configure your EGLDisplay and obtain an EGLSurface here ...
// ...

// Create a drawing context from the EGLDisplay
context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs);
if (context == EGL_NO_CONTEXT)
{
  return EGL_FALSE;
}   
   
// Make the context current
if (!eglMakeCurrent(display, surface, surface, context))
{
  return EGL_FALSE;
}

Контекст отрисовки в Direct3D 11 представлен объектом ID3D11Device1, который представляет адаптер и позволяет создавать ресурсы Direct3D, такие как буферы и шейдеры; и объектом ID3D11DeviceContext1, который позволяет управлять графическим конвейером и выполнять шейдеры.

Помните о уровнях функций Direct3D! Они используются для поддержки старых аппаратных платформ Direct3D с DirectX 9.1 до DirectX 11. Многие платформы, использующие оборудование с низкой мощностью графики, такие как планшеты, имеют доступ только к функциям DirectX 9.1, а более старое поддерживаемое графическое оборудование может быть от 9.1 до 11.

Создание контекста отрисовки с помощью DXGI и Direct3D


// ... 

UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
ComPtr<IDXGIDevice> dxgiDevice;

D3D_FEATURE_LEVEL featureLevels[] = 
{
        D3D_FEATURE_LEVEL_11_1,
        D3D_FEATURE_LEVEL_11_0,
        D3D_FEATURE_LEVEL_10_1,
        D3D_FEATURE_LEVEL_10_0,
        D3D_FEATURE_LEVEL_9_3,
        D3D_FEATURE_LEVEL_9_2,
        D3D_FEATURE_LEVEL_9_1
};

// Create the Direct3D 11 API device object and a corresponding context.
ComPtr<ID3D11Device> device;
ComPtr<ID3D11DeviceContext> d3dContext;

D3D11CreateDevice(
  nullptr, // Specify nullptr to use the default adapter.
  D3D_DRIVER_TYPE_HARDWARE,
  nullptr,
  creationFlags, // Set debug and Direct2D compatibility flags.
  featureLevels, // List of feature levels this app can support.
  ARRAYSIZE(featureLevels),
  D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for UWP apps.
  &device, // Returns the Direct3D device created.
  &m_featureLevel, // Returns feature level of device created.
  &d3dContext // Returns the device immediate context.
);

Рисование в ресурс текстуры или pixmap

Чтобы нарисовать текстуру с помощью OpenGL ES 2.0, настройте буфер пикселей или PBuffer. После успешной настройки и создания EGLSurface для него можно указать контекст отрисовки и выполнить конвейер шейдера для рисования в текстуру.

Рисование в буфер пикселей с помощью OpenGL ES 2.0

// Create a pixel buffer surface to draw into
EGLConfig pBufConfig;
EGLint totalpBufAttrs;

const EGLint pBufConfigAttrs[] =
{
    // Configure the pBuffer here...
};
 
eglChooseConfig(eglDsplay, pBufConfigAttrs, &pBufConfig, 1, &totalpBufAttrs);
EGLSurface pBuffer = eglCreatePbufferSurface(eglDisplay, pBufConfig, EGL_TEXTURE_RGBA); 

В Direct3D 11 вы создаете ресурс ID3D11Texture2D и делаете его целевым объектом отрисовки. Настройте целевой объект отрисовки с помощью D3D11_RENDER_TARGET_VIEW_DESC. При вызове метода ID3D11DeviceContext::D raw (или аналогичной операции Draw* в контексте устройства) с помощью этого целевого объекта отрисовки результаты извлекаются в текстуру.

Рисование в текстуру с помощью Direct3D 11

ComPtr<ID3D11Texture2D> renderTarget1;

D3D11_RENDER_TARGET_VIEW_DESC renderTargetDesc = {0};
// Configure renderTargetDesc here ...

m_d3dDevice->CreateRenderTargetView(
  renderTarget1.Get(),
  nullptr,
  &m_d3dRenderTargetViewWin);

// Later, in your render loop...

// Set the render target for the draw operation.
m_d3dContext->OMSetRenderTargets(
        1,
        d3dRenderTargetView.GetAddressOf(),
        nullptr);

Эта текстура может быть передана шейдеру, если она связана с ID3D11ShaderResourceView.

Рисование на экране

После использования EGLContext для настройки буферов и обновления данных выполните шейдеры, привязанные к нему, и нарисуйте результаты обратному буферу с помощью glDrawElements. Вы отображаете задний буфер путем вызова eglSwapBuffers.

Откройте GL ES 2.0: рисование на экране.

glDrawElements(GL_TRIANGLES, renderer->numIndices, GL_UNSIGNED_INT, 0);

eglSwapBuffers(drawContext->eglDisplay, drawContext->eglSurface);

В Direct3D 11 вы настраиваете буферы и привязываете шейдеры с идентификатором IDXGISwapChain::P resent1. Затем вы вызываете один из методов ID3D11DeviceContext1::D raw* для запуска шейдеров и рисования результатов в целевой объект отрисовки, настроенный в качестве обратного буфера для цепочки буферов. После этого вы просто представляете задний буфер для отображения путем вызова IDXGISwapChain::P resent1.

Direct3D 11: рисование на экране.


m_d3dContext->DrawIndexed(
        m_indexCount,
        0,
        0);

// ...

m_swapChainCoreWindow->Present1(1, 0, &parameters);

Освобождение графических ресурсов

В EGL вы освобождаете ресурсы окна, передав EGLDisplay в eglTerminate.

Завершение отображения с помощью EGL 1.4

EGLBoolean eglTerminate(eglDisplay);

В приложении UWP можно закрыть CoreWindow с CoreWindow::Close, хотя это можно использовать только для дополнительных окон пользовательского интерфейса. Основной поток пользовательского интерфейса и связанный с ним CoreWindow не может быть закрыт; скорее, срок действия истек операционной системой. Однако при закрытии вторичного CoreWindow событие CoreWindow::Closed вызывается.

Сопоставление справочника по API для EGL с Direct3D 11

EGL API Аналогичный API Direct3D 11 или поведение
eglBindAPI Недоступно
eglBindTexImage Вызовите ID3D11Device::CreateTexture2D , чтобы задать трехмерную текстуру.
eglChooseConfig Direct3D не предоставляет набор конфигураций буфера кадров по умолчанию. Конфигурация цепочки буферов
eglCopyBuffers Чтобы скопировать данные буфера, вызовите ID3D11DeviceContext::CopyStructureCount. Чтобы скопировать ресурс, вызовите ID3DDeviceCOntext::CopyResource.
eglCreateContext Создайте контекст устройства Direct3D, вызвав D3D11CreateDevice, который возвращает дескриптор устройству Direct3D и контекст direct3D по умолчанию (объект ID3D11DeviceContext1). Вы также можете создать отложенный контекст Direct3D, вызвав id3D11Device2::CreateDeferredContext для возвращаемого объекта ID3D11Device1.
eglCreatePbufferFromClientBuffer Все буферы считываются и записываются в виде подресурса Direct3D, например ID3D11Texture2D. Скопируйте из одного в другой совместимый подресурс тип с такими методами, как ID3D11DeviceContext1:CopyResource.
eglCreatePbufferSurface Чтобы создать устройство Direct3D без цепочки буферов, вызовите статический метод D3D11CreateDevice . Для целевого представления отрисовки Direct3D вызовите id3D11Device::CreateRenderTargetView.
eglCreatePixmapSurface Чтобы создать устройство Direct3D без цепочки буферов, вызовите статический метод D3D11CreateDevice . Для целевого представления отрисовки Direct3D вызовите id3D11Device::CreateRenderTargetView.
eglCreateWindowSurface Ontain an IDXGISwapChain1 (для буферов отображения) и ID3D11Device1 (виртуальный интерфейс для графического устройства и его ресурсов). Используйте id3D11Device1 для определения идентификатора ID3D11RenderTargetView, который можно использовать для создания буфера кадров, который вы предоставляете IDXGISwapChain1.
eglDetextContext Недоступно Используйте ID3D11DeviceContext::D iscardView1 , чтобы избавиться от целевого представления отрисовки. Чтобы закрыть родительский идентификатор 3D11DeviceContext1, задайте экземпляру значение NULL и дождитесь освобождения ресурсов платформы. Вы не можете напрямую уничтожить контекст устройства.
eglDefcSurface Недоступно Графические ресурсы очищаются при закрытии платформой приложения UWP CoreWindow.
eglGetCurrentDisplay Вызов CoreWindow::GetForCurrentThread , чтобы получить ссылку на текущее главное окно приложения.
eglGetCurrentSurface Это текущий идентификатор ID3D11RenderTargetView. Как правило, это ограничивается объектом отрисовщика.
eglGetError Ошибки получаются в виде HRESULTs, возвращаемых большинством методов в интерфейсах DirectX. Если метод не возвращает HRESULT, вызовите GetLastError. Чтобы преобразовать системную ошибку в значение HRESULT, используйте макрос HRESULT_FROM_WIN32 .
eglInitialize Вызов CoreWindow::GetForCurrentThread , чтобы получить ссылку на текущее главное окно приложения.
eglMakeCurrent Задайте целевой объект отрисовки для рисования текущего контекста с идентификатором ID3D11DeviceContext1::OMSetRenderTargets.
eglQueryContext Недоступно Однако вы можете получить целевые объекты отрисовки из экземпляра ID3D11Device1 , а также некоторые данные конфигурации. (См. ссылку для списка доступных методов.)
eglQuerySurface Недоступно Однако вы можете получить данные о портах просмотра и текущем графическом оборудовании из методов экземпляра ID3D11Device1 . (См. ссылку для списка доступных методов.)
eglReleaseTexImage Недоступно
eglReleaseThread Для общего многопоточного графического процессора считывает многопоточность.
eglSurfaceAttrib Используйте D3D11_RENDER_TARGET_VIEW_DESC для настройки целевого представления отрисовки Direct3D,
eglSwapBuffers Используйте IDXGISwapChain1::P resent1.
eglSwapInterval См. раздел IDXGISwapChain1.
eglTerminate CoreWindow, используемый для отображения выходных данных графического конвейера, управляется операционной системой.
eglWaitClient Для общих поверхностей используйте IDXGIKeyedMutex. Для общего многопоточного графического процессора считывает многопоточность.
eglWaitGL Для общих поверхностей используйте IDXGIKeyedMutex. Для общего многопоточного графического процессора считывает многопоточность.
eglWaitNative Для общих поверхностей используйте IDXGIKeyedMutex. Для общего многопоточного графического процессора считывает многопоточность.