ฉันชอบคิดถึงประสิทธิภาพในแง่ของ " ขีด จำกัด " มันเป็นวิธีที่สะดวกในการกำหนดแนวความคิดที่ค่อนข้างซับซ้อนเชื่อมต่อระบบ เมื่อคุณมีปัญหาด้านประสิทธิภาพคุณถามคำถาม: "ฉันกำลังกดปุ่มขีด จำกัด อะไร" (หรือ: "ฉันมี CPU / GPU ผูกไว้หรือไม่")
คุณสามารถแบ่งมันออกเป็นหลายระดับ ในระดับสูงสุดคุณมี CPU และ GPU คุณอาจใช้ CPU (GPU กำลังรอการใช้งาน CPU) หรือ GPU ถูกผูกไว้ (CPU กำลังรอ GPU) นี่คือโพสต์บล็อกที่ดีในหัวข้อ
คุณสามารถทำลายมันได้อีก ในด้านCPUคุณอาจใช้รอบทั้งหมดของข้อมูลที่มีอยู่แล้วในแคช CPU หรือคุณอาจจะมีหน่วยความจำ จำกัดทิ้งไม่ได้ใช้งานรอ CPU สำหรับข้อมูลที่จะมาจากหน่วยความจำหลัก ( เพื่อเพิ่มประสิทธิภาพการจัดวางข้อมูลของคุณ ) คุณสามารถทำลายมันลงไปอีก
(ในขณะที่ฉันกำลังทำภาพรวมกว้าง ๆ เกี่ยวกับประสิทธิภาพของ XNA ฉันจะชี้ให้เห็นว่าการจัดสรรประเภทการอ้างอิง ( class
ไม่ใช่struct
) ในขณะที่ราคาถูกปกติสามารถเรียกใช้ตัวเก็บรวบรวมขยะซึ่งจะเผาวงจรจำนวนมากโดยเฉพาะใน Xbox 360 . ดูที่นี่สำหรับรายละเอียด)
ทางด้านGPUฉันจะเริ่มโดยชี้ไปที่โพสต์บล็อกที่ยอดเยี่ยมซึ่งมีรายละเอียดมากมาย หากคุณต้องการรายละเอียดระดับบ้าบนไปป์ไลน์อ่านชุดของบล็อกโพสต์นี้ ( นี่คือวิธีที่ง่ายกว่า )
หากต้องการวางไว้ที่นี่เพียงบางส่วนของขนาดใหญ่คือ: " ขีด จำกัด การเติม " (จำนวนพิกเซลที่คุณสามารถเขียนลงใน backbuffer - บ่อยแค่ไหนที่คุณสามารถถอนเงินเกินจำนวนได้) " ขีด จำกัด shader " (ซับซ้อน shader ของคุณและ คุณสามารถส่งผ่านข้อมูลได้มากเพียงใด, " ขีด จำกัด การดึงข้อมูลพื้นผิว / พื้นผิวแบนด์วิดท์ " (จำนวนข้อมูลพื้นผิวที่คุณสามารถเข้าถึงได้)
และตอนนี้เรามาถึงตัวใหญ่ - ซึ่งเป็นสิ่งที่คุณถามจริง ๆ - ที่ CPU และ GPU ต้องโต้ตอบ (ผ่าน API และไดร์เวอร์ต่างๆ) มี " ขีด จำกัด แบตช์ " และ " แบนด์วิดธ์ " อย่างหลวม ๆ (โปรดทราบว่าส่วนหนึ่งของซีรีส์ที่ฉันพูดถึงไปก่อนหน้านี้มีรายละเอียดมากมาย)
แต่โดยทั่วไปชุด ( ตามที่คุณทราบแล้ว ) จะเกิดขึ้นทุกครั้งที่คุณเรียกใช้GraphicsDevice.Draw*
ฟังก์ชันใดฟังก์ชันหนึ่ง (หรือส่วนหนึ่งของ XNA เช่นSpriteBatch
นี้ทำเพื่อคุณ) ในขณะที่คุณไม่ต้องสงสัยเลยว่าคุณได้อ่านแล้วคุณจะได้รับไม่กี่พัน*ต่อเฟรม นี่เป็นขีด จำกัด ของ CPU - ดังนั้นจึงแข่งขันกับการใช้งาน CPU อื่น ๆ ของคุณ โดยพื้นฐานแล้วคนขับบรรจุหีบห่อทุกอย่างเกี่ยวกับสิ่งที่คุณบอกให้วาดและส่งไปยัง GPU
แล้วมีแบนด์วิดธ์ของ GPU นี่คือข้อมูลดิบที่คุณสามารถถ่ายโอนได้ที่นั่น ซึ่งรวมถึงข้อมูลสถานะทั้งหมดที่ไปพร้อมกับแบทช์ - ทุกอย่างจากการตั้งค่าสถานะการแสดงผลและค่าคงที่ / พารามิเตอร์ shader (ซึ่งรวมถึงสิ่งต่าง ๆ เช่นเมทริกซ์โลก / มุมมอง / โครงการ) ไปจนถึงจุดยอดเมื่อใช้DrawUser*
ฟังก์ชัน นอกจากนี้ยังรวมถึงการโทรไปยังSetData
และGetData
บนพื้นผิวบัฟเฟอร์จุดสุดยอด ฯลฯ
ณ จุดนี้ฉันควรจะพูดว่าสิ่งที่คุณสามารถโทรหาSetData
(พื้นผิว, จุดสุดยอดและดัชนีบัฟเฟอร์, ฯลฯ ) รวมทั้งEffect
s - ยังคงอยู่ในหน่วยความจำ GPU มันไม่ได้ถูกส่งไปยัง GPU อีกครั้งอย่างต่อเนื่อง คำสั่ง draw ที่อ้างอิงว่าข้อมูลนั้นจะถูกส่งไปพร้อมกับตัวชี้ไปยังข้อมูลนั้น
(เช่น: คุณสามารถส่งคำสั่งวาดจากเธรดหลักเท่านั้น แต่คุณสามารถทำได้SetData
บนเธรดใด ๆ )
XNA มีความซับซ้อนค่อนข้างสิ่งที่มีการแสดงผลการเรียนของรัฐ ( BlendState
, DepthStencilState
ฯลฯ ) ข้อมูลสถานะนี้จะถูกส่งต่อการเรียกการดึง (ในแต่ละชุด) ฉันไม่แน่ใจ 100% แต่ฉันรู้สึกว่ามันถูกส่งไปอย่างเกียจคร้าน (มันแค่ส่งการเปลี่ยนแปลงที่ระบุว่า) ไม่ว่าจะด้วยวิธีใดการเปลี่ยนแปลงสถานะจะมีราคาถูกจนถึงจุดว่างซึ่งสัมพันธ์กับต้นทุนของชุดการผลิต
ในที่สุดสิ่งสุดท้ายที่จะพูดถึงก็คือGPU ไปป์ไลน์ภายใน คุณไม่ต้องการบังคับให้ล้างข้อมูลโดยการเขียนไปยังข้อมูลที่ยังต้องอ่านหรืออ่านข้อมูลที่ยังต้องเขียน ไปป์ไลน์หมายถึงการรอให้การดำเนินการเสร็จสิ้นเพื่อให้ทุกอย่างอยู่ในสถานะที่สอดคล้องกันเมื่อเข้าถึงข้อมูล
ทั้งสองกรณีที่ต้องระวังคือการโทรหาGetData
สิ่งที่มีพลวัต - โดยเฉพาะอย่างยิ่งในสิ่งRenderTarget2D
ที่ GPU อาจจะเขียนถึง สิ่งนี้แย่มากสำหรับประสิทธิภาพ - อย่าทำ
อีกกรณีหนึ่งกำลังเรียกSetData
ใช้บัฟเฟอร์จุดสุดยอด / ดัชนี หากคุณต้องการทำสิ่งนี้บ่อยให้ใช้DynamicVertexBuffer
(ด้วยDynamicIndexBuffer
) สิ่งเหล่านี้ทำให้ GPU รู้ว่าพวกมันจะเปลี่ยนบ่อยๆและทำเวทย์มนตร์บัฟเฟอร์ภายในเพื่อหลีกเลี่ยงการสะบัดท่อ
(โปรดทราบว่าบัฟเฟอร์แบบไดนามิกเร็วกว่าDrawUser*
วิธีการ - แต่ต้องจัดสรรล่วงหน้าตามขนาดสูงสุดที่ต้องการ)
... และนั่นคือทุกสิ่งที่ฉันรู้เกี่ยวกับประสิทธิภาพของ XNA :)