Megosztás a következőn keresztül:


Teljesítményoptimalizálás (Direct3D 9)

A 3D-s ábrákat használó valós idejű alkalmazásokat létrehozó fejlesztők mindegyike a teljesítményoptimalizálás miatt aggódik. Ez a szakasz útmutatást nyújt a legjobb teljesítmény eléréséhez a kódból.

Általános teljesítménytippek

  • Csak akkor törölje a jelet, ha kell.
  • Az állapotváltozások minimalizálása és a fennmaradó állapotváltozások csoportosítása.
  • Használjon kisebb textúrákat, ha teheti.
  • Objektumok rajzolása a jelenetben elölről hátra.
  • Használjon háromszögcsíkokat listák és ventilátorok helyett. Az optimális csúcsgyorsítótár-teljesítmény érdekében a sávokat úgy kell elrendezni, hogy a háromszög csúcsait előbb, mint később használhassa fel.
  • Kecsesen csökkenti a különleges hatásokat, amelyek aránytalanul nagy mennyiségű rendszererőforrást igényelnek.
  • Folyamatosan tesztelje az alkalmazás teljesítményét.
  • Kis méretű csúcspufferkapcsolók.
  • Ha lehetséges, használjon statikus csúcspontpuffereket.
  • Használjon egy nagy statikus csúcspontpuffert FVF-enként statikus objektumokhoz, és ne objektumonként egyet.
  • Ha az alkalmazásnak véletlenszerű hozzáférésre van szüksége az AGP-memóriában lévő csúcspufferhez, válasszon egy 32 bájtos többszörös csúcsformátumot. Ellenkező esetben válassza ki a legkisebb megfelelő formátumot.
  • Rajzolás indexelt primitívekkel. Ez hatékonyabb csúcspont-gyorsítótárazást tehet lehetővé a hardveren belül.
  • Ha a mélységi puffer formátuma rajzsablon csatornát tartalmaz, mindig törölje a mélységet és a rajzsablon csatornát egyszerre.
  • Ha lehetséges, kombinálja a shader utasítást és az adatkimenetet. Például:
    // 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 
    

Adatbázisok és selejtezés

A Direct3D kiváló teljesítményéhez kulcsfontosságú, hogy megbízható adatbázist hoz létre a világ objektumaiból. Ez fontosabb, mint a raszterizálás vagy a hardver fejlesztése.

A lehető legkisebb sokszögszámot kell fenntartania. Alacsony sokszögszám tervezése alacsony sokszögű modellek készítésével a kezdetektől. Adjon hozzá sokszöget, ha ezt anélkül teheti meg, hogy a fejlesztési folyamat későbbi részében feláldozná a teljesítményt. Ne feledje, hogy a leggyorsabb sokszögek azok, amelyeket nem rajzol.

Primitívek kötegelése

A végrehajtás során a lehető legjobb renderelési teljesítmény eléréséhez próbáljon meg primitívekkel dolgozni kötegekben, és a renderelési állapot változásainak számát a lehető legalacsonyabban tartani. Ha például egy két textúrájú objektummal rendelkezik, csoportosítsa az első textúrát használó háromszögeket, és kövesse azokat a szükséges renderelési állapottal a textúra módosításához. Ezután csoportosítsa a második textúrát használó háromszögeket. A Direct3D legegyszerűbb hardvertámogatását renderelési állapotok kötegeivel és primitív kötegekkel hívjuk meg a hardver absztrakciós rétegén (HAL) keresztül. Minél hatékonyabban kötegelik az utasításokat, annál kevesebb HAL-hívás történik a végrehajtás során.

Világítási tippek

Mivel a fények csúcsonkénti költséget adnak az egyes renderelt keretekhez, jelentősen javíthatja a teljesítményt, ha körültekintően használja őket az alkalmazásban. A következő tippek többsége a maximából származik: "a leggyorsabb kód a soha el nem hívott kód".

  • Használjon minél több fényforrást. A teljes megvilágítási szint növeléséhez például használja a környezeti fényt új fényforrás hozzáadása helyett.
  • Az irányjelző lámpák hatékonyabbak, mint a pontfények vagy a reflektorfények. Az irányfények esetében a fény iránya rögzített, és nem kell csúcsonkénti alapon kiszámítani.
  • A reflektorfények hatékonyabbak lehetnek, mint a pontfények, mivel a fénycsúcson kívüli terület gyorsan kiszámítható. Attól függ, hogy a reflektorfények hatékonyabbak-e vagy sem, attól függ, hogy a jelenet mekkora részét megvilágítja a reflektorfény.
  • A tartományparaméter használatával a fényeket csak a megvilágítandó jelenet részeire korlátozhatja. Az összes fénytípus meglehetősen korán kilép, amikor kívül vannak a tartományon.
  • A specular highlights majdnem megduplázza a fény költségét. Csak akkor használja őket, ha kell. Ha lehetséges, állítsa a D3DRS_SPECULARENABLE renderelési állapotát 0-ra, az alapértelmezett értékre. Az anyagok meghatározásakor a spekuláris teljesítményértéket nullára kell állítania, hogy kikapcsolja az adott anyag specularis kiemeléseit; a spekuláris szín 0,0,0 értékre állítása nem elég.

Anyagminta mérete

A textúra-leképezés teljesítménye nagymértékben függ a memória sebességétől. Számos módon maximalizálhatja az alkalmazás textúráinak gyorsítótár-teljesítményét.

  • Tartsa kicsi a textúrákat. Minél kisebbek a textúrák, annál nagyobb az esély arra, hogy megmaradnak a fő CPU másodlagos gyorsítótárában.
  • Ne módosítsa a textúrákat primitív alapon. Próbálja meg csoportosítani a sokszögeket az általuk használt textúrák szerint.
  • Ha lehetséges, használjon négyzet alakú textúrákat. A leggyorsabbak azok a textúrák, amelyek mérete 256x256. Ha az alkalmazás például négy 128x128-at használ, próbálja meg biztosítani, hogy ugyanazt a palettát használják, és helyezze őket egy 256x256-os textúrába. Ez a technika csökkenti a textúra felcserélésének mennyiségét is. Természetesen nem szabad 256x256 textúrákat használni, kivéve, ha az alkalmazás megköveteli, hogy sok textúra, mert, mint említettük, textúrák kell tartani a lehető legkisebb.

Mátrix-átalakítások

A Direct3D a világot használja, és több belső adatstruktúra konfigurálásához beállított mátrixokat tekint meg. Minden alkalommal, amikor beállít egy új világot vagy egy mátrixot, a rendszer újraszámítja a kapcsolódó belső struktúrákat. Ezeknek az mátrixoknak a gyakran – például keretenként több ezer alkalommal – történő beállítása számításilag időigényes. Minimalizálhatja a szükséges számítások számát, ha összefűzi a világot, és a mátrixokat egy világnézeti mátrixba alakítja, amelyet világmátrixként állít be, majd a nézetmátrixot az identitásra állítja. Tartsa meg az egyes világ gyorsítótárazott másolatait, és tekintse meg a mátrixokat, hogy szükség szerint módosíthassa, összefűzhesse és visszaállíthassa a világmátrixot. A dokumentáció egyértelműsége érdekében a Direct3D-minták ritkán alkalmazzák ezt az optimalizálást.

Dinamikus textúrák használata

Ha szeretné megtudni, hogy az illesztőprogram támogatja-e a dinamikus textúrákat, ellenőrizze a D3DCAPS9 szerkezet D3DCAPS2_DYNAMICTEXTURES jelzőjét.

A dinamikus textúrák használatakor tartsa szem előtt az alábbi szempontokat.

  • Nem kezelhetők. A készletük például nem D3DPOOL_MANAGED.
  • A dinamikus textúrák zárolhatók, még akkor is, ha D3DPOOL_DEFAULT vannak létrehozva.
  • D3DLOCK_DISCARD a dinamikus textúrák érvényes zárolási jelzője.

Érdemes formátumonként és méretenként csak egy dinamikus textúrát létrehozni. A dinamikus mipmapek, kockák és kötetek nem ajánlottak, mivel a minden szint zárolása további többletterhelést okoz. Mipmaps esetén D3DLOCK_DISCARD csak a legfelső szinten engedélyezett. A rendszer minden szintet elvet, ha csak a legfelső szintet zárolja. Ez a viselkedés a kötetek és kockák esetében is ugyanaz. Kockák esetén a felső szint és a 0 arc zárolva van.

Az alábbi pszeudokód egy dinamikus textúra használatát szemlélteti.

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

Dinamikus csúcspontok és indexpufferek használata

A statikus csúcspontpuffer zárolása, miközben a grafikus processzor a puffert használja, jelentős teljesítménybeli büntetést vonhat maga után. A zárolási hívásnak meg kell várnia, amíg a grafikus processzor befejezi a csúcspontok vagy indexadatok olvasását a pufferből, mielőtt visszatérhet a hívó alkalmazáshoz, ami jelentős késést jelent. A képkockánként többszöri statikus puffer zárolása és renderelése szintén megakadályozza, hogy a grafikus processzor pufferelje a renderelési parancsokat, mivel a zárolási mutató visszaadása előtt be kell fejeznie a parancsokat. Pufferelt parancsok nélkül a grafikus processzor tétlen marad, amíg az alkalmazás be nem tölti a csúcspuffert vagy az indexpuffert, és kiad egy renderelési parancsot.

Ideális esetben a csúcs- vagy indexadatok soha nem változnának, de ez nem mindig lehetséges. Sok olyan helyzet van, amikor az alkalmazásnak minden keretben módosítania kell a csúcspont- vagy indexadatokat, akár keretenként akár többször is. Ilyen helyzetekben a csúcs- vagy indexpuffert D3DUSAGE_DYNAMIC kell létrehozni. Ez a használati jelző miatt a Direct3D a gyakori zárolási műveletekre optimalizál. D3DUSAGE_DYNAMIC csak akkor hasznos, ha a puffert gyakran zárolják; az állandó maradó adatokat statikus csúcspontba vagy indexpufferbe kell helyezni.

Ha dinamikus csúcspontpufferek használatakor teljesítménybeli javulást szeretne kapni, az alkalmazásnak meg kell hívnia IDirect3DVertexBuffer9::Lock vagy IDirect3DIndexBuffer9::Lock a megfelelő jelzőkkel. D3DLOCK_DISCARD azt jelzi, hogy az alkalmazásnak nem kell megőriznie a régi csúcs- vagy indexadatokat a pufferben. Ha a grafikus processzor továbbra is használja a puffert, amikor a zárolást D3DLOCK_DISCARD hívja meg, a rendszer a régi pufferadatok helyett egy új memóriaterületre mutató mutatót ad vissza. Ez lehetővé teszi, hogy a grafikus processzor továbbra is a régi adatokat használja, miközben az alkalmazás adatokat helyez el az új pufferben. Nincs szükség további memóriakezelésre az alkalmazásban; a régi puffert a rendszer automatikusan újra felhasználja vagy megsemmisíti, amikor a grafikus processzor befejeződött. Vegye figyelembe, hogy a puffer D3DLOCK_DISCARD való zárolása mindig elveti a teljes puffert, ha nem tér eltolást vagy korlátozott méretű mezőt ad meg, az nem őrzi meg a puffer feloldott területein található információkat.

Vannak olyan esetek, amikor az alkalmazás által a zárolásonként tárolandó adatok mennyisége kicsi, például négy csúcspontot ad hozzá a sprite megjelenítéséhez. D3DLOCK_NOOVERWRITE azt jelzi, hogy az alkalmazás nem írja felül a dinamikus pufferben már használt adatokat. A zárolási hívás egy mutatót ad vissza a régi adatokhoz, így az alkalmazás új adatokat vehet fel a csúcspont vagy indexpuffer nem használt régióiba. Az alkalmazás nem módosíthatja a rajzműveletben használt csúcspontokat vagy indexeket, mivel a grafikus processzor továbbra is használhatja őket. Az alkalmazásnak ezután D3DLOCK_DISCARD kell használnia, miután a dinamikus puffer megtelt egy új memóriaterület fogadásához, és a grafikus processzor befejezése után elveti a régi csúcs- vagy indexadatokat.

Az aszinkron lekérdezési mechanizmus hasznos annak megállapításához, hogy a csúcsok továbbra is használatban vannak-e a grafikus processzorban. Adjon ki egy D3DQUERYTYPE_EVENT típusú lekérdezést a csúcspontokat használó legutóbbi DrawPrimitive hívás után. A csúcspontok már nem használhatók, ha IDirect3DQuery9::GetData S_OK ad vissza. Ha a puffert D3DLOCK_DISCARD vagy jelző nélkül zárolja, az mindig garantálja, hogy a csúcsok megfelelően szinkronizálódnak a grafikus processzorral, de a jelölők nélküli zárolás a korábban ismertetett teljesítménybeli büntetést fogja érinteni. Más API-hívások, például IDirect3DDevice9::BeginScene, IDirect3DDevice9::EndSceneés IDirect3DDevice9::P resent nem garantálják, hogy a grafikus processzor csúcspontok használatával befejeződött.

Az alábbiakban bemutatjuk a dinamikus pufferek és a megfelelő zárolási jelzők használatát.

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

Hálók használata

A hálókat indexelt háromszögek helyett Direct3D indexelt háromszögekkel optimalizálhatja. A hardver rájön, hogy az egymást követő háromszögek 95%-a valójában csíkokat alkot, és ennek megfelelően igazítja. Sok illesztőprogram ezt a régebbi hardverek esetében is megteszi.

A D3DX hálóobjektumok minden háromszöget vagy arcot egy DWORD címkével, az arc attribútumának neveznek. A DWORD szemantikája felhasználó által definiált. Ezeket a D3DX használja a háló alhalmazokba való besorolásához. Az alkalmazás arconkénti attribútumokat állít be az ID3DXMesh::LockAttributeBuffer hívással. Az ID3DXMesh::Optimize metódussal a D3DXMESHOPT_ATTRSORT beállítással csoportosíthatja a háló csúcspontjait és arcait attribútumokra. Ha ez megtörtént, a mesh objektum egy attribútumtáblát számít ki, amelyet az alkalmazás ID3DXBaseMesh::GetAttributeTablemeghívásával szerezhet be. Ez a hívás 0 értéket ad vissza, ha a háló nem attribútumok szerint van rendezve. Az alkalmazás nem állíthat be attribútumtáblát, mert az ID3DXMesh::Optimize metódus hozza létre. Az attribútumrendezés adatérzékeny, ezért ha az alkalmazás tudja, hogy egy háló van rendezve, akkor is meg kell hívnia ID3DXMesh::Optimize az attribútumtábla létrehozásához.

Az alábbi témakörök a háló különböző attribútumait ismertetik.

Attribútumazonosító

Az attribútumazonosító egy olyan érték, amely arccsoportot társít egy attribútumcsoporthoz. Ez az azonosító az arcok ID3DXBaseMesh::D rawSubset rajzolását ismerteti. Az attribútumpufferben lévő arcokhoz attribútumazonosítók vannak megadva. Az attribútumazonosítók tényleges értékei 32 bitesek lehetnek, de gyakori, hogy 0–n értéket használnak, ahol n az attribútumok száma.

Attribútumpuffer

Az attribútumpuffer a DWORD-k tömbje (arconként egy), amely meghatározza, hogy az egyes arcok melyik attribútumcsoportba tartoznak. Ez a puffer egy háló létrehozásakor nullára van inicializálva, de vagy a terhelési rutinok töltik ki, vagy a felhasználónak kell kitöltenie, ha több, 0 azonosítójú attribútumra van szükség. Ez a puffer azokat az információkat tartalmazza, amelyek a háló rendezésére szolgálnak ID3DXMesh::Optimizeattribútumai alapján. Ha nincs attribútumtábla, ID3DXBaseMesh::D rawSubset megvizsgálja ezt a puffert, hogy kijelölje a rajzolni kívánt attribútum arcait.

Attribútumtábla

Az attribútumtábla a háló tulajdonában és fenntartásában lévő struktúra. Az egyiket csak úgy lehet létrehozni, ha meghívja ID3DXMesh::Optimize attribútum rendezéssel vagy erősebb optimalizálással engedélyezve. Az attribútumtáblával gyorsan kezdeményezhet egyszerű rajzhívást ID3DXBaseMesh::D rawSubset. Az egyetlen másik használat az, hogy a fejlődésben lévő hálók is fenntartják ezt a struktúrát, így látható, hogy milyen arcok és csúcsok aktívak a jelenlegi részletességi szinten.

Z-puffer teljesítménye

Az alkalmazások a z-pufferelés és a szövegezés használatakor növelhetik a teljesítményt, ha biztosítják, hogy a jelenetek elölről hátulra legyenek renderelve. A strukturált z-pufferelt primitíveket a rendszer előre teszteli a z-pufferrel egy vizsgálati vonal alapján. Ha egy korábban renderelt sokszög elrejti a vizsgálati vonalat, a rendszer gyorsan és hatékonyan elutasítja azt. A Z-pufferelés javíthatja a teljesítményt, de a technika akkor hasznos, ha egy jelenet többször rajzolja meg ugyanazt a képpontot. Ezt nehéz pontosan kiszámítani, de gyakran lehet közelíteni. Ha ugyanazt a képpontot kevesebb mint kétszer rajzolja meg, a legjobb teljesítményt úgy érheti el, hogy kikapcsolja a z-pufferelést, és hátulról elölről rendereli a jelenetet.

programozási tippek