Programmazione DirectX

Windows 8.1 introduce DirectX 11.2, che offre varie nuove funzionalità per migliorare le prestazioni delle tue app per giochi e grafica.

Funzionalità nuove o aggiornate in Windows 8.1

Collegamento di shader HLSL

[Accedi all'esempio del compilatore di shader HLSL ora.]

Windows 8.1 aggiunge funzionalità separate per la compilazione e il collegamento di shader HLSL, che consentono ai programmatori di grafica di creare funzioni HLSL precompilate, assemblarle in librerie e collegarle in shader completi in fase di esecuzione. Si tratta fondamentalmente di un processo equivalente alla compilazione, alle librerie e ai collegamenti separati di C/C++, che consente ai programmatori di comporre codice HLSL precompilato quando diventano disponibili ulteriori informazioni per finalizzare i calcoli.

I programmatori eseguono vari passaggi per creare uno shader finale tramite il collegamento dinamico in fase di esecuzione:

  • Creazione di un oggetto linker ID3D11Linker, che rappresenta un contesto di collegamento. Non è possibile usare un singolo contesto per produrre più shader. Si usa un contesto di collegamento per produrre un singolo shader e quindi il contesto di collegamento viene eliminato.

  • Uso di D3DLoadModule per caricare e impostare le librerie dai rispettivi blob.

  • Uso di D3DLoadModule per caricare e impostare un blob di shader di ingresso oppure creare uno shader con grafico di collegamento delle funzioni (vedi la prossima sezione).

  • Uso di ID3D11Module::CreateInstance per creare oggetti ID3D11ModuleInstance e quindi chiamata di funzioni su questi oggetti per riassociare le risorse ai rispettivi slot finali.

  • Aggiunta delle librerie al linker e quindi chiamata di ID3D11Linker::Link per produrre il bytecode dello shader finale che può poi essere caricato e usato nel runtime D3D corrente, proprio come uno shader completamente precompilato e collegato.

Grafico di collegamento delle funzioni (FLG, Function Linking Graph)

Windows 8.1 introduce anche il grafico di collegamento delle funzioni. Con il grafico di collegamento delle funzioni i programmatori possono costruire shader composti da una sequenza di chiamate di funzioni precompilate che si passano i valori l'una l'altra. Quando usi il grafico di collegamento delle funzioni, non serve scrivere codice HLSL e richiamare il compilatore HLSL. La struttura dello shader viene invece specificata a livello di programmazione tramite chiamate API C++. I nodi del grafico di collegamento delle funzioni rappresentano le firme di input e output e le chiamate di funzioni della libreria precompilate. L'ordine di registrazione dei nodi di chiamata delle funzioni determina la sequenza delle chiamate. Il nodo della firma di input deve essere specificato per primo, mentre il nodo della firma di output deve essere l'ultimo. I bordi del grafico di collegamento delle funzioni definiscono come i valori vengono passati da un nodo a un altro. I tipi di dati dei valori passati devono essere uguali perché non è prevista la conversione dei tipi implicita. Le regole per forma e swizzling seguono il comportamento di HLSL e i valori possono essere passati solo in avanti in questa sequenza. Per info sull'API del grafico di collegamento delle funzioni, vedi ID3D11FunctionLinkingGraph.

Compilatore HLSL incluso

[Accedi all'esempio del compilatore di shader HLSL ora.]

Il compilatore HLSL è ora incluso in Windows 8.1 e versioni successive. La maggiore parte delle API per la programmazione di shader può essere ora usata nelle app di Windows Store create per Windows 8.1 e versioni successive. Molte API per la programmazione di shader non potevano essere usate nelle app di Windows Store create per Windows 8. Le pagine di riferimento per queste API erano contrassegnate con una nota. Alcune API per shader (ad esempio, D3DCompileFromFile) possono però ancora essere usate solo per sviluppare app di Windows Store e non app da inviare a Windows Store. Le pagine di riferimento per queste API sono ancora contrassegnate con una nota.

Supporto dell'overlay della GPU

[Accedi alle catene di scambio in primo piano DirectX ora.]

Il supporto dell'overlay della GPU su più piani ti permette di mantenere un aspetto ottimale per le interfacce e la grafica 2D nella risoluzione nativa, mentre disegni le scene 3D in un buffer di frame ridotto e ridimensionato. Usa il nuovo metodo IDXGIOutput2::SupportsOverlays per stabilire se la piattaforma supporta più piani di disegno. Crea una nuova catena di scambio di overlay specificando il flag DXGI_SWAP_CHAIN_FLAG_FOREGROUND_LAYER (vedi DXGI_SWAP_CHAIN_FLAG).

Questa funzionalità consente di eseguire automaticamente il ridimensionamento e la composizione di due catene di scambio sull'hardware di overlay a funzione fissa, senza usare affatto le risorse della GPU. In questo modo rimangono disponibili più risorse della GPU per il codice di rendering dell'app. Le app possono già usare il codice per eseguire il rendering con più risoluzioni, ma ciò comporta copie e passaggi di composizione aggiuntivi, non ideali per le piattaforme meno potenti.

Puoi usare l'API IDXGISwapChain2::SetSourceSize per ridimensionare la catena di scambio '3D' inferiore a una risoluzione minore della nativa, mentre crei una nuova catena di scambio in primo piano con la risoluzione nativa, che contiene il contenuto '2D'. In questo modo puoi controllare il ridimensionamento di entrambe le catene di scambio.

Tiled Resources DirectX

[Accedi all'esempio di Tiled Resources Direct3D ora.]

Windows 8.1 include una nuova funzionalità Direct3D, detta Tiled Resources, che espone un modello di memoria grafica virtuale limitata alle app e permette quindi il mapping libero tra dati delle risorse logiche e memoria fisica. In questo modo è possibile creare risorse logiche di grandi dimensioni che usano piccole quantità di memoria fisica. Questa funzionalità risulta utile, ad esempio, per il terreno nei giochi e l'interfaccia utente delle app.

Per creare Tiled Resources si specifica il flag D3D11_RESOURCE_MISC_TILED. Vedi le funzioni API seguenti per l'uso della funzionalità Tiled Resources: UpdateTiles, UpdateTileMappings e GetResourceTiling.

Supporto della funzionalità Tiled Resources DirectX

Prima di usare la funzionalità Tiled Resources, devi scoprire se il dispositivo la supporta. Puoi usare il codice seguente per controllare il supporto della funzionalità Tiled Resources:


HRESULT hr = D3D11CreateDevice(
    nullptr,                    // Specify nullptr to use the default adapter.
    D3D_DRIVER_TYPE_HARDWARE,   // Create a device using the hardware graphics driver.
    0,                          // Should be 0 unless the driver is D3D_DRIVER_TYPE_SOFTWARE.
    creationFlags,              // Set debug and Direct2D compatibility flags.
    featureLevels,              // List of feature levels this app can support.
    ARRAYSIZE(featureLevels),   // Size of the list above.
    D3D11_SDK_VERSION,          // Always set this to D3D11_SDK_VERSION for Windows Store apps.
    &device,                    // Returns the Direct3D device created.
    &m_d3dFeatureLevel,         // Returns feature level of device created.
    &context                    // Returns the device immediate context.
    );

if (SUCCEEDED(hr))
{
    D3D11_FEATURE_DATA_D3D11_OPTIONS1 featureData;
    DX::ThrowIfFailed(
        device->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS1, &featureData, sizeof(featureData))
        );

    m_tiledResourcesTier = featureData.TiledResourcesTier;
}

API per la presentazione a bassa latenza Direct3D

[Accedi all'esempio di DirectXLatency ora.]

Windows 8.1 include un nuovo set di API per consentire alle app DirectX di presentare frame con minore latenza, consentendo così una risposta più rapida dell'interfaccia utente. Usando le nuove API, le app possono aspettare che la catena di scambio DXGI esegua la presentazione, invece della situazione in cui è la catena di scambio ad aspettare l'app. Come per il comportamento esistente di blocco alla presentazione, la latenza desiderata per i frame è regolabile tramite chiamate dell'API.

Uso dell'API di presentazione a bassa latenza

I programmatori possono usare l'API di presentazione a bassa latenza come segue.

  • Creare la catena di scambio con il flag DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT.

    
    DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0};
    swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT; // Enable GetFrameLatencyWaitableObject().
    
    
  • Le catene di scambio create con il flag DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT usano l'impostazione propria di latenza per catena di scambio invece di quella associata al dispositivo DXGI. La latenza per catena di scambio predefinita è 1 e garantisce che DXGI non accodi più di un frame per volta. In questo modo non solo si riduce la latenza ma ci si assicura anche che l'app eseguirà il rendering solo dopo ogni VSync, con un consumo energetico ridotto al minimo.

    È necessario trovare il giusto equilibrio tra l'impostazione della latenza e la quantità di lavoro di CPU/GPU eseguibile in parallelo. Se le attività di CPU e GPU serializzate di un'app superano 17 millisecondi (ms), la latenza dei frame dovrebbe essere impostata su 2, per garantire tempo sufficiente per l'esecuzione in parallelo delle attività di CPU/GPU. Se le attività della tua app per frame superano i 17 ms, imposta la latenza su 2 chiamando SetMaximumFrameLatency.

    
    DX::ThrowIfFailed(
        m_swapChain->SetMaximumFrameLatency(2)
        );
    
    

    Chiama quindi GetFrameLatencyWaitableObject per ottenere l'oggetto waitable della latenza del frame della catena di scambio.

    
    m_frameLatencyWaitableObject = m_swapChain->GetFrameLatencyWaitableObject();
    
    

    Nel ciclo di rendering assicurati di chiamare WaitForSingleObjectEx all'inizio di ogni ciclo. In questo modo ti assicuri che l'app attenda che la catena di scambio sia disponibile per nuove operazioni di rendering prima che l'app avvii il rendering di ogni nuovo frame.

    
    DWORD result = WaitForSingleObjectEx(
        m_frameLatencyWaitableObject,
        1000, // 1-second timeout (shouldn't ever occur)
        true
        );
    
    

Ridimensionamento del buffer di frame

Il nuovo ridimensionamento GPU permette di ridimensionare il buffer di frame per una grafica 3D uniforme. La nuova interfaccia IDXGISwapChain2 definisce un set di nuovi metodi per ottenere e impostare le dimensioni della catena di scambio di origine e definire la matrice di ridimensionamento per il buffer di frame.

API Trim DXGI e mapping dei buffer predefiniti

[Accedi all'esempio di rotazione della catena di scambio DXGI ora.]

Windows 8.1 aggiunge il nuovo metodo IDXGIDevice3::Trim DXGI, che permette alle app DirectX di rilasciare la memoria del dispositivo allocata dal driver di grafica, riducendo così il profilo della memoria del dispositivo quando è sospeso.

Windows 8.1 include inoltre una nuova operazione per il mapping dei buffer predefiniti per le app Direct3D che consente alla tua app di accedere direttamente ai buffer predefiniti di una GPU (se supportato dal dispositivo) senza dover ricorrere a un'operazione di copia intermedia più onerosa in un buffer temporaneo.

Multithreading con SurfaceImageSource

[Accedi all'esempio di interoperabilità tra DirectX e SurfaceImageSource in XAML ora.]

In Windows 8.1 le app possono anche accedere a elementi XAML (SIS) SurfaceImageSource e aggiornarli da un thread in background.

Le app possono ora chiamare ISurfaceImageSourceNativeWithD2D::BeginDraw da qualsiasi thread. In modo analogo, i nuovi metodi ISurfaceImageSourceNativeWithD2D:SuspendDraw e ISurfaceImageSourceNativeWithD2D:ResumeDraw consentono alle app di sospendere il disegno su un thread e riprenderlo su un altro.

Per sfruttare i vantaggi del multithreading, la tua app deve soddisfare queste condizioni:

  • Questi oggetti (se usati) devono essere impostati per il multithreading:
  • L'app chiama BeginDraw, SuspendDraw e ResumeDraw su qualsiasi thread, ma chiama EndDraw solo sul thread dell'interfaccia utente XAML. Tieni presente che non è obbligatorio che le app chiamino ResumeDraw prima di chiamare EndDraw e che tutti gli aggiornamenti di EndDraw inviati entro lo stesso tick di thread dell'interfaccia utente XAML verranno inviati nello stesso frame.

Ecco un esempio di codice che mostra come usare il multithreading con SurfaceImageSource.

// Create a SIS element and associate a multithreaded DirectX device
ComPtr<ISurfaceImageSourceNativeWithD2D> m_sisNative;

// ...

m_sisNative->SetDevice1(m_d3dDevice.Get());

POINT offset;
ComPtr<IDXGISurface> surface;

// Begin drawing.
HRESULT beginDrawHR = m_sisNative->BeginDraw1(
updateRect, IID_IDXGISurface, &surface, &offset);

// QI for target texture from DXGI surface.
ComPtr<ID3D11Texture2D> d3DTexture;
surface.As(&d3DTexture);

// Create render target view.
m_d3dDevice->CreateRenderTargetView(d3DTexture.Get(), nullptr,   
  &m_renderTargetView); 

// Render complex content.
// ...

// Suspend drawing before signaling the UI thread.
m_sisNative->SuspendDraw();

// Signal the UI thread that drawing is complete, so EndDraw() can be called.
// ...

// Call EndDraw() on the UI thread to commit changes for the current frame.
m_sisNative->EndDraw();

Aspetti fondamentali dei nuovi metodi ISurfaceImageSourceNative1::SetDevice1 e ISurfaceImageSourceNative1::BeginDraw1:

  • Diversamente da SetDevice(), SetDevice1() accetterà in alternativa ID2D1Device al posto di IDXGIDevice. Questo è un requisito per il supporto dell'invio in batch Direct2D su più superfici.

  • L'overload del nuovo metodo BeginDraw1() restituisce un contesto di dispositivo anziché restituire direttamente una superficie di scratch, se viene passato un dispositivo Direct2D a SetDevice.

Composizione DirectX interattiva di elementi visivi XAML

[Accedi all'esempio di interoperabilità tra DirectX e SwapChainPanel in XAML ora.]

Puoi usare la classe SwapChainPanel in Windows 8.1 per il rendering di contenuto grafico DirectX in un'app che usa XAML. In questo modo puoi ottenere prestazioni migliori e una latenza inferiore rispetto alla classe SurfaceImageSource usata in Windows 8, nonché restrizioni di layout ridotte rispetto alla classe SwapChainBackgroundPanel.

SwapChainPanel è simile a SwapChainBackgroundPanel in Windows 8, ma con minori restrizioni per quanto riguarda la posizione nella struttura ad albero XAML e l'uso. (Per SwapChainBackgroundPanel erano previste due restrizioni, ossia la visualizzazione a schermo intero e il posizionamento dietro tutto il contenuto XAML nell'albero di visualizzazione degli elementi).

SwapChainPanel supporta le opzioni di visualizzazione seguenti:

  • Dimensioni arbitrarie, trasformazioni e livelli di opacità

  • Interfoliazione con altri elementi visivi nella struttura ad albero XAML

  • Scorrimento con il controllo ScrollViewer

La tua app può anche avere più catene di scambio, ognuna per la presentazione di un singolo SwapChainPanel. Tieni presente però i possibili effetti negativi sulle prestazioni in caso di caricamento simultaneo di molte catene di scambio. Consigliamo di non usare più di quattro catene di scambio.

Gli oggetti SwapChainPanel vengono dichiarati come qualsiasi altro elemento XAML, in qualsiasi posizione della struttura ad albero degli elementi e con qualsiasi dimensione.

Ecco come dichiarare un SwapChainPanel nella tua pagina XAML.


<Page>
  <Grid>
    <SwapChainPanel x:Name=”mySwapChainPanel” Height=”300” Width=”400” />
    <!-- Additional XAML code ..>
</Grid>
</Page>


Come con SwapChainBackgroundPanel, le app creano una catena di scambio di supporto chiamando il metodo IDXGIFactory2::CreateSwapChainForComposition e associandola a un'istanza di SwapChainPanel. Puoi eseguire questa associazione usando un puntatore intelligente all'interfaccia ISwapChainPanelNative.

Per la catena di scambio creata valgono queste condizioni:

  • La catena di scambio può avere qualsiasi dimensione considerata valida dall'infrastruttura DXGI (DirectX Graphics Infrastructure) Microsoft.

  • La catena di scambio non viene estesa in caso di ridimensionamento da parte dell'utente. Il comportamento di ridimensionamento è invece simile all'impostazione di Stretch=None per un elemento Image.

  • Le app possono associare una catena di scambio (tramite il metodo ISwapChainPanelNative::SetSwapChain) solo sul thread dell'interfaccia utente principale (CoreWindow).

  • Una singola catena di scambio può essere associata a più SwapChainPanels. Le app possono eseguire il rendering e presentare la catena di scambio con qualsiasi intervallo, sia dal thread dell'interfaccia utente che da un thread in background, a condizione che vengano soddisfatte le condizioni per il multithreading DirectX.

Ecco un esempio di codice che mostra come associare una catena di scambio a un'istanza di SwapChainPanel.


ComPtr<ISwapChainPanelNative> panelNative;
 
reinterpret_cast<IUnknown*>(mySwapChainPanel)->QueryInterface(IID_PPV_ARGS(&panelNative));
panelNative->SetSwapChain(m_swapChain.Get());



Gestione di batch Direct2D con SurfaceImageSource

[Accedi all'esempio di interoperabilità tra DirectX e SurfaceImageSource in XAML ora.]

In Windows 8.1 le app possono aggiornare più elementi XAML SurfaceImageSource (SIS) e VirtualSurfaceImageSource (VSIS) senza richiedere un'operazione in batch Direct2D separata per ogni aggiornamento.

Le app possono scegliere esplicitamente questo nuovo comportamento di gestione dei batch chiamando il metodo QueryInterface e acquisendo un puntatore alla nuova interfaccia ISurfaceImageSourceNativeWithD2D.

Come per altre API di interoperabilità DirectX-XAML, queste sono interfacce COM "native" e non API di Windows Runtime, perché interagiscono direttamente con le API DirectX. Dopo l'accesso alle nuove interfacce, le app possono chiamare il metodo SetDevice1 su una delle interfacce e passare un oggetto ID2D1Device o IDXGIDevice.

Se l'app passa un dispositivo Direct2D, ciò indica che SIS deve supportare chiamate Direct2D in batch. Se viene chiamato il metodo BeginDraw sull'interfaccia ISurfaceImageSourceNativeWithD2D, l'app riceve un oggetto ID2D1DeviceContext invece di un oggetto IDXGISurface. Passa questo dispositivo al framework XAML che crea quindi la superficie corrispondente. Tutti i comandi di disegno inviati per ID2D1DeviceContext vengono quindi gestiti in batch (se possibile) con i comandi di disegno Direct2D per qualsiasi altro elemento SIS che:

Questo nuovo comportamento è ottimale quando un'app deve aggiornare contemporaneamente due elementi SIS con contenuto Direct2D. L'app usa la nuova funzionalità di batch per condividere un dispositivo e un contesto Direct2D, evitando così lo scaricamento dei batch Direct2D tra ogni aggiornamento SIS. Miglioramento delle prestazioni grazie alla riduzione del lavoro per la CPU.

Ecco un esempio di codice che mostra come eseguire tutto ciò.

// Create SIS elements and associate a multithreaded DirectX device.
ComPtr<ISurfaceImageSourceNativeWithD2D> m_sisNative1;
ComPtr<ISurfaceImageSourceNativeWithD2D> m_sisNative2;

// ...

m_sisNative1->SetDevice1(m_d3dDevice.Get());
m_sisNative2->SetDevice1(m_d3dDevice.Get());

// Set device for both SIS elements.
m_sisNative1->SetDevice(m_d2dDevice.Get());
m_sisNative2->SetDevice(m_d2dDevice.Get());

POINT offset;
ComPtr<ID2D1DeviceContext> d2dContext;

// Begin drawing SIS #1. 
HRESULT beginDrawHR = m_sisNative1->BeginDraw(
updateRect, IID_ID2D1DeviceContext, &d2dContext, &offset);
 
if (beginDrawHR == DXGI_ERROR_DEVICE_REMOVED || 
    beginDrawHR == DXGI_ERROR_DEVICE_RESET)
{ // Error checking }

// App does not call BeginDraw/EndDraw on Direct2D context;
// this is managed by the framework.

// Issue Direct2D drawing commands for SIS #1.
d2dContext->FillRectangle(D2D1::RectF(0,0,10,10), m_brush1.Get());

// Begin drawing SIS #2. 
beginDrawHR = m_sisNative2->BeginDraw(
updateRect, IID_ID2D1DeviceContext, &d2dContext, &offset);
 
if (beginDrawHR == DXGI_ERROR_DEVICE_REMOVED || 
    beginDrawHR == DXGI_ERROR_DEVICE_RESET)
{ // Error checking. }

// Issue Direct2D drawing commands for SIS #2.
d2dContext ->FillRectangle(D2D1::RectF(20,20,30,30), m_brush2.Get());

Nota  Ecco alcuni suggerimenti aggiuntivi:

 

 

Mostra:
© 2014 Microsoft