DirectX Graphics Infrastructure (DXGI): Best Practices
Microsoft DirectX Graphics Infrastructure (DXGI) is een nieuw subsysteem dat is geïntroduceerd met Windows Vista waarmee enkele van de taken op laag niveau worden ingekapseld die nodig zijn voor Direct3D 10, 10.1, 11 en 11.1. Vanuit het perspectief van een Direct3D 9-programmeur omvat DXGI de meeste code voor inventarisatie, het maken van wisselketens en presentaties die eerder in de Direct3D 9-API's waren verpakt. Wanneer u een app overdrat naar DXGI en Direct3D 10.x en Direct3D 11.x, moet u rekening houden met enkele overwegingen om ervoor te zorgen dat het proces soepel verloopt.
In dit artikel worden belangrijke problemen met het overzetten besproken.
- Full-Screen problemen
- meerdere monitors
- vensterstijlen en DXGI
- Multithreading en DXGI-
- Gamma- en DXGI-
- DXGI 1.1
- DXGI 1,2
problemen met Full-Screen
Bij het overzetten van Direct3D 9 naar DXGI en direct3D 10.x of Direct3D 11.x kunnen problemen met betrekking tot het verplaatsen van vensters naar de modus volledig scherm vaak hoofdpijn veroorzaken voor ontwikkelaars. De belangrijkste problemen ontstaan omdat Direct3D 9-toepassingen, in tegenstelling tot DXGI-toepassingen, een meer praktische benadering vereisen voor het bijhouden van vensterstijlen en vensterstatussen. Wanneer de modus veranderende code wordt overgezet om te worden uitgevoerd op DXGI, veroorzaakt dit vaak onverwacht gedrag.
Vaak hebben Direct3D 9-toepassingen de overgang verwerkt in de modus Volledig scherm door de resolutie van de frontbuffer in te stellen, het apparaat af te dwingen in de exclusieve modus volledig scherm en vervolgens de resolutie van de backbuffer in te stellen op overeenkomst. Er is een afzonderlijk pad gebruikt voor wijzigingen in de venstergrootte, omdat deze moeten worden beheerd vanuit het vensterproces wanneer de toepassing een WM_SIZE bericht heeft ontvangen.
DXGI probeert deze benadering te vereenvoudigen door de twee gevallen te combineren. Wanneer de vensterrand bijvoorbeeld in de venstermodus wordt gesleept, ontvangt de toepassing een WM_SIZE bericht. DXGI onderschept dit bericht en wijzigt automatisch de grootte van de frontbuffer. Het enige wat de toepassing moet doen, is IDXGISwapChain::ResizeBuffers aanroepen om het formaat van de backbuffer te wijzigen in de grootte die is doorgegeven als parameters in WM_SIZE. Als de toepassing moet schakelen tussen de modus volledig scherm en de venstermodus, kan de toepassing eenvoudig IDXGISwapChain::SetFullscreenStateaanroepen. DXGI wijzigt de grootte van de frontbuffer zodat deze overeenkomt met de zojuist geselecteerde modus voor volledig scherm en verzendt een WM_SIZE bericht naar de toepassing. De toepassing roept opnieuw ResizeBuffersaan, net zoals wanneer de vensterrand werd gesleept.
De methodologie van de voorgaande uitleg volgt een heel bepaald pad. DXGI stelt de volledige schermresolutie standaard in op de bureaubladresolutie. Veel toepassingen schakelen echter over naar een voorkeursresolutie voor volledig scherm. In dat geval biedt DXGI IDXGISwapChain::ResizeTarget. Dit moet worden aangeroepen voordat u SetFullscreenStateaanroept. Hoewel deze methoden in de tegenovergestelde volgorde kunnen worden aangeroepen (SetFullscreenState eerst, gevolgd door ResizeTarget), zorgt dit ervoor dat er een extra WM_SIZE bericht naar de toepassing wordt verzonden. (Als u dit doet, kan dit ook flikkeren, omdat DXGI kan worden gedwongen om twee moduswijzigingen uit te voeren.) Na het aanroepen van SetFullscreenState, is het raadzaam om ResizeTar get opnieuw aan te roepen met het RefreshRate lid van DXGI_MODE_DESC nul uitgeschakeld. Dit komt neer op een instructie zonder bewerking in DXGI, maar het kan problemen met de vernieuwingsfrequentie voorkomen, die hierna worden besproken.
Wanneer u zich in de modus Volledig scherm bevindt, wordt DWM (Desktop Window Manager) uitgeschakeld. DXGI kan een flip uitvoeren om de inhoud van de backbuffer weer te geven in plaats van een blit uit te voeren, wat het zou doen in de venstermodus. Deze prestatiewinst kan echter ongedaan worden gemaakt als niet aan bepaalde vereisten wordt voldaan. Om ervoor te zorgen dat DXGI een flip in plaats van een blit uitvoert, moeten de voorbuffer en achterbuffer identiek worden gedimensioneerd. Als de toepassing de WM_SIZE berichten correct verwerkt, is dit geen probleem. Ook moeten de formaten identiek zijn.
Het probleem voor de meeste toepassingen is de vernieuwingsfrequentie. De vernieuwingsfrequentie die is opgegeven in de aanroep naar ResizeTarget- moet een vernieuwingsfrequentie zijn die is geïnventariseerd door het IDXGIOutput--object dat door de wisselketen wordt gebruikt. DXGI kan deze waarde automatisch berekenen indien de toepassing het RefreshRate-lid van DXGI_MODE_DESC dat wordt doorgegeven aan ResizeTargetop nul zet. Het is belangrijk dat u niet ervan uitgaat dat bepaalde vernieuwingsfrequenties altijd worden ondersteund en dat u gewoon een waarde in code wilt codeeren. Vaak kiezen ontwikkelaars 60 Hz als vernieuwingsfrequentie, niet wetende dat de geïnventariseerd vernieuwingsfrequentie van de monitor ongeveer 60.000 / 1001 Hz van de monitor is. Als de vernieuwingsfrequentie niet overeenkomt met de verwachte vernieuwingssnelheid van 60, wordt DXGI gedwongen om een blit uit te voeren in de modus volledig scherm in plaats van een flip.
Het laatste probleem waarmee ontwikkelaars vaak te maken krijgen, is het wijzigen van resoluties op volledig scherm terwijl ze in de modus Volledig scherm blijven. Het aanroepen van ResizeTarget en SetFullscreenState lukt soms wel, maar de resolutie van het volledige scherm blijft de resolutie van het bureaublad. Ontwikkelaars kunnen ook een wisselketen voor volledig scherm maken en een specifieke resolutie geven, alleen om te vinden dat DXGI standaard de desktopresolutie heeft, ongeacht de nummers die zijn doorgegeven. Tenzij anders aangegeven, kiest DXGI standaard de desktopresolutie voor wisselketens op volledig scherm. Wanneer u een wisselketen voor volledig scherm maakt, moeten de Vlaggen lid van de DXGI_SWAP_CHAIN_DESC-structuur worden ingesteld op DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH om het standaardgedrag van DXGI te overschrijven. Deze vlag kan ook worden doorgegeven aan ResizeTarget- om deze functionaliteit dynamisch in of uit te schakelen.
Meerdere beeldschermen
Wanneer u DXGI met meerdere monitors gebruikt, zijn er twee regels die moeten worden gevolgd.
De eerste regel geldt voor het creëren van twee of meer swapchains voor volledig scherm op meerdere beeldschermen. Wanneer u dergelijke swapketens maakt, kunt u het beste alle wisselketens als venster maken en deze vervolgens instellen op volledig scherm. Als wisselketens worden gemaakt in de modus Volledig scherm, zorgt het maken van een tweede wisselketen ervoor dat een moduswijziging wordt verzonden naar de eerste wisselketen, waardoor de modus volledig scherm kan worden beëindigd.
De tweede regel is van toepassing op uitvoer. Let op de uitvoer die wordt gebruikt bij het maken van swap chains. Met DXGI controleert het IDXGIOutput object welk beeldscherm het schakelkanaal gebruikt wanneer deze naar volledig scherm gaat. In tegenstelling tot DXGI had Direct3D 9 geen concept van weergave-uitgangen.
Vensterstijlen en DXGI
Direct3D 9-toepassingen hadden veel werk te doen bij het schakelen tussen de modus volledig scherm en vensters. Veel van dit werk was het wijzigen van vensterstijlen voor het toevoegen en verwijderen van randen, het toevoegen van schuifbalken, enzovoort. Wanneer toepassingen worden overgezet naar DXGI en Direct3D 10.x of Direct3D 11.x, blijft deze code vaak aanwezig. Afhankelijk van de wijzigingen die worden aangebracht, kan het schakelen tussen modi onverwacht gedrag veroorzaken. Wanneer u bijvoorbeeld overschakelt naar de venstermodus, heeft de toepassing mogelijk geen vensterkader of vensterrand meer, ondanks dat er code is waarmee deze stijlen specifiek worden ingesteld. Dit komt doordat DXGI nu veel van deze stijlwijzigingen vanuit zichzelf afhandelt. Handmatige instelling van vensterstijlen kan dxgi verstoren en dit kan onverwacht gedrag veroorzaken.
Het aanbevolen gedrag is om zo weinig mogelijk werk te doen en DXGI de meeste interactie met de vensters te laten verwerken. Als de toepassing echter een eigen venstergedrag moet afhandelen, kan IDXGIFactory::MakeWindowAssociation worden gebruikt om DXGI te vertellen een deel van de automatische verwerking van vensters uit te schakelen.
Multithreading en DXGI
Er moet speciale aandacht worden besteed aan het gebruik van DXGI in een multithreaded-toepassing om ervoor te zorgen dat impasses niet optreden. Vanwege de nauwe interactie van DXGI met vensters, worden soms vensterberichten verzonden naar het bijbehorende toepassingsvenster. DXGI heeft de vensterwijzigingen nodig voordat deze kan worden voortgezet, dus wordt SendMessage-gebruikt. Dit is een synchrone aanroep. De toepassing moet het vensterbericht verwerken voordat SendMessage retourneert.
In een toepassing waar DXGI-aanroepen en de berichtpomp zich op dezelfde thread bevinden (of een toepassing met één thread), hoeft er weinig te gebeuren. Wanneer de DXGI-aanroep zich op dezelfde thread bevindt als de berichtpomp, roept SendMessage- de WindowProc-van het venster aan. Hierdoor wordt de berichtpomp omzeild en kan de uitvoering worden voortgezet na het aanroepen van SendMessage-. Houd er rekening mee dat IDXGISwapChain-aanroepen, zoals IDXGISwapChain::Present, ook worden beschouwd als DXGI-aanroepen; DXGI kan werk uitstellen van ResizeBuffers of ResizeTarget totdat Present wordt opgeroepen.
Als de DXGI-oproep en berichtpomp zich op verschillende threads bevinden, moet u ervoor zorgen dat er geen deadlocks voorkomen. Wanneer de berichtpomp en SendMessage zich op verschillende threads bevinden, voegt SendMessage- een bericht toe aan de berichtenwachtrij van het venster en wacht totdat het venster dat bericht verwerkt. Als de vensterprocedure bezet is of niet wordt aangeroepen door de berichtpomp, wordt het bericht mogelijk nooit verwerkt en wacht DXGI voor onbepaalde tijd.
Als een toepassing bijvoorbeeld de berichtpomp op de ene thread heeft en de rendering op een andere, kan het nodig zijn de modi te wijzigen. De berichtenpomp-thread vertelt de rendering-thread om de modus te wijzigen en wacht totdat de moduswijziging is voltooid. De renderingthread roept echter DXGI-functies aan, die op hun beurt SendMessage-aanroepen, die worden geblokkeerd totdat de berichtpomp het bericht verwerkt. Er treedt een impasse op omdat beide threads nu zijn geblokkeerd en op elkaar wachten. Om dit te voorkomen, blokkeert u nooit de berichtpomp. Als een blok onvermijdelijk is, moet alle DXGI-interactie plaatsvinden op dezelfde draad als de berichtpomp.
Gamma en DXGI
Hoewel gamma het beste kan worden verwerkt in Direct3D 10.x of Direct3D 11.x met behulp van SRGB-patronen, kan de gamma-ramp nog steeds nuttig zijn voor ontwikkelaars die een andere gammawaarde willen dan 2.2 of die een renderdoelindeling gebruiken die SRGB niet ondersteunt. Let op twee problemen bij het instellen van de gamma-ramp via DXGI. Het eerste probleem is dat de rampwaarden die zijn doorgegeven aan IDXGIOutput::SetGammaControl- floatwaarden zijn, niet WORD--waarden. Zorg er ook voor dat code die wordt overgezet vanuit Direct3D 9 niet probeert te converteren naar WORD- waarden voordat deze worden doorgegeven aan SetGammaControl-.
Het tweede probleem is dat SetGammaControl- na het wijzigen in de modus Volledig scherm mogelijk niet werkt, afhankelijk van het IDXGIOutput- object dat wordt gebruikt. Wanneer u overschakelen naar de modus Volledig scherm, maakt DXGI een nieuw uitvoerobject en gebruikt het object voor alle volgende bewerkingen in de uitvoer. Als SetGammaControl wordt aangeroepen op een uitvoer die is geconfigureerd vóór een overschakeling naar de modus Volledig scherm, wordt de oproep niet doorgevoerd naar de uitvoer die DXGI momenteel gebruikt. Als u dit wilt voorkomen, roept u IDXGISwapChain::GetContainingOutput aan om de huidige uitvoer op te halen en roept u SetGammaControl van deze uitvoer af om het juiste gedrag te krijgen.
Zie Gammacorrectie gebruikenvoor meer informatie over het gebruik van gammacorrectie.
DXGI 1.1
De Direct3D 11-runtime die is opgenomen in Windows 7 en op Windows Vista is geïnstalleerd, bevat versie 1.1 van DXGI. Met deze update worden definities toegevoegd voor een aantal nieuwe indelingen (met name BGRA, 10-bits X2-bias en de BC6H- en BC7-patrooncompressie van Direct3D 11), evenals een nieuwe versie van de DXGI-fabrieks- en adapterinterfaces (CreateDXGIFactory1, IDXGIFactory1, IDXGIAdapter1) voor het inventariseren van externe bureaubladverbindingen.
Wanneer u Direct3D 11 gebruikt, gebruikt de runtime DXGI 1.1 standaard bij het aanroepen van D3D11CreateDevice of D3D11CreateDeviceAndSwapChain met een NULL-IDXGIAdapter aanwijzer. Het mengen van DXGI 1.0 en DXGI 1.1 in hetzelfde proces wordt niet ondersteund. Het mengen van DXGI-objectexemplaren van verschillende factory's in hetzelfde proces wordt ook niet ondersteund. Wanneer u DirectX 11 gebruikt, gebruikt elk expliciet gebruik van de DXGI-interfaces daarom een IDXGIFactory1- gemaakt door het CreateDXGIFactory1 invoerpunt in 'DXGI.DLL' om ervoor te zorgen dat de toepassing altijd DXGI 1.1 gebruikt.
DXGI 1.2
De Direct3D 11.1-runtime die is opgenomen in Windows 8 bevat ook versie 1.2 van DXGI.
DXGI 1.2 maakt deze functies mogelijk:
stereorendering
Formaten van 16 bits per pixel
- DXGI_FORMAT_B5G6R5_UNORM en DXGI_FORMAT_B5G5R5A1_UNORM worden nu volledig ondersteund
- er is een nieuwe DXGI_FORMAT_B5G5R5A1_UNORM-indeling toegevoegd
video-indelingen
nieuwe DXGI-interfaces
Zie DXGI 1.2 Improvementsvoor meer informatie over DXGI 1.2-functies.