1个版本 (0个不稳定版本)
2.2.0-pre.4 | 2024年7月31日 |
---|---|
2.2.0-pre.3 |
|
2.2.0-pre.1 |
|
#297 in 游戏开发
141 每月下载次数
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 返回一个字符串时,它总是返回一个 Utf8CString
(Utf8CStr
的所有者版本),因为很难编码 FMOD 字符串的生命周期要求。
这适用于存储 C 字符串的结构,如 fmod::studio::AdvancedSettings
。将类似 AdvancedSettings
的结构转换为它们的 FFI 等价物是通过引用完成的,这样就不会将字符串的所有权传递给 FMOD。FMOD 似乎 会将其自己的内存中的字符串复制进来,所以这应该是可以接受的?
将 FFI 结构转换为类似 AdvancedSettings
的结构时,将 C 字符串指针复制到 Utf8CString
中。这 是不安全的,因为没有保证指针是有效的,或者字符串是空终止的 UTF-8。幸运的是,所有 FMOD 函数都返回 UTF-8 字符串,所以在实际中这并不是真正的问题。
未定义的行为和不安全的函数
我正在努力使这些绑定尽可能安全,如果您发现 UB,请报告!我知道有一些与线程安全(您可以初始化 FMOD 以使其线程不安全)和恐慌有关的案例,我正在积极修复这些问题。
目前有一些标记为不安全的函数,我不确定如何安全地实现它们。系统创建、初始化和清理是一个相当大的问题——创建系统是非常不安全的,并且某些函数只能在系统创建之前或之后调用。释放系统可能是所有操作中最不安全的,因为它会使与该系统关联的所有 FMOD 处理无效。
用户数据
由于任何想要创建的库都需要在 FMOD 对象释放时清理用户数据,因此创建安全的用户数据绑定非常困难。不幸的是,有许多情况下这是不可能的。EventDescription
和 EventInstance
是一个很好的例子——您可以在事件释放时设置回调以清理它们的用户数据。您还可以设置在卸载银行时清理用户数据的回调。这个回调将是清理 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