From 05dbf31fd1f673bf5d517b80e5378280643d7d97 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Sun, 3 May 2020 17:54:16 -0700 Subject: [PATCH] derive struct as uniform --- crates/bevy_derive/src/lib.rs | 183 ++++++++---------- crates/bevy_derive/src/modules.rs | 109 +++++++++++ crates/bevy_render/src/shader/uniform.rs | 12 +- .../src/shader/uniforms/local_to_world.rs | 2 +- crates/bevy_ui/src/rect.rs | 14 +- 5 files changed, 202 insertions(+), 118 deletions(-) create mode 100644 crates/bevy_derive/src/modules.rs diff --git a/crates/bevy_derive/src/lib.rs b/crates/bevy_derive/src/lib.rs index 419e553580..2a0793ffeb 100644 --- a/crates/bevy_derive/src/lib.rs +++ b/crates/bevy_derive/src/lib.rs @@ -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, } -#[derive(FromMeta, Debug)] -struct ModuleAttributeArgs { - #[darling(default)] - pub bevy_render: Option, - #[darling(default)] - pub bevy_asset: Option, - #[darling(default)] - pub bevy_core: Option, - #[darling(default)] - pub bevy_app: Option, - #[darling(default)] - pub legion: Option, - - /// 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::().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> { + 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> { + 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, } } diff --git a/crates/bevy_derive/src/modules.rs b/crates/bevy_derive/src/modules.rs new file mode 100644 index 0000000000..275976daec --- /dev/null +++ b/crates/bevy_derive/src/modules.rs @@ -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, + #[darling(default)] + pub bevy_asset: Option, + #[darling(default)] + pub bevy_core: Option, + #[darling(default)] + pub bevy_app: Option, + #[darling(default)] + pub legion: Option, + + /// 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::().unwrap()).unwrap() +} diff --git a/crates/bevy_render/src/shader/uniform.rs b/crates/bevy_render/src/shader/uniform.rs index 75c443038f..95b41b90f0 100644 --- a/crates/bevy_render/src/shader/uniform.rs +++ b/crates/bevy_render/src/shader/uniform.rs @@ -82,20 +82,20 @@ pub struct FieldInfo { } pub trait AsFieldBindType { - fn get_field_bind_type(&self) -> Option; + fn get_bind_type(&self) -> Option; } impl AsFieldBindType for ColorSource { - fn get_field_bind_type(&self) -> Option { + fn get_bind_type(&self) -> Option { 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> { - fn get_field_bind_type(&self) -> Option { + fn get_bind_type(&self) -> Option { match *self { Some(_) => Some(FieldBindType::Texture), None => None, @@ -104,7 +104,7 @@ impl AsFieldBindType for Option> { } impl AsFieldBindType for Handle { - fn get_field_bind_type(&self) -> Option { + fn get_bind_type(&self) -> Option { 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 { + default fn get_bind_type(&self) -> Option { Some(FieldBindType::Uniform { size: self.get_bytes_ref().unwrap().len(), }) diff --git a/crates/bevy_render/src/shader/uniforms/local_to_world.rs b/crates/bevy_render/src/shader/uniforms/local_to_world.rs index a6dabb3dd3..58199c44c0 100644 --- a/crates/bevy_render/src/shader/uniforms/local_to_world.rs +++ b/crates/bevy_render/src/shader/uniforms/local_to_world.rs @@ -65,7 +65,7 @@ impl AsUniforms for bevy_transform::prelude::LocalToWorld { } fn get_field_bind_type(&self, name: &str) -> Option { match name { - "object" => self.0.get_field_bind_type(), + "object" => self.0.get_bind_type(), _ => None, } } diff --git a/crates/bevy_ui/src/rect.rs b/crates/bevy_ui/src/rect.rs index b5f9dc84c0..4146de2c98 100644 --- a/crates/bevy_ui/src/rect.rs +++ b/crates/bevy_ui/src/rect.rs @@ -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, -} \ No newline at end of file +} + +impl GetBytes for Rect { + fn get_bytes(&self) -> Vec { self.as_bytes().to_vec() } + fn get_bytes_ref(&self) -> Option<&[u8]> { Some(self.as_bytes()) } +}