The*_*ist 6 data-oriented-design rust
在游戏引擎开发中,我们通常使用面向数据的设计来获得最佳的内存和计算性能.
我们以粒子系统为例.
在粒子系统中,我们有很多粒子,每个粒子可能有几个属性,如位置,速度等.
C++中的典型实现如下:
struct Particle {
float positionX, positionY, positionZ;
float velocityX, velocityY, velocityZ;
float mass;
// ...
};
struct ParticleSystem {
vector<Particle> particles;
// ...
};
Run Code Online (Sandbox Code Playgroud)
该实现的一个问题是粒子属性彼此交错.此内存布局不是缓存友好的,可能不适合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;
};
Run Code Online (Sandbox Code Playgroud)
现在我们只有一个分配,每个属性连续驻留在内存中.为了模拟粒子,我们可以编写以下代码:
symplecticEuler(ps.getAttribute("positionX"), ps.getAttribute("velocityX"), dt);
Run Code Online (Sandbox Code Playgroud)
该getAttribute
函数将获取特定属性的起始地址.
我想知道如何在Rust中实现它.
我的想法是首先创建一个名为的类ParticleSystem
,它需要几秒ParticleAttribute
来计算总缓冲区大小,然后为缓冲区分配内存.我认为这可以在Rust安全代码中完成.
下一步是实现getAttribute
函数,它将返回对特定属性的起始地址的引用.我需要你的帮助.如何获取带有偏移量的原始地址并将其转换为所需类型(例如float*)并将该原始指针包装到Rust中的可变引用?
另外,我认为我应该将该原始指针包装到对数组的可变引用中,因为我需要使用SIMD lib通过该引用加载四个元素.我如何使用Rust实现这一目标?
更新:提供有关属性的更多信息.属性的数量和详细信息在运行时确定.属性的类型可以变化,但我认为我们只需要支持原始属性(f32,f64,int,...).
这是实现DOD的一种非常复杂的方式,使用运行时查找getter的想法让我感到畏缩.
简单的版本是每个属性只有一个内存分配:
struct Particles {
x: Vec<f32>,
y: Vec<f32>,
}
Run Code Online (Sandbox Code Playgroud)
这需要事先知道属性.
然后没有shenanigan得到所有的ys,他们只是坐在那里,已经打字,等着你.
将其扩展为动态确定的属性并不复杂:
HashMap<String, xxx>
在运行时查找给定的属性enum
一个单独Value
存储在哈希映射中,它可以采用多种形式(另一种解决方案是使用特征)这变为:
#[derive(Debug, Hash, PartialEq, Eq)]
enum Value {
UniformInt(i64),
UniformFloat32(f32),
UniformFloat64(f64),
DistinctInt(Vec<i64>),
DistinctFloat32(Vec<f32>),
DistinctFloat64(Vec<f64>),
}
struct Particles {
store: HashMap<String, Value>,
}
Run Code Online (Sandbox Code Playgroud)
我们可以选择使用6个哈希映射...但除非先知道该类型是什么(当唯一的思考是一个字符串时),然后一个人必须一次查看所有的哈希映射:烦人,时间浪费.