ฉันจะเพิ่มประสิทธิภาพโลก Minecraft-esque voxel ได้อย่างไร?


76

ฉันพบว่าโลกขนาดใหญ่ที่ยอดเยี่ยมของ Minecraft นั้นช้ามากในการนำทางแม้ว่าจะใช้ Quad-Core และกราฟิกการ์ดเนื้อ

ฉันถือว่าความเชื่องช้าของ Minecraft มาจาก:

  • Java เนื่องจากการแบ่งพาร์ติชันและการจัดการหน่วยความจำเร็วขึ้นในภาษา C ++
  • การแบ่งพาร์ติชันที่อ่อนแอของโลก

ฉันอาจผิดทั้งสองข้อ อย่างไรก็ตามนี่ทำให้ฉันคิดถึงวิธีที่ดีที่สุดในการจัดการโลก voxel ขนาดใหญ่ มันเป็นโลก 3 มิติจริงที่บล็อกสามารถอยู่ในส่วนใดของโลกก็เป็นพื้นอาร์เรย์ใหญ่ 3D [x][y][z]ซึ่งแต่ละบล็อกในโลกที่มีประเภท (เช่นBlockType.Empty = 0, BlockType.Dirt = 1ฯลฯ )

ฉันคิดว่าเพื่อให้โลกประเภทนี้ทำงานได้ดีคุณจะต้อง:

  • ใช้ต้นไม้ที่มีความหลากหลาย ( oct / kd / bsp ) เพื่อแยกลูกบาศก์ทั้งหมดออก ดูเหมือนว่า oct / kd จะเป็นตัวเลือกที่ดีกว่าเนื่องจากคุณสามารถแบ่งพาร์ติชันในระดับต่อคิวบ์ไม่ใช่ระดับต่อสามเหลี่ยม
  • ใช้อัลกอริทึมบางอย่างเพื่อหาว่าบล็อกใดที่สามารถเห็นได้ในปัจจุบันเนื่องจากบล็อกที่อยู่ใกล้กับผู้ใช้มากขึ้นอาจทำให้บล็อกยุ่งเหยิง
  • ทำให้วัตถุบล็อกมีน้ำหนักเบาดังนั้นจึงเป็นการเพิ่มและนำออกจากต้นไม้ได้อย่างรวดเร็ว

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



2
ดังนั้นคุณถามอะไรจริง ๆ คุณกำลังขอวิธีการที่ดีในการจัดการโลกขนาดใหญ่หรือข้อเสนอแนะเกี่ยวกับวิธีการเฉพาะของคุณหรือความคิดเห็นในเรื่องของการจัดการโลกขนาดใหญ่?
doppelgreener

1
เพื่อให้ห่างไกลสิ่งที่ดีก็จะมากขึ้นเกี่ยวกับสิ่งที่พบมากที่สุดวิธีการทั่วไปในการเรียงลำดับของสิ่งเหล่านี้ ฉันไม่ได้เจาะจงเฉพาะหลังจากข้อเสนอแนะเกี่ยวกับวิธีการของฉันเป็นทั้งหมดที่ฉันได้เสนอคือสิ่งที่ฉันคาดว่าจะเกิดขึ้นในเชิงตรรกะ ฉันแค่ต้องการข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้จริง ๆ และก็ไม่มากนักหลังจากการค้นหาสองสามครั้ง ฉันเดาว่าคำถามของฉันไม่เพียงเกี่ยวกับการเรนเดอร์ประสิทธิภาพ แต่เกี่ยวกับวิธีการจัดการข้อมูลจำนวนมากเช่นการ chunking พื้นที่เป็นต้น
SomeXnaChump

2
ชัดเจนและเพิ่มคำถามในโพสต์ของคุณเพื่อให้เรารู้ว่าเรากำลังตอบคำถามอะไร ;)
doppelgreener

3
คุณหมายถึงอะไรโดย "ช้ามากที่จะนำทาง"? มีบางอย่างที่ช้าลงเมื่อเกมสร้างภูมิประเทศใหม่ แต่หลังจากนั้น minecraft มีแนวโน้มที่จะจัดการกับภูมิประเทศที่ดี
thedaian

คำตอบ:


106

Voxel Engine Rocks

หญ้าเครื่องยนต์ของ Voxel

เกี่ยวกับ Java vs C ++ ฉันได้เขียนเอ็นจิ้น voxel ทั้งคู่ (เวอร์ชั่น C ++ แสดงไว้ด้านบน) ฉันเคยเขียนเครื่องยนต์ voxel มาตั้งแต่ปี 2004 (ตอนที่พวกเขาไม่ทันสมัย) :) ฉันสามารถพูดด้วยความลังเลเล็กน้อยว่าประสิทธิภาพของ C ++ นั้นเหนือกว่ามาก (แต่ก็ยากที่จะเขียนโค้ด) มันเกี่ยวกับความเร็วในการคำนวณน้อยกว่าและเกี่ยวกับการจัดการหน่วยความจำ เมื่อคุณจัดสรร / ยกเลิกการจัดสรรข้อมูลให้มากที่สุดในโลก voxel C (++) คือภาษาที่จะเอาชนะ อย่างไรก็ตามคุณควรคิดถึงเป้าหมายของคุณ หากประสิทธิภาพเป็นสิ่งที่สำคัญที่สุดของคุณให้ไปกับ C ++ หากคุณเพียงต้องการเขียนเกมที่ไม่มีประสิทธิภาพออกมาอย่างเด็ดขาด Java นั้นเป็นที่ยอมรับอย่างแน่นอน มีกรณีเล็ก ๆ น้อย ๆ / ขอบ แต่โดยทั่วไปคุณสามารถคาดหวังให้ Java ทำงานประมาณ 1.75-2.0 เท่าช้ากว่า (เขียนได้ดี) C ++ คุณสามารถดูการทำงานของเครื่องยนต์รุ่นเก่าที่ได้รับการปรับปรุงประสิทธิภาพได้ไม่ดีที่นี่ (แก้ไข: รุ่นที่ใหม่กว่าที่นี่ ) ในขณะที่การสร้างชิ้นข้อมูลอาจดูช้า แต่โปรดจำไว้ว่ามันกำลังสร้างไดอะแกรม 3D voronoi แบบปริมาตรการคำนวณหาค่าพื้นฐานของพื้นผิวแสง AO และเงาบน CPU ด้วยวิธีการบังคับแบบเดรัจฉาน ฉันได้ลองใช้เทคนิคที่หลากหลายและฉันสามารถสร้างก้อนที่เร็วขึ้นประมาณ 100 เท่าโดยใช้เทคนิคการแคชและการอินสแตนซ์ที่หลากหลาย

เพื่อตอบคำถามที่เหลือมีหลายสิ่งที่คุณสามารถทำได้เพื่อปรับปรุงประสิทธิภาพ

  1. เก็บเอาไว้. ไม่ว่าคุณจะทำได้ที่ไหนคุณควรคำนวณข้อมูลหนึ่งครั้ง ตัวอย่างเช่นฉันอบแสงไฟเข้าฉาก มันสามารถใช้แสงแบบไดนามิก (ในพื้นที่หน้าจอเป็นกระบวนการหลัง) แต่การอบในแสงหมายความว่าฉันไม่ต้องผ่านในบรรทัดฐานสำหรับรูปสามเหลี่ยมซึ่งหมายความว่า ....
  2. ส่งข้อมูลน้อยไปยังการ์ดวิดีโอให้มากที่สุด สิ่งหนึ่งที่คนมักจะลืมคือยิ่งคุณส่งข้อมูลผ่าน GPU มากเท่าไหร่ก็ยิ่งมีเวลามากขึ้นเท่านั้น ฉันผ่านสีเดียวและตำแหน่งจุดสุดยอด ถ้าฉันต้องการที่จะทำรอบกลางวัน / กลางคืนฉันสามารถทำระดับสีหรือฉันสามารถคำนวณฉากที่ดวงอาทิตย์ค่อย ๆ เปลี่ยน

  3. เนื่องจากการส่งข้อมูลไปยัง GPU นั้นมีราคาแพงมากจึงเป็นไปได้ที่จะเขียนเอ็นจิ้นในซอฟต์แวร์ซึ่งเร็วกว่าในบางประเด็น ข้อดีของซอฟต์แวร์คือสามารถเข้าถึงการจัดการข้อมูล / หน่วยความจำทุกชนิดที่ไม่สามารถทำได้บน GPU

  4. เล่นกับขนาดแบทช์ หากคุณใช้ GPU ประสิทธิภาพอาจแตกต่างกันไปอย่างมากโดยขึ้นอยู่กับว่าแต่ละจุดยอดที่คุณผ่านนั้นมีขนาดใหญ่เพียงใด ดังนั้นเล่นกับขนาดของชิ้น (ถ้าคุณใช้ชิ้น) ฉันพบว่าชิ้น 64x64x64 ทำงานได้ดีทีเดียว ไม่ว่าอะไรก็ตามจงเก็บลูกบาศก์ของคุณให้เป็นลูกบาศก์ (ไม่มีปริซึมสี่เหลี่ยม) สิ่งนี้จะทำให้การเข้ารหัสและการดำเนินการต่าง ๆ (เช่นการเปลี่ยนแปลง) ง่ายขึ้นและในบางกรณีมีประสิทธิภาพมากขึ้น หากคุณเก็บค่าเดียวสำหรับความยาวของทุกมิติโปรดทราบว่าการลงทะเบียนน้อยกว่าสองครั้งที่สลับไปมาระหว่างการคำนวณ

  5. พิจารณารายการที่แสดง (สำหรับ OpenGL) แม้ว่าจะเป็นวิธี "เก่า" แต่ก็สามารถเร็วขึ้นได้ คุณต้องอบรายการที่แสดงลงในตัวแปร ... ถ้าคุณเรียกการดำเนินการสร้างรายการที่แสดงในเรียลไทม์มันจะช้าอย่างไม่น่าเชื่อ รายการที่แสดงเป็นอย่างไรเร็วขึ้น? มันอัพเดตสถานะ, vs แอ็ตทริบิวต์ต่อจุดสุดยอดเท่านั้น นี่หมายความว่าฉันสามารถส่งผ่านใบหน้าได้สูงสุดหกใบหน้าจากนั้นหนึ่งสี (เทียบกับสีสำหรับแต่ละจุดยอดของ voxel) หากคุณใช้ GL_QUADS และ cubic voxels สิ่งนี้สามารถบันทึกได้มากถึง 20 ไบต์ (160 บิต) ต่อ voxel! (15 ไบต์โดยไม่มีตัวอักษรถึงแม้ว่าโดยปกติแล้วคุณต้องการให้สิ่งต่าง ๆ เรียงกันแบบ 4 ไบต์)

  6. ฉันใช้วิธีการเดรัจฉานบังคับของการแสดงผล "ชิ้น" หรือหน้าข้อมูลซึ่งเป็นเทคนิคทั่วไป ซึ่งแตกต่างจาก octrees มันง่ายกว่า / เร็วกว่าในการอ่าน / ประมวลผลข้อมูลแม้ว่าจะเป็นมิตรกับหน่วยความจำน้อยกว่า (แต่ทุกวันนี้คุณสามารถรับหน่วยความจำ 64 กิกะไบต์สำหรับ $ 200- $ 300) ... ไม่ใช่ผู้ใช้ทั่วไป เห็นได้ชัดว่าคุณไม่สามารถจัดสรรอาเรย์ขนาดใหญ่สำหรับโลกทั้งใบได้ (ชุด 1024x1024x1024 ของ voxels คือหน่วยความจำ 4 กิกะไบต์โดยสมมติว่าใช้ 32-bit int ต่อ voxel) ดังนั้นคุณจัดสรร / dealloc อาร์เรย์ขนาดเล็กจำนวนมากขึ้นอยู่กับความใกล้ชิดกับผู้ชม นอกจากนี้คุณยังสามารถจัดสรรข้อมูลรับรายการแสดงผลที่จำเป็นจากนั้นถ่ายโอนข้อมูลเพื่อบันทึกหน่วยความจำ ฉันคิดว่าคำสั่งผสมในอุดมคติอาจใช้วิธีไฮบริดของ octrees และ arrays - เก็บข้อมูลไว้ใน array เมื่อทำการสร้างโพรซีเดอร์ของโลก, แสง, ฯลฯ

  7. แสดงผลใกล้ถึงไกล ... การตัดพิกเซลทำให้ประหยัดเวลา gpu จะโยนพิกเซลหากไม่ผ่านการทดสอบบัฟเฟอร์ความลึก

  8. แสดงผลชิ้น / เพจในวิวพอร์ตเท่านั้น (อธิบายตนเอง) แม้ว่า GPU จะรู้วิธีคลิปโพลีออนนอกวิวพอร์ตการส่งผ่านข้อมูลนี้ยังคงใช้เวลา ฉันไม่ทราบว่าโครงสร้างที่มีประสิทธิภาพที่สุดสำหรับเรื่องนี้คืออะไร ("น่าละอาย" ฉันไม่เคยเขียนแผนภูมิ BSP) แต่ถึงแม้จะเป็น raycast แบบง่าย ๆ บนพื้นฐานต่อชิ้นอาจปรับปรุงประสิทธิภาพและการทดสอบกับ frustum ที่ดูจะชัดเจน ประหยัดเวลา.

  9. ข้อมูลที่ชัดเจน แต่สำหรับมือใหม่: ลบทุกรูปหลายเหลี่ยมที่ไม่ได้อยู่บนพื้นผิว - เช่นถ้า voxel ประกอบด้วยใบหน้าหกหน้าให้ลบใบหน้าที่ไม่เคยแสดงผลออกมา (แตะ voxel อีกอัน)

  10. ตามกฎทั่วไปของทุกสิ่งที่คุณทำในการเขียนโปรแกรม: CACHE LOCALITY! หากคุณสามารถเก็บสิ่งต่าง ๆ ไว้ในแคชในตัวเครื่อง (แม้จะมีเวลาเพียงเล็กน้อยก็ตามมันก็จะสร้างความแตกต่างอย่างมหาศาล) นั่นหมายถึงการรักษาข้อมูลของคุณให้สอดคล้องกัน (ในพื้นที่หน่วยความจำเดียวกัน) และไม่สลับพื้นที่หน่วยความจำ นึกคิดทำงานกับหนึ่งชิ้นต่อเธรดและเก็บหน่วยความจำนั้นไว้ที่เธรดซึ่งไม่ได้ใช้กับ CPU แคชเท่านั้นนึกถึงลำดับชั้นของแคชแบบนี้ (ช้าที่สุดไปเร็วที่สุด): เครือข่าย (คลาวด์ / ฐานข้อมูล / ฯลฯ ) -> ฮาร์ดไดรฟ์ (รับ SSD หากคุณยังไม่มี), ram (รับ tripple channel หรือ RAM มากกว่าถ้าคุณยังไม่มี), CPU Cache, รีจิสเตอร์พยายามเก็บข้อมูลของคุณไว้ ปลายหลังและไม่สลับมากกว่าที่คุณต้อง

  11. Threading ทำมัน. โลก Voxel นั้นเหมาะสำหรับการทำเกลียวเนื่องจากแต่ละส่วนสามารถคำนวณได้ (ส่วนใหญ่) เป็นอิสระจากส่วนอื่น ๆ ... ฉันเห็นว่าการปรับปรุงใกล้เคียง 4x (บน 4 คอร์, 8 เธรดคอร์ i7) ในการสร้างโลกแบบขั้นตอนเมื่อฉันเขียน รูทีนสำหรับเธรด

  12. อย่าใช้ชนิดข้อมูลถ่าน / ไบต์ หรือกางเกงขาสั้น ผู้บริโภคทั่วไปของคุณจะมีโปรเซสเซอร์ AMD หรือ Intel ที่ทันสมัย ​​(เช่นเดียวกับคุณ) โปรเซสเซอร์เหล่านี้ไม่มีการลงทะเบียน 8 บิต พวกเขาคำนวณไบต์โดยใส่ลงในสล็อต 32 บิตแล้วแปลงกลับ (อาจ) ในหน่วยความจำ คอมไพเลอร์ของคุณอาจทำรายการวูดูทุกประเภท แต่การใช้หมายเลข 32 หรือ 64 บิตจะทำให้ได้ผลลัพธ์ที่คาดการณ์ได้มากที่สุด (และเร็วที่สุด) เช่นเดียวกันค่า "บูล" ไม่ใช้เวลา 1 บิต; คอมไพเลอร์มักจะใช้เต็ม 32 บิตสำหรับบูล อาจเป็นการดึงดูดให้ทำการบีบอัดข้อมูลบางประเภท ตัวอย่างเช่นคุณสามารถเก็บ 8 voxels เป็นตัวเลขเดียว (2 ^ 8 = 256 ชุด) หากพวกเขาเป็นประเภท / สีเดียวกันทั้งหมด อย่างไรก็ตามคุณต้องคิดเกี่ยวกับการแยกแยะเรื่องนี้ - มันอาจช่วยประหยัดหน่วยความจำได้มาก แต่มันยังสามารถขัดขวางประสิทธิภาพการทำงานแม้จะมีเวลาในการบีบอัดขนาดเล็กก็ตามเพราะแม้แต่ช่วงเวลาพิเศษจำนวนเล็กน้อยนั้นก็ปรับขนาดตามขนาดโลกของคุณ ลองนึกภาพการคำนวณ raycast; สำหรับทุกขั้นตอนของ raycast คุณจะต้องเรียกใช้อัลกอริธึมการคลายการบีบอัด (เว้นแต่คุณจะได้วิธีการคำนวณทั่วไปสำหรับ 8 voxels ในขั้นตอนเดียว)

  13. ตามที่ Jose Chavez กล่าวถึงรูปแบบการออกแบบฟลายเวทจะมีประโยชน์ เช่นเดียวกับที่คุณใช้บิตแมปเพื่อเป็นตัวแทนของเกมเรียงซ้อนในเกม 2D คุณสามารถสร้างโลกของคุณจากรูปแบบ 3 มิติหลายแบบ (หรือบล็อก) ข้อเสียของเรื่องนี้คือการทำซ้ำของพื้นผิว แต่คุณสามารถแก้ไขได้โดยใช้พื้นผิวที่แปรปรวน โดยทั่วไปแล้วคุณต้องการใช้การสร้างความสมดุลในทุกที่ที่ทำได้

  14. หลีกเลี่ยงการประมวลผลจุดยอดและพิกเซลใน shader เมื่อแสดงผลเรขาคณิต ในเอ็นจิ้น voxel คุณจะมีสามเหลี่ยมหลายอันอย่างหลีกเลี่ยงไม่ได้ดังนั้นแม้แต่ pixel shader ที่เรียบง่ายก็สามารถลดเวลาเรนเดอร์ของคุณได้อย่างมาก มันจะดีกว่าในการเรนเดอร์ให้กับบัฟเฟอร์จากนั้นคุณจะใช้ Pixel Shader เป็นโพสต์โปรเซส หากคุณไม่สามารถทำได้ให้ลองทำการคำนวณในจุดสุดยอดของคุณ การคำนวณอื่น ๆ ควรถูกอบลงในข้อมูลจุดสุดยอดที่เป็นไปได้ บัตรผ่านเพิ่มเติมมีราคาแพงมากหากคุณต้องแสดงผลเรขาคณิตทั้งหมดอีกครั้ง (เช่นการจับคู่เงาหรือการจับคู่สภาพแวดล้อม) บางครั้งมันจะเป็นการดีกว่าถ้าจะให้มีฉากแบบไดนามิกเพื่อให้ได้รายละเอียดที่สมบูรณ์ยิ่งขึ้น หากเกมของคุณมีฉากที่สามารถแก้ไขได้ (เช่นภูมิประเทศที่สามารถทำลายได้) คุณสามารถคำนวณฉากใหม่ได้เมื่อสิ่งต่าง ๆ ถูกทำลาย การคอมไพล์ใหม่ไม่แพงและควรใช้เวลาไม่กี่วินาที

  15. ผ่อนคลายลูปของคุณและรักษาอาร์เรย์ให้เรียบ! อย่าทำสิ่งนี้:

    for (i = 0; i < chunkLength; i++) {
     for (j = 0; j < chunkLength; j++) {
      for (k = 0; k < chunkLength; k++) {
       MyData[i][j][k] = newVal;
      }
     }
    }
    //Instead, do this:
    for (i = 0; i < chunkLengthCubed; i++) {
     //figure out x, y, z index of chunk using modulus and div operators on i
     //myData should have chunkLengthCubed number of indices, obviously
     myData[i] = newVal;
    }
    

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

แก้ไข 2: เมื่อใช้ลูปแบบหลายดัชนีเลือกที่ดีที่สุดในการวนลูป int คำสั่ง z, y, x มากกว่าวิธีอื่น คอมไพเลอร์ของคุณอาจปรับให้เหมาะสม แต่ฉันจะแปลกใจถ้ามันทำ สิ่งนี้จะช่วยเพิ่มประสิทธิภาพในการเข้าถึงหน่วยความจำและท้องถิ่น

for (k < 0; k < volumePitch; k++) {
    for (j = 0; j < volumePitch; j++) {
        for (i = 0; i < volumePitch; i++) {
            myIndex = k*volumePitch*volumePitch + j*volumePitch + i;
        }
    }
}
  1. บางครั้งคุณต้องตั้งสมมติฐานวางนัยทั่วไปและสังเวย สิ่งที่ดีที่สุดที่คุณสามารถทำได้คือการสมมติว่าโลกส่วนใหญ่ของคุณคงที่และเปลี่ยนแปลงเพียงสองพันเฟรมเท่านั้น สำหรับส่วนที่มีชีวิตชีวาของโลกสิ่งเหล่านั้นสามารถทำได้ในแบบแยกกัน ยังถือว่าส่วนใหญ่ของโลกของคุณทึบแสงอย่างสมบูรณ์ วัตถุโปร่งใสสามารถแสดงผลในผ่านแยก สมมติว่าพื้นผิวแตกต่างกันทุกหน่วย x เท่านั้นหรือวัตถุนั้นสามารถวางได้ทีละ x เท่านั้น สมมติว่าโลกมีขนาดคงที่ ... หากดึงดูดในโลกที่ไม่มีที่สิ้นสุดมันสามารถนำไปสู่ความต้องการของระบบที่คาดเดาไม่ได้ ตัวอย่างเช่นเพื่อลดความซับซ้อนของการสร้างรูปแบบ voronoi ในหินด้านบนฉันสันนิษฐานว่าจุดกึ่งกลางของ voronoi ทั้งหมดถูกโกหกในตารางที่มีเครื่องแบบโดยมีการชดเชยเล็กน้อย (กล่าวอีกนัยหนึ่งคือ สมมติว่าโลกที่ไม่ได้ห่อ (มีขอบ) สิ่งนี้สามารถลดความซับซ้อนจำนวนมากที่นำมาใช้โดยระบบพิกัดการห่อในราคาที่ถูกที่สุดสำหรับประสบการณ์ของผู้ใช้

คุณสามารถอ่านเพิ่มเติมเกี่ยวกับการใช้งานของฉันได้ที่เว็บไซต์ของฉัน


9
+1 สัมผัสที่ดีรวมถึงรูปภาพที่ด้านบนเพื่อเป็นแรงจูงใจในการอ่านเรียงความ ตอนนี้ฉันได้อ่านบทความที่ฉันสามารถพูดได้ว่าพวกเขาไม่จำเป็นและมันก็คุ้มค่า ;)
George Duckett

ขอบคุณ - ภาพมีค่าหนึ่งพันคำเช่นที่พวกเขาพูด :) นอกเหนือจากการทำให้ข้อความของฉันดูน่าหวาดกลัวน้อยลงฉันต้องการให้ผู้อ่านเข้าใจถึงจำนวนของ voxels ที่สามารถแสดงในอัตราที่สมเหตุสมผลโดยใช้เทคนิคที่อธิบายไว้
Gavan Woolery

14
ฉันยังหวังว่า SE จะยอมให้มีคำตอบเฉพาะที่ชื่นชอบ
joltmode

2
@PatrickMoriarty # 15 เป็นเคล็ดลับทั่วไป สมมติว่าคอมไพเลอร์ของคุณไม่ได้ทำให้การเพิ่มประสิทธิภาพนี้ (มันอาจคลี่วงของคุณ แต่มันอาจจะไม่กระชับอาร์เรย์หลายมิติ) คุณต้องการเก็บข้อมูลทั้งหมดของคุณไว้ในพื้นที่หน่วยความจำที่ต่อเนื่องกันสำหรับการแคช อาเรย์หลายมิติสามารถ (อาจ) ถูกจัดสรรข้ามช่องว่างมากมายเนื่องจากเป็นอาเรย์ของพอยน์เตอร์ สำหรับการคลายลูปลองคิดว่าโค้ดที่คอมไพล์มีหน้าตาเป็นอย่างไร เพื่อให้การลงทะเบียนและการแลกเปลี่ยนแคชน้อยที่สุดคุณต้องการสร้าง vars / คำแนะนำน้อยที่สุด ท่านใดที่คิดว่ารวบรวมมากขึ้น?
Gavan Woolery

2
ในขณะที่บางจุดที่นี่ดีโดยเฉพาะอย่างยิ่งเกี่ยวกับการแคชเธรดและการลดการถ่ายโอน GPU ให้น้อยที่สุด แต่บางจุดก็ไม่ถูกต้องอย่างมาก ที่ 5: ใช้ VBOs / VAO แทนรายการแสดงผลเสมอ 6: RAM เพิ่มเติมต้องการแบนด์วิดธ์เพิ่มขึ้น ด้วยโอกาสในการขายที่ 12: ตรงกันข้ามตรงข้ามเป็นจริงสำหรับหน่วยความจำสมัยใหม่ซึ่งทุกไบต์ที่บันทึกไว้จะเพิ่มโอกาสในการปรับข้อมูลลงในแคช 14: ใน Minecraft มีจุดยอดมากกว่าพิกเซล (ลูกบาศก์ไกลเหล่านั้น) ดังนั้นให้ย้ายการคำนวณไปที่ตัวเปลี่ยนพิกเซลไม่ใช่จากมันโดยควรมีการแรเงาที่เลื่อนออกไป

7

มีหลายสิ่งหลายอย่างที่ Minecraft สามารถทำได้อย่างมีประสิทธิภาพมากขึ้น ตัวอย่างเช่น Minecraft โหลดเสาแนวตั้งทั้งหมดประมาณ 16x16 แผ่นและแสดงผล ฉันรู้สึกว่ามันไม่มีประสิทธิภาพมากในการส่งและแสดงว่ากระเบื้องจำนวนมากไม่จำเป็น แต่ฉันไม่รู้สึกว่าการเลือกภาษาเป็นสิ่งสำคัญ

Java นั้นค่อนข้างเร็ว แต่สำหรับบางอย่างที่เน้นข้อมูลนี้ C ++ มีข้อได้เปรียบอย่างมากโดยมีค่าใช้จ่ายน้อยลงอย่างมากสำหรับการเข้าถึงอาร์เรย์และทำงานภายในไบต์ ในทางกลับกันมันง่ายกว่ามากในการทำเกลียวในทุกแพลตฟอร์มใน Java นอกเสียจากคุณวางแผนที่จะใช้ OpenMP หรือ OpenCL คุณจะไม่พบความสะดวกสบายใน C ++

ระบบในอุดมคติของฉันจะเป็นลำดับชั้นที่ซับซ้อนกว่าเล็กน้อย

ไทล์เป็นหน่วยเดียวมีแนวโน้มประมาณ 4 ไบต์เพื่อเก็บข้อมูลเช่นชนิดของวัสดุและแสง

ส่วนจะเป็นบล็อกสี่เหลี่ยมขนาด 32x32x32

  1. ธงจะถูกตั้งค่าสำหรับแต่ละด้านหกถ้าทั้งด้านนั้นเป็นบล็อกทึบ ที่จะอนุญาตให้ตัวแสดงภาพปิดส่วนที่อยู่ด้านหลังส่วนนั้น Minecraft ในปัจจุบันไม่ปรากฏว่ามีการทดสอบการบดเคี้ยว แต่มีการพูดถึงการมีการบดเคี้ยวฮาร์ดแวร์ที่มีอยู่ซึ่งอาจมีราคาแพง แต่ดีกว่าการเรนเดอร์จำนวนมากบนการ์ดระดับล่าง
  2. ส่วนจะถูกโหลดลงในหน่วยความจำระหว่างกิจกรรม (ผู้เล่น, NPCs, ฟิสิกส์ของน้ำ, การเติบโตของต้นไม้เป็นต้น) มิฉะนั้นจะถูกส่งโดยตรงยังคงถูกบีบอัดจากดิสก์ไปยังไคลเอนต์

ส่วนจะเป็นบล็อกของกลุ่ม 16x16x8

  1. ส่วนจะติดตามส่วนที่สูงที่สุดสำหรับแต่ละคอลัมน์แนวตั้งเพื่อให้ส่วนที่สูงกว่านั้นสามารถระบุได้ว่าว่างเปล่าอย่างรวดเร็ว
  2. นอกจากนี้ยังจะติดตามส่วนที่ถูกบดบังด้านล่างเพื่อให้ทุกส่วนที่ต้องการแสดงผลจากพื้นผิวสามารถจับได้อย่างรวดเร็ว
  3. ภาคจะติดตามในครั้งต่อไปที่แต่ละส่วนจะต้องมีการปรับปรุง (ฟิสิกส์ของน้ำการเจริญเติบโตของต้นไม้เป็นต้น) วิธีนี้การโหลดในแต่ละส่วนจะเพียงพอที่จะทำให้โลกมีชีวิตอยู่และโหลดในเซ็กเมนต์ที่นานพอที่จะทำงานให้เสร็จ
  4. ตำแหน่งเอนทิตีทั้งหมดจะถูกติดตามเทียบกับภาค การทำเช่นนี้จะช่วยป้องกันข้อผิดพลาดที่เกิดขึ้นใน Minecraft เมื่อเดินทางไกลจากศูนย์กลางแผนที่

โลกจะเป็นแผนที่ที่ไม่มีที่สิ้นสุดของภาค

  1. โลกจะต้องรับผิดชอบในการจัดการภาคและการปรับปรุงต่อไปของพวกเขา
  2. โลกจะส่งกลุ่มไปยังผู้เล่นตามเส้นทางที่มีศักยภาพ Minecraft ส่งกลุ่มที่ตอบสนองลูกค้าอย่างกระตือรือร้น

โดยทั่วไปแล้วฉันชอบแนวคิดนี้ แต่ภายในคุณจะทำแผนที่ส่วนต่าง ๆ ของโลกได้อย่างไร
Clashsoft

ในขณะที่อาเรย์จะเป็นทางออกที่ดีที่สุดสำหรับไทล์ในเซกเมนต์และเซกเมนต์ในเซกเตอร์เซกเตอร์ในโลกจะต้องการอะไรที่แตกต่างกันเพื่อให้มีขนาดแผนที่ที่ไม่มีที่สิ้นสุด ข้อเสนอแนะของฉันคือการใช้ตารางแฮช (หลอกพจนานุกรม <Vector2i, เซกเตอร์>) โดยใช้พิกัด XY สำหรับแฮช จากนั้นโลกก็สามารถค้นหาภาคที่พิกัดที่กำหนด
Josh Brown

6

Minecraft นั้นค่อนข้างเร็วแม้ใน 2-core ของฉัน Java ดูเหมือนจะเป็นปัจจัย จำกัด ที่นี่แม้ว่าจะมีความล่าช้าเล็กน้อยของเซิร์ฟเวอร์ เกมในพื้นที่ดูเหมือนจะทำได้ดีกว่าดังนั้นฉันจะถือว่าความไร้ประสิทธิภาพอยู่ที่นั่น

สำหรับคำถามของคุณ Notch (ผู้เขียน Minecraft) ได้บล็อกเกี่ยวกับเทคโนโลยีมาระยะหนึ่งแล้ว โดยเฉพาะอย่างยิ่งโลกจะถูกเก็บไว้ใน "ชิ้น" (บางครั้งคุณจะเห็นสิ่งเหล่านี้โดยเฉพาะอย่างยิ่งเมื่อมีใครหายไปในขณะที่โลกยังไม่ได้กรอก) ดังนั้นการเพิ่มประสิทธิภาพครั้งแรกคือการตัดสินใจว่าชิ้นสามารถมองเห็นหรือไม่ .

ภายในกลุ่มก้อนตามที่คุณคาดเดาได้แอพจะต้องตัดสินใจว่าสามารถมองเห็นบล็อกได้หรือไม่ขึ้นอยู่กับว่าบล็อกอื่นถูกบล็อกหรือไม่

หมายเหตุด้วยเช่นกันว่ามี Block FACES ซึ่งไม่สามารถมองเห็นได้โดยอาศัยการถูกบดบัง (เช่นบล็อกอื่นปิดบังใบหน้า) หรือทิศทางที่กล้องชี้ (หากกล้องหันไปทางทิศเหนือคุณสามารถ เห็นใบหน้าทิศเหนือของบล็อกใด ๆ !)

เทคนิคทั่วไปยังรวมถึงการไม่แยกวัตถุบล็อก แต่เป็น "กลุ่ม" ของประเภทบล็อกด้วยบล็อกต้นแบบเดียวสำหรับแต่ละบล็อกพร้อมกับชุดข้อมูลขั้นต่ำเพื่ออธิบายว่าบล็อกนี้อาจกำหนดเองได้อย่างไร ตัวอย่างเช่นไม่มีบล็อกหินแกรนิตแบบกำหนดเองใด ๆ (ที่ฉันรู้) แต่น้ำมีข้อมูลที่จะบอกว่ามันอยู่ลึกเท่าไรในแต่ละด้านใบหน้าซึ่งสามารถคำนวณทิศทางการไหลได้

คำถามของคุณไม่ชัดเจนหากคุณต้องการเพิ่มความเร็วเรนเดอร์ขนาดข้อมูลหรืออะไร การชี้แจงจะมีประโยชน์


4
"กระจุก" มักจะมีชื่อชิ้น
Marco

จับดี (+1); อัปเดตคำตอบแล้ว (เดิมทำเพื่อความทรงจำและลืมคำพูดที่ถูกต้อง)
Olie

ความไร้ประสิทธิภาพที่คุณอ้างถึงนั้นเป็นที่รู้จักกันในนาม "เครือข่าย" ซึ่งไม่เคยทำแบบเดียวกันสองครั้งแม้ว่าจะมีจุดปลายเดียวกันก็ตามที่สื่อสารกัน
Edwin Buck

4

นี่เป็นเพียงข้อมูลบางส่วนของข้อมูลทั่วไปและคำแนะนำซึ่งฉันสามารถให้ได้ในฐานะที่เป็นผู้ดัดแปลง Minecraft modder ที่มีประสบการณ์มากเกินไป (อย่างน้อยก็อาจให้คำแนะนำแก่คุณ)

เหตุผลที่ Minecraft นั้นช้ามีอะไรมากมายเกี่ยวกับการตัดสินใจในการออกแบบระดับต่ำที่น่าสงสัยตัวอย่างเช่นทุกครั้งที่มีการอ้างอิงบล็อกโดยการวางตำแหน่งเกมจะตรวจสอบพิกัดด้วยประมาณ 7 ถ้าข้อความเพื่อให้แน่ใจว่าไม่มีขอบเขต . นอกจากนี้ยังไม่มีวิธีที่จะคว้า 'chunk' (หน่วยบล็อกขนาด 16x16x256 เกมที่ทำงานด้วย) จากนั้นอ้างอิงบล็อกในนั้นโดยตรงเพื่อหลีกเลี่ยงการค้นหาแคชและ erm ปัญหาการตรวจสอบความโง่ (เช่นการอ้างอิงแต่ละบล็อกยังเกี่ยวข้องกับ การค้นหาชิ้นส่วนในสิ่งอื่น ๆ ) ใน mod ของฉันฉันได้สร้างวิธีการที่จะคว้าและเปลี่ยนอาร์เรย์ของบล็อกโดยตรงซึ่งเพิ่มการสร้างดันเจี้ยนขนาดใหญ่จากความล่าช้าอย่างไม่น่าเชื่อไปอย่างรวดเร็ว

แก้ไข: ลบการอ้างสิทธิ์ว่าการประกาศตัวแปรที่ขอบเขตที่ต่างกันส่งผลให้ได้รับประสิทธิภาพการทำงานซึ่งไม่ได้เกิดขึ้นจริง ฉันเชื่อว่าในขณะที่ฉันทำผลลัพธ์นี้กับสิ่งอื่นที่ฉันกำลังทดลอง (โดยเฉพาะการลบ casts ระหว่างคู่และลอยในรหัสที่เกี่ยวข้องกับการระเบิดโดยรวมเป็นสองเท่า ... เข้าใจได้ว่ามันมีผลกระทบมาก!)

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

ภาษาใดก็ตามที่คุณเลือกพยายามที่จะสนิทสนมกับการใช้งาน / รายละเอียดในระดับต่ำเพราะแม้แต่รายละเอียดเล็ก ๆ น้อย ๆ ในโครงการเช่นนี้อาจสร้างความแตกต่างได้ทั้งหมด (ตัวอย่างหนึ่งสำหรับฉันใน C ++ คือ "คอมไพเลอร์แบบอินไลน์ ตัวชี้? "ใช่มัน! สร้างความแตกต่างอย่างไม่น่าเชื่อในโครงการหนึ่งที่ฉันกำลังทำอยู่เพราะฉันมีโค้ดน้อยลงและมีข้อดีของการทำอินไลน์)

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

นอกจากนี้คำตอบของกาวินยังครอบคลุมถึงรายละเอียดบางอย่างที่ฉันไม่ต้องการย้ำ (และอีกมากมาย! เขามีความรู้ในเรื่องมากกว่าฉัน) และฉันเห็นด้วยกับเขาเป็นส่วนใหญ่ ฉันจะต้องทดสอบความคิดเห็นของเขาเกี่ยวกับโปรเซสเซอร์และขนาดที่สั้นลงฉันไม่เคยได้ยินมาก่อน - ฉันต้องการพิสูจน์ตัวเองว่ามันเป็นจริง!


2

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

สิ่งที่คุณทำกับข้อมูลนี้ขึ้นอยู่กับคุณแล้ว เพื่อประสิทธิภาพ GFX แล้วคุณสามารถใช้ตัดคลิปวัตถุที่ซ่อนวัตถุที่มีขนาดเล็กเกินไปที่จะมองเห็น ฯลฯ

หากคุณกำลังมองหาเทคนิคการแสดงผลกราฟิกฉันแน่ใจว่าคุณจะพบสิ่งต่าง ๆ บนอินเทอร์เน็ต


1

สิ่งที่ต้องพิจารณาคือรูปแบบการออกแบบFlyweight ฉันเชื่อว่าคำตอบส่วนใหญ่ที่นี่อ้างอิงถึงรูปแบบการออกแบบนี้ไม่ทางใดก็ทางหนึ่ง

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

แต่แม้กระทั่งสถานที่สามารถลดขนาดลงได้: ถ้าคุณรู้ว่าบล็อกของที่ดินเป็นประเภทเดียวทำไมคุณไม่เก็บขนาดของที่ดินนั้นเป็นบล็อกยักษ์หนึ่งบล็อกด้วยข้อมูลตำแหน่งที่ตั้งหนึ่งชุด?

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

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