From 430ceb3d06ebf49fad2d371d22e80a9595bec5ca Mon Sep 17 00:00:00 2001 From: nanoqsh Date: Thu, 11 Jan 2024 06:27:33 +0600 Subject: [PATCH] Uniforms --- dunge/src/bind.rs | 12 ++- dunge/src/context.rs | 9 +- dunge/src/el.rs | 7 ++ dunge/src/group.rs | 27 ++++-- dunge/src/lib.rs | 1 + dunge/src/shader.rs | 16 +++- dunge/src/state.rs | 1 + dunge/src/uniform.rs | 150 ++++++++++++++++++++++++++++++++++ dunge_macros/src/group.rs | 2 +- dunge_shader/src/eval.rs | 40 +++++++-- dunge_shader/src/group.rs | 8 +- dunge_shader/src/ops.rs | 134 ------------------------------ dunge_shader/src/ret.rs | 12 +++ dunge_shader/src/types.rs | 22 ++++- examples/triangle/src/main.rs | 32 ++++++-- 15 files changed, 305 insertions(+), 168 deletions(-) create mode 100644 dunge/src/uniform.rs delete mode 100644 dunge_shader/src/ops.rs diff --git a/dunge/src/bind.rs b/dunge/src/bind.rs index 99a589e..734c3a0 100644 --- a/dunge/src/bind.rs +++ b/dunge/src/bind.rs @@ -1,5 +1,8 @@ use { - crate::{group::BoundTexture, shader::Shader, state::State, texture::Sampler, Group}, + crate::{ + group::BoundTexture, shader::Shader, state::State, texture::Sampler, uniform::Uniform, + Group, + }, std::{any::TypeId, fmt, marker::PhantomData, sync::Arc}, wgpu::{ BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindingResource, Device, @@ -23,6 +26,13 @@ pub trait VisitMember<'a> { fn visit_member(self, visitor: &mut Visitor<'a>); } +impl<'a, V> VisitMember<'a> for &'a Uniform { + fn visit_member(self, visitor: &mut Visitor<'a>) { + let binding = self.buffer().as_entire_buffer_binding(); + visitor.push(BindingResource::Buffer(binding)); + } +} + impl<'a> VisitMember<'a> for BoundTexture<'a> { fn visit_member(self, visitor: &mut Visitor<'a>) { visitor.push(BindingResource::TextureView(self.get().view())); diff --git a/dunge/src/context.rs b/dunge/src/context.rs index 248397d..5990e66 100644 --- a/dunge/src/context.rs +++ b/dunge/src/context.rs @@ -11,6 +11,7 @@ use { texture::{ self, CopyBuffer, CopyBufferView, DrawTexture, Filter, Make, MapResult, Mapped, Sampler, }, + uniform::{Uniform, Value}, Vertex, }, std::{error, fmt, future::IntoFuture, sync::Arc}, @@ -24,7 +25,6 @@ impl Context { Self(Arc::new(state)) } - #[cfg(feature = "winit")] pub(crate) fn state(&self) -> &State { &self.0 } @@ -40,6 +40,13 @@ impl Context { Binder::new(&self.0, shader) } + pub fn make_uniform(&self, val: V) -> Uniform + where + V: Value, + { + Uniform::new(&self.0, &val.value()) + } + pub fn make_layer(&self, format: Format, shader: &Shader) -> Layer { Layer::new(&self.0, format, shader) } diff --git a/dunge/src/el.rs b/dunge/src/el.rs index c1a1704..c174ba7 100644 --- a/dunge/src/el.rs +++ b/dunge/src/el.rs @@ -84,6 +84,7 @@ where let mut ctrl = Control { close: Cell::default(), min_delta_time: Cell::new(Duration::from_secs_f32(1. / 60.)), + delta_time: Duration::ZERO, fps: 0, pressed_keys: vec![], released_keys: vec![], @@ -179,6 +180,7 @@ where } time.reset(); + ctrl.delta_time = delta_time; if let Some(fps) = fps.count(delta_time) { ctrl.fps = fps; } @@ -231,6 +233,7 @@ where pub struct Control { close: Cell, min_delta_time: Cell, + delta_time: Duration, fps: u32, pressed_keys: Vec, released_keys: Vec, @@ -245,6 +248,10 @@ impl Control { self.min_delta_time.set(min_delta_time); } + pub fn delta_time(&self) -> Duration { + self.delta_time + } + pub fn fps(&self) -> u32 { self.fps } diff --git a/dunge/src/group.rs b/dunge/src/group.rs index 4532353..fe03a60 100644 --- a/dunge/src/group.rs +++ b/dunge/src/group.rs @@ -1,7 +1,8 @@ use crate::{ sl::{GlobalOut, ReadGlobal, Ret}, texture::{BindTexture, Sampler, Texture}, - types::{self, GroupMemberType}, + types::{self, MemberType}, + uniform::{Uniform, Value}, }; pub use dunge_shader::group::{DeclareGroup, Projection}; @@ -26,30 +27,44 @@ impl<'a> BoundTexture<'a> { /// /// The trait is sealed because the derive macro relies on no new types being used. pub trait MemberProjection: private::Sealed { - const TYPE: GroupMemberType; + const TYPE: MemberType; type Field; fn member_projection(id: u32, binding: u32, out: GlobalOut) -> Self::Field; } +impl private::Sealed for &Uniform where V: Value {} + +impl MemberProjection for &Uniform +where + V: Value, +{ + const TYPE: MemberType = V::TYPE; + type Field = Ret; + + fn member_projection(id: u32, binding: u32, out: GlobalOut) -> Self::Field { + ReadGlobal::new(id, binding, Self::TYPE.is_value(), out) + } +} + impl private::Sealed for BoundTexture<'_> {} impl MemberProjection for BoundTexture<'_> { - const TYPE: GroupMemberType = GroupMemberType::Tx2df; + const TYPE: MemberType = MemberType::Tx2df; type Field = Ret>; fn member_projection(id: u32, binding: u32, out: GlobalOut) -> Self::Field { - ReadGlobal::new(id, binding, out) + ReadGlobal::new(id, binding, Self::TYPE.is_value(), out) } } impl private::Sealed for &Sampler {} impl MemberProjection for &Sampler { - const TYPE: GroupMemberType = GroupMemberType::Sampl; + const TYPE: MemberType = MemberType::Sampl; type Field = Ret; fn member_projection(id: u32, binding: u32, out: GlobalOut) -> Self::Field { - ReadGlobal::new(id, binding, out) + ReadGlobal::new(id, binding, Self::TYPE.is_value(), out) } } diff --git a/dunge/src/lib.rs b/dunge/src/lib.rs index b77297c..99686d9 100644 --- a/dunge/src/lib.rs +++ b/dunge/src/lib.rs @@ -10,6 +10,7 @@ pub mod mesh; pub mod shader; pub mod state; pub mod texture; +pub mod uniform; pub mod vertex; #[cfg(feature = "winit")] diff --git a/dunge/src/shader.rs b/dunge/src/shader.rs index a711190..a679f38 100644 --- a/dunge/src/shader.rs +++ b/dunge/src/shader.rs @@ -3,7 +3,7 @@ use { bind::TypedGroup, sl::{InputInfo, IntoModule, Module, Stages}, state::State, - types::{GroupMemberType, VectorType}, + types::{MemberType, VectorType}, }, std::marker::PhantomData, wgpu::{PipelineLayout, ShaderModule, VertexAttribute, VertexBufferLayout}, @@ -98,7 +98,17 @@ impl Inner { entries.clear(); for (binding, member) in iter::zip(0.., info.decl) { let entry = match member { - GroupMemberType::Tx2df => BindGroupLayoutEntry { + MemberType::Scalar(_) | MemberType::Vector(_) => BindGroupLayoutEntry { + binding, + visibility: visibility(info.stages), + ty: BindingType::Buffer { + ty: BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }, + MemberType::Tx2df => BindGroupLayoutEntry { binding, visibility: visibility(info.stages), ty: BindingType::Texture { @@ -108,7 +118,7 @@ impl Inner { }, count: None, }, - GroupMemberType::Sampl => BindGroupLayoutEntry { + MemberType::Sampl => BindGroupLayoutEntry { binding, visibility: visibility(info.stages), ty: BindingType::Sampler(SamplerBindingType::Filtering), diff --git a/dunge/src/state.rs b/dunge/src/state.rs index caad927..59da883 100644 --- a/dunge/src/state.rs +++ b/dunge/src/state.rs @@ -89,6 +89,7 @@ impl State { where D: Draw, { + self.queue.submit([]); draw.draw(render.0.make(&self.device, view)); let buffers = render.0.drain().map(CommandEncoder::finish); self.queue.submit(buffers); diff --git a/dunge/src/uniform.rs b/dunge/src/uniform.rs new file mode 100644 index 0000000..0ae5299 --- /dev/null +++ b/dunge/src/uniform.rs @@ -0,0 +1,150 @@ +use { + crate::{ + context::Context, + state::State, + types::{self, MemberType, ScalarType, VectorType}, + }, + std::{marker::PhantomData, sync::Arc}, + wgpu::Buffer, +}; + +#[derive(Clone)] +pub struct Uniform { + buf: Arc, + ty: PhantomData, +} + +impl Uniform { + pub(crate) fn new(state: &State, data: &Data) -> Self { + use wgpu::{ + util::{BufferInitDescriptor, DeviceExt}, + BufferUsages, + }; + + let buf = { + let desc = BufferInitDescriptor { + label: None, + contents: data.as_slice(), + usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST, + }; + + state.device().create_buffer_init(&desc) + }; + + Self { + buf: Arc::new(buf), + ty: PhantomData, + } + } + + pub fn update(&self, cx: &Context, val: V) + where + V: Value, + { + let queue = cx.state().queue(); + let data = val.value(); + queue.write_buffer(&self.buf, 0, data.as_slice()); + } + + pub(crate) fn buffer(&self) -> &Buffer { + &self.buf + } +} + +pub trait Value: private::Sealed { + const TYPE: MemberType; + type Type; + fn value(self) -> Data; +} + +impl private::Sealed for f32 {} + +impl Value for f32 { + const TYPE: MemberType = MemberType::Scalar(ScalarType::Float); + type Type = Self; + + fn value(self) -> Data { + Data([self, 0., 0., 0.]) + } +} + +impl private::Sealed for [f32; 2] {} + +impl Value for [f32; 2] { + const TYPE: MemberType = MemberType::Vector(VectorType::Vec2f); + type Type = types::Vec2; + + fn value(self) -> Data { + let [x, y] = self; + Data([x, y, 0., 0.]) + } +} + +impl private::Sealed for [f32; 3] {} + +impl Value for [f32; 3] { + const TYPE: MemberType = MemberType::Vector(VectorType::Vec3f); + type Type = types::Vec3; + + fn value(self) -> Data { + let [x, y, z] = self; + Data([x, y, z, 0.]) + } +} + +impl private::Sealed for [f32; 4] {} + +impl Value for [f32; 4] { + const TYPE: MemberType = MemberType::Vector(VectorType::Vec4f); + type Type = types::Vec4; + + fn value(self) -> Data { + let [x, y, z, w] = self; + Data([x, y, z, w]) + } +} + +impl private::Sealed for glam::Vec2 {} + +impl Value for glam::Vec2 { + const TYPE: MemberType = MemberType::Vector(VectorType::Vec2f); + type Type = types::Vec2; + + fn value(self) -> Data { + self.to_array().value() + } +} + +impl private::Sealed for glam::Vec3 {} + +impl Value for glam::Vec3 { + const TYPE: MemberType = MemberType::Vector(VectorType::Vec3f); + type Type = types::Vec3; + + fn value(self) -> Data { + self.to_array().value() + } +} + +impl private::Sealed for glam::Vec4 {} + +impl Value for glam::Vec4 { + const TYPE: MemberType = MemberType::Vector(VectorType::Vec4f); + type Type = types::Vec4; + + fn value(self) -> Data { + self.to_array().value() + } +} + +pub struct Data([f32; 4]); + +impl Data { + fn as_slice(&self) -> &[u8] { + bytemuck::cast_slice(&self.0) + } +} + +mod private { + pub trait Sealed {} +} diff --git a/dunge_macros/src/group.rs b/dunge_macros/src/group.rs index b8506ad..1c54175 100644 --- a/dunge_macros/src/group.rs +++ b/dunge_macros/src/group.rs @@ -90,7 +90,7 @@ pub(crate) fn derive(input: DeriveInput) -> TokenStream { ]); } - impl ::dunge::bind::Visit for Map<#(#anon_lts),*> { + impl ::dunge::bind::Visit for #name<#(#anon_lts),*> { fn visit<'a>(&'a self, visitor: &mut ::dunge::bind::Visitor<'a>) { #(#group_visit_members);*; } diff --git a/dunge_shader/src/eval.rs b/dunge_shader/src/eval.rs index 21114cc..0b20fd0 100644 --- a/dunge_shader/src/eval.rs +++ b/dunge_shader/src/eval.rs @@ -228,12 +228,18 @@ impl GlobalOut { pub struct ReadGlobal { id: u32, binding: u32, + is_value: bool, out: GlobalOut, } impl ReadGlobal { - pub const fn new(id: u32, binding: u32, out: GlobalOut) -> Ret { - Ret::new(Self { id, binding, out }) + pub const fn new(id: u32, binding: u32, is_value: bool, out: GlobalOut) -> Ret { + Ret::new(Self { + id, + binding, + is_value, + out, + }) } } @@ -244,12 +250,23 @@ where type Out = T; fn eval(self, en: &mut E) -> Expr { - let ReadGlobal { id, binding, out } = self.get(); + let ReadGlobal { + id, + binding, + is_value, + out, + } = self.get(); + out.with_stage(E::STAGE); let en = en.get_entry(); let res = ResourceBinding { group: id, binding }; let var = en.compl.globs.get(&res); - en.global(var) + let global = en.global(var); + if is_value { + en.load(global) + } else { + global + } } } @@ -623,6 +640,14 @@ impl Entry { Expr(self.exprs.append(ex, Span::UNDEFINED)) } + fn load(&mut self, ptr: Expr) -> Expr { + let ex = Expression::Load { pointer: ptr.0 }; + let handle = self.exprs.append(ex, Span::UNDEFINED); + let st = Statement::Emit(Range::new_from_bounds(handle, handle)); + self.stats.push(st, &self.exprs); + Expr(handle) + } + fn access_index(&mut self, base: Expr, index: u32) -> Expr { let ex = Expression::AccessIndex { base: base.0, @@ -802,9 +827,10 @@ impl Compiler { fn decl_group(&mut self, group: u32, decl: DeclareGroup) { for (binding, member) in iter::zip(0.., decl) { + let space = member.address_space(); let ty = self.types.insert(member.ty(), Span::UNDEFINED); let res = ResourceBinding { group, binding }; - self.globs.add(ty, res); + self.globs.add(space, ty, res); } } } @@ -816,11 +842,11 @@ struct Globals { } impl Globals { - fn add(&mut self, ty: Handle, res: ResourceBinding) { + fn add(&mut self, space: AddressSpace, ty: Handle, res: ResourceBinding) { self.handles.entry(res.clone()).or_insert_with(|| { let var = GlobalVariable { name: None, - space: AddressSpace::Handle, + space, binding: Some(res), ty, init: None, diff --git a/dunge_shader/src/group.rs b/dunge_shader/src/group.rs index 5c4c73d..474c501 100644 --- a/dunge_shader/src/group.rs +++ b/dunge_shader/src/group.rs @@ -1,5 +1,5 @@ use { - crate::{eval::GlobalOut, types::GroupMemberType}, + crate::{eval::GlobalOut, types::MemberType}, std::{iter, slice}, }; @@ -14,16 +14,16 @@ pub trait Projection { } #[derive(Clone, Copy)] -pub struct DeclareGroup(&'static [GroupMemberType]); +pub struct DeclareGroup(&'static [MemberType]); impl DeclareGroup { - pub const fn new(ts: &'static [GroupMemberType]) -> Self { + pub const fn new(ts: &'static [MemberType]) -> Self { Self(ts) } } impl IntoIterator for DeclareGroup { - type Item = GroupMemberType; + type Item = MemberType; type IntoIter = iter::Copied>; fn into_iter(self) -> Self::IntoIter { diff --git a/dunge_shader/src/ops.rs b/dunge_shader/src/ops.rs deleted file mode 100644 index 8ac24af..0000000 --- a/dunge_shader/src/ops.rs +++ /dev/null @@ -1,134 +0,0 @@ -use { - crate::{ - eval::{Eval, Expr, GetEntry, Op}, - ret::Ret, - types, - }, - std::ops, -}; - -pub struct Binary { - a: A, - b: B, - op: Op, -} - -impl Eval for Ret, O> -where - A: Eval, - B: Eval, - E: GetEntry, -{ - type Out = O; - - fn eval(self, en: &mut E) -> Expr { - let Binary { a, b, op } = self.get(); - let x = a.eval(en); - let y = b.eval(en); - en.get_entry().binary(op, x, y) - } -} - -macro_rules! impl_op { - ($o:ident :: $f:ident ( $a:ty, $b:ty ) -> $r:ty) => { - impl ops::$o> for $a { - type Output = Ret>, $r>; - - fn $f(self, b: Ret) -> Self::Output { - Ret::new(Binary { - a: self, - b, - op: Op::$o, - }) - } - } - - impl ops::$o<$a> for Ret { - type Output = Ret, $b>, $r>; - - fn $f(self, b: $a) -> Self::Output { - Ret::new(Binary { - a: self, - b, - op: Op::$o, - }) - } - } - }; -} - -impl_op!(Add::add(f32, f32) -> f32); -impl_op!(Sub::sub(f32, f32) -> f32); -impl_op!(Mul::mul(f32, f32) -> f32); -impl_op!(Div::div(f32, f32) -> f32); -impl_op!(Rem::rem(f32, f32) -> f32); -impl_op!(Add::add(i32, i32) -> i32); -impl_op!(Sub::sub(i32, i32) -> i32); -impl_op!(Mul::mul(i32, i32) -> i32); -impl_op!(Div::div(i32, i32) -> i32); -impl_op!(Rem::rem(i32, i32) -> i32); -impl_op!(Add::add(u32, u32) -> u32); -impl_op!(Sub::sub(u32, u32) -> u32); -impl_op!(Mul::mul(u32, u32) -> u32); -impl_op!(Div::div(u32, u32) -> u32); -impl_op!(Rem::rem(u32, u32) -> u32); - -impl ops::Mul for Ret -where - O: types::Vector, -{ - type Output = Ret, O>; - - fn mul(self, b: f32) -> Self::Output { - Ret::new(Binary { - a: self, - b, - op: Op::Mul, - }) - } -} - -impl ops::Mul> for f32 -where - O: types::Vector, -{ - type Output = Ret>, O>; - - fn mul(self, b: Ret) -> Self::Output { - Ret::new(Binary { - a: self, - b, - op: Op::Mul, - }) - } -} - -impl ops::Add> for Ret -where - O: types::Vector, -{ - type Output = Ret>, O>; - - fn add(self, b: Ret) -> Self::Output { - Ret::new(Binary { - a: self, - b, - op: Op::Add, - }) - } -} - -impl ops::Sub> for Ret -where - O: types::Vector, -{ - type Output = Ret>, O>; - - fn sub(self, b: Ret) -> Self::Output { - Ret::new(Binary { - a: self, - b, - op: Op::Sub, - }) - } -} diff --git a/dunge_shader/src/ret.rs b/dunge_shader/src/ret.rs index 3871b83..1e7c001 100644 --- a/dunge_shader/src/ret.rs +++ b/dunge_shader/src/ret.rs @@ -79,6 +79,18 @@ macro_rules! impl_op { }) } } + + impl ops::$o> for Ret { + type Output = Ret, Ret>, $r>; + + fn $f(self, b: Ret) -> Self::Output { + Ret::new(Binary { + a: self, + b, + op: Op::$o, + }) + } + } }; } diff --git a/dunge_shader/src/types.rs b/dunge_shader/src/types.rs index 0ecd15d..d074129 100644 --- a/dunge_shader/src/types.rs +++ b/dunge_shader/src/types.rs @@ -1,5 +1,5 @@ use { - naga::{ImageClass, ImageDimension, ScalarKind, Type, TypeInner, VectorSize}, + naga::{AddressSpace, ImageClass, ImageDimension, ScalarKind, Type, TypeInner, VectorSize}, std::marker::PhantomData, }; @@ -23,6 +23,7 @@ impl Scalar for bool { const TYPE: ScalarType = ScalarType::Bool; } +#[derive(Clone, Copy)] pub enum ScalarType { Float, Sint, @@ -190,18 +191,33 @@ const SAMPLER: Type = Type { }; #[derive(Clone, Copy)] -pub enum GroupMemberType { +pub enum MemberType { + Scalar(ScalarType), + Vector(VectorType), Tx2df, Sampl, } -impl GroupMemberType { +impl MemberType { + pub const fn is_value(self) -> bool { + matches!(self, Self::Scalar(_) | Self::Vector(_)) + } + pub(crate) const fn ty(self) -> Type { match self { + Self::Scalar(v) => v.ty(), + Self::Vector(v) => v.ty(), Self::Tx2df => TEXTURE2DF, Self::Sampl => SAMPLER, } } + + pub(crate) const fn address_space(self) -> AddressSpace { + match self { + Self::Scalar(_) | Self::Vector(_) => AddressSpace::Uniform, + Self::Tx2df | Self::Sampl => AddressSpace::Handle, + } + } } pub trait IntoVector { diff --git a/examples/triangle/src/main.rs b/examples/triangle/src/main.rs index 0b9cc1d..aebde95 100644 --- a/examples/triangle/src/main.rs +++ b/examples/triangle/src/main.rs @@ -13,21 +13,25 @@ async fn run() -> Result<(), Error> { color::Rgba, el::KeyCode, glam::Vec4, - sl::{self, Index, Out}, - update, Control, Frame, + sl::{self, Groups, Index, Out}, + uniform::Uniform, + update, Control, Frame, Group, }, std::f32::consts, }; const COLOR: Vec4 = Vec4::new(1., 0., 0., 1.); const THIRD: f32 = consts::TAU / 3.; - const R_OFFSET: f32 = -consts::TAU / 4.; - const Y_OFFSET: f32 = 0.25; - let triangle = |Index(index): Index| { - let [x, y] = sl::thunk(sl::f32(index) * THIRD + R_OFFSET); + #[derive(Group)] + struct Offset<'a> { + r: &'a Uniform, + } + + let triangle = |Index(idx): Index, Groups(offset): Groups| { + let [x, y] = sl::thunk(sl::f32(idx) * THIRD + offset.r); Out { - place: sl::vec4(sl::cos(x), sl::sin(y) + Y_OFFSET, 0., 1.), + place: sl::vec4(sl::cos(x), sl::sin(y), 0., 1.), color: COLOR, } }; @@ -35,6 +39,15 @@ async fn run() -> Result<(), Error> { let window = dunge::window().with_title("Triangle").await?; let cx = window.context(); let shader = cx.make_shader(triangle); + let mut r = 0.; + let uniform = cx.make_uniform(r); + let bind = { + let mut binder = cx.make_binder(&shader); + let offset = Offset { r: &uniform }; + binder.bind(&offset); + binder.into_binding() + }; + let layer = cx.make_layer(window.format(), &shader); let update = |ctrl: &Control| { for key in ctrl.pressed_keys() { @@ -42,11 +55,14 @@ async fn run() -> Result<(), Error> { ctrl.close(); } } + + r += ctrl.delta_time().as_secs_f32(); + uniform.update(&cx, r); }; let draw = |mut frame: Frame| { let clear = Rgba::from_standard([0., 0., 0., 1.]); - frame.layer(&layer, clear).bind_empty().draw_triangles(1); + frame.layer(&layer, clear).bind(&bind).draw_triangles(1); }; window.run(update::from_fn(update, draw))?;