Freigeben über


Surface-Freigabe zwischen Windows-Grafik-APIs

Dieses Thema bietet eine technische Übersicht über die Interoperabilität mithilfe der Oberflächenfreigabe zwischen Windows-Grafik-APIs, einschließlich Direct3D 11, Direct2D, DirectWrite, Direct3D 10 und Direct3D 9Ex. Wenn Sie bereits über kenntnisse dieser APIs verfügen, können Sie in diesem Dokument mehrere APIs verwenden, um in einer Anwendung, die für die Betriebssysteme Windows 7 oder Windows Vista entwickelt wurde, auf derselben Oberfläche zu rendern. Dieses Thema enthält außerdem Bewährte Methodenrichtlinien und Zeiger auf zusätzliche Ressourcen.

Hinweis

Für Direct2D- und DirectWrite-Interoperabilität auf der DirectX 11.1-Runtime können Sie Direct2D-Geräte und -Gerätekontexte verwenden, um direkt auf Direct3D 11-Geräten zu rendern.

 

Dieses Thema enthält folgende Abschnitte:

Einführung

In diesem Dokument bezieht sich die Interoperabilität der Windows-Grafik-API auf die Freigabe derselben Renderingoberfläche durch verschiedene APIs. Diese Art der Interoperabilität ermöglicht Es Anwendungen, überzeugende Displays zu erstellen, indem mehrere Windows-Grafik-APIs verwendet werden, und die Migration zu neuen Technologien zu vereinfachen, indem die Kompatibilität mit vorhandenen APIs beibehalten wird.

In Windows 7 (und Windows Vista SP2 mit Windows 7 Interop Pack, Vista 7IP) sind die Grafikrendering-APIs Direct3D 11, Direct2D, Direct3D 10.1, Direct3D 10.0, Direct3D 9Ex, Direct3D 9c und frühere Direct3D-APIs sowie GDI und GDI+. Windows Imaging Component (WIC) und DirectWrite sind verwandte Technologien für die Bildverarbeitung, und Direct2D führt Textrendering durch. Die DirectX-Videobeschleunigungs-API (DXVA) basiert auf Direct3D 9c und Direct3D 9Ex und wird für die Videoverarbeitung verwendet.

Da sich Windows-Grafik-APIs zu Direct3D-basiert entwickeln, investiert Microsoft mehr Anstrengungen, um die Interoperabilität zwischen APIs sicherzustellen. Neu entwickelte Direct3D-APIs und APIs höherer Ebene, die auf Direct3D-APIs basieren, bieten auch Unterstützung, wenn dies für die Überbrückung der Kompatibilität mit älteren APIs erforderlich ist. Zur Veranschaulichung können Direct2D-Anwendungen Direct3D 10.1 verwenden, indem sie ein Direct3D 10.1-Gerät freigeben. Außerdem können Direct3D 11-, Direct2D- und Direct3D 10.1-APIs die Vorteile der DirectX-Grafikinfrastruktur (DXGI) 1.1 nutzen, die synchronisierte freigegebene Oberflächen ermöglicht, die die Interoperabilität zwischen diesen APIs vollständig unterstützen. DXGI 1.1-basierte APIs arbeiten mit GDI und mit GDI+ nach Zuordnung zusammen, indem sie den GDI-Gerätekontext von einer DXGI 1.1-Oberfläche abrufen. Weitere Informationen finden Sie in der Dokumentation zur DXGI- und GDI-Interoperabilität auf MSDN.

Die nicht synchronisierte Surface-Freigabe wird von der Direct3D 9Ex-Runtime unterstützt. DXVA-basierte Videoanwendungen können das Direct3D 9Ex- und DXGI-Interoperabilitätshilfsprogramm für direct3D 9Ex-basierte DXVA-Interoperabilität mit Direct3D 11 für Compute-Shader verwenden oder mit Direct2D für 2D-Steuerelemente oder Textrendering zusammenarbeiten. WIC und DirectWrite auch mit GDI, Direct2D und anderen Direct3D-APIs zusammenarbeiten.

Direct3D 10.0, Direct3D 9c und ältere Direct3D-Runtimes unterstützen keine freigegebenen Oberflächen. Systemspeicherkopien werden weiterhin für die Interoperabilität mit GDI- oder DXGI-basierten APIs verwendet.

Beachten Sie, dass sich die Interoperabilitätsszenarien in diesem Dokument auf mehrere Grafik-APIs beziehen, die auf einer freigegebenen Renderingoberfläche und nicht im gleichen Anwendungsfenster gerendert werden. Die Synchronisierung für separate APIs für verschiedene Oberflächen, die dann im selben Fenster zusammengesetzt werden, liegt außerhalb des Bereichs dieses Artikels.

Übersicht über die API-Interoperabilität

Die Interoperabilität von Windows-Grafik-APIs für die Surface-Freigabe kann in API-zu-API-Szenarien und der entsprechenden Interoperabilitätsfunktionalität beschrieben werden. Ab Windows 7 und ab Windows Vista SP2 mit 7IP enthalten neue APIs und zugehörige Runtimes Direct2D und verwandte Technologien: Direct3D 11 und DXGI 1.1. Die GDI-Leistung wurde auch in Windows 7 verbessert. Direct3D 10.1 wurde in Windows Vista SP1 eingeführt. Das folgende Diagramm zeigt die Interoperabilitätsunterstützung zwischen APIs.

Diagramm der Interoperabilitätsunterstützung zwischen Windows-Grafik-APIs

In diesem Diagramm zeigen Pfeile Interoperabilitätsszenarien an, in denen auf dieselbe Oberfläche von den verbundenen APIs zugegriffen werden kann. Blaue Pfeile zeigen in Windows Vista eingeführte Interoperabilitätsmechanismen an. Grüne Pfeile zeigen die Interoperabilitätsunterstützung für neue APIs oder Verbesserungen an, mit denen ältere APIs mit neueren APIs zusammenarbeiten können. Grüne Pfeile stellen beispielsweise die Gerätefreigabe, synchronisierte Unterstützung für freigegebene Oberflächen, Direct3D 9Ex/DXGI-Synchronisierungshilfsprogramm und das Abrufen eines GDI-Gerätekontexts von einer kompatiblen Oberfläche dar.

Interoperabilitätsszenarien

Ab Windows 7 und Windows Vista 7IP unterstützen mainstream-Angebote von Windows-Grafik-APIs mehrere APIs, die auf der gleichen DXGI 1.1-Oberfläche gerendert werden.

Direct3D 11, Direct3D 10.1, Direct2D – Interoperabilität untereinander

Direct3D 11-, Direct3D 10.1- und Direct2D-APIs (und die zugehörigen APIs wie DirectWrite und WIC) können entweder mithilfe von Direct3D 10.1-Gerätefreigaben oder synchronisierten freigegebenen Oberflächen miteinander zusammenarbeiten.

Direct3D 10.1-Gerätefreigabe mit Direct2D

Die Gerätefreigabe zwischen Direct2D und Direct3D 10.1 ermöglicht es einer Anwendung, beide APIs zu verwenden, um nahtlos und effizient auf derselben DXGI 1.1-Oberfläche zu rendern, wobei dasselbe zugrunde liegende Direct3D-Geräteobjekt verwendet wird. Direct2D bietet die Möglichkeit, Direct2D-APIs mit einem vorhandenen Direct3D 10.1-Gerät aufzurufen, wobei die Tatsache genutzt wird, dass Direct2D auf Direct3D 10.1- und DXGI 1.1-Runtimes basiert. Die folgenden Codeausschnitte veranschaulichen, wie Direct2D das Direct3D 10.1-Geräterenderungsziel von einer DXGI 1.1-Oberfläche abruft, die dem Gerät zugeordnet ist. Das Direct3D 10.1-Geräterenderziel kann Direct2D-Zeichnungsaufrufe zwischen BeginDraw- und EndDraw-APIs ausführen.

// 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);

Anmerkungen

  • Das zugeordnete Direct3D 10.1-Gerät muss das BGRA-Format unterstützen. Dieses Gerät wurde durch Aufrufen von D3D10CreateDevice1 mit dem Parameter D3D10_CREATE_DEVICE_BGRA_SUPPORT erstellt. Das BGRA-Format wird ab Direct3D 10 feature level 9.1 unterstützt.
  • Die Anwendung sollte nicht mehrere ID2D1RenderTargets erstellen, die demselben Direct3D10.1-Gerät zugeordnet werden.
  • Um eine optimale Leistung zu erzielen, halten Sie immer mindestens eine Ressource bereit, z. B. Texturen oder Oberflächen, die dem Gerät zugeordnet sind.

Die Gerätefreigabe eignet sich für die In-Process-Singlethread-Nutzung eines Renderinggeräts, das von Direct3D 10.1- und Direct2D-Rendering-APIs gemeinsam genutzt wird. Synchronisierte freigegebene Oberflächen ermöglichen multithreadbasierte, prozessinterne und out-of-Process-Nutzung mehrerer Renderinggeräte, die von Direct3D 10.1-, Direct2D- und Direct3D 11-APIs verwendet werden.

Eine weitere Methode der Direct3D 10.1- und Direct2D-Interoperabilität ist die Verwendung von ID3D1RenderTarget::CreateSharedBitmap, die ein ID2D1Bitmap-Objekt aus IDXGISurface erstellt. Sie können eine Direct3D10.1-Szene in die Bitmap schreiben und mit Direct2D rendern. Weitere Informationen finden Sie unter ID2D1RenderTarget::CreateSharedBitmap-Methode.

Direct2D-Softwarerasterung

Die Gerätefreigabe mit Direct3D 10.1 wird bei Verwendung des Direct2D-Softwarerenderers nicht unterstützt, z. B. durch Angabe D2D1_RENDER_TARGET_USAGE_FORCE_SOFTWARE_RENDERING in D2D1_RENDER_TARGET_USAGE beim Erstellen eines Direct2D-Renderziels.

Direct2D kann den WARP10-Softwarerasterer verwenden, um das Gerät mit Direct3D 10 oder Direct3D 11 zu teilen, aber die Leistung sinkt erheblich.

DXGI 1.1 Synchronisierte freigegebene Oberflächen

Direct3D 11-, Direct3D 10.1- und Direct2D-APIs verwenden DXGI 1.1, das die Funktionalität zum Synchronisieren von Lese- und Schreibvorgängen auf derselben Videospeicheroberfläche (DXGISurface1) mit zwei oder mehr Direct3D-Geräten bietet. Die Renderinggeräte, die synchronisierte freigegebene Oberflächen verwenden, können Direct3D 10.1- oder Direct3D 11-Geräte sein, die jeweils im selben Prozess oder prozessübergreifend ausgeführt werden.

Anwendungen können synchronisierte freigegebene Oberflächen verwenden, um zwischen allen DXGI 1.1-basierten Geräten wie Direct3D 11 und Direct3D 10.1 oder zwischen Direct3D 11 und Direct2D zu arbeiten, indem sie das Direct3D 10.1-Gerät vom Direct2D-Renderzielobjekt abrufen.

Stellen Sie in Direct3D 10.1- und höher-APIs sicher, dass das Direct3D-Gerät mit einem DXGI 1.1-Adapterobjekt erstellt wird, das aus dem DXGI 1.1-Factoryobjekt aufgezählt wird. Rufen Sie CreateDXGIFactory1 auf, um das IDXGIFactory1-Objekt zu erstellen, und rufen Sie EnumAdapters1 auf, um das IDXGIAdapter1-Objekt aufzulisten. Das IDXGIAdapter1-Objekt muss als Teil des D3D10CreateDevice- oder D3D10CreateDeviceAndSwapChain-Aufrufs übergeben werden. Weitere Informationen zu DXGI 1.1-APIs finden Sie im Programmierhandbuch für DXGI.

APIs

D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX
Legen Sie beim Erstellen der synchronisierten freigegebenen Ressource D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX in D3D10_RESOURCE_MISC_FLAG fest.

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
Ermöglicht die Synchronisierung der erstellten Ressource mithilfe der IDXGIKeyedMutex::AcquireSync- und ReleaseSync-APIs. Die folgenden Direct3D 10.1-APIs zur Ressourcenerstellung, die alle einen D3D10_RESOURCE_MISC_FLAG-Parameter verwenden, wurden erweitert, um das neue Flag zu unterstützen.

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

Wenn eine der aufgeführten Funktionen mit festgelegtem D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX-Flag aufgerufen wird, kann die zurückgegebene Schnittstelle nach einer IDXGIKeyedMutex-Schnittstelle abgefragt werden, die AcquireSync- und ReleaseSync-APIs implementiert, um den Zugriff auf die Oberfläche zu synchronisieren. Das Gerät, das die Oberfläche erstellt, und jedes andere Gerät, das die Oberfläche öffnet (mit OpenSharedResource), muss IDXGIKeyedMutex::AcquireSync vor allen Renderingbefehlen auf der Oberfläche und IDXGIKeyedMutex::ReleaseSync aufrufen, wenn das Rendering abgeschlossen ist.
WARP- und REF-Geräte unterstützen keine freigegebenen Ressourcen. Der Versuch, eine Ressource mit diesem Flag auf einem WARP- oder REF-Gerät zu erstellen, führt dazu, dass die create-Methode einen E_OUTOFMEMORY Fehlercode zurückgibt.
IDXGIKEYEDMUTEX-SCHNITTSTELLE
Eine neue Schnittstelle in DXGI 1.1, IDXGIKeyedMutex, stellt einen mit Schlüssel versehenen Mutex dar, der exklusiven Zugriff auf eine freigegebene Ressource ermöglicht, die von mehreren Geräten verwendet wird. Eine Referenzdokumentation zu dieser Schnittstelle und den beiden Methoden AcquireSync und ReleaseSync finden Sie unter IDXGIKeyedMutex.

Beispiel: Synchronisierte Surface-Freigabe zwischen zwei Direct3D 10.1-Geräten

Das folgende Beispiel veranschaulicht die gemeinsame Nutzung einer Oberfläche zwischen zwei Direct3D 10.1-Geräten. Die synchronisierte freigegebene Oberfläche wird von einem Direct3D10.1-Gerät erstellt.

// 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;

Dasselbe Direct3D10.1-Gerät kann die synchronisierte freigegebene Oberfläche zum Rendern abrufen, indem AcquireSync aufgerufen wird und dann die Oberfläche für das Rendering des anderen Geräts freigegeben wird, indem ReleaseSync aufgerufen wird. Wenn die synchronisierte freigegebene Oberfläche nicht für ein anderes Direct3D-Gerät freigegeben wird, kann der Ersteller die synchronisierte freigegebene Oberfläche (zum Starten und Beenden des Renderings) abrufen und freigeben, indem er denselben Schlüsselwert abruft und losgibt.

// 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;

Das zweite Direct3D10.1-Gerät kann die synchronisierte freigegebene Oberfläche zum Rendern abrufen, indem AcquireSync aufgerufen wird. Anschließend wird die Oberfläche für das Rendering des ersten Geräts freigegeben, indem ReleaseSync aufgerufen wird. Beachten Sie, dass Gerät 2 die synchronisierte freigegebene Oberfläche mithilfe desselben Schlüsselwerts abrufen kann, der im ReleaseSync-Aufruf von Gerät 1 angegeben ist.

// 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;

Zusätzliche Geräte, die dieselbe Oberfläche nutzen, können abwechselnd die Surface mithilfe zusätzlicher Schlüssel abrufen und freigeben, wie in den folgenden Aufrufen gezeigt.

// 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);
...

Beachten Sie, dass eine reale Anwendung immer auf eine Zwischenoberfläche gerendert wird, die dann in die freigegebene Oberfläche kopiert wird, um zu verhindern, dass ein Gerät auf einem anderen Gerät wartet, das die Oberfläche gemeinsam verwendet.

Verwenden synchronisierter freigegebener Oberflächen mit Direct2D und Direct3D 11

Auf ähnliche Weise kann für die Freigabe zwischen Direct3D 11- und Direct3D 10.1-APIs eine synchronisierte freigegebene Oberfläche von einem API-Gerät erstellt und für die anderen API-Geräte innerhalb oder außerhalb des Prozesses freigegeben werden.

Anwendungen, die Direct2D verwenden, können ein Direct3D 10.1-Gerät gemeinsam nutzen und eine synchronisierte freigegebene Oberfläche verwenden, um mit Direct3D 11 oder anderen Direct3D 10.1-Geräten zu arbeiten, unabhängig davon, ob sie zum gleichen Prozess oder zu unterschiedlichen Prozessen gehören. Für Single-Process-Single-Thread-Anwendungen ist die Gerätefreigabe jedoch die leistungsstärkste und effizienteste Interoperabilitätsmethode zwischen Direct2D und Direct3D 10 oder Direct3D 11.

Softwarerasterizer

Synchronisierte freigegebene Oberflächen werden nicht unterstützt, wenn Anwendungen die Direct3D- oder Direct2D-Softwarerasterisierer verwenden, einschließlich des Verweisrasters und WARP, anstatt die Grafikhardwarebeschleunigung zu verwenden.

Interoperabilität zwischen Direct3D 9Ex- und DXGI-basierten APIs

Direct3D 9Ex-APIs beinhalteten den Begriff der Gemeinsamen Nutzung von Oberflächen, damit andere APIs von der freigegebenen Oberfläche lesen können. Um das Lesen und Schreiben auf einer freigegebenen Direct3D 9Ex-Oberfläche zu teilen, müssen Sie der Anwendung selbst eine manuelle Synchronisierung hinzufügen.

Direct3D 9Ex Shared Surfaces plus Manuelles Synchronisierungshilfsprogramm

Die grundlegendste Aufgabe in direct3D 9Ex und Direct3D 10 oder 11 ist die Übergabe einer einzelnen Oberfläche vom ersten Gerät (Gerät A) an das zweite Gerät (Gerät B), sodass das Rendering von Gerät A garantiert abgeschlossen ist, wenn Gerät B einen Griff auf der Oberfläche erhält. Daher kann Gerät B diese Oberfläche ohne Sorgen verwenden. Dies ist dem klassischen Produzenten-Verbraucher-Problem sehr ähnlich, und diese Diskussion modelliert das Problem auf diese Weise. Das erste Gerät, das die Oberfläche verwendet und dann aufgibt, ist der Produzent (Gerät A), und das Gerät, das zunächst wartet, ist der Consumer (Gerät B). Jede reale Anwendung ist komplexer als diese und verkettet mehrere Producer-Consumer-Bausteine, um die gewünschte Funktionalität zu erstellen.

Die Producer-Consumer-Bausteine werden mithilfe einer Warteschlange von Oberflächen im Hilfsprogramm implementiert. Oberflächen werden vom Hersteller in die Warteschlange gestellt und vom Verbraucher dequeuiert. Das Hilfsprogramm führt drei COM-Schnittstellen ein: ISurfaceQueue, ISurfaceProducer und ISurfaceConsumer.

High-Level Übersicht über das Hilfsprogramm

Das ISurfaceQueue-Objekt ist der Baustein für die Verwendung der freigegebenen Oberflächen. Es wird mit einem initialisierten Direct3D-Gerät und einer Beschreibung erstellt, um eine feste Anzahl freigegebener Oberflächen zu erstellen. Das Warteschlangenobjekt verwaltet die Erstellung von Ressourcen und das Öffnen von Code. Die Anzahl und der Typ der Oberflächen sind festgelegt; Sobald die Oberflächen erstellt wurden, kann die Anwendung sie nicht hinzufügen oder entfernen.

Jede instance des ISurfaceQueue-Objekts bietet eine Art Einbahnstraße, die verwendet werden kann, um Oberflächen vom produzierenden Gerät an das verbrauchende Gerät zu senden. Mehrere solche Einbahnstraßen können verwendet werden, um Oberflächenfreigabeszenarien zwischen Geräten bestimmter Anwendungen zu ermöglichen.

Erstellung/Objektlebensdauer
Es gibt zwei Möglichkeiten, das Warteschlangenobjekt zu erstellen: über CreateSurfaceQueue oder über die Clone-Methode von ISurfaceQueue. Da es sich bei den Schnittstellen um COM-Objekte handelt, gilt die standardmäßige COM-Lebensdauerverwaltung.
Producer/Consumer-Modell
Queue (): Der Producer ruft diese Funktion auf, um anzugeben, dass dies mit der Oberfläche erfolgt, die nun für ein anderes Gerät verfügbar werden kann. Nach der Rückkehr von dieser Funktion hat das Herstellergerät keine Rechte mehr für die Oberfläche und es ist unsicher, es weiterhin zu verwenden.
Dequeue (): Das verwendende Gerät ruft diese Funktion auf, um eine freigegebene Oberfläche zu erhalten. Die API garantiert, dass alle entfernten Oberflächen einsatzbereit sind.
Metadaten
Die API unterstützt das Zuordnen von Metadaten zu den freigegebenen Oberflächen.
Enqueue() hat die Möglichkeit, zusätzliche Metadaten anzugeben, die an das verwendende Gerät übergeben werden. Die Metadaten müssen zur Erstellungszeit kleiner als das maximum sein, das zur Erstellung bekannt ist.
Dequeue() kann optional einen Puffer und einen Zeiger auf die Größe des Puffers übergeben. Die Warteschlange füllt den Puffer mit den Metadaten aus dem entsprechenden Queue-Aufruf aus.
Klonen?
Jedes ISurfaceQueue-Objekt löst eine unidirektionale Synchronisierung auf. Wir gehen davon aus, dass die überwiegende Mehrheit der Anwendungen, die diese API verwenden, ein geschlossenes System verwenden. Das einfachste geschlossene System mit zwei Geräten, die Oberflächen hin und her senden, erfordert zwei Warteschlangen. Das ISurfaceQueue-Objekt verfügt über eine Clone()-Methode, mit der mehrere Warteschlangen erstellt werden können, die alle Teil derselben größeren Pipeline sind.
Clone erstellt ein neues ISurfaceQueue-Objekt aus einem vorhandenen Objekt und teilt alle geöffneten Ressourcen zwischen ihnen. Das resultierende Objekt hat genau die gleichen Oberflächen wie die Quellwarteschlange. Geklonte Warteschlangen können unterschiedliche Metadatengrößen aufweisen.
Surface
Die ISurfaceQueue übernimmt die Verantwortung für die Erstellung und Verwaltung ihrer Oberflächen. Es ist nicht zulässig, beliebige Oberflächen in Queque zu queen. Darüber hinaus sollte eine Oberfläche nur einen aktiven "Besitzer" haben. Es sollte sich entweder in einer bestimmten Warteschlange befinden oder von einem bestimmten Gerät verwendet werden. Es ist nicht gültig, sie in mehreren Warteschlangen zu haben oder dass Geräte die Oberfläche weiterhin verwenden, nachdem sie in die Warteschlange gestellt wurde.

API-Details

IsurfaceQueue

Die Warteschlange ist für das Erstellen und Verwalten der freigegebenen Ressourcen verantwortlich. Es bietet auch die Funktionalität zum Verketten mehrerer Warteschlangen mithilfe von Clone. Die Warteschlange verfügt über Methoden, die das produzierende Gerät und ein verbrauchendes Gerät öffnen. Nur eine von jedem kann jederzeit geöffnet werden.

Die Warteschlange macht die folgenden APIs verfügbar:

API Beschreibung
CreateSurfaceQueue Erstellt ein ISurfaceQueue-Objekt (die Stammwarteschlange).
ISurfaceQueue::OpenConsumer Gibt eine Schnittstelle zurück, über die das gerät, das verwendet wird, das aus dem Queue zu entfernen ist.
ISurfaceQueue::OpenProducer Gibt eine Schnittstelle zurück, über die das produzierende Gerät in den Queque gehen soll.
ISurfaceQueue::Clone Erstellt ein ISurfaceQueue-Objekt, das Oberflächen mit dem Stammwarteschlangenobjekt gemeinsam verwendet.

 

CreateSurfaceQueue

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

Mitglieder

Breite, Höhe Die Dimensionen der freigegebenen Oberflächen. Alle freigegebenen Oberflächen müssen die gleichen Dimensionen aufweisen.
Format Das Format der freigegebenen Oberflächen. Alle freigegebenen Oberflächen müssen das gleiche Format aufweisen. Die gültigen Formate hängen von den verwendeten Geräten ab, da verschiedene Gerätepaare verschiedene Formattypen gemeinsam nutzen können.
NumSurfaces Die Anzahl der Oberflächen, die Teil der Warteschlange sind. Dies ist eine feste Zahl.
MetaDataSize Die maximale Größe des Metadatenpuffers.
Flaggen Flags, um das Verhalten der Warteschlange zu steuern. Siehe Hinweise.

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

Parameter

pDesc [in] Die Beschreibung der zu erstellenden freigegebenen Surface-Warteschlange.

pDevice [in] Das Gerät, das zum Erstellen der freigegebenen Oberflächen verwendet werden soll. Dies ist ein expliziter Parameter aufgrund eines Features in Windows Vista. Für Oberflächen, die zwischen Direct3D 9 und Direct3D 10 gemeinsam genutzt werden, müssen die Oberflächen mit Direct3D 9 erstellt werden.

ppQueue [out] Enthält bei rückgabe einen Zeiger auf das ISurfaceQueue-Objekt.

Rückgabewerte

Wenn pDevice nicht in der Lage ist, Ressourcen gemeinsam zu nutzen, gibt diese Funktion DXGI_ERROR_INVALID_CALL zurück. Diese Funktion erstellt die Ressourcen. Wenn ein Fehler auftritt, wird ein Fehler zurückgegeben. Wenn dies erfolgreich ist, wird S_OK zurückgegeben.

Anmerkungen

Beim Erstellen des Warteschlangenobjekts werden auch alle Oberflächen erstellt. Es wird davon ausgegangen, dass alle Oberflächen 2D-Renderziele sind und mit den D3D10_BIND_RENDER_TARGET- und D3D10_BIND_SHADER_RESOURCE-Flags (oder den entsprechenden Flags für die verschiedenen Runtimes) erstellt werden.

Der Entwickler kann ein Flag angeben, das angibt, ob mehrere Threads auf die Warteschlange zugreifen. Wenn keine Flags festgelegt sind (Flags == 0), wird die Warteschlange von mehreren Threads verwendet. Der Entwickler kann den Single-Thread-Zugriff angeben, wodurch der Synchronisierungscode deaktiviert und eine Leistungsverbesserung für diese Fälle bereitgestellt wird. Jede geklonte Warteschlange verfügt über ein eigenes Flag, sodass verschiedene Warteschlangen im System über unterschiedliche Synchronisierungssteuerelemente verfügen können.

Öffnen eines Producers

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

Parameter

pDevice [in]

Das Herstellergerät, das Oberflächen in die Oberflächenwarteschlange einbindet.

ppProducer [out] Gibt ein Objekt an die Producer-Schnittstelle zurück.

Rückgabewerte

Wenn das Gerät nicht in der Lage ist, Oberflächen gemeinsam zu nutzen, gibt DXGI_ERROR_INVALID_CALL zurück.

Öffnen eines Consumer

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

Parameter
pDevice [in]
Das Consumergerät, das Oberflächen aus der Oberflächenwarteschlange entfernt. ppConsumer [out] Gibt ein Objekt an die Consumerschnittstelle zurück.

Rückgabewerte

Wenn das Gerät nicht in der Lage ist, Oberflächen gemeinsam zu nutzen, gibt DXGI_ERROR_INVALID_CALL zurück.

Anmerkungen

Diese Funktion öffnet alle Oberflächen in der Warteschlange für das Eingabegerät und speichert sie zwischen. Nachfolgende Aufrufe von Dequeue gehen einfach in den Cache und müssen die Oberflächen nicht jedes Mal erneut öffnen.

Klonen eines IDXGIXSurfaceQueue

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

MemberMetaDataSize und Flags haben das gleiche Verhalten wie für CreateSurfaceQueue.

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

Parameter

pDesc [in] Eine Struktur, die eine Beschreibung des zu erstellenden Clone-Objekts bereitstellt. Dieser Parameter sollte initialisiert werden.
ppQueue [out] Gibt das initialisierte Objekt zurück.

Anmerkungen

Sie können aus jedem vorhandenen Warteschlangenobjekt klonen, auch wenn es sich nicht um das Stammobjekt handelt.

IDXGIXSurfaceConsumer

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

Parameter
id [in]
Die REFIID einer 2D-Oberfläche des verbrauchenden Geräts.

  • Bei einem IDirect3DDevice9 sollte die REFIID __uuidof(IDirect3DTexture9) sein.
  • Bei einem ID3D10Device sollte die REFIID __uuidof(ID3D10Texture2D) sein.
  • Bei einem ID3D11Device sollte die REFIID __uuidof(ID3D11Texture2D) sein.

ppSurface [out] Gibt einen Zeiger auf die Oberfläche zurück.
pBuffer [in, out] Ein optionaler Parameter und, wenn nicht NULL, enthält bei der Rückgabe die Metadaten, die für den entsprechenden Queue-Aufruf übergeben wurden.
pBufferSize [in, out] Die Größe von pBuffer in Bytes. Gibt die Anzahl der in pBuffer zurückgegebenen Bytes zurück. Wenn der queue-Aufruf keine Metadaten bereitgestellt hat, ist pBuffer auf 0 festgelegt.
dwTimeout [in] Gibt einen Timeoutwert an. Weitere Informationen finden Sie in den Anmerkungen.

Rückgabewerte

Diese Funktion kann WAIT_TIMEOUT zurückgeben, wenn ein Timeoutwert angegeben wird und die Funktion vor dem Timeoutwert nicht zurückgegeben wird. Siehe Hinweise. Wenn keine Oberflächen verfügbar sind, gibt die Funktion zurück, wobei ppSurface auf NULL festgelegt ist, pBufferSize auf 0 festgelegt ist und der Rückgabewert 0x80070120 ist (WIN32_TO_HRESULT(WAIT_TIMEOUT)).

Anmerkungen

Diese API kann blockieren, wenn die Warteschlange leer ist. Der dwTimeout-Parameter funktioniert identisch mit den Windows-Synchronisierungs-APIs, z. B. WaitForSingleObject. Verwenden Sie für nicht blockierende Verhaltensweisen ein Timeout von 0.

ISurfaceProducer

Diese Schnittstelle bietet zwei Methoden, die es der App ermöglichen, Oberflächen in Quequeque zu stellen. Nachdem eine Oberfläche in Warteschlange gestellt wurde, ist der Oberflächenzeiger nicht mehr gültig und nicht mehr sicher zu verwenden. Die einzige Aktion, die die Anwendung mit dem Zeiger ausführen sollte, besteht darin, ihn freizugeben.

Methode Beschreibung
ISurfaceProducer::Queue Quediert eine Oberfläche für das Warteschlangenobjekt. Nachdem dieser Aufruf abgeschlossen ist, wird der Produzent mit der Oberfläche fertig und die Oberfläche ist für ein anderes Gerät bereit.
ISurfaceProducer::Flush Wird verwendet, wenn die Anwendungen ein nicht blockierendes Verhalten aufweisen sollen. Einzelheiten finden Sie in den Hinweisen.

 

Einreihen in die Warteschlange

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

Parameter
pSurface [in]
Die Oberfläche des produzierenden Geräts, das in Warteschlange gestellt werden muss. Diese Oberfläche muss eine entfernte Oberfläche aus demselben Warteschlangennetzwerk sein. pBuffer [in] Ein optionaler Parameter, der zum Übergeben von Metadaten verwendet wird. Es sollte auf die Daten verweisen, die an den Dequeue-Aufruf übergeben werden.
BufferSize [in] Die Größe von pBuffer in Bytes.
Flags [in] Ein optionaler Parameter, der das Verhalten dieser Funktion steuert. Das einzige Flag ist SURFACE_QUEUE_FLAG_ DO_NOT_WAIT. Weitere Informationen finden Sie in den Anmerkungen zu Flush. Wenn kein Flag übergeben wird (Flags == 0), wird das Standardblockierungsverhalten verwendet.

Rückgabewerte

Diese Funktion kann DXGI_ERROR_WAS_STILL_DRAWING zurückgeben, wenn ein SURFACE_QUEUE_FLAG_DO_NOT_WAIT-Flag verwendet wird.

Anmerkungen

  • Diese Funktion platziert die Oberfläche in der Warteschlange. Wenn die Anwendung SURFACE_QUEUE_FLAG_DO_NOT_WAIT nicht angibt, blockiert diese Funktion und führt eine GPU-CPU-Synchronisierung durch, um sicherzustellen, dass das gesamte Rendering auf der queuierten Oberfläche abgeschlossen ist. Wenn diese Funktion erfolgreich ist, ist eine Oberfläche für die Dequeue verfügbar. Wenn Sie ein nicht blockierende Verhalten wünschen, verwenden Sie das flag DO_NOT_WAIT. Weitere Informationen finden Sie unter Flush().
  • Gemäß den COM-Verweiszählungsregeln lautet die von Dequeue zurückgegebene Oberfläche AddRef(), sodass die Anwendung dies nicht tun muss. Nach dem Aufruf von Enqueue muss die Anwendung die Oberfläche freigeben, da sie sie nicht mehr verwendet.

Leerung

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

Parameter
Flaggen [in]
Das einzige Flag ist SURFACE_QUEUE_FLAG_ DO_NOT_WAIT. Siehe Hinweise. nSurfaces [out] Gibt die Anzahl der Noch ausstehenden und nicht geleerten Flächen zurück.

Rückgabewerte

Diese Funktion kann DXGI_ERROR_WAS_STILL_DRAWING zurückgeben, wenn das SURFACE_QUEUE_FLAG_DO_NOT_WAIT-Flag verwendet wird. Diese Funktion gibt S_OK zurück, wenn Oberflächen erfolgreich geleert wurden. Diese Funktion gibt DXGI_ERROR_WAS_STILL_DRAWING nur zurück, wenn keine Flächen geleert wurden. Zusammen geben der Rückgabewert und die nSurfaces der Anwendung an, welche Arbeit ausgeführt wurde und ob noch zu tun ist.

Anmerkungen

Flush ist nur dann sinnvoll, wenn beim vorherigen Aufruf zum Queue das Flag DO_NOT_WAIT verwendet wurde. Andernfalls ist es ein No-Op. Wenn der Aufruf zum Queue das Flag DO_NOT_WAIT verwendet hat, wird die Queue sofort zurückgegeben, und die GPU-CPU-Synchronisierung ist nicht garantiert. Die Oberfläche gilt weiterhin als queuiert, das produzierende Gerät kann sie nicht weiter verwenden, ist aber nicht für die Ausqueue verfügbar. Um zu versuchen, die Oberfläche für dequeue zu commiten, muss Flush aufgerufen werden. Flush versucht, alle Oberflächen zu committen, die derzeit in die Warteschlange gestellt werden. Wenn kein Flag an Flush übergeben wird, wird die gesamte Warteschlange blockiert und gelöscht, wodurch alle Darin befindlichen Oberflächen für die Dewarteschlange vorbereitet werden. Wenn das DO_NOT_WAIT-Flag verwendet wird, überprüft die Warteschlange die Oberflächen, um festzustellen, ob sie bereit sind. Dieser Schritt ist nicht blockierend. Surfaces, die die GPU-CPU-Synchronisierung abgeschlossen haben, sind für das Consumergerät bereit. Surfaces, die noch ausstehen, sind nicht betroffen. Die Funktion gibt die Anzahl der Oberflächen zurück, die noch geleert werden müssen.

Hinweis

Durch leeren wird die Semantik der Warteschlange nicht unterbrochen. Die API garantiert, dass surfaces, die zuerst in die Warteschlange gestellt wurden, vor surfaces in die Warteschlange eingereiht werden, unabhängig davon, wann die GPU-CPU-Synchronisierung erfolgt.

 

Direct3D 9Ex- und DXGI-Interophilfsprogramm: Verwendung

Wir erwarten, dass die meisten Anwendungsfälle zwei Geräte umfassen, die eine Reihe von Oberflächen gemeinsam nutzen. Da dies auch das einfachste Szenario ist, wird in diesem Dokument erläutert, wie Sie die APIs verwenden, um dieses Ziel zu erreichen, eine nicht blockierende Variante und endet mit einem kurzen Abschnitt zur Initialisierung für drei Geräte.

Zwei Geräte

Die Beispielanwendung, die dieses Hilfsprogramm verwendet, kann Direct3D 9Ex und Direct3D 11 zusammen verwenden. Die Anwendung kann Inhalte mit beiden Geräten verarbeiten und Inhalte mithilfe von Direct3D 9 präsentieren. Verarbeitung kann das Rendern von Inhalten, das Decodieren von Videos, das Ausführen von Compute-Shadern usw. bedeuten. Für jeden Frame wird die Anwendung zunächst mit Direct3D 11, dann mit Direct3D 9 verarbeitet und schließlich mit Direct3D 9 präsentiert. Darüber hinaus erzeugt die Verarbeitung mit Direct3D 11 einige Metadaten, die das vorhandene Direct3D 9 verwenden muss. In diesem Abschnitt wird die Hilfsverwendung in drei Teilen behandelt, die dieser Sequenz entsprechen: Initialisierung, Hauptschleife und Bereinigung.

Initialisierung
Die Initialisierung umfasst die folgenden Schritte:

  1. Initialisieren Sie beide Geräte.
  2. Erstellen Sie die Stammwarteschlange: m_11to9Queue.
  3. Klonen aus der Stammwarteschlange: m_9to11Queue.
  4. Rufen Sie OpenProducer/OpenConsumer für beide Warteschlangen auf.

Die Warteschlangennamen verwenden die Zahlen 9 und 11, um anzugeben, welche API der Producer und welcher Consumer ist: m_ProducertoconsumerQueue. Dementsprechend gibt m_11to9Queue eine Warteschlange an, für die das Direct3D 11-Gerät Oberflächen erzeugt, die das Direct3D 9-Gerät nutzt. Ebenso gibt m_9to11Queue eine Warteschlange an, für die Direct3D 9 Oberflächen erzeugt, die Direct3D 11 nutzt.
Die Stammwarteschlange ist zunächst voll, und alle geklonten Warteschlangen sind zunächst leer. Dies sollte für die Anwendung kein Problem darstellen, mit Ausnahme des ersten Zyklus der Enqueues und Dequeues und der Verfügbarkeit von Metadaten. Wenn eine Dequeue nach Metadaten fragt, aber keine festgelegt wurde (entweder, weil anfänglich nichts vorhanden ist oder die Warteschlange nichts festgelegt hat), erkennt die Dequeue, dass keine Metadaten empfangen wurden.

  1. Initialisieren Sie beide Geräte.

    m_pD3D9Device = InitializeD3D9ExDevice();
    m_pD3D11Device = InitializeD3D11Device();
    
  2. Erstellen Sie die Stammwarteschlange.
    In diesem Schritt werden auch die Oberflächen erstellt. Größen- und Formateinschränkungen sind identisch mit der Erstellung einer freigegebenen Ressource. Die Größe des Metadatenpuffers wird zum Zeitpunkt der Erstellung festgelegt, und in diesem Fall übergeben wir nur ein UINT.
    Die Warteschlange muss mit einer festen Anzahl von Oberflächen erstellt werden. Die Leistung variiert je nach Szenario. Mehrere Oberflächen erhöhen die Wahrscheinlichkeit, dass Geräte ausgelastet sind. Wenn beispielsweise nur eine Oberfläche vorhanden ist, erfolgt keine Parallelisierung zwischen den beiden Geräten. Auf der anderen Seite erhöht das Erhöhen der Anzahl der Oberflächen den Speicherbedarf, was die Leistung beeinträchtigen kann. In diesem Beispiel werden zwei Oberflächen verwendet.

    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. Klonen Sie die Stammwarteschlange.
    Jede geklonte Warteschlange muss die gleichen Oberflächen verwenden, kann jedoch unterschiedliche Metadatenpuffergrößen und unterschiedliche Flags aufweisen. In diesem Fall gibt es keine Metadaten von Direct3D 9 zu Direct3D 11.

    SURFACE_QUEUE_CLONE_DESC Desc;
    Desc.MetaDataSize = 0;
    Desc.Flags        = 0;
    
    m_11to9Queue->Clone(&Desc, &m_9to11Queue);
    
  4. Öffnen Sie die Producer- und Consumergeräte.
    Die Anwendung muss diesen Schritt ausführen, bevor Enqueue und Dequeue aufgerufen wird. Beim Öffnen eines Producers und Consumers werden Schnittstellen zurückgegeben, die die APIs in der Warteschlange bzw. aus der Warteschlange enthalten.

    // 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);
    

Main-Schleife
Die Verwendung der Warteschlange wird nach dem klassischen Producer/Consumer-Problem modelliert. Stellen Sie sich dies aus der Perspektive pro Gerät vor. Jedes Gerät muss die folgenden Schritte ausführen: Dequeue, um eine Oberfläche aus seiner verbrauchenden Warteschlange zu erhalten, den Prozess auf der Oberfläche auszuführen, und dann in die Produktionswarteschlange einreihen. Für das Direct3D 11-Gerät ist die Verwendung von Direct3D 9 fast identisch.

// 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);
}

Aufräumen
Dieser Schritt ist sehr einfach. Zusätzlich zu den normalen Schritten zum Bereinigen von Direct3D-APIs muss die Anwendung die Rückgabe-COM-Schnittstellen freigeben.

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

Nicht blockierende Verwendung

Das vorherige Beispiel ist für einen Multithread-Anwendungsfall sinnvoll, in dem jedes Gerät über einen eigenen Thread verfügt. Im Beispiel werden die blockierenden Versionen der APIs verwendet: INFINITE für Timeout und kein Flag, das in die Warteschlange eingereist werden soll. Wenn Sie das Hilfsprogramm nicht blockierend verwenden möchten, müssen Sie nur einige Änderungen vornehmen. In diesem Abschnitt wird die nicht blockierende Verwendung mit beiden Geräten in einem Thread veranschaulicht.

Initialisierung
Die Initialisierung ist mit Ausnahme der Flags identisch. Da es sich bei der Anwendung um Singlethreads handelt, verwenden Sie dieses Flag für die Erstellung. Dadurch wird ein Teil des Synchronisierungscodes deaktiviert, was die Leistung potenziell verbessert.

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);

Das Öffnen der Producer- und Consumergeräte ist identisch mit dem im Blockierungsbeispiel.
Verwenden der Warteschlange
Es gibt viele Möglichkeiten, die Warteschlange nicht blockierend mit verschiedenen Leistungsmerkmalen zu verwenden. Das folgende Beispiel ist einfach, weist jedoch aufgrund übermäßiger Dreh- und Abrufvorgänge eine schlechte Leistung auf. Trotz dieser Probleme zeigt das Beispiel die Verwendung des Helfers. Der Ansatz besteht darin, ständig in einer Schleife zu sitzen und aus der Warteschlange zu entfernen, zu verarbeiten, in die Warteschlange zu stellen und zu leeren. Wenn einer der Schritte fehlschlägt, weil die Ressource nicht verfügbar ist, versucht die Anwendung einfach die nächste Schleife erneut.

// 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);
}

Eine komplexere Lösung könnte den Rückgabewert aus der Warteschlange und der Leerung überprüfen, um zu ermitteln, ob eine Leerung erforderlich ist.

Drei Geräte

Die Erweiterung der vorherigen Beispiele auf mehrere Geräte ist einfach. Der folgende Code führt die Initialisierung durch. Nachdem die Producer/Consumer-Objekte erstellt wurden, ist der Code, mit dem sie verwendet werden sollen, identisch. Dieses Beispiel enthält drei Geräte und somit drei Warteschlangen. Oberflächen fließen von Direct3D 9 über Direct3D 10 bis 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);

Wie bereits erwähnt, funktioniert das Klonen auf die gleiche Weise, unabhängig davon, welche Warteschlange geklont wird. Beispielsweise könnte der zweite Clone-Aufruf aus dem m_9to10Queue-Objekt gewesen sein.

// 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);

Zusammenfassung

Sie können Lösungen erstellen, die die Interoperabilität verwenden, um die Leistungsfähigkeit mehrerer DirectX-APIs zu nutzen. Die Interoperabilität der Windows-Grafik-API bietet jetzt eine allgemeine Surface Management Runtime DXGI 1.1. Diese Runtime ermöglicht die Unterstützung für die synchronisierte Oberflächenfreigabe in neu entwickelten APIs, z. B. Direct3D 11, Direct3D 10.1 und Direct2D. Verbesserungen der Interoperabilität zwischen neuen APIs und vorhandenen APIs unterstützen die Anwendungsmigration und Abwärtskompatibilität. Direct3D 9Ex- und DXGI 1.1-Consumer-APIs können zusammenarbeiten, wie mit dem Synchronisierungsmechanismus gezeigt, der über Beispielhilfscode in der MSDN Code Gallery bereitgestellt wird.