mrchantey / koora

Assemblyscript 3D Game Framework
MIT License
27 stars 3 forks source link

Linear Component Array Memory Layout #3

Open mrchantey opened 2 years ago

mrchantey commented 2 years ago

It'd be great to explore a method for laying out the data of components in a 'linear' manner, as seen in the sparse set ECS approach. I originally had ideas about using Typed Arrays for all primitives in components, because I have some understanding of how to get them to all share the same ArrayBuffer, but as @MaxGraey points out in PR #1 this is less than ideal due to the extra layer of abstraction.

I would love to learn how this can be achieved in AssemblyScript, I suppose the ideal solution would have the following:

Here's an example to hopefully clarify the problem that I'm having:

class MyComponent{
  valA: f32
  valB: Vector3
  static byteStride: u16 = 32 // could this be auto generated?

}

const initialComponentCount = 10
//this is my imaginary alternative to ArrayBuffer
const componentPool = new MemoryAllocation(MyComponent.byteStride * initialComponentCount)

function addComponentToPool(index: u16){
  //somehow the new instance is assigned to that location in memory
  componentPool.set(new MyComponent(), MyComponent.byteStride * index)
}

function getComponentAtIndex(index:u16):MyComponent{
  return componentPool.get(MyComponent.byteStride * index)
}

function doJavascriptWebGLThingWithComponent(index:u16){
  // it may be better to do this cast in JS, so that we dont need to copy the data across on each usage
  const poolAsBufferView = Uint8Array.from(componentPool)
  const byteOffset = MyComponent.byteStride * index
  gl.bufferData(gl.ArrayBuffer,poolAsBufferView,gl.STATIC_DRAW,byteOffset,MyComponent.stride)
}

Edit: Just discovered the following functions, perhaps some combination may do the trick:

export declare function load<T>(ptr: usize, immOffset?: usize, immAlign?: usize): T;
export declare function store<T>(ptr: usize, value: auto, immOffset?: usize, immAlign?: usize): void;
export declare function sizeof<T>(): usize; // | u32 / u64
export declare function alignof<T>(): usize; // | u32 / u64
export declare function offsetof<T>(fieldName?: string): usize; // | u32 / u64
export declare function changetype<T>(value: auto): T;
mrchantey commented 2 years ago

Ok things are making more sense now. The syntax I was looking for was:

mrchantey commented 2 years ago

Worked out a 'solution', not very pretty though, details here