Последствия переписывания компонентов Firefox на Rust

Автор оригинала: Diane Hosfelt
  • Перевод
В прошлых статьях цикла мы обсудили безопасность памяти и безопасность потоков в Rust. В этой последней статье посмотрим на последствия реального применения Rust на примере проекта Quantum CSS.

Движок CSS применяет правила CSS на странице. Это нисходящий процесс, который спускается по дереву DOM, после расчёта родительского CSS дочерние стили можно вычислять независимо: идеальный вариант для параллельных вычислений. К 2017 году Mozilla предприняла две попытки распараллелить систему стилей с помощью C++. Обе провалились.

Разработка Quantum CSS началась, чтобы повысить производительность. Улучшение безопасности — просто удачный побочный эффект.


Между защитой памяти и багами информационной безопасности есть определённая связь. Поэтому мы ожидали, что применение Rust уменьшит поверхность атаки в Firefox. В этой статье рассмотрим потенциальные уязвимости, которые выявили в движке CSS с момента первоначального выпуска Firefox в 2002 году. Затем посмотрим на то, что можно и нельзя было предотвратить с помощью Rust.

За всё время в CSS-компоненте Firefox обнаружено 69 ошибок безопасности. Если бы у нас была машина времени и мы могли с самого начала написать его Rust, то 51 (73,9%) ошибка стала бы невозможной. Хотя Rust упрощает написание хорошего кода, он тоже не даёт абсолютной защиты.

Rust


Rust — это современный язык системного программирования, безопасный для типов и памяти. Как побочный эффект этих гарантий безопасности, программы Rust также потокобезопасны во время компиляции. Таким образом, Rust особенно хорошо подходит для:

  • безопасной обработки ненадёжных входящих данных;
  • параллелизма для повышения производительности;
  • интеграции отдельных компонентов в существующую кодовую базу.

Тем не менее, Rust явно не исправляет некоторые классы ошибок, особенно ошибки корректности. На самом деле, когда наши инженеры переписывали Quantum CSS, они случайно повторили критическую ошибку безопасности, которая ранее была исправлена в коде C++, они случайно удалили исправление бага 641731, который допускает утечку глобальной истории через SVG. Ошибку зарегистрировали заново как баг 1420001. Утечка истории оценивается как критическая уязвимость безопасности. Первоначальное исправление предсталяло собой дополнительную проверку, является ли документ SVG изображением. К сожалению, эту проверку упустили при переписывании кода.

Хотя автоматизированные тесты должны находить нарушения правила :visited вроде такого, на практике они не обнаружили эту ошибку. Чтобы ускорить автоматические тесты, мы временно отключили механизм, который тестировал эту функцию — тесты не особенно полезны, если они не выполняются. Риск повторной реализации логических ошибок можно уменьшить за счёт хорошего покрытия тестами. Но по-прежнему существует опасность появления новых логических ошибок.

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

Ошибки безопасности Quantum CSS


В целом, по умолчанию Rust предотвращает появление ошибок, связанных с памятью, границами, нулевыми/неинициализированными переменными и целочисленным переполнением. Нестандартный баг, упомянутый выше, остаётся возможным: происходит сбой из-за неудавшегося распределения памяти.

Ошибки безопасности по категориям


  • Память: 32
  • Границы: 12
  • Реализация: 12
  • Null: 7
  • Переполнение стека: 3
  • Целочисленное переполнение: 2
  • Другое: 1
В нашем анализе все баги связаны с безопасностью, но только 43 получили официальную оценку (её присваивают инженеры Mozilla по безопасности на основе квалифицированных предположений об «эксплуатируемости»). Обычные баги могут указывать на отсутствующие функции или какие-то сбои, которые необязательно приводят к утечке данных или изменению поведения. Официальные ошибки безопасности варьируются от низкой важности (если есть сильное ограничение на поверхности атаки) до критической уязвимости (может позволить злоумышленнику запускать произвольный код на платформе пользователя).

Уязвимости памяти часто классифицируются как серьёзные проблемы безопасности. Из 34 критических/серьёзных проблем 32 были связаны с памятью.

Распределение багов безопасности по серьёзности


  • Всего: 70
  • Ошибки безопасности: 43
  • Критические/серьёзные: 34
  • Исправлены Rust: 32

Сравнение Rust и C++


Баг 955913 — переполнение буфера кучи в функции GetCustomPropertyNameAt. Код использовал неправильную переменную для индексирования, что привело к интерпретации памяти после окончания массива. Это может вызвать сбой при доступе к плохому указателю или копирование памяти в строку, которая передаётся другому компоненту.

Порядок всех свойств CSS (в том числе кастомных, то есть пользовательских) хранится в массиве mOrder. Каждый элемент представлен либо значением свойства CSS, либо, в случае пользовательских свойств, значением, которое начинается с eCSSProperty_COUNT (общее количество некастомных свойств CSS). Чтобы получить имя пользовательских свойства, сначала необходимо получить значение из mOrder, а затем получить доступ к имени в соответствующем индексе массива mVariableOrder, который хранит имена кастомных свойств по порядку.

Уязвимый код C++:


    void GetCustomPropertyNameAt(uint32_t aIndex, nsAString& aResult) const {
            MOZ_ASSERT(mOrder[aIndex] >= eCSSProperty_COUNT);

            aResult.Truncate();
            aResult.AppendLiteral("var-");
            aResult.Append(mVariableOrder[aIndex]);

Проблема возникает в строке 6 при использовании aIndex для доступа к элементу массива mVariableOrder. Дело в том, что aIndex должен использоваться с массивом mOrder, а не mVariableOrder. Соответствующий элемент для пользовательского свойства, представленного aIndex в mOrder, на самом деле mOrder[aIndex] - eCSSProperty_COUNT.

Исправленный код C++:


    void Get CustomPropertyNameAt(uint32_t aIndex, nsAString& aResult) const {
      MOZ_ASSERT(mOrder[aIndex] >= eCSSProperty_COUNT);

      uint32_t variableIndex = mOrder[aIndex] - eCSSProperty_COUNT;
      aResult.Truncate();
      aResult.AppendLiteral("var-");
      aResult.Append(mVariableOrder[variableIndex]);
    }

Соответствующий код Rust


Хотя Rust в некотором роде похож на C++, но использует другие абстракции и структуры данных. Код Rust будет сильно отличаться от C++ (подробнее см. ниже). Во-первых, давайте рассмотрим, что произойдёт, если перевести уязвимый код как можно более буквально:

    fn GetCustomPropertyNameAt(&self, aIndex: usize) -> String {
        assert!(self.mOrder[aIndex] >= self.eCSSProperty_COUNT);

        let mut result = "var-".to_string();
        result += &self.mVariableOrder[aIndex];
        result
    }

Компилятор Rust примет такой код, потому что длину векторов невозможно определить до выполнения. В отличие от массивов, длина которых должна быть известна, у типа Vec в Rust динамический размер. Однако в реализации вектора стандартной библиотеки встроена проверка границ. При появлении недопустимого индекса программа немедленно завершается контролируемым образом, предотвращая любой несанкционированный доступ.

Реальный код в Quantum CSS использует очень разные структуры данных, поэтому точного эквивалента нет. Например, мы используем мощные встроенные структуры данных Rust для унификации порядка расположения и имён свойств. Это избавляет от необходимости поддерживать два независимых массива. Структуры данных Rust также улучшают инкапсуляцию данных и уменьшают вероятность таких логических ошибок. Поскольку код должен взаимодействовать с кодом C++ в других частях браузера, новая функция GetCustomPropertyNameAt не выглядит как идиоматический код Rust. Но она всё равно даёт все гарантии безопасности, обеспечивая при этом более понятную абстракцию базовых данных.

tl;dr


Поскольку уязвимости часто связаны с нарушением безопасности памяти, код Rust должен значительно уменьшить количество критических CVE. Но даже Rust не идеален. Разработчикам по-прежнему нужно отслеживать ошибки корректности и атаки с утечкой данных. Для поддержки безопасных библиотек по-прежнему необходимы код-ревью, тесты и фаззинг.

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

Комментарии 56

    +4
    похоже, что переводчик на результат google translate даже не взглянул…
    • НЛО прилетело и опубликовало эту надпись здесь
        +3

        Выше там приводится пример наиболее эквивалентного кода, который не завязан на особых фичах структур и который предотвращает возникновение ошибки. Фишка в том, что код, который был написан на c++ не мог быть буквально переписан на раст. Он был бы замечен за счёт внутренних проверок корректности во время компиляции.


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

        • НЛО прилетело и опубликовало эту надпись здесь
            +4
            Как кажется, автор пытался показать то, что дело не столько в структурах данных, а то что в Rust в принципе невозможна данная уязвимость (при написании safe-кода, конечно). Не всегда по тем или иным причинам изначально берется верная структура данных (никто не идеален), и автор говорит о том, что использование более безопасного языка позволило бы избежать многих проблем, с которыми они столкнулись на C++.
            • НЛО прилетело и опубликовало эту надпись здесь
                +9

                Склонен согласиться. Действительно, многие ошибки решаются хорошими встроенными типами, НО


                1. Большинство этих встроенных типов стали возможны как раз благодаря продвинутой системе типов. Аналоги на плюсах есть, но они сильно уступают по возможностям. Например, std::visit против нормального АДТ + паттерн матчинга, или unique_ptr/shared_ptr вместо родных растовых &mut T и &T с настоящим мувом.
                2. Раст в целом поощряет писать более абстрактные вещи, которые могут помочь найти внутренние противоречия программы, и раньше получить ошибку. Например там, где я в шарпах часто пишу int index в расте будет<I: Index>(index: I), с реализацией трейта для i32/usize/… В итоге для разных коллекций может быть реализован разный Index, и случайно обратиться в одно коллекцию по индексу другой не получится. Да, на любом языке можно навертеть таких проверок, но вопрос, где это проще сделать. Немного замороченный пример, где автор слегка упоролся типами. Но в целом, стараются писать достаточно обобщенно, и авторы библиотек, и прикладного кода. Никто не заставляет этим пользоваться, но возможность всегда есть. Оно с одной стороны проще темплейтной магии (хотя по коду по ссылке сказать это затруднительно), с другой надежнее, потому что работает для открытого множества типов Т, а не зависит от того, куда мы темплейт раскрываем.
                • НЛО прилетело и опубликовало эту надпись здесь
                    +4
                    В оригинальном коде ошибка в том что просто напросто использовали не ту переменную (точнее поле объекта). И вряд ли Rust как язык способен от такого защитить.

                    Вполне возможно, что в растокоде написали


                    struct MOrderIndex(usize);
                    struct MVariableOrder(usize);

                    И соответственно все в компайл-тайм выльется.


                    Очень примерный псевокод, как это может выглядеть: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=3fdbb4dcb698d06e9768e872cd3c5341


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

                    • НЛО прилетело и опубликовало эту надпись здесь
              +6
              В c++ тоже можно было бы взять правило — использовать только специальный тип вектора (с проверкой границ).
              Разница в том, что в с++ «можно», а в раст — «обязан». Код без проверок диапазона возможен только в unsafe блоках, а потому вызовет пристальное внимание и желание/жжение менеджера по глюках его убрать. В раст много всяких таких вещей просто встроено в платформу, это и делает его отличным от с++.
              И для с++ можно было написать кучу инструментов, вон статические анализаторы пишут. Правил нагородить и линтеров для них === порезать язык по самые гланды. Но это уже была бы платформа не «с++», а вот та «куча инструментов».
        • НЛО прилетело и опубликовало эту надпись здесь
            +4
            В Firefox 66 появилась настройка gfx.webrender.all в about:config, включающая webrender, который вроде бы частично на Rust.
            Можно попробовать ее выставить в true и сравнить например на output.jsbin.com/surane/quiet
            На мобильном процессоре ноутбука у Firefox – плавная анимация, у Chrome и Firefox без включения webrender – слайдшоу.
              +2
              А если открыть инструменты разработчика, то сразу становится понятно, что узкое место в демке — перерасчет стилей. Казалось бы, как здесь может помочь WebRender?..

              prnt.sc/n47kkv — Chrome
              prnt.sc/n47m4r — Firefox с включенным WebRender'ом
                +2
                В Firefox 66 появилась настройка gfx.webrender.all в about:config, включающая webrender, который вроде бы частично на Rust.
                Не частично, а практически полностью. Это результат адаптации одного из компонентов проекта servo — нового параллельного броузерного движка полностью написанного на Rust. CSS движок о котором говорится в статье (stylo) оттуда же.
                  +1
                  В Firefox 66 появилась настройка gfx.webrender.all в about:config, включающая webrender, который вроде бы частично на Rust.

                  Не в FF 66, а намного раньше.
                  «gfx.webrender.all = true» — этого мало.
                  «gfx.webrender.all.qualified = true» — всё равно мало.
                  Нужны ещё переменные среды «MOZ_ACCELERATED=1 MOZ_WEBRENDER=1».
                  С ними всё равно отстаёт от Хрома.
                  Я пробовал ускорить старые машины, на них с WebRender скорее медленнее, чем быстрее, и памяти больше жрёт.
                  Пока что WebRender — просто игрушка и задел на будущее, WebGL примерно то же самое по скорости.
                  Для желающих пощупать Мозилла советует иметь Win 10 + видеокарту Nvidia (достаточно новую).
                  Для ускорения Firefox на сегодня лучше вложить деньги в процессор и т.д. чем в новую видеокарту.
                    +2
                    В 66 версии она появилась в релизной сборке а не только в ночнике.
                  +1

                  На выходе нативный код. В теории не медленней С/С++. На практике зависит от алгоритмов и оптимизатора.

                    +3
                    Разработка Quantum CSS началась, чтобы повысить производительность. Улучшение безопасности — просто удачный побочный эффект.

                    Оно все и затевалось в первую очередь для улучшения производительности. Конкретнее об этом аспекте можно почитать в прошлых статьях цикла, на которые есть ссылки в начале статьи.

                      +6
                      У мозиллы в доках по переписыванию записано правило, что результат переписывания должен иметь околоэквивалентную (вроде 2% погрешности) или лучшую производительность. Собственно поэтому они не мгновенно выкатывали куски квантум рендера, а только после выполнения вот этих условий.
                        +6
                        После перехода на Quantum Firefox впервые запустил Facebook на древнем андроиде с 1Гб оперативки, более того — с вменяемым откликом на пользовательский ввод. Старые ФФ тупо падали во время загрузки от нехватки памяти и тормозов.
                        Также можно без особой опаски пользоваться релизными сборками под винду — ничего не висит и не жрет ресурсы. Раньше таким мог похвастаться только ночник.
                          0
                          С последним релизом на Linux Mint с NVIDIA окончательно отвалилось аппаратное ускорение. С ним теперь весь браузер — просто белый экран (раньше только окна расширений не показывались). А без него всё тормозит, проц молотит и греется, видосики не выше 720. Вынужденно перешёл на Vivaldi.
                          Под KDE при этом всё работает как раньше (плохо и с костылями), но есть ощущение, что это вопрос времени.
                          Я понимаю, что Mozilla не хочет разбираться с кривыми дровами Nvidia, но FF с ними сейчас хуже некуда. Свободолюбивые параноидальные пингвиноводы переходят на Хром: он работает.
                            +1
                            А что за драйвер? У меня 415.27, все в норме.
                              +1
                              Сейчас 418. Был, наверное, 415. Возможно дело в модели видюхи: у меня 1080Ti, и она, например, с nouveau работает, только если отключить modeset. Вы layer_acceler.force_enable ставили? У меня весь затык в ней: с false всегда тормозило, с true до недавнего времени худо-бедно работало. 1080 — настолько новая видюха, что в списки поддерживаемых она ещё не попала.
                              А вообще уже не важно: Vivaldi мне понравился. Но если бы не этот глюк, наверное не собрался бы попробовать.
                                +1
                                Нет, layers.acceleration.force-enabled стоял в false. Поставил true, ничего не поменялось, видео тянет и UHD спокойно; GTX980.
                              +1
                              Почему Mint, а не, например, Ubuntu? Может там получше с этим? И кстати, я не понимаю, каким образом работа связки браузер + видеодрайвер зависит от используемого менеджера рабочего стола… То есть если новая версия лисы не дружит с драйвером — странно что она у вас с ним дружит под KDE.
                                +1
                                Между браузером и видеокартой лежит композитный оконный менеджер, и он в разных DE очень сильно разный. Поэтому в разных DE разная производительность видео и игр, а так же свой набор особенностей, связанных с графикой. Приводить это всё к единому виду никто не спешит, потому что грядет Wayland, а Wayland не спешит, потому что некуда. Сидим на вантах.
                                  0
                                  А что, в режиме игр композитный оконный менеджер тоже участвует? Там же не надо ничего смешивать. Я думал, игры в полноэкранном режиме получают монопольный доступ к видеоадаптеру. А, стоп… Речь про внутриигровые оверлеи?)
                                    0
                                    Не только оверлеи. Я не уверен есть ли в современных WM опция делать это автоматом, но вообще есть сообщение _NET_WM_BYPASS_COMPOSITOR, которое все современные фреймворки посылают для фулскрина и композитор отключается (если WM конечно нормальный).
                                      0
                                      Есть опция автоматически отключаться для fullscreen окон. А вот включена ли она по-умолчанию, зависит от дистрибутива. Кроме того, юзвери часто выключают её в надежде побороть tearing.
                                        0
                                        А если вернуться от Linux к Windows, то отключается ли там в полноэкранном режиме DWM?
                                          0
                                          В играх часто 2 режима добавляют, exclusive fullscreen — в нем отключается; и fullscreen windowed (или borderless windowed), в нем нет, но из-за отсутствия возможной пере-инициализации переключение между игрой плавнее. Но на самом деле у обоих режимов бывают свои глюки, вот например для borderless steamcommunity.com/app/292030/discussions/0/615085406652736452

                                          Для абсолютной плавности, кстати, нужен еще VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE, другого пути пока нет, даже используя свопчейн и любой из методов презентеции включая freesync, gsync, Fast Sync etc. Оно конечно может и без него хорошо заработать, но не на любой хардваре, не в любом случае.
                                            0
                                            Вы сейчас про плавность в самой игре, чтобы и без рывков, и без тиринга?

                                            У меня например есть забавный опыт с Apex Legends и старой видеокартой AMD HD7850 (драйвера не такие старые, им год с небольшим): в режиме полного экрана если сделать Alt+Tab, то игра постоянно будет «пытаться» вернуться на экран — то есть сначала появится рабочий стол, можно будет кликнуть по какому-то ярлыку, запустить, например, браузер — но при сворачивании текущего активного окна игра будет переключать фокус на себя (и это не мгновенно, как вы понимаете). А потом её надо снова сворачивать через Alt+Tab. Именно из-за этой особенности я переключился в настройках в режим «fullscreen window». Но, должен сказать, такое наблюдается далеко не во всех играх — одно время этим страдал Fortnite (потом вроде пофиксили). Counter-Strike и большинство игр из Steam в этом плане ведут себя вменяемо и в полноэкранном режиме.
                                              0
                                              Чтобы победить тиринг, достаточно методов презентации. А вот чтобы корректно проводить рассчет мира, замеров на уходе кадра в свапчейн недостаточно. Нужно знать реальные времена отображения, причем неважно переменная дельта в игре используется или постоянная с высоким fps. Я пробовал использовать что-то типа noise фильтра в измерениях, но не получилось, уж больно разных характер искажений на разном железе; VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE — зе бест.
                                                0
                                                Что вообще означает это название? Вбил в гугл, ничего вменяемого он не выдал. Почему вообще там слово Google в названии?))

                                                И я не очень понимаю, в чём сложность расчёта мира. Разве нельзя просто создать таймер, срабатывающий раз в какой-то определённый интервал? В чём такой расчёт будет некорректным? Даже если мы пишем серверную часть игры — главное ведь, чтобы каждый тик считался для всех игроков одновременно (а так оно и будет), и уже дальше они получали результаты этой обработки в свой буфер/сокет. Если же у нас сингловая игра — всё ещё проще, не будет потребности писать хороший код предсказаний для клиента.

                                                Что я не учитываю?)

                                                Время, которое уходит на отрисовку, скорее всего будет меньше времени одного тика расчёта мира. Если больше — то плохо, но в любом случае рендер графики всегда выполняется в отдельном потоке. Теоретически можно его даже в отдельный процесс вывести. Тогда считать координаты объектов и коллизии мы будем отдельно, а на отрисовку просто выдавать асинхронные запросы после завершения расчёта каждого тика. Если тик посчитался быстрее, чем надо — то уже на поток рендера можно возложить задачу «немного подождать» до 1/60 секунды. Или смотря какой там стоит frame cap в настройках у игрока.
                                                  0
                                                  Гугл в названии, потому что это гугловое расширение, но десктопными драйверами поддерживается.

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

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

                                                  Чтобы ситуацию с прогнозом улучшить, гугл добавил 2 вещи: обратная связь от свапчейна, когда были реальные времена отрисовки и указание желаемого времени отображения.
                                                  Есть пример github.com/KhronosGroup/Vulkan-LoaderAndValidationLayers/blob/master/demos/cube.c#L1120
                                                  лучше чем он я наверно не расскажу.
                                                    0
                                                    Если мы ошибемся и эта ошибка между кадрами будет не константой — будет неплавность

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

                                                    А как же старые игры, выходившие в конце 90-ых — начале 00-ых? Тогда этого расширения ведь не было. Но жили же как-то? Игралось всё, причём довольно-таки плавно.

                                                    UPD: посмотрел код, идея в целом понятна (хотя не совсем, если честно).

                                                    Опять же, у нас есть GPU. У неё есть кадровый буфер. Если мы (движок игры) будем писать в этот буфер чаще, чем 60 раз в секунду, то особой проблемы не будет, т.к. GPU сама выберет из потока кадров каждый N-ый. Но чтобы рывков не было совсем — лучше конечно писать в этот буфер с частотой, равной частоте обновления монитора * N, где N — целое число. Иначе рывки таки будут.

                                                    Если например в игре стоит ограничение кадров 60 в секунду (очень старые игры некоторые вообще имели жёстко ограниченный FPS в 30 кадров), то мы можем помещать данные в буфер строго 30 раз за секунду — главное не опаздывать. Опять же, если GPU отрисует быстрее чем нужно — не проблема, она всё равно подождёт следующего обновления развёртки дисплея. А вот если она будет рисовать дольше… Но это уже говорит о том, что пользователь выставил неоптимальные для себя настройки графики.

                                                    Ещё есть такое соображение: на реальной системе скорость выдачи кадров действительно будет плавать. Дело в том, что разные места на карте создают разную нагрузку на процессор (и если игра идёт впритык, FPS будет проседать в «тяжёлых» местах). Также количество врагов вокруг и всяких пуль и снарядов в радиусе вокруг тоже влияет. И количество взрывов/облаков дыма, если они не полностью на GPU обрабатываются. Да даже если полностью, а видеокарта слабенькая.

                                                    Я что хочу сказать — у нас всё равно будет сильно проседать FPS в реальной ситуации, если только железо не является супер-мощным для данной игры (с запасом процентов в 40-50). И проседать он будет в местах, которые предсказать эвристикой внутри рендера почти невозможно (т.к. рендер ничего не знает про логику мира). И проседать будет процентов на 15-40. Такие просадки не компенсировать методом, который пытаются использовать авторы этого кода, насколько я могу судить. Поправьте, если не прав.
                                                      0
                                                      Да, если константа то все норм.
                                                      Проблема на самом деле была всегда. Раньше во всяких квестах и в 320x200 проблема была не заметна, а с какого-нибудь quake тиринг уже стал известной проблемой.

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

                                                      Да, если взять большое окно усреднения, то при ограничении на рефреш рейте все должно быть ок пока успеваем, но все станет очень плохо при просадках, что неприемлимо. Малое окно усредненеия — мешают спайки задержки. Такие дела. Задержать кадр тоже нельзя, при Fast Sync их там 3 штуки. Вот он кадр, готов, мы специально его рассчитали через синк, потому что знали что не успеем. Ждать опустошения очереди, а потом ударными темпами наполнять?

                                                      Вот только зачем все это шаманство, когда можно знать точно и можно намеренно пропуситить отображения, если знаешь что не успеешь? Все как раз куда проще становится.

                                                      PS: Я по факту делал распечатку дельты между тем, что выдает расширение и обычным предсказанием. Увы не всегда она менялась плавно чтобы быть незаметной.

                                                      PS2: Вот еще показательное видео www.youtube.com/watch?v=mVNRNOcLUuA
                                                      даже при наличии freesync и gsync из коробки идеально может и не заработать, надо шаманить.
                                                        0
                                                        Раньше во всяких квестах и в 320x200 проблема была не заметна, а с какого-нибудь quake тиринг уже стал известной проблемой.
                                                        Так vsync опция давным давно была доступна. Как она реализована в таком случае?

                                                        Вот он кадр, готов, мы специально его рассчитали через синк, потому что знали что не успеем. Ждать опустошения очереди, а потом ударными темпами наполнять?
                                                        Поделитесь, пожалуйста, ссылкой, где можно подробнее почитать теорию. Я половину не понял если честно)

                                                        но все станет очень плохо при просадках, что неприемлимо
                                                        Да куда уже хуже? Если у нас было 60, а стало даже 54-59 — мы неизбежно это заметим (я замечал в том же Fortnite, когда пытался захватывать сохранённую игру через Fraps). Или таки можно как-то скорректировать это так, что для игрока будет незаметно? Вообще, видеодрайвер выдаёт кадры строго с постоянной частотой, или тогда, когда мы ему скажем выдать следующий?

                                                        Задержать кадр тоже нельзя, при Fast Sync их там 3 штуки.
                                                        Почему вообще нельзя довериться видеодрайверу (наполняя буфер с несколько большей скоростью, чем требуется)? Потому что превышение нужной скорости на 5-10 процентов, а не вдвое, создаст рывки в движении? И зачем там именно три кадра? Я не в первый раз уже про тройную буферизацию слышу, но не очень понимаю смысл этого понятия.

                                                        UPD: почитал в вики и здесь. Всё равно не очень понял, каким образом поможет наличие третьего буфера при просадке. Если у нас резко упал FPS до 50-55, и мы серьёзно опаздываем — то монитор будет отображать наши кадры уже не по 1/60 секунды, а дольше, что при постоянной скорости пересчёта логики уже сделает движение более дёрганным — а если FPS будет ещё и скакать, то и степень дёрганности будет меняться. Я не очень представляю, как исправит ситуацию наличие дополнительного кадра «про запас» — это может вырулить только при очень незначительном и коротком подлаге, когда мы вот прямо сейчас запоздали с отрисовкой кадра однократно — но и то нам придётся наверстать по-хорошему, чтобы снова создать себе эту «фору».
                                                          0
                                                          Мм, навскидку вот тут более менее понятно про несколько популярных методов
                                                          www.youtube.com/watch?v=L07t_mY2LEU
                                                          Nvidia Fast Sync это и есть адаптация OpenGL Triple Buffering для DirectX.

                                                          При просадке очень сильно помогает, дело в том, что для глаза пропуск одного цикла отрисовки, но отображение следующего в идеальной позиции мира для этого времени воспринимается гораздо лучше, чем ошибка в позиции мира, без пропусков. Так что с расширением можно сделать чтобы движок идеально адаптировал задержку и все отрисовки были в идеальной позиции, несмотря на то, что кадры пропускаем. Более того, из-за спайков без расширения даже при упирании в vsync все равно могут быть небольшие проблемы из-за небольшого (обычно) дрожания при малом окне усреднения. Магия идеального подбора лимитера в 59гц — это все отсюда ноги растут. С расширением таких проблем нет, умеренные спайки будут незаметны вообще. Проблемы возникнут только при очень резком подскоке, тогда движок не сумеет быстро адаптировать задержку, чтобы был запас для пропуска кадров.
                                                            0
                                                            При просадке очень сильно помогает, дело в том, что для глаза пропуск одного цикла отрисовки, но отображение следующего в идеальной позиции мира для этого времени воспринимается гораздо лучше, чем ошибка в позиции мира, без пропусков. Так что с расширением можно сделать чтобы движок идеально адаптировал задержку и все отрисовки были в идеальной позиции, несмотря на то, что кадры пропускаем.
                                                            Примерно идею понял. А почему нельзя возложить эту задачу на видеодрайвер? Ну то есть прямо в него интегрировать этот режим. Смотрите, у нас ведь есть логическая часть движка, которая считает мир — и ей на проблемы видеокарты должно быть глубоко фиолетово (особенно если игра мультиплеерная). Не справляется — что поделать. Так что имхо логично передавать целую очередь кадров на упреждение (но не очень большую, чтобы игрок не заметил сильного лага, как раз 5-6 кадров самое то). Более того, теоретически, в случае, если игрок не двигается и есть ресуры, можно просчитать будущие позиции объектов, и попробовать отправить кадры «с предсказанием». А потом они могут сойтись с реальной ситуацией или не сойтись (если игрок вдруг резко побежит или подпрыгнет). Правда, проблема в том, что отозвать назад из такой очереди уже ничего будет нельзя, потому что она в видеопамяти будет уже на тот момент… Вроде бы.

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

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

                                                            Магия идеального подбора лимитера в 59гц — это все отсюда ноги растут.
                                                            Почему не 60, потому что один период суммарно уходит каждую секунду на «накладные расходы»?
                          • НЛО прилетело и опубликовало эту надпись здесь
                              +2
                              Шо опять?
                                +3

                                Почему? Как по мне, почти никого не колышет что там под капотом у браузера, его доля падает по другой причине. При прямом сравнении огнелис проигрывает хрому в удобстве, вот это действительно важно. Но главное, Firefox не рекламируют, а гугл очень настойчиво предлагает поставить хром на каждом своём сайте.

                                  +2
                                  При прямом сравнении огнелис проигрывает хрому в удобстве, вот это действительно важно

                                  В чём именно проигрывает? Например, функция поиска на странице. Как в хроме искать слова целиком или с учётом регистра, почему выделенный текст не попадает в строку поиска сразу при нажатии Ctrl+F? Или управление закладками, оно в зачаточном состоянии, не поддерживает тэги и т.д. и т.п. UI/UX оригинального хрома примитивный и дубовый, при этом практически не расширяется дополнениями и не развивается со времён появления.

                                    +2

                                    Давно пользуюсь лисом. После его чистой установки трачу время на настройку, так как иначе он раздражает, с хромом иначе — поставил и норм.
                                    Самая раздражающая штука — прокрутка страницы, значительно хуже чувствуется в сравнении с хромом и эджем. Она медленнее и плохо откликается на колёсико мыши.
                                    Новые закладки складывает в подменю Другие закладки. Может вам и нужна эта сложная система закладок, но я ценю простоту.
                                    Табы вкладок создаются после последней, а не текущей. Мешает когда вкладок становится много.
                                    Предупреждения о закрытии вкладок.
                                    Нет встроенного поиска по картинкам, переводчика, спелчекер лучше отключить чем он будет подсвечивать английские слова.
                                    Все эти настройки можно поменять только из about:config или поставив расширения. Но тут нужно знать что искать.
                                    До недавнего времени в огнелисе Новая вкладка была значительно хуже хрома, интерфейс был не менее дубовым по умолчанию, а сейчас он мало поддаётся изменениям с помощью расширений.

                                      +2

                                      Прокрутка страницы со сглаживанием или без раздражает?


                                      Табы — это вообще больное место хрома. ctrl+tab всегда работает в порядке расположения вкладок. Нельзя таким образом переключаться между двумя часто используемыми вкладками, что тоже порядком раздражает. Функциональность табов никак не конфигурируются вообще, ни в настройках, ни расширениями (у них просто нет доступа к этой подсистеме).


                                      Предупреждения — это видимо по умолчанию. В хроме по умолчанию сессия тоже не сохраняется при закрытии.


                                      В спеллчекер можно добавлять словари. Но в файрфоксе действительно есть проблема с multi language проверкой. Нельзя проверять одновременно и русский, и английский, например, только что-то одно, переключая словари. И расширение, которое это делало отвалилось при переезде файрфокса на мультипроцессинг.


                                      В десктопном хроме кстати до сих пор нет встроенного "просмотра для чтения"


                                      Агрессивное навязывание и распространение хрома особо раздражает, да. :)

                                        0
                                        Прокрутка страницы со сглаживанием или без раздражает?

                                        Прокруткой без сглаживания совсем не пользуюсь. Плавная — раздражает. Выставляю такие параметры, если вам интересно:


                                        general.smoothScroll.mouseWheel.durationMaxMS;150
                                        general.smoothScroll.mouseWheel.durationMinMS;100
                                        mousewheel.min_line_scroll_amount;48
                                        general.smoothScroll.msdPhysics.enabled;true

                                        Хотел сделать так же как в Edge, но недокрутил, невозможно найти внятное описание настроек. Где в исходниках браузера закопан алгоритм, хз.


                                        Предупреждения — это видимо по умолчанию

                                        browser.tabs.warnOnCloseOtherTabs можно изменить только из about:config.
                                        Так и поведение вкладок тоже — только browser.tabs.insertAfterCurrent, в интерфейсе нет.
                                        А вот, например, изменить папку для закладок, внезапно, даже из конфига нельзя, способно только расширение.
                                        Для спелчекера есть объединённый словарь, но тут есть и другие проблемы с проверкой орфографии.
                                        Ещё, исправляю корявый рендеринг шрифтов параметром gfx.font_rendering.cleartype_params.rendering_mode;5.

                                          0

                                          Для сглаживания скроллинга я когда-то пользовался расширением "Yet Another Smooth Scrolling", которое очень классно работало, и у которого были удобные визуальные настройки кривой сглаживания. Но потом как-то перешёл на скроллинг без сглаживания, а сглаживание стало как раз раздражать.

                                            0
                                            А в какой версии убрали галочку из окна предупреждения о закрытии нескольких вкладок? У меня предупреждения нет, в about:config для этого я, конечно же не лазил
                                            +1
                                            Предупреждения — это видимо по умолчанию. В хроме по умолчанию сессия тоже не сохраняется при закрытии.
                                            Но если нажать Ctrl+Shift+T, то Chrome восстановит после открытия сессию (то есть набор вкладок, который был открыт в момент закрытия).

                                            Кстати, в этом участке кода у них есть серьёзный баг, из-за которого полностью крашится Win XP и гаснет экран (на версиях Хрома, которые её ещё поддерживали, ясное дело), если вкладок очень много. Видимо, какой-то memory violation при сохранении вот этого самого сеанса. Или race condition. Хз. Я сначала думал на проблему со старой версией видеодрайвера, а теперь мне кажется, что он тут ни при чём, это именно доступ куда-то в RAM по неверному адресу, либо слишком большая пиковая нагрузка на HDD (и тут уже косяк из-за AHCI драйвера для XP, который к слову невероятно быстрый, куда быстрее того, что в Win 7).
                                            0
                                            Новая вкладка была значительно хуже хрома
                                            Чем именно? Top Sites одинаковый и там и там. А что ещё можно делать с её помощью?

                                            Табы вкладок создаются после последней, а не текущей.
                                            Некоторым это удобнее. В Opera Presto по умолчанию например тоже было так. Но посидев 3-4 года на Хроме, я тоже привык к тому, что новые вкладки идут не в конец (когда вкладок очень много, это правда спасает, что не надо крутить до упора вправо).

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

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

                                          В чем, ну кроме может отладчика?
                                          Но главное, Firefox не рекламируют, а гугл очень настойчиво предлагает поставить хром на каждом своём сайте.

                                          Вот тут, да. Правда может возникнуть вопрос, зачем ставить какую-то программу от разработчиков, скажем gmail, она ведь может оказаться с таким же убогим интерфейсом, но возможно не все пользователи об этом думают.
                                        0
                                        Пусть знающие скажут (а незнающие промолчат):
                                        Чем Rust лучше Ada + Ravenscar profile и SPARK (programming language)?
                                        Зачем нужно было создавать ещё одну сущность?
                                        Могло ли хватить дополнения Ada и SPARK нужными абстракциями?
                                        Мне действительно хочется узнать.
                                        Пока что предполагаю одну причину: разработчики писали на C/C++, на алгол-паскалевский синтаксис переходить не хотелось.
                                      • НЛО прилетело и опубликовало эту надпись здесь

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

                                        Самое читаемое