Interoperabilidade entre DirectX e XAML (aplicativos do Tempo de Execução do Windows em DirectX com C++)

Applies to Windows and Windows Phone

Com o lançamento do Windows 8, você pode usar a Linguagem de marcação de aplicativo extensível (XAML) e Microsoft DirectX juntos em seu aplicativo da Windows Store. A combinação do XAML e DirectX permite que você crie estruturas de interface de usuário flexíveis que interoperem com o seu conteúdo renderizado em DirectX e é particularmente útil para aplicativos com muitos gráficos. Este tópico explica a estrutura de um aplicativo da Windows Store que usa DirectX, e identifica os tipos importantes a serem usados na criação do seu aplicativo da Windows Store para funcionar com o DirectX.

Observação  As APIs do DirectX não são definidas como tipos de Tempo de execução do Windows, portanto, você normalmente usa extensões de componente Visual C++ (C++/CX) para desenvolver componentes de aplicativos de XAML da Windows Store que interoperam com DirectX. Dessa forma, você pode criar um aplicativo da Windows Store em C# e XAML usando DirectX, quando você encapsula as chamadas do DirectX em um arquivo de metadados do Tempo de Execução do Windows separado.

XAML e DirectX

O DirectX fornece duas bibliotecas poderosas para gráficos 2D e 3D: Direct2D e Microsoft Direct3D. Embora a XAML dê suporte para primitivos e efeitos básicos 2D, muitos aplicativos, como de modelagem e jogos, precisam de suporte gráfico mais complexo. Para esses, você pode usar Direct2D e Direct3D para renderizar parte ou todos os gráficos e usar XAML para todo o resto.

No cenário de interoperabilidade de XAML e DirectX, você precisa conhecer estes dois conceitos:

  • As superfícies fixas são regiões de tamanho da tela, definidas pelo XAML, em que você pode usar o DirectX para desenhar indiretamente, usando tipos de Windows::UI::Xaml::Media::Brush. Para superfícies compartilhadas, você não controla as chamadas para apresentar as cadeias de troca. As atualizações para a superfície compartilhada são sincronizadas para atualizar a estrutura do XAML.
  • A cadeia de troca em si. Isto proporciona o buffer de apoio do pipeline de renderização do DirectX, a área de memória que é apresentada para exibição após o destino de renderização estar completo.

Reflita sobre porque você está usando o DirectX. Ele será usado para compor ou animar um único controle que se encaixa dentro das dimensões da janela de exibição? A superfície composta pode ser cercada por outras superfícies, ou as bordas da tela? Ela conterá a saída que precisa ser renderizada e controlada em tempo real, como em um jogo?

Depois de determinar como pretende usar o DirectX, você pode usar um destes tipos de Tempo de Execução do Windows para incorporar renderização do DirectX em seu aplicativo da Windows Store:

  • Se você quiser compor uma imagem estática ou desenhar uma imagem nos intervalos acionados por evento, desenhe em uma superfície compartilhada com Windows::UI::Xaml::Media::Imaging::SurfaceImageSource. Esse tipo lida com uma superfície de desenho dimensionada do DirectX. Normalmente, você usa esse tipo ao compor uma imagem ou textura como um bitmap para exibição em um documento ou elemento da interface do usuário. Ele não funciona bem para interatividade em tempo real, como para um jogo de alto desempenho. Isso ocorre porque as atualizações de um objeto SurfaceImageSource são sincronizadas com as atualizações da interface do usuário da XAML, e isso pode introduzir latência no feedback visual que você fornece ao usuário, como uma taxa de quadros flutuante ou uma resposta fraca percebida para entrada em tempo real. Ainda assim, as atualizações são suficientemente rápidas para controles dinâmicos ou simulações de dados!

    Os objetos gráficos de SurfaceImageSource podem ser compostos com outros elementos da interface do usuário da XAML. Você pode transformar ou projetá-los, e a estrutura da XAML respeita qualquer opacidade ou valores de índice z.

  • Se a imagem for maior que o estado real fornecido da tela, e puder ser panorâmica ou ampliada pelo usuário, use Windows::UI::Xaml::Media::Imaging::VirtualSurfaceImageSource. Esse tipo manipula uma superfície de desenho dimensionada do DirectX que é maior que a tela. Como SurfaceImageSource, você usa isso ao compor uma imagem complexa ou controle dinamicamente. E, também como SurfaceImageSource, ela não funciona bem para jogos de alto desempenho. Alguns exemplos de elementos da XAML que podem usar um VirtualSurfaceImageSource são controles de mapa ou um visualizador de documentos para imagens grandes e densas.

  • Se você estiver usando o DirectX para apresentar gráficos atualizados em tempo real ou em uma situação em que as atualizações devem vir em intervalos regulares de baixa latência, use a classe SwapChainPanel para que você possa atualizar os gráficos sem sincronizar com o timer de atualização da estrutura da XAML. Esse tipo permite que você acesse diretamente a cadeia de alternância do dispositivo gráfico (IDXGISwapChain1) e a camada da XAML sobre o destino da renderização. Esse tipo funciona muito bem para jogos e outros aplicativos do DirectX em tela inteira que necessitam de uma interface de usuário baseada em XAML. É necessário conhecer bem o DirectX para usar essa abordagem, incluindo as tecnologias Microsoft DirectX Graphics Infrastructure (DXGI), Direct2D e Direct3D. Para saber mais, veja Guia de programação para Direct3D 11.

SurfaceImageSource

O SurfaceImageSource fornece superfícies compartilhadas do DirectX para atrair e depois compõe os bits no conteúdo do aplicativo.

Aqui está um processo básico para criar e atualizar um objeto SurfaceImageSource no code behind:

  1. Defina o tamanho da superfície compartilhada passando a altura e a largura ao construtor SurfaceImageSource. Você também pode indicar se a superfície precisa de suporte alfa (opacidade).

    Por exemplo:

    SurfaceImageSource^ surfaceImageSource = ref new SurfaceImageSource(400, 300);

  2. Obtenha um ponteiro para ISurfaceImageSourceNative. Disponha o objeto SurfaceImageSource como IInspectable (ou IUnknown), e chame QueryInterface nele para obter a implementação ISurfaceImageSourceNative adjacente. Você pode usar os métodos definidos nessa implementação para definir o dispositivo e executar as operações de desenho.

    
    
    Microsoft::WRL::ComPtr<ISurfaceImageSourceNative>	m_sisNative;
    // ...
    IInspectable* sisInspectable = (IInspectable*) reinterpret_cast<IInspectable*>(surfaceImageSource);
    sisInspectable->QueryInterface(__uuidof(ISurfaceImageSourceNative), (void **)&m_sisNative);
    	
    
    
  3. Configure o dispositivo DXGI chamando primeiro o D3D11CreateDevice e passando o dispositivo e o contexto para ISurfaceImageSourceNative::SetDevice. Por exemplo:

    
    
    Microsoft::WRL::ComPtr<ID3D11Device>				m_d3dDevice;
    Microsoft::WRL::ComPtr<ID3D11DeviceContext>			m_d3dContext;
    // ...
    D3D11CreateDevice(
    		NULL,
    		D3D_DRIVER_TYPE_HARDWARE,
    		NULL,
    		flags,
    		featureLevels,
    		ARRAYSIZE(featureLevels),
    		D3D11_SDK_VERSION,
    		&m_d3dDevice,
    		NULL,
    		&m_d3dContext
    		)
    	);	
    Microsoft::WRL::ComPtr<IDXGIDevice> dxgiDevice;
    m_d3dDevice.As(&dxgiDevice);
    // ...
    m_sisNative->SetDevice(dxgiDevice.Get());
    
    
  4. Forneça um ponteiro ao objeto IDXGISurface para ISurfaceImageSourceNative::BeginDraw, e insira na superfície usando o DirectX. Somente a área especificada para atualização no parâmetro updateRect é desenhada.

    Observação  Você pode ter somente uma operação BeginDraw de destaque ativa por vez por IDXGIDevice.

    Este método retorna o deslocamento do ponto (x,y) do retângulo-alvo atualizado no parâmetro offset. Você pode usar esse deslocamento para determinar onde desenhar em IDXGISurface.

    
    ComPtr<IDXGISurface> surface;
    
    HRESULT beginDrawHR = m_sisNative->BeginDraw(updateRect, &surface, &offset);
    if (beginDrawHR == DXGI_ERROR_DEVICE_REMOVED || beginDrawHR == DXGI_ERROR_DEVICE_RESET)
    {
    		  // device changed
    }
    else
    {
        // draw to IDXGISurface (the surface paramater)
    }
    
    
    
  5. Chame ISurfaceImageSourceNative::EndDraw para completar o bitmap. Passe este bitmap para um ImageBrush.

    
    m_sisNative->EndDraw();
    // ...
    // The SurfaceImageSource object's underlying ISurfaceImageSourceNative object contains the completed bitmap.
    ImageBrush^ brush = ref new ImageBrush();
    brush->ImageSource = surfaceImageSource;
    
    
    
  6. Use o ImageBrush para desenhar o bitmap.

Observação  Chamar o SurfaceImageSource::SetSource (herdado de IBitmapSource::SetSource) atualmente gera uma exceção. Não o chame do seu objeto SurfaceImageSource.

VirtualSurfaceImageSource

VirtualSurfaceImageSource estende a SurfaceImageSource quando o conteúdo é potencialmente maior que o que cabe na tela, assim o conteúdo deve ser virtualizado para ser renderizado de forma ideal.

VirtualSurfaceImageSource difere de SurfaceImageSource por usar uma chamada a IVirtualSurfaceImageSourceCallbacksNative::UpdatesNeeded, que você implementa para atualizar regiões da superfície à medida que se tornam visíveis na tela. Você não precisa limpar as regiões que estão ocultas, pois a estrutura da XAML cuida disso para você.

Aqui está um processo básico para criar e atualizar um objeto VirtualSurfaceImageSource no codebehind:

  1. Crie uma instância de VirtualSurfaceImageSource no tamanho desejado. Por exemplo:

    VirtualSurfaceImageSource^ virtualSIS = ref new VirtualSurfaceImageSource(2000, 2000);

  2. Obtenha um ponteiro para IVirtualSurfaceImageSourceNative. Disponha o objeto VirtualSurfaceImageSource como IInspectable ou IUnknown, e chame QueryInterface nele para obter a implementação IVirtualSurfaceImageSourceNative adjacente. Você pode usar os métodos definidos nessa implementação para definir o dispositivo e executar as operações de desenho.

    
    
    Microsoft::WRL::ComPtr<IVirtualSurfaceImageSourceNative>	m_vsisNative;
    // ...
    IInspectable* vsisInspectable = (IInspectable*) reinterpret_cast<IInspectable*>(virtualSIS);
    vsisInspectable->QueryInterface(__uuidof(IVirtualSurfaceImageSourceNative), (void **)&m_vsisNative);
    	
    
    
  3. Defina o dispositivo DXGI chamando IVirtualSurfaceImageSourceNative::SetDevice. Por exemplo:

    
    
    Microsoft::WRL::ComPtr<ID3D11Device>				m_d3dDevice;
    Microsoft::WRL::ComPtr<ID3D11DeviceContext>			m_d3dContext;
    // ...
    D3D11CreateDevice(
    		NULL,
    		D3D_DRIVER_TYPE_HARDWARE,
    		NULL,
    		flags,
    		featureLevels,
    		ARRAYSIZE(featureLevels),
    		D3D11_SDK_VERSION,
    		&m_d3dDevice,
    		NULL,
    		&m_d3dContext
    		)
    	);	
    Microsoft::WRL::ComPtr<IDXGIDevice> dxgiDevice;
    m_d3dDevice.As(&dxgiDevice);
    // ...
    m_vsisNative->SetDevice(dxgiDevice.Get());
    
    
  4. Chame IVirtualSurfaceImageSourceNative::RegisterForUpdatesNeeded, passando a referência à sua implementação de IVirtualSurfaceUpdatesCallbackNative.

    
    
    class MyContentImageSource : public IVirtualSurfaceUpdatesCallbackNative
    {
    // ...
      private:
         virtual HRESULT STDMETHODCALLTYPE UpdatesNeeded() override;
    }
    
    // ...
    
    HRESULT STDMETHODCALLTYPE MyContentImageSource::UpdatesNeeded()
    {
      // .. perform drawing here ...
    }
    void MyContentImageSource::Initialize()
    {
      // ...
      m_vsisNative->RegisterForUpdatesNeeded(this);
      // ...
    }
    
    
    

    A estrutura chamará a sua implementação de IVirtualSurfaceUpdatesCallbackNative::UpdatesNeeded quando uma região da VirtualSurfaceImageSource precisar ser atualizada.

    Isto pode acontecer quando a estrutura determinar que a região precisa ser desenhada (como quando o usuário faz panorâmicas ou amplia o modo de exibição da superfície) ou após o aplicativo ter chamado IVirtualSurfaceImageSourceNative::Invalidate nessa região.

  5. Em IVirtualSurfaceImageSourceNative::UpdatesNeeded, use os métodos IVirtualSurfaceImageSourceNative::GetUpdateRectCount e IVirtualSurfaceImageSourceNative::GetUpdateRects para determinar quais regiões da superfície precisam ser desenhadas.

    
    
    
    
    HRESULT STDMETHODCALLTYPE MyContentImageSource::UpdatesNeeded()
    {
        HRESULT hr = S_OK;
    
        try
        {
            ULONG drawingBoundsCount = 0;  
    
    		      m_vsisNative->GetUpdateRectCount(&drawingBoundsCount);
            std::unique_ptr<RECT[]> drawingBounds(new RECT[drawingBoundsCount]);
            m_vsisNative->GetUpdateRects(drawingBounds.get(), drawingBoundsCount);
            
            for (ULONG i = 0; i < drawingBoundsCount; ++i)
            {
                // Drawing code here ...
            }
        }
        catch (Platform::Exception^ exception)
        {
            hr = exception->HResult;
        }
    
        return hr;
    }
    
    
  6. Por último, para cada região que deve ser atualizada:

    1. Forneça um ponteiro ao objeto IDXGISurface para IVirtualSurfaceImageSourceNative::BeginDraw, e insira na superfície usando o DirectX. Apenas a área especificada para atualização no parâmetro updateRect será desenhada.

      Tal como acontece com IlSurfaceImageSourceNative::BeginDraw, este método retorna o deslocamento do ponto (x,y) do retângulo de destino atualizado no parâmetro offset. Use esse deslocamento para determinar onde desenhar dentro da IDXGISurface.

      Observação  Você pode ter somente uma operação BeginDraw de destaque ativa por vez por IDXGIDevice.

      
      ComPtr<IDXGISurface> bigSurface;
      
      HRESULT beginDrawHR = m_vsisNative->BeginDraw(updateRect, &bigSurface, &offset);
      if (beginDrawHR == DXGI_ERROR_DEVICE_REMOVED || beginDrawHR == DXGI_ERROR_DEVICE_RESET)
      {
      		  // device changed
      }
      else
      {
          // draw to IDXGISurface
      }
      
      
      
    2. Desenhe o conteúdo específico nessa região, mas restrinja o seu desenho às regiões delimitadas para obter melhor desempenho.

    3. Chame IVirtualSurfaceImageSourceNative::EndDraw. O resultado será um bitmap.

SwapChainPanel e jogos

O SwapChainPanel é o tipo de Tempo de execução do Windows desenvolvido para dar suporte a gráficos de alto desempenho e jogos, onde você gerencia a cadeia de alternância diretamente. Nesse caso, você cria a sua própria cadeia de alternância do DirectX e gerencia a apresentação do seu conteúdo renderizado. Você pode depois adicionar elementos XAML ao objeto SwapChainPanel, como menus, exibições de alertas e outras sobreposições da interface do usuário.

Para assegurar bom desempenho, há algumas limitações para o tipo SwapChainPanel:

  • Não há mais do que 4 instâncias SwapChainPanel por aplicativo.
  • Não há suporte às propriedades Opacity, RenderTransform, Projection e Clip herdadas pelo SwapChainPanel.
  • Você deve definir a altura e a largura da cadeia de alternância do DirectX (em DXGI_SWAP_CHAIN_DESC1) nas dimensões atuais da janela do aplicativo. Caso contrário, o conteúdo da exibição será ajustado (usando DXGI_SCALING_STRETCH) para caber.
  • Você deve definir o modo de dimensionamento da cadeia de alternância do DirectX (em DXGI_SWAP_CHAIN_DESC1) como DXGI_SCALING_STRETCH.
  • Você deve definir o modo alfa da cadeia de alternância do DirectX (em DXGI_SWAP_CHAIN_DESC1) como DXGI_ALPHA_MODE_PREMULTIPLIED.
  • Você deve criar a cadeia de alternância do DirectX chamando IDXGIFactory2::CreateSwapChainForComposition.

Atualize o SwapChainPanel com base nas necessidades do seu aplicativo, e não as atualizações da estrutura da XAML. Se você precisar sincronizar as atualizações de SwapChainPanel para aquelas da estrutura da XAML, registre-se para o evento Windows::UI::Xaml::Media::CompositionTarget::Rendering. Caso contrário, você deve considerar todos os problemas entre threads se tentar atualizar os elementos da XAML de um thread diferente daquele que atualiza o SwapChainPanel.

Há também algumas práticas gerais recomendadas a serem seguidas no design para que o seu aplicativo use o SwapChainPanel.

  • SwapChainPanel herda de Windows::UI::Xaml::Controls::Grid, e dá suporte a comportamento similar de layout. Familiarize-se com o tipo Grid e com suas propriedades.

  • Depois de uma cadeia de alternância do DirectX ser definida, todos os eventos de entrada que forem acionados para SwapChainPanel funcionarão do mesmo modo que fazem para qualquer outro elemento da XAML. Você não define um pincel de tela de fundo para SwapChainPanel, e você não precisa tratar de eventos de entrada do objeto CoreWindow do aplicativo diretamente como faz em aplicativos do DirectX que não usam o SwapChainPanel.

  • • Todo o conteúdo da árvore visual do elemento XAML em um filho direto de um SwapChainPanel é cortado para o tamanho do layout do filho imediato do objeto SwapChainPanel. Nenhum conteúdo que for transformado fora desses limites do layout será renderizado. Portanto, coloque todo o conteúdo da XAML que você animar com um Storyboard de XAML na árvore visual sob um elemento cujos limites de layout sejam grandes o suficiente para conter toda a gama da animação.

  • Limite o número de elementos visuais imediatos da XAML sob um SwapChainPanel. Se possível, agrupe os elementos que estão em estreita proximidade com um pai comum. Mas há uma perda de desempenho entre o número de filhos visuais imediatos e o tamanho dos filhos: o excesso ou elementos desnecessariamente grandes de XAML podem afetar o desempenho geral. Da mesma forma, não crie um elemento de tela inteira individual da XAML filho para o SwapChainPanel do seu aplicativo porque isso aumenta o exagero no aplicativo e diminui o desempenho. Como regra geral, não crie mais de 8 filhos visuais imediatos da XAML para o SwapChainPanel do seu aplicativo, e cada elemento deve ter um tamanho de layout somente grande o suficiente para conter o conteúdo visual do elemento. No entanto, você pode tornar a árvore visual de elementos em um elemento filho do SwapChainPanel suficientemente complexa sem prejudicar demais o desempenho.

Observação   Em geral, seus aplicativos do DirectX têm que criar cadeias de permuta na orientação paisagem e se igualar ao tamanho da janela de exibição (que normalmente é a resolução de tela nativa na maioria dos jogos da Windows Store). Isso garante que seu aplicativo use a implementação de cadeia de permuta ideal quando não tiver nenhuma sobreposição XAML visível. Se o aplicativo for girado para o modo retrato, seu aplicativo deve chamar IDXGISwapChain1::SetRotation na cadeia de troca existente, aplicar uma transformação ao conteúdo, se necessário, e depois chamar o SetSwapChain novamente na mesma cadeia de troca. Da mesma forma, seu aplicativo deve chamar SetSwapChain novamente na mesma cadeia de permuta sempre que a cadeia de permuta for redimensionada chamando IDXGISwapChain::ResizeBuffers.

Aqui está um processo básico para criar e atualizar um objeto SwapChainPanel no code behind:

  1. Obtenha uma instância individual de um painel de cadeia de alternância para o seu aplicativo. As instâncias estão indicadas no XAML com a marca <SwapChainPanel>.

    Windows::UI::Xaml::Controls::SwapChainPanel^ swapChainPanel;

    Essa é uma marca <SwapChainPanel> de exemplo.

    
    <SwapChainPanel x:Name="swapChainPanel">
        <SwapChainPanel.ColumnDefinitions>
            <ColumnDefinition Width="300*"/>
            <ColumnDefinition Width="1069*"/>
        </SwapChainPanel.ColumnDefinitions></SwapChainPanel>
    
    
    
  2. Obtenha um ponteiro para ISwapChainPanelNative. Disponha o objeto SwapChainPanel como IInspectable (ou IUnknown), e chame QueryInterface nele para obter a implementação ISwapChainPanelNative adjacente.

    
    
    Microsoft::WRL::ComPtr<ISwapChainPanelNative>	m_swapChainNative;
    // ...
    IInspectable* panelInspectable = (IInspectable*) reinterpret_cast<IInspectable*>(swapChainPanel);
    panelInspectable->QueryInterface(__uuidof(ISwapChainPanelNative), (void **)&m_swapChainNative);
    	
    
    
  3. Crie o dispositivo DXGI e a cadeia de alternância, e defina a cadeia de alternância como ISwapChainPanelNative enviando-a para SetSwapChain.

    
    
    Microsoft::WRL::ComPtr<IDXGISwapChain1>				m_swapChain;	
    // ...
    DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0};
            swapChainDesc.Width = m_bounds.Width;
            swapChainDesc.Height = m_bounds.Height;
            swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;           // this is the most common swapchain 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;
            swapChainDesc.Scaling = DXGI_SCALING_STRETCH;
            swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // we recommend using this swap effect for all applications
            swapChainDesc.Flags = 0;
            		
    // QI for DXGI device
    Microsoft::WRL::ComPtr<IDXGIDevice> dxgiDevice;
    m_d3dDevice.As(&dxgiDevice);
    
    // get the DXGI adapter
    Microsoft::WRL::ComPtr<IDXGIAdapter> dxgiAdapter;
    dxgiDevice->GetAdapter(&dxgiAdapter);
    
    // get the DXGI factory
    Microsoft::WRL::ComPtr<IDXGIFactory2> dxgiFactory;
    dxgiAdapter->GetParent(__uuidof(IDXGIFactory2), &dxgiFactory);
    // create swap chain by calling CreateSwapChainForComposition
    dxgiFactory->CreateSwapChainForComposition(
                m_d3dDevice.Get(),
                &swapChainDesc,
                nullptr,		// allow on any display 
                &m_swapChain
                );
    		
    m_swapChainNative->SetSwapChain(m_swapChain.Get());
    
    
  4. Desenhe na cadeia de alternância do DirectX, e apresente-o para exibir o conteúdo.

    
    HRESULT hr = m_swapChain->Present(1, 0);
    
    

    Os elementos XAML são atualizados quando o Tempo de execução Windows organiza/renderiza sinais lógicos de uma atualização.

Tópicos relacionados

SurfaceImageSource
VirtualSurfaceImageSource
SwapChainPanel
ISwapChainPanelNative
Guia de programação para Direct3D 11
Exemplos de SwapChainPanel
Exemplo de interoperabilidade de DirectX SwapChainPanel XAML
Exemplos de SurfaceImageSource
Exemplo de interoperabilidade entre DirectX SurfaceImageSource e XAML
Exemplos de VirtualSurfaceImageSource
Exemplo de aplicativo de revista do Direct2D

 

 

Mostrar:
© 2014 Microsoft