Материалът е на техническия директор на Croteam Алън Ладавак (Alen Ladavac), участвал в разработването на игрите Serious Sam и Talos Principle, който обяснява как е успял да намери причината за забавянето на графиката дори и при най-мощните компютърни системи. Първата част излезе в края на месец януари.

Какви са технологиите днес

С течение на времето се появиха по-сложни графични процесори, които ставаха все по-асинхронни. Това означава, че когато централния процесор подаде на графичния чип команда да рендира нещо на екрана, GPU просто записва тази команда в своя буфер, за да може CPU да върши друга работа, докато GPU извършва рендирането. Всичко това доведе до ситуация, че когато централния процесор съобщи на графичния, че има „край на кадъра“, GPU просто поставя и тази команда в своя буфер във вид на поредния фрагмент от данни. Той не се отнася към тази команда по някакъв специален начин. И как би могъл, след като все още обработва част от получените по-рано данни и команди. Той ще покаже на екрана кадъра, едва когато приключи цялата работа, зададена по-рано.

Ето защо, когато играта се опитва да изчисли таймингите чрез изваждане на времената на началото на два кадъра, информацията която получава, не е актуална. Нека се върнем към нашия пример с краткото видео от първата част. Това са кадрите, в които камерата се движи покрай дърветата:

Нека да си припомним информацията за таймингите и движението на обектите. При първите два кадъра таймингът на кадъра е 16,64 милисекунди (тоест, 1/60 от секундата), камерата се движи с еднаква скорост и дърветата са синхронизирани. При третия кадър (долу) играта е забелязала, че времето на кадъра е 24,8 милисекунди (което е повече от 1/60 от секундата), алгоритъмът е счел, че честотата на кадрите е намляла и е побързал да премести камерата малко по-далече… само за да види, че таймингът на четвъртия кадър е едва 10,7 милисекунди, след което камерата веднага започва да се движи по-бавно, а дърветата отново долу-горе са синхронни. (Синхронизацията напълно се възстановява след още два кадъра).

Имаме следната ситуация: играта измерва това, което тя счита за сигнал за началото на кадър, но тези тайминги по различни причини се променят, особено в една силно натоварена многозадачна система, какъвто е персоналния компютър. Ето защо, в някои моменти игровия алгоритъм пресмята, че не може да осигури 60 fps и започва да генерира кадри от анимацията, разчетени за по-малка честота на кадрите. Но поради асинхронния начин на работа на графичния процесор, той всъщност успява да осигури 60 fps, но само за всеки отделен кадър от тази поредица кадри.

Именно това ние възприемаме като забавяне – анимацията, генерирана за променящо се опресняване на кадрите (кардиограмата) всъщност се изобразява с правилната средна постоянна честота на кадрите.

Тоест, не би трябвало да има проблем – всичко наистина работи плавно, само че алгоритъмът на играта не може да разбере това.

Да се върнем към началото на материала. Най-после открихме причината на проблема (всъщност, това май е само илюзия, че има някакъв проблем, нали?). Ето какво ще направим за да проверим всичко това:

Във видеото по-горе първо виждаме ефекта на кардиограмата, а след това използваме малък трик, за да се избавим от нея.

В първата част на клипа още от самото начало се вижда проблема с кардиограмата. След това променяме една „магическа“ опция и всичко става идеално плавно.

Каква е тази вълшебна опция? В Serious Engine ние я наричаме sim_fSyncRate=60. Казано по друг начин, това означава цялостно прекратяване на измерването на таймингите и приемаме, че винаги имаме стабилни 60 fps. И благодарение на това всичко тръгна плавно. Възникващите грешки, които дразнят окото се дължат на грешните тайминги в анимациите.

Това ли е всичко? Достатъчно е да оставим играта в това състояние и всичко е готово?

Нима решението е толкова лесно?

За съжалени, не. Това е само тест за разработчиците. Ако престанем да измерваме честотата на опресняване на кадрите и предположим, че тя е винаги 60 Hz, то когато тя наистина падне под 60 fps, всичко ще се размаже. В потребителските персонални компютри това може да стане по редица причини – операционната система ще стартира някой фонов процес, ще се включи функцията за икономия на енергия, ще се задейства защитата на процесора от прегряване и т.н.

Тоест, когато програмно променяме честотата на кадрите, имаме дразнещ очите ефект, а ако не ги променяме, в един момент цялата игра започва сериозно да бави и средната честота на опресняването пада. Какво да направим?

Радикалното решение е да не се измерва времето на началото или края на кадъра, а времето на показване на изображението на екрана.

Но как да съобщим на играта, че изображението вече е на екрана. Можете да се учудите, но към днешен ден няма нито един метод, който да позволи това.

Неприятна изненада. Очакваше се това да е една от базовите функции на всеки графичен API. Но се оказа, че при непрекъснатото подобряване на системния софтуер е изпуснат основния проблем. Графичните API са еволюционирали във всички аспекти освен в един: приложението по никакъв начин не може да разбере, че кадърът вече е на екрана. Можем да разберем кога ще приключи неговото рендиране, но не и кога е показан.

Какво следва?

Ситуацията все пак не е чак толкова мрачна. Редица специалисти в графичната екосистема активно работят върху реализацията на правилния тайминг на кадрите за различните API. За Vulkan API вече излезе разширението VK_GOOGLE_display_timing, което много добре демонстрира своята полезност в първите представени геймърски проекти. Само че тази функция я има основно за Android и Linux.

We've been helping @Croteam with their quest to resolve frame pacing and stuttering problems that have been affecting all gaming platforms for a long time; the Linux graphics stack lets us create solutions. More details next week in @AlenL's GDC talk: https://t.co/04TU6bB2yU pic.twitter.com/sI0q4sGDv5

— Pierre-Loup Griffais (@Plagman2) March 15, 2018

Вече се работи върху създаването на аналогични по-качествени системи за всички основни графични API. Но не може да се каже кога точно тази работа ще приключи, понеже проблемът е заложен твърде дълбоко в структурата на различните операционни системи.

Нашата компания Croteam също много активно работи върху решаването на този проблем, като имаме подкрепата на редица видни специалисти и експерти.

Ние се стремим да представим едно лесно и достъпно решение, и когато това стане, ще представим ъпдейт за The Talos Principle, който реализира въпросната функция.

Други подробности

Композитният мениджър (Compositing window manager)

Композитният мениджър вече е вграден във всяка една операционна система. Именно той дава възможност за показване на прозрачни и матови програмни прозорци, размит фон, сенки, изскачащи прозорци и т.н. Някои композитни мениджъри дори могат да показват програмните прозорци в 3D вид. За да постигне това, мениджърът прихваща управлението върху последния етап в създаването на кадъра и решава какво да прави с него, преди то да стигне екрана на монитора. Всичко това неимоверно усложнява задачата за правилните тайминги.

В някои операционни системи компзитният мениджър може да бъде изключен. Но това не винаги е възможно, а и от друга страна, как да забраним на игрите да се стартират в прозоречен режим?

Управлението на захранването и отделяната топлина срещу сложността на рендирането

Няма как да не се съобразим с фалта, че всички съвременни централни и графични процесори не работят с фиксирана честота и я променят в зависимост от натоварването и температурата на кристала. Ето защо, в алгоритмите на игрите няма как да бъде заложено, че CPU и GPU ще работят с еднаква скорост при всеки кадър. От друга страна, и драйверите за хардуера няма как да очакват, че при всеки кадър в дадена игра, ще се извършва еднакъв по обем работа. За да можем да се съобразим с всичко това са необходими сложни софтуерни решения, чрез които тези две страни могат да си взаимодействат.

А не можем ли просто…

По-скоро не :). Към днешен ден измерването на времената на графичния процесор се счита за алтернатива на таймингите на дисплея. Но в този случай няма как да се отчете наличието на композитен мениджър и фактът, че нито един от таймингите при рендирането от страна на GPU всъщност не е директно синхронизиран с кадровата честота на дисплея. За една идеална анимация е необходимо съвсем точно да се знае времето, през което изображението се показва на екрана, а не кога е приключило рендирането.

The Elusive Frame Timing