Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Elke ontwikkelaar die realtime toepassingen maakt die gebruikmaken van 3D-afbeeldingen, maakt zich zorgen over prestatieoptimalisatie. Deze sectie bevat richtlijnen voor het verkrijgen van de beste prestaties van uw code.
- Algemene tips voor prestaties
- databases en
- Batching Primitives
- Verlichtingstips
- patroongrootte
- Matrix transformeert
- Dynamische patronen gebruiken
- dynamische hoekpunt- en indexbuffers gebruiken
- Meshes gebruiken
- Z-Buffer Performance
Algemene tips voor prestaties
- Wis alleen wanneer u dat wilt.
- Minimaliseer statuswijzigingen en groepeer de resterende statuswijzigingen.
- Gebruik kleinere patronen als u dit kunt doen.
- Teken objecten in uw scène van voor naar achter.
- Gebruik driehoekstrips in plaats van lijsten en ventilatoren. Voor optimale prestaties van de hoekpuntcache rangschikt u stroken om driehoekpunten eerder te hergebruiken in plaats van later.
- Verbreed speciale effecten die een onevenredig deel van systeembronnen vereisen, probleemloos.
- Test voortdurend de prestaties van uw toepassing.
- Beperk hoekpuntbufferschakelaars tot een minimum.
- Gebruik waar mogelijk statische hoekpuntbuffers.
- Gebruik één grote statische hoekpuntbuffer per FVF voor statische objecten, in plaats van één per object.
- Als uw toepassing willekeurige toegang nodig heeft tot de hoekpuntbuffer in het AGP-geheugen, kiest u een grootte van een hoekpuntindeling met een veelvoud van 32 bytes. Selecteer anders de kleinste indeling.
- Tekenen met geïndexeerde primitieven. Dit kan een efficiëntere vertexcaching binnen hardware mogelijk maken.
- Als de dieptebufferindeling een stencilkanaal bevat, wist u altijd tegelijkertijd de diepte- en stencilkanalen.
- Combineer waar mogelijk de shader-instructie en de gegevensuitvoer. Bijvoorbeeld:
// Rather than doing a multiply and add, and then output the data with // two instructions: mad r2, r1, v0, c0 mov oD0, r2 // Combine both in a single instruction, because this eliminates an // additional register copy. mad oD0, r1, v0, c0
Databases en afsningen
Het bouwen van een betrouwbare database van de objecten in uw wereld is essentieel voor uitstekende prestaties in Direct3D. Het is belangrijker dan verbeteringen in rasterisatie of hardware.
U moet het laagste aantal veelhoeken behouden dat u mogelijk kunt beheren. Ontwerp voor een laag aantal veelhoeken door vanaf het begin modellen met weinig veelhoeken te bouwen. Voeg veelhoeken toe als u dit kunt doen zonder de prestaties later in het ontwikkelingsproces op te offeren. Onthoud dat de snelste veelhoeken degene zijn die u niet tekent.
Primitieven batcheren
Als u de beste renderingprestaties wilt krijgen tijdens de uitvoering, probeert u met primitieven in batches te werken en het aantal wijzigingen in de renderstatus zo laag mogelijk te houden. Als u bijvoorbeeld een object met twee patronen hebt, groepeert u de driehoeken die het eerste patroon gebruiken en volgt u deze met de benodigde renderstatus om het patroon te wijzigen. Groepeer vervolgens alle driehoeken die gebruikmaken van het tweede patroon. De eenvoudigste hardwareondersteuning voor Direct3D wordt aangeroepen met batches van renderstatussen en batches primitieven via de hardwareabstractielaag (HAL). Hoe effectiever de instructies worden gebatcheerd, hoe minder HAL-aanroepen worden uitgevoerd tijdens de uitvoering.
Verlichtingstips
Omdat verlichting een kosten per hoekpunt toevoegt aan elk gerenderd frame, kunt u de prestaties aanzienlijk verbeteren door voorzichtig te zijn met hoe u ze in uw toepassing gebruikt. De meeste van de volgende tips zijn afgeleid van het maximum, 'de snelste code is code die nooit wordt aangeroepen'.
- Gebruik zo weinig mogelijk lichtbronnen. Als u bijvoorbeeld het algehele verlichtingsniveau wilt verhogen, gebruikt u het omgevingslicht in plaats van een nieuwe lichtbron toe te voegen.
- Directionele lichten zijn efficiënter dan puntlichten of spotlights. Voor richtingslichten is de richting van het licht vast en hoeft niet per hoekpunt te worden berekend.
- Spotlights kunnen efficiënter zijn dan puntlichten, omdat het gebied buiten het kegellicht snel wordt berekend. Of spotlights efficiënter zijn of niet, hangt af van hoeveel van uw scène wordt verlicht door de spotlight.
- Gebruik de bereikparameter om uw lichten te beperken tot alleen de delen van de scène die u nodig hebt om te verlichten. Alle lichttypen sluiten vrij vroeg af wanneer ze buiten bereik zijn.
- Specular markeert bijna het dubbele van de kosten van een licht. Gebruik ze alleen wanneer u dat moet doen. Stel de status D3DRS_SPECULARENABLE render in op 0, de standaardwaarde, indien mogelijk. Bij het definiëren van materialen moet u de speculaire machtswaarde instellen op nul om speculaire markeringen voor dat materiaal uit te schakelen; het instellen van de speculaire kleur op 0,0,0 is niet voldoende.
Patroongrootte
De prestaties van bitmaptoewijzing zijn sterk afhankelijk van de snelheid van het geheugen. Er zijn verschillende manieren om de cacheprestaties van de patronen van uw toepassing te maximaliseren.
- Houd de patronen klein. Hoe kleiner de patronen zijn, hoe groter de kans dat ze worden onderhouden in de secundaire cache van de hoofd-CPU.
- Wijzig de patronen niet per primitieve basis. Probeer veelhoeken gegroepeerd te houden in volgorde van de patronen die ze gebruiken.
- Gebruik waar mogelijk vierkante patronen. Patronen waarvan de afmetingen 256x256 zijn, zijn de snelste. Als uw toepassing bijvoorbeeld vier patronen van 128x128 gebruikt, probeert u ervoor te zorgen dat ze hetzelfde palet gebruiken en ze allemaal in één patroon van 256x256 plaatsen. Deze techniek vermindert ook de hoeveelheid textuurwisseling. Natuurlijk moet u geen 256x256 texturen gebruiken, tenzij uw toepassing zoveel tekst vereist, omdat patronen, zoals vermeld, zo klein mogelijk moeten worden gehouden.
Matrixtransformaties
Direct3D maakt gebruik van de wereld en bekijk matrices die u instelt om verschillende interne gegevensstructuren te configureren. Telkens wanneer u een nieuwe wereld of weergavematrix instelt, berekent het systeem de bijbehorende interne structuren opnieuw. Het regelmatig instellen van deze matrices, bijvoorbeeld duizenden keren per frame, kost rekenkracht veel tijd. U kunt het aantal vereiste berekeningen minimaliseren door uw wereld samen te voegen en matrices te bekijken in een wereldweergavematrix die u als wereldmatrix hebt ingesteld en vervolgens de weergavematrix in te stellen op de identiteit. Bewaar kopieën van afzonderlijke wereld en bekijk matrices in de cache, zodat u de wereldmatrix zo nodig kunt wijzigen, samenvoegen en opnieuw kunt instellen. Ter duidelijkheid in deze documentatie maken Direct3D-voorbeelden zelden gebruik van deze optimalisatie.
Dynamische patronen gebruiken
Als u wilt controleren of het stuurprogramma dynamische patronen ondersteunt, controleert u de D3DCAPS2_DYNAMICTEXTURES vlag van de D3DCAPS9-structuur.
Houd rekening met de volgende dingen bij het werken met dynamische patronen.
- Ze kunnen niet worden beheerd. De groep kan bijvoorbeeld niet worden D3DPOOL_MANAGED.
- Dynamische patronen kunnen worden vergrendeld, zelfs als ze worden gemaakt in D3DPOOL_DEFAULT.
- D3DLOCK_DISCARD is een geldige vergrendelingsvlag voor dynamische patronen.
Het is een goed idee om slechts één dynamisch patroon per indeling en mogelijk per grootte te maken. Dynamische mipmaps, kubussen en volumes worden niet aanbevolen vanwege de extra overhead bij het vergrendelen van elk niveau. Voor mipmaps is D3DLOCK_DISCARD alleen toegestaan op het hoogste niveau. Alle niveaus worden genegeerd door alleen het hoogste niveau te vergrendelen. Dit gedrag is hetzelfde voor volumes en kubussen. Voor kubussen zijn het hoogste niveau en het gezicht 0 vergrendeld.
De volgende pseudocode toont een voorbeeld van het gebruik van een dynamische textuur.
DrawProceduralTexture(pTex)
{
// pTex should not be very small because overhead of
// calling driver every D3DLOCK_DISCARD will not
// justify the performance gain. Experimentation is encouraged.
pTex->Lock(D3DLOCK_DISCARD);
<Overwrite *entire* texture>
pTex->Unlock();
pDev->SetTexture();
pDev->DrawPrimitive();
}
Dynamische hoekpunt- en indexbuffers gebruiken
Het vergrendelen van een statische hoekpuntbuffer terwijl de grafische processor de buffer gebruikt, kan een aanzienlijke prestatiestraf hebben. De vergrendelingsoproep moet wachten totdat de grafische processor klaar is met het lezen van hoekpunt- of indexgegevens uit de buffer voordat deze kan terugkeren naar de aanroepende toepassing, een aanzienlijke vertraging. Door een statische buffer meerdere keren per frame te vergrendelen en weer te geven, voorkomt u ook dat de grafische processor renderingopdrachten buffert, omdat deze opdrachten moet voltooien voordat de vergrendelingspunt wordt geretourneerd. Zonder gebufferde opdrachten blijft de grafische processor inactief totdat de toepassing de hoekpuntbuffer of indexbuffer vult en een renderingopdracht uitgeeft.
In het ideale instantie zouden de hoekpunt- of indexgegevens nooit veranderen, maar dit is niet altijd mogelijk. Er zijn veel situaties waarin de toepassing elk frame van hoekpunt of indexgegevens moet wijzigen, misschien zelfs meerdere keren per frame. Voor deze situaties moet de hoekpunt- of indexbuffer worden gemaakt met D3DUSAGE_DYNAMIC. Deze gebruiksvlag zorgt ervoor dat Direct3D wordt geoptimaliseerd voor frequente vergrendelingsbewerkingen. D3DUSAGE_DYNAMIC is alleen nuttig wanneer de buffer regelmatig wordt vergrendeld; gegevens die constant blijven, moeten in een statisch hoekpunt of indexbuffer worden geplaatst.
Als u een prestatieverbetering wilt ontvangen bij het gebruik van dynamische hoekpuntbuffers, moet de toepassing IDirect3DVertexBuffer9::Lock of IDirect3DIndexBuffer9::Lock aanroepen met de juiste vlaggen. D3DLOCK_DISCARD geeft aan dat de toepassing het oude hoekpunt of de indexgegevens niet in de buffer hoeft te bewaren. Als de grafische processor nog steeds gebruikmaakt van de buffer wanneer de vergrendeling wordt aangeroepen met D3DLOCK_DISCARD, wordt een aanwijzer naar een nieuw geheugengebied geretourneerd in plaats van de oude buffergegevens. Hierdoor kan de grafische processor de oude gegevens blijven gebruiken terwijl de toepassing gegevens in de nieuwe buffer plaatst. Er is geen extra geheugenbeheer vereist in de toepassing; de oude buffer wordt automatisch opnieuw gebruikt of vernietigd wanneer de grafische processor ermee klaar is. Houd er rekening mee dat het vergrendelen van een buffer met D3DLOCK_DISCARD de hele buffer altijd negeert, waarbij een niet-nul-offset of een beperkt grootteveld wordt opgegeven, geen informatie in ontgrendelde gebieden van de buffer behoudt.
Er zijn gevallen waarin de hoeveelheid gegevens die de toepassing per vergrendeling moet opslaan klein is, zoals het toevoegen van vier hoekpunten om een sprite weer te geven. D3DLOCK_NOOVERWRITE geeft aan dat de toepassing geen gegevens overschrijft die al in de dynamische buffer worden gebruikt. De vergrendelingsoproep retourneert een aanwijzer naar de oude gegevens, zodat de toepassing nieuwe gegevens kan toevoegen in ongebruikte regio's van het hoekpunt of de indexbuffer. De toepassing mag geen hoekpunten of indexen wijzigen die worden gebruikt in een tekenbewerking, omdat ze mogelijk nog steeds in gebruik zijn door de grafische processor. De toepassing moet vervolgens D3DLOCK_DISCARD gebruiken nadat de dynamische buffer vol is om een nieuw geheugengebied te ontvangen, waarbij het oude hoekpunt of de indexgegevens worden verwijderd nadat de grafische processor is voltooid.
Het asynchrone querymechanisme is handig om te bepalen of hoekpunten nog steeds worden gebruikt door de grafische processor. Geef een query van het type D3DQUERYTYPE_EVENT uit na de laatste DrawPrimitive-aanroep die gebruikmaakt van de hoekpunten. De hoekpunten worden niet meer gebruikt wanneer IDirect3DQuery9::GetData- S_OK retourneert. Het vergrendelen van een buffer met D3DLOCK_DISCARD of geen vlaggen garandeert altijd dat de hoekpunten correct worden gesynchroniseerd met de grafische processor, maar het gebruik van vergrendeling zonder vlaggen leidt tot de prestatiestraf die eerder is beschreven. Andere API-aanroepen zoals IDirect3DDevice9::BeginScene, IDirect3DDevice9::EndSceneen IDirect3DDevice9::P resent garanderen niet dat de grafische processor klaar is met behulp van hoekpunten.
Hieronder ziet u manieren om dynamische buffers en de juiste vergrendelingsvlagmen te gebruiken.
// USAGE STYLE 1
// Discard the entire vertex buffer and refill with thousands of vertices.
// Might contain multiple objects and/or require multiple DrawPrimitive
// calls separated by state changes, etc.
// Determine the size of data to be moved into the vertex buffer.
UINT nSizeOfData = nNumberOfVertices * m_nVertexStride;
// Discard and refill the used portion of the vertex buffer.
CONST DWORD dwLockFlags = D3DLOCK_DISCARD;
// Lock the vertex buffer.
BYTE* pBytes;
if( FAILED( m_pVertexBuffer->Lock( 0, 0, &pBytes, dwLockFlags ) ) )
return false;
// Copy the vertices into the vertex buffer.
memcpy( pBytes, pVertices, nSizeOfData );
m_pVertexBuffer->Unlock();
// Render the primitives.
m_pDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, nNumberOfVertices/3)
// USAGE STYLE 2
// Reusing one vertex buffer for multiple objects
// Determine the size of data to be moved into the vertex buffer.
UINT nSizeOfData = nNumberOfVertices * m_nVertexStride;
// No overwrite will be used if the vertices can fit into
// the space remaining in the vertex buffer.
DWORD dwLockFlags = D3DLOCK_NOOVERWRITE;
// Check to see if the entire vertex buffer has been used up yet.
if( m_nNextVertexData > m_nSizeOfVB - nSizeOfData )
{
// No space remains. Start over from the beginning
// of the vertex buffer.
dwLockFlags = D3DLOCK_DISCARD;
m_nNextVertexData = 0;
}
// Lock the vertex buffer.
BYTE* pBytes;
if( FAILED( m_pVertexBuffer->Lock( (UINT)m_nNextVertexData, nSizeOfData,
&pBytes, dwLockFlags ) ) )
return false;
// Copy the vertices into the vertex buffer.
memcpy( pBytes, pVertices, nSizeOfData );
m_pVertexBuffer->Unlock();
// Render the primitives.
m_pDevice->DrawPrimitive( D3DPT_TRIANGLELIST,
m_nNextVertexData/m_nVertexStride, nNumberOfVertices/3)
// Advance to the next position in the vertex buffer.
m_nNextVertexData += nSizeOfData;
Meshes gebruiken
U kunt meshes optimaliseren met behulp van geïndexeerde direct3D-driehoeken in plaats van geïndexeerde driehoekstrips. De hardware ontdekt dat 95 procent van de opeenvolgende driehoeken daadwerkelijk strips vormen en dienovereenkomstig aanpassen. Veel stuurprogramma's doen dit ook voor oudere hardware.
D3DX-mesh-objecten kunnen elke driehoek of elk gezicht hebben, getagd met een DWORD, het kenmerk van dat gezicht genoemd. De semantiek van het DWORD is door de gebruiker gedefinieerd. Ze worden door D3DX gebruikt om de mesh in subsets te classificeren. De toepassing stelt kenmerken per gezicht in met behulp van de ID3DXMesh::LockAttributeBuffer aanroep. De methode ID3DXMesh::Optimize heeft een optie om de mesh-hoekpunten en gezichten op kenmerken te groeperen met behulp van de optie D3DXMESHOPT_ATTRSORT. Wanneer dit gebeurt, berekent het mesh-object een kenmerktabel die kan worden verkregen door de toepassing door ID3DXBaseMesh::GetAttributeTableaan te roepen. Deze aanroep retourneert 0 als de mesh niet is gesorteerd op kenmerken. Een toepassing kan geen kenmerktabel instellen omdat deze wordt gegenereerd door de methode ID3DXMesh::Optimize. Het sorteren van kenmerken is gegevensgevoelig, dus als de toepassing weet dat een mesh is gesorteerd, moet deze nog steeds ID3DXMesh::Optimize aanroepen om de kenmerktabel te genereren.
In de volgende onderwerpen worden de verschillende kenmerken van een mesh beschreven.
Kenmerk-id
Een kenmerk-id is een waarde die een groep gezichten koppelt aan een kenmerkgroep. Deze id beschrijft welke subset van gezichten ID3DXBaseMesh::D rawSubset moet tekenen. Kenmerk-id's worden opgegeven voor de gezichten in de kenmerkbuffer. De werkelijke waarden van de kenmerk-id's kunnen alles zijn dat in 32 bits past, maar het is gebruikelijk om 0 tot n te gebruiken waarbij n het aantal kenmerken is.
Kenmerkbuffer
De kenmerkbuffer is een matrix van DWORDs (één per gezicht) die aangeeft in welke kenmerkgroep elk gezicht behoort. Deze buffer wordt geïnitialiseerd tot nul bij het maken van een mesh, maar wordt gevuld door de laadroutines of moet worden gevuld door de gebruiker als meer dan één kenmerk met id 0 gewenst is. Deze buffer bevat de informatie die wordt gebruikt om de mesh te sorteren op basis van kenmerken in ID3DXMesh::Optimize. Als er geen kenmerktabel aanwezig is, scant ID3DXBaseMesh::D rawSubset deze buffer om de gezichten van het opgegeven kenmerk te selecteren die moeten worden getekend.
Kenmerktabel
De kenmerktabel is een structuur die eigendom is van en wordt onderhouden door de mesh. De enige manier om er een te genereren is door ID3DXMesh::Optimaliseren met kenmerksortering of sterkere optimalisatie ingeschakeld. De kenmerktabel wordt gebruikt om snel één primitieve aanroep van tekenen te starten naar ID3DXBaseMesh::D rawSubset. Het enige andere gebruik is dat de voortgang van meshes deze structuur ook onderhoudt, zodat het mogelijk is te zien welke gezichten en hoekpunten actief zijn op het huidige detailniveau.
Prestaties van Z-buffer
Toepassingen kunnen de prestaties verbeteren bij het gebruik van z-buffering en texturing door ervoor te zorgen dat scènes van voor naar achter worden weergegeven. Gestructureerde z-buffered primitieven worden vooraf getest op de z-buffer op basis van een scanlijn. Als een scanlijn is verborgen door een eerder gegenereerde veelhoek, weigert het systeem deze snel en efficiënt. Z-buffering kan de prestaties verbeteren, maar de techniek is het handigst wanneer een scène meerdere pixels tekent. Dit is moeilijk om precies te berekenen, maar u kunt vaak een nauwe benadering maken. Als dezelfde pixels minder dan twee keer worden getekend, kunt u de beste prestaties bereiken door z-buffering uit te schakelen en de scène weer te geven van achter naar voor.
Verwante onderwerpen