ไลบรารีการกำหนดเฟรม   เป็นส่วนหนึ่งของ 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 จะล็อกบัฟเฟอร์กราฟิกใหม่ หากมี (หมายเหตุในแผนภาพระบุว่า "ไม่มีบัฟเฟอร์" และแสดงบัฟเฟอร์ก่อนหน้าซ้ำ)

การกำหนดเฟรมที่เหมาะสมที่ 30 Hz ในอุปกรณ์ 60 Hz

รูปที่ 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 เข้ากับเกม

แหล่งข้อมูลเพิ่มเติม