В Dolphin 5.0 можно было хотя бы загрузить любую игру с GameCube. Кроме одной. Сложный способ использования блока управления памятью в PowerPC Star Wars: The Clone Wars до недавнего времени не позволял играть в неё через Dolphin. Но в версии Dolphin 5.0-540 эта проблема наконец решена: эмулятор научился загружать все игры GameCube из официальной библиотеки консоли.
Star Wars: The Clone Wars запущена в Dolphin
Почему же Star Wars: The Clone Wars стала такой особенной? Чтобы по-настоящему разобраться, в чём тут дело, вам нужно немного узнать о том, как процессор PowerPC выполняет управление памятью и как этот процесс эмулируется в Dolphin.
Эмуляция блока управления памятью
Блок управления памятью (Memory Management Unit, MMU) отвечает за предоставление играм быстрого доступа к данным и коду. Вместо прямого доступа к имеющейся ОЗУ игры обращаются к виртуальной памяти, которая затем транслируется с помощью MMU в физическую память. Этот процесс может выполняться двумя способами: трансляцией адресов блоков (Block Address Translations, BAT) в случае больших блоков памяти или таблицами страниц в случае отображения небольших объёмов.
Игры получают доступ к виртуальной, а не реальной памяти по нескольким причинам. Во-первых, это даёт процессору возможность кэширования операций доступа, значительно повышая эффективность доступа к часто используемым значениям. Во-вторых, GameCube имеет всего 24МБ (и несколько специализированных областей) ОЗУ в адресном пространстве из 4ГБ; это значит, что большинство адресов памяти не имеет соответствующей им ОЗУ! Если игра попытается получить доступ к адресу реальной памяти, не имеющему на самом деле физической памяти, она получит в качестве данных «мусор» или просто вызовет сбой! Благодаря использованию виртуальной памяти MMU может сгенерировать исключение, давая игре возможность обработать эту ситуацию или предоставить разработчикам важную обратную связь с полезной информацией о произошедшей ошибке.
Dolphin может эмулировать MMU с определённой степенью точности на основании теорий о том, как должна вести себя игра.
MMU — это часть процессора GameCube. Фотография из Wikimedia
Теория 1: ПО выполняет запись только в действительную память
Если игры могут выполнять запись только в действительную память, то у Dolphin с этим не возникает никаких сложностей. Большинство игр использует стандартное BAT-отображение GameCube/Wii, и пока они не пытаются получить доступ к памяти за его пределами, всё, что нужно сделать — обеспечить операции доступа в нужные места.
Стандартное отображение имеет одну трансляцию адресов блоков (BAT), транслирующую всю физическую память. Поскольку BAT быстрее, чем таблицы страниц, большинство игр использует только их.
Подход Dolphin заключается в жёстком кодировании этой трансляции адреса блока (BAT) из виртуальной памяти и транслировании адреса в физическую память. Проще говоря, игра запрашивает один адрес в памяти, но консоль (а значит и Dolphin) передаёт ей другой из реальной памяти.
Существуют также другие важные отображаемые части, которые не рассматриваются в этой статье. Один из важнейших — это отображаемый в памяти ввод-вывод (Memory-Mapped Input/Output, MMIO), с помощью которого процессор взаимодействует с различными устройствами (дисковым приводом, картами памяти и т.д.). Также нужно упомянуть специальный загрузочный сектор, но поскольку игры не могут получать к нему доступ, пока о нём говорить особо незачем.
Режим MMU Off в эмуляторе — это настолько обрезанный функционал MMU, чтобы только позволять эмуляции MMU обеспечивать загрузку игр. Он просто предоставляет игре область памяти для работы и предполагает, что игра не будет нарушать правил. Удивительно, но большинство игр для GameCube и почти все игры Wii действительно работают с памятью правильно.
Для большинства игр GameCube, нарушающих правила, должна была потребоваться полная эмуляция MMU, но хитроумным разработчикам эмулятора удалось обмануть эти игры без снижения производительности с помощью MMU Speedhack.
Теория 2: использование недопустимой памяти играми можно предсказать
MMU Speedhack можно назвать «MMU Off+», потому что он всё ещё не выполняет серьёзной эмуляции MMU. Он отображает больше ОЗУ, чем имеет GameCube, чтобы заставить игры думать, что всё работает, не заботясь о том, что же игры делают на самом деле.
Поскольку игры используют эти адреса как расширенное ОЗУ, простое отображение их как действительной памяти работает! Но вы наверно не понимаете, почему же эти игры вообще пытаются получить доступ к недействительной памяти, правда?
Всё потому, что Nintendo создала библиотеку, позволяющую играм воспользоваться 16 МБ дополнительного ОЗУ GameCube в качестве расширенного ОЗУ. Так как это дополнительное ОЗУ связано с цифровым сигнальным процессором (DSP), оно чаще используется в качестве аудиопамяти, но технически её можно использовать почти для всего. Процессор не может напрямую отобразить дополнительное ОЗУ в пространстве адресов из-за отсутствующей аппаратной функции, поэтому игре приходится выполнять чтение и запись по адресу недействительной памяти, чтобы вызвать обработчик исключений. Этот обработчик исключений использует прямой доступ к памяти (Direct Memory Access, DMA), чтобы переместить данные из дополнительного ОЗУ в выделенный игрой кэш в физической памяти. Затем он даёт команду таблице страниц сообщать, что адрес ранее недействительной памяти теперь указывает на местоположение этого кэша, что позволяет игре работать без «вылетов»!
Каким-то образом хак эмулятора Dolphin умудряется работать почти для всех игр, использующих дополнительное ОЗУ в качестве расширенной памяти. Несмотря на свою кривизну, этот хак на удивление эффективен и быстро работает.
Теория 3: доступ к недействительной памяти непредсказуем
MMU Speedhack имеет высокую производительность, но он работает только в играх, алгоритмы использования памяти которых типичны и настолько часто встречаются, что были интегрированы в Dolphin. Такое жёстко заданное поведение позволяет эмулятору Dolphin предполагать, откуда игра будет выполнять считывание и не заботиться о том, чтобы гарантировать действительность памяти, к которой осуществляется доступ. Игры с MMU Speedhack настолько стандартизированы, что подчиняются очень простым правилам, однако эти правила имеют ограничения.
Часть игр применяла собственные обработчики исключений и использовала адреса памяти нестандартным способом, нарушая работу MMU Speedhack. В этих случаях Dolphin должен проверять и обеспечивать действительность адреса памяти, а уже затем передавать инструкции или данные в эмулируемый процессор; это получается гораздо медленнее, чем просто считать все адреса действительными.
До попыток Fiora и других разработчиков оптимизировать JIT и эмуляцию MMU, режим MMU Enabled был смертельным приговором для процесса игры.
Известна 21 игра, требующая для работы больше, чем просто MMU Speedhack.
Обработка проверок памяти (memcheck) медленнее, потому что она мешает оптимизации производительности fastmem. Fastmem отображает пространство адресов GameCube/Wii в памяти компьютера, а затем помечает для компьютера всю эмулируемую недействительную память как выделенную. Это позволяет эмулятору Dolphin использовать обработчик исключений процессора компьютера для выполнения «грязной работы» при перехвате исключений. Когда он перехватывает исключение, эмулятору для обработки адреса приходится перейти от fastmem (быстрой) к slowmem (медленной памяти), что может стать очень большой проблемой для производительности.
Проверки памяти — это базовая функция MMU Enabled, и это основная причина того, что игры со включенным MMU так медленно работали в Dolphin. Некоторые ситуации требовали возврата к интерпретатору, который не работает с fastmem; при этом доступ к памяти был даже медленнее, чем обычный доступ на консоли!
Несмотря на частую невозможность использования, всегда стоит пытаться применять fastmem практически для любого доступа; область загрузки fastmem хранит всего две инструкции, а тот же доступ с помощью slowmem может занимать до 1000 инструкций! Поскольку мы не знаем, находится ли указатель в действительной памяти или нет, мы просто всегда пытаемся применить fastmem из-за огромной разницы в производительности, которую она даёт нам, если срабатывает.
Как сказано выше, на самом деле slowmem стала немного быстрее, что позволило использовать её на практике. В основном это произошло благодаря перемещению проверок памяти в кэш кода Fiora's Far Code Cache. Благодаря оптимизации обработки проверок памяти и постоянной готовности всех инструкций и данных в играх с MMU значительно выросла производительность. Наибольший эффект заметен Rogue Squadron 3, скорость которого подскочила с жалких 4 до почти 45 кадров в секунду!
Обычно кэш Far Code Cache вместе с другими оптимизациями JIT в среднем приводил почти к двукратному росту производительности во всех играх, требующих MMU Enabled, с приятным исключением в виде Rogue Squadron 3. Именно благодаря этому сегодня пользователи мощных компьютеров могут хотя бы попробовать поиграть в игры со включенным MMU.
Теория 3.5: Factor 5 — гады
Конечно же, большинство пользователей даже не знало, насколько тормозил Star Wars Rogue Squadron 3 до реализации Far Code Cache, потому что он просто не загружался. По крайней мере, так было с версией NTSC. Версия PAL кое-как загружалась, и именно из неё брались характеристики скорости до появления Far Code Cache.
Даже если вам удавалось запустить игру, ничего не сломав, дела внезапно могли стать плохи, если важный кусок данных сохранялся на нескольких страницах.
Rebel Strike стала предпоследней загруженной NTSC-игрой по одной причине — в ней был противный мелкий трюк с MMU, отодвинувший эмуляцию больше чем на десятилетие. Rogue Squadron 3 мог хранить данные на нескольких страницах! При длительном считывании или записи Rebel Strike мог вызвать исключение, буквально переключающее в процессе с действительного на недействительный адрес! Раньше Dolphin ошибочно устанавливал исключение на начало чтения, а должен был устанавливать его там, где на самом деле возникло исключение.
Было бы красиво, если бы последней игрой с GameCube, загруженной в Dolphin, стала игра Factor 5 (от переводчика: разработчики Star Wars Rogue Squadron III: Rebel Strike), но оставалась ещё одна. И на этот раз дело заключалось не в мелкой ошибке.
Теория 4: игра определяет собственную действительную память
Теперь мы наконец переходим к Star Wars: The Clone Wars. После всей работы, сделанной разработчиками Dolphin для как можно более эффективной обработки BAT и таблиц страниц, эта игра решила выкинуть в окно одно из самых базовых допущений Dolphin: статичные BAT.
Что Dolphin делает?
Dolphin был совершенно не готов к ситуации, в которой потребуется настоящая эмуляция BAT. Какие бы настройки вы ни использовали, благодаря жёстко определённым допущениям всё заканчивалось очень кисло.
MMU Speedhack/MMU Off — в этом сочетании дела шли из рук вон плохо. Так как Dolphin на самом деле не эмулирует MMU, он начинал работу и решал передать процессору любой мусор, расположенный в 0x00000000. Игра закономерно «вываливалась», потому что это чрезвычайно глупое поведение.
MMU Enabled — если вы используете версию, выпущенную до этой статьи, то в режиме MMU Enabled процесс продвигается немного дальше. Dolphin удаётся эмулировать исключения и загружать обработчик ошибок, но эмулятор просто не способен обработать то, что происходит дальше. Clone Wars пользуется отключением BAT во время обработки исключений, чтобы получить больший контроль над управлением памятью. Затем она сообщает, что стандартные BAT и таблицы страниц недостаточно хороши и пытается создать собственные. Dolphin жёстко задаёт BAT. Они не изменяемы. Поэтому когда обработчик ошибок возвращает управление игре… она конечно же «падает». Но, по крайней мере, сам Dolphin продолжает работать, так что вы можете закрыть игру и поиграть во что-то другое.
Вы можете задать вопрос: «почему бы просто не создать специальные жёстко заданные BAT для этой игры?». Но на самом деле так не получится. Игра множество раз изменяет BAT во время миссий и в многопользовательском режиме; это означает, что требуется правильная эмуляция BAT.
Что Dolphin должен делать
Правильная эмуляция BAT подразумевает, что Dolphin должен иметь возможность включения и отключения BAT на основании действий игры, а не на предположениях о том, чего ей хочется. При этом возникает проблема: эффективная эмуляция MMU в Dolphin базируется на том, что он знает, где будет находиться действительная виртуальная память, и разрушение этого фундамента полностью разваливает Dolphin.
Star Wars: The Clone Wars — это единственная известная игра, пользующаяся четырьмя отображаемыми BAT инструкций и BAT данных для создания собственного отображения памяти. Для эмулирования этой игры код эмуляции MMU в Dolphin должен быть полностью переписан. При этом всё очень усложняется: fastmem, проверки памяти, и то, что между ними.
В отличие от собственных обработчиков исключений других игр, работающих с MMU Enabled, обработчик исключений The Clone Wars на самом деле реструктурирует память. Он отключает от адресного пространства стандартную BAT и во время игры много раз создаёт свою собственную. На предыдущих изображениях вы видели, что BAT становились всё более сложными, делая эмуляцию более полной, но The Clone Wars выкидывает всю эту работу на помойку!
В этот раз Dolphin не может делать никаких предположений. Только полная переделка ядра эмуляции BAT Dolphin способна справиться с этим наихудшим сценарием. Dynamic BATs (динамические BAT) — это полная эмуляция BAT, которая позволяет сделать то, чего просят игры, и отображать информацию правильно. «Благодаря» этому переписыванию огромного объёма кода от большей части жёстко заданных допущений в эмуляции MMU Dolphin пришлось избавиться.
Flipping the Page Table
(от переводчика: в заголовке авторы обыгрывают мем Flipping the Table, ни о каком «перевороте» таблицы страниц речи не идёт)
Разработчики Dolphin уже несколько лет знали, что необходимо для загрузки Star Wars: The Clone Wars. В далёкие времена (в эру версии 3.0) существовала ветка, которая могла загружать The Clone Wars с помощью использования интерпретатора. Это было неоправданно медленно и влияло на производительность игр, не использующих MMU; в ту эру производительность была очень болезненным вопросом.
Больше года текущая реализация (Dynamic BATs) даже «висела» в качестве pull request, и на тот момент уже могла загружать The Clone Wars. Она ждала своего времени так долго, что ею даже занялся другой разработчик!
Причина, по которой реализация этой функции заняла такое долгое время, заключается в том, что её невозможно было реализовать, не разрушив работу всего остального. Учитывая всё это, реализация разработчика magumagu была выполнена блестяще.
Как вы видите, очень большое внимание уделялось тому, чтобы сохранить скорость Dolphin несмотря на огромный объём переписанного кода MMU. В играх, не использующих MMU Enabled и использующих MMU Speedhack, производительность должна снизиться менее чем на 1%. Это большая разница с первоначальными планами, в которых для игр, не использующих MMU Enabled, падение должно было составить больше 30% даже с отключенной эмуляцией MMU!
Одна из важных особенностей реализации Dynamic BATs в том, что она всё равно работает с fastmem, несмотря на очевидное усложнение возможности перемещения действительной памяти! С другой стороны, пока игры с MMU пока работают немного медленнее. Падение на 8%-15% — это значительно, но мы сможем избавиться от него с помощью дальнейших оптимизаций. Мы посчитали, что огромное повышение точности стоит некоторой потери производительности.
Побочные эффекты переписывания кода жёстко заданных BAT
Более точная эмуляция MMU привела к тому, что частое странное поведение игр теперь эмулируется гораздо лучше. Конечно, Dynamic BATs действительно необходимы для работы только одной игры, но другие игры также получили преимущества более быстрого доступа для проверок памяти.
Пользователи обожают, когда игры «вываливаются», правда ведь? Существует довольно много игр для Wii, которые приводят к сбоям на консоли, когда игрок выполняет непредусмотренные действия. Иногда выполнение этих действий приводит даже к аварийному завершению Dolphin! Упрощённый доступ к проверкам памяти означает, что Dolphin может точно эмулировать хорошо известные глитчи в играх, не приводя к авариям Dolphin! Проблемы, от которых страдают Super Mario Galaxy или Twilight Princess теперь могут быть эмулированы и вызывать те же сбои, что и на консоли, не влияя на производительность!
«Баг» с дорожным указателем в Twilight Princess
Повторюсь, большинству игроков не нравится, когда в играх происходят сбои. С этой точки зрения, Rayman Raving Rabbids TV Party (и Rabbids Party Collection) больше не «вываливаются» при загрузке мини-игр. Возможно, так получилось из-за небольших изменений в управлении кэшем, или, что более вероятно, в жёстко заданных BAT/таблицах страниц были «баги». Сбой, который никто не мог устранить, просто исчез после переписывания кода — это стало приятным сюрпризом.
Улучшение общей эмуляции процессора также необъяснимо позволило работать ещё двум играм. We Love Golf!, созданная разработчиками Mario Golf, «вылетала» при попытке загрузки уровня. Summer Athletics 2009 просто не загружалась и выдавала на экран какую-то отладочную информацию. На эти игры не повлияла реализация Dynamic BATs; непонятным образом исправила поведение обеих игр сама очистка кода в процессе слияния веток. Согласно информации, известной о проблемах этих игр, вероятнее всего дело было в ошибках кэша инструкций и данных.
Ещё одним приятным сюрпризом стало улучшение работы кучи ромхаков в Dolphin. Ромхаки Wiimm's Mario Kart Wii More Fun правильно (ну, почти) загружают экран выбора трасс!
Очень-очень большая удача, что всё это вообще работает. Хак не очищает icache; вместо этого в управлении освобождением и сбросом icache он полагается на процессор PowerPC, поэтому считалось, что его невозможно эмулировать в Dolphin без значительного снижения производительности. Но теперь Dolphin очищает icache при изменении BAT. К сожалению, между трассами игроки должны возвращаться в основное меню. Если вы хотите, чтобы это было исправлено… то не стесняйтесь сообщить об этом нам. Но учтите, что при этом Dolphin придётся эмулировать эмуляцию кэша инструкций и данных, что в целом повысит требования к ресурсам для эмуляции процессора в 14 раз! Для хардкорных или стремящихся к точности эмуляции геймеров всё это нужно эмулировать, но всем остальным на самом деле не нужна полная и точная эмуляция кэша.
Если ромхакеры всё-таки захотят, чтобы их игры работали Dolphin, то мы, к сожалению, должны будем попросить их не полагаться на обработку icache/dcache процессором PowerPC. Например создатели Project M перед финальным релизом исправили использование icache/dcache, так что он должен правильно работать в Dolphin. Но если вы не хотите, чтобы пользователи Dolphin запускали ваши ромхаки, то вы знаете, как от этого защититься (по крайней мере, пока).
В заключение
После переписывания кода Dolphin совершил ещё один большой прыжок в точности движка: устранено несколько случайных сбоев (хотя большинство игроков и не заметит разницы). Это победа, хотя и с горьковатым привкусом: мы заставили запускаться последнюю игру с GameCube, но всё ещё осталось множество нераскрытых тайн. Конечно, некоторые игры всё ещё «вываливаются», есть много проблем, которые нужно решить, но полностью нерабочих игр уже не осталось.
На данный момент эмуляция MMU в Dolphin способна обеспечивать обработку любой известной игры. Единственными, кто может изменить это, могут стать Factor 5, но они вроде бы не делали ни одной игры для Wii…
Star Wars: Rogue Leaders на Wii
Однако выяснилось, что на Wii существует ещё одна игра Rogue Squadron. Она разработана Factor 5, поэтому должна эксплуатировать консоль так, как никакая игра до неё этого не делала. Но проблема в том, что игра не была выпущена, поэтому мы не можем открыть её в Dolphin. Хотя она всё-таки существует; может быть, кто-то сможет попробовать запустить её в Dolphin. Может быть, хотя бы посмотрит на «вылет», который она неизбежно вызовет. А может быть, она даже запустится, и получится сделать хотя бы один скриншот? (Ну пожалуйста!)
Ну да ладно. По крайней мере, у нас есть канал Netflix, разработанный основными членами Factor 5 уже в новой компании. И, как вы наверно уже могли догадаться, он не запускается в Dolphin.
Автор: PatientZero