Domanda

Devo capire come recuperare i dati dalle trame e superfici D3D alla memoria di sistema. Qual è il modo più veloce per fare queste cose e come?

Inoltre, se ho bisogno di un solo argomento, come si può leggere solo quella parte senza dover rileggere l'intera cosa nella memoria di sistema?

In breve sto cercando descrizioni concise di come copiare quanto segue nella memoria di sistema :

  1. una trama
  2. un sottoinsieme di una trama
  3. una superficie
  4. un sottoinsieme di una superficie
  5. una trama D3DUSAGE_RENDERTARGET
  6. un sottoinsieme di una trama D3DUSAGE_RENDERTARGET

Questo è Direct3D 9, ma sarebbero apprezzate anche le risposte sulle versioni più recenti di D3D.

È stato utile?

Soluzione

La parte più coinvolta è la lettura da una superficie presente nella memoria video ("pool predefinito"). Questo è spesso il rendering di obiettivi.

Vediamo prima le parti facili:

  1. leggere da una trama equivale a leggere dalla superficie a livello 0 di quella trama. Vedi sotto.
  2. lo stesso per il sottoinsieme di una trama.
  3. leggere da una superficie che si trova nel pool di memoria non predefinito ("sistema" o "gestito") lo sta semplicemente bloccando e leggendo i byte.
  4. lo stesso per il sottoinsieme di superfici. Basta bloccare la parte pertinente e leggerla.

Quindi ora abbiamo lasciato le superfici che si trovano nella memoria video ("pool predefinito"). Si tratterebbe di qualsiasi superficie / trama contrassegnata come destinazione di rendering, o qualsiasi superficie / trama normale creata nel pool predefinito o il backbuffer stesso. La parte complessa qui è che non puoi bloccarlo.

La risposta breve è: Metodo GetRenderTargetData su Dispositivo D3D.

Risposta più lunga (una descrizione approssimativa del codice che sarà riportato di seguito):

  1. rt = ottieni la superficie di destinazione del rendering (può essere la superficie della trama o il backbuffer, ecc.)
  2. se rt è multicampionato (GetDesc, controlla D3DSURFACE_DESC.MultiSampleType), quindi: a) crea un'altra superficie target di rendering della stessa dimensione, stesso formato ma senza multisampling; b) StretchRect da rt in questa nuova superficie; c) rt = questa nuova superficie (ovvero procedere su questa nuova superficie).
  3. off = crea superficie normale offscreen (CreaOffscreenPlainSurface, pool D3DPOOL_SYSTEMMEM)
  4. dispositivo- > GetRenderTargetData ( rt , spento )
  5. ora spento contiene i dati di destinazione del rendering. LockRect (), leggi i dati, UnlockRect () su di esso.
  6. pulizia

Segue una risposta ancora più lunga (incolla dalla base di codice su cui sto lavorando). Questo non verrà compilato per impostazione predefinita, poiché utilizza alcune classi, funzioni, macro e utilità del resto della base di codice; ma dovrebbe iniziare. Ho anche omesso la maggior parte del controllo degli errori (ad es. Se la larghezza / altezza dati è fuori dai limiti). Ho anche omesso la parte che legge i pixel effettivi e li converte eventualmente in un formato di destinazione adatto (è abbastanza facile, ma può richiedere molto tempo, a seconda del numero di conversioni di formato che desideri supportare).

bool GfxDeviceD3D9::ReadbackImage( /* params */ )
{
    HRESULT hr;
    IDirect3DDevice9* dev = GetD3DDevice();
    SurfacePointer renderTarget;
    hr = dev->GetRenderTarget( 0, &renderTarget );
    if( !renderTarget || FAILED(hr) )
        return false;

    D3DSURFACE_DESC rtDesc;
    renderTarget->GetDesc( &rtDesc );

    SurfacePointer resolvedSurface;
    if( rtDesc.MultiSampleType != D3DMULTISAMPLE_NONE )
    {
        hr = dev->CreateRenderTarget( rtDesc.Width, rtDesc.Height, rtDesc.Format, D3DMULTISAMPLE_NONE, 0, FALSE, &resolvedSurface, NULL );
        if( FAILED(hr) )
            return false;
        hr = dev->StretchRect( renderTarget, NULL, resolvedSurface, NULL, D3DTEXF_NONE );
        if( FAILED(hr) )
            return false;
        renderTarget = resolvedSurface;
    }

    SurfacePointer offscreenSurface;
    hr = dev->CreateOffscreenPlainSurface( rtDesc.Width, rtDesc.Height, rtDesc.Format, D3DPOOL_SYSTEMMEM, &offscreenSurface, NULL );
    if( FAILED(hr) )
        return false;

    hr = dev->GetRenderTargetData( renderTarget, offscreenSurface );
    bool ok = SUCCEEDED(hr);
    if( ok )
    {
        // Here we have data in offscreenSurface.
        D3DLOCKED_RECT lr;
        RECT rect;
        rect.left = 0;
        rect.right = rtDesc.Width;
        rect.top = 0;
        rect.bottom = rtDesc.Height;
        // Lock the surface to read pixels
        hr = offscreenSurface->LockRect( &lr, &rect, D3DLOCK_READONLY );
        if( SUCCEEDED(hr) )
        {
            // Pointer to data is lt.pBits, each row is
            // lr.Pitch bytes apart (often it is the same as width*bpp, but
            // can be larger if driver uses padding)

            // Read the data here!
            offscreenSurface->UnlockRect();
        }
        else
        {
            ok = false;
        }
    }

    return ok;
}

SurfacePointer nel codice sopra è un puntatore intelligente a un oggetto COM (rilascia oggetto su assegnazione o distruttore). Semplifica molto la gestione degli errori. Questo è molto simile a _comptr_t in Visual C ++.

Il codice sopra legge tutta la superficie. Se vuoi leggerne una parte in modo efficiente, credo che il modo più veloce sia approssimativamente:

  1. crea una superficie di pool predefinita delle dimensioni necessarie.
  2. StretchRect da una parte della superficie originale a quella più piccola.
  3. procedi normalmente con quello più piccolo.

In realtà questo è abbastanza simile a quello che fa il codice sopra per gestire superfici multicampionate. Se vuoi ottenere solo una parte di una superficie multi-campionata, puoi fare una risoluzione multisample e farne parte in un tratto StretchRect, credo.

Modifica : rimosso il pezzo di codice che legge effettivamente i pixel e formatta le conversioni. Non era direttamente correlato alla domanda e il codice era lungo.

Modifica : aggiornato per corrispondere alla domanda modificata.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top