mirror of
https://github.com/bevyengine/bevy
synced 2024-11-25 06:00:20 +00:00
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:
parent
e375addec6
commit
290b7dd9ab
39 changed files with 1846 additions and 668 deletions
|
@ -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"
|
||||
|
|
|
@ -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"] }
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
49
crates/crevice/crevice-derive/src/glsl.rs
Normal file
49
crates/crevice/crevice-derive/src/glsl.rs
Normal 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 )*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
288
crates/crevice/crevice-derive/src/layout.rs
Normal file
288
crates/crevice/crevice-derive/src/layout.rs
Normal 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
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
20
crates/crevice/crevice-tests/Cargo.toml
Normal file
20
crates/crevice/crevice-tests/Cargo.toml
Normal 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 }
|
268
crates/crevice/crevice-tests/src/gpu.rs
Normal file
268
crates/crevice/crevice-tests/src/gpu.rs
Normal 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)
|
||||
}
|
366
crates/crevice/crevice-tests/src/lib.rs
Normal file
366
crates/crevice/crevice-tests/src/lib.rs
Normal 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(),
|
||||
],
|
||||
})
|
||||
}
|
143
crates/crevice/crevice-tests/src/util.rs
Normal file
143
crates/crevice/crevice-tests/src/util.rs
Normal 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));
|
||||
}
|
||||
}};
|
||||
}
|
|
@ -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)
|
||||
}
|
93
crates/crevice/src/glsl.rs
Normal file
93
crates/crevice/src/glsl.rs
Normal 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
10
crates/crevice/src/imp.rs
Normal 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;
|
30
crates/crevice/src/imp/imp_cgmath.rs
Normal file
30
crates/crevice/src/imp/imp_cgmath.rs
Normal 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 },
|
||||
}
|
24
crates/crevice/src/imp/imp_glam.rs
Normal file
24
crates/crevice/src/imp/imp_glam.rs
Normal 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,
|
||||
}
|
30
crates/crevice/src/imp/imp_mint.rs
Normal file
30
crates/crevice/src/imp/imp_mint.rs
Normal 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 },
|
||||
}
|
24
crates/crevice/src/imp/imp_nalgebra.rs
Normal file
24
crates/crevice/src/imp/imp_nalgebra.rs
Normal 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>,
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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),
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
97
crates/crevice/src/util.rs
Normal file
97
crates/crevice/src/util.rs
Normal 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;
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
source: tests/test.rs
|
||||
expression: "TestGlsl::glsl_definition()"
|
||||
|
||||
---
|
||||
struct TestGlsl {
|
||||
vec3 foo[8][4];
|
||||
};
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
source: tests/test.rs
|
||||
expression: "TestGlsl::glsl_definition()"
|
||||
|
||||
---
|
||||
struct TestGlsl {
|
||||
vec3 foo;
|
||||
mat2 bar;
|
||||
};
|
61
crates/crevice/tests/test.rs
Normal file
61
crates/crevice/tests/test.rs
Normal 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());
|
||||
}
|
|
@ -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"] }
|
||||
|
|
|
@ -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"] }
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue