Pull to refresh

Comments 276

А разве мониторы с поддержкой AMD FreeSync и Nvidia G-Sync не решают эту проблему?
И раз уж такая тема, то что выбрать?
Только что прочел несколько статей в интернете.
Есть ещё технология Nvidia Fast Sync. Для него не нужно специального монитора.
Так как нужна удвоенная часта кадров. Если монитор у вас 60 Герцовый, то FPS в игре должно быть не меньше 120.
Но это само собой работает только на мощных современных видеокартах и процессорах.
UFO just landed and posted this here
это не решает проблему шаттеринга потому что мы не теряем кадры и *синк ничего с этим делать не будет, но это возможно поможет если мы скажем игре что всегда рисуем только 60 в случаях когда кадры всё-таки просядут, но это не точно… возсожно мы получим задержку анимации, что не приемлемо.
Статья про другое)
AMD FreeSync и Nvidia G-Sync — это по сути аппаратный vsync
Разве? Где там вообще какой-либо sync? Отрисовали кадр — передали на дисплей, где там вообще синхронизация? С чем синхронизируемся?
Это я к тому, что мне лично что FreeSync, что G-Sync видятся вообще асинхронными (ну понятно в определенных границах). Нет там какой-то внешней синхронизации.
У меня FreeSync, и я всё равно вижу подлагивания тормоза на 60p видео.
Естественно, потому что это видео — весь v-sync сделан «до нас», фрисинк с ним уже ничего не может сделать — максимум переключить режимы монитора для точного воспроизведения 24/50/60 к/с
FreeSync и G-Sync нужны исключительно для избавления от «рваных кадров» (tearing) путём синхронизации частоты обновления монитора с частотой кадров рендеринга GPU. Данную проблему это никак не решает, потому как тут именно GPU выдает кадры с различными промежутками времени, хотя в среднем и получаются необходимые 60 кадров, но выглядит картинка плохо. Мониторы с FreeSync и G-Sync будут подстраиваться под эти «статтеры» и выдавать все тоже дерганное изображение, ни на что не влияя.
Ещё как влияя — vrr позволяет выводить кадры не привязываясь к конкретной частоте обновления монитора и избавляя от pull-up/pull-down преобразований к фиксированным 60гц (или 75/120, смотря какой монитор)
GPU выдает кадры с различными промежутками времени — и монитор показывает кадры с различными промежутками времени
Ещё как влияя...

GPU выдает кадры с различными промежутками времени — и монитор показывает кадры с различными промежутками времени

Эти два высказывания противоречивы. Второе верно, монитор перестает влиять на частоту обновления и отображает только готовые кадры с GPU, которые в данном случае имеют существенный разброс времени рендеринга и делают картинку дерганой.

Еще раз, переменная частота кадров монитора не решает никаких проблем с рендерингом, она только устраняет разрыв кадра путем синхронизации обновления с GPU.
Противоречия здесь нет.
Прыгающие фреймтаймы никуда не деваются, только вот без VRR их пытаются запихнуть в фиксированные для 60гц 16.7мс, что во многих случаях значительно усугубляет проблему.
Фрисинк не панацея и от статеров не избавит, только вот визуально такое дерганное изображение с фрисинком и без — две большие разницы.

А вот от просто проседающего фпс (не статтеров) — фрисинк действительно панацея. Зачем резать фпс до 30, когда система выдает относительно стабильные 45? А зачем резать до 60, если система выдаёт все сто?

Если я правильно понял суть проблемы, эти технологии не могут решить проблему.
Приложение (игра) не получают реальных данных о том, сколько рендерился кадр и сколько он отображается, чтобы оно могло точно просчитать следующий кадр.

Простое решение есть — собрать машину на порядок мощнее, чем требуется, поставить игровым процессам самый высокий приоритет, отрубить все энергосберегайки, отключить предсказания о которых говорится в статье, и вуаля.
По большому счету в древних аркадах поэтому проблем и не было — все процессорное время пользовалось игрой и процессор работал со стабильной частотой.
Да, получается дорого и неэкологично, но для игровых компьютеров, я полагаю, вполне решение. Хотя и выглядит страшненько :)

UFO just landed and posted this here

Иногда так и есть. Сталкивался с ситуацией, когда загруженный седан (атмосферник, 1.5) глох при попытках заехать в горку (крутую). Пришлось отключить кондиционер.

Передачу пониже пробовали включать? Такой же автомобиль, только с автоматом. При заезде на очень крутую горку в Адыгее при нажатой тапке в пол стояла вторая передача, километров 70 в час скорости и нулевое ускорение. Вся моща уходила на подъём ))
На второй глох, на первой можно наверное было заехать.

Не понял. Как это — мы не можем узнать, когда кадр отображён? Разве момент привязки — не первый же Vertical Retrace после завершения рендеринга?

Его надо знать до начала построения кадра, вот в чём загвоздка.

Это да. Но не вижу, как тут могут помочь какие-то API: не могу представить себе системный вызов, позволяющий заглянуть в будущее.


Интересно, кстати — насколько быстрее (в типовой игре) рендерится кадр более низкого разрешения? Можно ли рендерить в двух разрешениях, и если мы не успели отрендерить нужный кадр в высоком — отображать в низком? Или же (но тут придётся поизвращаться) отображать афинную трансформацию предыдущего кадра (просто привязав его по нескольким точкам к текущему — чтобы "вздрагивал" в этом случае не весь кадр, а только подвижные объекты)?

Есть пример github.com/KhronosGroup/Vulkan-Tools/blob/master/cube/cube.c
Проблема в том что swapBuffers может висеть довольно долго и реальное отображение будет неизвестно когда в этом промежутке, это не обязательно конец swap+glFinish и подобному. Вот это расширение позволяет узнать когда же оно реально было. В будущее никто не заглядывает, просто учитываешь это при генерации следующего кадра. Тут конечно еще может быть время пока кадр дойдет до монитора, но оно обычно уже постоянное и на «stutter» уже не влияет.
Технология динамического разрешения применяеться например в Path of Exile. На слабом железе работает не так хорошо как хотелось бы, а на мощном оно и не надо.
В VR шлемах так делается, на уровне драйвера. Если движок не успевает нарисовать кадр, используется предыдущий, с хитрой деформацией в соответствии с текущим положением шлема. У них в железе ещё и четкая синхронизация сделана, в окулусе кажется есть фотодатчик в углу дисплея, чтобы прям реальное время отображения измерять (photon time).
Разве нельзя померить время рендеринга кадра и если немного убегает вперед картинка, то немного позже ее вывести?
Как я понял — убегает вперёд не кадр (он показывается вовремя, 60 кадров в секунду), а картинка в ней. Т.е. это рендер игры просчитывает будущее для объектов и рисует на текущий стабильный кадр.
нет, тогда будет другая ещё большая проблема мучить — управление станет «неотзывчивым»
так проблема именно в том, что измерения неверно выполняются
А разве мы не можем просто…

Можно просто. Если замеряемый fps около 60 то использовать указанный костыль, если видим что fps реально не успевает — выключаем костыль, действуем как обычно. Это, конечно, не идеально, но поскольку на уровне API решений нет, пока так сойдет…
Будет накапливаемый рассинхрон. Особенно онлайн-игроки вам передадут пламенный привет.
При выключении костыля синхронизация восстанавливается, для этого и проверяется условие.
Сделать ключевые кадры для сброса, раз в минуту например.
Да, только не раз в минуту, а по условию что «убежало» больше чем на 1-2 кадра
И картинка будет дёргаться на каждом таком кадре. За что боролись?
Ну это будет только если фпс ниже 60-ти в следствии каких-то временных факторов, то-есть во все остальное время будет жесткое ограничение таймингов. Я так вижу, могу ошибаться.
Вот на счёт «я так вижу» кстати можно поспорить.
На замыленный взгляд разработчика (у которого в голове «да от… тесь от меня») оно может быть вот прям почти нормально. Но именно на взгляд.
Из опыта: берём два одинаковых компа или ноутбука, ставим рядом, делаем им идеальную сеть, запускаем отыгрывать один и тот же сценарий.
На взгляд они вполне могут показывать одно и то же, ну, почти.
Но если включить звук — всё сразу становится понятно. На слух даже 1/100 рассинхрона моментально чувствуется. За 1/10 хочется убить.
Но если включить звук — всё сразу становится понятно. На слух даже 1/100 рассинхрона моментально чувствуется. За 1/10 хочется убить.

Вы когда в комнате разговариваете, то у вашего голоса с отраженным от стен эхом рассинхрон 1/100, и ничего.

Я вот играю на пс подключенным к Аймаку в качестве монитора, так как второй монитор некуда ставить, а к аймаку подключен через USB 3.0 внешнюю карту захвата ( люблю Эппл). Так вот хотя эта карта и передает 1080p при 60 fps задержка таки присутствует 1/70 — 1/100 примерно. Больше всего её видно на рабочем столе при движении мышью. А вот в играх, даже динамических и «задержко-зависимых» типа PUBG или Warthunder она практически не ощущается. Парадокс.
А вот в играх, даже динамических и «задержко-зависимых» типа PUBG или Warthunder она практически не ощущается. Парадокс.

Да никакого парадокса, просто задержка там сильно больше, чем 1/100, вот и все.

Попробуйте сравнить задержку с steam in-home streaming/nvidia moonlight/amd relive
Мил человек, ты просто гений! Спасибо за наводку! Moonlight полностью устроил: 4К, 60фпс, низкая задержка. Теперь как белый человек играть буду =) Спасибо!
Не за что, рад был помочь и надеюсь никаких артефактов сжатия не будет.
Ну и на будущее про консоли добавлю — на PS4 есть remote play c аппаратным кодированием 720p 60fps, на Ps4Pro уже 1080p 60fps, без HDR. На Xbox One это тоже есть, а вот switch пока обделён — хотя это и неудивительно, свитч легче взять целиком. Вдруг пригодится
Почему? Это же примерно то же самое, что делает adaptive v-sync? Или нет?
Столкнулся с этой проблемой в Crossout.
Купил 1080б а оно тормозит. Процессор проставивает, GPU простаивает — а оно тормозит.
Решил проблему тем, что в настройках игры выставил принудительную частоту кадров 60, а в драйвере включил FastSync.
Сразу стало плавно и никаких рассинхронизаций.
Движок не дурак — если не успевает рендерить 60fps, то сам «пропускает кадры».
А нельзя использовать метод скользящей средней вместо измерений частоты на каждом цикле или полного игнорирования?
А чем это поможет? Представьте что у вас swapBuffers только из-за синхронизации выполняется от 0мс до 16мс (заранее неизвестно сколько, этого вполне достаточно для stutter) + работает еще неизвестно сколько, если перед этим не было glFinish + общая нагрузка меняется немного от кадра к кадру + вы не знаете когда реально при работе swapBuffers произошло отображение. Это может уменьшить эффект, но реальную проблему этим не решить. Надо знать когда именно было отображение в swapBuffers + надо иметь возможность сообщить лучшее время отображения следующего кадра (мы же исходя из всех расчетов передвинули куда-то геометрию, где предполагаем след кадр будет отображен, но драйвер об этом ничего не знает), вот теперь это можно сделать используя VkPresentTimesInfoGOOGLE.
Почему вы не используете glFinish? Один фиг рендер в отдельном потоке, так что подождать пока кадр отрендерится и потом только продолжать пихать в GPU — вполне норм. Тем более, что лока на заполнение буфферов всё равно в этот момент нет и можно подготавливать следующий кадр, пока ждем финиш.
Я потому про него и написал, glFinish + swap получаем теоретическую неопределенность только 0-16мс с синком. Но принципиальным это не является, мы ведь считаем за время отображения конец swap. Если он таковым не является по неизвестной нам причине, вот возникло у драйвера еще работы на 5мс после свопа из-за архитекрутных/драйверных особенностей, то привет. Хорошо если оно постоянное, а если переменное (например переодическое GC запустили) — беда. Вот в статье как раз описано: выброс до 24.8мс на самом деле фейковый, если его принять за 16.6 — то все плавно, то-есть время отображения было 16.6, а 8.2мс драйвер занимался неизвестно чем. Плюс стратегий компенсации может быть множество, если мы хотим что-то предсказать и компенсировать, указать желаемое время отображения опять же без расширения способа нету.
Похожая проблема есть в Unity. Если собрать простой проект, где движется куб то можно заметить как он немного дергается.
Решается похожим способом:
Application.targetFrameRate = 60

Такой баг сложно воспроизвести. Скорее всего вы двигаете куб на константное значение в методе update, а надо константное значение умножать на Time.deltaTime. Тогда плавность будет нарушена только в очень редких случаях, как описано в статье

Вот она, квинтэссенция статьи, иллюстрирующая механику возникновения подобных проблем на пустом месте. Не пересчитывайте состояние мира в Update(), она привязана к фреймрейту. И не используйте deltaTime. Для этого есть FixedUpdate(). В Update вам нужно подготовить сцену для следующего кадра, в данном случае выставить в GameObject координаты объекта из последнего расчёта мира.
Если сделать вот так, то объект будет двигаться плавно, за исключением случаев, описанных в статье. По факту — выявить такие случае на этапе разработки на компьютере разработчика практически не реально.
Vector3 velocity;
void Start() {
velocity = transform.forward;
}
void Update() {
transform.position += velocity * Time.deltaTime;
}

И как же, по вашему, может быть решена проблема, описанная в статье, используя методы FixedUpdate? По моему мнению, проблема, описанная в статье, на данный момент, нерешаема в общем виде. Только костылями. Если вы придумали решение, будь-те любезны показать пример кода.
Вообще я разрабатываю сетевые игры. Например RTS стратегию. У меня координаты смещения объектов вычисляются вообще в отдельном потоке или приходят по сети. А в Update идет интерполяция между старым и новым положением. В любом случае, transform.position должен меняться в методе Update и всегда по какой-то формуле, зависимой от времени. Только так получается достигнуть плавного движения. Буду рад увидеть код с другим решением.
По формуле, зависимой от времени в FixedUpdate(), с источником времени, не привязанным к фреймрейту, в Update() только присваивание того, что насчиталось там. По моему мнению, проблемы не существует, только от подобного подхода с привязкой к фреймрейту, что как подмечено в статье, было нормально в 90-е, но не сейчас. Конечно, проседание времени кадра сюда не относится, это вопрос оптимизации. В итоге будет плавающая задержка между фактическим отображением кадра и текущим состоянием мира, но она колеблется в слишком небольших пределах, чтобы на что-то влиять, эффектов торможения от этого не должно быть.
FixedUpdate() как правило вызывается реже, чем Update. К примеру на один FixedUpdate у вас будет вызвано 5 раз Update, в этом случае у вас 5 кадров объект будет со старыми координатами, посчитанными в FixedUpdate. Потом вызовется FixedUpdate, и изображение дернется. Ну или даже если вы сделаете частоту вызова FixedUpdate чаще, чем в Update, все равно, будет разсинхрон, который приведет к дерганию.
В итоге будет плавающая задержка между фактическим отображением кадра и текущим состоянием мира, но она колеблется в слишком небольших пределах, чтобы на что-то влиять, эффектов торможения от этого не должно быть.

Вы это пробовали сами написать? У меня всегда в таких случая были только лаги. Так можно делать, если в Update пихать интерполяцию между старыми и новыми значениями, посчитанными в FixedUpdate(), но и то, объект то будет ускоряться то замедляться.
Рассмотрим пример. Пусть частота FixedUpdate() и Update () примерно равна. В FixedUpdate() мы изменяем координату на один. x+=1; В Update отображаем эту координату. За 9 итераций FixedUpdate выводит значение x {1,2,3,4,5,6,7,8,9}, Update вызывается без синзронизации, поэтому иногда он будут запаздывать, иногда брать значение раньше, чему нужно, может получиться к примеру {1,1,3,4,4,6,6,8,9} То есть иногда объект будет слишком спешить, иногда ждать.

Там даже есть delta именно для FixedUpdate.
На самом деле странное решение… перемещение объектов и камеры будет иметь ниже FPS чем у всяких эффектов при том что фреймрейт може быть высокий.

Там насколько помню частоту fixedUpdate можно поменять в настройках проекта. По умолчанию вроде 40 раз в секунду срабатывает. Можно поставить 100, этого будет более чем достаточно для плавности.

Но если вы используете какие-то сторонние ассеты и они написаны через ж..., то они начнут вести себя неадекватно при нестандартной частоте (сталкивался с таким).

Нет, из-за разницы между реальным таймом и фиксированным наоборот будут прыжки туда-сюда в отображении. Или вообще времени на 1 фиксед апдейт по каким либо причинам может не хватить и тогда картинка поплывёт. Кроме того инпут от пользователя приходит в реальном времени и его нужно обрабатывать как раз моментально, иначе управление будет «деревянным» что намного хуже для игр.
Для этого есть FixedUpdate().
Проявлятся будет в еще худшем виде. В какие-то кадры дерево может прыгнуть сразу на 2 шага вместо одного.
Данный баг воспроизвести довольно просто в любых версиях Unity. Добавьте к примеру куб на сцену и двигайте его как вам удобно: в Update с Time.deltaTime, через AddForce или FixedUpdate. Вы увидите как он дергается. Многие просто этого не замечают, отказываются видеть и верить и этот косяк присутствует даже в топовых мобильных играх.
Понятно что какие-то дерганья всегда есть, все работает с погрешностью. Но я, к примеру, не способен увидеть такие лаги, пока они не достигают критических значений. Может просто у вас зрение позволяет замечать больше, чем у большинства людей.
Тут еще дело в опыте. И я не говорю про игровой в целом, а про очень хорошо оптимизированные игры. Поиграв много в игры которые идеально себя ведут и очень отзывчивы, потом подобные мелочи очень в глаза бросаются.
Играл. Но не долго. Читал очень много комментариев про проблемы с подгрузкой текстур и лагах при вращении камеры, но на удивление у меня все было нормально.

А к чему вопрос?
Читал, что это самая стабильная в плане частоты кадров игра.
Увы, я даже немного слеповат.
В свое время продюсер погонял насчет того, что дергается картинка при хорошем FPS. Потом выяснилось, что если Unity принудительно не ограничивать в кадрах, то колебания между 80-100 кадрами в секунду очень плохо влияет на плавность картинки.

Кстати, посмотрел ещё на картинку с таймингами фреймов, подумал… Но ведь бредовые же тайминги! 24.8 — ещё туда-сюда (допустим, картинка отображалась в течение двух кадров, значит, "центр масс" попадёт на середину между ними, смещение в полкадра. А 10.7 и 14.3 — это какой-то болезненный бред. Не может такого быть. Все числа должны быть кратны 16.6 (если считать по моменту отображения кадра), на крайняк 8.3 (если считать по "серединам" и кадры отображаются разное время).

Это ведь время рендеринга кадра, а не вывода

Не-не-не, это не время рендеринга кадров (оно не может быть 16.6 — слишком много для 60 fps), это, насколько я могу судить, разница "физических" времён кадров (т.е. на какое время обсчитывается игровой мир в каждом кадре)

Почему не может? 16.(6) мс — это как раз время кадра для 60 фпс. Просто разделите 1 на 60, чтобы убедиться…

Именно поэтому. Нужен запас времени. А если обсчёт кадра занимает примерно 1/60 секунды — то даже крохотная задержка приведёт к тому, что мы не успеем вывести его вовремя, и выведем ещё на 1/60 секунды позже. Т.е. у нас половина кадров будет по 1/60 секунды, а половина по 2/60, гарантированное дрожание картинки.

Нет, ибо, многопоточность и асинхронность. Они же и косвенная причина статьи.

т.е. в старом добром однопоточном рендринге аля GL2 эту проблемму можно было бы избежать?

эммм. помнится ещё в лохматые года было ж придумано, что игровой движок существует со своим "фпс", которым рассчитывается вся логика, а отображение идёт отдельно от него по принципу "как успеет". это ведь отображение только. там можно дропать кадры, перерисовывать/актуализировать когда угодно. это ж дурь полная, если физика, например, завязана на фпс видеокарты.
ну и в сетевых всех играх так 100% — ведь не может же течение игрового времени для всех игроков считаться из усреднения их фпс?

Не знаю, насколько лохматыми были конец 90-x, но физика движка Quake 3 (id Tech 3) зависела от частоты кадров, хотя графика обрабатывалась на GPU. И в сетевых играх тоже.
Эмм, в q3 есть sv_fps, которая определяла частоту симуляции.
Ну и в 90-х, физика была попроще. От фпс мало зависело движение объектов.
sv_fps есть, только толком его никто и никогда не использовал. Геймеры и не понимали особо его сути, зато всем и обязательно нужно было иметь стабильные 120fps, которые ставились командой com_maxfps. А если частота кадров скакала, то были неприятные проблемы — в частности, без стабильных 120 фпс нельзя почти нельзя перепрыгнуть с мостика на рейлган на dm6 в osp mode.
Это уже, скорей всего связано с частотой опроса инпута, а не с физикой.
с мостика на рейлган можно прыгать с любой вменяемой частотой кадров, там проблема в правильных движениях, чтобы нужную скорость набрать.

а вот прыжок на t2 с лесенки на шарды (и в тысяче других подобных мест) действительно при фпс ниже 120 не удавался по высоте.
Скрытый текст
фанат defrag mode? :D
в 90-х, физика была попроще. От фпс мало зависело движение объектов

Нет, движок Quake при FPS выше ста начинал ощутимо глючить в расчётах физики позволяя использовать его багофичи для всяких сложных продвинутых трюков.
Извиняюсь за занудство, но это не совсем так. Насколько я понимаю, это не совсем глюки, а ошибки округления. И не при «выше 100 фпс», а при некоторых магических значениях — вроде бы 43, 72, 120 (могу ошибаться — давно это было). Подбиралось то фпс, которое стабильно могла обеспечить система. Это давало увеличенную высоту прыжка. Вплоть до того, что с неправильным фпс невозможно было запрыгнуть на некоторые объекты типа больших ящиков в RTCW.
Я в своё время мапил специальный уровень с площадками разной высоты и замерял высоту для каждого из значений FPS. После 500 FPS вообще начинались чудеса и самопроизвольные дисконнекты.
Также была pmove_fixed, которая отвязывала физику от частоты кадров. Но, как и sv_fps, появилась она далеко не сразу.

q3 вышла в декабре 99. Почти начало 2000-х.

Я не сравниваю современные игры с играми прошлых лет. justmara предположил, что зависимость физики от фпс видеокарты — полная дурь, но тем не менее, нечто подобное было.
зависимость физики от фпс видеокарты — полная дурь

А если физику считать через CUDA на видеокарте, тогда как?
зависимость физики от фпс видеокарты — полная дурь, но тем не менее, нечто подобное было.
Было-было. В Mafia 1 даже непроходимые места из-за этого есть.
Just like with the Exploding Celeste mission, this subquest is virtually impossible if you own a better hardware which gives a good framerate. But lowering the framerate will reduce the rate of bombs dropping and thus, the probability of your car being hit.

Еще почитать: pcgamingwiki.com/wiki/Glossary:Frame_rate_(FPS)#Frame_rate_capping
Мафия — это сугубо синглплеерная игра. Ей можно зависеть от фпс. Это упрощает движок — не надо делать никаких трюков чтобы положить частоту кадров на частоту физики.
pmove_fixed — это в некотором роде чит как и cl_timenudge и его всегда выключали на чемпионатах. Простая проверка скилла — с выключенным pmove_fixed на q3tourney2 было довольно проблематично выпрыгнуть из телепорта сразу на площадку к броне/рокету. С включенным pmove_fixed любой мог так сделать. /ностальгии тред
В Quake2. На этом был основан трюк с запрыгиванием на некоторые ящики.

Этот параметр называется tickrate. Он нужен для синхронизации состояний и к плавности картинки отношения не имеет. У недостаточного tickrate-а совершенно другие симптомы. Хороший пример это Battlefield 3 с его 10Hz на старте продаж. На YouTube думаю ещё остались видео с тем как это выглядело.

Неужели сложно сделать очередь кадров в GPU и выводить их с фиксированным FPS?
При этом считать движение с фиксированным dt будет немного проще.
А если CPU/GPU не успевает просчитать кадр, то повторять предыдущий, либо рендерить каждый кадр последовательными приближениями с лимитом на время выполнения.
Так делают, но это ухудшает отзывчивость и всё становится еще хуже.
Ребята, а есть ли какой-то способ сгладить эту проблему на стороне пользователя уже сейчас?
Мне бы очень хотелось знать, что я могу сделать, как обычный геймер. Это чертовски актуально, т.к. есть вполне отличные игры, которые «какбы» выдают свои 60fps, однако при этом картинка совсем-совсем не плавная.
На данный момент мне немного помогало лишь отключение V-Sync и таким образом разблокировка fps выше 60. Но при этом, как побочный эффект (о котором честно предупреждает Nvidia) появляются всякие разрывы изображения.
Можно ещё что-то попробовать? Помогут ли всякие мотиторы с G-Sync & FreeSync?
Можно попытаться в настройках процесса отключить несколько ядер. Если у вас потоков в проце много, конечно. Это может уменьшить средний FPS, но пиков станет меньше. Говорю в теории.
Раньше помогало снижение настроек, но я так понимаю, что всё. Опять же, поговаривают, что в красном лагере с этим получше — можно попросить у кого из знакомых «поиграться».

Вообще, помнится, раньше нВидиа умела рендерить с отключенным vSync и без разрывов, но с тех пор она объелась стеройдов и положила качество в жертву скорости. Теоретически, *Sync-и должны помочь — сути их как раз в синхронизации времени вывода с временем рендеринга, так что попробовать можно.
Не обязательно сильно выше 60. Через профили Nvidia например можно под каждое приложение выставить максимальную частоту экспериментально. Мне чаще всего подходит 62 кадра, v-sync принудительно выключаю везде.
Если у вас довольно мощный компьютер, может в какой-то степени помочь тройная буферизация.
G-Sync очень помог. Если кадры скачут, скажем, с 40 до 100, то визуально это действительно незаметно, разрывов изображения нет. Могут ощущаться изменения в отзывчивости (кнопки, интерфейс, клики), но это наверное уже от самих игр зависит.

Если fps падает ниже 30, G-Sync отключается, старые добрые лаги возвращаются.
Вы имеете ввиду, что вам помог G-Sync именно в том случае, который описывает автор статьи?
Я уточняю, т.к. тут очень специфическая проблема, fps нормальный, с V-Sync всегда железно 60 кадров, ничего не проседает вообще. Однако именно картинка, которую видишь своими глазами, а не на датчиках, совсем не плавная.
Автор описал достаточно распространённую проблему, которая встречается много где, много у кого. Есть весьма неудачные в этом плане игры, которые страдают такой проблемой. У других же игр, на том же железе, таких проблем нет вообще.
Т.е. достаточно странная ситуация, учитывая, что автор говорит о том, что проблема в первую очередь на стороне производителей видеокарт и драйверов.
Не уверен. Я сужу по WoW, там картинка в движении плавная. The Talos Principle у меня есть, попробую глянуть.
Ого! А вот это очень хорошо, т.к. у автора как раз там была эта беда! В идеале глянуть сначала с G-Sync, затем с без него. Но это уже заморачиваться немножко нужно.
Такая же проблема есть ещё в Soma, там её даже искать не нужно, она перманентная.

Если я правильно понял суть g-sync (что можем сами сказать монитору, что пора отображать новый кадр) — должен изрядно сгладить проблему. Мы просто не попадём в ситуацию, когда "отстали" (или решили, что отстали) аж на 1/60 или 1/120 секунды — можно же в этом случае просто отобразить кадр чуть-чуть позже.
Но полностью проблему (что для максимального fps нам надо заранее знать время обсчёта кадра) не уберёт.

G-sync так же проблему не решает! Проблема не в том, когда монитор отобразит отрендеренный кадр(этим занимается g-sync), но и что — для какого конкретно ближайшего будущего подготовленный кадр будет отображаться. Есть шанс в 50%(если успели до дедлайна) что монитор должен будет ждать дополнительные несколько миллисекунд, что бы начать поворачивать пиксели в точно расчитанное время, а не как быстрее. Это должно так же повысить плавность.

Так же включая лимитер/vsync, мы только повышаем вероятность уложиттся в дедлайн (который мы сами расчитали заранее до начала рендера) до отображения. В случае же если мы его совсем провалили, последний кадр нужно ВЫКИНУТЬ без отображения совсем, тк лучше никак чем поздно. Есть также возможность выключить подсетку дисплея во время пропуска, что бы мозг сам дорисовал пропуск, ака lightboost, но который включается только когда надо, а не всё время и между кадрами.
Продолжу, быстрый гуглеж говорит, что среднее время моргания глаза около 40-50 мс, поэтому есть шанс отрендерить аж 2 кадра совершенно незаметно для человека(главное гарантированно хотя бы 1). Черный экран при этом можно симулировать простой заливкой и без выключения подсветки на обычном экране. На 120-144Hz должно быть менее заметно и без какого либо g-sync.

Не решает, а смягчает: допустим, мы обсчитывали кадр для момента t, но не успели на пару миллисекунд. С g-sync мы его (если я правильно понял суть технологии) сможем отобразить в момент t+2, без — только в t+16.6.

В то же время gsync может отобразить кадр для t в время t-2, тк емо хочется быстрее, а так делать не надо.

Зачем/как? Если ему этот кадр отдать в момент t — не сможет. Если отдать в момент t-2 с командой "отобразить в момент t" — "не захочет".
Но, повторюсь — это если я правильно понял суть технологии...

Монитор для стерео-очков поддерживает высокие FPS.
Необязательно иметь именно монитор с высоким фреймрейтом, можно начать с того, чтобы отрисовка на gpu происходила значительно быстрее вывода, тогда время «рывка» сократится, и ошибки таймера окажутся за пределами восприятия. Надо понимать, что шина данных «cpu -> gpu -> монитор -> глаза» всегда имеет определённую степень асинхронности, поэтому кратное увеличение частоты обновления на отдельных её участках может сделать картинку значительно плавнее.
На нынешнем мониторе можете попробовать nvidia Fastsync/AMD Enhanced sync, но от этой проблемы они не помогут, да и фпс нужен больше герцовки раза в два — отсутствие разрывов и высокий фпс при пониженной задержке ввода. Но вот новый монитор я бы советовал брать сразу с VRR (G-Sync & FreeSync)
G-Sync & FreeSync помогут при нестабильных фреймтаймах и проседающего фпс — крайне полезная штука
Вся суть статьи одним предложением: «Вместо Time.deltaTime используйте Time.fixedDeltaTime». Это для Unity. Уверен что в других движках тоже можно сделать привязку перемещения/анимации к независимому от FPS таймеру.
Насколько я понял, тут глубже проблема. Движок считает Time.deltaTime, а потом ОС отправляет этот поток спать на, допустим, миллисекунду. Лишь ПОСЛЕ ЭТОГО движок считывает данные с контроллера (с опозданием на миллисекунду) и вся математика начинает косячить, из-за чего появляются неприятные эффекты.
Не выйдет — только если они всегда будут равны — а так не бывает.
В противном случае все объекты, которые передвигаются, будут двигаться со скоростью, зависящей от частоты кадров. В мультиплеере это вообще фиаско.
Вот и у меня сразу такая мысль была. Я так и не понял глубины глубин этой «проблемы». 30 лет назад действительно была привязка к частоте кадров, но в современном игрострое за такое по пальцам бьют. deltaTime можно использовать разве что для показа того самого индикатора FPS и только.
Не в fixed проблема. То есть отвязать анимации от рендера — это само собой разумеющееся, но основная проблема в том, что кадр может нарисоваться как через 16мс, так и через 24мс. То есть показан он будет в разное время, и отсюда будут рывки.
По большому счету, для сингл-плеера решение все-таки существует: для расчетов использовать не реальное время с последнего кадра, а 1/fps. Конечно же, fps надо считать буквально по количеству кадров, вместившихся в последнюю секунду. Пик как в статье будет полностью нивелирован, а при небольшом «проседании» fps, синхронизация наступит за секунду. Визуально все будет очень плавно и только немного мир замедлится/ускорится.
Можно даже использовать не целую секунду для расчетов, а последние 5-10 кадров, этого обычно достаточно для сглаживания пика. В зависимости от ситуации, среднее или медианное время последних N кадров может дать идеальный результат.
Проведём простую цепочку преобразований: 1/fps => 1 frame / ( N frames per second ) => 1*frame / ( N*frame / deltaTime_1 )
=>1/N * frame/frame * delta_time_1 => 1/N delta_time_1 = delta_time_n

То, что Вы предлагаете — это и есть подсчет времени, просто другое численное выражение( в долях от случайного временного интервала, а не в миллисекундах (тысячных долях от секунды).
fps = N/(frame_1_time + frame_2_time +… frame_N_time)

Считается обычно для некоего разумного числа последних кадров N, потому как для 1 кадра смысла не имеет, для 1000 тоже. Значение 1/fps является средним значением длительности последних N кадров.

То, что я предлагаю (и что используется уже лет 15 в разных движках для некоторых задач, н-р, в Unigine), это использование плавающего усредненного времени кадра, либо медианного времени кадра, взятые для ближайшего временного интервала.
Лучше лочить фпс через RivaTuner, чуть выше или ниже частоты обновления монитора с использованием FastSync и в целом все получается неплохо.



А зачем вообще нужны ка ие-то синхронизации???
Вот взять идушего человека которого фотографируют с какой-то частотой. Ему нужно знать эту частоту? Нет! Он просто идёт с какой-то скоростью, которая вообще никак не связана с частотой съёмки. А съёмка лишь позволяет заглянуть где он находился через какие-то промежутки времени.
Если игра будет считать перемещения не от частоты кадров, а по внутреннему таймеру много большей частоты независимо от частоты кадров, а запускать рендер как получится — разве это не решит проблемы?

У меня возник такой же вопрос. Если игра будет генерить данные для рендера по своему таймеру, а рендер тред будет забираться всегда последнюю актуальную версию, то по идее такой проблемы быть не должно. Мне кажется всё это идет от привычки всю игру в одном цикле делать: Посчитали игровое состояние -> Отрендерели -> померяли время снова считаем игровое состояние.
В этом и суть проблемы, человек идет себе, вы его фотографируете, но если вы потом из фоток склеете видео, то идеально плавным оно будет только в том случае, если вы делали снимки с одинаковым интервалом. Если где-то будет разный промежуток, глаз заметит рывок
Если бы всё было так просто то проблема бы не решалась выставлением константного deltaTime. Проблем в том что delta между кадрами на самом деле меньше чем фактическая, которую на данный момент измерить нельзя, если рендер будет всегда брать последнюю версию состояния, которая будет считаться с константным dt, то проблема не должна себя проявлять
Проблема в том что моменты когда рендер берет версию состояния могут сильно отличаться от моментов когда картинка выводится на экран.

Грубо говоря рендер вначале берет каждую 0.1 секунду данные:
0.0, 0.1, 0.2, 0.3…
А потом у нас где-то возникли тормоза на полкадра и следующее чтение случилось когда натикало уже 0.45:
0.0, 0.1, 0.2, 0.3, 0.45
При этом в реальности тормоза были только на CPU, а GPU вполне успешно продолжал работать и в «плановый» момент 0.5 затребовал новую информацию о состоянии мира. CPU в этот раз не протормозил и оперативно выдал запрошенное
0.0, 0.1, 0.2, 0.3, 0.45, 0.5,…
И все, приплыли, stutter. На экран в момент 0.4 будет показан кадр расчитанный для момента 0.45.
В этом объяснении я не понимаю одного, каким образом тогда выставление фиксированного dt в случае автора статьи решило проблему?
Вроде проблема в другом. CPU считал физику 0.0 0.1 0.2 0.3 0.4. GPU отвечал что нарисовал кадр в 0.1 0.2 0.3 0.45. CPU заметил что отрисовка запаздывает и занимает 0.15 а не 0.10. И следующий тик посчитал в 0.6 а не 0.5. И когда GPU пришёл заданными для 0.5 отдал ему данные для 0.6 что и вылилось в опережение кадра.
когда GPU пришёл заданными для 0.5 отдал ему данные для 0.6 что и вылилось в опережение кадра.

Вспомнилось «дежа вю — глюк Матрицы» (с)
Потому что у автора моменты времени считаются «по запросу». Физика считается не непрерывно а лишь в дискретные моменты времени. Принудительное выставление моментов на 0.0, 0.1, 0.2, 0.3, 0.4, 0.5 естественно решает проблему.
Именно такое решение и предлагается собственно, двигать мир независимо от рендера, показывать последний актуальный результат в результате пользователь видит мир с небольшой, но постоянной(при отсутствии реальных скачков фпс) задержкой, равной времени рендера кадра.
Что есть «последний актуальный результат»? В примере приведенном выше это 0.45 а отнюдь не 0.4 просто потому что запрос на получение результата случился чуть-чуть с запозданием и мир успел обновиться раньше чем GPU забрал нужное состояние для очередного кадра.

Так почти везде так и работает. Проблема из статьи — что-то специфичное для каких-то отдельных движков в отдельных играх.
Сама идея сперва синхронизировать стейт под фпс, а потом его обратно рассинхиронизировать отдает несколько альтернативной логикой :)
Вот где реальная проблема — это видео, т.к. там частота кадров фиксирована и без синхронизации нельзя.

Так и работает это как? Выставлением равномерного шага симуляции от кадра к кадру при неравномерной частоте кадров?

Никто стейт не «рассинхронизирует» специально, статья про то что это явление само собой возникает из-за невозможности обеспечить стабильную задержку между определением состояния мира и выводом кадра на экран.
Так и работает это как? Выставлением равномерного шага симуляции от кадра к кадру при неравномерной частоте кадров?

Симуляции вообще ничего не надо знать о кадрах. Она просто работает по своему таймеру. А рендер время от времени выдает, с-но, кадры.
Проблемы возникнут только тогда, когда вы захотите обеспечить какую-то конкретную частоту кадров (например, сделаете ограничение в 60 фпс).

Проблема с-но естественным образом возникает из-за того что рендер забирает кадры с неравномерной задержкой (точнее не с той же самой задержкой с которой они показываются). Только и всего. Конкретность частоты кадров здесь совершенно не при чем.
Проблема с-но естественным образом возникает из-за того что рендер забирает кадры с неравномерной задержкой (точнее не с той же самой задержкой с которой они показываются)

Это как раз не проблема. Забирает и забирает, фиг с ним, рывков от этого не возникнет.
Проблема начинается как раз тогда, когда вы попытаетесь синхронизировать частоту "забора", и для этого подкручиваете таймер самой симуляции. Эти ведь рывки из статьи — это не рывки отображения, это рывки симуляции. Видеокарта успевет рендерить равномерно, но сама симуляция из-за подкрутки становится неравномерной.
Если же вы не занимаетесь подкруткой таймера симуляции — то и рывков не будет, т.к. симуляция будет равномерной (если, конечно, она сама не начнет тормозить, но в реальности у нас практически все игры gpu-bound с огроменным запасом)

Забирает и забирает, фиг с ним, рывков от этого не возникнет.

Возникнет. Осознайте уже наконец что данные симуляции забираются рывками, неравномерно

Подкрутка симуляции которая так сбивает всех в данной статье — это просто способ не считать симуляцию в тех временных точках где она не нужна. Но проблема никак не связана с этой оптимизацией. Она будет возникать и при идеальной, непрерывной симуляции. Причем там дословно те же самые кадры с рывками будут забираться. Просто еще и куча «ненужных» будет выкидываться.
Возникнет. Осознайте уже наконец что данные симуляции забираются рывками, неравномерно

В рассматриваемом случае они забираются равномерно, но неравномерно происходит сама симуляция.


Подкрутка симуляции которая так сбивает всех в данной статье — это просто способ не считать симуляцию в тех временных точках где она не нужна. Но проблема никак не связана с этой оптимизацией.

Именно с ней она и связана. Из-за подкручивания таймера симуляция становится неравномерной — и когда вы равномерно выводите кадры неравномерной симуляции, то получаете рывки. Если не подкручивать кадры — проблемы просто нет.


Она будет возникать и при идеальной, непрерывной симуляции.

Откуда? Вы перечитайте еще раз пример с разбором из статьи. Кадр выводится за 16.5, но игра думает что он выведен за 24.8. При этом происходит подкрутка симуляции — и вместо того, чтобы через 16.5 сек вывести состояние через 16.5, выводится то состояние, в котором мир находится через 24.8. В результате симуляция мира опережает актуальное время, в которое выводится кадр, на 8.3, игра вам рендерит "будущее".
Если вы ничего не будете подкручивать, то через 16.5 игра выведет ровно то, что там и есть — состояние, в котором находится симуляция через 16.5.
При этом реальный фпс как раз 60 — именно об этом же и идет речь в статье, что фпс не падает, он держится ровно, кадры выводятся равномерно, но это не те кадры из-за того, что в силу подкрутки внутриигровое время симуляции скачкообразно опережает реальное.

В результате симуляция мира опережает актуальное время, в которое выводится кадр, на 8.3

Тут еще стоит добавить важный момент — сам рывок, что вы актуально видите, в большей степени связан не с этим опережающим кадром, а со следующим — когда происходит обратная синхронизация, и игра думает что получает кадр за 10.7


Если в первом случае вы испытываете "всего лишь" ускорение в 24.8/16.5 ~ 1.5 раза, то во втором кадре время замедляется, вы видите движение в 10.7 вместо 16.5, в итоге сравнительная скорость между двумя последовательными кадрами — 24.8/10.7 ~ 2.3.

В рассматриваемом случае они забираются равномерно, но неравномерно происходит сама симуляция.

Неверно. Забираются они неравномерно а симуляция просто подстраивается под этот неравномерный момент забора. Вот выводятся на экран результаты равномерно, но вывод на экран != момент забора данных

Если не подкручивать кадры — проблемы просто нет.

Совсем не подкручивать (грубо говоря забирать данные симуляции с равномерным интервалом) нельзя в силу того что симуляция так или иначе должна синхронизироваться с происходящим на экране, иначе у нас физика начнет зависеть от FPS (больше FPS — быстрее падают объекты и т.д.)

Кадр выводится за 16.5, но игра думает что он выведен за 24.8.

Она так думает потому что прошло 24.8 мс с момента последнего забора данных от симуляции. А не потому что ей так внезапно захотелось. Понимаете теперь? Мы выводим состояние в момент времени «24.8» потому что мы запросили его в момент времени «24.8».

Вот вам такой краевой пример: если таймер симуляции будет настроен на 100000 рассчетов/сек и будет игнорировать факт забора, то при любом ФПС игра будет выглядеть плавно.
Нет. Будет как в статье. Физика есть на любой момент данных, кадры выдаются на экран равномерно. Но картинка на них дергается, так как значение физики будет браться на неправильный момент.
Ладно, давайте проще. У нас вообще нет дисплея, есть некоторый таймер, который каждые 10 тиков опрашивает модель. Будет ли он наблюдать рывки, если модель ничего про этот таймер не знает, а просто производит внутреннюю симуляцию?
Если модель имеет внутренний таймер не кратный 10 тикам то будут рывки. Например модель считается быстрее каждые 9 тиков, то каждый 9 кадр будет в два раза быстрее.
Поэтому важно синхронизировать два таймера. Но корректных данных о тиках внешнего таймера нет.
Если модель имеет внутренний таймер не кратный 10 тикам то будут рывки.

Значит достаточно, чтобы внутренний таймер был кратен, или достаточно быстрым, чтобы рыки не были заметны. Например если будет рывок в 10 микросекунд то вряд ли он будет заметен.
Как раз на экране рывок в 10 мс виден по статье. Генерация физики 500-1000 раз в секунду лишь поуменьшит лаги но не устранит полностью. Слишком затратно по CPU.

А вот синхронизация на равный тики действительно решает проблему. Но только корректно померить тики видеокарты на данный момент нельзя
Как раз на экране рывок в 10 мс виден по статье.

10мкс в 1000 раз меньше

А вот синхронизация на равный тики действительно решает проблему. Но только корректно померить тики видеокарты на данный момент нельзя

Ну понятное дело, что идеальное решение иметь обратную связь. Но когда это будет…
Ну понятное дело, что идеальное решение иметь обратную связь. Но когда это будет…

Но раньше это идеальное решение практически всегда и работало. Просто сейчас видеокарта стала настолько сложной, что ситуация стала возникать чаще чем раньше
На шейдертое симуляция вообще высчитывается из всего диапазона float iGlobalTime, это эквивалентно 1/FLOAT_MIN рассчетов/сек, без учета ошибок округления. Это не помогает, проблема вот в чем:
image

Вот какую проблему решал и решил гугл.
Наскольок я понимаю, тут проблема в неконтролируемом GC. Что ж, чтобы это работало, никаких GC быть не должно и кадры должны выводиться с одной частотой (если они готовы).
UFO just landed and posted this here
Рендреринг кадров происходит быстрее, чем частота обновления на мониторе. Но видеокарта сообщает, что кадр отрисовался не сразу, а только после выполнения внутренних процедур.
Со стороны кажется, что рендер не поспевает и происходит адаптация под проседания fps.
UFO just landed and posted this here
Но видеокарта сообщает, что кадр отрисовался не сразу, а только после выполнения внутренних процедур.

Не надо спрашивать у видеокарты ничего — тогда и не существенно, что она сообщает.

> никаких GC быть не должно и кадры должны выводиться с одной частотой (если они готовы).

Проблема в том, что по факту это не так. Это может быть не GC, а что угодно, просто наблюдаемый факт, что драйвер чем-то занимается после реального обновления. У меня это проявляется и на Linux и на Windows. Драйверам никто не запрещает это делать, по документации все четко, ничего не нарушается. Да и от GC никуда не деться, если не на swap его выполнять (когда как раз кадр завершили и логично почистить), на идущий следом glClear()? В общем кривота это все, знать реальное время отображения нужно, без этого никуда.
Вы не поняли. Речь тут не идет о физике вообще. Как бы вы её ни считали, даже если она у вас непрерывная каким-то образом, а не дискретными кадрами — проблема предсказания положения объекта на момент отображения остается. Просто потому что на начало рендеринга кадра надо знать положение объекта на момент когда этот самый кадр будет в итоге отображен игроку. Иначе движение не будет плавным. И это время плохо предсказуемо. И даже для уже отрендеренных в прошлом кадров её сложно (невозможно?) узнать, про что и статья.
Вам нужно делать 1 фотографию в секунду, а получается примерно 0,9. И что теперь по такому таймеру делать? Пропускать каждый второй кадр?
Тоже думал об этом и кажется понял (спасибо kosmos89). Идея в том что чтобы вывести видео на экран нам в симуляции нужна последовательность моментов времени которым будут соответствовать показанные кадры. Эта последовательность не равномерна и зависит от GPU, т.к. время отрисовки каждого кадра варьируется. Так вот померять эту последовательность правильно — сложно. А при ошибочном замере имеем наблюдаемую проблему когда время симуляции рассинхронизируется с реальным временем показа картинки. И неважно считается ли при этом физика только в нужные для отображения моменты времени или равномерно по таймеру, так что две трети кадров остается невостребованной (последнее кстати явно выглядит как способ угробить производительность).
А разве мы не можем просто…

… считать физику независимо от отрисовки? То есть один поток считает физику (время вычислений на CPU отлично измеряется), а другой периодически у него забирает последнее актуальное состояние. Тогда эффекта опережения не может быть (во всяком случае по причине неверно рассчитанного dt).
Да собственно так и делается в современных играх. Потоки необязательно разные, но тики физики идут от стабильного таймера, и обычно с чатотой выше, чем кадры. В статье боль разработчиков, оставшихся душёй в тех самых 90-ых.
Проблема описанная в статье не в расчете физики. Проблема в том в какие моменты времени забираются рассчитанные данные. Эти моменты привязаны к тому что показывается на экране, т.е. зависят от GPU, и не являются равномерно распределенными. А дальше стоит потоку читающему состояние мира чуть-чуть притормозить с забором данных для кадра #50, как считающийся в параллельном потоке мир обновится и поток уже прочитает данные которые были бы валидны для кадра 50,1.
Отклонения такого порядка глазом не должны быть видны.
Ну вот как показывает практика, при задержке порядка 60 мс — отлично видны.
На винде это как раз примерное время preemption для потоков foreground-процесса
Не совсем понимаю. 60мс — это что? Отставание рендеринга от состояния мира? Каким образом это замерялось? Или это просто проседание времени кадра? Тогда конечно будет видно, это очень много. И в любом случае 60мс — это не доли кадра, это три с половиной кадра для 60FPS.
Ошибся, не 60 мс а 6 мс. Это отставание момента реального чтения мира от момента когда чтение мира было запрошено GPU. Замерялось путем замера момента времени обращения рендерера за состоянием мира и сопоставления этого времени с временем при равномерной частоте смены кадров
Не совсем уловил объяснение замера. Речь о вызовах в каком-то движке или вызовах OpenGL/DirectX? Каких именно? Вы ведь сами обновляете состояние сцены на GPU, а не он запрашивает состояние.
Ну, у Вас всегда есть какая-то логика которая решает когда пора начинать рисовать следующий кадр. Эта логика как-то синхронизируется с GPU потому что грубо говоря нет смысла начинать рисовать следующий кадр когда еще предыдущий не дорисовался. Это я и называю «запрос от GPU». Этой логике требуется определять состояние игрового мира для этого нового кадра. В идеале — на момент времени когда для этого кадра произойдет buffer swap, в реальности — скорее всего на момент времени swap buffer минус какой-то фиксированный лаг чтобы этот момент уже находился в прошлом а не в будущем. Но по факту запрос состояния мира идет на момент времени когда выполнение кода на CPU реально дошло до этой логики и она реально запросила данные у физического движка. Поскольку время от момента swap buffer до момента запроса состояния мира может непредсказуемым образом меняться то получаем описываемый в статье рассинхрон
Статья и перевод просто шикарны! Спасибо! Сам когда очень давно начинал в GL долго не мог понять, почему замер скорости участка кода рисования вместе в glFinish + SwapBuffers дает ~16ms вместо положенных ~60-100 микросекунд, если измерять без этих команд. Но, как оказывается, и эти 60-100 микросекунд — обман на самом деле.
Кстати у меня иногда проскакивает «статтер» и на правильном плавном видео, благо только 1 на весь ролик.

Интересненько (про видео), напомнило старые времена, когда принудительно выставляли на мониторе (CRT) частоту кадров, кратную частоте кадров видео. А позже ещё какие-то трюки были для просмотра 50Гц видео на 60Гц мониторе, я уж подзабыл.

> Кстати у меня иногда проскакивает «статтер» и на правильном плавном видео, благо только 1 на весь ролик.

У меня 2 раза, второй на обратном пути, почти незаметный.
Чую, производители железа будут против внедрения массовых программных оптимизаций :) Ведь это закон Мура может нарушить) Как тогда им продавать все более и более дорогие видеокарты?)
И все таки основная проблема тут в том, что время рендера кадра скачает. Анимации можно отвязать и показывать с фиксированным FPS, но так как кадр «плавает», то все это будет выглядеть дергано. А вот то, что время кадра так сильно скачает — я думаю это таки проблема оптимизации. Например постоянные реаллокации GPU ресурсов, объектов GAPI и тому подобное.
Пример: каждый кадр вызывают glBufferData с разными размерами, вместо того, чтобы один раз вызвать glBufferData с запасом, и обновлять его через glBufferSubData.
Время рендера кадра не скачет. В статье же сказано, что если принудительно считать временем рендера 16.6 то все хорошо. Кадры будут успевать отрисовываться. Но если пытаться узнать за какое время отрисовался кадр, то иногда получают завышенное число в 24 мс. Из которых на рендеринг кадра ушло меньше 16 мс, а остальное время драйвер занимался внутриними вещами уже после отрисовки и показа кадра.
Ну допустим. А зачем нам в этом случае вообще считать время кадра? Почему бы в этом случае не отвязать рендер от игровой логики? Сделать игровую логику и забор инпута с фиксированным фреймрейтом, а рендерить в остальное свободное время?
Рендер и так отвязан от игровой логики. Как указано в статье забор инпута с фиксированным фреймрейтом действительно решает проблему. Но это работает только пока вы успеваете рендерить кадр без просадки fps.
Если же надо рендерить при плавающем fps, то проблема никуда не девается. Физика игры берёться не на текущий момент, а на текущее время+ время рендеринга кадра. И если gpu говорит что не успевает отрисовать за 16, и успела только за 24 то игра пытается компенсировать просадку fps нарисовав кадр в более далеком будущем. Но в реальности просадки fps нет и кадр опережает реальный мир.
Но это работает только пока вы успеваете рендерить кадр без просадки fps.

Всмысле? Ну вот просел FPS до 20, а мы все равно забираем инпут и обновляем физику с частотой 60 раз в секунду. Почему это не работает то?

Физика игры берёться не на текущий момент, а на текущее время+ время рендеринга кадра.

Зачем физику брать на момент + время рендеринга кадра? Почему просто не брать физику от фиксированного времени без всякого плюс время рендеринга кадра?
Ну посчитали физику 60 в секунду, а потом пытаемся отобразить кадр попадающий не точно в то момент, а на 8 мс позднее. Ровно посередине тика расчета физики, для него какое значение брать. А если соседние с ним кадры попадёт ровно в тик? Будет два подряд одинаковых кадра?

Если не учитывать время рендеринга то картинка будет запаздывать, по ощущениям будет похоже что игра тормозит.

Физику принято считать чаще, чем частота кадров. Соответственно, два кадра одинаковыми не будут, а небольшую задержку (доли кадра) с состоянием мира вы на глаз не увидите.
Как раз небольшую задержку в полкадра на видео в статье мы и видим. Там кадр ошибся по времени в 8 мс.
Если не учитывать время рендеринга то картинка будет запаздывать, по ощущениям будет похоже что игра тормозит.
Уверяю вас, вы не почувствуете, что игра тормозит даже если изображение будет запаздывать на 16мс. Но ок, допустим мы знаем, что мы хотим получать 60FPS, и что у пользователя практически всегда будут стабильные 60FPS. В этом случае мы можем делать константную поправку на 16мс вперед. А если вдруг у пользователя FPS просядет до 30 в какой-то момент, то ок, он получит маленькую задержку в 16мс, что не смертельно.

В статье описывается как раз другой случай, когда при стабильных 60FPS время, когда кадр реально появляется на экране сильно ёрзает, и за счет этого мы собственно и видим рывки.
В статье описано как раз наоборот, кадр стабильно появляется на 60fps.
Он появляется вовремя и стабильно, но игра думает что иногда FPS проседает(хотя это и не правда) и пытается адаптироваться под проседание fps путём измения константы времени кадра. Что в свою очередь приводит к опережению по времени

Когда же они ставили постоянные 60 FPS они по факту и делали это поправку константой в 16 мс и лаги переставали наблюдаться.
Они пытались делать поправку от времени предыдущего кадра. Предыдущий кадр обрабатывался 24мс, они делают поправку на 24мс. В результате мало того, что кадр был нарисован с задержкой, так и для следующего кадра поправка будет в 8 секунд вперед.
Нарисован то кадр будет во время, но вот поправка действительно неверная. А что делать то если единственный источник откуда поправку модно взять( GPU) врет и выдаёт иногда не верные значения.
Странно, но на рабочем компе со слабой видюхой почему-то все представленные видео местами плавно, а местами тормозят.
Выскажу бредовую идею — GPU надо встраивать в монитор, а не в компьютер ?)
Идея бредовая — пропускная способность PCIE около 240 гбит, а HDMI — около 18 гбит.
К тому же совершенно не решает указанную выше проблему, а может даже ухудшит положение. Проблема не в том, что кадр идёт долго до монитора, а в том что драйвер отвечает что кадр отрисован с плавающей задержкой. При этом задержки на самом деле нет, это он просто занимался внутренними вещами.

У бетезды в движках при падении ФПС замедляется игровой таймер, и ничего, никаких проблем это не вызывает.

Это можно решить выделением отрисовки и обновления мира в разные потоки. Когда мы начинаем рендерить сцену то берём наиболее актуальный снапшот игрового мира.
Обновление же игрового мира при этом живёт своей жизнью.
Это одна из первых вещей с которыми сталкиваешься при разработке игры без привлечения готовых движков.
У меня стала наблюдаться проблема со схожим графиком «пульсаций» но в другом временном масштабе: каждые секунд пять (приблизительно) нормальное fps прерывается полным замиранием изображения на полсекунды.
Вот кто бы объяснил, в чём дело, и как с этим бороться. Я уж и vsync переключал, и другие опции крутил…

Началось это после перехода на Win7 с XP, причём на неновых уже играх — Чистое небо, Borderlands, Bioshock. На том же железе (где-то тех же лет сборки, уже тогда он был далеко не топ, но для подобных игр хватало — даже Crysis на средних настройках шёл).

Поставил на отдельный диск обратно XP — там всё нормально, только памяти не всем играм хватает (некоторые моды на сталкер довольно требовательны). Не, ну я понимаю, что сама операционка сколько-то ресурсов отжирает, и видимо, семерка таки побольше чем XP. Хотя встречал утверждения, что наоборот — семерка оптимизированнее и всё такое, а десятка так вообще…
Меня вот другое интересует, как эта вдруг проблема всплывает в 2018? А что, простите, раньше это было неизвестно? Никто не замечал?
Проблема настолько тонкая, что даже в комментариях к этой статье предлагают решения, хотя в статье объяснено, почему решения не существует.
Проблема тонкая и решение похоже потребует изменений на многих уровнях, возможно включая железный. Но эта проблема не новая. Почему она всплыла именно сейчас?
Раньше видеокарты были проще. Спрашивая время рендринга кадра он получает время рендринга + время работы внутренних служб GPU, которое выросло из-за усложнения видеокарт относительно прошлых поколений.
На сколько раньше? У карт 2016 года такой проблемы нет?
У всех карт появившихся после 90 годов такой эффект может присутствовать, просто чаще наблюдаются другие проблемы — производительности видеокарты не хватает для обеспечения плавности.
То есть только карты выпущенные 2017-2018 годах смогли обеспечить плавность и стала заметна другая проблема?
UFO just landed and posted this here
В том и дело, что судя по статье, проблема существует давно, с 2003 года ещё. В 2013 автор смог сам отделить её от других проблем. Но ведь разработкой игр и подобных приложений, требовательных к плавности, занимается в мире куча людей. Безусловно многие из них сталкивались с этим, наверняка писали об этом на профильных ресурсах, связывались с производителями видеокарт. Вот это всё где?
Мне больше интересно, есть ли еще аналогичные глобальные проблемы, о которых все знают, но молчат. Типа «сходите купите новую видеокарту» (а на самом деле, дело вовсе не в ней)

В статье говорится, что данная проблема автором была замечена еще в 2003 году, и до сих пор не решена (в процессе создания решения) и эту проблему создает не железо (в/карта), а софтовый стэк: ОС + graphics API + GPU driver

Потому что почему бы нет? Например теория вероятностей говорит о том, что точное вероятность того, что случайная действительная величина примет какое-то значение — ноль, но то, что она пример какое-то значение на интервале — достоверное событие (единица). Текущий момент ничем не лучше и не хуже любого другого. Просто так произошло.
Проблема еще и в мониторе. Если передвигать белый квадрат на черном фоне, то дергание и гостинг будет хорошо заметен. В фильмах против этого используют фейковые промежуточные кадры с размытием.
Так же, на ощущение лага очень сильно влияет инпут-лаг.
habr.com/post/308980
Ввели бы pending на пару кадров, считая предыдущее время рендеринга кадров, и при единичных отклонениях игнорируя текущее и брать предыдущие. Тогда проблема с единичными задержками бы полечилась (то есть если период в «кардиограмме» достаточно большой, то в чём вообще беда?)
Основной посыл в том, что конец swap нельзя считать за время отображения. Реально отображение произошло через 16мс, а драйвер висел еще 8мс (а может и не висеть, как ему захочется). Когда драйвер себе такое позволяет (а кто ему запрещал? в доке про swap ни слова когда именно будет отображение), ничего без костылей, которые будут работать в данном конкретном случае, но не заработают в другом, сделать уже ничего нельзя. Нужно вносить в документацию понятие времени отображения, что гугл и сделал.
Так вот почему PUBG при 60 FPS тормозит (нет). Там не просто слегка заметные лаги, а серьезные рывки.
P.S. Отличная статья, было интересно почитать.
Это лучше чем glFinish, если движок умеет ставить команды на поток. glFinish ждет сразу и нет возможности накидать команд дальше. А так: ставим точки синхронизации и кидаем команд сколько возможно, пока нет зависимостей с тем что уже считается, ждем на них, накидываем еще команд (при этом пока ждали уже все подготовили), когда знаем что точка прошла и теперь безопасно что-то трогать. При этом стараемся чтобы GPU был всегда нагружен. Однако в данной проблеме это ничем не поможет — нет такой точки «сейчас экран обновился», есть только «swap закончился».
1. Предложенное решение проблему не пофиксило. На последнем видео, где выставляют в консоли параметр, джиттер все еще присутствует — см. вниз экрана.

2. Эти странные люди зачем-то измеряют время рендеринга кадра, и уже из этой метрики выводят сначала актуальное 3d, а затем и пиксельное положение графического объекта.
Ибо только так можно добиться того, чтобы пиксельные координаты «забежали вперед», что у них и происходит (иного способа осуществить «связь» между framerate и положением объекта/сцены не просматривается).

Все их проблемы от неверного архитектурного решения. Так как они делают делать нельзя.
Правильно делается так — положение любого 3D-объекта, втч камеры должно быть функцией от переменной time. Переменная time предоставляется hardware, средствами os (но ни в коем случае не самопальными © «равномерно идущими» программными таймерами). Задача этой переменной time всегда предельно равномерно (хардварно) идти вперед, инкрементируя своё значение независимо от os, игры и графики.

Что произойдет в случае просадки FPS, неравномерного начала кадра (джиттеринга) или напротив, в случае увеличения FPS? А ничего. Переменная time как равномерно шла вперед, так и будет идти. Позиция (вначале 3D, а затем пиксельная) никогда неверно пересчитана не будет, так как положение отвязано от кадров, да и от прорисовки вообще.

Это и есть единственное гарантированное решение проблемы.
Забавно сколько людей не понимает сути проблемы — говорят, что привязка обсчёта физики к внешней функции исправит ситуацию.

Нельзя время забора кадра привязывать на внешний таймер идущий с равными тиками, так как тогда в случае просадки FPS или не равномерности по времени рендринга будет следующее — кадры будут обсчитанны для равномерного вывода, а выводиться будут через неравномерные промежутки и итоговая картинка будет дёрганной.
Нельзя время забора кадра привязывать на внешний таймер идущий с равными тиками, так как тогда в случае просадки FPS или не равномерности по времени рендринга будет следующее — кадры будут обсчитанны для равномерного вывода, а выводиться будут через неравномерные промежутки и итоговая картинка будет дёрганной.

В случае просадки FPS итоговая картинка в любом случае будет дерганной, и это никак не исправить, кроме как покупкой новой видеокарты или оптимизацией алгоритма в целом. Если гпу не успевает все что надо обсчитать — оно не успевает и никакие способы синхронизации вам не помог абсолютно никак.
Предложенный же вариант синхронизации не то что не помогает обеспечить большую плавность картинки, но и вредит, усиливая рассинхрон при определенных паттернах скачков. Это не говоря уже об описанном в статье эффекте. Тут выше кто-то верно заметил — ребята остались в 90-х, продолжая, зачем-то, мерить время в фпсах вместо того, чтобы мерить его в миллисекундах. Закономерно собрали грабли и теперь героически преодолевают собственноручно созданные проблемы.


а, и да:


Нельзя время забора кадра привязывать на внешний таймер

Никто не говорит о привязке времени забор кадра к таймеру. По внешнему таймеру должна идти симуляция, а когда забирать кадр — должна решать сама видеокарта.

Если FPS просел с 60 кадров на 59 то при адаптивном шаге под время рендринга кадра картинка останется практически плавной, а вот если оставить время тика физики постоянным то постепенно накопиться разница по времени между кадром и физикой, что приведёт к дерганности картинки.

Так в этом и проблема, что видеокарта говорит рендеринг кадра занял 24 мс, давай физику на следующие 24 мс. А на самом деле рендеринг кадра занял меньше 16 мс. Сейчас нет физической возможности правильно узнать время на которое надо посчитать физику для отображения.
> Так в этом и проблема, что видеокарта говорит рендеринг кадра занял 24 мс, давай физику на следующие 24 мс.

Так видеокарта не должна говорить никаких мс, она говорит: «дай мне _текущее состояние_». И ЦПУ его отдает.

> Сейчас нет физической возможности правильно узнать время на которое надо посчитать физику для отображения.

А и не надо ничего узнавать, надо просто считать состояние мира по внешнему таймеру, 300раз в секунду допустим, и все.

> Если FPS просел с 60 кадров на 59 то при адаптивном шаге под время рендринга кадра картинка останется практически плавной, а вот если оставить время тика физики постоянным то постепенно накопиться разница по времени между кадром и физикой, что приведёт к дерганности картинки.

С чего бы? Будет видеокарта в каждый момент n/59с опрашивать состояние мира и в (n+1)/59с выводить, откуда дерганья?
Никто не будет считать физику 300 раз в секунду, это слишком нагружает CPU. Все считают физику по одному разу на каждый кадр без промежуточных состояний.

В случае расчётов по физики фиксированным тикам не совпадающим с частой забора будет слудующее. Допустим физику считают каждые 16 мс. А выводят каждые 17 мс.
Время физики 0 16 32 48 64… 224 240 256
Время графики 0 17 34 51… 238 255 272
Видно что чем больше времени пройдёт тем больше рассинхронизация и для синхронизации придётся в один кадр запихнуть два тика физики.

Главный вопрос что есть «текущее» состояние мира? Время прихода команды на начало рендринга кадра плавает от кадра к кадру. Например для первого кадра это было в 8 мс, для второго в 2 мс, а для третьего в 12 мс. Видеокарта успевает обсчитывать все и выдать кадр стабильно каждые 16 мс, даже если команда на начало немного запоздало, но при таком подходе картинка получается дёрганной.
Никто не будет считать физику 300 раз в секунду, это слишком нагружает CPU. Все считают физику по одному разу на каждый кадр без промежуточных состояний.

Это вы сейчас сами придумали, практически во всех играх без лоченого фпс снижением настроек можно добиться существенно больше 60фпс (в общем-то обычно до сотен фпс) и никакой синхронизации там нет — просто задача gpu-bound и считать состояние процессора чаще не проблема.


Видно что чем больше времени пройдёт тем больше рассинхронизация

С временем физики 0 16 32 48 64… 224 240 256
Вы как-то немного забываете о наличии монитора, которые выводит ровно кадр на 16мс хоть ты тресни (ну или не 16, но в любом случае с фиксированной частотой). Если ваша физика генерирует кадр в 16мс — это ровно столько сколько в состоянии вывести ваш монитор, и вам нету смысла ничего подкручивать под скорость карты, т.к. монитор один черт выводит с другой скоростью.
Если у вас включен всинк, вы в любом случае будете наблюдать дублированные кадры (когда карта не успела отдать монитору кадр за 16 мс), либо, при выключенном всинке — полосы на экране, когда монитор возьмет недорендеренное изображение с буфера.


и для синхронизации придётся в один кадр запихнуть два тика физики.

Нет, не придется. В какой-то момент (в данном случае это будет 272мс, в этот момент видеокарта затребует состояние, и это будет не состояние 256мс физики, а состояние 272мс физики, оно успеет пересчитаться) временные отметки сойдутся, при этом карта успеет отрендерить 16 кадров, а симуляция — сделать 17 тиков. Т.о., карта пропустит один тик (то, что 256мс). Ну и что? Пусть хоть миллион тиков пропустит — нам это не важно, главное чтобы совпадало время.


Главный вопрос что есть «текущее» состояние мира?

Это то, которое сейчас лежит в памяти.


Видеокарта успевает обсчитывать все и выдать кадр стабильно каждые 16 мс, даже если команда на начало немного запоздало, но при таком подходе картинка получается дёрганной.

Да нету никаких "запоздалых команд", видеокарта просто нонстопом рендерит новые кадры по окончанию рендера предыдущих, с оглядкой на монитор при включенном всинке.
Если она выдает вам стабильные 16фпс — будет у вас ровная картинка. Конечно, если вы не из горе оптимизаторов вроде автора статьи, умудрившихся своими "оптимизациями" сделать так, чтобы игра тормозила не только на, с-но, тормозящем железе, но и на топовых печах :)

Спасибо вам за вашу настойчивость в отстаивании правильной точки зрения =)
Я в какой-то момент устал — некоторые комментаторы уж очень упорны в своей неправоте.

Я вообще сейчас все посчитал, и штука оказалось даже забавнее. Случай с физикой 16мс, 16мс монитор, 20мс рендер:
16 32 48 64 96 112
36 56 76 96 116 136
48 64 80 96 128 144
первая строка — мс на котором мы получаем результат физики, вторая — на котором результат отрендерен, третья — на котором он выводится на монитор.
Как видно, задержка стабильная по 32мс, то есть монитор выводит ровно со скоростью симуляции, при этом выпадает кадр на 112мс, видеокарта не успевает дать результат рендера и там дублируется предыдущий кадр. Т.о. получаем — кадры идут ровно с регулярными спайками по 2 кадра.
Теперь вариант с подкруткой физики под 20мс тики:
20 40 60 80 100 120
40 60 80 100 120 140
48 64 80 112 128 144
кадр на 96 выпадает аналогично предыдущему случаю, то есть будут присутствовать те же регулярные спайки, никакого чуда :)
но вот что интересно, задержки между результатом симуляции и выводом:
28 24 20 32 28 24
они ж, мать вашу, неравномерны :cry:
из-за некратности тиков симуляции и тиков монитора, натурально, кадры при выводе на монитор оказываются пропорционально ускорены. Но самое веселое вот этот участок: 20 32 28 — именно перед 32 у нас пропущенный кадр.
Так вот, вместо "ровный кадр — дубль — ровный кадр" в первом случае мы во втором, с подкруткой, имеем "ускоренный кадр — дубль — замедленный кадр (в полтора раза!) — ускоренный кадр".
Ежу понятно, что зрительно это будет значительно печальнее первого случая.
И вот в этом вот контексте у меня возникает закономерный вопрос — а то, что у них там "тормозит" — это действительно все из-за "видеокарты асинхронно блаблабла", или просто их "оптимизация" by-design кривая и все портит? :)
И это ведь еще не рассмотрен случай неравномерных тормозов, где рассихнрон пойдет вообще везде и всюду из-за того, что подстройка идет по таймингу предыдущего кадра (тайминг текущего мы не знаем — он же еще не отрендерен).


Как видно, с-но, синхра рендера с физикой ничего не дает, а вот синхра физики с монитором — весьма полезна. К счастью, для синхронизации физики с монитором никаких хитростей не надо — частота-то постоянная :)

На указанной машине машине все летает и быстро обсчитывается, поэтому при указании что время тика физики = 16 мс, все работает плавно.
Но разработчики также учитывают что машина пользователя может быть слабее и время рендринга может быть 20 мс. В таком случае надо поменять тик физики, иначе как вы выше написали будет неравномерно дрыгающая картинка. Для этого они спрашивают у видеокарты когда она закончила считать и вот тут возникают проблемы иногда видеокарта на этот вопрос отвечает полную чушь, несоответствующую действительности. А дальше начинаются визуальные лаги при том что все успевает обсчитаться.
Теперь вариант с подкруткой физики под 20мс тики:

Неверно. Если v-sync выключен (а это практически всегда так) то видеокарта не ждет момента начала отрисовки следующего кадра а выводит его немедленно по окончанию рендеринга. Переключение с кадра на кадр и может и будет происходить в середине отрисовки монитором очередного кадра, при этом рисуется верхняя часть от одного кадра а нижняя — от следующего (screen tearing). То есть реальная отрисовка идет

20 40 60 80 100 120
40 60 80 100 120 140
40 60 80 100 120 140

ну или

20 40 60 80 100 120
40 60 80 100 120 140
50 70 90 110 130 150

если дать 10 мс на задержку сигнала при передаче его в монитор

Если же включить v-sync, то он, что удивительное дело, начинает учитываться в рендере. Т.е. округляем время рендеринга вверх до целого числа кадров

20 48 80 112 144
48 80 112 144 176
58 90 122 154 186

Симуляция идет с равномерным шагом в 32 мс (2 кадра), никаких рывков и дублей. Просто у нас FPS теперь становится кратен частоте смены кадров. Это либо строго 60 fps либо сразу 30 (20, 15...) а 59 уже не будет.
Неверно. Если v-sync выключен (а это практически всегда так) то видеокарта не ждет момента начала отрисовки следующего кадра а выводит его немедленно по окончанию рендеринга.

Видеокарта не может ничего выводить, физически. Выводит монитор. И монитор выводит ровно столько кадров в секунду, сколько выводит — каждые 16мс в рассматриваемом случае. По-этому нижний ряд у вас будет всегда идти кратными 16мс шагами, и вы никак не можете на это повлиять. При этом не важно включен всинк или нет — монитор в любом случае выводит по 16мс, просто без всинка будут кадры с полосами и на мониторе будет сразу два состояния вместо одного, от чего лучше, конечно, не станет. В любом случае, почти все поголовно играют с включенным всинком (особенно в шутаны с высокой сенсой — без всинка при падении фпс они неиграбельны) — так что кейз без него обсуждать смысла нет.


Если же включить v-sync, то он, что удивительное дело, начинает учитываться в рендере.
Это либо строго 60 fps либо сразу 30 (20, 15...) а 59 уже не будет.

Да, в 90-х, до изобретения тройной буферизации, именно так все и происходило. Я смотрю, вы такой же контраморт, как и авторы статьи.


Неверно. Забираются они неравномерно а симуляция просто подстраивается под этот неравномерный момент забора.

Вы статью вообще читали? В рассматриваемом случае кадры забираются по факту равномерно (раз в 16мс), но из-за невозможности синхронизации — движок об этом не знает и начинает отдавать кадры как будто фпс скачет.


Совсем не подкручивать (грубо говоря забирать данные симуляции с равномерным интервалом) нельзя в силу того что симуляция так или иначе должна синхронизироваться с происходящим на экране, иначе у нас физика начнет зависеть от FPS

Чтобы фпс влиял на скорость таймера надо скорости привязать к фпс. Все можно продемонстрировать на примере вот этого куска текста из статьи:


Если один кадр занимает 1/60 секунды (16,67 мс), а персонаж бежит со скоростью 10 м/с, то в каждом кадре он перемещается на 1/6 метра. Но если кадр перестаёт занимать 1/60 секунды, а вместо этого внезапно начал занимать 1/30 секунды (33,33 мс), то надо начать перемещать персонажа на 1/3 метра (в два раза «быстрее») за кадр, чтобы на экране он продолжал двигаться с кажущейся постоянной скоростью.

Итак, авторы хотят, чтобы персонаж бежал со скоростью 10 м/с (заметьте в "скорость 10 м/с" ничего про кадры нет!). Как сделает здоровый человек? Заведет таймер и установить скорость персонажа 10 м/с (как и требуется) относительно этого таймера. Как делают горе-оптимизаторы (и вы в их числе)? Они зачем-то устанавливают скорость относительно фпс (ЗАЧЕМ? где там вообще что-то про фпс? какое отношение фпс имеет к скорости персонажа???), потом ВНЕЗАПНО оказывается, что при падении фпс и скорость, ыраженная в фпс, меняется. И вместо того, чтобы подумать об архитектуре решения, ребята просто подкручивают скорости (в тех самых фпсах).


Так вот, отвечая на ваше исходное замечание — если вы скорость меряете по абсолютному внешнему таймеру, без фпс, то ничего подкручивать не надо. И скорость при этом с изменением фпс меняться не будет, потому что фпс на нее просто тупо никак не влияет.


Она так думает потому что прошло 24.8 мс с момента последнего забора данных от симуляции.

Нет, с последнего забора прошло 16мс. Если бы с последнего забора прошло бы 24мс, то игра и кадр бы не смогла вывести раньше, чем через 24мс, тогда бы вы увидели сдублированный кадр (или недорендеренный, при выключенном всинке, но у них, понятное дело, всинк включен). А она новый кадр вывела вовремя. И он ровный!
Невозможно физически вывести на мониторе через 16мс новый полноценный кадр, если задержка перед забором была больше 16мс. Неужели это не очевидно?
Если же ваша карта запросила новый кадр на рендер через 24мс вместо 16мс — значит она тормозит и не способна выводить 60фпс. И, конечно, в этом случае будут артефакты, по-любому. Меньшие в том случае, если вы не осуществляете подкрутку симуляции и больше, в том — если осуществляете.

Видеокарта не запрашивает кадр. Это cpu говорит ей вот начало нового кадра, физика обсчитана на такое то время.
Чтобы знать на какое время считать следующий тик физики он спрашивает видеокарту сколько она рендерит кадр — драйвер отвечает 24 мс, но при этом успевает обсчитывать каждые 16 мс кадр.
Видеокарта не запрашивает кадр. Это cpu говорит ей вот начало нового кадра, физика обсчитана на такое то время.
Чтобы знать на какое время считать следующий тик физики он спрашивает видеокарту сколько она рендерит кадр — драйвер отвечает 24 мс, но при этом успевает обсчитывать каждые 16 мс кадр.

Ну вот у гореоптимизаторов так все и реализовано, что и приводит к проблемам. У нормальных людей видеокарта просто рендерит кадры по факту готовности.


Чтобы знать на какое время считать следующий тик физики он спрашивает видеокарту сколько она рендерит кадр — драйвер отвечает 24 мс, но при этом успевает обсчитывать каждые 16 мс кадр.

Вы понимаете, что в вашем описании работы гпу/цпу строго синхронна? гпу досчитала шаг — отдала цпу тайминг, цпу посчитал состояние — отдал гпу — гпу посчитала шаг…
При этом параллельно они работать не могут (т.к. цпу не может начать расчет состояния до того как ему придет тайминг, а гпу не может начать расчет до того, как будет завершен пересчет стейта), и вы добавите к затратам на рендер кадра видеокартой затраты на расчет стейта цпу. Одни проблемы.


Возвращаемся к исходному вопросу — зачем в принципе пытаться синхронизировать физику по фпс, если можно считать ее по отдельному таймеру и просто рендерить видеокартой по мере готовности?

Проблема в том, что этот отдельный таймер надо настраивать на тот же период, что и период забора кадров. Иначе как вы писали ниже будет постоянно повторяющаяся ситуация с неравномерностью видеоряда.
Иначе как вы писали ниже будет постоянно повторяющаяся ситуация с неравномерностью видеоряда.

Ну и пусть будет. Какая разница, если его никто не видит?

Неравномерностях. Картинки видно. В статье показаны отклонения на 10 мс и все замечают дерганность картинки.
В статье показаны отклонения на 10 мс и все замечают дерганность картинки.

В статье отклонения совсем другого характера, у нас же не о них речь.

Чем же они отличаются? В статье показано несколько кадров которые выводиться с одинаковой частотой, но не правильными значениями физики. Ровно тоже самое будет в случае не совпадения тиков физики и графики
Справедливости ради, полностью избежать повторяющихся кадров если время_рендеринга > времени_1_кадра невозможно. Если оба времени фиксированы то повторение неизбежно будет происходить периодически.

Вариант «давайте делать симуляцию с шагом точно равным времени 1 кадра» который предлагает Druu выглядит рабочим и более простым и надежным чем вариант с подстройкой. Он правда не лишен полностью описываемой в статье проблемы, но все же более устойчив к ней чем вариант с подкруткой.
UFO just landed and posted this here
Момент окончания рендеринга очередного кадра приблизительно известен. Это позволяет померить время между кадрами и экстраполировать ожидаемый момент времени когда будет выведен N+1й кадр. А отсюда уже можно определить что именно в этот кадр надо помещать чтобы изображение на экране шло в ногу с реальным временем
UFO just landed and posted this here
Вы просто очень бинарно все поделили: либо информации нет совсем, либо информация идеальна. Здесь же промежуточный вариант. Информация есть (поэтому на ее основе и запилили движок) и в 9 случаях из 10 эта информация верная. Но в оставшихся 10% случаев информация вроде как есть но по факту неверна. И в этом суть проблемы.
UFO just landed and posted this here
Но ведь как раз в определени времена одного кадра и появляется ошибка. Авторы статьи пытаются сделать шаг симуляции рваный времени 1 кадр, но если видеокарта иногда неправильно отдаёт время, то как определять шаг симуляции?
Нужно понимать, что приложения в ОС не работают в реальном времени, поэтому в общем случае любой таймер, настроенный строго на частоту вывода, будет несколько запаздывать. Поэтому внутренние таймеры должны работать кратно быстрее вывода. Это же касается рендеринга на gpu перед выдачей на монитор, если мы не используем G-SYNC.
Видеокарта не может ничего выводить, физически.

Прекрасно может — в экранный буфер.

Выводит монитор. И монитор выводит ровно столько кадров в секунду, сколько выводит — каждые 16мс в рассматриваемом случае.

Тем не менее если в момент времени 24 мс сменить экранный буфер, то на многих (если не на всех) мониторах частично будет выведен новый кадр. А самые новые мониторы так вообще умеют адаптировать частоту смены кадров к моментам смены буфера. Эти реально подождут до 24 мс обновляться а не тупо заберут в 16 и 32

Чтобы фпс влиял на скорость таймера надо скорости привязать к фпс. :

Еще раз: есть два возможных варианта. Либо «подкрутки нет» и Вы просто генерируете равномерную последовательность состояний мира которая выводится на экран, но тогда все оказывается привязано к fps. Либо Вы начинаете генерировать последовательность НЕ равномерную (неважно как — контролируя ли размер шага или просто выбирая какое именно из состояний мира выбрать) и налетаете на проблему с тем что скорость выборки должна в точности совпадать со скоростью вывода на экран, что нередко нарушается.

Вы статью вообще читали? В рассматриваемом случае кадры забираются по факту равномерно (раз в 16мс)

Боюсь что это вы статью не читали (а точнее не поняли). Это выводятся кадры равномерно а вот забираются данные для них неравномерно.

Если же ваша карта запросила новый кадр на рендер через 24мс вместо 16мс — значит она тормозит и не способна выводить 60фпс

Вот Вы статью прочитали а ключевой момент в ней полностью проигнорировали. Момент когда «монитор запрашивает новый кадр» и момент когда исполняемый код запрашивает новый кадр это два разных момента времени и к сожалению интервал между ними не постоянен. Понимаете? Это два разных события и задержка между ними может варьироваться в широких пределах. Обычно она более-менее стабильна, но иногда из-за многопоточного характера обработки данных в момент когда CPU должен был бы запрашивать новое состояние мира он оказывается занят другими задачами и откладывает немедленное выполнение потока с запросом. Доделав ту другую задачу CPU начинает выполнение потока, поток запрашивает таймер, а по таймеру — внезапно — натикало уже 24 мс.

В любом случае, почти все поголовно играют с включенным всинком

Мгм, в мое время v-sync поголовно выключали. Может конечно я отстал от жизни…

тройной буферизации

В случае тройной буферизации идет точно такая же привязка физики к моменту vsync а не к моменту завершения вывода в буфер. Для этого графическому движку достаточно просто округлить определяемый по таймеру ожидаемый момент вывоба кадра вверх до целого числа кадров, только и всего. И движок из описываемого примера это, разумеется, умеет делать.

20 64 80 112 128 144 160 192 208 224 240 272
40 60 80 100 120 140 160 180 200 220 240 260
48 64 80 112 128 144 160 192 208 224 240 272
64 80 112 128 144 160 192 208 224 240 272 288

Первая строчка — момент запрошенный у физического движка
Вторая — момент завершения рендеринга в буфер
Третья — момент вывода буфера на экран
Четвертая (копируемая в первую) — расчетное время следующего вывода буфера на экран исходя из предыдущего опыта
Боюсь что это вы статью не читали (а точнее не поняли). Это выводятся кадры равномерно а вот забираются данные для них неравномерно.

Я понял что вы имеете в виду, но в вашем случае игра получает корректные тайминги, а авторы говорят о некорректных, указывая это вполне однозначно. По-этому я склонен полагать, что авторы говорят о другой проблеме (именно некорректных таймингах).
В рассматриваемом же вами случае (если я понял верно) первый кадр рендерим за 16мс, потом карта что-то делает (неизвестно что, но мало ли у нее может быть занятий?) в течении 8мс и, т.о. запрашивает следующий кадр через 24мс от предыдущего запроса (о чем и отчитывается, вполне корректно). При этом сам кадр она рендерит за 8мс (или меньше) и, тем самым, успевает к отметке в 32мс.
Но в такой постановке проблема не в какой-то непонятной асихнронности и не в ошибочных таймингах — а именно в том, что игра слишком быстро (выше ожиданий, на основе которых мы подкручивали симуляцию, когда увидели задержку в 24мс) отрендерила следующий кадр. И у нас нет ведь никакой возможности предсказать заранее, что она его отрендерит за 8мс или 16мс или 20мс или сколько угодно (т.к. это в будущем). То есть проблема в рамках рассматриваемого подхода становится неустранимой принципиально, и никакое дополнительное апи или еще там чего не поможет, разве нет?

16мс, 16мс монитор, 20мс рендер:
16 32 48 64 96 112
36 56 76 96 116 136
48 64 80 96 128 144

А чего дальше свой ряд не продлили? У вас получится дерганье каждый пятый кадр, потому что он будет дублироваться на мониторе. Либо скипаться, если рендеринг будет быстрее 16мс.
Ну и с чего вы взяли, что рендер всегда 20мс? Он каждый раз разный практически всегда.
А чего дальше свой ряд не продлили? У вас получится дерганье каждый пятый кадр

Потому и не продлил, что дальше все повторяется циклически. Дерганье каждый пятый кадр, да. Потому что видеокарта физически не может больше 4 кадров отрендерить и вы с этим ничего не сделаете.
В случае подкручивания физики — у вас то же самое дерганье.


Ну и с чего вы взяли, что рендер всегда 20мс? Он каждый раз разный практически всегда.

Когда он разный, то ситуация во втором случае еще хуже по сравнению с первой. То есть я взял идеальный для "алгоритма подкручивания" вариант и в нем все плохо.

В случае подкручивание физики кадры будут идти через равные промежутки тиков физики. При включённой g-sync/freesync каждые 20мс будет выдаваться плавный кадр без всяких дёрганий
При включённой g-sync/freesync каждые 20мс будет выдаваться плавный кадр без всяких дёрганий

Действительно, при включенном g-sync/freesync ситуация будет лучше, но… только до тех пор, пока условия остаются полностью идеальными (фпс постоянный). Как только они идеальными быть перестают (фпс начнет меняться хоть как-то) — подкрутка снова превращается в тыкву, т.к. у вас нет возможности синхронизировать состояние в текущем фрейме при условии параллельной работы гпу/цпу. Только через фрейм.

Если FPS меняется не сильно (например с 60 упало до 50) то скачек будет с 16 мс до 20 мс всего 4 мс. После одного фрейма cpu пересчитает длину тиков физики и все придёт в норму.
Если FPS меняется не сильно (например с 60 упало до 50) то скачек будет с 16 мс до 20 мс всего 4 мс. После одного фрейма cpu пересчитает длину тиков физики и все придёт в норму.

Ну то есть в лучшем случае все будет ровно так же как и с равномерным таймером без подкруток.
Вот и возникает вопрос — зачем нужны подкрутки, если в любой реальной ситуации они могут лишь ухудшить положение?

Нет. В этом случае всего один кадр запоздает на 4 мс, а для всех последующих физика и рендер будет синхронным до следующего скачка FPS. В случае с фиксированным таймером скачки кадров будут каждый 5 кадр.
Нет. В этом случае всего один кадр запоздает на 4 мс, а для всех последующих физика и рендер будет синхронным до следующего скачка FPS.

Нет, будет сперва задержка, а потом кратное ускорение.
И это будет постоянно т.к. мы рассматриваем ситуация с немаксимальным фпс. То есть он, по факту, будет прыгать абсолютно каждый кадр.


Ну и да, это все в случае работающего гсинка/фрисинка, что редкость.

Был FPS 60 тик физики 16 мс и рендер 16мс. В какой то момент FPS скакнул вниз до 50. Если отследить и изменить тик физики до 20 то рендер и физика опять будут синхронны и дальше картинка будет плавной.
Если отследить и изменить тик физики до 20 то рендер и физика опять будут синхронны

Картинка никогда не будет синхронной и плавной, потому что фпс всегда будет прыгать, а в будущее заглянуть нельзя.

Картинка может будет не идеально плавной, но хотя бы не настолько дерганной как в случае с фиксированным таймером. Как еще по другому поступать то?

Да и fps прыгает только при значительной смене объектов рендера, например при смене локации в рамках же одной сцены он более-менее постоянен
Картинка может будет не идеально плавной, но хотя бы не настолько дерганной как в случае с фиксированным таймером.

В случае с таймером у вас дерганье не будет превышать дельты вашей симуляции. Учитывая уже упоминаемое gpu-bound, никто не мешает выполнять симуляцию значительно чаще, чем раз в 16мс, за счет чего и дерганье сведется к нулю.


Да и fps прыгает только при значительной смене объектов рендера

Да ну, фпс не прыгает только в одном случае — если он выше залоченого предела (60 например). То есть карта дает 80 (которые выводятся как 60) или карта дает 70 (которые как те же 60) — и, да, фпс не меняется :)
Но если он у вас ниже максимума, то обычно любое изменение сцены дает изменение фпс: шагнули, повернули камеру, какой-то непись вышел/вошел в зону рендеринга — ну то есть короче всегда, когда игрок что-то делает.

Учитывая уже упоминаемое gpu-bound, никто не мешает выполнять симуляцию значительно чаще


Мешает ограниченность CPU. Даже сейчас игры встречаются игры упирающиеся в CPU и не выдающие стабильные 60 fps именно из-за ограничений в процессоре. Для отсутствия же артефактов надо считать в разы больше.
Решение с плавующим таймером позволяет в разы снизить требования к cpu пользователя.
Мешает ограниченность CPU.

Но это исключения из правил, таких игр единицы — и в основном это скорее всякие стратегии, чем скуримы и крузисы. В редкой игре урезанием графена нельзя получить фпс под две сотни.

Две сотни FPS это обсчёт физики каждые 5 мс. В таком случае видеокарта считающая кадры по 16 мс
0 16 32 48 64 80
Получит следующие значения физики
0 15 30 45 60 80
Кадры выводятся равномерно, но вот разница по физике у них
15 15 15 15 20
Каждый пятый кадр будет на треть быстрее. Получим неравномерный видеоряд.

К тому же такие значения можно получить только на мощных компах на которых и так можно поставить фиксированный 60 FPS.
Каждый пятый кадр будет на треть быстрее. Получим неравномерный видеоряд.

Не важно, равномерный видеоряд или нет. Важно, видно это или нет :)


К тому же такие значения можно получить только на мощных компах на которых и так можно поставить фиксированный 60 FPS.

Но на этих компах как раз метод с подкручиванием тормозит, как видно из статьи :)

В статье искажения видны. Ускорения в треть также будут заметны.

На мощных компах метод с подкручиванием тормозит только потому что комп прикидывается, что не успевает рендерить.
В статье искажения видны. Ускорения в треть также будут заметны.

В статье искажения больше чем в 2 раза. Так что в треть — вполне возможно, что заметно и не будет.

Ну вот поэтому используют интерполяцию/экстраполяцию физики при рендеринге.
В случае подкручивания физики — у вас то же самое дерганье.

Ошибаетесь. Я уже приводил три (три, Карл!) возможных примера реализации с подкручиванием физики (double-buffer без vsync, double-buffer с vsync, triple-buffer с vsync) и ни в одном из них нет дергания.

Мы же рассматриваем случай с фпс меньшим 60, при наличии всинка регулярные дерганья тут будут в любом случае.

Так все примеры для 50 фпс посчитаны
Дублирующие кадры будут, да, но те что будут выводиться будут показаны верно
А что если рендер организовать построчно, как cmos матрица делает? 1080 кадров, каждый 1920x1 пиксель, после чего на лету все это в один кадр компонавать? G-sync потом это дело показывает на мониторе по окончании рендера последней линии. Да, будет rolling shutter, пусть и довольно специфический, не линейный, ну и что? Зато ни рывков ни горизонтальных разрывов. Может довольно хорошо паралелиться, особенно в верхней-средней части кадра. Есть возможность создания 1080Hz дисплея для совсем уж плавной картинки) Учитывая что это частота обновления рядков, не такая уж и большая инженерная проблема.
Ps да фантазирую, но вдруг это и вправду поможет?
Таким образом проблема предсказания физики для времени окончания рендера просто отпадает- нужно просто все строки рисовать на текущий момент времени либо совсем немного корректировать постепенно к концу кадра, тк тут мы уже будем знать достаточно информации для синхронизации всех кадров, что бы соседние строки не сильно отличались в длительности (но это чисто вариант оптимизации).
  • 1080 * 60 строчко-кадров в секунду? Это ж какая нагрузка на ЦП будет, чтобы для 65000 кадров в секунду считать физику и прочие подготовительные вычисления (например, сортировка по глубине)?
  • Rolling shutter ужасно выглядит. Не надо эту блевотину.
  • Рисование кадра — это не просто рисование 1080 линий и все. Перед этим считаются карты теней, кубические карты (анахронизм, но все равно) и буферы всякой всячины. Это константная часть, которую вам надо будет считать в 1080 раз чаще.

В общем — плохая идея.
Он выглядит ужасно на 30-и кадрах, но не на 120-и, и его не видно если рендерить в случайном порядке, а не сверху вниз. Тогда получится рендерить скажем, 1500 строчко-кадров за кадр(в зависимости от производительности), и под конец к дедлайну на отображение, использовать только последние 1080 строчек (рендер некоторых строк может повторяться), а если не успели, то брать их с предыдущего кадра. Так никакого наклона вертикальных предметов при движении не будет видно, а точность кадра по готовности значительно возрастет. Что насчёт процессора, это вопрос к оптимизации, а не к алгоритму, там что то можно придумать я думаю.
Ps.Вот бы сделать демо, как это выглядило бы в 2d, но у меня недостаточно знаний в графике для того чтобы это сделать пока, скажем просто двигающиеся линии по монитору под разными углами.
Pss. Физику можно не считать, только изменения в положении камеры.
>но не на 120-и
А вы пробовали?

>если рендерить в случайном порядке, а не сверху вниз.
Сейчас железо очень сильно полагается на пространственную когерентность доступа к памяти. Если вы сделаете случайные порядок — начнутся проблемы с текстурными кэшами, которые станут просто бесполезны, и широкой шиной памяти.

>Физику можно не считать, только изменения в положении камеры
Т.е. если вы едете в машине (камера движется) — для вас все гладко. А если машина перед вами (камера неподвижна) — она будет рывками?
А если камера привязана к физике? Ведь персонаж/машина, где едет персонаж движутся по физике.
Я предлагаю использовать функцию выбора строки рендера таким способом n=(121×n+757) mod 1080, возвращает от 0 до 1079 в цикле, без пропусков.
Рендер циклично повторяется по известному паттерну, который очень похож на рандомный. Возможно можно найти ещё более красивые алгоритмы. Как сложность — необходимо подбирать/расчитывать под каждое разрешение по отдельности. Главное приимущество — частота кадров всегда постоянная, всегда околонулевая задержка, а производительность влияет только на величину блюра(угол открытия выдержки) он может меняться от 0 градусов до нескольких сотен/тысяч градусов (где 360 — идеально плавная картинка), от кадра к кадру.
Ps я пробовал, выглядит эпично, но есть БОЛЬШИЕ проблемы с производительностью. Нужно здорово оптимизировать.
youtu.be/dNVtMmLlnoE тут объяснение эффекта. К тому же, не реалистичности ли мы добиваемся? Так давайте же симулировать физические эффекты, включая побочные что бы решить сущестующие гораздо более серьезные проблемы.
А если использовать цикличный псевдорандомный строчный dither (рендерить не сверху вниз а по повторяющемуся паттерну), можно с добавлением блюра сделать СОВЕРШЕННО ПЛАВНУЮ КАРТИНКУ без просадок, что то вроде модернизированного fastsync но с возможностью самому выборать частоту обновления кадров!

Ps прошу оценить, на сколько мой бред имеет смысл)
Вы с нетерпением ждали следующей части вашей любимой серии видеоигр для PC и она наконец вышла.

Если позволите, малость позанудствую. В оригинале сказано следующее:
You’ve been waiting for the next installment of your favorite PC game series for so long, and now it’s finally here.

Как мне кажется, лучше сказать не «видеоигр для PC», а, скажем, «видеоигр для ПК» или «компьютерных игр». Второй вариант несколько лучше, поскольку охватывает самые разные компьютерные ОС (Windows, macOS, Linux и др.).

Я в курсе, что PC — это производное от IBM PC compatible computer, но предпочитаю передавать эту аббревиатуру на русском языке, в зависимости от контекста, как компьютер, компьютерный, ПК или даже Windows (если из контекста понятно, что речь идет именно об этой ОС).
Жаль, что почти ничего не понятно, но очень интересно. Занимался когда-то 3D рендерингом (лет 15 назад), но до таких высот, конечно, не поднялся. Спасибо.
То есть, проблему в теории можно решить установкой нового железа, которое всегда обеспечит требуемые 60 fps?
Sign up to leave a comment.

Articles

Change theme settings