4bf20e7d27
20 Commits
Author | SHA1 | Message | Date | |
---|---|---|---|---|
![]() |
4bf20e7d27
|
Swap material and mesh bind groups (#10485)
# Objective - Materials should be a more frequent rebind then meshes (due to being able to use a single vertex buffer, such as in #10164) and therefore should be in a higher bind group. --- ## Changelog - For 2d and 3d mesh/material setups (but not UI materials, or other rendering setups such as gizmos, sprites, or text), mesh data is now in bind group 1, and material data is now in bind group 2, which is swapped from how they were before. ## Migration Guide - Custom 2d and 3d mesh/material shaders should now use bind group 2 `@group(2) @binding(x)` for their bound resources, instead of bind group 1. - Many internal pieces of rendering code have changed so that mesh data is now in bind group 1, and material data is now in bind group 2. Semi-custom rendering setups (that don't use the Material or Material2d APIs) should adapt to these changes. |
||
![]() |
6d0c11a28f
|
Bind group layout entries (#10224)
# Objective
- Follow up to #9694
## Solution
- Same api as #9694 but adapted for `BindGroupLayoutEntry`
- Use the same `ShaderStages` visibilty for all entries by default
- Add `BindingType` helper function that mirror the wgsl equivalent and
that make writing layouts much simpler.
Before:
```rust
let layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
label: Some("post_process_bind_group_layout"),
entries: &[
BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Texture {
sample_type: TextureSampleType::Float { filterable: true },
view_dimension: TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
BindGroupLayoutEntry {
binding: 1,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Sampler(SamplerBindingType::Filtering),
count: None,
},
BindGroupLayoutEntry {
binding: 2,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Buffer {
ty: bevy::render::render_resource::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: Some(PostProcessSettings::min_size()),
},
count: None,
},
],
});
```
After:
```rust
let layout = render_device.create_bind_group_layout(
"post_process_bind_group_layout"),
&BindGroupLayoutEntries::sequential(
ShaderStages::FRAGMENT,
(
texture_2d_f32(),
sampler(SamplerBindingType::Filtering),
uniform_buffer(false, Some(PostProcessSettings::min_size())),
),
),
);
```
Here's a more extreme example in bevy_solari:
|
||
![]() |
0c2c52a0cd
|
Derive Error for more error types (#10240)
# Objective Align all error-like types to implement `Error`. Fixes #10176 ## Solution - Derive `Error` on more types - Refactor instances of manual implementations that could be derived This adds thiserror as a dependency to bevy_transform, which might increase compilation time -- but I don't know of any situation where you might only use that but not any other crate that pulls in bevy_utils. The `contributors` example has a `LoadContributorsError` type, but as it's an example I have not updated it. Doing that would mean either having a `use bevy_internal::utils::thiserror::Error;` in an example file, or adding `thiserror` as a dev-dependency to the main `bevy` crate. --- ## Changelog - All `…Error` types now implement the `Error` trait |
||
![]() |
6f2a5cb862
|
Bind group entries (#9694)
# Objective Simplify bind group creation code. alternative to (and based on) #9476 ## Solution - Add a `BindGroupEntries` struct that can transparently be used where `&[BindGroupEntry<'b>]` is required in BindGroupDescriptors. Allows constructing the descriptor's entries as: ```rust render_device.create_bind_group( "my_bind_group", &my_layout, &BindGroupEntries::with_indexes(( (2, &my_sampler), (3, my_uniform), )), ); ``` instead of ```rust render_device.create_bind_group( "my_bind_group", &my_layout, &[ BindGroupEntry { binding: 2, resource: BindingResource::Sampler(&my_sampler), }, BindGroupEntry { binding: 3, resource: my_uniform, }, ], ); ``` or ```rust render_device.create_bind_group( "my_bind_group", &my_layout, &BindGroupEntries::sequential((&my_sampler, my_uniform)), ); ``` instead of ```rust render_device.create_bind_group( "my_bind_group", &my_layout, &[ BindGroupEntry { binding: 0, resource: BindingResource::Sampler(&my_sampler), }, BindGroupEntry { binding: 1, resource: my_uniform, }, ], ); ``` the structs has no user facing macros, is tuple-type-based so stack allocated, and has no noticeable impact on compile time. - Also adds a `DynamicBindGroupEntries` struct with a similar api that uses a `Vec` under the hood and allows extending the entries. - Modifies `RenderDevice::create_bind_group` to take separate arguments `label`, `layout` and `entries` instead of a `BindGroupDescriptor` struct. The struct can't be stored due to the internal references, and with only 3 members arguably does not add enough context to justify itself. - Modify the codebase to use the new api and the `BindGroupEntries` / `DynamicBindGroupEntries` structs where appropriate (whenever the entries slice contains more than 1 member). ## Migration Guide - Calls to `RenderDevice::create_bind_group({BindGroupDescriptor { label, layout, entries })` must be amended to `RenderDevice::create_bind_group(label, layout, entries)`. - If `label`s have been specified as `"bind_group_name".into()`, they need to change to just `"bind_group_name"`. `Some("bind_group_name")` and `None` will still work, but `Some("bind_group_name")` can optionally be simplified to just `"bind_group_name"`. --------- Co-authored-by: IceSentry <IceSentry@users.noreply.github.com> |
||
![]() |
c99351f7c2
|
allow extensions to StandardMaterial (#7820)
# Objective allow extending `Material`s (including the built in `StandardMaterial`) with custom vertex/fragment shaders and additional data, to easily get pbr lighting with custom modifications, or otherwise extend a base material. # Solution - added `ExtendedMaterial<B: Material, E: MaterialExtension>` which contains a base material and a user-defined extension. - added example `extended_material` showing how to use it - modified AsBindGroup to have "unprepared" functions that return raw resources / layout entries so that the extended material can combine them note: doesn't currently work with array resources, as i can't figure out how to make the OwnedBindingResource::get_binding() work, as wgpu requires a `&'a[&'a TextureView]` and i have a `Vec<TextureView>`. # Migration Guide manual implementations of `AsBindGroup` will need to be adjusted, the changes are pretty straightforward and can be seen in the diff for e.g. the `texture_binding_array` example. --------- Co-authored-by: Robert Swain <robert.swain@gmail.com> |
||
![]() |
687e379800
|
Updates for rust 1.73 (#10035)
# Objective - Updates for rust 1.73 ## Solution - new doc check for `redundant_explicit_links` - updated to text for compile fail tests --- ## Changelog - updates for rust 1.73 |
||
![]() |
7163aabf29
|
Use a single line for of large binding lists (#9849)
# Objective - When adding/removing bindings in large binding lists, git would generate very difficult-to-read diffs ## Solution - Move the `@group(X) @binding(Y)` into the same line as the binding type declaration |
||
![]() |
ee1368a032
|
Fix AsBindGroup derive, texture attribute, visibility flag parsing (#8868)
# Objective - Fix the AsBindGroup texture attribute visibility flag parsing - This appears to have been caused by a syn crate update which then the visibility code got updated - Also I noticed that by default the vertex and fragment flags were on, so visibility(compute) would actually make the texture visible to vertex, fragment and compute shaders, I fixed this too ## Solution - Update flag parsing to use MetaList.parse_nested_meta function, which loads the flags into a Vec then loop through those flags - Change initial visibility flags to use VisibilityFlags::default() rather than VisibilityFlags::vertex_fragment() |
||
![]() |
12aadfd4a6 |
Support raw buffers in AsBindGroup macro (#7701)
# Objective There was PR that introduced support for storage buffer is `AsBindGroup` macro [#6129](https://github.com/bevyengine/bevy/pull/6129), but it does not give more granular control over storage buffer, it will always copy all the data no matter which part of it was updated. There is also currently another open PR #6669 that tries to achieve exactly that, it is just not up to date and seems abandoned (Sorry if that is not right). In this PR I'm proposing a solution for both of these approaches to co-exist using `#[storage(n, buffer)]` and `#[storage(n)]` to distinguish between the cases. We could also discuss in this PR if there is a need to extend this support to DynamicBuffers as well. |
||
![]() |
ee4e98f8a9 |
Support storage buffers in derive AsBindGroup (#6129)
# Objective - Storage buffers are useful and not currently supported by the `AsBindGroup` derive which means you need to expand the macro if you need a storage buffer ## Solution - Add a new `#[storage]` attribute to the derive `AsBindGroup` macro. - Support and optional `read_only` parameter that defaults to false when not present. - Support visibility parameters like the texture and sampler attributes. --- ## Changelog - Add a new `#[storage(index)]` attribute to the derive `AsBindGroup` macro. Co-authored-by: IceSentry <IceSentry@users.noreply.github.com> |
||
![]() |
965ebeff59 |
Replace UUID based IDs with a atomic-counted ones (#6988)
# Objective - alternative to #2895 - as mentioned in #2535 the uuid based ids in the render module should be replaced with atomic-counted ones ## Solution - instead of generating a random UUID for each render resource, this implementation increases an atomic counter - this might be replaced by the ids of wgpu if they expose them directly in the future - I have not benchmarked this solution yet, but this should be slightly faster in theory. - Bevymark does not seem to be affected much by this change, which is to be expected. - Nothing of our API has changed, other than that the IDs have lost their IMO rather insignificant documentation. - Maybe the documentation could be added back into the macro, but this would complicate the code. |
||
![]() |
38d567d2c5 |
Make AsBindGroup unsized (#6937)
# Objective `AsBindGroup` can't be used as a trait object because of the constraint `Sized` and because of the associated function. This is a problem for [`bevy_atmosphere`](https://github.com/JonahPlusPlus/bevy_atmosphere) because it needs to use a trait that depends on `AsBindGroup` as a trait object, for switching out different shaders at runtime. The current solution it employs is reimplementing the trait and derive macro into that trait, instead of constraining to `AsBindGroup`. ## Solution Remove the `Sized` constraint from `AsBindGroup` and add the constraint `where Self: Sized` to the associated function `bind_group_layout`. Also change `PreparedBindGroup<T: AsBindGroup>` to `PreparedBindGroup<T>` and use it as `PreparedBindGroup<Self::Data>` instead of `PreparedBindGroup<Self>`. This weakens the constraints, but increases the flexibility of `AsBindGroup`. I'm not entirely sure why the `Sized` constraint was there, because it worked fine without it (maybe @cart wasn't aware of use cases for `AsBindGroup` as a trait object or this was just leftover from legacy code?). --- ## Changelog - `AsBindGroup` can be used as a trait object. |
||
![]() |
36691769ba |
Document undocumented features of AsBindGroup derive (#6910)
# Objective - https://github.com/bevyengine/bevy/pull/5364 Added a few features to the AsBindGroup derive, but if you don't know they exist they aren't documented anywhere. ## Solution - Document the new arguments in the doc block for the derive. |
||
![]() |
64642fbd3c |
Remove unnecessary struct in Material AsBindGroup example (#6701)
# Objective - Reduce confusion around uniform bindings in materials. I've seen multiple people on discord get confused by it because it uses a struct that is named the same in the rust code and the wgsl code, but doesn't contain the same data. Also, the only reason this works is mostly by chance because the memory happens to align correctly. ## Solution - Remove the confusing parts of the doc ## Notes It's not super clear in the diff why this causes confusion, but essentially, the rust code defines a `CustomMaterial` struct with a color and a texture, but in the wgsl code the struct with the same name only contains the color. People are confused by it because the struct in wgsl doesn't need to be there. You _can_ have complex structs on each side and the macro will even combine it for you if you reuse a binding index, but as it is now, this example seems to confuse more than help people. |
||
![]() |
2cd0bd7575 |
improve compile time by type-erasing wgpu structs (#5950)
# Objective structs containing wgpu types take a long time to compile. this is particularly bad for generics containing the wgpu structs (like the depth pipeline builder with `#[derive(SystemParam)]` i've been working on). we can avoid that by boxing and type-erasing in the bevy `render_resource` wrappers. type system magic is not a strength of mine so i guess there will be a cleaner way to achieve this, happy to take feedback or for it to be taken as a proof of concept if someone else wants to do a better job. ## Solution - add macros to box and type-erase in debug mode - leave current impl for release mode timings: <html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40"> <head> <meta name=ProgId content=Excel.Sheet> <meta name=Generator content="Microsoft Excel 15"> <link id=Main-File rel=Main-File href="file:///C:/Users/robfm/AppData/Local/Temp/msohtmlclip1/01/clip.htm"> <link rel=File-List href="file:///C:/Users/robfm/AppData/Local/Temp/msohtmlclip1/01/clip_filelist.xml"> <!--table {mso-displayed-decimal-separator:"\."; mso-displayed-thousand-separator:"\,";} @page {margin:.75in .7in .75in .7in; mso-header-margin:.3in; mso-footer-margin:.3in;} tr {mso-height-source:auto;} col {mso-width-source:auto;} br {mso-data-placement:same-cell;} td {padding-top:1px; padding-right:1px; padding-left:1px; mso-ignore:padding; color:black; font-size:11.0pt; font-weight:400; font-style:normal; text-decoration:none; font-family:Calibri, sans-serif; mso-font-charset:0; mso-number-format:General; text-align:general; vertical-align:bottom; border:none; mso-background-source:auto; mso-pattern:auto; mso-protection:locked visible; white-space:nowrap; mso-rotate:0;} .xl65 {mso-number-format:0%;} .xl66 {vertical-align:middle; white-space:normal;} .xl67 {vertical-align:middle;} --> </head> <body link="#0563C1" vlink="#954F72"> current | | | -- | -- | -- | -- | Total time: | 64.9s | | bevy_pbr v0.9.0-dev | 19.2s | | bevy_render v0.9.0-dev | 17.0s | | bevy_sprite v0.9.0-dev | 15.1s | | DepthPipelineBuilder | 18.7s | | | | with type-erasing | | | diff | Total time: | 49.0s | -24% | bevy_render v0.9.0-dev | 12.0s | -38% | bevy_pbr v0.9.0-dev | 8.7s | -49% | bevy_sprite v0.9.0-dev | 6.1s | -60% | DepthPipelineBuilder | 1.2s | -94% </body> </html> the depth pipeline builder is a binary with body: ```rust use std::{marker::PhantomData, hash::Hash}; use bevy::{prelude::*, ecs::system::SystemParam, pbr::{RenderMaterials, MaterialPipeline, ShadowPipeline}, render::{renderer::RenderDevice, render_resource::{SpecializedMeshPipelines, PipelineCache}, render_asset::RenderAssets}}; fn main() { println!("Hello, world p!\n"); } #[derive(SystemParam)] pub struct DepthPipelineBuilder<'w, 's, M: Material> where M::Data: Eq + Hash + Clone, { render_device: Res<'w, RenderDevice>, material_pipeline: Res<'w, MaterialPipeline<M>>, material_pipelines: ResMut<'w, SpecializedMeshPipelines<MaterialPipeline<M>>>, shadow_pipeline: Res<'w, ShadowPipeline>, pipeline_cache: ResMut<'w, PipelineCache>, render_meshes: Res<'w, RenderAssets<Mesh>>, render_materials: Res<'w, RenderMaterials<M>>, msaa: Res<'w, Msaa>, #[system_param(ignore)] _p: PhantomData<&'s M>, } ``` |
||
![]() |
814f8d1635 |
update wgpu to 0.13 (#5168)
# Objective - Update wgpu to 0.13 - ~~Wait, is wgpu 0.13 released? No, but I had most of the changes already ready since playing with webgpu~~ well it has been released now - Also update parking_lot to 0.12 and naga to 0.9 ## Solution - Update syntax for wgsl shaders https://github.com/gfx-rs/wgpu/blob/master/CHANGELOG.md#wgsl-syntax - Add a few options, remove some references: https://github.com/gfx-rs/wgpu/blob/master/CHANGELOG.md#other-breaking-changes - fragment inputs should now exactly match vertex outputs for locations, so I added exports for those to be able to reuse them https://github.com/gfx-rs/wgpu/pull/2704 |
||
![]() |
747b0c69b0 |
Better Materials: AsBindGroup trait and derive, simpler Material trait (#5053)
# Objective This PR reworks Bevy's Material system, making the user experience of defining Materials _much_ nicer. Bevy's previous material system leaves a lot to be desired: * Materials require manually implementing the `RenderAsset` trait, which involves manually generating the bind group, handling gpu buffer data transfer, looking up image textures, etc. Even the simplest single-texture material involves writing ~80 unnecessary lines of code. This was never the long term plan. * There are two material traits, which is confusing, hard to document, and often redundant: `Material` and `SpecializedMaterial`. `Material` implicitly implements `SpecializedMaterial`, and `SpecializedMaterial` is used in most high level apis to support both use cases. Most users shouldn't need to think about specialization at all (I consider it a "power-user tool"), so the fact that `SpecializedMaterial` is front-and-center in our apis is a miss. * Implementing either material trait involves a lot of "type soup". The "prepared asset" parameter is particularly heinous: `&<Self as RenderAsset>::PreparedAsset`. Defining vertex and fragment shaders is also more verbose than it needs to be. ## Solution Say hello to the new `Material` system: ```rust #[derive(AsBindGroup, TypeUuid, Debug, Clone)] #[uuid = "f690fdae-d598-45ab-8225-97e2a3f056e0"] pub struct CoolMaterial { #[uniform(0)] color: Color, #[texture(1)] #[sampler(2)] color_texture: Handle<Image>, } impl Material for CoolMaterial { fn fragment_shader() -> ShaderRef { "cool_material.wgsl".into() } } ``` Thats it! This same material would have required [~80 lines of complicated "type heavy" code](https://github.com/bevyengine/bevy/blob/v0.7.0/examples/shader/shader_material.rs) in the old Material system. Now it is just 14 lines of simple, readable code. This is thanks to a new consolidated `Material` trait and the new `AsBindGroup` trait / derive. ### The new `Material` trait The old "split" `Material` and `SpecializedMaterial` traits have been removed in favor of a new consolidated `Material` trait. All of the functions on the trait are optional. The difficulty of implementing `Material` has been reduced by simplifying dataflow and removing type complexity: ```rust // Old impl Material for CustomMaterial { fn fragment_shader(asset_server: &AssetServer) -> Option<Handle<Shader>> { Some(asset_server.load("custom_material.wgsl")) } fn alpha_mode(render_asset: &<Self as RenderAsset>::PreparedAsset) -> AlphaMode { render_asset.alpha_mode } } // New impl Material for CustomMaterial { fn fragment_shader() -> ShaderRef { "custom_material.wgsl".into() } fn alpha_mode(&self) -> AlphaMode { self.alpha_mode } } ``` Specialization is still supported, but it is hidden by default under the `specialize()` function (more on this later). ### The `AsBindGroup` trait / derive The `Material` trait now requires the `AsBindGroup` derive. This can be implemented manually relatively easily, but deriving it will almost always be preferable. Field attributes like `uniform` and `texture` are used to define which fields should be bindings, what their binding type is, and what index they should be bound at: ```rust #[derive(AsBindGroup)] struct CoolMaterial { #[uniform(0)] color: Color, #[texture(1)] #[sampler(2)] color_texture: Handle<Image>, } ``` In WGSL shaders, the binding looks like this: ```wgsl struct CoolMaterial { color: vec4<f32>; }; [[group(1), binding(0)]] var<uniform> material: CoolMaterial; [[group(1), binding(1)]] var color_texture: texture_2d<f32>; [[group(1), binding(2)]] var color_sampler: sampler; ``` Note that the "group" index is determined by the usage context. It is not defined in `AsBindGroup`. Bevy material bind groups are bound to group 1. The following field-level attributes are supported: * `uniform(BINDING_INDEX)` * The field will be converted to a shader-compatible type using the `ShaderType` trait, written to a `Buffer`, and bound as a uniform. It can also be derived for custom structs. * `texture(BINDING_INDEX)` * This field's `Handle<Image>` will be used to look up the matching `Texture` gpu resource, which will be bound as a texture in shaders. The field will be assumed to implement `Into<Option<Handle<Image>>>`. In practice, most fields should be a `Handle<Image>` or `Option<Handle<Image>>`. If the value of an `Option<Handle<Image>>` is `None`, the new `FallbackImage` resource will be used instead. This attribute can be used in conjunction with a `sampler` binding attribute (with a different binding index). * `sampler(BINDING_INDEX)` * Behaves exactly like the `texture` attribute, but sets the Image's sampler binding instead of the texture. Note that fields without field-level binding attributes will be ignored. ```rust #[derive(AsBindGroup)] struct CoolMaterial { #[uniform(0)] color: Color, this_field_is_ignored: String, } ``` As mentioned above, `Option<Handle<Image>>` is also supported: ```rust #[derive(AsBindGroup)] struct CoolMaterial { #[uniform(0)] color: Color, #[texture(1)] #[sampler(2)] color_texture: Option<Handle<Image>>, } ``` This is useful if you want a texture to be optional. When the value is `None`, the `FallbackImage` will be used for the binding instead, which defaults to "pure white". Field uniforms with the same binding index will be combined into a single binding: ```rust #[derive(AsBindGroup)] struct CoolMaterial { #[uniform(0)] color: Color, #[uniform(0)] roughness: f32, } ``` In WGSL shaders, the binding would look like this: ```wgsl struct CoolMaterial { color: vec4<f32>; roughness: f32; }; [[group(1), binding(0)]] var<uniform> material: CoolMaterial; ``` Some less common scenarios will require "struct-level" attributes. These are the currently supported struct-level attributes: * `uniform(BINDING_INDEX, ConvertedShaderType)` * Similar to the field-level `uniform` attribute, but instead the entire `AsBindGroup` value is converted to `ConvertedShaderType`, which must implement `ShaderType`. This is useful if more complicated conversion logic is required. * `bind_group_data(DataType)` * The `AsBindGroup` type will be converted to some `DataType` using `Into<DataType>` and stored as `AsBindGroup::Data` as part of the `AsBindGroup::as_bind_group` call. This is useful if data needs to be stored alongside the generated bind group, such as a unique identifier for a material's bind group. The most common use case for this attribute is "shader pipeline specialization". The previous `CoolMaterial` example illustrating "combining multiple field-level uniform attributes with the same binding index" can also be equivalently represented with a single struct-level uniform attribute: ```rust #[derive(AsBindGroup)] #[uniform(0, CoolMaterialUniform)] struct CoolMaterial { color: Color, roughness: f32, } #[derive(ShaderType)] struct CoolMaterialUniform { color: Color, roughness: f32, } impl From<&CoolMaterial> for CoolMaterialUniform { fn from(material: &CoolMaterial) -> CoolMaterialUniform { CoolMaterialUniform { color: material.color, roughness: material.roughness, } } } ``` ### Material Specialization Material shader specialization is now _much_ simpler: ```rust #[derive(AsBindGroup, TypeUuid, Debug, Clone)] #[uuid = "f690fdae-d598-45ab-8225-97e2a3f056e0"] #[bind_group_data(CoolMaterialKey)] struct CoolMaterial { #[uniform(0)] color: Color, is_red: bool, } #[derive(Copy, Clone, Hash, Eq, PartialEq)] struct CoolMaterialKey { is_red: bool, } impl From<&CoolMaterial> for CoolMaterialKey { fn from(material: &CoolMaterial) -> CoolMaterialKey { CoolMaterialKey { is_red: material.is_red, } } } impl Material for CoolMaterial { fn fragment_shader() -> ShaderRef { "cool_material.wgsl".into() } fn specialize( pipeline: &MaterialPipeline<Self>, descriptor: &mut RenderPipelineDescriptor, layout: &MeshVertexBufferLayout, key: MaterialPipelineKey<Self>, ) -> Result<(), SpecializedMeshPipelineError> { if key.bind_group_data.is_red { let fragment = descriptor.fragment.as_mut().unwrap(); fragment.shader_defs.push("IS_RED".to_string()); } Ok(()) } } ``` Setting `bind_group_data` is not required for specialization (it defaults to `()`). Scenarios like "custom vertex attributes" also benefit from this system: ```rust impl Material for CustomMaterial { fn vertex_shader() -> ShaderRef { "custom_material.wgsl".into() } fn fragment_shader() -> ShaderRef { "custom_material.wgsl".into() } fn specialize( pipeline: &MaterialPipeline<Self>, descriptor: &mut RenderPipelineDescriptor, layout: &MeshVertexBufferLayout, key: MaterialPipelineKey<Self>, ) -> Result<(), SpecializedMeshPipelineError> { let vertex_layout = layout.get_layout(&[ Mesh::ATTRIBUTE_POSITION.at_shader_location(0), ATTRIBUTE_BLEND_COLOR.at_shader_location(1), ])?; descriptor.vertex.buffers = vec![vertex_layout]; Ok(()) } } ``` ### Ported `StandardMaterial` to the new `Material` system Bevy's built-in PBR material uses the new Material system (including the AsBindGroup derive): ```rust #[derive(AsBindGroup, Debug, Clone, TypeUuid)] #[uuid = "7494888b-c082-457b-aacf-517228cc0c22"] #[bind_group_data(StandardMaterialKey)] #[uniform(0, StandardMaterialUniform)] pub struct StandardMaterial { pub base_color: Color, #[texture(1)] #[sampler(2)] pub base_color_texture: Option<Handle<Image>>, /* other fields omitted for brevity */ ``` ### Ported Bevy examples to the new `Material` system The overall complexity of Bevy's "custom shader examples" has gone down significantly. Take a look at the diffs if you want a dopamine spike. Please note that while this PR has a net increase in "lines of code", most of those extra lines come from added documentation. There is a significant reduction in the overall complexity of the code (even accounting for the new derive logic). --- ## Changelog ### Added * `AsBindGroup` trait and derive, which make it much easier to transfer data to the gpu and generate bind groups for a given type. ### Changed * The old `Material` and `SpecializedMaterial` traits have been replaced by a consolidated (much simpler) `Material` trait. Materials no longer implement `RenderAsset`. * `StandardMaterial` was ported to the new material system. There are no user-facing api changes to the `StandardMaterial` struct api, but it now implements `AsBindGroup` and `Material` instead of `RenderAsset` and `SpecializedMaterial`. ## Migration Guide The Material system has been reworked to be much simpler. We've removed a lot of boilerplate with the new `AsBindGroup` derive and the `Material` trait is simpler as well! ### Bevy 0.7 (old) ```rust #[derive(Debug, Clone, TypeUuid)] #[uuid = "f690fdae-d598-45ab-8225-97e2a3f056e0"] pub struct CustomMaterial { color: Color, color_texture: Handle<Image>, } #[derive(Clone)] pub struct GpuCustomMaterial { _buffer: Buffer, bind_group: BindGroup, } impl RenderAsset for CustomMaterial { type ExtractedAsset = CustomMaterial; type PreparedAsset = GpuCustomMaterial; type Param = (SRes<RenderDevice>, SRes<MaterialPipeline<Self>>); fn extract_asset(&self) -> Self::ExtractedAsset { self.clone() } fn prepare_asset( extracted_asset: Self::ExtractedAsset, (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: buffer.as_ref(), label: None, usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST, }); let (texture_view, texture_sampler) = if let Some(result) = material_pipeline .mesh_pipeline .get_image_texture(gpu_images, &Some(extracted_asset.color_texture.clone())) { result } else { return Err(PrepareAssetError::RetryNextUpdate(extracted_asset)); }; let bind_group = render_device.create_bind_group(&BindGroupDescriptor { entries: &[ BindGroupEntry { binding: 0, resource: buffer.as_entire_binding(), }, BindGroupEntry { binding: 0, resource: BindingResource::TextureView(texture_view), }, BindGroupEntry { binding: 1, resource: BindingResource::Sampler(texture_sampler), }, ], label: None, layout: &material_pipeline.material_layout, }); Ok(GpuCustomMaterial { _buffer: buffer, bind_group, }) } } impl Material for CustomMaterial { fn fragment_shader(asset_server: &AssetServer) -> Option<Handle<Shader>> { Some(asset_server.load("custom_material.wgsl")) } fn bind_group(render_asset: &<Self as RenderAsset>::PreparedAsset) -> &BindGroup { &render_asset.bind_group } fn bind_group_layout(render_device: &RenderDevice) -> BindGroupLayout { render_device.create_bind_group_layout(&BindGroupLayoutDescriptor { entries: &[ BindGroupLayoutEntry { binding: 0, visibility: ShaderStages::FRAGMENT, ty: BindingType::Buffer { ty: BufferBindingType::Uniform, has_dynamic_offset: false, min_binding_size: Some(Vec4::min_size()), }, count: None, }, BindGroupLayoutEntry { binding: 1, visibility: ShaderStages::FRAGMENT, ty: BindingType::Texture { multisampled: false, sample_type: TextureSampleType::Float { filterable: true }, view_dimension: TextureViewDimension::D2Array, }, count: None, }, BindGroupLayoutEntry { binding: 2, visibility: ShaderStages::FRAGMENT, ty: BindingType::Sampler(SamplerBindingType::Filtering), count: None, }, ], label: None, }) } } ``` ### Bevy 0.8 (new) ```rust impl Material for CustomMaterial { fn fragment_shader() -> ShaderRef { "custom_material.wgsl".into() } } #[derive(AsBindGroup, TypeUuid, Debug, Clone)] #[uuid = "f690fdae-d598-45ab-8225-97e2a3f056e0"] pub struct CustomMaterial { #[uniform(0)] color: Color, #[texture(1)] #[sampler(2)] color_texture: Handle<Image>, } ``` ## Future Work * Add support for more binding types (cubemaps, buffers, etc). This PR intentionally includes a bare minimum number of binding types to keep "reviewability" in check. * Consider optionally eliding binding indices using binding names. `AsBindGroup` could pass in (optional?) reflection info as a "hint". * This would make it possible for the derive to do this: ```rust #[derive(AsBindGroup)] pub struct CustomMaterial { #[uniform] color: Color, #[texture] #[sampler] color_texture: Option<Handle<Image>>, alpha_mode: AlphaMode, } ``` * Or this ```rust #[derive(AsBindGroup)] pub struct CustomMaterial { #[binding] color: Color, #[binding] color_texture: Option<Handle<Image>>, alpha_mode: AlphaMode, } ``` * Or even this (if we flip to "include bindings by default") ```rust #[derive(AsBindGroup)] pub struct CustomMaterial { color: Color, color_texture: Option<Handle<Image>>, #[binding(ignore)] alpha_mode: AlphaMode, } ``` * If we add the option to define custom draw functions for materials (which could be done in a type-erased way), I think that would be enough to support extra non-material bindings. Worth considering! |
||
![]() |
ffecb05a0a |
Replace old renderer with new renderer (#3312)
This makes the [New Bevy Renderer](#2535) the default (and only) renderer. The new renderer isn't _quite_ ready for the final release yet, but I want as many people as possible to start testing it so we can identify bugs and address feedback prior to release. The examples are all ported over and operational with a few exceptions: * I removed a good portion of the examples in the `shader` folder. We still have some work to do in order to make these examples possible / ergonomic / worthwhile: #3120 and "high level shader material plugins" are the big ones. This is a temporary measure. * Temporarily removed the multiple_windows example: doing this properly in the new renderer will require the upcoming "render targets" changes. Same goes for the render_to_texture example. * Removed z_sort_debug: entity visibility sort info is no longer available in app logic. we could do this on the "render app" side, but i dont consider it a priority. |
||
![]() |
b12c4d0a48 | render: simplify imports and cleanup prelude | ||
![]() |
0fec350411 | render: rename "Assignment" to "Binding" and "AssignmentSet" to "BindGroup" |