Оптимальная загрузка формата файла данных на игровой консоли - PullRequest
3 голосов
/ 13 ноября 2009

Мне нужно максимально эффективно загружать большие модели и другие структурированные двоичные данные на более старую игровую консоль на компакт-диске. Какой лучший способ сделать это? Данные будут экспортированы из приложения Python. Это довольно сложный хобби-проект.

Requierements:

  • не полагаться на полностью стандартную STL - хотя я мог бы использовать uSTL.
  • как можно меньше накладных расходов. Стремитесь к решению так хорошо. что он может быть использован на оригинальной Playstation, и при этом максимально современный и элегантный.
  • обратная / прямая совместимость не требуется.
  • не копировать большие куски вокруг - предпочтительно файлы загружаются в ОЗУ в фоновом режиме, и все большие куски доступны непосредственно оттуда позже.
  • не должен полагаться на цель с таким же порядком байтов и выравниванием, то есть плагин C в Python, который выгружает свои структуры на диск, не будет хорошей идеей.
  • должно позволять перемещать загруженные данные, так как для отдельных файлов 1/3 размера ОЗУ фрагментация может быть проблемой. Нет MMU, чтобы злоупотреблять.
  • Надежность - это большой бонус, так как мой объем внимания очень мал, то есть я бы изменил сохраняющуюся часть кода и забыл бы загружать одну или наоборот, так что по крайней мере глупая защита была бы хорошей.
  • Взаимозаменяемость между загруженными данными и сгенерированными во время выполнения данными без дополнительных затрат времени выполнения и без серьезных проблем с управлением памятью была бы хорошим бонусом.

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

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

Ответы [ 4 ]

4 голосов
/ 13 ноября 2009

На платформах, таких как Nintendo GameCube и DS, 3D-модели обычно хранятся в очень простом пользовательском формате:

  • Краткий заголовок, содержащий магическое число, идентифицирующее файл, количество вершин, нормали и т. Д., И необязательно контрольную сумму данных, следующих за заголовком (Adler-32, CRC-16 и т. Д.).
  • Возможно сжатый список 32-битных 3-кортежей с плавающей точкой для каждого вектора и нормали.
  • Возможно сжатый список ребер или граней.
  • Все данные в собственном формате с прямым порядком байтов целевой платформы.
  • Формат сжатия часто бывает тривиальным (Хаффман), простым (арифметическим) или стандартным (gzip). Все это требует очень мало памяти или вычислительной мощности.

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

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

3 голосов
/ 14 ноября 2009

Это обычный шаблон разработки игр.

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

Во время выполнения вы можете просто привести указатель на файл BLOB-объекта в памяти. Вы также можете иметь дело с вложенными структурами. Если вы сохраняете оглавление со смещением для всех значений указателя в BLOB-объекте, вы можете исправить указатели, чтобы они указывали на правильный адрес. Это похоже на работу загрузки DLL.

Я работал над библиотекой ruby, барбекю , которую я использую для приготовления данных для моей игры на iphone.

Вот схема памяти, которую я использую для заголовка BLOB-объекта:

// Memory layout
//
// p begining of file in memory.
// p + 0 : num_pointers
// p + 4 : offset 0
// p + 8 : offset 1
// ...
// p + ((num_pointers - 1) * 4) : offset n-1
// p + (num_pointers * 4) : num_pointers   // again so we can figure out 
//                                            what memory to free.
// p + ((num_pointers + 1) * 4) : start of cooked data
//

Вот как я загружаю двоичный файл BLOB-объекта и исправляю указатели:

void* bbq_load(const char* filename)
{
    unsigned char* p;
    int size = LoadFileToMemory(filename, &p);
    if(size <= 0)
        return 0;

    // get the start of the pointer table
    unsigned int* ptr_table = (unsigned int*)p;
    unsigned int num_ptrs = *ptr_table;
    ptr_table++;

    // get the start of the actual data
    // the 2 is to skip past both num_pointer values
    unsigned char* base = p + ((num_ptrs + 2) * sizeof(unsigned int));

    // fix up the pointers
    while ((ptr_table + 1) < (unsigned int*)base)
    {
        unsigned int* ptr = (unsigned int*)(base + *ptr_table);
        *ptr = (unsigned int)((unsigned char*)ptr + *ptr);
        ptr_table++;
    }

    return base;
}

Моя bbq библиотека не совсем готова для прайм-тайма, но она может дать вам некоторые идеи о том, как написать ее самостоятельно на python.

Удачи!

3 голосов
/ 13 ноября 2009

Замечу, что нигде в вашем описании вы не спрашиваете об «простоте программирования». : -)

Итак, вот что мне приходит в голову как способ создать это:

  • Данные должны быть в том же формате на диске, что и в памяти цели, чтобы она могла просто вытягивать большие двоичные объекты с диска в память без переформатирования. В зависимости от того, сколько свободы вы хотите поместить в память, «BLOB-объекты» могут быть целым файлом или меньшими битами в нем; Я недостаточно хорошо понимаю ваши данные, чтобы предложить, как их подразделить, но, вероятно, вы можете. Поскольку мы не можем полагаться на один и тот же порядок байтов и выравнивание на хосте, вам нужно быть немного умным при переводе вещей при записи файлов на стороне хоста, но, по крайней мере, в этом случае вам нужен ум только с одной стороны передачи, а не на обоих.

  • Чтобы обеспечить некоторую уверенность в том, что код на стороне назначения и на стороне хоста совпадает, вы должны записать это в форме, где вы предоставляете одно описание данных и имеете некоторый код генерации, который будет генерировать как C-код на целевой стороне и Python-код на стороне хоста из него. Вы могли бы даже заставить свой генератор генерировать небольшое случайное число «версии» в процессе, и сделать так, чтобы код на стороне хоста записал это в заголовок файла, а целевой объект проверил его и выдал ошибку, если они не совпадают. , (Смысл использования случайного значения заключается в том, что единственный информационный бит, который вас интересует, - совпадают ли они, и вам не нужно увеличивать его вручную.)

0 голосов
/ 13 ноября 2009

Подумайте о сохранении ваших данных в виде BLOB в БД SQLite. SQLite чрезвычайно переносим и легок, ANSI C, имеет интерфейсы C ++ и Python. Это позаботится о больших файлах, никакой фрагментации, записях переменной длины с быстрым доступом и так далее. Остальное - просто сериализация структур для этих BLOB-объектов.

...