Skip to content
Open
17 changes: 10 additions & 7 deletions crevice-derive/src/glsl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,15 @@ pub fn emit(input: DeriveInput) -> TokenStream {
let glsl_fields = fields.named.iter().map(|field| {
let field_ty = &field.ty;
let field_name_str = Literal::string(&field.ident.as_ref().unwrap().to_string());
let field_as = quote! {<#field_ty as ::crevice::glsl::GlslArray>};

quote! {
::crevice::glsl::GlslField {
ty: <#field_ty as ::crevice::glsl::Glsl>::NAME,
name: #field_name_str,
}
s.push_str("\t");
s.push_str(#field_as::NAME);
s.push_str(" ");
s.push_str(#field_name_str);
<#field_as::ArraySize as ::crevice::glsl::DimensionList>::push_to_string(s);
s.push_str(";\n");
}
});

Expand All @@ -38,9 +41,9 @@ pub fn emit(input: DeriveInput) -> TokenStream {
}

unsafe impl #impl_generics #struct_trait_path for #name #ty_generics #where_clause {
const FIELDS: &'static [::crevice::glsl::GlslField] = &[
#( #glsl_fields, )*
];
fn enumerate_fields(s: &mut String) {
#( #glsl_fields )*
}
}
}
}
7 changes: 7 additions & 0 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 padded_name = format_ident!("{}Padded", trait_name);
let padded_path: Path = parse_quote!(#mod_path::#padded_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 @@ -256,6 +259,10 @@ pub fn emit(
unsafe impl #impl_generics #mod_path::#trait_name for #generated_name #ty_generics #where_clause {
const ALIGNMENT: usize = #struct_alignment;
const PAD_AT_END: bool = true;
type Padded = #padded_path<Self, {::crevice::internal::align_offset(
::core::mem::size_of::<#generated_name>(),
#struct_alignment
)}>;
}

impl #impl_generics #as_trait_path for #input_name #ty_generics #where_clause {
Expand Down
41 changes: 41 additions & 0 deletions crevice-tests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,3 +323,44 @@ fn proper_offset_calculations_for_differing_member_sizes() {
trailing: 32,
});
}

#[test]
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]
fn array_strides_vec3() {
#[derive(Debug, PartialEq, AsStd140, AsStd430, GlslStruct)]
struct ArrayOfVector3 {
inner: [Vector3<f32>; 4],
}

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

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

test_round_trip_struct(ArrayOfVector3 {
inner: [
[0.0, 1.0, 2.0].into(),
[3.0, 4.0, 5.0].into(),
[6.0, 7.0, 8.0].into(),
[9.0, 10.0, 11.0].into(),
],
})
}
65 changes: 48 additions & 17 deletions src/glsl.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,33 @@
//! Defines traits and types for generating GLSL code from Rust definitions.

pub use crevice_derive::GlslStruct;
use std::marker::PhantomData;

/// Type-level linked list of array dimensions
pub struct Dimension<A, const N: usize> {
_marker: PhantomData<A>,
}

/// Type-level linked list terminator for array dimensions.
pub struct DimensionNil;

/// Trait for type-level array dimensions. Probably shouldn't be implemented outside this crate.
pub unsafe trait DimensionList {
/// Write dimensions in square brackets to a string, list tail to list head.
fn push_to_string(s: &mut String);
}

unsafe impl DimensionList for DimensionNil {
fn push_to_string(_: &mut String) {}
}

unsafe impl<A: DimensionList, const N: usize> DimensionList for Dimension<A, N> {
fn push_to_string(s: &mut String) {
use std::fmt::Write;
A::push_to_string(s);
write!(s, "[{}]", N).unwrap();
}
}

/// Trait for types that have a GLSL equivalent. Useful for generating GLSL code
/// from Rust structs.
Expand All @@ -9,21 +36,12 @@ pub unsafe trait Glsl {
const NAME: &'static str;
}

/// A field contained within a GLSL struct definition.
pub struct GlslField {
/// The type of the field, like `vec2` or `mat3`.
pub ty: &'static str,

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

/// Trait for types that can be represented as a struct in GLSL.
///
/// This trait should not generally be implemented by hand, but can be derived.
pub unsafe trait GlslStruct: Glsl {
/// The fields contained in this struct.
const FIELDS: &'static [GlslField];
fn enumerate_fields(s: &mut String);

/// Generates GLSL code that represents this struct and its fields.
fn glsl_definition() -> String {
Expand All @@ -32,19 +50,26 @@ pub unsafe trait GlslStruct: Glsl {
output.push_str(Self::NAME);
output.push_str(" {\n");

for field in Self::FIELDS {
output.push('\t');
output.push_str(field.ty);
output.push(' ');
output.push_str(field.name);
output.push_str(";\n");
}
Self::enumerate_fields(&mut output);

output.push_str("};");
output
}
}

/// Trait for types that are expressible as a GLSL type with (possibly zero) array dimensions.
pub unsafe trait GlslArray {
/// Base type name.
const NAME: &'static str;
/// Type-level linked list of array dimensions, ordered outer to inner.
type ArraySize: DimensionList;
}

unsafe impl<T: Glsl> GlslArray for T {
const NAME: &'static str = <T as Glsl>::NAME;
type ArraySize = DimensionNil;
}

unsafe impl Glsl for f32 {
const NAME: &'static str = "float";
}
Expand All @@ -60,3 +85,9 @@ unsafe impl Glsl for i32 {
unsafe impl Glsl for u32 {
const NAME: &'static str = "uint";
}

unsafe impl<T: GlslArray, const N: usize> GlslArray for [T; N] {
const NAME: &'static str = T::NAME;

type ArraySize = Dimension<T::ArraySize, N>;
}
10 changes: 9 additions & 1 deletion src/std140/dynamic_uniform.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use bytemuck::{Pod, Zeroable};

use crate::internal::max;
#[allow(unused_imports)]
use crate::internal::{align_offset, max};
use crate::std140::{AsStd140, Std140};

/// Wrapper type that aligns the inner type to at least 256 bytes.
Expand Down Expand Up @@ -28,6 +29,13 @@ pub struct DynamicUniformStd140<T>(T);

unsafe impl<T: Std140> Std140 for DynamicUniformStd140<T> {
const ALIGNMENT: usize = max(256, T::ALIGNMENT);
#[cfg(const_evaluatable_checked)]
type Padded = crate::std140::Std140Padded<
Self,
{ align_offset(core::mem::size_of::<T>(), max(256, T::ALIGNMENT)) },
>;
#[cfg(not(const_evaluatable_checked))]
type Padded = crate::std140::InvalidPadded;
}

unsafe impl<T: Zeroable> Zeroable for DynamicUniformStd140<T> {}
Expand Down
11 changes: 10 additions & 1 deletion src/std140/primitives.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
use bytemuck::{Pod, Zeroable};

use crate::glsl::Glsl;
use crate::std140::Std140;
use crate::std140::{Std140, Std140Padded};

use crate::internal::{align_offset, max};
use core::mem::size_of;

unsafe impl Std140 for f32 {
const ALIGNMENT: usize = 4;
type Padded = Std140Padded<Self, 12>;
}

unsafe impl Std140 for f64 {
const ALIGNMENT: usize = 8;
type Padded = Std140Padded<Self, 8>;
}

unsafe impl Std140 for i32 {
const ALIGNMENT: usize = 4;
type Padded = Std140Padded<Self, 12>;
}

unsafe impl Std140 for u32 {
const ALIGNMENT: usize = 4;
type Padded = Std140Padded<Self, 12>;
}

macro_rules! vectors {
Expand All @@ -39,6 +46,7 @@ macro_rules! vectors {

unsafe impl Std140 for $name {
const ALIGNMENT: usize = $align;
type Padded = Std140Padded<Self, {align_offset(size_of::<$name>(), max(16, $align))}>;
}

unsafe impl Glsl for $name {
Expand Down Expand Up @@ -98,6 +106,7 @@ macro_rules! matrices {
const ALIGNMENT: usize = $align;
/// Matrices are technically arrays of primitives, and as such require pad at end.
const PAD_AT_END: bool = true;
type Padded = Std140Padded<Self, {align_offset(size_of::<$name>(), max(16, $align))}>;
}

unsafe impl Glsl for $name {
Expand Down
Loading