From bfac8c8b6d3a288188331f6aa424eec2e6637667 Mon Sep 17 00:00:00 2001 From: nanoqsh Date: Tue, 16 Jan 2024 04:22:00 +0600 Subject: [PATCH] Unit structs in derive --- dunge/src/context.rs | 16 ++--------- dunge/src/instance.rs | 33 +++++++++++++++++++++-- dunge/src/state.rs | 13 ++++++++- dunge/tests/triangle_group.rs | 4 +-- dunge/tests/triangle_index.rs | 4 +-- dunge/tests/triangle_instance.rs | 18 ++++--------- dunge/tests/triangle_vertex.rs | 28 +++++-------------- dunge_macros/src/group.rs | 45 ++++++++++++++++++++++++------- dunge_macros/src/instance.rs | 44 +++++++++++++++++++++++------- dunge_macros/src/vertex.rs | 46 +++++++++++++++++++++++++------- examples/triangle/src/main.rs | 8 +++--- 11 files changed, 169 insertions(+), 90 deletions(-) diff --git a/dunge/src/context.rs b/dunge/src/context.rs index 9825c62..6eecbaa 100644 --- a/dunge/src/context.rs +++ b/dunge/src/context.rs @@ -1,17 +1,14 @@ use { crate::{ bind::{self, Binder, GroupHandler, UniqueBinding, Update, Visit}, - draw::Draw, format::Format, instance::Row, layer::Layer, mesh::{self, Mesh}, shader::Shader, sl::IntoModule, - state::{Render, State}, - texture::{ - self, CopyBuffer, CopyBufferView, DrawTexture, Filter, Make, MapResult, Mapped, Sampler, - }, + state::State, + texture::{self, CopyBuffer, CopyBufferView, Filter, Make, MapResult, Mapped, Sampler}, uniform::{Uniform, Value}, Vertex, }, @@ -90,15 +87,6 @@ impl Context { view.map(&self.0, tx, rx).await } - pub fn draw_to_texture(&self, render: &mut Render, texture: &T, draw: D) - where - T: DrawTexture, - D: Draw, - { - let view = texture.draw_texture().render_view(); - self.0.draw(render, view, draw); - } - pub fn update_group( &self, uni: &mut UniqueBinding, diff --git a/dunge/src/instance.rs b/dunge/src/instance.rs index abb40a6..841fda6 100644 --- a/dunge/src/instance.rs +++ b/dunge/src/instance.rs @@ -1,18 +1,19 @@ use { crate::{ + context::Context, sl::{ReadInstance, Ret}, state::State, types::{self, VectorType}, uniform::{self, Value}, Instance, }, - std::marker::PhantomData, + std::{error, fmt, marker::PhantomData}, wgpu::{Buffer, RenderPass}, }; pub use dunge_shader::instance::Projection; -/// Describes a group member type projection. +/// Describes an instance member type projection. /// /// The trait is sealed because the derive macro relies on no new types being used. pub trait MemberProjection: private::Sealed { @@ -135,8 +136,36 @@ impl Row { ty: PhantomData, } } + + pub fn update(&self, cx: &Context, data: &[U]) -> Result<(), UpdateError> + where + U: Value, + { + if data.len() != self.len as usize { + return Err(UpdateError); + } + + let queue = cx.state().queue(); + let data = uniform::values_as_bytes(data); + queue.write_buffer(&self.buf, 0, data.as_ref()); + Ok(()) + } } +/// An error returned from the [update](crate::instance::Row::update) function. +/// +/// Returned when passed data size is invalid. +#[derive(Debug)] +pub struct UpdateError; + +impl fmt::Display for UpdateError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "update error: the data size is invalid") + } +} + +impl error::Error for UpdateError {} + mod private { pub trait Sealed {} } diff --git a/dunge/src/state.rs b/dunge/src/state.rs index fbf90e5..19b4403 100644 --- a/dunge/src/state.rs +++ b/dunge/src/state.rs @@ -1,7 +1,7 @@ use { crate::{ color::Rgba, - context::Error, + context::{Context, Error}, draw::Draw, format::Format, layer::{Layer, SetLayer}, @@ -99,6 +99,17 @@ impl State { #[derive(Default)] pub struct Render(Encoders); +impl Render { + pub fn draw_to(&mut self, cx: &Context, texture: &T, draw: D) + where + T: DrawTexture, + D: Draw, + { + let view = texture.draw_texture().render_view(); + cx.state().draw(self, view, draw); + } +} + #[derive(Clone, Copy, Default)] pub struct Options { clear: Option, diff --git a/dunge/tests/triangle_group.rs b/dunge/tests/triangle_group.rs index 007f8e9..e297042 100644 --- a/dunge/tests/triangle_group.rs +++ b/dunge/tests/triangle_group.rs @@ -100,9 +100,7 @@ fn render() -> Result<(), Error> { frame.copy_texture(&buffer, &view); }); - let mut render = Render::default(); - cx.draw_to_texture(&mut render, &view, draw); - + Render::default().draw_to(&cx, &view, draw); let mapped = helpers::block_on({ let (tx, rx) = helpers::oneshot(); cx.map_view(buffer.view(), tx, rx) diff --git a/dunge/tests/triangle_index.rs b/dunge/tests/triangle_index.rs index 902b63b..625cb29 100644 --- a/dunge/tests/triangle_index.rs +++ b/dunge/tests/triangle_index.rs @@ -49,9 +49,7 @@ fn render() -> Result<(), Error> { frame.copy_texture(&buffer, &view); }); - let mut render = Render::default(); - cx.draw_to_texture(&mut render, &view, draw); - + Render::default().draw_to(&cx, &view, draw); let mapped = helpers::block_on({ let (tx, rx) = helpers::oneshot(); cx.map_view(buffer.view(), tx, rx) diff --git a/dunge/tests/triangle_instance.rs b/dunge/tests/triangle_instance.rs index 03b4d99..ea7c292 100644 --- a/dunge/tests/triangle_instance.rs +++ b/dunge/tests/triangle_instance.rs @@ -25,17 +25,14 @@ fn render() -> Result<(), Error> { const R_OFFSET: f32 = -consts::TAU / 4.; #[derive(Instance)] - struct Transform { - pos: Row<[f32; 2]>, - col: Row<[f32; 3]>, - } + struct Transform(Row<[f32; 2]>, Row<[f32; 3]>); let triangle = |t: InInstance, Index(index): Index| { let [x, y] = sl::thunk(sl::f32(index) * THIRD + R_OFFSET); - let p = sl::vec2(sl::cos(x), sl::sin(y)) * TRIANGLE_SIZE + t.pos; + let p = sl::vec2(sl::cos(x), sl::sin(y)) * TRIANGLE_SIZE + t.0; Out { place: sl::concat(p, Vec2::new(0., 1.)), - color: sl::vec4_with(sl::fragment(t.col), 1.), + color: sl::vec4_with(sl::fragment(t.1), 1.), } }; @@ -53,10 +50,7 @@ fn render() -> Result<(), Error> { const POS: [[f32; 2]; 3] = [[0.0, -0.375], [0.433, 0.375], [-0.433, 0.375]]; const COL: [[f32; 3]; 3] = [[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]]; - Transform { - pos: cx.make_row(&POS), - col: cx.make_row(&COL), - } + Transform(cx.make_row(&POS), cx.make_row(&COL)) }; let buffer = cx.make_copy_buffer(SIZE); @@ -71,9 +65,7 @@ fn render() -> Result<(), Error> { frame.copy_texture(&buffer, &view); }); - let mut render = Render::default(); - cx.draw_to_texture(&mut render, &view, draw); - + Render::default().draw_to(&cx, &view, draw); let mapped = helpers::block_on({ let (tx, rx) = helpers::oneshot(); cx.map_view(buffer.view(), tx, rx) diff --git a/dunge/tests/triangle_vertex.rs b/dunge/tests/triangle_vertex.rs index 2b94943..ec1ff91 100644 --- a/dunge/tests/triangle_vertex.rs +++ b/dunge/tests/triangle_vertex.rs @@ -23,14 +23,11 @@ fn render() -> Result<(), Error> { #[repr(C)] #[derive(Vertex)] - struct Vert { - pos: [f32; 2], - col: [f32; 3], - } + struct Vert([f32; 2], [f32; 3]); let triangle = |vert: InVertex| Out { - place: sl::concat(vert.pos, Vec2::new(0., 1.)), - color: sl::vec4_with(sl::fragment(vert.col), 1.), + place: sl::concat(vert.0, Vec2::new(0., 1.)), + color: sl::vec4_with(sl::fragment(vert.1), 1.), }; let cx = helpers::block_on(dunge::context())?; @@ -47,18 +44,9 @@ fn render() -> Result<(), Error> { use mesh::Data; const VERTS: [Vert; 3] = [ - Vert { - pos: [0., -0.75], - col: [1., 0., 0.], - }, - Vert { - pos: [0.866, 0.75], - col: [0., 1., 0.], - }, - Vert { - pos: [-0.866, 0.75], - col: [0., 0., 1.], - }, + Vert([0., -0.75], [1., 0., 0.]), + Vert([0.866, 0.75], [0., 1., 0.]), + Vert([-0.866, 0.75], [0., 0., 1.]), ]; let data = Data::from_verts(&VERTS); @@ -72,9 +60,7 @@ fn render() -> Result<(), Error> { frame.copy_texture(&buffer, &view); }); - let mut render = Render::default(); - cx.draw_to_texture(&mut render, &view, draw); - + Render::default().draw_to(&cx, &view, draw); let mapped = helpers::block_on({ let (tx, rx) = helpers::oneshot(); cx.map_view(buffer.view(), tx, rx) diff --git a/dunge_macros/src/group.rs b/dunge_macros/src/group.rs index 7f1afb3..387eb6b 100644 --- a/dunge_macros/src/group.rs +++ b/dunge_macros/src/group.rs @@ -1,7 +1,7 @@ use { crate::member, proc_macro2::{Span, TokenStream}, - syn::{spanned::Spanned, Data, DataStruct, DeriveInput, GenericParam, Ident, Lifetime}, + syn::{spanned::Spanned, Data, DataStruct, DeriveInput, Fields, GenericParam, Ident, Lifetime}, }; pub(crate) fn derive(input: DeriveInput) -> TokenStream { @@ -13,6 +13,16 @@ pub(crate) fn derive(input: DeriveInput) -> TokenStream { }; }; + let named = match &fields { + Fields::Named(_) => true, + Fields::Unnamed(_) => false, + Fields::Unit => { + return quote::quote_spanned! { input.ident.span() => + ::std::compile_error!("the group type cannot be a unit struct"); + } + } + }; + let mut lts = Vec::with_capacity(input.generics.params.len()); for param in input.generics.params { let GenericParam::Lifetime(param) = param else { @@ -48,7 +58,6 @@ pub(crate) fn derive(input: DeriveInput) -> TokenStream { }; let static_lts = lts.iter().map(|_| &static_lt); - let anon_lt = Lifetime { apostrophe: Span::call_site(), ident: Ident::new("_", Span::call_site()), @@ -73,7 +82,11 @@ pub(crate) fn derive(input: DeriveInput) -> TokenStream { let group_fields = iter::zip(0.., &fields).map(|(index, field)| { let ident = member::make(index, field.ident.clone()); let ty = &field.ty; - quote::quote! { #ident: <#ty as ::dunge::group::MemberProjection>::Field } + if named { + quote::quote! { #ident: <#ty as ::dunge::group::MemberProjection>::Field } + } else { + quote::quote! { <#ty as ::dunge::group::MemberProjection>::Field } + } }); let group_member_projections = iter::zip(0.., &fields).map(|(index, field)| { @@ -82,6 +95,20 @@ pub(crate) fn derive(input: DeriveInput) -> TokenStream { quote::quote! { #ident: <#ty as ::dunge::group::MemberProjection>::member_projection(id, #index, out.clone()) } }); + let projection = if named { + quote::quote! { + struct #projection_name<#(#lts),*> { + #(#group_fields),*, + } + } + } else { + quote::quote! { + struct #projection_name<#(#lts),*>( + #(#group_fields),*, + ); + } + }; + quote::quote! { impl<#(#lts),*> ::dunge::Group for #name<#(#lts),*> { type Projection = #projection_name<#(#static_lts),*>; @@ -96,9 +123,7 @@ pub(crate) fn derive(input: DeriveInput) -> TokenStream { } } - struct #projection_name<#(#lts),*> { - #(#group_fields),*, - } + #projection impl<#(#lts),*> ::dunge::group::Projection for #projection_name<#(#lts),*> { fn projection(id: ::core::primitive::u32, out: ::dunge::sl::GlobalOut) -> Self { @@ -183,10 +208,10 @@ mod tests { } } - struct MapProjection<'a> { - 0: as ::dunge::group::MemberProjection>::Field, - 1: <&'a Sampler as ::dunge::group::MemberProjection>::Field, - } + struct MapProjection<'a>( + as ::dunge::group::MemberProjection>::Field, + <&'a Sampler as ::dunge::group::MemberProjection>::Field, + ); impl<'a> ::dunge::group::Projection for MapProjection<'a> { fn projection(id: ::core::primitive::u32, out: ::dunge::sl::GlobalOut) -> Self { diff --git a/dunge_macros/src/instance.rs b/dunge_macros/src/instance.rs index ad1c179..f843d7a 100644 --- a/dunge_macros/src/instance.rs +++ b/dunge_macros/src/instance.rs @@ -1,7 +1,7 @@ use { crate::member, proc_macro2::TokenStream, - syn::{spanned::Spanned, Data, DataStruct, DeriveInput}, + syn::{spanned::Spanned, Data, DataStruct, DeriveInput, Fields}, }; pub(crate) fn derive(input: DeriveInput) -> TokenStream { @@ -13,6 +13,16 @@ pub(crate) fn derive(input: DeriveInput) -> TokenStream { }; }; + let named = match &fields { + Fields::Named(_) => true, + Fields::Unnamed(_) => false, + Fields::Unit => { + return quote::quote_spanned! { input.ident.span() => + ::std::compile_error!("the instance type cannot be a unit struct"); + } + } + }; + if !input.generics.params.is_empty() { return quote::quote_spanned! { input.generics.params.span() => ::std::compile_error!("the instance struct cannot have generic parameters"); @@ -40,7 +50,11 @@ pub(crate) fn derive(input: DeriveInput) -> TokenStream { let instance_fields = iter::zip(0.., &fields).map(|(index, field)| { let ident = member::make(index, field.ident.clone()); let ty = &field.ty; - quote::quote! { #ident: <#ty as ::dunge::instance::MemberProjection>::Field } + if named { + quote::quote! { #ident: <#ty as ::dunge::instance::MemberProjection>::Field } + } else { + quote::quote! { <#ty as ::dunge::instance::MemberProjection>::Field } + } }); let instance_member_projections = iter::zip(0.., &fields).map(|(index, field)| { @@ -49,6 +63,20 @@ pub(crate) fn derive(input: DeriveInput) -> TokenStream { quote::quote! { #ident: <#ty as ::dunge::instance::MemberProjection>::member_projection(id + #index) } }); + let projection = if named { + quote::quote! { + struct #projection_name { + #(#instance_fields),*, + } + } + } else { + quote::quote! { + struct #projection_name( + #(#instance_fields),*, + ); + } + }; + quote::quote! { impl ::dunge::Instance for #name { type Projection = #projection_name; @@ -63,9 +91,7 @@ pub(crate) fn derive(input: DeriveInput) -> TokenStream { } } - struct #projection_name { - #(#instance_fields),*, - } + #projection impl ::dunge::instance::Projection for #projection_name { fn projection(id: ::core::primitive::u32) -> Self { @@ -150,10 +176,10 @@ mod tests { } } - struct TransformProjection { - 0: as ::dunge::instance::MemberProjection>::Field, - 1: as ::dunge::instance::MemberProjection>::Field, - } + struct TransformProjection( + as ::dunge::instance::MemberProjection>::Field, + as ::dunge::instance::MemberProjection>::Field, + ); impl ::dunge::instance::Projection for TransformProjection { fn projection(id: ::core::primitive::u32) -> Self { diff --git a/dunge_macros/src/vertex.rs b/dunge_macros/src/vertex.rs index fdb3dc8..0e60fec 100644 --- a/dunge_macros/src/vertex.rs +++ b/dunge_macros/src/vertex.rs @@ -1,7 +1,9 @@ use { crate::member, proc_macro2::TokenStream, - syn::{meta::ParseNestedMeta, spanned::Spanned, Attribute, Data, DataStruct, DeriveInput}, + syn::{ + meta::ParseNestedMeta, spanned::Spanned, Attribute, Data, DataStruct, DeriveInput, Fields, + }, }; pub(crate) fn derive(input: DeriveInput) -> TokenStream { @@ -13,6 +15,16 @@ pub(crate) fn derive(input: DeriveInput) -> TokenStream { }; }; + let named = match &fields { + Fields::Named(_) => true, + Fields::Unnamed(_) => false, + Fields::Unit => { + return quote::quote_spanned! { input.ident.span() => + ::std::compile_error!("the vertex type cannot be a unit struct"); + } + } + }; + if !input.generics.params.is_empty() { return quote::quote_spanned! { input.generics.params.span() => ::std::compile_error!("the vertex struct cannot have generic parameters"); @@ -41,7 +53,11 @@ pub(crate) fn derive(input: DeriveInput) -> TokenStream { let projection_fields = iter::zip(0.., &fields).map(|(index, field)| { let ident = member::make(index, field.ident.clone()); let ty = &field.ty; - quote::quote! { #ident: <#ty as ::dunge::vertex::InputProjection>::Field } + if named { + quote::quote! { #ident: <#ty as ::dunge::vertex::InputProjection>::Field } + } else { + quote::quote! { <#ty as ::dunge::vertex::InputProjection>::Field } + } }); let projection_inputs = iter::zip(0.., &fields).map(|(index, field)| { @@ -50,6 +66,20 @@ pub(crate) fn derive(input: DeriveInput) -> TokenStream { quote::quote! { #ident: <#ty as ::dunge::vertex::InputProjection>::input_projection(id, #index) } }); + let projection = if named { + quote::quote! { + struct #projection_name { + #(#projection_fields),*, + } + } + } else { + quote::quote! { + struct #projection_name( + #(#projection_fields),*, + ); + } + }; + quote::quote! { unsafe impl ::dunge::Vertex for #name { type Projection = #projection_name; @@ -58,9 +88,7 @@ pub(crate) fn derive(input: DeriveInput) -> TokenStream { ]); } - struct #projection_name { - #(#projection_fields),*, - } + #projection impl ::dunge::vertex::Projection for #projection_name { fn projection(id: ::core::primitive::u32) -> Self { @@ -145,10 +173,10 @@ mod tests { ]); } - struct VertProjection { - 0: <[f32; 2] as ::dunge::vertex::InputProjection>::Field, - 1: <[f32; 3] as ::dunge::vertex::InputProjection>::Field, - } + struct VertProjection( + <[f32; 2] as ::dunge::vertex::InputProjection>::Field, + <[f32; 3] as ::dunge::vertex::InputProjection>::Field, + ); impl ::dunge::vertex::Projection for VertProjection { fn projection(id: ::core::primitive::u32) -> Self { diff --git a/examples/triangle/src/main.rs b/examples/triangle/src/main.rs index aebde95..0beb2f7 100644 --- a/examples/triangle/src/main.rs +++ b/examples/triangle/src/main.rs @@ -24,12 +24,10 @@ async fn run() -> Result<(), Error> { const THIRD: f32 = consts::TAU / 3.; #[derive(Group)] - struct Offset<'a> { - r: &'a Uniform, - } + struct Offset<'a>(&'a Uniform); let triangle = |Index(idx): Index, Groups(offset): Groups| { - let [x, y] = sl::thunk(sl::f32(idx) * THIRD + offset.r); + let [x, y] = sl::thunk(sl::f32(idx) * THIRD + offset.0); Out { place: sl::vec4(sl::cos(x), sl::sin(y), 0., 1.), color: COLOR, @@ -43,7 +41,7 @@ async fn run() -> Result<(), Error> { let uniform = cx.make_uniform(r); let bind = { let mut binder = cx.make_binder(&shader); - let offset = Offset { r: &uniform }; + let offset = Offset(&uniform); binder.bind(&offset); binder.into_binding() };