Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ graphite-proc-macros = { path = "proc-macros" }

# Workspace dependencies
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_json = "1.0"
serde-wasm-bindgen = "0.6"
Expand Down Expand Up @@ -154,7 +154,7 @@ parley = "0.5"
skrifa = "0.36"
pretty_assertions = "1.4"
fern = { version = "0.7", features = ["colored"] }
num_enum = "0.7"
num_enum = { version = "0.7", default-features = false }
num-derive = "0.4"
num-traits = { version = "0.2", default-features = false, features = ["libm"] }
specta = { version = "2.0.0-rc.22", features = [
Expand Down Expand Up @@ -193,7 +193,7 @@ open = "5.3"
poly-cool = "0.3"
spin = "0.10"
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" }

[workspace.lints.rust]
Expand Down
8 changes: 7 additions & 1 deletion node-graph/gcore-shaders/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,14 @@ std = [
"glam/serde",
"half/std",
"half/serde",
"num-traits/std"
"num-traits/std",
"num_enum/std",
]

[dependencies]
# Local dependencies
node-macro = { workspace = true }

# Local std dependencies
dyn-any = { workspace = true, optional = true }

Expand All @@ -35,6 +39,8 @@ glam = { workspace = true }
half = { workspace = true, default-features = false }
num-derive = { workspace = true }
num-traits = { workspace = true }
num_enum = { workspace = true }
spirv-std = { workspace = true }

# Workspace std dependencies
serde = { workspace = true, optional = true }
Expand Down
6 changes: 4 additions & 2 deletions node-graph/gcore-shaders/src/blending.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use core::fmt::Display;
use core::hash::{Hash, Hasher};
use node_macro::BufferStruct;
use num_enum::{FromPrimitive, IntoPrimitive};
#[cfg(not(feature = "std"))]
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", serde(default))]
pub struct AlphaBlending {
Expand Down Expand Up @@ -66,7 +68,7 @@ impl AlphaBlending {
}

#[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))]
pub enum BlendMode {
// Basic group
Expand Down
3 changes: 2 additions & 1 deletion node-graph/gcore-shaders/src/color/color_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use core::fmt::Debug;
use core::hash::Hash;
use glam::Vec4;
use half::f16;
use node_macro::BufferStruct;
#[cfg(not(feature = "std"))]
use num_traits::Euclid;
#[cfg(not(feature = "std"))]
Expand Down Expand Up @@ -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 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)]
#[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))]
pub struct Color {
red: f32,
Expand Down
1 change: 1 addition & 0 deletions node-graph/gcore-shaders/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub mod choice_type;
pub mod color;
pub mod context;
pub mod registry;
pub mod shaders;

pub use context::Ctx;
pub use glam;
Expand Down
114 changes: 114 additions & 0 deletions node-graph/gcore-shaders/src/shaders/buffer_struct/glam.rs
Original file line number Diff line number Diff line change
@@ -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]])
}
}
63 changes: 63 additions & 0 deletions node-graph/gcore-shaders/src/shaders/buffer_struct/mod.rs
Original file line number Diff line number Diff line change
@@ -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
}
}
Loading
Loading