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:
parent
acd7ba38cc
commit
f12b4da549
|
|
@ -2137,10 +2137,13 @@ dependencies = [
|
||||||
"graphene-core",
|
"graphene-core",
|
||||||
"half",
|
"half",
|
||||||
"log",
|
"log",
|
||||||
|
"node-macro",
|
||||||
"num-derive",
|
"num-derive",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
|
"num_enum",
|
||||||
"serde",
|
"serde",
|
||||||
"specta",
|
"specta",
|
||||||
|
"spirv-std",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -2186,6 +2189,7 @@ dependencies = [
|
||||||
"ndarray",
|
"ndarray",
|
||||||
"node-macro",
|
"node-macro",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
|
"num_enum",
|
||||||
"rand 0.9.2",
|
"rand 0.9.2",
|
||||||
"rand_chacha 0.9.0",
|
"rand_chacha 0.9.0",
|
||||||
"serde",
|
"serde",
|
||||||
|
|
@ -3470,6 +3474,7 @@ version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"convert_case 0.8.0",
|
"convert_case 0.8.0",
|
||||||
"graphene-core",
|
"graphene-core",
|
||||||
|
"graphene-core-shaders",
|
||||||
"indoc",
|
"indoc",
|
||||||
"proc-macro-crate",
|
"proc-macro-crate",
|
||||||
"proc-macro-error2",
|
"proc-macro-error2",
|
||||||
|
|
@ -5524,6 +5529,7 @@ version = "0.9.0"
|
||||||
source = "git+https://github.com/rust-gpu/rust-gpu?rev=c12f216121820580731440ee79ebc7403d6ea04f#c12f216121820580731440ee79ebc7403d6ea04f"
|
source = "git+https://github.com/rust-gpu/rust-gpu?rev=c12f216121820580731440ee79ebc7403d6ea04f#c12f216121820580731440ee79ebc7403d6ea04f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
|
"bytemuck",
|
||||||
"glam",
|
"glam",
|
||||||
"libm",
|
"libm",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ graphite-proc-macros = { path = "proc-macros" }
|
||||||
|
|
||||||
# Workspace dependencies
|
# Workspace dependencies
|
||||||
rustc-hash = "2.0"
|
rustc-hash = "2.0"
|
||||||
bytemuck = { version = "1.13", features = ["derive"] }
|
bytemuck = { version = "1.13", features = ["derive", "min_const_generics"] }
|
||||||
serde = { version = "1.0", features = ["derive", "rc"] }
|
serde = { version = "1.0", features = ["derive", "rc"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
serde-wasm-bindgen = "0.6"
|
serde-wasm-bindgen = "0.6"
|
||||||
|
|
@ -154,7 +154,7 @@ parley = "0.5"
|
||||||
skrifa = "0.36"
|
skrifa = "0.36"
|
||||||
pretty_assertions = "1.4"
|
pretty_assertions = "1.4"
|
||||||
fern = { version = "0.7", features = ["colored"] }
|
fern = { version = "0.7", features = ["colored"] }
|
||||||
num_enum = "0.7"
|
num_enum = { version = "0.7", default-features = false }
|
||||||
num-derive = "0.4"
|
num-derive = "0.4"
|
||||||
num-traits = { version = "0.2", default-features = false, features = ["libm"] }
|
num-traits = { version = "0.2", default-features = false, features = ["libm"] }
|
||||||
specta = { version = "2.0.0-rc.22", features = [
|
specta = { version = "2.0.0-rc.22", features = [
|
||||||
|
|
@ -193,7 +193,7 @@ open = "5.3"
|
||||||
poly-cool = "0.3"
|
poly-cool = "0.3"
|
||||||
spin = "0.10"
|
spin = "0.10"
|
||||||
clap = "4.5"
|
clap = "4.5"
|
||||||
spirv-std = { git = "https://github.com/rust-gpu/rust-gpu", rev = "c12f216121820580731440ee79ebc7403d6ea04f" }
|
spirv-std = { git = "https://github.com/rust-gpu/rust-gpu", rev = "c12f216121820580731440ee79ebc7403d6ea04f", features = ["bytemuck"] }
|
||||||
cargo-gpu = { git = "https://github.com/rust-gpu/cargo-gpu", rev = "f969528e87baa17a7d48eecf4a6fcfdcaaf30566" }
|
cargo-gpu = { git = "https://github.com/rust-gpu/cargo-gpu", rev = "f969528e87baa17a7d48eecf4a6fcfdcaaf30566" }
|
||||||
|
|
||||||
[workspace.lints.rust]
|
[workspace.lints.rust]
|
||||||
|
|
|
||||||
|
|
@ -22,10 +22,14 @@ std = [
|
||||||
"glam/serde",
|
"glam/serde",
|
||||||
"half/std",
|
"half/std",
|
||||||
"half/serde",
|
"half/serde",
|
||||||
"num-traits/std"
|
"num-traits/std",
|
||||||
|
"num_enum/std",
|
||||||
]
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
# Local dependencies
|
||||||
|
node-macro = { workspace = true }
|
||||||
|
|
||||||
# Local std dependencies
|
# Local std dependencies
|
||||||
dyn-any = { workspace = true, optional = true }
|
dyn-any = { workspace = true, optional = true }
|
||||||
|
|
||||||
|
|
@ -35,6 +39,8 @@ glam = { workspace = true }
|
||||||
half = { workspace = true, default-features = false }
|
half = { workspace = true, default-features = false }
|
||||||
num-derive = { workspace = true }
|
num-derive = { workspace = true }
|
||||||
num-traits = { workspace = true }
|
num-traits = { workspace = true }
|
||||||
|
num_enum = { workspace = true }
|
||||||
|
spirv-std = { workspace = true }
|
||||||
|
|
||||||
# Workspace std dependencies
|
# Workspace std dependencies
|
||||||
serde = { workspace = true, optional = true }
|
serde = { workspace = true, optional = true }
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
use core::fmt::Display;
|
use core::fmt::Display;
|
||||||
use core::hash::{Hash, Hasher};
|
use core::hash::{Hash, Hasher};
|
||||||
|
use node_macro::BufferStruct;
|
||||||
|
use num_enum::{FromPrimitive, IntoPrimitive};
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
use num_traits::float::Float;
|
use num_traits::float::Float;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq, BufferStruct)]
|
||||||
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
|
||||||
#[cfg_attr(feature = "std", serde(default))]
|
#[cfg_attr(feature = "std", serde(default))]
|
||||||
pub struct AlphaBlending {
|
pub struct AlphaBlending {
|
||||||
|
|
@ -66,7 +68,7 @@ impl AlphaBlending {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(i32)]
|
#[repr(i32)]
|
||||||
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, bytemuck::NoUninit)]
|
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, BufferStruct, FromPrimitive, IntoPrimitive)]
|
||||||
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
|
||||||
pub enum BlendMode {
|
pub enum BlendMode {
|
||||||
// Basic group
|
// Basic group
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ use core::fmt::Debug;
|
||||||
use core::hash::Hash;
|
use core::hash::Hash;
|
||||||
use glam::Vec4;
|
use glam::Vec4;
|
||||||
use half::f16;
|
use half::f16;
|
||||||
|
use node_macro::BufferStruct;
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
use num_traits::Euclid;
|
use num_traits::Euclid;
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
|
|
@ -215,7 +216,7 @@ impl Pixel for Luma {}
|
||||||
/// The other components (RGB) are stored as `f32` that range from `0.0` up to `f32::MAX`,
|
/// The other components (RGB) are stored as `f32` that range from `0.0` up to `f32::MAX`,
|
||||||
/// the values encode the brightness of each channel proportional to the light intensity in cd/m² (nits) in HDR, and `0.0` (black) to `1.0` (white) in SDR color.
|
/// the values encode the brightness of each channel proportional to the light intensity in cd/m² (nits) in HDR, and `0.0` (black) to `1.0` (white) in SDR color.
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Pod, Zeroable)]
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Pod, Zeroable, BufferStruct)]
|
||||||
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
|
||||||
pub struct Color {
|
pub struct Color {
|
||||||
red: f32,
|
red: f32,
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ pub mod choice_type;
|
||||||
pub mod color;
|
pub mod color;
|
||||||
pub mod context;
|
pub mod context;
|
||||||
pub mod registry;
|
pub mod registry;
|
||||||
|
pub mod shaders;
|
||||||
|
|
||||||
pub use context::Ctx;
|
pub use context::Ctx;
|
||||||
pub use glam;
|
pub use glam;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,114 @@
|
||||||
|
use crate::shaders::buffer_struct::BufferStruct;
|
||||||
|
|
||||||
|
macro_rules! glam_array {
|
||||||
|
($t:ty, $a:ty) => {
|
||||||
|
unsafe impl BufferStruct for $t {
|
||||||
|
type Buffer = $a;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn write(from: Self) -> Self::Buffer {
|
||||||
|
<$t>::to_array(&from)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn read(from: Self::Buffer) -> Self {
|
||||||
|
<$t>::from_array(from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! glam_cols_array {
|
||||||
|
($t:ty, $a:ty) => {
|
||||||
|
unsafe impl BufferStruct for $t {
|
||||||
|
type Buffer = $a;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn write(from: Self) -> Self::Buffer {
|
||||||
|
<$t>::to_cols_array(&from)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn read(from: Self::Buffer) -> Self {
|
||||||
|
<$t>::from_cols_array(&from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
glam_array!(glam::Vec2, [f32; 2]);
|
||||||
|
glam_array!(glam::Vec3, [f32; 3]);
|
||||||
|
// glam_array!(Vec3A, [f32; 4]);
|
||||||
|
glam_array!(glam::Vec4, [f32; 4]);
|
||||||
|
glam_array!(glam::Quat, [f32; 4]);
|
||||||
|
glam_cols_array!(glam::Mat2, [f32; 4]);
|
||||||
|
glam_cols_array!(glam::Mat3, [f32; 9]);
|
||||||
|
// glam_cols_array!(Mat3A, [f32; 4]);
|
||||||
|
glam_cols_array!(glam::Mat4, [f32; 16]);
|
||||||
|
glam_cols_array!(glam::Affine2, [f32; 6]);
|
||||||
|
glam_cols_array!(glam::Affine3A, [f32; 12]);
|
||||||
|
|
||||||
|
glam_array!(glam::DVec2, [f64; 2]);
|
||||||
|
glam_array!(glam::DVec3, [f64; 3]);
|
||||||
|
glam_array!(glam::DVec4, [f64; 4]);
|
||||||
|
glam_array!(glam::DQuat, [f64; 4]);
|
||||||
|
glam_cols_array!(glam::DMat2, [f64; 4]);
|
||||||
|
glam_cols_array!(glam::DMat3, [f64; 9]);
|
||||||
|
glam_cols_array!(glam::DMat4, [f64; 16]);
|
||||||
|
glam_cols_array!(glam::DAffine2, [f64; 6]);
|
||||||
|
glam_cols_array!(glam::DAffine3, [f64; 12]);
|
||||||
|
|
||||||
|
glam_array!(glam::I16Vec2, [i16; 2]);
|
||||||
|
glam_array!(glam::I16Vec3, [i16; 3]);
|
||||||
|
glam_array!(glam::I16Vec4, [i16; 4]);
|
||||||
|
|
||||||
|
glam_array!(glam::U16Vec2, [u16; 2]);
|
||||||
|
glam_array!(glam::U16Vec3, [u16; 3]);
|
||||||
|
glam_array!(glam::U16Vec4, [u16; 4]);
|
||||||
|
|
||||||
|
glam_array!(glam::IVec2, [i32; 2]);
|
||||||
|
glam_array!(glam::IVec3, [i32; 3]);
|
||||||
|
glam_array!(glam::IVec4, [i32; 4]);
|
||||||
|
|
||||||
|
glam_array!(glam::UVec2, [u32; 2]);
|
||||||
|
glam_array!(glam::UVec3, [u32; 3]);
|
||||||
|
glam_array!(glam::UVec4, [u32; 4]);
|
||||||
|
|
||||||
|
glam_array!(glam::I64Vec2, [i64; 2]);
|
||||||
|
glam_array!(glam::I64Vec3, [i64; 3]);
|
||||||
|
glam_array!(glam::I64Vec4, [i64; 4]);
|
||||||
|
|
||||||
|
glam_array!(glam::U64Vec2, [u64; 2]);
|
||||||
|
glam_array!(glam::U64Vec3, [u64; 3]);
|
||||||
|
glam_array!(glam::U64Vec4, [u64; 4]);
|
||||||
|
|
||||||
|
unsafe impl BufferStruct for glam::Vec3A {
|
||||||
|
type Buffer = [f32; 4];
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn write(from: Self) -> Self::Buffer {
|
||||||
|
glam::Vec4::to_array(&from.extend(0.))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn read(from: Self::Buffer) -> Self {
|
||||||
|
glam::Vec3A::from_vec4(glam::Vec4::from_array(from))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// do NOT use slices, otherwise spirv will fail to compile
|
||||||
|
unsafe impl BufferStruct for glam::Mat3A {
|
||||||
|
type Buffer = [f32; 12];
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn write(from: Self) -> Self::Buffer {
|
||||||
|
let a = from.to_cols_array();
|
||||||
|
[a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], 0., 0., 0.]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn read(from: Self::Buffer) -> Self {
|
||||||
|
let a = from;
|
||||||
|
glam::Mat3A::from_cols_array(&[a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
//! I (@firestar99) copied this entire mod from one of my projects, as I haven't uploaded that lib to crates. Hopefully
|
||||||
|
//! rust-gpu improves and this entire thing becomes unnecessary in the future.
|
||||||
|
//!
|
||||||
|
//! https://github.com/Firestar99/nanite-at-home/tree/008dac8df656959c71efeddd2d3ddabcb801771c/rust-gpu-bindless/crates/buffer-content
|
||||||
|
|
||||||
|
use bytemuck::Pod;
|
||||||
|
|
||||||
|
mod glam;
|
||||||
|
mod primitive;
|
||||||
|
|
||||||
|
/// A BufferStruct is a "parallel representation" of the original struct with some fundamental types remapped. This
|
||||||
|
/// struct hierarchy represents how data is stored in GPU Buffers, where all types must be [`Pod`] to allow
|
||||||
|
/// transmuting them to `&[u8]` with [`bytemuck`].
|
||||||
|
///
|
||||||
|
/// Notable type remappings (original: buffer):
|
||||||
|
/// * bool: u32 of 0 or 1
|
||||||
|
/// * any repr(u32) enum: u32 with remapping via [`num_enum`]
|
||||||
|
///
|
||||||
|
/// By adding `#[derive(ShaderStruct)]` to your struct (or enum), a parallel `{name}Buffer` struct is created with all
|
||||||
|
/// the members of the original struct, but with their types using the associated remapped types as specified by this
|
||||||
|
/// trait.
|
||||||
|
///
|
||||||
|
/// # Origin
|
||||||
|
/// I (@firestar99) copied this entire mod from my [Nanite-at-home] project, specifically the [buffer-content] crate
|
||||||
|
/// and the [buffer_struct] proc macro. The variant here has quite some modifications, to both cleaned up some of the
|
||||||
|
/// mistakes my implementation has and to customize it a bit for graphite.
|
||||||
|
///
|
||||||
|
/// Hopefully rust-gpu improves to the point where this remapping becomes unnecessary.
|
||||||
|
///
|
||||||
|
/// [Nanite-at-home]: https://github.com/Firestar99/nanite-at-home
|
||||||
|
/// [buffer-content]: https://github.com/Firestar99/nanite-at-home/tree/008dac8df656959c71efeddd2d3ddabcb801771c/rust-gpu-bindless/crates/buffer-content
|
||||||
|
/// [buffer_struct]: https://github.com/Firestar99/nanite-at-home/blob/008dac8df656959c71efeddd2d3ddabcb801771c/rust-gpu-bindless/crates/macros/src/buffer_struct.rs
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// The associated type Transfer must be the same on all targets. Writing followed by reading back a value must result
|
||||||
|
/// in the same value.
|
||||||
|
pub unsafe trait BufferStruct: Copy + Send + Sync + 'static {
|
||||||
|
type Buffer: Pod + Send + Sync;
|
||||||
|
|
||||||
|
fn write(from: Self) -> Self::Buffer;
|
||||||
|
|
||||||
|
fn read(from: Self::Buffer) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trait marking all [`BufferStruct`] whose read and write methods are identity. While [`BufferStruct`] only
|
||||||
|
/// requires `t == read(write(t))`, this trait additionally requires `t == read(t) == write(t)`. As this removes the
|
||||||
|
/// conversion requirement for writing to or reading from a buffer, one can acquire slices from buffers created of these
|
||||||
|
/// types.
|
||||||
|
///
|
||||||
|
/// Implementing this type is completely safe due to the [`Pod`] requirement.
|
||||||
|
pub trait BufferStructIdentity: Pod + Send + Sync {}
|
||||||
|
|
||||||
|
unsafe impl<T: BufferStructIdentity> BufferStruct for T {
|
||||||
|
type Buffer = Self;
|
||||||
|
|
||||||
|
fn write(from: Self) -> Self::Buffer {
|
||||||
|
from
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(from: Self::Buffer) -> Self {
|
||||||
|
from
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,135 @@
|
||||||
|
use crate::shaders::buffer_struct::{BufferStruct, BufferStructIdentity};
|
||||||
|
use bytemuck::Pod;
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
use core::num::Wrapping;
|
||||||
|
use spirv_std::arch::IndexUnchecked;
|
||||||
|
|
||||||
|
macro_rules! identity {
|
||||||
|
($t:ty) => {
|
||||||
|
impl BufferStructIdentity for $t {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
identity!(());
|
||||||
|
identity!(u8);
|
||||||
|
identity!(u16);
|
||||||
|
identity!(u32);
|
||||||
|
identity!(u64);
|
||||||
|
identity!(u128);
|
||||||
|
identity!(usize);
|
||||||
|
identity!(i8);
|
||||||
|
identity!(i16);
|
||||||
|
identity!(i32);
|
||||||
|
identity!(i64);
|
||||||
|
identity!(i128);
|
||||||
|
identity!(isize);
|
||||||
|
identity!(f32);
|
||||||
|
identity!(f64);
|
||||||
|
|
||||||
|
identity!(spirv_std::arch::SubgroupMask);
|
||||||
|
identity!(spirv_std::memory::Semantics);
|
||||||
|
identity!(spirv_std::ray_tracing::RayFlags);
|
||||||
|
identity!(spirv_std::indirect_command::DrawIndirectCommand);
|
||||||
|
identity!(spirv_std::indirect_command::DrawIndexedIndirectCommand);
|
||||||
|
identity!(spirv_std::indirect_command::DispatchIndirectCommand);
|
||||||
|
identity!(spirv_std::indirect_command::DrawMeshTasksIndirectCommandEXT);
|
||||||
|
identity!(spirv_std::indirect_command::TraceRaysIndirectCommandKHR);
|
||||||
|
// not pod
|
||||||
|
// identity!(spirv_std::indirect_command::TraceRaysIndirectCommand2KHR);
|
||||||
|
|
||||||
|
unsafe impl BufferStruct for bool {
|
||||||
|
type Buffer = u32;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn write(from: Self) -> Self::Buffer {
|
||||||
|
from as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn read(from: Self::Buffer) -> Self {
|
||||||
|
from != 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<T: BufferStruct> BufferStruct for Wrapping<T>
|
||||||
|
where
|
||||||
|
// unfortunately has to be Pod, even though AnyBitPattern would be sufficient,
|
||||||
|
// due to bytemuck doing `impl<T: Pod> AnyBitPattern for T {}`
|
||||||
|
// see https://github.com/Lokathor/bytemuck/issues/164
|
||||||
|
T::Buffer: Pod,
|
||||||
|
{
|
||||||
|
type Buffer = Wrapping<T::Buffer>;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn write(from: Self) -> Self::Buffer {
|
||||||
|
Wrapping(T::write(from.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn read(from: Self::Buffer) -> Self {
|
||||||
|
Wrapping(T::read(from.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<T: BufferStruct + 'static> BufferStruct for PhantomData<T> {
|
||||||
|
type Buffer = PhantomData<T>;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn write(_: Self) -> Self::Buffer {
|
||||||
|
PhantomData {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn read(_: Self::Buffer) -> Self {
|
||||||
|
PhantomData {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Potential problem: you can't impl this for an array of BufferStruct, as it'll conflict with this impl due to the
|
||||||
|
/// blanket impl on all BufferStructPlain types.
|
||||||
|
unsafe impl<T: BufferStruct, const N: usize> BufferStruct for [T; N]
|
||||||
|
where
|
||||||
|
// rust-gpu does not like `[T; N].map()` nor `core::array::from_fn()` nor transmuting arrays with a const generic
|
||||||
|
// length, so for now we need to require T: Default and T::Transfer: Default for all arrays.
|
||||||
|
T: Default,
|
||||||
|
// unfortunately has to be Pod, even though AnyBitPattern would be sufficient,
|
||||||
|
// due to bytemuck doing `impl<T: Pod> AnyBitPattern for T {}`
|
||||||
|
// see https://github.com/Lokathor/bytemuck/issues/164
|
||||||
|
T::Buffer: Pod + Default,
|
||||||
|
{
|
||||||
|
type Buffer = [T::Buffer; N];
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn write(from: Self) -> Self::Buffer {
|
||||||
|
unsafe {
|
||||||
|
let mut ret = [T::Buffer::default(); N];
|
||||||
|
for i in 0..N {
|
||||||
|
*ret.index_unchecked_mut(i) = T::write(*from.index_unchecked(i));
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn read(from: Self::Buffer) -> Self {
|
||||||
|
unsafe {
|
||||||
|
let mut ret = [T::default(); N];
|
||||||
|
for i in 0..N {
|
||||||
|
*ret.index_unchecked_mut(i) = T::read(*from.index_unchecked(i));
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn roundtrip_bool() {
|
||||||
|
for x in [false, true] {
|
||||||
|
assert_eq!(x, <bool as BufferStruct>::read(<bool as BufferStruct>::write(x)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
//! supporting infrastructure for shaders
|
||||||
|
|
||||||
|
pub mod buffer_struct;
|
||||||
|
|
||||||
|
pub mod __private {
|
||||||
|
pub use bytemuck;
|
||||||
|
pub use glam;
|
||||||
|
pub use num_enum;
|
||||||
|
pub use spirv_std;
|
||||||
|
}
|
||||||
|
|
@ -42,6 +42,7 @@ pub use graphene_core_shaders::AsU32;
|
||||||
pub use graphene_core_shaders::blending;
|
pub use graphene_core_shaders::blending;
|
||||||
pub use graphene_core_shaders::choice_type;
|
pub use graphene_core_shaders::choice_type;
|
||||||
pub use graphene_core_shaders::color;
|
pub use graphene_core_shaders::color;
|
||||||
|
pub use graphene_core_shaders::shaders;
|
||||||
pub use graphic::Graphic;
|
pub use graphic::Graphic;
|
||||||
pub use memo::MemoHash;
|
pub use memo::MemoHash;
|
||||||
pub use num_traits;
|
pub use num_traits;
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ bytemuck = { workspace = true }
|
||||||
glam = { workspace = true }
|
glam = { workspace = true }
|
||||||
spirv-std = { workspace = true }
|
spirv-std = { workspace = true }
|
||||||
num-traits = { workspace = true }
|
num-traits = { workspace = true }
|
||||||
|
num_enum = { workspace = true }
|
||||||
|
|
||||||
# Workspace std dependencies
|
# Workspace std dependencies
|
||||||
specta = { workspace = true, optional = true }
|
specta = { workspace = true, optional = true }
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
use crate::adjust::Adjust;
|
use crate::adjust::Adjust;
|
||||||
use crate::cubic_spline::CubicSplines;
|
use crate::cubic_spline::CubicSplines;
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
|
use glam::{Vec3, Vec4};
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use graphene_core::gradient::GradientStops;
|
use graphene_core::gradient::GradientStops;
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
|
|
@ -12,6 +13,8 @@ use graphene_core::table::Table;
|
||||||
use graphene_core_shaders::color::Color;
|
use graphene_core_shaders::color::Color;
|
||||||
use graphene_core_shaders::context::Ctx;
|
use graphene_core_shaders::context::Ctx;
|
||||||
use graphene_core_shaders::registry::types::{AngleF32, PercentageF32, SignedPercentageF32};
|
use graphene_core_shaders::registry::types::{AngleF32, PercentageF32, SignedPercentageF32};
|
||||||
|
use node_macro::BufferStruct;
|
||||||
|
use num_enum::{FromPrimitive, IntoPrimitive};
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
use num_traits::float::Float;
|
use num_traits::float::Float;
|
||||||
|
|
||||||
|
|
@ -30,7 +33,7 @@ use num_traits::float::Float;
|
||||||
// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#:~:text=%27clrL%27%20%3D%20Color%20Lookup
|
// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#:~:text=%27clrL%27%20%3D%20Color%20Lookup
|
||||||
// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#:~:text=Color%20Lookup%20(Photoshop%20CS6
|
// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#:~:text=Color%20Lookup%20(Photoshop%20CS6
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, node_macro::ChoiceType, bytemuck::NoUninit)]
|
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, node_macro::ChoiceType, bytemuck::NoUninit, BufferStruct, FromPrimitive, IntoPrimitive)]
|
||||||
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
|
||||||
#[widget(Dropdown)]
|
#[widget(Dropdown)]
|
||||||
#[repr(u32)]
|
#[repr(u32)]
|
||||||
|
|
@ -70,7 +73,7 @@ fn luminance<T: Adjust<Color>>(
|
||||||
input
|
input
|
||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node(category("Raster"), cfg(feature = "std"))]
|
#[node_macro::node(category("Raster"), shader_node(PerPixelAdjust))]
|
||||||
fn gamma_correction<T: Adjust<Color>>(
|
fn gamma_correction<T: Adjust<Color>>(
|
||||||
_: impl Ctx,
|
_: impl Ctx,
|
||||||
#[implementations(
|
#[implementations(
|
||||||
|
|
@ -138,6 +141,38 @@ fn make_opaque<T: Adjust<Color>>(
|
||||||
input
|
input
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// See [`brightness_contrast`]
|
||||||
|
#[node_macro::node(
|
||||||
|
name("Brightness/Contrast classic"),
|
||||||
|
category("Raster: Adjustment"),
|
||||||
|
properties("brightness_contrast_properties"),
|
||||||
|
shader_node(PerPixelAdjust)
|
||||||
|
)]
|
||||||
|
fn brightness_contrast_classic<T: Adjust<Color>>(
|
||||||
|
_: impl Ctx,
|
||||||
|
#[implementations(
|
||||||
|
Table<Raster<CPU>>,
|
||||||
|
Table<Color>,
|
||||||
|
Table<GradientStops>,
|
||||||
|
GradientStops,
|
||||||
|
)]
|
||||||
|
#[gpu_image]
|
||||||
|
mut input: T,
|
||||||
|
brightness: SignedPercentageF32,
|
||||||
|
contrast: SignedPercentageF32,
|
||||||
|
) -> T {
|
||||||
|
let brightness = brightness / 255.;
|
||||||
|
|
||||||
|
let contrast = contrast / 100.;
|
||||||
|
let contrast = if contrast > 0. { (contrast * core::f32::consts::FRAC_PI_2 - 0.01).tan() } else { contrast };
|
||||||
|
|
||||||
|
let offset = brightness * contrast + brightness - contrast / 2.;
|
||||||
|
|
||||||
|
input.adjust(|color| color.to_gamma_srgb().map_rgb(|c| (c + c * contrast + offset).clamp(0., 1.)).to_linear_srgb());
|
||||||
|
|
||||||
|
input
|
||||||
|
}
|
||||||
|
|
||||||
// Aims for interoperable compatibility with:
|
// Aims for interoperable compatibility with:
|
||||||
// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#:~:text=%27brit%27%20%3D%20Brightness/Contrast
|
// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#:~:text=%27brit%27%20%3D%20Brightness/Contrast
|
||||||
// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#:~:text=Padding-,Brightness%20and%20Contrast,-Key%20is%20%27brit
|
// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#:~:text=Padding-,Brightness%20and%20Contrast,-Key%20is%20%27brit
|
||||||
|
|
@ -146,7 +181,7 @@ fn make_opaque<T: Adjust<Color>>(
|
||||||
// https://geraldbakker.nl/psnumbers/brightness-contrast.html
|
// https://geraldbakker.nl/psnumbers/brightness-contrast.html
|
||||||
#[node_macro::node(name("Brightness/Contrast"), category("Raster: Adjustment"), properties("brightness_contrast_properties"), cfg(feature = "std"))]
|
#[node_macro::node(name("Brightness/Contrast"), category("Raster: Adjustment"), properties("brightness_contrast_properties"), cfg(feature = "std"))]
|
||||||
fn brightness_contrast<T: Adjust<Color>>(
|
fn brightness_contrast<T: Adjust<Color>>(
|
||||||
_: impl Ctx,
|
_ctx: impl Ctx,
|
||||||
#[implementations(
|
#[implementations(
|
||||||
Table<Raster<CPU>>,
|
Table<Raster<CPU>>,
|
||||||
Table<Color>,
|
Table<Color>,
|
||||||
|
|
@ -160,16 +195,7 @@ fn brightness_contrast<T: Adjust<Color>>(
|
||||||
use_classic: bool,
|
use_classic: bool,
|
||||||
) -> T {
|
) -> T {
|
||||||
if use_classic {
|
if use_classic {
|
||||||
let brightness = brightness / 255.;
|
return brightness_contrast_classic(_ctx, input, brightness, contrast);
|
||||||
|
|
||||||
let contrast = contrast / 100.;
|
|
||||||
let contrast = if contrast > 0. { (contrast * core::f32::consts::FRAC_PI_2 - 0.01).tan() } else { contrast };
|
|
||||||
|
|
||||||
let offset = brightness * contrast + brightness - contrast / 2.;
|
|
||||||
|
|
||||||
input.adjust(|color| color.to_gamma_srgb().map_rgb(|c| (c + c * contrast + offset).clamp(0., 1.)).to_linear_srgb());
|
|
||||||
|
|
||||||
return input;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const WINDOW_SIZE: usize = 1024;
|
const WINDOW_SIZE: usize = 1024;
|
||||||
|
|
@ -549,7 +575,8 @@ fn vibrance<T: Adjust<Color>>(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Color Channel
|
/// Color Channel
|
||||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, node_macro::ChoiceType)]
|
#[repr(u32)]
|
||||||
|
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, node_macro::ChoiceType, BufferStruct, FromPrimitive, IntoPrimitive)]
|
||||||
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
|
||||||
#[widget(Radio)]
|
#[widget(Radio)]
|
||||||
pub enum RedGreenBlue {
|
pub enum RedGreenBlue {
|
||||||
|
|
@ -560,7 +587,7 @@ pub enum RedGreenBlue {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Color Channel
|
/// Color Channel
|
||||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, node_macro::ChoiceType, bytemuck::NoUninit)]
|
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, node_macro::ChoiceType, bytemuck::NoUninit, BufferStruct, FromPrimitive, IntoPrimitive)]
|
||||||
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
|
||||||
#[widget(Radio)]
|
#[widget(Radio)]
|
||||||
#[repr(u32)]
|
#[repr(u32)]
|
||||||
|
|
@ -653,7 +680,7 @@ pub enum DomainWarpType {
|
||||||
// Aims for interoperable compatibility with:
|
// Aims for interoperable compatibility with:
|
||||||
// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#:~:text=%27mixr%27%20%3D%20Channel%20Mixer
|
// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#:~:text=%27mixr%27%20%3D%20Channel%20Mixer
|
||||||
// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#:~:text=Lab%20color%20only-,Channel%20Mixer,-Key%20is%20%27mixr
|
// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#:~:text=Lab%20color%20only-,Channel%20Mixer,-Key%20is%20%27mixr
|
||||||
#[node_macro::node(category("Raster: Adjustment"), properties("channel_mixer_properties"), cfg(feature = "std"))]
|
#[node_macro::node(category("Raster: Adjustment"), properties("channel_mixer_properties"), shader_node(PerPixelAdjust))]
|
||||||
fn channel_mixer<T: Adjust<Color>>(
|
fn channel_mixer<T: Adjust<Color>>(
|
||||||
_: impl Ctx,
|
_: impl Ctx,
|
||||||
#[implementations(
|
#[implementations(
|
||||||
|
|
@ -750,7 +777,8 @@ fn channel_mixer<T: Adjust<Color>>(
|
||||||
image
|
image
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, node_macro::ChoiceType)]
|
#[repr(u32)]
|
||||||
|
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, node_macro::ChoiceType, BufferStruct, FromPrimitive, IntoPrimitive)]
|
||||||
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
|
||||||
#[widget(Radio)]
|
#[widget(Radio)]
|
||||||
pub enum RelativeAbsolute {
|
pub enum RelativeAbsolute {
|
||||||
|
|
@ -759,8 +787,8 @@ pub enum RelativeAbsolute {
|
||||||
Absolute,
|
Absolute,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(u32)]
|
||||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, node_macro::ChoiceType)]
|
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, node_macro::ChoiceType, BufferStruct, FromPrimitive, IntoPrimitive)]
|
||||||
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
|
||||||
pub enum SelectiveColorChoice {
|
pub enum SelectiveColorChoice {
|
||||||
#[default]
|
#[default]
|
||||||
|
|
@ -783,7 +811,7 @@ pub enum SelectiveColorChoice {
|
||||||
//
|
//
|
||||||
// Algorithm based on:
|
// Algorithm based on:
|
||||||
// https://blog.pkh.me/p/22-understanding-selective-coloring-in-adobe-photoshop.html
|
// https://blog.pkh.me/p/22-understanding-selective-coloring-in-adobe-photoshop.html
|
||||||
#[node_macro::node(category("Raster: Adjustment"), properties("selective_color_properties"), cfg(feature = "std"))]
|
#[node_macro::node(category("Raster: Adjustment"), properties("selective_color_properties"), shader_node(PerPixelAdjust))]
|
||||||
fn selective_color<T: Adjust<Color>>(
|
fn selective_color<T: Adjust<Color>>(
|
||||||
_: impl Ctx,
|
_: impl Ctx,
|
||||||
#[implementations(
|
#[implementations(
|
||||||
|
|
@ -877,7 +905,7 @@ fn selective_color<T: Adjust<Color>>(
|
||||||
RelativeAbsolute::Absolute => (-1., -1., -1.),
|
RelativeAbsolute::Absolute => (-1., -1., -1.),
|
||||||
};
|
};
|
||||||
|
|
||||||
let (sum_r, sum_g, sum_b) = [
|
let array = [
|
||||||
(SelectiveColorChoice::Reds, (r_c, r_m, r_y, r_k)),
|
(SelectiveColorChoice::Reds, (r_c, r_m, r_y, r_k)),
|
||||||
(SelectiveColorChoice::Yellows, (y_c, y_m, y_y, y_k)),
|
(SelectiveColorChoice::Yellows, (y_c, y_m, y_y, y_k)),
|
||||||
(SelectiveColorChoice::Greens, (g_c, g_m, g_y, g_k)),
|
(SelectiveColorChoice::Greens, (g_c, g_m, g_y, g_k)),
|
||||||
|
|
@ -887,14 +915,16 @@ fn selective_color<T: Adjust<Color>>(
|
||||||
(SelectiveColorChoice::Whites, (w_c, w_m, w_y, w_k)),
|
(SelectiveColorChoice::Whites, (w_c, w_m, w_y, w_k)),
|
||||||
(SelectiveColorChoice::Neutrals, (n_c, n_m, n_y, n_k)),
|
(SelectiveColorChoice::Neutrals, (n_c, n_m, n_y, n_k)),
|
||||||
(SelectiveColorChoice::Blacks, (k_c, k_m, k_y, k_k)),
|
(SelectiveColorChoice::Blacks, (k_c, k_m, k_y, k_k)),
|
||||||
]
|
];
|
||||||
.into_iter()
|
let mut sum = Vec3::ZERO;
|
||||||
.fold((0., 0., 0.), |acc, (color_parameter_group, (c, m, y, k))| {
|
for i in 0..array.len() {
|
||||||
|
let (color_parameter_group, (c, m, y, k)) = array[i];
|
||||||
|
|
||||||
// Skip this color parameter group...
|
// Skip this color parameter group...
|
||||||
// ...if it's unchanged from the default of zero offset on all CMYK parameters, or...
|
// ...if it's unchanged from the default of zero offset on all CMYK parameters, or...
|
||||||
// ...if this pixel's color isn't in the range affected by this color parameter group
|
// ...if this pixel's color isn't in the range affected by this color parameter group
|
||||||
if (c < f32::EPSILON && m < f32::EPSILON && y < f32::EPSILON && k < f32::EPSILON) || (!pixel_color_range(color_parameter_group)) {
|
if (c < f32::EPSILON && m < f32::EPSILON && y < f32::EPSILON && k < f32::EPSILON) || (!pixel_color_range(color_parameter_group)) {
|
||||||
return acc;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (c, m, y, k) = (c / 100., m / 100., y / 100., k / 100.);
|
let (c, m, y, k) = (c / 100., m / 100., y / 100., k / 100.);
|
||||||
|
|
@ -907,14 +937,15 @@ fn selective_color<T: Adjust<Color>>(
|
||||||
SelectiveColorChoice::Blacks => 1. - max(r, g, b) * 2.,
|
SelectiveColorChoice::Blacks => 1. - max(r, g, b) * 2.,
|
||||||
};
|
};
|
||||||
|
|
||||||
let offset_r = ((c + k * (c + 1.)) * slope_r).clamp(-r, -r + 1.) * color_parameter_group_scale_factor;
|
let offset_r = f32::clamp((c + k * (c + 1.)) * slope_r, -r, -r + 1.) * color_parameter_group_scale_factor;
|
||||||
let offset_g = ((m + k * (m + 1.)) * slope_g).clamp(-g, -g + 1.) * color_parameter_group_scale_factor;
|
let offset_g = f32::clamp((m + k * (m + 1.)) * slope_g, -g, -g + 1.) * color_parameter_group_scale_factor;
|
||||||
let offset_b = ((y + k * (y + 1.)) * slope_b).clamp(-b, -b + 1.) * color_parameter_group_scale_factor;
|
let offset_b = f32::clamp((y + k * (y + 1.)) * slope_b, -b, -b + 1.) * color_parameter_group_scale_factor;
|
||||||
|
|
||||||
(acc.0 + offset_r, acc.1 + offset_g, acc.2 + offset_b)
|
sum += Vec3::new(offset_r, offset_g, offset_b);
|
||||||
});
|
}
|
||||||
|
|
||||||
let color = Color::from_rgbaf32_unchecked((r + sum_r).clamp(0., 1.), (g + sum_g).clamp(0., 1.), (b + sum_b).clamp(0., 1.), a);
|
let rgb = Vec3::new(r, g, b);
|
||||||
|
let color = Color::from_vec4(Vec4::from(((sum + rgb).clamp(Vec3::ZERO, Vec3::ONE), a)));
|
||||||
|
|
||||||
color.to_linear_srgb()
|
color.to_linear_srgb()
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -27,3 +27,4 @@ proc-macro-error2 = "2"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
graphene-core = { workspace = true }
|
graphene-core = { workspace = true }
|
||||||
|
graphene-core-shaders = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -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 crate::parsing::*;
|
||||||
use convert_case::{Case, Casing};
|
use convert_case::{Case, Casing};
|
||||||
use proc_macro_crate::FoundCrate;
|
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
use proc_macro2::TokenStream as TokenStream2;
|
||||||
use quote::{ToTokens, format_ident, quote, quote_spanned};
|
use quote::{ToTokens, format_ident, quote, quote_spanned};
|
||||||
use std::sync::atomic::AtomicU64;
|
use std::sync::atomic::AtomicU64;
|
||||||
|
|
@ -10,7 +9,7 @@ use syn::token::Comma;
|
||||||
use syn::{Error, Ident, PatIdent, Token, WhereClause, WherePredicate, parse_quote};
|
use syn::{Error, Ident, PatIdent, Token, WhereClause, WherePredicate, parse_quote};
|
||||||
static NODE_ID: AtomicU64 = AtomicU64::new(0);
|
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 {
|
let ParsedNodeFn {
|
||||||
vis,
|
vis,
|
||||||
attributes,
|
attributes,
|
||||||
|
|
@ -24,10 +23,10 @@ pub(crate) fn generate_node_code(parsed: &ParsedNodeFn) -> syn::Result<TokenStre
|
||||||
is_async,
|
is_async,
|
||||||
fields,
|
fields,
|
||||||
body,
|
body,
|
||||||
crate_name: graphene_core_crate,
|
|
||||||
description,
|
description,
|
||||||
..
|
..
|
||||||
} = parsed;
|
} = parsed;
|
||||||
|
let graphene_core = crate_ident.gcore()?;
|
||||||
|
|
||||||
let category = &attributes.category.as_ref().map(|value| quote!(Some(#value))).unwrap_or(quote!(None));
|
let category = &attributes.category.as_ref().map(|value| quote!(Some(#value))).unwrap_or(quote!(None));
|
||||||
let mod_name = format_ident!("_{}_mod", mod_name);
|
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 }
|
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 mut future_idents = Vec::new();
|
||||||
|
|
||||||
let field_types: Vec<_> = fields
|
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 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 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! {
|
Ok(quote! {
|
||||||
/// Underlying implementation for [#struct_name]
|
/// 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 crate::shader_nodes::{ShaderCodegen, ShaderTokens};
|
||||||
use syn::visit_mut::VisitMut;
|
use syn::visit_mut::VisitMut;
|
||||||
use syn::{GenericArgument, Lifetime, Type};
|
use syn::{GenericArgument, Lifetime, Type};
|
||||||
|
|
|
||||||
|
|
@ -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::TokenStream;
|
||||||
use proc_macro_error2::proc_macro_error;
|
use proc_macro_error2::proc_macro_error;
|
||||||
use syn::GenericParam;
|
use syn::GenericParam;
|
||||||
|
|
||||||
|
mod buffer_struct;
|
||||||
mod codegen;
|
mod codegen;
|
||||||
|
mod crate_ident;
|
||||||
mod derive_choice_type;
|
mod derive_choice_type;
|
||||||
mod parsing;
|
mod parsing;
|
||||||
mod shader_nodes;
|
mod shader_nodes;
|
||||||
|
|
@ -13,7 +16,7 @@ mod validation;
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn node(attr: TokenStream, item: TokenStream) -> TokenStream {
|
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
|
// 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.
|
/// 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.
|
/// Doc comments on a variant become tooltip text.
|
||||||
#[proc_macro_derive(ChoiceType, attributes(widget, menu_separator, label, icon))]
|
#[proc_macro_derive(ChoiceType, attributes(widget, menu_separator, label, icon))]
|
||||||
pub fn derive_choice_type(input_item: TokenStream) -> TokenStream {
|
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::codegen::generate_node_code;
|
||||||
|
use crate::crate_ident::CrateIdent;
|
||||||
use crate::shader_nodes::ShaderNodeType;
|
use crate::shader_nodes::ShaderNodeType;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|
@ -35,7 +36,6 @@ pub(crate) struct ParsedNodeFn {
|
||||||
pub(crate) is_async: bool,
|
pub(crate) is_async: bool,
|
||||||
pub(crate) fields: Vec<ParsedField>,
|
pub(crate) fields: Vec<ParsedField>,
|
||||||
pub(crate) body: TokenStream2,
|
pub(crate) body: TokenStream2,
|
||||||
pub(crate) crate_name: proc_macro_crate::FoundCrate,
|
|
||||||
pub(crate) description: String,
|
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 output_type = parse_output(&input_fn.sig.output)?;
|
||||||
let where_clause = input_fn.sig.generics.where_clause;
|
let where_clause = input_fn.sig.generics.where_clause;
|
||||||
let body = input_fn.block.to_token_stream();
|
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
|
let description = input_fn
|
||||||
.attrs
|
.attrs
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -350,7 +344,6 @@ fn parse_node_fn(attr: TokenStream2, item: TokenStream2) -> syn::Result<ParsedNo
|
||||||
fields,
|
fields,
|
||||||
where_clause,
|
where_clause,
|
||||||
body,
|
body,
|
||||||
crate_name,
|
|
||||||
description,
|
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
|
// Modify the new_node_fn function to use the code generation
|
||||||
pub fn new_node_fn(attr: TokenStream2, item: TokenStream2) -> TokenStream2 {
|
pub fn new_node_fn(attr: TokenStream2, item: TokenStream2) -> syn::Result<TokenStream2> {
|
||||||
let parse_result = parse_node_fn(attr, item.clone());
|
let crate_ident = CrateIdent::default();
|
||||||
let Ok(mut parsed_node) = parse_result else {
|
let mut parsed_node = parse_node_fn(attr, item.clone()).map_err(|e| Error::new(e.span(), format!("Failed to parse node function: {e}")))?;
|
||||||
let e = parse_result.unwrap_err();
|
|
||||||
return Error::new(e.span(), format!("Failed to parse node function: {e}")).to_compile_error();
|
|
||||||
};
|
|
||||||
|
|
||||||
parsed_node.replace_impl_trait_in_input();
|
parsed_node.replace_impl_trait_in_input();
|
||||||
if let Err(e) = crate::validation::validate_node_fn(&parsed_node) {
|
crate::validation::validate_node_fn(&parsed_node).map_err(|e| Error::new(e.span(), format!("Validation Error: {e}")))?;
|
||||||
return Error::new(e.span(), format!("Validation Error:\n{e}")).to_compile_error();
|
generate_node_code(&crate_ident, &parsed_node).map_err(|e| Error::new(e.span(), format!("Failed to generate node code: {e}")))
|
||||||
}
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ParsedNodeFn {
|
impl ParsedNodeFn {
|
||||||
|
|
@ -728,7 +709,6 @@ impl ParsedNodeFn {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use proc_macro_crate::FoundCrate;
|
|
||||||
use proc_macro2::Span;
|
use proc_macro2::Span;
|
||||||
use quote::{quote, quote_spanned};
|
use quote::{quote, quote_spanned};
|
||||||
use syn::parse_quote;
|
use syn::parse_quote;
|
||||||
|
|
@ -881,7 +861,6 @@ mod tests {
|
||||||
unit: None,
|
unit: None,
|
||||||
}],
|
}],
|
||||||
body: TokenStream2::new(),
|
body: TokenStream2::new(),
|
||||||
crate_name: FoundCrate::Itself,
|
|
||||||
description: String::from("Multi\nLine\n"),
|
description: String::from("Multi\nLine\n"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -964,7 +943,6 @@ mod tests {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
body: TokenStream2::new(),
|
body: TokenStream2::new(),
|
||||||
crate_name: FoundCrate::Itself,
|
|
||||||
description: String::from("Hello\n\t\t\t\tWorld\n"),
|
description: String::from("Hello\n\t\t\t\tWorld\n"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1028,7 +1006,6 @@ mod tests {
|
||||||
unit: None,
|
unit: None,
|
||||||
}],
|
}],
|
||||||
body: TokenStream2::new(),
|
body: TokenStream2::new(),
|
||||||
crate_name: FoundCrate::Itself,
|
|
||||||
description: "Test\n".into(),
|
description: "Test\n".into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1096,7 +1073,6 @@ mod tests {
|
||||||
unit: None,
|
unit: None,
|
||||||
}],
|
}],
|
||||||
body: TokenStream2::new(),
|
body: TokenStream2::new(),
|
||||||
crate_name: FoundCrate::Itself,
|
|
||||||
description: String::new(),
|
description: String::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1166,7 +1142,6 @@ mod tests {
|
||||||
unit: None,
|
unit: None,
|
||||||
}],
|
}],
|
||||||
body: TokenStream2::new(),
|
body: TokenStream2::new(),
|
||||||
crate_name: FoundCrate::Itself,
|
|
||||||
description: String::new(),
|
description: String::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1229,7 +1204,6 @@ mod tests {
|
||||||
unit: None,
|
unit: None,
|
||||||
}],
|
}],
|
||||||
body: TokenStream2::new(),
|
body: TokenStream2::new(),
|
||||||
crate_name: FoundCrate::Itself,
|
|
||||||
description: String::new(),
|
description: String::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1272,7 +1246,6 @@ mod tests {
|
||||||
is_async: false,
|
is_async: false,
|
||||||
fields: vec![],
|
fields: vec![],
|
||||||
body: TokenStream2::new(),
|
body: TokenStream2::new(),
|
||||||
crate_name: FoundCrate::Itself,
|
|
||||||
description: String::new(),
|
description: String::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::crate_ident::CrateIdent;
|
||||||
use crate::parsing::{NodeFnAttributes, ParsedNodeFn};
|
use crate::parsing::{NodeFnAttributes, ParsedNodeFn};
|
||||||
use crate::shader_nodes::per_pixel_adjust::PerPixelAdjust;
|
use crate::shader_nodes::per_pixel_adjust::PerPixelAdjust;
|
||||||
use proc_macro2::{Ident, TokenStream};
|
use proc_macro2::{Ident, TokenStream};
|
||||||
|
|
@ -50,11 +51,11 @@ impl Parse for ShaderNodeType {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ShaderCodegen {
|
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 {
|
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 {
|
match self {
|
||||||
ShaderNodeType::None | ShaderNodeType::ShaderNode => (),
|
ShaderNodeType::None | ShaderNodeType::ShaderNode => (),
|
||||||
_ => {
|
_ => {
|
||||||
|
|
@ -66,7 +67,7 @@ impl ShaderCodegen for ShaderNodeType {
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
ShaderNodeType::None | ShaderNodeType::ShaderNode => Ok(ShaderTokens::default()),
|
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::parsing::{Input, NodeFnAttributes, ParsedField, ParsedFieldType, ParsedNodeFn, RegularParsedField};
|
||||||
use crate::shader_nodes::{SHADER_NODES_FEATURE_GATE, ShaderCodegen, ShaderNodeType, ShaderTokens};
|
use crate::shader_nodes::{SHADER_NODES_FEATURE_GATE, ShaderCodegen, ShaderNodeType, ShaderTokens};
|
||||||
use convert_case::{Case, Casing};
|
use convert_case::{Case, Casing};
|
||||||
use proc_macro_crate::FoundCrate;
|
use proc_macro2::{Ident, Span, TokenStream};
|
||||||
use proc_macro2::{Ident, TokenStream};
|
|
||||||
use quote::{ToTokens, format_ident, quote};
|
use quote::{ToTokens, format_ident, quote};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use syn::parse::{Parse, ParseStream};
|
use syn::parse::{Parse, ParseStream};
|
||||||
use syn::punctuated::Punctuated;
|
use syn::punctuated::Punctuated;
|
||||||
use syn::{PatIdent, Type, parse_quote};
|
use syn::{LitStr, PatIdent, Type, parse_quote};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct PerPixelAdjust {}
|
pub struct PerPixelAdjust {}
|
||||||
|
|
@ -19,7 +19,7 @@ impl Parse for PerPixelAdjust {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ShaderCodegen 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 fn_name = &parsed.fn_name;
|
||||||
|
|
||||||
let mut params;
|
let mut params;
|
||||||
|
|
@ -74,6 +74,7 @@ impl ShaderCodegen for PerPixelAdjust {
|
||||||
let shader_node_mod = format_ident!("{}_shader_node", fn_name);
|
let shader_node_mod = format_ident!("{}_shader_node", fn_name);
|
||||||
|
|
||||||
let codegen = PerPixelAdjustCodegen {
|
let codegen = PerPixelAdjustCodegen {
|
||||||
|
crate_ident,
|
||||||
parsed,
|
parsed,
|
||||||
params,
|
params,
|
||||||
has_uniform,
|
has_uniform,
|
||||||
|
|
@ -93,6 +94,7 @@ impl ShaderCodegen for PerPixelAdjust {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PerPixelAdjustCodegen<'a> {
|
pub struct PerPixelAdjustCodegen<'a> {
|
||||||
|
crate_ident: &'a CrateIdent,
|
||||||
parsed: &'a ParsedNodeFn,
|
parsed: &'a ParsedNodeFn,
|
||||||
params: Vec<Param<'a>>,
|
params: Vec<Param<'a>>,
|
||||||
has_uniform: bool,
|
has_uniform: bool,
|
||||||
|
|
@ -107,6 +109,9 @@ pub struct PerPixelAdjustCodegen<'a> {
|
||||||
impl PerPixelAdjustCodegen<'_> {
|
impl PerPixelAdjustCodegen<'_> {
|
||||||
fn codegen_shader_entry_point(&self) -> syn::Result<TokenStream> {
|
fn codegen_shader_entry_point(&self) -> syn::Result<TokenStream> {
|
||||||
let fn_name = &self.parsed.fn_name;
|
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
|
let uniform_members = self
|
||||||
.params
|
.params
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -115,6 +120,16 @@ impl PerPixelAdjustCodegen<'_> {
|
||||||
ParamType::Uniform => Some(quote! {#ident: #ty}),
|
ParamType::Uniform => Some(quote! {#ident: #ty}),
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.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
|
let image_params = self
|
||||||
.params
|
.params
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -135,31 +150,28 @@ impl PerPixelAdjustCodegen<'_> {
|
||||||
|
|
||||||
let entry_point_mod = &self.entry_point_mod;
|
let entry_point_mod = &self.entry_point_mod;
|
||||||
let entry_point_name = &self.entry_point_name_ident;
|
let entry_point_name = &self.entry_point_name_ident;
|
||||||
let uniform_struct_ident = &self.uniform_struct_ident;
|
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
pub mod #entry_point_mod {
|
pub mod #entry_point_mod {
|
||||||
use super::*;
|
use super::*;
|
||||||
use graphene_core_shaders::color::Color;
|
use #gcore_shaders::color::Color;
|
||||||
use spirv_std::spirv;
|
use #reexport::glam::{Vec4, Vec4Swizzles};
|
||||||
use spirv_std::glam::{Vec4, Vec4Swizzles};
|
use #reexport::spirv_std::spirv;
|
||||||
use spirv_std::image::{Image2d, ImageWithMethods};
|
use #reexport::spirv_std::image::{Image2d, ImageWithMethods};
|
||||||
use spirv_std::image::sample_with::lod;
|
use #reexport::spirv_std::image::sample_with::lod;
|
||||||
|
|
||||||
pub const #entry_point_name: &str = core::concat!(core::module_path!(), "::entry_point");
|
pub const #entry_point_name: &str = core::concat!(core::module_path!(), "::entry_point");
|
||||||
|
|
||||||
#[repr(C)]
|
#uniform_struct
|
||||||
#[derive(Copy, Clone, bytemuck::NoUninit)]
|
#uniform_struct_shader_struct_derive
|
||||||
pub struct #uniform_struct_ident {
|
|
||||||
#(pub #uniform_members),*
|
|
||||||
}
|
|
||||||
|
|
||||||
#[spirv(fragment)]
|
#[spirv(fragment)]
|
||||||
pub fn entry_point(
|
pub fn entry_point(
|
||||||
#[spirv(frag_coord)] frag_coord: Vec4,
|
#[spirv(frag_coord)] frag_coord: Vec4,
|
||||||
color_out: &mut 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),*
|
#(#image_params),*
|
||||||
) {
|
) {
|
||||||
|
let uniform = <Uniform as #gcore_shaders::shaders::buffer_struct::BufferStruct>::read(*uniform);
|
||||||
let texel_coord = frag_coord.xy().as_uvec2();
|
let texel_coord = frag_coord.xy().as_uvec2();
|
||||||
let color: Color = #fn_name(#context, #(#call_args),*);
|
let color: Color = #fn_name(#context, #(#call_args),*);
|
||||||
*color_out = color.to_vec4();
|
*color_out = color.to_vec4();
|
||||||
|
|
@ -169,10 +181,8 @@ impl PerPixelAdjustCodegen<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn codegen_gpu_node(&self) -> syn::Result<TokenStream> {
|
fn codegen_gpu_node(&self) -> syn::Result<TokenStream> {
|
||||||
let gcore = match &self.parsed.crate_name {
|
let gcore = self.crate_ident.gcore()?;
|
||||||
FoundCrate::Itself => format_ident!("crate"),
|
let wgpu_executor = self.crate_ident.wgpu_executor()?;
|
||||||
FoundCrate::Name(name) => format_ident!("{name}"),
|
|
||||||
};
|
|
||||||
|
|
||||||
// adapt fields for gpu node
|
// adapt fields for gpu node
|
||||||
let raster_gpu: Type = parse_quote!(#gcore::table::Table<#gcore::raster_types::Raster<#gcore::raster_types::GPU>>);
|
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<_>>>()?;
|
.collect::<syn::Result<Vec<_>>>()?;
|
||||||
|
|
||||||
// insert wgpu_executor field
|
// insert wgpu_executor field
|
||||||
let wgpu_executor = format_ident!("__wgpu_executor");
|
let executor = format_ident!("__wgpu_executor");
|
||||||
fields.push(ParsedField {
|
fields.push(ParsedField {
|
||||||
pat_ident: PatIdent {
|
pat_ident: PatIdent {
|
||||||
attrs: vec![],
|
attrs: vec![],
|
||||||
by_ref: None,
|
by_ref: None,
|
||||||
mutability: None,
|
mutability: None,
|
||||||
ident: parse_quote!(#wgpu_executor),
|
ident: parse_quote!(#executor),
|
||||||
subpat: None,
|
subpat: None,
|
||||||
},
|
},
|
||||||
name: None,
|
name: None,
|
||||||
|
|
@ -271,7 +281,7 @@ impl PerPixelAdjustCodegen<'_> {
|
||||||
let entry_point_name = &self.entry_point_name;
|
let entry_point_name = &self.entry_point_name;
|
||||||
let body = quote! {
|
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,
|
wgsl_shader: crate::WGSL_SHADER,
|
||||||
fragment_shader_name: super::#entry_point_name,
|
fragment_shader_name: super::#entry_point_name,
|
||||||
has_uniform: #has_uniform,
|
has_uniform: #has_uniform,
|
||||||
|
|
@ -280,9 +290,13 @@ impl PerPixelAdjustCodegen<'_> {
|
||||||
};
|
};
|
||||||
|
|
||||||
// call node codegen
|
// 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 {
|
let mut parsed_node_fn = ParsedNodeFn {
|
||||||
vis: self.parsed.vis.clone(),
|
vis: self.parsed.vis.clone(),
|
||||||
attributes: NodeFnAttributes {
|
attributes: NodeFnAttributes {
|
||||||
|
display_name: Some(display_name),
|
||||||
shader_node: Some(ShaderNodeType::ShaderNode),
|
shader_node: Some(ShaderNodeType::ShaderNode),
|
||||||
..self.parsed.attributes.clone()
|
..self.parsed.attributes.clone()
|
||||||
},
|
},
|
||||||
|
|
@ -301,11 +315,10 @@ impl PerPixelAdjustCodegen<'_> {
|
||||||
is_async: true,
|
is_async: true,
|
||||||
fields,
|
fields,
|
||||||
body,
|
body,
|
||||||
crate_name: self.parsed.crate_name.clone(),
|
description: self.parsed.description.clone(),
|
||||||
description: "".to_string(),
|
|
||||||
};
|
};
|
||||||
parsed_node_fn.replace_impl_trait_in_input();
|
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`
|
// wrap node in `mod #gpu_node_mod`
|
||||||
let shader_node_mod = &self.shader_node_mod;
|
let shader_node_mod = &self.shader_node_mod;
|
||||||
|
|
@ -313,7 +326,7 @@ impl PerPixelAdjustCodegen<'_> {
|
||||||
#[cfg(feature = #SHADER_NODES_FEATURE_GATE)]
|
#[cfg(feature = #SHADER_NODES_FEATURE_GATE)]
|
||||||
mod #shader_node_mod {
|
mod #shader_node_mod {
|
||||||
use super::*;
|
use super::*;
|
||||||
use wgpu_executor::WgpuExecutor;
|
use #wgpu_executor::WgpuExecutor;
|
||||||
|
|
||||||
#gpu_node_impl
|
#gpu_node_impl
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use crate::Context;
|
use crate::Context;
|
||||||
use crate::shader_runtime::{FULLSCREEN_VERTEX_SHADER_NAME, ShaderRuntime};
|
use crate::shader_runtime::{FULLSCREEN_VERTEX_SHADER_NAME, ShaderRuntime};
|
||||||
use bytemuck::NoUninit;
|
|
||||||
use futures::lock::Mutex;
|
use futures::lock::Mutex;
|
||||||
use graphene_core::raster_types::{GPU, Raster};
|
use graphene_core::raster_types::{GPU, Raster};
|
||||||
|
use graphene_core::shaders::buffer_struct::BufferStruct;
|
||||||
use graphene_core::table::{Table, TableRow};
|
use graphene_core::table::{Table, TableRow};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
@ -27,7 +27,7 @@ impl PerPixelAdjustShaderRuntime {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ShaderRuntime {
|
impl ShaderRuntime {
|
||||||
pub async fn run_per_pixel_adjust<T: NoUninit>(&self, shaders: &Shaders<'_>, textures: Table<Raster<GPU>>, args: Option<&T>) -> Table<Raster<GPU>> {
|
pub async fn run_per_pixel_adjust<T: BufferStruct>(&self, shaders: &Shaders<'_>, textures: Table<Raster<GPU>>, args: Option<&T>) -> Table<Raster<GPU>> {
|
||||||
let mut cache = self.per_pixel_adjust.pipeline_cache.lock().await;
|
let mut cache = self.per_pixel_adjust.pipeline_cache.lock().await;
|
||||||
let pipeline = cache
|
let pipeline = cache
|
||||||
.entry(shaders.fragment_shader_name.to_owned())
|
.entry(shaders.fragment_shader_name.to_owned())
|
||||||
|
|
@ -38,7 +38,7 @@ impl ShaderRuntime {
|
||||||
device.create_buffer_init(&BufferInitDescriptor {
|
device.create_buffer_init(&BufferInitDescriptor {
|
||||||
label: Some(&format!("{} arg buffer", pipeline.name.as_str())),
|
label: Some(&format!("{} arg buffer", pipeline.name.as_str())),
|
||||||
usage: BufferUsages::STORAGE,
|
usage: BufferUsages::STORAGE,
|
||||||
contents: bytemuck::bytes_of(args),
|
contents: bytemuck::bytes_of(&T::write(*args)),
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
pipeline.dispatch(&self.context, textures, arg_buffer)
|
pipeline.dispatch(&self.context, textures, arg_buffer)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue