derive struct as uniform

This commit is contained in:
Carter Anderson 2020-05-03 17:54:16 -07:00
parent 736faa3f46
commit 05dbf31fd1
5 changed files with 202 additions and 118 deletions

View file

@ -1,5 +1,8 @@
extern crate proc_macro;
mod modules;
use modules::{get_modules, get_path};
use darling::FromMeta;
use inflector::Inflector;
use proc_macro::TokenStream;
@ -12,112 +15,6 @@ struct EntityArchetypeAttributeArgs {
pub tag: Option<bool>,
}
#[derive(FromMeta, Debug)]
struct ModuleAttributeArgs {
#[darling(default)]
pub bevy_render: Option<String>,
#[darling(default)]
pub bevy_asset: Option<String>,
#[darling(default)]
pub bevy_core: Option<String>,
#[darling(default)]
pub bevy_app: Option<String>,
#[darling(default)]
pub legion: Option<String>,
/// If true, it will use the meta "bevy" crate for dependencies by default (ex: bevy:app). If this is set to false, the individual bevy crates
/// will be used (ex: "bevy_app"). Defaults to "true"
#[darling(default)]
pub meta: bool,
}
struct Modules {
pub bevy_render: String,
pub bevy_asset: String,
pub bevy_core: String,
pub bevy_app: String,
pub legion: String,
}
impl Modules {
pub fn meta() -> Modules {
Modules {
bevy_asset: "bevy::asset".to_string(),
bevy_render: "bevy::render".to_string(),
bevy_core: "bevy::core".to_string(),
bevy_app: "bevy::app".to_string(),
legion: "bevy".to_string(),
}
}
pub fn external() -> Modules {
Modules {
bevy_asset: "bevy_asset".to_string(),
bevy_render: "bevy_render".to_string(),
bevy_core: "bevy_core".to_string(),
bevy_app: "bevy_app".to_string(),
legion: "legion".to_string(),
}
}
}
impl Default for ModuleAttributeArgs {
fn default() -> Self {
ModuleAttributeArgs {
bevy_asset: None,
bevy_render: None,
bevy_core: None,
bevy_app: None,
legion: None,
meta: true,
}
}
}
static MODULE_ATTRIBUTE_NAME: &'static str = "module";
fn get_modules(ast: &DeriveInput) -> Modules {
let module_attribute_args = ast
.attrs
.iter()
.find(|a| a.path.get_ident().as_ref().unwrap().to_string() == MODULE_ATTRIBUTE_NAME)
.map(|a| {
ModuleAttributeArgs::from_meta(&a.parse_meta().unwrap())
.unwrap_or_else(|_err| ModuleAttributeArgs::default())
});
if let Some(module_attribute_args) = module_attribute_args {
let mut modules = if module_attribute_args.meta {
Modules::meta()
} else {
Modules::external()
};
if let Some(path) = module_attribute_args.bevy_asset {
modules.bevy_asset = path;
}
if let Some(path) = module_attribute_args.bevy_render {
modules.bevy_render = path;
}
if let Some(path) = module_attribute_args.bevy_core {
modules.bevy_core = path;
}
if let Some(path) = module_attribute_args.bevy_app {
modules.bevy_app = path;
}
modules
} else {
Modules::meta()
}
}
fn get_path(path_str: &str) -> Path {
syn::parse(path_str.parse::<TokenStream>().unwrap()).unwrap()
}
#[proc_macro_derive(Resource)]
pub fn derive_resource(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
@ -153,6 +50,78 @@ pub fn derive_resource(input: TokenStream) -> TokenStream {
})
}
#[proc_macro_derive(Uniform, attributes(uniform, module))]
pub fn derive_uniform(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let modules = get_modules(&ast);
let bevy_asset_path = get_path(&modules.bevy_asset);
let bevy_core_path = get_path(&modules.bevy_core);
let bevy_render_path = get_path(&modules.bevy_render);
let generics = ast.generics;
let (impl_generics, ty_generics, _where_clause) = generics.split_for_impl();
let struct_name = &ast.ident;
let struct_name_string = struct_name.to_string();
TokenStream::from(quote! {
impl #impl_generics #bevy_render_path::shader::AsUniforms for #struct_name#ty_generics {
fn get_field_infos() -> &'static [#bevy_render_path::shader::FieldInfo] {
static FIELD_INFOS: &[#bevy_render_path::shader::FieldInfo] = &[
#bevy_render_path::shader::FieldInfo {
name: #struct_name_string,
uniform_name: #struct_name_string,
texture_name: #struct_name_string,
sampler_name: #struct_name_string,
is_instanceable: false,
}
];
&FIELD_INFOS
}
fn get_field_bind_type(&self, name: &str) -> Option<#bevy_render_path::shader::FieldBindType> {
use #bevy_render_path::shader::AsFieldBindType;
match name {
#struct_name_string => self.get_bind_type(),
_ => None,
}
}
fn get_uniform_bytes(&self, name: &str) -> Option<Vec<u8>> {
use #bevy_core_path::bytes::GetBytes;
match name {
#struct_name_string => Some(self.get_bytes()),
_ => None,
}
}
fn get_uniform_bytes_ref(&self, name: &str) -> Option<&[u8]> {
use #bevy_core_path::bytes::GetBytes;
match name {
#struct_name_string => self.get_bytes_ref(),
_ => None,
}
}
fn get_uniform_texture(&self, name: &str) -> Option<#bevy_asset_path::Handle<#bevy_render_path::texture::Texture>> {
None
}
// TODO: move this to field_info and add has_shader_def(&self, &str) -> bool
// TODO: this will be very allocation heavy. find a way to either make this allocation free
// or alternatively only run it when the shader_defs have changed
fn get_shader_defs(&self) -> Option<Vec<String>> {
None
}
fn get_vertex_buffer_descriptor() -> Option<&'static #bevy_render_path::pipeline::VertexBufferDescriptor> {
None
}
}
})
}
#[proc_macro_derive(EntityArchetype, attributes(tag, module))]
pub fn derive_entity_archetype(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
@ -449,7 +418,7 @@ pub fn derive_uniforms(input: TokenStream) -> TokenStream {
fn get_field_bind_type(&self, name: &str) -> Option<#bevy_render_path::shader::FieldBindType> {
use #bevy_render_path::shader::AsFieldBindType;
match name {
#(#active_uniform_field_name_strings => self.#active_uniform_field_names.get_field_bind_type(),)*
#(#active_uniform_field_name_strings => self.#active_uniform_field_names.get_bind_type(),)*
_ => None,
}
}

View file

@ -0,0 +1,109 @@
use darling::FromMeta;
use proc_macro::TokenStream;
use syn::{DeriveInput, Path};
#[derive(FromMeta, Debug)]
pub struct ModuleAttributeArgs {
#[darling(default)]
pub bevy_render: Option<String>,
#[darling(default)]
pub bevy_asset: Option<String>,
#[darling(default)]
pub bevy_core: Option<String>,
#[darling(default)]
pub bevy_app: Option<String>,
#[darling(default)]
pub legion: Option<String>,
/// If true, it will use the meta "bevy" crate for dependencies by default (ex: bevy:app). If this is set to false, the individual bevy crates
/// will be used (ex: "bevy_app"). Defaults to "true"
#[darling(default)]
pub meta: bool,
}
pub struct Modules {
pub bevy_render: String,
pub bevy_asset: String,
pub bevy_core: String,
pub bevy_app: String,
pub legion: String,
}
impl Modules {
pub fn meta() -> Modules {
Modules {
bevy_asset: "bevy::asset".to_string(),
bevy_render: "bevy::render".to_string(),
bevy_core: "bevy::core".to_string(),
bevy_app: "bevy::app".to_string(),
legion: "bevy".to_string(),
}
}
pub fn external() -> Modules {
Modules {
bevy_asset: "bevy_asset".to_string(),
bevy_render: "bevy_render".to_string(),
bevy_core: "bevy_core".to_string(),
bevy_app: "bevy_app".to_string(),
legion: "legion".to_string(),
}
}
}
impl Default for ModuleAttributeArgs {
fn default() -> Self {
ModuleAttributeArgs {
bevy_asset: None,
bevy_render: None,
bevy_core: None,
bevy_app: None,
legion: None,
meta: true,
}
}
}
pub static MODULE_ATTRIBUTE_NAME: &'static str = "module";
pub fn get_modules(ast: &DeriveInput) -> Modules {
let module_attribute_args = ast
.attrs
.iter()
.find(|a| a.path.get_ident().as_ref().unwrap().to_string() == MODULE_ATTRIBUTE_NAME)
.map(|a| {
ModuleAttributeArgs::from_meta(&a.parse_meta().unwrap())
.unwrap_or_else(|_err| ModuleAttributeArgs::default())
});
if let Some(module_attribute_args) = module_attribute_args {
let mut modules = if module_attribute_args.meta {
Modules::meta()
} else {
Modules::external()
};
if let Some(path) = module_attribute_args.bevy_asset {
modules.bevy_asset = path;
}
if let Some(path) = module_attribute_args.bevy_render {
modules.bevy_render = path;
}
if let Some(path) = module_attribute_args.bevy_core {
modules.bevy_core = path;
}
if let Some(path) = module_attribute_args.bevy_app {
modules.bevy_app = path;
}
modules
} else {
Modules::meta()
}
}
pub fn get_path(path_str: &str) -> Path {
syn::parse(path_str.parse::<TokenStream>().unwrap()).unwrap()
}

View file

@ -82,20 +82,20 @@ pub struct FieldInfo {
}
pub trait AsFieldBindType {
fn get_field_bind_type(&self) -> Option<FieldBindType>;
fn get_bind_type(&self) -> Option<FieldBindType>;
}
impl AsFieldBindType for ColorSource {
fn get_field_bind_type(&self) -> Option<FieldBindType> {
fn get_bind_type(&self) -> Option<FieldBindType> {
match *self {
ColorSource::Texture(_) => Some(FieldBindType::Texture),
ColorSource::Color(color) => color.get_field_bind_type(),
ColorSource::Color(color) => color.get_bind_type(),
}
}
}
impl AsFieldBindType for Option<Handle<Texture>> {
fn get_field_bind_type(&self) -> Option<FieldBindType> {
fn get_bind_type(&self) -> Option<FieldBindType> {
match *self {
Some(_) => Some(FieldBindType::Texture),
None => None,
@ -104,7 +104,7 @@ impl AsFieldBindType for Option<Handle<Texture>> {
}
impl AsFieldBindType for Handle<Texture> {
fn get_field_bind_type(&self) -> Option<FieldBindType> {
fn get_bind_type(&self) -> Option<FieldBindType> {
Some(FieldBindType::Texture)
}
}
@ -114,7 +114,7 @@ where
T: GetBytes,
{
// TODO: this breaks if get_bytes_ref() isn't supported for a datatype
default fn get_field_bind_type(&self) -> Option<FieldBindType> {
default fn get_bind_type(&self) -> Option<FieldBindType> {
Some(FieldBindType::Uniform {
size: self.get_bytes_ref().unwrap().len(),
})

View file

@ -65,7 +65,7 @@ impl AsUniforms for bevy_transform::prelude::LocalToWorld {
}
fn get_field_bind_type(&self, name: &str) -> Option<FieldBindType> {
match name {
"object" => self.0.get_field_bind_type(),
"object" => self.0.get_bind_type(),
_ => None,
}
}

View file

@ -1,13 +1,19 @@
use glam::Vec2;
use bevy_core::bytes::GetBytes;
use bevy_derive::Uniform;
use bevy_render::Color;
use bevy_derive::Uniforms;
use glam::Vec2;
use zerocopy::AsBytes;
#[repr(C)]
#[derive(Default, Clone, Copy, Debug, Uniforms, AsBytes)]
#[derive(Default, Clone, Copy, Debug, Uniform, AsBytes)]
#[module(meta = "false")]
pub struct Rect {
pub position: Vec2,
pub size: Vec2,
pub color: Color,
pub z_index: f32,
}
}
impl GetBytes for Rect {
fn get_bytes(&self) -> Vec<u8> { self.as_bytes().to_vec() }
fn get_bytes_ref(&self) -> Option<&[u8]> { Some(self.as_bytes()) }
}