ИГРА БЕЗ РАЗРЫВОВ

Игры под PC. Спайки, лаги, подтормаживания. В контракте описаны, спорить сложно.

Но что делать если НЕ БЫЛО НИ ЕДИНОГО РАЗРЫВА? На выходных возникла идея потестировать стримминг на PC. В результате усилий родился вот этот уродец http://ifolder.ru/6963373

Есть плоский файл, объемом на гигабайт. Камера движется в плоскости XY, в определенной области видимости создаются текстуры ( POOL_MANAGED, честная функция CreateTexture ). Для текстуры создается файловый запрос со случайного смещения. Файл открывается с флагами FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED. Я немного напомню – первый флажочек отключает буфферизацию IO на уровне файловой системы, второй флажочек разрешает асинхронные операции.

Асинхронное чтение в Windows работает очень просто – ты даешь внутрь указатель на область куда читать, размер и указатель на OVERLAPPED структурку. После начала операции в структуре взводится флажочек IO_PENDING, после завершения чтения этот флажок будет снят.

Утверждается, что асинхронный вызов ReadFile представляет удобные средства абстракции для DMA операций с жестким диском. Запросы могут складываться в очередь, выполняться в оптимизированном порядке, так далее. Цена асинхронного чтения без буфферизации – все смещения и размеры должны быть не абы какими, а кратными грануляции жесткого диска ( обычно степень двойки, до 4 килобайт ).

Итак, технология ниппель. Текстура создается в managed пуле с помощью CreateTexture. Выделяется с помощью VirtualAlloc буфер с правильным выравниванием, туда мы направим чтение со случайного смещения в ресурсном файле. Когда чтение будет завершено – мы зальем данные в текстуру. Могли бы в ReadFile передать и прямо Lock от текстуры, да только этот буфер с неправильным выравниванием.

Здравствуй, тройное копирование! Впрочем, в реальности все грамотные пацаны используют всякие LZO и zlibы, так что копирование сразу после дисковых операций можно заменить на декомпрессию.

Итак, написал я программку, 70 мегабайт активная область с текстурами. В уме держим 70 мегабайт на managed зеркало и 70 мегабайт на эти самые области стримминга, выделенные с помощью VirtualAlloc. Всего 512 мегабайт памяти на одышливом ноуте, с трудом треть свободна.

И – все летает. Стабильные 6-7 мегабайт в секунду трафика с жесткого диска. Больше он выдать не может – когда камера останавливается, то еще 3-4 секунд текстуры догружаются. Никаких спайков. Никаких лагов. Никаких тормозов. НЕТ РАЗРЫВОВ.

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

Возникает ощущение что тормоза начинаются там, где кончается асинхронность. Надо отправлять сразу десять файловых запросов. Лучше сотню. Лучше тысячу. Никогда не ждать. Выполнился запрос – и слава Богу. Вы видели такой код в играх? Я – нет.

И у меня в проекте унылое говно, как и у всех. Куча синхронных чтений. Что асинхронное – то memory map от файла, который как-то там в соседнем потоке дрочится. Чуть менее унылое говно, но всего лишь чуть.

И тормозит именно этот код. А не мифическая “говноплатформа PC”. Не отмазывайтесь. В ваших проектах нет асинхронного стримминга, а есть куча синхронного говнокода.

  • http://users.livejournal.com/_zerg/ _zerg

    > Стабильные 6-7 мегабайт в секунду трафика с жесткого диска. Больше он выдать не может
    Есть мнение что выдать то может, только ты (прога) не можешь быстрее их обработать.

  • IronPeter

    Я ничего не делаю, я жду, когда мне скажут “готово”. Нагрузка процессора кстати 30-40%.

    Диск такой. Древний, 5400 оборотов. Файлик как лег – так лег. Куски случайные, по 150 килобайт. Так и должно быть.

  • http://users.livejournal.com/_zerg/ _zerg

    Случаные кстати не смотрел, а на последовательном чтении у меня получалось несколько больше.

  • IronPeter

    Может, где-то в стране эльфов игра и читает все данные для уровня единым куском. Но эта страна страшно далеко от нас.

    В обычной PC игре речь идет скорее про тысячу-другую разнородных ресурсов на уровень, раскиданных в ресурсном файле. Живут обычно с таким. Пытаются жить, точнее.

  • look4awhile

    на PS2 был dvd, какой читал 3.5 мб в сек
    и 300 мсек seek – какой лечил от желания читать непоследовательно
    кто в армии служил – тот в цирке не смеётся

  • IronPeter

    Да я про PC. Вон TBM тоже вроде читает из плоского media – файла. Всего лишь относительно последовательно. Я не видел, чтобы на PC лечили шаринг дублированием ресурсов.

    А вообще кажется забавным закон 10-20%.

    Вот возьмем пиковые характеристики.

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

    Пик пропускной способности памяти. Пик убивается случайным доступом.

    Пик по flopам. Пик убивается доступом к памяти, провисаниями конвеера процессора.

    Пик видеокарты по треугольникам. Убивается обработкой пухелей.

    Пик обработки пухелей. Убивается мелкими треугольниками, сэмплингом текстур. Доступом к памяти в разном виде убивается.

    В результате все друг друга убивают. И от пиковых значений остаются 10-20 процентов.

  • Dmitry Tyurev

    В том говно-стримминге, который видел недавно, чтение с диска почему-то было наименьшей из проблем. Да, memory map в потоке, но тормозов и рывков от него не замечено. А вот с чем проблемы есть:
    - Угадать какие ресурсы потребуются в ближайшее время.
    - Что делать, если нужно рисовать текстуру, а она ещё не загрузилась.
    - При первой отрисовке текстуры в кадре наблюдается рывок. Для dxtN текстур рывок сильнее, для непакованных – слабее. Такое ощущение, что драйвер как-то тяжело конвертит dxtN текстуры перед их копированием в локальную память. Сделать это асинхронным, вроде как, нельзя. Только размазать – создавать по одной текстуре в кадр.

  • IronPeter

    На NV для DXTN достаточно сказать memcpy. А непакованные скорее всего посвиззлят. Вроде обратная ситуация.

  • Dmitry Tyurev

    Угу. Но почему-то рывок намного сильнее именно с dxtn. Тестировал на радеоне. К счастью текстур больше 512х512 почти нет, а то было бы совсем фигово.

  • CEMEH

    Петя, а ты это все на XP или на Висте сморел?

  • IronPeter

    Семен, одышливый ноут с 512 мегабайтами памяти – как туда Виста встанет-то :)?

  • Aliot

    На Радеоне свиззлят и DXTn текстуры.

  • Dmitry Tyurev

    Антон, а вы это побороли? В смысле, получилось ли сделать плавный стримминг текстур на радеоне?

  • http://users.livejournal.com/_zerg/ _zerg

    Семен, а есть особая разница между ХР и Вистой?
    Петр, я потестил со случайным чтением – у меня производительность даже выше, чем с последовательным чтением.

  • Aliot

    Через задницу.
    Надо отрисовывать (типа пререндер) по мипмапам.
    То есть сначала дальний итд.
    Вернее, можно сразу по несколько слоев.
    Тогда рывок меньше.

    Полностью побороть нельзя – например, для 2048х2048 эта проблема заруливает все остальные проблемы при стриминге текстур.
    Потому, хоть я и согласен с Петром в целом про то, что все козлы – но PC-таки говноплатформа.

    Куча хаков и куча трюков – это не одно и то же. На PC – все через хаки.

  • Aliot

    Ну и кстати.
    В наших проектах куча синхронного говнокода, да.
    Но в одном из наших проектах был один сплошный стриминг.
    И он, сука, тормозил.
    Причем именно так – на отрисовки первого кадра с:
    * новым шейдером (не компиляция – прогрузка. но это хоть удалось победить.)
    * новой текстурой

    При том, что текстуры даже были в pool default. И протормозы были именно на свиззлинге, как мне кажется.
    И была куча pre-render и всякой прочей фигни.
    Разной для АТИ и и НВидия. итд. И если удавалось влезть в видео-память – то все работало вменяемо. Но то был пот и слезы.

    С тех пор на PC я и люблю синхронный говнокод – синхронно все загрузили, и усё.
    Все наши игры грузятся несколько секунд – с винта.

    А на приставках – можно и стримать. Но не все.

  • IronPeter

    Aliot, а можно снять по фреймам статистику, что грузится? Просто – width height FMT. Сколько там за 10-20 секунд всего загрузилось.
    У меня есть очень простое чувство. Что ежели суммарный объем всего стаффа не сильно больше LVM и если прогнать эту статистику в те же 10 секунд и параллельно рендерять эти текстуры на кубиках – то никаких рывков не будет. Ни на радеоне, ни на NV. Готов поспорить на пиво.

    Если случайный доступ с диска быстрее последовательного по throughout – то надо сесть и аккуратно глазами поискать ошибку

  • Dmitry Tyurev

    > Надо отрисовывать (типа пререндер) по мипмапам.
    >

    Кто-то из местных говорил, что текстура не может грузиться в LVN по отдельным мипам – только вся целиком. У тебя другая информация? :)

  • Dmitry Tyurev

    > и параллельно рендерять эти текстуры на кубиках – то никаких рывков не будет.

    А почему именно на кубиках? Для мипов? Мы рендерим на билборде под основной сценой. 1 текстура за кадр. Рывки (от свизла?) никуда не деваются, но немного маскируются. Пробовали сортировать текстуры по размеру (чтобы не было: кадр – загрузка 512х512, кадр – загрузка 32х32, кадр загрузка 512х512). На глаз плавности нифига не добавило – выкинули.

  • IronPeter

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

  • Dmitry Tyurev

    Кстати, в WOWe стримминг абсолютно плавный. Ни единого рывка даже на радеонах. Никто не хочет пореверсинженерить? ;)

  • IronPeter

    В последний раз, когда я их смотрел, у них все текстуры были в managed пуле. Без всяких тонкостей. Возможно что-то пропустил.
    Просто там, где другие используют дохрена текстур 1024×1024 ( к примеру для террейна ), они используют несколько текстурок 256 x 256.

  • http://gaijin.ru todace

    когда я смотрел в последний раз, в wow было как пишет Петр :)

  • Dmitry Tyurev

    То есть, просто CreateTexture, без затей? :) Тогда основной вопрос – в какие моменты времени могут создаваться новые текстуры? Может ли создаться 10 текстур в кадре или, например, 1 новая текстура на кадр?

  • Aliot

    2 IronPeter
    Про тест – сейчас никак не могу проверить – проект зашиплен уж года два.
    Про “рывки” – мне непонятно, как их не будет. Вернее, мне непонятно, что это такое – “рывки”.

    Далее про без тестов, на пальцах.
    Очевидно, что загрузка текстуры (не с диска, а в память) – синхронная операция. Очевидно, что текстура 2048 грузится в 16 раз медленее, чем 512. Если выделено на стримминг 10 мс – то не вписываешься элементарно (опять же, я не про сейчас, а про 2 года назад. сейчас не знаю). Понятно, что если текстуры в managed и в LVM не влазит – то начинается и вовсе не контролируемая ерунда. Причем не на отрисовки на кубиках твоей свежей текстуры – а на отрисовки кадра. Но и без этого запросто вылетали за 10мс (тем более, что если потрачено уже 8, то даже еще 7 дает 15мс). Очевидно, что если LVM хватает – проблем меньше в разы. Но на PC, кстати, и это проблема – фик узнаешь сколько у тебя реально LVM есть. Но и если хватает – все равно не сильно приятные ощущения.

    Про случайный доступ – видимо не мне уже?

    2 Dmitry Tyurev
    У меня нет информации про сейчас.
    Насколько я помню, отличия были. Возможно, надо было пересоздавать текстуры и блитить сюрфейсы.
    Дело ж не в том, грузится ли в LVM по мипам, дело в свиззлинге.
    Возможно, грузили по мипам просто.
    Не помню :(

    Но Анрыл3 сейчас (давеча смотрел) тоже по мипам грузит.
    Про WoW – уже все написали. Стримай текстуры не больше 512х512 – и будет полное счастье.

  • Aliot

    Уточнение.
    10 мс – взято для примера. Я не помню сколько давалось.
    И да, при ситуации когда LVM хватало – все в итоге победили. Типа, PC не такая плохая, да.
    Но на самом деле – на PC надо делать WoW – будет еще лучше.

  • IronPeter

    >Очевидно, что загрузка текстуры (не с диска, а в память) – синхронная операция

    Очевидно, что в случае managed pool – нет. У видеокарточки есть блиты, прямо как команды push buffer. Чисто теоретически, можно сделать blit из системной памяти в видео асинхронно, силами карточки.

    К сожалению, по техническим причинам такие прямые блиты в DX9 на XP не проходят. В новой драйверной модели Vista – вполне себе. Lock от managed ресурса может дать копию в памяти, доступной для блитов карточкой. Или же выдать место в swizzled апертуре, тогда вообще никакие копирования не нужны.

    Даже в случае XP у драйвера есть пространство для маневра. Между посылкой DP2 TEXBLT пакета и реальным рисованием есть время.

  • Aliot

    Теоретически – можно блитить асинхронно, понятно. Правда, еще надо свиззлить, но тоже можно асинхронно это делать. В драйвере.
    Но повлиять на этот процесс разработчик игры напрямую не может, на XP во всяком случае (потому как висты тогда не было :)).
    Типа, как драйвер написан, так оно и работает. А написан он так, что эта операция синхронна.

  • IronPeter

    Блит должен уметь на лету свиззлить ( и умеет, как я понимаю ).

    На драйверы надо аккуратненько смотреть, что они делают. Я не видел, как они написаны. Можно поглядеть. Из того, что происходит до функции DP2 – видно что есть большая свобода. У драйвера есть внутри запасец в пару фреймов, он свободен в том, как его тратить.