Not
Åtkomst till denna sida kräver auktorisation. Du kan prova att logga in eller byta katalog.
Åtkomst till denna sida kräver auktorisation. Du kan prova att byta katalog.
Alla utvecklare som skapar realtidsprogram som använder 3D-grafik bryr sig om prestandaoptimering. Det här avsnittet innehåller riktlinjer för att få bästa möjliga prestanda från din kod.
- Allmänna prestandatips
- databaser och
- Batchbearbetning av primitiver
- ljustips
- strukturstorlek
- matris transformerar
- Använda dynamiska texturer
- Med dynamiskt hörn och indexbuffertar
- använda mesh-
- Z-buffertprestanda
Allmänna prestandatips
- Rensa bara när du måste.
- Minimera tillståndsändringar och gruppera återstående tillståndsändringar.
- Använd mindre texturer, om du kan göra det.
- Rita objekt i din scen framifrån och tillbaka.
- Använd triangelband i stället för listor och fläktar. För optimal prestanda för hörncache, ordna remsor för att återanvända triangelhörn tidigare, snarare än senare.
- Försämra specialeffekterna på ett korrekt sätt som kräver en oproportionerlig andel av systemresurserna.
- Testa hela tiden programmets prestanda.
- Minimera brytpunktsbuffertar.
- Använd statiska hörnbuffertar där det är möjligt.
- Använd en stor statisk brytpunktsbuffert per FVF för statiska objekt i stället för en per objekt.
- Om programmet behöver slumpmässig åtkomst till hörnbufferten i AGP-minnet väljer du en hörnformatstorlek som är en multipel av 32 byte. Annars väljer du det minsta lämpliga formatet.
- Rita med indexerade primitiver. Detta kan möjliggöra effektivare hörncachelagring i maskinvaran.
- Om djupbuffertformatet innehåller en stencilkanal rensar du alltid djup- och stencilkanalerna samtidigt.
- Kombinera skuggningsinstruktionen och datautdata där det är möjligt. Till exempel:
// 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
Databaser och gallring
Att skapa en tillförlitlig databas med objekten i din värld är nyckeln till utmärkta prestanda i Direct3D. Det är viktigare än förbättringar av rastrering eller maskinvara.
Du bör behålla det lägsta antalet polygoner som du kan hantera. Utforma för ett lågt polygonantal genom att skapa modeller med låg polygon från början. Lägg till polygoner om du kan göra det utan att offra prestanda senare i utvecklingsprocessen. Kom ihåg att de snabbaste polygonerna är de som du inte ritar.
Batchbearbetning av primitiver
Om du vill få bästa renderingsprestanda under körningen kan du försöka arbeta med primitiver i batchar och hålla antalet ändringar i återgivningstillståndet så lågt som möjligt. Om du till exempel har ett objekt med två texturer grupperar du trianglar som använder den första strukturen och följer dem med det återgivningstillstånd som krävs för att ändra strukturen. Gruppera sedan alla trianglar som använder den andra strukturen. Det enklaste maskinvarustödet för Direct3D anropas med batchar med återgivningstillstånd och batchar med primitiver via maskinvaruabstraktionsskiktet (HAL). Ju effektivare instruktionerna batchas, desto färre HAL-anrop utförs under körningen.
Ljustips
Eftersom lampor lägger till en kostnad per hörn för varje renderad ram kan du förbättra prestanda avsevärt genom att vara försiktig med hur du använder dem i ditt program. De flesta av följande tips kommer från maximen "den snabbaste koden är kod som aldrig anropas".
- Använd så få ljuskällor som möjligt. Om du vill öka den övergripande belysningsnivån använder du till exempel det omgivande ljuset i stället för att lägga till en ny ljuskälla.
- Riktningsljus är effektivare än punktljus eller spotlights. För riktningsljus är ljusriktningen fast och behöver inte beräknas per brytpunkt.
- Spotlights kan vara effektivare än punktljus, eftersom området utanför konen av ljus beräknas snabbt. Om spotlights är effektivare eller inte beror på hur mycket av din scen som tänds av rampljuset.
- Använd intervallparametern för att begränsa lamporna till de delar av scenen som du behöver belysa. Alla ljustyper avslutas ganska tidigt när de är utom räckhåll.
- Specular markerar nästan dubbelt så mycket som kostnaden för ett ljus. Använd dem bara när du måste. Ange D3DRS_SPECULARENABLE återgivningstillståndet till 0, standardvärdet, när det är möjligt. När du definierar material måste du ange det spektulära effektvärdet till noll för att stänga av spektulära markeringar för materialet. det räcker inte att bara ange den spektulära färgen till 0,0,0.
Texturstorlek
Prestanda för strukturmappning är starkt beroende av minnets hastighet. Det finns ett antal sätt att maximera cacheprestandan för ditt programs texturer.
- Håll texturerna små. Ju mindre texturerna är, desto större chans har de att underhållas i huvudprocessorns sekundära cache.
- Ändra inte texturerna per primitiv basis. Försök att hålla polygoner grupperade i ordning efter de texturer de använder.
- Använd kvadratiska texturer när det är möjligt. Texturer vars dimensioner är 256x256 är de snabbaste. Om ditt program använder fyra 128x128-texturer kan du till exempel försöka se till att de använder samma palett och placera dem alla i en textur på 256x256. Den här tekniken minskar också mängden texturväxling. Naturligtvis bör du inte använda 256x256 texturer om inte ditt program kräver så mycket textur eftersom, som nämnts, texturer bör hållas så liten som möjligt.
Matristransformeringar
Direct3D använder världen och visar matriser som du anger för att konfigurera flera interna datastrukturer. Varje gång du anger en ny värld eller vymatris beräknar systemet om de associerade interna strukturerna. Att ställa in dessa matriser ofta – till exempel tusentals gånger per bildruta – är beräkningsmässigt tidskrävande. Du kan minimera antalet nödvändiga beräkningar genom att sammanfoga din värld och visa matriser i en matris med världsvyer som du anger som världsmatris och sedan ange visningsmatrisen till identiteten. Behåll cachelagrade kopior av en enskild värld och visa matriser så att du kan ändra, sammanfoga och återställa världsmatrisen efter behov. För tydlighetens skull använder Direct3D-exempel sällan den här optimeringen.
Använda dynamiska texturer
Om du vill ta reda på om drivrutinen stöder dynamiska texturer kontrollerar du D3DCAPS2_DYNAMICTEXTURES-flaggan för D3DCAPS9 struktur.
Tänk på följande när du arbetar med dynamiska texturer.
- De kan inte hanteras. Deras pool kan till exempel inte D3DPOOL_MANAGED.
- Dynamiska texturer kan låsas, även om de skapas i D3DPOOL_DEFAULT.
- D3DLOCK_DISCARD är en giltig låsflagga för dynamiska texturer.
Det är en bra idé att bara skapa en dynamisk struktur per format och eventuellt per storlek. Dynamiska mipmaps, kuber och volymer rekommenderas inte på grund av de extra kostnaderna för att låsa varje nivå. För mipmaps tillåts D3DLOCK_DISCARD endast på den översta nivån. Alla nivåer ignoreras genom att bara låsa den översta nivån. Det här beteendet är detsamma för volymer och kuber. För kuber är den översta nivån och ansiktet 0 låsta.
Följande pseudokod visar ett exempel på hur du använder en dynamisk struktur.
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();
}
Använda dynamiska brytpunkter och indexbuffertar
Om du låser en statisk brytpunktsbuffert medan grafikprocessorn använder bufferten kan det medföra betydande prestanda. Låsanropet måste vänta tills grafikprocessorn har läst hörn eller indexdata från bufferten innan den kan återgå till det anropande programmet, vilket är en betydande fördröjning. Om du låser och sedan återger från en statisk buffert flera gånger per bildruta hindras även grafikprocessorn från att buffrar renderingskommandon, eftersom den måste slutföra kommandona innan låspekaren returneras. Utan buffrade kommandon förblir grafikprocessorn inaktiv tills programmet har fyllt hörnbufferten eller indexbufferten och utfärdar ett återgivningskommando.
Helst skulle hörn- eller indexdata aldrig ändras, men det är inte alltid möjligt. Det finns många situationer där programmet måste ändra hörn eller indexdata varje bildruta, kanske till och med flera gånger per bildruta. För dessa situationer bör hörn- eller indexbufferten skapas med D3DUSAGE_DYNAMIC. Den här användningsflaggan gör att Direct3D optimeras för frekventa låsåtgärder. D3DUSAGE_DYNAMIC är bara användbart när bufferten låses ofta. data som förblir konstanta ska placeras i ett statiskt hörn eller en indexbuffert.
För att få en prestandaförbättring när du använder dynamiska hörnbuffertar måste programmet anropa IDirect3DVertexBuffer9::Lås eller IDirect3DIndexBuffer9::Lås med lämpliga flaggor. D3DLOCK_DISCARD anger att programmet inte behöver behålla det gamla hörnet eller indexdata i bufferten. Om grafikprocessorn fortfarande använder bufferten när lås anropas med D3DLOCK_DISCARD returneras en pekare till en ny minnesregion i stället för gamla buffertdata. På så sätt kan grafikprocessorn fortsätta att använda gamla data medan programmet placerar data i den nya bufferten. Ingen ytterligare minneshantering krävs i programmet. den gamla bufferten återanvänds eller förstörs automatiskt när grafikprocessorn är klar med den. Observera att om du låser en buffert med D3DLOCK_DISCARD alltid tar bort hela bufferten, bevaras inte information i olåst område i bufferten om du anger ett fält med förskjutning utan noll eller begränsad storlek.
Det finns fall där mängden data som programmet behöver lagra per lås är liten, till exempel att lägga till fyra hörn för att återge en sprite. D3DLOCK_NOOVERWRITE anger att programmet inte kommer att skriva över data som redan används i den dynamiska bufferten. Låsanropet returnerar en pekare till gamla data, vilket gör att programmet kan lägga till nya data i oanvända regioner i brytpunkten eller indexbufferten. Programmet bör inte ändra hörn eller index som används i en ritningsåtgärd eftersom de fortfarande kan användas av grafikprocessorn. Programmet bör sedan använda D3DLOCK_DISCARD när den dynamiska bufferten är full för att ta emot en ny minnesregion och ta bort det gamla hörnet eller indexdata när grafikprocessorn är klar.
Den asynkrona frågemekanismen är användbar för att avgöra om hörn fortfarande används av grafikprocessorn. Utfärda en fråga av typen D3DQUERYTYPE_EVENT efter det senaste DrawPrimitive-anropet som använder hörnen. Hörnen används inte längre när IDirect3DQuery9::GetData returnerar S_OK. Om du låser en buffert med D3DLOCK_DISCARD eller inga flaggor garanteras alltid att hörnen synkroniseras korrekt med grafikprocessorn, men att använda lås utan flaggor medför prestandastraffet som beskrevs tidigare. Andra API-anrop som IDirect3DDevice9::BeginScene, IDirect3DDevice9::EndSceneoch IDirect3DDevice9::P resent garanterar inte att grafikprocessorn är klar med hjälp av hörn.
Nedan visas sätt att använda dynamiska buffertar och rätt låsflaggor.
// 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;
Använda nät
Du kan optimera nät med direct3D-indexerade trianglar i stället för indexerade triangelremsor. Maskinvaran kommer att upptäcka att 95 procent av efterföljande trianglar faktiskt bildar remsor och justerar därefter. Många drivrutiner gör detta för äldre maskinvara också.
D3DX mesh-objekt kan ha varje triangel, eller ansikte, taggat med ett DWORD, som kallas attributet för det ansiktet. Semantiken för DWORD är användardefinierad. De används av D3DX för att klassificera nätet i delmängder. Programmet anger per ansiktsattribut med hjälp av anropet ID3DXMesh::LockAttributeBuffer. Metoden ID3DXMesh::Optimera har ett alternativ för att gruppera näthörn och ansikten på attribut med hjälp av alternativet D3DXMESHOPT_ATTRSORT. När detta är klart beräknar mesh-objektet en attributtabell som kan hämtas av programmet genom att anropa ID3DXBaseMesh::GetAttributeTable. Det här anropet returnerar 0 om nätet inte sorteras efter attribut. Det finns inget sätt för ett program att ange en attributtabell eftersom den genereras av metoden ID3DXMesh::Optimize. Attributsortering är datakänslig, så om programmet vet att ett nät är attributsorterat måste det fortfarande anropa ID3DXMesh::Optimera för att generera attributtabellen.
I följande avsnitt beskrivs de olika attributen för ett nät.
Attribut-ID
Ett attribut-ID är ett värde som associerar en grupp med ansikten med en attributgrupp. I det här ID:t beskrivs vilken delmängd av ansikten ID3DXBaseMesh::D rawSubset ska rita. Attribut-ID anges för ansikten i attributbufferten. De faktiska värdena för attribut-ID:n kan vara allt som passar i 32 bitar, men det är vanligt att använda 0 till n där n är antalet attribut.
Attributbuffert
Attributbufferten är en matris med DWORD:er (en per ansikte) som anger vilken attributgrupp varje ansikte tillhör. Den här bufferten initieras till noll när ett nät skapas, men fylls antingen i av belastningsrutinerna eller måste fyllas av användaren om mer än ett attribut med ID 0 önskas. Den här bufferten innehåller den information som används för att sortera nätet baserat på attribut i ID3DXMesh::Optimera. Om det inte finns någon attributtabell genomsöker ID3DXBaseMesh::D rawSubset den här bufferten för att välja ansikten för det angivna attributet som ska ritas.
Attributtabell
Attributtabellen är en struktur som ägs och underhålls av nätet. Det enda sättet för en att genereras är genom att anropa ID3DXMesh::Optimera med attributsortering eller starkare optimering aktiverat. Attributtabellen används för att snabbt initiera ett primitivt anrop till ID3DXBaseMesh::D rawSubset. Den enda andra användningen är att förloppsmaskor också upprätthåller den här strukturen, så det är möjligt att se vilka ansikten och hörn som är aktiva på den aktuella detaljnivån.
Z-buffertprestanda
Program kan öka prestandan när du använder z-buffring och textur genom att se till att scener återges framifrån och tillbaka. Strukturerade z-buffrade primitiver testas i förväg mot z-bufferten på skanningslinjebasis. Om en genomsökningslinje döljs av en tidigare renderad polygon avvisar systemet den snabbt och effektivt. Z-buffring kan förbättra prestanda, men tekniken är mest användbar när en scen ritar samma bildpunkter mer än en gång. Det är svårt att beräkna exakt, men du kan ofta göra en nära uppskattning. Om samma bildpunkter ritas mindre än två gånger kan du uppnå bästa prestanda genom att stänga av z-buffring och återge scenen framifrån.
Relaterade ämnen