Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
f2b51c1
MVP type layout 2
chronicl May 22, 2025
4b7e4a4
Add gpu_repr(uniform) to layout example
chronicl May 22, 2025
2a9a4ed
Add GpuStore: GpuAligned back
chronicl May 22, 2025
4619d60
Cleanup type layout Vertex
chronicl May 22, 2025
fb24c94
Fix example
chronicl May 22, 2025
8222864
Rebase
chronicl May 23, 2025
8420a3c
Small import change
chronicl May 23, 2025
6ce7e49
Fix example name
chronicl May 23, 2025
c5b9088
PackedVector to ir type conversion
chronicl May 23, 2025
bdcf157
Remove alignment rounding duplication
chronicl May 23, 2025
f3690d0
Add byte_size and align for LayoutableType
chronicl May 23, 2025
e255e1b
Make default use_color false
chronicl May 23, 2025
3f1dcf7
Decouple gpu_layout from GpuLayout
chronicl May 23, 2025
e19e85b
Move Display impl of ScalarType
chronicl May 23, 2025
e2f41cd
Make TryFrom<Layoutabletype> for StoreType fallible started
chronicl May 24, 2025
1aecb7e
Make ContainsPackedVecError simple
chronicl May 24, 2025
38b535e
Test LayoutableType -> StoreType conversion
chronicl May 24, 2025
f49aa5b
Make align of all types 1 when Repr::Packed
chronicl May 24, 2025
1d3aadc
Fix shame::Struct layout trait impls
chronicl May 24, 2025
ea6f659
Move LayoutCalculator
chronicl May 24, 2025
c4456f2
Split TypeLayout<Repr> into TypeLayout and GpuTypeLayout<Repr>
chronicl May 26, 2025
b11e5a3
Fix FieldLayout construction from LayoutableType
chronicl May 26, 2025
c22bfa7
Change GpuLayout impl of PackedVec
chronicl May 26, 2025
6dc9776
Change gpu_layout
chronicl May 26, 2025
3d8ddb4
Fix FieldLayout runtime sized array construction
chronicl May 26, 2025
02dd2c5
Cleanup and FieldOffsets redone
chronicl May 26, 2025
23af34a
Fix repr_c_struct_layout
chronicl May 27, 2025
6c3d170
Some comment cleanup
chronicl May 29, 2025
cf1392c
Begin storage/uniform buffer any api rework
chronicl May 27, 2025
64ca485
.
chronicl May 27, 2025
8a188c6
MVP storage/uniform buffer any api rework
chronicl May 30, 2025
1c6cc5c
Remove unused layout_constraints file
chronicl May 30, 2025
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
35 changes: 18 additions & 17 deletions examples/api_showcase/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
#![allow(unused, clippy::no_effect)]
use shame::gpu_layout;
use shame as sm;
use shame::prelude::*;
use shame::aliases::*;

#[rustfmt::skip]
fn make_pipeline(some_param: u32) -> Result<sm::results::RenderPipeline, sm::EncodingErrors> {

// start a pipeline encoding with the default settings.
// (in the `shame_wgpu` examples, this is wrapped by the `Gpu` object)
//
// compared to earlier versions of `shame`, pipeline
//
// compared to earlier versions of `shame`, pipeline
// encoding is no longer based on a closure, but based on a
// RAII guard `sm::EncodingGuard<...>` instead.
// That way you can use the `?` operator for your own
Expand Down Expand Up @@ -60,7 +61,7 @@ fn make_pipeline(some_param: u32) -> Result<sm::results::RenderPipeline, sm::Enc
// only supported in vertex buffers, not storage/uniform buffers.
// This is reflected in the traits that are derived for `MyVertexFormat`.
//
// note: vertex layouts support #[gpu_repr(packed)] to prevent padding
// note: vertex layouts support #[gpu_repr(packed)] to prevent padding
// between fields of a struct, which often happens with 3 dimensional vectors
#[derive(sm::GpuLayout)]
struct MyVertexFormat {
Expand All @@ -76,7 +77,7 @@ fn make_pipeline(some_param: u32) -> Result<sm::results::RenderPipeline, sm::Enc
// import storage or uniform buffers via the `group0` iterator
//
// these iterators exist so that you can abstract them away in your own
// api-specific layer that suits your needs such that you can represent
// api-specific layer that suits your needs such that you can represent
// bind groups as types.
//
// `Transforms` is checked for compatibility with `TransformsOnCpu` here.
Expand All @@ -85,7 +86,7 @@ fn make_pipeline(some_param: u32) -> Result<sm::results::RenderPipeline, sm::Enc
// (once rusts const-generics are more powerful this may be moved to compile-time)
let xforms_sto: sm::Buffer<Transforms, sm::mem::Storage> = group0.next();
let xforms_uni: sm::Buffer<Transforms, sm::mem::Uniform> = group0.next();

// conditional code generation based on pipeline parameter
if some_param > 0 {
// if not further specified, defaults to `sm::mem::Storage`
Expand All @@ -99,7 +100,7 @@ fn make_pipeline(some_param: u32) -> Result<sm::results::RenderPipeline, sm::Enc
let my_vec3 = sm::vec!(1.0, 2.0, 3.0);
let my_vec4 = sm::vec!(my_vec3, 0.0); // component concatenation, like usual in shaders
let my_vec4 = my_vec3.extend(0.0); // or like this

let my_normal = sm::vec!(1.0, 1.0, 0.0).normalize();
let rgb = my_normal.remap(-1.0..=1.0, 0.0..=1.0); // remap linear ranges (instead of " * 0.5 + 0.5")

Expand All @@ -117,7 +118,7 @@ fn make_pipeline(some_param: u32) -> Result<sm::results::RenderPipeline, sm::Enc
let k = alpha.rsub1().sqrt().rsub1(); // same as above

// iterate over components of vec
let sum: f32x1 = my_vec4.into_iter().map(|x| x * x).sum();
let sum: f32x1 = my_vec4.into_iter().map(|x| x * x).sum();

let linear = xform.resize() as f32x3x3; // generic matrix resize
let linear = linear * sm::mat::id(); // generic identity matrix
Expand Down Expand Up @@ -162,8 +163,8 @@ fn make_pipeline(some_param: u32) -> Result<sm::results::RenderPipeline, sm::Enc
my_vec.y.set(3.0);

// rusts `if/while/for` means conditional code generation and loop-unrolling,
//
// therefore shader control flow uses closures.
//
// therefore shader control flow uses closures.
// The api is designed after rusts `bool::then` and the likes:

let rust_bool = true;
Expand All @@ -172,12 +173,12 @@ fn make_pipeline(some_param: u32) -> Result<sm::results::RenderPipeline, sm::Enc

let shame_bool = true.to_gpu();
// condition appears in the shader as `if true { k = 1 }`
// (the `move` keyword is required for safety,
// (the `move` keyword is required for safety,
// but can be turned off via a shame crate-feature)
shame_bool.then(move || k.set(1));

// a variety of different shader-loop functions also exist

// for loop doesn't appear in the shader, only `k = 1; k = 2; k = 3; ...`
for i in 0..=10 {
k.set(i)
Expand All @@ -202,7 +203,7 @@ fn make_pipeline(some_param: u32) -> Result<sm::results::RenderPipeline, sm::Enc

// clipping planes
let primitive = primitive.clip(mirror_distances);

// use `rasterize`, `rasterize_multisample` or `rasterize_supersample`.
// Rasterization gives access to the fragment-stage api via `frag`
let frag = primitive.rasterize(sm::Accuracy::Relaxed);
Expand All @@ -225,7 +226,7 @@ fn make_pipeline(some_param: u32) -> Result<sm::results::RenderPipeline, sm::Enc
// individual partial derivatives are accessible as fields.
duv.dx;
duv.dy;

// `fwidth` was renamed to `dxy_manhattan` because "width" is misleading
frag.quad.dxy_manhattan(uv, sm::GradPrecision::Coarse);

Expand Down Expand Up @@ -270,7 +271,7 @@ fn make_pipeline(some_param: u32) -> Result<sm::results::RenderPipeline, sm::Enc
// `shame` makes it impossible to use these features if they don't apply to the situation.

// this causes a compiler error, because Rg8Unorm has no alpha channel:
// targets.next::<tfmt::Rg8Unorm>().set_with_alpha_to_coverage(rg);
// targets.next::<tfmt::Rg8Unorm>().set_with_alpha_to_coverage(rg);

// finish the encoding and obtain the pipeline setup info + shader code.
encoder.finish()
Expand Down Expand Up @@ -486,13 +487,13 @@ struct Mat2([[f32; 2]; 2]);
// tell `shame` about the layout semantics of your cpu types
// Mat2::layout() == sm::f32x2x2::layout()
impl sm::CpuLayout for Mat2 {
fn cpu_layout() -> sm::TypeLayout { sm::f32x2x2::gpu_layout() }
fn cpu_layout() -> sm::TypeLayout { gpu_layout::<sm::f32x2x2>() }
}

#[repr(C, align(16))]
struct Mat4([[f32; 4]; 4]);
impl sm::CpuLayout for Mat4 {
fn cpu_layout() -> sm::TypeLayout { sm::f32x4x4::gpu_layout() }
fn cpu_layout() -> sm::TypeLayout { gpu_layout::<sm::f32x4x4>() }
}

// using "duck-traiting" allows you to define layouts for foreign cpu-types,
Expand Down
8 changes: 4 additions & 4 deletions examples/hello_triangles/src/util/shame_glam.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//!
//! implement `shame::CpuLayout` for some glam types.

use shame as sm;
use shame::{self as sm, gpu_layout};
use sm::GpuLayout;

/// circumventing the orphan rule by defining our own trait.
Expand All @@ -14,14 +14,14 @@ pub trait CpuLayoutExt {

// glam::Vec4 matches sm::f32x4 in size and alignment
impl CpuLayoutExt for glam::Vec4 {
fn cpu_layout() -> sm::TypeLayout { sm::f32x4::gpu_layout() }
fn cpu_layout() -> sm::TypeLayout { gpu_layout::<sm::f32x4>() }
}

// glam::Vec2 only matches sm::f32x2 if it has 8 byte alignment
impl CpuLayoutExt for glam::Vec2 {
fn cpu_layout() -> sm::TypeLayout {
if align_of::<Self>() == 8 {
sm::f32x2::gpu_layout()
gpu_layout::<sm::f32x2>()
} else {
panic!("glam needs to use the `cuda` crate feature for Vec2 to be 8 byte aligned");
}
Expand All @@ -30,5 +30,5 @@ impl CpuLayoutExt for glam::Vec2 {

// glam::Mat4 matches sm::f32x4x4 in size and alignment
impl CpuLayoutExt for glam::Mat4 {
fn cpu_layout() -> sm::TypeLayout { sm::f32x4x4::gpu_layout() }
fn cpu_layout() -> sm::TypeLayout { gpu_layout::<sm::f32x4x4>() }
}
14 changes: 6 additions & 8 deletions examples/shame_wgpu/src/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ pub fn sample_type(st: sm::TextureSampleUsageType) -> wgpu::TextureSampleType {
}
}

/// converts `tf` into a `wgpu::TextureFormat` if supported.
/// converts `tf` into a `wgpu::TextureFormat` if supported.
/// If `tf` is `ExtraTextureFormats::SurfaceFormat`, then the provided `surface_format` argument
/// is returned if it is `Some`. Otherwise an error is returned.
#[rustfmt::skip]
Expand Down Expand Up @@ -194,7 +194,7 @@ pub fn texture_format(tf: &dyn sm::TextureFormatId, surface_format: Option<wgpu:
SmTf::EacR11Snorm => wgpu::TextureFormat::EacR11Snorm,
SmTf::EacRg11Unorm => wgpu::TextureFormat::EacRg11Unorm,
SmTf::EacRg11Snorm => wgpu::TextureFormat::EacRg11Snorm,
SmTf::Astc { block, channel } => wgpu::TextureFormat::Astc {
SmTf::Astc { block, channel } => wgpu::TextureFormat::Astc {
block: match block {
SmASTCb::B4x4 => wgpu::AstcBlock::B4x4,
SmASTCb::B5x4 => wgpu::AstcBlock::B5x4,
Expand All @@ -210,12 +210,12 @@ pub fn texture_format(tf: &dyn sm::TextureFormatId, surface_format: Option<wgpu:
SmASTCb::B10x10 => wgpu::AstcBlock::B10x10,
SmASTCb::B12x10 => wgpu::AstcBlock::B12x10,
SmASTCb::B12x12 => wgpu::AstcBlock::B12x12,
},
},
channel: match channel {
SmASTCc::Unorm => wgpu::AstcChannel::Unorm,
SmASTCc::UnormSrgb => wgpu::AstcChannel::UnormSrgb,
SmASTCc::Hdr => wgpu::AstcChannel::Hdr,
}
}
},
};
Ok(wtf)
Expand Down Expand Up @@ -449,7 +449,7 @@ fn color_writes(write_mask: smr::ChannelWrites) -> wgpu::ColorWrites {

#[rustfmt::skip]
fn vertex_format(format: smr::VertexAttribFormat) -> Result<wgpu::VertexFormat, ShameToWgpuError> {
use smr::ScalarType as S;
use shame::any::layout::ScalarType as S;
use smr::Len as L;
use wgpu::VertexFormat as W;
let unsupported = Err(ShameToWgpuError::UnsupportedVertexAttribFormat(format));
Expand Down Expand Up @@ -479,10 +479,8 @@ fn vertex_format(format: smr::VertexAttribFormat) -> Result<wgpu::VertexFormat,
(S::I32, L::X2) => W::Sint32x2,
(S::I32, L::X3) => W::Sint32x3,
(S::I32, L::X4) => W::Sint32x4,

(S::Bool, _) => return unsupported,
},

smr::VertexAttribFormat::Coarse(p) => {
use smr::PackedScalarType as PS;
use smr::PackedFloat as Norm;
Expand Down
7 changes: 7 additions & 0 deletions examples/type_layout/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "type_layout"
version = "0.1.0"
edition = "2024"

[dependencies]
shame = { path = "../../shame/" }
57 changes: 57 additions & 0 deletions examples/type_layout/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#![allow(dead_code, unused)]
//! Demonstration of the TypeLayout and TypeLayout Builder API.

use layout::{repr, Repr, SizedStruct};
use shame::{
any::{
self,
layout::{
self, FieldOptions, LayoutableSized, Len, RuntimeSizedArrayField, ScalarType, SizedField, SizedType,
UnsizedStruct, Vector, GpuTypeLayout,
},
U32PowerOf2,
},
boolx1, f32x1, f32x2, f32x3, f32x4, gpu_layout, Array, GpuLayout, GpuSized, TypeLayout, VertexAttribute,
VertexLayout,
};

fn main() {
// We'll start by replicating this struct using `any::layout` types.
#[derive(GpuLayout)]
struct Vertex {
position: f32x3,
normal: f32x3,
uv: f32x2,
}

// SizedStruct::new immediately takes the first field of the struct, because
// structs need to have at least one field.
let sized_struct = SizedStruct::new("Vertex", "position", f32x3::layoutable_type_sized())
.extend("normal", f32x3::layoutable_type_sized())
.extend("uv", f32x1::layoutable_type_sized());

let storage = GpuTypeLayout::<repr::Storage>::new(sized_struct.clone());
let packed = GpuTypeLayout::<repr::Packed>::new(sized_struct);
assert_ne!(storage.layout(), packed.layout());

// Does not exist:
// let uniform = GpuTypeLayout::<repr::Uniform>::new(sized_struct.clone());

// However we can try to upgrade a GpuTypeLayout::<repr::Storage>
let uniform = GpuTypeLayout::<repr::Uniform>::try_from(storage.clone()).unwrap();

// Which if it succeeds, guarantees:
assert_eq!(storage.layout(), uniform.layout());

// // Let's end on a pretty error message
let mut sized_struct = SizedStruct::new("D", "a", f32x2::layoutable_type_sized())
// This has align of 4 for storage and align of 16 for uniform.
.extend("b", Array::<f32x1, shame::Size<1>>::layoutable_type_sized());

let storage = GpuTypeLayout::<repr::Storage>::new(sized_struct.clone());
let uniform_result = GpuTypeLayout::<repr::Uniform>::try_from(storage.clone());
match uniform_result {
Err(e) => println!("This error is a showcase:\n{}", e),
Ok(u_layout) => println!("It unexpectedly worked, ohh no."),
}
}
38 changes: 29 additions & 9 deletions shame/src/common/po2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,11 @@ pub enum U32PowerOf2 {
_2147483648,
}

impl From<U32PowerOf2> for u32 {
impl U32PowerOf2 {
/// Returns the corresponding u32.
#[rustfmt::skip]
fn from(value: U32PowerOf2) -> Self {
match value {
pub const fn as_u32(self) -> u32 {
match self {
U32PowerOf2::_1 => 1_u32 ,
U32PowerOf2::_2 => 2_u32 ,
U32PowerOf2::_4 => 4_u32 ,
Expand Down Expand Up @@ -76,6 +77,18 @@ impl From<U32PowerOf2> for u32 {
U32PowerOf2::_2147483648 => 2147483648_u32,
}
}

/// Returns the corresponding u64.
pub const fn as_u64(self) -> u64 { self.as_u32() as u64 }
}

impl From<U32PowerOf2> for u32 {
fn from(value: U32PowerOf2) -> Self { value.as_u32() }
}

impl U32PowerOf2 {
/// Returns the maximum between `self` and `other`.
pub const fn max(self, other: Self) -> Self { if self as u32 > other as u32 { self } else { other } }
}

#[derive(Debug)]
Expand All @@ -89,11 +102,10 @@ impl Display for NotAU32PowerOf2 {

impl std::error::Error for NotAU32PowerOf2 {}

impl TryFrom<u32> for U32PowerOf2 {
type Error = NotAU32PowerOf2;

fn try_from(value: u32) -> Result<Self, Self::Error> {
Ok(match value {
impl U32PowerOf2 {
/// Tries to convert a u32 to U32PowerOf2.
pub const fn try_from_u32(value: u32) -> Option<Self> {
Some(match value {
1 => U32PowerOf2::_1,
2 => U32PowerOf2::_2,
4 => U32PowerOf2::_4,
Expand Down Expand Up @@ -126,11 +138,19 @@ impl TryFrom<u32> for U32PowerOf2 {
536870912 => U32PowerOf2::_536870912,
1073741824 => U32PowerOf2::_1073741824,
2147483648 => U32PowerOf2::_2147483648,
n => return Err(NotAU32PowerOf2(n)),
n => return None,
})
}
}

impl TryFrom<u32> for U32PowerOf2 {
type Error = NotAU32PowerOf2;

fn try_from(value: u32) -> Result<Self, Self::Error> {
U32PowerOf2::try_from_u32(value).ok_or(NotAU32PowerOf2(value))
}
}

impl From<U32PowerOf2> for u64 {
fn from(value: U32PowerOf2) -> Self { u32::from(value) as u64 }
}
13 changes: 11 additions & 2 deletions shame/src/common/proc_macro_reexports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub use crate::call_info;
pub use crate::frontend::any::render_io::VertexAttribFormat;
pub use crate::frontend::any::shared_io::BindPath;
pub use crate::frontend::any::shared_io::BindingType;
pub use crate::frontend::any::shared_io::BufferBindingType;
pub use crate::frontend::any::Any;
pub use crate::frontend::any::InvalidReason;
pub use crate::frontend::encoding::buffer::BufferAddressSpace;
Expand All @@ -25,10 +26,11 @@ pub use crate::frontend::rust_types::struct_::BufferFields;
pub use crate::frontend::rust_types::struct_::SizedFields;
pub use crate::frontend::rust_types::type_layout::FieldLayout;
pub use crate::frontend::rust_types::type_layout::FieldLayoutWithOffset;
pub use crate::frontend::rust_types::type_layout::layoutable::FieldOptions;
pub use crate::frontend::rust_types::type_layout::StructLayout;
pub use crate::frontend::rust_types::type_layout::StructLayoutError;
pub use crate::frontend::rust_types::type_layout::Repr;
pub use crate::frontend::rust_types::type_layout::repr;
pub use crate::frontend::rust_types::type_layout::TypeLayout;
pub use crate::frontend::rust_types::type_layout::TypeLayoutRules;
pub use crate::frontend::rust_types::type_layout::TypeLayoutSemantics;
pub use crate::frontend::rust_types::type_traits::BindingArgs;
pub use crate::frontend::rust_types::type_traits::GpuAligned;
Expand All @@ -40,6 +42,13 @@ pub use crate::frontend::rust_types::type_traits::NoBools;
pub use crate::frontend::rust_types::type_traits::NoHandles;
pub use crate::frontend::rust_types::type_traits::VertexAttribute;
pub use crate::frontend::rust_types::type_traits::GpuLayoutField;
pub use crate::frontend::rust_types::type_layout::layoutable::SizedStruct;
pub use crate::frontend::rust_types::type_layout::layoutable::Layoutable;
pub use crate::frontend::rust_types::type_layout::layoutable::LayoutableSized;
pub use crate::frontend::rust_types::type_layout::layoutable::LayoutableType;
pub use crate::frontend::rust_types::type_layout::layoutable::SizedType;
pub use crate::frontend::rust_types::type_layout::layoutable::SizedOrArray;
pub use crate::frontend::rust_types::type_layout::layoutable::builder::StructFromPartsError;
pub use crate::frontend::rust_types::AsAny;
pub use crate::frontend::rust_types::GpuType;
#[allow(missing_docs)]
Expand Down
Loading