mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
Shaders: add BufferStruct to support bool and enums (#3109)
* node-macro: modernize `node` macro * node-macro: add `CrateIdent` struct containing resolved crate paths * shaders: add trait `BufferStruct` and derive macro * shaders: `gamma_correction` and `channel_mixer` gpu nodes * shaders: `selective_color` gpu node * shaders: `brightness_contrast_classic` gpu node * shaders: append GPU to display name * node-macro: fixup doc links * shaders: consistently append " GPU" to all shader node names
This commit is contained in:
263
node-graph/node-macro/src/buffer_struct.rs
Normal file
263
node-graph/node-macro/src/buffer_struct.rs
Normal file
@@ -0,0 +1,263 @@
|
||||
use crate::crate_ident::CrateIdent;
|
||||
use proc_macro2::{Ident, Span, TokenStream};
|
||||
use quote::{ToTokens, format_ident, quote};
|
||||
use std::collections::HashSet;
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::visit_mut::VisitMut;
|
||||
use syn::{Fields, GenericParam, Generics, Item, ItemEnum, ItemStruct, Meta, MetaList, Path, PathSegment, Result, Token, TypeParam, TypeParamBound, visit_mut};
|
||||
|
||||
pub fn derive_buffer_struct(crate_ident: &CrateIdent, content: proc_macro::TokenStream) -> Result<TokenStream> {
|
||||
let item = syn::parse::<Item>(content)?;
|
||||
match &item {
|
||||
Item::Enum(item) => derive_buffer_struct_enum(crate_ident, item),
|
||||
Item::Struct(item) => derive_buffer_struct_struct(crate_ident, item),
|
||||
_ => Err(syn::Error::new_spanned(&item, "Expected a struct or an enum")),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn derive_buffer_struct_enum(crate_ident: &CrateIdent, item: &ItemEnum) -> Result<TokenStream> {
|
||||
let gcore_shaders = crate_ident.gcore_shaders()?;
|
||||
let mod_buffer_struct = quote!(#gcore_shaders::shaders::buffer_struct);
|
||||
let reexport = quote!(#gcore_shaders::shaders::__private);
|
||||
|
||||
if !item.generics.params.is_empty() {
|
||||
return Err(syn::Error::new_spanned(&item.generics, "enum must not have any generics"));
|
||||
}
|
||||
|
||||
let enum_requirements_error = || {
|
||||
syn::Error::new(
|
||||
Span::call_site(),
|
||||
"deriving `BufferStruct` on an enum requires `#[repr(u32)]` and `#[derive(num_enum::FromPrimitive, num_enum::IntoPrimitive)]`",
|
||||
)
|
||||
};
|
||||
let repr_path = Path::from(format_ident!("repr"));
|
||||
let repr = item
|
||||
.attrs
|
||||
.iter()
|
||||
.filter_map(|a| match &a.meta {
|
||||
Meta::List(MetaList { path, tokens, .. }) if *path == repr_path => Some(tokens),
|
||||
_ => None,
|
||||
})
|
||||
.next()
|
||||
.ok_or_else(enum_requirements_error)?;
|
||||
|
||||
let ident = &item.ident;
|
||||
Ok(quote! {
|
||||
unsafe impl #mod_buffer_struct::BufferStruct for #ident
|
||||
{
|
||||
type Buffer = #repr;
|
||||
|
||||
fn write(from: Self) -> Self::Buffer {
|
||||
<#repr as From<Self>>::from(from)
|
||||
}
|
||||
|
||||
fn read(from: Self::Buffer) -> Self {
|
||||
<Self as #reexport::num_enum::FromPrimitive>::from_primitive(from)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// see [`BufferStruct`] docs
|
||||
///
|
||||
/// This is also largely copied from my (@firestar99) project and adjusted
|
||||
///
|
||||
/// [`BufferStruct`]: `graphene_core_shaders::shaders::buffer_struct::BufferStruct`
|
||||
pub fn derive_buffer_struct_struct(crate_ident: &CrateIdent, item: &ItemStruct) -> Result<TokenStream> {
|
||||
let gcore_shaders = crate_ident.gcore_shaders()?;
|
||||
let mod_buffer_struct = quote!(#gcore_shaders::shaders::buffer_struct);
|
||||
let reexport = quote!(#gcore_shaders::shaders::__private);
|
||||
|
||||
let generics = item
|
||||
.generics
|
||||
.params
|
||||
.iter()
|
||||
.filter_map(|g| match g {
|
||||
GenericParam::Lifetime(_) => None,
|
||||
GenericParam::Type(t) => Some(t.ident.clone()),
|
||||
GenericParam::Const(c) => Some(c.ident.clone()),
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut members_buffer = Punctuated::<TokenStream, Token![,]>::new();
|
||||
let mut write = Punctuated::<TokenStream, Token![,]>::new();
|
||||
let mut read = Punctuated::<TokenStream, Token![,]>::new();
|
||||
let mut gen_name_gen = GenericNameGen::new();
|
||||
let mut gen_ref_tys = Vec::new();
|
||||
let (members_buffer, write, read) = match &item.fields {
|
||||
Fields::Named(named) => {
|
||||
for f in &named.named {
|
||||
let name = f.ident.as_ref().unwrap();
|
||||
let mut ty = f.ty.clone();
|
||||
let mut visitor = GenericsVisitor::new(&item.ident, &generics);
|
||||
visit_mut::visit_type_mut(&mut visitor, &mut ty);
|
||||
if visitor.found_generics {
|
||||
gen_ref_tys.push(f.ty.clone());
|
||||
let gen_ident = gen_name_gen.next();
|
||||
members_buffer.push(quote!(#name: #gen_ident));
|
||||
} else {
|
||||
members_buffer.push(quote! {
|
||||
#name: <#ty as #mod_buffer_struct::BufferStruct>::Buffer
|
||||
});
|
||||
}
|
||||
|
||||
write.push(quote! {
|
||||
#name: <#ty as #mod_buffer_struct::BufferStruct>::write(from.#name)
|
||||
});
|
||||
read.push(quote! {
|
||||
#name: <#ty as #mod_buffer_struct::BufferStruct>::read(from.#name)
|
||||
});
|
||||
}
|
||||
(quote!({#members_buffer}), quote!(Self::Buffer {#write}), quote!(Self {#read}))
|
||||
}
|
||||
Fields::Unnamed(unnamed) => {
|
||||
for (i, f) in unnamed.unnamed.iter().enumerate() {
|
||||
let mut ty = f.ty.clone();
|
||||
let mut visitor = GenericsVisitor::new(&item.ident, &generics);
|
||||
visit_mut::visit_type_mut(&mut visitor, &mut ty);
|
||||
if visitor.found_generics {
|
||||
gen_ref_tys.push(f.ty.clone());
|
||||
members_buffer.push(gen_name_gen.next().into_token_stream());
|
||||
} else {
|
||||
members_buffer.push(quote! {
|
||||
<#ty as #mod_buffer_struct::BufferStruct>::Buffer
|
||||
});
|
||||
}
|
||||
|
||||
let index = syn::Index::from(i);
|
||||
write.push(quote! {
|
||||
<#ty as #mod_buffer_struct::BufferStruct>::write(from.#index)
|
||||
});
|
||||
read.push(quote! {
|
||||
<#ty as #mod_buffer_struct::BufferStruct>::read(from.#index)
|
||||
});
|
||||
}
|
||||
(quote!((#members_buffer);), quote!(Self::Buffer(#write)), quote!(Self(#read)))
|
||||
}
|
||||
Fields::Unit => (quote!(;), quote!(let _ = from; Self::Buffer {}), quote!(let _ = from; Self::Shader {})),
|
||||
};
|
||||
|
||||
let generics_decl = &item.generics;
|
||||
let generics_ref = decl_to_ref(item.generics.params.iter());
|
||||
let generics_where = gen_ref_tys
|
||||
.iter()
|
||||
.map(|ty| quote!(#ty: #mod_buffer_struct::BufferStruct))
|
||||
.collect::<Punctuated<TokenStream, Token![,]>>()
|
||||
.into_token_stream();
|
||||
|
||||
let generics_decl_any = gen_name_gen.decl(quote! {
|
||||
#reexport::bytemuck::Pod + Send + Sync
|
||||
});
|
||||
let generics_ref_buffer = gen_ref_tys
|
||||
.iter()
|
||||
.map(|ty| quote!(<#ty as #mod_buffer_struct::BufferStruct>::Buffer))
|
||||
.collect::<Punctuated<TokenStream, Token![,]>>()
|
||||
.into_token_stream();
|
||||
|
||||
let vis = &item.vis;
|
||||
let ident = &item.ident;
|
||||
let buffer_ident = format_ident!("{}Buffer", ident);
|
||||
Ok(quote! {
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, #reexport::bytemuck::Zeroable, #reexport::bytemuck::Pod)]
|
||||
#vis struct #buffer_ident #generics_decl_any #members_buffer
|
||||
|
||||
unsafe impl #generics_decl #mod_buffer_struct::BufferStruct for #ident #generics_ref
|
||||
where
|
||||
#ident #generics_ref: Copy,
|
||||
#generics_where
|
||||
{
|
||||
type Buffer = #buffer_ident <#generics_ref_buffer>;
|
||||
|
||||
fn write(from: Self) -> Self::Buffer {
|
||||
#write
|
||||
}
|
||||
|
||||
fn read(from: Self::Buffer) -> Self {
|
||||
#read
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
struct GenericsVisitor<'a> {
|
||||
self_ident: &'a Ident,
|
||||
generics: &'a HashSet<Ident>,
|
||||
found_generics: bool,
|
||||
}
|
||||
|
||||
impl<'a> GenericsVisitor<'a> {
|
||||
pub fn new(self_ident: &'a Ident, generics: &'a HashSet<Ident>) -> Self {
|
||||
Self {
|
||||
self_ident,
|
||||
generics,
|
||||
found_generics: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitMut for GenericsVisitor<'_> {
|
||||
fn visit_ident_mut(&mut self, i: &mut Ident) {
|
||||
if self.generics.contains(i) {
|
||||
self.found_generics = true;
|
||||
}
|
||||
visit_mut::visit_ident_mut(self, i);
|
||||
}
|
||||
|
||||
fn visit_path_segment_mut(&mut self, i: &mut PathSegment) {
|
||||
if i.ident.to_string() == "Self" {
|
||||
i.ident = self.self_ident.clone();
|
||||
}
|
||||
visit_mut::visit_path_segment_mut(self, i);
|
||||
}
|
||||
}
|
||||
|
||||
struct GenericNameGen(u32);
|
||||
|
||||
impl GenericNameGen {
|
||||
pub fn new() -> Self {
|
||||
Self(0)
|
||||
}
|
||||
|
||||
pub fn next(&mut self) -> Ident {
|
||||
let i = self.0;
|
||||
self.0 += 1;
|
||||
format_ident!("T{}", i)
|
||||
}
|
||||
|
||||
pub fn decl(self, ty: TokenStream) -> Generics {
|
||||
let params: Punctuated<GenericParam, Token![,]> = (0..self.0)
|
||||
.map(|i| {
|
||||
GenericParam::Type(TypeParam {
|
||||
attrs: Vec::new(),
|
||||
ident: format_ident!("T{}", i),
|
||||
colon_token: Some(Default::default()),
|
||||
bounds: Punctuated::from_iter([TypeParamBound::Verbatim(ty.clone())]),
|
||||
eq_token: None,
|
||||
default: None,
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
if !params.is_empty() {
|
||||
Generics {
|
||||
lt_token: Some(Default::default()),
|
||||
params,
|
||||
gt_token: Some(Default::default()),
|
||||
where_clause: None,
|
||||
}
|
||||
} else {
|
||||
Generics::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn decl_to_ref<'a>(generics: impl Iterator<Item = &'a GenericParam>) -> TokenStream {
|
||||
let out = generics
|
||||
.map(|generic| match generic {
|
||||
GenericParam::Lifetime(l) => l.lifetime.to_token_stream(),
|
||||
GenericParam::Type(t) => t.ident.to_token_stream(),
|
||||
GenericParam::Const(c) => c.ident.to_token_stream(),
|
||||
})
|
||||
.collect::<Punctuated<TokenStream, Token![,]>>();
|
||||
if out.is_empty() { TokenStream::new() } else { quote!(<#out>) }
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
use crate::parsing::*;
|
||||
use convert_case::{Case, Casing};
|
||||
use proc_macro_crate::FoundCrate;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{ToTokens, format_ident, quote, quote_spanned};
|
||||
use std::sync::atomic::AtomicU64;
|
||||
@@ -10,7 +9,7 @@ use syn::token::Comma;
|
||||
use syn::{Error, Ident, PatIdent, Token, WhereClause, WherePredicate, parse_quote};
|
||||
static NODE_ID: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
pub(crate) fn generate_node_code(parsed: &ParsedNodeFn) -> syn::Result<TokenStream2> {
|
||||
pub(crate) fn generate_node_code(crate_ident: &CrateIdent, parsed: &ParsedNodeFn) -> syn::Result<TokenStream2> {
|
||||
let ParsedNodeFn {
|
||||
vis,
|
||||
attributes,
|
||||
@@ -24,10 +23,10 @@ pub(crate) fn generate_node_code(parsed: &ParsedNodeFn) -> syn::Result<TokenStre
|
||||
is_async,
|
||||
fields,
|
||||
body,
|
||||
crate_name: graphene_core_crate,
|
||||
description,
|
||||
..
|
||||
} = parsed;
|
||||
let graphene_core = crate_ident.gcore()?;
|
||||
|
||||
let category = &attributes.category.as_ref().map(|value| quote!(Some(#value))).unwrap_or(quote!(None));
|
||||
let mod_name = format_ident!("_{}_mod", mod_name);
|
||||
@@ -62,14 +61,6 @@ pub(crate) fn generate_node_code(parsed: &ParsedNodeFn) -> syn::Result<TokenStre
|
||||
quote! { pub(super) #name: #r#gen }
|
||||
});
|
||||
|
||||
let graphene_core = match graphene_core_crate {
|
||||
FoundCrate::Itself => quote!(crate),
|
||||
FoundCrate::Name(name) => {
|
||||
let ident = Ident::new(name, proc_macro2::Span::call_site());
|
||||
quote!( #ident )
|
||||
}
|
||||
};
|
||||
|
||||
let mut future_idents = Vec::new();
|
||||
|
||||
let field_types: Vec<_> = fields
|
||||
@@ -297,7 +288,7 @@ pub(crate) fn generate_node_code(parsed: &ParsedNodeFn) -> syn::Result<TokenStre
|
||||
|
||||
let cfg = crate::shader_nodes::modify_cfg(attributes);
|
||||
let node_input_accessor = generate_node_input_references(parsed, fn_generics, &field_idents, &graphene_core, &identifier, &cfg);
|
||||
let ShaderTokens { shader_entry_point, gpu_node } = attributes.shader_node.as_ref().map(|n| n.codegen(parsed)).unwrap_or(Ok(ShaderTokens::default()))?;
|
||||
let ShaderTokens { shader_entry_point, gpu_node } = attributes.shader_node.as_ref().map(|n| n.codegen(crate_ident, parsed)).unwrap_or(Ok(ShaderTokens::default()))?;
|
||||
|
||||
Ok(quote! {
|
||||
/// Underlying implementation for [#struct_name]
|
||||
@@ -595,6 +586,7 @@ fn generate_register_node_impl(parsed: &ParsedNodeFn, field_names: &[&Ident], st
|
||||
})
|
||||
}
|
||||
|
||||
use crate::crate_ident::CrateIdent;
|
||||
use crate::shader_nodes::{ShaderCodegen, ShaderTokens};
|
||||
use syn::visit_mut::VisitMut;
|
||||
use syn::{GenericArgument, Lifetime, Type};
|
||||
|
||||
46
node-graph/node-macro/src/crate_ident.rs
Normal file
46
node-graph/node-macro/src/crate_ident.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
use proc_macro_crate::{FoundCrate, crate_name};
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
pub struct CrateIdent {
|
||||
gcore: syn::Result<TokenStream>,
|
||||
gcore_shaders: syn::Result<TokenStream>,
|
||||
wgpu_executor: syn::Result<TokenStream>,
|
||||
}
|
||||
|
||||
impl CrateIdent {
|
||||
pub fn gcore(&self) -> syn::Result<&TokenStream> {
|
||||
self.gcore.as_ref().map_err(Clone::clone)
|
||||
}
|
||||
|
||||
pub fn gcore_shaders(&self) -> syn::Result<&TokenStream> {
|
||||
self.gcore_shaders.as_ref().map_err(Clone::clone)
|
||||
}
|
||||
|
||||
pub fn wgpu_executor(&self) -> syn::Result<&TokenStream> {
|
||||
self.wgpu_executor.as_ref().map_err(Clone::clone)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for CrateIdent {
|
||||
fn default() -> Self {
|
||||
let find_crate = |orig_name| match crate_name(orig_name) {
|
||||
Ok(FoundCrate::Itself) => Ok(quote!(crate)),
|
||||
Ok(FoundCrate::Name(name)) => {
|
||||
let name = format_ident!("{}", name);
|
||||
Ok(quote!(::#name))
|
||||
}
|
||||
Err(e) => Err(syn::Error::new(Span::call_site(), &format!("Could not find dependency on `{orig_name}`:\n{e}"))),
|
||||
};
|
||||
|
||||
let gcore = find_crate("graphene-core");
|
||||
let gcore_shaders = find_crate("graphene-core-shaders").or_else(|eshaders| {
|
||||
gcore
|
||||
.as_ref()
|
||||
.map(Clone::clone)
|
||||
.map_err(|ecore| syn::Error::new(Span::call_site(), &format!("{ecore}\n\nFallback: {eshaders}")))
|
||||
});
|
||||
let wgpu_executor = find_crate("wgpu-executor");
|
||||
Self { gcore, gcore_shaders, wgpu_executor }
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
use crate::crate_ident::CrateIdent;
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro_error2::proc_macro_error;
|
||||
use syn::GenericParam;
|
||||
|
||||
mod buffer_struct;
|
||||
mod codegen;
|
||||
mod crate_ident;
|
||||
mod derive_choice_type;
|
||||
mod parsing;
|
||||
mod shader_nodes;
|
||||
@@ -13,7 +16,7 @@ mod validation;
|
||||
#[proc_macro_attribute]
|
||||
pub fn node(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
// Performs the `node_impl` macro's functionality of attaching an `impl Node for TheGivenStruct` block to the node struct
|
||||
parsing::new_node_fn(attr.into(), item.into()).into()
|
||||
parsing::new_node_fn(attr.into(), item.into()).unwrap_or_else(|err| err.to_compile_error()).into()
|
||||
}
|
||||
|
||||
/// Generate meta-information for an enum.
|
||||
@@ -27,5 +30,12 @@ pub fn node(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
/// Doc comments on a variant become tooltip text.
|
||||
#[proc_macro_derive(ChoiceType, attributes(widget, menu_separator, label, icon))]
|
||||
pub fn derive_choice_type(input_item: TokenStream) -> TokenStream {
|
||||
TokenStream::from(derive_choice_type::derive_choice_type_impl(input_item.into()).unwrap_or_else(|err| err.to_compile_error()))
|
||||
derive_choice_type::derive_choice_type_impl(input_item.into()).unwrap_or_else(|err| err.to_compile_error()).into()
|
||||
}
|
||||
|
||||
/// Derive a struct to implement `ShaderStruct`, see that for docs.
|
||||
#[proc_macro_derive(BufferStruct)]
|
||||
pub fn derive_buffer_struct(input_item: TokenStream) -> TokenStream {
|
||||
let crate_ident = CrateIdent::default();
|
||||
TokenStream::from(buffer_struct::derive_buffer_struct(&crate_ident, input_item.into()).unwrap_or_else(|err| err.to_compile_error()))
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ use syn::{
|
||||
};
|
||||
|
||||
use crate::codegen::generate_node_code;
|
||||
use crate::crate_ident::CrateIdent;
|
||||
use crate::shader_nodes::ShaderNodeType;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -35,7 +36,6 @@ pub(crate) struct ParsedNodeFn {
|
||||
pub(crate) is_async: bool,
|
||||
pub(crate) fields: Vec<ParsedField>,
|
||||
pub(crate) body: TokenStream2,
|
||||
pub(crate) crate_name: proc_macro_crate::FoundCrate,
|
||||
pub(crate) description: String,
|
||||
}
|
||||
|
||||
@@ -314,12 +314,6 @@ fn parse_node_fn(attr: TokenStream2, item: TokenStream2) -> syn::Result<ParsedNo
|
||||
let output_type = parse_output(&input_fn.sig.output)?;
|
||||
let where_clause = input_fn.sig.generics.where_clause;
|
||||
let body = input_fn.block.to_token_stream();
|
||||
let crate_name = proc_macro_crate::crate_name("graphene-core").map_err(|e| {
|
||||
Error::new(
|
||||
proc_macro2::Span::call_site(),
|
||||
format!("Failed to find location of graphene_core. Make sure it is imported as a dependency: {e}"),
|
||||
)
|
||||
})?;
|
||||
let description = input_fn
|
||||
.attrs
|
||||
.iter()
|
||||
@@ -350,7 +344,6 @@ fn parse_node_fn(attr: TokenStream2, item: TokenStream2) -> syn::Result<ParsedNo
|
||||
fields,
|
||||
where_clause,
|
||||
body,
|
||||
crate_name,
|
||||
description,
|
||||
})
|
||||
}
|
||||
@@ -680,24 +673,12 @@ fn extract_attribute<'a>(attrs: &'a [Attribute], name: &str) -> Option<&'a Attri
|
||||
}
|
||||
|
||||
// Modify the new_node_fn function to use the code generation
|
||||
pub fn new_node_fn(attr: TokenStream2, item: TokenStream2) -> TokenStream2 {
|
||||
let parse_result = parse_node_fn(attr, item.clone());
|
||||
let Ok(mut parsed_node) = parse_result else {
|
||||
let e = parse_result.unwrap_err();
|
||||
return Error::new(e.span(), format!("Failed to parse node function: {e}")).to_compile_error();
|
||||
};
|
||||
|
||||
pub fn new_node_fn(attr: TokenStream2, item: TokenStream2) -> syn::Result<TokenStream2> {
|
||||
let crate_ident = CrateIdent::default();
|
||||
let mut parsed_node = parse_node_fn(attr, item.clone()).map_err(|e| Error::new(e.span(), format!("Failed to parse node function: {e}")))?;
|
||||
parsed_node.replace_impl_trait_in_input();
|
||||
if let Err(e) = crate::validation::validate_node_fn(&parsed_node) {
|
||||
return Error::new(e.span(), format!("Validation Error:\n{e}")).to_compile_error();
|
||||
}
|
||||
match generate_node_code(&parsed_node) {
|
||||
Ok(parsed) => parsed,
|
||||
Err(e) => {
|
||||
// Return the error as a compile error
|
||||
Error::new(e.span(), format!("Failed to parse node function: {e}")).to_compile_error()
|
||||
}
|
||||
}
|
||||
crate::validation::validate_node_fn(&parsed_node).map_err(|e| Error::new(e.span(), format!("Validation Error: {e}")))?;
|
||||
generate_node_code(&crate_ident, &parsed_node).map_err(|e| Error::new(e.span(), format!("Failed to generate node code: {e}")))
|
||||
}
|
||||
|
||||
impl ParsedNodeFn {
|
||||
@@ -728,7 +709,6 @@ impl ParsedNodeFn {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use proc_macro_crate::FoundCrate;
|
||||
use proc_macro2::Span;
|
||||
use quote::{quote, quote_spanned};
|
||||
use syn::parse_quote;
|
||||
@@ -881,7 +861,6 @@ mod tests {
|
||||
unit: None,
|
||||
}],
|
||||
body: TokenStream2::new(),
|
||||
crate_name: FoundCrate::Itself,
|
||||
description: String::from("Multi\nLine\n"),
|
||||
};
|
||||
|
||||
@@ -964,7 +943,6 @@ mod tests {
|
||||
},
|
||||
],
|
||||
body: TokenStream2::new(),
|
||||
crate_name: FoundCrate::Itself,
|
||||
description: String::from("Hello\n\t\t\t\tWorld\n"),
|
||||
};
|
||||
|
||||
@@ -1028,7 +1006,6 @@ mod tests {
|
||||
unit: None,
|
||||
}],
|
||||
body: TokenStream2::new(),
|
||||
crate_name: FoundCrate::Itself,
|
||||
description: "Test\n".into(),
|
||||
};
|
||||
|
||||
@@ -1096,7 +1073,6 @@ mod tests {
|
||||
unit: None,
|
||||
}],
|
||||
body: TokenStream2::new(),
|
||||
crate_name: FoundCrate::Itself,
|
||||
description: String::new(),
|
||||
};
|
||||
|
||||
@@ -1166,7 +1142,6 @@ mod tests {
|
||||
unit: None,
|
||||
}],
|
||||
body: TokenStream2::new(),
|
||||
crate_name: FoundCrate::Itself,
|
||||
description: String::new(),
|
||||
};
|
||||
|
||||
@@ -1229,7 +1204,6 @@ mod tests {
|
||||
unit: None,
|
||||
}],
|
||||
body: TokenStream2::new(),
|
||||
crate_name: FoundCrate::Itself,
|
||||
description: String::new(),
|
||||
};
|
||||
|
||||
@@ -1272,7 +1246,6 @@ mod tests {
|
||||
is_async: false,
|
||||
fields: vec![],
|
||||
body: TokenStream2::new(),
|
||||
crate_name: FoundCrate::Itself,
|
||||
description: String::new(),
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::crate_ident::CrateIdent;
|
||||
use crate::parsing::{NodeFnAttributes, ParsedNodeFn};
|
||||
use crate::shader_nodes::per_pixel_adjust::PerPixelAdjust;
|
||||
use proc_macro2::{Ident, TokenStream};
|
||||
@@ -50,11 +51,11 @@ impl Parse for ShaderNodeType {
|
||||
}
|
||||
|
||||
pub trait ShaderCodegen {
|
||||
fn codegen(&self, parsed: &ParsedNodeFn) -> syn::Result<ShaderTokens>;
|
||||
fn codegen(&self, crate_ident: &CrateIdent, parsed: &ParsedNodeFn) -> syn::Result<ShaderTokens>;
|
||||
}
|
||||
|
||||
impl ShaderCodegen for ShaderNodeType {
|
||||
fn codegen(&self, parsed: &ParsedNodeFn) -> syn::Result<ShaderTokens> {
|
||||
fn codegen(&self, crate_ident: &CrateIdent, parsed: &ParsedNodeFn) -> syn::Result<ShaderTokens> {
|
||||
match self {
|
||||
ShaderNodeType::None | ShaderNodeType::ShaderNode => (),
|
||||
_ => {
|
||||
@@ -66,7 +67,7 @@ impl ShaderCodegen for ShaderNodeType {
|
||||
|
||||
match self {
|
||||
ShaderNodeType::None | ShaderNodeType::ShaderNode => Ok(ShaderTokens::default()),
|
||||
ShaderNodeType::PerPixelAdjust(x) => x.codegen(parsed),
|
||||
ShaderNodeType::PerPixelAdjust(x) => x.codegen(crate_ident, parsed),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use crate::crate_ident::CrateIdent;
|
||||
use crate::parsing::{Input, NodeFnAttributes, ParsedField, ParsedFieldType, ParsedNodeFn, RegularParsedField};
|
||||
use crate::shader_nodes::{SHADER_NODES_FEATURE_GATE, ShaderCodegen, ShaderNodeType, ShaderTokens};
|
||||
use convert_case::{Case, Casing};
|
||||
use proc_macro_crate::FoundCrate;
|
||||
use proc_macro2::{Ident, TokenStream};
|
||||
use proc_macro2::{Ident, Span, TokenStream};
|
||||
use quote::{ToTokens, format_ident, quote};
|
||||
use std::borrow::Cow;
|
||||
use syn::parse::{Parse, ParseStream};
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::{PatIdent, Type, parse_quote};
|
||||
use syn::{LitStr, PatIdent, Type, parse_quote};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PerPixelAdjust {}
|
||||
@@ -19,7 +19,7 @@ impl Parse for PerPixelAdjust {
|
||||
}
|
||||
|
||||
impl ShaderCodegen for PerPixelAdjust {
|
||||
fn codegen(&self, parsed: &ParsedNodeFn) -> syn::Result<ShaderTokens> {
|
||||
fn codegen(&self, crate_ident: &CrateIdent, parsed: &ParsedNodeFn) -> syn::Result<ShaderTokens> {
|
||||
let fn_name = &parsed.fn_name;
|
||||
|
||||
let mut params;
|
||||
@@ -74,6 +74,7 @@ impl ShaderCodegen for PerPixelAdjust {
|
||||
let shader_node_mod = format_ident!("{}_shader_node", fn_name);
|
||||
|
||||
let codegen = PerPixelAdjustCodegen {
|
||||
crate_ident,
|
||||
parsed,
|
||||
params,
|
||||
has_uniform,
|
||||
@@ -93,6 +94,7 @@ impl ShaderCodegen for PerPixelAdjust {
|
||||
}
|
||||
|
||||
pub struct PerPixelAdjustCodegen<'a> {
|
||||
crate_ident: &'a CrateIdent,
|
||||
parsed: &'a ParsedNodeFn,
|
||||
params: Vec<Param<'a>>,
|
||||
has_uniform: bool,
|
||||
@@ -107,6 +109,9 @@ pub struct PerPixelAdjustCodegen<'a> {
|
||||
impl PerPixelAdjustCodegen<'_> {
|
||||
fn codegen_shader_entry_point(&self) -> syn::Result<TokenStream> {
|
||||
let fn_name = &self.parsed.fn_name;
|
||||
let gcore_shaders = self.crate_ident.gcore_shaders()?;
|
||||
let reexport = quote!(#gcore_shaders::shaders::__private);
|
||||
|
||||
let uniform_members = self
|
||||
.params
|
||||
.iter()
|
||||
@@ -115,6 +120,16 @@ impl PerPixelAdjustCodegen<'_> {
|
||||
ParamType::Uniform => Some(quote! {#ident: #ty}),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let uniform_struct_ident = &self.uniform_struct_ident;
|
||||
let uniform_struct = parse_quote! {
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct #uniform_struct_ident {
|
||||
#(pub #uniform_members),*
|
||||
}
|
||||
};
|
||||
let uniform_struct_shader_struct_derive = crate::buffer_struct::derive_buffer_struct_struct(&self.crate_ident, &uniform_struct)?;
|
||||
|
||||
let image_params = self
|
||||
.params
|
||||
.iter()
|
||||
@@ -135,31 +150,28 @@ impl PerPixelAdjustCodegen<'_> {
|
||||
|
||||
let entry_point_mod = &self.entry_point_mod;
|
||||
let entry_point_name = &self.entry_point_name_ident;
|
||||
let uniform_struct_ident = &self.uniform_struct_ident;
|
||||
Ok(quote! {
|
||||
pub mod #entry_point_mod {
|
||||
use super::*;
|
||||
use graphene_core_shaders::color::Color;
|
||||
use spirv_std::spirv;
|
||||
use spirv_std::glam::{Vec4, Vec4Swizzles};
|
||||
use spirv_std::image::{Image2d, ImageWithMethods};
|
||||
use spirv_std::image::sample_with::lod;
|
||||
use #gcore_shaders::color::Color;
|
||||
use #reexport::glam::{Vec4, Vec4Swizzles};
|
||||
use #reexport::spirv_std::spirv;
|
||||
use #reexport::spirv_std::image::{Image2d, ImageWithMethods};
|
||||
use #reexport::spirv_std::image::sample_with::lod;
|
||||
|
||||
pub const #entry_point_name: &str = core::concat!(core::module_path!(), "::entry_point");
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, bytemuck::NoUninit)]
|
||||
pub struct #uniform_struct_ident {
|
||||
#(pub #uniform_members),*
|
||||
}
|
||||
#uniform_struct
|
||||
#uniform_struct_shader_struct_derive
|
||||
|
||||
#[spirv(fragment)]
|
||||
pub fn entry_point(
|
||||
#[spirv(frag_coord)] frag_coord: Vec4,
|
||||
color_out: &mut Vec4,
|
||||
#[spirv(descriptor_set = 0, binding = 0, storage_buffer)] uniform: &Uniform,
|
||||
#[spirv(descriptor_set = 0, binding = 0, storage_buffer)] uniform: &UniformBuffer,
|
||||
#(#image_params),*
|
||||
) {
|
||||
let uniform = <Uniform as #gcore_shaders::shaders::buffer_struct::BufferStruct>::read(*uniform);
|
||||
let texel_coord = frag_coord.xy().as_uvec2();
|
||||
let color: Color = #fn_name(#context, #(#call_args),*);
|
||||
*color_out = color.to_vec4();
|
||||
@@ -169,10 +181,8 @@ impl PerPixelAdjustCodegen<'_> {
|
||||
}
|
||||
|
||||
fn codegen_gpu_node(&self) -> syn::Result<TokenStream> {
|
||||
let gcore = match &self.parsed.crate_name {
|
||||
FoundCrate::Itself => format_ident!("crate"),
|
||||
FoundCrate::Name(name) => format_ident!("{name}"),
|
||||
};
|
||||
let gcore = self.crate_ident.gcore()?;
|
||||
let wgpu_executor = self.crate_ident.wgpu_executor()?;
|
||||
|
||||
// adapt fields for gpu node
|
||||
let raster_gpu: Type = parse_quote!(#gcore::table::Table<#gcore::raster_types::Raster<#gcore::raster_types::GPU>>);
|
||||
@@ -207,13 +217,13 @@ impl PerPixelAdjustCodegen<'_> {
|
||||
.collect::<syn::Result<Vec<_>>>()?;
|
||||
|
||||
// insert wgpu_executor field
|
||||
let wgpu_executor = format_ident!("__wgpu_executor");
|
||||
let executor = format_ident!("__wgpu_executor");
|
||||
fields.push(ParsedField {
|
||||
pat_ident: PatIdent {
|
||||
attrs: vec![],
|
||||
by_ref: None,
|
||||
mutability: None,
|
||||
ident: parse_quote!(#wgpu_executor),
|
||||
ident: parse_quote!(#executor),
|
||||
subpat: None,
|
||||
},
|
||||
name: None,
|
||||
@@ -271,7 +281,7 @@ impl PerPixelAdjustCodegen<'_> {
|
||||
let entry_point_name = &self.entry_point_name;
|
||||
let body = quote! {
|
||||
{
|
||||
#wgpu_executor.shader_runtime.run_per_pixel_adjust(&::wgpu_executor::shader_runtime::per_pixel_adjust_runtime::Shaders {
|
||||
#executor.shader_runtime.run_per_pixel_adjust(&::wgpu_executor::shader_runtime::per_pixel_adjust_runtime::Shaders {
|
||||
wgsl_shader: crate::WGSL_SHADER,
|
||||
fragment_shader_name: super::#entry_point_name,
|
||||
has_uniform: #has_uniform,
|
||||
@@ -280,9 +290,13 @@ impl PerPixelAdjustCodegen<'_> {
|
||||
};
|
||||
|
||||
// call node codegen
|
||||
let display_name = self.parsed.attributes.display_name.clone();
|
||||
let display_name = display_name.unwrap_or_else(|| LitStr::new(&self.shader_node_mod.to_string().strip_suffix("_shader_node").unwrap().to_case(Case::Title), Span::call_site()));
|
||||
let display_name = LitStr::new(&format!("{} GPU", display_name.value()), display_name.span());
|
||||
let mut parsed_node_fn = ParsedNodeFn {
|
||||
vis: self.parsed.vis.clone(),
|
||||
attributes: NodeFnAttributes {
|
||||
display_name: Some(display_name),
|
||||
shader_node: Some(ShaderNodeType::ShaderNode),
|
||||
..self.parsed.attributes.clone()
|
||||
},
|
||||
@@ -301,11 +315,10 @@ impl PerPixelAdjustCodegen<'_> {
|
||||
is_async: true,
|
||||
fields,
|
||||
body,
|
||||
crate_name: self.parsed.crate_name.clone(),
|
||||
description: "".to_string(),
|
||||
description: self.parsed.description.clone(),
|
||||
};
|
||||
parsed_node_fn.replace_impl_trait_in_input();
|
||||
let gpu_node_impl = crate::codegen::generate_node_code(&parsed_node_fn)?;
|
||||
let gpu_node_impl = crate::codegen::generate_node_code(self.crate_ident, &parsed_node_fn)?;
|
||||
|
||||
// wrap node in `mod #gpu_node_mod`
|
||||
let shader_node_mod = &self.shader_node_mod;
|
||||
@@ -313,7 +326,7 @@ impl PerPixelAdjustCodegen<'_> {
|
||||
#[cfg(feature = #SHADER_NODES_FEATURE_GATE)]
|
||||
mod #shader_node_mod {
|
||||
use super::*;
|
||||
use wgpu_executor::WgpuExecutor;
|
||||
use #wgpu_executor::WgpuExecutor;
|
||||
|
||||
#gpu_node_impl
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user