Interoperabilità di DirectX e XAML (app di Windows Runtime scritte in DirectX con C++)

Applies to Windows and Windows Phone

Con il rilascio di Windows 8, puoi usare XAML (Extensible Application Markup Language) e Microsoft DirectX contemporaneamente nelle tue app di Windows Store. La combinazione di XAML e DirectX consente di sviluppare framework di interfaccia utente flessibili in grado di interagire con i contenuti di cui viene eseguito il rendering tramite DirectX, il che è particolarmente utile per le app in cui vengono ampiamente usati contenuti grafici. In questo argomento viene spiegata la struttura di un'app di Windows Store che usa DirectX e vengono identificati i tipi importanti da usare quando crei un'app di Windows Store per lavorare con DirectX.

Nota  Le API DirectX non sono definite come tipi di Windows Runtime, quindi in genere usi le estensioni del componente Visual C++ (C++/CX) per sviluppare componenti app di Windows Store XAML per l'interoperabilità con DirectX. Puoi anche creare un'app di Windows Store con C# e XAML che usa DirectX se esegui il wrapping delle chiamate DirectX in un file di metadati separato di Windows Runtime.

XAML e DirectX

DirectX fornisce due potenti librerie per grafica 2D e 3D: Direct2D e Microsoft Direct3D. Anche se XAML fornisce il supporto per effetti e primitive 2D di base, molte app, ad esempio quelle per la modellazione e il gioco, necessitano di un supporto grafico più complesso. I questi casi, puoi usare Direct2D e Direct3D per eseguire il rendering di una parte o di tutta la grafica e XAML per tutto il resto.

Per quanto riguarda lo scenario di interoperabilità di XAML e DirectX, devi conoscere questi due concetti:

  • Le superfici condivise sono aree dimensionate della visualizzazione, definite da XAML, nelle quali è possibile disegnare indirettamente tramite DirectX mediante i tipi Windows::UI::Xaml::Media::Brush. Per le superfici condivise, non controlli le chiamate per la presentazione delle catene di scambio. Gli aggiornamenti della superficie condivisa vengono sincronizzate con gli aggiornamenti del framework XAML.
  • La catena di scambio stessa. In questo modo è disponibile il buffer nascosto della pipeline di rendering DirectX, l'area di memoria presentata per la visualizzazione dopo che è stata completata la destinazione del rendering.

Considera l'uso di DirectX. Servirà per comporre o animare un singolo controllo di dimensioni tali da rientrare nella finestra di visualizzazione? La superficie composita può essere nascosta da altre superfici o dai bordi dello schermo? Conterrà output di cui è necessario eseguire il rendering e che deve essere controllato in tempo reale, come in un gioco?

Dopo aver determinato in che modo intendi usare DirectX, usa uno di questi tipi di Windows Runtime per incorporare il rendering DirectX nell'app di Windows Store:

  • Se vuoi comporre un'immagine statica oppure disegnarne una complessa a intervalli guidati dagli eventi, disegna su una superficie condivisa con Windows::UI::Xaml::Media::Imaging::SurfaceImageSource. Questo tipo gestisce una superficie di disegno DirectX ridimensionata. Solitamente, usi questo tipo quando componi un'immagine o trama come un bitmap per la visualizzazione in un documento o elemento dell'interfaccia utente. Non funziona correttamente per interattività in tempo reale, ad esempio un gioco ad alte prestazioni. Questo problema si verifica perché gli aggiornamenti di un oggetto SurfaceImageSource sono sincronizzati con gli aggiornamenti dell'interfaccia utente di XAML che possono introdurre una latenza nel riscontro visivo fornito all'utente, come un'oscillazione della velocità del frame o una bassa risposta percepita degli input in tempo reale. Gli aggiornamenti sono comunque abbastanza rapidi per i controlli dinamici o le simulazioni di dati!

    Gli oggetti grafici SurfaceImageSource possono essere composti da altri elementi dell'interfaccia utente XAML. Puoi trasformarli o progettarli e il framework XAML rispetta tutte le opacità o valori di z-index.

  • Se l'immagine è più grande dell'area dello schermo fornita e l'utente è in grado di fare una panoramica o eseguire lo zoom, usare Windows::UI::Xaml::Media::Imaging::VirtualSurfaceImageSource. Questo tipo gestisce una superficie di disegno DirectX ridimensionata più grande dello schermo. Come SurfaceImageSource, lo usi quando componi un'immagine complessa o un controllo dinamico. Inoltre, sempre come SurfaceImageSource, non funziona correttamente per giochi ad alte prestazioni. Alcuni esempi di elementi XAML che possono usare VirtualSurfaceImageSource sono i controlli mappa o un grande visualizzatore di documenti a immagine densa.

  • Se stai usando DirectX per presentare grafica aggiornata in tempo reale o in una situazione dove gli aggiornamenti sono disponibili a intervalli regolari a bassa latenza, usa la classe SwapChainPanel così puoi aggiornare la grafica senza sincronizzare il timer di aggiornamento del framework XAML. Questo tipo abilita l'accesso diretto alla grafica della catena di scambio del dispositivo (IDXGISwapChain1) e ai livelli XAML in cima alla destinazione di rendering. Questo tipo funziona perfettamente con i giochi e altre app DirectX a schermo intero che richiedono un'interfaccia utente basata su XAML. Per usare questo approccio devi conoscere bene DirectX, incluse le tecnologie Microsoft DirectX Graphics Infrastructure (DXGI), Direct2D e Direct3D. Per altre informazioni, vedi Guida alla programmazione per Direct3D 11.

SurfaceImageSource

SurfaceImageSource fornisce le superfici condivise DirectX in cui disegnare e quindi compone i bit nel contenuto dell'app.

Ecco il processo base per la creazione e l'aggiornamento di un oggetto SurfaceImageSource nel code-behind:

  1. Definiamo le dimensioni della superficie condivisa passando l'altezza e la larghezza al costruttore SurfaceImageSource. Puoi anche indicare se la superficie deve supportare il canale alfa (opacità).

    Ad esempio:

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

  2. Ottieni un puntatore a ISurfaceImageSourceNative. Esegui il cast dell'oggetto SurfaceImageSource come IInspectable (o IUnknown) e chiama QueryInterface per ottenere l'implementazione di ISurfaceImageSourceNative sottostante. Usa il metodo definito in questa implementazione per impostare il dispositivo ed eseguire le operazioni di disegno.

    
    
    Microsoft::WRL::ComPtr<ISurfaceImageSourceNative>	m_sisNative;
    // ...
    IInspectable* sisInspectable = (IInspectable*) reinterpret_cast<IInspectable*>(surfaceImageSource);
    sisInspectable->QueryInterface(__uuidof(ISurfaceImageSourceNative), (void **)&m_sisNative);
    	
    
    
  3. Imposta il dispositivo DXGI chiamando innanzitutto D3D11CreateDevice e quindi passando il dispositivo e il contesto a ISurfaceImageSourceNative::SetDevice. Ad esempio:

    
    
    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. Fornisci un puntatore all'oggetto IDXGISurface per ISurfaceImageSourceNative::BeginDraw e disegna nella superficie mediante DirectX. Verrà disegnata solo l'area specificata per l'aggiornamento nel parametro updateRect.

    Nota  Puoi avere una sola operazione BeginDraw in attesa e attiva alla volta per ogni IDXGIDevice.

    Questo metodo restituisce l'offset del punto (x,y) del rettangolo di destinazione aggiornato nel parametro offset. Questo offset consente di determinare la posizione in cui disegnare in 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. Chiama ISurfaceImageSourceNative::EndDraw per completare la bitmap. Passa la bitmap a un ImageBrush.

    
    m_sisNative->EndDraw();
    // ...
    // The SurfaceImageSource object's underlying ISurfaceImageSourceNative object contains the completed bitmap.
    ImageBrush^ brush = ref new ImageBrush();
    brush->ImageSource = surfaceImageSource;
    
    
    
  6. Usa ImageBrush per disegnare la bitmap.

Nota  La chiamata a SurfaceImageSource::SetSource (ereditato da IBitmapSource::SetSource) attualmente genera un'eccezione. Non chiamarlo dall'oggetto SurfaceImageSource.

VirtualSurfaceImageSource

VirtualSurfaceImageSource estende SurfaceImageSource quando il contenuto è potenzialmente più grande dello schermo, in modo da poter virtualizzare il contenuto e ottimizzarlo.

VirtualSurfaceImageSource differisce da SurfaceImageSource in quanto usa un callback, IVirtualSurfaceImageSourceCallbacksNative::UpdatesNeeded, che tu implementi per aggiornare aree della superficie non appena diventano visibili sullo schermo. Non è necessario cancellare aree nascoste in quanto se ne occupa il framework XAML.

Processo base per la creazione e l'aggiornamento di un oggetto VirtualSurfaceImageSource nel code-behind:

  1. Crea un'istanza di VirtualSurfaceImageSource con le dimensioni desiderate. Ad esempio:

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

  2. Ottieni un puntatore a IVirtualSurfaceImageSourceNative. Esegui il cast dell'oggetto VirtualSurfaceImageSource come IInspectable o IUnknown e chiama QueryInterface per ottenere l'implementazione di IVirtualSurfaceImageSourceNative sottostante. Usa il metodo definito in questa implementazione per impostare il dispositivo ed eseguire le operazioni di disegno.

    
    
    Microsoft::WRL::ComPtr<IVirtualSurfaceImageSourceNative>	m_vsisNative;
    // ...
    IInspectable* vsisInspectable = (IInspectable*) reinterpret_cast<IInspectable*>(virtualSIS);
    vsisInspectable->QueryInterface(__uuidof(IVirtualSurfaceImageSourceNative), (void **)&m_vsisNative);
    	
    
    
  3. Imposta il dispositivo DXGI chiamando IVirtualSurfaceImageSourceNative::SetDevice. Ad esempio:

    
    
    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. Chiama IVirtualSurfaceImageSourceNative::RegisterForUpdatesNeeded, passando a un riferimento per la tua implementazione di 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);
      // ...
    }
    
    
    

    Il framework chiama la tua implementazione di IVirtualSurfaceUpdatesCallbackNative::UpdatesNeeded quando è necessario aggiornare un'area del VirtualSurfaceImageSource.

    Può avvenire quando il framework determina l'area da disegnare (ad esempio quando l'utente effettua una panoramica o lo zoom sulla visualizzazione della superficie), o dopo che l'app ha chiamato IVirtualSurfaceImageSourceNative::Invalidate su quell'area.

  5. In IVirtualSurfaceImageSourceNative::UpdatesNeeded, usa i metodi IVirtualSurfaceImageSourceNative::GetUpdateRectCount e IVirtualSurfaceImageSourceNative::GetUpdateRects per determinare le aree della superficie da disegnare.

    
    
    
    
    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. Infine, per ogni area da aggiornare:

    1. Fornisci un puntatore all'oggetto IDXGISurface per IVirtualSurfaceImageSourceNative::BeginDraw e disegna nella superficie mediante DirectX. Verrà disegnata solo l'area specificata per l'aggiornamento nel parametro updateRect.

      Come nel caso di IlSurfaceImageSourceNative::BeginDraw, questo metodo restituisce l'offset del punto (x,y) del rettangolo di destinazione aggiornato nel parametro offset. Questo offset consente di determinare la posizione in cui disegnare in IDXGISurface.

      Nota  Puoi avere una sola operazione BeginDraw in attesa e attiva alla volta per ogni 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. Disegna il contenuto specifico in tale area ma vincola il disegno alle aree associate per ottenere migliori prestazioni.

    3. Chiama IVirtualSurfaceImageSourceNative::EndDraw. Il risultato è una bitmap.

SwapChainPanel e giochi

SwapChainPanel è il tipo di Windows Runtime progettato per supportare grafica e giochi ad alte prestazioni, dove gestisci direttamente la catena di scambio. In questo caso, crea la tua catena di scambio DirectX e gestisci la presentazione del contenuto renderizzato. Puoi aggiungere elementi XAML all'oggetto SwapChainPanel, ad esempio menu, visualizzazioni di avviso e sovrapposizioni dell'interfaccia utente.

Per garantire delle buone prestazioni, esistono alcuni limiti al tipo SwapChainPanel:

  • Sono disponibili non più di 4 istanze SwapChainPanel per app.
  • Le proprietà Opacity, RenderTransform, Projection e Clip ereditate da SwapChainPanel non sono supportate.
  • Devi impostare l'altezza e la larghezza della catena di scambio DirectX (in DXGI_SWAP_CHAIN_DESC1) alle dimensioni attuali della finestra dell'app. Se non lo fai, il contenuto da visualizzare verrà adattato (usando DXGI_SCALING_STRETCH).
  • Devi impostare la modalità di proporzioni della catena di scambio DirectX (in DXGI_SWAP_CHAIN_DESC1) su DXGI_SCALING_STRETCH.
  • Non puoi impostare la modalità alfa della catena di scambio DirectX (in DXGI_SWAP_CHAIN_DESC1) su DXGI_ALPHA_MODE_PREMULTIPLIED.
  • Devi creare la catena di scambio DirectX chiamando IDXGIFactory2::CreateSwapChainForComposition.

Aggiorna SwapChainPanel in base alle esigenze dell'app e non gli aggiornamenti del framework XAML. Se devi sincronizzare gli aggiornamenti di SwapChainPanel con quelli del framework XAML, effettua la registrazione per l'evento Windows::UI::Xaml::Media::CompositionTarget::Rendering. In caso contrario, se provi ad aggiornare gli elementi XAML da un thread differente da quello che aggiorna SwapChainPanel devi considerare tutti i problemi di cross-thread.

Esistono anche delle procedure consigliate da seguire nella progettazione dell'app per usare SwapChainPanel.

  • SwapChainPanel eredita da Windows::UI::Xaml::Controls::Grid e supporta simili comportamenti di layout. Acquisisci dimestichezza con il tipo Grid e le sue proprietà.

  • Dopo aver impostato una catena di scambio DirectX, tutti gli eventi di input generati per SwapChainPanel funzionano come per tutti gli altri elementi XAML. Non impostando il pennello di sfondo per SwapChainPanel non hai bisogno di gestire direttamente eventi di input dall'oggetto CoreWindow dell'app, come per le app DirectX che non usano SwapChainPanel.

  • • Tutto il contenuto di un albero di un elemento XAML visivo in un figlio diretto di SwapChainPanel viene troncato in base alle dimensioni del layout di SwapChainPanel dell'oggetto immediato. Tutto il contenuto trasformato al di fuori di queste associazioni di layout non verrà renderizzato. Posiziona quindi tutto il contenuto XAML animato con una Storyboard XAML nella struttura ad albero visiva in un elemento con associazioni dell'oggetto abbastanza grandi da contenere l'intera gamma dell'animazione.

  • Limita il numero di elementi XAML visivi immediati in SwapChainPanel. Se possibile, raggruppa gli elementi in stretta vicinanza in un membro padre comune. Si verifica un compromesso delle prestazioni tra il numero di figli Visual immediati e le dimensioni del figlio: gli elementi XAML in numero elevato o inutilmente grandi possono ridurre le prestazioni generali. Allo stesso modo, non creare un singolo elemento XAML figlio a schermo intero per SwapChainPanel dell'app perché in questo modo viene aumentato il sovra disegno dell'app e si riducono le prestazioni. Come regola, non devi creare più di 8 figli Visual XAML immediati per SwapChainPanel dell'app e ogni elemento deve avere dimensioni di layout non più grandi del necessario per contenere il contenuto visivo dell'elemento. Puoi creare la struttura ad albero visiva degli elementi in un elemento figlio del SwapChainPanel sufficientemente complesso senza diminuire le prestazioni.

Nota   In genere, le app DirectX dovrebbero creare catene di scambio con orientamento orizzontale di dimensioni pari a quelle della finestra di visualizzazione (di norma la risoluzione dello schermo nativa nella maggior parte dei giochi di Windows Store). Ciò garantisce che l'app usi l'implementazione della catena di scambio ottimale quando non è presente alcun overlay XAML visibile. Se l'app viene ruotata in modalità verticale, dovrebbe chiamare IDXGISwapChain1::SetRotation sulla catena di scambio esistente, applicare una trasformazione al contenuto (se necessario) e quindi chiamare di nuovo SetSwapChain sulla stessa catena di scambio. Analogamente, l'app deve chiamare di nuovo SetSwapChain sulla stessa catena di scambio ogni volta che quest'ultima viene ridimensionata chiamando IDXGISwapChain::ResizeBuffers.

Ecco il processo base per la creazione e l'aggiornamento di un oggetto SwapChainPanel nel code-behind:

  1. Ottieni un'istanza del pannello della catena di scambio dell'app. Le istanze vengono indicate in XAML tramite il tag <SwapChainPanel>.

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

    Ecco un esempio di tag <SwapChainPanel>.

    
    <SwapChainPanel x:Name="swapChainPanel">
        <SwapChainPanel.ColumnDefinitions>
            <ColumnDefinition Width="300*"/>
            <ColumnDefinition Width="1069*"/>
        </SwapChainPanel.ColumnDefinitions></SwapChainPanel>
    
    
    
  2. Ottieni un puntatore a ISwapChainPanelNative. Esegui il cast dell'oggetto SwapChainPanel come IInspectable (o IUnknown) e chiama QueryInterface per ottenere l'implementazione di ISwapChainPanelNative sottostante.

    
    
    Microsoft::WRL::ComPtr<ISwapChainPanelNative>	m_swapChainNative;
    // ...
    IInspectable* panelInspectable = (IInspectable*) reinterpret_cast<IInspectable*>(swapChainPanel);
    panelInspectable->QueryInterface(__uuidof(ISwapChainPanelNative), (void **)&m_swapChainNative);
    	
    
    
  3. Crea un dispositivo DXGI e la catena di scambio impostandola su ISwapChainPanelNative passandola a 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. Disegna sulla catena di scambio DirectX e presentala per visualizzare il contenuto.

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

    Gli elementi XAML vengono aggiornati quando la logica di layout/rendering di Windows Runtime segnala un aggiornamento.

Argomenti correlati

SurfaceImageSource
VirtualSurfaceImageSource
SwapChainPanel
ISwapChainPanelNative
Guida alla programmazione per Direct3D 11
Esempi di SwapChainPanel
Esempio di interoperabilità di SwapChainPanel DirectX e XAML
Esempi di SurfaceImageSource
Esempio di interoperabilità di SurfaceImageSource DirectX e XAML
Esempi di VirtualSurfaceImageSource
Esempio di app di rivista Direct2D

 

 

Mostra:
© 2014 Microsoft