From 361f0f85a0bae1216ad8e737d16623790d8457ae Mon Sep 17 00:00:00 2001 From: Schell Carl Scivally Date: Sat, 25 Nov 2023 09:00:47 +1300 Subject: [PATCH] moved shader annotations into renderling-shader --- Cargo.toml | 3 +- NOTES.md | 7 +- README.md | 1 + crates/renderling-derive/src/lib.rs | 66 ++- crates/renderling-shader/src/array.rs | 19 +- crates/renderling-shader/src/convolution.rs | 52 ++- crates/renderling-shader/src/debug.rs | 9 +- crates/renderling-shader/src/id.rs | 32 ++ crates/renderling-shader/src/skybox.rs | 28 +- crates/renderling-shader/src/slab.rs | 24 +- crates/renderling-shader/src/stage.rs | 164 +++---- crates/renderling-shader/src/tonemapping.rs | 22 +- crates/renderling-shader/src/ui.rs | 18 +- crates/renderling/src/bloom.rs | 20 +- crates/renderling/src/cubemap.rs | 14 +- crates/renderling/src/diffuse_irradiance.rs | 6 +- crates/renderling/src/hdr.rs | 14 +- .../renderling/src/ibl/diffuse_irradiance.rs | 5 +- .../src/ibl/prefiltered_environment.rs | 8 +- .../src/linkage/compute_cull_entities.spv | Bin 2164 -> 0 bytes .../linkage/convolution-fragment_bloom.spv | Bin 0 -> 3780 bytes ...volution-fragment_brdf_lut_convolution.spv | Bin 0 -> 5212 bytes .../convolution-fragment_generate_mipmap.spv | Bin 0 -> 608 bytes ...fragment_prefilter_environment_cubemap.spv | Bin 0 -> 6552 bytes ...onvolution-vertex_brdf_lut_convolution.spv | Bin 0 -> 588 bytes .../convolution-vertex_generate_mipmap.spv | Bin 0 -> 1172 bytes ...n-vertex_prefilter_environment_cubemap.spv | Bin 0 -> 4120 bytes .../renderling/src/linkage/fragment_bloom.spv | Bin 3768 -> 0 bytes .../linkage/fragment_brdf_lut_convolution.spv | Bin 5200 -> 0 bytes .../src/linkage/fragment_cubemap.spv | Bin 1088 -> 0 bytes .../src/linkage/fragment_equirectangular.spv | Bin 1356 -> 0 bytes .../linkage/fragment_equirectangular.spv.zip | Bin 1197 -> 0 bytes .../src/linkage/fragment_generate_mipmap.spv | Bin 596 -> 0 bytes ...fragment_prefilter_environment_cubemap.spv | Bin 6540 -> 0 bytes .../src/linkage/fragment_tonemapping.spv | Bin 5468 -> 0 bytes .../src/linkage/main_fragment_scene.spv | Bin 52552 -> 0 bytes .../src/linkage/main_vertex_scene.spv | Bin 43892 -> 0 bytes .../src/linkage/skybox-fragment_cubemap.spv | Bin 0 -> 1096 bytes .../skybox-fragment_equirectangular.spv | Bin 0 -> 1364 bytes .../renderling/src/linkage/skybox-vertex.spv | Bin 0 -> 3324 bytes .../skybox-vertex_position_passthru.spv | Bin 0 -> 4104 bytes .../linkage/stage-compute_cull_entities.spv | Bin 0 -> 2172 bytes .../src/linkage/stage-main_fragment_scene.spv | Bin 0 -> 52560 bytes .../src/linkage/stage-main_vertex_scene.spv | Bin 0 -> 43900 bytes .../src/linkage/stage-new_stage_vertex.spv | Bin 0 -> 60988 bytes .../renderling/src/linkage/stage_fragment.spv | Bin 135560 -> 0 bytes .../renderling/src/linkage/stage_vertex.spv | Bin 58828 -> 0 bytes .../src/linkage/tonemapping-fragment.spv | Bin 0 -> 5468 bytes .../src/linkage/tonemapping-vertex.spv | Bin 0 -> 1136 bytes crates/renderling/src/linkage/ui-fragment.spv | Bin 0 -> 1056 bytes crates/renderling/src/linkage/ui-vertex.spv | Bin 0 -> 2368 bytes crates/renderling/src/linkage/ui_fragment.spv | Bin 1052 -> 0 bytes crates/renderling/src/linkage/ui_vertex.spv | Bin 2368 -> 0 bytes .../linkage/vertex_brdf_lut_convolution.spv | Bin 572 -> 0 bytes .../src/linkage/vertex_generate_mipmap.spv | Bin 1160 -> 0 bytes .../src/linkage/vertex_position_passthru.spv | Bin 4096 -> 0 bytes .../vertex_prefilter_environment_cubemap.spv | Bin 4108 -> 0 bytes .../renderling/src/linkage/vertex_skybox.spv | Bin 3324 -> 0 bytes .../src/linkage/vertex_tonemapping.spv | Bin 1136 -> 0 bytes .../renderling/src/prefiltered_environment.rs | 137 ------ crates/renderling/src/scene.rs | 18 +- crates/renderling/src/skybox.rs | 16 +- crates/renderling/src/slab.rs | 12 +- crates/renderling/src/stage.rs | 106 ++++- crates/renderling/src/stage/light.rs | 14 +- crates/renderling/src/texture.rs | 8 +- crates/renderling/src/ui.rs | 15 +- shaders/Cargo.toml | 3 +- .../{rust-toolchain => rust-toolchain.toml} | 0 shaders/shader-crate/src/lib.rs | 402 +----------------- shaders/src/main.rs | 31 +- 71 files changed, 514 insertions(+), 760 deletions(-) delete mode 100644 crates/renderling/src/linkage/compute_cull_entities.spv create mode 100644 crates/renderling/src/linkage/convolution-fragment_bloom.spv create mode 100644 crates/renderling/src/linkage/convolution-fragment_brdf_lut_convolution.spv create mode 100644 crates/renderling/src/linkage/convolution-fragment_generate_mipmap.spv create mode 100644 crates/renderling/src/linkage/convolution-fragment_prefilter_environment_cubemap.spv create mode 100644 crates/renderling/src/linkage/convolution-vertex_brdf_lut_convolution.spv create mode 100644 crates/renderling/src/linkage/convolution-vertex_generate_mipmap.spv create mode 100644 crates/renderling/src/linkage/convolution-vertex_prefilter_environment_cubemap.spv delete mode 100644 crates/renderling/src/linkage/fragment_bloom.spv delete mode 100644 crates/renderling/src/linkage/fragment_brdf_lut_convolution.spv delete mode 100644 crates/renderling/src/linkage/fragment_cubemap.spv delete mode 100644 crates/renderling/src/linkage/fragment_equirectangular.spv delete mode 100644 crates/renderling/src/linkage/fragment_equirectangular.spv.zip delete mode 100644 crates/renderling/src/linkage/fragment_generate_mipmap.spv delete mode 100644 crates/renderling/src/linkage/fragment_prefilter_environment_cubemap.spv delete mode 100644 crates/renderling/src/linkage/fragment_tonemapping.spv delete mode 100644 crates/renderling/src/linkage/main_fragment_scene.spv delete mode 100644 crates/renderling/src/linkage/main_vertex_scene.spv create mode 100644 crates/renderling/src/linkage/skybox-fragment_cubemap.spv create mode 100644 crates/renderling/src/linkage/skybox-fragment_equirectangular.spv create mode 100644 crates/renderling/src/linkage/skybox-vertex.spv create mode 100644 crates/renderling/src/linkage/skybox-vertex_position_passthru.spv create mode 100644 crates/renderling/src/linkage/stage-compute_cull_entities.spv create mode 100644 crates/renderling/src/linkage/stage-main_fragment_scene.spv create mode 100644 crates/renderling/src/linkage/stage-main_vertex_scene.spv create mode 100644 crates/renderling/src/linkage/stage-new_stage_vertex.spv delete mode 100644 crates/renderling/src/linkage/stage_fragment.spv delete mode 100644 crates/renderling/src/linkage/stage_vertex.spv create mode 100644 crates/renderling/src/linkage/tonemapping-fragment.spv create mode 100644 crates/renderling/src/linkage/tonemapping-vertex.spv create mode 100644 crates/renderling/src/linkage/ui-fragment.spv create mode 100644 crates/renderling/src/linkage/ui-vertex.spv delete mode 100644 crates/renderling/src/linkage/ui_fragment.spv delete mode 100644 crates/renderling/src/linkage/ui_vertex.spv delete mode 100644 crates/renderling/src/linkage/vertex_brdf_lut_convolution.spv delete mode 100644 crates/renderling/src/linkage/vertex_generate_mipmap.spv delete mode 100644 crates/renderling/src/linkage/vertex_position_passthru.spv delete mode 100644 crates/renderling/src/linkage/vertex_prefilter_environment_cubemap.spv delete mode 100644 crates/renderling/src/linkage/vertex_skybox.spv delete mode 100644 crates/renderling/src/linkage/vertex_tonemapping.spv delete mode 100644 crates/renderling/src/prefiltered_environment.rs rename shaders/{rust-toolchain => rust-toolchain.toml} (100%) diff --git a/Cargo.toml b/Cargo.toml index 90a708d3..f613c64f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,8 @@ members = [ "crates/renderling", "crates/renderling-shader", "crates/renderling-gpui", - "crates/sandbox", "crates/renderling-derive", + "crates/renderling-derive", + "crates/sandbox", ] exclude = ["./shaders"] diff --git a/NOTES.md b/NOTES.md index 2e8ec433..419d1e22 100644 --- a/NOTES.md +++ b/NOTES.md @@ -12,14 +12,15 @@ Just pro-cons on tech choices and little things I don't want to forget whil impl * sharing code on CPU and GPU - sanity testing GPU code on CPU using regular tests + - ability to run shaders on either CPU or GPU and profile * it's Rust - using cargo and Rust module system - expressions! ## cons / limititions -* can't use enums (but you can't in glsl or hlsl or msl or wgsl either) -* struct layout size/alignment errors can be really tricky +* ~~can't use enums (but you can't in glsl or hlsl or msl or wgsl either)~~ you _can_ but they must be simple +* ~~struct layout size/alignment errors can be really tricky~~ solved by using a slab * rust code must be no-std * don't use `while let` or `while` loops * for loops are hit or miss, sometimes they work and sometimes they don't @@ -46,8 +47,6 @@ Just pro-cons on tech choices and little things I don't want to forget whil impl ## cons * no support for arrays of textures on web, yet -* not yet 1.0 (on by default in chrome beta) -* ~~what happens if WebGPU the standard fails? (everyone doubts it will)~~ * atomics are not supported in the Naga SPIRV frontend, which limits the capabilities of compute - see [the related Naga issue](https://github.com/gfx-rs/naga/issues/2301) diff --git a/README.md b/README.md index a83273ad..1d92081d 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ + # renderling 🍖 This aspires to be a modern "GPU-driven" renderer. It is alpha software. I'm still learning, but quickly! diff --git a/crates/renderling-derive/src/lib.rs b/crates/renderling-derive/src/lib.rs index 21927a63..9d9ecae1 100644 --- a/crates/renderling-derive/src/lib.rs +++ b/crates/renderling-derive/src/lib.rs @@ -1,13 +1,13 @@ //! Provides derive macros for `renderling`. use quote::quote; use syn::{ - spanned::Spanned, Data, DataStruct, DeriveInput, Fields, FieldsNamed, FieldsUnnamed, - Ident, Type, WhereClause, WherePredicate, Index, + spanned::Spanned, Data, DataStruct, DeriveInput, Fields, FieldsNamed, FieldsUnnamed, Ident, + Index, Type, WhereClause, WherePredicate, }; enum FieldName { Index(Index), - Ident(Ident) + Ident(Ident), } struct Params { @@ -54,7 +54,12 @@ fn get_params(input: &DeriveInput) -> syn::Result { .ident .clone() .map(FieldName::Ident) - .unwrap_or_else(|| FieldName::Index(Index{index:i as u32, span: field.span()})) + .unwrap_or_else(|| { + FieldName::Index(Index { + index: i as u32, + span: field.span(), + }) + }) }) .collect(); Ok(Params { @@ -72,7 +77,7 @@ pub fn derive_from_slab(input: proc_macro::TokenStream) -> proc_macro::TokenStre let Params { sizes, field_tys, - field_names + field_names, } = match get_params(&input) { Ok(c) => c, Err(e) => return e.into_compile_error().into(), @@ -93,17 +98,21 @@ pub fn derive_from_slab(input: proc_macro::TokenStream) -> proc_macro::TokenStre constrain_system_data_types(where_clause, &field_tys) } let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - let set_index_field_names = field_names.iter().map(|name| match name { - FieldName::Index(i) => quote!{ - let index = self.#i.read_slab(index, slab); - }, - FieldName::Ident(field) => quote! { - let index = self.#field.read_slab(index, slab); - }, - }) - .collect::>(); - let write_index_field_names = field_names.into_iter().map(|name| match name { - FieldName::Index(i) => quote!{ + let set_index_field_names = field_names + .iter() + .map(|name| match name { + FieldName::Index(i) => quote! { + let index = self.#i.read_slab(index, slab); + }, + FieldName::Ident(field) => quote! { + let index = self.#field.read_slab(index, slab); + }, + }) + .collect::>(); + let write_index_field_names = field_names + .iter() + .map(|name| match name { + FieldName::Index(i) => quote! { let index = self.#i.write_slab(index, slab); }, FieldName::Ident(field) => quote! { @@ -111,7 +120,32 @@ pub fn derive_from_slab(input: proc_macro::TokenStream) -> proc_macro::TokenStre }, }) .collect::>(); + + let mut offset_tys = vec![]; + let mut offsets = vec![]; + for (name, ty) in field_names.iter().zip(field_tys.iter()) { + let ident = match name { + FieldName::Index(i) => Ident::new(&format!("offset_of_{}", i.index), i.span), + FieldName::Ident(field) => Ident::new(&format!("offset_of_{}", field), field.span()), + }; + offsets.push( + quote!{ + pub fn #ident() -> usize { + #(<#offset_tys as renderling_shader::slab::Slabbed>::slab_size()+)* + 0 + } + } + ); + offset_tys.push(ty.clone()); + } + let output = quote! { + #[automatically_derived] + /// Offsets into the slab buffer for each field. + impl #impl_generics #name #ty_generics { + #(#offsets)* + } + #[automatically_derived] impl #impl_generics renderling_shader::slab::Slabbed for #name #ty_generics #where_clause { diff --git a/crates/renderling-shader/src/array.rs b/crates/renderling-shader/src/array.rs index b79946c1..17911185 100644 --- a/crates/renderling-shader/src/array.rs +++ b/crates/renderling-shader/src/array.rs @@ -4,15 +4,30 @@ use core::marker::PhantomData; use crate::id::Id; use crate::slab::Slabbed; -#[cfg_attr(not(target_arch = "spirv"), derive(Debug))] #[repr(C)] -#[derive(Clone, Copy, PartialEq)] +#[derive(Clone, Copy)] pub struct Array { index: u32, len: u32, _phantom: PhantomData, } +impl core::fmt::Debug for Array { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("Array") + .field("index", &self.index) + .field("len", &self.len) + .field("_phantom", &self._phantom) + .finish() + } +} + +impl PartialEq for Array { + fn eq(&self, other: &Self) -> bool { + self.index == other.index && self.len == other.len + } +} + impl Slabbed for Array { fn slab_size() -> usize { 2 diff --git a/crates/renderling-shader/src/convolution.rs b/crates/renderling-shader/src/convolution.rs index a4475a1d..d84321f2 100644 --- a/crates/renderling-shader/src/convolution.rs +++ b/crates/renderling-shader/src/convolution.rs @@ -5,7 +5,7 @@ use glam::{UVec2, Vec2, Vec3, Vec4, Vec4Swizzles}; use spirv_std::{ image::{Cubemap, Image2d}, num_traits::Zero, - Sampler, + spirv, Sampler, }; #[cfg(target_arch = "spirv")] @@ -148,21 +148,23 @@ pub fn integrate_brdf_doesnt_work(mut n_dot_v: f32, roughness: f32) -> Vec2 { Vec2::new(a, b) } +#[spirv(vertex)] pub fn vertex_prefilter_environment_cubemap( - constants: &GpuConstants, + #[spirv(uniform, descriptor_set = 0, binding = 0)] constants: &GpuConstants, in_pos: Vec3, out_pos: &mut Vec3, - gl_pos: &mut Vec4, + #[spirv(position)] gl_pos: &mut Vec4, ) { *out_pos = in_pos; *gl_pos = constants.camera_projection * constants.camera_view * in_pos.extend(1.0); } /// Lambertian prefilter. +#[spirv(fragment)] pub fn fragment_prefilter_environment_cubemap( - roughness: &f32, - environment_cubemap: &Cubemap, - sampler: &Sampler, + #[spirv(uniform, descriptor_set = 0, binding = 1)] roughness: &f32, + #[spirv(descriptor_set = 0, binding = 2)] environment_cubemap: &Cubemap, + #[spirv(descriptor_set = 0, binding = 3)] sampler: &Sampler, in_pos: Vec3, frag_color: &mut Vec4, ) { @@ -222,29 +224,37 @@ pub fn calc_lod(n_dot_l: f32) -> f32 { .log2() } -pub fn vertex_generate_mipmap(vertex_id: u32, out_uv: &mut Vec2, gl_pos: &mut Vec4) { +#[spirv(vertex)] +pub fn vertex_generate_mipmap( + #[spirv(vertex_index)] vertex_id: u32, + out_uv: &mut Vec2, + #[spirv(position)] gl_pos: &mut Vec4, +) { let i = vertex_id as usize; *out_uv = crate::math::UV_COORD_QUAD_CCW[i]; *gl_pos = crate::math::CLIP_SPACE_COORD_QUAD_CCW[i]; } +#[spirv(fragment)] pub fn fragment_generate_mipmap( - texture: &Image2d, - sampler: &Sampler, + #[spirv(descriptor_set = 0, binding = 0)] texture: &Image2d, + #[spirv(descriptor_set = 0, binding = 1)] sampler: &Sampler, in_uv: Vec2, frag_color: &mut Vec4, ) { *frag_color = texture.sample(*sampler, in_uv); } +#[spirv(fragment)] pub fn fragment_bloom( - horizontal: bool, - UVec2{x, y}: &UVec2, - texture: &Image2d, - sampler: &Sampler, + #[spirv(uniform, descriptor_set = 0, binding = 0)] horizontal: &u32, + #[spirv(uniform, descriptor_set = 0, binding = 1)] UVec2 { x, y }: &UVec2, + #[spirv(descriptor_set = 0, binding = 2)] texture: &Image2d, + #[spirv(descriptor_set = 0, binding = 3)] sampler: &Sampler, in_uv: Vec2, frag_color: &mut Vec4, ) { + let horizontal = *horizontal != 0; let weight = [0.227027f32, 0.1945946, 0.1216216, 0.054054, 0.016216]; let texel_offset = 1.0 / Vec2::new(*x as f32, *y as f32); let mut result = texture.sample(*sampler, in_uv).xyz() * weight[0]; @@ -263,6 +273,22 @@ pub fn fragment_bloom( *frag_color = result.extend(1.0); } +#[spirv(vertex)] +pub fn vertex_brdf_lut_convolution( + in_pos: glam::Vec3, + in_uv: glam::Vec2, + out_uv: &mut glam::Vec2, + #[spirv(position)] gl_pos: &mut glam::Vec4, +) { + *out_uv = in_uv; + *gl_pos = in_pos.extend(1.0); +} + +#[spirv(fragment)] +pub fn fragment_brdf_lut_convolution(in_uv: glam::Vec2, out_color: &mut glam::Vec2) { + *out_color = integrate_brdf(in_uv.x, in_uv.y); +} + #[cfg(test)] mod test { use super::*; diff --git a/crates/renderling-shader/src/debug.rs b/crates/renderling-shader/src/debug.rs index cde5eea4..83fd6518 100644 --- a/crates/renderling-shader/src/debug.rs +++ b/crates/renderling-shader/src/debug.rs @@ -92,7 +92,7 @@ impl DebugChannel { Self::Emissive, Self::UvEmissive, Self::EmissiveFactor, - Self::EmissiveStrength + Self::EmissiveStrength, ] } } @@ -101,10 +101,15 @@ impl DebugChannel { /// /// Create one using `DebugChannel::into`. #[repr(transparent)] -#[cfg_attr(not(target_arch = "spirv"), derive(Debug))] #[derive(Default, Clone, Copy, PartialEq, Eq, bytemuck::Pod, bytemuck::Zeroable, Slabbed)] pub struct DebugMode(u32); +impl core::fmt::Debug for DebugMode { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_tuple("DebugMode").field(&self.0).finish() + } +} + impl From for DebugMode { fn from(value: DebugChannel) -> Self { DebugMode(value as u32) diff --git a/crates/renderling-shader/src/id.rs b/crates/renderling-shader/src/id.rs index 69cc67bd..c36dff76 100644 --- a/crates/renderling-shader/src/id.rs +++ b/crates/renderling-shader/src/id.rs @@ -78,6 +78,38 @@ impl std::fmt::Debug for Id { } } +impl core::ops::Add for Id { + type Output = Self; + + fn add(self, rhs: usize) -> Self::Output { + Id::new(self.0 + rhs as u32) + } +} + +impl core::ops::Add> for usize { + type Output = Id; + + fn add(self, rhs: Id) -> Self::Output { + Id::new(self as u32 + rhs.0 as u32) + } +} + +impl core::ops::Add for Id { + type Output = Self; + + fn add(self, rhs: u32) -> Self::Output { + Id::new(self.0 + rhs) + } +} + +impl core::ops::Add> for u32 { + type Output = Id; + + fn add(self, rhs: Id) -> Self::Output { + Id::new(self + rhs.0) + } +} + impl Id { pub const NONE: Self = Id::new(ID_NONE); diff --git a/crates/renderling-shader/src/skybox.rs b/crates/renderling-shader/src/skybox.rs index b8329f35..210cb35a 100644 --- a/crates/renderling-shader/src/skybox.rs +++ b/crates/renderling-shader/src/skybox.rs @@ -1,7 +1,10 @@ //! Skybox shader. use glam::{Mat3, Mat4, Vec2, Vec3, Vec4, Vec4Swizzles}; -use spirv_std::{image::{Image2d, Cubemap}, Sampler}; +use spirv_std::{ + image::{Cubemap, Image2d}, + spirv, Sampler, +}; #[cfg(target_arch = "spirv")] use spirv_std::num_traits::Float; @@ -19,7 +22,13 @@ pub fn direction_to_equirectangular_uv(dir: Vec3) -> Vec2 { uv } -pub fn vertex(vertex_id: u32, constants: &GpuConstants, local_pos: &mut Vec3, gl_pos: &mut Vec4) { +#[spirv(vertex)] +pub fn vertex( + #[spirv(vertex_index)] vertex_id: u32, + #[spirv(uniform, descriptor_set = 0, binding = 0)] constants: &GpuConstants, + local_pos: &mut Vec3, + #[spirv(position)] gl_pos: &mut Vec4, +) { let point = math::CUBE[vertex_id as usize]; *local_pos = point; let camera_view_without_translation = Mat3::from_mat4(constants.camera_view); @@ -29,9 +38,10 @@ pub fn vertex(vertex_id: u32, constants: &GpuConstants, local_pos: &mut Vec3, gl } /// Colors a skybox using a cubemap texture. +#[spirv(fragment)] pub fn fragment_cubemap( - texture: &Cubemap, - sampler: &Sampler, + #[spirv(descriptor_set = 0, binding = 1)] texture: &Cubemap, + #[spirv(descriptor_set = 0, binding = 2)] sampler: &Sampler, local_pos: Vec3, out_color: &mut Vec4, ) { @@ -43,20 +53,22 @@ pub fn fragment_cubemap( /// while transforming `gl_pos` by the camera projection*view; /// /// Used to create a cubemap from an equirectangular image as well as cubemap convolutions. +#[spirv(vertex)] pub fn vertex_position_passthru( - constants: &GpuConstants, + #[spirv(uniform, descriptor_set = 0, binding = 0)] constants: &GpuConstants, in_pos: Vec3, local_pos: &mut Vec3, - gl_pos: &mut Vec4, + #[spirv(position)] gl_pos: &mut Vec4, ) { *local_pos = in_pos; *gl_pos = constants.camera_projection * constants.camera_view * in_pos.extend(1.0); } /// Colors a skybox using an equirectangular texture. +#[spirv(fragment)] pub fn fragment_equirectangular( - texture: &Image2d, - sampler: &Sampler, + #[spirv(descriptor_set = 0, binding = 1)] texture: &Image2d, + #[spirv(descriptor_set = 0, binding = 2)] sampler: &Sampler, local_pos: Vec3, out_color: &mut Vec4, ) { diff --git a/crates/renderling-shader/src/slab.rs b/crates/renderling-shader/src/slab.rs index a2a61a1b..bf9a918a 100644 --- a/crates/renderling-shader/src/slab.rs +++ b/crates/renderling-shader/src/slab.rs @@ -12,7 +12,7 @@ use crate::id::Id; /// so long as those types are relatively simple. So far, autoderiving /// fields with these types will **not compile** on one or more targets: /// * `PhantomData` - will not compile on `target_arch = "spirv"` -pub trait Slabbed: std::any::Any + std::fmt::Debug + Sized { +pub trait Slabbed: core::any::Any + Sized { /// The number of `u32`s this type occupies in a slab of `&[u32]`. fn slab_size() -> usize; @@ -34,6 +34,23 @@ pub trait Slabbed: std::any::Any + std::fmt::Debug + Sized { fn write_slab(&self, index: usize, slab: &mut [u32]) -> usize; } +impl Slabbed for bool { + fn slab_size() -> usize { + 1 + } + + fn read_slab(&mut self, index: usize, slab: &[u32]) -> usize { + let mut proxy = 0u32; + let index = proxy.read_slab(index, slab); + *self = proxy == 1; + index + } + + fn write_slab(&self, index: usize, slab: &mut [u32]) -> usize { + if *self { 1u32 } else { 0u32 }.write_slab(index, slab) + } +} + impl Slabbed for u32 { fn slab_size() -> usize { 1 @@ -245,7 +262,6 @@ impl Slabbed for glam::Quat { let index = z.write_slab(index, slab); w.write_slab(index, slab) } - } impl Slabbed for glam::UVec2 { @@ -326,7 +342,7 @@ impl Slabbed for glam::UVec4 { } } -impl Slabbed for PhantomData { +impl Slabbed for PhantomData { fn slab_size() -> usize { 0 } @@ -338,7 +354,6 @@ impl Slabbed for PhantomData { fn write_slab(&self, index: usize, _: &mut [u32]) -> usize { index } - } pub trait Slab { @@ -350,7 +365,6 @@ pub trait Slab { /// Return the next index, or the same index if writing would overlap the slab. fn write(&mut self, t: &T, index: usize) -> usize; - /// Write a slice of the type into the slab at the index. /// /// Return the next index, or the same index if writing would overlap the slab. diff --git a/crates/renderling-shader/src/stage.rs b/crates/renderling-shader/src/stage.rs index b99cd609..8f884387 100644 --- a/crates/renderling-shader/src/stage.rs +++ b/crates/renderling-shader/src/stage.rs @@ -8,7 +8,7 @@ use glam::{mat3, Mat4, Quat, UVec2, UVec3, Vec2, Vec3, Vec4, Vec4Swizzles}; use spirv_std::{ image::{Cubemap, Image2d}, - Sampler, + spirv, Sampler, }; #[cfg(target_arch = "spirv")] @@ -21,7 +21,7 @@ use crate::{ debug::*, id::{Id, ID_NONE}, pbr::{self, PbrMaterial}, - slab::{Slabbed, Slab}, + slab::{Slab, Slabbed}, IsMatrix, IsVector, }; @@ -545,18 +545,19 @@ fn stage_texture_color( } #[allow(clippy::too_many_arguments)] +#[spirv(vertex)] /// Scene vertex shader. pub fn main_vertex_scene( // which entity are we drawing - instance_index: u32, + #[spirv(instance_index)] instance_index: u32, // which vertex are we drawing - vertex_index: u32, + #[spirv(vertex_index)] vertex_index: u32, - constants: &GpuConstants, - vertices: &[Vertex], - entities: &[GpuEntity], + #[spirv(uniform, descriptor_set = 0, binding = 0)] constants: &GpuConstants, + #[spirv(storage_buffer, descriptor_set = 0, binding = 1)] vertices: &[Vertex], + #[spirv(storage_buffer, descriptor_set = 0, binding = 2)] entities: &[GpuEntity], - out_material: &mut u32, + #[spirv(flat)] out_material: &mut u32, out_color: &mut Vec4, out_uv0: &mut Vec2, out_uv1: &mut Vec2, @@ -566,7 +567,7 @@ pub fn main_vertex_scene( // position of the vertex/fragment in world space out_pos: &mut Vec3, - gl_pos: &mut Vec4, + #[spirv(position)] gl_pos: &mut Vec4, ) { let entity = entities[instance_index as usize]; let vertex = entity.get_vertex(vertex_index, vertices); @@ -604,24 +605,25 @@ pub fn main_vertex_scene( } #[allow(clippy::too_many_arguments)] +#[spirv(fragment)] /// Scene fragment shader. pub fn main_fragment_scene( - atlas: &Image2d, - atlas_sampler: &Sampler, - - irradiance: &Cubemap, - irradiance_sampler: &Sampler, - prefiltered: &Cubemap, - prefiltered_sampler: &Sampler, - brdf: &Image2d, - brdf_sampler: &Sampler, - - constants: &GpuConstants, - lights: &[GpuLight], - materials: &[pbr::PbrMaterial], - textures: &[GpuTexture], - - in_material: u32, + #[spirv(descriptor_set = 1, binding = 0)] atlas: &Image2d, + #[spirv(descriptor_set = 1, binding = 1)] atlas_sampler: &Sampler, + + #[spirv(descriptor_set = 0, binding = 5)] irradiance: &Cubemap, + #[spirv(descriptor_set = 0, binding = 6)] irradiance_sampler: &Sampler, + #[spirv(descriptor_set = 0, binding = 7)] prefiltered: &Cubemap, + #[spirv(descriptor_set = 0, binding = 8)] prefiltered_sampler: &Sampler, + #[spirv(descriptor_set = 0, binding = 9)] brdf: &Image2d, + #[spirv(descriptor_set = 0, binding = 10)] brdf_sampler: &Sampler, + + #[spirv(uniform, descriptor_set = 0, binding = 0)] constants: &GpuConstants, + #[spirv(storage_buffer, descriptor_set = 0, binding = 3)] lights: &[GpuLight], + #[spirv(storage_buffer, descriptor_set = 0, binding = 4)] materials: &[pbr::PbrMaterial], + #[spirv(storage_buffer, descriptor_set = 1, binding = 2)] textures: &[GpuTexture], + + #[spirv(flat)] in_material: u32, in_color: Vec4, in_uv0: Vec2, in_uv1: Vec2, @@ -867,6 +869,30 @@ pub fn main_fragment_scene( .extend(1.0); } +/// A camera used for transforming the stage during rendering. +#[cfg_attr(not(target_arch = "spirv"), derive(Debug))] +#[repr(C)] +#[derive(Default, Clone, Copy, PartialEq, Slabbed)] +pub struct Camera { + pub projection: Mat4, + pub view: Mat4, + pub position: Vec3, +} + +/// Holds important info about the stage. +/// +/// This should be the first struct in the stage's slab. +#[cfg_attr(not(target_arch = "spirv"), derive(Debug))] +#[repr(C)] +#[derive(Default, Clone, Copy, PartialEq, Slabbed)] +pub struct StageLegend { + pub atlas_size: UVec2, + pub debug_mode: DebugMode, + pub has_skybox: bool, + pub has_lighting: bool, + pub light_array: Array, +} + /// A fully-computed unit of rendering, roughly meaning a mesh with model matrix /// transformations. #[cfg_attr(not(target_arch = "spirv"), derive(Debug))] @@ -877,27 +903,26 @@ pub struct RenderUnit { pub id: Id, // Points to an array of `Vertex` in the stage's slab. pub vertices: Array, - // Points to a `PbrMaterial` the stage's slab. + // Points to a `PbrMaterial` in the stage's slab. pub material: Id, - // Points to a `Mat4` the stage's slab. - pub camera_projection: Id, - // Points to a `Mat4` the stage's slab. - pub camera_view: Id, + // Points to a `Camera` in the stage's slab. + pub camera: Id, pub position: Vec3, pub rotation: Quat, pub scale: Vec3, } -pub fn stage_vertex( +#[spirv(vertex)] +pub fn new_stage_vertex( // Which render unit are we rendering - instance_index: u32, + #[spirv(instance_index)] instance_index: u32, // Which vertex within the render unit are we rendering - vertex_index: u32, - unit_slab: &[u32], - stage_slab: &[u32], - - out_material: &mut u32, + #[spirv(vertex_index)] vertex_index: u32, + #[spirv(storage_buffer, descriptor_set = 0, binding = 0)] unit_slab: &[u32], + #[spirv(storage_buffer, descriptor_set = 0, binding = 1)] stage_slab: &[u32], + #[spirv(flat)] out_camera: &mut u32, + #[spirv(flat)] out_material: &mut u32, out_color: &mut Vec4, out_uv0: &mut Vec2, out_uv1: &mut Vec2, @@ -906,8 +931,7 @@ pub fn stage_vertex( out_bitangent: &mut Vec3, // position of the vertex/fragment in world space out_pos: &mut Vec3, - - gl_pos: &mut Vec4, + #[spirv(position)] gl_pos: &mut Vec4, ) { let unit_id: Id = Id::from(instance_index); let unit = unit_slab.read(unit_id); @@ -933,17 +957,9 @@ pub fn stage_vertex( *out_norm = normal_w; let view_pos = model_matrix * vertex.position.xyz().extend(1.0); *out_pos = view_pos.xyz(); - let camera_projection = stage_slab.read(unit.camera_projection); - let camera_view = stage_slab.read(unit.camera_view); - *gl_pos = camera_projection * camera_view * view_pos; -} - -/// Represents a "legend" of various stage buffers. -#[cfg_attr(not(target_arch = "spirv"), derive(Debug))] -#[derive(Copy, Clone, Default, Slabbed)] -pub struct Legend { - pub constants: GpuConstants, - pub lights: Array, + let camera = stage_slab.read(unit.camera); + *out_camera = unit.camera.into(); + *gl_pos = camera.projection * camera.view * view_pos; } #[allow(clippy::too_many_arguments)] @@ -963,6 +979,7 @@ pub fn stage_fragment( slab: &[u32], + in_camera: u32, in_material: u32, in_color: Vec4, in_uv0: Vec2, @@ -975,8 +992,14 @@ pub fn stage_fragment( output: &mut Vec4, brigtness: &mut Vec4, ) { - let Legend { constants, lights } = slab.read(Id::new(0)); - let material = if in_material == ID_NONE || !constants.toggles.get_use_lighting() { + let StageLegend { + atlas_size, + debug_mode, + has_skybox: _, + has_lighting, + light_array, + } = slab.read(Id::new(0)); + let material = if in_material == ID_NONE || !has_lighting { // without an explicit material (or if the entire render has no lighting) // the entity will not participate in any lighting calculations pbr::PbrMaterial { @@ -997,7 +1020,7 @@ pub fn stage_fragment( albedo_tex_uv, atlas, atlas_sampler, - constants.atlas_size, + atlas_size, slab, ); @@ -1011,7 +1034,7 @@ pub fn stage_fragment( metallic_roughness_uv, atlas, atlas_sampler, - constants.atlas_size, + atlas_size, slab, ); @@ -1025,7 +1048,7 @@ pub fn stage_fragment( normal_tex_uv, atlas, atlas_sampler, - constants.atlas_size, + atlas_size, slab, ); @@ -1039,7 +1062,7 @@ pub fn stage_fragment( ao_tex_uv, atlas, atlas_sampler, - constants.atlas_size, + atlas_size, slab, ); @@ -1053,7 +1076,7 @@ pub fn stage_fragment( emissive_tex_uv, atlas, atlas_sampler, - constants.atlas_size, + atlas_size, slab, ); @@ -1081,28 +1104,22 @@ pub fn stage_fragment( let emissive = emissive_tex_color.xyz() * material.emissive_factor.xyz() * material.emissive_factor.w; let irradiance = pbr::sample_irradiance(irradiance, irradiance_sampler, n); + let camera = slab.read(Id::::new(in_camera)); let specular = pbr::sample_specular_reflection( prefiltered, prefiltered_sampler, - constants.camera_pos.xyz(), - in_pos, - n, - roughness, - ); - let brdf = pbr::sample_brdf( - brdf, - brdf_sampler, - constants.camera_pos.xyz(), + camera.position, in_pos, n, roughness, ); + let brdf = pbr::sample_brdf(brdf, brdf_sampler, camera.position, in_pos, n, roughness); fn colorize(u: Vec3) -> Vec4 { ((u.alt_norm_or_zero() + Vec3::splat(1.0)) / 2.0).extend(1.0) } - match constants.debug_mode.into() { + match debug_mode.into() { DebugChannel::None => {} DebugChannel::UvCoords0 => { *output = colorize(Vec3::new(in_uv0.x, in_uv0.y, 0.0)); @@ -1184,7 +1201,7 @@ pub fn stage_fragment( *output = match material.lighting_model { LightingModel::PBR_LIGHTING => pbr::stage_shade_fragment( - constants.camera_pos.xyz(), + camera.position, n, in_pos, albedo.xyz(), @@ -1195,7 +1212,7 @@ pub fn stage_fragment( irradiance, specular, brdf, - lights, + light_array, slab, ), _unlit => in_color * albedo_tex_color * material.albedo_factor, @@ -1211,10 +1228,15 @@ pub fn stage_fragment( .extend(1.0); } +#[spirv(compute(threads(32)))] /// Compute the draw calls for this frame. /// /// This should be called with `groupcount = (entities.len() / threads) + 1`. -pub fn compute_cull_entities(entities: &[GpuEntity], draws: &mut [DrawIndirect], global_id: UVec3) { +pub fn compute_cull_entities( + #[spirv(storage_buffer, descriptor_set = 0, binding = 2)] entities: &[GpuEntity], + #[spirv(storage_buffer, descriptor_set = 1, binding = 0)] draws: &mut [DrawIndirect], + #[spirv(global_invocation_id)] global_id: UVec3, +) { let i = global_id.x as usize; if i > entities.len() { @@ -1249,7 +1271,7 @@ pub fn compute_cull_entities(entities: &[GpuEntity], draws: &mut [DrawIndirect], #[cfg(test)] mod test { - use crate::{self as renderling_shader, slab::Slab, id::Id}; + use crate::{self as renderling_shader, id::Id, slab::Slab}; use renderling_shader::slab::Slabbed; #[derive(Default, Debug, PartialEq, Slabbed)] diff --git a/crates/renderling-shader/src/tonemapping.rs b/crates/renderling-shader/src/tonemapping.rs index c94647b7..7c5bff14 100644 --- a/crates/renderling-shader/src/tonemapping.rs +++ b/crates/renderling-shader/src/tonemapping.rs @@ -5,7 +5,7 @@ //! * https://64.github.io/tonemapping use glam::{mat3, Mat3, Vec2, Vec3, Vec4, Vec4Swizzles}; -use spirv_std::{image::Image2d, Sampler}; +use spirv_std::{image::Image2d, spirv, Sampler}; const GAMMA: f32 = 2.2; const INV_GAMMA: f32 = 1.0 / GAMMA; @@ -118,7 +118,7 @@ pub fn tonemap(mut color: Vec4, constants: &TonemapConstants) -> Vec4 { // Use Reinhard tone mapping color / (color + Vec4::ONE) } - _ => color + _ => color, } } @@ -130,18 +130,24 @@ const QUAD_2D_POINTS: [(Vec2, Vec2); 6] = { [tl, bl, br, tl, br, tr] }; -pub fn vertex(vertex_id: u32, out_uv: &mut glam::Vec2, gl_pos: &mut glam::Vec4) { +#[spirv(vertex)] +pub fn vertex( + #[spirv(vertex_index)] vertex_id: u32, + out_uv: &mut glam::Vec2, + #[spirv(position)] gl_pos: &mut glam::Vec4, +) { let (pos, uv) = QUAD_2D_POINTS[vertex_id as usize]; *out_uv = uv; *gl_pos = pos.extend(0.0).extend(1.0); } +#[spirv(fragment)] pub fn fragment( - texture: &Image2d, - sampler: &Sampler, - constants: &TonemapConstants, - bloom_texture: &Image2d, - bloom_sampler: &Sampler, + #[spirv(descriptor_set = 0, binding = 0)] texture: &Image2d, + #[spirv(descriptor_set = 0, binding = 1)] sampler: &Sampler, + #[spirv(uniform, descriptor_set = 1, binding = 0)] constants: &TonemapConstants, + #[spirv(descriptor_set = 2, binding = 0)] bloom_texture: &Image2d, + #[spirv(descriptor_set = 2, binding = 1)] bloom_sampler: &Sampler, in_uv: glam::Vec2, output: &mut glam::Vec4, ) { diff --git a/crates/renderling-shader/src/ui.rs b/crates/renderling-shader/src/ui.rs index 2968fdc1..ccd57968 100644 --- a/crates/renderling-shader/src/ui.rs +++ b/crates/renderling-shader/src/ui.rs @@ -3,7 +3,7 @@ //! This is mostly for rendering text. use glam::{Mat4, UVec2, Vec2, Vec4}; -use spirv_std::{image::Image2d, Sampler}; +use spirv_std::{image::Image2d, spirv, Sampler}; /// A vertex in a mesh. #[cfg_attr(not(target_arch = "spirv"), derive(Debug))] @@ -79,19 +79,20 @@ impl Default for UiDrawParams { } #[allow(clippy::too_many_arguments)] +#[spirv(vertex)] pub fn vertex( - constants: &UiConstants, - params: &UiDrawParams, + #[spirv(uniform, descriptor_set = 0, binding = 0)] constants: &UiConstants, + #[spirv(uniform, descriptor_set = 2, binding = 0)] params: &UiDrawParams, in_pos: Vec2, in_uv: Vec2, in_color: Vec4, - out_mode: &mut u32, + #[spirv(flat)] out_mode: &mut u32, out_color: &mut Vec4, out_uv: &mut Vec2, - gl_pos: &mut Vec4, + #[spirv(position)] gl_pos: &mut Vec4, ) { *out_mode = params.mode.0; *out_color = in_color; @@ -113,11 +114,12 @@ pub fn vertex( *gl_pos = proj * view * model * Vec4::new(in_pos.x, in_pos.y, 0.0, 1.0); } +#[spirv(fragment)] pub fn fragment( - texture: &Image2d, - sampler: &Sampler, + #[spirv(descriptor_set = 1, binding = 0)] texture: &Image2d, + #[spirv(descriptor_set = 1, binding = 1)] sampler: &Sampler, - in_mode: u32, + #[spirv(flat)] in_mode: u32, in_color: Vec4, in_uv: Vec2, diff --git a/crates/renderling/src/bloom.rs b/crates/renderling/src/bloom.rs index 165af143..cb5a1cdf 100644 --- a/crates/renderling/src/bloom.rs +++ b/crates/renderling/src/bloom.rs @@ -89,9 +89,10 @@ fn create_pipeline(device: &wgpu::Device) -> wgpu::RenderPipeline { label: Some("bloom filter"), layout: Some(&pp_layout), vertex: wgpu::VertexState { - module: &device - .create_shader_module(wgpu::include_spirv!("linkage/vertex_generate_mipmap.spv")), - entry_point: "vertex_generate_mipmap", + module: &device.create_shader_module(wgpu::include_spirv!( + "linkage/convolution-vertex_generate_mipmap.spv" + )), + entry_point: "convolution::vertex_generate_mipmap", buffers: &[], }, primitive: wgpu::PrimitiveState { @@ -105,9 +106,10 @@ fn create_pipeline(device: &wgpu::Device) -> wgpu::RenderPipeline { }, depth_stencil: None, fragment: Some(wgpu::FragmentState { - module: &device - .create_shader_module(wgpu::include_spirv!("linkage/fragment_bloom.spv")), - entry_point: "fragment_bloom", + module: &device.create_shader_module(wgpu::include_spirv!( + "linkage/convolution-fragment_bloom.spv" + )), + entry_point: "convolution::fragment_bloom", targets: &[Some(wgpu::ColorTargetState { format: wgpu::TextureFormat::Rgba16Float, blend: Some(wgpu::BlendState::ALPHA_BLENDING), @@ -395,7 +397,11 @@ mod test { img_diff::assert_img_eq("bloom/on.png", img); { - let bloom = renderling.graph.get_resource_mut::().unwrap().unwrap(); + let bloom = renderling + .graph + .get_resource_mut::() + .unwrap() + .unwrap(); bloom.on = false; } let img = renderling.render_image().unwrap(); diff --git a/crates/renderling/src/cubemap.rs b/crates/renderling/src/cubemap.rs index aaeb73b7..8eed46d7 100644 --- a/crates/renderling/src/cubemap.rs +++ b/crates/renderling/src/cubemap.rs @@ -74,10 +74,12 @@ impl CubemapMakingRenderPipeline { /// images. pub fn new(device: &wgpu::Device, format: wgpu::TextureFormat) -> Self { log::trace!("creating cubemap-making render pipeline with format '{format:?}'"); - let vertex_shader = - device.create_shader_module(wgpu::include_spirv!("linkage/vertex_position_passthru.spv")); - let fragment_shader = device - .create_shader_module(wgpu::include_spirv!("linkage/fragment_equirectangular.spv")); + let vertex_shader = device.create_shader_module(wgpu::include_spirv!( + "linkage/skybox-vertex_position_passthru.spv" + )); + let fragment_shader = device.create_shader_module(wgpu::include_spirv!( + "linkage/skybox-fragment_equirectangular.spv" + )); let bg_layout = cubemap_making_bindgroup_layout(device); let pp_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("cubemap-making pipeline layout"), @@ -90,7 +92,7 @@ impl CubemapMakingRenderPipeline { layout: Some(&pp_layout), vertex: wgpu::VertexState { module: &vertex_shader, - entry_point: "vertex_position_passthru", + entry_point: "skybox::vertex_position_passthru", buffers: &[wgpu::VertexBufferLayout { array_stride: { let position_size = std::mem::size_of::(); @@ -119,7 +121,7 @@ impl CubemapMakingRenderPipeline { }, fragment: Some(wgpu::FragmentState { module: &fragment_shader, - entry_point: "fragment_equirectangular", + entry_point: "skybox::fragment_equirectangular", targets: &[Some(wgpu::ColorTargetState { format, blend: Some(wgpu::BlendState::ALPHA_BLENDING), diff --git a/crates/renderling/src/diffuse_irradiance.rs b/crates/renderling/src/diffuse_irradiance.rs index c399489c..9c83bed6 100644 --- a/crates/renderling/src/diffuse_irradiance.rs +++ b/crates/renderling/src/diffuse_irradiance.rs @@ -3,7 +3,9 @@ use renderling_shader::scene::GpuConstants; use crate::Uniform; -pub fn diffuse_irradiance_convolution_bindgroup_layout(device: &wgpu::Device) -> wgpu::BindGroupLayout { +pub fn diffuse_irradiance_convolution_bindgroup_layout( + device: &wgpu::Device, +) -> wgpu::BindGroupLayout { device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { label: Some("convolution bindgroup"), entries: &[ @@ -92,7 +94,7 @@ impl DiffuseIrradianceConvolutionRenderPipeline { layout: Some(&pp_layout), vertex: wgpu::VertexState { module: &vertex_shader, - entry_point: "vertex_position_passthru", + entry_point: "skybox::vertex_position_passthru", buffers: &[wgpu::VertexBufferLayout { array_stride: { let position_size = std::mem::size_of::(); diff --git a/crates/renderling/src/hdr.rs b/crates/renderling/src/hdr.rs index 599b616c..b0d833c9 100644 --- a/crates/renderling/src/hdr.rs +++ b/crates/renderling/src/hdr.rs @@ -160,9 +160,9 @@ pub fn create_hdr_render_surface( let hdr_texture = HdrSurface::create_texture(&device, &queue, size.width, size.height); let label = Some("hdr tonemapping"); let vertex_shader = - device.create_shader_module(wgpu::include_spirv!("linkage/vertex_tonemapping.spv")); + device.create_shader_module(wgpu::include_spirv!("linkage/tonemapping-vertex.spv")); let fragment_shader = - device.create_shader_module(wgpu::include_spirv!("linkage/fragment_tonemapping.spv")); + device.create_shader_module(wgpu::include_spirv!("linkage/tonemapping-fragment.spv")); let hdr_texture_layout = scene_hdr_surface_bindgroup_layout(&device); let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label, @@ -174,7 +174,7 @@ pub fn create_hdr_render_surface( layout: Some(&layout), vertex: wgpu::VertexState { module: &vertex_shader, - entry_point: "vertex_tonemapping", + entry_point: "tonemapping::vertex", buffers: &[], }, primitive: wgpu::PrimitiveState { @@ -189,7 +189,7 @@ pub fn create_hdr_render_surface( depth_stencil: None, fragment: Some(wgpu::FragmentState { module: &fragment_shader, - entry_point: "fragment_tonemapping", + entry_point: "tonemapping::fragment", targets: &[Some(wgpu::ColorTargetState { format: target.format().add_srgb_suffix(), blend: Some(wgpu::BlendState::ALPHA_BLENDING), @@ -250,7 +250,11 @@ pub fn clear_surface_hdr_and_depth( &device, &queue, Some("clear_frame_and_depth"), - vec![&frame.view, &hdr.hdr_texture.view, &hdr.brightness_texture.view], + vec![ + &frame.view, + &hdr.hdr_texture.view, + &hdr.brightness_texture.view, + ], Some(&depth_view), color, ); diff --git a/crates/renderling/src/ibl/diffuse_irradiance.rs b/crates/renderling/src/ibl/diffuse_irradiance.rs index 9d32f4db..010395bf 100644 --- a/crates/renderling/src/ibl/diffuse_irradiance.rs +++ b/crates/renderling/src/ibl/diffuse_irradiance.rs @@ -75,10 +75,11 @@ impl DiffuseIrradianceConvolutionRenderPipeline { pub fn new(device: &wgpu::Device, format: wgpu::TextureFormat) -> Self { log::trace!("creating convolution render pipeline with format '{format:?}'"); let vertex_shader = device.create_shader_module(wgpu::include_spirv!( - "../linkage/vertex_position_passthru.spv" + "../linkage/skybox-vertex_position_passthru.spv" )); log::trace!("creating fragment shader"); let fragment_shader = device.create_shader_module(wgpu::include_wgsl!( + // TODO: rewrite this shader in Rust after atomics are added to naga spv "../wgsl/diffuse_irradiance_convolution.wgsl" )); log::trace!(" done."); @@ -95,7 +96,7 @@ impl DiffuseIrradianceConvolutionRenderPipeline { layout: Some(&pp_layout), vertex: wgpu::VertexState { module: &vertex_shader, - entry_point: "vertex_position_passthru", + entry_point: "skybox::vertex_position_passthru", buffers: &[wgpu::VertexBufferLayout { array_stride: { let position_size = std::mem::size_of::(); diff --git a/crates/renderling/src/ibl/prefiltered_environment.rs b/crates/renderling/src/ibl/prefiltered_environment.rs index c38bf3a3..117c8651 100644 --- a/crates/renderling/src/ibl/prefiltered_environment.rs +++ b/crates/renderling/src/ibl/prefiltered_environment.rs @@ -85,17 +85,17 @@ pub fn create_pipeline_and_bindgroup( push_constant_ranges: &[], }); let vertex_shader = device.create_shader_module(wgpu::include_spirv!( - "../linkage/vertex_prefilter_environment_cubemap.spv" + "../linkage/convolution-vertex_prefilter_environment_cubemap.spv" )); let fragment_shader = device.create_shader_module(wgpu::include_spirv!( - "../linkage/fragment_prefilter_environment_cubemap.spv" + "../linkage/convolution-fragment_prefilter_environment_cubemap.spv" )); let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: Some("prefiltered environment"), layout: Some(&pp_layout), vertex: wgpu::VertexState { module: &vertex_shader, - entry_point: "vertex_prefilter_environment_cubemap", + entry_point: "convolution::vertex_prefilter_environment_cubemap", buffers: &[wgpu::VertexBufferLayout { array_stride: 3 * std::mem::size_of::() as u64, step_mode: wgpu::VertexStepMode::Vertex, @@ -121,7 +121,7 @@ pub fn create_pipeline_and_bindgroup( }, fragment: Some(wgpu::FragmentState { module: &fragment_shader, - entry_point: "fragment_prefilter_environment_cubemap", + entry_point: "convolution::fragment_prefilter_environment_cubemap", targets: &[Some(wgpu::ColorTargetState { format: wgpu::TextureFormat::Rgba16Float, blend: Some(wgpu::BlendState { diff --git a/crates/renderling/src/linkage/compute_cull_entities.spv b/crates/renderling/src/linkage/compute_cull_entities.spv deleted file mode 100644 index 8a7f5a1dc511c39d42ea729e1902827d16daec54..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2164 zcmaKr$xc;45QZDBfCDlQ4sde-C&U<>03{$q5^x5mr7Q>w5*!lYJ$wll4hvt&hcFI_ z(eJx-kq!$xsZ&*dja}8J);!jhTAETyooSt~r#m$#5&5^ilsZzaf&1-D?WqN>edEom zx4S!=>l?e<+v}UJceZx6Hs66aL;49S-l~RRnoU}rgBXG~y9^1#Xi*-TaXHK!I zz^oxQ2`y8t9o`CASN(XQo98IH#~4pwQO~%*qTb;Gi+a`xKV!@ny;{Thh%vf-Kl>6p zQDAKu8!9mC=~oq)a}x8OQP(0VzrEU5A!=`dzITuH{3Y$ zDtn@q{>Iy%Cv^L>zPk0T;|}`Kdf*l086dhBUO|J{+^4!Ry;SXokKi)^SDwi6gUFuH zot1rreiGT9LqCOVKYgI5;dO54&aGEc_}gm*4N=dz*t1yJhmoD3c6V*hVs)nGyV3uD zRMxZ)YnW%4TliMYQAhS|s8`gf!}SxJ&AN9KyNIv%a_-|w=ds=Hv94&sGILR+kA1&^o_keK{l6f%X>bBd%UN%XV7!V8p?~zXafEM@-4>R=F!Bg eWgq4-#@JF9SNDBr_f3?#xO%)l`5*q28R#D%x^n3N diff --git a/crates/renderling/src/linkage/convolution-fragment_bloom.spv b/crates/renderling/src/linkage/convolution-fragment_bloom.spv new file mode 100644 index 0000000000000000000000000000000000000000..2efb01370fa2b4b55eee7d817529bc6f98057110 GIT binary patch literal 3780 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 literal 0 HcmV?d00001 diff --git a/crates/renderling/src/linkage/convolution-fragment_generate_mipmap.spv b/crates/renderling/src/linkage/convolution-fragment_generate_mipmap.spv new file mode 100644 index 0000000000000000000000000000000000000000..ce759111ea86edc9caf6a6bac71717eeb4a25984 GIT binary patch literal 608 zcmYk2+e*Vw5JgW@OR1Np#Y?djY4yp=vy_Uy`Um=0g2YlVNh#6)^)Gx8Tq`Evz?_}E zXJ*gIX-Ajc&}oGb;&97p3_}!D#IK_e`k`Ivo-yhUX#KUwKa2N|a$V$$#oIPnWoce6 zS81MZlQLaq>rIwy=!-~ao$HVTnDbro303W!H01XE=g!VZXROOFNJFl_b)(@;{eZg8)(*Za`>4Scc(PGfv0cgnY>sjyayxv1lZ|`#&dieeER;>Y5 z$PGPt-YV>sAA{EQa)-QmZ@G7HpE2QU1UhR?FLxVvysGzacKJWwRL9^0z4}i`z2*MX k8L78i&nr^DXT02VU2FEep!sGuwXWAA)4qq=51Z%YFTp+?_5c6? literal 0 HcmV?d00001 diff --git a/crates/renderling/src/linkage/convolution-fragment_prefilter_environment_cubemap.spv b/crates/renderling/src/linkage/convolution-fragment_prefilter_environment_cubemap.spv new file mode 100644 index 0000000000000000000000000000000000000000..37fc00965b24d93ecd27400d7d29432895d48365 GIT binary patch literal 6552 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 Ce35qm literal 0 HcmV?d00001 diff --git a/crates/renderling/src/linkage/convolution-vertex_brdf_lut_convolution.spv b/crates/renderling/src/linkage/convolution-vertex_brdf_lut_convolution.spv new file mode 100644 index 0000000000000000000000000000000000000000..6b37843c7052a9a757e6d96a552041e9eb67743f GIT binary patch literal 588 zcmYk3T}uLC5QV2Tk+R%OQ!5ao%kCqL>eAc#1q*&b5H5&`m;KfLg*QRZv%265voq(+ zd(O=6R+9T#RF5Jeja#0cW+WkF{%uDzqEhfa)3_{Z`;&aJ&cD7_pZQ`qT+f!P*(Mt= zr}GRhJ7_9M2=&KAl^9XU^$CwyzFwdm_U>U{z1Z0u+)l^?qKo#FELJ1dN33Q5J|nA> z7oU^uscW%GgM0^*lC7Wj8h2Fp0M_qN4uZKJ9hqcGOwm9=U;sqDqpiFM Zd(?{Wb3?7(`}X|?&hXvs{o(LL`~xBmA4mWI literal 0 HcmV?d00001 diff --git a/crates/renderling/src/linkage/convolution-vertex_generate_mipmap.spv b/crates/renderling/src/linkage/convolution-vertex_generate_mipmap.spv new file mode 100644 index 0000000000000000000000000000000000000000..70f72adabf0768291f424410b52622b2115baffe GIT binary patch literal 1172 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 diff --git a/crates/renderling/src/linkage/fragment_brdf_lut_convolution.spv b/crates/renderling/src/linkage/fragment_brdf_lut_convolution.spv deleted file mode 100644 index c4d41b412f1c33b5824b64ec9dff8cb691dde5bb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5200 zcmZ9Qd#u-E9LK-9s3x5#rBqH%7ZoL4P#vX_p8FD%9#_RPx&-?7V^X>Qad_M2@`}usH=lMM6`JUhD-K(u%)%Sp^ zs#>b0z3F%ksd`l+_WOob)j`#PwcoMqtYuR#U%zVV%vr~&8&q`-@c)+T;HnROFWTyL zD=)m{f;H<`oVRY(>J=AXv3|w*Yu8-47UQC|Yv9$Z9gL;EA8l#Xw<1;jw3R-Xa}je) z2md>M49!PhJpN&Lqwrg4KEqItrf;PUhf}9--d6l0>5VV0-%6kOLr{&y9}99+-B;C= z5-6Y$L$eH0?(#(Afbv5SkH0Ph@824iaE$f+u%`-Fp>@I&n=6M4C zU|RS&HGl7dx#V<@owGTkpC`{k&E=Zf8{8?_=kP?k5Pu6#{6+XPc;YX{-^&w!34RMt z{8RDw^ThAK|Brw1m*NZPJD1b+tBup*FT=ZP=XLY5U#H_|udK6cY^7&8^*WC!XL&M< zw$83KV^0EeOvWe2TsNrNI4Ax*{Eu2*pBsN+%~$n#a*m6;^e=fXmS9_Zq1ogfOYd3p z@!bA_cNu*&KM71yN`06J-AH`?zh7I z#$5j_Hbd&159*WC-u>`r=^zG7`}M&E=Mc=c+shMkIZ2_<7Vs=*llR*OPRi@Q=h$j3fnx+#XQCc z_cXS-lewN_*n>va=U;G#7HFxlz>kxOo>hkp<@Zco7mV2hu^*u*;sU)-8TH?ZgyHgXIC6IYiUE%=U?%h#U~-E-3$yg6*%%^5$l!K^I@7Qq7JNBeT9JPr*r{IGa=N@D~=3>h^F4}q6&gI}9+IDccrgnbO z_Cf1Bo@b5TlMBG*e2*^YzVm%`Dos3XbNOAQqj0`seOG$lSkn%)a?T~#nFRyt-lMa@ z#LeUHHs`9LiTkd|T5lhp#Ssaa-Z7i`u8l`fkUTGro7HZGuF+-cK%ziU4vY6oa4Q_)jW#r88EhbV`h(5V9WFA##R>0 zwdQQC0u#@;)kTA(d(W;0Q)lh7kJn(U70W(uz|KC(d9EBA{@Mm_PI=>mzp=qPe|gV+ zYI+m4IgFz=b9ts<;)(SvcIM(3o4WAZ8oaS=j!Dg5EW9;Wo0@OOmP^gw#P+e~ayw`{ zY1UZV+NseOPtEsYTXSQj=5NT;kCV$D?ZM_#=kxbA6Y;p7Ej4j~j*YSz_7B-(cu3F=&3IBEv z-Z}ZZWX{1m_{K1o+T{L#1&P@b>q9WyI+tT?>cW5A;EiQ-OwPe4h4&n&%{llKTQ29| zCu|?jf!t@beKhN?Z9UcKi{~8ti0wHrR?fkGY}Y8)9Ow9)gP*ZI2gX)!%+&W?#>(?y z9bLb}zX!&rzL({lcewW(oO|8mK5KB+S#Ddo|MuYZs?M7l7-&jqJTI=NdYZSJ5jHBN1nP(d|%dhL`KK7jFICwRg=h4_c&QoqY zZ34}CX*&ls`r?`AL~P?5tEo|&c}~JMk8#vHJ~e5N4PK~`9zq3W-IqJl3FB*F_&atWS`)*H-bB)IDOuys%_Z@R>rx{0IjeGG2 by?bNtIdlH^$rarGWDb3w66;@f`9#|P+q?)m diff --git a/crates/renderling/src/linkage/fragment_cubemap.spv b/crates/renderling/src/linkage/fragment_cubemap.spv deleted file mode 100644 index 71b56f7481afca63f5b857326f345a731ec797c4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1088 zcmYk5TT7Ht6o%J~ITj6BXr8OjLv~R}L`o3qr8gpqHgg%~Aq~c??7At)!%e@YKh%$i zQV@KekG*9!yx&^yTJK(at-WWus*^p@U5SX^IOFTO7u7JL{nHlpi4Qk*nyVUl8C3YHr2wZZt z<3LU36ZkHs}b{}4VL2?6Fxn~r72eImn_i^G7cAUsCR{tp)^&`NQ z`Dn@7izDnA!?)IhG8QH8C;kZTnFZDwp7V_E<@RUIjN!NyQ}C>F_Pi>|@r=jGS1X^! znlHd8Cp^KLd!2ILt$DfqIk!vh?B1n)$*ryLn&VZRKlkB%{D-Yz=g}6}RbYL&cd!Pm zzedzKwtyN}j_u+bv!|a)w})?B)Z=EkcOPHP3t%0gF4y^hZyo!~ds_H;k9lF8I#b%u z0Wj~osQu0P`X7Ng-{K!P@Ez;AU>b9W;1h6u{d24ux$)C9$2WX`7uNbktiZN`eVXgs z=l;L^0}AX9FafOH2G;aWr?AJsH)&AMw>?QQ@489M-|cCNTPNSoMv7yuV{cNd(h+N< bnD@9!Kh|pk_p3MWObc{T!*jjJH0!m>@Ra+mDcL*Mmx0X z`anMzVz+zBV3rE<)Q5>H;1GL9u*TLlK9Xb4 ztPqc4tuKE7)fl!4a}=vTg~t6+KjLF>DRpN?(w^chHTLH@v5Z}wQ~mGadsZpgzx+hz zjYm0mvfxu1MspnBo=;${Z=a{QQR9=qo&wR=6dY^D+NW`Pfcnch_U`3Ee+GZ-N39;Q zFa7b>W?j9#c~36mD%Kuc#@?NGFL#l*>V3uh-rsMc1?&~H_Rs*Hf0d{6+|L|ln|qKs zdtA%9F21}NNV|)@5AoHU1J)5;5VarSd!GHoo;Cc~V_sNifGOJ z-kLGz8+`N58Qj~bEJS72FJrEMFNJ7=R`+FvNz$`U?jN%)OhtU5YoU#VPP7 z&;2D5i`yb{lvSF7ShyDn_GWGo5?&b;E%%3G-;5tm`agtA{BPKQIDLL^MpD7KH#@5i zt6kn*{Oruko#pS$xY~^-@wq%{*)#QtrRGAGnEj<4GLvuoY5f#s`G3<-i~5I;EI-^* zF{(?snLFu{X-M^@6?wVWU!85=I``UTx3__tqwZ(ft~Qeq-?JrZ&9!wLMMf8{b9{*V z#wdS>@s(Bmg>LsA_IdA*C;P7t{_7X}`(^snxl8Xa-KckXQq+gtFPHaBo+R^|z0NDr zHq3g`~Pye%v5r_~pmE8=;RjJT5x2!LsOFaQZZx<;MPVFPz@_?CF`y&vfLXYZ^b= z71;-52Tt1E_(S^KbE}W*Pc_yz-;l685v}wu(sD-rOo_Db!Tz~3tN*X9Ig+<(?T0%b z{&Y7VPugob=kPvrOrNwXPwsY4yIh#MnptyC;(5zvnc`P+|5<-Fns1i<^|ohsr1{=R zXW{QPce=Olu`pRC8GkkENBzyEY!y59|J`+O>XYh*{`Wtps+89|7k}v8uf}#y_UNv+ zdMZEGZSSu>a`doSC#zi9{D_R&RG;^ZpUowY@72i?H%zV%NpyRwAK=Z-@u{mrqLqn( z0hFi%uqOA#z~rh9Oz!dVzK+iR!4dj;@kFP5IgtH^Y?g^B{rKe{D5nEDhI9Rt)nj`Juya|=GyAt3I1?)+769gP#7SG6@x>3g5` zKI84jsySir&XlsA*SmHD9nQ{i?66lt2+*k@=LUE)GKnxFaxStbK{*!&mNbG`MEeFy nK1R0**%hEbgMlTD?}33#r63IOW(CF;0|OHf-Ure-??oAj_E;ve>3^b+)a+g@m3cJ4WI z=G@ua%BG!YA4EhRPkfDjWFceSpGS0~Rr7vx&>_|S=eAf?<$AYVmFsd_?8;^JwW*4Y z;*3H#A+v`nuKpT8)E(J8wvmKdq?uS#O)Y7CWp+PP{n$T!KYO3 zk+Lqv-dC*FT@10F={~W$`tNF<{9 diff --git a/crates/renderling/src/linkage/fragment_prefilter_environment_cubemap.spv b/crates/renderling/src/linkage/fragment_prefilter_environment_cubemap.spv deleted file mode 100644 index 24a6e4d1f33df24ca43ee9608ea8caf5bb850fa9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6540 zcmZ9QZLn5V6~_+>80llEZ+eNr?AxJM6f|KB&UL2f2;>~I-t|jK= z=#=k%)fp3-%4YhI+60o;Sj{+PS-z#FsZ(S5g^ZlC-|sy8|9FPY*>|t?U;nk%-fQpk zoWmJ4a_YEh%!sP0CRJ~YWT2f;jjTi*Up=v^K3t8h^?_9vuj<*k>#CmKIkV&)SB-M0 z|4yntT8&0F#O58FuHHJdZP(@7cMNUb@`YVPJ1!sEwr9(ZYq!WywK{rnH@YK!! zLA=rR9Odh$-8nb;&O^I7wX4T|5}LUu(`_dc`bli1=y7 zGeEi+mAgy4X~;Hp{kv=J1ATg}5BC{!2C{Q&@mllgDOlpoL?1yD*ARU!@_4#yJeSejuB!vxeYGa`X`sfLNAD`~eB=?x@)ppo;X=mA zbmuso9XIA8y6aBYN4&4bT6I~BOzS(fnouo4$ULk^nlWvzacN7ex$6Ds+KrpqENgLk zqYt$7#UCgC3S{%74xd3jo<6_C@~j#!zAKTxarFL0;jco63a52WKHK14sI40A4|g@% zT3Ylq=m%)gFGfFy7X5SRzoA86i#~}K{Sx$dXwlc9zt6wu>(K?&t>dNY)$q*dm!Vzv zv%P&C%zX;79Ba6N(Rzzn2X)Van7U`)drmB|HX>Kmp%vOaciwY0b@zx8)onT!&&ksS z^PaGNIUmn}73@0!+Q^?Zr+RkMmwF2JRh6) z*A!&A&S4?`o|ma$?)Pdq*5UzlF6Q(sw^HS6vI^*ySb80c2x4 z$M}37ovZfIZa2!58`vHgoW%q}vfI_KHw=5%zfQ^C(c zHm@yMZ^48gF7=*+B3GOBo~w~&y|tZ7KaXyF<>3;49)%Us@#GzZE55Ms zg7pi`nD%F0$vuXV%{0_eW%7<@f(6GFSckugx5u!Pwtx*v(=0H#GMD zHSBVotNuCj@1vVTUY;r56sYwxMSJG<9Jsc`9$9!k+hc3Zt@Ugg)rccE@kbZ@IQ*M;gfn||SrE!>YB!yN~%O>XY}@yI?`7~1|5P+ZGsy7r&2XFeZ4gsjc?ok4zgeOGv$ zE}p(0+-J*Qi_Lct-#e1;1Y~W-4|Y<)7~1PP8Ko1Fw$$=@WbxGU6lCLAOXE6MIf;8J zvTL&i`vjQzZK>fW(e>4qTrVJtCszk@aychs%Sntg3+6l$<3(ig#5fB%G0f%qJQ=CUPjFS2s9|^RZk9P?A8gpIfp&AozPe$ze%j5Mdt?o=IgBGW>)%^2@x&TN&RX@c z%ZvTShTT|peR7ZND|YV@xw%JfLe`dh1FPbhoV<*1A29=RLYd&F3| zNA5v(joO-i`sW_G57~Rf*z%2;bGw!WYqtekS1@aldaXyX{@PNHOOd(i-&6O_Sn?9* z^T@_Fe0(;5an(NKF~~7a@@z!*JAu5M_gCNfDT!|I^^smsZn{w)ZJ+kkW@%t)r{Iq?IzL&0_vUQZBE}k=U zkcF6kCC-`eAuyo&+lh8_g#S3Y-?QX|f4ITd?svBA@6XXUqRYvddZO?}WZaZU1Fi|^ll+x$AR-<#BzFg}IdcfdhW*#&tZx_g&x3??#@-==T7}WsH7LZsnR#e+%gb^Bad8M>&1; yaC`^t5i1-=InFU0*EtK_@3Y2nl;d3GxK6(@IX_45L+92-pIvar|5D-e=>G@dR!#W; diff --git a/crates/renderling/src/linkage/fragment_tonemapping.spv b/crates/renderling/src/linkage/fragment_tonemapping.spv deleted file mode 100644 index f146c675392c699bfa0b36bceeb627091b63f607..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5468 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 diff --git a/crates/renderling/src/linkage/main_vertex_scene.spv b/crates/renderling/src/linkage/main_vertex_scene.spv deleted file mode 100644 index 1467f551d4bcc99923a092182f86ae6a8983437f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43892 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 b{NB9G-@4Wuj(^?4zhSL8{4eR=yJq|!DQZjI diff --git a/crates/renderling/src/linkage/skybox-fragment_cubemap.spv b/crates/renderling/src/linkage/skybox-fragment_cubemap.spv new file mode 100644 index 0000000000000000000000000000000000000000..0642e9b64f3833ae36875d4833ec369f87c0003f GIT binary patch literal 1096 zcmYk5$xf6}5QU3oG!TZ+f{Ze*G{g`!1_CbGV1R(QBs8{Qq-nyC;6~d8%F3tlg?I?U zkeK*=&Ao=39IEP6-E*s|e;NyutdQVV)dUF~p10#J)d|mb189-!)d-xjn}AV2>VSXLWaSues@bxqs)qA$o^hpw{D+_}G|C;85Keb2e)oO>smDdj`wmJmXB_{OiYGh~7h^^cwqwufBe z&&-~iom^^OnLK{tsJtB^t4e;mLm_lvXTZ|+yO(d>nVMQ?RO>fui_J^5+soG)wfSas zvA%qx+K4ssJ3$T<=wfXi%+SgBKA`B!9RhNqKa8#D+ha4|$>h6-dHu-Wjn|7`1ij?; zBswI|(C)^tCd{rJ`v(TaW#@e(@n zM@%4x*v4m#0%H-*HimC+?-1+8u^+9!FGv3XJ~G`GwuOTj@}1wBDEf*sO|;E~JHQ@? z@vS>e&fR?%^7V^o--V*@Exf(=h|fQQJ@|9DY~J4Hl#_JwK5-83^#|`F`V3rmt^l8R zmS5+-2P!~`vHRE+;7lti?*X=P(G|8PwJmHp$AEo=yx8XR{pY=xu;+ixUcp)T8_T))pU>N*nT(mdPdBfyZ{wo zZgZXWzxc<2=Ew|uhOTWoi;pEYtKe+}Cn=Ho5irG5du zmc-k4|B%MLyFL9L{3hd#KVsX*ygkN&`8oC-Lw^GHkmq;dt>-~U>icbe23c^P8o$>s zX-wV)V*WS3rg8hlJ!)wjwGmrLW6l(L^)%+q=D3gjt^(`joA*7HnIeZa_m8bkf&Y)> BWzGNq literal 0 HcmV?d00001 diff --git a/crates/renderling/src/linkage/skybox-vertex.spv b/crates/renderling/src/linkage/skybox-vertex.spv new file mode 100644 index 0000000000000000000000000000000000000000..5a049b9f44d1404cbc87b534fadaf1511406f4d3 GIT binary patch literal 3324 zcmZ9O$#0HP6vnSkB*voV)MqZL#uh~f5j8E)h=mBP);xq(wN@SV;OV4o`5;ugMc~T7TDEmC^70E&| zx~jr1eHw}xgGFhQUz4zkghku}?O^oLoTe4CHbm z;=54-MqR{sYrx=*Z<22Rh_?m*m}hhLcZ~D!o!~ba-x~0ke_O~?;_HgOwhN~R&db@* zISV#A^|9HhiH=YQA9QeR;;BO%J2B1yj~^JEc6jsM68hu&uSj~8hdxInaNy*j=h2MY z=Wh0?1fMZs;Px5MKFrB^CZvO(P%XZfLFwdX8M}}%U}HlWV`j!K38N;<*yW4?8@rOR z7<*k9XJOgzM#g}R-OL!Vp4$oOo||VhQ`pHOu8d`7{Tb6%&37u}5j&l+SociEsKZ&v zcUD4;=yxvT+R~#u)AQ27nWOs{kWDW2|V+Fr>6P0foC4@)O4R8q|-a!l(CPp z2PR>kWYar-&iz?-?456weCEFdp5EcfZT@TE=^dUr=D!7=-ucFI9ON^< zGw{53cygQH6?on|Jax=>2c8*nerlTU3HBQp?kMMW7K68KcE5zSm{@(I; NH`?I;^y{-r@*iq<%@P0r literal 0 HcmV?d00001 diff --git a/crates/renderling/src/linkage/skybox-vertex_position_passthru.spv b/crates/renderling/src/linkage/skybox-vertex_position_passthru.spv new file mode 100644 index 0000000000000000000000000000000000000000..94e7cdd343fa748ffc77444aba59ce11810c4398 GIT binary patch literal 4104 zcmZ9PO>9+F6oqeF3X%eCDL+P`zP1YGuL0%HBKS1MqE-TEfW{Cakw8PBwXYDEV2N>t zfN|nLV~kNWPW=f}kQm3t7$ZWM3dAXDK-c%)U1Y_R9PU2r+xP5s&fAwjOLO0hqP3|g ziuPiFhel`7ToCfSF|R1vis{8F2}8rL3=h;M-xyfCZZ&yxiWVOAKii9rddz#Z!zcHT zofsTEeqdtqz=?h1W3|!A(XpfZ#$T`1CJ#*LKn)jOu&HJ4X4ZrO7L1_3#`%rU8r<$!N`%#aWVGlz( zF|4PqlHXYO#+tg_v1WhY4f%L5U!Zz(w616_6lvF!bt7kb{XMjt^F_JKiY9~iI`u@u zTX*q5{qCsAK5HvW4L{ZfN{u@~!^>(->gKy^w5D!qk84dX zUw;ty7GidF!<%4}Z?vpcp;#Hfl{Co}KrkVs!FmskgbSK~4Tn z!w+}F-QmxClVWr33yv@Ir-E~LaQv8W4$eKo@!@;iqL_Qk`+iz$?lHBh*5opLXU{4| z%lm$=)bicuOO3m*udRxGuBu{cau?<=C^n}KoVzf8F*r5h+=cl|!Kn}DE`07`#l4#D zDSnHWH92=`+q5Q^;d5_SjF#u#S!#LiS4%C=y-S+U#opB9F6?`EaO%Lh3-dj}sR`#U z%twM#AI@F)+^;F-8&2~y$RZO44tQ}U&HBt@$2O8`GNJN3+*) z#l8!AA0N~(e=|6}52v2_L~wc^PHppAaC#q(51;XvV$R5K=66mhwx_CMa`0*XmSS`I z5{^&vDaGdWB^;mTr-Rd%aD3X++luj&J#$8D?@7;)gHP}IZg6@Aj!*Nm!RZI6m#^PsMo3p1Gs7Ytl31;M05l6`Y=dz_Y_w&n?n1y)VNRg$4n)h(VBc~ZPNI=F}2y!&i+qpmv*ky?6pIh?_!r2 z-l$>T8GLuisb@Yn_@0te+q^6INXhYGz9=}~GH1q<`QqSw%WyoIcL(QNhU3Znk>GsG za6FwZ|Bc+Q7!UM>wPjjUmp-tzTx)9a+gn?qHT&~>Z!c!l|GmCaYxZOh-XVwov$QG) Lc2~dfM$P{KG({0e literal 0 HcmV?d00001 diff --git a/crates/renderling/src/linkage/stage-compute_cull_entities.spv b/crates/renderling/src/linkage/stage-compute_cull_entities.spv new file mode 100644 index 0000000000000000000000000000000000000000..b09f4f66f3f628a4705d50e60ee2e0e6150598ff GIT binary patch literal 2172 zcmaLW*^W zepP$v6BoUbb=F$n8u!}!3=eHv)Ge6Tb=^pJH(#`}8|onB$JJf8q8pyYeMe#~h~$4U zbN}J=SZqrk~C{nt3$+Lc&m3&iwFbx3Jahx082N*;}I{`Q}8GUszhrVzqN|YKW_y!-gB?9O{om zn$^C|LdsK@j;fb<`Q}4qn@@kX{`Te$)+TyoxzgiuonF^OgN;baU)#vF0w{rs&RF_E_{m zcw_oIqd8%0`*b$-#5fSG_Qbz4a$Xp2KJk2V*!=Ea zjQH_LKD`d-&ADGbkY5e7?#*6`lWW%Rdtx=H0w**xBfC|HGTS z6Z^UohU@2_va<`3$a(lLhA)MkhhFnsj^3Zge^~QeiOyfKSAU50A6D$O==`<&>(OEU zeRb^0J(KzHfAt literal 0 HcmV?d00001 diff --git a/crates/renderling/src/linkage/stage-main_fragment_scene.spv b/crates/renderling/src/linkage/stage-main_fragment_scene.spv new file mode 100644 index 0000000000000000000000000000000000000000..b616dd96db80efe3da2a64d83ef536fe32e83828 GIT binary patch literal 52560 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 E08h92)&Kwi literal 0 HcmV?d00001 diff --git a/crates/renderling/src/linkage/stage-main_vertex_scene.spv b/crates/renderling/src/linkage/stage-main_vertex_scene.spv new file mode 100644 index 0000000000000000000000000000000000000000..b18725f9e0a65be76be887b7395add3b8fbf1fdd GIT binary patch literal 43900 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 BWzYZs literal 0 HcmV?d00001 diff --git a/crates/renderling/src/linkage/stage-new_stage_vertex.spv b/crates/renderling/src/linkage/stage-new_stage_vertex.spv new file mode 100644 index 0000000000000000000000000000000000000000..8614ec389e38233f3bbad1969231b918f689bed1 GIT binary patch literal 60988 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 Nt(A?O($%v?$22RelqvuK diff --git a/crates/renderling/src/linkage/stage_vertex.spv b/crates/renderling/src/linkage/stage_vertex.spv deleted file mode 100644 index 651915224b085896f4e25596ea255c1828264995..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 58828 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 literal 0 HcmV?d00001 diff --git a/crates/renderling/src/linkage/ui-fragment.spv b/crates/renderling/src/linkage/ui-fragment.spv new file mode 100644 index 0000000000000000000000000000000000000000..8a70212e2e2784c858fecb7636e16b8f26c1cca6 GIT binary patch literal 1056 zcmYk3$xcE+5Jj6w;zAHn2x1gZOfIBlO6sFN$>jqN|jI*o4utwmhHkKQ1JDwSr4mtm@S_w#T(SE8N6YsRziHM~9Q zczZa9H5>R0uva5YQe%{tQxnZt^GtKJO|-@f#OAHtWFFtgI&E^+T>{RnwuM*gUHfV5 zf5;hoNB={;vGc|MHnx}l8ui{&t!CcY;@lm6+ILZ_`A%PC+xP)mXIlf-H+H6V;5^3O zX9L*J*tgsS_A~a5$H0D={t|6&W++qe0o;x=CUp{}_dunU~m z{RQXzw>{vDTI4$H<-BL)_EXN;BG*kh=a1Y0d*{<$(;hwcYR#SV8{VN0>}@Y=tNCU& n=XOSC^}O0c;5^nH*QotS>kZgj)4Qqn{1NZq-<$j8HeK)sST!xz literal 0 HcmV?d00001 diff --git a/crates/renderling/src/linkage/ui-vertex.spv b/crates/renderling/src/linkage/ui-vertex.spv new file mode 100644 index 0000000000000000000000000000000000000000..2eb44998c0b6660a1c063ffaf982fea2be9368a7 GIT binary patch literal 2368 zcmZYATZ`6J6bA6^ydElb#xh4cO|On76q;paXP9gor2#A_#hZ@4Hw0!7Xd{vz~R_Yw!Q-Xl8Ls+q9vzHrGBIWy`v^ z%`}Ythx=RG+BQ%0wKEsa9D42Y%ZH8}eVX5Fn+Ethmw(nwpUmfpB{>GH)ZZQ`*p z+@{Fp$d<@mk-H=JM7BoO+Q!KaTXen)5q;44mwr0g?u%|MPPbMZf1Da?sh;*HJ+Z^q zFlOhJx`P|$5zEin#O{a`H|m_+>g~$Tj~%wEo^$ZuJ7K5iqVF5%W;L|a+2s^(j^<#| z&67U0k0$4y?KU$Q`kHt7&Zm?4=;O%lI) zL)rI+c{n@#WY6(PuvNPH8@jIEqmkCumrwMZ-hjMnjI*o9PY;V7<0{|&;Ogl>uvmWL zAIq**=?8=PI!DDn9xO(E{@$G!@lRxD<6r%GhdB19vhy{Ah3MX-IjOf0FK+_J*9W6E zZXs2?Cw|2F)DYv%u!(slyBM7Ma;S}?v%9a-_3wS?iCur*N9oSvO~}D+Ufu* z(v$h<>r=+omP@}j(bsPtOC|Tr-*WGNM?0GRWMbtw9??(f&jy<>-OQFE`dba_Zcap; z$D464{LJOK2-~T2s((7{F64lnLw>oO<7A|A$yYs`>b7&~%=wlhIIFmoV4S(hH_rZi zr1I?#mQVZ(-L@@$^cN%Iwnx~Cf2s4nP5j$Eel1u`^}Rj8RmZndeb0txs~+cqaaM6V zI){FzpKm^j+Wd>#6RhTtI~z_fa+KfsV6nLB@5|s(I5Qpg_f>ed>gDTToK@U6okOqY z=H21PNEO+_*6^xT-TVA&J^Laj~sO@k>ZF7{X z?AHf&eVC~{mGegI;@o?2Uxef9-i!OK`?>ex{^@@1z3RQ0c|`rcAK!}}%l_8DuEt92 zdaOHNkKONpRovCi$y>GG3#TWx;@%I|6Rzrg5M2E@tNdci{^7vx-0bqy-F*}+hjSJ8 haj-aNIh%Lsx9O8$zHEL2y%oPR{(oZoH(eZ${0F?|mzV$m literal 0 HcmV?d00001 diff --git a/crates/renderling/src/linkage/ui_fragment.spv b/crates/renderling/src/linkage/ui_fragment.spv deleted file mode 100644 index d1a6e30eeec70ab7ed8d41d8b3bdb7ff1d681dbb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1052 zcmYk3$x1^(5JlTW4T2=bBo1*(jEeJ&2%-xIvek_)#f6FsU5I{yKjUBd5#j)X=e%@7 z3ht}ARn=9u`{i@3Lg?!WA(X-yU#%Q+!HoW+8bUGTv%KF@7!195>#q;jch5KXkGD@R zbcU?US|8{K1u#U`UgI{g^USFntK$0Hf5(c)2qg@L_xpFmJtrx9)wc^GsRYyu5~2pS;495qy)(DBgI5 z9`}y{`^Lin=*)DFdy~mE=hi!6)?24*BIYM!KJh8I=iG6?IeGeTa`pP&_Q<`T@13~w z)TK=j&EVB%uNsoRDp51$y|ZuZxjm+PPv>dg`$00M+)dFgg`%ZH}O>*CP9;iv~ zy%vD8bT^T`c7ibzAJLhJdJs0y^~YxB5=2F|HZ^5y!OjnYs+8-sMq*{bN=g9 zphhj_)`-n{&y-t_IklzSM$D-{#1GIW7ww`fS`=vzw1_tKEecu$ z4J-G3g9a$6h#(@0B8Z^h@4e5lm!3F1=luU?dd_*hrTW3?;4UEH3*+O>+=hnO)_Wb5X(hB4}%Vhru(=YOo2$5{Q$qhALSSSexh*6ZPp zdg`9YJ;lrP@6S>FYxY3F_U%PpTi%@dJC`!z(RN1K_N(o@v}^vQd=71ib0)=DXIhMP zzW-ty@>x4?PdB-Hxhoo+UhIweU8~i@%lfb@iQB}jZ69TP-IxC2<+K;E3fyLH{oGHP z7dcD)B8PR2ah`IF2@Crmb{%4U>-l|{Kk_}& z$g+vBALsVs@-QzSgfPFyIsU>L<{l%LJ&hpFC+wr>&PUt5j3Umb3hHc*AnP6=M2qf z{E5W1;-`HQF>XB~7x<}!`?o88J;hI>8xwuE5gc{gP4wLkE*Cv^po@6|>r9yN&Gf!r z3~TEjSRcAIi`Xofy_h5X=Fp85i~e4q`w2MHVt+5e<)W8Y=whD0UMEbr%I4kq8;r^Yde?me)t$X}(K; Yn;xR;E9Y;Z--^F8{yxh6p^GEPUv9UU;s5{u diff --git a/crates/renderling/src/linkage/vertex_brdf_lut_convolution.spv b/crates/renderling/src/linkage/vertex_brdf_lut_convolution.spv deleted file mode 100644 index b682afb6ce6d5bd5ab5f225d2e9993230f073557..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 572 zcmXw#%W48)424rKi1sp$*J`0yT)G#bv|YN@Cm3jJp->zUD_!F|>jS>%-Zh|NHkY0-@mNNmXqpo%PS48`2+P@}x zzjd*%5XBemXtC!W=N?U&X0MRj#_ zQk_-j#~0O6@3h}L?gw5`d$f>0qs15T6u9KtqRx7uLg8y@*`P9oN6W4DBuMTpB6XtTi%^Cftfh+SDHT!j#`OJXd zd`mgZXC8Af!e{{No+ZZ|(sSyqQ=8}Jk@jwFIqm{`R-H%xm%v$+(XM-a#opRP*1b;L z8nf;Vd-!s({}yR}XJLJLHukXy&o*oDymzKfX!oA1J=>R)O?^rCo-2U$S&#XJ zJ*X==q5leSp2GeIH4kj7iT}^GmTaE?*KZx2K96nVbNN3~>-UEA++w+ISzr($=P2fEr@FF~0fNSFV4z_@HXZ9QWHqv#EueK6=1m<-A z13&L+2N+9V^LS^wK)rdqGr4=~u}9OhELh>@5`0>(gXD{*Hrx+6@RNt8 zj?WyOJ$n4up{a*wW@e8}KOygC`qp{OUgiXm&t*)<9b|orVKdr3qq(-)3T8CdQyVUt z>#J$fx(j(a)Ev>WUZ-eTZ?0%rZ@y^ycfA*j<~h_}Voq>MZ^ZT7Z}E2i^WeEd|a zTGtq1TECM&UB`SE--r9IXS%m{=JXwXj51}ZyPP<6-e>BrAeQaHhxc?PQ_T_WDq`1j zsChS6Gp%(*+dyosBib0Tn&*qQvDNewZBwgxPBpz;&vf7V?hWMbTeTa>t#v%x`rAyb zZhiLHLTsJyrfw^-dab*O+&cZGZX2|dyiGyMQ*Jlcea~Yt={+TtybTi zXf^L5zV0N>xm>%$^e)2hA`W+-4%52`zneJRJv&VAB7BlK-2FRD?;_{EhuCxLDd)MD z*mGCyK5}avsoO)WR?oe+)w)aF1FhzI@wJaQzFgZqy^HYug}aa3y9hr}xO>XIi|~Vm zyT9DK$hjXR_8YF=A0k(GEJVERk{mt^uQ^fIPUs|Kj@Hyh}-NbTz zhMz3lzLe`T{OQ8&OSwMd=^0`@)t)&;o;~duYxJ2lpDWy+kx?J}0+lWa*hNi0zrGeMxT5RP8Hrdqyo@zb4LI*faWY zkMM5_w`b(;7yfPG_Ke)U!_N_CeS1c(kDT#)V$WE6<_Ge;W6xNl&#d`l;r5JNpW#0h zZqLZ|8UAzO_Kf@-GoF4S)>G}7-^k<1p0P%sS@ZY8?HRc~!~ZDUo{{S_{LjMe8M!{= z>2Kl@rX$)v#A^0OG!wOMX8Pj-v{U5PN9#lL@5ZVvM?3vLZ3WtVtHtX8TJB;WOmFTH zzOwNBEqA~0!NL!;+`YqB6@IYg`UqcJxZkp8)>HUU;eN|}Q??1^aCkh`ya5N(Xyz5MN?T}$rz{@(XDOBepVzLDHDUBh>*@&7En)X4tA Hz&7T;Zt@Jl diff --git a/crates/renderling/src/linkage/vertex_prefilter_environment_cubemap.spv b/crates/renderling/src/linkage/vertex_prefilter_environment_cubemap.spv deleted file mode 100644 index 4d0b7fba7226304b73697c564212b5d8290a85d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4108 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 diff --git a/crates/renderling/src/linkage/vertex_tonemapping.spv b/crates/renderling/src/linkage/vertex_tonemapping.spv deleted file mode 100644 index 92268a7038eec2f60726e20afd927e9034246842..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1136 zcmYk5+fGwa5Qe8fK@?gMXax#xtcOSo8VRL9fN%wu_yop_hHxPgLyVU`l5fDX@DvJ( z3E#K(UeZPW&gq|7vt~Ax?8aRn+Od>t)Lj3nxU*98OoyW*=@U*n; zWd(TViCZ(%;56u93Ymc>v1z2XcWc*5+ukzlI?{UjwY@{zyjiklkgIr}A=Nu@yl)nG zw5`vm@{{cO-1>Ex_zmm|NbzP7zuqf*upjTWhv!JS-ZMuJ3rKnTeMhQed=WUC72?j* zzC{b$x#^Q*-6iA%(BEbL1<$!S#|Pgsy6+M4tl_*{z*x$Cfo`ryz6{;GRL|U1AU6Yv z_Yz&saBjQw4d=eXC)ZfYeO>Z~`+HMzjCJYQH@*e-?fuqKSITuB?~u;JIXK_6d-u@e z-R`>rJnsK5(_cf%b(S4q{yw(5=mPh1z$T4(|8MHJzja{TUHl=wf%M#wYwSJv0K7x} zh^^Y+M_?^;{pP&Oem8-!#M?qoJioQX>!BO7r{Y}^pXkYNb{km7lYD)2eZKpY*ZF_3 c1LPV@yS1L*tg%~q_C5CQ8-{KD|ESgn_b!|, - roughness: &Uniform, - environment_texture: &crate::Texture, -) -> (wgpu::RenderPipeline, wgpu::BindGroup) { - let label = Some("prefiltered environment"); - let bindgroup_layout_desc = wgpu::BindGroupLayoutDescriptor { - label, - entries: &[ - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::VERTEX, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 2, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Texture { - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - view_dimension: wgpu::TextureViewDimension::Cube, - multisampled: false, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 3, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), - count: None, - }, - ], - }; - let bg_layout = device.create_bind_group_layout(&bindgroup_layout_desc); - let bindgroup = device.create_bind_group(&wgpu::BindGroupDescriptor { - label, - layout: &bg_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::Buffer( - constants.buffer().as_entire_buffer_binding(), - ), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::Buffer( - roughness.buffer().as_entire_buffer_binding(), - ), - }, - wgpu::BindGroupEntry { - binding: 2, - resource: wgpu::BindingResource::TextureView(&environment_texture.view), - }, - wgpu::BindGroupEntry { - binding: 3, - resource: wgpu::BindingResource::Sampler(&environment_texture.sampler), - }, - ], - }); - let pp_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label, - bind_group_layouts: &[&bg_layout], - push_constant_ranges: &[], - }); - let vertex_shader = device.create_shader_module(wgpu::include_spirv!( - "linkage/vertex_prefilter_environment_cubemap.spv" - )); - let fragment_shader = device.create_shader_module(wgpu::include_spirv!( - "linkage/fragment_prefilter_environment_cubemap.spv" - )); - let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("prefiltered environment"), - layout: Some(&pp_layout), - vertex: wgpu::VertexState { - module: &vertex_shader, - entry_point: "vertex_prefilter_environment_cubemap", - buffers: &[wgpu::VertexBufferLayout { - array_stride: 3 * std::mem::size_of::() as u64, - step_mode: wgpu::VertexStepMode::Vertex, - attributes: &wgpu::vertex_attr_array![ - 0 => Float32x3 - ], - }], - }, - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, - cull_mode: None, - unclipped_depth: false, - polygon_mode: wgpu::PolygonMode::Fill, - conservative: false, - }, - depth_stencil: None, - multisample: wgpu::MultisampleState { - mask: !0, - alpha_to_coverage_enabled: false, - count: 1, - }, - fragment: Some(wgpu::FragmentState { - module: &fragment_shader, - entry_point: "fragment_prefilter_environment_cubemap", - targets: &[Some(wgpu::ColorTargetState { - format: wgpu::TextureFormat::Rgba16Float, - blend: Some(wgpu::BlendState { - color: wgpu::BlendComponent::REPLACE, - alpha: wgpu::BlendComponent::REPLACE, - }), - write_mask: wgpu::ColorWrites::ALL, - })], - }), - multiview: None, - }); - (pipeline, bindgroup) -} diff --git a/crates/renderling/src/scene.rs b/crates/renderling/src/scene.rs index 91bf559f..c63ee4cb 100644 --- a/crates/renderling/src/scene.rs +++ b/crates/renderling/src/scene.rs @@ -863,9 +863,10 @@ pub fn create_scene_render_pipeline( log::trace!("creating scene render pipeline with format '{format:?}'"); let label = Some("scene render pipeline"); let vertex_shader = - device.create_shader_module(wgpu::include_spirv!("linkage/main_vertex_scene.spv")); - let fragment_shader = - device.create_shader_module(wgpu::include_spirv!("linkage/main_fragment_scene.spv")); + device.create_shader_module(wgpu::include_spirv!("linkage/stage-main_vertex_scene.spv")); + let fragment_shader = device.create_shader_module(wgpu::include_spirv!( + "linkage/stage-main_fragment_scene.spv" + )); let scene_buffers_layout = scene_buffers_bindgroup_layout(device); let scene_atlas_layout = scene_atlas_bindgroup_layout(device); let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { @@ -878,7 +879,7 @@ pub fn create_scene_render_pipeline( layout: Some(&layout), vertex: wgpu::VertexState { module: &vertex_shader, - entry_point: "main_vertex_scene", + entry_point: "stage::main_vertex_scene", buffers: &[], }, primitive: wgpu::PrimitiveState { @@ -904,7 +905,7 @@ pub fn create_scene_render_pipeline( }, fragment: Some(wgpu::FragmentState { module: &fragment_shader, - entry_point: "main_fragment_scene", + entry_point: "stage::main_fragment_scene", targets: &[ Some(wgpu::ColorTargetState { format, @@ -925,8 +926,9 @@ pub fn create_scene_render_pipeline( pub fn create_scene_compute_cull_pipeline(device: &wgpu::Device) -> wgpu::ComputePipeline { let label = Some("scene compute cull pipeline"); - let shader_crate = - device.create_shader_module(wgpu::include_spirv!("linkage/compute_cull_entities.spv")); + let shader_crate = device.create_shader_module(wgpu::include_spirv!( + "linkage/stage-compute_cull_entities.spv" + )); let scene_buffers_layout = scene_buffers_bindgroup_layout(device); let indirect_buffers_layout = scene_draw_indirect_bindgroup_layout(device); let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { @@ -938,7 +940,7 @@ pub fn create_scene_compute_cull_pipeline(device: &wgpu::Device) -> wgpu::Comput label, layout: Some(&layout), module: &shader_crate, - entry_point: "compute_cull_entities", + entry_point: "stage::compute_cull_entities", }); pipeline } diff --git a/crates/renderling/src/skybox.rs b/crates/renderling/src/skybox.rs index 7ab00bc0..fd0ac397 100644 --- a/crates/renderling/src/skybox.rs +++ b/crates/renderling/src/skybox.rs @@ -73,9 +73,9 @@ pub fn create_skybox_render_pipeline( ) -> SkyboxRenderPipeline { log::trace!("creating skybox render pipeline with format '{format:?}'"); let vertex_shader = - device.create_shader_module(wgpu::include_spirv!("linkage/vertex_skybox.spv")); + device.create_shader_module(wgpu::include_spirv!("linkage/skybox-vertex.spv")); let fragment_shader = - device.create_shader_module(wgpu::include_spirv!("linkage/fragment_cubemap.spv")); + device.create_shader_module(wgpu::include_spirv!("linkage/skybox-fragment_cubemap.spv")); let bg_layout = skybox_bindgroup_layout(device); let pp_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("skybox pipeline layout"), @@ -88,7 +88,7 @@ pub fn create_skybox_render_pipeline( layout: Some(&pp_layout), vertex: wgpu::VertexState { module: &vertex_shader, - entry_point: "vertex_skybox", + entry_point: "skybox::vertex", buffers: &[], }, primitive: wgpu::PrimitiveState { @@ -114,7 +114,7 @@ pub fn create_skybox_render_pipeline( }, fragment: Some(wgpu::FragmentState { module: &fragment_shader, - entry_point: "fragment_cubemap", + entry_point: "skybox::fragment_cubemap", targets: &[Some(wgpu::ColorTargetState { format, blend: Some(wgpu::BlendState::ALPHA_BLENDING), @@ -621,17 +621,17 @@ impl Skybox { crate::mesh::Mesh::from_vertices(device, Some("brdf_lut"), vertices); let vertex_module = device.create_shader_module(wgpu::include_spirv!( - "linkage/vertex_brdf_lut_convolution.spv" + "linkage/convolution-vertex_brdf_lut_convolution.spv" )); let fragment_module = device.create_shader_module(wgpu::include_spirv!( - "linkage/fragment_brdf_lut_convolution.spv" + "linkage/convolution-fragment_brdf_lut_convolution.spv" )); let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: Some("brdf_lut_convolution"), layout: None, vertex: wgpu::VertexState { module: &vertex_module, - entry_point: "vertex_brdf_lut_convolution", + entry_point: "convolution::vertex_brdf_lut_convolution", buffers: &[wgpu::VertexBufferLayout { array_stride: (3 + 2) * std::mem::size_of::() as u64, step_mode: wgpu::VertexStepMode::Vertex, @@ -658,7 +658,7 @@ impl Skybox { }, fragment: Some(wgpu::FragmentState { module: &fragment_module, - entry_point: "fragment_brdf_lut_convolution", + entry_point: "convolution::fragment_brdf_lut_convolution", targets: &[Some(wgpu::ColorTargetState { format: wgpu::TextureFormat::Rg16Float, blend: Some(wgpu::BlendState { diff --git a/crates/renderling/src/slab.rs b/crates/renderling/src/slab.rs index 50cc1e56..698514ae 100644 --- a/crates/renderling/src/slab.rs +++ b/crates/renderling/src/slab.rs @@ -2,8 +2,9 @@ use std::ops::Deref; use renderling_shader::{ + array::Array, id::Id, - slab::{Slab, Slabbed}, array::Array, + slab::{Slab, Slabbed}, }; use snafu::{ResultExt, Snafu}; @@ -92,7 +93,7 @@ impl SlabBuffer { Ok(()) } - pub async fn read( + pub async fn read( &self, device: &wgpu::Device, queue: &wgpu::Queue, @@ -137,7 +138,7 @@ impl SlabBuffer { } /// Append to the end of the buffer. - pub fn append( + pub fn append( &mut self, device: &wgpu::Device, queue: &wgpu::Queue, @@ -154,7 +155,7 @@ impl SlabBuffer { } /// Append a slice to the end of the buffer, returning a slab array. - pub fn append_slice( + pub fn append_slice( &mut self, device: &wgpu::Device, queue: &wgpu::Queue, @@ -169,7 +170,8 @@ impl SlabBuffer { for t in ts.iter() { // UNWRAP: Safe because we just checked that there is enough capacity, // and added some if not. - self.write(device, queue, Id::::from(self.len), t).unwrap(); + self.write(device, queue, Id::::from(self.len), t) + .unwrap(); } self.len += size * len; Array::new(index, len as u32) diff --git a/crates/renderling/src/stage.rs b/crates/renderling/src/stage.rs index 187425a4..2bf080aa 100644 --- a/crates/renderling/src/stage.rs +++ b/crates/renderling/src/stage.rs @@ -2,13 +2,15 @@ //! //! Provides a `Stage` object that can be used to render a scene graph. use renderling_shader::{ + array::Array, + debug::DebugMode, id::Id, slab::Slabbed, - stage::GpuConstants, array::Array, + stage::{GpuLight, StageLegend}, }; use snafu::Snafu; -use crate::{SlabBuffer, Device, Queue}; +use crate::{Atlas, Device, Queue, SlabBuffer}; pub mod light; @@ -24,33 +26,71 @@ pub enum StageError { Async { source: wgpu::BufferAsyncError }, } -/// Builds the stage -pub struct StageSlab { - pub(crate) slab: SlabBuffer, +/// Represents an entire scene worth of rendering data. +pub struct Stage { + pub(crate) stage_slab: SlabBuffer, + pub(crate) render_unit_slab: SlabBuffer, + //pub(crate) atlas: Atlas, pub(crate) device: Device, pub(crate) queue: Queue, } -impl StageSlab { +impl Stage { /// Create a new stage slab with `capacity`, which is - pub fn new(device: Device, queue: Queue, constants: GpuConstants) -> Self { + pub fn new(device: Device, queue: Queue, legend: StageLegend) -> Self { let mut s = Self { - slab: SlabBuffer::new(&device, 256), + stage_slab: SlabBuffer::new(&device, 256), + render_unit_slab: SlabBuffer::new(&device, 256), + //atlas: Atlas::new(&device, &queue, 0, 0), device, queue, }; - let _ = s.append(&constants); + let _ = s.append(&legend); s } /// Add an object to the slab and return its ID. - pub fn append(&mut self, object: &T) -> Id { - self.slab.append(&self.device, &self.queue, object) + pub fn append(&mut self, object: &T) -> Id { + self.stage_slab.append(&self.device, &self.queue, object) } /// Add a slice of objects to the slab and return an [`Array`]. - pub fn append_slice(&mut self, objects: &[T]) -> Array { - self.slab.append_slice(&self.device, &self.queue, objects) + pub fn append_slice( + &mut self, + objects: &[T], + ) -> Array { + self.stage_slab + .append_slice(&self.device, &self.queue, objects) + } + + /// Set the debug mode. + pub fn set_debug_mode(&mut self, debug_mode: DebugMode) { + let id = Id::::from(StageLegend::offset_of_debug_mode()); + // UNWRAP: safe because the debug mode offset is guaranteed to be valid. + self.stage_slab + .write(&self.device, &self.queue, id, &debug_mode) + .unwrap(); + } + + /// Set the debug mode. + pub fn with_debug_mode(mut self, debug_mode: DebugMode) -> Self { + self.set_debug_mode(debug_mode); + self + } + + /// Set whether the stage uses lighting. + pub fn set_has_lighting(&mut self, use_lighting: bool) { + let id = Id::::from(StageLegend::offset_of_has_lighting()); + // UNWRAP: safe because the has lighting offset is guaranteed to be valid. + self.stage_slab + .write(&self.device, &self.queue, id, &use_lighting) + .unwrap(); + } + + /// Set whether the stage uses lighting. + pub fn with_lighting(mut self, use_lighting: bool) -> Self { + self.set_has_lighting(use_lighting); + self } /// Create a new spot light and return its builder. @@ -67,4 +107,44 @@ impl StageSlab { pub fn new_point_light(&mut self) -> light::GpuPointLightBuilder { light::GpuPointLightBuilder::new(self) } + + /// Set the light array. + /// + /// This should be an iterator over the ids of all the lights on the stage. + pub fn set_light_array( + &mut self, + lights: impl IntoIterator>, + ) -> Array> { + let lights = lights.into_iter().collect::>(); + let light_array = self.append_slice(&lights); + let id = Id::>>::from(StageLegend::offset_of_light_array()); + // UNWRAP: safe because we just appended the array, and the light array offset is + // guaranteed to be valid. + self.stage_slab + .write(&self.device, &self.queue, id, &light_array) + .unwrap(); + light_array + } + + /// Set the light array. + /// + /// This should be an iterator over the ids of all the lights on the stage. + pub fn with_light_array(mut self, lights: impl IntoIterator>) -> Self { + self.set_light_array(lights); + self + } +} + +#[cfg(test)] +mod test { + use crate::Renderling; + + use super::*; + + #[test] + fn stage_new() { + let r = Renderling::headless(10, 10).unwrap(); + let (device, queue) = r.get_device_and_queue_owned(); + let _stage = Stage::new(device, queue, StageLegend::default()); + } } diff --git a/crates/renderling/src/stage/light.rs b/crates/renderling/src/stage/light.rs index 0aac300d..217ae0b1 100644 --- a/crates/renderling/src/stage/light.rs +++ b/crates/renderling/src/stage/light.rs @@ -3,7 +3,7 @@ use renderling_shader::id::Id; use renderling_shader::stage::{GpuLight, LightType}; use glam::{Vec3, Vec4}; -use crate::StageSlab; +use crate::Stage; #[cfg(feature = "gltf")] pub fn from_gltf_light_kind(kind: gltf::khr_lights_punctual::Kind) -> LightType { @@ -26,11 +26,11 @@ pub fn gltf_light_intensity_units(kind: gltf::khr_lights_punctual::Kind) -> &'st /// A builder for a spot light. pub struct GpuSpotLightBuilder<'a> { inner: GpuLight, - stage: &'a mut StageSlab, + stage: &'a mut Stage, } impl<'a> GpuSpotLightBuilder<'a> { - pub fn new(stage: &'a mut StageSlab) -> GpuSpotLightBuilder<'a> { + pub fn new(stage: &'a mut Stage) -> GpuSpotLightBuilder<'a> { let inner = GpuLight { light_type: LightType::SPOT_LIGHT, ..Default::default() @@ -91,11 +91,11 @@ impl<'a> GpuSpotLightBuilder<'a> { /// This is like the sun, or the moon. pub struct GpuDirectionalLightBuilder<'a> { inner: GpuLight, - stage: &'a mut StageSlab, + stage: &'a mut Stage, } impl<'a> GpuDirectionalLightBuilder<'a> { - pub fn new(stage: &'a mut StageSlab) -> GpuDirectionalLightBuilder<'a> { + pub fn new(stage: &'a mut Stage) -> GpuDirectionalLightBuilder<'a> { let inner = GpuLight { light_type: LightType::DIRECTIONAL_LIGHT, ..Default::default() @@ -131,11 +131,11 @@ impl<'a> GpuDirectionalLightBuilder<'a> { pub struct GpuPointLightBuilder<'a> { inner: GpuLight, - stage: &'a mut StageSlab, + stage: &'a mut Stage, } impl<'a> GpuPointLightBuilder<'a> { - pub fn new(stage: &mut StageSlab) -> GpuPointLightBuilder<'_> { + pub fn new(stage: &mut Stage) -> GpuPointLightBuilder<'_> { let inner = GpuLight { light_type: LightType::POINT_LIGHT, ..Default::default() diff --git a/crates/renderling/src/texture.rs b/crates/renderling/src/texture.rs index 8ce37373..9b0a1427 100644 --- a/crates/renderling/src/texture.rs +++ b/crates/renderling/src/texture.rs @@ -595,9 +595,9 @@ impl Texture { layout: Some(&pp_layout), vertex: wgpu::VertexState { module: &device.create_shader_module(wgpu::include_spirv!( - "linkage/vertex_generate_mipmap.spv" + "linkage/convolution-vertex_generate_mipmap.spv" )), - entry_point: "vertex_generate_mipmap", + entry_point: "convolution::vertex_generate_mipmap", buffers: &[], }, primitive: wgpu::PrimitiveState { @@ -608,9 +608,9 @@ impl Texture { }, fragment: Some(wgpu::FragmentState { module: &device.create_shader_module(wgpu::include_spirv!( - "linkage/fragment_generate_mipmap.spv" + "linkage/convolution-fragment_generate_mipmap.spv" )), - entry_point: "fragment_generate_mipmap", + entry_point: "convolution::fragment_generate_mipmap", targets: &[Some(wgpu::ColorTargetState { format: self.texture.format(), blend: None, diff --git a/crates/renderling/src/ui.rs b/crates/renderling/src/ui.rs index a537802b..485bc0f2 100644 --- a/crates/renderling/src/ui.rs +++ b/crates/renderling/src/ui.rs @@ -11,7 +11,7 @@ use glam::{UVec2, Vec2, Vec4}; use snafu::prelude::*; use crate::{ - mesh::Mesh, frame::FrameTextureView, Device, Queue, RenderTarget, Renderling, Texture, Uniform, + frame::FrameTextureView, mesh::Mesh, Device, Queue, RenderTarget, Renderling, Texture, Uniform, View, ViewMut, }; @@ -101,9 +101,9 @@ pub fn create_ui_pipeline( format: wgpu::TextureFormat, ) -> wgpu::RenderPipeline { let label = Some("ui render pipeline"); - let vertex_shader = device.create_shader_module(wgpu::include_spirv!("linkage/ui_vertex.spv")); + let vertex_shader = device.create_shader_module(wgpu::include_spirv!("linkage/ui-vertex.spv")); let fragment_shader = - device.create_shader_module(wgpu::include_spirv!("linkage/ui_fragment.spv")); + device.create_shader_module(wgpu::include_spirv!("linkage/ui-fragment.spv")); let constants_bindgroup_layout = constants_bindgroup_layout(device); let draw_params_bindgroup_layout = draw_params_bindgroup_layout(device); let texture_bindgroup_layout = ui_texture_bindgroup_layout(device); @@ -121,7 +121,7 @@ pub fn create_ui_pipeline( layout: Some(&layout), vertex: wgpu::VertexState { module: &vertex_shader, - entry_point: "ui_vertex", + entry_point: "ui::vertex", buffers: &[wgpu::VertexBufferLayout { array_stride: { let position_size = std::mem::size_of::(); @@ -154,7 +154,7 @@ pub fn create_ui_pipeline( }, fragment: Some(wgpu::FragmentState { module: &fragment_shader, - entry_point: "ui_fragment", + entry_point: "ui::fragment", targets: &[Some(wgpu::ColorTargetState { format: format.add_srgb_suffix(), blend: Some(wgpu::BlendState::ALPHA_BLENDING), @@ -453,7 +453,7 @@ impl<'a> UiSceneBuilder<'a> { img, Some("UiSceneBuilder::new_texture_from_dynamic_image"), None, - 1 + 1, ) } @@ -568,9 +568,8 @@ pub fn setup_ui_render_graph( r.graph.add_resource(pipeline); use crate::{ - graph, frame::{clear_frame_and_depth, create_frame, present}, - Graph, + graph, Graph, }; let pre_render = crate::graph!(create_frame, clear_frame_and_depth, ui_scene_update).with_barrier(); diff --git a/shaders/Cargo.toml b/shaders/Cargo.toml index f5093c0b..551b2b4e 100644 --- a/shaders/Cargo.toml +++ b/shaders/Cargo.toml @@ -2,11 +2,12 @@ name = "shaders" version = "0.1.0" edition = "2021" -description = "Generates the shader .spv files for the renderling project" +description = "Generates shader .spv files from rust-gpu shader crates" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +clap = { version = "4.4.8", features = ["derive"] } env_logger = "^0.10" log = "^0.4" spirv-builder = "^0.9" diff --git a/shaders/rust-toolchain b/shaders/rust-toolchain.toml similarity index 100% rename from shaders/rust-toolchain rename to shaders/rust-toolchain.toml diff --git a/shaders/shader-crate/src/lib.rs b/shaders/shader-crate/src/lib.rs index 63eb70c4..0fb9a7a2 100644 --- a/shaders/shader-crate/src/lib.rs +++ b/shaders/shader-crate/src/lib.rs @@ -1,404 +1,4 @@ //! Shader entry points. #![no_std] #![feature(lang_items)] -use renderling_shader::{convolution, pbr, skybox, stage, tonemapping, ui}; -use spirv_std::{ - glam, - image::{Cubemap, Image2d}, - spirv, Sampler, -}; - -#[spirv(vertex)] -pub fn ui_vertex( - #[spirv(uniform, descriptor_set = 0, binding = 0)] constants: &ui::UiConstants, - #[spirv(uniform, descriptor_set = 2, binding = 0)] params: &ui::UiDrawParams, - - in_pos: glam::Vec2, - in_uv: glam::Vec2, - in_color: glam::Vec4, - - #[spirv(flat)] out_mode: &mut u32, - out_color: &mut glam::Vec4, - out_uv: &mut glam::Vec2, - #[spirv(position)] gl_pos: &mut glam::Vec4, -) { - ui::vertex( - constants, params, in_pos, in_uv, in_color, out_mode, out_color, out_uv, gl_pos, - ) -} - -#[spirv(fragment)] -pub fn ui_fragment( - #[spirv(descriptor_set = 1, binding = 0)] texture: &Image2d, - #[spirv(descriptor_set = 1, binding = 1)] sampler: &Sampler, - - #[spirv(flat)] in_mode: u32, - in_color: glam::Vec4, - in_uv: glam::Vec2, - - output: &mut glam::Vec4, -) { - ui::fragment(texture, sampler, in_mode, in_color, in_uv, output) -} - -#[spirv(vertex)] -pub fn main_vertex_scene( - #[spirv(uniform, descriptor_set = 0, binding = 0)] constants: &stage::GpuConstants, - #[spirv(storage_buffer, descriptor_set = 0, binding = 1)] vertices: &[stage::Vertex], - #[spirv(storage_buffer, descriptor_set = 0, binding = 2)] entities: &[stage::GpuEntity], - - //// which entity are we drawing - #[spirv(instance_index)] instance_id: u32, - // which vertex on that entity - #[spirv(vertex_index)] vertex_id: u32, - - #[spirv(flat)] out_material: &mut u32, - out_color: &mut glam::Vec4, - out_uv0: &mut glam::Vec2, - out_uv1: &mut glam::Vec2, - out_norm: &mut glam::Vec3, - out_tangent: &mut glam::Vec3, - out_bitangent: &mut glam::Vec3, - out_pos: &mut glam::Vec3, - #[spirv(position)] gl_pos: &mut glam::Vec4, -) { - stage::main_vertex_scene( - instance_id, - vertex_id, - constants, - vertices, - entities, - out_material, - out_color, - out_uv0, - out_uv1, - out_norm, - out_tangent, - out_bitangent, - out_pos, - gl_pos, - ) -} - -#[spirv(fragment)] -pub fn main_fragment_scene( - #[spirv(descriptor_set = 1, binding = 0)] atlas: &Image2d, - #[spirv(descriptor_set = 1, binding = 1)] sampler: &Sampler, - - #[spirv(uniform, descriptor_set = 0, binding = 0)] constants: &stage::GpuConstants, - #[spirv(storage_buffer, descriptor_set = 0, binding = 3)] lights: &[stage::GpuLight], - #[spirv(storage_buffer, descriptor_set = 0, binding = 4)] materials: &[pbr::PbrMaterial], - #[spirv(storage_buffer, descriptor_set = 1, binding = 2)] textures: &[stage::GpuTexture], - - #[spirv(descriptor_set = 0, binding = 5)] irradiance: &Cubemap, - #[spirv(descriptor_set = 0, binding = 6)] irradiance_sampler: &Sampler, - #[spirv(descriptor_set = 0, binding = 7)] prefiltered: &Cubemap, - #[spirv(descriptor_set = 0, binding = 8)] prefiltered_sampler: &Sampler, - #[spirv(descriptor_set = 0, binding = 9)] brdf: &Image2d, - #[spirv(descriptor_set = 0, binding = 10)] brdf_sampler: &Sampler, - - //// which entity are we drawing - #[spirv(flat)] in_material: u32, - in_color: glam::Vec4, - in_uv0: glam::Vec2, - in_uv1: glam::Vec2, - in_norm: glam::Vec3, - in_tangent: glam::Vec3, - in_bitangent: glam::Vec3, - in_pos: glam::Vec3, - - output: &mut glam::Vec4, - brightness: &mut glam::Vec4, -) { - stage::main_fragment_scene( - atlas, - sampler, - irradiance, - irradiance_sampler, - prefiltered, - prefiltered_sampler, - brdf, - brdf_sampler, - constants, - lights, - materials, - textures, - in_material, - in_color, - in_uv0, - in_uv1, - in_norm, - in_tangent, - in_bitangent, - in_pos, - output, - brightness, - ) -} - -#[spirv(compute(threads(32)))] -pub fn compute_cull_entities( - #[spirv(storage_buffer, descriptor_set = 0, binding = 2)] entities: &[stage::GpuEntity], - - #[spirv(storage_buffer, descriptor_set = 1, binding = 0)] draws: &mut [stage::DrawIndirect], - - #[spirv(global_invocation_id)] global_id: glam::UVec3, -) { - stage::compute_cull_entities(entities, draws, global_id) -} - -#[spirv(vertex)] -pub fn vertex_tonemapping( - #[spirv(vertex_index)] vertex_id: u32, - out_uv: &mut glam::Vec2, - #[spirv(position)] gl_pos: &mut glam::Vec4, -) { - tonemapping::vertex(vertex_id, out_uv, gl_pos) -} - -#[spirv(fragment)] -pub fn fragment_tonemapping( - #[spirv(descriptor_set = 0, binding = 0)] texture: &Image2d, - #[spirv(descriptor_set = 0, binding = 1)] sampler: &Sampler, - #[spirv(uniform, descriptor_set = 1, binding = 0)] constants: &tonemapping::TonemapConstants, - #[spirv(descriptor_set = 2, binding = 0)] bloom_texture: &Image2d, - #[spirv(descriptor_set = 2, binding = 1)] bloom_sampler: &Sampler, - in_uv: glam::Vec2, - - output: &mut glam::Vec4, -) { - tonemapping::fragment( - texture, - sampler, - constants, - bloom_texture, - bloom_sampler, - in_uv, - output, - ) -} - -#[spirv(vertex)] -pub fn vertex_skybox( - #[spirv(vertex_index)] vertex_id: u32, - #[spirv(uniform, descriptor_set = 0, binding = 0)] constants: &stage::GpuConstants, - local_pos: &mut glam::Vec3, - #[spirv(position)] gl_pos: &mut glam::Vec4, -) { - skybox::vertex(vertex_id, constants, local_pos, gl_pos) -} - -#[spirv(vertex)] -pub fn vertex_position_passthru( - #[spirv(uniform, descriptor_set = 0, binding = 0)] constants: &stage::GpuConstants, - in_pos: glam::Vec3, - local_pos: &mut glam::Vec3, - #[spirv(position)] gl_pos: &mut glam::Vec4, -) { - skybox::vertex_position_passthru(constants, in_pos, local_pos, gl_pos) -} - -#[spirv(fragment)] -pub fn fragment_equirectangular( - #[spirv(descriptor_set = 0, binding = 1)] texture: &Image2d, - #[spirv(descriptor_set = 0, binding = 2)] sampler: &Sampler, - in_local_pos: glam::Vec3, - out_color: &mut glam::Vec4, -) { - skybox::fragment_equirectangular(texture, sampler, in_local_pos, out_color) -} - -#[spirv(fragment)] -pub fn fragment_cubemap( - #[spirv(descriptor_set = 0, binding = 1)] texture: &Cubemap, - #[spirv(descriptor_set = 0, binding = 2)] sampler: &Sampler, - in_local_pos: glam::Vec3, - out_color: &mut glam::Vec4, -) { - skybox::fragment_cubemap(texture, sampler, in_local_pos, out_color) -} - -#[spirv(vertex)] -pub fn vertex_brdf_lut_convolution( - in_pos: glam::Vec3, - in_uv: glam::Vec2, - out_uv: &mut glam::Vec2, - #[spirv(position)] gl_pos: &mut glam::Vec4, -) { - *out_uv = in_uv; - *gl_pos = in_pos.extend(1.0); -} - -#[spirv(fragment)] -pub fn fragment_brdf_lut_convolution(in_uv: glam::Vec2, out_color: &mut glam::Vec2) { - *out_color = convolution::integrate_brdf(in_uv.x, in_uv.y); -} - -#[spirv(vertex)] -pub fn vertex_prefilter_environment_cubemap( - #[spirv(uniform, descriptor_set = 0, binding = 0)] constants: &stage::GpuConstants, - in_pos: glam::Vec3, - out_pos: &mut glam::Vec3, - #[spirv(position)] gl_pos: &mut glam::Vec4, -) { - convolution::vertex_prefilter_environment_cubemap(constants, in_pos, out_pos, gl_pos) -} - -#[spirv(fragment)] -pub fn fragment_prefilter_environment_cubemap( - #[spirv(uniform, descriptor_set = 0, binding = 1)] roughness: &f32, - #[spirv(descriptor_set = 0, binding = 2)] environment_cubemap: &Cubemap, - #[spirv(descriptor_set = 0, binding = 3)] sampler: &Sampler, - in_pos: glam::Vec3, - frag_color: &mut glam::Vec4, -) { - convolution::fragment_prefilter_environment_cubemap( - roughness, - environment_cubemap, - sampler, - in_pos, - frag_color, - ) -} - -#[spirv(vertex)] -pub fn vertex_generate_mipmap( - #[spirv(vertex_index)] vertex_id: u32, - out_uv: &mut glam::Vec2, - #[spirv(position)] gl_pos: &mut glam::Vec4, -) { - convolution::vertex_generate_mipmap(vertex_id, out_uv, gl_pos) -} - -#[spirv(fragment)] -pub fn fragment_generate_mipmap( - #[spirv(descriptor_set = 0, binding = 0)] texture: &Image2d, - #[spirv(descriptor_set = 0, binding = 1)] sampler: &Sampler, - in_uv: glam::Vec2, - frag_color: &mut glam::Vec4, -) { - convolution::fragment_generate_mipmap(texture, sampler, in_uv, frag_color) -} - -//#[spirv(fragment)] -// pub fn fragment_convolve_diffuse_irradiance( -// #[spirv(descriptor_set = 0, binding = 1)] texture: &Cubemap, -// #[spirv(descriptor_set = 0, binding = 2)] sampler: &Sampler, -// in_local_pos: glam::Vec3, -// out_color: &mut glam::Vec4, -//) { -// convolution::fragment_convolve_diffuse_irradiance(texture, sampler, -// in_local_pos, out_color) -//} - -#[spirv(fragment)] -pub fn fragment_bloom( - #[spirv(uniform, descriptor_set = 0, binding = 0)] horizontal: &u32, - #[spirv(uniform, descriptor_set = 0, binding = 1)] size: &glam::UVec2, - #[spirv(descriptor_set = 0, binding = 2)] texture: &Image2d, - #[spirv(descriptor_set = 0, binding = 3)] sampler: &Sampler, - in_uv: glam::Vec2, - frag_color: &mut glam::Vec4, -) { - convolution::fragment_bloom(*horizontal == 0, size, texture, sampler, in_uv, frag_color) -} - -#[spirv(vertex)] -pub fn stage_vertex( - // Which render unit are we rendering - #[spirv(instance_index)] instance_index: u32, - // Which vertex within the render unit are we rendering - #[spirv(vertex_index)] vertex_index: u32, - #[spirv(storage_buffer, descriptor_set = 0, binding = 0)] unit_slab: &[u32], - #[spirv(storage_buffer, descriptor_set = 0, binding = 1)] stage_slab: &[u32], - - #[spirv(flat)] out_material: &mut u32, - out_color: &mut glam::Vec4, - out_uv0: &mut glam::Vec2, - out_uv1: &mut glam::Vec2, - out_norm: &mut glam::Vec3, - out_tangent: &mut glam::Vec3, - out_bitangent: &mut glam::Vec3, - - // position of the vertex/fragment in world space - out_pos: &mut glam::Vec3, - - #[spirv(position)] gl_pos: &mut glam::Vec4, -) { - renderling_shader::stage::stage_vertex( - instance_index, - vertex_index, - unit_slab, - stage_slab, - out_material, - out_color, - out_uv0, - out_uv1, - out_norm, - out_tangent, - out_bitangent, - out_pos, - gl_pos, - ) -} - -#[spirv(fragment)] -pub fn stage_fragment( - #[spirv(descriptor_set = 1, binding = 0)] - atlas: &Image2d, - #[spirv(descriptor_set = 1, binding = 1)] - atlas_sampler: &Sampler, - - #[spirv(descriptor_set = 1, binding = 2)] - irradiance: &Cubemap, - #[spirv(descriptor_set = 1, binding = 3)] - irradiance_sampler: &Sampler, - - #[spirv(descriptor_set = 1, binding = 4)] - prefiltered: &Cubemap, - #[spirv(descriptor_set = 1, binding = 5)] - prefiltered_sampler: &Sampler, - - #[spirv(descriptor_set = 1, binding = 6)] - brdf: &Image2d, - #[spirv(descriptor_set = 1, binding = 7)] - brdf_sampler: &Sampler, - - #[spirv(storage_buffer, descriptor_set = 0, binding = 8)] - slab: &[u32], - - #[spirv(flat)] - in_material: u32, - in_color: glam::Vec4, - in_uv0: glam::Vec2, - in_uv1: glam::Vec2, - in_norm: glam::Vec3, - in_tangent: glam::Vec3, - in_bitangent: glam::Vec3, - in_pos: glam::Vec3, - - output: &mut glam::Vec4, - brigtness: &mut glam::Vec4, -) { - renderling_shader::stage::stage_fragment( - atlas, - atlas_sampler, - irradiance, - irradiance_sampler, - prefiltered, - prefiltered_sampler, - brdf, - brdf_sampler, - slab, - in_material, - in_color, - in_uv0, - in_uv1, - in_norm, - in_tangent, - in_bitangent, - in_pos, - output, - brigtness, - ) -} +pub use renderling_shader::*; diff --git a/shaders/src/main.rs b/shaders/src/main.rs index 2d9b5781..f39af2e9 100644 --- a/shaders/src/main.rs +++ b/shaders/src/main.rs @@ -1,15 +1,38 @@ //! This program builds the rust-gpu shaders and writes the spv files into the //! main source repo. -//! -//! See the crates/renderling/src/shaders/mod.rs for more info. +use clap::Parser; use spirv_builder::{CompileResult, MetadataPrintout, ModuleResult, SpirvBuilder}; +#[derive(Parser)] +#[clap(author, version, about)] +struct Cli { + /// Sets the verbosity level + #[clap(short, action = clap::ArgAction::Count)] + verbosity: u8, + + /// Path to the output directory for the compiled shaders. + #[clap(long, short, default_value = "shaders")] + output_dir: std::path::PathBuf, +} + fn main() -> Result<(), Box> { + let Cli { + verbosity, + output_dir, + } = Cli::parse(); + let level = match verbosity { + 0 => log::LevelFilter::Warn, + 1 => log::LevelFilter::Info, + 2 => log::LevelFilter::Debug, + _ => log::LevelFilter::Trace, + }; env_logger::builder() - .filter_level(log::LevelFilter::Trace) + .filter_level(level) .try_init() .unwrap(); + std::fs::create_dir_all(&output_dir).unwrap(); + let CompileResult { entry_points, module, @@ -18,7 +41,7 @@ fn main() -> Result<(), Box> { .multimodule(true) .build()?; - let dir = std::path::PathBuf::from("../crates/renderling/src/linkage"); + let dir = output_dir; match module { ModuleResult::MultiModule(modules) => { for (entry, filepath) in modules.into_iter() {