Question

Je dois trouver comment récupérer les données des textures et surfaces D3D dans la mémoire système. Quel est le moyen le plus rapide de faire de telles choses et comment?

De plus, si je n'ai besoin que d'un sous-arbre, comment peut-on relire uniquement cette partie sans avoir à relire l'intégralité de la mémoire dans la mémoire système?

En résumé, je cherche des descriptions concises sur la manière de copier les éléments suivants dans la mémoire système :

  1. une texture
  2. un sous-ensemble d'une texture
  3. une surface
  4. un sous-ensemble d'une surface
  5. une texture D3DUSAGE_RENDERTARGET
  6. un sous-ensemble d'une texture D3DUSAGE_RENDERTARGET

Ceci est Direct3D 9, mais des réponses sur les versions plus récentes de D3D seraient également appréciées.

Était-ce utile?

La solution

La partie la plus impliquée consiste à lire une surface de la mémoire vidéo ("pool par défaut"). C’est le plus souvent les cibles de rendu.

Commençons par les parties les plus faciles:

  1. lire une texture est identique à lire une surface de niveau 0 sur cette texture. Voir ci-dessous.
  2. identique pour le sous-ensemble d'une texture.
  3. lire sur une surface située dans un pool de mémoire autre que celui par défaut ("système" ou "géré") ne fait que le verrouiller et la lecture d'octets.
  4. identique pour le sous-ensemble de la surface. Verrouillez simplement la partie pertinente et lisez-la.

Nous avons donc maintenant des surfaces laissées dans la mémoire vidéo ("pool par défaut"). Cela peut être n'importe quelle surface / texture marquée comme cible de rendu, ou toute surface / texture régulière que vous avez créée dans le pool par défaut, ou le tampon arrière lui-même. La partie complexe ici est que vous ne pouvez pas le verrouiller.

La réponse courte est la suivante: Méthode GetRenderTargetData sur Périphérique D3D.

Réponse plus longue (aperçu du code ci-dessous):

  1. rt = obtenir le rendu de la surface cible (il peut s’agir de la surface de la texture, de la mémoire tampon, etc.)
  2. si rt est multi-échantillon (GetDesc, vérifiez D3DSURFACE_DESC.MultiSampleType), puis: a) créez une autre surface cible de rendu de même taille, de même format mais sans sans multi-échantillonnage; b) StretchRect de rt dans cette nouvelle surface; c) rt = cette nouvelle surface (c'est-à-dire, continuez sur cette nouvelle surface).
  3. désactivé = créer une surface plane hors écran (CreateOffscreenPlainSurface, pool D3DPOOL_SYSTEMMEM)
  4. device- > GetRenderTargetData ( rt , désactivé )
  5. now off contient les données cible de rendu. LockRect (), lecture de données, UnlockRect () dessus.
  6. nettoyage

Une réponse encore plus longue (coller à partir de la base de code sur laquelle je travaille) suit. Cela ne sera pas compilé immédiatement, car il utilise certaines classes, fonctions, macros et utilitaires du reste de codebase; mais cela devrait vous aider à démarrer. J'ai également omis la plupart des erreurs de vérification (par exemple, si la largeur / hauteur donnée est hors limites). J'ai également omis la partie qui lit les pixels réels et éventuellement les convertit au format de destination approprié (c'est assez facile, mais cela peut être long, en fonction du nombre de conversions de format que vous souhaitez prendre en charge).

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 dans le code ci-dessus est un pointeur intelligent sur un objet COM (il libère un objet lors d'une affectation ou d'un destructeur). Simplifie beaucoup la gestion des erreurs. Ceci est très similaire à _comptr_t dans Visual C ++.

Le code ci-dessus lit toute la surface. Si vous voulez en lire juste une partie efficacement, alors je pense que le moyen le plus rapide est à peu près:

  1. créer une surface de pool par défaut de la taille requise.
  2. StretchRect d'une partie de la surface d'origine à celle plus petite.
  3. procédez normalement avec le plus petit.

En fait, cela ressemble beaucoup à ce que le code ci-dessus fait pour gérer des surfaces multi-échantillonnées. Si vous souhaitez obtenir uniquement une partie d'une surface multi-échantillonnée, vous pouvez effectuer une résolution sur plusieurs échantillons et la placer dans un seul StretchRect, je pense.

Modifier : élément de code supprimé qui permet la lecture réelle des pixels et des conversions de format. N'était pas directement lié à la question, et le code était long.

Modifier : mis à jour pour correspondre à la question modifiée.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top