├── .gitignore ├── .travis.yml ├── .vscode └── settings.json ├── Cargo.toml ├── LICENSE-MIT ├── README.md ├── appveyor.yml ├── rasen-dsl ├── Cargo.toml ├── README.md ├── benches │ └── exec.rs ├── build.rs ├── codegen │ ├── defs.rs │ ├── functions.rs │ ├── math.rs │ ├── mod.rs │ ├── mul.rs │ ├── operations.rs │ └── types.rs ├── src │ ├── context │ │ ├── execute.rs │ │ ├── mod.rs │ │ └── parse.rs │ ├── lib.rs │ ├── module.rs │ ├── operations.rs │ ├── prelude.rs │ ├── types.rs │ └── value.rs └── tests │ ├── build.rs │ └── exec.rs ├── rasen-plugin ├── Cargo.toml ├── README.md ├── benches │ └── graph.rs ├── src │ └── lib.rs └── tests │ ├── graph.rs │ └── snapshots │ ├── graph__basic-plugin.frag.snap │ ├── graph__basic-plugin.vert.snap │ └── graph__functions.snap ├── rasen ├── Cargo.toml ├── README.md ├── benches │ └── build.rs ├── build.rs ├── src │ ├── builder │ │ ├── function.rs │ │ ├── mod.rs │ │ └── module.rs │ ├── errors.rs │ ├── graph.rs │ ├── lib.rs │ ├── module.rs │ ├── node.rs │ ├── operations │ │ ├── flow.rs │ │ ├── glsl.rs │ │ ├── math.rs │ │ └── mod.rs │ ├── prelude.rs │ └── types.rs └── tests │ ├── build.rs │ ├── loop.rs │ └── snapshots │ ├── build__basic.frag.snap │ ├── build__basic.vert.snap │ ├── build__functions.snap │ └── loop__loop.frag.snap ├── rustfmt.toml └── tests ├── dsl.rs ├── graph.rs ├── plugin.rs └── update.rs /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.iml 3 | tests/*.spv 4 | target 5 | Cargo.lock 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Set up the Rust toolchain. 2 | language: rust 3 | matrix: 4 | include: 5 | # - rust: stable 6 | # - rust: beta 7 | - rust: nightly 8 | 9 | # Since this build doesn't require sudo, we can turn it off and improve build 10 | # performance. 11 | sudo: false 12 | 13 | # Force third-party crates to persist from previous builds and update only when 14 | # needed. 15 | cache: cargo 16 | 17 | # Run tests and benchmarks. 18 | script: 19 | - cargo test 20 | - cargo bench 21 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "prettier.tabWidth": 4, 3 | "rust.clippy_preference": "on", 4 | "rust.goto_def_racer_fallback": true, 5 | "rust.unstable_features": true, 6 | "editor.formatOnSave": true 7 | } 8 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "rasen", 4 | "rasen-dsl", 5 | "rasen-plugin", 6 | ] 7 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Rasen 2 | ================ 3 | 4 | [![crates.io](https://img.shields.io/crates/v/rasen.svg)](https://crates.io/crates/rasen/) 5 | [![AppVeyor Status](https://ci.appveyor.com/api/projects/status/github/leops/rasen?svg=true)](https://ci.appveyor.com/project/leops/rasen) 6 | [![Travis Status](https://travis-ci.org/leops/rasen.svg?branch=master)](https://travis-ci.org/leops/rasen/) 7 | 8 | # What is Rasen ? 9 | 10 | Rasen is an umbrella project for a bunch of SPIR-V and Rust related experiments. The core idea of Rasen is to 11 | provide a fast and safe compiler for SPIR-V modules, however the core compiler only provide a backend to generate 12 | a shader program from an intermediate representation, specifically a "dataflow graph". In traditional, text-based 13 | languages such a graph is a usually a metadata generated by the compiler, but some game engine (eg. Unreal) provide 14 | an editor to directly edit such a graph, which proved to be a very useful way of quickly prototyping materials. 15 | 16 | In practice, Rasen as a whole is designed with two (seemingly unrelated) use case in mind: 17 | 18 | 1. To be used as an online shader compiler embedded in a game engine (or any other application doing 3D rendering). 19 | Rasen aims to be fast enough to recompile a shader within a frame's budget, and the resulting SPIR-V bytecode can 20 | then be retargeted to GLSL, HLSL or MSL using SPIRV-Cross to accomodate the current platform. 21 | 2. To provide a fallback environment for compute shaders in Rust. Rasen uses a special DSL crate to parse Rust code 22 | "from the inside" and generate a dataflow graph for execution on the GPU, but on constrained platforms where this 23 | capability is unavailable the Rust code is still valid and can be run on the CPU instead (and ultimately should be 24 | just as fast as raw rust code). As a sidenote, this should also help making shaders more testable and debuggable as 25 | they can be now be run on the CPU too. 26 | 27 | # Core 28 | 29 | The `rasen` crate contains the core graph compiler itself. It provides graph building utilities (the `Graph` struct), 30 | various types (`rasen::types::*`) and operations (`rasen::node::*`) definitions, and SPIR-V compilation utilities (the 31 | `ModuleBuilder` struct). 32 | 33 | The API is intentionally low-level, as the use case for the core compiler is to act as a backend for a graph-based 34 | material editor in a game engine. It's perfectly possible to use this crate as-is by creating a `Graph` struct and 35 | building the module node-by-node, however this method tends to be quite verbose for "static" shaders: 36 | 37 | ```rust 38 | extern crate rasen; 39 | 40 | use rasen::prelude::*; 41 | 42 | fn main() { 43 | let mut graph = Graph::new(); 44 | 45 | // A vec3 input at location 0 46 | let normal = graph.add_node(Node::Input(0, TypeName::VEC3, VariableName::Named(String::from("a_normal")))); 47 | 48 | // Some ambient light constants 49 | let min_light = graph.add_node(Node::Constant(TypedValue::Float(0.1))); 50 | let max_light = graph.add_node(Node::Constant(TypedValue::Float(1.0))); 51 | let light_dir = graph.add_node(Node::Constant(TypedValue::Vec3(0.3, -0.5, 0.2))); 52 | 53 | // The Material color (also a constant) 54 | let mat_color = graph.add_node(Node::Constant(TypedValue::Vec4(0.25, 0.625, 1.0, 1.0))); 55 | 56 | // Some usual function calls 57 | let normalize = graph.add_node(Node::Normalize); 58 | let dot = graph.add_node(Node::Dot); 59 | let clamp = graph.add_node(Node::Clamp); 60 | let multiply = graph.add_node(Node::Multiply); 61 | 62 | // And a vec4 output at location 0 63 | let color = graph.add_node(Node::Output(0, TypeName::VEC4, VariableName::Named(String::from("o_color")))); 64 | 65 | // Normalize the normal 66 | graph.add_edge(normal, normalize, 0); 67 | 68 | // Compute the dot product of the surface normal and the light direction 69 | graph.add_edge(normalize, dot, 0); 70 | graph.add_edge(light_dir, dot, 1); 71 | 72 | // Restrict the result into the ambient light range 73 | graph.add_edge(dot, clamp, 0); 74 | graph.add_edge(min_light, clamp, 1); 75 | graph.add_edge(max_light, clamp, 2); 76 | 77 | // Multiply the light intensity by the surface color 78 | graph.add_edge(clamp, multiply, 0); 79 | graph.add_edge(mat_color, multiply, 1); 80 | 81 | // Write the result to the output 82 | graph.add_edge(multiply, color, 0); 83 | 84 | let bytecode = build_program(&graph, ShaderType::Fragment).unwrap(); 85 | // bytecode is now a Vec you can pass to Vulkan to create the shader module 86 | } 87 | ``` 88 | 89 | # DSL 90 | 91 | To reduce the amount of boilerplate, the `rasen_dsl` crate provides a bunch of utility function to write shaders as 92 | perfectly valid Rust code: 93 | 94 | ```rust 95 | extern crate rasen; 96 | extern crate rasen_dsl; 97 | 98 | use rasen_dsl::prelude::*; 99 | 100 | fn main() { 101 | let shader = Module::new(); 102 | 103 | let normal: Value = normalize(shader.input(0, "a_normal")); 104 | let light = vec3(0.3, -0.5, 0.2); 105 | let color = vec4(0.25, 0.625, 1.0, 1.0); 106 | 107 | let res = clamp(dot(normal, light), 0.1f32, 1.0f32) * color; 108 | shader.output(0, "o_color", res); 109 | 110 | let bytecode = shader.build(ShaderType::Fragment).unwrap(); 111 | // bytecode is now a Vec you can pass to Vulkan to create the shader module 112 | } 113 | ``` 114 | 115 | This crate is even more experimental than the Rasen compiler itself, it already provides all the features exposed by 116 | the compiler but they might not be completely spec compliant (for instance the typings constraint on the various GLSL 117 | functions may be more, or less strict than required by the OpenGL specification). 118 | 119 | Ultimately, the goal for the DSL crate (beside being a statically-checked equivalent of the graph builder) is to expose 120 | an API to test the execution of a shader on the CPU, with all the debugging tools that such an environment provides. The 121 | library currently provides all the conversion primitives to turn your scalar / vectors / matrices into Value<\_> types to 122 | test your program, however most GLSL operations are left unimplemented. 123 | 124 | # Plugin 125 | 126 | Finally, the `rasen_plugin` crate is a compiler plugin exposing a few utility macro and attributes to make writing 127 | shaders in Rust event easier: 128 | 129 | ```rust 130 | use rasen_dsl::prelude::*; 131 | 132 | #[rasen(module)] 133 | pub fn basic_vert(a_pos: Value, projection: Value, view: Value, model: Value) -> Value { 134 | let mvp = projection * view * model; 135 | mvp * vec4!(a_pos, 1.0f32) 136 | } 137 | ``` 138 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # Set up the build environment. 2 | environment: 3 | matrix: 4 | - channel: nightly 5 | target: x86_64-pc-windows-msvc 6 | 7 | # Set up the Rust toolchain. 8 | install: 9 | - set PATH=C:\Program Files\Git\mingw64\bin;%PATH% 10 | - curl -fsS "https://win.rustup.rs/" -o rustup-init.exe 11 | - rustup-init.exe -y --default-toolchain %channel%-%target% 12 | - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin 13 | - del /f rasen\benches\data 14 | - mklink /J rasen\benches\data rasen\tests\data 15 | - del /f rasen-dsl\tests\data 16 | - mklink /J rasen-dsl\tests\data rasen\tests\data 17 | - del /f rasen-dsl\benches\data 18 | - mklink /J rasen-dsl\benches\data rasen\tests\data 19 | - rustc -V 20 | - cargo -V 21 | 22 | # Force third-party crates to persist from previous builds and update only when 23 | # needed. 24 | cache: 25 | - .cargo -> rust-%channel%-date, cargo-%channel%-build 26 | 27 | # Since this is not a .NET project, we can flip the build system off. 28 | build: false 29 | 30 | # Run tests and benchmarks. 31 | test_script: 32 | - cargo test 33 | - cargo bench 34 | -------------------------------------------------------------------------------- /rasen-dsl/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rasen-dsl" 3 | version = "0.2.0" 4 | authors = ["l3ops "] 5 | description = "Wrapper for the Rasen compiler to write SPIR-V shaders in Rust" 6 | documentation = "https://docs.rs/rasen-dsl/" 7 | homepage = "https://github.com/leops/rasen#readme" 8 | repository = "https://github.com/leops/rasen" 9 | readme = "README.md" 10 | license = "MIT" 11 | build = "build.rs" 12 | keywords = [ 13 | "SPIRV", 14 | "Vulkan", 15 | "GLSL", 16 | ] 17 | categories = [ 18 | "development-tools", 19 | "rendering", 20 | ] 21 | 22 | [dependencies] 23 | rasen = { version = "0.12.0", path = "../rasen" } 24 | 25 | [build-dependencies] 26 | syn = "0.15.15" 27 | quote = "0.6.8" 28 | proc-macro2 = "0.4.20" 29 | 30 | [dev-dependencies] 31 | pretty_assertions = "0.5.1" 32 | rspirv = "0.5.4" 33 | -------------------------------------------------------------------------------- /rasen-dsl/README.md: -------------------------------------------------------------------------------- 1 | rasen-dsl 2 | ================ 3 | 4 | The `rasen_dsl` crate provides a bunch of utility function to write shaders as perfectly valid Rust code: 5 | ```rust 6 | extern crate rasen; 7 | extern crate rasen_dsl; 8 | 9 | use rasen_dsl::prelude::*; 10 | 11 | fn main() { 12 | let shader = Module::new(); 13 | 14 | let normal: Value = normalize(shader.input(0, "a_normal")); 15 | let light = vec3(0.3, -0.5, 0.2); 16 | let color = vec4(0.25, 0.625, 1.0, 1.0); 17 | 18 | let res = clamp(dot(normal, light), 0.1f32, 1.0f32) * color; 19 | shader.output(0, "o_color", res); 20 | 21 | let bytecode = shader.build(ShaderType::Fragment).unwrap(); 22 | // bytecode is now a Vec you can pass to Vulkan to create the shader module 23 | } 24 | ``` 25 | 26 | This crate is even more experimental than the Rasen compiler itself but it already provides all the features exposed by 27 | the compiler. 28 | 29 | Ultimately, the goal for the DSL crate (beside being a statically-checked equivalent of the graph builder) is to expose 30 | an API to test the execution of a shader on the CPU, with all the debugging tools that such an environment provides. The 31 | library currently provides all the conversion primitives to turn your scalar / vectors / matrices into Value<_> types to 32 | test your program, however most GLSL operations are left unimplemented. 33 | -------------------------------------------------------------------------------- /rasen-dsl/benches/exec.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate rasen; 4 | extern crate rasen_dsl; 5 | extern crate test; 6 | 7 | use rasen_dsl::prelude::*; 8 | use std::f32::consts::PI; 9 | use test::Bencher; 10 | 11 | include!("../../tests/dsl.rs"); 12 | 13 | #[bench] 14 | fn bench_run_basic_frag(b: &mut Bencher) { 15 | b.iter(|| { 16 | basic_frag( 17 | vec3(0.0f32, 1.0f32, 0.0f32), 18 | vec2(0.0f32, 0.0f32), 19 | Value::of(Sampler(Vec4([0.25f32, 0.625f32, 1.0f32, 1.0f32]))), 20 | ); 21 | }); 22 | } 23 | 24 | #[bench] 25 | fn bench_run_basic_vert(b: &mut Bencher) { 26 | b.iter(|| { 27 | let a_pos = vec3(1.0f32, 2.0f32, 3.0f32); 28 | let a_normal = vec3(0.0f32, 1.0f32, 0.0f32); 29 | let a_uv = vec2(0.5f32, 0.5f32); 30 | 31 | #[rustfmt::skip] 32 | let projection = Mat4([ 33 | 1.0, 0.0, 0.0, 0.0, 34 | 0.0, 1.0, 0.0, 0.0, 35 | 0.0, 0.0, 1.0, 0.0, 36 | 0.0, 0.0, 0.0, 1.0 37 | ]); 38 | #[rustfmt::skip] 39 | let view = Mat4([ 40 | 1.0, 0.0, 0.0, 0.0, 41 | 0.0, 1.0, 0.0, 0.0, 42 | 0.0, 0.0, 1.0, 0.0, 43 | 0.0, 0.0, 0.0, 1.0 44 | ]); 45 | #[rustfmt::skip] 46 | let model = Mat4([ 47 | 1.0, 0.0, 0.0, 0.0, 48 | 0.0, 1.0, 0.0, 0.0, 49 | 0.0, 0.0, 1.0, 0.0, 50 | 0.0, 0.0, 0.0, 1.0 51 | ]); 52 | 53 | basic_vert( 54 | a_pos, 55 | a_normal, 56 | a_uv, 57 | Value::of(projection), 58 | Value::of(view), 59 | Value::of(model), 60 | ) 61 | }); 62 | } 63 | 64 | #[bench] 65 | fn bench_run_functions(b: &mut Bencher) { 66 | b.iter(|| { 67 | functions(Value::of(PI)); 68 | }); 69 | } 70 | -------------------------------------------------------------------------------- /rasen-dsl/build.rs: -------------------------------------------------------------------------------- 1 | //! Internal procedural macro provider for the rasen-dsl crate 2 | 3 | #![recursion_limit = "256"] 4 | #![warn(clippy::all, clippy::pedantic)] 5 | 6 | extern crate syn; 7 | #[macro_use] 8 | extern crate quote; 9 | extern crate proc_macro2; 10 | 11 | use proc_macro2::TokenStream; 12 | use std::{env, fmt::Write as FmtWrite, fs::File, io::Write, path::Path}; 13 | 14 | mod codegen; 15 | use codegen::*; 16 | 17 | struct Files { 18 | types: Vec, 19 | operations: Vec, 20 | container: Vec, 21 | context: Vec, 22 | parse: Vec, 23 | execute: Vec, 24 | module: Vec, 25 | } 26 | 27 | impl Files { 28 | fn create() -> Self { 29 | Files { 30 | types: Vec::new(), 31 | operations: Vec::new(), 32 | container: Vec::new(), 33 | context: Vec::new(), 34 | parse: Vec::new(), 35 | execute: Vec::new(), 36 | module: Vec::new(), 37 | } 38 | } 39 | 40 | fn write(self) { 41 | let out_dir = env::var("OUT_DIR").unwrap(); 42 | 43 | { 44 | let path = Path::new(&out_dir).join("types.rs"); 45 | let mut file = File::create(&path).unwrap(); 46 | 47 | let tokens = self.types; 48 | write_tokens(&mut file, quote! { #( #tokens )* }); 49 | } 50 | 51 | { 52 | let path = Path::new(&out_dir).join("operations.rs"); 53 | let mut file = File::create(&path).unwrap(); 54 | 55 | let tokens = self.operations; 56 | write_tokens(&mut file, quote! { #( #tokens )* }); 57 | } 58 | 59 | { 60 | let path = Path::new(&out_dir).join("container.rs"); 61 | let mut file = File::create(&path).unwrap(); 62 | 63 | let tokens = self.container; 64 | write_tokens( 65 | &mut file, 66 | quote! { 67 | pub trait Container { 68 | type Value: Copy; 69 | #( #tokens )* 70 | 71 | fn sample(sampler: Value>, uv: Value) -> Value 72 | where Self: Container> + Container + Container; 73 | } 74 | }, 75 | ); 76 | } 77 | 78 | { 79 | let path = Path::new(&out_dir).join("context.rs"); 80 | let mut file = File::create(&path).unwrap(); 81 | 82 | let tokens = self.context; 83 | write_tokens( 84 | &mut file, 85 | quote! { 86 | pub trait Context: Container> + #( #tokens )+* {} 87 | }, 88 | ); 89 | } 90 | 91 | { 92 | let path = Path::new(&out_dir).join("parse.rs"); 93 | let mut file = File::create(&path).unwrap(); 94 | 95 | let tokens = self.parse; 96 | write_tokens( 97 | &mut file, 98 | quote! { 99 | impl Container for Parse { 100 | type Value = ParseNode; 101 | #( #tokens )* 102 | 103 | fn sample(sampler: Value>, uv: Value) -> Value { 104 | with_graph(|graph| { 105 | let node = graph.add_node(Node::Sample); 106 | graph.add_edge(sampler.0, node, 0); 107 | graph.add_edge(uv.0, node, 1); 108 | Value(node) 109 | }) 110 | } 111 | } 112 | }, 113 | ); 114 | } 115 | 116 | { 117 | let path = Path::new(&out_dir).join("execute.rs"); 118 | let mut file = File::create(&path).unwrap(); 119 | 120 | let tokens = self.execute; 121 | write_tokens( 122 | &mut file, 123 | quote! { 124 | impl Container for Execute { 125 | type Value = T; 126 | #( #tokens )* 127 | 128 | #[inline] 129 | fn sample(sampler: Value>, _uv: Value) -> Value { 130 | Value((sampler.0).0) 131 | } 132 | } 133 | }, 134 | ); 135 | } 136 | 137 | { 138 | let path = Path::new(&out_dir).join("module.rs"); 139 | let mut file = File::create(&path).unwrap(); 140 | 141 | let tokens = self.module; 142 | write_tokens(&mut file, quote! { #( #tokens )* }); 143 | } 144 | } 145 | } 146 | 147 | fn write_tokens(file: &mut File, tokens: TokenStream) { 148 | let mut line = String::new(); 149 | write!(line, "{}", tokens).unwrap(); 150 | 151 | writeln!(file, "{}", { 152 | line.chars() 153 | .flat_map(|chr| match chr { 154 | '{' => vec!['{', '\n'], 155 | ';' => vec![';', '\n'], 156 | '}' => vec!['\n', '}'], 157 | any => vec![any], 158 | }) 159 | .collect::() 160 | }) 161 | .unwrap(); 162 | } 163 | 164 | /// Create the declarations of all the GLSL type structs 165 | fn decl_types(files: &mut Files) { 166 | for tokens in types::type_structs() { 167 | let [types, container, context, parse, execute] = tokens; 168 | files.types.push(types); 169 | files.container.push(container); 170 | files.context.push(context); 171 | files.parse.push(parse); 172 | files.execute.push(execute); 173 | } 174 | } 175 | 176 | /// Create the declarations of all the GLSL operation functions, 177 | /// and implement the math traits for the GLSL types 178 | fn decl_operations(files: &mut Files) { 179 | for tokens in operations::impl_operations() { 180 | let [container, parse, execute, operations] = tokens; 181 | files.container.push(container); 182 | files.parse.push(parse); 183 | files.execute.push(execute); 184 | files.operations.push(operations); 185 | } 186 | for tokens in math::impl_math() { 187 | let (container, parse, execute, types) = tokens; 188 | files.types.extend(types); 189 | files.container.push(container); 190 | files.parse.push(parse); 191 | files.execute.push(execute); 192 | } 193 | for tokens in functions::impl_fn() { 194 | files.module.push(tokens); 195 | } 196 | } 197 | 198 | fn main() { 199 | let mut files = Files::create(); 200 | decl_types(&mut files); 201 | decl_operations(&mut files); 202 | files.write(); 203 | } 204 | -------------------------------------------------------------------------------- /rasen-dsl/codegen/defs.rs: -------------------------------------------------------------------------------- 1 | //! Type metadata providers 2 | 3 | use proc_macro2::{Ident, Span}; 4 | 5 | const INTS: [(&'static str, &'static str, &'static str); 3] = [ 6 | ("Bool", "b", "bool"), 7 | ("Int", "i", "i32"), 8 | ("UInt", "u", "u32"), 9 | ]; 10 | const FLOATS: [(&'static str, &'static str, &'static str); 2] = 11 | [("Float", "", "f32"), ("Double", "d", "f64")]; 12 | 13 | #[derive(Copy, Clone, Eq, PartialEq)] 14 | pub enum Category { 15 | SCALAR, 16 | VECTOR, 17 | MATRIX, 18 | } 19 | 20 | #[derive(Clone)] 21 | pub struct Type { 22 | pub name: Ident, 23 | pub category: Category, 24 | pub component: Option>, 25 | pub size: Option, 26 | pub ty: &'static str, 27 | } 28 | 29 | fn scalar_type(name: &str, ty: &'static str) -> Type { 30 | Type { 31 | name: Ident::new(&name, Span::call_site()), 32 | category: Category::SCALAR, 33 | component: None, 34 | size: None, 35 | ty: ty, 36 | } 37 | } 38 | 39 | fn vector_type(name: &str, scalar: &str, ty: &'static str, size: u32) -> Type { 40 | Type { 41 | name: Ident::new(&name, Span::call_site()), 42 | category: Category::VECTOR, 43 | component: Some(Box::new(scalar_type(scalar, ty))), 44 | size: Some(size), 45 | ty: ty, 46 | } 47 | } 48 | 49 | fn matrix_type(name: &str, vec: &str, scalar: &str, ty: &'static str, size: u32) -> Type { 50 | Type { 51 | name: Ident::new(&name, Span::call_site()), 52 | category: Category::MATRIX, 53 | component: Some(Box::new(vector_type(vec, scalar, ty, size))), 54 | size: Some(size), 55 | ty: ty, 56 | } 57 | } 58 | 59 | pub fn all_types() -> Vec { 60 | let mut res = Vec::new(); 61 | 62 | for &(res_name, _, ty) in INTS.iter().chain(FLOATS.iter()) { 63 | res.push(scalar_type(res_name, ty)); 64 | } 65 | 66 | for size in 2u32..=4u32 { 67 | for &(scalar_name, prefix, ty) in &INTS { 68 | let vec_name = format!("{}Vec{}", prefix.to_string().to_uppercase(), size); 69 | res.push(vector_type(&vec_name, scalar_name, ty, size)); 70 | } 71 | 72 | for &(scalar_name, prefix, ty) in &FLOATS { 73 | let vec_name = format!("{}Vec{}", prefix.to_string().to_uppercase(), size); 74 | res.push(vector_type(&vec_name, scalar_name, ty, size)); 75 | 76 | let mat_name = format!("{}Mat{}", prefix.to_string().to_uppercase(), size); 77 | res.push(matrix_type(&mat_name, &vec_name, scalar_name, ty, size)); 78 | } 79 | } 80 | 81 | res 82 | } 83 | 84 | #[derive(Clone)] 85 | pub struct Node { 86 | pub name: Ident, 87 | pub args: Option>, 88 | pub result: Type, 89 | } 90 | -------------------------------------------------------------------------------- /rasen-dsl/codegen/functions.rs: -------------------------------------------------------------------------------- 1 | //! Fn trait implementations 2 | 3 | use proc_macro2::{Ident, Literal, Span, TokenStream}; 4 | 5 | pub fn impl_fn() -> Vec { 6 | (1..9u32) 7 | .map(|size| { 8 | let generics: Vec<_> = (0..size).map(|index| Ident::new(&format!("T{}", index), Span::call_site())).collect(); 9 | let values: Vec<_> = generics.iter().map(|ty| quote! { Value, }).collect(); 10 | let types: Vec<_> = generics.iter().map(|ty| quote! { #ty: AsTypeName, }).collect(); 11 | let containers: Vec<_> = generics.iter().map(|ty| quote! { Container<#ty, Value = ParseNode> }).collect(); 12 | 13 | let create: Vec<_> = generics.iter() 14 | .enumerate() 15 | .map(|(index, ty)| { 16 | let index = index as u32; 17 | quote! { 18 | with_graph(|graph| Value(graph.add_node(Node::Parameter(#index, #ty::TYPE_NAME)))), 19 | } 20 | }) 21 | .collect(); 22 | 23 | let edges: Vec<_> = (0..size) 24 | .map(|index| { 25 | let field = Literal::u32_unsuffixed(index); 26 | quote! { 27 | graph.add_edge((self.#field).0, node, #index); 28 | } 29 | }) 30 | .collect(); 31 | 32 | quote! { 33 | impl<#( #generics ),*> FnArgs for (#( #values )*) where #( #types )* Parse: #( #containers )+* { 34 | fn create() -> Self { 35 | ( #( #create )* ) 36 | } 37 | 38 | fn call(self, func: FunctionRef) -> ParseNode { 39 | with_graph(|graph| { 40 | let node = graph.add_node(Node::Call(func)); 41 | #( #edges )* 42 | node 43 | }) 44 | } 45 | } 46 | } 47 | }) 48 | .collect() 49 | } 50 | -------------------------------------------------------------------------------- /rasen-dsl/codegen/math.rs: -------------------------------------------------------------------------------- 1 | //! Mul trait implementation 2 | 3 | use codegen::{ 4 | defs::{all_types, Category, Type}, 5 | mul::impl_mul_variant, 6 | }; 7 | use proc_macro2::{Ident, Punct, Spacing, Span, TokenStream}; 8 | 9 | fn impl_math_variant( 10 | (trait_id, _, operator): (Ident, Ident, Punct), 11 | left_res: Type, 12 | right_res: Type, 13 | ) -> Option { 14 | let (mut result, op_impl) = match ( 15 | left_res.category, 16 | left_res.ty, 17 | right_res.category, 18 | right_res.ty, 19 | ) { 20 | (_, "bool", _, _) 21 | | (_, _, _, "bool") 22 | | (Category::MATRIX, _, _, _) 23 | | (_, _, Category::MATRIX, _) 24 | | (Category::SCALAR, _, Category::SCALAR, _) => return None, 25 | 26 | (lc, lt, rc, rt) if lc == rc && lt == rt && left_res.size == right_res.size => ( 27 | left_res.name.clone(), 28 | match lc { 29 | Category::MATRIX => unreachable!(), 30 | Category::SCALAR => quote! { 31 | self #operator rhs 32 | }, 33 | Category::VECTOR => { 34 | let result = left_res.name.clone(); 35 | let l_fields: Vec<_> = { 36 | (0..left_res.size.unwrap()) 37 | .map(|i| Ident::new(&format!("l_{}", i), Span::call_site())) 38 | .collect() 39 | }; 40 | let r_fields: Vec<_> = { 41 | (0..left_res.size.unwrap()) 42 | .map(|i| Ident::new(&format!("r_{}", i), Span::call_site())) 43 | .collect() 44 | }; 45 | let res_fields: Vec<_> = { 46 | l_fields 47 | .iter() 48 | .zip(r_fields.iter()) 49 | .map(|(l_f, r_f)| { 50 | quote! { #l_f #operator #r_f } 51 | }) 52 | .collect() 53 | }; 54 | 55 | quote! { 56 | let #result([ #( #l_fields ),* ]) = self; 57 | let #result([ #( #r_fields ),* ]) = rhs; 58 | #result([ #( #res_fields ),* ]) 59 | } 60 | } 61 | }, 62 | ), 63 | 64 | _ => return None, 65 | }; 66 | 67 | let left_type = left_res.name.clone(); 68 | let right_type = right_res.name.clone(); 69 | let method = Ident::new(&trait_id.to_string().to_lowercase(), Span::call_site()); 70 | 71 | if left_type.to_string() == result.to_string() { 72 | result = Ident::new("Self", Span::call_site()); 73 | } 74 | 75 | Some(quote! { 76 | impl #trait_id<#right_type> for #left_type { 77 | type Output = #result; 78 | 79 | #[inline] 80 | fn #method(self, rhs: #right_type) -> Self::Output { 81 | #op_impl 82 | } 83 | } 84 | }) 85 | } 86 | 87 | const MATH_OPS: [(&str, &str, char); 5] = [ 88 | ("Add", "Add", '+'), 89 | ("Sub", "Subtract", '-'), 90 | ("Mul", "Multiply", '*'), 91 | ("Div", "Divide", '/'), 92 | ("Rem", "Modulus", '%'), 93 | ]; 94 | 95 | pub fn impl_math() -> Vec<(TokenStream, TokenStream, TokenStream, Vec)> { 96 | MATH_OPS 97 | .into_iter() 98 | .map(|&(trait_name, node, operator)| { 99 | let lower = Ident::new(&trait_name.to_string().to_lowercase(), Span::call_site()); 100 | let trait_id = Ident::new(trait_name, Span::call_site()); 101 | let node_id = Ident::new(node, Span::call_site()); 102 | let op = Punct::new(operator, Spacing::Alone); 103 | 104 | ( 105 | quote! { 106 | fn #lower(lhs: Value, rhs: Value) -> Value 107 | where 108 | T: #trait_id, 109 | T::Output: Copy, 110 | Self: Container + Container; 111 | }, 112 | quote! { 113 | fn #lower(lhs: Value, rhs: Value) -> Value 114 | where 115 | T: #trait_id, 116 | T::Output: Copy, 117 | { 118 | with_graph(|graph| { 119 | let node = graph.add_node(Node::#node_id); 120 | graph.add_edge(lhs.0, node, 0); 121 | graph.add_edge(rhs.0, node, 1); 122 | Value(node) 123 | }) 124 | } 125 | }, 126 | quote! { 127 | fn #lower(lhs: Value, rhs: Value) -> Value 128 | where 129 | T: #trait_id, 130 | T::Output: Copy, 131 | { 132 | Value(lhs.0 #op rhs.0) 133 | } 134 | }, 135 | 136 | all_types() 137 | .into_iter() 138 | .flat_map(|left_type| { 139 | all_types() 140 | .into_iter() 141 | .filter_map(|right_type| { 142 | if trait_name == "Mul" { 143 | impl_mul_variant( 144 | left_type.clone(), 145 | right_type.clone(), 146 | ) 147 | } else { 148 | impl_math_variant( 149 | ( 150 | Ident::new(&trait_name, Span::call_site()), 151 | Ident::new(&node, Span::call_site()), 152 | Punct::new(operator, Spacing::Alone), 153 | ), 154 | left_type.clone(), 155 | right_type.clone(), 156 | ) 157 | } 158 | }) 159 | .collect::>() 160 | }) 161 | .collect() 162 | ) 163 | }) 164 | .collect() 165 | } 166 | -------------------------------------------------------------------------------- /rasen-dsl/codegen/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod defs; 2 | pub mod functions; 3 | pub mod types; 4 | pub mod operations; 5 | pub mod mul; 6 | pub mod math; 7 | -------------------------------------------------------------------------------- /rasen-dsl/codegen/mul.rs: -------------------------------------------------------------------------------- 1 | //! Mul trait implementation 2 | 3 | use codegen::defs::{Category, Type}; 4 | use proc_macro2::{Ident, Span, TokenStream}; 5 | 6 | fn impl_vector_times_scalar( 7 | result: Ident, 8 | size: u32, 9 | vector: TokenStream, 10 | scalar: TokenStream, 11 | ) -> TokenStream { 12 | let v_fields: Vec<_> = { 13 | (0..size) 14 | .map(|i| Ident::new(&format!("v_{}", i), Span::call_site())) 15 | .collect() 16 | }; 17 | let res_fields: Vec<_> = { 18 | v_fields 19 | .iter() 20 | .map(|f| { 21 | quote! { #f * other } 22 | }) 23 | .collect() 24 | }; 25 | 26 | quote! { 27 | let #result([ #( #v_fields ),* ]) = #vector; 28 | let other = #scalar; 29 | #result([ #( #res_fields ),* ]) 30 | } 31 | } 32 | 33 | fn impl_vector_times_matrix( 34 | result: Ident, 35 | size: u32, 36 | vector: TokenStream, 37 | matrix: TokenStream, 38 | ) -> TokenStream { 39 | let v_fields = { (0..size).map(|i| Ident::new(&format!("v_{}", i), Span::call_site())) }; 40 | let res_fields = { 41 | (0..size).map(|i| { 42 | let sum = { 43 | (0..size).map(|j| { 44 | let f = Ident::new(&format!("v_{}", j), Span::call_site()); 45 | let index = ((i * size) + j) as usize; 46 | quote! { #f * matrix[#index] } 47 | }) 48 | }; 49 | 50 | quote! { #( #sum )+* } 51 | }) 52 | }; 53 | 54 | quote! { 55 | let #result([ #( #v_fields ),* ]) = #vector; 56 | let matrix = #matrix; 57 | #result([ #( #res_fields ),* ]) 58 | } 59 | } 60 | 61 | pub fn impl_mul_variant(left_res: Type, right_res: Type) -> Option { 62 | let (result, mul_impl) = match ( 63 | left_res.category, 64 | left_res.ty, 65 | right_res.category, 66 | right_res.ty, 67 | ) { 68 | (_, "bool", _, _) | (_, _, _, "bool") | 69 | (Category::SCALAR, _, Category::SCALAR, _) => return None, 70 | 71 | (lc, lt, rc, rt) if lc == rc && lt == rt && left_res.size == right_res.size => ( 72 | left_res.name.clone(), 73 | match lc { 74 | Category::SCALAR => { 75 | quote! { 76 | (self * rhs) 77 | } 78 | } 79 | Category::VECTOR => { 80 | let result = left_res.name.clone(); 81 | let l_fields: Vec<_> = { 82 | (0..left_res.size.unwrap()) 83 | .map(|i| Ident::new(&format!("l_{}", i), Span::call_site())) 84 | .collect() 85 | }; 86 | let r_fields: Vec<_> = { 87 | (0..left_res.size.unwrap()) 88 | .map(|i| Ident::new(&format!("r_{}", i), Span::call_site())) 89 | .collect() 90 | }; 91 | let res_fields: Vec<_> = { 92 | l_fields 93 | .iter() 94 | .zip(r_fields.iter()) 95 | .map(|(l_f, r_f)| { 96 | quote! { #l_f * #r_f } 97 | }) 98 | .collect() 99 | }; 100 | 101 | quote! { 102 | let #result([ #( #l_fields ),* ]) = self; 103 | let #result([ #( #r_fields ),* ]) = rhs; 104 | #result([ #( #res_fields ),* ]) 105 | } 106 | } 107 | Category::MATRIX => { 108 | let result = left_res.name.clone(); 109 | let size = left_res.size.unwrap() as usize; 110 | let res_fields: Vec<_> = { 111 | (0..size) 112 | .flat_map(|i| { 113 | (0..size).map(move |j| { 114 | let sum = { 115 | (0..size).map(|k| { 116 | let l = i * size + k; 117 | let r = k * size + j; 118 | quote! { left_mat[#l] * right_mat[#r] } 119 | }) 120 | }; 121 | 122 | quote! { #( #sum )+* } 123 | }) 124 | }) 125 | .collect() 126 | }; 127 | 128 | quote! { 129 | let left_mat = self.0; 130 | let right_mat = rhs.0; 131 | #result([ #( #res_fields, )* ]) 132 | } 133 | } 134 | }, 135 | ), 136 | 137 | (Category::VECTOR, lt, rc, rt) 138 | if lt == rt && left_res.size.unwrap() == right_res.size.or(left_res.size).unwrap() => 139 | { 140 | ( 141 | left_res.name.clone(), 142 | match rc { 143 | Category::VECTOR => unreachable!(), 144 | Category::SCALAR => impl_vector_times_scalar( 145 | left_res.name.clone(), 146 | left_res.size.unwrap(), 147 | quote! { self }, 148 | quote! { rhs }, 149 | ), 150 | Category::MATRIX => impl_vector_times_matrix( 151 | left_res.name.clone(), 152 | left_res.size.unwrap(), 153 | quote! { self }, 154 | quote! { rhs.0 }, 155 | ), 156 | }, 157 | ) 158 | } 159 | (lc, lt, Category::VECTOR, rt) 160 | if lt == rt && right_res.size.unwrap() == left_res.size.or(right_res.size).unwrap() => 161 | { 162 | ( 163 | right_res.name.clone(), 164 | match lc { 165 | Category::VECTOR => unreachable!(), 166 | Category::SCALAR => impl_vector_times_scalar( 167 | right_res.name.clone(), 168 | right_res.size.unwrap(), 169 | quote! { rhs }, 170 | quote! { self }, 171 | ), 172 | Category::MATRIX => impl_vector_times_matrix( 173 | right_res.name.clone(), 174 | right_res.size.unwrap(), 175 | quote! { rhs }, 176 | quote! { self.0 }, 177 | ), 178 | }, 179 | ) 180 | } 181 | 182 | _ => return None, 183 | }; 184 | 185 | let left_type = left_res.name.clone(); 186 | let right_type = right_res.name.clone(); 187 | 188 | Some(quote! { 189 | impl Mul<#right_type> for #left_type { 190 | type Output = #result; 191 | 192 | #[inline] 193 | fn mul(self, rhs: #right_type) -> Self::Output { 194 | #mul_impl 195 | } 196 | } 197 | }) 198 | } 199 | -------------------------------------------------------------------------------- /rasen-dsl/codegen/types.rs: -------------------------------------------------------------------------------- 1 | //! GLSL Types declarations 2 | 3 | use codegen::{ 4 | defs::{all_types, Category, Type}, 5 | }; 6 | use proc_macro2::{Ident, Span, TokenStream}; 7 | 8 | fn type_scalar(name: &Ident, kind: &'static str) -> [TokenStream; 5] { 9 | let upper = Ident::new(&name.to_string().to_uppercase(), Span::call_site()); 10 | let lower = Ident::new(&name.to_string().to_lowercase(), Span::call_site()); 11 | let ty = Ident::new(kind, Span::call_site()); 12 | 13 | let mut traits = Vec::new(); 14 | 15 | match kind { 16 | "bool" => {}, 17 | 18 | "i32" => { 19 | traits.push(quote! { 20 | impl GenType for #name { 21 | #[inline] 22 | fn zero() -> Self { 0 } 23 | #[inline] 24 | fn one() -> Self { 1 } 25 | #[inline] 26 | fn min(self, rhs: Self) -> Self { std::cmp::Ord::min(self, rhs) } 27 | #[inline] 28 | fn max(self, rhs: Self) -> Self { std::cmp::Ord::max(self, rhs) } 29 | } 30 | 31 | impl Numerical for #name { 32 | #[inline] 33 | fn pow(self, rhs: Self) -> Self { self.pow(std::convert::TryInto::try_into(rhs).unwrap()) } 34 | } 35 | }); 36 | }, 37 | "u32" => { 38 | traits.push(quote! { 39 | impl GenType for #name { 40 | #[inline] 41 | fn zero() -> Self { 0 } 42 | #[inline] 43 | fn one() -> Self { 1 } 44 | #[inline] 45 | fn min(self, rhs: Self) -> Self { std::cmp::Ord::min(self, rhs) } 46 | #[inline] 47 | fn max(self, rhs: Self) -> Self { std::cmp::Ord::max(self, rhs) } 48 | } 49 | 50 | impl Numerical for #name { 51 | #[inline] 52 | fn pow(self, rhs: Self) -> Self { self.pow(rhs) } 53 | } 54 | }); 55 | }, 56 | 57 | _ => { 58 | traits.push(quote! { 59 | impl GenType for #name { 60 | #[inline] 61 | fn zero() -> Self { 0.0 } 62 | #[inline] 63 | fn one() -> Self { 1.0 } 64 | #[inline] 65 | fn min(self, rhs: Self) -> Self { self.min(rhs) } 66 | #[inline] 67 | fn max(self, rhs: Self) -> Self { self.max(rhs) } 68 | } 69 | 70 | impl Numerical for #name { 71 | #[inline] 72 | fn pow(self, rhs: Self) -> Self { self.powf(rhs) } 73 | } 74 | 75 | impl Floating for #name { 76 | #[inline] 77 | fn sqrt(self) -> Self { self.sqrt() } 78 | #[inline] 79 | fn floor(self) -> Self { self.floor() } 80 | #[inline] 81 | fn ceil(self) -> Self { self.ceil() } 82 | #[inline] 83 | fn round(self) -> Self { self.round() } 84 | #[inline] 85 | fn sin(self) -> Self { self.sin() } 86 | #[inline] 87 | fn cos(self) -> Self { self.cos() } 88 | #[inline] 89 | fn tan(self) -> Self { self.tan() } 90 | #[inline] 91 | fn ln(self) -> Self { self.ln() } 92 | #[inline] 93 | fn abs(self) -> Self { self.abs() } 94 | } 95 | }); 96 | } 97 | } 98 | 99 | let value = quote! { 100 | pub type #name = #ty; 101 | 102 | impl AsTypeName for #name { 103 | const TYPE_NAME: &'static TypeName = TypeName::#upper; 104 | } 105 | 106 | impl> IntoValue for #name { 107 | type Output = #name; 108 | 109 | #[inline] 110 | fn into_value(self) -> Value { 111 | C::#lower(self) 112 | } 113 | } 114 | 115 | #( #traits )* 116 | }; 117 | 118 | let container = quote! { 119 | fn #lower(value: #name) -> Value 120 | where 121 | Self: Container<#name>; 122 | }; 123 | let context = quote! { 124 | Container<#name> 125 | }; 126 | let parse = quote! { 127 | fn #lower(value: #name) -> Value { 128 | with_graph(|graph| Value(graph.add_node(Node::Constant(TypedValue::#name(value))))) 129 | } 130 | }; 131 | let execute = quote! { 132 | #[inline] 133 | fn #lower(value: #name) -> Value { 134 | Value(value) 135 | } 136 | }; 137 | 138 | [value, container, context, parse, execute] 139 | } 140 | 141 | fn type_vector( 142 | name: &Ident, 143 | ty: &'static str, 144 | component: Option>, 145 | size: Option, 146 | ) -> [TokenStream; 5] { 147 | let component = component.unwrap(); 148 | let comp = component.name.clone(); 149 | let size = size.unwrap() as usize; 150 | 151 | let kind = ty; 152 | let ty = Ident::new(ty, Span::call_site()); 153 | 154 | let lower = Ident::new(&name.to_string().to_lowercase(), Span::call_site()); 155 | let upper = Ident::new(&name.to_string().to_uppercase(), Span::call_site()); 156 | 157 | let args1: Vec<_> = (0..size) 158 | .map(|i| Ident::new(&format!("arg_{}", i), Span::call_site())) 159 | .collect(); 160 | let args2 = args1.clone(); 161 | 162 | let parse_edges: Vec<_> = args1.iter() 163 | .enumerate() 164 | .map(|(index, ident)| { 165 | let ident = ident.clone(); 166 | let index = index as u32; 167 | quote! { graph.add_edge(#ident.0, node, #index); } 168 | }) 169 | .collect(); 170 | 171 | let container_args: Vec<_> = { 172 | args1 173 | .iter() 174 | .map(|name| { 175 | let comp = comp.clone(); 176 | quote! { #name: Value } 177 | }) 178 | .collect() 179 | }; 180 | let arg_list: Vec<_> = { 181 | args1 182 | .iter() 183 | .map(|name| { 184 | let comp = comp.clone(); 185 | quote! { #name: impl IntoValue } 186 | }) 187 | .collect() 188 | }; 189 | {} 190 | 191 | let mut traits = Vec::new(); 192 | 193 | if kind != "bool" { 194 | let values: Vec<_> = (0..size).map(|_| quote! { v }).collect(); 195 | 196 | let self1: Vec<_> = (0..size) 197 | .map(|i| { 198 | let i = i as usize; 199 | quote! { self.0[#i] } 200 | }) 201 | .collect(); 202 | let rhs1: Vec<_> = (0..size) 203 | .map(|i| { 204 | let i = i as usize; 205 | quote! { rhs.0[#i] } 206 | }) 207 | .collect(); 208 | 209 | let self2 = self1.clone(); 210 | let rhs2 = rhs1.clone(); 211 | 212 | traits.push(quote! { 213 | impl GenType for #name { 214 | #[inline] 215 | fn zero() -> Self { 216 | Self::spread(#ty::zero()) 217 | } 218 | #[inline] 219 | fn one() -> Self { 220 | Self::spread(#ty::one()) 221 | } 222 | #[inline] 223 | fn min(self, rhs: Self) -> Self { 224 | #name([ #( GenType::min(#self1, #rhs1) ),* ]) 225 | } 226 | #[inline] 227 | fn max(self, rhs: Self) -> Self { 228 | #name([ #( GenType::max(#self2, #rhs2) ),* ]) 229 | } 230 | } 231 | 232 | impl Vector for #name { 233 | type Scalar = #ty; 234 | 235 | #[inline] 236 | fn spread(v: #ty) -> Self { 237 | #name([ #( #values ),* ]) 238 | } 239 | } 240 | }); 241 | 242 | if kind != "i32" && kind != "u32" { 243 | let rhs_fields1: Vec<_> = (0..size) 244 | .map(|index| { 245 | let index = index as usize; 246 | quote! { rhs.0[#index] } 247 | }) 248 | .collect(); 249 | 250 | let fields1: Vec<_> = (0..size) 251 | .map(|index| { 252 | let index = index as usize; 253 | quote! { self.0[#index] } 254 | }) 255 | .collect(); 256 | 257 | let fields2 = fields1.clone(); 258 | let fields3 = fields1.clone(); 259 | 260 | if size == 3 { 261 | traits.push(quote! { 262 | impl Vector3 for #name { 263 | fn cross(&self, rhs: &Self) -> Self { 264 | let #name(arg_0) = self; 265 | let #name(arg_1) = rhs; 266 | 267 | #name([ 268 | arg_0[1] * arg_1[2] - arg_1[1] * arg_0[2], 269 | arg_0[2] * arg_1[0] - arg_1[2] * arg_0[0], 270 | arg_0[0] * arg_1[1] - arg_1[0] * arg_0[1], 271 | ]) 272 | } 273 | } 274 | }); 275 | } 276 | 277 | traits.push(quote! { 278 | impl VectorFloating for #name { 279 | fn normalize(&self) -> Self { 280 | let length = self.length(); 281 | #name([ #( #fields1 / length ),* ]) 282 | } 283 | 284 | fn dot(&self, rhs: &Self) -> Self::Scalar { 285 | #( #rhs_fields1 * #fields2 )+* 286 | } 287 | 288 | fn length_squared(&self) -> Self::Scalar { 289 | #( #fields3.powi(2) )+* 290 | } 291 | } 292 | }); 293 | } 294 | } 295 | 296 | let value = quote! { 297 | #[derive(Copy, Clone, Debug)] 298 | pub struct #name( pub [ #ty ; #size ] ); 299 | 300 | impl AsTypeName for #name { 301 | const TYPE_NAME: &'static TypeName = TypeName::#upper; 302 | } 303 | 304 | impl Index for #name { 305 | type Output = #ty; 306 | 307 | fn index(&self, index: u32) -> &Self::Output { 308 | &self.0[index as usize] 309 | } 310 | } 311 | 312 | #( #traits )* 313 | 314 | #[inline] 315 | pub fn #lower( #( #arg_list ),* ) -> Value { 316 | >::#lower( #( #args1.into_value() ),* ) 317 | } 318 | }; 319 | 320 | let parse_args = container_args.clone(); 321 | let execute_args = container_args.clone(); 322 | 323 | let container = quote! { 324 | fn #lower( #( #container_args ),* ) -> Value 325 | where 326 | Self: Container<#comp> + Container<#name>; 327 | }; 328 | let context = quote! { 329 | Container<#name> 330 | }; 331 | 332 | let parse = quote! { 333 | fn #lower( #( #parse_args ),* ) -> Value { 334 | with_graph(|graph| { 335 | let node = graph.add_node(Node::Construct(TypeName::#upper)); 336 | #( #parse_edges )* 337 | Value(node) 338 | }) 339 | } 340 | }; 341 | let execute = quote! { 342 | #[inline] 343 | fn #lower( #( #execute_args ),* ) -> Value { 344 | Value(#name([ #( #args2.0 ),* ])) 345 | } 346 | }; 347 | 348 | [value, container, context, parse, execute] 349 | } 350 | 351 | fn type_matrix( 352 | name: &Ident, 353 | ty: &'static str, 354 | component: Option>, 355 | size: Option, 356 | ) -> [TokenStream; 5] { 357 | let vector = component.unwrap(); 358 | let comp = vector.name.clone(); 359 | let size = size.unwrap(); 360 | 361 | let ty = Ident::new(ty, Span::call_site()); 362 | let mat_size = (size * size) as usize; 363 | 364 | let lower = Ident::new(&name.to_string().to_lowercase(), Span::call_site()); 365 | let upper = Ident::new(&name.to_string().to_uppercase(), Span::call_site()); 366 | 367 | let args: Vec<_> = (0..size) 368 | .map(|i| Ident::new(&format!("arg_{}", i), Span::call_site())) 369 | .collect(); 370 | 371 | let parse_edges: Vec<_> = args.iter() 372 | .enumerate() 373 | .map(|(index, ident)| { 374 | let ident = ident.clone(); 375 | let index = index as u32; 376 | quote! { graph.add_edge(#ident.0, node, #index); } 377 | }) 378 | .collect(); 379 | 380 | let execute_unwrap: Vec<_> = args.iter() 381 | .map(|ident| { 382 | let ident = quote! { (#ident.0).0 }; 383 | let items: Vec<_> = (0..(size as usize)) 384 | .map(|i| quote! { #ident[#i] }) 385 | .collect(); 386 | 387 | quote! { #( #items ),* } 388 | }) 389 | .collect(); 390 | 391 | let container_args: Vec<_> = { 392 | args 393 | .iter() 394 | .map(|name| { 395 | let comp = comp.clone(); 396 | quote! { #name: Value } 397 | }) 398 | .collect() 399 | }; 400 | let arg_list: Vec<_> = { 401 | args 402 | .iter() 403 | .map(|name| { 404 | let comp = comp.clone(); 405 | quote! { #name: impl IntoValue } 406 | }) 407 | .collect() 408 | }; 409 | {} 410 | 411 | let value = quote! { 412 | #[derive(Copy, Clone, Debug)] 413 | pub struct #name(pub [ #ty ; #mat_size ]); 414 | 415 | impl AsTypeName for #name { 416 | const TYPE_NAME: &'static TypeName = TypeName::#upper; 417 | } 418 | 419 | impl Index for #name { 420 | type Output = #ty; 421 | 422 | fn index(&self, index: u32) -> &Self::Output { 423 | &self.0[index as usize] 424 | } 425 | } 426 | 427 | impl Matrix for #name { 428 | fn inverse(self) -> Self { 429 | unimplemented!() 430 | } 431 | } 432 | 433 | #[inline] 434 | pub fn #lower( #( #arg_list ),* ) -> Value { 435 | >::#lower( #( #args.into_value() ),* ) 436 | } 437 | }; 438 | 439 | let parse_args = container_args.clone(); 440 | let execute_args = container_args.clone(); 441 | 442 | let container = quote! { 443 | fn #lower( #( #container_args ),* ) -> Value 444 | where 445 | Self: Container<#comp> + Container<#name>; 446 | }; 447 | let context = quote! { 448 | Container<#name> 449 | }; 450 | 451 | let parse = quote! { 452 | fn #lower( #( #parse_args ),* ) -> Value { 453 | with_graph(|graph| { 454 | let node = graph.add_node(Node::Construct(TypeName::#upper)); 455 | #( #parse_edges )* 456 | Value(node) 457 | }) 458 | } 459 | }; 460 | let execute = quote! { 461 | #[inline] 462 | fn #lower( #( #execute_args ),* ) -> Value { 463 | Value(#name([ #( #execute_unwrap ),* ])) 464 | } 465 | }; 466 | 467 | [value, container, context, parse, execute] 468 | } 469 | 470 | pub fn type_structs() -> Vec<[TokenStream; 5]> { 471 | all_types().iter() 472 | .filter_map(|ty| { 473 | let Type { name, category, ty, component, size, .. } = ty.clone(); 474 | let decl = match category { 475 | Category::SCALAR => type_scalar(&name, ty), 476 | Category::VECTOR => type_vector(&name, ty, component, size), 477 | Category::MATRIX => type_matrix(&name, ty, component, size), 478 | }; 479 | 480 | Some(decl) 481 | }) 482 | .collect() 483 | } 484 | -------------------------------------------------------------------------------- /rasen-dsl/src/context/execute.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Add, Sub, Mul, Div, Rem, Index}; 2 | 3 | use crate::{context::{Container, Context}, value::Value, types::*}; 4 | 5 | pub enum Execute {} 6 | 7 | include! { 8 | concat!(env!("OUT_DIR"), "/execute.rs") 9 | } 10 | 11 | impl Context for Execute {} 12 | -------------------------------------------------------------------------------- /rasen-dsl/src/context/mod.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Add, Sub, Mul, Div, Rem, Index}; 2 | 3 | use crate::{value::Value, types::*}; 4 | 5 | pub mod parse; 6 | pub mod execute; 7 | 8 | include! { 9 | concat!(env!("OUT_DIR"), "/container.rs") 10 | } 11 | 12 | include! { 13 | concat!(env!("OUT_DIR"), "/context.rs") 14 | } 15 | -------------------------------------------------------------------------------- /rasen-dsl/src/context/parse.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Add, Sub, Mul, Div, Rem, Index}; 2 | 3 | use rasen::prelude::{NodeIndex, Node, TypeName, TypedValue}; 4 | 5 | use crate::{context::{Container, Context}, value::Value, types::*, module::with_graph}; 6 | 7 | pub(crate) type ParseNode = NodeIndex; 8 | 9 | pub enum Parse {} 10 | 11 | include! { 12 | concat!(env!("OUT_DIR"), "/parse.rs") 13 | } 14 | 15 | impl Context for Parse {} 16 | -------------------------------------------------------------------------------- /rasen-dsl/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Exposes a simple DSL for the construction of a data-flow graph for the rasen compiler 2 | //! 3 | //! ``` 4 | //! # extern crate rasen; 5 | //! # extern crate rasen_dsl; 6 | //! # use rasen_dsl::prelude::*; 7 | //! # fn main() { 8 | //! let shader = Module::build(|module| { 9 | //! let normal = normalize(module.input(0, "a_normal")); 10 | //! let light = vec3(0.3f32, -0.5f32, 0.2f32); 11 | //! let color = vec4(0.25f32, 0.625f32, 1.0f32, 1.0f32); 12 | //! 13 | //! let res = clamp(dot(normal, light), 0.1f32, 1.0f32) * color; 14 | //! module.output(0, "o_color", res); 15 | //! }); 16 | //! 17 | //! # #[allow(unused_variables)] 18 | //! let bytecode = build_program(&shader, ShaderType::Fragment).unwrap(); 19 | //! # } 20 | //! ``` 21 | 22 | #![feature(fn_traits, unboxed_closures)] 23 | #![warn(clippy::all, clippy::pedantic)] 24 | 25 | extern crate rasen; 26 | 27 | pub mod context; 28 | pub mod module; 29 | pub mod operations; 30 | pub mod prelude; 31 | pub mod types; 32 | pub mod value; 33 | -------------------------------------------------------------------------------- /rasen-dsl/src/module.rs: -------------------------------------------------------------------------------- 1 | //! Module builder utility 2 | 3 | use std::{ 4 | rc::Rc, 5 | cell::RefCell, 6 | ops::{Fn, FnMut, FnOnce}, 7 | }; 8 | 9 | use crate::{ 10 | context::{ 11 | parse::{Parse, ParseNode}, 12 | Container, 13 | }, 14 | types::AsTypeName, 15 | value::Value, 16 | }; 17 | use rasen::{ 18 | module::FunctionRef, 19 | prelude::{Graph, Module as ModuleImpl, Node, VariableName, BuiltIn}, 20 | }; 21 | 22 | type Shared = Rc>; 23 | type BuilderContext = Option<(Shared, Option)>; 24 | 25 | thread_local! { 26 | static CONTEXT: RefCell = RefCell::new(None); 27 | } 28 | 29 | pub(crate) fn with_graph(thunk: impl FnOnce(&mut Graph) -> T) -> T { 30 | CONTEXT.with(|ctx| { 31 | let ctx = ctx.borrow(); 32 | let (module, func) = ctx 33 | .as_ref() 34 | .expect("Module builder called outside of Module::with"); 35 | 36 | let mut module = module.borrow_mut(); 37 | if let Some(func) = func { 38 | thunk(&mut module[*func]) 39 | } else { 40 | thunk(&mut module.main) 41 | } 42 | }) 43 | } 44 | 45 | fn using_module(push: impl FnOnce(&mut BuilderContext) -> (), code: impl FnOnce() -> (), pop: impl FnOnce(&mut BuilderContext) -> T) -> T { 46 | CONTEXT.with(move |cell| { 47 | let mut ctx = cell.borrow_mut(); 48 | push(&mut ctx as &mut BuilderContext); 49 | }); 50 | 51 | code(); 52 | 53 | CONTEXT.with(move |cell| { 54 | let mut ctx = cell.borrow_mut(); 55 | pop(&mut ctx as &mut BuilderContext) 56 | }) 57 | } 58 | 59 | pub trait IntoVariableName { 60 | fn into_variable_name(self) -> VariableName; 61 | } 62 | 63 | impl IntoVariableName for BuiltIn { 64 | fn into_variable_name(self) -> VariableName { 65 | VariableName::BuiltIn(self) 66 | } 67 | } 68 | 69 | impl IntoVariableName for String { 70 | fn into_variable_name(self) -> VariableName { 71 | VariableName::Named(self) 72 | } 73 | } 74 | 75 | impl<'a> IntoVariableName for &'a str { 76 | fn into_variable_name(self) -> VariableName { 77 | VariableName::Named(self.into()) 78 | } 79 | } 80 | 81 | impl IntoVariableName for Option { 82 | fn into_variable_name(self) -> VariableName { 83 | if let Some(inner) = self { 84 | inner.into_variable_name() 85 | } else { 86 | VariableName::None 87 | } 88 | } 89 | } 90 | 91 | pub struct Module; 92 | 93 | impl Module { 94 | pub fn build(thunk: impl FnOnce(&Self) -> ()) -> ModuleImpl { 95 | let module = Rc::new(RefCell::new( 96 | ModuleImpl::default() 97 | )); 98 | 99 | using_module( 100 | |ctx| { 101 | debug_assert!(ctx.is_none(), "Module::build called recursively"); 102 | *ctx = Some((module, None)); 103 | }, 104 | || { 105 | thunk(&Module); 106 | }, 107 | |ctx| { 108 | let value = ctx.take(); 109 | 110 | let (module, func) = value.expect("Builder is missing in thread local key"); 111 | debug_assert!(func.is_none(), "Module builder unwrapped from function context"); 112 | 113 | let module = Rc::try_unwrap(module).expect("Module builder has live references"); 114 | module.into_inner() 115 | }, 116 | ) 117 | } 118 | 119 | pub fn input(&self, index: u32, name: impl IntoVariableName) -> Value 120 | where 121 | T: AsTypeName, 122 | Parse: Container, 123 | { 124 | with_graph(|graph| { 125 | Value(graph.add_node(Node::Input(index, T::TYPE_NAME, name.into_variable_name()))) 126 | }) 127 | } 128 | 129 | pub fn uniform(&self, index: u32, name: impl IntoVariableName) -> Value 130 | where 131 | T: AsTypeName, 132 | Parse: Container, 133 | { 134 | with_graph(|graph| { 135 | Value(graph.add_node(Node::Uniform(index, T::TYPE_NAME, name.into_variable_name()))) 136 | }) 137 | } 138 | 139 | pub fn output(&self, index: u32, name: impl IntoVariableName, value: Value) 140 | where 141 | T: AsTypeName, 142 | Parse: Container, 143 | { 144 | with_graph(|graph| { 145 | let node = graph.add_node(Node::Output(index, T::TYPE_NAME, name.into_variable_name())); 146 | graph.add_edge(value.0, node, 0); 147 | }); 148 | } 149 | 150 | pub fn function(&self, function: F) -> impl Fn> 151 | where 152 | F: FnOnce>, 153 | A: FnArgs, 154 | Parse: Container, 155 | { 156 | let func = using_module( 157 | |ctx| { 158 | if let Some((module, func)) = ctx { 159 | debug_assert!(func.is_none(), "Cannot build functions recursively"); 160 | let mut module = module.borrow_mut(); 161 | *func = Some(module.add_function()); 162 | } else { 163 | panic!("Function builder called outside of module builder"); 164 | } 165 | }, 166 | || { 167 | let res = function.call_once(A::create()); 168 | with_graph(|graph| { 169 | let node = graph.add_node(Node::Return); 170 | graph.add_edge(res.0, node, 0); 171 | }); 172 | }, 173 | |ctx| { 174 | if let Some((_module, func)) = ctx { 175 | let func = func.take(); 176 | func.expect("Function builder unwrapped outside of function context") 177 | } else { 178 | panic!("Function builder unwrapped outside of module builder") 179 | } 180 | }, 181 | ); 182 | 183 | FnWrapper(move |args: A| -> Value { 184 | Value(args.call(func)) 185 | }) 186 | } 187 | } 188 | 189 | pub trait FnArgs { 190 | fn create() -> Self; 191 | fn call(self, func: FunctionRef) -> ParseNode; 192 | } 193 | 194 | include! { 195 | concat!(env!("OUT_DIR"), "/module.rs") 196 | } 197 | 198 | struct FnWrapper(F); 199 | 200 | impl, A> Fn for FnWrapper { 201 | extern "rust-call" fn call(&self, args: A) -> Self::Output { 202 | (self.0)(args) 203 | } 204 | } 205 | 206 | impl, A> FnMut for FnWrapper { 207 | extern "rust-call" fn call_mut(&mut self, args: A) -> Self::Output { 208 | (self.0)(args) 209 | } 210 | } 211 | 212 | impl, A> FnOnce for FnWrapper { 213 | type Output = F::Output; 214 | 215 | extern "rust-call" fn call_once(self, args: A) -> Self::Output { 216 | (self.0)(args) 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /rasen-dsl/src/operations.rs: -------------------------------------------------------------------------------- 1 | //! Exposes Rust counterparts of common GLSL functions 2 | 3 | use std::{ 4 | cmp::PartialOrd, 5 | ops::{Add, Div, Index, Mul, Sub}, 6 | }; 7 | 8 | use crate::{ 9 | context::{Container, Context}, 10 | types::*, 11 | value::{IntoValue, Value}, 12 | }; 13 | 14 | include! { 15 | concat!(env!("OUT_DIR"), "/operations.rs") 16 | } 17 | 18 | #[inline] 19 | pub fn sample( 20 | sample: impl IntoValue>, 21 | index: impl IntoValue, 22 | ) -> Value 23 | where 24 | C: Context + Container> + Container, 25 | V: Copy, 26 | { 27 | >>::sample(sample.into_value(), index.into_value()) 28 | } 29 | -------------------------------------------------------------------------------- /rasen-dsl/src/prelude.rs: -------------------------------------------------------------------------------- 1 | //! Exports everything you need to have in scope for the DSL to work 2 | 3 | pub use rasen::prelude::*; 4 | 5 | pub use crate::types::*; 6 | pub use crate::context::Context; 7 | pub use crate::value::{Value, IntoValue}; 8 | pub use crate::module::Module; 9 | pub use crate::operations::*; 10 | -------------------------------------------------------------------------------- /rasen-dsl/src/types.rs: -------------------------------------------------------------------------------- 1 | //! Exports Rust counterparts for all the common GLSL types, along with a few marker traits 2 | 3 | use rasen::prelude::{Dim, TypeName}; 4 | 5 | use std::ops::{Add, Div, Index, Mul, Rem, Sub}; 6 | 7 | use crate::{ 8 | context::{Container, Context}, 9 | value::{IntoValue, Value}, 10 | }; 11 | 12 | pub trait AsTypeName { 13 | const TYPE_NAME: &'static TypeName; 14 | } 15 | 16 | pub trait GenType: Copy { 17 | fn zero() -> Self; 18 | fn one() -> Self; 19 | fn min(self, rhs: Self) -> Self; 20 | fn max(self, rhs: Self) -> Self; 21 | } 22 | 23 | pub trait Numerical: GenType { 24 | fn pow(self, rhs: Self) -> Self; 25 | } 26 | 27 | pub trait Floating: Numerical { 28 | fn sqrt(self) -> Self; 29 | fn floor(self) -> Self; 30 | fn ceil(self) -> Self; 31 | fn round(self) -> Self; 32 | fn sin(self) -> Self; 33 | fn cos(self) -> Self; 34 | fn tan(self) -> Self; 35 | fn ln(self) -> Self; 36 | fn abs(self) -> Self; 37 | } 38 | 39 | pub trait Vector: GenType { 40 | type Scalar: Numerical; 41 | fn spread(v: Self::Scalar) -> Self; 42 | } 43 | 44 | pub trait VectorFloating: Vector 45 | where 46 | Self::Scalar: Floating, 47 | { 48 | fn dot(&self, rhs: &Self) -> Self::Scalar; 49 | 50 | fn normalize(&self) -> Self; 51 | fn length_squared(&self) -> Self::Scalar; 52 | 53 | fn length(&self) -> Self::Scalar { 54 | self.length_squared().sqrt() 55 | } 56 | } 57 | 58 | pub trait Vector3: Vector { 59 | fn cross(&self, rhs: &Self) -> Self; 60 | } 61 | 62 | pub trait Matrix { 63 | fn inverse(self) -> Self; 64 | } 65 | 66 | include!(concat!(env!("OUT_DIR"), "/types.rs")); 67 | 68 | #[derive(Copy, Clone, Debug)] 69 | pub struct Sampler(pub V); 70 | 71 | impl AsTypeName for Sampler 72 | where 73 | ::Scalar: AsTypeName, 74 | { 75 | const TYPE_NAME: &'static TypeName = 76 | &TypeName::Sampler(<::Scalar as AsTypeName>::TYPE_NAME, Dim::Dim2D); 77 | } 78 | -------------------------------------------------------------------------------- /rasen-dsl/src/value.rs: -------------------------------------------------------------------------------- 1 | //! Definitions for the Value type 2 | 3 | use std::{ 4 | ops::{Add, Sub, Mul, Div, Rem}, 5 | }; 6 | 7 | use crate::context::{Container, execute::Execute}; 8 | 9 | pub struct Value + ?Sized, T>(pub(crate) C::Value); 10 | 11 | impl, T> Copy for Value where C::Value: Copy {} 12 | 13 | impl, T> Clone for Value 14 | where 15 | C::Value: Clone, 16 | { 17 | fn clone(&self) -> Self { 18 | Value(self.0.clone()) 19 | } 20 | } 21 | 22 | impl Value 23 | where 24 | Execute: Container, 25 | { 26 | pub fn of(value: T) -> Self { 27 | Value(value) 28 | } 29 | 30 | pub fn read(self) -> T { 31 | self.0 32 | } 33 | } 34 | 35 | impl Add> for Value 36 | where 37 | T: Add, 38 | R: Copy, 39 | T::Output: Copy, 40 | C: Container + Container + Container, 41 | { 42 | type Output = Value; 43 | fn add(self, rhs: Value) -> Self::Output { 44 | C::add(self, rhs) 45 | } 46 | } 47 | 48 | impl Sub> for Value 49 | where 50 | T: Sub, 51 | R: Copy, 52 | T::Output: Copy, 53 | C: Container + Container + Container, 54 | { 55 | type Output = Value; 56 | fn sub(self, rhs: Value) -> Self::Output { 57 | C::sub(self, rhs) 58 | } 59 | } 60 | 61 | impl Mul> for Value 62 | where 63 | T: Mul, 64 | R: Copy, 65 | T::Output: Copy, 66 | C: Container + Container + Container, 67 | { 68 | type Output = Value; 69 | fn mul(self, rhs: Value) -> Self::Output { 70 | C::mul(self, rhs) 71 | } 72 | } 73 | 74 | impl Div> for Value 75 | where 76 | T: Div, 77 | R: Copy, 78 | T::Output: Copy, 79 | C: Container + Container + Container, 80 | { 81 | type Output = Value; 82 | fn div(self, rhs: Value) -> Self::Output { 83 | C::div(self, rhs) 84 | } 85 | } 86 | 87 | impl Rem> for Value 88 | where 89 | T: Rem, 90 | R: Copy, 91 | T::Output: Copy, 92 | C: Container + Container + Container, 93 | { 94 | type Output = Value; 95 | fn rem(self, rhs: Value) -> Self::Output { 96 | C::rem(self, rhs) 97 | } 98 | } 99 | 100 | pub trait IntoValue { 101 | type Output; 102 | fn into_value(self) -> Value 103 | where 104 | C: Container; 105 | } 106 | 107 | impl, T> IntoValue for Value { 108 | type Output = T; 109 | 110 | fn into_value(self) -> Value 111 | where 112 | C: Container, 113 | { 114 | self 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /rasen-dsl/tests/build.rs: -------------------------------------------------------------------------------- 1 | extern crate rasen_dsl; 2 | 3 | use rasen_dsl::prelude::*; 4 | 5 | include!("../../tests/dsl.rs"); 6 | 7 | #[test] 8 | fn test_build_basic_vert() { 9 | let _graph = Module::build(|module| { 10 | let (pos, norm, uv) = basic_vert( 11 | module.input(0, "a_pos"), 12 | module.input(1, "a_normal"), 13 | module.input(2, "a_uv"), 14 | module.uniform(0, "projection"), 15 | module.uniform(1, "view"), 16 | module.uniform(2, "model"), 17 | ); 18 | 19 | module.output(0, "v_pos", pos); 20 | module.output(1, "v_norm", norm); 21 | module.output(2, "a_uv", uv); 22 | }); 23 | } 24 | -------------------------------------------------------------------------------- /rasen-dsl/tests/exec.rs: -------------------------------------------------------------------------------- 1 | use std::f32::consts::PI; 2 | 3 | extern crate rasen; 4 | extern crate rasen_dsl; 5 | #[macro_use] 6 | extern crate pretty_assertions; 7 | extern crate rspirv; 8 | 9 | use rasen_dsl::prelude::*; 10 | 11 | include!("../../tests/dsl.rs"); 12 | 13 | #[test] 14 | fn test_run_basic_vert() { 15 | let a_pos = vec3(1.0f32, 2.0f32, 3.0f32); 16 | let a_normal = vec3(0.0f32, 1.0f32, 0.0f32); 17 | let a_uv = vec2(0.5f32, 0.5f32); 18 | 19 | #[rustfmt::skip] 20 | let projection = Mat4([ 21 | 1.0, 0.0, 0.0, 0.0, 22 | 0.0, 1.0, 0.0, 0.0, 23 | 0.0, 0.0, 1.0, 0.0, 24 | 0.0, 0.0, 0.0, 1.0, 25 | ]); 26 | #[rustfmt::skip] 27 | let view = Mat4([ 28 | 1.0, 0.0, 0.0, 0.0, 29 | 0.0, 1.0, 0.0, 0.0, 30 | 0.0, 0.0, 1.0, 0.0, 31 | 0.0, 0.0, 0.0, 1.0, 32 | ]); 33 | #[rustfmt::skip] 34 | let model = Mat4([ 35 | 1.0, 0.0, 0.0, 0.0, 36 | 0.0, 1.0, 0.0, 0.0, 37 | 0.0, 0.0, 1.0, 0.0, 38 | 0.0, 0.0, 0.0, 1.0, 39 | ]); 40 | 41 | let (v_pos, v_norm, v_uv) = basic_vert( 42 | a_pos, 43 | a_normal, 44 | a_uv, 45 | Value::of(projection), 46 | Value::of(view), 47 | Value::of(model), 48 | ); 49 | 50 | let Vec4(v_pos) = v_pos.read(); 51 | assert_eq!(v_pos, [1.0, 2.0, 3.0, 1.0]); 52 | 53 | let Vec4(v_norm) = v_norm.read(); 54 | assert_eq!( 55 | v_norm, 56 | [0.0, 1.0, 0.0, 1.0] 57 | ); 58 | 59 | let Vec2(v_uv) = v_uv.read(); 60 | assert_eq!(v_uv, [0.5, 0.5]); 61 | } 62 | 63 | #[test] 64 | fn test_run_basic_frag() { 65 | let color = basic_frag( 66 | vec3(0.0f32, 1.0f32, 0.0f32), 67 | vec2(0.0f32, 0.0f32), 68 | Value::of(Sampler(Vec4([0.25f32, 0.625f32, 1.0f32, 1.0f32]))), 69 | ); 70 | 71 | let Vec4(color) = color.read(); 72 | assert_eq!( 73 | color, 74 | [0.025, 0.0625, 0.1, 0.1] 75 | ); 76 | } 77 | 78 | #[test] 79 | #[allow(clippy::float_cmp)] 80 | fn test_run_functions() { 81 | let result = functions(Value::of(PI)); 82 | let result = result.read(); 83 | assert_eq!(result, PI); 84 | } 85 | -------------------------------------------------------------------------------- /rasen-plugin/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rasen-plugin" 3 | version = "0.1.0" 4 | authors = ["l3ops "] 5 | 6 | [lib] 7 | plugin = true 8 | 9 | [dependencies] 10 | quote = "0.6.8" 11 | proc-macro2 = "0.4.20" 12 | 13 | [dev-dependencies] 14 | rspirv = "0.5.4" 15 | rasen = { version = "0.12.0", path = "../rasen" } 16 | rasen-dsl = { version = "0.2.0", path = "../rasen-dsl" } 17 | insta = { git = "https://github.com/mitsuhiko/insta", rev="7432f45", version = "0.5.2" } 18 | -------------------------------------------------------------------------------- /rasen-plugin/README.md: -------------------------------------------------------------------------------- 1 | rasen-plugin 2 | ================ 3 | 4 | The `rasen_plugin` crate is a compiler plugin exposing a few utility macro and attributes to make writing 5 | shaders in Rust event easier: 6 | ```rust 7 | use rasen_dsl::prelude::*; 8 | 9 | #[rasen(module)] 10 | pub fn basic_vert(a_pos: Value, projection: Value, view: Value, model: Value) -> Value { 11 | let mvp = projection * view * model; 12 | mvp * vec4!(a_pos, 1.0f32) 13 | } 14 | ``` 15 | -------------------------------------------------------------------------------- /rasen-plugin/benches/graph.rs: -------------------------------------------------------------------------------- 1 | #![feature(test, plugin, custom_attribute)] 2 | #![plugin(rasen_plugin)] 3 | 4 | extern crate rasen; 5 | extern crate rasen_dsl; 6 | extern crate test; 7 | 8 | use rasen_dsl::prelude::*; 9 | use std::f32::consts::PI; 10 | use test::Bencher; 11 | 12 | include!("../../tests/plugin.rs"); 13 | 14 | #[bench] 15 | fn bench_construct_basic_frag(b: &mut Bencher) { 16 | b.iter(basic_frag_module); 17 | } 18 | 19 | #[bench] 20 | fn bench_construct_basic_vert(b: &mut Bencher) { 21 | b.iter(basic_vert_module); 22 | } 23 | 24 | #[bench] 25 | fn bench_call_function(b: &mut Bencher) { 26 | b.iter(|| func(PI.into())); 27 | } 28 | 29 | #[bench] 30 | fn bench_construct_function(b: &mut Bencher) { 31 | b.iter(functions_module); 32 | } 33 | -------------------------------------------------------------------------------- /rasen-plugin/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Compiler plugin providing various syntax extensions to simplify writing shaders with the Rasen DSL 2 | //! 3 | //! # Module wrapper 4 | //! The `rasen(module)` attribute on a function will generate a wrapper function with the `_module` suffix, 5 | //! returning the corresponding Module object. 6 | //! 7 | //! The `rasen(function)` attribute flags a function to be exported when called from a module builder function. 8 | //! Otherwise, due to the way the compiler works on data-flow graph, any function called in Rust will be inlined 9 | //! in the resulting code. 10 | //! 11 | //! ``` 12 | //! #![feature(plugin, custom_attribute)] 13 | //! #![plugin(rasen_plugin)] 14 | //! 15 | //! extern crate rasen; 16 | //! extern crate rasen_dsl; 17 | //! use rasen_dsl::prelude::*; // Some features require the prelude to be imported in scope 18 | //! 19 | //! #[rasen(function)] // This function will be exported to the SPIR-V code 20 | //! fn clamp_light(value: Value) -> Value { 21 | //! clamp(value, 0.1f32, 1.0f32) 22 | //! } 23 | //! 24 | //! #[rasen(module)] // This will create the function basic_frag_module() -> Module 25 | //! fn basic_frag(a_normal: Value) -> Value { 26 | //! let normal = normalize(a_normal); 27 | //! let light = Vec3(0.3, -0.5, 0.2); 28 | //! let color = Vec4(0.25, 0.625, 1.0, 1.0); 29 | //! 30 | //! clamp_light(dot(normal, light)) * color 31 | //! } 32 | //! # basic_frag_module() 33 | //! ``` 34 | //! 35 | //! # Index macro 36 | //! The `idx!` macro allows indexing vector values with the GLSL swizzle syntax: 37 | //! 38 | //! ``` 39 | //! # #![feature(plugin)] 40 | //! # #![plugin(rasen_plugin)] 41 | //! # extern crate rasen; 42 | //! # extern crate rasen_dsl; 43 | //! # use rasen_dsl::prelude::*; 44 | //! # #[rasen(module)] 45 | //! fn swizzle(a_pos: Value, a_color: Value) -> (Value, Value) { 46 | //! let pos = idx!(a_pos, xy); 47 | //! let col = idx!(a_color, rgb); 48 | //! (pos, col) 49 | //! } 50 | //! # swizzle_module() 51 | //! ``` 52 | //! 53 | //! # Vector macros 54 | //! Finally, there is a macro counterpart for all the vector constructor functions. However, the 55 | //! macros are variadic, and behave similarly to their GLSL counterparts. 56 | //! 57 | //! ``` 58 | //! # #![feature(plugin)] 59 | //! # #![plugin(rasen_plugin)] 60 | //! # extern crate rasen; 61 | //! # extern crate rasen_dsl; 62 | //! # use rasen_dsl::prelude::*; 63 | //! # #[rasen(module)] 64 | //! fn constructors(a_xy: Value, a_zw: Value) -> (Value, Value) { 65 | //! let pos = vec3!(a_xy, 1.0f32); 66 | //! let norm = vec4!(a_xy, a_zw); 67 | //! (pos, norm) 68 | //! } 69 | //! # constructors_module() 70 | //! ``` 71 | 72 | #![recursion_limit="256"] 73 | #![feature(plugin_registrar, rustc_private, custom_attribute, box_syntax)] 74 | #![warn(clippy::all, clippy::pedantic)] 75 | 76 | extern crate rustc_plugin; 77 | extern crate syntax; 78 | #[macro_use] extern crate quote; 79 | extern crate proc_macro2; 80 | 81 | use syntax::source_map::{Span, FileName}; 82 | use syntax::symbol::Symbol; 83 | use syntax::ext::build::AstBuilder; 84 | use rustc_plugin::registry::Registry; 85 | use syntax::parse::{self, token::{Token, Lit}}; 86 | use syntax::tokenstream::{TokenStream, TokenTree}; 87 | use syntax::ast::{ 88 | self, Item, ItemKind, 89 | MetaItem, MetaItemKind, NestedMetaItemKind, 90 | FnDecl, FunctionRetTy, TyKind, PatKind, ExprKind, 91 | }; 92 | use syntax::ext::base::{Annotatable, ExtCtxt, SyntaxExtension, MacResult, MacEager, TTMacroExpander}; 93 | use syntax::source_map::edition::Edition; 94 | 95 | use proc_macro2::{Ident, Span as MacroSpan}; 96 | 97 | #[allow(clippy::cast_possible_truncation)] 98 | fn insert_module_wrapper(ecx: &mut ExtCtxt, span: Span, item: Annotatable) -> Vec { 99 | let tokens = if let Annotatable::Item(ref item) = item { 100 | let Item { ident, ref node, .. } = **item; 101 | let fn_name = Ident::new(&format!("{}", ident), MacroSpan::call_site()); 102 | let aux_name = Ident::new(&format!("{}_module", ident), MacroSpan::call_site()); 103 | 104 | if let ItemKind::Fn(ref decl, ..) = *node { 105 | let FnDecl { ref inputs, ref output, .. } = **decl; 106 | 107 | let args: Vec<_> = { 108 | inputs.iter() 109 | .map(|arg| match arg.pat.node { 110 | PatKind::Ident(_, ident, _) => Ident::new(&format!("{}", ident.name), MacroSpan::call_site()), 111 | _ => panic!("unimplemented {:?}", arg.pat.node), 112 | }) 113 | .collect() 114 | }; 115 | 116 | let (attributes, uniforms): (Vec<_>, Vec<_>) = { 117 | args.clone() 118 | .into_iter() 119 | .partition(|ident| { 120 | let name = format!("{}", ident); 121 | name.starts_with("a_") 122 | }) 123 | }; 124 | 125 | let attr_names: Vec<_> = attributes.iter().map(|id| id.to_string()).collect(); 126 | let uni_names: Vec<_> = uniforms.iter().map(|id| id.to_string()).collect(); 127 | 128 | let (output, outputs) = match *output { 129 | FunctionRetTy::Ty(ref ty) => match ty.node { 130 | TyKind::Tup(ref fields) => { 131 | let list: Vec<_> = { 132 | (0..fields.len()) 133 | .map(|id| { 134 | Ident::new(&format!("out_{}", id), MacroSpan::call_site()) 135 | }) 136 | .collect() 137 | }; 138 | 139 | let outputs = list.clone(); 140 | (quote! { ( #( #list ),* ) }, outputs) 141 | }, 142 | TyKind::Path(_, _) => { 143 | let output = Ident::new("output", MacroSpan::call_site()); 144 | (quote!{ #output }, vec![ output ]) 145 | }, 146 | _ => panic!("unimplemented {:?}", ty.node), 147 | }, 148 | _ => panic!("unimplemented {:?}", output), 149 | }; 150 | 151 | let attr_id: Vec<_> = (0..attributes.len()).map(|i| i as u32).collect(); 152 | let uni_id: Vec<_> = (0..uniforms.len()).map(|i| i as u32).collect(); 153 | let out_id: Vec<_> = (0..outputs.len()).map(|i| i as u32).collect(); 154 | 155 | Some((format!("{}_module", ident), quote! { 156 | #[allow(dead_code)] 157 | pub fn #aux_name() -> Module { 158 | let module = Module::new(); 159 | #( let #attributes = module.input(#attr_id, #attr_names); )* 160 | #( let #uniforms = module.uniform(#uni_id, #uni_names); )* 161 | let #output = #fn_name( #( #args ),* ); 162 | #( module.output(#out_id, None, #outputs); )* 163 | module 164 | } 165 | })) 166 | } else { 167 | None 168 | } 169 | } else { 170 | None 171 | }; 172 | 173 | if let Some((name, tokens)) = tokens { 174 | let mut parser = parse::new_parser_from_source_str( 175 | ecx.parse_sess, 176 | FileName::Custom(name), 177 | tokens.to_string(), 178 | ); 179 | vec![ 180 | item, 181 | Annotatable::Item(parser.parse_item().expect("result").expect("option")), 182 | ] 183 | } else { 184 | ecx.span_fatal(span, "Unsupported item for Rasen module attribute") 185 | } 186 | } 187 | 188 | /* 189 | * fn name(args: T, ...) -> R { 190 | * let func = |args: T, ...| -> R { 191 | * // Code 192 | * }; 193 | * 194 | * if let Some(module) = args.get_module().or_else(...) { 195 | * let func = module.function(func); 196 | * func(args, ...) 197 | * } else { 198 | * func(args, ...) 199 | * } 200 | * } 201 | */ 202 | fn insert_function_wrapper(ecx: &mut ExtCtxt, span: Span, item: Annotatable) -> Vec { 203 | if let Annotatable::Item(item) = item { 204 | let Item { ident, ref attrs, ref node, span, .. } = *item; 205 | if let ItemKind::Fn(ref decl, header, ref generics, ref block) = *node { 206 | let FnDecl { ref inputs, ref output, .. } = **decl; 207 | return vec![ 208 | Annotatable::Item(ecx.item( 209 | span, ident, 210 | attrs.clone(), 211 | ItemKind::Fn( 212 | ecx.fn_decl( 213 | inputs.clone(), 214 | output.clone(), 215 | ), 216 | header, 217 | generics.clone(), 218 | ecx.block(block.span, vec![ 219 | ecx.stmt_let( 220 | block.span, false, 221 | ecx.ident_of("func"), 222 | ecx.lambda_fn_decl( 223 | block.span, 224 | ecx.fn_decl( 225 | inputs.clone(), 226 | output.clone(), 227 | ), 228 | ecx.expr_block(block.clone()), 229 | block.span, 230 | ), 231 | ), 232 | ecx.stmt_expr(ecx.expr(block.span, ExprKind::IfLet( 233 | vec![ 234 | ecx.pat_some( 235 | block.span, 236 | ecx.pat_ident(block.span, ecx.ident_of("module")), 237 | ) 238 | ], 239 | inputs.iter() 240 | .fold(None, |current, arg| match arg.pat.node { 241 | PatKind::Ident(_, ident, _) => Some({ 242 | let id = ecx.expr_ident(ident.span, ident); 243 | let module = ecx.expr_method_call( 244 | ident.span, id, 245 | ecx.ident_of("get_module"), 246 | Vec::new(), 247 | ); 248 | 249 | if let Some(chain) = current { 250 | ecx.expr_method_call( 251 | ident.span, chain, 252 | ecx.ident_of("or_else"), 253 | vec![ 254 | ecx.lambda0(ident.span, module), 255 | ], 256 | ) 257 | } else { 258 | module 259 | } 260 | }), 261 | _ => ecx.span_fatal(arg.pat.span, "Unsupported destructuring"), 262 | }) 263 | .unwrap(), 264 | ecx.block(block.span, vec![ 265 | ecx.stmt_let( 266 | block.span, false, 267 | ecx.ident_of("func"), 268 | ecx.expr_method_call( 269 | block.span, 270 | ecx.expr_ident(block.span, ecx.ident_of("module")), 271 | ecx.ident_of("function"), 272 | vec![ 273 | ecx.expr_ident(block.span, ecx.ident_of("func")), 274 | ], 275 | ), 276 | ), 277 | ecx.stmt_expr(ecx.expr_call_ident( 278 | block.span, 279 | ecx.ident_of("func"), 280 | inputs.iter() 281 | .map(|arg| match arg.pat.node { 282 | PatKind::Ident(_, ident, _) => ecx.expr_ident(ident.span, ident), 283 | _ => ecx.span_fatal(arg.pat.span, "Unsupported destructuring"), 284 | }) 285 | .collect(), 286 | )), 287 | ]), 288 | Some( 289 | ecx.expr_call_ident( 290 | block.span, 291 | ecx.ident_of("func"), 292 | inputs.iter() 293 | .map(|arg| match arg.pat.node { 294 | PatKind::Ident(_, ident, _) => ecx.expr_ident(ident.span, ident), 295 | _ => ecx.span_fatal(arg.pat.span, "Unsupported destructuring"), 296 | }) 297 | .collect(), 298 | ), 299 | ), 300 | ))), 301 | ]), 302 | ), 303 | )), 304 | ]; 305 | } 306 | } 307 | 308 | ecx.span_fatal(span, "Unsupported item for Rasen function attribute") 309 | } 310 | 311 | fn rasen_attribute(ecx: &mut ExtCtxt, _: Span, meta_item: &MetaItem, item: Annotatable) -> Vec { 312 | let res = match meta_item.node { 313 | MetaItemKind::List(ref list) if list.len() == 1 => { 314 | let first = &list[0]; 315 | match first.node { 316 | NestedMetaItemKind::MetaItem(MetaItem { ref ident, node: MetaItemKind::Word, span }) => { 317 | if ident.segments.len() == 1 { 318 | let segment = &ident.segments[0]; 319 | if segment.ident.name == Symbol::intern("module") { 320 | Ok(insert_module_wrapper(ecx, span, item)) 321 | } else if segment.ident.name == Symbol::intern("function") { 322 | Ok(insert_function_wrapper(ecx, span, item)) 323 | } else { 324 | Err(span) 325 | } 326 | } else { 327 | Err(span) 328 | } 329 | }, 330 | _ => Err(first.span) 331 | } 332 | }, 333 | _ => Err(meta_item.span), 334 | }; 335 | 336 | match res { 337 | Ok(res) => res, 338 | Err(span) => ecx.span_fatal(span, "Unsupported rasen attribute"), 339 | } 340 | } 341 | 342 | fn idx_macro<'cx>(ecx: &'cx mut ExtCtxt, span: Span, tt: &[TokenTree]) -> Box { 343 | match (&tt[0], &tt[2]) { 344 | (&TokenTree::Token(_, Token::Ident(obj, _)), &TokenTree::Token(_, Token::Ident(index, _))) => { 345 | let index = index.to_string(); 346 | if index.is_empty() { 347 | ecx.span_fatal(span, "Empty composite field"); 348 | } 349 | 350 | let mut fields: Vec<_> = { 351 | index.chars() 352 | .map(|field| { 353 | let index = match field { 354 | 'x' | 'r' | 's' => 0, 355 | 'y' | 'g' | 't' => 1, 356 | 'z' | 'b' | 'p' => 2, 357 | 'w' | 'a' | 'q' => 3, 358 | _ => ecx.span_fatal(span, &format!("invalid composite field {}", field)), 359 | }; 360 | 361 | ecx.expr_call_ident( 362 | span, 363 | ast::Ident::from_str("index"), 364 | vec![ 365 | ecx.expr_addr_of( 366 | span, 367 | ecx.expr_ident(span, obj), 368 | ), 369 | ecx.expr_u32(span, index), 370 | ], 371 | ) 372 | }) 373 | .collect() 374 | }; 375 | 376 | let count = fields.len(); 377 | MacEager::expr( 378 | if count > 1 { 379 | ecx.expr_call_ident( 380 | span, 381 | ast::Ident::from_str(&format!("vec{}", count)), 382 | fields, 383 | ) 384 | } else { 385 | fields.remove(0) 386 | } 387 | ) 388 | }, 389 | _ => { 390 | box MacEager::default() 391 | }, 392 | } 393 | } 394 | 395 | #[derive(Clone)] 396 | struct CompositeMacro<'a>{ 397 | pub func: &'a str, 398 | pub float_ty: Option, 399 | pub int_ty: Option, 400 | } 401 | 402 | impl<'a> TTMacroExpander for CompositeMacro<'a> { 403 | fn expand<'cx>(&self, ecx: &'cx mut ExtCtxt, span: Span, ts: TokenStream, _: Option) -> Box { 404 | let func = ast::Ident::from_str(self.func); 405 | let vec = ast::Ident::from_str("vec"); 406 | 407 | let size = self.func.chars().last().unwrap().to_digit(10).unwrap() as usize; 408 | let indices: Vec<_> = { 409 | (0..size) 410 | .map(|i| { 411 | ecx.expr_method_call( 412 | span, 413 | ecx.expr(span, ExprKind::Index( 414 | ecx.expr_ident(span, vec), 415 | ecx.expr_usize(span, i), 416 | )), 417 | ast::Ident::from_str("clone"), 418 | vec![], 419 | ) 420 | }) 421 | .collect() 422 | }; 423 | 424 | let mut block = vec![ 425 | ecx.stmt_let( 426 | span, true, vec, 427 | ecx.expr_vec_ng(span), 428 | ), 429 | ]; 430 | 431 | block.extend( 432 | ts.trees() 433 | .filter_map(|tt| { 434 | let expr = match tt { 435 | TokenTree::Token(_, token) => match token { 436 | Token::Ident(id, _) => ecx.expr_ident(span, id), 437 | Token::Literal(lit, _) => match lit { 438 | Lit::Integer(name) => { 439 | let val: u128 = format!("{}", name).parse().unwrap(); 440 | ecx.expr_lit(span, ast::LitKind::Int( 441 | val, self.int_ty.unwrap(), 442 | )) 443 | }, 444 | Lit::Float(name) => ecx.expr_lit(span, ast::LitKind::Float( 445 | name, self.float_ty.unwrap(), 446 | )), 447 | _ => return None, 448 | }, 449 | _ => return None, 450 | }, 451 | _ => return None, 452 | }; 453 | 454 | Some(ecx.stmt_expr( 455 | ecx.expr_method_call( 456 | span, 457 | ecx.expr_ident(span, vec), 458 | ast::Ident::from_str("extend"), 459 | vec![ 460 | ecx.expr_call( 461 | span, 462 | ecx.expr_path(ecx.path(span, vec![ 463 | ast::Ident::from_str("ValueIter"), 464 | ast::Ident::from_str("iter"), 465 | ])), 466 | vec![ 467 | ecx.expr_addr_of(span, expr), 468 | ], 469 | ), 470 | ], 471 | ), 472 | )) 473 | }) 474 | ); 475 | 476 | block.push( 477 | ecx.stmt_expr( 478 | ecx.expr_call_ident(span, func, indices), 479 | ), 480 | ); 481 | 482 | MacEager::expr( 483 | ecx.expr_block( 484 | ecx.block(span, block), 485 | ), 486 | ) 487 | } 488 | } 489 | 490 | const COMPOSITE_MACROS: &[CompositeMacro<'static>] = &[ 491 | CompositeMacro { 492 | func: "bvec2", 493 | float_ty: None, 494 | int_ty: None, 495 | }, 496 | CompositeMacro { 497 | func: "bvec3", 498 | float_ty: None, 499 | int_ty: None, 500 | }, 501 | CompositeMacro { 502 | func: "bvec4", 503 | float_ty: None, 504 | int_ty: None, 505 | }, 506 | CompositeMacro { 507 | func: "dvec2", 508 | float_ty: Some(ast::FloatTy::F64), 509 | int_ty: None, 510 | }, 511 | CompositeMacro { 512 | func: "dvec3", 513 | float_ty: Some(ast::FloatTy::F64), 514 | int_ty: None, 515 | }, 516 | CompositeMacro { 517 | func: "dvec4", 518 | float_ty: Some(ast::FloatTy::F64), 519 | int_ty: None, 520 | }, 521 | CompositeMacro { 522 | func: "ivec2", 523 | float_ty: None, 524 | int_ty: Some(ast::LitIntType::Signed(ast::IntTy::I32)), 525 | }, 526 | CompositeMacro { 527 | func: "ivec3", 528 | float_ty: None, 529 | int_ty: Some(ast::LitIntType::Signed(ast::IntTy::I32)), 530 | }, 531 | CompositeMacro { 532 | func: "ivec4", 533 | float_ty: None, 534 | int_ty: Some(ast::LitIntType::Signed(ast::IntTy::I32)), 535 | }, 536 | CompositeMacro { 537 | func: "uvec2", 538 | float_ty: None, 539 | int_ty: Some(ast::LitIntType::Unsigned(ast::UintTy::U32)), 540 | }, 541 | CompositeMacro { 542 | func: "uvec3", 543 | float_ty: None, 544 | int_ty: Some(ast::LitIntType::Unsigned(ast::UintTy::U32)), 545 | }, 546 | CompositeMacro { 547 | func: "uvec4", 548 | float_ty: None, 549 | int_ty: Some(ast::LitIntType::Unsigned(ast::UintTy::U32)), 550 | }, 551 | CompositeMacro { 552 | func: "vec2", 553 | float_ty: Some(ast::FloatTy::F32), 554 | int_ty: None, 555 | }, 556 | CompositeMacro { 557 | func: "vec3", 558 | float_ty: Some(ast::FloatTy::F32), 559 | int_ty: None, 560 | }, 561 | CompositeMacro { 562 | func: "vec4", 563 | float_ty: Some(ast::FloatTy::F32), 564 | int_ty: None, 565 | }, 566 | ]; 567 | 568 | #[plugin_registrar] 569 | pub fn plugin_registrar(reg: &mut Registry) { 570 | reg.register_macro("idx", idx_macro); 571 | 572 | for cmp_macro in COMPOSITE_MACROS { 573 | reg.register_syntax_extension( 574 | Symbol::intern(cmp_macro.func), 575 | SyntaxExtension::NormalTT { 576 | expander: box cmp_macro.clone(), 577 | allow_internal_unsafe: false, 578 | allow_internal_unstable: false, 579 | def_info: None, 580 | unstable_feature: None, 581 | local_inner_macros: false, 582 | edition: Edition::Edition2018, 583 | }, 584 | ); 585 | } 586 | 587 | reg.register_syntax_extension( 588 | Symbol::intern("rasen"), 589 | SyntaxExtension::MultiModifier(box rasen_attribute) 590 | ); 591 | } 592 | -------------------------------------------------------------------------------- /rasen-plugin/tests/graph.rs: -------------------------------------------------------------------------------- 1 | #![feature(plugin, custom_attribute, try_from)] 2 | #![plugin(rasen_plugin)] 3 | 4 | extern crate rasen; 5 | extern crate rasen_dsl; 6 | extern crate rspirv; 7 | extern crate insta; 8 | 9 | use rasen_dsl::prelude::*; 10 | use std::f32::consts::PI; 11 | 12 | include!("../../tests/plugin.rs"); 13 | include!("../../tests/update.rs"); 14 | 15 | #[test] 16 | fn gen_basic_vert() { 17 | let module = basic_vert_module(); 18 | let assembly = module 19 | .build_assembly(Settings { 20 | mod_type: ShaderType::Vertex, 21 | uniforms_name: Some(String::from("Uniforms")), 22 | }) 23 | .unwrap(); 24 | 25 | assert_spirv_snapshot_matches!("basic-plugin.vert", assembly); 26 | } 27 | 28 | #[test] 29 | fn gen_basic_frag() { 30 | let module = basic_frag_module(); 31 | let assembly = module 32 | .build_assembly(Settings { 33 | mod_type: ShaderType::Fragment, 34 | uniforms_name: Some(String::from("Uniforms")), 35 | }) 36 | .unwrap(); 37 | 38 | assert_spirv_snapshot_matches!("basic-plugin.frag", assembly); 39 | } 40 | 41 | #[test] 42 | #[allow(clippy::float_cmp)] 43 | fn call_functions() { 44 | let result = func(PI.into()); 45 | let result = match result { 46 | Value::Concrete(v) => v, 47 | _ => panic!("result is not concrete"), 48 | }; 49 | assert_eq!(result, PI); 50 | } 51 | 52 | #[test] 53 | fn gen_functions() { 54 | let module = functions_module(); 55 | let assembly = module 56 | .build_assembly(Settings { 57 | mod_type: ShaderType::Vertex, 58 | uniforms_name: Some(String::from("Uniforms")), 59 | }) 60 | .unwrap(); 61 | 62 | assert_spirv_snapshot_matches!("functions", assembly); 63 | } 64 | -------------------------------------------------------------------------------- /rasen-plugin/tests/snapshots/graph__basic-plugin.frag.snap: -------------------------------------------------------------------------------- 1 | --- 2 | created: "2019-01-26T12:17:44.121870400+00:00" 3 | creator: insta@0.5.2 4 | expression: assembly 5 | source: rasen-plugin/tests/graph.rs 6 | --- 7 | ; SPIR-V 8 | ; Version: 1.1 9 | ; Generator: Unknown 10 | ; Bound: 34 11 | OpCapability Shader 12 | %10 = OpExtInstImport "GLSL.std.450" 13 | OpMemoryModel Logical GLSL450 14 | OpEntryPoint Fragment %4 "main" %8 %27 %33 15 | OpExecutionMode %4 OriginUpperLeft 16 | OpName %8 "a_normal" 17 | OpName %23 "material" 18 | OpName %27 "a_uv" 19 | OpDecorate %8 Location 0 20 | OpDecorate %23 Location 0 21 | OpDecorate %27 Location 1 22 | OpDecorate %33 Location 0 23 | %5 = OpTypeFloat 32 24 | %29 = OpTypeVector %5 4 25 | %32 = OpTypePointer Output %29 26 | %33 = OpVariable %32 Output 27 | %25 = OpTypeVector %5 2 28 | %26 = OpTypePointer Input %25 29 | %27 = OpVariable %26 Input 30 | %20 = OpTypeImage %5 2D 0 0 0 1 Unknown 31 | %21 = OpTypeSampledImage %20 32 | %22 = OpTypePointer Uniform %21 33 | %23 = OpVariable %22 Uniform 34 | %18 = OpConstant %5 1.0 35 | %17 = OpConstant %5 0.1 36 | %14 = OpConstant %5 0.2 37 | %13 = OpConstant %5 -0.5 38 | %12 = OpConstant %5 0.3 39 | %6 = OpTypeVector %5 3 40 | %15 = OpConstantComposite %6 %12 %13 %14 41 | %7 = OpTypePointer Input %6 42 | %8 = OpVariable %7 Input 43 | %1 = OpTypeVoid 44 | %2 = OpTypeFunction %1 45 | %4 = OpFunction %1 None %2 46 | %3 = OpLabel 47 | %9 = OpLoad %6 %8 None 48 | %11 = OpExtInst %6 %10 Normalize %9 49 | %16 = OpDot %5 %11 %15 50 | %19 = OpExtInst %5 %10 FClamp %16 %17 %18 51 | %24 = OpLoad %21 %23 None 52 | %28 = OpLoad %25 %27 None 53 | %30 = OpImageSampleImplicitLod %29 %24 %28 54 | %31 = OpVectorTimesScalar %29 %19 %30 55 | OpStore %33 %31 None 56 | OpReturn 57 | OpFunctionEnd 58 | -------------------------------------------------------------------------------- /rasen-plugin/tests/snapshots/graph__basic-plugin.vert.snap: -------------------------------------------------------------------------------- 1 | --- 2 | created: "2019-01-26T12:17:44.121870400+00:00" 3 | creator: insta@0.5.2 4 | expression: assembly 5 | source: rasen-plugin/tests/graph.rs 6 | --- 7 | ; SPIR-V 8 | ; Version: 1.1 9 | ; Generator: Unknown 10 | ; Bound: 56 11 | OpCapability Shader 12 | OpMemoryModel Logical GLSL450 13 | OpEntryPoint Vertex %4 "main" %26 %37 %52 %35 %49 %55 14 | OpName %11 "Uniforms" 15 | OpMemberName %9 0 "projection" 16 | OpMemberName %9 1 "view" 17 | OpMemberName %9 2 "model" 18 | OpName %26 "a_pos" 19 | OpName %37 "a_normal" 20 | OpName %52 "a_uv" 21 | OpDecorate %9 Block 22 | OpMemberDecorate %9 0 MatrixStride 16 23 | OpMemberDecorate %9 0 ColMajor 24 | OpMemberDecorate %9 1 MatrixStride 16 25 | OpMemberDecorate %9 1 ColMajor 26 | OpMemberDecorate %9 2 MatrixStride 16 27 | OpMemberDecorate %9 2 ColMajor 28 | OpDecorate %26 Location 0 29 | OpDecorate %35 Location 0 30 | OpDecorate %37 Location 1 31 | OpDecorate %49 Location 1 32 | OpDecorate %52 Location 2 33 | OpDecorate %55 Location 2 34 | OpMemberDecorate %9 0 Offset 0 35 | OpMemberDecorate %9 1 Offset 64 36 | OpMemberDecorate %9 2 Offset 128 37 | %12 = OpTypeInt 32 1 38 | %20 = OpConstant %12 2 39 | %16 = OpConstant %12 1 40 | %13 = OpConstant %12 0 41 | %5 = OpTypeFloat 32 42 | %50 = OpTypeVector %5 2 43 | %54 = OpTypePointer Output %50 44 | %55 = OpVariable %54 Output 45 | %51 = OpTypePointer Input %50 46 | %52 = OpVariable %51 Input 47 | %31 = OpConstant %5 1.0 48 | %24 = OpTypeVector %5 3 49 | %48 = OpTypePointer Output %24 50 | %49 = OpVariable %48 Output 51 | %36 = OpTypePointer Input %24 52 | %37 = OpVariable %36 Input 53 | %25 = OpTypePointer Input %24 54 | %26 = OpVariable %25 Input 55 | %6 = OpTypeVector %5 4 56 | %34 = OpTypePointer Output %6 57 | %35 = OpVariable %34 Output 58 | %7 = OpTypeMatrix %6 4 59 | %8 = OpTypePointer Uniform %7 60 | %1 = OpTypeVoid 61 | %2 = OpTypeFunction %1 62 | %9 = OpTypeStruct %7 %7 %7 63 | %10 = OpTypePointer Uniform %9 64 | %11 = OpVariable %10 Uniform 65 | %4 = OpFunction %1 None %2 66 | %3 = OpLabel 67 | %14 = OpAccessChain %8 %11 %13 68 | %15 = OpLoad %7 %14 None 69 | %17 = OpAccessChain %8 %11 %16 70 | %18 = OpLoad %7 %17 None 71 | %19 = OpMatrixTimesMatrix %7 %15 %18 72 | %21 = OpAccessChain %8 %11 %20 73 | %22 = OpLoad %7 %21 None 74 | %23 = OpMatrixTimesMatrix %7 %19 %22 75 | %27 = OpLoad %24 %26 None 76 | %28 = OpCompositeExtract %5 %27 0 77 | %29 = OpCompositeExtract %5 %27 1 78 | %30 = OpCompositeExtract %5 %27 2 79 | %32 = OpCompositeConstruct %6 %28 %29 %30 %31 80 | %33 = OpMatrixTimesVector %6 %23 %32 81 | OpStore %35 %33 None 82 | %38 = OpLoad %24 %37 None 83 | %39 = OpCompositeExtract %5 %38 0 84 | %40 = OpCompositeExtract %5 %38 1 85 | %41 = OpCompositeExtract %5 %38 2 86 | %42 = OpCompositeConstruct %6 %39 %40 %41 %31 87 | %43 = OpMatrixTimesVector %6 %22 %42 88 | %44 = OpCompositeExtract %5 %43 0 89 | %45 = OpCompositeExtract %5 %43 1 90 | %46 = OpCompositeExtract %5 %43 2 91 | %47 = OpCompositeConstruct %24 %44 %45 %46 92 | OpStore %49 %47 None 93 | %53 = OpLoad %50 %52 None 94 | OpStore %55 %53 None 95 | OpReturn 96 | OpFunctionEnd 97 | -------------------------------------------------------------------------------- /rasen-plugin/tests/snapshots/graph__functions.snap: -------------------------------------------------------------------------------- 1 | --- 2 | created: "2019-01-26T12:17:44.121870400+00:00" 3 | creator: insta@0.5.2 4 | expression: assembly 5 | source: rasen-plugin/tests/graph.rs 6 | --- 7 | ; SPIR-V 8 | ; Version: 1.1 9 | ; Generator: Unknown 10 | ; Bound: 16 11 | OpCapability Shader 12 | OpMemoryModel Logical GLSL450 13 | OpEntryPoint Vertex %4 "main" %11 %15 14 | OpName %11 "a_input" 15 | OpDecorate %11 Location 0 16 | OpDecorate %15 Location 0 17 | %6 = OpTypeFloat 32 18 | %14 = OpTypePointer Output %6 19 | %15 = OpVariable %14 Output 20 | %10 = OpTypePointer Input %6 21 | %11 = OpVariable %10 Input 22 | %9 = OpTypeFunction %6 %6 23 | %1 = OpTypeVoid 24 | %2 = OpTypeFunction %1 25 | %4 = OpFunction %1 None %2 26 | %3 = OpLabel 27 | %12 = OpLoad %6 %11 None 28 | %13 = OpFunctionCall %6 %5 %12 29 | OpStore %15 %13 None 30 | OpReturn 31 | OpFunctionEnd 32 | %5 = OpFunction %6 None %9 33 | %7 = OpFunctionParameter %6 34 | %8 = OpLabel 35 | OpReturnValue %7 36 | OpFunctionEnd 37 | -------------------------------------------------------------------------------- /rasen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rasen" 3 | version = "0.12.0" 4 | authors = ["l3ops "] 5 | description = "Build a SPIR-V module from a data flow graph" 6 | documentation = "https://docs.rs/rasen/" 7 | homepage = "https://github.com/leops/rasen#readme" 8 | repository = "https://github.com/leops/rasen" 9 | readme = "README.md" 10 | license = "MIT" 11 | build = "build.rs" 12 | keywords = [ 13 | "SPIRV", 14 | "Vulkan", 15 | "GLSL", 16 | ] 17 | categories = [ 18 | "development-tools", 19 | "rendering", 20 | ] 21 | 22 | [badges] 23 | appveyor = { repository = "leops/rasen" } 24 | travis-ci = { repository = "leops/rasen" } 25 | 26 | [dependencies] 27 | petgraph = "0.4.13" 28 | error-chain = { version = "0.12.0", default-features = false } 29 | spirv_headers = "1.3.4" 30 | rspirv = "0.5.4" 31 | fnv = "1.0.6" 32 | 33 | [build-dependencies] 34 | quote = "0.6.8" 35 | proc-macro2 = "0.4.20" 36 | 37 | [dev-dependencies] 38 | insta = { git = "https://github.com/mitsuhiko/insta", rev="7432f45", version = "0.5.2" } 39 | -------------------------------------------------------------------------------- /rasen/README.md: -------------------------------------------------------------------------------- 1 | rasen 2 | ================ 3 | 4 | The `rasen` crate contains the core graph compiler itself. It provides graph building utilities (the `Graph` struct), 5 | various types (`rasen::types::*`) and operations (`rasen::node::*`) definitions, and SPIR-V compilation utilities (the 6 | `ModuleBuilder` struct). 7 | 8 | The API is intentionally low-level, as the use case for the core compiler is to act as a backend for a graph-based 9 | material editor in a game engine. It's perfectly possible to use this crate as-is by creating a `Graph` struct and 10 | building the module node-by-node, however this method tends to be quite verbose for "static" shaders: 11 | 12 | ```rust 13 | extern crate rasen; 14 | 15 | use rasen::prelude::*; 16 | 17 | fn main() { 18 | let mut graph = Graph::new(); 19 | 20 | // A vec3 input at location 0 21 | let normal = graph.add_node(Node::Input(0, TypeName::VEC3, VariableName::Named(String::from("a_normal")))); 22 | 23 | // Some ambient light constants 24 | let min_light = graph.add_node(Node::Constant(TypedValue::Float(0.1))); 25 | let max_light = graph.add_node(Node::Constant(TypedValue::Float(1.0))); 26 | let light_dir = graph.add_node(Node::Constant(TypedValue::Vec3(0.3, -0.5, 0.2))); 27 | 28 | // The Material color (also a constant) 29 | let mat_color = graph.add_node(Node::Constant(TypedValue::Vec4(0.25, 0.625, 1.0, 1.0))); 30 | 31 | // Some usual function calls 32 | let normalize = graph.add_node(Node::Normalize); 33 | let dot = graph.add_node(Node::Dot); 34 | let clamp = graph.add_node(Node::Clamp); 35 | let multiply = graph.add_node(Node::Multiply); 36 | 37 | // And a vec4 output at location 0 38 | let color = graph.add_node(Node::Output(0, TypeName::VEC4, VariableName::Named(String::from("o_color")))); 39 | 40 | // Normalize the normal 41 | graph.add_edge(normal, normalize, 0); 42 | 43 | // Compute the dot product of the surface normal and the light direction 44 | graph.add_edge(normalize, dot, 0); 45 | graph.add_edge(light_dir, dot, 1); 46 | 47 | // Restrict the result into the ambient light range 48 | graph.add_edge(dot, clamp, 0); 49 | graph.add_edge(min_light, clamp, 1); 50 | graph.add_edge(max_light, clamp, 2); 51 | 52 | // Multiply the light intensity by the surface color 53 | graph.add_edge(clamp, multiply, 0); 54 | graph.add_edge(mat_color, multiply, 1); 55 | 56 | // Write the result to the output 57 | graph.add_edge(multiply, color, 0); 58 | 59 | let bytecode = build_program(&graph, ShaderType::Fragment).unwrap(); 60 | // bytecode is now a Vec you can pass to Vulkan to create the shader module 61 | } 62 | ``` 63 | -------------------------------------------------------------------------------- /rasen/benches/build.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate rasen; 4 | extern crate test; 5 | 6 | use rasen::prelude::*; 7 | use test::Bencher; 8 | 9 | include!("../../tests/graph.rs"); 10 | 11 | #[bench] 12 | fn bench_build_basic_vert(b: &mut Bencher) { 13 | let graph = build_basic_vert(); 14 | b.iter(|| { 15 | ModuleBuilder::from_graph( 16 | &graph, 17 | Settings { 18 | mod_type: ShaderType::Vertex, 19 | uniforms_name: Some(String::from("Uniforms")), 20 | }, 21 | ) 22 | .unwrap() 23 | .build() 24 | .unwrap() 25 | }); 26 | } 27 | 28 | #[bench] 29 | fn bench_build_basic_frag(b: &mut Bencher) { 30 | let graph = build_basic_frag(); 31 | b.iter(|| { 32 | ModuleBuilder::from_graph( 33 | &graph, 34 | Settings { 35 | mod_type: ShaderType::Fragment, 36 | uniforms_name: Some(String::from("Uniforms")), 37 | }, 38 | ) 39 | .unwrap() 40 | .build() 41 | .unwrap() 42 | }); 43 | } 44 | -------------------------------------------------------------------------------- /rasen/src/builder/function.rs: -------------------------------------------------------------------------------- 1 | use std::iter; 2 | 3 | use fnv::FnvHashMap as HashMap; 4 | use petgraph::graph::NodeIndex; 5 | 6 | use rspirv::mr::{BasicBlock, Function, Instruction, Operand}; 7 | use spirv_headers::*; 8 | 9 | use super::{ 10 | module::{FunctionData, VOID_ID}, 11 | Builder as BuilderTrait, 12 | }; 13 | use errors::*; 14 | use module::FunctionRef; 15 | use types::{TypeName, TypedValue}; 16 | 17 | pub(crate) struct Builder<'a> { 18 | module: &'a mut dyn BuilderTrait, 19 | results: HashMap, (&'static TypeName, Word)>, 20 | 21 | id: Word, 22 | args: Vec<(&'static TypeName, Instruction)>, 23 | res: Option<&'static TypeName>, 24 | instructions: Vec, 25 | } 26 | 27 | impl<'a> Builder<'a> { 28 | pub fn new(module: &'a mut dyn BuilderTrait) -> Builder<'a> { 29 | let id = module.get_id(); 30 | Builder { 31 | module, 32 | results: HashMap::default(), 33 | 34 | id, 35 | args: Vec::new(), 36 | res: None, 37 | instructions: vec![Instruction::new(Op::Return, None, None, Vec::new())], 38 | } 39 | } 40 | 41 | pub fn build(self) { 42 | let Builder { 43 | module, 44 | id, 45 | args, 46 | instructions, 47 | res, 48 | .. 49 | } = self; 50 | 51 | let label_id = module.get_id(); 52 | let result_type = if let Some(ty) = res { 53 | module.register_type(ty) 54 | } else { 55 | VOID_ID 56 | }; 57 | 58 | let (args_ty, parameters): (Vec<_>, _) = args.into_iter().unzip(); 59 | 60 | let func_type = module.get_id(); 61 | let func_def = Instruction::new( 62 | Op::TypeFunction, 63 | None, 64 | Some(func_type), 65 | iter::once(result_type) 66 | .chain(args_ty.iter().map(|ty| module.register_type(ty))) 67 | .map(Operand::IdRef) 68 | .collect(), 69 | ); 70 | 71 | module.push_declaration(func_def); 72 | 73 | module.push_function(( 74 | id, 75 | args_ty, 76 | res, 77 | Function { 78 | def: Some(Instruction::new( 79 | Op::Function, 80 | Some(result_type), 81 | Some(id), 82 | vec![ 83 | Operand::FunctionControl(FunctionControl::empty()), 84 | Operand::IdRef(func_type), 85 | ], 86 | )), 87 | end: Some(Instruction::new(Op::FunctionEnd, None, None, Vec::new())), 88 | parameters, 89 | basic_blocks: vec![BasicBlock { 90 | label: Some(Instruction::new( 91 | Op::Label, 92 | None, 93 | Some(label_id), 94 | Vec::new(), 95 | )), 96 | instructions, 97 | }], 98 | }, 99 | )); 100 | } 101 | } 102 | 103 | impl<'a> BuilderTrait for Builder<'a> { 104 | fn get_id(&mut self) -> Word { 105 | self.module.get_id() 106 | } 107 | 108 | fn import_set(&mut self, name: &'static str) -> Word { 109 | self.module.import_set(name) 110 | } 111 | 112 | fn register_type(&mut self, type_id: &'static TypeName) -> Word { 113 | self.module.register_type(type_id) 114 | } 115 | 116 | fn register_constant(&mut self, constant: &TypedValue) -> Result { 117 | self.module.register_constant(constant) 118 | } 119 | 120 | fn register_uniform(&mut self, location: u32, type_id: &'static TypeName) -> (Word, Word) { 121 | self.module.register_uniform(location, type_id) 122 | } 123 | 124 | fn push_instruction(&mut self, inst: Instruction) { 125 | let index = self.instructions.len() - 1; 126 | self.instructions.insert(index, inst) 127 | } 128 | 129 | fn push_declaration(&mut self, inst: Instruction) { 130 | self.module.push_declaration(inst) 131 | } 132 | 133 | fn push_output(&mut self, id: Word) { 134 | self.module.push_output(id) 135 | } 136 | 137 | fn push_input(&mut self, id: Word) { 138 | self.module.push_input(id) 139 | } 140 | 141 | fn push_annotation(&mut self, inst: Instruction) { 142 | self.module.push_annotation(inst) 143 | } 144 | 145 | fn push_debug(&mut self, inst: Instruction) { 146 | self.module.push_debug(inst) 147 | } 148 | 149 | fn push_function(&mut self, func: FunctionData) { 150 | self.module.push_function(func) 151 | } 152 | 153 | fn push_parameter( 154 | &mut self, 155 | location: u32, 156 | ty: &'static TypeName, 157 | inst: Instruction, 158 | ) -> Result<()> { 159 | let index = location as usize; 160 | while self.args.len() <= index { 161 | self.args.push(( 162 | TypeName::VOID, 163 | Instruction::new(Op::FunctionParameter, None, None, Vec::new()), 164 | )); 165 | } 166 | 167 | self.args[index] = (ty, inst); 168 | 169 | Ok(()) 170 | } 171 | 172 | fn set_return(&mut self, ty: &'static TypeName, inst: Instruction) -> Result<()> { 173 | let last = self 174 | .instructions 175 | .last_mut() 176 | .expect("instructions should not be empty"); 177 | 178 | if last.class.opcode != Op::Return { 179 | Err(String::from("Function has already returned"))?; 180 | } 181 | 182 | *last = inst; 183 | 184 | self.res = Some(ty); 185 | 186 | Ok(()) 187 | } 188 | 189 | fn get_result(&self, index: NodeIndex) -> Option<(&'static TypeName, u32)> { 190 | self.results.get(&index).cloned() 191 | } 192 | 193 | fn set_result(&mut self, index: NodeIndex, res: (&'static TypeName, u32)) { 194 | self.results.insert(index, res); 195 | } 196 | 197 | fn get_function( 198 | &self, 199 | index: FunctionRef, 200 | ) -> Option<(Word, &[&'static TypeName], Option<&'static TypeName>)> { 201 | self.module.get_function(index) 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /rasen/src/builder/mod.rs: -------------------------------------------------------------------------------- 1 | use petgraph::graph::NodeIndex; 2 | use rspirv::mr::Instruction; 3 | use spirv_headers::Word; 4 | 5 | use errors::*; 6 | use graph::*; 7 | use module::FunctionRef; 8 | use types::{TypeName, TypedValue}; 9 | 10 | mod function; 11 | mod module; 12 | 13 | pub(crate) use self::module::FunctionData; 14 | pub use self::module::{Builder as ModuleBuilder, Settings}; 15 | 16 | pub(crate) trait Builder { 17 | /// Acquire a new identifier to be used in the module 18 | fn get_id(&mut self) -> Word; 19 | 20 | /// Import an instruction set to the module, returning its ID 21 | fn import_set(&mut self, name: &'static str) -> Word; 22 | 23 | /// Get the ID corresponding to a Type 24 | fn register_type(&mut self, type_id: &'static TypeName) -> Word; 25 | 26 | /// Add a new constant to the module, returning its ID 27 | fn register_constant(&mut self, constant: &TypedValue) -> Result; 28 | 29 | fn register_uniform(&mut self, location: u32, type_id: &'static TypeName) -> (Word, Word); 30 | 31 | fn push_instruction(&mut self, inst: Instruction); 32 | fn push_declaration(&mut self, inst: Instruction); 33 | fn push_output(&mut self, id: Word); 34 | fn push_input(&mut self, id: Word); 35 | fn push_annotation(&mut self, inst: Instruction); 36 | fn push_debug(&mut self, inst: Instruction); 37 | fn push_function(&mut self, func: FunctionData); 38 | 39 | fn push_parameter( 40 | &mut self, 41 | location: Word, 42 | ty: &'static TypeName, 43 | inst: Instruction, 44 | ) -> Result<()>; 45 | fn set_return(&mut self, ty: &'static TypeName, inst: Instruction) -> Result<()>; 46 | 47 | fn get_result(&self, index: NodeIndex) -> Option<(&'static TypeName, u32)>; 48 | fn set_result(&mut self, index: NodeIndex, res: (&'static TypeName, u32)); 49 | 50 | fn get_function( 51 | &self, 52 | index: FunctionRef, 53 | ) -> Option<(Word, &[&'static TypeName], Option<&'static TypeName>)>; 54 | 55 | fn visit(&mut self, graph: &Graph, index: NodeIndex) -> Result<(&'static TypeName, u32)> 56 | where 57 | Self: Sized, 58 | { 59 | if let Some(res) = self.get_result(index) { 60 | return Ok(res); 61 | } 62 | 63 | let args: Result> = { 64 | graph 65 | .arguments(index) 66 | .map(|edge| self.visit(graph, edge)) 67 | .collect() 68 | }; 69 | 70 | let node = &graph[index]; 71 | let res = { 72 | node.get_result(self, args?) 73 | .chain_err(|| ErrorKind::BuildError(node.to_string(), index.index()))? 74 | }; 75 | 76 | self.set_result(index, res); 77 | Ok(res) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /rasen/src/errors.rs: -------------------------------------------------------------------------------- 1 | //! Error-related definitions generated by `error_chain` 2 | 3 | use types::TypeName; 4 | use module::FunctionRef; 5 | 6 | error_chain! { 7 | errors { 8 | /// The compiler was provided with a cyclic graph, but the data flow graph 9 | /// should always be acyclic 10 | CyclicGraph { 11 | description("graph is cyclic") 12 | display("graph is cyclic") 13 | } 14 | 15 | /// A call node is referencing an unknown function 16 | MissingFunction(index: FunctionRef) { 17 | description("missing function") 18 | display("missing function {:?}", index) 19 | } 20 | 21 | /// A node is not receiving the expected number of values 22 | WrongArgumentsCount(actual: usize, expected: usize) { 23 | description("wrong number of arguments") 24 | display("got {} arguments, expected {}", actual, expected) 25 | } 26 | 27 | /// A composite type was acessed with an invalid index (eg. 2 in a `vec2`) 28 | IndexOutOfBound(index: u32, len: u32) { 29 | description("index out of bounds") 30 | display("index out of bounds ({} >= {})", index, len) 31 | } 32 | 33 | /// A generic arguments error, usually thrown when a node receives a combination of 34 | /// types it cannot handle 35 | BadArguments(args: Box<[&'static TypeName]>) { 36 | description("bad arguments") 37 | display("bad arguments: {:?}", args) 38 | } 39 | 40 | /// Hopefully temporary error type, thrown when creating a constant with a type not yet supported 41 | UnsupportedConstant(ty: &'static TypeName) { 42 | description("unsupported constant type") 43 | display("unsupported constant type {:?}", ty) 44 | } 45 | 46 | /// A node is used in an incompatible context 47 | UnsupportedOperation(name: &'static str) { 48 | description("unsupported operation") 49 | display("unsupported operation {}", name) 50 | } 51 | 52 | /// Used to wrap another error with metadata about its origin node 53 | BuildError(node: &'static str, id: usize) { 54 | description("build error") 55 | display("compilation failed at {} node with id {}", node, id) 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /rasen/src/graph.rs: -------------------------------------------------------------------------------- 1 | //! Graph building helpers 2 | 3 | use std::ops::Index; 4 | 5 | use petgraph::{algo, graph::NodeIndex, visit::EdgeRef, Graph as PetGraph, Incoming, Outgoing}; 6 | 7 | use super::node::*; 8 | 9 | /// Convenience wrapper for [`petgraph::Graph`](petgraph::graph::Graph) 10 | #[derive(Debug)] 11 | pub struct Graph { 12 | graph: PetGraph, 13 | } 14 | 15 | impl Default for Graph { 16 | /// Create a new empty graph 17 | fn default() -> Self { 18 | Self { 19 | graph: PetGraph::new(), 20 | } 21 | } 22 | } 23 | 24 | impl Graph { 25 | /// Add a node to the graph 26 | pub fn add_node(&mut self, node: Node) -> NodeIndex { 27 | self.graph.add_node(node) 28 | } 29 | 30 | /// Add an edge between two nodes in the graph, infering the result type of the origin node 31 | pub fn add_edge(&mut self, from: NodeIndex, to: NodeIndex, index: u32) { 32 | self.graph.add_edge(from, to, index); 33 | } 34 | 35 | pub(crate) fn has_cycle(&self) -> bool { 36 | algo::is_cyclic_directed(&self.graph) 37 | } 38 | 39 | /// List all the outputs of the graph 40 | pub(crate) fn outputs<'a>(&'a self) -> impl Iterator> + 'a { 41 | self.graph 42 | .externals(Outgoing) 43 | .filter(move |index| match self.graph.node_weight(*index) { 44 | Some(&Node::Output(_, _, _)) | Some(&Node::Return) => true, 45 | _ => false, 46 | }) 47 | } 48 | 49 | /// List the incoming connections for a node 50 | pub(crate) fn arguments<'a>( 51 | &'a self, 52 | index: NodeIndex, 53 | ) -> impl Iterator> + 'a { 54 | let mut vec: Vec<_> = self.graph.edges_directed(index, Incoming).collect(); 55 | 56 | vec.sort_by_key(|e| e.weight()); 57 | 58 | vec.into_iter().map(|e| e.source()) 59 | } 60 | } 61 | 62 | impl Index> for Graph { 63 | type Output = Node; 64 | 65 | /// Get a node from the graph 66 | fn index(&self, index: NodeIndex) -> &Node { 67 | &self.graph[index] 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /rasen/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Build a SPIR-V module from a data flow graph 2 | //! 3 | //! This library lets you define a shader module as a `Graph` of `Node`, 4 | //! describing the operations needed to obtain the outputs of the shader. 5 | //! 6 | //! ``` 7 | //! # extern crate rasen; 8 | //! # use rasen::prelude::*; 9 | //! # fn main() { 10 | //! let mut graph = Graph::default(); 11 | //! 12 | //! // A vec3 input at location 0 13 | //! let normal = graph.add_node(Node::Input(0, TypeName::VEC3, VariableName::Named(String::from("a_normal")))); 14 | //! 15 | //! // Some ambient light constants 16 | //! let min_light = graph.add_node(Node::Constant(TypedValue::Float(0.1))); 17 | //! let max_light = graph.add_node(Node::Constant(TypedValue::Float(1.0))); 18 | //! let light_dir = graph.add_node(Node::Constant(TypedValue::Vec3(0.3, -0.5, 0.2))); 19 | //! 20 | //! // The Material color (also a constant) 21 | //! let mat_color = graph.add_node(Node::Constant(TypedValue::Vec4(0.25, 0.625, 1.0, 1.0))); 22 | //! 23 | //! // Some usual function calls 24 | //! let normalize = graph.add_node(Node::Normalize); 25 | //! let dot = graph.add_node(Node::Dot); 26 | //! let clamp = graph.add_node(Node::Clamp); 27 | //! let multiply = graph.add_node(Node::Multiply); 28 | //! 29 | //! // And a vec4 output at location 0 30 | //! let color = graph.add_node(Node::Output(0, TypeName::VEC4, VariableName::Named(String::from("o_color")))); 31 | //! 32 | //! // Normalize the normal 33 | //! graph.add_edge(normal, normalize, 0); 34 | //! 35 | //! // Compute the dot product of the surface normal and the light direction 36 | //! graph.add_edge(normalize, dot, 0); 37 | //! graph.add_edge(light_dir, dot, 1); 38 | //! 39 | //! // Restrict the result into the ambient light range 40 | //! graph.add_edge(dot, clamp, 0); 41 | //! graph.add_edge(min_light, clamp, 1); 42 | //! graph.add_edge(max_light, clamp, 2); 43 | //! 44 | //! // Multiply the light intensity by the surface color 45 | //! graph.add_edge(clamp, multiply, 0); 46 | //! graph.add_edge(mat_color, multiply, 1); 47 | //! 48 | //! // Write the result to the output 49 | //! graph.add_edge(multiply, color, 0); 50 | //! 51 | //! # #[allow(unused_variables)] 52 | //! let bytecode = build_program(&graph, ShaderType::Fragment).unwrap(); 53 | //! // bytecode is now a Vec you can pass to Vulkan to create the shader module 54 | //! # } 55 | //! ``` 56 | //! 57 | //! On a lower level, you can use the `ModuleBuilder` struct to build your module by adding instructions 58 | //! directly into it. 59 | //! 60 | 61 | #![warn(clippy::all, clippy::pedantic)] 62 | 63 | extern crate petgraph; 64 | extern crate rspirv; 65 | extern crate spirv_headers; 66 | #[macro_use] 67 | extern crate error_chain; 68 | extern crate fnv; 69 | 70 | mod builder; 71 | mod node; 72 | mod operations; 73 | mod types; 74 | 75 | pub mod errors; 76 | pub mod graph; 77 | pub mod module; 78 | pub mod prelude; 79 | -------------------------------------------------------------------------------- /rasen/src/module.rs: -------------------------------------------------------------------------------- 1 | //! A Module is the advanced entry point for the Rasen compiler. It holds the graph for the main function, 2 | //! as well as the subgraphs for all the user-defined functions 3 | 4 | use graph::Graph; 5 | use std::ops::{Index, IndexMut}; 6 | 7 | /// An opaque pointer struct to a function 8 | #[derive(Copy, Clone, Debug)] 9 | pub struct FunctionRef(pub(crate) usize); 10 | 11 | /// A container for complex shader programs with multiple functions 12 | #[derive(Debug, Default)] 13 | pub struct Module { 14 | pub main: Graph, 15 | pub(crate) functions: Vec, 16 | } 17 | 18 | impl Module { 19 | /// Add a function to the graph 20 | pub fn add_function(&mut self) -> FunctionRef { 21 | let index = self.functions.len(); 22 | self.functions.push(Graph::default()); 23 | FunctionRef(index) 24 | } 25 | 26 | /// Get a reference to a function's graph from its index 27 | pub fn function(&mut self, index: FunctionRef) -> Option<&mut Graph> { 28 | self.functions.get_mut(index.0) 29 | } 30 | } 31 | 32 | impl Index for Module { 33 | type Output = Graph; 34 | 35 | fn index(&self, index: FunctionRef) -> &Graph { 36 | &self.functions[index.0] 37 | } 38 | } 39 | 40 | impl IndexMut for Module { 41 | fn index_mut(&mut self, index: FunctionRef) -> &mut Graph { 42 | &mut self.functions[index.0] 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /rasen/src/node.rs: -------------------------------------------------------------------------------- 1 | //! Definition and implentations of all the graph operations 2 | 3 | use rspirv::mr::{Instruction, Operand}; 4 | use spirv_headers::*; 5 | use std::{fmt, iter}; 6 | 7 | use builder::Builder; 8 | use errors::*; 9 | use module::FunctionRef; 10 | use operations; 11 | use types::*; 12 | 13 | include!(concat!(env!("OUT_DIR"), "/node.rs")); 14 | 15 | impl Node { 16 | /// Insert this Node into a Program 17 | #[allow(clippy::cast_possible_wrap)] 18 | pub(crate) fn get_result( 19 | &self, 20 | module: &mut impl Builder, 21 | args: Vec<(&'static TypeName, u32)>, 22 | ) -> Result<(&'static TypeName, u32)> { 23 | // use spirv_headers::GLOp::*; 24 | 25 | macro_rules! impl_glsl_call { 26 | ( $function:ident, $argc:expr ) => {{ 27 | if args.len() != $argc { 28 | bail!(ErrorKind::WrongArgumentsCount(args.len(), $argc)); 29 | } 30 | 31 | let ext_id = module.import_set("GLSL.std.450"); 32 | 33 | let (res_ty, _) = args[0]; 34 | let res_type = module.register_type(res_ty); 35 | 36 | let mut operands = Vec::with_capacity($argc + 2); 37 | operands.push(Operand::IdRef(ext_id)); 38 | operands.push(Operand::LiteralExtInstInteger( 39 | ::spirv_headers::GLOp::$function as u32, 40 | )); 41 | operands.extend(args.into_iter().map(|(_, rid)| Operand::IdRef(rid))); 42 | 43 | let result_id = module.get_id(); 44 | 45 | module.push_instruction(Instruction::new( 46 | Op::ExtInst, 47 | Some(res_type), 48 | Some(result_id), 49 | operands, 50 | )); 51 | 52 | Ok((res_ty, result_id)) 53 | }}; 54 | } 55 | 56 | match *self { 57 | Node::Output(location, attr_type, ref name) => { 58 | if args.len() != 1 { 59 | bail!(ErrorKind::WrongArgumentsCount(args.len(), 1)); 60 | } 61 | 62 | let (arg_type, arg_value) = args[0]; 63 | if arg_type != attr_type { 64 | bail!(ErrorKind::BadArguments(Box::new([arg_type]))); 65 | } 66 | 67 | let type_id = module.register_type(attr_type); 68 | let ptr_type = module.get_id(); 69 | 70 | module.push_declaration(Instruction::new( 71 | Op::TypePointer, 72 | None, 73 | Some(ptr_type), 74 | vec![ 75 | Operand::StorageClass(StorageClass::Output), 76 | Operand::IdRef(type_id), 77 | ], 78 | )); 79 | 80 | let var_id = module.get_id(); 81 | 82 | module.push_output(var_id); 83 | 84 | module.push_declaration(Instruction::new( 85 | Op::Variable, 86 | Some(ptr_type), 87 | Some(var_id), 88 | vec![Operand::StorageClass(StorageClass::Output)], 89 | )); 90 | 91 | module.push_annotation(Instruction::new( 92 | Op::Decorate, 93 | None, 94 | None, 95 | vec![ 96 | Operand::IdRef(var_id), 97 | Operand::Decoration(Decoration::Location), 98 | Operand::LiteralInt32(location), 99 | ], 100 | )); 101 | 102 | name.decorate_variable(module, var_id); 103 | 104 | module.push_instruction(Instruction::new( 105 | Op::Store, 106 | None, 107 | None, 108 | vec![ 109 | Operand::IdRef(var_id), 110 | Operand::IdRef(arg_value), 111 | Operand::MemoryAccess(MemoryAccess::empty()), 112 | ], 113 | )); 114 | 115 | Ok((attr_type, var_id)) 116 | } 117 | 118 | Node::Input(location, attr_type, ref name) => { 119 | let type_id = module.register_type(attr_type); 120 | 121 | let ptr_type = module.get_id(); 122 | 123 | module.push_declaration(Instruction::new( 124 | Op::TypePointer, 125 | None, 126 | Some(ptr_type), 127 | vec![ 128 | Operand::StorageClass(StorageClass::Input), 129 | Operand::IdRef(type_id), 130 | ], 131 | )); 132 | 133 | let var_id = module.get_id(); 134 | 135 | module.push_input(var_id); 136 | 137 | module.push_declaration(Instruction::new( 138 | Op::Variable, 139 | Some(ptr_type), 140 | Some(var_id), 141 | vec![Operand::StorageClass(StorageClass::Input)], 142 | )); 143 | 144 | module.push_annotation(Instruction::new( 145 | Op::Decorate, 146 | None, 147 | None, 148 | vec![ 149 | Operand::IdRef(var_id), 150 | Operand::Decoration(Decoration::Location), 151 | Operand::LiteralInt32(location), 152 | ], 153 | )); 154 | 155 | name.decorate_variable(module, var_id); 156 | 157 | let res_id = module.get_id(); 158 | 159 | module.push_instruction(Instruction::new( 160 | Op::Load, 161 | Some(type_id), 162 | Some(res_id), 163 | vec![ 164 | Operand::IdRef(var_id), 165 | Operand::MemoryAccess(MemoryAccess::empty()), 166 | ], 167 | )); 168 | 169 | Ok((attr_type, res_id)) 170 | } 171 | 172 | Node::Uniform(location, attr_type, ref name) => { 173 | let type_id = module.register_type(attr_type); 174 | let ptr_type = module.register_type(attr_type.as_ptr(true)); 175 | 176 | if let attr_type @ &TypeName::Sampler(_, _) = attr_type { 177 | let var_id = module.get_id(); 178 | module.push_declaration(Instruction::new( 179 | Op::Variable, 180 | Some(ptr_type), 181 | Some(var_id), 182 | vec![Operand::StorageClass(StorageClass::Uniform)], 183 | )); 184 | 185 | module.push_annotation(Instruction::new( 186 | Op::Decorate, 187 | None, 188 | None, 189 | vec![ 190 | Operand::IdRef(var_id), 191 | Operand::Decoration(Decoration::Location), 192 | Operand::LiteralInt32(location), 193 | ], 194 | )); 195 | 196 | name.decorate_variable(module, var_id); 197 | 198 | let res_id = module.get_id(); 199 | module.push_instruction(Instruction::new( 200 | Op::Load, 201 | Some(type_id), 202 | Some(res_id), 203 | vec![ 204 | Operand::IdRef(var_id), 205 | Operand::MemoryAccess(MemoryAccess::empty()), 206 | ], 207 | )); 208 | 209 | Ok((attr_type, res_id)) 210 | } else { 211 | let (struct_id, var_id) = module.register_uniform(location, attr_type); 212 | let index_id = module.register_constant(&TypedValue::Int(location as i32))?; 213 | 214 | name.decorate_member(module, struct_id, location); 215 | 216 | if let TypeName::Mat(_, vec) = attr_type { 217 | module.push_annotation(Instruction::new( 218 | Op::MemberDecorate, 219 | None, 220 | None, 221 | vec![ 222 | Operand::IdRef(struct_id), 223 | Operand::LiteralInt32(location), 224 | Operand::Decoration(Decoration::MatrixStride), 225 | Operand::LiteralInt32(vec.size()), 226 | ], 227 | )); 228 | module.push_annotation(Instruction::new( 229 | Op::MemberDecorate, 230 | None, 231 | None, 232 | vec![ 233 | Operand::IdRef(struct_id), 234 | Operand::LiteralInt32(location), 235 | Operand::Decoration(Decoration::ColMajor), 236 | ], 237 | )); 238 | } 239 | 240 | let chain_id = module.get_id(); 241 | module.push_instruction(Instruction::new( 242 | Op::AccessChain, 243 | Some(ptr_type), 244 | Some(chain_id), 245 | vec![Operand::IdRef(var_id), Operand::IdRef(index_id)], 246 | )); 247 | 248 | let res_id = module.get_id(); 249 | module.push_instruction(Instruction::new( 250 | Op::Load, 251 | Some(type_id), 252 | Some(res_id), 253 | vec![ 254 | Operand::IdRef(chain_id), 255 | Operand::MemoryAccess(MemoryAccess::empty()), 256 | ], 257 | )); 258 | 259 | Ok((attr_type, res_id)) 260 | } 261 | } 262 | 263 | Node::Constant(ref const_type) => Ok(( 264 | const_type.to_type_name(), 265 | module.register_constant(const_type)?, 266 | )), 267 | 268 | Node::Call(index) => { 269 | let (result, args) = { 270 | let (func_id, args_type, result_type) = 271 | if let Some(res) = module.get_function(index) { 272 | res 273 | } else { 274 | bail!(ErrorKind::MissingFunction(index)) 275 | }; 276 | 277 | ( 278 | result_type, 279 | iter::once(Ok(Operand::IdRef(func_id))) 280 | .chain(args.into_iter().zip(args_type).map(|((val, id), arg)| { 281 | if val == *arg { 282 | Ok(Operand::IdRef(id)) 283 | } else { 284 | bail!(ErrorKind::BadArguments(Box::new([val]))); 285 | } 286 | })) 287 | .collect::>()?, 288 | ) 289 | }; 290 | 291 | let type_id = result.map(|ty| module.register_type(ty)); 292 | 293 | let res_id = module.get_id(); 294 | module.push_instruction(Instruction::new( 295 | Op::FunctionCall, 296 | type_id, 297 | Some(res_id), 298 | args, 299 | )); 300 | 301 | Ok((result.unwrap_or(TypeName::VOID), res_id)) 302 | } 303 | 304 | Node::Parameter(location, attr_type) => { 305 | let type_id = module.register_type(attr_type); 306 | 307 | let res_id = module.get_id(); 308 | module.push_parameter( 309 | location, 310 | attr_type, 311 | Instruction::new( 312 | Op::FunctionParameter, 313 | Some(type_id), 314 | Some(res_id), 315 | Vec::new(), 316 | ), 317 | )?; 318 | 319 | Ok((attr_type, res_id)) 320 | } 321 | 322 | Node::Return => { 323 | if args.len() != 1 { 324 | bail!(ErrorKind::WrongArgumentsCount(args.len(), 1)); 325 | } 326 | 327 | let (arg_type, arg_value) = args[0]; 328 | 329 | module.set_return( 330 | arg_type, 331 | Instruction::new(Op::ReturnValue, None, None, vec![Operand::IdRef(arg_value)]), 332 | )?; 333 | 334 | Ok((arg_type, arg_value)) 335 | } 336 | 337 | Node::Loop(cond, body) => operations::loop_(cond, body, module, args), 338 | 339 | Node::Construct(output_type) => { 340 | let type_id = module.register_type(output_type); 341 | let res_id = module.get_id(); 342 | 343 | module.push_instruction(Instruction::new( 344 | Op::CompositeConstruct, 345 | Some(type_id), 346 | Some(res_id), 347 | match *output_type { 348 | TypeName::Vec(size, data_type) => { 349 | if args.len() != size as usize { 350 | bail!(ErrorKind::WrongArgumentsCount(args.len(), size as usize)); 351 | } 352 | 353 | let res: Result> = { 354 | args.into_iter() 355 | .map(|(ty, val)| { 356 | if ty != data_type { 357 | bail!(ErrorKind::BadArguments(Box::new([ty]))); 358 | } 359 | 360 | Ok(Operand::IdRef(val)) 361 | }) 362 | .collect() 363 | }; 364 | 365 | res? 366 | } 367 | TypeName::Mat(size, vec_type) => { 368 | if args.len() != size as usize { 369 | bail!(ErrorKind::WrongArgumentsCount(args.len(), size as usize)); 370 | } 371 | 372 | let res: Result> = { 373 | args.into_iter() 374 | .map(|(ty, val)| { 375 | if ty != vec_type { 376 | bail!(ErrorKind::BadArguments(Box::new([ty]))); 377 | } 378 | 379 | Ok(Operand::IdRef(val)) 380 | }) 381 | .collect() 382 | }; 383 | 384 | res? 385 | } 386 | _ => bail!(ErrorKind::BadArguments(Box::new([output_type]))), 387 | }, 388 | )); 389 | 390 | Ok((output_type, res_id)) 391 | } 392 | 393 | Node::Extract(index) => { 394 | if args.len() != 1 { 395 | bail!(ErrorKind::WrongArgumentsCount(args.len(), 1)); 396 | } 397 | 398 | let (arg_type, arg_value) = args[0]; 399 | match *arg_type { 400 | TypeName::Vec(len, data_ty) => { 401 | if index >= len { 402 | bail!(ErrorKind::IndexOutOfBound(index, len)); 403 | } 404 | 405 | let type_id = module.register_type(data_ty); 406 | let res_id = module.get_id(); 407 | 408 | module.push_instruction(Instruction::new( 409 | Op::CompositeExtract, 410 | Some(type_id), 411 | Some(res_id), 412 | vec![Operand::IdRef(arg_value), Operand::LiteralInt32(index)], 413 | )); 414 | 415 | Ok((data_ty, res_id)) 416 | } 417 | _ => bail!(ErrorKind::BadArguments(Box::new([arg_type]))), 418 | } 419 | } 420 | 421 | Node::Add => operations::add(module, args), 422 | Node::Subtract => operations::subtract(module, args), 423 | Node::Multiply => operations::multiply(module, &args), 424 | Node::Divide => operations::divide(module, args), 425 | Node::Modulus => operations::modulus(module, args), 426 | Node::Dot => operations::dot(module, &args), 427 | 428 | Node::Clamp => operations::clamp(module, args), 429 | Node::Mix => operations::mix(module, args), 430 | 431 | Node::Normalize => impl_glsl_call!(Normalize, 1), 432 | Node::Cross => impl_glsl_call!(Cross, 2), 433 | 434 | Node::Pow => impl_glsl_call!(Pow, 2), 435 | Node::Sqrt => impl_glsl_call!(Sqrt, 1), 436 | Node::Log => impl_glsl_call!(Log, 1), 437 | Node::Floor => impl_glsl_call!(Floor, 1), 438 | Node::Ceil => impl_glsl_call!(Ceil, 1), 439 | Node::Round => impl_glsl_call!(Round, 1), 440 | Node::Abs => impl_glsl_call!(FAbs, 1), 441 | Node::Step => impl_glsl_call!(Step, 2), 442 | Node::Smoothstep => impl_glsl_call!(SmoothStep, 3), 443 | Node::Inverse => impl_glsl_call!(MatrixInverse, 1), 444 | 445 | Node::Sin => operations::sin(module, args), 446 | Node::Cos => operations::cos(module, args), 447 | Node::Tan => operations::tan(module, args), 448 | 449 | Node::Min => operations::min(module, args), 450 | Node::Max => operations::max(module, args), 451 | 452 | Node::Length => operations::length(module, args), 453 | Node::Distance => operations::distance(module, &args), 454 | Node::Reflect => operations::reflect(module, &args), 455 | Node::Refract => operations::refract(module, &args), 456 | 457 | Node::Sample => operations::sample(module, &args), 458 | 459 | Node::Equal => operations::eq(module, &args), 460 | Node::NotEqual => operations::ne(module, &args), 461 | Node::Greater => operations::gt(module, &args), 462 | Node::GreaterEqual => operations::gte(module, &args), 463 | Node::Less => operations::lt(module, &args), 464 | Node::LessEqual => operations::lte(module, &args), 465 | } 466 | } 467 | } 468 | 469 | impl fmt::Display for Node { 470 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 471 | write!(f, "{}", self.to_string()) 472 | } 473 | } 474 | 475 | /// Name information for a variable 476 | /// Can be a well-known builtin, a custom string, or nothing 477 | /// (the variable is only adressed by its location) 478 | #[derive(Debug)] 479 | pub enum VariableName { 480 | BuiltIn(BuiltIn), 481 | Named(String), 482 | None, 483 | } 484 | 485 | impl VariableName { 486 | pub(crate) fn decorate_variable(&self, module: &mut impl Builder, var_id: Word) { 487 | match *self { 488 | VariableName::BuiltIn(built_in) => { 489 | module.push_annotation(Instruction::new( 490 | Op::Decorate, 491 | None, 492 | None, 493 | vec![ 494 | Operand::IdRef(var_id), 495 | Operand::Decoration(Decoration::BuiltIn), 496 | Operand::BuiltIn(built_in), 497 | ], 498 | )); 499 | } 500 | 501 | VariableName::Named(ref name) => { 502 | module.push_debug(Instruction::new( 503 | Op::Name, 504 | None, 505 | None, 506 | vec![Operand::IdRef(var_id), Operand::LiteralString(name.clone())], 507 | )); 508 | } 509 | 510 | VariableName::None => {} 511 | } 512 | } 513 | 514 | fn decorate_member(&self, module: &mut impl Builder, var_id: Word, offset: Word) { 515 | match *self { 516 | VariableName::BuiltIn(built_in) => { 517 | module.push_annotation(Instruction::new( 518 | Op::MemberDecorate, 519 | None, 520 | None, 521 | vec![ 522 | Operand::IdRef(var_id), 523 | Operand::LiteralInt32(offset), 524 | Operand::Decoration(Decoration::BuiltIn), 525 | Operand::BuiltIn(built_in), 526 | ], 527 | )); 528 | } 529 | 530 | VariableName::Named(ref name) => { 531 | module.push_debug(Instruction::new( 532 | Op::MemberName, 533 | None, 534 | None, 535 | vec![ 536 | Operand::IdRef(var_id), 537 | Operand::LiteralInt32(offset), 538 | Operand::LiteralString(name.clone()), 539 | ], 540 | )); 541 | } 542 | 543 | VariableName::None => {} 544 | } 545 | } 546 | } 547 | -------------------------------------------------------------------------------- /rasen/src/operations/flow.rs: -------------------------------------------------------------------------------- 1 | use rspirv::mr::{Instruction, Operand}; 2 | use spirv_headers::{LoopControl, MemoryAccess, Op, StorageClass}; 3 | 4 | use builder::Builder; 5 | use errors::{ErrorKind, Result}; 6 | use module::FunctionRef; 7 | use types::TypeName; 8 | 9 | pub(crate) fn loop_( 10 | cond: FunctionRef, 11 | body: FunctionRef, 12 | module: &mut impl Builder, 13 | args: Vec<(&'static TypeName, u32)>, 14 | ) -> Result<(&'static TypeName, u32)> { 15 | if args.len() != 1 { 16 | bail!(ErrorKind::WrongArgumentsCount(args.len(), 1)); 17 | } 18 | 19 | let (init_ty, init_id) = args[0]; 20 | 21 | let (cond_id, cond_args, cond_res) = if let Some(res) = module.get_function(cond) { 22 | res 23 | } else { 24 | bail!(ErrorKind::MissingFunction(cond)) 25 | }; 26 | 27 | assert_eq!(cond_args, &[init_ty]); 28 | assert_eq!( 29 | cond_res, 30 | Some(TypeName::BOOL), 31 | "Condition function should return a boolean" 32 | ); 33 | 34 | let (body_id, body_args, body_res) = if let Some(res) = module.get_function(body) { 35 | res 36 | } else { 37 | bail!(ErrorKind::MissingFunction(body)) 38 | }; 39 | 40 | assert_eq!(body_args, &[init_ty]); 41 | assert_eq!(body_res, Some(init_ty)); 42 | 43 | let res_type = module.register_type(init_ty); 44 | let var_type = module.register_type(init_ty.as_ptr(false)); 45 | let bool_type = module.register_type(TypeName::BOOL); 46 | 47 | let state_id = module.get_id(); 48 | module.push_instruction(Instruction::new( 49 | Op::Variable, 50 | Some(var_type), 51 | Some(state_id), 52 | vec![Operand::StorageClass(StorageClass::Function)], 53 | )); 54 | 55 | module.push_instruction(Instruction::new( 56 | Op::Store, 57 | None, 58 | None, 59 | vec![ 60 | Operand::IdRef(state_id), 61 | Operand::IdRef(init_id), 62 | Operand::MemoryAccess(MemoryAccess::empty()), 63 | ], 64 | )); 65 | let header_block = module.get_id(); 66 | module.push_instruction(Instruction::new( 67 | Op::Branch, 68 | None, 69 | None, 70 | vec![Operand::IdRef(header_block)], 71 | )); 72 | 73 | // Header Block 74 | module.push_instruction(Instruction::new( 75 | Op::Label, 76 | None, 77 | Some(header_block), 78 | Vec::new(), 79 | )); 80 | 81 | let merge_block = module.get_id(); 82 | let continue_block = module.get_id(); 83 | 84 | module.push_instruction(Instruction::new( 85 | Op::LoopMerge, 86 | None, 87 | None, 88 | vec![ 89 | Operand::IdRef(merge_block), 90 | Operand::IdRef(continue_block), 91 | Operand::LoopControl(LoopControl::NONE), 92 | ], 93 | )); 94 | 95 | let entry_block = module.get_id(); 96 | module.push_instruction(Instruction::new( 97 | Op::Branch, 98 | None, 99 | None, 100 | vec![Operand::IdRef(entry_block)], 101 | )); 102 | 103 | // Entry Block 104 | module.push_instruction(Instruction::new( 105 | Op::Label, 106 | None, 107 | Some(entry_block), 108 | Vec::new(), 109 | )); 110 | 111 | let cond_load_id = module.get_id(); 112 | module.push_instruction(Instruction::new( 113 | Op::Load, 114 | Some(res_type), 115 | Some(cond_load_id), 116 | vec![ 117 | Operand::IdRef(state_id), 118 | Operand::MemoryAccess(MemoryAccess::empty()), 119 | ], 120 | )); 121 | 122 | let check_id = module.get_id(); 123 | module.push_instruction(Instruction::new( 124 | Op::FunctionCall, 125 | Some(bool_type), 126 | Some(check_id), 127 | vec![Operand::IdRef(cond_id), Operand::IdRef(cond_load_id)], 128 | )); 129 | 130 | let body_block = module.get_id(); 131 | module.push_instruction(Instruction::new( 132 | Op::BranchConditional, 133 | None, 134 | None, 135 | vec![ 136 | Operand::IdRef(check_id), 137 | Operand::IdRef(body_block), 138 | Operand::IdRef(merge_block), 139 | ], 140 | )); 141 | 142 | // Body Block 143 | module.push_instruction(Instruction::new( 144 | Op::Label, 145 | None, 146 | Some(body_block), 147 | Vec::new(), 148 | )); 149 | 150 | let ret_id = module.get_id(); 151 | module.push_instruction(Instruction::new( 152 | Op::FunctionCall, 153 | Some(res_type), 154 | Some(ret_id), 155 | vec![Operand::IdRef(body_id), Operand::IdRef(cond_load_id)], 156 | )); 157 | 158 | module.push_instruction(Instruction::new( 159 | Op::Store, 160 | None, 161 | None, 162 | vec![ 163 | Operand::IdRef(state_id), 164 | Operand::IdRef(ret_id), 165 | Operand::MemoryAccess(MemoryAccess::empty()), 166 | ], 167 | )); 168 | 169 | module.push_instruction(Instruction::new( 170 | Op::Branch, 171 | None, 172 | None, 173 | vec![Operand::IdRef(continue_block)], 174 | )); 175 | 176 | // Continue Block 177 | module.push_instruction(Instruction::new( 178 | Op::Label, 179 | None, 180 | Some(continue_block), 181 | Vec::new(), 182 | )); 183 | 184 | module.push_instruction(Instruction::new( 185 | Op::Branch, 186 | None, 187 | None, 188 | vec![Operand::IdRef(header_block)], 189 | )); 190 | 191 | // Merge Block 192 | module.push_instruction(Instruction::new( 193 | Op::Label, 194 | None, 195 | Some(merge_block), 196 | Vec::new(), 197 | )); 198 | 199 | let result_id = module.get_id(); 200 | module.push_instruction(Instruction::new( 201 | Op::Load, 202 | Some(res_type), 203 | Some(result_id), 204 | vec![ 205 | Operand::IdRef(state_id), 206 | Operand::MemoryAccess(MemoryAccess::empty()), 207 | ], 208 | )); 209 | 210 | Ok((init_ty, result_id)) 211 | } 212 | -------------------------------------------------------------------------------- /rasen/src/operations/glsl.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::enum_glob_use)] 2 | 3 | use spirv_headers::*; 4 | use spirv_headers::GLOp::*; 5 | use rspirv::mr::{ 6 | Instruction, Operand 7 | }; 8 | 9 | use builder::Builder; 10 | use types::*; 11 | use errors::*; 12 | 13 | macro_rules! unary_vec { 14 | ( $name:ident, $op:ident ) => { 15 | #[inline] 16 | pub(crate) fn $name(builder: &mut B, args: Vec<(&'static TypeName, u32)>) -> Result<(&'static TypeName, u32)> { 17 | use types::TypeName::*; 18 | 19 | if args.len() != 1 { 20 | bail!(ErrorKind::WrongArgumentsCount(args.len(), 1)); 21 | } 22 | 23 | let (arg_ty, arg_val) = args[0]; 24 | let (res_type, scalar) = if let Vec(_, scalar) = *arg_ty { 25 | (builder.register_type(scalar), scalar) 26 | } else { 27 | bail!(ErrorKind::BadArguments(Box::new([ arg_ty ]))); 28 | }; 29 | 30 | let res_id = builder.get_id(); 31 | let ext_id = builder.import_set("GLSL.std.450"); 32 | 33 | builder.push_instruction( 34 | Instruction::new( 35 | Op::ExtInst, 36 | Some(res_type), 37 | Some(res_id), 38 | vec![ 39 | Operand::IdRef(ext_id), 40 | Operand::LiteralExtInstInteger($op as Word), 41 | Operand::IdRef(arg_val) 42 | ] 43 | ) 44 | ); 45 | 46 | Ok((scalar, res_id)) 47 | } 48 | }; 49 | } 50 | 51 | unary_vec!(sin, Sin); 52 | unary_vec!(cos, Cos); 53 | unary_vec!(tan, Tan); 54 | unary_vec!(length, Length); 55 | 56 | macro_rules! variadic_any { 57 | ( $name:ident, $op:ident, $scode:ident, $ucode:ident, $fcode:ident ) => { 58 | #[inline] 59 | pub(crate) fn $name(builder: &mut B, args: Vec<(&'static TypeName, u32)>) -> Result<(&'static TypeName, u32)> { 60 | use types::TypeName::*; 61 | 62 | let (l_arg, r_arg) = match args.len() { 63 | 2 => ( 64 | args[0], 65 | args[1], 66 | ), 67 | n if n > 2 => ( 68 | $name(builder, args[0..n - 1].to_vec())?, 69 | args[n - 1], 70 | ), 71 | n => bail!(ErrorKind::WrongArgumentsCount(n, 2)), 72 | }; 73 | 74 | let (l_type, l_value) = l_arg; 75 | let (r_type, r_value) = r_arg; 76 | 77 | let inst_id = match (l_type, r_type) { 78 | _ if l_type == r_type && r_type.is_signed() => $scode, 79 | (&Vec(l_len, l_scalar), &Vec(r_len, r_scalar)) if l_len == r_len && l_scalar == r_scalar && r_scalar.is_signed() => $scode, 80 | 81 | _ if l_type == r_type && r_type.is_integer() && !r_type.is_signed() => $ucode, 82 | (&Vec(l_len, l_scalar), &Vec(r_len, r_scalar)) if l_len == r_len && l_scalar == r_scalar && r_scalar.is_integer() => $ucode, 83 | 84 | _ if l_type == r_type && r_type.is_float() => $fcode, 85 | (&Vec(l_len, l_scalar), &Vec(r_len, r_scalar)) if l_len == r_len && l_scalar == r_scalar && r_scalar.is_float() => $fcode, 86 | 87 | _ => bail!(ErrorKind::BadArguments(Box::new([ 88 | l_type, r_type 89 | ]))), 90 | }; 91 | 92 | let res_type = builder.register_type(l_type); 93 | let res_id = builder.get_id(); 94 | 95 | let ext_id = builder.import_set("GLSL.std.450"); 96 | 97 | builder.push_instruction( 98 | Instruction::new( 99 | Op::ExtInst, 100 | Some(res_type), 101 | Some(res_id), 102 | vec![ 103 | Operand::IdRef(ext_id), 104 | Operand::LiteralExtInstInteger(inst_id as Word), 105 | Operand::IdRef(l_value), 106 | Operand::IdRef(r_value) 107 | ] 108 | ) 109 | ); 110 | 111 | Ok((l_type, res_id)) 112 | } 113 | }; 114 | } 115 | 116 | variadic_any!(min, Min, SMin, UMin, FMin); 117 | variadic_any!(max, Max, SMax, UMax, FMax); 118 | 119 | macro_rules! trinary_any { 120 | ($name:ident, $op:ident, $fcode:ident$(, $scode:ident, $ucode:ident )*) => { 121 | #[inline] 122 | pub(crate) fn $name(builder: &mut B, args: Vec<(&'static TypeName, u32)>) -> Result<(&'static TypeName, u32)> { 123 | use types::TypeName::*; 124 | 125 | if args.len() != 3 { 126 | bail!(ErrorKind::WrongArgumentsCount(args.len(), 3)); 127 | } 128 | 129 | let (a_type, a_value) = args[0]; 130 | let (b_type, b_value) = args[1]; 131 | let (c_type, c_value) = args[2]; 132 | 133 | let inst_id = match (a_type, b_type, c_type) { 134 | $( 135 | _ if a_type == b_type && b_type == c_type && a_type.is_signed() => $scode, 136 | (&Vec(a_len, a_scalar), &Vec(b_len, b_scalar), &Vec(c_len, c_scalar)) if a_len == b_len && b_len == c_len && a_scalar == b_scalar && b_scalar == c_scalar && a_scalar.is_signed() => $scode, 137 | 138 | _ if a_type == b_type && b_type == c_type && a_type.is_integer() && !a_type.is_signed() => $ucode, 139 | (&Vec(a_len, a_scalar), &Vec(b_len, b_scalar), &Vec(c_len, c_scalar)) if a_len == b_len && b_len == c_len && a_scalar == b_scalar && b_scalar == c_scalar && a_scalar.is_integer() && !a_type.is_signed() => $ucode, 140 | )* 141 | 142 | _ if a_type == b_type && b_type == c_type && a_type.is_float() => $fcode, 143 | (&Vec(a_len, a_scalar), &Vec(b_len, b_scalar), &Vec(c_len, c_scalar)) if a_len == b_len && b_len == c_len && a_scalar == b_scalar && b_scalar == c_scalar && a_scalar.is_float() => $fcode, 144 | 145 | _ => bail!(ErrorKind::BadArguments(Box::new([ 146 | a_type, b_type, c_type 147 | ]))), 148 | }; 149 | 150 | let res_type = builder.register_type(a_type); 151 | let res_id = builder.get_id(); 152 | 153 | let ext_id = builder.import_set("GLSL.std.450"); 154 | 155 | builder.push_instruction( 156 | Instruction::new( 157 | Op::ExtInst, 158 | Some(res_type), 159 | Some(res_id), 160 | vec![ 161 | Operand::IdRef(ext_id), 162 | Operand::LiteralExtInstInteger(inst_id as Word), 163 | Operand::IdRef(a_value), 164 | Operand::IdRef(b_value), 165 | Operand::IdRef(c_value) 166 | ] 167 | ) 168 | ); 169 | 170 | Ok((a_type, res_id)) 171 | } 172 | }; 173 | } 174 | 175 | trinary_any!(clamp, Clamp, FClamp, SClamp, UClamp); 176 | trinary_any!(mix, Mix, FMix); 177 | 178 | #[inline] 179 | pub(crate) fn distance(builder: &mut B, args: &[(&'static TypeName, u32)]) -> Result<(&'static TypeName, u32)> { 180 | use types::TypeName::*; 181 | 182 | if args.len() != 2 { 183 | bail!(ErrorKind::WrongArgumentsCount(args.len(), 2)); 184 | } 185 | 186 | let (l_type, l_value) = args[0]; 187 | let (r_type, r_value) = args[1]; 188 | 189 | match (l_type, r_type) { 190 | (&Vec(l_size, l_scalar), &Vec(r_size, r_scalar)) if l_size == r_size && l_scalar == r_scalar => { 191 | let res_type = builder.register_type(l_scalar); 192 | 193 | let res_id = builder.get_id(); 194 | let ext_id = builder.import_set("GLSL.std.450"); 195 | 196 | builder.push_instruction( 197 | Instruction::new( 198 | Op::ExtInst, 199 | Some(res_type), 200 | Some(res_id), 201 | vec![ 202 | Operand::IdRef(ext_id), 203 | Operand::LiteralExtInstInteger(Distance as u32), 204 | Operand::IdRef(l_value), 205 | Operand::IdRef(r_value) 206 | ] 207 | ) 208 | ); 209 | 210 | Ok((l_scalar, res_id)) 211 | }, 212 | _ => bail!(ErrorKind::BadArguments(Box::new([ 213 | l_type, r_type 214 | ]))), 215 | } 216 | } 217 | 218 | #[inline] 219 | pub(crate) fn reflect(builder: &mut B, args: &[(&'static TypeName, u32)]) -> Result<(&'static TypeName, u32)> { 220 | use types::TypeName::*; 221 | 222 | if args.len() != 2 { 223 | bail!(ErrorKind::WrongArgumentsCount(args.len(), 2)); 224 | } 225 | 226 | let (l_type, l_value) = args[0]; 227 | let (r_type, r_value) = args[1]; 228 | 229 | match (l_type, r_type) { 230 | (&Vec(l_size, l_scalar), &Vec(r_size, r_scalar)) if l_size == r_size && l_scalar == r_scalar => { 231 | let vec_type = builder.register_type(l_type); 232 | 233 | let result_id = builder.get_id(); 234 | let ext_id = builder.import_set("GLSL.std.450"); 235 | 236 | builder.push_instruction( 237 | Instruction::new( 238 | Op::ExtInst, 239 | Some(vec_type), 240 | Some(result_id), 241 | vec![ 242 | Operand::IdRef(ext_id), 243 | Operand::LiteralExtInstInteger(Reflect as u32), 244 | Operand::IdRef(l_value), 245 | Operand::IdRef(r_value) 246 | ] 247 | ) 248 | ); 249 | 250 | Ok((l_type, result_id)) 251 | }, 252 | _ => bail!(ErrorKind::BadArguments(Box::new([ l_type, r_type ]))), 253 | } 254 | } 255 | 256 | #[inline] 257 | pub(crate) fn refract(builder: &mut B, args: &[(&'static TypeName, u32)]) -> Result<(&'static TypeName, u32)> { 258 | use types::TypeName::*; 259 | 260 | if args.len() != 3 { 261 | bail!(ErrorKind::WrongArgumentsCount(args.len(), 3)); 262 | } 263 | 264 | let (l_type, l_value) = args[0]; 265 | let (r_type, r_value) = args[1]; 266 | let (i_type, i_value) = args[2]; 267 | 268 | match (l_type, r_type) { 269 | (&Vec(l_size, l_scalar), &Vec(r_size, r_scalar)) if l_size == r_size && l_scalar == r_scalar && l_scalar == i_type && i_type.is_float() => { 270 | let vec_type = builder.register_type(l_type); 271 | 272 | let res_id = builder.get_id(); 273 | let ext_id = builder.import_set("GLSL.std.450"); 274 | 275 | builder.push_instruction( 276 | Instruction::new( 277 | Op::ExtInst, 278 | Some(vec_type), 279 | Some(res_id), 280 | vec![ 281 | Operand::IdRef(ext_id), 282 | Operand::LiteralExtInstInteger(Refract as u32), 283 | Operand::IdRef(l_value), 284 | Operand::IdRef(r_value), 285 | Operand::IdRef(i_value) 286 | ] 287 | ) 288 | ); 289 | 290 | Ok((l_type, res_id)) 291 | }, 292 | _ => bail!(ErrorKind::BadArguments(Box::new([ l_type, r_type, i_type ]))), 293 | } 294 | } 295 | 296 | #[inline] 297 | pub(crate) fn sample(builder: &mut B, args: &[(&'static TypeName, u32)]) -> Result<(&'static TypeName, u32)> { 298 | use types::TypeName::*; 299 | 300 | if args.len() < 2 || args.len() > 3 { 301 | bail!(ErrorKind::WrongArgumentsCount(args.len(), 2)); 302 | } 303 | 304 | let (image_type, image_value) = args[0]; 305 | let (coords_type, coords_value) = args[1]; 306 | 307 | match (image_type, coords_type) { 308 | (&Sampler(sampled_type, Dim::Dim1D), &Vec(1, coords_scalar)) | 309 | (&Sampler(sampled_type, Dim::Dim2D), &Vec(2, coords_scalar)) | 310 | (&Sampler(sampled_type, Dim::Dim3D), &Vec(3, coords_scalar)) | 311 | (&Sampler(sampled_type, Dim::DimCube), &Vec(3, coords_scalar)) | 312 | (&Sampler(sampled_type, Dim::DimRect), &Vec(2, coords_scalar)) | 313 | (&Sampler(sampled_type, Dim::DimBuffer), &Vec(1, coords_scalar)) | 314 | 315 | (&Sampler(sampled_type, Dim::DimBuffer), coords_scalar) | 316 | (&Sampler(sampled_type, Dim::Dim1D), coords_scalar) if sampled_type.is_num() && coords_scalar.is_float() => { 317 | let res_type = match *sampled_type { 318 | Int(true) => TypeName::IVEC4, 319 | Int(false) => TypeName::UVEC4, 320 | Float(false) => TypeName::VEC4, 321 | Float(true) => TypeName::DVEC4, 322 | _ => unreachable!(), 323 | }; 324 | 325 | let vec_type = builder.register_type(res_type); 326 | let res_id = builder.get_id(); 327 | let mut operands = vec![ 328 | Operand::IdRef(image_value), 329 | Operand::IdRef(coords_value), 330 | ]; 331 | 332 | if let Some(&(bias_type, bias_value)) = args.get(2) { 333 | if bias_type != TypeName::FLOAT { 334 | bail!(ErrorKind::BadArguments(Box::new([ image_type, coords_type, bias_type ]))); 335 | } 336 | 337 | operands.push(Operand::ImageOperands(ImageOperands::BIAS)); 338 | operands.push(Operand::IdRef(bias_value)); 339 | } 340 | 341 | builder.push_instruction( 342 | Instruction::new( 343 | Op::ImageSampleImplicitLod, 344 | Some(vec_type), 345 | Some(res_id), 346 | operands, 347 | ) 348 | ); 349 | 350 | Ok((res_type, res_id)) 351 | }, 352 | 353 | _ => if let Some(&(bias_type, _)) = args.get(2) { 354 | bail!(ErrorKind::BadArguments(Box::new([ image_type, coords_type, bias_type ]))) 355 | } else { 356 | bail!(ErrorKind::BadArguments(Box::new([ image_type, coords_type ]))) 357 | }, 358 | } 359 | } 360 | -------------------------------------------------------------------------------- /rasen/src/operations/math.rs: -------------------------------------------------------------------------------- 1 | use rspirv::mr::{Instruction, Operand}; 2 | use spirv_headers::*; 3 | 4 | use builder::Builder; 5 | use errors::*; 6 | use types::*; 7 | 8 | #[inline] 9 | fn imul(builder: &mut B, res_type: &'static TypeName, res_id: u32, lhs: u32, rhs: u32) { 10 | let res_type = builder.register_type(res_type); 11 | 12 | builder.push_instruction(Instruction::new( 13 | Op::IMul, 14 | Some(res_type), 15 | Some(res_id), 16 | vec![Operand::IdRef(lhs), Operand::IdRef(rhs)], 17 | )); 18 | } 19 | #[inline] 20 | fn fmul(builder: &mut B, res_type: &'static TypeName, res_id: u32, lhs: u32, rhs: u32) { 21 | let res_type = builder.register_type(res_type); 22 | 23 | builder.push_instruction(Instruction::new( 24 | Op::FMul, 25 | Some(res_type), 26 | Some(res_id), 27 | vec![Operand::IdRef(lhs), Operand::IdRef(rhs)], 28 | )); 29 | } 30 | #[inline] 31 | fn vector_times_scalar( 32 | builder: &mut B, 33 | res_type: &'static TypeName, 34 | res_id: u32, 35 | vector: u32, 36 | scalar: u32, 37 | ) { 38 | let res_type = builder.register_type(res_type); 39 | 40 | builder.push_instruction(Instruction::new( 41 | Op::VectorTimesScalar, 42 | Some(res_type), 43 | Some(res_id), 44 | vec![Operand::IdRef(vector), Operand::IdRef(scalar)], 45 | )); 46 | } 47 | #[inline] 48 | fn matrix_times_scalar( 49 | builder: &mut B, 50 | res_type: &'static TypeName, 51 | res_id: u32, 52 | matrix: u32, 53 | scalar: u32, 54 | ) { 55 | let res_type = builder.register_type(res_type); 56 | 57 | builder.push_instruction(Instruction::new( 58 | Op::MatrixTimesScalar, 59 | Some(res_type), 60 | Some(res_id), 61 | vec![Operand::IdRef(matrix), Operand::IdRef(scalar)], 62 | )); 63 | } 64 | 65 | #[inline] 66 | pub(crate) fn multiply( 67 | builder: &mut B, 68 | args: &[(&'static TypeName, u32)], 69 | ) -> Result<(&'static TypeName, u32)> { 70 | use types::TypeName::*; 71 | 72 | let (l_arg, r_arg) = match args.len() { 73 | 2 => (args[0], args[1]), 74 | n if n > 2 => (multiply(builder, &args[0..n - 1])?, args[n - 1]), 75 | n => bail!(ErrorKind::WrongArgumentsCount(n, 2)), 76 | }; 77 | 78 | let (l_type, l_value) = l_arg; 79 | let (r_type, r_value) = r_arg; 80 | let res_id = builder.get_id(); 81 | 82 | let res_type = match (l_type, r_type) { 83 | _ if l_type == r_type && r_type.is_integer() => { 84 | imul(builder, l_type, res_id, l_value, r_value); 85 | l_type 86 | } 87 | (&Vec(l_len, l_scalar), &Vec(r_len, r_scalar)) 88 | if l_len == r_len && l_scalar == r_scalar && r_scalar.is_integer() => 89 | { 90 | imul(builder, l_type, res_id, l_value, r_value); 91 | l_type 92 | } 93 | 94 | _ if l_type == r_type && l_type.is_float() => { 95 | fmul(builder, l_type, res_id, l_value, r_value); 96 | l_type 97 | } 98 | (&Vec(l_len, l_scalar), &Vec(r_len, r_scalar)) 99 | if l_len == r_len && l_scalar == r_scalar && r_scalar.is_float() => 100 | { 101 | fmul(builder, l_type, res_id, l_value, r_value); 102 | l_type 103 | } 104 | 105 | (&Vec(_, v_scalar), t_scalar) if t_scalar == v_scalar && t_scalar.is_float() => { 106 | vector_times_scalar(builder, l_type, res_id, r_value, l_value); 107 | l_type 108 | } 109 | (t_scalar, &Vec(_, v_scalar)) if t_scalar == v_scalar && t_scalar.is_float() => { 110 | vector_times_scalar(builder, r_type, res_id, l_value, r_value); 111 | r_type 112 | } 113 | 114 | (&Mat(_, &Vec(_, m_scalar)), t_scalar) if t_scalar == m_scalar && t_scalar.is_float() => { 115 | matrix_times_scalar(builder, l_type, res_id, l_value, r_value); 116 | l_type 117 | } 118 | (t_scalar, &Mat(_, &Vec(_, m_scalar))) if t_scalar == m_scalar && t_scalar.is_float() => { 119 | matrix_times_scalar(builder, r_type, res_id, r_value, l_value); 120 | r_type 121 | } 122 | 123 | (&Vec(v_len, l_scalar), &Mat(m_len, &Vec(_, r_scalar))) 124 | if v_len == m_len && l_scalar == r_scalar && l_scalar.is_float() => 125 | { 126 | let res_type = builder.register_type(l_type); 127 | 128 | builder.push_instruction(Instruction::new( 129 | Op::VectorTimesMatrix, 130 | Some(res_type), 131 | Some(res_id), 132 | vec![Operand::IdRef(l_value), Operand::IdRef(r_value)], 133 | )); 134 | 135 | l_type 136 | } 137 | (&Mat(m_len, &Vec(_, l_scalar)), &Vec(v_len, r_scalar)) 138 | if v_len == m_len && l_scalar == r_scalar && l_scalar.is_float() => 139 | { 140 | let res_type = builder.register_type(r_type); 141 | 142 | builder.push_instruction(Instruction::new( 143 | Op::MatrixTimesVector, 144 | Some(res_type), 145 | Some(res_id), 146 | vec![Operand::IdRef(l_value), Operand::IdRef(r_value)], 147 | )); 148 | 149 | r_type 150 | } 151 | 152 | (&Mat(l_len, &Vec(_, l_scalar)), &Mat(r_len, &Vec(_, r_scalar))) 153 | if l_len == r_len && l_scalar == r_scalar && l_scalar.is_float() => 154 | { 155 | let res_type = builder.register_type(l_type); 156 | 157 | builder.push_instruction(Instruction::new( 158 | Op::MatrixTimesMatrix, 159 | Some(res_type), 160 | Some(res_id), 161 | vec![Operand::IdRef(l_value), Operand::IdRef(r_value)], 162 | )); 163 | 164 | l_type 165 | } 166 | 167 | _ => bail!(ErrorKind::BadArguments(Box::new([l_type, r_type]))), 168 | }; 169 | 170 | Ok((res_type, res_id)) 171 | } 172 | 173 | #[inline] 174 | pub(crate) fn dot( 175 | builder: &mut B, 176 | args: &[(&'static TypeName, u32)], 177 | ) -> Result<(&'static TypeName, u32)> { 178 | use types::TypeName::*; 179 | 180 | if args.len() != 2 { 181 | bail!(ErrorKind::WrongArgumentsCount(args.len(), 2)); 182 | } 183 | 184 | let (l_type, l_value) = args[0]; 185 | let (r_type, r_value) = args[1]; 186 | match (l_type, r_type) { 187 | (&Vec(l_size, l_scalar), &Vec(r_size, r_scalar)) 188 | if l_size == r_size && l_scalar == r_scalar && l_scalar.is_float() => 189 | { 190 | let res_type = builder.register_type(l_scalar); 191 | 192 | let result_id = builder.get_id(); 193 | 194 | builder.push_instruction(Instruction::new( 195 | Op::Dot, 196 | Some(res_type), 197 | Some(result_id), 198 | vec![Operand::IdRef(l_value), Operand::IdRef(r_value)], 199 | )); 200 | 201 | Ok((l_scalar, result_id)) 202 | } 203 | _ => bail!(ErrorKind::BadArguments(Box::new([l_type, r_type]))), 204 | } 205 | } 206 | 207 | macro_rules! impl_math_op { 208 | ( $name:ident, $node:ident, $variadic:expr, $( $opcode:ident ),* ) => { 209 | #[inline] 210 | pub(crate) fn $name(builder: &mut B, args: Vec<(&'static TypeName, u32)>) -> Result<(&'static TypeName, u32)> { 211 | use types::TypeName::*; 212 | 213 | let (l_arg, r_arg) = match args.len() { 214 | 2 => ( 215 | args[0], 216 | args[1], 217 | ), 218 | n if $variadic && n > 2 => ( 219 | $name(builder, args[0..n - 1].to_vec())?, 220 | args[n - 1], 221 | ), 222 | n => bail!(ErrorKind::WrongArgumentsCount(n, 2)), 223 | }; 224 | 225 | let result_id = builder.get_id(); 226 | 227 | let (l_type, l_value) = l_arg; 228 | let (r_type, r_value) = r_arg; 229 | 230 | macro_rules! match_types { 231 | ( $uopcode:ident, $sopcode:ident, $fopcode:ident ) => { 232 | match (l_type, r_type) { 233 | _ if l_type == r_type && r_type.is_signed() => { 234 | let res_type = builder.register_type(l_type); 235 | 236 | builder.push_instruction(Instruction::new( 237 | Op::$sopcode, 238 | Some(res_type), Some(result_id), 239 | vec![ 240 | Operand::IdRef(l_value), 241 | Operand::IdRef(r_value), 242 | ] 243 | )); 244 | 245 | l_type 246 | }, 247 | (&Vec(l_len, l_scalar), &Vec(r_len, r_scalar)) if l_len == r_len && l_scalar == r_scalar && r_scalar.is_signed() => { 248 | let res_type = builder.register_type(l_type); 249 | 250 | builder.push_instruction(Instruction::new( 251 | Op::$sopcode, 252 | Some(res_type), Some(result_id), 253 | vec![ 254 | Operand::IdRef(l_value), 255 | Operand::IdRef(r_value), 256 | ] 257 | )); 258 | 259 | l_type 260 | }, 261 | 262 | _ if l_type == r_type && r_type.is_integer() && !r_type.is_signed() => { 263 | let res_type = builder.register_type(l_type); 264 | 265 | builder.push_instruction(Instruction::new( 266 | Op::$uopcode, 267 | Some(res_type), Some(result_id), 268 | vec![ 269 | Operand::IdRef(l_value), 270 | Operand::IdRef(r_value), 271 | ] 272 | )); 273 | 274 | l_type 275 | }, 276 | (&Vec(l_len, l_scalar), &Vec(r_len, r_scalar)) if l_len == r_len && l_scalar == r_scalar && r_scalar.is_integer() => { 277 | let res_type = builder.register_type(l_type); 278 | 279 | builder.push_instruction(Instruction::new( 280 | Op::$uopcode, 281 | Some(res_type), Some(result_id), 282 | vec![ 283 | Operand::IdRef(l_value), 284 | Operand::IdRef(r_value), 285 | ] 286 | )); 287 | 288 | l_type 289 | }, 290 | 291 | _ if l_type == r_type && r_type.is_float() => { 292 | let res_type = builder.register_type(l_type); 293 | 294 | builder.push_instruction(Instruction::new( 295 | Op::$fopcode, 296 | Some(res_type), Some(result_id), 297 | vec![ 298 | Operand::IdRef(l_value), 299 | Operand::IdRef(r_value), 300 | ], 301 | )); 302 | 303 | l_type 304 | }, 305 | (&Vec(l_len, l_scalar), &Vec(r_len, r_scalar)) if l_len == r_len && l_scalar == r_scalar && r_scalar.is_float() => { 306 | let res_type = builder.register_type(l_type); 307 | 308 | builder.push_instruction( 309 | Instruction::new( 310 | Op::$fopcode, 311 | Some(res_type), 312 | Some(result_id), 313 | vec![ 314 | Operand::IdRef(l_value), 315 | Operand::IdRef(r_value), 316 | ] 317 | ) 318 | ); 319 | 320 | l_type 321 | }, 322 | 323 | _ => bail!(ErrorKind::BadArguments(Box::new([ l_type, r_type ]))), 324 | } 325 | }; 326 | ( $iopcode:ident, $fopcode:ident ) => { 327 | match (l_type, r_type) { 328 | _ if l_type == r_type && r_type.is_integer() => { 329 | let res_type = builder.register_type(l_type); 330 | 331 | builder.push_instruction(Instruction::new( 332 | Op::$iopcode, 333 | Some(res_type), Some(result_id), 334 | vec![ 335 | Operand::IdRef(l_value), 336 | Operand::IdRef(r_value), 337 | ] 338 | )); 339 | 340 | l_type 341 | }, 342 | (&Vec(l_len, l_scalar), &Vec(r_len, r_scalar)) if l_len == r_len && l_scalar == r_scalar && r_scalar.is_integer() => { 343 | let res_type = builder.register_type(l_type); 344 | 345 | builder.push_instruction(Instruction::new( 346 | Op::$iopcode, 347 | Some(res_type), Some(result_id), 348 | vec![ 349 | Operand::IdRef(r_value), 350 | Operand::IdRef(l_value), 351 | ] 352 | )); 353 | 354 | l_type 355 | }, 356 | 357 | _ if l_type == r_type && r_type.is_float() => { 358 | let res_type = builder.register_type(l_type); 359 | 360 | builder.push_instruction(Instruction::new( 361 | Op::$fopcode, 362 | Some(res_type), Some(result_id), 363 | vec![ 364 | Operand::IdRef(l_value), 365 | Operand::IdRef(r_value), 366 | ] 367 | )); 368 | 369 | l_type 370 | }, 371 | (&Vec(l_len, l_scalar), &Vec(r_len, r_scalar)) if l_len == r_len && l_scalar == r_scalar && l_scalar.is_float() => { 372 | let res_type = builder.register_type(l_type); 373 | 374 | builder.push_instruction(Instruction::new( 375 | Op::$fopcode, 376 | Some(res_type), Some(result_id), 377 | vec![ 378 | Operand::IdRef(l_value), 379 | Operand::IdRef(r_value), 380 | ] 381 | )); 382 | 383 | l_type 384 | }, 385 | 386 | _ => bail!(ErrorKind::BadArguments(Box::new([ l_type, r_type ]))), 387 | } 388 | }; 389 | } 390 | 391 | let res_type = match_types!( $( $opcode ),* ); 392 | Ok((res_type, result_id)) 393 | } 394 | }; 395 | } 396 | 397 | impl_math_op!(add, Add, true, IAdd, FAdd); 398 | impl_math_op!(subtract, Subtract, true, ISub, FSub); 399 | impl_math_op!(divide, Divide, true, UDiv, SDiv, FDiv); 400 | impl_math_op!(modulus, Modulus, false, UMod, SMod, FMod); 401 | 402 | macro_rules! impl_logical_op { 403 | ($func:ident, $u_op:ident, $s_op:ident, $f_op:ident) => { 404 | pub(crate) fn $func( 405 | builder: &mut B, 406 | args: &[(&'static TypeName, u32)], 407 | ) -> Result<(&'static TypeName, u32)> { 408 | use types::TypeName::*; 409 | 410 | let (l_arg, r_arg) = if args.len() == 2 { 411 | (args[0], args[1]) 412 | } else { 413 | bail!(ErrorKind::WrongArgumentsCount(args.len(), 2)) 414 | }; 415 | 416 | let result_id = builder.get_id(); 417 | 418 | let (l_type, l_value) = l_arg; 419 | let (r_type, r_value) = r_arg; 420 | 421 | let res_type = builder.register_type(TypeName::BOOL); 422 | 423 | match (l_type, r_type) { 424 | _ if l_type == r_type && r_type.is_signed() => { 425 | builder.push_instruction(Instruction::new( 426 | Op::$s_op, 427 | Some(res_type), 428 | Some(result_id), 429 | vec![Operand::IdRef(l_value), Operand::IdRef(r_value)], 430 | )); 431 | } 432 | (&Vec(l_len, l_scalar), &Vec(r_len, r_scalar)) 433 | if l_len == r_len && l_scalar == r_scalar && r_scalar.is_signed() => 434 | { 435 | builder.push_instruction(Instruction::new( 436 | Op::$s_op, 437 | Some(res_type), 438 | Some(result_id), 439 | vec![Operand::IdRef(l_value), Operand::IdRef(r_value)], 440 | )); 441 | } 442 | 443 | _ if l_type == r_type && r_type.is_integer() && !r_type.is_signed() => { 444 | builder.push_instruction(Instruction::new( 445 | Op::$u_op, 446 | Some(res_type), 447 | Some(result_id), 448 | vec![Operand::IdRef(l_value), Operand::IdRef(r_value)], 449 | )); 450 | } 451 | (&Vec(l_len, l_scalar), &Vec(r_len, r_scalar)) 452 | if l_len == r_len && l_scalar == r_scalar && r_scalar.is_integer() => 453 | { 454 | builder.push_instruction(Instruction::new( 455 | Op::$u_op, 456 | Some(res_type), 457 | Some(result_id), 458 | vec![Operand::IdRef(l_value), Operand::IdRef(r_value)], 459 | )); 460 | } 461 | 462 | _ if l_type == r_type && r_type.is_float() => { 463 | builder.push_instruction(Instruction::new( 464 | Op::$f_op, 465 | Some(res_type), 466 | Some(result_id), 467 | vec![Operand::IdRef(l_value), Operand::IdRef(r_value)], 468 | )); 469 | } 470 | (&Vec(l_len, l_scalar), &Vec(r_len, r_scalar)) 471 | if l_len == r_len && l_scalar == r_scalar && r_scalar.is_float() => 472 | { 473 | builder.push_instruction(Instruction::new( 474 | Op::$f_op, 475 | Some(res_type), 476 | Some(result_id), 477 | vec![Operand::IdRef(l_value), Operand::IdRef(r_value)], 478 | )); 479 | } 480 | 481 | _ => bail!(ErrorKind::BadArguments(Box::new([l_type, r_type]))), 482 | } 483 | 484 | Ok((TypeName::BOOL, result_id)) 485 | } 486 | }; 487 | } 488 | 489 | impl_logical_op!(eq, IEqual, IEqual, FOrdEqual); 490 | impl_logical_op!(ne, INotEqual, INotEqual, FOrdNotEqual); 491 | impl_logical_op!(gt, UGreaterThan, SGreaterThan, FOrdGreaterThan); 492 | impl_logical_op!( 493 | gte, 494 | UGreaterThanEqual, 495 | SGreaterThanEqual, 496 | FOrdGreaterThanEqual 497 | ); 498 | impl_logical_op!(lt, ULessThan, SLessThan, FOrdLessThan); 499 | impl_logical_op!(lte, ULessThanEqual, SLessThanEqual, FOrdLessThanEqual); 500 | -------------------------------------------------------------------------------- /rasen/src/operations/mod.rs: -------------------------------------------------------------------------------- 1 | mod flow; 2 | mod glsl; 3 | mod math; 4 | 5 | pub(crate) use self::{flow::*, glsl::*, math::*}; 6 | -------------------------------------------------------------------------------- /rasen/src/prelude.rs: -------------------------------------------------------------------------------- 1 | //! Exports everything you probably want to have in scope to get started with Rasen 2 | 3 | use std::convert::TryFrom; 4 | 5 | pub use builder::*; 6 | pub use graph::Graph; 7 | pub use module::Module; 8 | pub use node::*; 9 | pub use types::*; 10 | 11 | pub use petgraph::graph::NodeIndex; 12 | pub use spirv_headers::{BuiltIn, ExecutionModel as ShaderType}; 13 | 14 | use errors::{Error, Result}; 15 | 16 | /// Transform a node graph to SPIR-V bytecode 17 | pub fn build_program<'a, I, S>(graph: &'a I, settings: S) -> Result> 18 | where 19 | ModuleBuilder: TryFrom<(&'a I, S), Error = Error>, 20 | { 21 | let program = ModuleBuilder::try_from((graph, settings))?; 22 | program.into_binary() 23 | } 24 | 25 | /// Transform a node graph to SPIR-V assembly 26 | pub fn build_program_assembly<'a, I, S>(graph: &'a I, settings: S) -> Result 27 | where 28 | ModuleBuilder: TryFrom<(&'a I, S), Error = Error>, 29 | { 30 | let program = ModuleBuilder::try_from((graph, settings))?; 31 | program.into_assembly() 32 | } 33 | -------------------------------------------------------------------------------- /rasen/src/types.rs: -------------------------------------------------------------------------------- 1 | pub use spirv_headers::Dim; 2 | use spirv_headers::StorageClass; 3 | use std::fmt; 4 | 5 | /// Describes a SPIR-V data type 6 | #[derive(Eq, PartialEq, Hash)] 7 | pub enum TypeName { 8 | Void, 9 | /// Basic boolean type 10 | Bool, 11 | /// Integer type, signed or not 12 | Int(bool /* is_signed */), 13 | /// Floating-point type, with single or double precision 14 | Float(bool /* is_double */), 15 | /// Vector type of n components of given scalar type 16 | Vec( 17 | u32, /* component_count */ 18 | &'static TypeName, /* component_type */ 19 | ), 20 | /// Matrix type of n columns of given vector type 21 | Mat( 22 | u32, /* column_count */ 23 | &'static TypeName, /* column_type */ 24 | ), 25 | /// Composite type of an image and an actual sampler object 26 | Sampler( 27 | &'static TypeName, /* sampled_type */ 28 | Dim, /* dimensionality */ 29 | ), 30 | 31 | #[doc(hidden)] 32 | _Pointer(&'static TypeName, StorageClass), 33 | } 34 | 35 | include!(concat!(env!("OUT_DIR"), "/types.rs")); 36 | 37 | impl TypeName { 38 | pub const VOID: &'static Self = &TypeName::Void; 39 | pub const BOOL: &'static Self = &TypeName::Bool; 40 | pub const INT: &'static Self = &TypeName::Int(true); 41 | pub const UINT: &'static Self = &TypeName::Int(false); 42 | pub const FLOAT: &'static Self = &TypeName::Float(false); 43 | pub const DOUBLE: &'static Self = &TypeName::Float(true); 44 | 45 | pub(crate) const FLOAT_PTR_UNI: &'static Self = 46 | &TypeName::_Pointer(Self::FLOAT, StorageClass::Uniform); 47 | pub(crate) const FLOAT_PTR_FUN: &'static Self = 48 | &TypeName::_Pointer(Self::FLOAT, StorageClass::Function); 49 | 50 | #[inline] 51 | pub(crate) fn is_integer(&self) -> bool { 52 | match *self { 53 | TypeName::Int(_) => true, 54 | _ => false, 55 | } 56 | } 57 | #[inline] 58 | pub(crate) fn is_signed(&self) -> bool { 59 | match *self { 60 | TypeName::Int(true) => true, 61 | _ => false, 62 | } 63 | } 64 | 65 | #[inline] 66 | pub(crate) fn is_float(&self) -> bool { 67 | match *self { 68 | TypeName::Float(_) => true, 69 | _ => false, 70 | } 71 | } 72 | 73 | #[inline] 74 | pub(crate) fn is_num(&self) -> bool { 75 | self.is_integer() || self.is_float() 76 | } 77 | 78 | #[inline] 79 | pub(crate) fn size(&self) -> u32 { 80 | match *self { 81 | TypeName::Void | TypeName::Sampler(..) | TypeName::_Pointer(..) => 0, 82 | 83 | TypeName::Bool | TypeName::Int(_) | TypeName::Float(false) => 4, 84 | 85 | TypeName::Float(true) => 8, 86 | 87 | TypeName::Vec(len, ty) | TypeName::Mat(len, ty) => len * ty.size(), 88 | } 89 | } 90 | } 91 | 92 | fn print_type_prefix(f: &mut fmt::Formatter, ty: &TypeName) -> fmt::Result { 93 | match *ty { 94 | TypeName::Bool => write!(f, "b"), 95 | TypeName::Int(true) => write!(f, "i"), 96 | TypeName::Int(false) => write!(f, "u"), 97 | TypeName::Float(false) => Ok(()), 98 | TypeName::Float(true) => write!(f, "d"), 99 | _ => Err(fmt::Error), 100 | } 101 | } 102 | 103 | impl fmt::Debug for TypeName { 104 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 105 | match *self { 106 | TypeName::Void => write!(f, "void"), 107 | TypeName::Bool => write!(f, "bool"), 108 | TypeName::Int(true) => write!(f, "int"), 109 | TypeName::Int(false) => write!(f, "uint"), 110 | TypeName::Float(false) => write!(f, "float"), 111 | TypeName::Float(true) => write!(f, "double"), 112 | 113 | TypeName::Vec(len, scalar) => { 114 | print_type_prefix(f, scalar)?; 115 | write!(f, "vec{}", len) 116 | } 117 | 118 | TypeName::Mat(columns, vec) => match *vec { 119 | TypeName::Vec(rows, &TypeName::Float(false)) if columns == rows => { 120 | write!(f, "mat{}", rows) 121 | } 122 | TypeName::Vec(rows, &TypeName::Float(true)) if columns == rows => { 123 | write!(f, "dmat{}", rows) 124 | } 125 | _ => Err(fmt::Error), 126 | }, 127 | 128 | TypeName::Sampler(sampled_type, dimensionality) => { 129 | print_type_prefix(f, sampled_type)?; 130 | write!(f, "sampler")?; 131 | match dimensionality { 132 | Dim::Dim1D => write!(f, "1D"), 133 | Dim::Dim2D => write!(f, "2D"), 134 | Dim::Dim3D => write!(f, "3D"), 135 | Dim::DimCube => write!(f, "Cube"), 136 | Dim::DimRect => write!(f, "2DRect"), 137 | Dim::DimBuffer => write!(f, "Buffer"), 138 | Dim::DimSubpassData => write!(f, "SubpassData"), 139 | } 140 | } 141 | 142 | TypeName::_Pointer(inner, _) => write!(f, "&{:?}", inner), 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /rasen/tests/build.rs: -------------------------------------------------------------------------------- 1 | extern crate insta; 2 | extern crate rasen; 3 | extern crate rspirv; 4 | 5 | use rasen::prelude::*; 6 | 7 | include!("../../tests/graph.rs"); 8 | include!("../../tests/update.rs"); 9 | 10 | #[test] 11 | fn test_build_basic_vert() { 12 | let graph = build_basic_vert(); 13 | let assembly = build_module( 14 | &graph, 15 | Settings { 16 | mod_type: ShaderType::Vertex, 17 | uniforms_name: Some(String::from("Uniforms")), 18 | }, 19 | ) 20 | .unwrap(); 21 | 22 | assert_spirv_snapshot_matches!("basic.vert", assembly); 23 | } 24 | 25 | #[test] 26 | fn test_build_basic_frag() { 27 | let graph = build_basic_frag(); 28 | let assembly = build_module( 29 | &graph, 30 | Settings { 31 | mod_type: ShaderType::Fragment, 32 | uniforms_name: Some(String::from("Test1")), 33 | }, 34 | ) 35 | .unwrap(); 36 | 37 | assert_spirv_snapshot_matches!("basic.frag", assembly); 38 | } 39 | 40 | #[test] 41 | fn test_build_function() { 42 | let mut module = Module::default(); 43 | 44 | let func = module.add_function(); 45 | 46 | { 47 | let graph = &mut module[func]; 48 | let input = graph.add_node(Node::Parameter(0, TypeName::FLOAT)); 49 | let output = graph.add_node(Node::Return); 50 | graph.add_edge(input, output, 0); 51 | } 52 | 53 | { 54 | let graph = &mut module.main; 55 | let input = graph.add_node(Node::Input( 56 | 0, 57 | TypeName::FLOAT, 58 | VariableName::Named(String::from("a_input")), 59 | )); 60 | let call = graph.add_node(Node::Call(func)); 61 | let output = graph.add_node(Node::Output(0, TypeName::FLOAT, VariableName::None)); 62 | graph.add_edge(input, call, 0); 63 | graph.add_edge(call, output, 0); 64 | } 65 | 66 | let assembly = build_module( 67 | &module, 68 | Settings { 69 | mod_type: ShaderType::Vertex, 70 | uniforms_name: Some(String::from("Uniforms")), 71 | }, 72 | ) 73 | .unwrap(); 74 | 75 | assert_spirv_snapshot_matches!("functions", assembly); 76 | } 77 | -------------------------------------------------------------------------------- /rasen/tests/loop.rs: -------------------------------------------------------------------------------- 1 | extern crate insta; 2 | extern crate rasen; 3 | extern crate rspirv; 4 | 5 | use rasen::prelude::*; 6 | 7 | include!("../../tests/update.rs"); 8 | 9 | #[test] 10 | fn test_build_loop() { 11 | let mut module = Module::default(); 12 | 13 | let func_cond = module.add_function(); 14 | 15 | { 16 | let graph = &mut module[func_cond]; 17 | let input = graph.add_node(Node::Parameter(0, TypeName::FLOAT)); 18 | let cmp = graph.add_node(Node::Greater); 19 | let ten = graph.add_node(Node::Constant(TypedValue::Float(10.0))); 20 | let output = graph.add_node(Node::Return); 21 | 22 | graph.add_edge(input, cmp, 0); 23 | graph.add_edge(ten, cmp, 1); 24 | graph.add_edge(cmp, output, 0); 25 | } 26 | 27 | let func_body = module.add_function(); 28 | 29 | { 30 | let graph = &mut module[func_body]; 31 | let input = graph.add_node(Node::Parameter(0, TypeName::FLOAT)); 32 | let add = graph.add_node(Node::Add); 33 | let one = graph.add_node(Node::Constant(TypedValue::Float(1.0))); 34 | let output = graph.add_node(Node::Return); 35 | 36 | graph.add_edge(input, add, 0); 37 | graph.add_edge(one, add, 1); 38 | graph.add_edge(add, output, 0); 39 | } 40 | 41 | { 42 | let graph = &mut module.main; 43 | let input = graph.add_node(Node::Input( 44 | 0, 45 | TypeName::FLOAT, 46 | VariableName::Named(String::from("i_value")), 47 | )); 48 | let reduce = graph.add_node(Node::Loop(func_cond, func_body)); 49 | let output = graph.add_node(Node::Output( 50 | 0, 51 | TypeName::FLOAT, 52 | VariableName::Named(String::from("o_value")), 53 | )); 54 | 55 | graph.add_edge(input, reduce, 0); 56 | graph.add_edge(reduce, output, 0); 57 | } 58 | 59 | let builder = ModuleBuilder::from_module( 60 | &module, 61 | Settings { 62 | mod_type: ShaderType::Fragment, 63 | uniforms_name: Some(String::from("uniforms")), 64 | }, 65 | ) 66 | .expect("from_module"); 67 | 68 | let assembly = builder.into_assembly().expect("build"); 69 | assert_spirv_snapshot_matches!("loop.frag", assembly); 70 | } 71 | -------------------------------------------------------------------------------- /rasen/tests/snapshots/build__basic.frag.snap: -------------------------------------------------------------------------------- 1 | --- 2 | created: "2019-01-26T10:36:38.867929600+00:00" 3 | creator: insta@0.5.2 4 | expression: assembly 5 | source: rasen/tests/build.rs 6 | --- 7 | ; SPIR-V 8 | ; Version: 1.1 9 | ; Generator: Unknown 10 | ; Bound: 34 11 | OpCapability Shader 12 | %10 = OpExtInstImport "GLSL.std.450" 13 | OpMemoryModel Logical GLSL450 14 | OpEntryPoint Fragment %4 "main" %8 %27 %33 15 | OpExecutionMode %4 OriginUpperLeft 16 | OpName %8 "f_normal" 17 | OpName %23 "u_material" 18 | OpName %27 "f_uv" 19 | OpName %33 "o_col" 20 | OpDecorate %8 Location 0 21 | OpDecorate %23 Location 0 22 | OpDecorate %27 Location 1 23 | OpDecorate %33 Location 0 24 | %5 = OpTypeFloat 32 25 | %29 = OpTypeVector %5 4 26 | %32 = OpTypePointer Output %29 27 | %33 = OpVariable %32 Output 28 | %25 = OpTypeVector %5 2 29 | %26 = OpTypePointer Input %25 30 | %27 = OpVariable %26 Input 31 | %20 = OpTypeImage %5 2D 0 0 0 1 Unknown 32 | %21 = OpTypeSampledImage %20 33 | %22 = OpTypePointer Uniform %21 34 | %23 = OpVariable %22 Uniform 35 | %18 = OpConstant %5 1.0 36 | %17 = OpConstant %5 0.1 37 | %14 = OpConstant %5 0.2 38 | %13 = OpConstant %5 -0.5 39 | %12 = OpConstant %5 0.3 40 | %6 = OpTypeVector %5 3 41 | %15 = OpConstantComposite %6 %12 %13 %14 42 | %7 = OpTypePointer Input %6 43 | %8 = OpVariable %7 Input 44 | %1 = OpTypeVoid 45 | %2 = OpTypeFunction %1 46 | %4 = OpFunction %1 None %2 47 | %3 = OpLabel 48 | %9 = OpLoad %6 %8 None 49 | %11 = OpExtInst %6 %10 Normalize %9 50 | %16 = OpDot %5 %11 %15 51 | %19 = OpExtInst %5 %10 FClamp %16 %17 %18 52 | %24 = OpLoad %21 %23 None 53 | %28 = OpLoad %25 %27 None 54 | %30 = OpImageSampleImplicitLod %29 %24 %28 55 | %31 = OpVectorTimesScalar %29 %19 %30 56 | OpStore %33 %31 None 57 | OpReturn 58 | OpFunctionEnd 59 | -------------------------------------------------------------------------------- /rasen/tests/snapshots/build__basic.vert.snap: -------------------------------------------------------------------------------- 1 | --- 2 | created: "2019-01-26T10:36:38.867929600+00:00" 3 | creator: insta@0.5.2 4 | expression: assembly 5 | source: rasen/tests/build.rs 6 | --- 7 | ; SPIR-V 8 | ; Version: 1.1 9 | ; Generator: Unknown 10 | ; Bound: 56 11 | OpCapability Shader 12 | OpMemoryModel Logical GLSL450 13 | OpEntryPoint Vertex %4 "main" %26 %37 %52 %35 %49 %55 14 | OpName %11 "Uniforms" 15 | OpMemberName %9 0 "u_projection" 16 | OpMemberName %9 1 "u_view" 17 | OpMemberName %9 2 "u_model" 18 | OpName %26 "a_pos" 19 | OpName %37 "a_normal" 20 | OpName %49 "f_norm" 21 | OpName %52 "a_uv" 22 | OpName %55 "f_uv" 23 | OpDecorate %9 Block 24 | OpMemberDecorate %9 0 MatrixStride 16 25 | OpMemberDecorate %9 0 ColMajor 26 | OpMemberDecorate %9 1 MatrixStride 16 27 | OpMemberDecorate %9 1 ColMajor 28 | OpMemberDecorate %9 2 MatrixStride 16 29 | OpMemberDecorate %9 2 ColMajor 30 | OpDecorate %26 Location 0 31 | OpDecorate %35 Location 0 32 | OpDecorate %35 BuiltIn Position 33 | OpDecorate %37 Location 1 34 | OpDecorate %49 Location 1 35 | OpDecorate %52 Location 2 36 | OpDecorate %55 Location 2 37 | OpMemberDecorate %9 0 Offset 0 38 | OpMemberDecorate %9 1 Offset 64 39 | OpMemberDecorate %9 2 Offset 128 40 | %12 = OpTypeInt 32 1 41 | %20 = OpConstant %12 2 42 | %16 = OpConstant %12 1 43 | %13 = OpConstant %12 0 44 | %5 = OpTypeFloat 32 45 | %50 = OpTypeVector %5 2 46 | %54 = OpTypePointer Output %50 47 | %55 = OpVariable %54 Output 48 | %51 = OpTypePointer Input %50 49 | %52 = OpVariable %51 Input 50 | %31 = OpConstant %5 1.0 51 | %24 = OpTypeVector %5 3 52 | %48 = OpTypePointer Output %24 53 | %49 = OpVariable %48 Output 54 | %36 = OpTypePointer Input %24 55 | %37 = OpVariable %36 Input 56 | %25 = OpTypePointer Input %24 57 | %26 = OpVariable %25 Input 58 | %6 = OpTypeVector %5 4 59 | %34 = OpTypePointer Output %6 60 | %35 = OpVariable %34 Output 61 | %7 = OpTypeMatrix %6 4 62 | %8 = OpTypePointer Uniform %7 63 | %1 = OpTypeVoid 64 | %2 = OpTypeFunction %1 65 | %9 = OpTypeStruct %7 %7 %7 66 | %10 = OpTypePointer Uniform %9 67 | %11 = OpVariable %10 Uniform 68 | %4 = OpFunction %1 None %2 69 | %3 = OpLabel 70 | %14 = OpAccessChain %8 %11 %13 71 | %15 = OpLoad %7 %14 None 72 | %17 = OpAccessChain %8 %11 %16 73 | %18 = OpLoad %7 %17 None 74 | %19 = OpMatrixTimesMatrix %7 %15 %18 75 | %21 = OpAccessChain %8 %11 %20 76 | %22 = OpLoad %7 %21 None 77 | %23 = OpMatrixTimesMatrix %7 %19 %22 78 | %27 = OpLoad %24 %26 None 79 | %28 = OpCompositeExtract %5 %27 0 80 | %29 = OpCompositeExtract %5 %27 1 81 | %30 = OpCompositeExtract %5 %27 2 82 | %32 = OpCompositeConstruct %6 %28 %29 %30 %31 83 | %33 = OpMatrixTimesVector %6 %23 %32 84 | OpStore %35 %33 None 85 | %38 = OpLoad %24 %37 None 86 | %39 = OpCompositeExtract %5 %38 0 87 | %40 = OpCompositeExtract %5 %38 1 88 | %41 = OpCompositeExtract %5 %38 2 89 | %42 = OpCompositeConstruct %6 %39 %40 %41 %31 90 | %43 = OpMatrixTimesVector %6 %22 %42 91 | %44 = OpCompositeExtract %5 %43 0 92 | %45 = OpCompositeExtract %5 %43 1 93 | %46 = OpCompositeExtract %5 %43 2 94 | %47 = OpCompositeConstruct %24 %44 %45 %46 95 | OpStore %49 %47 None 96 | %53 = OpLoad %50 %52 None 97 | OpStore %55 %53 None 98 | OpReturn 99 | OpFunctionEnd 100 | -------------------------------------------------------------------------------- /rasen/tests/snapshots/build__functions.snap: -------------------------------------------------------------------------------- 1 | --- 2 | created: "2019-01-26T10:36:38.867929600+00:00" 3 | creator: insta@0.5.2 4 | expression: assembly 5 | source: rasen/tests/build.rs 6 | --- 7 | ; SPIR-V 8 | ; Version: 1.1 9 | ; Generator: Unknown 10 | ; Bound: 16 11 | OpCapability Shader 12 | OpMemoryModel Logical GLSL450 13 | OpEntryPoint Vertex %4 "main" %11 %15 14 | OpName %11 "a_input" 15 | OpDecorate %11 Location 0 16 | OpDecorate %15 Location 0 17 | %6 = OpTypeFloat 32 18 | %14 = OpTypePointer Output %6 19 | %15 = OpVariable %14 Output 20 | %10 = OpTypePointer Input %6 21 | %11 = OpVariable %10 Input 22 | %9 = OpTypeFunction %6 %6 23 | %1 = OpTypeVoid 24 | %2 = OpTypeFunction %1 25 | %4 = OpFunction %1 None %2 26 | %3 = OpLabel 27 | %12 = OpLoad %6 %11 None 28 | %13 = OpFunctionCall %6 %5 %12 29 | OpStore %15 %13 None 30 | OpReturn 31 | OpFunctionEnd 32 | %5 = OpFunction %6 None %9 33 | %7 = OpFunctionParameter %6 34 | %8 = OpLabel 35 | OpReturnValue %7 36 | OpFunctionEnd 37 | -------------------------------------------------------------------------------- /rasen/tests/snapshots/loop__loop.frag.snap: -------------------------------------------------------------------------------- 1 | --- 2 | created: "2019-10-25T17:42:55.042347900+00:00" 3 | creator: insta@0.5.2 4 | expression: assembly 5 | source: rasen/tests/loop.rs 6 | --- 7 | ; SPIR-V 8 | ; Version: 1.1 9 | ; Generator: Unknown 10 | ; Bound: 35 11 | OpCapability Shader 12 | OpMemoryModel Logical GLSL450 13 | OpEntryPoint Fragment %4 "main" %20 %34 14 | OpExecutionMode %4 OriginUpperLeft 15 | OpName %20 "i_value" 16 | OpName %34 "o_value" 17 | OpDecorate %20 Location 0 18 | OpDecorate %34 Location 0 19 | %10 = OpTypeBool 20 | %6 = OpTypeFloat 32 21 | %33 = OpTypePointer Output %6 22 | %34 = OpVariable %33 Output 23 | %22 = OpTypePointer Function %6 24 | %19 = OpTypePointer Input %6 25 | %20 = OpVariable %19 Input 26 | %18 = OpTypeFunction %6 %6 27 | %15 = OpConstant %6 1.0 28 | %12 = OpTypeFunction %10 %6 29 | %8 = OpConstant %6 10.0 30 | %1 = OpTypeVoid 31 | %2 = OpTypeFunction %1 32 | %4 = OpFunction %1 None %2 33 | %3 = OpLabel 34 | %21 = OpLoad %6 %20 None 35 | %23 = OpVariable %22 Function 36 | OpStore %23 %21 None 37 | OpBranch %24 38 | %24 = OpLabel 39 | OpLoopMerge %25 %26 None 40 | OpBranch %27 41 | %27 = OpLabel 42 | %28 = OpLoad %6 %23 None 43 | %29 = OpFunctionCall %10 %5 %28 44 | OpBranchConditional %29 %30 %25 45 | %30 = OpLabel 46 | %31 = OpFunctionCall %6 %13 %28 47 | OpStore %23 %31 None 48 | OpBranch %26 49 | %26 = OpLabel 50 | OpBranch %24 51 | %25 = OpLabel 52 | %32 = OpLoad %6 %23 None 53 | OpStore %34 %32 None 54 | OpReturn 55 | OpFunctionEnd 56 | %5 = OpFunction %10 None %12 57 | %7 = OpFunctionParameter %6 58 | %11 = OpLabel 59 | %9 = OpFOrdGreaterThan %10 %7 %8 60 | OpReturnValue %9 61 | OpFunctionEnd 62 | %13 = OpFunction %6 None %18 63 | %14 = OpFunctionParameter %6 64 | %17 = OpLabel 65 | %16 = OpFAdd %6 %14 %15 66 | OpReturnValue %16 67 | OpFunctionEnd 68 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | merge_imports = true -------------------------------------------------------------------------------- /tests/dsl.rs: -------------------------------------------------------------------------------- 1 | pub fn basic_vert( 2 | a_pos: Value, 3 | a_normal: Value, 4 | a_uv: Value, 5 | projection: Value, 6 | view: Value, 7 | model: Value, 8 | ) -> (Value, Value, Value) { 9 | let mvp = projection * view * model; 10 | 11 | let v_pos = mvp * vec4(index(a_pos, 0), index(a_pos, 1), index(a_pos, 2), 1.0f32); 12 | 13 | let v_norm = model 14 | * vec4( 15 | index(a_normal, 0), 16 | index(a_normal, 1), 17 | index(a_normal, 2), 18 | 1.0f32, 19 | ); 20 | 21 | (v_pos, v_norm, a_uv) 22 | } 23 | 24 | pub fn basic_frag( 25 | a_normal: Value, 26 | a_uv: Value, 27 | material: Value>, 28 | ) -> Value { 29 | let normal = normalize(a_normal); 30 | let light = vec3(0.3f32, -0.5f32, 0.2f32); 31 | let color = sample(material, a_uv); 32 | 33 | clamp(dot(normal, light), 0.1f32, 1.0f32) * color 34 | } 35 | 36 | fn extract_color(input: Value) -> Value { 37 | vec3( 38 | index(input, 0), 39 | index(input, 1), 40 | index(input, 2), 41 | ) 42 | } 43 | 44 | pub fn composite_ycrcb( 45 | a_uv: Value, 46 | luma_sampler: Value>, 47 | chroma_sampler: Value>, 48 | scene_sampler: Value>, 49 | ) -> Value { 50 | let ycbcr_to_rgb = mat4( 51 | vec4( 1.0000f32, 1.0000f32, 1.0000f32, 0.0000f32), 52 | vec4( 0.0000f32, -0.3441f32, 1.7720f32, 0.0000f32), 53 | vec4( 1.4020f32, -0.7141f32, 0.0000f32, 0.0000f32), 54 | vec4(-0.7010f32, 0.5291f32, -0.8860f32, 1.0000f32), 55 | ); 56 | 57 | let luma = sample(luma_sampler, a_uv); 58 | let chroma = sample(chroma_sampler, a_uv); 59 | let scene = sample(scene_sampler, a_uv); 60 | 61 | let input = vec4( 62 | index(luma, 0), 63 | index(chroma, 0), 64 | index(chroma, 1), 65 | 1.0f32, 66 | ); 67 | let output = ycbcr_to_rgb * input; 68 | 69 | let alpha = index(scene, 3); 70 | let composite = mix( 71 | extract_color(output), 72 | extract_color(scene), 73 | vec3(alpha, alpha, alpha), 74 | ); 75 | 76 | vec4( 77 | index(composite, 0), 78 | index(composite, 1), 79 | index(composite, 2), 80 | 1.0f32, 81 | ) 82 | } 83 | 84 | fn func(input: Value) -> Value { 85 | input 86 | } 87 | 88 | pub fn functions(a_input: Value) -> Value { 89 | func(a_input) 90 | } 91 | -------------------------------------------------------------------------------- /tests/graph.rs: -------------------------------------------------------------------------------- 1 | fn build_basic_vert() -> Graph { 2 | let mut graph = Graph::default(); 3 | 4 | let pos = graph.add_node(Node::Input(0, TypeName::VEC3, VariableName::Named(String::from("a_pos")))); 5 | let normal = graph.add_node(Node::Input(1, TypeName::VEC3, VariableName::Named(String::from("a_normal")))); 6 | let uv = graph.add_node(Node::Input(2, TypeName::VEC2, VariableName::Named(String::from("a_uv")))); 7 | 8 | let projection = graph.add_node(Node::Uniform(0, TypeName::MAT4, VariableName::Named(String::from("u_projection")))); 9 | let view = graph.add_node(Node::Uniform(1, TypeName::MAT4, VariableName::Named(String::from("u_view")))); 10 | let model = graph.add_node(Node::Uniform(2, TypeName::MAT4, VariableName::Named(String::from("u_model")))); 11 | 12 | let one = graph.add_node(Node::Constant(TypedValue::Float(1.0))); 13 | 14 | let vp = graph.add_node(Node::Multiply); 15 | let mvp = graph.add_node(Node::Multiply); 16 | let mul_pos = graph.add_node(Node::Multiply); 17 | let mul_norm = graph.add_node(Node::Multiply); 18 | let pos_x = graph.add_node(Node::Extract(0)); 19 | let pos_y = graph.add_node(Node::Extract(1)); 20 | let pos_z = graph.add_node(Node::Extract(2)); 21 | let norm_x = graph.add_node(Node::Extract(0)); 22 | let norm_y = graph.add_node(Node::Extract(1)); 23 | let norm_z = graph.add_node(Node::Extract(2)); 24 | let norm_x2 = graph.add_node(Node::Extract(0)); 25 | let norm_y2 = graph.add_node(Node::Extract(1)); 26 | let norm_z2 = graph.add_node(Node::Extract(2)); 27 | let pos_4 = graph.add_node(Node::Construct(TypeName::VEC4)); 28 | let norm_4 = graph.add_node(Node::Construct(TypeName::VEC4)); 29 | let norm_3 = graph.add_node(Node::Construct(TypeName::VEC3)); 30 | 31 | let o_pos = graph.add_node(Node::Output(0, TypeName::VEC4, VariableName::BuiltIn(BuiltIn::Position))); 32 | let o_norm = graph.add_node(Node::Output(1, TypeName::VEC3, VariableName::Named(String::from("f_norm")))); 33 | let o_uv = graph.add_node(Node::Output(2, TypeName::VEC2, VariableName::Named(String::from("f_uv")))); 34 | 35 | graph.add_edge(projection, vp, 0); 36 | graph.add_edge(view, vp, 1); 37 | graph.add_edge(vp, mvp, 0); 38 | graph.add_edge(model, mvp, 1); 39 | 40 | graph.add_edge(pos, pos_x, 0); 41 | graph.add_edge(pos, pos_y, 0); 42 | graph.add_edge(pos, pos_z, 0); 43 | graph.add_edge(normal, norm_x, 0); 44 | graph.add_edge(normal, norm_y, 0); 45 | graph.add_edge(normal, norm_z, 0); 46 | 47 | graph.add_edge(pos_x, pos_4, 0); 48 | graph.add_edge(pos_y, pos_4, 1); 49 | graph.add_edge(pos_z, pos_4, 2); 50 | graph.add_edge(one, pos_4, 3); 51 | 52 | graph.add_edge(mvp, mul_pos, 0); 53 | graph.add_edge(pos_4, mul_pos, 1); 54 | 55 | graph.add_edge(norm_x, norm_4, 0); 56 | graph.add_edge(norm_y, norm_4, 1); 57 | graph.add_edge(norm_z, norm_4, 2); 58 | graph.add_edge(one, norm_4, 3); 59 | 60 | graph.add_edge(model, mul_norm, 0); 61 | graph.add_edge(norm_4, mul_norm, 1); 62 | 63 | graph.add_edge(mul_norm, norm_x2, 0); 64 | graph.add_edge(mul_norm, norm_y2, 0); 65 | graph.add_edge(mul_norm, norm_z2, 0); 66 | 67 | graph.add_edge(norm_x2, norm_3, 0); 68 | graph.add_edge(norm_y2, norm_3, 1); 69 | graph.add_edge(norm_z2, norm_3, 2); 70 | 71 | graph.add_edge(mul_pos, o_pos, 0); 72 | graph.add_edge(norm_3, o_norm, 0); 73 | graph.add_edge(uv, o_uv, 0); 74 | 75 | graph 76 | } 77 | 78 | fn build_basic_frag() -> Graph { 79 | let mut graph = Graph::default(); 80 | 81 | let normal = graph.add_node(Node::Input(0, TypeName::VEC3, VariableName::Named(String::from("f_normal")))); 82 | let uv = graph.add_node(Node::Input(1, TypeName::VEC2, VariableName::Named(String::from("f_uv")))); 83 | 84 | let material = graph.add_node(Node::Uniform(0, TypeName::SAMPLER2D, VariableName::Named(String::from("u_material")))); 85 | 86 | let min_light = graph.add_node(Node::Constant(TypedValue::Float(0.1))); 87 | let max_light = graph.add_node(Node::Constant(TypedValue::Float(1.0))); 88 | let light_dir = graph.add_node(Node::Constant(TypedValue::Vec3(0.3, -0.5, 0.2))); 89 | 90 | let normalize = graph.add_node(Node::Normalize); 91 | let dot = graph.add_node(Node::Dot); 92 | let clamp = graph.add_node(Node::Clamp); 93 | let sample = graph.add_node(Node::Sample); 94 | let multiply = graph.add_node(Node::Multiply); 95 | 96 | let color = graph.add_node(Node::Output(0, TypeName::VEC4, VariableName::Named(String::from("o_col")))); 97 | 98 | graph.add_edge(normal, normalize, 0); 99 | 100 | graph.add_edge(normalize, dot, 0); 101 | graph.add_edge(light_dir, dot, 1); 102 | 103 | graph.add_edge(dot, clamp, 0); 104 | graph.add_edge(min_light, clamp, 1); 105 | graph.add_edge(max_light, clamp, 2); 106 | 107 | graph.add_edge(material, sample, 0); 108 | graph.add_edge(uv, sample, 1); 109 | 110 | graph.add_edge(clamp, multiply, 0); 111 | graph.add_edge(sample, multiply, 1); 112 | 113 | graph.add_edge(multiply, color, 0); 114 | 115 | graph 116 | } 117 | -------------------------------------------------------------------------------- /tests/plugin.rs: -------------------------------------------------------------------------------- 1 | #[rasen(module)] 2 | #[allow(clippy::needless_pass_by_value)] 3 | pub fn basic_vert( 4 | a_pos: Value, 5 | a_normal: Value, 6 | a_uv: Value, 7 | projection: Value, 8 | view: Value, 9 | model: Value, 10 | ) -> (Value, Value, Value) { 11 | let mvp = projection * view * model.clone(); 12 | 13 | let v_pos = mvp * vec4!(a_pos, 1.0f32); 14 | let v_norm = model * vec4!(a_normal, 1.0f32); 15 | 16 | (v_pos, idx!(v_norm, xyz), a_uv) 17 | } 18 | 19 | #[rasen(module)] 20 | pub fn basic_frag( 21 | a_normal: Value, 22 | a_uv: Value, 23 | material: Value>, 24 | ) -> Value { 25 | let normal = normalize(a_normal); 26 | let light = vec3(0.3f32, -0.5f32, 0.2f32); 27 | let color = sample(material, a_uv); 28 | 29 | clamp(dot(normal, light), 0.1f32, 1.0f32) * color 30 | } 31 | 32 | #[rasen(function)] 33 | fn func(input: Value) -> Value { 34 | input 35 | } 36 | 37 | #[rasen(module)] 38 | pub fn functions(a_input: Value) -> Value { 39 | func(a_input) 40 | } 41 | -------------------------------------------------------------------------------- /tests/update.rs: -------------------------------------------------------------------------------- 1 | mod _update_utils { 2 | use rasen::{ 3 | errors::{self, Error}, 4 | prelude::ModuleBuilder, 5 | }; 6 | use rspirv::{ 7 | binary::{Assemble, Disassemble}, 8 | mr::{load_words, Module as SpirvModule}, 9 | }; 10 | use std::{ 11 | convert::TryFrom, 12 | fmt, 13 | str, 14 | }; 15 | 16 | pub enum ModuleWrapper { 17 | Module(Box), 18 | String(String), 19 | Static(&'static str), 20 | } 21 | 22 | impl Clone for ModuleWrapper { 23 | fn clone(&self) -> ModuleWrapper { 24 | match *self { 25 | ModuleWrapper::Module(ref module) => { 26 | ModuleWrapper::Module(Box::new(load_words(module.assemble()).unwrap())) 27 | } 28 | ModuleWrapper::String(ref string) => ModuleWrapper::String(string.clone()), 29 | ModuleWrapper::Static(string) => ModuleWrapper::Static(string), 30 | } 31 | } 32 | } 33 | 34 | impl<'a> From<&'a ModuleWrapper> for ModuleWrapper { 35 | fn from(other: &'a ModuleWrapper) -> ModuleWrapper { 36 | other.clone() 37 | } 38 | } 39 | 40 | impl From for ModuleWrapper { 41 | fn from(string: String) -> ModuleWrapper { 42 | ModuleWrapper::String(string) 43 | } 44 | } 45 | 46 | impl ToString for ModuleWrapper { 47 | fn to_string(&self) -> String { 48 | match *self { 49 | ModuleWrapper::Module(ref module) => module.disassemble(), 50 | ModuleWrapper::String(ref string) => string.clone(), 51 | ModuleWrapper::Static(string) => string.into(), 52 | } 53 | } 54 | } 55 | 56 | impl fmt::Debug for ModuleWrapper { 57 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 58 | write!(fmt, "{}", self.to_string()) 59 | } 60 | } 61 | 62 | impl PartialEq for ModuleWrapper { 63 | fn eq(&self, other: &Self) -> bool { 64 | let this: String = self.to_string(); 65 | let other: String = other.to_string(); 66 | this == other 67 | } 68 | } 69 | 70 | pub fn build_module<'a, I, T>(graph: &'a I, mod_type: T) -> errors::Result 71 | where 72 | ModuleBuilder: TryFrom<(&'a I, T), Error = Error>, 73 | { 74 | Ok(ModuleWrapper::Module(Box::new( 75 | ModuleBuilder::try_from((graph, mod_type))?.build()?, 76 | ))) 77 | } 78 | } 79 | 80 | pub use self::_update_utils::build_module; 81 | 82 | macro_rules! assert_spirv_snapshot_matches { 83 | ($reference:expr, $actual:expr) => {{ 84 | use insta::assert_snapshot_matches; 85 | let value = self::_update_utils::ModuleWrapper::from($actual).to_string(); 86 | assert_snapshot_matches!($reference, value, stringify!($actual)) 87 | }}; 88 | } 89 | --------------------------------------------------------------------------------