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

As{Std140, Std430} for arrays of As{Std140, Std430} #27

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ resolver = "2"
[features]
default = ["std"]
std = []
arrays = ["crevice-derive/arrays", "const_format"]

[workspace]
members = ["crevice-derive", "crevice-tests"]
default-members = ["crevice-derive", "crevice-tests"]

[dependencies]
crevice-derive = { version = "0.8.0", path = "crevice-derive" }
const_format = {version = "0.2.22", optional = true}

bytemuck = "1.4.1"
mint = "0.5.8"
Expand Down
3 changes: 3 additions & 0 deletions crevice-derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ default = []
# Enable methods that let you introspect into the generated structs.
debug-methods = []

# Enable arrays support
arrays = []

[lib]
proc-macro = true

Expand Down
32 changes: 30 additions & 2 deletions crevice-derive/src/glsl.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use proc_macro2::{Literal, TokenStream};
use quote::quote;
use syn::{parse_quote, Data, DeriveInput, Fields, Path};
#[cfg(feature = "arrays")]
use syn::TypeArray;
use syn::{parse_quote, Data, DeriveInput, Expr, Fields, Path, Type};

pub fn emit(input: DeriveInput) -> TokenStream {
let fields = match &input.data {
Expand All @@ -22,12 +24,14 @@ pub fn emit(input: DeriveInput) -> TokenStream {

let glsl_fields = fields.named.iter().map(|field| {
let field_ty = &field.ty;
let (base_ty, array_suffix) = remove_array_layers(field_ty);
let field_name_str = Literal::string(&field.ident.as_ref().unwrap().to_string());

quote! {
::crevice::glsl::GlslField {
ty: <#field_ty as ::crevice::glsl::Glsl>::NAME,
ty: <#base_ty as ::crevice::glsl::Glsl>::NAME,
name: #field_name_str,
dim: #array_suffix,
}
}
});
Expand All @@ -44,3 +48,27 @@ pub fn emit(input: DeriveInput) -> TokenStream {
}
}
}

#[cfg(feature = "arrays")]
fn remove_array_layers(mut ty: &Type) -> (&Type, Expr) {
let mut suffix = quote!("");

loop {
match ty {
&Type::Array(TypeArray {
ref elem, ref len, ..
}) => {
ty = elem.as_ref();
suffix = quote!(
::crevice::internal::const_format::concatcp!("[", (#len as usize), "]", #suffix)
);
}
_ => break,
}
}
(ty, Expr::Verbatim(suffix))
}
#[cfg(not(feature = "arrays"))]
fn remove_array_layers(ty: &Type) -> (&Type, Expr) {
(ty, Expr::Verbatim(quote!("")))
}
47 changes: 19 additions & 28 deletions crevice-derive/src/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ pub fn emit(
let as_trait_method = format_ident!("as_{}", mod_name);
let from_trait_method = format_ident!("from_{}", mod_name);

let array_name = format_ident!("{}ArrayItem", trait_name);
let array_path: Path = parse_quote!(#mod_path::#array_name);

let visibility = input.vis;
let input_name = input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
Expand Down Expand Up @@ -50,14 +53,6 @@ pub fn emit(
}
};

// Gives an expression telling whether the type should have trailing padding
// at least equal to its alignment.
let layout_pad_at_end_of_ty = |ty: &Type| {
quote! {
<<#ty as #as_trait_path>::Output as #trait_path>::PAD_AT_END
}
};

let field_alignments = fields.iter().map(|field| layout_alignment_of_ty(&field.ty));
let struct_alignment = quote! {
::crevice::internal::max_arr([
Expand Down Expand Up @@ -100,15 +95,11 @@ pub fn emit(
output.into_iter().collect::<TokenStream>()
};

let pad_fn_impls: TokenStream = fields
let pad_fn_impls: TokenStream = pad_fns
.iter()
.enumerate()
.map(|(index, prev_field)| {
let pad_fn = &pad_fns[index];

.map(|(index, pad_fn)| {
let starting_offset = offset_after_field(index);
let prev_field_has_end_padding = layout_pad_at_end_of_ty(&prev_field.ty);
let prev_field_alignment = layout_alignment_of_ty(&prev_field.ty);

let next_field_or_self_alignment = fields
.get(index + 1)
Expand All @@ -125,21 +116,10 @@ pub fn emit(
// alignment we are.
let starting_offset = #starting_offset;

// If the previous field is a struct or array, we must align
// the next field to at least THAT field's alignment.
let min_alignment = if #prev_field_has_end_padding {
#prev_field_alignment
} else {
0
};

// We set our target alignment to the larger of the
// alignment due to the previous field and the alignment
// requirement of the next field.
let alignment = ::crevice::internal::max(
#next_field_or_self_alignment,
min_alignment,
);
let alignment = #next_field_or_self_alignment;

// Using everything we've got, compute our padding amount.
::crevice::internal::align_offset(starting_offset, alignment)
Expand Down Expand Up @@ -246,18 +226,29 @@ pub fn emit(
quote!()
};

let array_item_impl = if cfg!(feature = "arrays") {
quote! {
unsafe impl #impl_generics #array_path for #generated_name #ty_generics #where_clause {
type Padding = [u8; 0];
}
}
} else {
quote!()
};

quote! {
#pad_fn_impls
#struct_definition

unsafe impl #impl_generics ::crevice::internal::bytemuck::Zeroable for #generated_name #ty_generics #where_clause {}
unsafe impl #impl_generics ::crevice::internal::bytemuck::Pod for #generated_name #ty_generics #where_clause {}

unsafe impl #impl_generics #mod_path::#trait_name for #generated_name #ty_generics #where_clause {
unsafe impl #impl_generics #trait_path for #generated_name #ty_generics #where_clause {
const ALIGNMENT: usize = #struct_alignment;
const PAD_AT_END: bool = true;
}

#array_item_impl

impl #impl_generics #as_trait_path for #input_name #ty_generics #where_clause {
type Output = #generated_name;

Expand Down
1 change: 1 addition & 0 deletions crevice-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ edition = "2018"

[features]
wgpu-validation = ["wgpu", "naga", "futures"]
arrays = ["crevice/arrays"]

[dependencies]
crevice = { path = ".." }
Expand Down
4 changes: 3 additions & 1 deletion crevice-tests/src/gpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,11 @@ where
"std140 value did not round-trip through wgpu successfully.\n\
Input: {:?}\n\
Output: {:?}\n\n\
Data: {:?}\n\
Bytes: {:?}\n\n\
GLSL shader:\n{}\n\n\
WGSL shader:\n{}",
value, output, glsl_shader, wgsl_shader,
value, output, data, bytes, glsl_shader, wgsl_shader,
);

panic!("wgpu round-trip failure for {}", T::NAME);
Expand Down
67 changes: 67 additions & 0 deletions crevice-tests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -342,3 +342,70 @@ fn bools_and_bool_vectors() {
y: 8,
});
}

#[test]
#[cfg(feature = "arrays")]
fn array_strides_small_value() {
#[derive(Debug, PartialEq, AsStd140, AsStd430)]
struct ArrayOfSmallValues {
inner: [f32; 4],
}

assert_std140!((size = 64, align = 16) ArrayOfSmallValues {
inner: 0,
});

assert_std430!((size = 16, align = 4) ArrayOfSmallValues {
inner: 0,
});
}

#[test]
#[cfg(feature = "arrays")]
fn array_strides_vec3() {
#[derive(Debug, PartialEq, AsStd140, AsStd430, GlslStruct)]
struct ArrayOfVector3 {
inner: [Vector3<u32>; 6],
}

assert_std140!((size = 96, align = 16) ArrayOfVector3 {
inner: 0,
});

assert_std430!((size = 96, align = 16) ArrayOfVector3 {
inner: 0,
});

test_round_trip_struct(ArrayOfVector3 {
inner: [
[0x00010203, 0x04050607, 0x08091011].into(),
[0x12131415, 0x16171819, 0x20212223].into(),
[0x24252627, 0x28293031, 0x32333435].into(),
[0x36373839, 0x40414243, 0x44454647].into(),
[0x48495051, 0x52535455, 0x56575859].into(),
[0x60616263, 0x64656667, 0x68697071].into(),
],
})
}

#[test]
#[cfg(feature = "arrays")]
fn array_of_custom_struct_works() {
#[derive(Debug, PartialEq, AsStd140, AsStd430)]
struct SomeStruct {
x: f32,
}

#[derive(Debug, PartialEq, AsStd140, AsStd430)]
struct ArrayOfStructs {
inner: [SomeStruct; 4],
}

assert_std140!((size = 64, align = 16) ArrayOfStructs {
inner: 0,
});

assert_std430!((size = 16, align = 4) ArrayOfStructs {
inner: 0,
});
}
4 changes: 4 additions & 0 deletions src/glsl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ pub struct GlslField {

/// The field's name. This must be a valid GLSL identifier.
pub name: &'static str,

/// The field's array dimensions. This is a string of the form "[1][2]..."
pub dim: &'static str,
}

/// Trait for types that can be represented as a struct in GLSL.
Expand All @@ -37,6 +40,7 @@ pub unsafe trait GlslStruct: Glsl {
output.push_str(field.ty);
output.push(' ');
output.push_str(field.name);
output.push_str(field.dim);
output.push_str(";\n");
}

Expand Down
3 changes: 3 additions & 0 deletions src/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,6 @@ pub const fn max_arr<const N: usize>(input: [usize; N]) -> usize {

max
}

#[cfg(feature = "arrays")]
pub use const_format;
3 changes: 2 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,8 @@ Crevice supports Rust 1.52.1 and newer due to use of new `const fn` features.
[glsl-layout]: https://github.com/rustgd/glsl-layout
*/

#![deny(missing_docs)]
#![cfg_attr(feature = "arrays", feature(generic_const_exprs))]
//#![deny(missing_docs)]
#![cfg_attr(not(feature = "std"), no_std)]

#[macro_use]
Expand Down
4 changes: 4 additions & 0 deletions src/std140.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//! Defines traits and types for working with data adhering to GLSL's `std140`
//! layout specification.

#[cfg(feature = "arrays")]
mod arrays;
mod dynamic_uniform;
mod primitives;
mod sizer;
Expand All @@ -10,6 +12,8 @@ mod writer;

pub use crate::bool::Bool;

#[cfg(feature = "arrays")]
pub use self::arrays::Std140ArrayItem;
pub use self::dynamic_uniform::*;
pub use self::primitives::*;
pub use self::sizer::*;
Expand Down
Loading