ไลบรารีการกำหนดเฟรม เป็นส่วนหนึ่งของ Android Game Development Kit
ไลบรารีการกำหนดเฟรมของ Android หรือที่เรียกว่า Swappy เป็นส่วนหนึ่งของไลบรารี AGDK ซึ่งจะช่วยให้เกม OpenGL และ Vulkan แสดงผลได้อย่างราบรื่นและกำหนดอัตราการแสดงเฟรมได้อย่างถูกต้องบน Android เอกสารนี้กําหนดอัตราการแสดงเฟรม อธิบายสถานการณ์ที่จําเป็นต้องใช้อัตราการแสดงเฟรม และแสดงวิธีที่ไลบรารีจัดการสถานการณ์เหล่านี้ หากต้องการข้ามไปยังการใช้การกำหนดเฟรมในเกมโดยตรง โปรดดูขั้นตอนถัดไป
ฉากหลัง
การกำหนดเฟรมคือการซิงค์ตรรกะและลูปการแสดงผลของเกมกับระบบย่อยการแสดงผลของระบบปฏิบัติการและฮาร์ดแวร์การแสดงผลที่เกี่ยวข้อง ระบบย่อยของจอแสดงผล Android ได้รับการออกแบบมาเพื่อหลีกเลี่ยงข้อบกพร่องที่มองเห็นได้ (หรือที่เรียกว่าการฉีกขาด) ซึ่งอาจเกิดขึ้นเมื่อฮาร์ดแวร์ของจอแสดงผลเปลี่ยนไปใช้เฟรมใหม่ในระหว่างการอัปเดต ระบบย่อยของจอแสดงผลจะดำเนินการต่อไปนี้เพื่อหลีกเลี่ยงข้อบกพร่องเหล่านี้
- บัฟเฟอร์เฟรมที่ผ่านมาภายใน
- ตรวจหาการส่งเฟรมที่ล่าช้า
- แสดงเฟรมที่ผ่านมาซ้ำเมื่อตรวจพบเฟรมที่ล่าช้า
เกมจะแจ้งให้ SurfaceFlinger ซึ่งเป็นคอมโพสิตอร์ภายในระบบย่อยของจอแสดงผลทราบว่าได้ส่งการเรียกใช้การวาดที่จำเป็นทั้งหมดสำหรับเฟรมแล้ว (โดยการเรียก eglSwapBuffers
หรือ vkQueuePresentKHR
) SurfaceFlinger จะส่งสัญญาณความพร้อมของเฟรมไปยังฮาร์ดแวร์ของจอแสดงผลโดยใช้ตัวล็อก จากนั้นฮาร์ดแวร์แสดงผลจะแสดงเฟรมที่ระบุ ฮาร์ดแวร์ของจอแสดงผลจะทำงานด้วยอัตราคงที่ เช่น 60 Hz และหากไม่มีเฟรมใหม่เมื่อฮาร์ดแวร์ต้องการ ฮาร์ดแวร์จะแสดงเฟรมก่อนหน้าอีกครั้ง
เวลาที่เฟรมไม่สอดคล้องกันมักเกิดขึ้นเมื่อลูปการแสดงผลของเกมแสดงผลในอัตราที่แตกต่างจากฮาร์ดแวร์การแสดงผลเดิม หากเกมที่ทำงานด้วยอัตราเฟรม 30 FPS พยายามแสดงผลในอุปกรณ์ที่รองรับ 60 FPS โดยค่าเริ่มต้น ลูปการแสดงผลของเกมจะไม่ทราบว่าเฟรมที่ซ้ำกันยังคงอยู่บนหน้าจออีก 16 มิลลิวินาที โดยทั่วไปแล้ว การตัดการเชื่อมต่อนี้จะทำให้เฟรมมีเวลาไม่สอดคล้องกันอย่างมาก เช่น 49 มิลลิวินาที, 16 มิลลิวินาที, 33 มิลลิวินาที ฉากที่ซับซ้อนเกินไปจะยิ่งทำให้ปัญหานี้รุนแรงขึ้น เนื่องจากจะทำให้เฟรมบางส่วนหายไป
โซลูชันที่ไม่เหมาะสม
เกมต่างๆ เคยใช้โซลูชันต่อไปนี้ในการกำหนดเฟรมเรตในอดีต ซึ่งมักจะส่งผลให้เฟรมแสดงผลไม่สม่ำเสมอและเวลาในการตอบสนองของอินพุตเพิ่มขึ้น
ส่งเฟรมให้เร็วที่สุดเท่าที่ API การเรนเดอร์อนุญาต
วิธีนี้เชื่อมโยงเกมกับกิจกรรม SurfaceFlinger แบบผันแปรและเพิ่มเวลาในการตอบสนองของเฟรม ไปป์ไลน์การแสดงผลมีคิวเฟรม ซึ่งโดยทั่วไปจะมีขนาด 2 ซึ่งจะเต็มหากเกมพยายามแสดงเฟรมเร็วเกินไป เมื่อไม่มีพื้นที่ในคิวแล้ว การเรียก OpenGL หรือ Vulkan จะบล็อก Game Loop (หรืออย่างน้อยก็เธรดการแสดงผล) จากนั้นเกมจะจำเป็นต้องรอให้ฮาร์ดแวร์แสดงผลแสดงเฟรม และแรงดันย้อนกลับนี้จะซิงค์คอมโพเนนต์ 2 รายการเข้าด้วยกัน สถานการณ์นี้เรียกว่าการอัดบัฟเฟอร์หรือการอัดคิว กระบวนการแสดงผลไม่ทราบว่าเกิดอะไรขึ้น อัตราเฟรมจึงมีความไม่สอดคล้องกันมากขึ้น หากเกมสุ่มตัวอย่างอินพุตก่อนเฟรม เวลาในการตอบสนองของอินพุตจะแย่ลง
ใช้ Android Choreographer เพียงอย่างเดียว
เกมยังใช้ Android Choreographer เพื่อการซิงค์ด้วย คอมโพเนนต์นี้พร้อมใช้งานใน Java ตั้งแต่ API 16 และ C++ ตั้งแต่ API 24 โดยจะส่งการนับเวลาเป็นวินาทีตามปกติด้วยความถี่เดียวกับระบบย่อยของจอแสดงผล ยังมีรายละเอียดปลีกย่อยเกี่ยวกับเวลาที่ส่งการนับนี้สัมพันธ์กับ VSYNC ของฮาร์ดแวร์จริง และระยะห่างเหล่านี้จะแตกต่างกันไปตามอุปกรณ์ เฟรมที่ยาวอาจยังเกิดการบัฟเฟอร์
ข้อดีของไลบรารีการกำหนดเฟรม
ไลบรารีการกำหนดเฟรมใช้ Android Choreographer ในการซิงค์และจัดการกับความแปรปรวนในการส่งข้อมูลการนับเวลาให้คุณ โดยจะใช้การประทับเวลาของการแสดงเพื่อตรวจสอบว่าเฟรมแสดงในเวลาที่เหมาะสมและซิงค์รั้วเพื่อหลีกเลี่ยงการบีบอัดบัฟเฟอร์ ไลบรารีจะใช้ NDK Choreographer หากมี และจะใช้ Java Choreographer แทนหากไม่มี
ไลบรารีจะจัดการอัตราการรีเฟรชหลายรายการหากอุปกรณ์รองรับ ซึ่งจะช่วยให้เกมมีความยืดหยุ่นมากขึ้นในการแสดงเฟรม เช่น สำหรับอุปกรณ์ที่รองรับอัตราการรีเฟรช 60 Hz และ 90 Hz เกมที่ไม่สามารถแสดงผล 60 เฟรมต่อวินาทีจะลดเป็น 45 FPS แทน 30 FPS เพื่อให้ราบรื่น ไลบรารีจะตรวจหาอัตราเฟรมที่คาดไว้ของเกมและปรับเวลาแสดงเฟรมโดยอัตโนมัติ นอกจากนี้ คลังการจัดการเฟรมยังช่วยยืดอายุการใช้งานแบตเตอรี่ด้วยเนื่องจากจะหลีกเลี่ยงการอัปเดตการแสดงผลที่ไม่จำเป็น เช่น หากเกมแสดงผลที่ 60 FPS แต่จอแสดงผลอัปเดตที่ 120 Hz หน้าจอจะอัปเดต 2 ครั้งสำหรับทุกเฟรม ไลบรารีการกำหนดอัตราการแสดงเฟรมจะหลีกเลี่ยงปัญหานี้โดยการตั้งค่าอัตราการรีเฟรชเป็นค่าที่อุปกรณ์รองรับซึ่งใกล้เคียงกับอัตราเฟรมเป้าหมายมากที่สุด
วิธีการทำงาน
ส่วนต่อไปนี้จะแสดงวิธีที่คลังการจัดเฟรมทำงานกับเฟรมเกมที่ยาวและสั้นเพื่อให้ได้การจัดเฟรมที่ถูกต้อง
อัตราเฟรมที่ถูกต้องที่ 30 Hz
เมื่อแสดงผลที่ 30 Hz ในอุปกรณ์ 60 Hz สถานการณ์ในอุดมคติบน Android จะแสดงในรูปที่ 1 SurfaceFlinger จะล็อกบัฟเฟอร์กราฟิกใหม่ หากมี (หมายเหตุในแผนภาพระบุว่า "ไม่มีบัฟเฟอร์" และแสดงบัฟเฟอร์ก่อนหน้าซ้ำ)
รูปที่ 1 การกำหนดเฟรมที่เหมาะที่สุดที่ 30 Hz ในอุปกรณ์ 60 Hz
เฟรมเกมสั้นทำให้กระตุก
ในอุปกรณ์สมัยใหม่ส่วนใหญ่ เครื่องมือสร้างเกมจะอาศัยผู้จัดระเบียบแพลตฟอร์มในการส่งการนับเวลาเพื่อขับเคลื่อนการส่งเฟรม อย่างไรก็ตาม ยังคงมีความเป็นไปได้ที่อัตราเฟรมจะไม่ดีเนื่องจากเฟรมสั้นดังที่เห็นในรูปที่ 2 ผู้เล่นจะรู้สึกว่าเฟรมสั้นๆ ตามด้วยเฟรมยาวๆ เป็นวิดีโอที่กระตุก
รูปที่ 2 เฟรมการแข่งขัน C แบบสั้นทำให้เฟรม B แสดงเพียงเฟรมเดียว ตามด้วยเฟรม C หลายเฟรม
ไลบรารีการกำหนดเฟรมแก้ไขปัญหานี้โดยใช้การประทับเวลาของงานนำเสนอ ไลบรารีใช้ส่วนขยายการประทับเวลาของการแสดง EGL_ANDROID_presentation_time
และ VK_GOOGLE_display_timing
เพื่อไม่ให้แสดงเฟรมก่อนเวลาอันควร ดังที่เห็นในรูปที่ 3
รูปที่ 3 เฟรมการแข่งขัน ข. ที่แสดง 2 ครั้งเพื่อให้การแสดงผลราบรื่นขึ้น
เฟรมที่ยาวจะทําให้วิดีโอกระตุกและเกิดความล่าช้า
เมื่อปริมาณงานของการแสดงผลใช้เวลานานกว่าปริมาณงานของแอปพลิเคชัน ระบบจะเพิ่มเฟรมพิเศษลงในคิว ซึ่งทำให้วิดีโอกระตุกอีกครั้งและอาจทำให้เกิดเวลาในการตอบสนองของเฟรมเพิ่มขึ้นเนื่องจากการเพิ่มข้อมูลลงในบัฟเฟอร์ (ดูรูปที่ 4) ไลบรารีนี้จะช่วยขจัดทั้งการกระตุกและเฟรมเพิ่มเติมของเวลาในการตอบสนอง
รูปที่ 4 เฟรม B แบบยาวให้การกำหนดเวลาที่ไม่ถูกต้องสำหรับ 2 เฟรม ได้แก่ A และ B
ไลบรารีแก้ปัญหานี้โดยใช้ Sync Fence (EGL_KHR_fence_sync
และ VkFence
) เพื่อแทรกการรอลงในแอปพลิเคชัน ซึ่งช่วยให้ไปป์ไลน์การแสดงผลทันแทนที่จะปล่อยให้มีแรงดันย้อนกลับเพิ่มขึ้น เฟรม ก. ยังคงแสดงเฟรมเพิ่มเติม แต่เฟรม ข. แสดงอย่างถูกต้องแล้วดังที่เห็นในรูปที่ 5
รูปที่ 5 เฟรม C และ D รอแสดง
โหมดการทำงานที่รองรับ
คุณสามารถกําหนดค่าคลังการเว้นเฟรมให้ทํางานในโหมดใดโหมดหนึ่งต่อไปนี้
- โหมดอัตโนมัติปิดอยู่ + ไปป์ไลน์
- โหมดอัตโนมัติเปิดอยู่ + ไปป์ไลน์
- โหมดอัตโนมัติเปิดอยู่ + โหมดไปป์ไลน์อัตโนมัติ (ไปป์ไลน์/ไม่ใช่ไปป์ไลน์)
โหมดที่แนะนำ
คุณสามารถทดสอบโหมดอัตโนมัติและโหมดไปป์ไลน์ได้ แต่ให้เริ่มต้นด้วยการปิดโหมดดังกล่าวและใส่ข้อมูลต่อไปนี้หลังจากเริ่มต้น Swappy
swappyAutoSwapInterval(false);
swappyAutoPipelineMode(false);
swappyEnableStats(false);
swappySwapIntervalNS(1000000000L/yourPreferredFrameRateInHz);
โหมดไปป์ไลน์
โดยปกติแล้วคลังจะใช้รูปแบบไปป์ไลน์เพื่อประสานงานกับภาระงานของเครื่องยนต์ ซึ่งจะแยกภาระงานของ CPU และ GPU ออกจากกันตามขอบเขต VSYNC
รูปที่ 6 โหมดไปป์ไลน์
โหมดที่ไม่ใช่ไปป์ไลน์
โดยทั่วไปแล้ว แนวทางนี้จะส่งผลให้เวลาในการตอบสนองของหน้าจออินพุตลดลงและคาดการณ์ได้มากขึ้น ในกรณีที่เกมมีเวลาที่ใช้ในการแสดงผลเฟรมต่ำมาก ปริมาณงานของทั้ง CPU และ GPU อาจพอดีกับช่วงเวลาการแลกเปลี่ยนข้อมูลเพียงครั้งเดียว ในกรณีนี้ แนวทางแบบไม่ใช้ไปป์ไลน์จะให้เวลาในการตอบสนองของหน้าจออินพุตต่ำกว่า
รูปที่ 7 โหมดที่ไม่ใช่ไปป์ไลน์
โหมดอัตโนมัติ
เกมส่วนใหญ่ไม่ทราบวิธีเลือกช่วงเวลาสลับ ซึ่งก็คือระยะเวลาที่แสดงแต่ละเฟรม (เช่น 33.3 มิลลิวินาทีสำหรับ 30 Hz) ในอุปกรณ์บางรุ่น เกมอาจแสดงผลที่ 60 FPS ได้ ในขณะที่อุปกรณ์อีกรุ่นอาจต้องลดค่าให้ต่ำลง โหมดอัตโนมัติจะวัดเวลาของ CPU และ GPU เพื่อดำเนินการต่อไปนี้
- เลือกช่วงเวลาสลับโดยอัตโนมัติ: เกมที่แสดงผล 30 Hz ในบางฉากและ 60 Hz ในบางฉากจะช่วยให้คลังปรับช่วงเวลานี้แบบไดนามิกได้
- ปิดใช้งานไปป์ไลน์สำหรับเฟรมที่เร็วเป็นพิเศษ: มอบเวลาในการตอบสนองของหน้าจอการป้อนข้อมูลที่ดีที่สุดในทุกกรณี
อัตราการรีเฟรชหลายรายการ
อุปกรณ์ที่รองรับอัตราการรีเฟรชหลายระดับมีความยืดหยุ่นมากขึ้นในการเลือกช่วงเวลาการแลกเปลี่ยนที่ดูราบรื่น
- ในอุปกรณ์ 60 Hz: 60 FPS / 30 FPS / 20 FPS
- ในอุปกรณ์ 60 Hz + 90 Hz: 90 FPS / 60 FPS / 45 FPS / 30 FPS
- ในอุปกรณ์ 60 Hz + 90 Hz + 120 Hz: 120 FPS / 90 FPS / 60 FPS / 45 FPS / 40 FPS / 30 FPS
คลังภาพจะเลือกอัตราการรีเฟรชที่ตรงกับระยะเวลาการแสดงผลจริงของเฟรมเกมมากที่สุด ซึ่งจะช่วยให้ได้รับประสบการณ์การรับชมที่ดีขึ้น
ดูข้อมูลเพิ่มเติมเกี่ยวกับการกำหนดเฟรมอัตราการรีเฟรชหลายรายการได้ที่บล็อกโพสต์การแสดงผลที่อัตรารีเฟรชสูงใน Android
สถิติเฟรม
ไลบรารี Frame Pacing มีสถิติต่อไปนี้สําหรับการแก้ไขข้อบกพร่องและการสร้างโปรไฟล์
- ฮิสโตแกรมของจำนวนการรีเฟรชหน้าจอที่เฟรมรออยู่ในคิวคอมโพสิตหลังจากการเรนเดอร์เสร็จสมบูรณ์
- ฮิสโตแกรมของจํานวนการรีเฟรชหน้าจอที่ผ่านไประหว่างเวลาที่ขอนำเสนอกับเวลาปัจจุบันจริง
- ฮิสโตแกรมของจํานวนการรีเฟรชหน้าจอที่ผ่านไประหว่างเฟรม 2 เฟรมติดต่อกัน
- ฮิสโตแกรมของจำนวนการรีเฟรชหน้าจอที่เกิดขึ้นระหว่างที่ CPU เริ่มทํางานสําหรับเฟรมนี้กับเวลาปัจจุบันจริง
ขั้นตอนถัดไป
ดูคู่มือต่อไปนี้เพื่อผสานรวมไลบรารีการกำหนดเฟรมของ Android เข้ากับเกม
- ผสานรวมการกำหนดเฟรมของ Android เข้ากับโปรแกรมแสดงผล OpenGL
- ผสานรวมการกำหนดเฟรมของ Android เข้ากับโปรแกรมแสดงผล Vulkan
แหล่งข้อมูลเพิ่มเติม
- Mir 2 ปรับปรุงประสิทธิภาพการแสดงผลโดยใช้ Swappy ซึ่งช่วยลดอัตราเซสชันที่ช้าจาก 40% เหลือ 10%