Aracılığıyla paylaş


Performans İyileştirmeleri (Direct3D 9)

3B grafikler kullanan gerçek zamanlı uygulamalar oluşturan her geliştirici performans iyileştirmesinden endişe duyar. Bu bölümde, kodunuzdan en iyi performansı almaya yönelik yönergeler sağlanır.

Genel Performans İpuçları

  • Yalnızca gerektiğinde temizleyin.
  • Durum değişikliklerini en aza indirin ve kalan durum değişikliklerini gruplandırın.
  • Bunu yapabilirseniz daha küçük dokular kullanın.
  • Sahnenizdeki nesneleri önden arkaya çizin.
  • Listeler ve fanlar yerine üçgen şeritler kullanın. En iyi köşe önbelleği performansı için şeritleri üçgen köşeleri daha sonra değil daha erken yeniden kullanacak şekilde düzenleyin.
  • Sistem kaynaklarının orantısız bir paylaşımını gerektiren özel etkileri düzgün bir şekilde düşürün.
  • Uygulamanızın performansını sürekli test edin.
  • Köşe arabelleği anahtarlarını simge durumuna küçültün.
  • Mümkün olduğunda statik köşe arabellekleri kullanın.
  • Statik nesneler için her nesne için bir değil, FVF başına bir büyük statik köşe arabelleği kullanın.
  • Uygulamanızın AGP belleğindeki köşe arabelleğine rastgele erişmesi gerekiyorsa, 32 bayt'ın katı olan bir köşe biçimi boyutu seçin. Aksi takdirde en küçük uygun biçimi seçin.
  • Dizine alınan ilkelleri kullanarak çizim. Bu, donanım içinde daha verimli köşe önbelleğe alma olanağı sağlayabilir.
  • Derinlik arabelleği biçimi bir kalıp kanalı içeriyorsa, derinlik ve kalıp kanallarını her zaman aynı anda temizleyin.
  • Mümkün olduğunda gölgelendirici yönergesini ve veri çıkışını birleştirin. Mesela:
    // 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 
    

Veritabanları ve Culling

Dünyanızdaki nesnelerin güvenilir bir veritabanını oluşturmak, Direct3D'de mükemmel performansın anahtarıdır. Tarama veya donanım geliştirmelerinden daha önemlidir.

Yönetebileceğiniz en düşük çokgen sayısını korumanız gerekir. Başlangıçtan itibaren düşük poligonlu modeller oluşturarak düşük çokgen sayısı için tasarım. Geliştirme sürecinin ilerleyen bölümlerinde performansdan ödün vermeden bunu yapabilirseniz çokgenler ekleyin. Unutmayın, en hızlı poligonlar çizmediklerinizdir.

Temel Öğe Toplu İşlemleri

Yürütme sırasında en iyi işleme performansını elde etmek için toplu olarak temel öğelerle çalışmayı ve işleme durumu değişikliklerini mümkün olduğunca düşük tutmayı deneyin. Örneğin, iki dokulu bir nesneniz varsa, ilk dokuyu kullanan üçgenleri gruplandırın ve dokuyu değiştirmek için gerekli işleme durumuyla bunları izleyin. Ardından ikinci dokuyu kullanan tüm üçgenleri gruplandırın. Direct3D için en basit donanım desteği, donanım soyutlama katmanı (HAL) aracılığıyla toplu işleme durumları ve temel öğelerle çağrılır. Yönergeler ne kadar etkili bir şekilde toplu işlenirse yürütme sırasında o kadar az HAL çağrısı gerçekleştirilir.

Aydınlatma İpuçları

Işıklar işlenen her çerçeveye köşe başına maliyet eklediğinden, bunları uygulamanızda nasıl kullandığınıza dikkat ederek performansı önemli ölçüde geliştirebilirsiniz. Aşağıdaki ipuçlarının çoğu, "en hızlı kod, hiçbir zaman çağrılmamış koddur" üst düzeyden türetilir.

  • Mümkün olduğunca az ışık kaynağı kullanın. Örneğin genel aydınlatma düzeyini artırmak için yeni bir ışık kaynağı eklemek yerine ortam ışığını kullanın.
  • Yön lambaları, nokta ışıklarından veya spot ışıklarından daha verimlidir. Yön lambaları için ışık yönü sabittir ve köşe başına hesaplanması gerekmez.
  • Işık konisi dışındaki alan hızlı bir şekilde hesaplandığından spot ışıklardan daha verimli olabilir. Spotların daha verimli olup olmadığı, sahnenizin ne kadarının öne çıkanlar tarafından aydınlatıldığına bağlıdır.
  • Işıklarınızı yalnızca sahnenin aydınlatmanız gereken bölümleriyle sınırlandırmak için range parametresini kullanın. Tüm ışık türleri, menzil dışındayken oldukça erken çıkılır.
  • Specular highlights neredeyse bir ışığın maliyetinin iki katıdır. Bunları yalnızca gerektiğinde kullanın. mümkün olduğunda D3DRS_SPECULARENABLE işleme durumunu varsayılan değer olan 0 olarak ayarlayın. Malzemeleri tanımlarken, bu malzemenin belirtik vurgularını kapatmak için belirtik güç değerini sıfır olarak ayarlamanız gerekir; yalnızca belirtik rengi 0,0,0 olarak ayarlamak yeterli değildir.

Doku Boyutu

Doku eşleme performansı, belleğin hızına büyük ölçüde bağlıdır. Uygulamanızın dokularının önbellek performansını en üst düzeye çıkarmanın çeşitli yolları vardır.

  • Dokuları küçük tutun. Dokular ne kadar küçük olursa, ana CPU'nun ikincil önbelleğinde korunma şansları o kadar yüksektir.
  • Dokuları ilkel temelinde değiştirmeyin. Çokgenleri kullandıkları dokular sırasına göre gruplandırmaya çalışın.
  • Mümkün olduğunda kare dokuları kullanın. Boyutları 256x256 olan dokular en hızlıdır. Uygulamanız dört adet 128x128 doku kullanıyorsa, bunların aynı paleti kullandığından emin olun ve bunların tümünü tek bir 256x256 dokuya yerleştirin. Bu teknik doku değiştirme miktarını da azaltır. Tabii ki, uygulamanız bu kadar çok doku gerektirmediği sürece 256x256 dokuları kullanmamalısınız çünkü belirtildiği gibi dokular mümkün olduğunca küçük tutulmalıdır.

Matris Dönüşümleri

Direct3D dünyayı kullanır ve çeşitli iç veri yapılarını yapılandırmak için ayarladığınız matrisleri görüntüler. Her yeni dünya veya görünüm matrisi ayarladığınızda, sistem ilişkili iç yapıları yeniden hesaplar. Bu matrislerin sık sık ayarlanması (örneğin, çerçeve başına binlerce kez) hesaplama açısından zaman alır. Dünyanızı birleştirerek ve matrisleri dünya matrisi olarak ayarladığınız bir dünya görünümü matrisine dönüştürerek ve ardından görünüm matrisini kimliğe ayarlayarak gerekli hesaplamaların sayısını en aza indirebilirsiniz. Gerektiğinde dünya matrisini değiştirebilmeniz, birleştirebilmeniz ve sıfırlayabileceğiniz tek tek dünyanın önbelleğe alınmış kopyalarını tutun ve matrisleri görüntüleyin. Bu belgelerde netlik sağlamak için Direct3D örnekleri bu iyileştirmeyi nadiren uygular.

Dinamik Dokuları Kullanma

Sürücünün dinamik dokuları desteklediğini öğrenmek için D3DCAPS9 yapısının D3DCAPS2_DYNAMICTEXTURES bayrağını denetleyin.

Dinamik dokularla çalışırken aşağıdaki noktaları göz önünde bulundurun.

  • Bunlar yönetilemez. Örneğin, havuzları D3DPOOL_MANAGED olamaz.
  • Dinamik dokular, D3DPOOL_DEFAULT oluşturulmuş olsalar bile kilitlenebilir.
  • D3DLOCK_DISCARD, dinamik dokular için geçerli bir kilit bayrağıdır.

Biçim başına ve büyük olasılıkla boyut başına yalnızca bir dinamik doku oluşturmak iyi bir fikirdir. Her düzeyi kilitleme ek yükü nedeniyle dinamik mipmap'ler, küpler ve birimler önerilmez. Mipmap'ler için D3DLOCK_DISCARD yalnızca en üst düzeyde izin verilir. Tüm düzeyler yalnızca en üst düzey kilitlenerek atılır. Bu davranış birimler ve küpler için aynıdır. Küpler için üst düzey ve yüz 0 kilitlenir.

Aşağıdaki sahte kodda dinamik doku kullanma örneği gösterilmektedir.

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

Dinamik Köşe ve Dizin Arabelleklerini Kullanma

Grafik işlemcisi arabelleği kullanırken statik bir köşe arabelleği kilitlemenin önemli bir performans cezası olabilir. Kilit çağrısı, grafik işlemcisinin çağrı uygulamasına geri dönebilmesi için arabellekten köşe veya dizin verilerini okumayı bitirene kadar beklemesi gerekir. Bu önemli bir gecikmedir. Kilit işaretçisini döndürmeden önce komutları bitirmesi gerektiğinden, çerçeve başına birkaç kez statik arabellekten kilitleme ve işleme, grafik işlemcinin işleme komutlarını arabelleğe almasına da engel olur. Arabelleğe alınan komutlar olmadan, uygulama köşe arabelleği veya dizin arabelleğinin doldurulması bitene kadar grafik işlemci boşta kalır ve bir işleme komutu döndürür.

İdeal olarak köşe veya dizin verileri hiçbir zaman değişmez, ancak bu her zaman mümkün değildir. Uygulamanın her karede köşeyi değiştirmesi veya verileri dizine alması gereken birçok durum vardır, hatta çerçeve başına birden çok kez bile olabilir. Bu durumlarda köşe veya dizin arabelleği D3DUSAGE_DYNAMIC ile oluşturulmalıdır. Bu kullanım bayrağı Direct3D'nin sık sık kilit işlemleri için iyileştirmesine neden olur. D3DUSAGE_DYNAMIC yalnızca arabellek sık sık kilitlendiğinde yararlıdır; sabit kalan veriler statik bir köşeye veya dizin arabelleğine yerleştirilmelidir.

Dinamik köşe arabelleklerini kullanırken performans geliştirmesi almak için uygulamanın IDirect3DVertexBuffer9::Lock veya IDirect3DIndexBuffer9::Lock uygun bayraklarla çağırması gerekir. D3DLOCK_DISCARD, uygulamanın eski köşeyi veya dizin verilerini arabellekte tutması gerekmediğini gösterir. D3DLOCK_DISCARD ile kilit çağrıldığında grafik işlemci hala arabelleği kullanıyorsa, eski arabellek verileri yerine yeni bir bellek bölgesine yönelik bir işaretçi döndürülür. Bu, uygulama verileri yeni arabelleğe yerleştirirken grafik işlemcisinin eski verileri kullanmaya devam etmesini sağlar. Uygulamada ek bellek yönetimi gerekmez; grafik işlemcisi tamamlandığında eski arabellek otomatik olarak yeniden kullanılır veya yok edilir. D3DLOCK_DISCARD ile bir arabelleği kilitlemenin, sıfır olmayan uzaklık veya sınırlı boyut alanı belirterek arabelleğinde her zaman tüm arabelleği attığını, arabelleğinde kilidi açılmış alanlardaki bilgileri korumadığını unutmayın.

Bir sprite işlemek için dört köşe eklemek gibi kilit başına uygulamanın depolaması gereken veri miktarının küçük olduğu durumlar vardır. D3DLOCK_NOOVERWRITE, uygulamanın dinamik arabellekte zaten kullanımda olan verilerin üzerine yazılmadığını gösterir. Kilit çağrısı, uygulamanın köşenin veya dizin arabelleğinin kullanılmayan bölgelerine yeni veri eklemesine olanak sağlayan eski verilere yönelik bir işaretçi döndürür. Uygulama, çizim işleminde kullanılan köşeleri veya dizinleri grafik işlemcisi tarafından kullanılmaya devam ediyor olabileceğinden değiştirmemelidir. Daha sonra, grafik işlemcisi tamamlandıktan sonra eski köşeyi veya dizin verilerini atarak yeni bir bellek bölgesi almak için dinamik arabellek dolduktan sonra uygulama D3DLOCK_DISCARD kullanmalıdır.

Zaman uyumsuz sorgu mekanizması, köşelerin grafik işlemcisi tarafından hala kullanımda olup olmadığını belirlemek için kullanışlıdır. Köşeleri kullanan son DrawPrimitive çağrısından sonra D3DQUERYTYPE_EVENT türünde bir sorgu oluşturun. IDirect3DQuery9::GetDataS_OK döndürdüğünde köşeler artık kullanımda değildir. D3DLOCK_DISCARD veya hiç bayrak olmadan bir arabelleği kilitlemek her zaman köşelerin grafik işlemcisiyle düzgün eşitlenmesini garanti eder, ancak bayraksız kilit kullanmak daha önce açıklanan performans cezasına neden olur. IDirect3DDevice9::BeginScene, IDirect3DDevice9::EndScenegibi diğer API çağrıları ve IDirect3DDevice9::P resent grafik işlemcinin köşeleri kullanmayı tamamladığından emin olmaz.

Aşağıda dinamik arabellekleri ve uygun kilit bayraklarını kullanmanın yolları yer almaktadır.

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

Tire Kullanma

Dizinlenmiş üçgen şeritler yerine Direct3D dizinlenmiş üçgenleri kullanarak çizgileri iyileştirebilirsiniz. Donanım, ardışık üçgenlerin yüzde 95'inin aslında şeritler oluşturduğunu ve buna göre ayarlandığını keşfedecektir. Birçok sürücü bunu eski donanımlar için de yapar.

D3DX mesh nesneleri, bu yüzün özniteliği olarak adlandırılan bir DWORD ile etiketlenmiş her üçgene veya yüze sahip olabilir. DWORD semantiği kullanıcı tanımlıdır. Ağı alt kümeler halinde sınıflandırmak için D3DX tarafından kullanılır. Uygulama ID3DXMesh::LockAttributeBuffer çağrısını kullanarak yüz başına öznitelikleri ayarlar. ID3DXMesh::Optimize yöntemi, D3DXMESHOPT_ATTRSORT seçeneğini kullanarak özniteliklerdeki mesh köşelerini ve yüzlerini gruplandırma seçeneğine sahiptir. Bu yapıldığında, mesh nesnesi ID3DXBaseMesh::GetAttributeTableçağrılarak uygulama tarafından elde edilebilecek bir öznitelik tablosu hesaplar. Bu çağrı, ağ özniteliklere göre sıralanmamışsa 0 döndürür. ID3DXMesh::Optimize yöntemi tarafından oluşturulduğundan, bir uygulamanın öznitelik tablosu ayarlamasının hiçbir yolu yoktur. Öznitelik sıralama veriye duyarlıdır, bu nedenle uygulama bir ağın öznitelik sıralandığını biliyorsa, öznitelik tablosunu oluşturmak için id3DXMesh::Optimize çağırması gerekir.

Aşağıdaki konular bir ağın farklı özniteliklerini açıklar.

Öznitelik Kimliği

Öznitelik kimliği, bir grup yüzü bir öznitelik grubuyla ilişkilendiren bir değerdir. Bu kimlik, ID3DXBaseMesh::D rawSubsetyüzlerin hangi alt kümesini çizmesi gerektiğini açıklar. Öznitelik arabelleğindeki yüzler için öznitelik kimlikleri belirtilir. Öznitelik kimliklerinin gerçek değerleri 32 bit'e uyan herhangi bir şey olabilir, ancak 0 ile n arasında bir değer kullanılır; burada n öznitelik sayısıdır.

Öznitelik AraBelleği

Öznitelik arabelleği, her yüzün hangi öznitelik grubuna ait olduğunu belirten bir DWORD dizisidir (yüz başına bir tane). Bu arabellek, ağ oluşturulurken sıfır olarak başlatılır, ancak yük yordamları tarafından doldurulur veya kimlik 0'a sahip birden fazla öznitelik isteniyorsa kullanıcı tarafından doldurulması gerekir. Bu arabellek, ID3DXMesh::Optimizeiçindeki özniteliklere göre ağı sıralamak için kullanılan bilgileri içerir. Öznitelik tablosu yoksa ID3DXBaseMesh::D rawSubset, çizecek özniteliğin yüzlerini seçmek için bu arabelleği tarar.

Öznitelik Tablosu

Öznitelik tablosu, ağ tarafından sahip olunan ve bakımı yapılan bir yapıdır. Birinin oluşturulmasının tek yolu, ID3DXMesh:: öznitelik sıralama veya daha güçlü iyileştirme etkin olarak en iyi duruma getirme çağrısı yapmaktır. Öznitelik tablosu, ID3DXBaseMesh::D rawSubsetiçin hızlı bir şekilde tek bir çizim ilkel çağrısı başlatmak için kullanılır. Diğer tek kullanım, ilerleyen kafeslerin de bu yapıyı korumasıdır, bu nedenle geçerli ayrıntı düzeyinde hangi yüzlerin ve köşelerin etkin olduğunu görmek mümkündür.

Z Arabellek Performansı

Uygulamalar, sahnelerin önden arkaya işlenmesini sağlayarak z arabelleği ve doku oluşturma kullanılırken performansı artırabilir. Dokulu z arabelleğe alınmış temel öğeler, tarama çizgisi temelinde z-buffer'a karşı önceden test edilir. Tarama çizgisi önceden işlenmiş bir çokgen tarafından gizlenirse, sistem bunu hızlı ve verimli bir şekilde reddeder. Z arabelleğe alma performansı geliştirebilir, ancak teknik en çok bir sahne aynı pikselleri birden çok kez çizdiğinde kullanışlıdır. Bunu tam olarak hesaplamak zordur, ancak genellikle yakın bir tahminde bulunabilirsiniz. Aynı pikseller ikiden az çizilmişse, z arabelleği kapatmayı kapatıp sahneyi arkadan öne doğru işleyerek en iyi performansı elde edebilirsiniz.

Programlama İpuçları