Compartilhar via


Comparar o código EGL com DXGI e Direct3D

APIs importantes

A DXGI (Interface Gráfica do DirectX) e várias APIs do Direct3D desempenham a mesma função que o EGL. Este tópico ajuda você a entender o DXGI e o Direct3D 11 da perspectiva do EGL.

DXGI e Direct3D, como EGL, fornecem métodos para configurar recursos gráficos, obter um contexto de renderização para seus sombreadores desenharem e exibir os resultados em uma janela. No entanto, o DXGI e o Direct3D têm muito mais opções e exigem mais esforço para serem configurados corretamente ao portar do EGL.

Observação Esta orientação é baseada na especificação aberta do Khronos Group para EGL 1.4, encontrada aqui: Khronos Native Platform Graphics Interface (EGL versão 1.4 - 6 de abril de 2011) [PDF]. As diferenças na sintaxe específica de outras plataformas e linguagens de desenvolvimento não são abordadas neste guia.

 

Como o DXGI e o Direct3D se comparam?

A grande vantagem do EGL sobre DXGI e Direct3D é que é relativamente simples começar a desenhar em uma superfície de janela. Isso ocorre porque o OpenGL ES 2.0 — e, portanto, o EGL — é uma especificação implementada por vários provedores de plataforma, enquanto o DXGI e o Direct3D são uma única referência à qual os drivers do fornecedor de hardware devem estar em conformidade. Isso significa que a Microsoft deve implementar um conjunto de APIs que habilite o conjunto mais amplo possível de recursos do fornecedor, em vez de se concentrar em um subconjunto funcional oferecido por um fornecedor específico ou combinando comandos de instalação específicos do fornecedor em APIs mais simples. Por outro lado, o Direct3D fornece um único conjunto de APIs que abrangem uma ampla variedade de plataformas de hardware gráfico e níveis de recursos e oferecem mais flexibilidade para desenvolvedores experientes com a plataforma.

Assim como o EGL, o DXGI e o Direct3D fornecem APIs para os seguintes comportamentos:

  • Obtenção, leitura e gravação em um buffer de quadros (chamado de "cadeia de troca" no DXGI).
  • Associando o buffer de quadros a uma janela de interface do usuário.
  • Obter e configurar contextos de renderização nos quais desenhar.
  • Emitir comandos para o pipeline de gráficos para um contexto de renderização específico.
  • Criar e gerenciar recursos de sombreador e associá-los a um conteúdo de renderização.
  • Renderização para destinos de renderização específicos (como texturas).
  • Atualizando a superfície de exibição da janela com os resultados da renderização com os recursos gráficos.

Para ver o processo básico do Direct3D para configurar o pipeline de gráficos, confira o modelo de aplicativo DirectX 11 (Universal Windows) no Microsoft Visual Studio 2015. A classe de renderização base fornece uma boa linha de base para configurar a infraestrutura gráfica do Direct3D 11 e configurar recursos básicos nela, além de dar suporte a recursos de aplicativo UWP (Plataforma Universal do Windows), como rotação de tela.

O EGL tem muito poucas APIs em relação ao Direct3D 11, e navegar no último pode ser um desafio se você não estiver familiarizado com a nomenclatura e o jargão específicos da plataforma. Aqui está uma visão geral simples para ajudá-lo a se orientar.

Primeiro, examine o objeto EGL básico para o mapeamento da interface Direct3D:

Abstração EGL Representação semelhante do Direct3D
EGLDisplay No Direct3D (para aplicativos UWP), o identificador de exibição é obtido por meio da API Windows::UI::CoreWindow (ou da interface ICoreWindowInterop que expõe o HWND). O adaptador e a configuração de hardware são definidos com as interfaces COM IDXGIAdapter e IDXGIDevice1, respectivamente.
EGLSurface No Direct3D, os buffers e outros recursos de janela (visíveis ou fora da tela) são criados e configurados por interfaces DXGI específicas, incluindo IDXGIFactory2 (uma implementação de padrão de fábrica usada para adquirir recursos DXGI, como oIDXGISwapChain1 (buffers de exibição). O ID3D11Device1 que representa o dispositivo gráfico e seus recursos é adquirido com D3D11Device::CreateDevice. Para destinos de renderização, use a interface ID3D11RenderTargetView .
EGLContext No Direct3D, você configura e emite comandos para o pipeline de gráficos com a interface ID3D11DeviceContext1 .
EGLConfig No Direct3D 11, você cria e configura recursos gráficos, como buffers, texturas, estênceis e sombreadores com métodos na interface ID3D11Device1.

 

Agora, aqui está o processo mais básico para configurar uma exibição gráfica simples, recursos e contexto em DXGI e Direct3D para um aplicativo UWP.

  1. Obtenha um identificador para o objeto CoreWindow para o thread principal da interface do usuário do aplicativo chamando CoreWindow::GetForCurrentThread.
  2. Para aplicativos UWP, adquira uma cadeia de troca do IDXGIAdapter2 com IDXGIFactory2::CreateSwapChainForCoreWindow e passe a referência CoreWindow obtida na etapa 1. Você receberá uma instância IDXGISwapChain1 em troca. Defina o escopo para o objeto renderizador e seu thread de renderização.
  3. Obtenha instâncias ID3D11Device1 e ID3D11DeviceContext1 chamando o método D3D11Device::CreateDevice . Defina o escopo deles para o objeto renderizador também.
  4. Crie sombreadores, texturas e outros recursos usando métodos no objeto ID3D11Device1 do renderizador.
  5. Defina buffers, execute sombreadores e gerencie os estágios do pipeline usando métodos no objeto ID3D11DeviceContext1 do renderizador.
  6. Quando o pipeline for executado e um quadro for desenhado para o buffer traseiro, apresente-o à tela com IDXGISwapChain1::P resent1.

Para examinar esse processo com mais detalhes, examine Introdução aos elementos gráficos do DirectX. O restante deste artigo aborda muitas das etapas comuns para configuração e gerenciamento básicos de pipeline gráfico.

Observação Os aplicativos da área de trabalho do Windows têm APIs diferentes para obter uma cadeia de troca Direct3D, como D3D11Device::CreateDeviceAndSwapChain, e não usam um objeto CoreWindow.

 

Obtendo uma janela para exibição

Neste exemplo, eglGetDisplay recebe um HWND para um recurso de janela específico para a plataforma Microsoft Windows. Outras plataformas, como o iOS da Apple (Cocoa) e o Android do Google, têm identificadores ou referências diferentes a recursos de janela e podem ter sintaxe de chamada completamente diferente. Depois de obter uma exibição, você a inicializa, define a configuração preferida e cria uma superfície com um buffer traseiro no qual você pode desenhar.

Obter um display e configurá-lo com 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;
}

No Direct3D, a janela principal de um aplicativo UWP é representada pelo objeto CoreWindow, que pode ser obtido do objeto do aplicativo chamando CoreWindow::GetForCurrentThread como parte do processo de inicialização do "provedor de exibição" que você constrói para o Direct3D. (Se você estiver usando a interoperabilidade Direct3D-XAML, use o provedor de exibição da estrutura XAML.) O processo de criação de um provedor de exibição Direct3D é abordado em Como configurar seu aplicativo para exibir um modo de exibição.

Obtendo um CoreWindow para Direct3D.

CoreWindow::GetForCurrentThread();

Depois que a referência CoreWindow for obtida, a janela deverá ser ativada, o que executa o método Run do objeto principal e inicia o processamento do evento da janela. Depois disso, crie um ID3D11Device1 e um ID3D11DeviceContext1 e use-os para obter o IDXGIDevice1 e o IDXGIAdapter subjacentes para que você possa obter um objeto IDXGIFactory2 para criar um recurso de cadeia de troca com base na configuração do DXGI_SWAP_CHAIN_DESC1.

Configurando e definindo a cadeia de troca DXGI no CoreWindow para 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);
}

Chame o método IDXGISwapChain1::P resent1 depois de preparar um quadro para exibi-lo.

Observe que, no Direct3D 11, não há uma abstração idêntica ao EGLSurface. (Há IDXGISurface1, mas é usado de forma diferente.) A aproximação conceitual mais próxima é o objeto ID3D11RenderTargetView que usamos para atribuir uma textura (ID3D11Texture2D) como o buffer traseiro no qual nosso pipeline de sombreador será desenhado.

Configurando o buffer traseiro para a cadeia de troca no 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);

Uma boa prática é chamar esse código sempre que a janela for criada ou mudar de tamanho. Durante a renderização, defina a exibição de destino de renderização com ID3D11DeviceContext1::OMSetRenderTargets antes de configurar qualquer outro sub-recurso, como buffers de vértice ou sombreadores.

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

Criando um contexto de renderização

No EGL 1.4, uma "exibição" representa um conjunto de recursos de janela. Normalmente, você configura uma "superfície" para a exibição fornecendo um conjunto de atributos para o objeto de exibição e obtendo uma superfície em troca. Você cria um contexto para exibir o conteúdo da superfície criando esse contexto e vinculando-o à superfície e à exibição.

O fluxo de chamadas geralmente é semelhante a este:

  • Chame eglGetDisplay com o identificador para um recurso de exibição ou janela e obtenha um objeto de exibição.
  • Inicialize a exibição com eglInitialize.
  • Obtenha a configuração de exibição disponível e selecione uma com eglGetConfigs e eglChooseConfig.
  • Crie uma superfície de janela com eglCreateWindowSurface.
  • Crie um contexto de exibição para desenhar com eglCreateContext.
  • Associe o contexto de exibição à exibição e à superfície com eglMakeCurrent.

Na seção anterior, criamos o EGLDisplay e o EGLSurface, e agora usamos o EGLDisplay para criar um contexto e associar esse contexto à exibição, usando o EGLSurface configurado para parametrizar a saída.

Obtendo um contexto de renderização com 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;
}

Um contexto de renderização no Direct3D 11 é representado por um objeto ID3D11Device1, que representa o adaptador e permite que você crie recursos do Direct3D, como buffers e sombreadores; e pelo objeto ID3D11DeviceContext1, que permite gerenciar o pipeline de gráficos e executar os sombreadores.

Esteja ciente dos níveis de recursos do Direct3D! Eles são usados para dar suporte a plataformas de hardware Direct3D mais antigas, do DirectX 9.1 ao DirectX 11. Muitas plataformas que usam hardware gráfico de baixo consumo de energia, como tablets, só têm acesso aos recursos do DirectX 9.1, e o hardware gráfico com suporte mais antigo pode ser do 9.1 ao 11.

Criando um contexto de renderização com 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.
);

Desenho em um recurso de textura ou pixmap

Para desenhar em uma textura com o OpenGL ES 2.0, configure um buffer de pixel ou PBuffer. Depois de configurar e criar um EGLSurface para ele, você pode fornecê-lo com um contexto de renderização e executar o pipeline do sombreador para desenhar na textura.

Desenhe em um buffer de pixels com o 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); 

No Direct3D 11, você cria um recurso ID3D11Texture2D e o transforma em um destino de renderização. Configure o destino de renderização usando D3D11_RENDER_TARGET_VIEW_DESC. Quando você chama o método ID3D11DeviceContext::D raw (ou uma operação Draw* semelhante no contexto do dispositivo) usando esse destino de renderização, os resultados são desenhados em uma textura.

Desenhar em uma textura com o 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);

Essa textura pode ser passada para um sombreador se estiver associada a um ID3D11ShaderResourceView.

Desenhando na tela

Depois de usar o EGLContext para configurar seus buffers e atualizar seus dados, execute os sombreadores associados a ele e desenhe os resultados para o buffer traseiro com glDrawElements. Você exibe o buffer traseiro chamando eglSwapBuffers.

Open GL ES 2.0: Desenhando na tela.

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

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

No Direct3D 11, você configura seus buffers e associa sombreadores com seu IDXGISwapChain::P resent1. Em seguida, chame um dos métodos ID3D11DeviceContext1::D raw* para executar os sombreadores e desenhar os resultados para um destino de renderização configurado como o buffer traseiro para a cadeia de troca. Depois disso, basta apresentar o buffer de fundo para a exibição chamando IDXGISwapChain::P resent1.

Direct3D 11: Desenhando na tela.


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

// ...

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

Liberando recursos gráficos

No EGL, você libera os recursos da janela passando o EGLDisplay para eglTerminate.

Terminando uma exibição com EGL 1.4

EGLBoolean eglTerminate(eglDisplay);

Em um aplicativo UWP, você pode fechar o CoreWindow com CoreWindow::Close, embora isso só possa ser usado para janelas de interface do usuário secundárias. O thread de interface do usuário principal e seu CoreWindow associado não podem ser fechados; em vez disso, eles expiram pelo sistema operacional. No entanto, quando um CoreWindow secundário é fechado, o evento CoreWindow::Closed é gerado.

Mapeamento de referência de API para EGL para Direct3D 11

EGL API API ou comportamento semelhante do Direct3D 11
eglBindAPI N/D
eglBindTexImage Chame ID3D11Device::CreateTexture2D para definir uma textura 2D.
eglChooseConfig O Direct3D não fornece um conjunto de configurações de buffer de quadro padrão. A configuração da cadeia de troca
eglCopyBuffers Para copiar dados de buffer, chame ID3D11DeviceContext::CopyStructureCount. Para copiar um recurso, chame ID3DDeviceCOntext::CopyResource.
eglCreateContext Crie um contexto de dispositivo Direct3D chamando D3D11CreateDevice, que retorna um identificador para um dispositivo Direct3D e um contexto imediato Direct3D padrão (objeto ID3D11DeviceContext1). Você também pode criar um contexto adiado do Direct3D chamando ID3D11Device2::CreateDeferredContext no objeto ID3D11Device1 retornado.
eglCreatePbufferFromClientBuffer Todos os buffers são lidos e gravados como um sub-recurso Direct3D, como um ID3D11Texture2D. Copie de um para outro tipo de sub-recurso compatível com métodos como ID3D11DeviceContext1:CopyResource.
eglCreatePbufferSurface Para criar um dispositivo Direct3D sem cadeia de troca, chame o método estático D3D11CreateDevice . Para uma exibição de destino de renderização do Direct3D, chame ID3D11Device::CreateRenderTargetView.
eglCreatePixmapSurface Para criar um dispositivo Direct3D sem cadeia de troca, chame o método estático D3D11CreateDevice . Para uma exibição de destino de renderização do Direct3D, chame ID3D11Device::CreateRenderTargetView.
eglCreateWindowSurface Conecte um IDXGISwapChain1 (para os buffers de exibição) e um ID3D11Device1 (uma interface virtual para o dispositivo gráfico e seus recursos). Use o ID3D11Device1 para definir um ID3D11RenderTargetView que você pode usar para criar o buffer de quadros fornecido ao IDXGISwapChain1.
eglDestroyContext N/D Use ID3D11DeviceContext::D iscardView1 para se livrar de uma exibição de destino de renderização. Para fechar o ID3D11DeviceContext1 pai, defina a instância como nula e aguarde até que a plataforma recupere seus recursos. Você não pode destruir o contexto do dispositivo diretamente.
eglDestroySurface N/D Os recursos gráficos são limpos quando o CoreWindow do aplicativo UWP é fechado pela plataforma.
eglGetCurrentDisplay Chame CoreWindow::GetForCurrentThread para obter uma referência à janela principal atual do aplicativo.
eglGetCurrentSurface Este é o ID3D11RenderTargetView atual. Normalmente, isso tem como escopo o objeto renderizador.
eglGetError Os erros são obtidos como HRESULTs retornados pela maioria dos métodos em interfaces DirectX. Se o método não retornar um HRESULT, chame GetLastError. Para converter um erro do sistema em um valor HRESULT, use a macro HRESULT_FROM_WIN32.
eglInitialize Chame CoreWindow::GetForCurrentThread para obter uma referência à janela principal atual do aplicativo.
eglMakeCurrent Defina um destino de renderização para desenhar no contexto atual com ID3D11DeviceContext1::OMSetRenderTargets.
eglQueryContext N/D No entanto, você pode adquirir destinos de renderização de uma instância ID3D11Device1 , bem como alguns dados de configuração. (Veja o link para a lista de métodos disponíveis.)
eglQuerySurface N/D No entanto, você pode adquirir dados sobre viewports e o hardware gráfico atual de métodos em uma instância ID3D11Device1. (Veja o link para a lista de métodos disponíveis.)
eglReleaseTexImage N/D
eglReleaseThread Para multithreading geral de GPU, leia Multithreading.
eglSurfaceAttrib Use D3D11_RENDER_TARGET_VIEW_DESC para configurar uma exibição de destino de renderização do Direct3D,
eglSwapBuffers Use IDXGISwapChain1::P resent1.
eglSwapInterval Consulte IDXGISwapChain1.
eglTerminate O CoreWindow usado para exibir a saída do pipeline de gráficos é gerenciado pelo sistema operacional.
eglWaitClient Para superfícies compartilhadas, use IDXGIKeyedMutex. Para multithreading geral de GPU, leia Multithreading.
eglWaitGL Para superfícies compartilhadas, use IDXGIKeyedMutex. Para multithreading geral de GPU, leia Multithreading.
eglWaitNativo Para superfícies compartilhadas, use IDXGIKeyedMutex. Para multithreading geral de GPU, leia Multithreading.