Partager via


Partage de surface entre les API graphiques Windows

Cette rubrique présente une vue d’ensemble technique de l’interopérabilité à l’aide du partage de surface entre les API graphiques Windows, notamment Direct3D 11, Direct2D, DirectWrite, Direct3D 10 et Direct3D 9Ex. Si vous connaissez déjà ces API, cette rubrique peut vous aider à utiliser plusieurs API pour effectuer un rendu sur la même surface dans une application conçue pour les systèmes d’exploitation Windows 7 ou Windows Vista. Elle fournit également les bonnes pratiques recommandées ainsi que des liens vers des ressources supplémentaires.

Remarque

Pour l’interopérabilité Direct2D et DirectWrite sur le runtime DirectX 11.1, vous pouvez utiliser des périphériques et des contextes Direct2D pour effectuer un rendu directement sur des appareils Direct3D 11.

 

Cette rubrique contient les sections suivantes.

Introduction

Dans ce document, l’interopérabilité de l’API graphique Windows désigne le partage d'une même surface de rendu par différentes API. Cette interopérabilité permet aux applications de créer des affichages attrayants grâce à plusieurs API graphiques Windows, et de faciliter la migration vers de nouvelles technologies tout en conservant la compatibilité avec les API existantes.

Dans Windows 7 (et Windows Vista SP2 avec Windows 7 Interop Pack, Vista 7IP), les API de rendu graphique sont Direct3D 11, Direct2D, Direct3D 10.1, Direct3D 10.0, Direct3D 9Ex, Direct3D 9c et les API Direct3D antérieures, ainsi que GDI et GDI+. Le composant d’imagerie Windows (WIC) et DirectWrite sont des technologies connexes pour le traitement d’images, tandis que Direct2D effectue le rendu du texte. L’API d’accélération vidéo DirectX (DXVA), basée sur Direct3D 9c et Direct3D 9Ex, sert pour le traitement vidéo.

À mesure que les API graphiques Windows adoptent progressivement Direct3D, Microsoft s'efforce encore davantage de garantir l’interopérabilité entre les API. Les API Direct3D nouvellement développées et les API de niveau supérieur basées sur les API Direct3D permettent également de combler les lacunes en matière de compatibilité avec les API plus anciennes, si nécessaire. Par exemple, les applications Direct2D peuvent utiliser Direct3D 10.1 en partageant un appareil Direct3D 10.1. Par ailleurs, les API Direct3D 11, Direct2D et Direct3D 10.1 peuvent toutes utiliser DirectX Graphics Infrastructure (DXGI) 1.1. Cela signifie qu'elles peuvent partager des surfaces d'une manière synchronisée et qui permet une interopérabilité complète entre ces API. Les API basées DXGI 1.1 interagissent avec GDI et GDI+ par association. Autrement dit, elles obtiennent le contexte d’appareil GDI à partir d’une surface DXGI 1.1.

Le partage de surface non synchronisé est compatible avec le runtime Direct3D 9Ex. Les applications vidéo basées sur DXVA peuvent utiliser l’assistant d’interopérabilité Direct3D 9Ex et DXGI pour l’interopérabilité DXVA basée sur Direct3D 9Ex avec Direct3D 11 pour le nuanceur de calcul. Elles peuvent également interagir avec Direct2D pour les contrôles 2D ou le rendu de texte. WIC et DirectWrite interagissent également avec GDI, Direct2D et, par association, d’autres API Direct3D.

Direct3D 10.0, Direct3D 9c et les runtimes Direct3D plus anciens ne sont pas compatibles avec les surfaces partagées. Les copies de mémoire système continueront d’être utilisées à des fins d'interopérabilité avec les API basées sur GDI ou DXGI.

Notez que les scénarios d’interopérabilité de ce document font référence à plusieurs API graphiques qui effectuent un rendu sur une surface de rendu partagée, plutôt que sur la même fenêtre d’application. La synchronisation pour les API distinctes ciblant différentes surfaces, et qui sont ensuite composées sur la même fenêtre ne sera pas mentionnée dans le présent document.

Vue d'ensemble de l'interopérabilité des API

L’interopérabilité du partage de surface des API graphiques Windows peut être décrite en termes de scénarios d'API à API, qui s'accompagnent d'une fonctionnalité d’interopérabilité. À compter de Windows 7 et de Windows Vista SP2 avec 7IP, les nouvelles API et les runtimes associés incluent Direct2D et les technologies connexes, à savoir Direct3D 11 et DXGI 1.1. Les performances de GDI ont également été améliorées dans Windows 7. Direct3D 10.1 a été introduit dans Windows Vista SP1. Le diagramme suivant illustre la prise en charge de l’interopérabilité entre les API.

diagramme illustrant la prise en charge de l’interopérabilité entre les API graphiques Windows

Dans ce diagramme, les flèches illustrent des scénarios d’interopérabilité dans lesquels les API connectées peuvent accéder à une même surface. Les flèches bleues indiquent les mécanismes d’interopérabilité introduits dans Windows Vista. Les flèches vertes indiquent la prise en charge de l’interopérabilité des nouvelles API ou des améliorations qui aident les API plus anciennes à interagir avec les API plus récentes. Par exemple, les flèches vertes représentent le partage d’appareils, la prise en charge des surfaces partagées synchronisées, l’assistant de synchronisation Direct3D 9Ex/DXGI ou encore l’obtention d’un contexte d’appareil GDI à partir d’une surface compatible.

Scénarios d’interopérabilité

À compter de Windows 7 et de Windows Vista 7IP, les offres standard des API graphiques Windows prennent en charge le rendu de plusieurs API sur la même surface DXGI 1.1.

Interopérabilité de Direct 3D 11, Direct3D 10.1 et Direct2D

Les API Direct3D 11, Direct3D 10.1 et Direct2D (et ses API connexes, comme DirectWrite et WIC) peuvent interagir entre elles à l’aide du partage d’appareils Direct3D 10.1 ou de surfaces partagées synchronisées.

Partage d’appareils Direct3D 10.1 avec Direct2D

Le partage d’appareils entre Direct2D et Direct3D 10.1 permet à une application donnée d’utiliser les deux API pour effectuer un rendu efficacement et de manière fluide sur la même surface DXGI 1.1, à l’aide du même objet d’appareil Direct3D sous-jacent. Direct2D permet d’appeler des API Direct2D à l’aide d’un appareil Direct3D 10.1 existant, en tirant parti du fait que Direct2D est basé sur les runtimes Direct3D 10.1 et DXGI 1.1. Les extraits de code suivants illustrent la façon dont Direct2D obtient la cible de rendu d’appareil Direct3D 10.1 à partir d’une surface DXGI 1.1 associée à l’appareil. La cible de rendu de l’appareil Direct3D 10.1 peut exécuter des appels « drawing » Direct2D entre les API BeginDraw et EndDraw.

// Direct3D 10.1 Device and Swapchain creation
HRESULT hr = D3D10CreateDeviceandSwapChain1(
                pAdapter,
                DriverType,
                Software,
                D3D10_CREATE_DEVICE_BGRA_SUPPORT,
                featureLevel,
                D3D10_1_SDK_VERSION,
                pSwapChainDesc,
                &pSwapChain,
                &pDevice
                );

hr = pSwapChain->GetBuffer(
        0,
        __uuidof(IDXGISurface),
        (void **)&pDXGIBackBuffer
        ));

// Direct3D 10.1 API rendering calls
...

hr = D2D1CreateFactory(
        D2D1_FACTORY_TYPE_SINGLE_THREADED,
        &m_spD2DFactory
        ));

pD2DFactory->CreateDxgiSurfaceRenderTarget(
        pDXGIBackBuffer,
        &renderTargetProperties,
        &pD2DBackBufferRenderTarget
        ));
...

pD2DBackBufferRenderTarget->BeginDraw();
//Direct2D API rendering calls
...

pD2DBackBufferRenderTarget->EndDraw();

pSwapChain->Present(0, 0);

Remarques

  • L’appareil Direct3D 10.1 associé doit prendre en charge le format BGRA. Cet appareil a été créé en appelant D3D10CreateDevice1 avec le paramètre D3D10_CREATE_DEVICE_BGRA_SUPPORT. Le format BGRA est pris en charge à partir du niveau de fonctionnalité 9.1 de Direct3D 10.
  • L’application ne doit pas créer plusieurs ID2D1RenderTargets effectuant une association au même appareil Direct3D10.1.
  • Pour des performances optimales, conservez au moins une ressource à tout moment, comme des textures ou des surfaces associées à l’appareil.

Le partage d’appareils convient à l’utilisation in-process et monothread d’un appareil de rendu partagé par les API de rendu Direct3D 10.1 et Direct2D. Les surfaces partagées synchronisées permettent l’utilisation multithread, in-process et out-of-process de plusieurs appareils de rendu utilisés par les API Direct3D 10.1, Direct2D et Direct3D 11.

Vous pouvez également, à des fins d’interopérabilité Direct3D 10.1 et Direct2D, utiliser ID3D1RenderTarget::CreateSharedBitmap. Cette fonction crée un objet ID2D1Bitmap à partir d’IDXGISurface. Vous pouvez rédiger une scène Direct3D10.1 dans le bitmap et en effectuer le rendu avec Direct2D. Pour en savoir plus, consultez la méthode ID2D1RenderTarget::CreateSharedBitmap.

Rastérisation logicielle Direct2D

Le partage d’appareils avec Direct3D 10.1 n’est pas compatible avec l’utilisation du convertisseur logiciel Direct2D, par exemple en spécifiant D2D1_RENDER_TARGET_USAGE_FORCE_SOFTWARE_RENDERING dans D2D1_RENDER_TARGET_USAGE lorsque vous créez une cible de rendu Direct2D.

Direct2D peut utiliser la rastérisation logicielle WARP10 pour partager l’appareil avec Direct3D 10 ou Direct3D 11. Toutefois, les performances seront considérablement amoindries.

Surfaces partagées synchronisées DXGI 1.1

Les API Direct3D 11, Direct3D 10.1 et Direct2D utilisent toutes DXGI 1.1, qui permet de synchroniser la lecture et l’écriture d'au moins deux appareils Direct3D dans la même surface de mémoire vidéo (DXGISurface1). Les appareils de rendu qui utilisent des surfaces partagées synchronisées peuvent être des appareils Direct3D 10.1 ou Direct3D 11. Chacun de ces appareils s'exécute dans le même processus ou sur plusieurs processus.

Les applications peuvent utiliser des surfaces partagées synchronisées pour favoriser les interactions entre les appareils basés DXGI 1.1, tel que Direct3D 11 et Direct3D 10.1, ou entre Direct3D 11 et Direct2D. Pour ce faire, elles doivent obtenir l’appareil Direct3D 10.1 à partir de l’objet cible de rendu Direct2D.

Dans les API Direct3D 10.1 et ultérieures, pour utiliser DXGI 1.1, vérifiez que l’appareil Direct3D est créé à l’aide d’un objet adaptateur DXGI 1.1, lequel est énuméré à partir de l’objet d’usine DXGI 1.1. Appelez CreateDXGIFactory1 pour créer l’objet IDXGIFactory1 et EnumAdapters1 pour énumérer l’objet IDXGIAdapter1. L’objet IDXGIAdapter1 doit être transmis dans le cadre de l’appel de D3D10CreateDevice ou de D3D10CreateDeviceAndSwapChain. Pour en savoir plus sur les API DXGI 1.1, consultez le Guide de programmation pour DXGI.

API

D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX
Lorsque vous créez la ressource partagée synchronisée, définissez D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX dans D3D10_RESOURCE_MISC_FLAG.

typedef enum D3D10_RESOURCE_MISC_FLAG {
    D3D10_RESOURCE_MISC_GENERATE_MIPS      = 0x1L,
    D3D10_RESOURCE_MISC_SHARED             = 0x2L,
    D3D10_RESOURCE_MISC_TEXTURECUBE        = 0x4L,
    D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX  = 0x10L,
    D3D10_RESOURCE_MISC_GDI_COMPATIBLE     = 0x20L,
}   D3D10_RESOURCE_MISC_FLAG;

D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX
Permet de synchroniser la ressource créée à l’aide des API IDXGIKeyedMutex::AcquireSync et ReleaseSync. Les API Direct3D 10.1 de création de ressources suivantes qui prennent toutes un paramètre D3D10_RESOURCE_MISC_FLAG ont été étendues pour prendre en charge le nouvel indicateur.

  • ID3D10Device1::CreateTexture1D
  • ID3D10Device1::CreateTexture2D
  • ID3D10Device1::CreateTexture3D
  • ID3D10Device1::CreateBuffer

Si l’une des fonctions listées est appelée avec l'indicateur D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX défini, l’interface renvoyée peut être interrogée pour une interface IDXGIKeyedMutex, qui implémente les API AcquireSync et ReleaseSync afin de synchroniser l’accès à la surface. L’appareil qui crée la surface et tout autre appareil ouvrant cette dernière (à l’aide d’OpenSharedResource) doit obligatoirement appeler IDXGIKeyedMutex::AcquireSync avant toute commande de rendu sur la surface, puis IDXGIKeyedMutex::ReleaseSync une fois le rendu terminé.
Les appareils WARP et REF ne sont pas compatibles avec les ressources partagées. Si vous tentez de créer une ressource avec cet indicateur sur un appareil WARP ou REF, la méthode de création renverra un code d’erreur E_OUTOFMEMORY.
INTERFACE IDXGIKEYEDMUTEX
Une nouvelle interface dans DXGI 1.1, IDXGIKeyedMutex, représente un mutex à clé. Il vous permet d'accéder à titre exclusif à une ressource partagée utilisée par plusieurs appareils. Pour obtenir une documentation de référence sur cette interface et ses deux méthodes, AcquireSync et ReleaseSync, consultez IDXGIKeyedMutex.

Exemple : partage de surface synchronisé entre deux appareils Direct3D 10.1

L’exemple ci-dessous illustre le partage d’une surface entre deux appareils Direct3D 10.1. La surface partagée synchronisée est créée par un appareil Direct3D10.1.

// Create Sync Shared Surface using Direct3D10.1 Device 1.
D3D10_TEXTURE2D_DESC desc;
ZeroMemory( &desc, sizeof(desc) );
desc.Width = width;
desc.Height = height;
desc.MipLevels = 1;
desc.ArraySize = 1;
// must match swapchain format in order to CopySubresourceRegion.
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.SampleDesc.Count = 1;
desc.Usage = D3D10_USAGE_DEFAULT;
// creates 2D texture as a Synchronized Shared Surface.
desc.MiscFlags = D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX;
desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
ID3D10Texture2D* g_pShared = NULL;
g_pd3dDevice1->CreateTexture2D( &desc, NULL, &g_pShared );

// QI IDXGIResource interface to synchronized shared surface.
IDXGIResource* pDXGIResource = NULL;
g_pShared->QueryInterface(__uuidof(IDXGIResource), (LPVOID*) &pDXGIResource);

// obtain handle to IDXGIResource object.
pDXGIResource->GetSharedHandle(&g_hsharedHandle);
pDXGIResource->Release();
if ( !g_hsharedHandle )
    return E_FAIL;

// QI IDXGIKeyedMutex interface of synchronized shared surface's resource handle.
hr = g_pShared->QueryInterface( __uuidof(IDXGIKeyedMutex),
    (LPVOID*)&g_pDXGIKeyedMutex_dev1 );
If ( FAILED( hr ) || ( g_pDXGIKeyedMutex_dev1 == NULL ) )
    return E_FAIL;

Le même appareil Direct3D10.1 peut obtenir la surface partagée synchronisée pour effectuer le rendu en appelant AcquireSync, puis en libérant la surface pour le rendu de l’autre appareil en appelant ReleaseSync. Lorsque vous ne partagez pas la surface partagée synchronisée avec un autre appareil Direct3D, le créateur peut obtenir et libérer l’aire partagée synchronisée (pour démarrer et terminer le rendu) en acquérant et en mettant en production la même paire clé-valeur.

// Obtain handle to Sync Shared Surface created by Direct3D10.1 Device 1.
hr = g_pd3dDevice2->OpenSharedResource( g_hsharedHandle,__uuidof(ID3D10Texture2D),
                                        (LPVOID*) &g_pdev2Shared);
if (FAILED (hr))
    return hr;
hr = g_pdev2Shared->QueryInterface( __uuidof(IDXGIKeyedMutex),
                                    (LPVOID*) &g_pDXGIKeyedMutex_dev2);
if( FAILED( hr ) || ( g_pDXGIKeyedMutex_dev2 == NULL ) )
    return E_FAIL;

// Rendering onto Sync Shared Surface from D3D10.1 Device 1 using D3D10.1 Device 2.
UINT acqKey = 1;
UINT relKey = 0;
DWORD timeOut = 5;
DWORD result = g_pDXGIKeyedMutex_dev2->AcquireSync(acqKey, timeOut);
if ( result == WAIT_OBJECT_0 )
    // Rendering calls using Device 2.
else
    // Handle unable to acquire shared surface error.
result = g_pDXGIKeyedMutex_dev2->ReleaseSync(relKey));
if (result == WAIT_OBJECT_0)
    return S_OK;

Le deuxième appareil Direct3D10.1 peut obtenir la surface partagée synchronisée pour effectuer le rendu en appelant AcquireSync, puis en libérant la surface pour le rendu du premier appareil en appelant ReleaseSync. Notez que le deuxième appareil peut acquérir la surface partagée synchronisée à l’aide de la même paire clé-valeur que celle spécifiée dans l’appel ReleaseSync du premier appareil.

// Rendering onto Sync Shared Surface from D3D10.1 Device 1 using D3D10.1 Device 1.
UINT acqKey = 0;
UINT relKey = 1;
DWORD timeOut = 5;
DWORD result = g_pDXGIKeyedMutex_dev1->AcquireSync(acqKey, timeOut);
if (result == WAIT_OBJECT_0)
    // Rendering calls using Device 1.
else
    // Handle unable to acquire shared surface error.
result = g_pDXGIKeyedMutex_dev1->ReleaseSync(relKey));
if ( result == WAIT_OBJECT_0 )
    return S_OK;

Des appareils supplémentaires qui partagent la même surface peuvent acquérir et libérer la surface à tour de rôle à l’aide de clés supplémentaires, comme indiqué dans les appels suivants.

// Within Device 1's process/thread:
// Rendering onto Sync Shared Surface from D3D10.1 Device 1 using D3D10.1 Device 1
result = g_pDXGIKeyedMutex_dev1->AcquireSync(0, timeOut);
// Rendering calls using Device 1
...
result = g_pDXGIKeyedMutex_dev1->ReleaseSync(1);
...
////////////////////////////////////////////////////////////////////////////
// Within Device 2's process/thread:
// Rendering onto Sync Shared Surface from D3D10.1 Device 1 using D3D10.1 Device 2
result = g_pDXGIKeyedMutex_dev2->AcquireSync(1, timeOut);
// Rendering calls using Device 2
...
result = g_pDXGIKeyedMutex_dev1->ReleaseSync(2);

////////////////////////////////////////////////////////////////////////////
// Within Device 3's process/thread:
// Rendering onto Sync Shared Surface from D3D10.1 Device 1 using D3D10.1 Device 3
result = g_pDXGIKeyedMutex_dev1->AcquireSync(2, timeOut);
// Rendering calls using Device 3
...
result = g_pDXGIKeyedMutex_dev1->ReleaseSync(0);
...

Notez qu’une application réelle peut toujours s’afficher sur une surface intermédiaire qui est ensuite copiée dans la surface partagée afin d'éviter que, sur une surface partagée, un appareil en attende un autre.

Utilisation de surfaces partagées synchronisées avec Direct2D et Direct3D 11

Le partage entre les API Direct3D 11 et Direct3D 10.1 fonctionne de manière similaire, c'est-à-dire qu'une surface partagée synchronisée peut être créée à partir d’un appareil API et partagée avec les autres appareils d’API, dans le processus ou en dehors.

Les applications qui utilisent Direct2D peuvent partager un appareil Direct3D 10.1 et utiliser une surface partagée synchronisée pour interagir avec des appareils Direct3D 11 ou d’autres appareils Direct3D 10.1, qu’ils appartiennent ou non au même processus. Toutefois, pour les applications monothread à un seul processus, le partage d’appareils est la méthode la plus performante et efficace d’interopérabilité entre Direct2D et Direct3D 10 ou Direct3D 11.

Rastériseur logiciel

Les surfaces partagées synchronisées ne sont pas prises en charge lorsque les applications utilisent les rastériseurs logiciels Direct3D ou Direct2D, y compris le rastériseur de référence et WARP, plutôt que d’utiliser l’accélération matérielle graphique.

Interopérabilité entre Direct3D 9EX et les API basées sur DXGI

Les API Direct3D 9Ex incluent la notion de partage de surface afin de donner aux autres API un accès en lecture à partir de la surface partagée. Pour donner un accès en lecture et en écriture dans une surface partagée Direct3D 9Ex, vous devez ajouter une synchronisation manuelle à l’application elle-même.

Surfaces partagées Direct3D 9Ex et assistant de synchronisation manuelle

La tâche fondamentale en matière d'interopérabilité entre Direct3D 9Ex et Direct3D 10 ou 11 consiste à faire passer une surface unique du premier appareil (appareil A) au deuxième (appareil B) de sorte que lorsque l’appareil B acquiert un handle sur la surface, le rendu de l’appareil A soit forcément terminé. L'appareil B peut ainsi utiliser la surface sans problème. Ce cas de figure ressemble au problème classique du producteur-consommateur. Cette partie envisage le problème sous cet angle. Le premier appareil qui utilise la surface, puis la libère correspond au producteur (Appareil A). Celui qui l'attend correspond au consommateur (Appareil B). Les vraies applications sont plus complexes : elles sont constituées de plusieurs blocs producteur-consommateur consécutifs permettant de créer la fonctionnalité souhaitée.

Les blocs producteur-consommateur sont implémentés dans l’assistant au moyen d’une file d’attente de surfaces. Les surfaces sont mises en file d’attente par le producteur, et en sont retirées par le consommateur. L’assistant propose trois interfaces COM : ISurfaceQueue, ISurfaceProducer et ISurfaceConsumer.

Vue d’ensemble générale de l’assistant

L’objet ISurfaceQueue est le bloc de base permettant d'utiliser les surfaces partagées. Il est créé avec un appareil Direct3D initialisé et une description pour créer un nombre déterminé de surfaces partagées. L’objet de file d’attente gère la création de ressources et l’ouverture du code. Le nombre et le type de surfaces sont fixes. Une fois celles-ci créées, l’application ne peut plus en ajouter ni les supprimer.

Chaque instance de l’objet ISurfaceQueue est en quelque sorte une voie à sens unique qui peut être utilisée pour envoyer des surfaces de l’appareil de production à l’appareil consommateur. Vous pouvez en utiliser plusieurs pour créer des scénarios de partage de surface entre des appareils d’applications spécifiques.

Durée de vie de la création/de l’objet
Il existe deux façons de créer l’objet file d’attente : avec CreateSurfaceQueue ou en suivant la méthode Clone d’ISurfaceQueue. Ces interfaces sont des objets COM et ont donc une durée de vie conforme à ce format.
Modèle producteur/consommateur
Enqueue () : le producteur appelle cette fonction pour indiquer qu'il a terminé d'utiliser la surface, laquelle est libre pour un autre appareil. Lorsque cette fonction est renvoyée, l’appareil producteur n’a plus de droits sur la surface, et continuer à l'utiliser comporte des risques.
Dequeue () : l’appareil consommateur appelle cette fonction pour obtenir une surface partagée. L’API vérifie que toutes les surfaces retirées de la file d’attente sont prêtes à être utilisées.
Métadonnées
L’API prend en charge l’association de métadonnées aux surfaces partagées.
Enqueue() a la possibilité de spécifier des métadonnées supplémentaires qui seront transmises à l’appareil consommateur. Les métadonnées ne doivent pas dépasser une valeur maximale connue au moment de la création.
Dequeue() peut, à titre facultatif, transmettre une mémoire tampon et un pointeur vers la taille de cette mémoire tampon. La file d’attente remplit la mémoire tampon avec les métadonnées de l’appel Enqueue correspondant.
clonage
Chaque objet ISurfaceQueue résout une synchronisation unidirectionnelle. Nous partons du principe que la grande majorité des applications qui se servent de cette API utiliseront un système fermé. Dans sa forme la plus simple, un système fermé avec deux appareils qui s'envoient des surfaces à tour de rôle nécessite deux files d'attente. L’objet ISurfaceQueue a une méthode Clone() pour permettre de créer plusieurs files d’attente, lesquelles font toutes partie d'un même pipeline plus grand.
Clone crée un objet ISurfaceQueue à partir d’un objet existant et partage toutes les ressources ouvertes entre elles. L’objet résultant présente exactement les mêmes surfaces que la file d’attente source. La taille des métadonnées peut varier d'une file d'attente clonée à l'autre.
de travail
L’ISurfaceQueue est chargée de créer et de gérer ses surfaces. Il ne peut pas mettre en file d'attente des surfaces arbitraires. Par ailleurs, une surface ne doit avoir qu’un seul « propriétaire » actif. Elle doit se trouver dans une file d’attente spécifique ou être utilisée par un appareil spécifique. Elle ne peut pas se situer sur plusieurs files d'attendre, ni être utilisée par un appareil une fois qu'elle a été mise en file d'attente.

API Details

IsurfaceQueue

La file d’attente est chargée de créer et de gérer les ressources partagées. Elle fournit également la fonctionnalité permettant d'enchaîner plusieurs files d’attente à l’aide de Clone. La file d’attente dispose de méthodes qui ouvrent un appareil producteur et un appareil consommateur. Un seul appareil que chaque type peut être ouvert simultanément.

La file d’attente expose les API suivantes :

API Description
CreateSurfaceQueue Crée un objet ISurfaceQueue (file d’attente « racine »).
ISurfaceQueue::OpenConsumer Renvoie une interface pour que l’appareil consommateur soit retiré de la file d'attente.
ISurfaceQueue::OpenProducer Renvoie une interface pour que l'appareil producteur soit mis en file d'attente.
ISurfaceQueue::Clone Crée un objet ISurfaceQueue qui partage des surfaces avec l’objet de file d’attente racine.

 

CreateSurfaceQueue

typedef struct SURFACE_QUEUE_DESC {
  UINT            Width;
  UINT            Height;
  DXGI_FORMAT     Format;
  UINT            NumSurfaces;
  UINT            MetaDataSize;
  DWORD           Flags;
} SURFACE_QUEUE_DESC;

Members (Membres)

Width, Height : les dimensions des surfaces partagées. Toutes les surfaces partagées doivent avoir les mêmes dimensions.
Format : le format des surfaces partagées. Toutes les surfaces partagées doivent avoir le même format. Les formats valides dépendent des appareils qui seront utilisés, car différentes paires d’appareils peuvent partager différents types de format.
NumSurfaces : le nombre de surfaces qui font partie de la file d’attente. Ce nombre est fixe.
MetaDataSize : la taille maximale de la mémoire tampon de métadonnées.
Flags : indicateurs permettant de contrôler le comportement de la file d’attente. Consultez la section Notes.

HRESULT CreateSurfaceQueue(
  [in]   SURFACE_QUEUE_DESC *pDesc,
  [in]   IUnknown *pDevice,
  [out]  IDXGIXSurfaceQueue **ppQueue
);

Paramètres

pDesc [in] : description de la file d’attente de surface partagée à créer.

pDevice [in] : l’appareil à utiliser pour créer les surfaces partagées. Il s’agit d’un paramètre explicite en raison d’une fonctionnalité dans Windows Vista. Pour les surfaces partagées entre Direct3D 9 et Direct3D 10, les surfaces doivent être créées avec Direct3D 9.

ppQueue [out] : lorsque cette valeur est renvoyée, elle contient un pointeur vers l’objet ISurfaceQueue.

Valeurs renvoyées

Si pDevice n’est pas en mesure de partager des ressources, cette fonction renvoie DXGI_ERROR_INVALID_CALL. Cette fonction crée les ressources. En cas d'échec, elle renvoie un code d'erreur. Si elle aboutit, elle renvoie S_OK.

Remarques

Créer l’objet file d’attente crée également toutes les surfaces. On part du principe que toutes les surfaces sont des cibles de rendu 2D qui seront créées avec des indicateurs D3D10_BIND_RENDER_TARGET et D3D10_BIND_SHADER_RESOURCE définis (ou indicateurs équivalents pour les différents runtimes).

Le développeur peut spécifier un indicateur qui précise si la file d’attente est accessible par plusieurs threads. Si aucun indicateur n’est défini (Flags == 0), la file d’attente sera utilisée par plusieurs threads. Le développeur peut spécifier un accès monothread, qui désactive le code de synchronisation et améliore les performances pour ces cas. Chaque file d’attente clonée dispose de son propre indicateur. Il est donc possible que différentes files d’attente du système présentent des contrôles de synchronisation différents.

Ouvrir un producteur

HRESULT OpenProducer(
  [in]   IUnknown *pDevice,
  [out]  IDXGIXSurfaceProducer **ppProducer
);

Paramètres

pDevice [in]

Appareil producteur qui met en file d’attente les surfaces.

ppProducer [out] : renvoie un objet à l’interface du producteur.

Valeurs renvoyées

Si l’appareil n’est pas en mesure de partager des surfaces, renvoie DXGI_ERROR_INVALID_CALL.

Ouvrir un consommateur

HRESULT OpenConsumer(
  [in]   IUnknown *pDevice,
  [out]  IDXGIXSurfaceConsumer **ppConsumer
);

Paramètres
pDevice [in]
Appareil consommateur qui retire les surfaces de la file d'attente. ppConsumer [out] : renvoie un objet à l’interface consommateur.

Valeurs renvoyées

Si l’appareil n’est pas en mesure de partager des surfaces, renvoie DXGI_ERROR_INVALID_CALL.

Remarques

Cette fonction ouvre toutes les surfaces de la file d’attente pour l’appareil d’entrée, puis les met en cache. Les appels de Dequeue suivants accèdent simplement au cache, sans avoir à systématiquement rouvrir les surfaces.

Clonage d’un IDXGIXSurfaceQueue

typedef struct SHARED_SURFACE_QUEUE_CLONE_DESC {
  UINT         MetaDataSize;
  DWORD        Flags;
} SHARED_SURFACE_QUEUE_CLONE_DESC;

Les MetaDataSize et Flags membres se comportent de la même manière qu'avec CreateSurfaceQueue.

HRESULT Clone(
  [in]   SHARED_SURFACE_QUEUE_CLONE_DESC *pDesc,
  [out]  IDXGIXSurfaceQueue **ppQueue
);

Paramètres

pDesc [in] : structure qui fournit une description de l’objet Clone à créer. Ce paramètre doit être initialisé.
ppQueue [out] : renvoie l’objet initialisé.

Remarques

Vous pouvez cloner à partir d’un objet de file d’attente existant, même s’il ne s'agit pas de l'objet racine.

IDXGIXSurfaceConsumer

HRESULT Dequeue(
  [in]      REFIID    id,
  [out]     void      **ppSurface,
  [in,out]  void      *pBuffer,
  [in,out]  UINT      *pBufferSize,
  [in]      DWORD     dwTimeout
);

Paramètres
id [in]
REFIID d’une surface 2D de l’appareil consommateur.

  • Pour un IDirect3DDevice9, le REFIID doit être __uuidof(IDirect3DTexture9).
  • Pour un ID3D10Device, le REFIID doit être __uuidof(ID3D10Texture2D).
  • Pour un ID3D11Device, le REFIID doit être __uuidof(ID3D11Texture2D).

ppSurface [out] : renvoie un pointeur vers la surface.
pBuffer [in, out] : paramètre facultatif. S’il ne renvoie pas NULL, il contient les métadonnées qui ont été transmises sur l’appel de file d’attente correspondant.
pBufferSize [in, out] : taille de pBuffer, en octets. Renvoie le nombre d’octets renvoyés dans pBuffer. Si l’appel enqueue n’a pas fourni de métadonnées, pBuffer est défini sur 0.
dwTimeout [in] : indique une valeur de délai d’expiration. Pour en savoir plus, consultez les remarques.

Valeurs renvoyées

Cette fonction peut renvoyer WAIT_TIMEOUT si une valeur de délai d’attente est spécifiée et si la fonction ne renvoie rien avant la valeur de délai d’expiration. Consultez la section Notes. Si aucune surface n’est disponible, la fonction est renvoyée avec ppSurface défini sur NULL, pBufferSize défini sur 0 et la valeur renvoyée est 0x80070120 (WIN32_TO_HRESULT(WAIT_TIMEOUT)).

Remarques

Cette API peut se bloquer si la file d’attente est vide. Le paramètre dwTimeout fonctionne de manière identique aux API de synchronisation Windows, comme WaitForSingleObject. Pour éviter les blocages, définissez le délai d'expiration sur 0.

ISurfaceProducer

Cette interface fournit deux méthodes qui permettent à l’application de mettre en file d’attente des surfaces. Une fois qu’une surface est mise en file d’attente, le pointeur de surface n’est plus valide et ne peut plus être utilisé en toute sécurité. L'application doit alors libérer le pointeur.

Méthode Description
ISurfaceProducer::Enqueue Met en file d’attente une surface dans l’objet file d’attente. Une fois cet appel terminé, le producteur a terminé d'utiliser la surface, laquelle est libre pour un autre appareil.
ISurfaceProducer::Flush Utilisé si les applications doivent avoir un comportement non bloquant. Pour plus d’informations, consultez Remarques.

 

Enqueue (empiler)

HRESULT Enqueue(
  [in]  IUnknown *pSurface,
  [in]  void *pBuffer,
  [in]  UINT BufferSize,
  [in]  DWORD Flags
);

Paramètres
pSurface [in]
Surface de l’appareil de production devant être mise en file d’attente. Il doit s'agir d'une surface ayant été retirée d'une file d'attente appartenant au même réseau. pBuffer [in] : paramètre facultatif, utilisé pour transmettre des métadonnées. Il doit pointer vers les données qui seront transmises à l’appel de retrait de la file d'attente.
BufferSize [in] taille de pBuffer, en octets.
Flags [in] : paramètre facultatif permettant de contrôler le comportement de cette fonction. Le seul indicateur est SURFACE_QUEUE_FLAG_DO_NOT_WAIT. Consultez les remarques concernant le vidage. Si aucun indicateur n’est transmis (Indicateurs == 0), le comportement de blocage par défaut est utilisé.

Valeurs renvoyées

Cette fonction peut renvoyer DXGI_ERROR_WAS_STILL_DRAWING si un indicateur SURFACE_QUEUE_FLAG_DO_NOT_WAIT est utilisé.

Remarques

  • Cette fonction met la surface en file d’attente. Si l’application ne spécifie pas SURFACE_QUEUE_FLAG_DO_NOT_WAIT, cette fonction bloque et effectue une synchronisation GPU-Processeur pour vérifier que tout le rendu sur la surface mise en file d’attente est terminé. Si cette fonction aboutit, une surface sera disponible pour la file d’attente. Pour éviter tout comportement bloquant, utilisez l’indicateur DO_NOT_WAIT. Pour plus d’informations, consultez Flush().
  • Conformément aux règles de comptage de référence COM, la surface renvoyée par Dequeue sera AddRef() afin que l’application n’ait pas besoin de le faire. Après avoir appelé Enqueue, l’application doit libérer la surface, celle-ci n'étant plus utilisée.

Purge

HRESULT Flush(
  [in]  DWORD Flags,
  [out] UINT *nSurfaces
);

Paramètres
Flags [in]
Le seul indicateur est SURFACE_QUEUE_FLAG_DO_NOT_WAIT. Consultez la section Notes. nSurfaces [out] : renvoie le nombre de surfaces qui sont toujours en attente et non vidées.

Valeurs renvoyées

Cette fonction peut renvoyer DXGI_ERROR_WAS_STILL_DRAWING si l'indicateur SURFACE_QUEUE_FLAG_DO_NOT_WAIT est utilisé. Cette fonction renvoie S_OK si des surfaces ont bien été vidées. Cette fonction renvoie DXGI_ERROR_WAS_STILL_DRAWING uniquement si aucune surface n’a été vidée. Ensemble, la valeur de retour et nSurfaces indiquent à l’application les tâches qui ont été effectuées et celles qui restent à faire.

Remarques

Le vidage n’est utile que si l’appel précédent à la file d’attente a utilisé l’indicateur de DO_NOT_WAIT. Sinon, il ne fonctionnera pas. Si l’appel de mise en file d’attente a utilisé l’indicateur DO_NOT_WAIT, la mise en file d’attente est renvoyée immédiatement et la synchronisation GPU-Processeur n’est pas garantie. La surface est toujours considérée comme mise en file d’attente, l’appareil de production ne peut pas continuer à l’utiliser, mais elle ne peut pas être retirée de la file d'attente. Pour que la surface soit retirée de la file d'attente, vous devez appeler Flush. Flush tente de valider toutes les surfaces actuellement en file d'attente. Si aucun indicateur n’est transmis à Vidage, la fonction est bloquée et efface l'intégralité de la file d'attente. Toutes les surfaces qui s'y trouvent sont alors prêtes à y être retirées. Si l’indicateur DO_NOT_WAIT est utilisé, la file d’attente vérifie si l'une des surfaces est prête. Cette étape n’est pas bloquante. Les surfaces qui ont terminé la synchronisation GPU-Processeur sont prêtes pour l’appareil consommateur. Les surfaces qui sont toujours en attente ne seront pas affectées. La fonction renvoie le nombre de surfaces qui doivent toujours être vidées.

Remarque

Le vidage n’interrompt pas la sémantique de file d’attente. L’API s'assure que les surfaces sont validées dans l'ordre où elles ont été mises en file attente, indépendamment du moment où la synchronisation GPU-Processeur se produit.

 

Utilisation de l'application d'interopérabilité Direct3D 9Ex et DXGI

Nous pensons que la plupart des cas d’utilisation impliqueront deux appareils partageant un certain nombre de surfaces. Comme il s’agit également du scénario le plus simple, le présent document explique comment utiliser les API pour atteindre cet objectif, présente une variation non bloquante de cette méthode et se termine par une brève section sur l’initialisation pour trois appareils.

Deux appareils

L’exemple d’application qui utilise cet assistant peut utiliser Direct3D 9Ex et Direct3D 11 en même temps. L’application peut traiter du contenu avec les deux appareils et présenter du contenu avec Direct3D 9. Le traitement peut désigner, entre autres, le rendu de contenu, le décodage vidéo ou l’exécution de nuanceurs de calcul. Pour chaque image, l’application traite avec Direct3D 11, puis avec Direct3D 9, et enfin présente avec Direct3D 9. Par ailleurs, le traitement avec Direct3D 11 produira certaines métadonnées que la présentation Direct3D 9 devra consommer. Cette section explique en détail chaque partie de cette séquence : initialisation, boucle principale et nettoyage.

Initialisation
L'initialisation implique les étapes suivantes :

  1. Initialisation des deux appareils.
  2. Création de la file d’attente racine : m_11to9Queue.
  3. Clonage à partir de la file d’attente racine : m_9to11Queue.
  4. Appel d'OpenProducer/OpenConsumer sur les deux files d’attente.

Les noms de file d’attente utilisent les nombres 9 et 11 pour distinguer l'API du producteur à celle du consommateur : m_producertoconsumerQueue. Par conséquent, m_11to9Queue indique une file d’attente pour laquelle l’appareil Direct3D 11 produit des surfaces que consomme l’appareil Direct3D 9. De même, m_9to11Queue indique une file d’attente pour laquelle Direct3D 9 produit des surfaces consommées par Direct3D 11.
Au début, la file d'attente racine est pleine, et les files d'attente clonées sont vides. Cela ne devrait pas poser de problème pour l'application, sauf en ce qui concerne le premier cycle de mise en file d'attente et de retrait de la file d'attente, ainsi que la disponibilité des métadonnées. Si un retrait de la file d'attente demande des métadonnées, mais qu’aucune n’a été définie (soit parce qu’il n’y a rien au départ, soit parce que la mise en file d’attente n’en a pas défini), la commande voit qu’aucune métadonnée n’a été reçue.

  1. Initialisation des deux appareils.

    m_pD3D9Device = InitializeD3D9ExDevice();
    m_pD3D11Device = InitializeD3D11Device();
    
  2. Création de la file d’attente racine.
    Cette étape permet également de créer les surfaces. Les restrictions de taille et de format sont les mêmes que lorsque vous créez une source partagée. La taille de la mémoire tampon de métadonnées est fixe au moment de la création. Dans ce cas, nous transmettons simplement un UINT.
    La file d’attente doit être créée avec un nombre fixe de surfaces. Les performances varient en fonction du scénario. L’utilisation de plusieurs surfaces augmente les chances que les appareils soient occupés. Par exemple, si vous n'avez qu’une seule surface, il n’y aura pas de parallélisation entre les deux appareils. En revanche, plus le nombre de surface est élevé, plus l'empreinte mémoire augmente, ce qui peut se répercuter sur les performances. Dans l'exemple suivant, deux surfaces sont utilisées.

    SURFACE_QUEUE_DESC Desc;
    Desc.Width        = 640;
    Desc.Height       = 480;
    Desc.Format       = DXGI_FORMAT_R16G16B16A16_FLOAT;
    Desc.NumSurfaces  = 2;
    Desc.MetaDataSize = sizeof(UINT);
    Desc.Flags        = 0;
    
    CreateSurfaceQueue(&Desc, m_pD3D9Device, &m_11to9Queue);
    
  3. Clonage de la file d'attente racine.
    Chaque file d’attente clonée doit utiliser les mêmes surfaces, mais peut avoir des tailles de mémoire tampon de métadonnées et des indicateurs différents. Dans ce cas, il n’existe aucune métadonnée de Direct3D 9 à Direct3D 11.

    SURFACE_QUEUE_CLONE_DESC Desc;
    Desc.MetaDataSize = 0;
    Desc.Flags        = 0;
    
    m_11to9Queue->Clone(&Desc, &m_9to11Queue);
    
  4. Ouvrir les appareils producteur et consommateur.
    L’application doit effectuer cette étape avant d’appeler Enqueue et Dequeue. Ouvrir un producteur et un consommateur renvoie des interfaces contenant les API Enqueue et Dequeue.

    // Open for m_p9to11Queue.
    m_p9to11Queue->OpenProducer(m_pD3D9Device, &m_pD3D9Producer);
    m_p9to11Queue->OpenConsumer(m_pD3D11Device, &m_pD3D11Consumer);
    
    // Open for m_p11to9Queue.
    m_p11to9Queue->OpenProducer(m_pD3D11Device, &m_pD3D11Producer);
    m_p11to9Queue->OpenConsumer(m_pD3D9Device, &m_pD3D9Consumer);
    

Boucle principale
L’utilisation de la file d’attente est modélisée selon le problème classique du producteur et du consommateur. Envisagez le problème appareil par appareil. Chaque appareil doit effectuer ces étapes : Dequeue pour extraire une surface de la file d’attente consommateur et effectuer le traitement, puis Enqueue pour mettre la surface en file d'attente de production. Pour l’appareil Direct3D 11, Direct3D 9 fonctionne presque à l'identique.

// Direct3D 9 Device.
IDirect3DTexture9* pTexture9 = NULL;
REFIID             surfaceID9 = _uuidof(IDirect3DTexture9);
UINT               metaData;
UINT               metaDataSize;
while (!done)
{
    // Dequeue surface.
    m_pD3D9Consumer->Dequeue(surfaceID9, (void**)&pSurface9,
                             &metaData, &metaDataSize, INFINITE);

    // Process the surface.
    ProcessD3D9(pSurface9);

    // Present the surface using the meta data.
    PresentD3D9(pSurface9, metaData, metaDataSize);

    // Enqueue surface.
    m_pD3D9Producer->Enqueue(pSurface9, NULL, 0, 0);
}

Nettoyage
Cette étape est très simple. Outre les étapes normales de nettoyage des API Direct3D, l’application doit libérer les interfaces COM de retour.

m_pD3D9Producer->Release();
m_pD3D9Consumer->Release();
m_pD3D11Producer->Release();
m_pD3D11Consumer->Release();
m_p9to11Queue->Release();
m_p11to9Queue->Release();

Utilisation non bloquante

L’exemple précédent convient à un cas d’utilisation multithread dans lequel chaque appareil a son propre thread. Il utilise les versions bloquantes des API : INFINITE pour le délai d’attente, et aucun indicateur n’est en file d’attente. Si vous souhaitez utiliser l’assistant de manière non bloquante, vous devez apporter seulement quelques modifications. Cette section illustre un exemple d’utilisation non bloquante avec les deux appareils sur un thread.

Initialisation
L’initialisation est identique, à l’exception des indicateurs. Étant donné que l’application est monothread, utilisez cet indicateur pour la création. Cela a pour effet de désactiver une partie du code de synchronisation, ce qui peut améliorer les performances.

SURFACE_QUEUE_DESC Desc;
Desc.Width        = 640;
Desc.Height       = 480;
Desc.Format       = DXGI_FORMAT_R16G16B16A16_FLOAT;
Desc.NumSurfaces  = 2;
Desc.MetaDataSize = sizeof(UINT);
Desc.Flags        = SURFACE_QUEUE_FLAG_SINGLE_THREADED;

CreateSurfaceQueue(&Desc, m_pD3D9Device, &m_11to9Queue);
SURFACE_QUEUE_CLONE_DESC Desc;
Desc.MetaDataSize = 0;
Desc.Flags        = SURFACE_QUEUE_FLAG_SINGLE_THREADED;

m_11to9Queue->Clone(&Desc, &m_9to11Queue);

Pour ouvrir les appareils producteur et consommateur, la procédure est la même que dans l’exemple bloquant.
Utilisation de la file d’attente
Il existe de nombreuses façons d’utiliser la file d’attente de manière non bloquante avec différentes caractéristiques de performances. L’exemple suivant est simple, mais présente des performances médiocres en raison d’une rotation et d’une interrogation excessives. Malgré ces problèmes, l’exemple illustre comment utiliser l’assistant. L’approche consiste à répéter une boucle de retrait de la file d'attente, traitement, mise en file d'attente et vidage. Si l’une des étapes échoue parce que la ressource n’est pas disponible, l’application réessaye simplement la boucle suivante.

// Direct3D 11 Device.
ID3D11Texture2D* pSurface11 = NULL;
REFIID           surfaceID11 = __uuidof(ID3D11Texture2D);
UINT             metaData;
while (!done)
{
    //
    // D3D11 Portion.
    //

    // Dequeue surface.
    hr = m_pD3D11Consumer->Dequeue(surfaceID11,
                                   (void**)&pSurface11,
                                   NULL, 0, 0);
    // Only continue if we got a surface.
    if (SUCCEEDED(hr))
    {
        // Process the surface and return some meta data.
        ProcessD3D11(pSurface11, &metaData);

        // Enqueue surface.
        m_pD3D11Producer->Enqueue(pSurface11, &metaData,
                                  sizeof(UINT),
                                  SURFACE_QUEUE_FLAG_DO_NOT_WAIT);
    }
    // Flush the queue to check if any surfaces completed.
    m_pD3D11Producer->Flush(NULL,SURFACE_QUEUE_FLAG_DO_NOT_WAIT);

    //
    // Do the same with the Direct3D 9 Device.
    //

    // Dequeue surface.
    hr = m_pD3D9Consumer->Dequeue(surfaceID9,
                                  (void**)&pSurface9,
                                  &metaData,
                                  &metaDataSize, 0);
    // Only continue if we got a surface.
    if (SUCCEEDED(hr)))
    {
        // Process the surface.
        ProcessD3D9(pSurface9);

        // Present the surface using the meta data.
        PresentD3D9(pSurface9, metaData, metaDataSize);

        // Enqueue surface.
        m_pD3D9Producer->Enqueue(pSurface9, NULL, 0,
                                 SURFACE_QUEUE_FLAG_DO_NOT_WAIT);
    }
    // Flush the queue to check if any surfaces completed.
    m_pD3D9Producer->Flush(NULL,SURFACE_QUEUE_FLAG_DO_NOT_WAIT);
}

Une solution plus complexe consiste à vérifier la valeur renvoyée lors de la mise en file d’attente et du vidage pour déterminer si le vidage est nécessaire.

Trois appareils

Vous pouvez facilement appliquer les exemples précédents pour couvrir plusieurs appareils. Le code suivant permet d'effectuer l'initialisation. Une fois les objets producteur et consommateur créés, le code à utiliser est le même. L'exemple comporte trois appareils, et donc trois files d’attente. Les surfaces passent de Direct3D 9 à Direct3D 10, puis à Direct3D 11.

SURFACE_QUEUE_DESC Desc;
Desc.Width        = 640;
Desc.Height       = 480;
Desc.Format       = DXGI_FORMAT_R16G16B16A16_FLOAT;
Desc.NumSurfaces  = 2;
Desc.MetaDataSize = sizeof(UINT);
Desc.Flags        = 0;

SURFACE_QUEUE_CLONE_DESC Desc;
Desc.MetaDataSize = 0;
Desc.Flags        = 0;

CreateSurfaceQueue(&Desc, m_pD3D9Device, &m_11to9Queue);
m_11to9Queue->Clone(&Desc, &m_9to10Queue);
m_11to9Queue->Clone(&Desc, &m_10to11Queue);

Comme mentionné précédemment, le clonage fonctionne de la même façon, quelle que soit la file d’attente clonée. Par exemple, le deuxième appel Clone peut avoir été basé sur l’objet m_9to10Queue.

// Open for m_p9to10Queue.
m_p9to10Queue->OpenProducer(m_pD3D9Device, &m_pD3D9Producer);
m_p9to10Queue->OpenConsumer(m_pD3D10Device, &m_pD3D10Consumer);

// Open for m_p10to11Queue.
m_p10to11Queue->OpenProducer(m_pD3D10Device, &m_pD3D10Producer);
m_p10to11Queue->OpenConsumer(m_pD3D11Device, &m_pD3D11Consumer);

// Open for m_p11to9Queue.
m_p11to9Queue->OpenProducer(m_pD3D11Device, &m_pD3D11Producer);
m_p11to9Queue->OpenConsumer(m_pD3D9Device, &m_pD3D9Consumer);

Conclusion

Vous pouvez créer des solutions d’interopérabilité pour tirer parti de plusieurs API DirectX. L’interopérabilité des API graphiques Windows vous permet désormais de bénéficier d'un runtime de gestion de surface commun DXGI 1.1. Ce runtime prend en charge le partage de surface synchronisée dans les API nouvellement développées, telles que Direct3D 11, Direct3D 10.1 et Direct2D. Les améliorations de l’interopérabilité entre les nouvelles API et les API existantes facilitent la migration des applications et la rétrocompatibilité. Direct3D 9Ex et les API consommateur DXGI 1.1 peuvent avoir une interopérabilité, comme le montre le mécanisme de synchronisation disponible dans les anciennes applications d’exemple Win32, que l’on peut trouver dans le référentiel archivé MSDN Code Gallery Microsoft Samples.