背景
在游戏引擎开发中,我们通常使用面向数据的设计来优化内存和计算性能。
我们以粒子系统为例。
在一个粒子系统中,我们有很多粒子,每个粒子可能有几个属性,比如位置、速度等。
C++ 中的典型实现如下所示:
struct Particle {
float positionX, positionY, positionZ;
float velocityX, velocityY, velocityZ;
float mass;
// ...
};
struct ParticleSystem {
vector<Particle> particles;
// ...
};
这种实现的一个问题是粒子属性是相互交错的。这种内存布局对缓存不友好,可能不适合 SIMD 计算。
相反,在面向数据的设计中,我们编写以下代码:
struct ParticleAttribute {
size_t size;
size_t alignment;
const char* semantic;
};
struct ParticleSystem {
ParticleSystem(
size_t numParticles,
const ParticleAttribute* attributes,
size_t bufferSize) {
for (size_t i = 0; i < numAttributes; ++i) {
bufferSize += attributes[i].size * numParticles;
// Also add paddings to satisfy the alignment requirements.
}
particleBuffer = malloc(bufferSize);
}
uint8* getAttribute(const char* semantic) {
// Locate the semantic in attributes array.
// Compute the offset to the starting address of that attribute.
}
uint8* particleBuffer;
};
现在我们只有一个分配,每个属性都连续驻留在内存中。为了模拟粒子,我们可以编写以下代码:
symplecticEuler(ps.getAttribute("positionX"), ps.getAttribute("velocityX"), dt);
该getAttribute
函数将获取特定属性的起始地址。
问题
我想知道如何在 Rust 中实现这一点。
我的想法是首先创建一个名为 的类ParticleSystem
,它需要几个ParticleAttribute
s 来计算总缓冲区大小,然后为缓冲区分配内存。我认为这可以在 Rust 安全代码中完成。
下一步是实现getAttribute
函数,它将返回对特定属性起始地址的引用。我在这里需要你的帮助。如何获取带有偏移量的原始地址并将其转换为所需的类型(例如 float*)并将该原始指针包装到 Rust 中的可变引用?
此外,我认为我应该将该原始指针包装到对数组的可变引用,因为我需要使用 SIMD lib 通过该引用加载四个元素。如何使用 Rust 实现这一点?
更新:提供有关属性的更多信息。属性的数量和详细信息在运行时确定。属性的类型可能会有所不同,但我认为我们只需要支持原始类型(f32、f64、ints、...)。