การจัดการสถานะกราฟิกและส่วนประกอบ?


11

ฉันมักจะทำ optimazation ก่อนกำหนดจำนวนมากเมื่อต้องรับมือกับกราฟิก มีหลักการบางอย่างที่ฉันพยายามทำตามอยู่เสมอ:

  • รักษาจำนวนองค์ประกอบ D3D ให้น้อยที่สุด (สถานะการแสดงผลบัฟเฟอร์ตัวติดตั้ง ฯลฯ )
  • ผูกส่วนประกอบถ้าจำเป็นจริงๆ (ไม่ได้ผูกไว้แล้ว ฯลฯ )
  • เชี่ยวชาญส่วนประกอบให้มากที่สุด (ตั้งค่า BindFlags ที่จำเป็นเท่านั้นเป็นต้น)

สิ่งนี้ทำให้ฉันสร้างสิ่งห่อหุ้มที่ซับซ้อนมากสำหรับการจัดการส่วนประกอบที่สร้างขึ้นและสถานะของการวางท่อในปัจจุบัน สิ่งนี้ไม่เพียงใช้เวลาในการพัฒนาที่มีค่าของฉันเท่านั้น แต่ยังเพิ่มความซับซ้อนอีกชั้นหนึ่ง

และที่เลวร้ายที่สุดของทั้งหมด: ฉันไม่รู้ด้วยซ้ำว่ามันคุ้มค่ากับปัญหาหรือไม่

ข้อควรพิจารณาในการปรับให้เหมาะสมของฉันบางอย่างอาจมีการใช้งานในระดับที่ต่ำกว่าและฉันเพิ่งทำซ้ำพวกเขาเพิ่มเวลาที่สูญเสียใน CPU การพิจารณาอื่น ๆ อาจไม่จำเป็นอย่างสมบูรณ์เนื่องจากผลกระทบต่อประสิทธิภาพการทำงานนั้นเล็กน้อย

ดังนั้นคำถามของฉันคือ:

  1. แนวทางใดข้างต้นที่ใช้ได้และฉันควรทำตามเท่าไหร่?
  2. GPU จัดการกับการเปลี่ยนแปลงสถานะอย่างไร
  3. จะเกิดอะไรขึ้นถ้าฉันเปลี่ยนสถานะที่ไม่เคยใช้ (ไม่มีการเรียกสายขณะที่ใช้งาน)
  4. บทลงโทษที่เกิดขึ้นจริงจากการผูกส่วนประกอบต่าง ๆ มีอะไรบ้าง?
  5. ข้อควรพิจารณาด้านประสิทธิภาพอื่น ๆ ที่ควรทำ

โปรดอย่าเพิ่งบอกฉันว่าฉันไม่ควรสนใจเรื่องการแสดงจนกว่าจะถึงขีด จำกัด ที่แท้จริง ในขณะที่เห็นได้ชัดว่าเป็นเรื่องจริงจากมุมมองของภาคปฏิบัติฉันให้ความสนใจในทฤษฎีเป็นหลัก ฉันต้องการต่อสู้กับการสร้างกรอบกราฟิกที่ดีที่สุดและฉันไม่คิดว่าฉันจะทำเช่นนั้นได้ด้วยการบรรยายเรื่อง

การจัดการส่วนประกอบ

ขณะนี้ฉันกำลังเขียนแอพพลิเคชั่น DirectX 11 ใน C # โดยใช้ SlimDX เป็น wrapper ที่มีการจัดการ มันเป็น wrapper ระดับต่ำมากและสิ่งที่เป็นนามธรรมในปัจจุบันของฉันถูกสร้างขึ้นบนมัน

มีข้อดีบางอย่างที่เห็นได้ชัดเมื่อใช้ Direct3D Abstraction การตั้งค่าสภาพแวดล้อมที่โหลด shaders การตั้งค่าคงที่และการวาดภาพตาข่ายง่ายมากและใช้มากน้อยรหัส นอกจากนี้เนื่องจากมันจัดการการสร้างและการกำจัดส่วนประกอบส่วนใหญ่จึงสามารถนำกลับมาใช้ใหม่ได้ทุกที่โดยอัตโนมัติและฉันเกือบจะหลีกเลี่ยงการรั่วไหลของหน่วยความจำได้อย่างสมบูรณ์

  1. โดยปกติคุณจะจัดการส่วนประกอบกราฟิกและทรัพยากรทั้งหมดอย่างไร
  2. คุณช่วยแนะนำ wrappers ที่มีการจัดการทำบางอย่างที่คล้ายกับตัวอย่างด้านล่างของฉันได้ไหม

นี่คือตัวอย่างของการใช้งานปัจจุบันของฉัน ฉันมีความสุขมากกับส่วนต่อประสาน มันมีความยืดหยุ่นเพียงพอสำหรับความต้องการของฉันและใช้งานและเข้าใจได้ง่ายมาก:

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

8
สำหรับคำถามประเภทนี้ฉันจะไม่บรรยาย "การเพิ่มประสิทธิภาพก่อนวัยอันควร" ฉันจะให้ "การเปลี่ยนแปลงโปรไฟล์เพื่อให้คุณเห็นด้วยตนเอง" การบรรยาย
Tetrad

@ Tetrad ฉันเกือบละอายใจที่จะยอมรับว่านี่เป็นคำแนะนำที่ดีงาม ฉันควรทำโปรไฟล์มากกว่านี้อย่างแน่นอน
Lucius

1
หมายเลขการรวบรวมเป็นเวอร์ชัน "gamedev" หรือไม่เกิดขึ้น "แน่นอน =)
Patrick Hughes

คำตอบ:


3

ฉันชอบวิธีการที่เป็นนามธรรมที่ Hodgman ระบุไว้ในหัวข้อเหล่านี้ใน gamedev.net:

เขาอธิบายระบบการแสดงผลสามระดับ:

  1. API การแสดงผลระดับต่ำที่ยอมรับ "คำสั่ง" ทำให้ไม่เกินความแตกต่างในกราฟิค API ที่ต่างกันเช่น Direct3D 9, Direct3D 11 และ OpenGL "คำสั่ง" แต่ละรายการจะจับคู่กับสถานะที่แตกต่างกันหรือการเรียกเช่นการผูกสตรีมจุดสุดยอดหรือพื้นผิวหรือการวาดแบบดั้งเดิม
  2. API ที่ยอมรับ "เรนเดอร์ไอเท็ม" ซึ่งรวมกลุ่มสถานะทั้งหมดและการเรียกใช้ครั้งเดียวที่จำเป็นในการเรนเดอร์วัตถุบางอย่างและเรียงลำดับและแปลพวกมันเป็นคำสั่งที่ส่งไปยังระดับแรก สถานะการเรนเดอร์มีการเรียกแบบวาดและสแต็กของ "กลุ่มรัฐ" ซึ่งสถานะกลุ่มเปลี่ยนแปลงตามหลักเหตุผล ตัวอย่างเช่นคุณมีกลุ่มรัฐสำหรับการส่งผ่านการแสดงผลกลุ่มรัฐสำหรับวัสดุกลุ่มรัฐสำหรับเรขาคณิตกลุ่มรัฐสำหรับอินสแตนซ์และอื่น ๆ ระดับนี้มีหน้าที่รับผิดชอบในการจัดเรียงรายการการแสดงผลเหล่านี้เพื่อลดการเปลี่ยนแปลงสถานะที่ซ้ำซ้อนและกำจัดการเปลี่ยนแปลงสถานะใด ๆ ซึ่งอันที่จริงแล้วซ้ำซ้อน
  3. ระบบระดับสูงเช่นกราฟฉากหรือโปรแกรมแสดงภาพ GUI ซึ่งจะส่งรายการแสดงผลไปยังระดับที่สอง สิ่งสำคัญที่ควรสังเกตคือระบบเหล่านี้ไม่ทราบเกี่ยวกับอัลกอริทึมการเรียงลำดับสถานะหรือ API การแสดงผลเฉพาะทำให้พวกเขาไม่เชื่อเรื่องพระเจ้าแพลตฟอร์มอย่างสมบูรณ์ อีกทั้งยังใช้งานง่ายเมื่อมีการใช้ API ระดับล่าง

โดยสรุปโมเดลนี้แก้ปัญหาของคุณทั้งสองพร้อมกัน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.