Frame Pacing Library بخشی از کیت توسعه بازی اندروید .
کتابخانه Android Frame Pacing که با نام Swappy نیز شناخته میشود، بخشی از کتابخانههای AGDK است. این به بازیهای OpenGL و Vulkan کمک میکند تا به رندرینگ روان و تنظیم فریم صحیح در اندروید دست یابند. این سند سرعت قاب را تعریف میکند، موقعیتهایی را که نیاز به سرعت قاب است توصیف میکند، و نشان میدهد که چگونه کتابخانه به این موقعیتها رسیدگی میکند. اگر میخواهید مستقیماً به اجرای تنظیم فریم در بازی خود بپرید، مرحله بعدی را ببینید.
پس زمینه
فریم ریتم همگام سازی منطق و حلقه رندر بازی با زیرسیستم نمایش سیستم عامل و سخت افزار نمایشگر زیرین است. زیرسیستم نمایشگر اندروید برای جلوگیری از مصنوعات بصری (معروف به پاره شدن ) طراحی شده است که ممکن است هنگام تغییر بخش سخت افزار نمایشگر به یک قاب جدید از طریق یک به روز رسانی رخ دهد. برای جلوگیری از این مصنوعات، زیرسیستم نمایشگر موارد زیر را انجام می دهد:
- فریم های گذشته را به صورت داخلی بافر می کند
- ارسال فریم های دیرهنگام را تشخیص می دهد
- نمایش فریم های گذشته را در صورت شناسایی فریم های دیرهنگام تکرار می کند
یک بازی به SurfaceFlinger ، سازنده در زیرسیستم نمایش، اطلاع می دهد که تمام فراخوان های قرعه کشی مورد نیاز برای یک فریم را ارسال کرده است (با فراخوانی eglSwapBuffers
یا vkQueuePresentKHR
). SurfaceFlinger در دسترس بودن یک فریم را به سخت افزار نمایشگر با استفاده از یک قفل سیگنال می دهد. سپس سخت افزار نمایشگر قاب داده شده را نشان می دهد. سخت افزار نمایشگر با یک نرخ ثابت، مثلاً 60 هرتز، تیک می زند، و اگر زمانی که سخت افزار به فریم جدیدی نیاز دارد، فریم جدیدی وجود نداشته باشد، سخت افزار دوباره فریم قبلی را نمایش می دهد.
زمانهای فریم متناقض اغلب زمانی اتفاق میافتد که یک حلقه رندر بازی با نرخ متفاوتی نسبت به سختافزار صفحهنمایش اصلی رندر میشود. اگر یک بازی در حال اجرا با سرعت 30 فریم در ثانیه، سعی کند روی دستگاهی رندر کند که به طور بومی از 60 فریم در ثانیه پشتیبانی می کند، حلقه رندر بازی متوجه نمی شود که یک فریم تکراری برای 16 میلی ثانیه بیشتر روی صفحه باقی می ماند. این قطع ارتباط معمولاً ناسازگاری قابل توجهی در زمانهای فریم ایجاد میکند، مانند: 49 میلیثانیه، 16 میلیثانیه، 33 میلیثانیه. صحنه های بیش از حد پیچیده این مشکل را تشدید می کنند، زیرا باعث ایجاد فریم های از دست رفته می شوند.
راه حل های غیر بهینه
راهحلهای زیر برای تنظیم فریم در گذشته توسط بازیها به کار گرفته شدهاند و معمولاً منجر به زمانهای فریم متناقض و افزایش تأخیر ورودی میشوند.
فریمها را به همان سرعتی که API رندر اجازه میدهد ارسال کنید
این رویکرد یک بازی را به فعالیت متغیر SurfaceFlinger گره میزند و یک فریم اضافی از تأخیر را معرفی میکند. خط لوله نمایش شامل یک صف از فریم ها است، معمولاً به اندازه 2، که اگر بازی بخواهد خیلی سریع فریم ها را ارائه دهد، پر می شود. بدون فضای بیشتری در صف، حلقه بازی (یا حداقل موضوع رندر) توسط یک تماس OpenGL یا Vulkan مسدود می شود. سپس بازی مجبور میشود منتظر بماند تا سختافزار نمایشگر یک فریم را نشان دهد و این فشار برگشتی این دو جزء را همگامسازی میکند. این وضعیت به عنوان buffer-stuffing یا queue-stuffing شناخته می شود. فرآیند رندر متوجه نمی شود که چه اتفاقی در حال رخ دادن است، بنابراین ناسازگاری نرخ فریم بدتر می شود. اگر بازی از ورودی قبل از فریم نمونه برداری کند، تأخیر ورودی بدتر می شود.
از Android Choreographer به تنهایی استفاده کنید
بازی ها همچنین از Android Choreographer برای همگام سازی استفاده می کنند. این مؤلفه که در جاوا از API 16 و در C++ از API 24 در دسترس است، تیک های منظم را با همان فرکانس زیرسیستم نمایش ارائه می دهد. هنوز نکات ظریفی در مورد زمان ارائه این تیک نسبت به VSYNC سخت افزار واقعی وجود دارد و این افست ها بسته به دستگاه متفاوت است. پر کردن بافر ممکن است همچنان برای فریم های بلند رخ دهد.
مزایای کتابخانه Frame Pacing
کتابخانه Frame Pacing از Android Choreographer برای همگامسازی استفاده میکند و با تنوع در تحویل تیک برای شما سروکار دارد. از مهرهای زمانی ارائه استفاده می کند تا مطمئن شود فریم ها در زمان مناسب ارائه می شوند و حصارها را همگام می کند تا از پر شدن بافر جلوگیری شود. کتابخانه در صورت موجود بودن از NDK Choreographer استفاده میکند و در صورت نبودن به رقصگر جاوا بازمیگردد.
اگر دستگاه از آن پشتیبانی کند، کتابخانه چندین نرخ بهروزرسانی را کنترل میکند، که به بازی انعطافپذیری بیشتری در ارائه فریم میدهد. به عنوان مثال، برای دستگاهی که از نرخ نوسازی 60 هرتز و همچنین 90 هرتز پشتیبانی می کند، بازی ای که نمی تواند 60 فریم در ثانیه تولید کند، می تواند به جای 30 فریم در ثانیه به 45 فریم در ثانیه کاهش یابد تا روان بماند. کتابخانه نرخ فریم مورد انتظار بازی را تشخیص میدهد و زمانهای ارائه فریم را بر اساس آن به صورت خودکار تنظیم میکند. کتابخانه Frame Pacing همچنین عمر باتری را بهبود میبخشد زیرا از بهروزرسانیهای غیرضروری نمایشگر جلوگیری میکند. به عنوان مثال، اگر یک بازی با سرعت 60 فریم در ثانیه رندر می شود اما صفحه نمایش با فرکانس 120 هرتز به روز می شود، صفحه نمایش برای هر فریم دو بار به روز می شود. کتابخانه Frame Pacing با تنظیم نرخ تازه سازی روی مقدار پشتیبانی شده توسط دستگاهی که نزدیکترین به نرخ فریم مورد نظر است، از این امر جلوگیری می کند.
چگونه کار می کند
بخشهای زیر نشان میدهند که چگونه کتابخانه Frame Pacing با فریمهای بلند و کوتاه بازی به منظور دستیابی به سرعت فریم صحیح برخورد میکند.
سرعت فریم صحیح در 30 هرتز
هنگام رندر کردن با فرکانس 30 هرتز در دستگاه 60 هرتز، وضعیت ایدهآل در Android در شکل 1 نشان داده شده است. SurfaceFlinger بافرهای گرافیکی جدیدی را در صورت وجود میبندد (NB در نمودار نشان میدهد "بدون بافر" موجود است و مورد قبلی تکرار میشود).
شکل 1. سرعت فریم ایده آل در 30 هرتز در دستگاه 60 هرتز
فریم های کوتاه بازی منجر به لکنت زبان می شود
در اکثر دستگاههای مدرن، موتورهای بازی به طراح رقص پلتفرم تکیه میکنند که تیکهایی را برای ارسال فریمها ارائه میدهد. با این حال، همچنان که در شکل 2 مشاهده می شود، به دلیل فریم های کوتاه، امکان سرعت ضعیف فریم وجود دارد. بازیکن فریم های کوتاه به دنبال فریم های بلند را به عنوان لکنت درک می کند.
شکل 2. فریم بازی کوتاه C باعث می شود که فریم B فقط یک فریم و به دنبال آن چندین فریم C ارائه دهد
کتابخانه Frame Pacing این مشکل را با استفاده از مهرهای زمانی ارائه حل می کند. کتابخانه از پسوندهای مهر زمان ارائه EGL_ANDROID_presentation_time
و VK_GOOGLE_display_timing
استفاده میکند تا فریمها زودتر ارائه نشوند، همانطور که در شکل 3 مشاهده میشود.
شکل 3. فریم بازی B دو بار برای نمایش روانتر ارائه شده است
فریم های بلند منجر به لکنت و تاخیر می شود
وقتی حجم کاری نمایش بیشتر از حجم کاری برنامه طول می کشد، فریم های اضافی به یک صف اضافه می شود. این یک بار دیگر منجر به لکنت زبان می شود و همچنین ممکن است به یک چارچوب اضافی از تاخیر به دلیل پر کردن بافر منجر شود (شکل 4 را ببینید). کتابخانه هم لکنت و هم چارچوب اضافی تأخیر را حذف می کند.
شکل 4. فریم بلند B برای 2 فریم - A و B، ضربان نادرستی می دهد
کتابخانه این مشکل را با استفاده از حصارهای همگامسازی ( EGL_KHR_fence_sync
و VkFence
) برای تزریق انتظارها به برنامهای که به خط لوله نمایشگر اجازه میدهد بهجای ایجاد فشار برگشتی پاسخ دهد، حل میکند. قاب A همچنان یک فریم اضافی را ارائه می دهد، اما فریم B اکنون به درستی نمایش داده می شود، همانطور که در شکل 5 مشاهده می شود.
شکل 5. فریم های C و D منتظر ارائه هستند
حالت های عملیاتی پشتیبانی شده
میتوانید کتابخانه Frame Pacing را برای عملکرد در یکی از سه حالت زیر پیکربندی کنید:
- حالت خودکار خاموش + خط لوله
- حالت خودکار روشن + خط لوله
- حالت خودکار روشن + حالت خط لوله خودکار (خط لوله/غیر خط لوله)
حالت پیشنهادی
میتوانید حالتهای خودکار و حالتهای خط لوله را آزمایش کنید، اما با خاموش کردن آنها و اضافه کردن موارد زیر پس از شروع Swappy شروع میکنید:
swappyAutoSwapInterval(false);
swappyAutoPipelineMode(false);
swappyEnableStats(false);
swappySwapIntervalNS(1000000000L/yourPreferredFrameRateInHz);
حالت خط لوله
برای هماهنگ کردن بارهای کاری موتور، کتابخانه معمولاً از یک مدل خط لوله استفاده می کند که بارهای کاری CPU و GPU را در سراسر مرزهای VSYNC جدا می کند.
شکل 6. حالت خط لوله
حالت غیر خط لوله
به طور کلی، این رویکرد منجر به تأخیر صفحه ورودی کمتر و قابل پیش بینی تر می شود. در مواردی که یک بازی زمان فریم بسیار پایینی دارد، هر دو بار کاری CPU و GPU ممکن است در یک بازه تعویض واحد قرار بگیرند. در این مورد، یک رویکرد غیر خط لوله در واقع تاخیر کمتری در صفحه ورودی ارائه میکند.
شکل 7. حالت غیر خط لوله
حالت خودکار
اکثر بازیها نمیدانند که چگونه فاصله تعویض را انتخاب کنند، یعنی مدت زمانی که هر فریم ارائه میشود (مثلاً 33.3 میلیثانیه برای 30 هرتز). در برخی از دستگاهها، یک بازی میتواند با سرعت 60 فریم در ثانیه رندر شود، در حالی که در دستگاههای دیگر ممکن است نیاز به کاهش به مقدار کمتری داشته باشد. حالت خودکار زمان های CPU و GPU را برای انجام کارهای زیر اندازه گیری می کند:
- انتخاب خودکار فواصل مبادله : بازی هایی که در برخی صحنه ها 30 هرتز و در برخی دیگر 60 هرتز ارائه می دهند، می توانند به کتابخانه اجازه دهند این فاصله را به صورت پویا تنظیم کند.
- خط لوله را برای فریم های فوق سریع غیرفعال کنید : تأخیر صفحه ورودی بهینه را در همه موارد ارائه می دهد.
نرخ تازه سازی چندگانه
دستگاههایی که از چندین نرخ بهروزرسانی پشتیبانی میکنند، انعطافپذیری بالاتری در انتخاب یک فاصله زمانی مبادله ارائه میکنند که صاف به نظر میرسد:
- در دستگاه های 60 هرتز : 60 FPS / 30 FPS / 20 FPS
- در دستگاه های 60 هرتز + 90 هرتز : 90 FPS / 60 FPS / 45 FPS / 30 FPS
- در دستگاه های 60 هرتز + 90 هرتز + 120 هرتز : 120 FPS / 90 FPS / 60 FPS / 45 FPS / 40 FPS / 30 FPS
کتابخانه نرخ تازهسازی را انتخاب میکند که به بهترین وجه با مدت زمان رندر واقعی فریمهای بازی مطابقت داشته باشد و تجربه بصری بهتری ارائه دهد.
برای اطلاعات بیشتر در مورد سرعتدهی فریمهای چندگانه، به رندر با نرخ تازهسازی بالا در پست وبلاگ اندروید مراجعه کنید.
آمار قاب
کتابخانه Frame Pacing آمار زیر را برای اهداف اشکال زدایی و پروفایل ارائه می دهد:
- یک هیستوگرام از تعداد صفحه نمایش، فریمی را که پس از تکمیل رندر در صف ترکیب منتظر مانده است، تازه می کند.
- یک هیستوگرام از تعداد رفرش های صفحه که بین زمان ارائه درخواستی و زمان فعلی واقعی انجام شده است.
- یک هیستوگرام از تعداد رفرش های صفحه که بین دو فریم متوالی ارسال می شود.
- یک هیستوگرام از تعداد رفرش های صفحه که بین شروع کار CPU برای این فریم و زمان فعلی واقعی انجام می شود.
مرحله بعدی
برای ادغام کتابخانه Android Frame Pacing در بازی خود به یکی از راهنماهای زیر مراجعه کنید:
- Android Frame Pacing را در رندر OpenGL خود ادغام کنید
- Android Frame Pacing را در رندر Vulkan خود ادغام کنید