mirror of
https://github.com/bevyengine/bevy
synced 2024-11-25 14:10:19 +00:00
Migrate to encase from crevice (#4339)
# Objective - Unify buffer APIs - Also see #4272 ## Solution - Replace vendored `crevice` with `encase` --- ## Changelog Changed `StorageBuffer` Added `DynamicStorageBuffer` Replaced `UniformVec` with `UniformBuffer` Replaced `DynamicUniformVec` with `DynamicUniformBuffer` ## Migration Guide ### `StorageBuffer` removed `set_body()`, `values()`, `values_mut()`, `clear()`, `push()`, `append()` added `set()`, `get()`, `get_mut()` ### `UniformVec` -> `UniformBuffer` renamed `uniform_buffer()` to `buffer()` removed `len()`, `is_empty()`, `capacity()`, `push()`, `reserve()`, `clear()`, `values()` added `set()`, `get()` ### `DynamicUniformVec` -> `DynamicUniformBuffer` renamed `uniform_buffer()` to `buffer()` removed `capacity()`, `reserve()` Co-authored-by: Carter Anderson <mcanders1@gmail.com>
This commit is contained in:
parent
1320818f96
commit
7cb4d3cb43
57 changed files with 553 additions and 4287 deletions
|
@ -1,36 +0,0 @@
|
|||
[package]
|
||||
name = "bevy_crevice"
|
||||
description = "Create GLSL-compatible versions of structs with explicitly-initialized padding (Bevy version)"
|
||||
version = "0.8.0-dev"
|
||||
edition = "2021"
|
||||
authors = ["Lucien Greathouse <me@lpghatguy.com>"]
|
||||
documentation = "https://docs.rs/crevice"
|
||||
homepage = "https://github.com/LPGhatguy/crevice"
|
||||
repository = "https://github.com/bevyengine/bevy"
|
||||
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
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = []
|
||||
|
||||
# [workspace]
|
||||
# members = ["crevice-derive", "crevice-tests"]
|
||||
# default-members = ["crevice-derive", "crevice-tests"]
|
||||
|
||||
[dependencies]
|
||||
bevy-crevice-derive = { version = "0.8.0-dev", path = "bevy-crevice-derive" }
|
||||
|
||||
bytemuck = "1.4.1"
|
||||
mint = "0.5.8"
|
||||
|
||||
cgmath = { version = "0.18.0", optional = true }
|
||||
glam = { version = "0.20.0", features = ["mint"], optional = true }
|
||||
nalgebra = { version = "0.30.0", features = ["mint"], optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
insta = "0.16.1"
|
|
@ -1,201 +0,0 @@
|
|||
i Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -1,19 +0,0 @@
|
|||
Copyright (c) 2020 Lucien Greathouse
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,181 +0,0 @@
|
|||
# Bevy Crevice
|
||||
|
||||
This is a fork of [Crevice](https://crates.io/crates/crevice) for
|
||||
[Bevy](https://bevyengine.org).
|
||||
|
||||
For use outside of Bevy, you should consider
|
||||
using [Crevice](https://crates.io/crates/crevice) directly.
|
||||
|
||||
It was forked to allow better integration in Bevy:
|
||||
|
||||
* Easier derive macro usage, without needing to depend on `Crevice` directly.
|
||||
* Use of unmerged features (as of the fork), like
|
||||
[Array Support](https://github.com/LPGhatguy/crevice/pull/27/).
|
||||
* Renaming of traits and macros to better match Bevy API.
|
||||
|
||||
## Crevice
|
||||
|
||||
Crevice creates GLSL-compatible versions of types through the power of derive
|
||||
macros. Generated structures provide an [`as_bytes`][std140::Std140::as_bytes]
|
||||
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 types from many
|
||||
math crates, can generate GLSL source from structs, and explicitly initializes
|
||||
padding to remove one source of undefined behavior.
|
||||
|
||||
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.20, 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`][std140::AsStd140] and
|
||||
using [`as_std140`][std140::AsStd140::as_std140] and
|
||||
[`as_bytes`][std140::Std140::as_bytes] to turn the result into bytes.
|
||||
|
||||
```glsl
|
||||
uniform MAIN {
|
||||
mat3 orientation;
|
||||
vec3 position;
|
||||
float scale;
|
||||
} main;
|
||||
```
|
||||
|
||||
```rust
|
||||
use bevy_crevice::std140::{AsStd140, Std140};
|
||||
|
||||
#[derive(AsStd140)]
|
||||
struct MainUniform {
|
||||
orientation: mint::ColumnMatrix3<f32>,
|
||||
position: mint::Vector3<f32>,
|
||||
scale: f32,
|
||||
}
|
||||
|
||||
let value = MainUniform {
|
||||
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,
|
||||
};
|
||||
|
||||
let value_std140 = value.as_std140();
|
||||
|
||||
upload_data_to_gpu(value_std140.as_bytes());
|
||||
```
|
||||
|
||||
### Sequential Types
|
||||
|
||||
More complicated data can be uploaded using the std140
|
||||
[`Writer`][std140::Writer] type.
|
||||
|
||||
```glsl
|
||||
struct PointLight {
|
||||
vec3 position;
|
||||
vec3 color;
|
||||
float brightness;
|
||||
};
|
||||
|
||||
buffer POINT_LIGHTS {
|
||||
uint len;
|
||||
PointLight[] lights;
|
||||
} point_lights;
|
||||
```
|
||||
|
||||
```rust
|
||||
use bevy_crevice::std140::{self, AsStd140};
|
||||
|
||||
#[derive(AsStd140)]
|
||||
struct PointLight {
|
||||
position: mint::Vector3<f32>,
|
||||
color: mint::Vector3<f32>,
|
||||
brightness: f32,
|
||||
}
|
||||
|
||||
let lights = vec![
|
||||
PointLight {
|
||||
position: [0.0, 1.0, 0.0].into(),
|
||||
color: [1.0, 0.0, 0.0].into(),
|
||||
brightness: 0.6,
|
||||
},
|
||||
PointLight {
|
||||
position: [0.0, 4.0, 3.0].into(),
|
||||
color: [1.0, 1.0, 1.0].into(),
|
||||
brightness: 1.0,
|
||||
},
|
||||
];
|
||||
|
||||
let target_buffer = map_gpu_buffer_for_write();
|
||||
let mut writer = std140::Writer::new(target_buffer);
|
||||
|
||||
let light_count = lights.len() as u32;
|
||||
writer.write(&light_count)?;
|
||||
|
||||
// Crevice will automatically insert the required padding to align the
|
||||
// PointLight structure correctly. In this case, there will be 12 bytes of
|
||||
// padding between the length field and the light list.
|
||||
|
||||
writer.write(lights.as_slice())?;
|
||||
|
||||
unmap_gpu_buffer();
|
||||
|
||||
```
|
||||
|
||||
## 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
|
||||
|
||||
[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](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.
|
|
@ -1,25 +0,0 @@
|
|||
# Crevice
|
||||
|
||||
{{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
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or 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.
|
|
@ -1,27 +0,0 @@
|
|||
[package]
|
||||
name = "bevy-crevice-derive"
|
||||
description = "Derive crate for the 'crevice' crate (Bevy version)"
|
||||
version = "0.8.0-dev"
|
||||
edition = "2018"
|
||||
authors = ["Lucien Greathouse <me@lpghatguy.com>"]
|
||||
documentation = "https://docs.rs/crevice-derive"
|
||||
homepage = "https://github.com/LPGhatguy/crevice"
|
||||
repository = "https://github.com/bevyengine/bevy"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
# Enable methods that let you introspect into the generated structs.
|
||||
debug-methods = []
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
syn = "1.0.40"
|
||||
quote = "1.0.7"
|
||||
proc-macro2 = "1.0.21"
|
||||
bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.8.0-dev" }
|
|
@ -1,48 +0,0 @@
|
|||
use bevy_macro_utils::get_named_struct_fields;
|
||||
use proc_macro2::{Literal, TokenStream};
|
||||
use quote::quote;
|
||||
use syn::{parse_quote, DeriveInput, Path};
|
||||
|
||||
pub fn emit(input: DeriveInput) -> TokenStream {
|
||||
let bevy_crevice_path = crate::bevy_crevice_path();
|
||||
|
||||
let fields = match get_named_struct_fields(&input.data) {
|
||||
Ok(fields) => fields,
|
||||
Err(e) => return e.into_compile_error(),
|
||||
};
|
||||
|
||||
let base_trait_path: Path = parse_quote!(#bevy_crevice_path::glsl::Glsl);
|
||||
let struct_trait_path: Path = parse_quote!(#bevy_crevice_path::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 #bevy_crevice_path::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 #bevy_crevice_path::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 )*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,287 +0,0 @@
|
|||
use bevy_macro_utils::get_named_struct_fields;
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::{format_ident, quote};
|
||||
use syn::{parse_quote, DeriveInput, Ident, Path, Type};
|
||||
|
||||
pub fn emit(
|
||||
input: DeriveInput,
|
||||
trait_name: &'static str,
|
||||
mod_name: &'static str,
|
||||
min_struct_alignment: usize,
|
||||
) -> TokenStream {
|
||||
let bevy_crevice_path = crate::bevy_crevice_path();
|
||||
|
||||
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!(#bevy_crevice_path::#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 get_named_struct_fields(&input.data) {
|
||||
Ok(fields) => fields.named.iter().collect(),
|
||||
Err(e) => return e.into_compile_error(),
|
||||
};
|
||||
|
||||
// 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! {
|
||||
#bevy_crevice_path::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 = #bevy_crevice_path::internal::max(
|
||||
#next_field_or_self_alignment,
|
||||
min_alignment,
|
||||
);
|
||||
|
||||
// Using everything we've got, compute our padding amount.
|
||||
#bevy_crevice_path::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 = #bevy_crevice_path::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 #bevy_crevice_path::internal::bytemuck::Zeroable for #generated_name #ty_generics #where_clause {}
|
||||
unsafe impl #impl_generics #bevy_crevice_path::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, {#bevy_crevice_path::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
|
||||
|
||||
..#bevy_crevice_path::internal::bytemuck::Zeroable::zeroed()
|
||||
}
|
||||
}
|
||||
|
||||
fn #from_trait_method(input: Self::Output) -> Self {
|
||||
Self {
|
||||
#input_struct_field_init
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#debug_methods
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
mod glsl;
|
||||
mod layout;
|
||||
|
||||
use bevy_macro_utils::BevyManifest;
|
||||
use proc_macro::TokenStream as CompilerTokenStream;
|
||||
|
||||
use syn::{parse_macro_input, DeriveInput, Path};
|
||||
|
||||
#[proc_macro_derive(AsStd140)]
|
||||
pub fn derive_as_std140(input: CompilerTokenStream) -> CompilerTokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
let expanded = layout::emit(input, "Std140", "std140", 16);
|
||||
|
||||
CompilerTokenStream::from(expanded)
|
||||
}
|
||||
|
||||
#[proc_macro_derive(AsStd430)]
|
||||
pub fn derive_as_std430(input: CompilerTokenStream) -> CompilerTokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
let expanded = layout::emit(input, "Std430", "std430", 0);
|
||||
|
||||
CompilerTokenStream::from(expanded)
|
||||
}
|
||||
|
||||
#[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);
|
||||
|
||||
CompilerTokenStream::from(expanded)
|
||||
}
|
||||
|
||||
const BEVY: &str = "bevy";
|
||||
const BEVY_CREVICE: &str = "bevy_crevice";
|
||||
const BEVY_RENDER: &str = "bevy_render";
|
||||
|
||||
fn bevy_crevice_path() -> Path {
|
||||
let bevy_manifest = BevyManifest::default();
|
||||
bevy_manifest
|
||||
.maybe_get_path(crate::BEVY)
|
||||
.map(|bevy_path| {
|
||||
let mut segments = bevy_path.segments;
|
||||
segments.push(BevyManifest::parse_str("render"));
|
||||
Path {
|
||||
leading_colon: None,
|
||||
segments,
|
||||
}
|
||||
})
|
||||
.or_else(|| bevy_manifest.maybe_get_path(crate::BEVY_RENDER))
|
||||
.map(|bevy_render_path| {
|
||||
let mut segments = bevy_render_path.segments;
|
||||
segments.push(BevyManifest::parse_str("render_resource"));
|
||||
Path {
|
||||
leading_colon: None,
|
||||
segments,
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| bevy_manifest.get_path(crate::BEVY_CREVICE))
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
[package]
|
||||
name = "crevice-tests"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
|
||||
[features]
|
||||
wgpu-validation = ["wgpu", "naga", "futures"]
|
||||
|
||||
[dependencies]
|
||||
bevy_crevice = { path = ".." }
|
||||
bevy-crevice-derive = { path = "../bevy-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.8.0", features = ["glsl-in", "wgsl-out"], optional = true }
|
||||
wgpu = { version = "0.12.0", optional = true }
|
|
@ -1,268 +0,0 @@
|
|||
use std::borrow::Cow;
|
||||
use std::fmt::Debug;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use bevy_crevice::glsl::{Glsl, GlslStruct};
|
||||
use bevy_crevice::std140::{AsStd140, Std140};
|
||||
use bevy_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)
|
||||
}
|
|
@ -1,366 +0,0 @@
|
|||
#![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 bevy_crevice::glsl::GlslStruct;
|
||||
use bevy_crevice::std140::AsStd140;
|
||||
use bevy_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(),
|
||||
],
|
||||
})
|
||||
}
|
|
@ -1,143 +0,0 @@
|
|||
#[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,93 +0,0 @@
|
|||
//! Defines traits and types for generating GLSL code from Rust definitions.
|
||||
|
||||
pub use bevy_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>;
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
mod imp_mint;
|
||||
|
||||
#[cfg(feature = "cgmath")]
|
||||
mod imp_cgmath;
|
||||
|
||||
#[cfg(feature = "glam")]
|
||||
mod imp_glam;
|
||||
|
||||
#[cfg(feature = "nalgebra")]
|
||||
mod imp_nalgebra;
|
|
@ -1,30 +0,0 @@
|
|||
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 },
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
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,
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
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 },
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
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>,
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
//! This module is internal to crevice but used by its derive macro. No
|
||||
//! guarantees are made about its contents.
|
||||
|
||||
pub use bytemuck;
|
||||
|
||||
/// Gives the number of bytes needed to make `offset` be aligned to `alignment`.
|
||||
pub const fn align_offset(offset: usize, alignment: usize) -> usize {
|
||||
if alignment == 0 || offset % alignment == 0 {
|
||||
0
|
||||
} else {
|
||||
alignment - offset % alignment
|
||||
}
|
||||
}
|
||||
|
||||
/// Max of two `usize`. Implemented because the `max` method from `Ord` cannot
|
||||
/// be used in const fns.
|
||||
pub const fn max(a: usize, b: usize) -> usize {
|
||||
if a > b {
|
||||
a
|
||||
} else {
|
||||
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,174 +0,0 @@
|
|||
#![allow(
|
||||
clippy::new_without_default,
|
||||
clippy::needless_update,
|
||||
clippy::len_without_is_empty,
|
||||
clippy::needless_range_loop,
|
||||
clippy::all,
|
||||
clippy::doc_markdown
|
||||
)]
|
||||
/*!
|
||||
[![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)
|
||||
[![crevice docs](https://img.shields.io/badge/docs-docs.rs-orange.svg)](https://docs.rs/crevice)
|
||||
|
||||
Crevice creates GLSL-compatible versions of types through the power of derive
|
||||
macros. Generated structures provide an [`as_bytes`][std140::Std140::as_bytes]
|
||||
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 types from many
|
||||
math crates, can generate GLSL source from structs, and explicitly initializes
|
||||
padding to remove one source of undefined behavior.
|
||||
|
||||
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`][std140::AsStd140] and
|
||||
using [`as_std140`][std140::AsStd140::as_std140] and
|
||||
[`as_bytes`][std140::Std140::as_bytes] to turn the result into bytes.
|
||||
|
||||
```glsl
|
||||
uniform MAIN {
|
||||
mat3 orientation;
|
||||
vec3 position;
|
||||
float scale;
|
||||
} main;
|
||||
```
|
||||
|
||||
```rust
|
||||
use bevy_crevice::std140::{AsStd140, Std140};
|
||||
|
||||
#[derive(AsStd140)]
|
||||
struct MainUniform {
|
||||
orientation: mint::ColumnMatrix3<f32>,
|
||||
position: mint::Vector3<f32>,
|
||||
scale: f32,
|
||||
}
|
||||
|
||||
let value = MainUniform {
|
||||
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,
|
||||
};
|
||||
|
||||
let value_std140 = value.as_std140();
|
||||
|
||||
# fn upload_data_to_gpu(_value: &[u8]) {}
|
||||
upload_data_to_gpu(value_std140.as_bytes());
|
||||
```
|
||||
|
||||
### Sequential Types
|
||||
|
||||
More complicated data can be uploaded using the std140
|
||||
[`Writer`][std140::Writer] type.
|
||||
|
||||
```glsl
|
||||
struct PointLight {
|
||||
vec3 position;
|
||||
vec3 color;
|
||||
float brightness;
|
||||
};
|
||||
|
||||
buffer POINT_LIGHTS {
|
||||
uint len;
|
||||
PointLight[] lights;
|
||||
} point_lights;
|
||||
```
|
||||
|
||||
```rust
|
||||
use bevy_crevice::std140::{self, AsStd140};
|
||||
|
||||
#[derive(AsStd140)]
|
||||
struct PointLight {
|
||||
position: mint::Vector3<f32>,
|
||||
color: mint::Vector3<f32>,
|
||||
brightness: f32,
|
||||
}
|
||||
|
||||
let lights = vec![
|
||||
PointLight {
|
||||
position: [0.0, 1.0, 0.0].into(),
|
||||
color: [1.0, 0.0, 0.0].into(),
|
||||
brightness: 0.6,
|
||||
},
|
||||
PointLight {
|
||||
position: [0.0, 4.0, 3.0].into(),
|
||||
color: [1.0, 1.0, 1.0].into(),
|
||||
brightness: 1.0,
|
||||
},
|
||||
];
|
||||
|
||||
# fn map_gpu_buffer_for_write() -> &'static mut [u8] {
|
||||
# Box::leak(vec![0; 1024].into_boxed_slice())
|
||||
# }
|
||||
let target_buffer = map_gpu_buffer_for_write();
|
||||
let mut writer = std140::Writer::new(target_buffer);
|
||||
|
||||
let light_count = lights.len() as u32;
|
||||
writer.write(&light_count)?;
|
||||
|
||||
// Crevice will automatically insert the required padding to align the
|
||||
// PointLight structure correctly. In this case, there will be 12 bytes of
|
||||
// padding between the length field and the light list.
|
||||
|
||||
writer.write(lights.as_slice())?;
|
||||
|
||||
# fn unmap_gpu_buffer() {}
|
||||
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.52.1 and newer due to use of new `const fn` features.
|
||||
|
||||
[glsl-layout]: https://github.com/rustgd/glsl-layout
|
||||
*/
|
||||
|
||||
#![deny(missing_docs)]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[macro_use]
|
||||
mod util;
|
||||
|
||||
pub mod glsl;
|
||||
pub mod std140;
|
||||
pub mod std430;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub mod internal;
|
||||
|
||||
mod imp;
|
|
@ -1,18 +0,0 @@
|
|||
//! Defines traits and types for working with data adhering to GLSL's `std140`
|
||||
//! layout specification.
|
||||
|
||||
mod dynamic_uniform;
|
||||
mod primitives;
|
||||
mod sizer;
|
||||
mod traits;
|
||||
#[cfg(feature = "std")]
|
||||
mod writer;
|
||||
|
||||
pub use self::dynamic_uniform::*;
|
||||
pub use self::primitives::*;
|
||||
pub use self::sizer::*;
|
||||
pub use self::traits::*;
|
||||
#[cfg(feature = "std")]
|
||||
pub use self::writer::*;
|
||||
|
||||
pub use bevy_crevice_derive::AsStd140;
|
|
@ -1,68 +0,0 @@
|
|||
use bytemuck::{Pod, Zeroable};
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use crate::internal::{align_offset, max};
|
||||
use crate::std140::{AsStd140, Std140};
|
||||
|
||||
/// Wrapper type that aligns the inner type to at least 256 bytes.
|
||||
///
|
||||
/// This type is useful for ensuring correct alignment when creating dynamic
|
||||
/// uniform buffers in APIs like WebGPU.
|
||||
pub struct DynamicUniform<T>(pub T);
|
||||
|
||||
impl<T: AsStd140> AsStd140 for DynamicUniform<T> {
|
||||
type Output = DynamicUniformStd140<<T as AsStd140>::Output>;
|
||||
|
||||
fn as_std140(&self) -> Self::Output {
|
||||
DynamicUniformStd140(self.0.as_std140())
|
||||
}
|
||||
|
||||
fn from_std140(value: Self::Output) -> Self {
|
||||
DynamicUniform(<T as AsStd140>::from_std140(value.0))
|
||||
}
|
||||
}
|
||||
|
||||
/// std140 variant of [`DynamicUniform`].
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(transparent)]
|
||||
pub struct DynamicUniformStd140<T>(T);
|
||||
|
||||
unsafe impl<T: Std140> Std140 for DynamicUniformStd140<T> {
|
||||
const ALIGNMENT: usize = max(256, T::ALIGNMENT);
|
||||
#[cfg(const_evaluatable_checked)]
|
||||
type Padded = crate::std140::Std140Padded<
|
||||
Self,
|
||||
{ align_offset(core::mem::size_of::<T>(), max(256, T::ALIGNMENT)) },
|
||||
>;
|
||||
#[cfg(not(const_evaluatable_checked))]
|
||||
type Padded = crate::std140::InvalidPadded;
|
||||
}
|
||||
|
||||
unsafe impl<T: Zeroable> Zeroable for DynamicUniformStd140<T> {}
|
||||
unsafe impl<T: Pod> Pod for DynamicUniformStd140<T> {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
use crate::std140::{self, WriteStd140};
|
||||
|
||||
#[test]
|
||||
fn size_is_unchanged() {
|
||||
let dynamic_f32 = DynamicUniform(0.0f32);
|
||||
|
||||
assert_eq!(dynamic_f32.std140_size(), 0.0f32.std140_size());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alignment_applies() {
|
||||
let mut output = Vec::new();
|
||||
let mut writer = std140::Writer::new(&mut output);
|
||||
|
||||
writer.write(&DynamicUniform(0.0f32)).unwrap();
|
||||
assert_eq!(writer.len(), 4);
|
||||
|
||||
writer.write(&DynamicUniform(1.0f32)).unwrap();
|
||||
assert_eq!(writer.len(), 260);
|
||||
}
|
||||
}
|
|
@ -1,175 +0,0 @@
|
|||
use bytemuck::{Pod, Zeroable};
|
||||
|
||||
use crate::glsl::Glsl;
|
||||
use crate::std140::{Std140, Std140Padded};
|
||||
|
||||
use crate::internal::{align_offset, max};
|
||||
use core::mem::size_of;
|
||||
|
||||
unsafe impl Std140 for f32 {
|
||||
const ALIGNMENT: usize = 4;
|
||||
type Padded = Std140Padded<Self, 12>;
|
||||
}
|
||||
|
||||
unsafe impl Std140 for f64 {
|
||||
const ALIGNMENT: usize = 8;
|
||||
type Padded = Std140Padded<Self, 8>;
|
||||
}
|
||||
|
||||
unsafe impl Std140 for i32 {
|
||||
const ALIGNMENT: usize = 4;
|
||||
type Padded = Std140Padded<Self, 12>;
|
||||
}
|
||||
|
||||
unsafe impl Std140 for u32 {
|
||||
const ALIGNMENT: usize = 4;
|
||||
type Padded = Std140Padded<Self, 12>;
|
||||
}
|
||||
|
||||
macro_rules! vectors {
|
||||
(
|
||||
$(
|
||||
#[$doc:meta] align($align:literal) $glsl_name:ident $name:ident <$prim:ident> ($($field:ident),+)
|
||||
)+
|
||||
) => {
|
||||
$(
|
||||
#[$doc]
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[repr(C)]
|
||||
pub struct $name {
|
||||
$(pub $field: $prim,)+
|
||||
}
|
||||
|
||||
unsafe impl Zeroable for $name {}
|
||||
unsafe impl Pod for $name {}
|
||||
|
||||
unsafe impl Std140 for $name {
|
||||
const ALIGNMENT: usize = $align;
|
||||
type Padded = Std140Padded<Self, {align_offset(size_of::<$name>(), max(16, $align))}>;
|
||||
}
|
||||
|
||||
unsafe impl Glsl for $name {
|
||||
const NAME: &'static str = stringify!($glsl_name);
|
||||
}
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
||||
vectors! {
|
||||
#[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 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 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)
|
||||
|
||||
// bool vectors are disabled due to https://github.com/LPGhatguy/crevice/issues/36
|
||||
|
||||
// #[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 {
|
||||
(
|
||||
$(
|
||||
#[$doc:meta]
|
||||
align($align:literal)
|
||||
$glsl_name:ident $name:ident {
|
||||
$($field:ident: $field_ty:ty,)+
|
||||
}
|
||||
)+
|
||||
) => {
|
||||
$(
|
||||
#[$doc]
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[repr(C)]
|
||||
pub struct $name {
|
||||
$(pub $field: $field_ty,)+
|
||||
}
|
||||
|
||||
unsafe impl Zeroable for $name {}
|
||||
unsafe impl Pod for $name {}
|
||||
|
||||
unsafe impl Std140 for $name {
|
||||
const ALIGNMENT: usize = $align;
|
||||
/// Matrices are technically arrays of primitives, and as such require pad at end.
|
||||
const PAD_AT_END: bool = true;
|
||||
type Padded = Std140Padded<Self, {align_offset(size_of::<$name>(), max(16, $align))}>;
|
||||
}
|
||||
|
||||
unsafe impl Glsl for $name {
|
||||
const NAME: &'static str = stringify!($glsl_name);
|
||||
}
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
||||
matrices! {
|
||||
#[doc = "Corresponds to a GLSL `mat2` in std140 layout."]
|
||||
align(16)
|
||||
mat2 Mat2 {
|
||||
x: Vec2,
|
||||
_pad_x: [f32; 2],
|
||||
y: Vec2,
|
||||
_pad_y: [f32; 2],
|
||||
}
|
||||
|
||||
#[doc = "Corresponds to a GLSL `mat3` in std140 layout."]
|
||||
align(16)
|
||||
mat3 Mat3 {
|
||||
x: Vec3,
|
||||
_pad_x: f32,
|
||||
y: Vec3,
|
||||
_pad_y: f32,
|
||||
z: Vec3,
|
||||
_pad_z: f32,
|
||||
}
|
||||
|
||||
#[doc = "Corresponds to a GLSL `mat4` in std140 layout."]
|
||||
align(16)
|
||||
mat4 Mat4 {
|
||||
x: Vec4,
|
||||
y: Vec4,
|
||||
z: Vec4,
|
||||
w: Vec4,
|
||||
}
|
||||
|
||||
#[doc = "Corresponds to a GLSL `dmat2` in std140 layout."]
|
||||
align(16)
|
||||
dmat2 DMat2 {
|
||||
x: DVec2,
|
||||
y: DVec2,
|
||||
}
|
||||
|
||||
#[doc = "Corresponds to a GLSL `dmat3` in std140 layout."]
|
||||
align(32)
|
||||
dmat3 DMat3 {
|
||||
x: DVec3,
|
||||
_pad_x: f64,
|
||||
y: DVec3,
|
||||
_pad_y: f64,
|
||||
z: DVec3,
|
||||
_pad_z: f64,
|
||||
}
|
||||
|
||||
#[doc = "Corresponds to a GLSL `dmat3` in std140 layout."]
|
||||
align(32)
|
||||
dmat4 DMat4 {
|
||||
x: DVec4,
|
||||
y: DVec4,
|
||||
z: DVec4,
|
||||
w: DVec4,
|
||||
}
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
use core::mem::size_of;
|
||||
|
||||
use crate::internal::align_offset;
|
||||
use crate::std140::{AsStd140, Std140};
|
||||
|
||||
/**
|
||||
Type that computes the buffer size needed by a series of `std140` types laid
|
||||
out.
|
||||
|
||||
This type works well well when paired with `Writer`, precomputing a buffer's
|
||||
size to alleviate the need to dynamically re-allocate buffers.
|
||||
|
||||
## Example
|
||||
|
||||
```glsl
|
||||
struct Frob {
|
||||
vec3 size;
|
||||
float frobiness;
|
||||
}
|
||||
|
||||
buffer FROBS {
|
||||
uint len;
|
||||
Frob[] frobs;
|
||||
} frobs;
|
||||
```
|
||||
|
||||
```
|
||||
use bevy_crevice::std140::{self, AsStd140};
|
||||
|
||||
#[derive(AsStd140)]
|
||||
struct Frob {
|
||||
size: mint::Vector3<f32>,
|
||||
frobiness: f32,
|
||||
}
|
||||
|
||||
// Many APIs require that buffers contain at least enough space for all
|
||||
// fixed-size bindiongs to a buffer as well as one element of any arrays, if
|
||||
// there are any.
|
||||
let mut sizer = std140::Sizer::new();
|
||||
sizer.add::<u32>();
|
||||
sizer.add::<Frob>();
|
||||
|
||||
# fn create_buffer_with_size(size: usize) {}
|
||||
let buffer = create_buffer_with_size(sizer.len());
|
||||
# assert_eq!(sizer.len(), 32);
|
||||
```
|
||||
*/
|
||||
pub struct Sizer {
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
impl Sizer {
|
||||
/// Create a new `Sizer`.
|
||||
pub fn new() -> Self {
|
||||
Self { offset: 0 }
|
||||
}
|
||||
|
||||
/// Add a type's necessary padding and size to the `Sizer`. Returns the
|
||||
/// offset into the buffer where that type would be written.
|
||||
pub fn add<T>(&mut self) -> usize
|
||||
where
|
||||
T: AsStd140,
|
||||
{
|
||||
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;
|
||||
let write_here = self.offset;
|
||||
|
||||
self.offset += size;
|
||||
|
||||
write_here
|
||||
}
|
||||
|
||||
/// Returns the number of bytes required to contain all the types added to
|
||||
/// the `Sizer`.
|
||||
pub fn len(&self) -> usize {
|
||||
self.offset
|
||||
}
|
||||
}
|
|
@ -1,284 +0,0 @@
|
|||
use core::mem::{size_of, MaybeUninit};
|
||||
#[cfg(feature = "std")]
|
||||
use std::io::{self, Write};
|
||||
|
||||
use bytemuck::{bytes_of, Pod, Zeroable};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use crate::std140::Writer;
|
||||
|
||||
/// Trait implemented for all `std140` primitives. Generally should not be
|
||||
/// implemented outside this crate.
|
||||
pub unsafe trait Std140: Copy + Zeroable + Pod {
|
||||
/// The required alignment of the type. Must be a power of two.
|
||||
///
|
||||
/// This is distinct from the value returned by `std::mem::align_of` because
|
||||
/// `AsStd140` structs do not use Rust's alignment. This enables them to
|
||||
/// 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>
|
||||
/// (rule 4 and 9)
|
||||
const PAD_AT_END: bool = false;
|
||||
/// Padded type (Std140Padded specialization)
|
||||
/// The usual implementation is
|
||||
/// type Padded = Std140Padded<Self, {align_offset(size_of::<Self>(), max(16, ALIGNMENT))}>;
|
||||
type Padded: Std140Convertible<Self>;
|
||||
|
||||
/// Casts the type to a byte array. Implementors should not override this
|
||||
/// method.
|
||||
///
|
||||
/// # Safety
|
||||
/// This is always safe due to the requirements of [`bytemuck::Pod`] being a
|
||||
/// prerequisite for this trait.
|
||||
fn as_bytes(&self) -> &[u8] {
|
||||
bytes_of(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait specifically for Std140::Padded, implements conversions between padded type and base type.
|
||||
pub trait Std140Convertible<T: Std140>: Copy {
|
||||
/// Convert from self to Std140
|
||||
fn into_std140(self) -> T;
|
||||
/// Convert from Std140 to self
|
||||
fn from_std140(_: T) -> Self;
|
||||
}
|
||||
|
||||
impl<T: Std140> Std140Convertible<T> for T {
|
||||
fn into_std140(self) -> T {
|
||||
self
|
||||
}
|
||||
fn from_std140(also_self: T) -> Self {
|
||||
also_self
|
||||
}
|
||||
}
|
||||
|
||||
/// Unfortunately, we cannot easily derive padded representation for generic Std140 types.
|
||||
/// For now, we'll just use this empty enum with no values.
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum InvalidPadded {}
|
||||
impl<T: Std140> Std140Convertible<T> for InvalidPadded {
|
||||
fn into_std140(self) -> T {
|
||||
unimplemented!()
|
||||
}
|
||||
fn from_std140(_: T) -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
/**
|
||||
Trait implemented for all types that can be turned into `std140` values.
|
||||
*
|
||||
This trait can often be `#[derive]`'d instead of manually implementing it. Any
|
||||
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 math crates, like cgmath, nalgebra, and
|
||||
ultraviolet support mint.
|
||||
|
||||
## Example
|
||||
|
||||
```glsl
|
||||
uniform CAMERA {
|
||||
mat4 view;
|
||||
mat4 projection;
|
||||
} camera;
|
||||
```
|
||||
|
||||
```no_run
|
||||
use bevy_crevice::std140::{AsStd140, Std140};
|
||||
|
||||
#[derive(AsStd140)]
|
||||
struct CameraUniform {
|
||||
view: mint::ColumnMatrix4<f32>,
|
||||
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,
|
||||
projection,
|
||||
};
|
||||
|
||||
# fn write_to_gpu_buffer(bytes: &[u8]) {}
|
||||
let camera_std140 = camera.as_std140();
|
||||
write_to_gpu_buffer(camera_std140.as_bytes());
|
||||
```
|
||||
*/
|
||||
pub trait AsStd140 {
|
||||
/// The `std140` version of this value.
|
||||
type Output: Std140;
|
||||
|
||||
/// Convert this value into the `std140` version of itself.
|
||||
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::Output>()
|
||||
}
|
||||
|
||||
/// Converts from `std140` version of self to self.
|
||||
fn from_std140(val: Self::Output) -> Self;
|
||||
}
|
||||
|
||||
impl<T> AsStd140 for T
|
||||
where
|
||||
T: Std140,
|
||||
{
|
||||
type Output = Self;
|
||||
|
||||
fn as_std140(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
|
||||
fn from_std140(x: Self) -> Self {
|
||||
x
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Std140Padded<T: Std140, const PAD: usize> {
|
||||
inner: T,
|
||||
_padding: [u8; PAD],
|
||||
}
|
||||
|
||||
unsafe impl<T: Std140, const PAD: usize> Zeroable for Std140Padded<T, PAD> {}
|
||||
unsafe impl<T: Std140, const PAD: usize> Pod for Std140Padded<T, PAD> {}
|
||||
|
||||
impl<T: Std140, const PAD: usize> Std140Convertible<T> for Std140Padded<T, PAD> {
|
||||
fn into_std140(self) -> T {
|
||||
self.inner
|
||||
}
|
||||
|
||||
fn from_std140(inner: T) -> Self {
|
||||
Self {
|
||||
inner,
|
||||
_padding: [0u8; PAD],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(transparent)]
|
||||
pub struct Std140Array<T: Std140, const N: usize>([T::Padded; N]);
|
||||
|
||||
unsafe impl<T: Std140, const N: usize> Zeroable for Std140Array<T, N> where T::Padded: Zeroable {}
|
||||
unsafe impl<T: Std140, const N: usize> Pod for Std140Array<T, N> where T::Padded: Pod {}
|
||||
unsafe impl<T: Std140, const N: usize> Std140 for Std140Array<T, N>
|
||||
where
|
||||
T::Padded: Pod,
|
||||
{
|
||||
const ALIGNMENT: usize = crate::internal::max(T::ALIGNMENT, 16);
|
||||
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::Output as Std140>::Padded: Pod,
|
||||
{
|
||||
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()));
|
||||
}
|
||||
|
||||
Self::Output::from_uninit_array(res)
|
||||
}
|
||||
|
||||
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(T::from_std140(Std140Convertible::into_std140(val.0[i])));
|
||||
}
|
||||
unsafe { core::mem::transmute_copy(&res) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait implemented for all types that can be written into a buffer as
|
||||
/// `std140` bytes. This type is more general than [`AsStd140`]: all `AsStd140`
|
||||
/// types implement `WriteStd140`, but not the other way around.
|
||||
///
|
||||
/// While `AsStd140` requires implementers to return a type that implements the
|
||||
/// `Std140` trait, `WriteStd140` directly writes bytes using a [`Writer`]. This
|
||||
/// makes `WriteStd140` usable for writing slices or other DSTs that could not
|
||||
/// implement `AsStd140` without allocating new memory on the heap.
|
||||
#[cfg(feature = "std")]
|
||||
pub trait WriteStd140 {
|
||||
/// Writes this value into the given [`Writer`] using `std140` layout rules.
|
||||
///
|
||||
/// Should return the offset of the first byte of this type, as returned by
|
||||
/// the first call to [`Writer::write`].
|
||||
fn write_std140<W: Write>(&self, writer: &mut Writer<W>) -> io::Result<usize>;
|
||||
|
||||
/// The space required to write this value using `std140` layout rules. This
|
||||
/// does not include alignment padding that may be needed before or after
|
||||
/// this type when written as part of a larger buffer.
|
||||
fn std140_size(&self) -> usize {
|
||||
let mut writer = Writer::new(io::sink());
|
||||
self.write_std140(&mut writer).unwrap();
|
||||
writer.len()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<T> WriteStd140 for T
|
||||
where
|
||||
T: AsStd140,
|
||||
{
|
||||
fn write_std140<W: Write>(&self, writer: &mut Writer<W>) -> io::Result<usize> {
|
||||
writer.write_std140(&self.as_std140())
|
||||
}
|
||||
|
||||
fn std140_size(&self) -> usize {
|
||||
size_of::<<Self as AsStd140>::Output>()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<T> WriteStd140 for [T]
|
||||
where
|
||||
T: WriteStd140,
|
||||
{
|
||||
fn write_std140<W: Write>(&self, writer: &mut Writer<W>) -> io::Result<usize> {
|
||||
// if no items are written, offset is current position of the writer
|
||||
let mut offset = writer.len();
|
||||
|
||||
let mut iter = self.iter();
|
||||
|
||||
if let Some(item) = iter.next() {
|
||||
offset = item.write_std140(writer)?;
|
||||
}
|
||||
|
||||
for item in iter {
|
||||
item.write_std140(writer)?;
|
||||
}
|
||||
|
||||
Ok(offset)
|
||||
}
|
||||
|
||||
fn std140_size(&self) -> usize {
|
||||
let mut writer = Writer::new(io::sink());
|
||||
self.write_std140(&mut writer).unwrap();
|
||||
writer.len()
|
||||
}
|
||||
}
|
|
@ -1,162 +0,0 @@
|
|||
use std::io::{self, Write};
|
||||
use std::mem::size_of;
|
||||
|
||||
use bytemuck::bytes_of;
|
||||
|
||||
use crate::internal::align_offset;
|
||||
use crate::std140::{AsStd140, Std140, WriteStd140};
|
||||
|
||||
/**
|
||||
Type that enables writing correctly aligned `std140` values to a buffer.
|
||||
|
||||
`Writer` is useful when many values need to be laid out in a row that cannot be
|
||||
represented by a struct alone, like dynamically sized arrays or dynamically
|
||||
laid-out values.
|
||||
|
||||
## Example
|
||||
In this example, we'll write a length-prefixed list of lights to a buffer.
|
||||
`std140::Writer` helps align correctly, even across multiple structs, which can
|
||||
be tricky and error-prone otherwise.
|
||||
|
||||
```glsl
|
||||
struct PointLight {
|
||||
vec3 position;
|
||||
vec3 color;
|
||||
float brightness;
|
||||
};
|
||||
|
||||
buffer POINT_LIGHTS {
|
||||
uint len;
|
||||
PointLight[] lights;
|
||||
} point_lights;
|
||||
```
|
||||
|
||||
```
|
||||
use bevy_crevice::std140::{self, AsStd140};
|
||||
|
||||
#[derive(AsStd140)]
|
||||
struct PointLight {
|
||||
position: mint::Vector3<f32>,
|
||||
color: mint::Vector3<f32>,
|
||||
brightness: f32,
|
||||
}
|
||||
|
||||
let lights = vec![
|
||||
PointLight {
|
||||
position: [0.0, 1.0, 0.0].into(),
|
||||
color: [1.0, 0.0, 0.0].into(),
|
||||
brightness: 0.6,
|
||||
},
|
||||
PointLight {
|
||||
position: [0.0, 4.0, 3.0].into(),
|
||||
color: [1.0, 1.0, 1.0].into(),
|
||||
brightness: 1.0,
|
||||
},
|
||||
];
|
||||
|
||||
# fn map_gpu_buffer_for_write() -> &'static mut [u8] {
|
||||
# Box::leak(vec![0; 1024].into_boxed_slice())
|
||||
# }
|
||||
let target_buffer = map_gpu_buffer_for_write();
|
||||
let mut writer = std140::Writer::new(target_buffer);
|
||||
|
||||
let light_count = lights.len() as u32;
|
||||
writer.write(&light_count)?;
|
||||
|
||||
// Crevice will automatically insert the required padding to align the
|
||||
// PointLight structure correctly. In this case, there will be 12 bytes of
|
||||
// padding between the length field and the light list.
|
||||
|
||||
writer.write(lights.as_slice())?;
|
||||
|
||||
# fn unmap_gpu_buffer() {}
|
||||
unmap_gpu_buffer();
|
||||
|
||||
# Ok::<(), std::io::Error>(())
|
||||
```
|
||||
*/
|
||||
pub struct Writer<W> {
|
||||
writer: W,
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
impl<W: Write> Writer<W> {
|
||||
/// Create a new `Writer`, wrapping a buffer, file, or other type that
|
||||
/// implements [`std::io::Write`].
|
||||
pub fn new(writer: W) -> Self {
|
||||
Self { writer, offset: 0 }
|
||||
}
|
||||
|
||||
/// Write a new value to the underlying buffer, writing zeroed padding where
|
||||
/// necessary.
|
||||
///
|
||||
/// Returns the offset into the buffer that the value was written to.
|
||||
pub fn write<T>(&mut self, value: &T) -> io::Result<usize>
|
||||
where
|
||||
T: WriteStd140 + ?Sized,
|
||||
{
|
||||
value.write_std140(self)
|
||||
}
|
||||
|
||||
/// Write an iterator of values to the underlying buffer.
|
||||
///
|
||||
/// Returns the offset into the buffer that the first value was written to.
|
||||
/// If no values were written, returns the `len()`.
|
||||
pub fn write_iter<I, T>(&mut self, iter: I) -> io::Result<usize>
|
||||
where
|
||||
I: IntoIterator<Item = T>,
|
||||
T: WriteStd140,
|
||||
{
|
||||
let mut offset = self.offset;
|
||||
|
||||
let mut iter = iter.into_iter();
|
||||
|
||||
if let Some(item) = iter.next() {
|
||||
offset = item.write_std140(self)?;
|
||||
}
|
||||
|
||||
for item in iter {
|
||||
item.write_std140(self)?;
|
||||
}
|
||||
|
||||
Ok(offset)
|
||||
}
|
||||
|
||||
/// Write an `Std140` type to the underlying buffer.
|
||||
pub fn write_std140<T>(&mut self, value: &T) -> io::Result<usize>
|
||||
where
|
||||
T: Std140,
|
||||
{
|
||||
let padding = align_offset(self.offset, T::ALIGNMENT);
|
||||
|
||||
for _ in 0..padding {
|
||||
self.writer.write_all(&[0])?;
|
||||
}
|
||||
self.offset += padding;
|
||||
|
||||
let value = value.as_std140();
|
||||
self.writer.write_all(bytes_of(&value))?;
|
||||
|
||||
let write_here = self.offset;
|
||||
self.offset += size_of::<T>();
|
||||
|
||||
Ok(write_here)
|
||||
}
|
||||
|
||||
/// Write a slice of values to the underlying buffer.
|
||||
#[deprecated(
|
||||
since = "0.6.0",
|
||||
note = "Use `write` instead -- it now works on slices."
|
||||
)]
|
||||
pub fn write_slice<T>(&mut self, slice: &[T]) -> io::Result<usize>
|
||||
where
|
||||
T: AsStd140,
|
||||
{
|
||||
self.write(slice)
|
||||
}
|
||||
|
||||
/// Returns the amount of data written by this `Writer`.
|
||||
pub fn len(&self) -> usize {
|
||||
self.offset
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
//! Defines traits and types for working with data adhering to GLSL's `std140`
|
||||
//! layout specification.
|
||||
|
||||
mod primitives;
|
||||
mod sizer;
|
||||
mod traits;
|
||||
#[cfg(feature = "std")]
|
||||
mod writer;
|
||||
|
||||
pub use self::primitives::*;
|
||||
pub use self::sizer::*;
|
||||
pub use self::traits::*;
|
||||
#[cfg(feature = "std")]
|
||||
pub use self::writer::*;
|
||||
|
||||
pub use bevy_crevice_derive::AsStd430;
|
|
@ -1,173 +0,0 @@
|
|||
use bytemuck::{Pod, Zeroable};
|
||||
|
||||
use crate::glsl::Glsl;
|
||||
use crate::std430::{Std430, Std430Padded};
|
||||
|
||||
use crate::internal::align_offset;
|
||||
use core::mem::size_of;
|
||||
|
||||
unsafe impl Std430 for f32 {
|
||||
const ALIGNMENT: usize = 4;
|
||||
type Padded = Self;
|
||||
}
|
||||
|
||||
unsafe impl Std430 for f64 {
|
||||
const ALIGNMENT: usize = 8;
|
||||
type Padded = Self;
|
||||
}
|
||||
|
||||
unsafe impl Std430 for i32 {
|
||||
const ALIGNMENT: usize = 4;
|
||||
type Padded = Self;
|
||||
}
|
||||
|
||||
unsafe impl Std430 for u32 {
|
||||
const ALIGNMENT: usize = 4;
|
||||
type Padded = Self;
|
||||
}
|
||||
|
||||
macro_rules! vectors {
|
||||
(
|
||||
$(
|
||||
#[$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,)+
|
||||
}
|
||||
|
||||
unsafe impl Zeroable for $name {}
|
||||
unsafe impl Pod for $name {}
|
||||
|
||||
unsafe impl Std430 for $name {
|
||||
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 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 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 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)
|
||||
|
||||
// bool vectors are disabled due to https://github.com/LPGhatguy/crevice/issues/36
|
||||
|
||||
// #[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 {
|
||||
(
|
||||
$(
|
||||
#[$doc:meta]
|
||||
align($align:literal)
|
||||
$glsl_name:ident $name:ident {
|
||||
$($field:ident: $field_ty:ty,)+
|
||||
}
|
||||
)+
|
||||
) => {
|
||||
$(
|
||||
#[$doc]
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[repr(C)]
|
||||
pub struct $name {
|
||||
$(pub $field: $field_ty,)+
|
||||
}
|
||||
|
||||
unsafe impl Zeroable for $name {}
|
||||
unsafe impl Pod for $name {}
|
||||
|
||||
unsafe impl Std430 for $name {
|
||||
const ALIGNMENT: usize = $align;
|
||||
/// Matrices are technically arrays of primitives, and as such require pad at end.
|
||||
const PAD_AT_END: bool = true;
|
||||
type Padded = 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(8)
|
||||
mat2 Mat2 {
|
||||
x: Vec2,
|
||||
y: Vec2,
|
||||
}
|
||||
|
||||
#[doc = "Corresponds to a GLSL `mat3` in std430 layout."]
|
||||
align(16)
|
||||
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 {
|
||||
x: Vec4,
|
||||
y: Vec4,
|
||||
z: Vec4,
|
||||
w: Vec4,
|
||||
}
|
||||
|
||||
#[doc = "Corresponds to a GLSL `dmat2` in std430 layout."]
|
||||
align(16)
|
||||
dmat2 DMat2 {
|
||||
x: DVec2,
|
||||
y: DVec2,
|
||||
}
|
||||
|
||||
#[doc = "Corresponds to a GLSL `dmat3` in std430 layout."]
|
||||
align(32)
|
||||
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 {
|
||||
x: DVec4,
|
||||
y: DVec4,
|
||||
z: DVec4,
|
||||
w: DVec4,
|
||||
}
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
use core::mem::size_of;
|
||||
|
||||
use crate::internal::align_offset;
|
||||
use crate::std430::{AsStd430, Std430};
|
||||
|
||||
/**
|
||||
Type that computes the buffer size needed by a series of `std430` types laid
|
||||
out.
|
||||
|
||||
This type works well well when paired with `Writer`, precomputing a buffer's
|
||||
size to alleviate the need to dynamically re-allocate buffers.
|
||||
|
||||
## Example
|
||||
|
||||
```glsl
|
||||
struct Frob {
|
||||
vec3 size;
|
||||
float frobiness;
|
||||
}
|
||||
|
||||
buffer FROBS {
|
||||
uint len;
|
||||
Frob[] frobs;
|
||||
} frobs;
|
||||
```
|
||||
|
||||
```
|
||||
use bevy_crevice::std430::{self, AsStd430};
|
||||
|
||||
#[derive(AsStd430)]
|
||||
struct Frob {
|
||||
size: mint::Vector3<f32>,
|
||||
frobiness: f32,
|
||||
}
|
||||
|
||||
// Many APIs require that buffers contain at least enough space for all
|
||||
// fixed-size bindiongs to a buffer as well as one element of any arrays, if
|
||||
// there are any.
|
||||
let mut sizer = std430::Sizer::new();
|
||||
sizer.add::<u32>();
|
||||
sizer.add::<Frob>();
|
||||
|
||||
# fn create_buffer_with_size(size: usize) {}
|
||||
let buffer = create_buffer_with_size(sizer.len());
|
||||
# assert_eq!(sizer.len(), 32);
|
||||
```
|
||||
*/
|
||||
pub struct Sizer {
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
impl Sizer {
|
||||
/// Create a new `Sizer`.
|
||||
pub fn new() -> Self {
|
||||
Self { offset: 0 }
|
||||
}
|
||||
|
||||
/// Add a type's necessary padding and size to the `Sizer`. Returns the
|
||||
/// offset into the buffer where that type would be written.
|
||||
pub fn add<T>(&mut self) -> usize
|
||||
where
|
||||
T: AsStd430,
|
||||
{
|
||||
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;
|
||||
let write_here = self.offset;
|
||||
|
||||
self.offset += size;
|
||||
|
||||
write_here
|
||||
}
|
||||
|
||||
/// Returns the number of bytes required to contain all the types added to
|
||||
/// the `Sizer`.
|
||||
pub fn len(&self) -> usize {
|
||||
self.offset
|
||||
}
|
||||
}
|
|
@ -1,291 +0,0 @@
|
|||
use core::mem::{size_of, MaybeUninit};
|
||||
#[cfg(feature = "std")]
|
||||
use std::io::{self, Write};
|
||||
|
||||
use bytemuck::{bytes_of, Pod, Zeroable};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use crate::std430::Writer;
|
||||
|
||||
/// Trait implemented for all `std430` primitives. Generally should not be
|
||||
/// implemented outside this crate.
|
||||
pub unsafe trait Std430: Copy + Zeroable + Pod {
|
||||
/// The required alignment of the type. Must be a power of two.
|
||||
///
|
||||
/// This is distinct from the value returned by `std::mem::align_of` because
|
||||
/// `AsStd430` structs do not use Rust's alignment. This enables them to
|
||||
/// 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>
|
||||
/// (rule 4 and 9)
|
||||
const PAD_AT_END: bool = false;
|
||||
/// Padded type (Std430Padded specialization)
|
||||
/// The usual implementation is
|
||||
/// type Padded = Std430Padded<Self, {align_offset(size_of::<Self>(), ALIGNMENT)}>;
|
||||
type Padded: Std430Convertible<Self>;
|
||||
|
||||
/// Casts the type to a byte array. Implementors should not override this
|
||||
/// method.
|
||||
///
|
||||
/// # Safety
|
||||
/// This is always safe due to the requirements of [`bytemuck::Pod`] being a
|
||||
/// prerequisite for this trait.
|
||||
fn as_bytes(&self) -> &[u8] {
|
||||
bytes_of(self)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Std430 for () {
|
||||
const ALIGNMENT: usize = 0;
|
||||
|
||||
const PAD_AT_END: bool = false;
|
||||
|
||||
type Padded = ();
|
||||
}
|
||||
|
||||
/// Trait specifically for Std430::Padded, implements conversions between padded type and base type.
|
||||
pub trait Std430Convertible<T: Std430>: Copy {
|
||||
/// Convert from self to Std430
|
||||
fn into_std430(self) -> T;
|
||||
/// Convert from Std430 to self
|
||||
fn from_std430(_: T) -> Self;
|
||||
}
|
||||
|
||||
impl<T: Std430> Std430Convertible<T> for T {
|
||||
fn into_std430(self) -> T {
|
||||
self
|
||||
}
|
||||
fn from_std430(also_self: T) -> Self {
|
||||
also_self
|
||||
}
|
||||
}
|
||||
|
||||
/// Unfortunately, we cannot easily derive padded representation for generic Std140 types.
|
||||
/// For now, we'll just use this empty enum with no values.
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum InvalidPadded {}
|
||||
impl<T: Std430> Std430Convertible<T> for InvalidPadded {
|
||||
fn into_std430(self) -> T {
|
||||
unimplemented!()
|
||||
}
|
||||
fn from_std430(_: T) -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
/**
|
||||
Trait implemented for all types that can be turned into `std430` values.
|
||||
|
||||
This trait can often be `#[derive]`'d instead of manually implementing it. Any
|
||||
struct which contains only fields that also implement `AsStd430` can derive
|
||||
`AsStd430`.
|
||||
|
||||
Types from the mint crate implement `AsStd430`, making them convenient for use
|
||||
in uniform types. Most Rust geometry crates, like cgmath, nalgebra, and
|
||||
ultraviolet support mint.
|
||||
|
||||
## Example
|
||||
|
||||
```glsl
|
||||
uniform CAMERA {
|
||||
mat4 view;
|
||||
mat4 projection;
|
||||
} camera;
|
||||
```
|
||||
|
||||
```no_run
|
||||
use bevy_crevice::std430::{AsStd430, Std430};
|
||||
|
||||
#[derive(AsStd430)]
|
||||
struct CameraUniform {
|
||||
view: mint::ColumnMatrix4<f32>,
|
||||
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,
|
||||
projection,
|
||||
};
|
||||
|
||||
# fn write_to_gpu_buffer(bytes: &[u8]) {}
|
||||
let camera_std430 = camera.as_std430();
|
||||
write_to_gpu_buffer(camera_std430.as_bytes());
|
||||
```
|
||||
*/
|
||||
pub trait AsStd430 {
|
||||
/// The `std430` version of this value.
|
||||
type Output: Std430;
|
||||
|
||||
/// Convert this value into the `std430` version of itself.
|
||||
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::Output>()
|
||||
}
|
||||
|
||||
/// Converts from `std430` version of self to self.
|
||||
fn from_std430(value: Self::Output) -> Self;
|
||||
}
|
||||
|
||||
impl<T> AsStd430 for T
|
||||
where
|
||||
T: Std430,
|
||||
{
|
||||
type Output = Self;
|
||||
|
||||
fn as_std430(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
|
||||
fn from_std430(value: Self) -> Self {
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Std430Padded<T: Std430, const PAD: usize> {
|
||||
inner: T,
|
||||
_padding: [u8; PAD],
|
||||
}
|
||||
|
||||
unsafe impl<T: Std430, const PAD: usize> Zeroable for Std430Padded<T, PAD> {}
|
||||
unsafe impl<T: Std430, const PAD: usize> Pod for Std430Padded<T, PAD> {}
|
||||
|
||||
impl<T: Std430, const PAD: usize> Std430Convertible<T> for Std430Padded<T, PAD> {
|
||||
fn into_std430(self) -> T {
|
||||
self.inner
|
||||
}
|
||||
|
||||
fn from_std430(inner: T) -> Self {
|
||||
Self {
|
||||
inner,
|
||||
_padding: [0u8; PAD],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(transparent)]
|
||||
pub struct Std430Array<T: Std430, const N: usize>([T::Padded; N]);
|
||||
|
||||
unsafe impl<T: Std430, const N: usize> Zeroable for Std430Array<T, N> where T::Padded: Zeroable {}
|
||||
unsafe impl<T: Std430, const N: usize> Pod for Std430Array<T, N> where T::Padded: Pod {}
|
||||
unsafe impl<T: Std430, const N: usize> Std430 for Std430Array<T, N>
|
||||
where
|
||||
T::Padded: Pod,
|
||||
{
|
||||
const ALIGNMENT: usize = T::ALIGNMENT;
|
||||
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::Output as Std430>::Padded: Pod,
|
||||
{
|
||||
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()));
|
||||
}
|
||||
|
||||
Self::Output::from_uninit_array(res)
|
||||
}
|
||||
|
||||
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(T::from_std430(val.0[i].into_std430()));
|
||||
}
|
||||
unsafe { core::mem::transmute_copy(&res) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait implemented for all types that can be written into a buffer as
|
||||
/// `std430` bytes. This type is more general than [`AsStd430`]: all `AsStd430`
|
||||
/// types implement `WriteStd430`, but not the other way around.
|
||||
///
|
||||
/// While `AsStd430` requires implementers to return a type that implements the
|
||||
/// `Std430` trait, `WriteStd430` directly writes bytes using a [`Writer`]. This
|
||||
/// makes `WriteStd430` usable for writing slices or other DSTs that could not
|
||||
/// implement `AsStd430` without allocating new memory on the heap.
|
||||
#[cfg(feature = "std")]
|
||||
pub trait WriteStd430 {
|
||||
/// Writes this value into the given [`Writer`] using `std430` layout rules.
|
||||
///
|
||||
/// Should return the offset of the first byte of this type, as returned by
|
||||
/// the first call to [`Writer::write`].
|
||||
fn write_std430<W: Write>(&self, writer: &mut Writer<W>) -> io::Result<usize>;
|
||||
|
||||
/// The space required to write this value using `std430` layout rules. This
|
||||
/// does not include alignment padding that may be needed before or after
|
||||
/// this type when written as part of a larger buffer.
|
||||
fn std430_size(&self) -> usize {
|
||||
let mut writer = Writer::new(io::sink());
|
||||
self.write_std430(&mut writer).unwrap();
|
||||
writer.len()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<T> WriteStd430 for T
|
||||
where
|
||||
T: AsStd430,
|
||||
{
|
||||
fn write_std430<W: Write>(&self, writer: &mut Writer<W>) -> io::Result<usize> {
|
||||
writer.write_std430(&self.as_std430())
|
||||
}
|
||||
|
||||
fn std430_size(&self) -> usize {
|
||||
size_of::<<Self as AsStd430>::Output>()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<T> WriteStd430 for [T]
|
||||
where
|
||||
T: WriteStd430,
|
||||
{
|
||||
fn write_std430<W: Write>(&self, writer: &mut Writer<W>) -> io::Result<usize> {
|
||||
let mut offset = writer.len();
|
||||
|
||||
let mut iter = self.iter();
|
||||
|
||||
if let Some(item) = iter.next() {
|
||||
offset = item.write_std430(writer)?;
|
||||
}
|
||||
|
||||
for item in iter {
|
||||
item.write_std430(writer)?;
|
||||
}
|
||||
|
||||
Ok(offset)
|
||||
}
|
||||
|
||||
fn std430_size(&self) -> usize {
|
||||
let mut writer = Writer::new(io::sink());
|
||||
self.write_std430(&mut writer).unwrap();
|
||||
writer.len()
|
||||
}
|
||||
}
|
|
@ -1,150 +0,0 @@
|
|||
use std::io::{self, Write};
|
||||
use std::mem::size_of;
|
||||
|
||||
use bytemuck::bytes_of;
|
||||
|
||||
use crate::internal::align_offset;
|
||||
use crate::std430::{AsStd430, Std430, WriteStd430};
|
||||
|
||||
/**
|
||||
Type that enables writing correctly aligned `std430` values to a buffer.
|
||||
|
||||
`Writer` is useful when many values need to be laid out in a row that cannot be
|
||||
represented by a struct alone, like dynamically sized arrays or dynamically
|
||||
laid-out values.
|
||||
|
||||
## Example
|
||||
In this example, we'll write a length-prefixed list of lights to a buffer.
|
||||
`std430::Writer` helps align correctly, even across multiple structs, which can
|
||||
be tricky and error-prone otherwise.
|
||||
|
||||
```glsl
|
||||
struct PointLight {
|
||||
vec3 position;
|
||||
vec3 color;
|
||||
float brightness;
|
||||
};
|
||||
|
||||
buffer POINT_LIGHTS {
|
||||
uint len;
|
||||
PointLight[] lights;
|
||||
} point_lights;
|
||||
```
|
||||
|
||||
```
|
||||
use bevy_crevice::std430::{self, AsStd430};
|
||||
|
||||
#[derive(AsStd430)]
|
||||
struct PointLight {
|
||||
position: mint::Vector3<f32>,
|
||||
color: mint::Vector3<f32>,
|
||||
brightness: f32,
|
||||
}
|
||||
|
||||
let lights = vec![
|
||||
PointLight {
|
||||
position: [0.0, 1.0, 0.0].into(),
|
||||
color: [1.0, 0.0, 0.0].into(),
|
||||
brightness: 0.6,
|
||||
},
|
||||
PointLight {
|
||||
position: [0.0, 4.0, 3.0].into(),
|
||||
color: [1.0, 1.0, 1.0].into(),
|
||||
brightness: 1.0,
|
||||
},
|
||||
];
|
||||
|
||||
# fn map_gpu_buffer_for_write() -> &'static mut [u8] {
|
||||
# Box::leak(vec![0; 1024].into_boxed_slice())
|
||||
# }
|
||||
let target_buffer = map_gpu_buffer_for_write();
|
||||
let mut writer = std430::Writer::new(target_buffer);
|
||||
|
||||
let light_count = lights.len() as u32;
|
||||
writer.write(&light_count)?;
|
||||
|
||||
// Crevice will automatically insert the required padding to align the
|
||||
// PointLight structure correctly. In this case, there will be 12 bytes of
|
||||
// padding between the length field and the light list.
|
||||
|
||||
writer.write(lights.as_slice())?;
|
||||
|
||||
# fn unmap_gpu_buffer() {}
|
||||
unmap_gpu_buffer();
|
||||
|
||||
# Ok::<(), std::io::Error>(())
|
||||
```
|
||||
*/
|
||||
pub struct Writer<W> {
|
||||
writer: W,
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
impl<W: Write> Writer<W> {
|
||||
/// Create a new `Writer`, wrapping a buffer, file, or other type that
|
||||
/// implements [`std::io::Write`].
|
||||
pub fn new(writer: W) -> Self {
|
||||
Self { writer, offset: 0 }
|
||||
}
|
||||
|
||||
/// Write a new value to the underlying buffer, writing zeroed padding where
|
||||
/// necessary.
|
||||
///
|
||||
/// Returns the offset into the buffer that the value was written to.
|
||||
pub fn write<T>(&mut self, value: &T) -> io::Result<usize>
|
||||
where
|
||||
T: WriteStd430 + ?Sized,
|
||||
{
|
||||
value.write_std430(self)
|
||||
}
|
||||
|
||||
/// Write an iterator of values to the underlying buffer.
|
||||
///
|
||||
/// Returns the offset into the buffer that the first value was written to.
|
||||
/// If no values were written, returns the `len()`.
|
||||
pub fn write_iter<I, T>(&mut self, iter: I) -> io::Result<usize>
|
||||
where
|
||||
I: IntoIterator<Item = T>,
|
||||
T: WriteStd430,
|
||||
{
|
||||
let mut offset = self.offset;
|
||||
|
||||
let mut iter = iter.into_iter();
|
||||
|
||||
if let Some(item) = iter.next() {
|
||||
offset = item.write_std430(self)?;
|
||||
}
|
||||
|
||||
for item in iter {
|
||||
item.write_std430(self)?;
|
||||
}
|
||||
|
||||
Ok(offset)
|
||||
}
|
||||
|
||||
/// Write an `Std430` type to the underlying buffer.
|
||||
pub fn write_std430<T>(&mut self, value: &T) -> io::Result<usize>
|
||||
where
|
||||
T: Std430,
|
||||
{
|
||||
let padding = align_offset(self.offset, T::ALIGNMENT);
|
||||
|
||||
for _ in 0..padding {
|
||||
self.writer.write_all(&[0])?;
|
||||
}
|
||||
self.offset += padding;
|
||||
|
||||
let value = value.as_std430();
|
||||
self.writer.write_all(bytes_of(&value))?;
|
||||
|
||||
let write_here = self.offset;
|
||||
self.offset += size_of::<T>();
|
||||
|
||||
Ok(write_here)
|
||||
}
|
||||
|
||||
/// Returns the amount of data written by this `Writer`.
|
||||
pub fn len(&self) -> usize {
|
||||
self.offset
|
||||
}
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
#![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;
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
source: tests/test.rs
|
||||
expression: "TestGlsl::glsl_definition()"
|
||||
|
||||
---
|
||||
struct TestGlsl {
|
||||
vec3 foo[8][4];
|
||||
};
|
|
@ -1,9 +0,0 @@
|
|||
---
|
||||
source: tests/test.rs
|
||||
expression: "TestGlsl::glsl_definition()"
|
||||
|
||||
---
|
||||
struct TestGlsl {
|
||||
vec3 foo;
|
||||
mat2 bar;
|
||||
};
|
|
@ -1,61 +0,0 @@
|
|||
use bevy_crevice::glsl::GlslStruct;
|
||||
use bevy_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());
|
||||
}
|
16
crates/bevy_encase_derive/Cargo.toml
Normal file
16
crates/bevy_encase_derive/Cargo.toml
Normal file
|
@ -0,0 +1,16 @@
|
|||
[package]
|
||||
name = "bevy_encase_derive"
|
||||
version = "0.8.0-dev"
|
||||
edition = "2021"
|
||||
description = "Bevy derive macro for encase"
|
||||
homepage = "https://bevyengine.org"
|
||||
repository = "https://github.com/bevyengine/bevy"
|
||||
license = "MIT OR Apache-2.0"
|
||||
keywords = ["bevy"]
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
bevy_macro_utils = { path = "../bevy_macro_utils", version = "0.8.0-dev" }
|
||||
encase_derive_impl = "0.2"
|
40
crates/bevy_encase_derive/src/lib.rs
Normal file
40
crates/bevy_encase_derive/src/lib.rs
Normal file
|
@ -0,0 +1,40 @@
|
|||
use bevy_macro_utils::BevyManifest;
|
||||
use encase_derive_impl::{implement, syn};
|
||||
|
||||
const BEVY: &str = "bevy";
|
||||
const BEVY_RENDER: &str = "bevy_render";
|
||||
const ENCASE: &str = "encase";
|
||||
|
||||
fn bevy_encase_path() -> syn::Path {
|
||||
let bevy_manifest = BevyManifest::default();
|
||||
bevy_manifest
|
||||
.maybe_get_path(BEVY)
|
||||
.map(|bevy_path| {
|
||||
let mut segments = bevy_path.segments;
|
||||
segments.push(BevyManifest::parse_str("render"));
|
||||
syn::Path {
|
||||
leading_colon: None,
|
||||
segments,
|
||||
}
|
||||
})
|
||||
.or_else(|| bevy_manifest.maybe_get_path(BEVY_RENDER))
|
||||
.map(|bevy_render_path| {
|
||||
let mut segments = bevy_render_path.segments;
|
||||
segments.push(BevyManifest::parse_str("render_resource"));
|
||||
syn::Path {
|
||||
leading_colon: None,
|
||||
segments,
|
||||
}
|
||||
})
|
||||
.map(|path| {
|
||||
let mut segments = path.segments;
|
||||
segments.push(BevyManifest::parse_str(ENCASE));
|
||||
syn::Path {
|
||||
leading_colon: None,
|
||||
segments,
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| bevy_manifest.get_path(ENCASE))
|
||||
}
|
||||
|
||||
implement!(bevy_encase_path());
|
|
@ -8,10 +8,7 @@ use bevy_render::{
|
|||
mesh::MeshVertexBufferLayout,
|
||||
prelude::Shader,
|
||||
render_asset::{PrepareAssetError, RenderAsset, RenderAssets},
|
||||
render_resource::{
|
||||
std140::{AsStd140, Std140},
|
||||
*,
|
||||
},
|
||||
render_resource::*,
|
||||
renderer::RenderDevice,
|
||||
texture::Image,
|
||||
};
|
||||
|
@ -140,7 +137,7 @@ bitflags::bitflags! {
|
|||
}
|
||||
|
||||
/// The GPU representation of the uniform data of a [`StandardMaterial`].
|
||||
#[derive(Clone, Default, AsStd140)]
|
||||
#[derive(Clone, Default, ShaderType)]
|
||||
pub struct StandardMaterialUniformData {
|
||||
/// Doubles as diffuse albedo for non-metallic, specular for metallic and a mix for everything
|
||||
/// in between.
|
||||
|
@ -296,12 +293,15 @@ impl RenderAsset for StandardMaterial {
|
|||
flags: flags.bits(),
|
||||
alpha_cutoff,
|
||||
};
|
||||
let value_std140 = value.as_std140();
|
||||
|
||||
let byte_buffer = [0u8; StandardMaterialUniformData::SIZE.get() as usize];
|
||||
let mut buffer = encase::UniformBuffer::new(byte_buffer);
|
||||
buffer.write(&value).unwrap();
|
||||
|
||||
let buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
|
||||
label: Some("pbr_standard_material_uniform_buffer"),
|
||||
usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
|
||||
contents: value_std140.as_bytes(),
|
||||
contents: buffer.as_ref(),
|
||||
});
|
||||
let bind_group = render_device.create_bind_group(&BindGroupDescriptor {
|
||||
entries: &[
|
||||
|
@ -423,9 +423,7 @@ impl SpecializedMaterial for StandardMaterial {
|
|||
ty: BindingType::Buffer {
|
||||
ty: BufferBindingType::Uniform,
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: BufferSize::new(
|
||||
StandardMaterialUniformData::std140_size_static() as u64,
|
||||
),
|
||||
min_binding_size: Some(StandardMaterialUniformData::min_size()),
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
|
|
|
@ -21,7 +21,7 @@ use bevy_render::{
|
|||
EntityRenderCommand, PhaseItem, RenderCommandResult, RenderPhase, SetItemPipeline,
|
||||
TrackedRenderPass,
|
||||
},
|
||||
render_resource::{std140::AsStd140, std430::AsStd430, *},
|
||||
render_resource::*,
|
||||
renderer::{RenderContext, RenderDevice, RenderQueue},
|
||||
texture::*,
|
||||
view::{
|
||||
|
@ -34,7 +34,7 @@ use bevy_utils::{
|
|||
tracing::{error, warn},
|
||||
HashMap,
|
||||
};
|
||||
use std::num::NonZeroU32;
|
||||
use std::num::{NonZeroU32, NonZeroU64};
|
||||
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemLabel)]
|
||||
pub enum RenderLightSystems {
|
||||
|
@ -78,8 +78,7 @@ pub struct ExtractedDirectionalLight {
|
|||
|
||||
pub type ExtractedDirectionalLightShadowMap = DirectionalLightShadowMap;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, AsStd140, AsStd430, Default, Debug)]
|
||||
#[derive(Copy, Clone, ShaderType, Default, Debug)]
|
||||
pub struct GpuPointLight {
|
||||
// The lower-right 2x2 values of the projection matrix 22 23 32 33
|
||||
projection_lr: Vec4,
|
||||
|
@ -90,13 +89,28 @@ pub struct GpuPointLight {
|
|||
shadow_normal_bias: f32,
|
||||
}
|
||||
|
||||
#[derive(ShaderType)]
|
||||
pub struct GpuPointLightsUniform {
|
||||
data: Box<[GpuPointLight; MAX_UNIFORM_BUFFER_POINT_LIGHTS]>,
|
||||
}
|
||||
|
||||
impl Default for GpuPointLightsUniform {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
data: Box::new([GpuPointLight::default(); MAX_UNIFORM_BUFFER_POINT_LIGHTS]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(ShaderType, Default)]
|
||||
pub struct GpuPointLightsStorage {
|
||||
#[size(runtime)]
|
||||
data: Vec<GpuPointLight>,
|
||||
}
|
||||
|
||||
pub enum GpuPointLights {
|
||||
Uniform {
|
||||
buffer: UniformVec<[GpuPointLight; MAX_UNIFORM_BUFFER_POINT_LIGHTS]>,
|
||||
},
|
||||
Storage {
|
||||
buffer: StorageBuffer<GpuPointLight>,
|
||||
},
|
||||
Uniform(UniformBuffer<GpuPointLightsUniform>),
|
||||
Storage(StorageBuffer<GpuPointLightsStorage>),
|
||||
}
|
||||
|
||||
impl GpuPointLights {
|
||||
|
@ -108,66 +122,48 @@ impl GpuPointLights {
|
|||
}
|
||||
|
||||
fn uniform() -> Self {
|
||||
Self::Uniform {
|
||||
buffer: UniformVec::default(),
|
||||
}
|
||||
Self::Uniform(UniformBuffer::default())
|
||||
}
|
||||
|
||||
fn storage() -> Self {
|
||||
Self::Storage {
|
||||
buffer: StorageBuffer::default(),
|
||||
}
|
||||
Self::Storage(StorageBuffer::default())
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
fn set(&mut self, mut lights: Vec<GpuPointLight>) {
|
||||
match self {
|
||||
GpuPointLights::Uniform { buffer } => buffer.clear(),
|
||||
GpuPointLights::Storage { buffer } => buffer.clear(),
|
||||
}
|
||||
}
|
||||
|
||||
fn push(&mut self, mut lights: Vec<GpuPointLight>) {
|
||||
match self {
|
||||
GpuPointLights::Uniform { buffer } => {
|
||||
// NOTE: This iterator construction allows moving and padding with default
|
||||
// values and is like this to avoid unnecessary cloning.
|
||||
let gpu_point_lights = lights
|
||||
.drain(..)
|
||||
.chain(std::iter::repeat_with(GpuPointLight::default))
|
||||
.take(MAX_UNIFORM_BUFFER_POINT_LIGHTS)
|
||||
.collect::<Vec<_>>();
|
||||
buffer.push(gpu_point_lights.try_into().unwrap());
|
||||
GpuPointLights::Uniform(buffer) => {
|
||||
let len = lights.len().min(MAX_UNIFORM_BUFFER_POINT_LIGHTS);
|
||||
let src = &lights[..len];
|
||||
let dst = &mut buffer.get_mut().data[..len];
|
||||
dst.copy_from_slice(src);
|
||||
}
|
||||
GpuPointLights::Storage { buffer } => {
|
||||
buffer.append(&mut lights);
|
||||
GpuPointLights::Storage(buffer) => {
|
||||
buffer.get_mut().data.clear();
|
||||
buffer.get_mut().data.append(&mut lights);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write_buffer(&mut self, render_device: &RenderDevice, render_queue: &RenderQueue) {
|
||||
match self {
|
||||
GpuPointLights::Uniform { buffer } => buffer.write_buffer(render_device, render_queue),
|
||||
GpuPointLights::Storage { buffer } => buffer.write_buffer(render_device, render_queue),
|
||||
GpuPointLights::Uniform(buffer) => buffer.write_buffer(render_device, render_queue),
|
||||
GpuPointLights::Storage(buffer) => buffer.write_buffer(render_device, render_queue),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn binding(&self) -> Option<BindingResource> {
|
||||
match self {
|
||||
GpuPointLights::Uniform { buffer } => buffer.binding(),
|
||||
GpuPointLights::Storage { buffer } => buffer.binding(),
|
||||
GpuPointLights::Uniform(buffer) => buffer.binding(),
|
||||
GpuPointLights::Storage(buffer) => buffer.binding(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
match self {
|
||||
GpuPointLights::Uniform { buffer } => buffer.len(),
|
||||
GpuPointLights::Storage { buffer } => buffer.values().len(),
|
||||
pub fn min_size(buffer_binding_type: BufferBindingType) -> NonZeroU64 {
|
||||
match buffer_binding_type {
|
||||
BufferBindingType::Storage { .. } => GpuPointLightsStorage::min_size(),
|
||||
BufferBindingType::Uniform => GpuPointLightsUniform::min_size(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: These must match the bit flags in bevy_pbr2/src/render/pbr.frag!
|
||||
|
@ -180,8 +176,7 @@ bitflags::bitflags! {
|
|||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, AsStd140, Default, Debug)]
|
||||
#[derive(Copy, Clone, ShaderType, Default, Debug)]
|
||||
pub struct GpuDirectionalLight {
|
||||
view_projection: Mat4,
|
||||
color: Vec4,
|
||||
|
@ -201,10 +196,8 @@ bitflags::bitflags! {
|
|||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, AsStd140)]
|
||||
#[derive(Copy, Clone, Debug, ShaderType)]
|
||||
pub struct GpuLights {
|
||||
// TODO: this comes first to work around a WGSL alignment issue. We need to solve this issue before releasing the renderer rework
|
||||
directional_lights: [GpuDirectionalLight; MAX_DIRECTIONAL_LIGHTS],
|
||||
ambient_color: Vec4,
|
||||
// xyz are x/y/z cluster dimensions and w is the number of clusters
|
||||
|
@ -245,7 +238,7 @@ impl FromWorld for ShadowPipeline {
|
|||
ty: BindingType::Buffer {
|
||||
ty: BufferBindingType::Uniform,
|
||||
has_dynamic_offset: true,
|
||||
min_binding_size: BufferSize::new(ViewUniform::std140_size_static() as u64),
|
||||
min_binding_size: Some(ViewUniform::min_size()),
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
|
@ -629,7 +622,7 @@ impl GlobalLightMeta {
|
|||
|
||||
#[derive(Default)]
|
||||
pub struct LightMeta {
|
||||
pub view_gpu_lights: DynamicUniformVec<GpuLights>,
|
||||
pub view_gpu_lights: DynamicUniformBuffer<GpuLights>,
|
||||
pub shadow_view_bind_group: Option<BindGroup>,
|
||||
}
|
||||
|
||||
|
@ -688,7 +681,6 @@ pub fn prepare_lights(
|
|||
.map(|CubeMapFace { target, up }| GlobalTransform::identity().looking_at(*target, *up))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
global_light_meta.gpu_point_lights.clear();
|
||||
global_light_meta.entity_to_index.clear();
|
||||
|
||||
let mut point_lights: Vec<_> = point_lights.iter().collect::<Vec<_>>();
|
||||
|
@ -744,7 +736,8 @@ pub fn prepare_lights(
|
|||
});
|
||||
global_light_meta.entity_to_index.insert(entity, index);
|
||||
}
|
||||
global_light_meta.gpu_point_lights.push(gpu_point_lights);
|
||||
|
||||
global_light_meta.gpu_point_lights.set(gpu_point_lights);
|
||||
global_light_meta
|
||||
.gpu_point_lights
|
||||
.write_buffer(&render_device, &render_queue);
|
||||
|
@ -1027,16 +1020,58 @@ fn pack_offset_and_count(offset: usize, count: usize) -> u32 {
|
|||
| (count as u32 & CLUSTER_COUNT_MASK)
|
||||
}
|
||||
|
||||
#[derive(ShaderType)]
|
||||
struct GpuClusterLightIndexListsUniform {
|
||||
data: Box<[UVec4; ViewClusterBindings::MAX_UNIFORM_ITEMS]>,
|
||||
}
|
||||
|
||||
// NOTE: Assert at compile time that GpuClusterLightIndexListsUniform
|
||||
// fits within the maximum uniform buffer binding size
|
||||
const _: () = assert!(GpuClusterLightIndexListsUniform::SIZE.get() <= 16384);
|
||||
|
||||
impl Default for GpuClusterLightIndexListsUniform {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
data: Box::new([UVec4::ZERO; ViewClusterBindings::MAX_UNIFORM_ITEMS]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(ShaderType)]
|
||||
struct GpuClusterOffsetsAndCountsUniform {
|
||||
data: Box<[UVec4; ViewClusterBindings::MAX_UNIFORM_ITEMS]>,
|
||||
}
|
||||
|
||||
impl Default for GpuClusterOffsetsAndCountsUniform {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
data: Box::new([UVec4::ZERO; ViewClusterBindings::MAX_UNIFORM_ITEMS]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(ShaderType, Default)]
|
||||
struct GpuClusterLightIndexListsStorage {
|
||||
#[size(runtime)]
|
||||
data: Vec<u32>,
|
||||
}
|
||||
|
||||
#[derive(ShaderType, Default)]
|
||||
struct GpuClusterOffsetsAndCountsStorage {
|
||||
#[size(runtime)]
|
||||
data: Vec<UVec2>,
|
||||
}
|
||||
|
||||
enum ViewClusterBuffers {
|
||||
Uniform {
|
||||
// NOTE: UVec4 is because all arrays in Std140 layout have 16-byte alignment
|
||||
cluster_light_index_lists: UniformVec<[UVec4; ViewClusterBindings::MAX_UNIFORM_ITEMS]>,
|
||||
cluster_light_index_lists: UniformBuffer<GpuClusterLightIndexListsUniform>,
|
||||
// NOTE: UVec4 is because all arrays in Std140 layout have 16-byte alignment
|
||||
cluster_offsets_and_counts: UniformVec<[UVec4; ViewClusterBindings::MAX_UNIFORM_ITEMS]>,
|
||||
cluster_offsets_and_counts: UniformBuffer<GpuClusterOffsetsAndCountsUniform>,
|
||||
},
|
||||
Storage {
|
||||
cluster_light_index_lists: StorageBuffer<u32>,
|
||||
cluster_offsets_and_counts: StorageBuffer<UVec2>,
|
||||
cluster_light_index_lists: StorageBuffer<GpuClusterLightIndexListsStorage>,
|
||||
cluster_offsets_and_counts: StorageBuffer<GpuClusterOffsetsAndCountsStorage>,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -1050,8 +1085,8 @@ impl ViewClusterBuffers {
|
|||
|
||||
fn uniform() -> Self {
|
||||
ViewClusterBuffers::Uniform {
|
||||
cluster_light_index_lists: UniformVec::default(),
|
||||
cluster_offsets_and_counts: UniformVec::default(),
|
||||
cluster_light_index_lists: UniformBuffer::default(),
|
||||
cluster_offsets_and_counts: UniformBuffer::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1083,24 +1118,22 @@ impl ViewClusterBindings {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn reserve_and_clear(&mut self) {
|
||||
pub fn clear(&mut self) {
|
||||
match &mut self.buffers {
|
||||
ViewClusterBuffers::Uniform {
|
||||
cluster_light_index_lists,
|
||||
cluster_offsets_and_counts,
|
||||
} => {
|
||||
cluster_light_index_lists.clear();
|
||||
cluster_light_index_lists.push([UVec4::ZERO; Self::MAX_UNIFORM_ITEMS]);
|
||||
cluster_offsets_and_counts.clear();
|
||||
cluster_offsets_and_counts.push([UVec4::ZERO; Self::MAX_UNIFORM_ITEMS]);
|
||||
*cluster_light_index_lists.get_mut().data = [UVec4::ZERO; Self::MAX_UNIFORM_ITEMS];
|
||||
*cluster_offsets_and_counts.get_mut().data = [UVec4::ZERO; Self::MAX_UNIFORM_ITEMS];
|
||||
}
|
||||
ViewClusterBuffers::Storage {
|
||||
cluster_light_index_lists,
|
||||
cluster_offsets_and_counts,
|
||||
..
|
||||
} => {
|
||||
cluster_light_index_lists.clear();
|
||||
cluster_offsets_and_counts.clear();
|
||||
cluster_light_index_lists.get_mut().data.clear();
|
||||
cluster_offsets_and_counts.get_mut().data.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1119,13 +1152,16 @@ impl ViewClusterBindings {
|
|||
let component = self.n_offsets & ((1 << 2) - 1);
|
||||
let packed = pack_offset_and_count(offset, count);
|
||||
|
||||
cluster_offsets_and_counts.get_mut(0)[array_index][component] = packed;
|
||||
cluster_offsets_and_counts.get_mut().data[array_index][component] = packed;
|
||||
}
|
||||
ViewClusterBuffers::Storage {
|
||||
cluster_offsets_and_counts,
|
||||
..
|
||||
} => {
|
||||
cluster_offsets_and_counts.push(UVec2::new(offset as u32, count as u32));
|
||||
cluster_offsets_and_counts
|
||||
.get_mut()
|
||||
.data
|
||||
.push(UVec2::new(offset as u32, count as u32));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1147,14 +1183,14 @@ impl ViewClusterBindings {
|
|||
let sub_index = self.n_indices & ((1 << 2) - 1);
|
||||
let index = index as u32 & POINT_LIGHT_INDEX_MASK;
|
||||
|
||||
cluster_light_index_lists.get_mut(0)[array_index][component] |=
|
||||
cluster_light_index_lists.get_mut().data[array_index][component] |=
|
||||
index << (8 * sub_index);
|
||||
}
|
||||
ViewClusterBuffers::Storage {
|
||||
cluster_light_index_lists,
|
||||
..
|
||||
} => {
|
||||
cluster_light_index_lists.push(index as u32);
|
||||
cluster_light_index_lists.get_mut().data.push(index as u32);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1205,6 +1241,24 @@ impl ViewClusterBindings {
|
|||
} => cluster_offsets_and_counts.binding(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn min_size_cluster_light_index_lists(
|
||||
buffer_binding_type: BufferBindingType,
|
||||
) -> NonZeroU64 {
|
||||
match buffer_binding_type {
|
||||
BufferBindingType::Storage { .. } => GpuClusterLightIndexListsStorage::min_size(),
|
||||
BufferBindingType::Uniform => GpuClusterLightIndexListsUniform::min_size(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn min_size_cluster_offsets_and_counts(
|
||||
buffer_binding_type: BufferBindingType,
|
||||
) -> NonZeroU64 {
|
||||
match buffer_binding_type {
|
||||
BufferBindingType::Storage { .. } => GpuClusterOffsetsAndCountsStorage::min_size(),
|
||||
BufferBindingType::Uniform => GpuClusterOffsetsAndCountsUniform::min_size(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn prepare_clusters(
|
||||
|
@ -1230,7 +1284,7 @@ pub fn prepare_clusters(
|
|||
for (entity, cluster_config, extracted_clusters) in views.iter() {
|
||||
let mut view_clusters_bindings =
|
||||
ViewClusterBindings::new(mesh_pipeline.clustered_forward_buffer_binding_type);
|
||||
view_clusters_bindings.reserve_and_clear();
|
||||
view_clusters_bindings.clear();
|
||||
|
||||
let mut indices_full = false;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
GlobalLightMeta, GpuLights, LightMeta, NotShadowCaster, NotShadowReceiver, ShadowPipeline,
|
||||
ViewClusterBindings, ViewLightsUniformOffset, ViewShadowBindings,
|
||||
GlobalLightMeta, GpuLights, GpuPointLights, LightMeta, NotShadowCaster, NotShadowReceiver,
|
||||
ShadowPipeline, ViewClusterBindings, ViewLightsUniformOffset, ViewShadowBindings,
|
||||
CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT,
|
||||
};
|
||||
use bevy_app::Plugin;
|
||||
|
@ -19,7 +19,7 @@ use bevy_render::{
|
|||
render_asset::RenderAssets,
|
||||
render_component::{ComponentUniforms, DynamicUniformIndex, UniformComponentPlugin},
|
||||
render_phase::{EntityRenderCommand, RenderCommandResult, TrackedRenderPass},
|
||||
render_resource::{std140::AsStd140, *},
|
||||
render_resource::*,
|
||||
renderer::{RenderDevice, RenderQueue},
|
||||
texture::{BevyDefault, GpuImage, Image, TextureFormatPixelInfo},
|
||||
view::{ComputedVisibility, ViewUniform, ViewUniformOffset, ViewUniforms},
|
||||
|
@ -76,7 +76,7 @@ impl Plugin for MeshRenderPlugin {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Component, AsStd140, Clone)]
|
||||
#[derive(Component, ShaderType, Clone)]
|
||||
pub struct MeshUniform {
|
||||
pub transform: Mat4,
|
||||
pub inverse_transpose_model: Mat4,
|
||||
|
@ -267,10 +267,7 @@ impl FromWorld for MeshPipeline {
|
|||
let render_device = world.resource::<RenderDevice>();
|
||||
let clustered_forward_buffer_binding_type = render_device
|
||||
.get_supported_read_only_binding_type(CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT);
|
||||
let cluster_min_binding_size = match clustered_forward_buffer_binding_type {
|
||||
BufferBindingType::Storage { .. } => None,
|
||||
BufferBindingType::Uniform => BufferSize::new(16384),
|
||||
};
|
||||
|
||||
let view_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
||||
entries: &[
|
||||
// View
|
||||
|
@ -280,7 +277,7 @@ impl FromWorld for MeshPipeline {
|
|||
ty: BindingType::Buffer {
|
||||
ty: BufferBindingType::Uniform,
|
||||
has_dynamic_offset: true,
|
||||
min_binding_size: BufferSize::new(ViewUniform::std140_size_static() as u64),
|
||||
min_binding_size: Some(ViewUniform::min_size()),
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
|
@ -291,7 +288,7 @@ impl FromWorld for MeshPipeline {
|
|||
ty: BindingType::Buffer {
|
||||
ty: BufferBindingType::Uniform,
|
||||
has_dynamic_offset: true,
|
||||
min_binding_size: BufferSize::new(GpuLights::std140_size_static() as u64),
|
||||
min_binding_size: Some(GpuLights::min_size()),
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
|
@ -344,10 +341,9 @@ impl FromWorld for MeshPipeline {
|
|||
ty: BindingType::Buffer {
|
||||
ty: clustered_forward_buffer_binding_type,
|
||||
has_dynamic_offset: false,
|
||||
// NOTE (when no storage buffers): Static size for uniform buffers.
|
||||
// GpuPointLight has a padded size of 64 bytes, so 16384 / 64 = 256
|
||||
// point lights max
|
||||
min_binding_size: cluster_min_binding_size,
|
||||
min_binding_size: Some(GpuPointLights::min_size(
|
||||
clustered_forward_buffer_binding_type,
|
||||
)),
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
|
@ -358,9 +354,11 @@ impl FromWorld for MeshPipeline {
|
|||
ty: BindingType::Buffer {
|
||||
ty: clustered_forward_buffer_binding_type,
|
||||
has_dynamic_offset: false,
|
||||
// NOTE (when no storage buffers): With 256 point lights max, indices
|
||||
// need 8 bits so use u8
|
||||
min_binding_size: cluster_min_binding_size,
|
||||
min_binding_size: Some(
|
||||
ViewClusterBindings::min_size_cluster_light_index_lists(
|
||||
clustered_forward_buffer_binding_type,
|
||||
),
|
||||
),
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
|
@ -371,12 +369,11 @@ impl FromWorld for MeshPipeline {
|
|||
ty: BindingType::Buffer {
|
||||
ty: clustered_forward_buffer_binding_type,
|
||||
has_dynamic_offset: false,
|
||||
// NOTE (when no storage buffers): The offset needs to address 16384
|
||||
// indices, which needs 14 bits. The count can be at most all 256 lights
|
||||
// so 8 bits.
|
||||
// NOTE: Pack the offset into the upper 19 bits and the count into the
|
||||
// lower 13 bits.
|
||||
min_binding_size: cluster_min_binding_size,
|
||||
min_binding_size: Some(
|
||||
ViewClusterBindings::min_size_cluster_offsets_and_counts(
|
||||
clustered_forward_buffer_binding_type,
|
||||
),
|
||||
),
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
|
@ -390,7 +387,7 @@ impl FromWorld for MeshPipeline {
|
|||
ty: BindingType::Buffer {
|
||||
ty: BufferBindingType::Uniform,
|
||||
has_dynamic_offset: true,
|
||||
min_binding_size: BufferSize::new(MeshUniform::std140_size_static() as u64),
|
||||
min_binding_size: Some(MeshUniform::min_size()),
|
||||
},
|
||||
count: None,
|
||||
};
|
||||
|
@ -688,7 +685,7 @@ pub fn queue_mesh_bind_group(
|
|||
skinned: None,
|
||||
};
|
||||
|
||||
if let Some(skinned_joints_buffer) = skinned_mesh_uniform.buffer.uniform_buffer() {
|
||||
if let Some(skinned_joints_buffer) = skinned_mesh_uniform.buffer.buffer() {
|
||||
mesh_bind_group.skinned = Some(render_device.create_bind_group(&BindGroupDescriptor {
|
||||
entries: &[
|
||||
BindGroupEntry {
|
||||
|
@ -712,9 +709,15 @@ pub fn queue_mesh_bind_group(
|
|||
}
|
||||
}
|
||||
|
||||
// NOTE: This is using BufferVec because it is using a trick to allow a fixed-size array
|
||||
// in a uniform buffer to be used like a variable-sized array by only writing the valid data
|
||||
// into the buffer, knowing the number of valid items starting from the dynamic offset, and
|
||||
// ignoring the rest, whether they're valid for other dynamic offsets or not. This trick may
|
||||
// be supported later in encase, and then we should make use of it.
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct SkinnedMeshUniform {
|
||||
pub buffer: UniformVec<Mat4>,
|
||||
pub buffer: BufferVec<Mat4>,
|
||||
}
|
||||
|
||||
pub fn prepare_skinned_meshes(
|
||||
|
|
|
@ -31,9 +31,9 @@ webgl = ["wgpu/webgl"]
|
|||
bevy_app = { path = "../bevy_app", version = "0.8.0-dev" }
|
||||
bevy_asset = { path = "../bevy_asset", version = "0.8.0-dev" }
|
||||
bevy_core = { path = "../bevy_core", version = "0.8.0-dev" }
|
||||
bevy_crevice = { path = "../bevy_crevice", version = "0.8.0-dev", features = ["glam"] }
|
||||
bevy_derive = { path = "../bevy_derive", version = "0.8.0-dev" }
|
||||
bevy_ecs = { path = "../bevy_ecs", version = "0.8.0-dev" }
|
||||
bevy_encase_derive = { path = "../bevy_encase_derive", version = "0.8.0-dev" }
|
||||
bevy_math = { path = "../bevy_math", version = "0.8.0-dev" }
|
||||
bevy_reflect = { path = "../bevy_reflect", version = "0.8.0-dev", features = ["bevy"] }
|
||||
bevy_transform = { path = "../bevy_transform", version = "0.8.0-dev" }
|
||||
|
@ -67,3 +67,4 @@ flate2 = { version = "1.0.22", optional = true }
|
|||
ruzstd = { version = "0.2.4", optional = true }
|
||||
# For transcoding of UASTC/ETC1S universal formats, and for .basis file support
|
||||
basis-universal = { version = "0.2.0", optional = true }
|
||||
encase = { version = "0.2", features = ["glam"] }
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
render_resource::{std140::AsStd140, DynamicUniformVec},
|
||||
render_resource::{encase::internal::WriteInto, DynamicUniformBuffer, ShaderType},
|
||||
renderer::{RenderDevice, RenderQueue},
|
||||
view::ComputedVisibility,
|
||||
RenderApp, RenderStage,
|
||||
|
@ -58,7 +58,7 @@ impl<C> Default for UniformComponentPlugin<C> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<C: Component + AsStd140 + Clone> Plugin for UniformComponentPlugin<C> {
|
||||
impl<C: Component + ShaderType + WriteInto + Clone> Plugin for UniformComponentPlugin<C> {
|
||||
fn build(&self, app: &mut App) {
|
||||
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
|
||||
render_app
|
||||
|
@ -69,12 +69,12 @@ impl<C: Component + AsStd140 + Clone> Plugin for UniformComponentPlugin<C> {
|
|||
}
|
||||
|
||||
/// Stores all uniforms of the component type.
|
||||
pub struct ComponentUniforms<C: Component + AsStd140> {
|
||||
uniforms: DynamicUniformVec<C>,
|
||||
pub struct ComponentUniforms<C: Component + ShaderType> {
|
||||
uniforms: DynamicUniformBuffer<C>,
|
||||
}
|
||||
|
||||
impl<C: Component + AsStd140> Deref for ComponentUniforms<C> {
|
||||
type Target = DynamicUniformVec<C>;
|
||||
impl<C: Component + ShaderType> Deref for ComponentUniforms<C> {
|
||||
type Target = DynamicUniformBuffer<C>;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
|
@ -82,14 +82,14 @@ impl<C: Component + AsStd140> Deref for ComponentUniforms<C> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<C: Component + AsStd140> ComponentUniforms<C> {
|
||||
impl<C: Component + ShaderType> ComponentUniforms<C> {
|
||||
#[inline]
|
||||
pub fn uniforms(&self) -> &DynamicUniformVec<C> {
|
||||
pub fn uniforms(&self) -> &DynamicUniformBuffer<C> {
|
||||
&self.uniforms
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Component + AsStd140> Default for ComponentUniforms<C> {
|
||||
impl<C: Component + ShaderType> Default for ComponentUniforms<C> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
uniforms: Default::default(),
|
||||
|
@ -106,7 +106,7 @@ fn prepare_uniform_components<C: Component>(
|
|||
mut component_uniforms: ResMut<ComponentUniforms<C>>,
|
||||
components: Query<(Entity, &C)>,
|
||||
) where
|
||||
C: AsStd140 + Clone,
|
||||
C: ShaderType + WriteInto + Clone,
|
||||
{
|
||||
component_uniforms.uniforms.clear();
|
||||
let entities = components
|
||||
|
|
|
@ -8,7 +8,7 @@ mod pipeline_specializer;
|
|||
mod shader;
|
||||
mod storage_buffer;
|
||||
mod texture;
|
||||
mod uniform_vec;
|
||||
mod uniform_buffer;
|
||||
|
||||
pub use bind_group::*;
|
||||
pub use bind_group_layout::*;
|
||||
|
@ -20,7 +20,7 @@ pub use pipeline_specializer::*;
|
|||
pub use shader::*;
|
||||
pub use storage_buffer::*;
|
||||
pub use texture::*;
|
||||
pub use uniform_vec::*;
|
||||
pub use uniform_buffer::*;
|
||||
|
||||
// TODO: decide where re-exports should go
|
||||
pub use wgpu::{
|
||||
|
@ -44,6 +44,11 @@ pub use wgpu::{
|
|||
VertexStepMode,
|
||||
};
|
||||
|
||||
pub use bevy_crevice::*;
|
||||
pub mod encase {
|
||||
pub use bevy_encase_derive::ShaderType;
|
||||
pub use encase::*;
|
||||
}
|
||||
|
||||
pub use self::encase::{ShaderType, Size as ShaderSize};
|
||||
|
||||
pub use naga::ShaderStage;
|
||||
|
|
|
@ -1,42 +1,105 @@
|
|||
use super::Buffer;
|
||||
use crate::renderer::{RenderDevice, RenderQueue};
|
||||
use bevy_crevice::std430::{self, AsStd430, Std430};
|
||||
use bevy_utils::tracing::warn;
|
||||
use std::num::NonZeroU64;
|
||||
use wgpu::{BindingResource, BufferBinding, BufferDescriptor, BufferUsages};
|
||||
use encase::{
|
||||
internal::WriteInto, DynamicStorageBuffer as DynamicStorageBufferWrapper, ShaderType,
|
||||
StorageBuffer as StorageBufferWrapper,
|
||||
};
|
||||
use wgpu::{util::BufferInitDescriptor, BindingResource, BufferBinding, BufferUsages};
|
||||
|
||||
/// A helper for a storage buffer binding with a body, or a variable-sized array, or both.
|
||||
pub struct StorageBuffer<T: AsStd430, U: AsStd430 = ()> {
|
||||
body: U,
|
||||
values: Vec<T>,
|
||||
scratch: Vec<u8>,
|
||||
storage_buffer: Option<Buffer>,
|
||||
pub struct StorageBuffer<T: ShaderType> {
|
||||
value: T,
|
||||
scratch: StorageBufferWrapper<Vec<u8>>,
|
||||
buffer: Option<Buffer>,
|
||||
capacity: usize,
|
||||
}
|
||||
|
||||
impl<T: AsStd430, U: AsStd430 + Default> Default for StorageBuffer<T, U> {
|
||||
/// Creates a new [`StorageBuffer`]
|
||||
///
|
||||
/// This does not immediately allocate system/video RAM buffers.
|
||||
fn default() -> Self {
|
||||
impl<T: ShaderType> From<T> for StorageBuffer<T> {
|
||||
fn from(value: T) -> Self {
|
||||
Self {
|
||||
body: U::default(),
|
||||
values: Vec::new(),
|
||||
scratch: Vec::new(),
|
||||
storage_buffer: None,
|
||||
value,
|
||||
scratch: StorageBufferWrapper::new(Vec::new()),
|
||||
buffer: None,
|
||||
capacity: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsStd430, U: AsStd430> StorageBuffer<T, U> {
|
||||
// NOTE: AsStd430::std430_size_static() uses size_of internally but trait functions cannot be
|
||||
// marked as const functions
|
||||
const BODY_SIZE: usize = std::mem::size_of::<U::Output>();
|
||||
const ITEM_SIZE: usize = std::mem::size_of::<T::Output>();
|
||||
impl<T: ShaderType + Default> Default for StorageBuffer<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
value: T::default(),
|
||||
scratch: StorageBufferWrapper::new(Vec::new()),
|
||||
buffer: None,
|
||||
capacity: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the reference to the underlying buffer, if one has been allocated.
|
||||
impl<T: ShaderType + WriteInto> StorageBuffer<T> {
|
||||
#[inline]
|
||||
pub fn buffer(&self) -> Option<&Buffer> {
|
||||
self.storage_buffer.as_ref()
|
||||
self.buffer.as_ref()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn binding(&self) -> Option<BindingResource> {
|
||||
Some(BindingResource::Buffer(
|
||||
self.buffer()?.as_entire_buffer_binding(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn set(&mut self, value: T) {
|
||||
self.value = value;
|
||||
}
|
||||
|
||||
pub fn get(&self) -> &T {
|
||||
&self.value
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.value
|
||||
}
|
||||
|
||||
pub fn write_buffer(&mut self, device: &RenderDevice, queue: &RenderQueue) {
|
||||
self.scratch.write(&self.value).unwrap();
|
||||
|
||||
let size = self.scratch.as_ref().len();
|
||||
|
||||
if self.capacity < size {
|
||||
self.buffer = Some(device.create_buffer_with_data(&BufferInitDescriptor {
|
||||
label: None,
|
||||
usage: BufferUsages::COPY_DST | BufferUsages::STORAGE,
|
||||
contents: self.scratch.as_ref(),
|
||||
}));
|
||||
self.capacity = size;
|
||||
} else if let Some(buffer) = &self.buffer {
|
||||
queue.write_buffer(buffer, 0, self.scratch.as_ref());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DynamicStorageBuffer<T: ShaderType> {
|
||||
values: Vec<T>,
|
||||
scratch: DynamicStorageBufferWrapper<Vec<u8>>,
|
||||
buffer: Option<Buffer>,
|
||||
capacity: usize,
|
||||
}
|
||||
|
||||
impl<T: ShaderType> Default for DynamicStorageBuffer<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
values: Vec::new(),
|
||||
scratch: DynamicStorageBufferWrapper::new(Vec::new()),
|
||||
buffer: None,
|
||||
capacity: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ShaderType + WriteInto> DynamicStorageBuffer<T> {
|
||||
#[inline]
|
||||
pub fn buffer(&self) -> Option<&Buffer> {
|
||||
self.buffer.as_ref()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -44,171 +107,47 @@ impl<T: AsStd430, U: AsStd430> StorageBuffer<T, U> {
|
|||
Some(BindingResource::Buffer(BufferBinding {
|
||||
buffer: self.buffer()?,
|
||||
offset: 0,
|
||||
size: Some(NonZeroU64::new((self.size()) as u64).unwrap()),
|
||||
size: Some(T::min_size()),
|
||||
}))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_body(&mut self, body: U) {
|
||||
self.body = body;
|
||||
pub fn len(&self) -> usize {
|
||||
self.values.len()
|
||||
}
|
||||
|
||||
fn reserve_buffer(&mut self, device: &RenderDevice) -> bool {
|
||||
let size = self.size();
|
||||
if self.storage_buffer.is_none() || size > self.scratch.len() {
|
||||
self.scratch.resize(size, 0);
|
||||
self.storage_buffer = Some(device.create_buffer(&BufferDescriptor {
|
||||
label: None,
|
||||
size: size as wgpu::BufferAddress,
|
||||
usage: BufferUsages::COPY_DST | BufferUsages::STORAGE,
|
||||
mapped_at_creation: false,
|
||||
}));
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.values.is_empty()
|
||||
}
|
||||
|
||||
fn size(&self) -> usize {
|
||||
let mut size = 0;
|
||||
size += Self::BODY_SIZE;
|
||||
if Self::ITEM_SIZE > 0 {
|
||||
if size > 0 {
|
||||
// Pad according to the array item type's alignment
|
||||
size = (size + <U as AsStd430>::Output::ALIGNMENT - 1)
|
||||
& !(<U as AsStd430>::Output::ALIGNMENT - 1);
|
||||
}
|
||||
// Variable size arrays must have at least 1 element
|
||||
size += Self::ITEM_SIZE * self.values.len().max(1);
|
||||
}
|
||||
size
|
||||
#[inline]
|
||||
pub fn push(&mut self, value: T) -> u32 {
|
||||
let offset = self.scratch.write(&value).unwrap() as u32;
|
||||
self.values.push(value);
|
||||
offset
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn write_buffer(&mut self, device: &RenderDevice, queue: &RenderQueue) {
|
||||
self.reserve_buffer(device);
|
||||
if let Some(storage_buffer) = &self.storage_buffer {
|
||||
let range = 0..self.size();
|
||||
let mut writer = std430::Writer::new(&mut self.scratch[range.clone()]);
|
||||
let mut offset = 0;
|
||||
// First write the struct body if there is one
|
||||
if Self::BODY_SIZE > 0 {
|
||||
if let Ok(new_offset) = writer.write(&self.body).map_err(|e| warn!("{:?}", e)) {
|
||||
offset = new_offset;
|
||||
}
|
||||
}
|
||||
if Self::ITEM_SIZE > 0 {
|
||||
if self.values.is_empty() {
|
||||
// Zero-out the padding and dummy array item in the case of the array being empty
|
||||
for i in offset..self.size() {
|
||||
self.scratch[i] = 0;
|
||||
}
|
||||
} else {
|
||||
// Then write the array. Note that padding bytes may be added between the body
|
||||
// and the array in order to align the array to the alignment requirements of its
|
||||
// items
|
||||
writer
|
||||
.write(self.values.as_slice())
|
||||
.map_err(|e| warn!("{:?}", e))
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
queue.write_buffer(storage_buffer, 0, &self.scratch[range]);
|
||||
let size = self.scratch.as_ref().len();
|
||||
|
||||
if self.capacity < size {
|
||||
self.buffer = Some(device.create_buffer_with_data(&BufferInitDescriptor {
|
||||
label: None,
|
||||
usage: BufferUsages::COPY_DST | BufferUsages::STORAGE,
|
||||
contents: self.scratch.as_ref(),
|
||||
}));
|
||||
self.capacity = size;
|
||||
} else if let Some(buffer) = &self.buffer {
|
||||
queue.write_buffer(buffer, 0, self.scratch.as_ref());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn values(&self) -> &[T] {
|
||||
&self.values
|
||||
}
|
||||
|
||||
pub fn values_mut(&mut self) -> &mut [T] {
|
||||
&mut self.values
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear(&mut self) {
|
||||
self.values.clear();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn push(&mut self, value: T) {
|
||||
self.values.push(value);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn append(&mut self, values: &mut Vec<T>) {
|
||||
self.values.append(values);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::StorageBuffer;
|
||||
use bevy_crevice::std430;
|
||||
use bevy_crevice::std430::AsStd430;
|
||||
use bevy_crevice::std430::Std430;
|
||||
use bevy_math::Vec3;
|
||||
use bevy_math::Vec4;
|
||||
|
||||
//Note:
|
||||
//A Vec3 has 12 bytes and needs to be padded to 16 bytes, when converted to std430
|
||||
//https://www.w3.org/TR/WGSL/#alignment-and-size
|
||||
#[derive(AsStd430, Default)]
|
||||
struct NotInherentlyAligned {
|
||||
data: Vec3,
|
||||
}
|
||||
|
||||
//Note:
|
||||
//A Vec4 has 16 bytes and does not need to be padded to fit in std430
|
||||
//https://www.w3.org/TR/WGSL/#alignment-and-size
|
||||
#[derive(AsStd430)]
|
||||
struct InherentlyAligned {
|
||||
data: Vec4,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn storage_buffer_correctly_sized_nonaligned() {
|
||||
let mut buffer: StorageBuffer<NotInherentlyAligned> = StorageBuffer::default();
|
||||
buffer.push(NotInherentlyAligned { data: Vec3::ONE });
|
||||
|
||||
let actual_size = buffer.size();
|
||||
|
||||
let data = [NotInherentlyAligned { data: Vec3::ONE }].as_std430();
|
||||
let data_as_bytes = data.as_bytes();
|
||||
|
||||
assert_eq!(actual_size, data_as_bytes.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn storage_buffer_correctly_sized_aligned() {
|
||||
let mut buffer: StorageBuffer<InherentlyAligned> = StorageBuffer::default();
|
||||
buffer.push(InherentlyAligned { data: Vec4::ONE });
|
||||
|
||||
let actual_size = buffer.size();
|
||||
|
||||
let data = [InherentlyAligned { data: Vec4::ONE }].as_std430();
|
||||
let data_as_bytes = data.as_bytes();
|
||||
|
||||
assert_eq!(actual_size, data_as_bytes.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn storage_buffer_correctly_sized_item_and_body() {
|
||||
let mut buffer: StorageBuffer<NotInherentlyAligned, NotInherentlyAligned> =
|
||||
StorageBuffer::default();
|
||||
buffer.push(NotInherentlyAligned { data: Vec3::ONE });
|
||||
buffer.set_body(NotInherentlyAligned { data: Vec3::ONE });
|
||||
|
||||
let calculated_size = buffer.size();
|
||||
|
||||
//Emulate Write
|
||||
let mut scratch = Vec::<u8>::new();
|
||||
scratch.resize(calculated_size, 0);
|
||||
let mut writer = std430::Writer::new(&mut scratch[0..calculated_size]);
|
||||
writer
|
||||
.write(&buffer.body)
|
||||
.expect("Buffer has enough space to write the body.");
|
||||
writer
|
||||
.write(buffer.values.as_slice())
|
||||
.expect("Buffer has enough space to write the values.");
|
||||
self.scratch.as_mut().clear();
|
||||
self.scratch.set_offset(0);
|
||||
}
|
||||
}
|
||||
|
|
150
crates/bevy_render/src/render_resource/uniform_buffer.rs
Normal file
150
crates/bevy_render/src/render_resource/uniform_buffer.rs
Normal file
|
@ -0,0 +1,150 @@
|
|||
use crate::{
|
||||
render_resource::Buffer,
|
||||
renderer::{RenderDevice, RenderQueue},
|
||||
};
|
||||
use encase::{
|
||||
internal::WriteInto, DynamicUniformBuffer as DynamicUniformBufferWrapper, ShaderType,
|
||||
UniformBuffer as UniformBufferWrapper,
|
||||
};
|
||||
use wgpu::{util::BufferInitDescriptor, BindingResource, BufferBinding, BufferUsages};
|
||||
|
||||
pub struct UniformBuffer<T: ShaderType> {
|
||||
value: T,
|
||||
scratch: UniformBufferWrapper<Vec<u8>>,
|
||||
buffer: Option<Buffer>,
|
||||
}
|
||||
|
||||
impl<T: ShaderType> From<T> for UniformBuffer<T> {
|
||||
fn from(value: T) -> Self {
|
||||
Self {
|
||||
value,
|
||||
scratch: UniformBufferWrapper::new(Vec::new()),
|
||||
buffer: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ShaderType + Default> Default for UniformBuffer<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
value: T::default(),
|
||||
scratch: UniformBufferWrapper::new(Vec::new()),
|
||||
buffer: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ShaderType + WriteInto> UniformBuffer<T> {
|
||||
#[inline]
|
||||
pub fn buffer(&self) -> Option<&Buffer> {
|
||||
self.buffer.as_ref()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn binding(&self) -> Option<BindingResource> {
|
||||
Some(BindingResource::Buffer(
|
||||
self.buffer()?.as_entire_buffer_binding(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn set(&mut self, value: T) {
|
||||
self.value = value;
|
||||
}
|
||||
|
||||
pub fn get(&self) -> &T {
|
||||
&self.value
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.value
|
||||
}
|
||||
|
||||
pub fn write_buffer(&mut self, device: &RenderDevice, queue: &RenderQueue) {
|
||||
self.scratch.write(&self.value).unwrap();
|
||||
|
||||
match &self.buffer {
|
||||
Some(buffer) => queue.write_buffer(buffer, 0, self.scratch.as_ref()),
|
||||
None => {
|
||||
self.buffer = Some(device.create_buffer_with_data(&BufferInitDescriptor {
|
||||
label: None,
|
||||
usage: BufferUsages::COPY_DST | BufferUsages::UNIFORM,
|
||||
contents: self.scratch.as_ref(),
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DynamicUniformBuffer<T: ShaderType> {
|
||||
values: Vec<T>,
|
||||
scratch: DynamicUniformBufferWrapper<Vec<u8>>,
|
||||
buffer: Option<Buffer>,
|
||||
capacity: usize,
|
||||
}
|
||||
|
||||
impl<T: ShaderType> Default for DynamicUniformBuffer<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
values: Vec::new(),
|
||||
scratch: DynamicUniformBufferWrapper::new(Vec::new()),
|
||||
buffer: None,
|
||||
capacity: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ShaderType + WriteInto> DynamicUniformBuffer<T> {
|
||||
#[inline]
|
||||
pub fn buffer(&self) -> Option<&Buffer> {
|
||||
self.buffer.as_ref()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn binding(&self) -> Option<BindingResource> {
|
||||
Some(BindingResource::Buffer(BufferBinding {
|
||||
buffer: self.buffer()?,
|
||||
offset: 0,
|
||||
size: Some(T::min_size()),
|
||||
}))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.values.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.values.is_empty()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn push(&mut self, value: T) -> u32 {
|
||||
let offset = self.scratch.write(&value).unwrap() as u32;
|
||||
self.values.push(value);
|
||||
offset
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn write_buffer(&mut self, device: &RenderDevice, queue: &RenderQueue) {
|
||||
let size = self.scratch.as_ref().len();
|
||||
|
||||
if self.capacity < size {
|
||||
self.buffer = Some(device.create_buffer_with_data(&BufferInitDescriptor {
|
||||
label: None,
|
||||
usage: BufferUsages::COPY_DST | BufferUsages::UNIFORM,
|
||||
contents: self.scratch.as_ref(),
|
||||
}));
|
||||
self.capacity = size;
|
||||
} else if let Some(buffer) = &self.buffer {
|
||||
queue.write_buffer(buffer, 0, self.scratch.as_ref());
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear(&mut self) {
|
||||
self.values.clear();
|
||||
self.scratch.as_mut().clear();
|
||||
self.scratch.set_offset(0);
|
||||
}
|
||||
}
|
|
@ -1,166 +0,0 @@
|
|||
use crate::{
|
||||
render_resource::std140::{self, AsStd140, DynamicUniform, Std140},
|
||||
render_resource::Buffer,
|
||||
renderer::{RenderDevice, RenderQueue},
|
||||
};
|
||||
use std::num::NonZeroU64;
|
||||
use wgpu::{BindingResource, BufferBinding, BufferDescriptor, BufferUsages};
|
||||
|
||||
pub struct UniformVec<T: AsStd140> {
|
||||
values: Vec<T>,
|
||||
scratch: Vec<u8>,
|
||||
uniform_buffer: Option<Buffer>,
|
||||
capacity: usize,
|
||||
item_size: usize,
|
||||
}
|
||||
|
||||
impl<T: AsStd140> Default for UniformVec<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
values: Vec::new(),
|
||||
scratch: Vec::new(),
|
||||
uniform_buffer: None,
|
||||
capacity: 0,
|
||||
item_size: (T::std140_size_static() + <T as AsStd140>::Output::ALIGNMENT - 1)
|
||||
& !(<T as AsStd140>::Output::ALIGNMENT - 1),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsStd140> UniformVec<T> {
|
||||
#[inline]
|
||||
pub fn uniform_buffer(&self) -> Option<&Buffer> {
|
||||
self.uniform_buffer.as_ref()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn binding(&self) -> Option<BindingResource> {
|
||||
Some(BindingResource::Buffer(BufferBinding {
|
||||
buffer: self.uniform_buffer()?,
|
||||
offset: 0,
|
||||
size: Some(NonZeroU64::new(self.item_size as u64).unwrap()),
|
||||
}))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.values.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.values.is_empty()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.capacity
|
||||
}
|
||||
|
||||
pub fn push(&mut self, value: T) -> usize {
|
||||
let index = self.values.len();
|
||||
self.values.push(value);
|
||||
index
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, index: usize) -> &mut T {
|
||||
&mut self.values[index]
|
||||
}
|
||||
|
||||
pub fn reserve(&mut self, capacity: usize, device: &RenderDevice) -> bool {
|
||||
if capacity > self.capacity {
|
||||
self.capacity = capacity;
|
||||
let size = self.item_size * capacity;
|
||||
self.scratch.resize(size, 0);
|
||||
self.uniform_buffer = Some(device.create_buffer(&BufferDescriptor {
|
||||
label: None,
|
||||
size: size as wgpu::BufferAddress,
|
||||
usage: BufferUsages::COPY_DST | BufferUsages::UNIFORM,
|
||||
mapped_at_creation: false,
|
||||
}));
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_buffer(&mut self, device: &RenderDevice, queue: &RenderQueue) {
|
||||
if self.values.is_empty() {
|
||||
return;
|
||||
}
|
||||
self.reserve(self.values.len(), device);
|
||||
if let Some(uniform_buffer) = &self.uniform_buffer {
|
||||
let range = 0..self.item_size * self.values.len();
|
||||
let mut writer = std140::Writer::new(&mut self.scratch[range.clone()]);
|
||||
writer.write(self.values.as_slice()).unwrap();
|
||||
queue.write_buffer(uniform_buffer, 0, &self.scratch[range]);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.values.clear();
|
||||
}
|
||||
|
||||
pub fn values(&self) -> &[T] {
|
||||
&self.values
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DynamicUniformVec<T: AsStd140> {
|
||||
uniform_vec: UniformVec<DynamicUniform<T>>,
|
||||
}
|
||||
|
||||
impl<T: AsStd140> Default for DynamicUniformVec<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
uniform_vec: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsStd140> DynamicUniformVec<T> {
|
||||
#[inline]
|
||||
pub fn uniform_buffer(&self) -> Option<&Buffer> {
|
||||
self.uniform_vec.uniform_buffer()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn binding(&self) -> Option<BindingResource> {
|
||||
self.uniform_vec.binding()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.uniform_vec.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.uniform_vec.is_empty()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.uniform_vec.capacity()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn push(&mut self, value: T) -> u32 {
|
||||
(self.uniform_vec.push(DynamicUniform(value)) * self.uniform_vec.item_size) as u32
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn reserve(&mut self, capacity: usize, device: &RenderDevice) {
|
||||
self.uniform_vec.reserve(capacity, device);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn write_buffer(&mut self, device: &RenderDevice, queue: &RenderQueue) {
|
||||
self.uniform_vec.write_buffer(device, queue);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear(&mut self) {
|
||||
self.uniform_vec.clear();
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@ use crate::{
|
|||
camera::ExtractedCamera,
|
||||
prelude::Image,
|
||||
render_asset::RenderAssets,
|
||||
render_resource::{std140::AsStd140, DynamicUniformVec, Texture, TextureView},
|
||||
render_resource::{DynamicUniformBuffer, ShaderType, Texture, TextureView},
|
||||
renderer::{RenderDevice, RenderQueue},
|
||||
texture::{BevyDefault, TextureCache},
|
||||
RenderApp, RenderStage,
|
||||
|
@ -83,7 +83,7 @@ pub struct ExtractedView {
|
|||
pub height: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, AsStd140)]
|
||||
#[derive(Clone, ShaderType)]
|
||||
pub struct ViewUniform {
|
||||
view_proj: Mat4,
|
||||
view: Mat4,
|
||||
|
@ -96,7 +96,7 @@ pub struct ViewUniform {
|
|||
|
||||
#[derive(Default)]
|
||||
pub struct ViewUniforms {
|
||||
pub uniforms: DynamicUniformVec<ViewUniform>,
|
||||
pub uniforms: DynamicUniformBuffer<ViewUniform>,
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
|
|
|
@ -7,10 +7,7 @@ use bevy_render::{
|
|||
color::Color,
|
||||
prelude::Shader,
|
||||
render_asset::{PrepareAssetError, RenderAsset, RenderAssets},
|
||||
render_resource::{
|
||||
std140::{AsStd140, Std140},
|
||||
*,
|
||||
},
|
||||
render_resource::*,
|
||||
renderer::RenderDevice,
|
||||
texture::Image,
|
||||
};
|
||||
|
@ -92,7 +89,7 @@ bitflags::bitflags! {
|
|||
}
|
||||
|
||||
/// The GPU representation of the uniform data of a [`ColorMaterial`].
|
||||
#[derive(Clone, Default, AsStd140)]
|
||||
#[derive(Clone, Default, ShaderType)]
|
||||
pub struct ColorMaterialUniformData {
|
||||
pub color: Vec4,
|
||||
pub flags: u32,
|
||||
|
@ -145,12 +142,15 @@ impl RenderAsset for ColorMaterial {
|
|||
color: material.color.as_linear_rgba_f32().into(),
|
||||
flags: flags.bits(),
|
||||
};
|
||||
let value_std140 = value.as_std140();
|
||||
|
||||
let byte_buffer = [0u8; ColorMaterialUniformData::SIZE.get() as usize];
|
||||
let mut buffer = encase::UniformBuffer::new(byte_buffer);
|
||||
buffer.write(&value).unwrap();
|
||||
|
||||
let buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
|
||||
label: Some("color_material_uniform_buffer"),
|
||||
usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
|
||||
contents: value_std140.as_bytes(),
|
||||
contents: buffer.as_ref(),
|
||||
});
|
||||
let bind_group = render_device.create_bind_group(&BindGroupDescriptor {
|
||||
entries: &[
|
||||
|
@ -201,9 +201,7 @@ impl Material2d for ColorMaterial {
|
|||
ty: BindingType::Buffer {
|
||||
ty: BufferBindingType::Uniform,
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: BufferSize::new(
|
||||
ColorMaterialUniformData::std140_size_static() as u64,
|
||||
),
|
||||
min_binding_size: Some(ColorMaterialUniformData::min_size()),
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
|
|
|
@ -11,7 +11,7 @@ use bevy_render::{
|
|||
render_asset::RenderAssets,
|
||||
render_component::{ComponentUniforms, DynamicUniformIndex, UniformComponentPlugin},
|
||||
render_phase::{EntityRenderCommand, RenderCommandResult, TrackedRenderPass},
|
||||
render_resource::{std140::AsStd140, *},
|
||||
render_resource::*,
|
||||
renderer::{RenderDevice, RenderQueue},
|
||||
texture::{BevyDefault, GpuImage, Image, TextureFormatPixelInfo},
|
||||
view::{ComputedVisibility, ExtractedView, ViewUniform, ViewUniformOffset, ViewUniforms},
|
||||
|
@ -71,7 +71,7 @@ impl Plugin for Mesh2dRenderPlugin {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Component, AsStd140, Clone)]
|
||||
#[derive(Component, ShaderType, Clone)]
|
||||
pub struct Mesh2dUniform {
|
||||
pub transform: Mat4,
|
||||
pub inverse_transpose_model: Mat4,
|
||||
|
@ -134,7 +134,7 @@ impl FromWorld for Mesh2dPipeline {
|
|||
ty: BindingType::Buffer {
|
||||
ty: BufferBindingType::Uniform,
|
||||
has_dynamic_offset: true,
|
||||
min_binding_size: BufferSize::new(ViewUniform::std140_size_static() as u64),
|
||||
min_binding_size: Some(ViewUniform::min_size()),
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
|
@ -149,7 +149,7 @@ impl FromWorld for Mesh2dPipeline {
|
|||
ty: BindingType::Buffer {
|
||||
ty: BufferBindingType::Uniform,
|
||||
has_dynamic_offset: true,
|
||||
min_binding_size: BufferSize::new(Mesh2dUniform::std140_size_static() as u64),
|
||||
min_binding_size: Some(Mesh2dUniform::min_size()),
|
||||
},
|
||||
count: None,
|
||||
}],
|
||||
|
|
|
@ -19,7 +19,7 @@ use bevy_render::{
|
|||
BatchedPhaseItem, DrawFunctions, EntityRenderCommand, RenderCommand, RenderCommandResult,
|
||||
RenderPhase, SetItemPipeline, TrackedRenderPass,
|
||||
},
|
||||
render_resource::{std140::AsStd140, *},
|
||||
render_resource::*,
|
||||
renderer::{RenderDevice, RenderQueue},
|
||||
texture::{BevyDefault, Image},
|
||||
view::{Msaa, ViewUniform, ViewUniformOffset, ViewUniforms, Visibility},
|
||||
|
@ -48,7 +48,7 @@ impl FromWorld for SpritePipeline {
|
|||
ty: BindingType::Buffer {
|
||||
ty: BufferBindingType::Uniform,
|
||||
has_dynamic_offset: true,
|
||||
min_binding_size: BufferSize::new(ViewUniform::std140_size_static() as u64),
|
||||
min_binding_size: Some(ViewUniform::min_size()),
|
||||
},
|
||||
count: None,
|
||||
}],
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
use bevy_ecs::prelude::*;
|
||||
use bevy_render::{
|
||||
render_resource::{std140::AsStd140, *},
|
||||
renderer::RenderDevice,
|
||||
texture::BevyDefault,
|
||||
view::ViewUniform,
|
||||
render_resource::*, renderer::RenderDevice, texture::BevyDefault, view::ViewUniform,
|
||||
};
|
||||
|
||||
pub struct UiPipeline {
|
||||
|
@ -23,7 +20,7 @@ impl FromWorld for UiPipeline {
|
|||
ty: BindingType::Buffer {
|
||||
ty: BufferBindingType::Uniform,
|
||||
has_dynamic_offset: true,
|
||||
min_binding_size: BufferSize::new(ViewUniform::std140_size_static() as u64),
|
||||
min_binding_size: Some(ViewUniform::min_size()),
|
||||
},
|
||||
count: None,
|
||||
}],
|
||||
|
|
|
@ -7,7 +7,6 @@ yanked = "deny"
|
|||
notice = "deny"
|
||||
ignore = [
|
||||
"RUSTSEC-2020-0056", # from gilrs v0.8.1 - unmaintained - https://github.com/koute/stdweb/issues/403
|
||||
"RUSTSEC-2020-0095", # from crevice dev dependency - unmaintained - https://github.com/johannhof/difference.rs/issues/45
|
||||
]
|
||||
|
||||
[licenses]
|
||||
|
@ -15,6 +14,7 @@ unlicensed = "deny"
|
|||
copyleft = "deny"
|
||||
allow = [
|
||||
"MIT",
|
||||
"MIT-0",
|
||||
"Apache-2.0",
|
||||
"BSD-3-Clause",
|
||||
"ISC",
|
||||
|
|
|
@ -9,11 +9,10 @@ use bevy::{
|
|||
mesh::{MeshVertexAttribute, MeshVertexBufferLayout},
|
||||
render_asset::{PrepareAssetError, RenderAsset},
|
||||
render_resource::{
|
||||
std140::{AsStd140, Std140},
|
||||
BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout,
|
||||
BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingType, Buffer,
|
||||
BufferBindingType, BufferInitDescriptor, BufferSize, BufferUsages,
|
||||
RenderPipelineDescriptor, ShaderStages, SpecializedMeshPipelineError, VertexFormat,
|
||||
BufferBindingType, BufferInitDescriptor, BufferUsages, RenderPipelineDescriptor,
|
||||
ShaderSize, ShaderStages, ShaderType, SpecializedMeshPipelineError, VertexFormat,
|
||||
},
|
||||
renderer::RenderDevice,
|
||||
},
|
||||
|
@ -89,8 +88,13 @@ impl RenderAsset for CustomMaterial {
|
|||
(render_device, material_pipeline): &mut SystemParamItem<Self::Param>,
|
||||
) -> Result<Self::PreparedAsset, PrepareAssetError<Self::ExtractedAsset>> {
|
||||
let color = Vec4::from_slice(&extracted_asset.color.as_linear_rgba_f32());
|
||||
|
||||
let byte_buffer = [0u8; Vec4::SIZE.get() as usize];
|
||||
let mut buffer = bevy::render::render_resource::encase::UniformBuffer::new(byte_buffer);
|
||||
buffer.write(&color).unwrap();
|
||||
|
||||
let buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
|
||||
contents: color.as_std140().as_bytes(),
|
||||
contents: buffer.as_ref(),
|
||||
label: None,
|
||||
usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
|
||||
});
|
||||
|
@ -130,7 +134,7 @@ impl Material for CustomMaterial {
|
|||
ty: BindingType::Buffer {
|
||||
ty: BufferBindingType::Uniform,
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: BufferSize::new(Vec4::std140_size_static() as u64),
|
||||
min_binding_size: Some(Vec4::min_size()),
|
||||
},
|
||||
count: None,
|
||||
}],
|
||||
|
|
|
@ -8,10 +8,10 @@ use bevy::{
|
|||
render::{
|
||||
render_asset::{PrepareAssetError, RenderAsset},
|
||||
render_resource::{
|
||||
std140::{AsStd140, Std140},
|
||||
BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout,
|
||||
encase, BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout,
|
||||
BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingType, Buffer,
|
||||
BufferBindingType, BufferInitDescriptor, BufferSize, BufferUsages, ShaderStages,
|
||||
BufferBindingType, BufferInitDescriptor, BufferUsages, ShaderSize, ShaderStages,
|
||||
ShaderType,
|
||||
},
|
||||
renderer::RenderDevice,
|
||||
},
|
||||
|
@ -75,8 +75,13 @@ impl RenderAsset for CustomMaterial {
|
|||
(render_device, material_pipeline): &mut SystemParamItem<Self::Param>,
|
||||
) -> Result<Self::PreparedAsset, PrepareAssetError<Self::ExtractedAsset>> {
|
||||
let color = Vec4::from_slice(&extracted_asset.color.as_linear_rgba_f32());
|
||||
|
||||
let byte_buffer = [0u8; Vec4::SIZE.get() as usize];
|
||||
let mut buffer = encase::UniformBuffer::new(byte_buffer);
|
||||
buffer.write(&color).unwrap();
|
||||
|
||||
let buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
|
||||
contents: color.as_std140().as_bytes(),
|
||||
contents: buffer.as_ref(),
|
||||
label: None,
|
||||
usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
|
||||
});
|
||||
|
@ -123,7 +128,7 @@ impl Material for CustomMaterial {
|
|||
ty: BindingType::Buffer {
|
||||
ty: BufferBindingType::Uniform,
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: BufferSize::new(Vec4::std140_size_static() as u64),
|
||||
min_binding_size: Some(Vec4::min_size()),
|
||||
},
|
||||
count: None,
|
||||
}],
|
||||
|
|
|
@ -8,10 +8,7 @@ use bevy::{
|
|||
render::{
|
||||
mesh::MeshVertexBufferLayout,
|
||||
render_asset::{PrepareAssetError, RenderAsset},
|
||||
render_resource::{
|
||||
std140::{AsStd140, Std140},
|
||||
*,
|
||||
},
|
||||
render_resource::*,
|
||||
renderer::RenderDevice,
|
||||
},
|
||||
};
|
||||
|
@ -72,8 +69,13 @@ impl RenderAsset for CustomMaterial {
|
|||
(render_device, material_pipeline): &mut SystemParamItem<Self::Param>,
|
||||
) -> Result<Self::PreparedAsset, PrepareAssetError<Self::ExtractedAsset>> {
|
||||
let color = Vec4::from_slice(&extracted_asset.color.as_linear_rgba_f32());
|
||||
|
||||
let byte_buffer = [0u8; Vec4::SIZE.get() as usize];
|
||||
let mut buffer = encase::UniformBuffer::new(byte_buffer);
|
||||
buffer.write(&color).unwrap();
|
||||
|
||||
let buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
|
||||
contents: color.as_std140().as_bytes(),
|
||||
contents: buffer.as_ref(),
|
||||
label: None,
|
||||
usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
|
||||
});
|
||||
|
@ -129,7 +131,7 @@ impl SpecializedMaterial for CustomMaterial {
|
|||
ty: BindingType::Buffer {
|
||||
ty: BufferBindingType::Uniform,
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: BufferSize::new(Vec4::std140_size_static() as u64),
|
||||
min_binding_size: Some(Vec4::min_size()),
|
||||
},
|
||||
count: None,
|
||||
}],
|
||||
|
|
|
@ -20,8 +20,7 @@ crates=(
|
|||
bevy_hierarchy
|
||||
bevy_transform
|
||||
bevy_window
|
||||
bevy_crevice/bevy-crevice-derive
|
||||
bevy_crevice
|
||||
bevy_encase_derive
|
||||
bevy_render
|
||||
bevy_core_pipeline
|
||||
bevy_input
|
||||
|
|
Loading…
Reference in a new issue