shader_defs: new leaner shader defs. they are now separate from uniforms

This commit is contained in:
Carter Anderson 2020-06-07 22:24:53 -07:00
parent fd8f87400d
commit 62c434274f
12 changed files with 195 additions and 66 deletions

View File

@ -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)

View File

@ -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::<Vec<&Ident>>();
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)
}
}
})
}

View File

@ -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<Vec<String>> {
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::<Vec<String>>())
None
}
}
})

View File

@ -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<Handle<Texture>>,
}

View File

@ -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::*;
pub use uniform::*;

View File

@ -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<Self::Item> {
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<Handle<Texture>> {
fn is_defined(&self) -> bool {
self.is_some()
}
}
pub fn shader_def_system<T>(shader_defs: Com<T>, mut renderable: ComMut<Renderable>)
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<T>(
assets: Res<Assets<T>>,
asset_handle: Com<Handle<T>>,
mut renderable: ComMut<Renderable>,
) 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());
}
}

View File

@ -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<FieldBindType>;
}
pub fn shader_def_system<T>(uniforms: Com<T>, mut renderable: ComMut<Renderable>)
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<T>(
assets: Res<Assets<T>>,
asset_handle: Com<Handle<T>>,
mut renderable: ComMut<Renderable>,
) 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>;
}

View File

@ -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<Handle<Texture>>,
}

View File

@ -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<Texture>,
// 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<Rect>,
#[uniform(ignore)]
#[render_resources(ignore)]
pub texture_handles: Option<HashMap<Handle<Texture>, 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,

View File

@ -8,7 +8,7 @@ fn main() {
.run();
}
#[derive(Uniforms, RenderResources, Default)]
#[derive(RenderResources, Default)]
struct MyMaterial {
pub color: Color,
}

View File

@ -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,
}

View File

@ -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,
},