From 62c434274f4dd0c1c62ff6ccfd4c32f6f329ff89 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Sun, 7 Jun 2020 22:24:53 -0700 Subject: [PATCH] shader_defs: new leaner shader defs. they are now separate from uniforms --- crates/bevy_derive/src/lib.rs | 6 ++ crates/bevy_derive/src/shader_defs.rs | 71 +++++++++++++++ crates/bevy_derive/src/uniforms.rs | 10 +-- crates/bevy_pbr/src/material.rs | 6 +- crates/bevy_render/src/shader/mod.rs | 4 +- crates/bevy_render/src/shader/shader_defs.rs | 92 ++++++++++++++++++++ crates/bevy_render/src/shader/uniform.rs | 41 +-------- crates/bevy_sprite/src/color_material.rs | 6 +- crates/bevy_sprite/src/texture_atlas.rs | 11 +-- examples/shader/shader_custom_material.rs | 2 +- examples/shader/shader_defs.rs | 4 +- src/prelude.rs | 8 +- 12 files changed, 195 insertions(+), 66 deletions(-) create mode 100644 crates/bevy_derive/src/shader_defs.rs create mode 100644 crates/bevy_render/src/shader/shader_defs.rs diff --git a/crates/bevy_derive/src/lib.rs b/crates/bevy_derive/src/lib.rs index bcff61c1d1..bdd509097e 100644 --- a/crates/bevy_derive/src/lib.rs +++ b/crates/bevy_derive/src/lib.rs @@ -8,6 +8,7 @@ mod modules; mod render_resources; mod render_resource; mod resource; +mod shader_defs; mod uniforms; mod as_vertex_buffer_descriptor; @@ -43,6 +44,11 @@ pub fn derive_render_resource(input: TokenStream) -> TokenStream { render_resource::derive_render_resource(input) } +#[proc_macro_derive(ShaderDefs, attributes(shader_def, module))] +pub fn derive_shader_defs(input: TokenStream) -> TokenStream { + shader_defs::derive_shader_defs(input) +} + #[proc_macro_derive(AsVertexBufferDescriptor, attributes(vertex, module))] pub fn derive_as_vertex_buffer_descriptor(input: TokenStream) -> TokenStream { as_vertex_buffer_descriptor::derive_as_vertex_buffer_descriptor(input) diff --git a/crates/bevy_derive/src/shader_defs.rs b/crates/bevy_derive/src/shader_defs.rs new file mode 100644 index 0000000000..72d96ceb50 --- /dev/null +++ b/crates/bevy_derive/src/shader_defs.rs @@ -0,0 +1,71 @@ +use crate::modules::{get_modules, get_path}; +use proc_macro::TokenStream; +use proc_macro2::Ident; +use inflector::Inflector; +use quote::quote; +use syn::{parse_macro_input, Data, DataStruct, DeriveInput, Fields, Path}; + +static SHADER_DEF_ATTRIBUTE_NAME: &'static str = "shader_def"; + +pub fn derive_shader_defs(input: TokenStream) -> TokenStream { + let ast = parse_macro_input!(input as DeriveInput); + let modules = get_modules(&ast); + let bevy_render_path: Path = get_path(&modules.bevy_render); + + let fields = match &ast.data { + Data::Struct(DataStruct { + fields: Fields::Named(fields), + .. + }) => &fields.named, + _ => panic!("expected a struct with named fields"), + }; + + let shader_def_idents = fields + .iter() + .filter(|f| { + f.attrs + .iter() + .find(|a| { + a.path.get_ident().as_ref().unwrap().to_string() == SHADER_DEF_ATTRIBUTE_NAME + }) + .is_some() + }) + .map(|f| f.ident.as_ref().unwrap()) + .collect::>(); + let struct_name = &ast.ident; + let struct_name_pascal_case = ast.ident.to_string().to_pascal_case(); + let shader_defs = shader_def_idents + .iter() + .map(|i| format!("{}_{}", struct_name_pascal_case, i.to_string()).to_uppercase()); + + let shader_defs_len = shader_defs.len(); + let shader_def_indices = 0..shader_defs_len; + + let generics = ast.generics; + let (impl_generics, ty_generics, _where_clause) = generics.split_for_impl(); + + + TokenStream::from(quote! { + impl #impl_generics #bevy_render_path::shader::ShaderDefs for #struct_name#ty_generics { + fn shader_defs_len(&self) -> usize { + #shader_defs_len + } + + fn get_shader_def(&self, index: usize) -> Option<&str> { + use #bevy_render_path::shader::ShaderDef; + match index { + #(#shader_def_indices => if self.#shader_def_idents.is_defined() { + Some(#shader_defs) + } else { + None + },)* + _ => None, + } + } + + fn iter_shader_defs(&self) -> #bevy_render_path::shader::ShaderDefIterator { + #bevy_render_path::shader::ShaderDefIterator::new(self) + } + } + }) +} diff --git a/crates/bevy_derive/src/uniforms.rs b/crates/bevy_derive/src/uniforms.rs index d709486f4d..64f1dec236 100644 --- a/crates/bevy_derive/src/uniforms.rs +++ b/crates/bevy_derive/src/uniforms.rs @@ -138,15 +138,7 @@ pub fn derive_uniforms(input: TokenStream) -> TokenStream { // 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> { - use #bevy_render_path::shader::ShaderDefSuffixProvider; - let mut potential_shader_defs: Vec<(&'static str, Option<&'static str>)> = vec![ - #((#shader_def_field_names_screaming_snake, self.#shader_def_field_names.get_shader_def()),)* - ]; - - Some(potential_shader_defs.drain(..) - .filter(|(f, shader_def)| shader_def.is_some()) - .map(|(f, shader_def)| format!("{}_{}{}", #struct_name_uppercase, f, shader_def.unwrap())) - .collect::>()) + None } } }) diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index 5cf832d602..abe44677c0 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -1,10 +1,10 @@ use bevy_asset::{self, Handle}; -use bevy_render::{render_resource::RenderResources, shader::Uniforms, texture::Texture, Color}; +use bevy_render::{render_resource::RenderResources, shader::ShaderDefs, texture::Texture, Color}; -#[derive(Uniforms, RenderResources)] +#[derive(RenderResources, ShaderDefs)] pub struct StandardMaterial { pub albedo: Color, - #[uniform(shader_def)] + #[shader_def] pub albedo_texture: Option>, } diff --git a/crates/bevy_render/src/shader/mod.rs b/crates/bevy_render/src/shader/mod.rs index 2923813364..cef3fdcc53 100644 --- a/crates/bevy_render/src/shader/mod.rs +++ b/crates/bevy_render/src/shader/mod.rs @@ -1,8 +1,10 @@ mod shader; +mod shader_defs; mod shader_reflect; mod uniform; pub mod uniforms; pub use shader::*; +pub use shader_defs::*; pub use shader_reflect::*; -pub use uniform::*; \ No newline at end of file +pub use uniform::*; diff --git a/crates/bevy_render/src/shader/shader_defs.rs b/crates/bevy_render/src/shader/shader_defs.rs new file mode 100644 index 0000000000..e1fc1a9a74 --- /dev/null +++ b/crates/bevy_render/src/shader/shader_defs.rs @@ -0,0 +1,92 @@ + +use bevy_asset::{Assets, Handle}; +use crate::{Renderable, texture::Texture}; +use legion::prelude::{Res, Com, ComMut}; + +pub use bevy_derive::ShaderDefs; + +pub trait ShaderDef { + fn is_defined(&self) -> bool; +} + +pub trait ShaderDefs { + fn shader_defs_len(&self) -> usize; + fn get_shader_def(&self, index: usize) -> Option<&str>; + fn iter_shader_defs(&self) -> ShaderDefIterator; +} + + +pub struct ShaderDefIterator<'a> { + shader_defs: &'a dyn ShaderDefs, + index: usize, +} + +impl<'a> ShaderDefIterator<'a> { + pub fn new(shader_defs: &'a dyn ShaderDefs) -> Self { + Self { + shader_defs, + index: 0, + } + } +} +impl<'a> Iterator for ShaderDefIterator<'a> { + type Item = &'a str; + fn next(&mut self) -> Option { + loop { + if self.index == self.shader_defs.shader_defs_len() { + return None; + } + let shader_def = self + .shader_defs + .get_shader_def(self.index); + self.index += 1; + if shader_def.is_some() { + return shader_def; + } + } + } +} + +impl ShaderDef for bool { + fn is_defined(&self) -> bool { + *self + } +} + +impl ShaderDef for Option> { + fn is_defined(&self) -> bool { + self.is_some() + } +} + +pub fn shader_def_system(shader_defs: Com, mut renderable: ComMut) +where + T: ShaderDefs + Send + Sync + 'static, +{ + for shader_def in shader_defs.iter_shader_defs() { + renderable + .render_resource_assignments + .pipeline_specialization + .shader_specialization + .shader_defs + .insert(shader_def.to_string()); + } +} + +pub fn asset_shader_def_system( + assets: Res>, + asset_handle: Com>, + mut renderable: ComMut, +) where + T: ShaderDefs + Send + Sync + 'static, +{ + let shader_defs = assets.get(&asset_handle).unwrap(); + for shader_def in shader_defs.iter_shader_defs() { + renderable + .render_resource_assignments + .pipeline_specialization + .shader_specialization + .shader_defs + .insert(shader_def.to_string()); + } +} \ No newline at end of file diff --git a/crates/bevy_render/src/shader/uniform.rs b/crates/bevy_render/src/shader/uniform.rs index be231b5ab4..ab1807b7e8 100644 --- a/crates/bevy_render/src/shader/uniform.rs +++ b/crates/bevy_render/src/shader/uniform.rs @@ -1,6 +1,5 @@ -use crate::{pipeline::BindType, texture::Texture, Renderable}; -use bevy_asset::{Assets, Handle}; -use legion::prelude::*; +use crate::{pipeline::BindType, texture::Texture}; +use bevy_asset::Handle; pub use bevy_derive::{Uniform, Uniforms}; @@ -13,42 +12,6 @@ pub trait Uniforms: Send + Sync + 'static { fn get_field_bind_type(&self, name: &str) -> Option; } -pub fn shader_def_system(uniforms: Com, mut renderable: ComMut) -where - T: Uniforms + Send + Sync + 'static, -{ - if let Some(shader_defs) = uniforms.get_shader_defs() { - renderable - .render_resource_assignments - .pipeline_specialization - .shader_specialization - .shader_defs - .extend(shader_defs) - } -} - -pub fn asset_shader_def_system( - assets: Res>, - asset_handle: Com>, - mut renderable: ComMut, -) where - T: Uniforms + Send + Sync + 'static, -{ - if !renderable.is_visible || renderable.is_instanced { - return; - } - - let uniforms = assets.get(&asset_handle).unwrap(); - if let Some(shader_defs) = uniforms.get_shader_defs() { - renderable - .render_resource_assignments - .pipeline_specialization - .shader_specialization - .shader_defs - .extend(shader_defs) - } -} - pub trait ShaderDefSuffixProvider { fn get_shader_def(&self) -> Option<&'static str>; } diff --git a/crates/bevy_sprite/src/color_material.rs b/crates/bevy_sprite/src/color_material.rs index b3652eea1a..adde69dc3c 100644 --- a/crates/bevy_sprite/src/color_material.rs +++ b/crates/bevy_sprite/src/color_material.rs @@ -1,10 +1,10 @@ use bevy_asset::{self, Handle}; -use bevy_render::{texture::Texture, Color, render_resource::RenderResources, shader::Uniforms}; +use bevy_render::{render_resource::RenderResources, shader::ShaderDefs, texture::Texture, Color}; -#[derive(Uniforms, RenderResources)] +#[derive(RenderResources, ShaderDefs)] pub struct ColorMaterial { pub color: Color, - #[uniform(shader_def)] + #[shader_def] pub texture: Option>, } diff --git a/crates/bevy_sprite/src/texture_atlas.rs b/crates/bevy_sprite/src/texture_atlas.rs index 18f0475ef8..cf6535258d 100644 --- a/crates/bevy_sprite/src/texture_atlas.rs +++ b/crates/bevy_sprite/src/texture_atlas.rs @@ -1,26 +1,27 @@ use crate::Rect; use bevy_asset::Handle; -use bevy_render::{texture::Texture, render_resource::{RenderResources, RenderResource}, shader::{Uniforms, Uniform}}; use bevy_core::bytes::Bytes; +use bevy_render::{ + render_resource::{RenderResource, RenderResources}, + texture::Texture, +}; use glam::{Vec2, Vec3}; use std::collections::HashMap; -#[derive(Uniforms, RenderResources)] +#[derive(RenderResources)] pub struct TextureAtlas { pub texture: Handle, // TODO: add support to Uniforms derive to write dimensions and sprites to the same buffer pub dimensions: Vec2, - #[uniform(buffer)] #[render_resources(buffer)] pub textures: Vec, - #[uniform(ignore)] #[render_resources(ignore)] pub texture_handles: Option, usize>>, } // NOTE: cannot do `unsafe impl Byteable` here because Vec3 takes up the space of a Vec4. If/when glam changes this we can swap out // Bytes for Byteable as a micro-optimization. https://github.com/bitshifter/glam-rs/issues/36 -#[derive(Uniform, Bytes, RenderResources, RenderResource, Default)] +#[derive(Bytes, RenderResources, RenderResource, Default)] #[render_resources(from_self)] pub struct TextureAtlasSprite { pub position: Vec3, diff --git a/examples/shader/shader_custom_material.rs b/examples/shader/shader_custom_material.rs index 66a0d1a461..e24512398c 100644 --- a/examples/shader/shader_custom_material.rs +++ b/examples/shader/shader_custom_material.rs @@ -8,7 +8,7 @@ fn main() { .run(); } -#[derive(Uniforms, RenderResources, Default)] +#[derive(RenderResources, Default)] struct MyMaterial { pub color: Color, } diff --git a/examples/shader/shader_defs.rs b/examples/shader/shader_defs.rs index feca9eab60..e25454c888 100644 --- a/examples/shader/shader_defs.rs +++ b/examples/shader/shader_defs.rs @@ -12,11 +12,11 @@ fn main() { .run(); } -#[derive(Uniforms, RenderResources, Default)] +#[derive(RenderResources, ShaderDefs, Default)] struct MyMaterial { pub color: Color, - #[uniform(ignore, shader_def)] #[render_resources(ignore)] + #[shader_def] pub always_red: bool, } diff --git a/src/prelude.rs b/src/prelude.rs index 8b22b57cf8..e03e14c15e 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,7 +1,7 @@ pub use crate::{ app::{ - schedule_runner::ScheduleRunnerPlugin, stage, App, AppBuilder, AppPlugin, EntityArchetype, - EventReader, Events, FromResources, System, DynamicAppPlugin + schedule_runner::ScheduleRunnerPlugin, stage, App, AppBuilder, AppPlugin, DynamicAppPlugin, + EntityArchetype, EventReader, Events, FromResources, System, }, asset::{AddAsset, AssetEvent, AssetServer, Assets, Handle}, core::{ @@ -25,8 +25,10 @@ pub use crate::{ }, RenderGraph, }, - shader::{Shader, ShaderDefSuffixProvider, ShaderStage, ShaderStages, Uniforms}, render_resource::RenderResources, + shader::{ + Shader, ShaderDefSuffixProvider, ShaderDefs, ShaderStage, ShaderStages, Uniforms, + }, texture::Texture, Camera, Color, ColorSource, OrthographicProjection, PerspectiveProjection, Renderable, },