Last active
January 4, 2019 11:42
-
-
Save Frizi/22e5e16ad3c9d33164504dec01142882 to your computer and use it in GitHub Desktop.
StreamEncoder related types prototype
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use amethyst::{ | |
core::nalgebra::{Vector2, Vector4}, | |
ecs::{ | |
join::{BitAnd, Join, JoinIter}, | |
world::Index, | |
Component, ReadStorage, | |
}, | |
shred::{DynamicSystemData, Resources, SystemData}, | |
}; | |
/// Represents shader input attribute. | |
/// Holds a spir-v side type name and corresponding rust-side type. | |
trait ShaderInputType { | |
const NAME: &'static str; | |
type Value; | |
} | |
struct EncVec4; | |
impl ShaderInputType for EncVec4 { | |
const NAME: &'static str = "vec4"; | |
type Value = Vector4<f32>; | |
} | |
struct EncVec2; | |
impl ShaderInputType for EncVec2 { | |
const NAME: &'static str = "vec2"; | |
type Value = Vector2<f32>; | |
} | |
/// Combined type that maps a shader attribute layout (a tuple of `ShaderInputType`s) | |
/// into the corresponding output of an encoder. | |
trait EncodingValue { | |
type Value; | |
} | |
impl<A: ShaderInputType> EncodingValue for A { | |
type Value = A::Value; | |
} | |
impl<A: EncodingValue, B: EncodingValue> EncodingValue for (A, B) { | |
type Value = (A::Value, B::Value); | |
} | |
impl<A: EncodingValue, B: EncodingValue, C: EncodingValue> EncodingValue for (A, B, C) { | |
type Value = (A::Value, B::Value, C::Value); | |
} | |
// TODO: more tuple implementations in a macro | |
/// A compile-time definition of a shader attribute to encode. | |
/// | |
/// It is defined by a combination of `ShaderInputType` and a property name. | |
/// Allows to target attributes like `mat4 model` or `vec2 pos`; | |
trait EncAttribute { | |
const PROPERTY: &'static str; | |
type EncodedType: ShaderInputType + EncodingValue; | |
} | |
/// A compile-time list of `EncAttribute`s. | |
trait EncAttributes { | |
type EncodedType: EncodingValue; | |
/// Retreive a vec of associated (type name, property) tuples at runtime | |
fn get_props() -> Vec<(&'static str, &'static str)>; | |
} | |
impl<A: EncAttribute> EncAttributes for A { | |
type EncodedType = A::EncodedType; | |
fn get_props() -> Vec<(&'static str, &'static str)> { | |
vec![(A::EncodedType::NAME, A::PROPERTY)] | |
} | |
} | |
impl<A: EncAttributes, B: EncAttributes> EncAttributes for (A, B) { | |
type EncodedType = (A::EncodedType, B::EncodedType); | |
fn get_props() -> Vec<(&'static str, &'static str)> { | |
let mut vec = A::get_props(); | |
vec.extend(B::get_props()); | |
vec | |
} | |
} | |
impl<A: EncAttributes, B: EncAttributes, C: EncAttributes> EncAttributes for (A, B, C) { | |
type EncodedType = (A::EncodedType, B::EncodedType, C::EncodedType); | |
fn get_props() -> Vec<(&'static str, &'static str)> { | |
let mut vec = A::get_props(); | |
vec.extend(B::get_props()); | |
vec.extend(C::get_props()); | |
vec | |
} | |
} | |
// TODO: more tuple implementations in a macro | |
/// Shader attribute `vec4 tint` | |
struct TintAttribute; | |
impl EncAttribute for TintAttribute { | |
const PROPERTY: &'static str = "tint"; | |
type EncodedType = EncVec4; | |
} | |
/// Shader attribute `vec4 pos` | |
struct Pos2DAttribute; | |
impl EncAttribute for Pos2DAttribute { | |
const PROPERTY: &'static str = "pos"; | |
type EncodedType = EncVec4; | |
} | |
/// Shader attribute `vec4 dir_x` | |
struct DirXAttribute; | |
impl EncAttribute for DirXAttribute { | |
const PROPERTY: &'static str = "dir_x"; | |
type EncodedType = EncVec4; | |
} | |
/// Shader attribute `vec4 dir_y` | |
struct DirYAttribute; | |
impl EncAttribute for DirYAttribute { | |
const PROPERTY: &'static str = "dir_y"; | |
type EncodedType = EncVec4; | |
} | |
/// Trait that defines the encoding buffer writing stragety for a specified | |
/// shader layout. | |
/// Every encoder must push exactly one value per iterated entity to the buffer. | |
/// | |
/// The encoding scheduler is free to implement it in any way that is appropriate | |
/// for given situation. For example, multiple `EncodeBuffer` views might use | |
/// the same underlying buffer, but write with a common stride and different offsets. | |
trait EncodeBuffer<T: EncodingValue> { | |
fn push(&mut self, data: T::Value); | |
} | |
type EncType<'a, 'j, T> = | |
<<T as StreamEncoder<'a, 'j>>::EncodedAttributes as EncAttributes>::EncodedType; | |
/// A main trait that defines a strategy to encode specified stream of attributes | |
/// by iteration over declared set of components in the world. The encoder might also | |
/// use additional resources from the world. | |
/// | |
/// Every encoder must push exactly one value per iterated entity to the buffer. | |
trait StreamEncoder<'a, 'j> { | |
type EncodedAttributes: EncAttributes; | |
type Components: EncoderJoin<'a> + Join; | |
type SystemData: DynamicSystemData<'a>; | |
fn encode<B: EncodeBuffer<EncType<'a, 'j, Self>>>( | |
buffer: &mut B, | |
iter: JoinIter<Self::Components>, | |
system_data: Self::SystemData, | |
); | |
} | |
/// A list of encoders that have to run (possibly in parallel) in order to encode | |
/// the entire set of required shader attributes (shader layout). | |
struct EncoderBundle {} // TODO | |
/// A list of encoders that have to run (usually in sequence) in order to encode | |
/// all possible component permutations for a given shader layout. | |
struct LayoutEncoder {} // TODO | |
// example implementations | |
use amethyst::renderer::Rgba; | |
/// An encoder that encodes `Rgba` component into a stream of `vec4 tint`. | |
pub struct RgbaTintEncoder; | |
impl<'a: 'j, 'j> StreamEncoder<'a, 'j> for RgbaTintEncoder { | |
type EncodedAttributes = TintAttribute; | |
type Components = Encode<'a, 'j, Rgba>; | |
type SystemData = (); | |
fn encode<B: EncodeBuffer<EncType<'a, 'j, Self>>>( | |
buffer: &mut B, | |
iter: JoinIter<Self::Components>, | |
system_data: Self::SystemData, | |
) { | |
for rgba in iter { | |
buffer.push(Vector4::new(rgba.0, rgba.1, rgba.2, rgba.3)); | |
} | |
} | |
} | |
use amethyst::{ | |
assets::AssetStorage, | |
core::GlobalTransform, | |
ecs::Read, | |
renderer::{SpriteRender, SpriteSheet}, | |
}; | |
/// An encoder that encodes `GlobalTransform` and `RenderSpriteFlat2D` components | |
/// into streams of `vec4 pos`, `vec4 dir_x` and `vec4 dir_y`. | |
pub struct SpriteTransformEncoder; | |
impl<'a: 'j, 'j> StreamEncoder<'a, 'j> for SpriteTransformEncoder { | |
type EncodedAttributes = (Pos2DAttribute, DirXAttribute, DirYAttribute); | |
type Components = ( | |
Encode<'a, 'j, GlobalTransform>, | |
Encode<'a, 'j, SpriteRender>, | |
); | |
type SystemData = (Read<'a, AssetStorage<SpriteSheet>>); | |
fn encode<B: EncodeBuffer<EncType<'a, 'j, Self>>>( | |
buffer: &mut B, | |
iter: JoinIter<Self::Components>, | |
storage: Self::SystemData, | |
) { | |
for (transform, sprite_render) in iter { | |
let ref sprite_sheet = storage.get(&sprite_render.sprite_sheet).unwrap(); | |
let ref sprite = sprite_sheet.sprites[sprite_render.sprite_number]; | |
let dir_x = transform.0.column(0) * sprite.width; | |
let dir_y = transform.0.column(1) * sprite.height; | |
let pos = transform.0 * Vector4::new(-sprite.offsets[0], -sprite.offsets[1], 0.0, 1.0); | |
buffer.push((pos, dir_x, dir_y)); | |
} | |
} | |
} | |
/// A read-only access to a component storage. Component types listed in the list of `Encoder`s | |
/// on a `StreamEncoder` trait are used for scheduling the encoding for rendering. | |
/// | |
/// Constrained in the same way as `ReadStorage`. You can't use `WriteStorage` with the same inner type at the same time. | |
pub struct Encode<'a, 'j, A: Component>(&'j ReadStorage<'a, A>); | |
/// A read-only joinable composable list of component types. | |
/// TODO: Allow for constraining the iterated list of components by external BitVec | |
trait EncoderJoin<'a> { | |
type Data: Join; | |
unsafe fn open(self) -> (<Self::Data as Join>::Mask, <Self::Data as Join>::Value); | |
} | |
impl<'a: 'j, 'j, A: Component> Join for Encode<'a, 'j, A> { | |
type Mask = <&'j ReadStorage<'a, A> as Join>::Mask; | |
type Value = <&'j ReadStorage<'a, A> as Join>::Value; | |
type Type = <&'j ReadStorage<'a, A> as Join>::Type; | |
unsafe fn open(self) -> (Self::Mask, Self::Value) { | |
Join::open(self.0) | |
} | |
unsafe fn get(value: &mut Self::Value, id: Index) -> Self::Type { | |
<&'j ReadStorage<'a, A> as Join>::get(value, id) | |
} | |
} | |
impl<'a, D: Join> Join for EncoderJoin<'a, Data = D> | |
where | |
Self: Sized, | |
{ | |
type Mask = <D as Join>::Mask; | |
type Value = <D as Join>::Value; | |
type Type = <D as Join>::Type; | |
unsafe fn open(self) -> (Self::Mask, Self::Value) { | |
EncoderJoin::open(self) | |
} | |
unsafe fn get(value: &mut Self::Value, id: Index) -> Self::Type { | |
D::get(value, id) | |
} | |
} | |
impl<'a, 'j, A: Component> EncoderJoin<'a> for Encode<'a, 'j, A> { | |
type Data = &'j ReadStorage<'a, A>; | |
unsafe fn open(self) -> (<Self::Data as Join>::Mask, <Self::Data as Join>::Value) { | |
Join::open(self.0) | |
} | |
} | |
macro_rules! define_open { | |
// use variables to indicate the arity of the tuple | |
($($from:ident),*) => { | |
impl<'a, $($from,)*> EncoderJoin<'a> for ($($from),*,) | |
where $($from: EncoderJoin<'a> + Join),*, | |
($(<$from as Join>::Mask,)*): BitAnd, | |
{ | |
type Data = ($($from::Data),*,); | |
#[allow(non_snake_case)] | |
unsafe fn open(self) -> (<Self::Data as Join>::Mask, <Self::Data as Join>::Value) { | |
let ($($from,)*) = self; | |
let ($($from,)*) = ($(EncoderJoin::open($from),)*); | |
( | |
($($from.0),*,).and(), | |
($($from.1),*,) | |
) | |
} | |
} | |
} | |
} | |
define_open! {A} | |
define_open! {A, B} | |
define_open! {A, B, C} | |
define_open! {A, B, C, D} | |
define_open! {A, B, C, D, E} | |
define_open! {A, B, C, D, E, F} | |
define_open! {A, B, C, D, E, F, G} | |
define_open! {A, B, C, D, E, F, G, H} | |
define_open! {A, B, C, D, E, F, G, H, I} | |
define_open! {A, B, C, D, E, F, G, H, I, J} | |
define_open! {A, B, C, D, E, F, G, H, I, J, K} | |
define_open! {A, B, C, D, E, F, G, H, I, J, K, L} | |
define_open! {A, B, C, D, E, F, G, H, I, J, K, L, M} | |
define_open! {A, B, C, D, E, F, G, H, I, J, K, L, M, N} | |
define_open! {A, B, C, D, E, F, G, H, I, J, K, L, M, N, O} | |
define_open! {A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P} | |
// Fetching | |
trait EncodeFetcher<'a> { | |
type SystemData: SystemData<'a>; | |
fn scoped_fetch<F: FnOnce(Self)>(res: &'a Resources, f: F) | |
where | |
Self: Sized; | |
} | |
impl<'a: 'j, 'j, A: Component> EncodeFetcher<'a> for Encode<'a, 'j, A> { | |
type SystemData = ReadStorage<'a, A>; | |
fn scoped_fetch<F: FnOnce(Encode<'a, 'j, A>)>(res: &'a Resources, f: F) | |
where | |
Self: Sized, | |
{ | |
let data = <Self::SystemData as SystemData>::fetch(res); | |
let enc = Self(&data); | |
f(enc); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment