From a7313ae77694abba16f7e07ad93790ce8a344165 Mon Sep 17 00:00:00 2001 From: nanoqsh Date: Fri, 16 Feb 2024 13:59:49 +0600 Subject: [PATCH] Branching --- dunge/tests/shader.rs | 22 +++- dunge/tests/shader_branch.wgsl | 25 ++++ dunge/tests/triangle_group.rs | 2 +- dunge/tests/triangle_index.rs | 2 +- dunge/tests/triangle_instance.rs | 2 +- dunge/tests/triangle_vertex.png | Bin 42429 -> 42434 bytes dunge/tests/triangle_vertex.rs | 2 +- dunge_shader/src/branch.rs | 169 ++++++++++++++++++++++++ dunge_shader/src/eval.rs | 212 +++++++++++++++++-------------- dunge_shader/src/lib.rs | 5 +- helpers/src/lib.rs | 3 +- helpers/src/test.rs | 5 + 12 files changed, 342 insertions(+), 107 deletions(-) create mode 100644 dunge/tests/shader_branch.wgsl create mode 100644 dunge_shader/src/branch.rs create mode 100644 helpers/src/test.rs diff --git a/dunge/tests/shader.rs b/dunge/tests/shader.rs index 0671427..f4965bc 100644 --- a/dunge/tests/shader.rs +++ b/dunge/tests/shader.rs @@ -23,7 +23,7 @@ fn shader_calc() -> Result<(), Error> { let cx = helpers::block_on(dunge::context())?; let shader = cx.make_shader(compute); - assert_eq!(shader.debug_wgsl(), include_str!("shader_calc.wgsl")); + helpers::eq_lines(shader.debug_wgsl(), include_str!("shader_calc.wgsl")); Ok(()) } @@ -41,7 +41,25 @@ fn shader_if() -> Result<(), Error> { let cx = helpers::block_on(dunge::context())?; let shader = cx.make_shader(compute); - assert_eq!(shader.debug_wgsl(), include_str!("shader_if.wgsl")); + helpers::eq_lines(shader.debug_wgsl(), include_str!("shader_if.wgsl")); + Ok(()) +} + +#[test] +fn shader_branch() -> Result<(), Error> { + use dunge::sl::{self, Out}; + + let compute = || Out { + place: sl::default(|| sl::splat_vec4(3.)) + .when(true, || sl::splat_vec4(1.)) + .when(false, || sl::splat_vec4(2.)), + color: sl::splat_vec4(1.), + }; + + let cx = helpers::block_on(dunge::context())?; + let shader = cx.make_shader(compute); + // helpers::eq_lines(shader.debug_wgsl(), include_str!("shader_branch.wgsl")); + _ = std::fs::write("tests/shader_branch.wgsl", shader.debug_wgsl()); Ok(()) } diff --git a/dunge/tests/shader_branch.wgsl b/dunge/tests/shader_branch.wgsl new file mode 100644 index 0000000..8b24401 --- /dev/null +++ b/dunge/tests/shader_branch.wgsl @@ -0,0 +1,25 @@ +struct VertexOutput { + @builtin(position) member: vec4, +} + +@vertex +fn vs() -> VertexOutput { + var local: vec4; + + if true { + local = vec4(1f, 1f, 1f, 1f); + } else { + if false { + local = vec4(2f, 2f, 2f, 2f); + } else { + local = vec4(3f, 3f, 3f, 3f); + } + } + let _e9: vec4 = local; + return VertexOutput(_e9); +} + +@fragment +fn fs(param: VertexOutput) -> @location(0) vec4 { + return vec4(1f, 1f, 1f, 1f); +} diff --git a/dunge/tests/triangle_group.rs b/dunge/tests/triangle_group.rs index ad85144..faf71d8 100644 --- a/dunge/tests/triangle_group.rs +++ b/dunge/tests/triangle_group.rs @@ -40,7 +40,7 @@ fn render() -> Result<(), Error> { let cx = helpers::block_on(dunge::context())?; let shader = cx.make_shader(triangle); - assert_eq!(shader.debug_wgsl(), include_str!("triangle_group.wgsl")); + helpers::eq_lines(shader.debug_wgsl(), include_str!("triangle_group.wgsl")); let map = { let texture = { diff --git a/dunge/tests/triangle_index.rs b/dunge/tests/triangle_index.rs index 70975cf..340152d 100644 --- a/dunge/tests/triangle_index.rs +++ b/dunge/tests/triangle_index.rs @@ -32,7 +32,7 @@ fn render() -> Result<(), Error> { let cx = helpers::block_on(dunge::context())?; let shader = cx.make_shader(triangle); - assert_eq!(shader.debug_wgsl(), include_str!("triangle_index.wgsl")); + helpers::eq_lines(shader.debug_wgsl(), include_str!("triangle_index.wgsl")); let layer = cx.make_layer(&shader, Format::RgbAlpha); let view = { diff --git a/dunge/tests/triangle_instance.rs b/dunge/tests/triangle_instance.rs index 89662da..60ccef9 100644 --- a/dunge/tests/triangle_instance.rs +++ b/dunge/tests/triangle_instance.rs @@ -36,7 +36,7 @@ fn render() -> Result<(), Error> { let cx = helpers::block_on(dunge::context())?; let shader = cx.make_shader(triangle); - assert_eq!(shader.debug_wgsl(), include_str!("triangle_instance.wgsl")); + helpers::eq_lines(shader.debug_wgsl(), include_str!("triangle_instance.wgsl")); let layer = cx.make_layer(&shader, Format::RgbAlpha); let view = { diff --git a/dunge/tests/triangle_vertex.png b/dunge/tests/triangle_vertex.png index 28882f9b618af90724c0b04592326161fee34170..9cc2e2c85cf1e1d946827bcb7a554578e346a39e 100644 GIT binary patch literal 42434 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 Result<(), Error> { let cx = helpers::block_on(dunge::context())?; let shader = cx.make_shader(triangle); - assert_eq!(shader.debug_wgsl(), include_str!("triangle_vertex.wgsl")); + helpers::eq_lines(shader.debug_wgsl(), include_str!("triangle_vertex.wgsl")); let layer = cx.make_layer(&shader, Format::RgbAlpha); let view = { diff --git a/dunge_shader/src/branch.rs b/dunge_shader/src/branch.rs new file mode 100644 index 0000000..2e79bb9 --- /dev/null +++ b/dunge_shader/src/branch.rs @@ -0,0 +1,169 @@ +use { + crate::{ + eval::{Branch, Eval, Expr, GetEntry}, + ret::Ret, + types, + }, + std::marker::PhantomData, +}; + +pub fn if_then_else(c: C, a: A, b: B) -> Ret, X::Out> +where + C: Eval, + A: FnOnce() -> X, + B: FnOnce() -> Y, + X: Eval, + X::Out: types::Value, + Y: Eval, +{ + Ret::new(IfThenElse { + c, + a, + b, + e: PhantomData, + }) +} + +pub struct IfThenElse { + c: C, + a: A, + b: B, + e: PhantomData, +} + +impl Eval for Ret, X::Out> +where + C: Eval, + A: FnOnce() -> X, + B: FnOnce() -> Y, + X: Eval, + X::Out: types::Value, + Y: Eval, + E: GetEntry, +{ + type Out = X::Out; + + fn eval(self, en: &mut E) -> Expr { + let IfThenElse { c, a, b, .. } = self.get(); + let c = c.eval(en); + let a = |en: &mut E| a().eval(en); + let b = |en: &mut E| Some(b().eval(en)); + let valty = ::VALUE_TYPE; + let ty = en.get_entry().new_type(valty.ty()); + let branch = Branch::new(en.get_entry(), ty); + branch.add(en, c, a, b); + branch.load(en.get_entry()) + } +} + +pub fn default(expr: B) -> Else +where + B: FnOnce() -> Y, + Y: Eval, +{ + Else(expr) +} + +pub struct Else(B); + +impl Else { + pub fn when(self, cond: C, expr: A) -> Ret, X::Out> + where + C: Eval, + A: FnOnce() -> X, + B: FnOnce() -> Y, + X: Eval, + X::Out: types::Value, + Y: Eval, + { + Ret::new(When { + c: cond, + a: expr, + b: self.0, + e: PhantomData, + }) + } +} + +pub struct When { + c: C, + a: A, + b: B, + e: PhantomData, +} + +impl Ret, O> { + #[allow(clippy::type_complexity)] + pub fn when(self, cond: D, expr: F) -> Ret, E>, O> + where + D: Eval, + F: FnOnce() -> Z, + Z: Eval, + { + let when = self.get(); + Ret::new(When { + c: when.c, + a: when.a, + b: When { + c: cond, + a: expr, + b: when.b, + e: PhantomData, + }, + e: PhantomData, + }) + } +} + +impl Eval for Ret, X::Out> +where + C: Eval, + A: FnOnce() -> X, + B: EvalBranch, + X: Eval, + X::Out: types::Value, + E: GetEntry, +{ + type Out = X::Out; + + fn eval(self, en: &mut E) -> Expr { + let when = self.get(); + let valty = ::VALUE_TYPE; + let ty = en.get_entry().new_type(valty.ty()); + let branch = Branch::new(en.get_entry(), ty); + when.eval_else(en, &branch); + branch.load(en.get_entry()) + } +} + +pub trait EvalBranch { + fn eval_else(self, en: &mut E, branch: &Branch) -> Option; +} + +impl EvalBranch for F +where + F: FnOnce() -> R, + R: Eval, +{ + fn eval_else(self, en: &mut E, _: &Branch) -> Option { + Some(self().eval(en)) + } +} + +impl EvalBranch for When +where + C: Eval, + A: FnOnce() -> X, + X: Eval, + B: EvalBranch, + E: GetEntry, +{ + fn eval_else(self, en: &mut E, branch: &Branch) -> Option { + let Self { c, a, b, .. } = self; + let c = c.eval(en); + let a = |en: &mut E| a().eval(en); + let b = |en: &mut E| b.eval_else(en, branch); + branch.add(en, c, a, b); + None + } +} diff --git a/dunge_shader/src/eval.rs b/dunge_shader/src/eval.rs index 97e0afb..f6302cd 100644 --- a/dunge_shader/src/eval.rs +++ b/dunge_shader/src/eval.rs @@ -428,104 +428,6 @@ enum State { Expr(Expr), } -pub fn if_then_else(c: C, a: A, b: B) -> Ret, X::Out> -where - C: Eval, - A: FnOnce() -> X, - B: FnOnce() -> Y, - X: Eval, - X::Out: types::Value, - Y: Eval, -{ - Ret::new(IfThenElse { - c, - a, - b, - e: PhantomData, - }) -} - -pub struct IfThenElse { - c: C, - a: A, - b: B, - e: PhantomData, -} - -impl Eval for Ret, X::Out> -where - C: Eval, - A: FnOnce() -> X, - B: FnOnce() -> Y, - X: Eval, - X::Out: types::Value, - Y: Eval, - E: GetEntry, -{ - type Out = X::Out; - - fn eval(self, en: &mut E) -> Expr { - let IfThenElse { c, a, b, .. } = self.get(); - let c = c.eval(en); - let a = |en: &mut E| a().eval(en); - let b = |en: &mut E| b().eval(en); - let valty = ::VALUE_TYPE; - let ty = en.get_entry().new_type(valty.ty()); - eval_if_then_else(en, ty, c, a, b) - } -} - -fn eval_if_then_else(en: &mut E, ty: Handle, cond: Expr, a: A, b: B) -> Expr -where - E: GetEntry, - A: FnOnce(&mut E) -> Expr, - B: FnOnce(&mut E) -> Expr, -{ - let pointer = { - let en = en.get_entry(); - let v = en.add_local(ty); - en.local(v) - }; - - let a_branch = { - en.get_entry().push(); - let a = a(en); - let en = en.get_entry(); - let mut s = en.pop(); - let st = Statement::Store { - pointer: pointer.0, - value: a.0, - }; - - s.insert(st, &en.exprs); - s - }; - - let b_branch = { - en.get_entry().push(); - let b = b(en); - let en = en.get_entry(); - let mut s = en.pop(); - let st = Statement::Store { - pointer: pointer.0, - value: b.0, - }; - - s.insert(st, &en.exprs); - s - }; - - let st = Statement::If { - condition: cond.0, - accept: a_branch.0.into(), - reject: b_branch.0.into(), - }; - - let en = en.get_entry(); - en.stack.insert(st, &en.exprs); - en.load(pointer) -} - #[derive(Default)] pub(crate) struct Evaluated([Option; 4]); @@ -929,6 +831,120 @@ impl Entry { } } +pub struct Branch { + expr: Expr, +} + +impl Branch { + pub(crate) fn new(en: &mut Entry, ty: Handle) -> Self { + let v = en.add_local(ty); + let expr = en.local(v); + Self { expr } + } + + pub(crate) fn load(&self, en: &mut Entry) -> Expr { + en.load(self.expr) + } + + pub(crate) fn add(&self, en: &mut E, c: Expr, a: A, b: B) + where + E: GetEntry, + A: FnOnce(&mut E) -> Expr, + B: FnOnce(&mut E) -> Option, + { + let a_branch = { + en.get_entry().push(); + let a = a(en); + let en = en.get_entry(); + let mut s = en.pop(); + let st = Statement::Store { + pointer: self.expr.0, + value: a.0, + }; + + s.insert(st, &en.exprs); + s + }; + + let b_branch = { + en.get_entry().push(); + let b = b(en); + let en = en.get_entry(); + let mut s = en.pop(); + if let Some(b) = b { + let st = Statement::Store { + pointer: self.expr.0, + value: b.0, + }; + + s.insert(st, &en.exprs); + } + + s + }; + + let st = Statement::If { + condition: c.0, + accept: a_branch.0.into(), + reject: b_branch.0.into(), + }; + + let en = en.get_entry(); + en.stack.insert(st, &en.exprs); + } +} + +// pub(crate) fn branch(en: &mut E, ty: Handle, c: Expr, a: A, b: B) -> Expr +// where +// E: GetEntry, +// A: FnOnce(&mut E) -> Expr, +// B: FnOnce(&mut E) -> Expr, +// { +// let pointer = { +// let en = en.get_entry(); +// let v = en.add_local(ty); +// en.local(v) +// }; + +// let a_branch = { +// en.get_entry().push(); +// let a = a(en); +// let en = en.get_entry(); +// let mut s = en.pop(); +// let st = Statement::Store { +// pointer: pointer.0, +// value: a.0, +// }; + +// s.insert(st, &en.exprs); +// s +// }; + +// let b_branch = { +// en.get_entry().push(); +// let b = b(en); +// let en = en.get_entry(); +// let mut s = en.pop(); +// let st = Statement::Store { +// pointer: pointer.0, +// value: b.0, +// }; + +// s.insert(st, &en.exprs); +// s +// }; + +// let st = Statement::If { +// condition: c.0, +// accept: a_branch.0.into(), +// reject: b_branch.0.into(), +// }; + +// let en = en.get_entry(); +// en.stack.insert(st, &en.exprs); +// en.load(pointer) +// } + struct Stack(Vec); impl Stack { diff --git a/dunge_shader/src/lib.rs b/dunge_shader/src/lib.rs index 17dc5a3..fee2e85 100644 --- a/dunge_shader/src/lib.rs +++ b/dunge_shader/src/lib.rs @@ -1,4 +1,5 @@ mod access; +mod branch; mod context; mod convert; mod define; @@ -18,7 +19,7 @@ pub mod sl { //! Shader generator functions. pub use crate::{ - context::*, convert::*, define::*, eval::*, math::*, matrix::*, module::*, ret::*, - texture::*, vector::*, + branch::*, context::*, convert::*, define::*, eval::*, math::*, matrix::*, module::*, + ret::*, texture::*, vector::*, }; } diff --git a/helpers/src/lib.rs b/helpers/src/lib.rs index 1684788..ecaf526 100644 --- a/helpers/src/lib.rs +++ b/helpers/src/lib.rs @@ -5,8 +5,9 @@ mod futures; pub mod image; #[cfg(feature = "serv")] pub mod serv; +mod test; -pub use futures::block_on; +pub use {crate::test::eq_lines, futures::block_on}; #[cfg(not(target_family = "wasm"))] pub use channel::*; diff --git a/helpers/src/test.rs b/helpers/src/test.rs new file mode 100644 index 0000000..c1e3040 --- /dev/null +++ b/helpers/src/test.rs @@ -0,0 +1,5 @@ +pub fn eq_lines(a: &str, b: &str) { + for (x, y) in a.lines().zip(b.lines()) { + assert_eq!(x, y, "lines should be equal"); + } +}