Procedura: Usare le risorse dinamiche

API importanti

È possibile creare e usare risorse dinamiche quando l'app deve modificare i dati in tali risorse. È possibile creare trame e buffer per l'utilizzo dinamico.

Informazioni importanti

Tecnologie

Prerequisiti

Partiamo dal presupposto che tu abbia familiarità con C++. Devi inoltre avere un'esperienza di base dei concetti di programmazione di grafica.

Istruzioni

Passaggio 1: Specificare l'utilizzo dinamico

Se si vuole che l'app possa apportare modifiche alle risorse, è necessario specificare tali risorse come dinamico e scrivibile quando vengono create.

Per specificare l'utilizzo dinamico

  1. Specificare l'utilizzo delle risorse come dinamico. Ad esempio, specificare il valore D3D11_USAGE_DYNAMIC nel membro Utilizzo di D3D11_BUFFER_DESC per un vertice o un buffer costante e specificare il valore D3D11_USAGE_DYNAMIC nel membro UtilizzodiD3D11_TEXTURE2D_DESC per una trama 2D.
  2. Specificare la risorsa come scrivibile. Ad esempio, specificare il valore D3D11_CPU_ACCESS_WRITE nel membro CPUAccessFlags di D3D11_BUFFER_DESC o D3D11_TEXTURE2D_DESC.
  3. Passare la descrizione della risorsa alla funzione di creazione. Ad esempio, passare l'indirizzo di D3D11_BUFFER_DESC a ID3D11Device::CreateBuffer e passare l'indirizzo di D3D11_TEXTURE2D_DESC a ID3D11Device::CreateTexture2D.

Ecco un esempio di creazione di un buffer di vertice dinamico:

    // Create a vertex buffer for a triangle.

    float2 triangleVertices[] =
    {
        float2(-0.5f, -0.5f),
        float2(0.0f, 0.5f),
        float2(0.5f, -0.5f),
    };

    D3D11_BUFFER_DESC vertexBufferDesc = { 0 };
    vertexBufferDesc.ByteWidth = sizeof(float2)* ARRAYSIZE(triangleVertices);
    vertexBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
    vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    vertexBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    vertexBufferDesc.MiscFlags = 0;
    vertexBufferDesc.StructureByteStride = 0;

    D3D11_SUBRESOURCE_DATA vertexBufferData;
    vertexBufferData.pSysMem = triangleVertices;
    vertexBufferData.SysMemPitch = 0;
    vertexBufferData.SysMemSlicePitch = 0;

    DX::ThrowIfFailed(
        m_d3dDevice->CreateBuffer(
        &vertexBufferDesc,
        &vertexBufferData,
        &vertexBuffer2
        )
        );

Passaggio 2: Modificare una risorsa dinamica

Se è stata specificata una risorsa come dinamica e scrivibile al momento della creazione, è possibile apportare modifiche a tale risorsa.

Per modificare i dati in una risorsa dinamica

  1. Configurare i nuovi dati per la risorsa. Dichiarare una variabile di tipo D3D11_MAPPED_SUBRESOURCE e inizializzarla su zero. Questa variabile verrà usata per modificare la risorsa.
  2. Disabilitare l'accesso dell'unità di elaborazione grafica (GPU) ai dati che si desidera modificare e ottenere un puntatore alla memoria contenente i dati. Per ottenere questo puntatore, passare D3D11_MAP_WRITE_DISCARD al parametro MapType quando si chiama ID3D11DeviceContext::Map. Impostare questo puntatore sull'indirizzo della variabile D3D11_MAPPED_SUBRESOURCE dal passaggio precedente.
  3. Scrivere i nuovi dati nella memoria.
  4. Chiamare ID3D11DeviceContext::Unmap per ripristinare l'accesso alla GPU ai dati al termine della scrittura dei nuovi dati.

Ecco un esempio di modifica di un buffer del vertice dinamico:

// This might get called as the result of a click event to double the size of the triangle.
void TriangleRenderer::MapDoubleVertices()
{
    D3D11_MAPPED_SUBRESOURCE mappedResource;
    ZeroMemory(&mappedResource, sizeof(D3D11_MAPPED_SUBRESOURCE));
    float2 vertices[] =
    {
        float2(-1.0f, -1.0f),
        float2(0.0f, 1.0f),
        float2(1.0f, -1.0f),
    };
    //  Disable GPU access to the vertex buffer data.
    auto m_d3dContext = m_deviceResources->GetD3DDeviceContext();
    m_d3dContext->Map(vertexBuffer2.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
    //  Update the vertex buffer here.
    memcpy(mappedResource.pData, vertices, sizeof(vertices));
    //  Reenable GPU access to the vertex buffer data.
    m_d3dContext->Unmap(vertexBuffer2.Get(), 0);
}

Commenti

Uso di trame dinamiche

È consigliabile creare una sola trama dinamica per formato e possibilmente per dimensione. Non è consigliabile creare mipmap dinamici, cubi e volumi a causa del sovraccarico aggiuntivo nel mapping di ogni livello. Per mipmap, è possibile specificare D3D11_MAP_WRITE_DISCARD solo al livello superiore. Tutti i livelli vengono ignorati eseguendo il mapping solo al livello superiore. Questo comportamento è lo stesso per volumi e cubi. Per i cubi, il livello superiore e il viso 0 vengono mappati.

Uso di buffer dinamici

Quando si chiama Map on a static vertex buffer mentre la GPU usa il buffer, si ottiene una significativa penalità delle prestazioni. In questa situazione, map deve attendere fino a quando la GPU non viene completata la lettura dei vertici o i dati di indice dal buffer prima che Map possa tornare all'app chiamante, che causa un ritardo significativo. La chiamata a Mapping e quindi il rendering da un buffer statico più volte per frame impedisce anche al rendering della GPU di eseguire il buffering dei comandi perché deve completare i comandi prima di restituire il puntatore della mappa. Senza comandi memorizzati nel buffer, la GPU rimane inattiva fino al termine del completamento del riempimento del buffer del vertice o del buffer di indice e genera un comando di rendering.

Idealmente i dati del vertice o dell'indice non cambiano mai, ma questo non è sempre possibile. Esistono molte situazioni in cui l'app deve modificare i dati del vertice o dell'indice ogni frame, forse anche più volte per frame. Per queste situazioni, è consigliabile creare il vertice o il buffer di indice con D3D11_USAGE_DYNAMIC. Questo flag di utilizzo causa l'ottimizzazione del runtime per le operazioni di mappa frequenti. D3D11_USAGE_DYNAMIC è utile solo quando il buffer viene mappato spesso; se i dati rimangono costanti, posizionare i dati in un vertice statico o in un buffer di indice.

Per ricevere un miglioramento delle prestazioni quando si usano buffer di vertice dinamici, l'app deve chiamare Map con i valori di D3D11_MAP appropriati. D3D11_MAP_WRITE_DISCARD indica che l'app non deve mantenere i dati del vertice o dell'indice precedenti nel buffer. Se la GPU usa ancora il buffer quando si chiama Map with D3D11_MAP_WRITE_DISCARD, il runtime restituisce un puntatore a una nuova area di memoria anziché ai dati del buffer precedente. Ciò consente alla GPU di continuare a usare i dati precedenti mentre l'app inserisce i dati nel nuovo buffer. Non è necessaria alcuna gestione di memoria aggiuntiva nell'app; il buffer precedente viene riutilizzato o eliminato automaticamente al termine della GPU.

Nota

Quando si esegue il mapping di un buffer con D3D11_MAP_WRITE_DISCARD, il runtime elimina sempre l'intero buffer. Non è possibile conservare informazioni in aree non mappate del buffer specificando un campo di dimensioni non zero o limitato.

 

Esistono casi in cui la quantità di dati che l'app deve archiviare per mappa è piccola, ad esempio l'aggiunta di quattro vertici per eseguire il rendering di uno sprite. D3D11_MAP_WRITE_NO_OVERWRITE indica che l'app non sovrascrive i dati già in uso nel buffer dinamico. La chiamata Mappa restituirà un puntatore ai dati precedenti, che consentirà all'app di aggiungere nuovi dati nelle aree inutilizzate del vertice o del buffer di indice. L'app non deve modificare vertici o indici usati in un'operazione di disegno perché potrebbero essere ancora in uso dalla GPU. È consigliabile che l'app usi quindi D3D11_MAP_WRITE_DISCARD dopo che il buffer dinamico è pieno per ricevere una nuova area di memoria, che elimina i dati del vertice o dell'indice precedenti dopo il completamento della GPU.

Il meccanismo di query asincrona è utile per determinare se i vertici sono ancora in uso dalla GPU. Eseguire una query di tipo D3D11_QUERY_EVENT dopo l'ultima chiamata di disegno che usa i vertici. I vertici non vengono più usati quando ID3D11DeviceContext::GetData restituisce S_OK. Quando si esegue il mapping di un buffer con D3D11_MAP_WRITE_DISCARD o nessun valore mappa, è sempre garantito che i vertici vengano sincronizzati correttamente con la GPU. Tuttavia, quando si esegue il mapping senza valori mappa, si incorrerà nella penalità delle prestazioni descritta in precedenza.

Nota

Il runtime Direct3D 11.1, disponibile a partire da Windows 8, consente di eseguire il mapping di buffer costanti dinamiche e viste delle risorse shader (SRV) di buffer dinamici con D3D11_MAP_WRITE_NO_OVERWRITE. I runtime Direct3D 11 e versioni precedenti non sovrascrivono il mapping di aggiornamento parziale al vertice o ai buffer di indice. Per determinare se un dispositivo Direct3D supporta queste funzionalità, chiamare ID3D11Device::CheckFeatureSupport con D3D11_FEATURE_D3D11_OPTIONS. CheckFeatureSupport riempie i membri di una struttura D3D11_FEATURE_DATA_D3D11_OPTIONS con le funzionalità del dispositivo. I membri pertinenti sono MapNoOverwriteOnDynamicConstantBuffer e MapNoOverwriteOnDynamicBufferSRV.

 

Come usare Direct3D 11