Closed MaikKlein closed 6 years ago
#[derive(Packed)]
pub struct SomeStruct{
pub v: Vec4<f32>,
pub f: f32
}
Maybe packed should just create a complete new struct under the hood.
// Generated
pub struct SomeStructPacked{
pub v: Vec4<f32>,
pub f: f32,
_p1: f32,
_p2: f32,
_p3: f32,
}
trait Packing {
type Packed;
}
struct Packed<T: Packing>{
_m: PhantomData<T>,
data: T::Packed
}
// generated
impl Packing for SomeStruct {
type Packed = SomeStructPacked;
}
And then you would create those structs directly Packed<SomeStruct>::new(..);
.
I think a possible implementation of the generated code could look like this:
pub trait Alignment {
const REST: usize;
}
#[repr(C)]
struct Align<A: Alignemnt> {
data: A,
_align: [u8; A::REST]
}
impl Alignment for Vec3<f32> {
const REST: usize = 4; //
}
impl Alignment for f32 {
const REST: usize = 0;
}
#[repr(C, align = "16")]
pub SomeStructPacked{
v: Align<Vec3<f32>>,
f: Align<f32>
}
#[derive(Packed)]
pub struct SomeStruct{
pub v: Vec3<f32>,
pub f: f32
}
Requirements
Standard Uniform Buffer Layout
Every member of an OpTypeStruct with storage class of Uniform and a decoration of Block (uniform buffers) must be laid out according to the following rules:
The Offset decoration must be a multiple of its base alignment.
Any ArrayStride or MatrixStride decoration must be an integer multiple of the base alignment of the array or matrix from above.
The Offset decoration of a member must not place it between the end of a structure or an array and the next multiple of the base alignment of that structure or array.
The numeric order of Offset decorations need not follow member declaration order.
reference
These also then follow the rules from
std140
. Also mentioned in14.5.4
.Additionally from the SPIR-V spec
Composite objects in the UniformConstant, Uniform, and PushConstant Storage Classes must be explicitly laid out. The following apply to all the aggregate and matrix types describing such an object, recursively through their nested types:
Each structure-type member must have an Offset Decoration.
Each array type must have an ArrayStride Decoration.
Each structure-type member that is a matrix or array-of-matrices must have be decorated with
a MatrixStride Decoration, and
one of the RowMajor or ColMajor Decorations.
The ArrayStride, MatrixStride, and Offset Decorations must be large enough to hold the size of the objects they affect (that is, specifying overlap is invalid). Each ArrayStride and MatrixStride must be greater than zero, and no two members of a given structure can be assigned to the same Offset.
Meaning
RLSL needs to explicit decorate members of a struct with the correct offset. That offset needs to follow the rules defined in
std140
, and this only applies to PushConstant and Uniform.Implementation
Idea 1
RLSL can easily generate the correct offset from the rules from
std140
but then the Rust and RLSL code would get out of sync.One idea would be to only check if the struct is conformant with the rules from
std140
. Then RLSL would just extract the offset that was defined by the user.Generally annotation of structs doesn't seem reasonable, for example you don't want your
Vec3<f32>
to be 16 bytes wide in the vertex input. Either accept the performance hit, or create separate types for the all the uniforms.Idea 2
Compute the correct layout inside RLSL and implement
Packed
in normal Rust as a custom derive.It would then know how to serialize the specified struct with the rules of
std140
. AndPacked
could look like thisFor example if you have
And if you want to send it to the shader you have to pack them correctly
And inside RLSL you can just use
SomeStruct
directly.I am unsure how exactly I could implement it because custom derive doesn't expose type information but I think there should be a way. I might have to use
const_fn
. Alternatively everything could be computed in a build script with the reflection api.How do you currently handle alignment? What would be your preferred API?