Forums :: Register :: Login |
anonymous user |
Common forum | 1 | 2 | next »» | Create new thread
|
dDIMA
Posts: 18 |
2008-11-21 09:08:49
| reply!
В развитие темы про ресурсы, которую затрагивали на канале, кратко расскажу свое текущее видение вопроса. Что будет интересно - спрашивайте, буду раскрывать тему. Итак. Ресурс - это данные. Блок данных некоторой природы, про которую ресурс-манагер даже не догадывается. Он только знает, что эти данные обрабатываются некоторой зарегистрированной фабрикой (у блока данных есть factory id), и что эти данные имеют некоторое имя (оно требуется для разных внешних связей). Блоки объединены в файл (операция чисто формальная, чтобы было быстрее и грузить, и адресоваться между блоками). С точки зрения игры вообще как такового понятия уровня не существует. В какие-то моменты времени можно грузить эти блоки (или бинарные секции). Бинарная секция появилась в памяти - заработал какой-то обработчик. Бинарная секция выгрузилась из памяти - обработчик удалился. Разумеется, система предлагает некоторые "готовые" решения по загрузке данных (датабаза, уровень и т.п.), но это все формально. Хочется стриминг - выгрузи один блок и подгрузи второй. Хочется на консоли выгружаемое меню - сделай меню отдельным уровнем. Хочется на PC, чтобы меню было всегда - засунь его в database. Бинарные секции формируются на основе внешнего описания. Они могут ссылаться друг на друга. Если в пределах файла - то есть уникальный guid для связывания. Если между независимыми файлами - то можно написать extern "имя" - система в момент загрузки файла найдет и подцепит ранее загруженное объявление. Вот если очень кратко. Спрашивайте, чего интересно. Адрес дилера тоже могу подкинуть :) |
|
IronPeter
Posts: 127 |
to: dDIMA, 2008-11-21 10:08:56
| reply!
> В развитие темы про ресурсы, которую затрагивали на канале В общем-то это я баламутил воду. Как я понимаю, излагается видение платформы Red Key. Думаю, что хотелось бы обсуждать конкретную имплементацию. На некоторой референсной платформе. Пусть будет PS3, мне она ближе :). Она характерная в своей ебанутости. Для начала - хранение на диске/BRD. Можно делать абстрактные операции DMA, виртуализованные в гипервизоре, можно формировать прямо SATA очередь запросов. Правильно ли я понимаю, что этот самый ресурсный файл является единицей обращения к жесткому диску? Т.е. аллоцировали память ( причем заранее известно сколько - т.е. ресурсный файл должен быть аннотирован своей длиной, снаружи ), сформировали файловый запрос, отправили и забыли. Потом получили асинхронную нотификацию, начали парсить и вызывать разные фабрики по идентификаторам. Возникает первый вопрос. Есть ли на уровне этого ресурсного файла компрессия? Она per-file, per-section, есть ли кастомные методы сжатия? Если я хочу zipped DXT текстуру, которая после распаковки лежит в памяти RSX, то что мне надо сказать? MemCopy, MemoryStream::Read( void *data, size_t count ) или inflate от zlib? Такой же вопрос про blob с фиксапами - тот самый blob я могу смонтировать прямо на выданном мне куске памяти? Возникает второй вопрос - имеется ли фиксированная разбивка по потокам? Где происходят тяжелые операции распаковки ( которые хорошо умеют работать в параллельном потоке ). И где происходят легкие операции инжектинга данных в систему ( как по мне - так это надо делать где-то в главном цикле игры, в синхронном стиле ). Далее. Связи между файлами данных видны снаружи? Если хочу загрузить модельку, которая референсит пошаренный текстурный пак - то правильно ли я понимаю, что команда на загрузку текстурного пака придет только после DMA на файл модельки? Насколько разбиение по файликам является data-driven? Поясняю. Вот возьмем простой пример. Большая continuous карта, на ней стоят объектики. Объектики с двумя лодами для геометрии, с коллизионной геометрией и шаренными текстурами. В процессе девелопмента хочется грузить мелкогранулированный суп. Вот моделька, как она есть, проэкспортированная из Майки. В ней 2 лода, коллизионная геометрия и ссылки на текстуры, которые россыпью на диске лежат. Билд система должна знать соответствие ".ma" -> "файл с данными". И прямо так и билдить. Так хочется построить техпроцесс. В релизе хочется другого. Хочется группировать данные по-разному. Вот тут - текстурки. Отдельно собраны сурфейсы для первого лода, отдельно все остальные лоды. Текстурки собраны по локальности обращения. В процессе игры текстурка теряет свою идентичность ( вроде одна и та же текстурка, но меняет, сцука, число лодов! ). Низкие лоды можно запекать прямо по месту использования. Для геометрии ровно та же фигня - в зависимости от удаленности стримятся разные уровни детализации ( причем низкие могут быть массивно раздублированы и запечены в уровне ). Опять таки объект теряет свою идентичность ( лоды-то сменились, но анимационный стейт сохранить надо! ). Коллизии тоже живут только в небольшом радиусе. Кажется, что такая модель требует мелкой нарезки объектов на уровне С++. И серьезных возможностей по подмене внутренностей объекта на другой ( с сохранением всех внешних связей ). Этот самый ресур-менеджер как-то помогает нам реализовывать вышеизложенный вариант? |
|
dDIMA
Posts: 18 |
to: IronPeter, 2008-11-21 10:44:30
| reply!
> Правильно ли я понимаю, что этот самый ресурсный файл > является единицей обращения к жесткому диску? Да. Файл состоит из секций, причем это формирование секций относительно произвольное. Можно собрать модель с текстурами, можно текстуры вынести во внешний пак. > Есть ли на уровне этого ресурсного файла компрессия? В redkey сейчас нет, планируется, что она будет. Вообще задача трасфера файла в оперативную память сама по себе достаточно хорошо отделяется и формалируется. Будет ли он зажатым, будет ли подниматься по DMA или по TCP/IP (такое тоже надо для PSP - там можно поиграть в мультиплеер, получив нужный код по WiFi от дружеской консоли) - неважно. Главное - есть команда на загрузку и есть нотификация о завершении загрузки. > Такой же вопрос про blob с фиксапами - тот самый blob > я могу смонтировать прямо на выданном мне куске памяти? Бинарные секции разбиты на 3 типа: 1. ALLTIME_PLACED - данные поднимаются в память и остаются там до конца уровня. Может использоваться для фиксапа, плейсмент new и т.п. 2. LOADTIME_DISCARDED - данные поднимаются в память, по окончании загрузки бинарной секции память освобождается. Может использоваться для данных, которые после окончания загрузки не нужны. Например, DX текстуру, которую перекинули в залоченную память. 3. ALLTIME_SAVED - данные поднимаются в память, остаются там до конца уровня. ВО время сейвов этот блок памяти сбрасывается на диск. При загрузках сейвов используется новй сохраненный блок. Разбивка на типы при подготовке бинарной секции целиком лежит на совести конкретной экспортной фабрики. Ресурс-менеджеру пофиг, что и куда скинуто. > Возникает второй вопрос - имеется ли фиксированная разбивка по потокам? Пока что нет. Весь процесс идет в одном потоке, фабрики могут самостоятельно что-то сделать в другом. Но система событий RedKey позволяет параллельно обрабатывать загрузку в любое доступное количество процессоров, сейчас этот механизм временно отключен. Кроме того см. следующий пункт. > Далее. Связи между файлами данных видны снаружи? > Если хочу загрузить модельку, которая референсит пошаренный > текстурный пак - то правильно ли я понимаю, что команда на > загрузку текстурного пака придет только после DMA на файл модельки? Есть только связи между секциями. И есть правило - если секция 1 имеет связь с секцией 2, то секция 2 гарантированно обработается до того, как начнется обработка 1. Применительно к твоему вопросу: Если в память поднялась моделька, и с нее есть ссылки на текстуры, то произойдет следующее: - файл с текстурами будет поставлен в очередь на загрузку. - пока он будет грузиться и парситься, ресурс менеджер будет обрабатывать то, что ему доступно (то, у кого нет связей с незагруженными данными) - когда все дочерние связи будут подняты, в фабрику пойдет уведомление о загрузке модели. Есть еще альтернативный механизм загрузки - extern "name". Он больше напоминает рантайм линковку - в случае деклараций extern указанные бинарные секции должны быть подняты в память силами программиста до того, как будет загружен указанный файл. Например сейчас для оптимзиации по памяти мы выкинули часть текстур из уровней и засунули их в датабазу. Текстуры поднимаются сразу, из файлов уровней на них висят ссылки extern. Контроль за такими ссылками целиком на совести программиста. > Вот возьмем простой пример. Большая continuous карта, на ней > стоят объектики. ... Еще раз уточню, что разбиение на файлы является произвольным. Минимальная единица изменения ресурс-менеджера - это бинарная секция (из трех частей ALLTIME_PLACED, ...). Объединение и разделение в файлы может (с некоторыми ограниченями) варьироваться. Формат самой бинарной секции тоже может зависеть от режима экспорта (для отладки, для мастера). Это уже личное дело каждой отдельной бинарной секции - что она там сохранит. Например, скрипт секция может хранить только ссылку на внешний текстовый скрипт-файл, или вшитый в себя компилированный байт-код. С подменой объектов есть некоторые сложности. Вообще система ориентирована только на _изменение_ данных на лету (если речь идет про POD-like структуры), то такое изменение вообще делается автоматом и не требует реализации со стороны программиста. Впрочем, добавить новые связи или секции в таком варианте невозможно. Плюс к тому есть подмена объектов в виде перезагрузки нового полноценного бинаря. Такое тоже было сделано, но сейчас пока что на уровне экспериментов и доводок до ума... |
|
IronPeter
Posts: 127 |
to: dDIMA, 2008-11-21 11:41:07
| reply!
Я все же немного не понял, если у каждой секции есть свой тип. То единица стримминга файл, а единица мемори-менеджмента - секция в файле? То есть можно на уровне менеджера памяти дискардить отдельные куски в большой аллокации > Разбивка на типы при подготовке бинарной секции целиком лежит на совести > конкретной экспортной фабрики. Ресурс-менеджеру пофиг, что и куда скинуто. Пофиг-то ему пофиг... А в рантайме генерируется прямо настоящий граф объектов? Вот к примеру, дебажно разложили модельку на стопиццот анимаций, стопиццот текстурок и собственно геометрию. Каждый фрагмент в своем файлике. В рантайме поднимется живой С++ граф? Чтобы можно было ползать по ссылкам, дергать методы? Ситуация извне отличается от случая, когда все лежит в одном файле? Или просто - более латентная загрузка, но внешне все то же самое? > > Далее. Связи между файлами данных видны снаружи? > > Если хочу загрузить модельку, которая референсит пошаренный > > текстурный пак - то правильно ли я понимаю, что команда на > > загрузку текстурного пака придет только после DMA на файл модельки? > Есть только связи между секциями. И есть правило - если секция 1 имеет связь с > секцией 2, то секция 2 гарантированно обработается до того, как начнется > обработка 1. Применительно к твоему вопросу: > Если в память поднялась моделька, и с нее есть ссылки на текстуры, то > произойдет следующее: > - файл с текстурами будет поставлен в очередь на загрузку. > - пока он будет грузиться и парситься, ресурс менеджер будет обрабатывать то, > что ему доступно (то, у кого нет связей с незагруженными данными) > - когда все дочерние связи будут подняты, в фабрику пойдет уведомление о > загрузке модели. Э не! Чудес не бывает. Мы не можем узнать, что для конкретной модельки нам надо грузить текстуры. Эта информация должна быть нам доступна или снаружи ( скажем, при старте загружаются вся информация о зависимостях между всеми датапаками ). Или мы ее должны выцепить после загрузки датапака из некоторой линк-секции, включенной в хидер датапака. То есть сделав тяжелую высоколатентную DMA операцию. В результате латентности доступа складываются, так? Ну и простенький вопрос - сами файлы - бинарные секции рефкаунтятся? Скажем, одна моделька попросила текстурку, вторая моделька попросила... Как оно работает? Примерно как dll - c рефкаунтом и DllMain на attach - detach? |
|
dDIMA
Posts: 18 |
to: IronPeter, 2008-11-21 15:52:55
| reply!
> То единица стримминга файл, а единица мемори-менеджмента - секция > в файле? Каждая бинарная секция состоит из трех частей (точнее, _до_ трех частей). Части лежать в отдельных файлах. Т.е. везде, где говорится про бинарный файл, подразумевается 3 файла указанных типов. > А в рантайме генерируется прямо настоящий граф объектов? Не в рантайме. Граф сгенерирован на экспорте, на загрузке он только анализируется, чтобы иметь гарантированные правила "импорт детей вперед". > Ситуация извне отличается от случая, когда все лежит в одном файле? Нет, не отличается. > Или мы ее должны выцепить после загрузки датапака из некоторой > линк-секции, включенной в хидер датапака. Связи запоминаются на экспорте и хранятся в заголовке датапака, да. > Скажем, одна моделька попросила текстурку, вторая моделька попросила... > Как оно работает? Примерно как dll - c рефкаунтом и DllMain на attach > - detach? Тут ситуация двойственная. С одной стороны, менеджер имеет внутренние механизмы для объединения одинаковых readonly секций в одну. С другой стороны, помощь со стороны экспортеров всячески приветствуется. Если они на этапе экспорта могут предсказать, что 2 секции будут одинаковы, то постпроцессинг с мержингом будет не нужен. Рефкаунта нет - выгрузка простым циничным методом free(data); |
|
IronPeter
Posts: 127 |
to: dDIMA, 2008-11-22 14:03:06
| reply!
Я кажется начал понимать основные идеи, но пока не полностью. > > А в рантайме генерируется прямо настоящий граф объектов? > Не в рантайме. Граф сгенерирован на экспорте, на загрузке он только > анализируется, чтобы иметь гарантированные правила "импорт детей вперед". > Давай смоделируем базовый пример. У меня пока картина целиком не вытанцовывается. [code] class Model { Texture *diffuseTexture; }; [/code] Вот такой класс. Эта самая текстурка лежит в другой бинарной секции, в другом файле. Файлик поднимается по зависимостям, там загрузчик находит TEXTURE_ID, вызывает функцию-обработчик. И? Как diffuseTexture примет указатель на конкретный экземпля типа Texture? Вроде пока не было никакой типизации у системы. Даже оставляя тонкости с разными там множественными наследованиями. Аналогично пусть у нас загружается второй экземпляр модельки. Как он подцепит уже загруженную текстурку? Система хранит какие-то там хеши? Но как они могут быть, если free на конкретный ресурс говорит не система, а пользователь? Правильно ли я понимаю, что проблему выгрузки текстурки должен решать сам класс текстуры? Скажем вызвав free на себя, по обнулению рефкаунта. Но так быть не может - потому что есть возможность, когда текстурки ( незаметным извне образом ) могут рождаться на большом куске памяти. И free надо вызывать для куска. |
|
dDIMA
Posts: 18 |
to: IronPeter, 2008-11-25 10:34:44
| reply!
> Я кажется начал понимать основные идеи, но пока не полностью. > Давай смоделируем базовый пример. У меня пока картина целиком не > вытанцовывается. > > [code] > class Model > { > Texture *diffuseTexture; > }; > [/code] > > Вот такой класс. Эта самая текстурка лежит в другой бинарной секции, в другом > файле. Файлик поднимается по зависимостям, там загрузчик находит TEXTURE_ID, > вызывает функцию-обработчик. И? Как diffuseTexture примет указатель на > конкретный экземпля типа Texture? Вроде пока не было никакой типизации у > системы. Даже оставляя тонкости с разными там множественными наследованиями. > Точно также, как пример конкретное описание Model: Model = "MainHero" { diffuseTexture = "heroSkin"; } Это формирует 2 бинарные секции для MainHero (тип Model) и для HeroSkin (тип Texture). Оня связаны друг с другом. Загрузка MainHero сможет пройти только после того, как heroSkin уже поднят в память. Выгрузка возможна только в обратном порядке. Если есть еще Model = "MainHero2" { diffuseTexture = "heroSkin"; }, то heroSkin будет все равно только один (впрочем, если для него написать noshare, то тогда и текстура тоже размножится. |
|
IronPeter
Posts: 127 |
to: dDIMA, 2008-11-25 13:01:54
| reply!
> Это формирует 2 бинарные секции для MainHero (тип Model) и для HeroSkin (тип > Texture). Оня связаны друг с другом. Загрузка MainHero сможет пройти только > после того, как heroSkin уже поднят в память. Выгрузка возможна только в > обратном порядке. > Если есть еще Model = "MainHero2" { diffuseTexture = "heroSkin"; }, то heroSkin > будет все равно только один (впрочем, если для него написать noshare, то тогда > и текстура тоже размножится. Ну погоди. У меня есть несколько правил, и они не подводили. Правила простые "чудес не бывает" и "данные не из воздуха берутся." Пока на диске у нас очень простые данные - это файлы с зависимостями. Если надо организовать фичу "heroSkin будет все равно только один" - то надо реализовать hash. Загруженных объектов. Тупой такой, пионерский, в виде имя::ресурс. Еще вопрос, добавлять ли в него вообще все объекты ( а, судя по всему, надо добавлять ). Или на этапе билда данных отделять просто текстуры от шаренных текстур, флажками. Я не знаю, твоя аннотация [noshare] - она per class или per class instance. Если хеш, то тогда и рефкаунт. И внешние управление владением, чтобы выгрузить текстуру по выгрузке последнего оунера. Не так? |
|
dDIMA
Posts: 18 |
to: IronPeter, 2008-11-25 15:51:59
| reply!
> Ну погоди. У меня есть несколько правил, и они не подводили. Правила простые > "чудес не бывает" и "данные не из воздуха берутся." :)))))))))))))) > > Пока на диске у нас очень простые данные - это файлы с зависимостями. Если надо > организовать фичу "heroSkin будет все равно только один" - то надо реализовать > hash. Загруженных объектов. Тупой такой, пионерский, в виде имя::ресурс. Со скрипом - соглашусь. Если без скрипа, то есть 2 пути: а) хеш для текстур, который реализуется на этапе экспорта (это как бы в помощь ресурс-менеджеру) и б) компоновщик, который одинаковое умеет объединять. > > Еще вопрос, добавлять ли в него вообще все объекты ( а, судя по всему, надо > добавлять ). Или на этапе билда данных отделять просто текстуры от шаренных > текстур, флажками. Я не знаю, твоя аннотация [noshare] - она per class или per > class instance. per class instance > Если хеш, то тогда и рефкаунт. И внешние управление владением, чтобы выгрузить > текстуру по выгрузке последнего оунера. Не так? > Нет, не так. Выгрузка - она либо human-controlled (это например лоды или оверлеи), в этом случае структура данных такова, что выгрузить лишнего не удастся. Либо выгрузка уровня - она free(data); |
|
IronPeter
Posts: 127 |
to: dDIMA, 2008-11-25 19:19:24
| reply!
> Со скрипом - соглашусь. Если без скрипа, то есть 2 пути: а) хеш для текстур, > который реализуется на этапе экспорта (это как бы в помощь ресурс-менеджеру) и > б) компоновщик, который одинаковое умеет объединять. Э, нет, без скрипа! Потому что мы вроде выяснили, что информация о ссылках каждого бинарного файла хранится в нем самом. Если у нас есть общий репозитарий всех объектов и ссылок, то и зависимости надо хранить в нем. И сохранять факт загруженности просто как указатель в большом массиве ( ну явно в проекте будет меньше тысячи мешей и 10 тысяч текстур - жалкие килобайты ). Но тогда и зависимости тут же хранить, а не в данных. > Либо выгрузка уровня - она free(data); Э, нет! Вот держит уровень текстурки. Сказали мы free - текстурки в памяти остались. А они зашаренные со следующим уровнем. Кто их убьет и когда? |
|
dDIMA
Posts: 18 |
to: IronPeter, 2008-11-28 08:12:58
| reply!
> > Если у нас есть общий репозитарий всех объектов и ссылок, то и зависимости надо > хранить в нем. И сохранять факт загруженности просто как указатель в большом > массиве ( ну явно в проекте будет меньше тысячи мешей и 10 тысяч текстур - > жалкие килобайты ). Но тогда и зависимости тут же хранить, а не в данных. > > Э, нет! Вот держит уровень текстурки. Сказали мы free - текстурки в памяти > остались. А они зашаренные со следующим уровнем. Кто их убьет и когда? Петя, я далек от мысли делать прошарку всей игры целиком. Игра таки бъётся на логические куски. И с текстурами для следующего уровня - вопрос для ручного решения: а) либо текстуры в уровнях независимы - тогда они выгружаются и загружаются с каждым уровнем заново. б) либо текстуры совсем сильно прошарены - тогда они грузятся заранее и всегда присутствуют в памяти, если загружен хотя бы 1 уровень. Делать же комбинаторную перестановку из всех возможных последовательностей загруженных уровней, последовательных перемещений игрока по всем возможным траекториям с загрузкой и выгрузкой всех возможных оверлеев в произвольном порядке как то не очень хочется.... |
|
IronPeter
Posts: 127 |
to: dDIMA, 2008-11-28 11:11:53
| reply!
> Петя, я далек от мысли делать прошарку всей игры целиком. Игра таки бъётся на > логические куски. И с текстурами для следующего уровня - вопрос для ручного > решения: > а) либо текстуры в уровнях независимы - тогда они выгружаются и загружаются с > каждым уровнем заново. > б) либо текстуры совсем сильно прошарены - тогда они грузятся заранее и всегда > присутствуют в памяти, если загружен хотя бы 1 уровень. Бывают разные игры. Бывают игры без четкого понятия "уровень". Вот тривиальнейший пример - онлайн игра про мужиков с мечами. Игрались мы, игрались, тут нам говорят: "привет, в вашей зоне видимости появился мужик. На мужике ботинки ( реф на текстурку ), пояс ( реф на текстурку ), наплечники ( реф на модельку ), меч ( реф на модельку ), энчант на мече ( fx )." Тут еще бы можно подумать, и загрузить все модельки/текстуры итемов, fxы заранее. Добавить бесплатных ползунков типа "зеленая рубашка, красная рубашка". Оно было бы так, если бы в игре не было бы creatures. И не было бы hunters, которые могут приручить любую тварюшку, и с ней ходить. А вот текстурок на тварюшках уже сотни мегабайт в DXT. Если у нас hunters не могут приручить тварюшку - это будет странно. Уже в Ultima Online умели - и вдруг разучились. Как можно решать. Вся обвязка ( собственно информация о взаимосвязях объектов, про их размеры, etc ) - довольно легкая. В игре не больше 10 тысяч текстур, не больше 10 тысяч моделей. Сотню килобайт под описатели ( которые всегда с нами ) отдать можно. Пусть наружу торчат хендлы для стримминга, которыми мы оркеструем. Пусть есть пулы. Текстуры с некоторого момента начинают иметь в два раза меньший размер, потом - в четыре раза меньший. Геометрия - просто фейлится при загрузке, выдает нулевой рантайм-хендл. Такое я с легкостью напишу на PC. Но на PS3 еще легче. Пулы проще дефрагментировать, проще делать DMA ( диском, SPU или RSX ). Но это ладно. Скажем, что совсем неконсольный геймплей, потому что дизайн гейский, писишный, непредсказуемый и недостойный. Пусть будет консольный. Пусть будет мир. Мир простой, двухмерный. В мире стоят объекты с текстурками. При удалении грузить надо старшие мипы, старшие лоды геометрии. Каждый кусок имеет свой набор уникальных данных, но их мало. Большинство данных зашаренные. Владение текстуркой, скажем, может передаваться по цепочке зон. Как бы ты решал это? Просто на уровне кода. И тулов. Чтобы работало в девелоперском и релизном билде. Где конкретно в высоком уровне мы говорим "зона, загрузись"? Как там учитывается факт, что 90% контента в общем-то зашарено с другими зонами. Или оно разбивается билдером, и код должен знать эту информацию, прямо списком некоторых датапаков, которые висят на ребрах графа? Проблема в том, что у графа ребер сильно больше, чем вершин. Если можно, то просто напиши code snippet. Того куска, который управляет стриммингом зоны ( с мешами, текстурами, коллизиями ). Осмысленно для зоны иметь список используемых бинарных данных. Считать diff того, что есть с тем, что надо. И формировать широкий такой асинхронный запрос на ресурсы. Вот будут новые консоли - там будет очень высоколатентный интернет и низколатентный твердотельный жесткий диск. Можно будет говорить по сетке такой resource gather запрос, и очень асинхронно получать результат. И в рантайме быстро читать раскиданные фрагменты. |
|
Bugdog
Posts: 1 |
to: dDIMA, 2008-12-03 20:26:42
| reply!
Читаю, виртуально прокручиваю примеры. Понял что мета-информация для ресурсов это полезная вещь, и применяю давно. В остальном приблизиться не очень получается. Наверно всё это можно объяснить на основе стэка, тогда станут понятны слои и объекты которые в них крутятся. Вы ведь именно такую модель строите? (чтобы избежать "нагруженных" загрузчиков и т.д.) Хотел спросить, какую роль выполняет система для отработки device lost (graphics/sound)? |
Common forum | 1 | 2 | next »» | Create new thread