Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat/struct-slab-offset and shader annotations #59

Merged
merged 1 commit into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
7 changes: 3 additions & 4 deletions NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

# renderling 🍖

This aspires to be a modern "GPU-driven" renderer. It is alpha software. I'm still learning, but quickly!
Expand Down
66 changes: 50 additions & 16 deletions crates/renderling-derive/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -54,7 +54,12 @@ fn get_params(input: &DeriveInput) -> syn::Result<Params> {
.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 {
Expand All @@ -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(),
Expand All @@ -93,25 +98,54 @@ 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::<Vec<_>>();
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::<Vec<_>>();
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! {
let index = self.#field.write_slab(index, slab);
},
})
.collect::<Vec<_>>();

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
{
Expand Down
19 changes: 17 additions & 2 deletions crates/renderling-shader/src/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T: Slabbed> {
index: u32,
len: u32,
_phantom: PhantomData<T>,
}

impl<T: Slabbed> core::fmt::Debug for Array<T> {
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<T: Slabbed> PartialEq for Array<T> {
fn eq(&self, other: &Self) -> bool {
self.index == other.index && self.len == other.len
}
}

impl<T: Slabbed> Slabbed for Array<T> {
fn slab_size() -> usize {
2
Expand Down
52 changes: 39 additions & 13 deletions crates/renderling-shader/src/convolution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down Expand Up @@ -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,
) {
Expand Down Expand Up @@ -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];
Expand All @@ -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::*;
Expand Down
9 changes: 7 additions & 2 deletions crates/renderling-shader/src/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ impl DebugChannel {
Self::Emissive,
Self::UvEmissive,
Self::EmissiveFactor,
Self::EmissiveStrength
Self::EmissiveStrength,
]
}
}
Expand All @@ -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<DebugChannel> for DebugMode {
fn from(value: DebugChannel) -> Self {
DebugMode(value as u32)
Expand Down
32 changes: 32 additions & 0 deletions crates/renderling-shader/src/id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,38 @@ impl<T> std::fmt::Debug for Id<T> {
}
}

impl<T> core::ops::Add<usize> for Id<T> {
type Output = Self;

fn add(self, rhs: usize) -> Self::Output {
Id::new(self.0 + rhs as u32)
}
}

impl<T> core::ops::Add<Id<T>> for usize {
type Output = Id<T>;

fn add(self, rhs: Id<T>) -> Self::Output {
Id::new(self as u32 + rhs.0 as u32)
}
}

impl<T> core::ops::Add<u32> for Id<T> {
type Output = Self;

fn add(self, rhs: u32) -> Self::Output {
Id::new(self.0 + rhs)
}
}

impl<T> core::ops::Add<Id<T>> for u32 {
type Output = Id<T>;

fn add(self, rhs: Id<T>) -> Self::Output {
Id::new(self + rhs.0)
}
}

impl<T> Id<T> {
pub const NONE: Self = Id::new(ID_NONE);

Expand Down
Loading