Confrontare il codice EGL con DXGI e Direct3D

API importanti

DirectX Graphics Interface (DXGI) e diverse API Direct3D svolgono lo stesso ruolo di EGL. Questo argomento consente di comprendere DXGI e Direct3D 11 dal punto di vista di EGL.

DXGI e Direct3D, come EGL, forniscono metodi per configurare le risorse grafiche, ottenere un contesto di rendering per gli shader da disegnare e visualizzare i risultati in una finestra. Tuttavia, DXGI e Direct3D hanno alcune altre opzioni e richiedono più sforzi per configurare correttamente durante la conversione da EGL.

Nota Questa guida è basata sulla specifica aperta del gruppo Khronos per EGL 1.4, disponibile qui: Khronos Native Platform Graphics Interface (EGL Versione 1.4 - 6 aprile 2011) [PDF]. Le differenze nella sintassi specifiche per altre piattaforme e linguaggi di sviluppo non sono descritte in queste linee guida.

 

Come confronta DXGI e Direct3D?

Il grande vantaggio di EGL rispetto a DXGI e Direct3D è che è relativamente semplice iniziare a disegnare in una superficie di finestra. Ciò è dovuto al fatto che OpenGL ES 2.0, e quindi EGL, è una specifica implementata da più provider di piattaforme, mentre DXGI e Direct3D sono un singolo riferimento a cui i driver del fornitore di hardware devono essere conformi. Ciò significa che Microsoft deve implementare un set di API che consentano il set più ampio possibile di funzionalità del fornitore, invece di concentrarsi su un subset funzionale offerto da un fornitore specifico o combinando comandi di installazione specifici del fornitore in API più semplici. D'altra parte, Direct3D offre un unico set di API che coprono un'ampia gamma di piattaforme hardware grafiche e livelli di funzionalità e offre maggiore flessibilità per gli sviluppatori esperti con la piattaforma.

Come EGL, DXGI e Direct3D forniscono API per i comportamenti seguenti:

  • Ottenere e leggere e scrivere in un buffer di frame (chiamato "catena di scambio" in DXGI).
  • Associazione del buffer dei fotogrammi a una finestra dell'interfaccia utente.
  • Ottenere e configurare i contesti di rendering in cui disegnare.
  • Esecuzione di comandi alla pipeline grafica per un contesto di rendering specifico.
  • Creazione e gestione delle risorse dello shader e associazione a un contenuto di rendering.
  • Rendering in destinazioni di rendering specifiche, ad esempio trame.
  • Aggiornamento della superficie di visualizzazione della finestra con i risultati del rendering con le risorse grafiche.

Per visualizzare il processo Direct3D di base per la configurazione della pipeline grafica, vedere il modello App DirectX 11 (Windows universale) in Microsoft Visual Studio 2015. La classe di rendering di base in esso fornisce una buona linea di base per configurare l'infrastruttura grafica Direct3D 11 e configurare le risorse di base su di esso, oltre a supportare le funzionalità dell'app piattaforma UWP (Universal Windows Platform) (UWP), ad esempio la rotazione dello schermo.

EGL ha pochissime API relative a Direct3D 11 e lo spostamento in quest'ultimo può essere una sfida se non si ha familiarità con la denominazione e il gergo particolare per la piattaforma. Ecco una semplice panoramica che consente di orientarsi.

Esaminare prima di tutto l'oggetto EGL di base con il mapping dell'interfaccia Direct3D:

Astrazione EGL Rappresentazione Direct3D simile
EGLDisplay In Direct3D (per le app UWP), l'handle di visualizzazione viene ottenuto tramite l'API Windows::UI::CoreWindow (o l'interfaccia ICoreWindowInterop che espone HWND). La configurazione dell'adattatore e dell'hardware viene impostata rispettivamente con le interfacce COM IDXGIAdapter e IDXGIDevice1.
EGLSurface In Direct3D, i buffer e altre risorse finestra (visibili o fuori schermo) vengono creati e configurati da interfacce DXGI specifiche, tra cui IDXGIFactory2 (implementazione del modello di fabbrica usata per acquisire risorse DXGI, adesempio IDXGISwapChain1 (buffer di visualizzazione). ID3D11Device1 che rappresenta il dispositivo grafico e le relative risorse, viene acquisito con D3D11Device::CreateDevice. Per le destinazioni di rendering, usare l'interfaccia ID3D11RenderTargetView .
EGLContext In Direct3D si configurano ed emettono comandi per la pipeline grafica con l'interfaccia ID3D11DeviceContext1 .
EGLConfig In Direct3D 11 si creano e si configurano risorse grafiche come buffer, trame, stencil e shader con metodi nell'interfaccia ID3D11Device1 .

 

Ecco ora il processo più semplice per configurare una semplice visualizzazione grafica, risorse e contesto in DXGI e Direct3D per un'app UWP.

  1. Ottenere un handle per l'oggetto CoreWindow per il thread principale dell'interfaccia utente dell'app chiamando CoreWindow::GetForCurrentThread.
  2. Per le app UWP, acquisire una catena di scambio da IDXGIAdapter2 con IDXGIFactory2::CreateSwapChainForCoreWindow e passarvi il riferimento CoreWindow ottenuto nel passaggio 1. Si otterrà un'istanza di IDXGISwapChain1 in cambio. Definire l'ambito per l'oggetto renderer e il relativo thread di rendering.
  3. Ottenere istanze ID3D11Device1 and ID3D11DeviceContext1 chiamando il metodo D3D11Device::CreateDevice. Definire anche l'ambito dell'oggetto renderer.
  4. Creare shader, trame e altre risorse usando metodi nell'oggetto ID3D11Device1 del render.
  5. Definire buffer, eseguire shader e gestire le fasi della pipeline usando i metodi nell'oggetto ID3D11DeviceContext1 del render.
  6. Quando la pipeline è stata eseguita e un frame viene disegnato nel buffer nascosto, presentarlo sullo schermo con IDXGISwapChain1::Present1.

Per esaminare questo processo in modo più dettagliato, vedere Introduzione alla grafica DirectX. Il resto di questo articolo illustra molti dei passaggi comuni per la configurazione e la gestione della pipeline grafica di base.

Nota Le app desktop di Windows hanno API diverse per ottenere una catena di scambio Direct3D, ad esempio D3D11Device::CreateDeviceAndSwapChain e non usano un oggetto CoreWindow .

 

Recupero di una finestra per la visualizzazione

In questo esempio, eglGetDisplay viene passato un HWND per una risorsa finestra specifica della piattaforma Microsoft Windows. Altre piattaforme, ad esempio iOS (Cocoa) di Apple e Android di Google, hanno handle o riferimenti diversi alle risorse finestra e possono avere una sintassi chiamante diversa. Dopo aver ottenuto uno schermo, inizializzarlo, impostare la configurazione preferita e creare una superficie con un buffer nascosto in cui è possibile disegnare.

Ottenere una visualizzazione e configurarla con 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;
}

In Direct3D la finestra principale di un'app UWP è rappresentata dall'oggetto CoreWindow, che può essere ottenuto dall'oggetto app chiamando CoreWindow::GetForCurrentThread come parte del processo di inizializzazione del "provider di visualizzazioni" creato per Direct3D. Se usi l'interoperabilità Direct3D-XAML, usa il provider di visualizzazione del framework XAML. Il processo di creazione di un provider di visualizzazioni Direct3D è illustrato in Come configurare l'app per visualizzare una visualizzazione.

Recupero di un coreWindow per Direct3D.

CoreWindow::GetForCurrentThread();

Dopo aver ottenuto il riferimento CoreWindow, la finestra deve essere attivata, che esegue il metodo Run dell'oggetto principale e avvia l'elaborazione dell'evento finestra. Successivamente, creare un ID3D11Device1 e un ID3D11DeviceContext1 e usarli per ottenere l'IDXGIDevice1 e IDXGIAdapter sottostante in modo da poter ottenere un oggetto IDXGIFactory2 per creare una risorsa della catena di scambio in base alla configurazione DXGI_SWAP_CHAIN_DESC1.

Configurazione e impostazione della catena di scambio DXGI in CoreWindow per 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);
}

Chiamare il metodo IDXGISwapChain1::P resent1 dopo aver preparato un frame per visualizzarlo.

Si noti che in Direct3D 11 non esiste un'astrazione identica a EGLSurface. (C'è IDXGISurface1, ma viene usato in modo diverso. L'approssimazione concettuale più vicina è l'oggetto ID3D11RenderTargetView usato per assegnare una trama (ID3D11Texture2D) come buffer nascosto in cui verrà disegnare la pipeline dello shader.

Configurazione del buffer di backup per la catena di scambio in 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);

È consigliabile chiamare questo codice ogni volta che viene creata o modificata la dimensione della finestra. Durante il rendering, impostare la visualizzazione di destinazione di rendering con ID3D11DeviceContext1::OMSetRenderTargets prima di configurare qualsiasi altra sottorisorsa come vertex buffer o shader.

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

Creazione di un contesto di rendering

In EGL 1.4 un "display" rappresenta un set di risorse della finestra. In genere, si configura una "superficie" per la visualizzazione fornendo un set di attributi all'oggetto display e ottenendo una superficie in cambio. Si crea un contesto per visualizzare il contenuto della superficie creando tale contesto e associandolo alla superficie e allo schermo.

Il flusso di chiamata è in genere simile al seguente:

  • Chiamare eglGetDisplay con l'handle per una risorsa di visualizzazione o finestra e ottenere un oggetto visualizzato.
  • Inizializzare la visualizzazione con eglInitialize.
  • Ottenere la configurazione di visualizzazione disponibile e selezionare una con eglGetConfigs e eglChooseConfig.
  • Creare una superficie di finestra con eglCreateWindowSurface.
  • Creare un contesto di visualizzazione per il disegno con eglCreateContext.
  • Associare il contesto di visualizzazione alla visualizzazione e alla superficie con eglMakeCurrent.

n la sezione precedente, abbiamo creato EGLDisplay e EGLSurface, e ora usiamo EGLDisplay per creare un contesto e associare tale contesto alla visualizzazione, usando L'EGLSurface configurato per parametrizzare l'output.

Recupero di un contesto di rendering con 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;
}

Un contesto di rendering in Direct3D 11 è rappresentato da un oggetto ID3D11Device1 , che rappresenta l'adattatore e consente di creare risorse Direct3D, ad esempio buffer e shader, e dall'oggetto ID3D11DeviceContext1 , che consente di gestire la pipeline grafica ed eseguire gli shader.

Tieni presente i livelli di funzionalità Direct3D! Questi vengono usati per supportare le piattaforme hardware Direct3D precedenti, da DirectX 9.1 a DirectX 11. Molte piattaforme che usano hardware grafico a basso consumo, ad esempio tablet, hanno accesso solo alle funzionalità directX 9.1 e l'hardware grafico supportato meno recente potrebbe essere compreso tra 9,1 e 11.

Creazione di un contesto di rendering con DXGI e 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.
);

Disegno in una risorsa trama o pixmap

Per disegnare in una trama con OpenGL ES 2.0, configurare un buffer di pixel o PBuffer. Dopo aver configurato e creato correttamente un oggetto EGLSurface, è possibile specificarlo con un contesto di rendering ed eseguire la pipeline dello shader per disegnare nella trama.

Disegnare in un buffer di pixel con 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); 

In Direct3D 11 si crea una risorsa ID3D11Texture2D e si crea una destinazione di rendering. Configurare la destinazione di rendering usando D3D11_RENDER_TARGET_VIEW_DESC. Quando chiami il metodo ID3D11DeviceContext::D raw (o un'operazione Draw* simile nel contesto del dispositivo) usando questa destinazione di rendering, i risultati vengono disegnati in una trama.

Disegnare in una trama con 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);

Questa trama può essere passata a uno shader se è associata a un ID3D11ShaderResourceView.

Disegnare sullo schermo

Dopo aver usato EGLContext per configurare i buffer e aggiornare i dati, eseguire gli shader associati e disegnare i risultati nel buffer nascosto con glDrawElements. Il buffer nascosto viene visualizzato chiamando eglSwapBuffers.

Apri GL ES 2.0: Disegno sullo schermo.

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

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

In Direct3D 11 si configurano i buffer e si associano shader con IDXGISwapChain::Present1. Chiama quindi uno dei metodi ID3D11DeviceContext1::D raw* per eseguire gli shader e disegnare i risultati in una destinazione di rendering configurata come buffer nascosto per la catena di scambio. Successivamente, è sufficiente presentare il buffer nascosto alla visualizzazione chiamando IDXGISwapChain::P resent1.

Direct3D 11: Disegno sullo schermo.


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

// ...

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

Rilascio di risorse grafiche

In EGL le risorse della finestra vengono rilasciate passando EGLDisplay a eglTerminate.

Terminazione di un display con EGL 1.4

EGLBoolean eglTerminate(eglDisplay);

In un'app UWP puoi chiudere CoreWindow con CoreWindow::Close, anche se può essere usato solo per le finestre dell'interfaccia utente secondarie. Non è possibile chiudere il thread principale dell'interfaccia utente e il relativo coreWindow associato; piuttosto, sono scaduti dal sistema operativo. Tuttavia, quando un CoreWindow secondario viene chiuso, viene generato l'evento CoreWindow::Closed .

Mapping di riferimento API per EGL a Direct3D 11

EGL API Comportamento o API Direct3D 11 simile
eglBindAPI N/D.
eglBindTexImage Chiamare ID3D11Device::CreateTexture2D per impostare una trama 2D.
eglChooseConfig Direct3D non fornisce un set di configurazioni predefinite del buffer dei frame. Configurazione della catena di scambio
eglCopyBuffers Per copiare dati del buffer, chiamare ID3D11DeviceContext::CopyStructureCount. Per copiare una risorsa, chiamare ID3DDeviceCOntext::CopyResource.
eglCreateContext Creare un contesto di dispositivo Direct3D chiamando D3D11CreateDevice, che restituisce sia un handle a un dispositivo Direct3D che un contesto immediato Direct3D predefinito (OGGETTO ID3D11DeviceContext1). È anche possibile creare un contesto posticipato Direct3D chiamando ID3D11Device2::CreateDeferredContext sull'oggetto ID3D11Device1 restituito.
eglCreatePbufferFromClientBuffer Tutti i buffer vengono letti e scritti come sottorisorsa Direct3D, ad esempio un ID3D11Texture2D. Copiare da uno a un altro tipo di sottorisorsa compatibile con metodi come ID3D11DeviceContext1:CopyResource.
eglCreatePbufferSurface Per creare un dispositivo Direct3D senza catena di scambio, chiamare il metodo statico D3D11CreateDevice . Per una visualizzazione di destinazione di rendering Direct3D, chiamare ID3D11Device::CreateRenderTargetView.
eglCreatePixmapSurface Per creare un dispositivo Direct3D senza catena di scambio, chiamare il metodo statico D3D11CreateDevice . Per una visualizzazione di destinazione di rendering Direct3D, chiamare ID3D11Device::CreateRenderTargetView.
eglCreateWindowSurface Ottenere IDXGISwapChain1 (per i buffer di visualizzazione) e ID3D11Device1 (un'interfaccia virtuale per il dispositivo grafico e le relative risorse). Usa ID3D11Device1 per definire un ID3D11RenderTargetView che puoi usare per creare il buffer frame fornito all'IDXGISwapChain1.
eglDestroyContext N/D. Usare ID3D11DeviceContext::D iscardView1 per eliminare una visualizzazione di destinazione di rendering. Per chiudere il padre ID3D11DeviceContext1, impostare l'istanza su Null e attendere che la piattaforma recuperi le risorse. Non è possibile eliminare direttamente il contesto del dispositivo.
eglDestroySurface N/D. Le risorse grafiche vengono pulite quando coreWindow dell'app UWP viene chiusa dalla piattaforma.
eglGetCurrentDisplay Chiama CoreWindow::GetForCurrentThread per ottenere un riferimento alla finestra dell'app principale corrente.
eglGetCurrentSurface Si tratta dell'ID3D11RenderTargetView corrente. In genere, questo ambito è limitato all'oggetto renderer.
eglGetError Gli errori vengono ottenuti come HRESULT restituiti dalla maggior parte dei metodi nelle interfacce DirectX. Se il metodo non restituisce un HRESULT, chiamare GetLastError. Per convertire un errore di sistema in un valore HRESULT, utilizzare la macro HRESULT_FROM_WIN32.
eglInitialize Chiama CoreWindow::GetForCurrentThread per ottenere un riferimento alla finestra dell'app principale corrente.
eglMakeCurrent Impostare una destinazione di rendering per il disegno nel contesto corrente con ID3D11DeviceContext1::OMSetRenderTargets.
eglQueryContext N/D. Tuttavia, è possibile acquisire destinazioni di rendering da un'istanza id3D11Device1 , nonché alcuni dati di configurazione. Vedere il collegamento per l'elenco dei metodi disponibili.
eglQuerySurface N/D. Tuttavia, è possibile acquisire dati sui viewport e sull'hardware grafico corrente dai metodi in un'istanza ID3D11Device1 . Vedere il collegamento per l'elenco dei metodi disponibili.
eglReleaseTexImage N/D.
eglReleaseThread Per il multithreading GPU generale, leggere Multithreading.
eglSurfaceAttrib Usare D3D11_RENDER_TARGET_VIEW_DESC per configurare una visualizzazione di destinazione di rendering Direct3D,
eglSwapBuffers Usare IDXGISwapChain1::Present1.
eglSwapInterval Vedere IDXGISwapChain1.
eglTerminate CoreWindow usato per visualizzare l'output della pipeline grafica viene gestito dal sistema operativo.
eglWaitClient Per le superfici condivise, usare IDXGIKeyedMutex. Per il multithreading GPU generale, leggere Multithreading.
eglWaitGL Per le superfici condivise, usare IDXGIKeyedMutex. Per il multithreading GPU generale, leggere Multithreading.
eglWaitNative Per le superfici condivise, usare IDXGIKeyedMutex. Per il multithreading GPU generale, leggere Multithreading.