Mengelola keadaan grafis dan komponen?

11

Saya sering cenderung melakukan banyak optimalisasi prematur ketika berhadapan dengan grafik. Ada beberapa prinsip yang selalu saya coba ikuti:

  • Jaga agar jumlah komponen D3D minimum. (Status render, buffer, shader, dll.)
  • Hanya mengikat komponen jika benar-benar diperlukan. (Belum terikat, dll.)
  • Spesialisasi komponen sebanyak mungkin. (Hanya mengatur BindFlag yang diperlukan, dll.)

Ini membuat saya membangun pembungkus yang sangat rumit untuk mengelola komponen yang dibuat dan kondisi perpipaan saat ini. Ini tidak hanya menghabiskan banyak waktu pengembangan saya yang berharga, tetapi juga menambah lapisan kompleksitas yang besar.

Dan yang terburuk: saya bahkan tidak tahu apakah itu sepadan dengan masalahnya.

Beberapa pertimbangan optimasi saya mungkin sudah diimplementasikan pada level yang lebih rendah dan saya hanya mereplikasi mereka, juga membuang-buang waktu pada CPU. Pertimbangan lain mungkin sama sekali tidak perlu, karena efek pada kinerja dapat diabaikan.

Jadi pertanyaan saya adalah:

  1. Manakah dari pedoman di atas yang valid dan sejauh mana saya harus mengikutinya?
  2. Bagaimana cara GPU menangani perubahan?
  3. Apa yang terjadi jika saya mengubah keadaan yang tidak pernah digunakan? (Tidak ada panggilan draw yang dilakukan saat sedang aktif.)
  4. Apa hukuman kinerja aktual untuk mengikat berbagai komponen yang berbeda?
  5. Pertimbangan kinerja apa yang harus dibuat?

Tolong jangan hanya memberi tahu saya, bahwa saya seharusnya tidak peduli dengan kinerja sampai saya mencapai batas yang sebenarnya. Sementara itu jelas benar dari sudut pandang praktis, saya terutama tertarik pada teori. Entah bagaimana saya perlu melawan keinginan membangun kerangka kerja grafis yang optimal dan saya tidak berpikir saya bisa melakukannya dengan "kuliah optimasi prematur" yang biasa.

Mengelola komponen

Saat ini saya sedang menulis aplikasi DirectX 11 dalam C # menggunakan SlimDX sebagai pembungkus yang dikelola. Ini adalah pembungkus tingkat yang sangat rendah dan abstraksi saya saat ini dibangun di atasnya.

Ada beberapa keuntungan yang jelas ketika menggunakan abstraksi Direct3D. Menyiapkan lingkungan, memuat shader, menyetel konstanta, dan menggambar mesh jauh lebih sederhana dan menggunakan kode yang jauh lebih sedikit. Juga, karena mengelola pembuatan dan pembuangan sebagian besar komponen, mereka dapat secara otomatis digunakan kembali di mana - mana dan saya hampir sepenuhnya menghindari kebocoran memori.

  1. Bagaimana Anda biasanya mengelola semua komponen grafis dan sumber daya?
  2. Dapatkah Anda merekomendasikan pembungkus terkelola melakukan sesuatu yang mirip dengan contoh saya di bawah ini?

Ini adalah contoh implementasi saya saat ini. Saya cukup senang dengan antarmuka. Ini memiliki fleksibilitas yang cukup untuk kebutuhan saya dan sangat mudah digunakan dan dipahami:

// Init D3D environment
var window = new RenderForm();
var d3d = new Direct3D(window, GraphicsSettings.Default);
var graphics = new GraphicsManager(d3d.Device);

// Load assets
var mesh = GeometryPackage.FromFile(d3d, "teapot.gp");
var texture = Texture.FromFile(d3d, "bricks.dds");

// Render states
graphics.SetViewports(new Viewport(0, 0, 800, 600);
graphics.SetRasterizer(wireFrame: false, culling: CullMode.Back);
graphics.SetDepthState(depthEnabled: true, depthWriteEnabled: true);
graphics.SetBlendState(BlendMethod.Transparency);

// Input layout
graphics.SetLayout("effect.fx", "VS", "vs_4_0",
    new InputElement("POSITION", 0, Format.R32G32B32_Float, 0),
    new InputElement("TEXCOORD", 0, Format.R32G32_Float, 0)
);

// Vertex shader
graphics.SetShader(Shader.Vertex, "effect.fx", "VS", "vs_4_0");
graphics.SetConstants(Shader.Vertex, 0, 4, stream => stream.Write(wvpMatrix));

// Pixel shader
graphics.SetShader(Shader.Pixel, "effect.fx", "PS", "ps_4_0");
graphics.SetTexture(Shader.Pixel, 0, texture);
graphics.SetSampler(Shader.Pixel, 0, Sampler.AnisotropicWrap);
graphics.SetConstants(Shader.Pixel, 0, 1, stream => stream.Write(new Color4(1, 0, 1, 0);

d3d.Run(() =>
{
    // Draw and present
    d3d.BackBuffer.Clear(new Color4(1, 0, 0.5f, 1));
    graphics.SetOutput(d3d.BackBuffer);
    graphics.Draw(mesh);
    d3d.Present();
}
Lucius
sumber
8
Untuk pertanyaan seperti ini saya tidak akan memberikan kuliah "optimasi prematur", saya akan memberi Anda "perubahan profil sehingga Anda dapat melihat sendiri" ceramah.
Tetrad
@Tetrad Saya hampir malu untuk mengakui bahwa ini adalah beberapa saran yang lumayan. Saya pasti harus melakukan lebih banyak profil.
Lucius
1
Nomor profil adalah versi gamedev dari "foto atau itu tidak terjadi" pasti =)
Patrick Hughes

Jawaban:

3

Saya suka pendekatan abstraksi yang diuraikan oleh Hodgman di utas-utas ini di gamedev.net:

Dia menjelaskan sistem rendering tiga tingkat:

  1. API rendering tingkat rendah yang menerima "perintah", mengabstraksi tidak lebih dari perbedaan di berbagai grafik API, seperti Direct3D 9, Direct3D 11, dan OpenGL. Setiap "perintah" memetakan ke keadaan atau draw-call yang berbeda, seperti mengikat aliran simpul atau tekstur, atau menggambar primitif.
  2. API yang menerima "render items", yang mengelompokkan semua negara bagian dan satu draw-call yang diperlukan untuk merender objek tertentu, dan mengurutkan dan menerjemahkannya ke dalam perintah yang dikirim ke level pertama. Status render berisi panggilan-draw dan tumpukan "grup negara", yang secara logis mengelompokkan perubahan-negara. Misalnya, Anda akan memiliki grup negara untuk render pass, grup negara untuk materi, grup negara untuk geometri, grup negara untuk instance, dan sebagainya. Level ini bertanggung jawab untuk menyortir item render ini untuk mengurangi perubahan status redundan dan memusnahkan perubahan status apa pun yang, pada kenyataannya, redundan.
  3. Sistem tingkat tinggi seperti grafik adegan atau render GUI yang mengirimkan item render ke tingkat kedua. Yang penting untuk diperhatikan adalah bahwa sistem ini tidak tahu tentang algoritma penyortiran status atau API rendering tertentu, menjadikannya sepenuhnya platform-agnostik. Mereka juga mudah digunakan setelah API tingkat rendah diimplementasikan.

Kesimpulannya, model ini menyelesaikan kedua masalah Anda sekaligus.

jmegaffin
sumber