Update vendored Crevice to 0.8.0 + PR for arrays (#3059)

# Objective

- Update vendor crevice to have the latest update from crevice 0.8.0
- Using https://github.com/ElectronicRU/crevice/tree/arrays which has the changes to make arrays work

## Solution

- Also updated glam and hexasphere to only have one version of glam
- From the original PR, using crevice to write GLSL code containing arrays would probably not work but it's not something used by Bevy
This commit is contained in:
François 2021-11-12 01:39:25 +00:00
parent e375addec6
commit 290b7dd9ab
39 changed files with 1846 additions and 668 deletions

View file

@ -108,7 +108,7 @@ ron = "0.6.2"
serde = { version = "1", features = ["derive"] }
# Needed to poll Task examples
futures-lite = "1.11.3"
crevice = {path = "crates/crevice"}
crevice = { path = "crates/crevice", version = "0.8.0", features = ["glam"] }
[[example]]
name = "hello_world"

View file

@ -13,5 +13,5 @@ license = "MIT OR Apache-2.0"
keywords = ["bevy"]
[dependencies]
glam = { version = "0.15.1", features = ["serde", "bytemuck"] }
glam = { version = "0.20.0", features = ["serde", "bytemuck"] }
bevy_reflect = { path = "../bevy_reflect", version = "0.5.0", features = ["bevy"] }

View file

@ -28,7 +28,7 @@ parking_lot = "0.11.0"
thiserror = "1.0"
serde = "1"
smallvec = { version = "1.6", features = ["serde", "union", "const_generics"], optional = true }
glam = { version = "0.15.1", features = ["serde"], optional = true }
glam = { version = "0.20.0", features = ["serde"], optional = true }
[dev-dependencies]
ron = "0.6.2"

View file

@ -32,7 +32,7 @@ downcast-rs = "1.2.0"
thiserror = "1.0"
anyhow = "1.0.4"
hex = "0.4.2"
hexasphere = "4.0.0"
hexasphere = "6.0.0"
parking_lot = "0.11.0"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]

View file

@ -1,8 +1,8 @@
[package]
name = "crevice"
description = "Create GLSL-compatible versions of structs with explicitly-initialized padding"
version = "0.6.0"
edition = "2018"
version = "0.8.0"
edition = "2021"
authors = ["Lucien Greathouse <me@lpghatguy.com>"]
documentation = "https://docs.rs/crevice"
homepage = "https://github.com/LPGhatguy/crevice"
@ -10,6 +10,7 @@ repository = "https://github.com/LPGhatguy/crevice"
readme = "README.md"
keywords = ["glsl", "std140", "std430"]
license = "MIT OR Apache-2.0"
# resolver = "2"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -17,12 +18,19 @@ license = "MIT OR Apache-2.0"
default = ["std"]
std = []
# [workspace]
# members = ["crevice-derive", "crevice-tests"]
# default-members = ["crevice-derive", "crevice-tests"]
[dependencies]
crevice-derive = { version = "0.6.0", path = "crevice-derive" }
crevice-derive = { version = "0.8.0", path = "crevice-derive" }
bytemuck = "1.4.1"
mint = "0.5.5"
glam = "0.15.1"
mint = "0.5.8"
cgmath = { version = "0.18.0", optional = true }
glam = { version = "0.20.0", features = ["mint"], optional = true }
nalgebra = { version = "0.29.0", features = ["mint"], optional = true }
[dev-dependencies]
crevice-derive = { version = "0.6.0", path = "crevice-derive" }
insta = "0.16.1"

View file

@ -11,19 +11,34 @@ method to allow safely packing data into buffers for uploading.
Generated structs also implement [`bytemuck::Zeroable`] and
[`bytemuck::Pod`] for use with other libraries.
Crevice is similar to [`glsl-layout`][glsl-layout], but supports `mint` types
and explicitly initializes padding to remove one source of undefined behavior.
Crevice is similar to [`glsl-layout`][glsl-layout], but supports types from many
math crates, can generate GLSL source from structs, and explicitly initializes
padding to remove one source of undefined behavior.
Examples in this crate use cgmath, but any math crate that works with the mint
crate will also work. Some other crates include nalgebra, ultraviolet, glam, and
vek.
Crevice has support for many Rust math libraries via feature flags, and most
other math libraries by use of the mint crate. Crevice currently supports:
* mint 0.5, enabled by default
* cgmath 0.18, using the `cgmath` feature
* nalgebra 0.29, using the `nalgebra` feature
* glam 0.19, using the `glam` feature
PRs are welcome to add or update math libraries to Crevice.
If your math library is not supported, it's possible to define structs using the
types from mint and convert your math library's types into mint types. This is
supported by most Rust math libraries.
Your math library may require you to turn on a feature flag to get mint support.
For example, cgmath requires the "mint" feature to be enabled to allow
conversions to and from mint types.
## Examples
### Single Value
Uploading many types can be done by deriving `AsStd140` and using
[`as_std140`][std140::AsStd140::as_std140] and
Uploading many types can be done by deriving [`AsStd140`][std140::AsStd140] and
using [`as_std140`][std140::AsStd140::as_std140] and
[`as_bytes`][std140::Std140::as_bytes] to turn the result into bytes.
```glsl
@ -36,8 +51,6 @@ uniform MAIN {
```rust
use crevice::std140::{AsStd140, Std140};
use cgmath::prelude::*;
use cgmath::{Matrix3, Vector3};
#[derive(AsStd140)]
struct MainUniform {
@ -47,8 +60,12 @@ struct MainUniform {
}
let value = MainUniform {
orientation: Matrix3::identity().into(),
position: Vector3::new(1.0, 2.0, 3.0).into(),
orientation: [
[1.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, 0.0, 1.0],
].into(),
position: [1.0, 2.0, 3.0].into(),
scale: 4.0,
};
@ -57,9 +74,10 @@ let value_std140 = value.as_std140();
upload_data_to_gpu(value_std140.as_bytes());
```
#### Sequential Types
### Sequential Types
More complicated data can be uploaded using the std140 `Writer` type.
More complicated data can be uploaded using the std140
[`Writer`][std140::Writer] type.
```glsl
struct PointLight {
@ -113,24 +131,40 @@ unmap_gpu_buffer();
```
### Minimum Supported Rust Version (MSRV)
## Features
Crevice supports Rust 1.46.0 and newer due to use of new `const fn` features.
* `std` (default): Enables [`std::io::Write`]-based structs.
* `cgmath`: Enables support for types from cgmath.
* `nalgebra`: Enables support for types from nalgebra.
* `glam`: Enables support for types from glam.
## Minimum Supported Rust Version (MSRV)
Crevice supports Rust 1.52.1 and newer due to use of new `const fn` features.
[glsl-layout]: https://github.com/rustgd/glsl-layout
[Zeroable]: https://docs.rs/bytemuck/latest/bytemuck/trait.Zeroable.html
[Pod]: https://docs.rs/bytemuck/latest/bytemuck/trait.Pod.html
[TypeLayout]: https://docs.rs/type-layout/latest/type_layout/trait.TypeLayout.html
[std140::AsStd140]: https://docs.rs/crevice/latest/crevice/std140/trait.AsStd140.html
[std140::AsStd140::as_std140]: https://docs.rs/crevice/latest/crevice/std140/trait.AsStd140.html#method.as_std140
[std140::Std140::as_bytes]: https://docs.rs/crevice/latest/crevice/std140/trait.Std140.html#method.as_bytes
[std140::Writer]: https://docs.rs/crevice/latest/crevice/std140/struct.Writer.html
[`std::io::Write`]: https://doc.rust-lang.org/stable/std/io/trait.Write.html
[`bytemuck::Pod`]: https://docs.rs/bytemuck/latest/bytemuck/trait.Pod.html
[`bytemuck::Zeroable`]: https://docs.rs/bytemuck/latest/bytemuck/trait.Zeroable.html
## License
Licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0))
* MIT license ([LICENSE-MIT](LICENSE-MIT) or [http://opensource.org/licenses/MIT](http://opensource.org/licenses/MIT))
* Apache License, Version 2.0, ([LICENSE-APACHE](http://www.apache.org/licenses/LICENSE-2.0))
* MIT license ([LICENSE-MIT](http://opensource.org/licenses/MIT))
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall
be dual licensed as above, without any additional terms or conditions.

View file

@ -2,6 +2,16 @@
{{readme}}
[std140::AsStd140]: https://docs.rs/crevice/latest/crevice/std140/trait.AsStd140.html
[std140::AsStd140::as_std140]: https://docs.rs/crevice/latest/crevice/std140/trait.AsStd140.html#method.as_std140
[std140::Std140::as_bytes]: https://docs.rs/crevice/latest/crevice/std140/trait.Std140.html#method.as_bytes
[std140::Writer]: https://docs.rs/crevice/latest/crevice/std140/struct.Writer.html
[`std::io::Write`]: https://doc.rust-lang.org/stable/std/io/trait.Write.html
[`bytemuck::Pod`]: https://docs.rs/bytemuck/latest/bytemuck/trait.Pod.html
[`bytemuck::Zeroable`]: https://docs.rs/bytemuck/latest/bytemuck/trait.Zeroable.html
## License
Licensed under either of

View file

@ -1,7 +1,7 @@
[package]
name = "crevice-derive"
description = "Derive crate for the 'crevice' crate"
version = "0.6.0"
version = "0.8.0"
edition = "2018"
authors = ["Lucien Greathouse <me@lpghatguy.com>"]
documentation = "https://docs.rs/crevice-derive"
@ -9,6 +9,12 @@ homepage = "https://github.com/LPGhatguy/crevice"
repository = "https://github.com/LPGhatguy/crevice"
license = "MIT OR Apache-2.0"
[features]
default = []
# Enable methods that let you introspect into the generated structs.
debug-methods = []
[lib]
proc-macro = true

View file

@ -0,0 +1,49 @@
use proc_macro2::{Literal, TokenStream};
use quote::quote;
use syn::{parse_quote, Data, DeriveInput, Fields, Path};
pub fn emit(input: DeriveInput) -> TokenStream {
let fields = match &input.data {
Data::Struct(data) => match &data.fields {
Fields::Named(fields) => fields,
Fields::Unnamed(_) => panic!("Tuple structs are not supported"),
Fields::Unit => panic!("Unit structs are not supported"),
},
Data::Enum(_) | Data::Union(_) => panic!("Only structs are supported"),
};
let base_trait_path: Path = parse_quote!(::crevice::glsl::Glsl);
let struct_trait_path: Path = parse_quote!(::crevice::glsl::GlslStruct);
let name = input.ident;
let name_str = Literal::string(&name.to_string());
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
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! {
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");
}
});
quote! {
unsafe impl #impl_generics #base_trait_path for #name #ty_generics #where_clause {
const NAME: &'static str = #name_str;
}
unsafe impl #impl_generics #struct_trait_path for #name #ty_generics #where_clause {
fn enumerate_fields(s: &mut String) {
#( #glsl_fields )*
}
}
}
}

View file

@ -0,0 +1,288 @@
use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote};
use syn::{parse_quote, Data, DeriveInput, Fields, Ident, Path, Type};
pub fn emit(
input: DeriveInput,
trait_name: &'static str,
mod_name: &'static str,
min_struct_alignment: usize,
) -> TokenStream {
let mod_name = Ident::new(mod_name, Span::call_site());
let trait_name = Ident::new(trait_name, Span::call_site());
let mod_path: Path = parse_quote!(::crevice::#mod_name);
let trait_path: Path = parse_quote!(#mod_path::#trait_name);
let as_trait_name = format_ident!("As{}", trait_name);
let as_trait_path: Path = parse_quote!(#mod_path::#as_trait_name);
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();
let generated_name = format_ident!("{}{}", trait_name, input_name);
// Crevice's derive only works on regular structs. We could potentially
// support transparent tuple structs in the future.
let fields: Vec<_> = match &input.data {
Data::Struct(data) => match &data.fields {
Fields::Named(fields) => fields.named.iter().collect(),
Fields::Unnamed(_) => panic!("Tuple structs are not supported"),
Fields::Unit => panic!("Unit structs are not supported"),
},
Data::Enum(_) | Data::Union(_) => panic!("Only structs are supported"),
};
// Gives the layout-specific version of the given type.
let layout_version_of_ty = |ty: &Type| {
quote! {
<#ty as #as_trait_path>::Output
}
};
// Gives an expression returning the layout-specific alignment for the type.
let layout_alignment_of_ty = |ty: &Type| {
quote! {
<<#ty as #as_trait_path>::Output as #trait_path>::ALIGNMENT
}
};
// 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([
#min_struct_alignment,
#(#field_alignments,)*
])
};
// Generate names for each padding calculation function.
let pad_fns: Vec<_> = (0..fields.len())
.map(|index| format_ident!("_{}__{}Pad{}", input_name, trait_name, index))
.collect();
// Computes the offset immediately AFTER the field with the given index.
//
// This function depends on the generated padding calculation functions to
// do correct alignment. Be careful not to cause recursion!
let offset_after_field = |target: usize| {
let mut output = vec![quote!(0usize)];
for index in 0..=target {
let field_ty = &fields[index].ty;
let layout_ty = layout_version_of_ty(field_ty);
output.push(quote! {
+ ::core::mem::size_of::<#layout_ty>()
});
// For every field except our target field, also add the generated
// padding. Padding occurs after each field, so it isn't included in
// this value.
if index < target {
let pad_fn = &pad_fns[index];
output.push(quote! {
+ #pad_fn()
});
}
}
output.into_iter().collect::<TokenStream>()
};
let pad_fn_impls: TokenStream = fields
.iter()
.enumerate()
.map(|(index, prev_field)| {
let pad_fn = &pad_fns[index];
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)
.map(|next_field| layout_alignment_of_ty(&next_field.ty))
.unwrap_or(quote!(#struct_alignment));
quote! {
/// Tells how many bytes of padding have to be inserted after
/// the field with index #index.
#[allow(non_snake_case)]
const fn #pad_fn() -> usize {
// First up, calculate our offset into the struct so far.
// We'll use this value to figure out how far out of
// 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,
);
// Using everything we've got, compute our padding amount.
::crevice::internal::align_offset(starting_offset, alignment)
}
}
})
.collect();
let generated_struct_fields: TokenStream = fields
.iter()
.enumerate()
.map(|(index, field)| {
let field_name = field.ident.as_ref().unwrap();
let field_ty = layout_version_of_ty(&field.ty);
let pad_field_name = format_ident!("_pad{}", index);
let pad_fn = &pad_fns[index];
quote! {
#field_name: #field_ty,
#pad_field_name: [u8; #pad_fn()],
}
})
.collect();
let generated_struct_field_init: TokenStream = fields
.iter()
.map(|field| {
let field_name = field.ident.as_ref().unwrap();
quote! {
#field_name: self.#field_name.#as_trait_method(),
}
})
.collect();
let input_struct_field_init: TokenStream = fields
.iter()
.map(|field| {
let field_name = field.ident.as_ref().unwrap();
quote! {
#field_name: #as_trait_path::#from_trait_method(input.#field_name),
}
})
.collect();
let struct_definition = quote! {
#[derive(Debug, Clone, Copy)]
#[repr(C)]
#[allow(non_snake_case)]
#visibility struct #generated_name #ty_generics #where_clause {
#generated_struct_fields
}
};
let debug_methods = if cfg!(feature = "debug-methods") {
let debug_fields: TokenStream = fields
.iter()
.map(|field| {
let field_name = field.ident.as_ref().unwrap();
let field_ty = &field.ty;
quote! {
fields.push(Field {
name: stringify!(#field_name),
size: ::core::mem::size_of::<#field_ty>(),
offset: (&zeroed.#field_name as *const _ as usize)
- (&zeroed as *const _ as usize),
});
}
})
.collect();
quote! {
impl #impl_generics #generated_name #ty_generics #where_clause {
fn debug_metrics() -> String {
let size = ::core::mem::size_of::<Self>();
let align = <Self as #trait_path>::ALIGNMENT;
let zeroed: Self = ::crevice::internal::bytemuck::Zeroable::zeroed();
#[derive(Debug)]
struct Field {
name: &'static str,
offset: usize,
size: usize,
}
let mut fields = Vec::new();
#debug_fields
format!("Size {}, Align {}, fields: {:#?}", size, align, fields)
}
fn debug_definitions() -> &'static str {
stringify!(
#struct_definition
#pad_fn_impls
)
}
}
}
} 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 {
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 {
type Output = #generated_name;
fn #as_trait_method(&self) -> Self::Output {
Self::Output {
#generated_struct_field_init
..::crevice::internal::bytemuck::Zeroable::zeroed()
}
}
fn #from_trait_method(input: Self::Output) -> Self {
Self {
#input_struct_field_init
}
}
}
#debug_methods
}
}

View file

@ -1,13 +1,14 @@
mod glsl;
mod layout;
use proc_macro::TokenStream as CompilerTokenStream;
use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote};
use syn::{parse_macro_input, parse_quote, Data, DeriveInput, Fields, Ident, Path};
use syn::{parse_macro_input, DeriveInput};
#[proc_macro_derive(AsStd140)]
pub fn derive_as_std140(input: CompilerTokenStream) -> CompilerTokenStream {
let input = parse_macro_input!(input as DeriveInput);
let expanded = EmitOptions::new("Std140", "std140", 16).emit(input);
let expanded = layout::emit(input, "Std140", "std140", 16);
CompilerTokenStream::from(expanded)
}
@ -15,278 +16,15 @@ pub fn derive_as_std140(input: CompilerTokenStream) -> CompilerTokenStream {
#[proc_macro_derive(AsStd430)]
pub fn derive_as_std430(input: CompilerTokenStream) -> CompilerTokenStream {
let input = parse_macro_input!(input as DeriveInput);
let expanded = EmitOptions::new("Std430", "std430", 0).emit(input);
let expanded = layout::emit(input, "Std430", "std430", 0);
CompilerTokenStream::from(expanded)
}
struct EmitOptions {
/// The Rust-friendly name of the layout, like Std140.
layout_name: Ident,
#[proc_macro_derive(GlslStruct)]
pub fn derive_glsl_struct(input: CompilerTokenStream) -> CompilerTokenStream {
let input = parse_macro_input!(input as DeriveInput);
let expanded = glsl::emit(input);
/// The minimum alignment for a struct in this layout.
min_struct_alignment: usize,
/// The fully-qualified path to the Crevice module containing everything for
/// this layout.
mod_path: Path,
/// The fully-qualified path to the trait defining a type in this layout.
trait_path: Path,
/// The fully-qualified path to the trait implemented for types that can be
/// converted into this layout, like AsStd140.
as_trait_path: Path,
/// The name of the associated type contained in AsTrait.
as_trait_assoc: Ident,
/// The name of the method used to convert from AsTrait to Trait.
as_trait_method: Ident,
// The name of the method used to convert from Trait to AsTrait.
from_trait_method: Ident,
/// The name of the struct used for Padded type.
padded_name: Ident,
}
impl EmitOptions {
fn new(layout_name: &'static str, mod_name: &'static str, min_struct_alignment: usize) -> Self {
let mod_name = Ident::new(mod_name, Span::call_site());
let layout_name = Ident::new(layout_name, Span::call_site());
let mod_path = parse_quote!(::crevice::#mod_name);
let trait_path = parse_quote!(#mod_path::#layout_name);
let as_trait_name = format_ident!("As{}", layout_name);
let as_trait_path = parse_quote!(#mod_path::#as_trait_name);
let as_trait_assoc = format_ident!("{}Type", layout_name);
let as_trait_method = format_ident!("as_{}", mod_name);
let from_trait_method = format_ident!("from_{}", mod_name);
let padded_name = format_ident!("{}Padded", layout_name);
Self {
layout_name,
min_struct_alignment,
mod_path,
trait_path,
as_trait_path,
as_trait_assoc,
as_trait_method,
from_trait_method,
padded_name,
}
}
fn emit(&self, input: DeriveInput) -> TokenStream {
let min_struct_alignment = self.min_struct_alignment;
let layout_name = &self.layout_name;
let mod_path = &self.mod_path;
let trait_path = &self.trait_path;
let as_trait_path = &self.as_trait_path;
let as_trait_assoc = &self.as_trait_assoc;
let as_trait_method = &self.as_trait_method;
let from_trait_method = &self.from_trait_method;
let padded_name = &self.padded_name;
let visibility = input.vis;
let name = input.ident;
let generated_name = format_ident!("{}{}", layout_name, name);
let alignment_mod_name = format_ident!("{}{}Alignment", layout_name, name);
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let fields = match &input.data {
Data::Struct(data) => match &data.fields {
Fields::Named(fields) => fields,
Fields::Unnamed(_) => panic!("Tuple structs are not supported"),
Fields::Unit => panic!("Unit structs are not supported"),
},
Data::Enum(_) | Data::Union(_) => panic!("Only structs are supported"),
};
// Generate the names we'll use for calculating alignment of each field.
// Each name will turn into a const fn that's invoked to compute the
// size of a padding array before each field.
let align_names: Vec<_> = fields
.named
.iter()
.map(|field| format_ident!("_{}_align", field.ident.as_ref().unwrap()))
.collect();
// Generate one function per field that is used to apply alignment
// padding. Each function invokes all previous functions to calculate
// the total offset into the struct for the current field, then aligns
// up to the nearest multiple of alignment.
let alignment_calculators: Vec<_> = fields
.named
.iter()
.enumerate()
.map(|(index, field)| {
let align_name = &align_names[index];
let offset_accumulation =
fields
.named
.iter()
.zip(&align_names)
.take(index)
.map(|(field, align_name)| {
let field_ty = &field.ty;
quote! {
offset += #align_name();
offset += ::core::mem::size_of::<<#field_ty as #as_trait_path>::#as_trait_assoc>();
}
});
let pad_at_end = index
.checked_sub(1)
.map_or(quote!{0usize}, |prev_index|{
let field = &fields.named[prev_index];
let field_ty = &field.ty;
quote! {
if <<#field_ty as #as_trait_path>::#as_trait_assoc as #mod_path::#layout_name>::PAD_AT_END {
<<#field_ty as #as_trait_path>::#as_trait_assoc as #mod_path::#layout_name>::ALIGNMENT
}
else {
0usize
}
}
});
let field_ty = &field.ty;
quote! {
pub const fn #align_name() -> usize {
let mut offset = 0;
#( #offset_accumulation )*
::crevice::internal::align_offset(
offset,
::crevice::internal::max(
<<#field_ty as #as_trait_path>::#as_trait_assoc as #mod_path::#layout_name>::ALIGNMENT,
#pad_at_end
)
)
}
}
})
.collect();
// Generate the struct fields that will be present in the generated
// struct. Each field in the original struct turns into two fields in
// the generated struct:
//
// * Alignment, a byte array whose size is computed from #align_name().
// * Data, the layout-specific version of the original field.
let generated_fields: Vec<_> = fields
.named
.iter()
.zip(&align_names)
.map(|(field, align_name)| {
let field_ty = &field.ty;
let field_name = field.ident.as_ref().unwrap();
quote! {
#align_name: [u8; #alignment_mod_name::#align_name()],
#field_name: <#field_ty as #as_trait_path>::#as_trait_assoc,
}
})
.collect();
// Generate an initializer for each field in the original struct.
// Alignment fields are filled in with zeroes using struct update
// syntax.
let field_initializers: Vec<_> = fields
.named
.iter()
.map(|field| {
let field_name = field.ident.as_ref().unwrap();
quote!(#field_name: self.#field_name.#as_trait_method())
})
.collect();
let field_unwrappers: Vec<_> = fields
.named
.iter()
.map(|field|{
let field_name = field.ident.as_ref().unwrap();
let field_ty = &field.ty;
quote!(#field_name: <#field_ty as #as_trait_path>::#from_trait_method(value.#field_name))
})
.collect();
// This fold builds up an expression that finds the maximum alignment out of
// all of the fields in the struct. For this struct:
//
// struct Foo { a: ty1, b: ty2 }
//
// ...we should generate an expression like this:
//
// max(ty2_align, max(ty1_align, min_align))
let struct_alignment = fields.named.iter().fold(
quote!(#min_struct_alignment),
|last, field| {
let field_ty = &field.ty;
quote! {
::crevice::internal::max(
<<#field_ty as #as_trait_path>::#as_trait_assoc as #trait_path>::ALIGNMENT,
#last,
)
}
},
);
quote! {
#[allow(non_snake_case)]
mod #alignment_mod_name {
use super::*;
#( #alignment_calculators )*
}
#[derive(Debug, Clone, Copy)]
#[repr(C)]
#visibility struct #generated_name #ty_generics #where_clause {
#( #generated_fields )*
}
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::#layout_name for #generated_name #ty_generics #where_clause {
const ALIGNMENT: usize = #struct_alignment;
const PAD_AT_END: bool = true;
type Padded = #mod_path::#padded_name<Self, {::crevice::internal::align_offset(
::core::mem::size_of::<#generated_name>(),
#struct_alignment
)}>;
}
impl #impl_generics #as_trait_path for #name #ty_generics #where_clause {
type #as_trait_assoc = #generated_name;
fn #as_trait_method(&self) -> Self::#as_trait_assoc {
Self::#as_trait_assoc {
#( #field_initializers, )*
..::crevice::internal::bytemuck::Zeroable::zeroed()
}
}
fn #from_trait_method(value: Self::#as_trait_assoc) -> Self {
Self {
#( #field_unwrappers, )*
}
}
}
}
}
CompilerTokenStream::from(expanded)
}

View file

@ -0,0 +1,20 @@
[package]
name = "crevice-tests"
version = "0.1.0"
edition = "2018"
[features]
wgpu-validation = ["wgpu", "naga", "futures"]
[dependencies]
crevice = { path = ".." }
crevice-derive = { path = "../crevice-derive", features = ["debug-methods"] }
anyhow = "1.0.44"
bytemuck = "1.7.2"
memoffset = "0.6.4"
mint = "0.5.5"
futures = { version = "0.3.17", features = ["executor"], optional = true }
naga = { version = "0.7.0", features = ["glsl-in", "wgsl-out"], optional = true }
wgpu = { version = "0.11.0", optional = true }

View file

@ -0,0 +1,268 @@
use std::borrow::Cow;
use std::fmt::Debug;
use std::marker::PhantomData;
use crevice::glsl::{Glsl, GlslStruct};
use crevice::std140::{AsStd140, Std140};
use crevice::std430::{AsStd430, Std430};
use futures::executor::block_on;
use wgpu::util::DeviceExt;
const BASE_SHADER: &str = "#version 450
{struct_definition}
layout({layout}, set = 0, binding = 0) readonly buffer INPUT {
{struct_name} in_data;
};
layout({layout}, set = 0, binding = 1) buffer OUTPUT {
{struct_name} out_data;
};
void main() {
out_data = in_data;
}";
pub fn test_round_trip_struct<T: Debug + PartialEq + AsStd140 + AsStd430 + GlslStruct>(value: T) {
let shader_std140 = glsl_shader_for_struct::<T>("std140");
let shader_std430 = glsl_shader_for_struct::<T>("std430");
let context = Context::new();
context.test_round_trip_std140(&shader_std140, &value);
context.test_round_trip_std430(&shader_std430, &value);
}
pub fn test_round_trip_primitive<T: Debug + PartialEq + AsStd140 + AsStd430 + Glsl>(value: T) {
let shader_std140 = glsl_shader_for_primitive::<T>("std140");
let shader_std430 = glsl_shader_for_primitive::<T>("std430");
let context = Context::new();
context.test_round_trip_std140(&shader_std140, &value);
context.test_round_trip_std430(&shader_std430, &value);
}
fn glsl_shader_for_struct<T: GlslStruct>(layout: &str) -> String {
BASE_SHADER
.replace("{struct_name}", T::NAME)
.replace("{struct_definition}", &T::glsl_definition())
.replace("{layout}", layout)
}
fn glsl_shader_for_primitive<T: Glsl>(layout: &str) -> String {
BASE_SHADER
.replace("{struct_name}", T::NAME)
.replace("{struct_definition}", "")
.replace("{layout}", layout)
}
fn compile_glsl(glsl: &str) -> String {
match compile(glsl) {
Ok(shader) => shader,
Err(err) => {
eprintln!("Bad shader: {}", glsl);
panic!("{}", err);
}
}
}
struct Context<T> {
device: wgpu::Device,
queue: wgpu::Queue,
_phantom: PhantomData<*const T>,
}
impl<T> Context<T>
where
T: Debug + PartialEq + AsStd140 + AsStd430 + Glsl,
{
fn new() -> Self {
let (device, queue) = setup();
Self {
device,
queue,
_phantom: PhantomData,
}
}
fn test_round_trip_std140(&self, glsl_shader: &str, value: &T) {
let mut data = Vec::new();
data.extend_from_slice(value.as_std140().as_bytes());
let wgsl_shader = compile_glsl(glsl_shader);
let bytes = self.round_trip(&wgsl_shader, &data);
let std140 = bytemuck::from_bytes::<<T as AsStd140>::Output>(&bytes);
let output = T::from_std140(*std140);
if value != &output {
println!(
"std140 value did not round-trip through wgpu successfully.\n\
Input: {:?}\n\
Output: {:?}\n\n\
GLSL shader:\n{}\n\n\
WGSL shader:\n{}",
value, output, glsl_shader, wgsl_shader,
);
panic!("wgpu round-trip failure for {}", T::NAME);
}
}
fn test_round_trip_std430(&self, glsl_shader: &str, value: &T) {
let mut data = Vec::new();
data.extend_from_slice(value.as_std430().as_bytes());
let wgsl_shader = compile_glsl(glsl_shader);
let bytes = self.round_trip(&wgsl_shader, &data);
let std430 = bytemuck::from_bytes::<<T as AsStd430>::Output>(&bytes);
let output = T::from_std430(*std430);
if value != &output {
println!(
"std430 value did not round-trip through wgpu successfully.\n\
Input: {:?}\n\
Output: {:?}\n\n\
GLSL shader:\n{}\n\n\
WGSL shader:\n{}",
value, output, glsl_shader, wgsl_shader,
);
panic!("wgpu round-trip failure for {}", T::NAME);
}
}
fn round_trip(&self, shader: &str, data: &[u8]) -> Vec<u8> {
let input_buffer = self
.device
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Input Buffer"),
contents: &data,
usage: wgpu::BufferUsages::STORAGE
| wgpu::BufferUsages::COPY_DST
| wgpu::BufferUsages::COPY_SRC,
});
let output_gpu_buffer = self.device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Output Buffer"),
size: data.len() as wgpu::BufferAddress,
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC,
mapped_at_creation: false,
});
let output_cpu_buffer = self.device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Output Buffer"),
size: data.len() as wgpu::BufferAddress,
usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let cs_module = self
.device
.create_shader_module(&wgpu::ShaderModuleDescriptor {
label: None,
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(shader)),
});
let compute_pipeline =
self.device
.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
label: None,
layout: None,
module: &cs_module,
entry_point: "main",
});
let bind_group_layout = compute_pipeline.get_bind_group_layout(0);
let bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
layout: &bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: input_buffer.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 1,
resource: output_gpu_buffer.as_entire_binding(),
},
],
});
let mut encoder = self
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
{
let mut cpass =
encoder.begin_compute_pass(&wgpu::ComputePassDescriptor { label: None });
cpass.set_pipeline(&compute_pipeline);
cpass.set_bind_group(0, &bind_group, &[]);
cpass.dispatch(1, 1, 1);
}
encoder.copy_buffer_to_buffer(
&output_gpu_buffer,
0,
&output_cpu_buffer,
0,
data.len() as wgpu::BufferAddress,
);
self.queue.submit(std::iter::once(encoder.finish()));
let output_slice = output_cpu_buffer.slice(..);
let output_future = output_slice.map_async(wgpu::MapMode::Read);
self.device.poll(wgpu::Maintain::Wait);
block_on(output_future).unwrap();
let output = output_slice.get_mapped_range().to_vec();
output_cpu_buffer.unmap();
output
}
}
fn setup() -> (wgpu::Device, wgpu::Queue) {
let instance = wgpu::Instance::new(wgpu::Backends::all());
let adapter =
block_on(instance.request_adapter(&wgpu::RequestAdapterOptions::default())).unwrap();
println!("Adapter info: {:#?}", adapter.get_info());
block_on(adapter.request_device(
&wgpu::DeviceDescriptor {
label: None,
features: wgpu::Features::empty(),
limits: wgpu::Limits::downlevel_defaults(),
},
None,
))
.unwrap()
}
fn compile(glsl_source: &str) -> anyhow::Result<String> {
let mut parser = naga::front::glsl::Parser::default();
let module = parser
.parse(
&naga::front::glsl::Options {
stage: naga::ShaderStage::Compute,
defines: Default::default(),
},
glsl_source,
)
.map_err(|err| anyhow::format_err!("{:?}", err))?;
let info = naga::valid::Validator::new(
naga::valid::ValidationFlags::default(),
naga::valid::Capabilities::all(),
)
.validate(&module)?;
let wgsl = naga::back::wgsl::write_string(&module, &info)?;
Ok(wgsl)
}

View file

@ -0,0 +1,366 @@
#![cfg(test)]
#[cfg(feature = "wgpu-validation")]
mod gpu;
#[cfg(feature = "wgpu-validation")]
use gpu::{test_round_trip_primitive, test_round_trip_struct};
#[cfg(not(feature = "wgpu-validation"))]
fn test_round_trip_struct<T>(_value: T) {}
#[cfg(not(feature = "wgpu-validation"))]
fn test_round_trip_primitive<T>(_value: T) {}
#[macro_use]
mod util;
use crevice::glsl::GlslStruct;
use crevice::std140::AsStd140;
use crevice::std430::AsStd430;
use mint::{ColumnMatrix2, ColumnMatrix3, ColumnMatrix4, Vector2, Vector3, Vector4};
#[test]
fn two_f32() {
#[derive(Debug, PartialEq, AsStd140, AsStd430, GlslStruct)]
struct TwoF32 {
x: f32,
y: f32,
}
assert_std140!((size = 16, align = 16) TwoF32 {
x: 0,
y: 4,
});
assert_std430!((size = 8, align = 4) TwoF32 {
x: 0,
y: 4,
});
test_round_trip_struct(TwoF32 { x: 5.0, y: 7.0 });
}
#[test]
fn vec2() {
#[derive(Debug, PartialEq, AsStd140, AsStd430, GlslStruct)]
struct UseVec2 {
one: Vector2<f32>,
}
assert_std140!((size = 16, align = 16) UseVec2 {
one: 0,
});
test_round_trip_struct(UseVec2 {
one: [1.0, 2.0].into(),
});
}
#[test]
fn mat2_bare() {
type Mat2 = ColumnMatrix2<f32>;
assert_std140!((size = 32, align = 16) Mat2 {
x: 0,
y: 16,
});
assert_std430!((size = 16, align = 8) Mat2 {
x: 0,
y: 8,
});
// Naga doesn't work with std140 mat2 values.
// https://github.com/gfx-rs/naga/issues/1400
// test_round_trip_primitive(Mat2 {
// x: [1.0, 2.0].into(),
// y: [3.0, 4.0].into(),
// });
}
#[test]
fn mat3_bare() {
type Mat3 = ColumnMatrix3<f32>;
assert_std140!((size = 48, align = 16) Mat3 {
x: 0,
y: 16,
z: 32,
});
// Naga produces invalid HLSL for mat3 value.
// https://github.com/gfx-rs/naga/issues/1466
// test_round_trip_primitive(Mat3 {
// x: [1.0, 2.0, 3.0].into(),
// y: [4.0, 5.0, 6.0].into(),
// z: [7.0, 8.0, 9.0].into(),
// });
}
#[test]
fn mat4_bare() {
type Mat4 = ColumnMatrix4<f32>;
assert_std140!((size = 64, align = 16) Mat4 {
x: 0,
y: 16,
z: 32,
w: 48,
});
test_round_trip_primitive(Mat4 {
x: [1.0, 2.0, 3.0, 4.0].into(),
y: [5.0, 6.0, 7.0, 8.0].into(),
z: [9.0, 10.0, 11.0, 12.0].into(),
w: [13.0, 14.0, 15.0, 16.0].into(),
});
}
#[test]
fn mat3() {
#[derive(Debug, PartialEq, AsStd140, AsStd430, GlslStruct)]
struct TestData {
one: ColumnMatrix3<f32>,
}
// Naga produces invalid HLSL for mat3 value.
// https://github.com/gfx-rs/naga/issues/1466
// test_round_trip_struct(TestData {
// one: [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]].into(),
// });
}
#[test]
fn dvec4() {
#[derive(Debug, PartialEq, AsStd140, AsStd430, GlslStruct)]
struct UsingDVec4 {
doubles: Vector4<f64>,
}
assert_std140!((size = 32, align = 32) UsingDVec4 {
doubles: 0,
});
// Naga does not appear to support doubles.
// https://github.com/gfx-rs/naga/issues/1272
// test_round_trip_struct(UsingDVec4 {
// doubles: [1.0, 2.0, 3.0, 4.0].into(),
// });
}
#[test]
fn four_f64() {
#[derive(Debug, PartialEq, AsStd140, AsStd430, GlslStruct)]
struct FourF64 {
x: f64,
y: f64,
z: f64,
w: f64,
}
assert_std140!((size = 32, align = 16) FourF64 {
x: 0,
y: 8,
z: 16,
w: 24,
});
// Naga does not appear to support doubles.
// https://github.com/gfx-rs/naga/issues/1272
// test_round_trip_struct(FourF64 {
// x: 5.0,
// y: 7.0,
// z: 9.0,
// w: 11.0,
// });
}
#[test]
fn two_vec3() {
#[derive(Debug, PartialEq, AsStd140, AsStd430, GlslStruct)]
struct TwoVec3 {
one: Vector3<f32>,
two: Vector3<f32>,
}
print_std140!(TwoVec3);
print_std430!(TwoVec3);
assert_std140!((size = 32, align = 16) TwoVec3 {
one: 0,
two: 16,
});
assert_std430!((size = 32, align = 16) TwoVec3 {
one: 0,
two: 16,
});
test_round_trip_struct(TwoVec3 {
one: [1.0, 2.0, 3.0].into(),
two: [4.0, 5.0, 6.0].into(),
});
}
#[test]
fn two_vec4() {
#[derive(Debug, PartialEq, AsStd140, AsStd430, GlslStruct)]
struct TwoVec4 {
one: Vector4<f32>,
two: Vector4<f32>,
}
assert_std140!((size = 32, align = 16) TwoVec4 {
one: 0,
two: 16,
});
test_round_trip_struct(TwoVec4 {
one: [1.0, 2.0, 3.0, 4.0].into(),
two: [5.0, 6.0, 7.0, 8.0].into(),
});
}
#[test]
fn vec3_then_f32() {
#[derive(Debug, PartialEq, AsStd140, AsStd430, GlslStruct)]
struct Vec3ThenF32 {
one: Vector3<f32>,
two: f32,
}
assert_std140!((size = 16, align = 16) Vec3ThenF32 {
one: 0,
two: 12,
});
test_round_trip_struct(Vec3ThenF32 {
one: [1.0, 2.0, 3.0].into(),
two: 4.0,
});
}
#[test]
fn mat3_padding() {
#[derive(Debug, PartialEq, AsStd140, AsStd430, GlslStruct)]
struct Mat3Padding {
// Three rows of 16 bytes (3x f32 + 4 bytes padding)
one: mint::ColumnMatrix3<f32>,
two: f32,
}
assert_std140!((size = 64, align = 16) Mat3Padding {
one: 0,
two: 48,
});
// Naga produces invalid HLSL for mat3 value.
// https://github.com/gfx-rs/naga/issues/1466
// test_round_trip_struct(Mat3Padding {
// one: [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]].into(),
// two: 10.0,
// });
}
#[test]
fn padding_after_struct() {
#[derive(AsStd140)]
struct TwoF32 {
x: f32,
}
#[derive(AsStd140)]
struct PaddingAfterStruct {
base_value: TwoF32,
// There should be 8 bytes of padding inserted here.
small_field: f32,
}
assert_std140!((size = 32, align = 16) PaddingAfterStruct {
base_value: 0,
small_field: 16,
});
}
#[test]
fn proper_offset_calculations_for_differing_member_sizes() {
#[derive(AsStd140)]
struct Foo {
x: f32,
}
#[derive(AsStd140)]
struct Bar {
first: Foo,
second: Foo,
}
#[derive(AsStd140)]
struct Outer {
leading: Bar,
trailing: Foo,
}
// Offset Size Contents
// 0 4 Bar.leading.first.x
// 4 12 [padding]
// 16 4 Bar.leading.second.x
// 20 12 [padding]
// 32 4 Bar.trailing.x
// 36 12 [padding]
//
// Total size is 48.
assert_std140!((size = 48, align = 16) Outer {
leading: 0,
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(),
],
})
}

View file

@ -0,0 +1,143 @@
#[macro_export]
macro_rules! print_std140 {
($type:ty) => {
println!(
"{}",
<$type as crevice::std140::AsStd140>::Output::debug_metrics()
);
println!();
println!();
println!(
"{}",
<$type as crevice::std140::AsStd140>::Output::debug_definitions()
);
};
}
#[macro_export]
macro_rules! print_std430 {
($type:ty) => {
println!(
"{}",
<$type as crevice::std430::AsStd430>::Output::debug_metrics()
);
println!();
println!();
println!(
"{}",
<$type as crevice::std430::AsStd430>::Output::debug_definitions()
);
};
}
#[macro_export]
macro_rules! assert_std140 {
((size = $size:literal, align = $align:literal) $struct:ident {
$( $field:ident: $offset:literal, )*
}) => {{
type Target = <$struct as crevice::std140::AsStd140>::Output;
let mut fail = false;
let actual_size = std::mem::size_of::<Target>();
if actual_size != $size {
fail = true;
println!(
"Invalid size for std140 struct {}\n\
Expected: {}\n\
Actual: {}\n",
stringify!($struct),
$size,
actual_size,
);
}
let actual_alignment = <Target as crevice::std140::Std140>::ALIGNMENT;
if actual_alignment != $align {
fail = true;
println!(
"Invalid alignment for std140 struct {}\n\
Expected: {}\n\
Actual: {}\n",
stringify!($struct),
$align,
actual_alignment,
);
}
$({
let actual_offset = memoffset::offset_of!(Target, $field);
if actual_offset != $offset {
fail = true;
println!(
"Invalid offset for field {}\n\
Expected: {}\n\
Actual: {}\n",
stringify!($field),
$offset,
actual_offset,
);
}
})*
if fail {
panic!("Invalid std140 result for {}", stringify!($struct));
}
}};
}
#[macro_export]
macro_rules! assert_std430 {
((size = $size:literal, align = $align:literal) $struct:ident {
$( $field:ident: $offset:literal, )*
}) => {{
type Target = <$struct as crevice::std430::AsStd430>::Output;
let mut fail = false;
let actual_size = std::mem::size_of::<Target>();
if actual_size != $size {
fail = true;
println!(
"Invalid size for std430 struct {}\n\
Expected: {}\n\
Actual: {}\n",
stringify!($struct),
$size,
actual_size,
);
}
let actual_alignment = <Target as crevice::std430::Std430>::ALIGNMENT;
if actual_alignment != $align {
fail = true;
println!(
"Invalid alignment for std430 struct {}\n\
Expected: {}\n\
Actual: {}\n",
stringify!($struct),
$align,
actual_alignment,
);
}
$({
let actual_offset = memoffset::offset_of!(Target, $field);
if actual_offset != $offset {
fail = true;
println!(
"Invalid offset for std430 field {}\n\
Expected: {}\n\
Actual: {}\n",
stringify!($field),
$offset,
actual_offset,
);
}
})*
if fail {
panic!("Invalid std430 result for {}", stringify!($struct));
}
}};
}

View file

@ -1,103 +0,0 @@
use bytemuck::Zeroable;
use crate::std140::{self, AsStd140};
use crate::std430::{self, AsStd430};
macro_rules! glam_vectors {
( $( $glam_ty:ty, $std_name:ident, ( $($field:ident),* ), )* ) => {
$(
impl AsStd140 for $glam_ty {
type Std140Type = std140::$std_name;
fn as_std140(&self) -> Self::Std140Type {
std140::$std_name {
$(
$field: self.$field,
)*
}
}
fn from_std140(value: Self::Std140Type) -> Self {
Self::new($(value.$field,)*)
}
}
impl AsStd430 for $glam_ty {
type Std430Type = std430::$std_name;
fn as_std430(&self) -> Self::Std430Type {
std430::$std_name {
$(
$field: self.$field,
)*
}
}
fn from_std430(value: Self::Std430Type) -> Self {
Self::new($(value.$field,)*)
}
}
)*
};
}
glam_vectors! {
glam::UVec4, UVec4, (x, y, z, w),
glam::Vec2, Vec2, (x, y),
glam::Vec3, Vec3, (x, y, z),
glam::Vec4, Vec4, (x, y, z, w),
}
macro_rules! glam_matrices {
( $( $glam_ty:ty, $std_name:ident, ( $($glam_field:ident),* ), ( $($field:ident),* ))* ) => {
$(
impl AsStd140 for $glam_ty {
type Std140Type = std140::$std_name;
fn as_std140(&self) -> Self::Std140Type {
std140::$std_name {
$(
$field: self.$glam_field.as_std140(),
)*
..Zeroable::zeroed()
}
}
fn from_std140(value: Self::Std140Type) -> Self {
Self::from_cols(
$(
<_ as AsStd140>::from_std140(value.$field),
)*
)
}
}
impl AsStd430 for $glam_ty {
type Std430Type = std430::$std_name;
fn as_std430(&self) -> Self::Std430Type {
std430::$std_name {
$(
$field: self.$glam_field.as_std430(),
)*
..Zeroable::zeroed()
}
}
fn from_std430(value: Self::Std430Type) -> Self {
Self::from_cols(
$(
<_ as AsStd430>::from_std430(value.$field),
)*
)
}
}
)*
};
}
glam_matrices! {
glam::Mat2, Mat2, (x_axis, y_axis), (x, y)
glam::Mat3, Mat3, (x_axis, y_axis, z_axis), (x, y, z)
glam::Mat4, Mat4, (x_axis, y_axis, z_axis, w_axis), (x, y, z, w)
}

View file

@ -0,0 +1,93 @@
//! 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.
pub unsafe trait Glsl {
/// The name of this type in GLSL, like `vec2` or `mat4`.
const 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.
fn enumerate_fields(s: &mut String);
/// Generates GLSL code that represents this struct and its fields.
fn glsl_definition() -> String {
let mut output = String::new();
output.push_str("struct ");
output.push_str(Self::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";
}
unsafe impl Glsl for f64 {
const NAME: &'static str = "double";
}
unsafe impl Glsl for i32 {
const NAME: &'static str = "int";
}
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
crates/crevice/src/imp.rs Normal file
View file

@ -0,0 +1,10 @@
mod imp_mint;
#[cfg(feature = "cgmath")]
mod imp_cgmath;
#[cfg(feature = "glam")]
mod imp_glam;
#[cfg(feature = "nalgebra")]
mod imp_nalgebra;

View file

@ -0,0 +1,30 @@
easy_impl! {
Vec2 cgmath::Vector2<f32> { x, y },
Vec3 cgmath::Vector3<f32> { x, y, z },
Vec4 cgmath::Vector4<f32> { x, y, z, w },
IVec2 cgmath::Vector2<i32> { x, y },
IVec3 cgmath::Vector3<i32> { x, y, z },
IVec4 cgmath::Vector4<i32> { x, y, z, w },
UVec2 cgmath::Vector2<u32> { x, y },
UVec3 cgmath::Vector3<u32> { x, y, z },
UVec4 cgmath::Vector4<u32> { x, y, z, w },
// bool vectors are disabled due to https://github.com/LPGhatguy/crevice/issues/36
// BVec2 cgmath::Vector2<bool> { x, y },
// BVec3 cgmath::Vector3<bool> { x, y, z },
// BVec4 cgmath::Vector4<bool> { x, y, z, w },
DVec2 cgmath::Vector2<f64> { x, y },
DVec3 cgmath::Vector3<f64> { x, y, z },
DVec4 cgmath::Vector4<f64> { x, y, z, w },
Mat2 cgmath::Matrix2<f32> { x, y },
Mat3 cgmath::Matrix3<f32> { x, y, z },
Mat4 cgmath::Matrix4<f32> { x, y, z, w },
DMat2 cgmath::Matrix2<f64> { x, y },
DMat3 cgmath::Matrix3<f64> { x, y, z },
DMat4 cgmath::Matrix4<f64> { x, y, z, w },
}

View file

@ -0,0 +1,24 @@
minty_impl! {
mint::Vector2<f32> => glam::Vec2,
mint::Vector3<f32> => glam::Vec3,
mint::Vector4<f32> => glam::Vec4,
mint::Vector2<i32> => glam::IVec2,
mint::Vector3<i32> => glam::IVec3,
mint::Vector4<i32> => glam::IVec4,
mint::Vector2<u32> => glam::UVec2,
mint::Vector3<u32> => glam::UVec3,
mint::Vector4<u32> => glam::UVec4,
// bool vectors are disabled due to https://github.com/LPGhatguy/crevice/issues/36
// mint::Vector2<bool> => glam::BVec2,
// mint::Vector3<bool> => glam::BVec3,
// mint::Vector4<bool> => glam::BVec4,
mint::Vector2<f64> => glam::DVec2,
mint::Vector3<f64> => glam::DVec3,
mint::Vector4<f64> => glam::DVec4,
mint::ColumnMatrix2<f32> => glam::Mat2,
mint::ColumnMatrix3<f32> => glam::Mat3,
mint::ColumnMatrix4<f32> => glam::Mat4,
mint::ColumnMatrix2<f64> => glam::DMat2,
mint::ColumnMatrix3<f64> => glam::DMat3,
mint::ColumnMatrix4<f64> => glam::DMat4,
}

View file

@ -0,0 +1,30 @@
easy_impl! {
Vec2 mint::Vector2<f32> { x, y },
Vec3 mint::Vector3<f32> { x, y, z },
Vec4 mint::Vector4<f32> { x, y, z, w },
IVec2 mint::Vector2<i32> { x, y },
IVec3 mint::Vector3<i32> { x, y, z },
IVec4 mint::Vector4<i32> { x, y, z, w },
UVec2 mint::Vector2<u32> { x, y },
UVec3 mint::Vector3<u32> { x, y, z },
UVec4 mint::Vector4<u32> { x, y, z, w },
// bool vectors are disabled due to https://github.com/LPGhatguy/crevice/issues/36
// BVec2 mint::Vector2<bool> { x, y },
// BVec3 mint::Vector3<bool> { x, y, z },
// BVec4 mint::Vector4<bool> { x, y, z, w },
DVec2 mint::Vector2<f64> { x, y },
DVec3 mint::Vector3<f64> { x, y, z },
DVec4 mint::Vector4<f64> { x, y, z, w },
Mat2 mint::ColumnMatrix2<f32> { x, y },
Mat3 mint::ColumnMatrix3<f32> { x, y, z },
Mat4 mint::ColumnMatrix4<f32> { x, y, z, w },
DMat2 mint::ColumnMatrix2<f64> { x, y },
DMat3 mint::ColumnMatrix3<f64> { x, y, z },
DMat4 mint::ColumnMatrix4<f64> { x, y, z, w },
}

View file

@ -0,0 +1,24 @@
minty_impl! {
mint::Vector2<f32> => nalgebra::Vector2<f32>,
mint::Vector3<f32> => nalgebra::Vector3<f32>,
mint::Vector4<f32> => nalgebra::Vector4<f32>,
mint::Vector2<i32> => nalgebra::Vector2<i32>,
mint::Vector3<i32> => nalgebra::Vector3<i32>,
mint::Vector4<i32> => nalgebra::Vector4<i32>,
mint::Vector2<u32> => nalgebra::Vector2<u32>,
mint::Vector3<u32> => nalgebra::Vector3<u32>,
mint::Vector4<u32> => nalgebra::Vector4<u32>,
// bool vectors are disabled due to https://github.com/LPGhatguy/crevice/issues/36
// mint::Vector2<bool> => nalgebra::Vector2<bool>,
// mint::Vector3<bool> => nalgebra::Vector3<bool>,
// mint::Vector4<bool> => nalgebra::Vector4<bool>,
mint::Vector2<f64> => nalgebra::Vector2<f64>,
mint::Vector3<f64> => nalgebra::Vector3<f64>,
mint::Vector4<f64> => nalgebra::Vector4<f64>,
mint::ColumnMatrix2<f32> => nalgebra::Matrix2<f32>,
mint::ColumnMatrix3<f32> => nalgebra::Matrix3<f32>,
mint::ColumnMatrix4<f32> => nalgebra::Matrix4<f32>,
mint::ColumnMatrix2<f64> => nalgebra::Matrix2<f64>,
mint::ColumnMatrix3<f64> => nalgebra::Matrix3<f64>,
mint::ColumnMatrix4<f64> => nalgebra::Matrix4<f64>,
}

View file

@ -3,9 +3,9 @@
pub use bytemuck;
/// Align the given struct offset up to the given alignment.
/// Gives the number of bytes needed to make `offset` be aligned to `alignment`.
pub const fn align_offset(offset: usize, alignment: usize) -> usize {
if offset % alignment == 0 {
if alignment == 0 || offset % alignment == 0 {
0
} else {
alignment - offset % alignment
@ -21,3 +21,20 @@ pub const fn max(a: usize, b: usize) -> usize {
b
}
}
/// Max of an array of `usize`. This function's implementation is funky because
/// we have no for loops!
pub const fn max_arr<const N: usize>(input: [usize; N]) -> usize {
let mut max = 0;
let mut i = 0;
while i < N {
if input[i] > max {
max = input[i];
}
i += 1;
}
max
}

View file

@ -1,5 +1,9 @@
#![allow(clippy::all)]
#![allow(
clippy::new_without_default,
clippy::needless_update,
clippy::len_without_is_empty,
clippy::needless_range_loop
)]
/*!
[![GitHub CI Status](https://github.com/LPGhatguy/crevice/workflows/CI/badge.svg)](https://github.com/LPGhatguy/crevice/actions)
[![crevice on crates.io](https://img.shields.io/crates/v/crevice.svg)](https://crates.io/crates/crevice)
@ -12,19 +16,34 @@ method to allow safely packing data into buffers for uploading.
Generated structs also implement [`bytemuck::Zeroable`] and
[`bytemuck::Pod`] for use with other libraries.
Crevice is similar to [`glsl-layout`][glsl-layout], but supports `mint` types
and explicitly initializes padding to remove one source of undefined behavior.
Crevice is similar to [`glsl-layout`][glsl-layout], but supports types from many
math crates, can generate GLSL source from structs, and explicitly initializes
padding to remove one source of undefined behavior.
Examples in this crate use cgmath, but any math crate that works with the mint
crate will also work. Some other crates include nalgebra, ultraviolet, glam, and
vek.
Crevice has support for many Rust math libraries via feature flags, and most
other math libraries by use of the mint crate. Crevice currently supports:
* mint 0.5, enabled by default
* cgmath 0.18, using the `cgmath` feature
* nalgebra 0.29, using the `nalgebra` feature
* glam 0.19, using the `glam` feature
PRs are welcome to add or update math libraries to Crevice.
If your math library is not supported, it's possible to define structs using the
types from mint and convert your math library's types into mint types. This is
supported by most Rust math libraries.
Your math library may require you to turn on a feature flag to get mint support.
For example, cgmath requires the "mint" feature to be enabled to allow
conversions to and from mint types.
## Examples
### Single Value
Uploading many types can be done by deriving `AsStd140` and using
[`as_std140`][std140::AsStd140::as_std140] and
Uploading many types can be done by deriving [`AsStd140`][std140::AsStd140] and
using [`as_std140`][std140::AsStd140::as_std140] and
[`as_bytes`][std140::Std140::as_bytes] to turn the result into bytes.
```glsl
@ -35,10 +54,8 @@ uniform MAIN {
} main;
```
```skip
```rust
use crevice::std140::{AsStd140, Std140};
use cgmath::prelude::*;
use cgmath::{Matrix3, Vector3};
#[derive(AsStd140)]
struct MainUniform {
@ -48,8 +65,12 @@ struct MainUniform {
}
let value = MainUniform {
orientation: Matrix3::identity().into(),
position: Vector3::new(1.0, 2.0, 3.0).into(),
orientation: [
[1.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, 0.0, 1.0],
].into(),
position: [1.0, 2.0, 3.0].into(),
scale: 4.0,
};
@ -61,7 +82,8 @@ upload_data_to_gpu(value_std140.as_bytes());
### Sequential Types
More complicated data can be uploaded using the std140 `Writer` type.
More complicated data can be uploaded using the std140
[`Writer`][std140::Writer] type.
```glsl
struct PointLight {
@ -76,7 +98,7 @@ buffer POINT_LIGHTS {
} point_lights;
```
```skip
```rust
use crevice::std140::{self, AsStd140};
#[derive(AsStd140)]
@ -120,25 +142,31 @@ unmap_gpu_buffer();
# Ok::<(), std::io::Error>(())
```
## Features
* `std` (default): Enables [`std::io::Write`]-based structs.
* `cgmath`: Enables support for types from cgmath.
* `nalgebra`: Enables support for types from nalgebra.
* `glam`: Enables support for types from glam.
## Minimum Supported Rust Version (MSRV)
Crevice supports Rust 1.46.0 and newer due to use of new `const fn` features.
Crevice supports Rust 1.52.1 and newer due to use of new `const fn` features.
[glsl-layout]: https://github.com/rustgd/glsl-layout
[Zeroable]: https://docs.rs/bytemuck/latest/bytemuck/trait.Zeroable.html
[Pod]: https://docs.rs/bytemuck/latest/bytemuck/trait.Pod.html
[TypeLayout]: https://docs.rs/type-layout/latest/type_layout/trait.TypeLayout.html
*/
#![deny(missing_docs)]
#![cfg_attr(not(feature = "std"), no_std)]
#[macro_use]
mod util;
pub mod glsl;
pub mod std140;
pub mod std430;
#[doc(hidden)]
pub mod internal;
mod mint;
mod glam;
mod imp;

View file

@ -1,130 +0,0 @@
use bytemuck::Zeroable;
use crate::std140::{self, AsStd140};
use crate::std430::{self, AsStd430};
macro_rules! mint_vectors {
( $( $mint_ty:ty, $std_name:ident, ( $($field:ident),* ), )* ) => {
$(
impl AsStd140 for $mint_ty {
type Std140Type = std140::$std_name;
fn as_std140(&self) -> Self::Std140Type {
std140::$std_name {
$(
$field: self.$field,
)*
}
}
fn from_std140(value: Self::Std140Type) -> Self {
Self {
$(
$field: value.$field,
)*
}
}
}
impl AsStd430 for $mint_ty {
type Std430Type = std430::$std_name;
fn as_std430(&self) -> Self::Std430Type {
std430::$std_name {
$(
$field: self.$field,
)*
}
}
fn from_std430(value: Self::Std430Type) -> Self {
Self {
$(
$field: value.$field,
)*
}
}
}
)*
};
}
mint_vectors! {
mint::Vector2<f32>, Vec2, (x, y),
mint::Vector3<f32>, Vec3, (x, y, z),
mint::Vector4<f32>, Vec4, (x, y, z, w),
mint::Vector2<i32>, IVec2, (x, y),
mint::Vector3<i32>, IVec3, (x, y, z),
mint::Vector4<i32>, IVec4, (x, y, z, w),
mint::Vector2<u32>, UVec2, (x, y),
mint::Vector3<u32>, UVec3, (x, y, z),
mint::Vector4<u32>, UVec4, (x, y, z, w),
mint::Vector2<bool>, BVec2, (x, y),
mint::Vector3<bool>, BVec3, (x, y, z),
mint::Vector4<bool>, BVec4, (x, y, z, w),
mint::Vector2<f64>, DVec2, (x, y),
mint::Vector3<f64>, DVec3, (x, y, z),
mint::Vector4<f64>, DVec4, (x, y, z, w),
}
macro_rules! mint_matrices {
( $( $mint_ty:ty, $std_name:ident, ( $($field:ident),* ), )* ) => {
$(
impl AsStd140 for $mint_ty {
type Std140Type = std140::$std_name;
fn as_std140(&self) -> Self::Std140Type {
std140::$std_name {
$(
$field: self.$field.as_std140(),
)*
..Zeroable::zeroed()
}
}
fn from_std140(value: Self::Std140Type) -> Self {
Self {
$(
$field: <_ as AsStd140>::from_std140(value.$field),
)*
}
}
}
impl AsStd430 for $mint_ty {
type Std430Type = std430::$std_name;
fn as_std430(&self) -> Self::Std430Type {
std430::$std_name {
$(
$field: self.$field.as_std430(),
)*
..Zeroable::zeroed()
}
}
fn from_std430(value: Self::Std430Type) -> Self {
Self {
$(
$field: <_ as AsStd430>::from_std430(value.$field),
)*
}
}
}
)*
};
}
mint_matrices! {
mint::ColumnMatrix2<f32>, Mat2, (x, y),
mint::ColumnMatrix3<f32>, Mat3, (x, y, z),
mint::ColumnMatrix4<f32>, Mat4, (x, y, z, w),
mint::ColumnMatrix2<f64>, DMat2, (x, y),
mint::ColumnMatrix3<f64>, DMat3, (x, y, z),
mint::ColumnMatrix4<f64>, DMat4, (x, y, z, w),
}

View file

@ -11,13 +11,13 @@ use crate::std140::{AsStd140, Std140};
pub struct DynamicUniform<T>(pub T);
impl<T: AsStd140> AsStd140 for DynamicUniform<T> {
type Std140Type = DynamicUniformStd140<<T as AsStd140>::Std140Type>;
type Output = DynamicUniformStd140<<T as AsStd140>::Output>;
fn as_std140(&self) -> Self::Std140Type {
fn as_std140(&self) -> Self::Output {
DynamicUniformStd140(self.0.as_std140())
}
fn from_std140(value: Self::Std140Type) -> Self {
fn from_std140(value: Self::Output) -> Self {
DynamicUniform(<T as AsStd140>::from_std140(value.0))
}
}

View file

@ -1,5 +1,6 @@
use bytemuck::{Pod, Zeroable};
use crate::glsl::Glsl;
use crate::std140::{Std140, Std140Padded};
use crate::internal::{align_offset, max};
@ -28,13 +29,14 @@ unsafe impl Std140 for u32 {
macro_rules! vectors {
(
$(
#[$doc:meta] align($align:literal) $name:ident <$prim:ident> ($($field:ident),+)
#[$doc:meta] align($align:literal) $glsl_name:ident $name:ident <$prim:ident> ($($field:ident),+)
)+
) => {
$(
#[$doc]
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(C)]
pub struct $name {
$(pub $field: $prim,)+
}
@ -46,30 +48,36 @@ macro_rules! vectors {
const ALIGNMENT: usize = $align;
type Padded = Std140Padded<Self, {align_offset(size_of::<$name>(), max(16, $align))}>;
}
unsafe impl Glsl for $name {
const NAME: &'static str = stringify!($glsl_name);
}
)+
};
}
vectors! {
#[doc = "Corresponds to a GLSL `vec2` in std140 layout."] align(8) Vec2<f32>(x, y)
#[doc = "Corresponds to a GLSL `vec3` in std140 layout."] align(16) Vec3<f32>(x, y, z)
#[doc = "Corresponds to a GLSL `vec4` in std140 layout."] align(16) Vec4<f32>(x, y, z, w)
#[doc = "Corresponds to a GLSL `vec2` in std140 layout."] align(8) vec2 Vec2<f32>(x, y)
#[doc = "Corresponds to a GLSL `vec3` in std140 layout."] align(16) vec3 Vec3<f32>(x, y, z)
#[doc = "Corresponds to a GLSL `vec4` in std140 layout."] align(16) vec4 Vec4<f32>(x, y, z, w)
#[doc = "Corresponds to a GLSL `ivec2` in std140 layout."] align(8) IVec2<i32>(x, y)
#[doc = "Corresponds to a GLSL `ivec3` in std140 layout."] align(16) IVec3<i32>(x, y, z)
#[doc = "Corresponds to a GLSL `ivec4` in std140 layout."] align(16) IVec4<i32>(x, y, z, w)
#[doc = "Corresponds to a GLSL `ivec2` in std140 layout."] align(8) ivec2 IVec2<i32>(x, y)
#[doc = "Corresponds to a GLSL `ivec3` in std140 layout."] align(16) ivec3 IVec3<i32>(x, y, z)
#[doc = "Corresponds to a GLSL `ivec4` in std140 layout."] align(16) ivec4 IVec4<i32>(x, y, z, w)
#[doc = "Corresponds to a GLSL `uvec2` in std140 layout."] align(8) UVec2<u32>(x, y)
#[doc = "Corresponds to a GLSL `uvec3` in std140 layout."] align(16) UVec3<u32>(x, y, z)
#[doc = "Corresponds to a GLSL `uvec4` in std140 layout."] align(16) UVec4<u32>(x, y, z, w)
#[doc = "Corresponds to a GLSL `uvec2` in std140 layout."] align(8) uvec2 UVec2<u32>(x, y)
#[doc = "Corresponds to a GLSL `uvec3` in std140 layout."] align(16) uvec3 UVec3<u32>(x, y, z)
#[doc = "Corresponds to a GLSL `uvec4` in std140 layout."] align(16) uvec4 UVec4<u32>(x, y, z, w)
#[doc = "Corresponds to a GLSL `bvec2` in std140 layout."] align(8) BVec2<bool>(x, y)
#[doc = "Corresponds to a GLSL `bvec3` in std140 layout."] align(16) BVec3<bool>(x, y, z)
#[doc = "Corresponds to a GLSL `bvec4` in std140 layout."] align(16) BVec4<bool>(x, y, z, w)
// bool vectors are disabled due to https://github.com/LPGhatguy/crevice/issues/36
#[doc = "Corresponds to a GLSL `dvec2` in std140 layout."] align(16) DVec2<f64>(x, y)
#[doc = "Corresponds to a GLSL `dvec3` in std140 layout."] align(32) DVec3<f64>(x, y, z)
#[doc = "Corresponds to a GLSL `dvec4` in std140 layout."] align(32) DVec4<f64>(x, y, z, w)
// #[doc = "Corresponds to a GLSL `bvec2` in std140 layout."] align(8) bvec2 BVec2<bool>(x, y)
// #[doc = "Corresponds to a GLSL `bvec3` in std140 layout."] align(16) bvec3 BVec3<bool>(x, y, z)
// #[doc = "Corresponds to a GLSL `bvec4` in std140 layout."] align(16) bvec4 BVec4<bool>(x, y, z, w)
#[doc = "Corresponds to a GLSL `dvec2` in std140 layout."] align(16) dvec2 DVec2<f64>(x, y)
#[doc = "Corresponds to a GLSL `dvec3` in std140 layout."] align(32) dvec3 DVec3<f64>(x, y, z)
#[doc = "Corresponds to a GLSL `dvec4` in std140 layout."] align(32) dvec4 DVec4<f64>(x, y, z, w)
}
macro_rules! matrices {
@ -77,7 +85,7 @@ macro_rules! matrices {
$(
#[$doc:meta]
align($align:literal)
$name:ident {
$glsl_name:ident $name:ident {
$($field:ident: $field_ty:ty,)+
}
)+
@ -86,6 +94,7 @@ macro_rules! matrices {
#[$doc]
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct $name {
$(pub $field: $field_ty,)+
}
@ -99,6 +108,10 @@ macro_rules! matrices {
const PAD_AT_END: bool = true;
type Padded = Std140Padded<Self, {align_offset(size_of::<$name>(), max(16, $align))}>;
}
unsafe impl Glsl for $name {
const NAME: &'static str = stringify!($glsl_name);
}
)+
};
}
@ -106,7 +119,7 @@ macro_rules! matrices {
matrices! {
#[doc = "Corresponds to a GLSL `mat2` in std140 layout."]
align(16)
Mat2 {
mat2 Mat2 {
x: Vec2,
_pad_x: [f32; 2],
y: Vec2,
@ -115,7 +128,7 @@ matrices! {
#[doc = "Corresponds to a GLSL `mat3` in std140 layout."]
align(16)
Mat3 {
mat3 Mat3 {
x: Vec3,
_pad_x: f32,
y: Vec3,
@ -126,7 +139,7 @@ matrices! {
#[doc = "Corresponds to a GLSL `mat4` in std140 layout."]
align(16)
Mat4 {
mat4 Mat4 {
x: Vec4,
y: Vec4,
z: Vec4,
@ -135,14 +148,14 @@ matrices! {
#[doc = "Corresponds to a GLSL `dmat2` in std140 layout."]
align(16)
DMat2 {
dmat2 DMat2 {
x: DVec2,
y: DVec2,
}
#[doc = "Corresponds to a GLSL `dmat3` in std140 layout."]
align(32)
DMat3 {
dmat3 DMat3 {
x: DVec3,
_pad_x: f64,
y: DVec3,
@ -153,7 +166,7 @@ matrices! {
#[doc = "Corresponds to a GLSL `dmat3` in std140 layout."]
align(32)
DMat4 {
dmat4 DMat4 {
x: DVec4,
y: DVec4,
z: DVec4,

View file

@ -61,8 +61,8 @@ impl Sizer {
where
T: AsStd140,
{
let size = size_of::<<T as AsStd140>::Std140Type>();
let alignment = <T as AsStd140>::Std140Type::ALIGNMENT;
let size = size_of::<<T as AsStd140>::Output>();
let alignment = <T as AsStd140>::Output::ALIGNMENT;
let padding = align_offset(self.offset, alignment);
self.offset += padding;

View file

@ -17,9 +17,10 @@ pub unsafe trait Std140: Copy + Zeroable + Pod {
/// control and zero their padding bytes, making converting them to and from
/// slices safe.
const ALIGNMENT: usize;
/// Whether this type requires a padding at the end (ie, is a struct or an array
/// of primitives).
/// See https://www.khronos.org/registry/OpenGL/specs/gl/glspec45.core.pdf#page=159
/// See <https://www.khronos.org/registry/OpenGL/specs/gl/glspec45.core.pdf#page=159>
/// (rule 4 and 9)
const PAD_AT_END: bool = false;
/// Padded type (Std140Padded specialization)
@ -75,7 +76,7 @@ struct which contains only fields that also implement `AsStd140` can derive
`AsStd140`.
Types from the mint crate implement `AsStd140`, making them convenient for use
in uniform types. Most Rust geometry crates, like cgmath, nalgebra, and
in uniform types. Most Rust math crates, like cgmath, nalgebra, and
ultraviolet support mint.
## Example
@ -87,9 +88,7 @@ uniform CAMERA {
} camera;
```
```skip
use cgmath::prelude::*;
use cgmath::{Matrix4, Deg, perspective};
```no_run
use crevice::std140::{AsStd140, Std140};
#[derive(AsStd140)]
@ -98,9 +97,12 @@ struct CameraUniform {
projection: mint::ColumnMatrix4<f32>,
}
let view: mint::ColumnMatrix4<f32> = todo!("your math code here");
let projection: mint::ColumnMatrix4<f32> = todo!("your math code here");
let camera = CameraUniform {
view: Matrix4::identity().into(),
projection: perspective(Deg(60.0), 16.0/9.0, 0.01, 100.0).into(),
view,
projection,
};
# fn write_to_gpu_buffer(bytes: &[u8]) {}
@ -110,26 +112,26 @@ write_to_gpu_buffer(camera_std140.as_bytes());
*/
pub trait AsStd140 {
/// The `std140` version of this value.
type Std140Type: Std140;
type Output: Std140;
/// Convert this value into the `std140` version of itself.
fn as_std140(&self) -> Self::Std140Type;
fn as_std140(&self) -> Self::Output;
/// Returns the size of the `std140` version of this type. Useful for
/// pre-sizing buffers.
fn std140_size_static() -> usize {
size_of::<Self::Std140Type>()
size_of::<Self::Output>()
}
/// Converts from `std140` version of self to self.
fn from_std140(val: Self::Std140Type) -> Self;
fn from_std140(val: Self::Output) -> Self;
}
impl<T> AsStd140 for T
where
T: Std140,
{
type Std140Type = Self;
type Output = Self;
fn as_std140(&self) -> Self {
*self
@ -178,29 +180,36 @@ where
type Padded = Self;
}
impl<T: Std140, const N: usize> Std140Array<T, N> {
fn uninit_array() -> [MaybeUninit<T::Padded>; N] {
unsafe { MaybeUninit::uninit().assume_init() }
}
fn from_uninit_array(a: [MaybeUninit<T::Padded>; N]) -> Self {
unsafe { core::mem::transmute_copy(&a) }
}
}
impl<T: AsStd140, const N: usize> AsStd140 for [T; N]
where
<T::Std140Type as Std140>::Padded: Pod,
<T::Output as Std140>::Padded: Pod,
{
type Std140Type = Std140Array<T::Std140Type, N>;
fn as_std140(&self) -> Self::Std140Type {
let mut res: [MaybeUninit<<T::Std140Type as Std140>::Padded>; N] =
unsafe { MaybeUninit::uninit().assume_init() };
type Output = Std140Array<T::Output, N>;
fn as_std140(&self) -> Self::Output {
let mut res = Self::Output::uninit_array();
for i in 0..N {
res[i] = MaybeUninit::new(Std140Convertible::from_std140(self[i].as_std140()));
}
unsafe { core::mem::transmute_copy(&res) }
Self::Output::from_uninit_array(res)
}
fn from_std140(val: Self::Std140Type) -> Self {
fn from_std140(val: Self::Output) -> Self {
let mut res: [MaybeUninit<T>; N] = unsafe { MaybeUninit::uninit().assume_init() };
for i in 0..N {
res[i] = MaybeUninit::new(AsStd140::from_std140(val.0[i].into_std140()));
res[i] = MaybeUninit::new(T::from_std140(Std140Convertible::into_std140(val.0[i])));
}
unsafe { core::mem::transmute_copy(&res) }
}
}
@ -241,7 +250,7 @@ where
}
fn std140_size(&self) -> usize {
size_of::<<Self as AsStd140>::Std140Type>()
size_of::<<Self as AsStd140>::Output>()
}
}

View file

@ -1,5 +1,6 @@
use bytemuck::{Pod, Zeroable};
use crate::glsl::Glsl;
use crate::std430::{Std430, Std430Padded};
use crate::internal::align_offset;
@ -28,13 +29,14 @@ unsafe impl Std430 for u32 {
macro_rules! vectors {
(
$(
#[$doc:meta] align($align:literal) $name:ident <$prim:ident> ($($field:ident),+)
#[$doc:meta] align($align:literal) $glsl_name:ident $name:ident <$prim:ident> ($($field:ident),+)
)+
) => {
$(
#[$doc]
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct $name {
$(pub $field: $prim,)+
}
@ -46,30 +48,36 @@ macro_rules! vectors {
const ALIGNMENT: usize = $align;
type Padded = Std430Padded<Self, {align_offset(size_of::<$name>(), $align)}>;
}
unsafe impl Glsl for $name {
const NAME: &'static str = stringify!($glsl_name);
}
)+
};
}
vectors! {
#[doc = "Corresponds to a GLSL `vec2` in std430 layout."] align(8) Vec2<f32>(x, y)
#[doc = "Corresponds to a GLSL `vec3` in std430 layout."] align(16) Vec3<f32>(x, y, z)
#[doc = "Corresponds to a GLSL `vec4` in std430 layout."] align(16) Vec4<f32>(x, y, z, w)
#[doc = "Corresponds to a GLSL `vec2` in std430 layout."] align(8) vec2 Vec2<f32>(x, y)
#[doc = "Corresponds to a GLSL `vec3` in std430 layout."] align(16) vec3 Vec3<f32>(x, y, z)
#[doc = "Corresponds to a GLSL `vec4` in std430 layout."] align(16) vec4 Vec4<f32>(x, y, z, w)
#[doc = "Corresponds to a GLSL `ivec2` in std140 layout."] align(8) IVec2<i32>(x, y)
#[doc = "Corresponds to a GLSL `ivec3` in std140 layout."] align(16) IVec3<i32>(x, y, z)
#[doc = "Corresponds to a GLSL `ivec4` in std140 layout."] align(16) IVec4<i32>(x, y, z, w)
#[doc = "Corresponds to a GLSL `ivec2` in std430 layout."] align(8) ivec2 IVec2<i32>(x, y)
#[doc = "Corresponds to a GLSL `ivec3` in std430 layout."] align(16) ivec3 IVec3<i32>(x, y, z)
#[doc = "Corresponds to a GLSL `ivec4` in std430 layout."] align(16) ivec4 IVec4<i32>(x, y, z, w)
#[doc = "Corresponds to a GLSL `uvec2` in std140 layout."] align(8) UVec2<u32>(x, y)
#[doc = "Corresponds to a GLSL `uvec3` in std140 layout."] align(16) UVec3<u32>(x, y, z)
#[doc = "Corresponds to a GLSL `uvec4` in std140 layout."] align(16) UVec4<u32>(x, y, z, w)
#[doc = "Corresponds to a GLSL `uvec2` in std430 layout."] align(8) uvec2 UVec2<u32>(x, y)
#[doc = "Corresponds to a GLSL `uvec3` in std430 layout."] align(16) uvec3 UVec3<u32>(x, y, z)
#[doc = "Corresponds to a GLSL `uvec4` in std430 layout."] align(16) uvec4 UVec4<u32>(x, y, z, w)
#[doc = "Corresponds to a GLSL `bvec2` in std140 layout."] align(8) BVec2<bool>(x, y)
#[doc = "Corresponds to a GLSL `bvec3` in std140 layout."] align(16) BVec3<bool>(x, y, z)
#[doc = "Corresponds to a GLSL `bvec4` in std140 layout."] align(16) BVec4<bool>(x, y, z, w)
// bool vectors are disabled due to https://github.com/LPGhatguy/crevice/issues/36
#[doc = "Corresponds to a GLSL `dvec2` in std430 layout."] align(16) DVec2<f64>(x, y)
#[doc = "Corresponds to a GLSL `dvec3` in std430 layout."] align(32) DVec3<f64>(x, y, z)
#[doc = "Corresponds to a GLSL `dvec4` in std430 layout."] align(32) DVec4<f64>(x, y, z, w)
// #[doc = "Corresponds to a GLSL `bvec2` in std430 layout."] align(8) bvec2 BVec2<bool>(x, y)
// #[doc = "Corresponds to a GLSL `bvec3` in std430 layout."] align(16) bvec3 BVec3<bool>(x, y, z)
// #[doc = "Corresponds to a GLSL `bvec4` in std430 layout."] align(16) bvec4 BVec4<bool>(x, y, z, w)
#[doc = "Corresponds to a GLSL `dvec2` in std430 layout."] align(16) dvec2 DVec2<f64>(x, y)
#[doc = "Corresponds to a GLSL `dvec3` in std430 layout."] align(32) dvec3 DVec3<f64>(x, y, z)
#[doc = "Corresponds to a GLSL `dvec4` in std430 layout."] align(32) dvec4 DVec4<f64>(x, y, z, w)
}
macro_rules! matrices {
@ -77,7 +85,7 @@ macro_rules! matrices {
$(
#[$doc:meta]
align($align:literal)
$name:ident {
$glsl_name:ident $name:ident {
$($field:ident: $field_ty:ty,)+
}
)+
@ -86,6 +94,7 @@ macro_rules! matrices {
#[$doc]
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct $name {
$(pub $field: $field_ty,)+
}
@ -99,29 +108,36 @@ macro_rules! matrices {
const PAD_AT_END: bool = true;
type Padded = Std430Padded<Self, {align_offset(size_of::<$name>(), $align)}>;
}
unsafe impl Glsl for $name {
const NAME: &'static str = stringify!($glsl_name);
}
)+
};
}
matrices! {
#[doc = "Corresponds to a GLSL `mat2` in std430 layout."]
align(16)
Mat2 {
align(8)
mat2 Mat2 {
x: Vec2,
y: Vec2,
}
#[doc = "Corresponds to a GLSL `mat3` in std430 layout."]
align(16)
Mat3 {
mat3 Mat3 {
x: Vec3,
_pad_x: f32,
y: Vec3,
_pad_y: f32,
z: Vec3,
_pad_z: f32,
}
#[doc = "Corresponds to a GLSL `mat4` in std430 layout."]
align(16)
Mat4 {
mat4 Mat4 {
x: Vec4,
y: Vec4,
z: Vec4,
@ -130,22 +146,25 @@ matrices! {
#[doc = "Corresponds to a GLSL `dmat2` in std430 layout."]
align(16)
DMat2 {
dmat2 DMat2 {
x: DVec2,
y: DVec2,
}
#[doc = "Corresponds to a GLSL `dmat3` in std430 layout."]
align(32)
DMat3 {
dmat3 DMat3 {
x: DVec3,
_pad_x: f64,
y: DVec3,
_pad_y: f64,
z: DVec3,
_pad_z: f64,
}
#[doc = "Corresponds to a GLSL `dmat3` in std430 layout."]
align(32)
DMat4 {
dmat4 DMat4 {
x: DVec4,
y: DVec4,
z: DVec4,

View file

@ -61,8 +61,8 @@ impl Sizer {
where
T: AsStd430,
{
let size = size_of::<<T as AsStd430>::Std430Type>();
let alignment = <T as AsStd430>::Std430Type::ALIGNMENT;
let size = size_of::<<T as AsStd430>::Output>();
let alignment = <T as AsStd430>::Output::ALIGNMENT;
let padding = align_offset(self.offset, alignment);
self.offset += padding;

View file

@ -17,9 +17,10 @@ pub unsafe trait Std430: Copy + Zeroable + Pod {
/// control and zero their padding bytes, making converting them to and from
/// slices safe.
const ALIGNMENT: usize;
/// Whether this type requires a padding at the end (ie, is a struct or an array
/// of primitives).
/// See https://www.khronos.org/registry/OpenGL/specs/gl/glspec45.core.pdf#page=159
/// See <https://www.khronos.org/registry/OpenGL/specs/gl/glspec45.core.pdf#page=159>
/// (rule 4 and 9)
const PAD_AT_END: bool = false;
/// Padded type (Std430Padded specialization)
@ -87,9 +88,7 @@ uniform CAMERA {
} camera;
```
```skip
use cgmath::prelude::*;
use cgmath::{Matrix4, Deg, perspective};
```no_run
use crevice::std430::{AsStd430, Std430};
#[derive(AsStd430)]
@ -98,9 +97,12 @@ struct CameraUniform {
projection: mint::ColumnMatrix4<f32>,
}
let view: mint::ColumnMatrix4<f32> = todo!("your math code here");
let projection: mint::ColumnMatrix4<f32> = todo!("your math code here");
let camera = CameraUniform {
view: Matrix4::identity().into(),
projection: perspective(Deg(60.0), 16.0/9.0, 0.01, 100.0).into(),
view,
projection,
};
# fn write_to_gpu_buffer(bytes: &[u8]) {}
@ -110,26 +112,26 @@ write_to_gpu_buffer(camera_std430.as_bytes());
*/
pub trait AsStd430 {
/// The `std430` version of this value.
type Std430Type: Std430;
type Output: Std430;
/// Convert this value into the `std430` version of itself.
fn as_std430(&self) -> Self::Std430Type;
fn as_std430(&self) -> Self::Output;
/// Returns the size of the `std430` version of this type. Useful for
/// pre-sizing buffers.
fn std430_size_static() -> usize {
size_of::<Self::Std430Type>()
size_of::<Self::Output>()
}
/// Converts from `std430` version of self to self.
fn from_std430(value: Self::Std430Type) -> Self;
fn from_std430(value: Self::Output) -> Self;
}
impl<T> AsStd430 for T
where
T: Std430,
{
type Std430Type = Self;
type Output = Self;
fn as_std430(&self) -> Self {
*self
@ -178,29 +180,36 @@ where
type Padded = Self;
}
impl<T: Std430, const N: usize> Std430Array<T, N> {
fn uninit_array() -> [MaybeUninit<T::Padded>; N] {
unsafe { MaybeUninit::uninit().assume_init() }
}
fn from_uninit_array(a: [MaybeUninit<T::Padded>; N]) -> Self {
unsafe { core::mem::transmute_copy(&a) }
}
}
impl<T: AsStd430, const N: usize> AsStd430 for [T; N]
where
<T::Std430Type as Std430>::Padded: Pod,
<T::Output as Std430>::Padded: Pod,
{
type Std430Type = Std430Array<T::Std430Type, N>;
fn as_std430(&self) -> Self::Std430Type {
let mut res: [MaybeUninit<<T::Std430Type as Std430>::Padded>; N] =
unsafe { MaybeUninit::uninit().assume_init() };
type Output = Std430Array<T::Output, N>;
fn as_std430(&self) -> Self::Output {
let mut res = Self::Output::uninit_array();
for i in 0..N {
res[i] = MaybeUninit::new(Std430Convertible::from_std430(self[i].as_std430()));
}
unsafe { core::mem::transmute_copy(&res) }
Self::Output::from_uninit_array(res)
}
fn from_std430(val: Self::Std430Type) -> Self {
fn from_std430(val: Self::Output) -> Self {
let mut res: [MaybeUninit<T>; N] = unsafe { MaybeUninit::uninit().assume_init() };
for i in 0..N {
res[i] = MaybeUninit::new(AsStd430::from_std430(val.0[i].into_std430()));
res[i] = MaybeUninit::new(T::from_std430(val.0[i].into_std430()));
}
unsafe { core::mem::transmute_copy(&res) }
}
}
@ -241,7 +250,7 @@ where
}
fn std430_size(&self) -> usize {
size_of::<<Self as AsStd430>::Std430Type>()
size_of::<<Self as AsStd430>::Output>()
}
}

View file

@ -0,0 +1,97 @@
#![allow(unused_macros)]
macro_rules! easy_impl {
( $( $std_name:ident $imp_ty:ty { $($field:ident),* }, )* ) => {
$(
impl crate::std140::AsStd140 for $imp_ty {
type Output = crate::std140::$std_name;
#[inline]
fn as_std140(&self) -> Self::Output {
crate::std140::$std_name {
$(
$field: self.$field.as_std140(),
)*
..bytemuck::Zeroable::zeroed()
}
}
#[inline]
fn from_std140(value: Self::Output) -> Self {
Self {
$(
$field: <_ as crate::std140::AsStd140>::from_std140(value.$field),
)*
}
}
}
impl crate::std430::AsStd430 for $imp_ty {
type Output = crate::std430::$std_name;
#[inline]
fn as_std430(&self) -> Self::Output {
crate::std430::$std_name {
$(
$field: self.$field.as_std430(),
)*
..bytemuck::Zeroable::zeroed()
}
}
#[inline]
fn from_std430(value: Self::Output) -> Self {
Self {
$(
$field: <_ as crate::std430::AsStd430>::from_std430(value.$field),
)*
}
}
}
unsafe impl crate::glsl::Glsl for $imp_ty {
const NAME: &'static str = crate::std140::$std_name::NAME;
}
)*
};
}
macro_rules! minty_impl {
( $( $mint_ty:ty => $imp_ty:ty, )* ) => {
$(
impl crate::std140::AsStd140 for $imp_ty {
type Output = <$mint_ty as crate::std140::AsStd140>::Output;
#[inline]
fn as_std140(&self) -> Self::Output {
let mint: $mint_ty = (*self).into();
mint.as_std140()
}
#[inline]
fn from_std140(value: Self::Output) -> Self {
<$mint_ty>::from_std140(value).into()
}
}
impl crate::std430::AsStd430 for $imp_ty {
type Output = <$mint_ty as crate::std430::AsStd430>::Output;
#[inline]
fn as_std430(&self) -> Self::Output {
let mint: $mint_ty = (*self).into();
mint.as_std430()
}
#[inline]
fn from_std430(value: Self::Output) -> Self {
<$mint_ty>::from_std430(value).into()
}
}
unsafe impl crate::glsl::Glsl for $imp_ty {
const NAME: &'static str = <$mint_ty>::NAME;
}
)*
};
}

View file

@ -0,0 +1,8 @@
---
source: tests/test.rs
expression: "TestGlsl::glsl_definition()"
---
struct TestGlsl {
vec3 foo[8][4];
};

View file

@ -0,0 +1,9 @@
---
source: tests/test.rs
expression: "TestGlsl::glsl_definition()"
---
struct TestGlsl {
vec3 foo;
mat2 bar;
};

View file

@ -0,0 +1,61 @@
use crevice::glsl::GlslStruct;
use crevice::std140::AsStd140;
#[test]
fn there_and_back_again() {
#[derive(AsStd140, Debug, PartialEq)]
struct ThereAndBackAgain {
view: mint::ColumnMatrix3<f32>,
origin: mint::Vector3<f32>,
}
let x = ThereAndBackAgain {
view: mint::ColumnMatrix3 {
x: mint::Vector3 {
x: 1.0,
y: 0.0,
z: 0.0,
},
y: mint::Vector3 {
x: 0.0,
y: 1.0,
z: 0.0,
},
z: mint::Vector3 {
x: 0.0,
y: 0.0,
z: 1.0,
},
},
origin: mint::Vector3 {
x: 0.0,
y: 1.0,
z: 2.0,
},
};
let x_as = x.as_std140();
assert_eq!(<ThereAndBackAgain as AsStd140>::from_std140(x_as), x);
}
#[test]
fn generate_struct_glsl() {
#[allow(dead_code)]
#[derive(GlslStruct)]
struct TestGlsl {
foo: mint::Vector3<f32>,
bar: mint::ColumnMatrix2<f32>,
}
insta::assert_display_snapshot!(TestGlsl::glsl_definition());
}
#[test]
fn generate_struct_array_glsl() {
#[allow(dead_code)]
#[derive(GlslStruct)]
struct TestGlsl {
foo: [[mint::Vector3<f32>; 8]; 4],
}
insta::assert_display_snapshot!(TestGlsl::glsl_definition());
}

View file

@ -29,5 +29,5 @@ bevy_utils = { path = "../../crates/bevy_utils", version = "0.5.0" }
bitflags = "1.2"
# direct dependency required for derive macro
bytemuck = { version = "1", features = ["derive"] }
crevice = { path = "../../crates/crevice", version = "0.6.0" }
crevice = { path = "../../crates/crevice", version = "0.8.0", features = ["glam"] }
wgpu = { version = "0.11.0", features = ["spirv"] }

View file

@ -40,10 +40,10 @@ thiserror = "1.0"
futures-lite = "1.4.0"
anyhow = "1.0"
hex = "0.4.2"
hexasphere = "4.0"
hexasphere = "6.0.0"
parking_lot = "0.11.0"
regex = "1.5"
crevice = { path = "../../crates/crevice", version = "0.6.0" }
crevice = { path = "../../crates/crevice", version = "0.8.0", features = ["glam"] }
[target.'cfg(target_arch = "wasm32")'.dependencies]
wgpu = { version = "0.11.0", features = ["spirv", "webgl"] }

View file

@ -21,8 +21,8 @@ impl<T: AsStd140> Default for UniformVec<T> {
scratch: Vec::new(),
uniform_buffer: None,
capacity: 0,
item_size: (T::std140_size_static() + <T as AsStd140>::Std140Type::ALIGNMENT - 1)
& !(<T as AsStd140>::Std140Type::ALIGNMENT - 1),
item_size: (T::std140_size_static() + <T as AsStd140>::Output::ALIGNMENT - 1)
& !(<T as AsStd140>::Output::ALIGNMENT - 1),
}
}
}