3

我需要尽可能高效地在基于 CD 的旧游戏机上加载大型模型和其他结构化二进制数据。最好的方法是什么?数据将从 Python 应用程序中导出。这是一个相当精致的爱好项目。

要求:

  • 不依赖完全符合标准的 STL——不过我可能会使用 uSTL。
  • 尽可能少的开销。瞄准一个很好的解决方案。它可以在最初的 Playstation 上使用,而且尽可能现代和优雅。
  • 不需要向后/向前兼容性。
  • 不复制大块 - 最好在后台将文件加载到 RAM 中,然后从那里直接访问所有大块。
  • 不应依赖具有相同字节顺序和对齐方式的目标,即 Python 中的 C 插件将其结构转储到磁盘不是一个好主意。
  • 应该允许移动加载的数据,因为单个文件是 RAM 大小的 1/3,碎片可能是一个问题。没有可滥用的 MMU。
  • 健壮性是一个很大的好处,因为我的注意力范围很短,即我会更改保存部分代码并忘记加载代码,反之亦然,所以至少一个愚蠢的保护措施会很好。
  • 加载数据和运行时生成的数据之间的可交换性,没有运行时开销,也没有严重的内存管理问题,这将是一个很好的奖励。

我有一个在 Python 微不足道的、有限语法的 C 头文件中解析的半计划,它将使用带有偏移量而不是指针的结构,以及在主应用程序中使用 getter 的便利包装器结构/类,它将偏移量转换为正确类型的指针/参考,但我想听听你的建议。

澄清:请求主要是关于数据加载框架和内存管理问题。

4

4 回答 4

4

在 Nintendo GameCube 和 DS 等平台上,3D 模型通常以非常简单的自定义格式存储:

  • 一个简短的标头,包含标识文件的幻数、顶点数、法线等,以及标头后面的可选数据校验和(Adler-32、CRC-16 等)。
  • 每个向量和法线的可能压缩的 32 位浮点 3 元组列表。
  • 可能压缩的边或面列表。
  • 所有数据都采用目标平台的本机字节序格式。
  • 压缩格式通常是简单的(Huffman)、简单的(算术)或标准的(gzip)。所有这些都需要很少的内存或计算能力。

您可以将这样的格式作为提示:它是一种非常紧凑的表示。

我的建议是使用与您的内存数据结构最相似的格式,以尽量减少后处理和复制。如果这意味着您自己创建格式,那就这样吧。你有极端的需求,所以需要极端的措施。

于 2009-11-13T09:13:56.553 回答
3

我注意到您的描述中没有任何地方要求“易于编程”。:-)

因此,这就是我想到的一种创建方式:

  • 数据应该与目标内存中的数据格式相同,这样它就可以简单地将 blob 从磁盘拉到内存中,而无需重新格式化。根据您在将内容放入内存时想要多大的自由度,“blob”可能是整个文件,也可能是其中的较小位;我不太了解您的数据,无法建议如何细分它,但大概您可以。因为我们不能依赖主机上相同的字节顺序和对齐方式,所以在主机端编写文件时,你需要在翻译方面有点聪明,但至少这样你只需要一方面的聪明才智转移而不是两者。

  • 为了确保目标端和主机端代码匹配,您应该以提供单个数据描述的形式编写此代码,并有一些生成代码将生成目标端 C 代码和来自它的主机端 Python 代码。您甚至可以让您的生成器在此过程中生成一个小的随机“版本”号,并让主机端代码将其写入文件头并让目标端检查它,如果它们不匹配,则会给您一个错误. (使用随机值的意义在于,您关心的唯一信息位是它们是否匹配,并且您不想手动增加它。)

于 2009-11-13T09:14:45.957 回答
3

这是一种常见的游戏开发模式。

通常的方法是在离线预处理步骤中处理数据。生成的 blob 可以以最小的开销流入。blob 依赖于平台,并且应该包含目标平台的正确对齐和字节序。

在运行时,您可以简单地将指针转换为内存中的 blob 文件。您也可以处理嵌套结构。如果您保留一个目录,其中包含对 blob 中所有指针值的偏移量,那么您可以修复指针以指向正确的地址。这类似于 dll 加载的工作方式。

我一直在开发一个 ruby​​ 库bbq,我用它来为我的 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;
}

My bbq library isn't quite ready for prime time, but it could give you some ideas on how to write one yourself in python.

Good Luck!

于 2009-11-14T05:25:59.997 回答
0

考虑将数据作为 BLOB 存储在 SQLite DB 中。SQLite 是极其便携和轻量级的,ANSI C,同时具有 C++ 和 Python 接口。这将处理大文件、无碎片、快速访问的可变长度记录等。其余的只是将结构序列化为这些 BLOB。

于 2009-11-13T07:40:50.983 回答