1个版本 (0个不稳定版本)

2.2.0-pre.42024年7月31日
2.2.0-pre.3 2024年6月11日
2.2.0-pre.1 2024年6月4日

#297 in 游戏开发

Download history 158/week @ 2024-06-01 243/week @ 2024-06-08 22/week @ 2024-06-15 132/week @ 2024-07-27 9/week @ 2024-08-03

141 每月下载次数

MPL-2.0 许可证

600KB
10K SLoC

fmod-oxide

fmod声音引擎的安全Rust绑定。这个crate试图尽可能接近Rust和低成本,同时不牺牲任何API。某些API,如从指针加载银行,被标记为不安全,但仍然可用。

目前处于Beta版。

我目前正在与另一个游戏一起开发这个crate,这意味着这个crate的大部分实际测试都来自一个来源,并且只在Windows/Linux系统上进行。这意味着对于某些函数/用例,我还没有完全掌握API。(自定义文件系统、混合矩阵、Mac支持等)尽管如此,crate的大部分功能已经完整。我需要修复一些API文档的问题,添加更多FMOD版本的支持,并在发布前再次检查所有内容的安全性。

文档

大部分文档直接复制自FMOD文档,但是某些信息(如参数值)被排除。请参阅FMOD文档以获取更多信息。

内存管理 & 复制类型

所有FMOD对象都是Copy、Clone、Send和Sync,因为可能存在对同一对象的多个引用。(例如,加载一个bank然后通过其路径检索它)有许多用例可能需要获取某些内容(如bank)并且不再使用。实现Drop以自动释放内容将与特定用例相冲突,因此这个crate选择使用手动release()方法。

这个crate目前没有防止使用后释放,尽管这是我计划要做的事情。

字符串类型

“fmod-oxide”旨在尽可能实现零成本,因此它使用来自 lanyard crate 的 UTF-8 C 字符串作为其字符串类型。这意味着所有 FMOD 函数都接受一个 &Utf8CStr 而不是 &str&CStr。由于 &Utf8CStr 的构建成本非常低(甚至可以使用 c! 宏静态完成),因此这不应该成为问题。

当 FMOD 返回一个字符串时,它总是返回一个 Utf8CStringUtf8CStr 的所有者版本),因为很难编码 FMOD 字符串的生命周期要求。

这适用于存储 C 字符串的结构,如 fmod::studio::AdvancedSettings。将类似 AdvancedSettings 的结构转换为它们的 FFI 等价物是通过引用完成的,这样就不会将字符串的所有权传递给 FMOD。FMOD 似乎 会将其自己的内存中的字符串复制进来,所以这应该是可以接受的?

将 FFI 结构转换为类似 AdvancedSettings 的结构时,将 C 字符串指针复制到 Utf8CString 中。这 是不安全的,因为没有保证指针是有效的,或者字符串是空终止的 UTF-8。幸运的是,所有 FMOD 函数都返回 UTF-8 字符串,所以在实际中这并不是真正的问题。

未定义的行为和不安全的函数

我正在努力使这些绑定尽可能安全,如果您发现 UB,请报告!我知道有一些与线程安全(您可以初始化 FMOD 以使其线程不安全)和恐慌有关的案例,我正在积极修复这些问题。

目前有一些标记为不安全的函数,我不确定如何安全地实现它们。系统创建、初始化和清理是一个相当大的问题——创建系统是非常不安全的,并且某些函数只能在系统创建之前或之后调用。释放系统可能是所有操作中最不安全的,因为它会使与该系统关联的所有 FMOD 处理无效。

用户数据

由于任何想要创建的库都需要在 FMOD 对象释放时清理用户数据,因此创建安全的用户数据绑定非常困难。不幸的是,有许多情况下这是不可能的。EventDescriptionEventInstance 是一个很好的例子——您可以在事件释放时设置回调以清理它们的用户数据。您还可以设置在卸载银行时清理用户数据的回调。这个回调将是清理 EventDescription 上的用户数据的完美地方,但是您不能在回调触发时访问银行的 EventDescription

这里几乎唯一的选项是要求用户手动释放用户数据或泄露内存。这两种方法都不是好的解决方案。

目前,这个 crate 将用户数据与其所有者一起存储在全局槽映射中,并且有时会删除具有无效所有者的用户数据。这种方法与标记和清除 GC(Rust 没有这种 GC)配合使用效果最好。我们可以通过在 System::update 中执行此检查来部分解决此问题。这将使 System::update 变得昂贵——它将添加一个额外的 O(n) 复杂度,这与这个 crate 的目的相悖。

在这个系统中,将userdata与单个系统关联起来很困难,因此每次释放任何系统时,我们都需要清除slotmap。通常在执行结束时释放系统,所以这可能不会成为问题。唯一的其他解决方案是将任何返回对象的userdata指针设置为每个系统拥有的hashmap。

我还在思考如何解决这个问题,但我可能会将这个问题放在功能门后面,并提供*mut c_void的设置器和获取器。这不是我想要的方案,但也没办法。

如果有一个简单的方法来强制在编译时强制执行T是指针大小且不需要Drop(则在编译时),那么我就可以使用我在这个crate早期就想采用的方案,将T转换为*mut c_void。(见这个提交。)

与其他crate的差异

libfmod与这个crate相似,但它的主要区别在于它是从FMOD文档自动生成的,而不是像这个crate一样使用手写的绑定。因为它自动生成,所以它的发布周期比这个crate要快得多,但API更接近C API。

rust-fmod已过时,没有工作室绑定,并且存在重大的安全问题(userdata接受一个&'a mut T并且不做类型检查,系统创建函数未标记为不安全等)

fmod-rs说实话,直到最近我才知道这个crate。它缺少工作室绑定,并设计用于与bevy一起使用。有几个决策(如引用计数的Handle类型)很有趣,但并不免费。如果我的crate不适合你,它绝对值得检查!

依赖关系

~0.4–2.9MB
~59K SLoC