Delen via


Multithreaded Direct2D-apps

Als u Direct2D--apps ontwikkelt, moet u mogelijk toegang krijgen tot Direct2D-resources vanuit meer dan één thread. In andere gevallen kunt u multithreading gebruiken om betere prestaties of betere reactiesnelheid te krijgen (zoals het gebruik van één thread voor schermweergave en een afzonderlijke thread voor offline rendering).

In dit onderwerp worden de aanbevolen procedures beschreven voor het ontwikkelen van multithreaded Direct2D-apps met weinig tot geen Direct3D rendering. Softwarefouten die worden veroorzaakt door gelijktijdigheidsproblemen, kunnen moeilijk worden opgespoord en het is handig om uw beleid voor multithreading te plannen en de aanbevolen procedures te volgen die hier worden beschreven.

Notitie

Als u toegang krijgt tot twee Direct2D resources die zijn gemaakt op basis van twee verschillende Direct2D-factory's met één thread, veroorzaakt dit geen toegangsconflicten zolang de onderliggende Direct3D apparaten en apparaatcontexten ook verschillend zijn. Wanneer u het hebt over het openen van Direct2D-resources in dit artikel, betekent dit echt 'toegang tot Direct2D-resources die zijn gemaakt op basis van hetzelfde Direct2D-apparaat', tenzij anders wordt aangegeven.

Ontwikkelen van Thread-Safe-apps die alleen Direct2D-API's aanroepen

U kunt een multithreaded Instantie van Direct2D factory maken. U kunt een multithreaded factory en alle bijbehorende resources uit meer dan één thread gebruiken en delen, maar toegang tot deze resources (via Direct2D-aanroepen) wordt geserialiseerd door Direct2D, zodat er geen toegangsconflicten optreden. Als uw app alleen Direct2D-API's aanroept, wordt deze beveiliging automatisch uitgevoerd door Direct2D op een gedetailleerd niveau met minimale overhead. De code voor het maken van een multithreaded factory hier.

ID2D1Factory* m_D2DFactory;

// Create a Direct2D factory.
HRESULT hr = D2D1CreateFactory(
    D2D1_FACTORY_TYPE_MULTI_THREADED,
    &m_D2DFactory
);

In de afbeelding ziet u hoe Direct2D twee threads serialiseert die alleen aanroepen doen met behulp van de Direct2D-API.

diagram van twee geserialiseerde threads.

Ontwikkelen van Thread-Safe Direct2D-apps met minimale Direct3D- of DXGI-aanroepen

Het is meer dan vaak dat een Direct2D--app ook enkele Direct3D-- of DXGI-aanroepen doet. Een weergavethread wordt bijvoorbeeld getekend in Direct2D en vervolgens weergegeven met behulp van een DXGI-wisselketen.

In dit geval is het garanderen van thread-veiligheid ingewikkelder: sommige Direct2D- roept indirect toegang tot de onderliggende Direct3D--resources, die gelijktijdig toegankelijk zijn voor een andere thread die Direct3D of DXGI aanroept. Aangezien deze Direct3D- of DXGI-aanroepen buiten het bewustzijn en de controle van Direct2D vallen, moet u een multithreaded Direct2D-fabriek maken, maar u moet wel mor doen om toegangsconflicten te voorkomen.

Het diagram hier toont een Direct3D resourcetoegangsconflict vanwege thread T0 die indirect toegang heeft tot een resource via een Direct2D--aanroep en T2 die rechtstreeks via een Direct3D- of DXGI-aanroep dezelfde resource opent.

Notitie

De threadbeveiliging die Direct2D- biedt (de blauwe vergrendeling in deze afbeelding) helpt in dit geval niet.

 

threadbeveiligingsdiagram.

We raden u aan expliciet de vergrendeling te verkrijgen die Direct2D- gebruikt voor interne toegangssynchronisatie en deze vergrendeling toepassen wanneer een thread Direct3D- of DXGI-aanroepen moet maken die mogelijk een toegangsconflict veroorzaken, zoals hier wordt weergegeven. In het bijzonder moet u speciale aandacht besteden aan code die gebruikmaakt van uitzonderingen of een early-outsysteem op basis van HRESULT-retourcodes. Daarom raden we u aan om een RAII-patroon (Resource Acquisition Is Initialization) te gebruiken om de Enter- aan te roepen en methoden te verlaten.

Notitie

Het is belangrijk dat u aanroepen koppelt aan de Enter- en methoden verlaten, anders kan uw app een impasse vormen.

 

De code hier toont een voorbeeld van wanneer u moet vergrendelen en vervolgens ontgrendelen rond Direct3D- of DXGI-aanroepen.

void MyApp::DrawFromThread2()
{
    // We are accessing Direct3D resources directly without Direct2D's knowledge, so we
    // must manually acquire and apply the Direct2D factory lock.
    ID2D1Multithread* m_D2DMultithread;
    m_D2DFactory->QueryInterface(IID_PPV_ARGS(&m_D2DMultithread));
    m_D2DMultithread->Enter();
    
    // Now it is safe to make Direct3D/DXGI calls, such as IDXGISwapChain::Present
    MakeDirect3DCalls();

    // It is absolutely critical that the factory lock be released upon
    // exiting this function, or else any consequent Direct2D calls will be blocked.
    m_D2DMultithread->Leave();
}

Notitie

Sommige Direct3D-- of DXGI-aanroepen (met name IDXGISwapChain::P resent) kunnen vergrendelingen verkrijgen en/of callbacks activeren in de code van de aanroepende functie of methode. U moet hiervan op de hoogte zijn en ervoor zorgen dat dergelijk gedrag geen impasses veroorzaakt. Zie het onderwerp DXGI Overview voor meer informatie.

 

diagram voor direct2d en direct3d threadvergrendeling.

Wanneer u de methoden Enter en Verlaten gebruikt, worden de aanroepen beveiligd door de automatische Direct2D- en de expliciete vergrendeling, zodat de app geen toegangsconflict ondervindt.

Er zijn andere methoden om dit probleem te omzeilen. We raden u echter aan expliciet Direct3D-- of DXGI-aanroepen te beveiligen met de Direct2D--vergrendeling, omdat deze meestal betere prestaties biedt omdat het gelijktijdigheid op veel nauwkeuriger niveau beschermt en met lagere overhead onder direct2D-dekking.

Atomiciteit van stateful bewerkingen garanderen

Hoewel thread-veiligheidsfuncties van DirectX- ervoor kunnen zorgen dat er geen twee afzonderlijke API-aanroepen gelijktijdig worden uitgevoerd, moet u er ook voor zorgen dat threads die stateful API-aanroepen maken elkaar niet verstoren. Hier volgt een voorbeeld.

  1. Er zijn twee regels tekst die u wilt weergeven op zowel het scherm (op thread 0) als het scherm buiten het scherm (op Thread 1): Regel 1 is 'A is groter' en Regel 2 is 'dan B', beide worden getekend met een effen zwarte borstel.
  2. Thread 1 tekent de eerste tekstregel.
  3. Thread 0 reageert op een gebruikersinvoer, werkt beide tekstregels bij naar respectievelijk 'B is kleiner' en 'dan A' en wijzigt de borstelkleur in effen rood voor zijn eigen tekening;
  4. Thread 1 gaat verder met het tekenen van de tweede tekstregel, die nu "dan A" is, met de rode kleurborstel;
  5. Ten slotte krijgen we twee regels tekst op het tekendoel buiten het scherm: "A is groter" in zwart en "dan A" in rood.

een diagram van schermthreads in- en uitschakelen.

In de bovenste rij tekent Thread 0 met huidige teksttekenreeksen en de huidige zwarte kwast. Thread 1 voltooit alleen tekening buiten het scherm op de bovenste helft.

In de middelste rij reageert Thread 0 op gebruikersinteractie, werkt de teksttekenreeksen en het penseel bij en vernieuwt het scherm. Op dit moment wordt Thread 1 geblokkeerd. In de onderste rij wordt de uiteindelijke weergave buiten het scherm na Thread 1 hervat met het tekenen van de onderste helft met een gewijzigde kwast en een gewijzigde tekenreeks.

Om dit probleem op te lossen, raden we u aan een afzonderlijke context te hebben voor elke thread, zodat:

  • U moet een kopie van de apparaatcontext maken, zodat veranderlijke resources (d.w. resources die kunnen variëren tijdens het weergeven of afdrukken, zoals tekstinhoud of het effen kleurborstel in het voorbeeld) niet veranderen wanneer u weergeeft. In dit voorbeeld moet u een kopie van deze twee regels tekst en het kleurenborstel onderhouden voordat u tekent. Door dit te doen, garandeert u dat elke thread volledige en consistente inhoud heeft om te tekenen en presenteren.
  • U moet resources met veel gewicht delen (zoals bitmaps en complexe effectgrafieken) die eenmaal worden geïnitialiseerd en die vervolgens nooit zijn gewijzigd in threads om de prestaties te verbeteren.
  • U kunt lichtgewicht resources delen (zoals effen kleurborstels en tekstopmaak) die eenmaal worden geïnitialiseerd en vervolgens nooit worden gewijzigd in threads of niet

Samenvatting

Wanneer u multithreaded Direct2D-apps ontwikkelt, moet u een multithreaded Direct2D-factory maken en vervolgens alle Direct2D-resources van die factory afleiden. Als een thread Direct3D-- of DXGI-aanroepen uitvoert, moet u ook expliciet de Direct2D-vergrendeling toepassen om deze Direct3D- of DXGI-aanroepen te bewaken. Bovendien moet u de contextintegriteit garanderen door een kopie van veranderlijke resources voor elke thread te hebben.