mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
239 commits
Author | SHA1 | Message | Date | |
---|---|---|---|---|
Patrick Walton
|
16531fb3e3
|
Implement GPU frustum culling. (#12889)
This commit implements opt-in GPU frustum culling, built on top of the infrastructure in https://github.com/bevyengine/bevy/pull/12773. To enable it on a camera, add the `GpuCulling` component to it. To additionally disable CPU frustum culling, add the `NoCpuCulling` component. Note that adding `GpuCulling` without `NoCpuCulling` *currently* does nothing useful. The reason why `GpuCulling` doesn't automatically imply `NoCpuCulling` is that I intend to follow this patch up with GPU two-phase occlusion culling, and CPU frustum culling plus GPU occlusion culling seems like a very commonly-desired mode. Adding the `GpuCulling` component to a view puts that view into *indirect mode*. This mode makes all drawcalls indirect, relying on the mesh preprocessing shader to allocate instances dynamically. In indirect mode, the `PreprocessWorkItem` `output_index` points not to a `MeshUniform` instance slot but instead to a set of `wgpu` `IndirectParameters`, from which it allocates an instance slot dynamically if frustum culling succeeds. Batch building has been updated to allocate and track indirect parameter slots, and the AABBs are now supplied to the GPU as `MeshCullingData`. A small amount of code relating to the frustum culling has been borrowed from meshlets and moved into `maths.wgsl`. Note that standard Bevy frustum culling uses AABBs, while meshlets use bounding spheres; this means that not as much code can be shared as one might think. This patch doesn't provide any way to perform GPU culling on shadow maps, to avoid making this patch bigger than it already is. That can be a followup. ## Changelog ### Added * Frustum culling can now optionally be done on the GPU. To enable it, add the `GpuCulling` component to a camera. * To disable CPU frustum culling, add `NoCpuCulling` to a camera. Note that `GpuCulling` doesn't automatically imply `NoCpuCulling`. |
||
Patrick Walton
|
11817f4ba4
|
Generate MeshUniform s on the GPU via compute shader where available. (#12773)
Currently, `MeshUniform`s are rather large: 160 bytes. They're also somewhat expensive to compute, because they involve taking the inverse of a 3x4 matrix. Finally, if a mesh is present in multiple views, that mesh will have a separate `MeshUniform` for each and every view, which is wasteful. This commit fixes these issues by introducing the concept of a *mesh input uniform* and adding a *mesh uniform building* compute shader pass. The `MeshInputUniform` is simply the minimum amount of data needed for the GPU to compute the full `MeshUniform`. Most of this data is just the transform and is therefore only 64 bytes. `MeshInputUniform`s are computed during the *extraction* phase, much like skins are today, in order to avoid needlessly copying transforms around on CPU. (In fact, the render app has been changed to only store the translation of each mesh; it no longer cares about any other part of the transform, which is stored only on the GPU and the main world.) Before rendering, the `build_mesh_uniforms` pass runs to expand the `MeshInputUniform`s to the full `MeshUniform`. The mesh uniform building pass does the following, all on GPU: 1. Copy the appropriate fields of the `MeshInputUniform` to the `MeshUniform` slot. If a single mesh is present in multiple views, this effectively duplicates it into each view. 2. Compute the inverse transpose of the model transform, used for transforming normals. 3. If applicable, copy the mesh's transform from the previous frame for TAA. To support this, we double-buffer the `MeshInputUniform`s over two frames and swap the buffers each frame. The `MeshInputUniform`s for the current frame contain the index of that mesh's `MeshInputUniform` for the previous frame. This commit produces wins in virtually every CPU part of the pipeline: `extract_meshes`, `queue_material_meshes`, `batch_and_prepare_render_phase`, and especially `write_batched_instance_buffer` are all faster. Shrinking the amount of CPU data that has to be shuffled around speeds up the entire rendering process. | Benchmark | This branch | `main` | Speedup | |------------------------|-------------|---------|---------| | `many_cubes -nfc` | 17.259 | 24.529 | 42.12% | | `many_cubes -nfc -vpi` | 302.116 | 312.123 | 3.31% | | `many_foxes` | 3.227 | 3.515 | 8.92% | Because mesh uniform building requires compute shader, and WebGL 2 has no compute shader, the existing CPU mesh uniform building code has been left as-is. Many types now have both CPU mesh uniform building and GPU mesh uniform building modes. Developers can opt into the old CPU mesh uniform building by setting the `use_gpu_uniform_builder` option on `PbrPlugin` to `false`. Below are graphs of the CPU portions of `many-cubes --no-frustum-culling`. Yellow is this branch, red is `main`. `extract_meshes`: ![Screenshot 2024-04-02 124842](https://github.com/bevyengine/bevy/assets/157897/a6748ea4-dd05-47b6-9254-45d07d33cb10) It's notable that we get a small win even though we're now writing to a GPU buffer. `queue_material_meshes`: ![Screenshot 2024-04-02 124911](https://github.com/bevyengine/bevy/assets/157897/ecb44d78-65dc-448d-ba85-2de91aa2ad94) There's a bit of a regression here; not sure what's causing it. In any case it's very outweighed by the other gains. `batch_and_prepare_render_phase`: ![Screenshot 2024-04-02 125123](https://github.com/bevyengine/bevy/assets/157897/4e20fc86-f9dd-4e5c-8623-837e4258f435) There's a huge win here, enough to make batching basically drop off the profile. `write_batched_instance_buffer`: ![Screenshot 2024-04-02 125237](https://github.com/bevyengine/bevy/assets/157897/401a5c32-9dc1-4991-996d-eb1cac6014b2) There's a massive improvement here, as expected. Note that a lot of it simply comes from the fact that `MeshInputUniform` is `Pod`. (This isn't a maintainability problem in my view because `MeshInputUniform` is so simple: just 16 tightly-packed words.) ## Changelog ### Added * Per-mesh instance data is now generated on GPU with a compute shader instead of CPU, resulting in rendering performance improvements on platforms where compute shaders are supported. ## Migration guide * Custom render phases now need multiple systems beyond just `batch_and_prepare_render_phase`. Code that was previously creating custom render phases should now add a `BinnedRenderPhasePlugin` or `SortedRenderPhasePlugin` as appropriate instead of directly adding `batch_and_prepare_render_phase`. |
||
Robert Swain
|
ab7cbfa8fc
|
Consolidate Render(Ui)Materials(2d) into RenderAssets (#12827)
# Objective - Replace `RenderMaterials` / `RenderMaterials2d` / `RenderUiMaterials` with `RenderAssets` to enable implementing changes to one thing, `RenderAssets`, that applies to all use cases rather than duplicating changes everywhere for multiple things that should be one thing. - Adopts #8149 ## Solution - Make RenderAsset generic over the destination type rather than the source type as in #8149 - Use `RenderAssets<PreparedMaterial<M>>` etc for render materials --- ## Changelog - Changed: - The `RenderAsset` trait is now implemented on the destination type. Its `SourceAsset` associated type refers to the type of the source asset. - `RenderMaterials`, `RenderMaterials2d`, and `RenderUiMaterials` have been replaced by `RenderAssets<PreparedMaterial<M>>` and similar. ## Migration Guide - `RenderAsset` is now implemented for the destination type rather that the source asset type. The source asset type is now the `RenderAsset` trait's `SourceAsset` associated type. |
||
François Mockers
|
de3ec47f3f
|
Fix example game of life (#12897)
# Objective - Example `compute_shader_game_of_life` is random and not following the rules of the game of life: at each steps, it randomly reads some pixel of the current step and some of the previous step instead of only from the previous step - Fixes #9353 ## Solution - Adopted from #9678 - Added a switch of the texture displayed every frame otherwise the game of life looks wrong - Added a way to display the texture bigger so that I could manually check everything was right --------- Co-authored-by: Sludge <96552222+SludgePhD@users.noreply.github.com> Co-authored-by: IceSentry <IceSentry@users.noreply.github.com> |
||
IceSentry
|
08b41878d7
|
Add gpu readback example (#12877)
# Objective
- It's pretty common for users to want to read data back from the gpu
and into the main world
## Solution
- Add a simple example that shows how to read data back from the gpu and
send it to the main world using a channel.
- The example is largely based on this wgpu example but adapted to bevy
-
|
||
Patrick Walton
|
37522fd0ae
|
Micro-optimize queue_material_meshes , primarily to remove bit manipulation. (#12791)
This commit makes the following optimizations: ## `MeshPipelineKey`/`BaseMeshPipelineKey` split `MeshPipelineKey` has been split into `BaseMeshPipelineKey`, which lives in `bevy_render` and `MeshPipelineKey`, which lives in `bevy_pbr`. Conceptually, `BaseMeshPipelineKey` is a superclass of `MeshPipelineKey`. For `BaseMeshPipelineKey`, the bits start at the highest (most significant) bit and grow downward toward the lowest bit; for `MeshPipelineKey`, the bits start at the lowest bit and grow upward toward the highest bit. This prevents them from colliding. The goal of this is to avoid having to reassemble bits of the pipeline key for every mesh every frame. Instead, we can just use a bitwise or operation to combine the pieces that make up a `MeshPipelineKey`. ## `specialize_slow` Previously, all of `specialize()` was marked as `#[inline]`. This bloated `queue_material_meshes` unnecessarily, as a large chunk of it ended up being a slow path that was rarely hit. This commit refactors the function to move the slow path to `specialize_slow()`. Together, these two changes shave about 5% off `queue_material_meshes`: ![Screenshot 2024-03-29 130002](https://github.com/bevyengine/bevy/assets/157897/a7e5a994-a807-4328-b314-9003429dcdd2) ## Migration Guide - The `primitive_topology` field on `GpuMesh` is now an accessor method: `GpuMesh::primitive_topology()`. - For performance reasons, `MeshPipelineKey` has been split into `BaseMeshPipelineKey`, which lives in `bevy_render`, and `MeshPipelineKey`, which lives in `bevy_pbr`. These two should be combined with bitwise-or to produce the final `MeshPipelineKey`. |
||
BD103
|
84363f2fab
|
Remove redundant imports (#12817)
# Objective - There are several redundant imports in the tests and examples that are not caught by CI because additional flags need to be passed. ## Solution - Run `cargo check --workspace --tests` and `cargo check --workspace --examples`, then fix all warnings. - Add `test-check` to CI, which will be run in the check-compiles job. This should catch future warnings for tests. Examples are already checked, but I'm not yet sure why they weren't caught. ## Discussion - Should the `--tests` and `--examples` flags be added to CI, so this is caught in the future? - If so, #12818 will need to be merged first. It was also a warning raised by checking the examples, but I chose to split off into a separate PR. --------- Co-authored-by: François Mockers <francois.mockers@vleue.com> |
||
Cameron
|
01649f13e2
|
Refactor App and SubApp internals for better separation (#9202)
# Objective This is a necessary precursor to #9122 (this was split from that PR to reduce the amount of code to review all at once). Moving `!Send` resource ownership to `App` will make it unambiguously `!Send`. `SubApp` must be `Send`, so it can't wrap `App`. ## Solution Refactor `App` and `SubApp` to not have a recursive relationship. Since `SubApp` no longer wraps `App`, once `!Send` resources are moved out of `World` and into `App`, `SubApp` will become unambiguously `Send`. There could be less code duplication between `App` and `SubApp`, but that would break `App` method chaining. ## Changelog - `SubApp` no longer wraps `App`. - `App` fields are no longer publicly accessible. - `App` can no longer be converted into a `SubApp`. - Various methods now return references to a `SubApp` instead of an `App`. ## Migration Guide - To construct a sub-app, use `SubApp::new()`. `App` can no longer convert into `SubApp`. - If you implemented a trait for `App`, you may want to implement it for `SubApp` as well. - If you're accessing `app.world` directly, you now have to use `app.world()` and `app.world_mut()`. - `App::sub_app` now returns `&SubApp`. - `App::sub_app_mut` now returns `&mut SubApp`. - `App::get_sub_app` now returns `Option<&SubApp>.` - `App::get_sub_app_mut` now returns `Option<&mut SubApp>.` |
||
agiletelescope
|
3e1c84690b
|
Added a small comment to post_processing example with instructions on how to make it work for 2d (#12775)
# Objective - `examples/shader/post_processing.rs` is a shader example that works for 3d - I recently tried to update this example to get it to work on 2d but failed to do so - Then I created a discord help thread to help me figure this out. [here's the link to the thread](https://discordapp.com/channels/691052431525675048/1221819669116354723). ## Solution - The solution is to replace all instances of 3d structures with their respective 2d counterparts ## Changelog - Added a small comment that explains how to get the example to work on 2d #### Please do suggest changes if any --------- Co-authored-by: IceSentry <IceSentry@users.noreply.github.com> |
||
Patrick Walton
|
4dadebd9c4
|
Improve performance by binning together opaque items instead of sorting them. (#12453)
Today, we sort all entities added to all phases, even the phases that don't strictly need sorting, such as the opaque and shadow phases. This results in a performance loss because our `PhaseItem`s are rather large in memory, so sorting is slow. Additionally, determining the boundaries of batches is an O(n) process. This commit makes Bevy instead applicable place phase items into *bins* keyed by *bin keys*, which have the invariant that everything in the same bin is potentially batchable. This makes determining batch boundaries O(1), because everything in the same bin can be batched. Instead of sorting each entity, we now sort only the bin keys. This drops the sorting time to near-zero on workloads with few bins like `many_cubes --no-frustum-culling`. Memory usage is improved too, with batch boundaries and dynamic indices now implicit instead of explicit. The improved memory usage results in a significant win even on unbatchable workloads like `many_cubes --no-frustum-culling --vary-material-data-per-instance`, presumably due to cache effects. Not all phases can be binned; some, such as transparent and transmissive phases, must still be sorted. To handle this, this commit splits `PhaseItem` into `BinnedPhaseItem` and `SortedPhaseItem`. Most of the logic that today deals with `PhaseItem`s has been moved to `SortedPhaseItem`. `BinnedPhaseItem` has the new logic. Frame time results (in ms/frame) are as follows: | Benchmark | `binning` | `main` | Speedup | | ------------------------ | --------- | ------- | ------- | | `many_cubes -nfc -vpi` | 232.179 | 312.123 | 34.43% | | `many_cubes -nfc` | 25.874 | 30.117 | 16.40% | | `many_foxes` | 3.276 | 3.515 | 7.30% | (`-nfc` is short for `--no-frustum-culling`; `-vpi` is short for `--vary-per-instance`.) --- ## Changelog ### Changed * Render phases have been split into binned and sorted phases. Binned phases, such as the common opaque phase, achieve improved CPU performance by avoiding the sorting step. ## Migration Guide - `PhaseItem` has been split into `BinnedPhaseItem` and `SortedPhaseItem`. If your code has custom `PhaseItem`s, you will need to migrate them to one of these two types. `SortedPhaseItem` requires the fewest code changes, but you may want to pick `BinnedPhaseItem` if your phase doesn't require sorting, as that enables higher performance. ## Tracy graphs `many-cubes --no-frustum-culling`, `main` branch: <img width="1064" alt="Screenshot 2024-03-12 180037" src="https://github.com/bevyengine/bevy/assets/157897/e1180ce8-8e89-46d2-85e3-f59f72109a55"> `many-cubes --no-frustum-culling`, this branch: <img width="1064" alt="Screenshot 2024-03-12 180011" src="https://github.com/bevyengine/bevy/assets/157897/0899f036-6075-44c5-a972-44d95895f46c"> You can see that `batch_and_prepare_binned_render_phase` is a much smaller fraction of the time. Zooming in on that function, with yellow being this branch and red being `main`, we see: <img width="1064" alt="Screenshot 2024-03-12 175832" src="https://github.com/bevyengine/bevy/assets/157897/0dfc8d3f-49f4-496e-8825-a66e64d356d0"> The binning happens in `queue_material_meshes`. Again with yellow being this branch and red being `main`: <img width="1064" alt="Screenshot 2024-03-12 175755" src="https://github.com/bevyengine/bevy/assets/157897/b9b20dc1-11c8-400c-a6cc-1c2e09c1bb96"> We can see that there is a small regression in `queue_material_meshes` performance, but it's not nearly enough to outweigh the large gains in `batch_and_prepare_binned_render_phase`. --------- Co-authored-by: James Liu <contact@jamessliu.com> |
||
Vitaliy Sapronenko
|
67cc605e9f
|
Removed Into<AssedId<T>> for Handle<T> as mentioned in #12600 (#12655)
Fixes #12600 ## Solution Removed Into<AssetId<T>> for Handle<T> as proposed in Issue conversation, fixed dependent code ## Migration guide If you use passing Handle by value as AssetId, you should pass reference or call .id() method on it Before (0.13): `assets.insert(handle, value);` After (0.14): `assets.insert(&handle, value);` or `assets.insert(handle.id(), value);` |
||
Patrick Walton
|
f9cc91d5a1
|
Intern mesh vertex buffer layouts so that we don't have to compare them over and over. (#12216)
Although we cached hashes of `MeshVertexBufferLayout`, we were paying the cost of `PartialEq` on `InnerMeshVertexBufferLayout` for every entity, every frame. This patch changes that logic to place `MeshVertexBufferLayout`s in `Arc`s so that they can be compared and hashed by pointer. This results in a 28% speedup in the `queue_material_meshes` phase of `many_cubes`, with frustum culling disabled. Additionally, this patch contains two minor changes: 1. This commit flattens the specialized mesh pipeline cache to one level of hash tables instead of two. This saves a hash lookup. 2. The example `many_cubes` has been given a `--no-frustum-culling` flag, to aid in benchmarking. See the Tracy profile: <img width="1064" alt="Screenshot 2024-02-29 144406" src="https://github.com/bevyengine/bevy/assets/157897/18632f1d-1fdd-4ac7-90ed-2d10306b2a1e"> ## Migration guide * Duplicate `MeshVertexBufferLayout`s are now combined into a single object, `MeshVertexBufferLayoutRef`, which contains an atomically-reference-counted pointer to the layout. Code that was using `MeshVertexBufferLayout` may need to be updated to use `MeshVertexBufferLayoutRef` instead. |
||
Alice Cecile
|
599e5e4e76
|
Migrate from LegacyColor to bevy_color::Color (#12163)
# Objective - As part of the migration process we need to a) see the end effect of the migration on user ergonomics b) check for serious perf regressions c) actually migrate the code - To accomplish this, I'm going to attempt to migrate all of the remaining user-facing usages of `LegacyColor` in one PR, being careful to keep a clean commit history. - Fixes #12056. ## Solution I've chosen to use the polymorphic `Color` type as our standard user-facing API. - [x] Migrate `bevy_gizmos`. - [x] Take `impl Into<Color>` in all `bevy_gizmos` APIs - [x] Migrate sprites - [x] Migrate UI - [x] Migrate `ColorMaterial` - [x] Migrate `MaterialMesh2D` - [x] Migrate fog - [x] Migrate lights - [x] Migrate StandardMaterial - [x] Migrate wireframes - [x] Migrate clear color - [x] Migrate text - [x] Migrate gltf loader - [x] Register color types for reflection - [x] Remove `LegacyColor` - [x] Make sure CI passes Incidental improvements to ease migration: - added `Color::srgba_u8`, `Color::srgba_from_array` and friends - added `set_alpha`, `is_fully_transparent` and `is_fully_opaque` to the `Alpha` trait - add and immediately deprecate (lol) `Color::rgb` and friends in favor of more explicit and consistent `Color::srgb` - standardized on white and black for most example text colors - added vector field traits to `LinearRgba`: ~~`Add`, `Sub`, `AddAssign`, `SubAssign`,~~ `Mul<f32>` and `Div<f32>`. Multiplications and divisions do not scale alpha. `Add` and `Sub` have been cut from this PR. - added `LinearRgba` and `Srgba` `RED/GREEN/BLUE` - added `LinearRgba_to_f32_array` and `LinearRgba::to_u32` ## Migration Guide Bevy's color types have changed! Wherever you used a `bevy::render::Color`, a `bevy::color::Color` is used instead. These are quite similar! Both are enums storing a color in a specific color space (or to be more precise, using a specific color model). However, each of the different color models now has its own type. TODO... - `Color::rgba`, `Color::rgb`, `Color::rbga_u8`, `Color::rgb_u8`, `Color::rgb_from_array` are now `Color::srgba`, `Color::srgb`, `Color::srgba_u8`, `Color::srgb_u8` and `Color::srgb_from_array`. - `Color::set_a` and `Color::a` is now `Color::set_alpha` and `Color::alpha`. These are part of the `Alpha` trait in `bevy_color`. - `Color::is_fully_transparent` is now part of the `Alpha` trait in `bevy_color` - `Color::r`, `Color::set_r`, `Color::with_r` and the equivalents for `g`, `b` `h`, `s` and `l` have been removed due to causing silent relatively expensive conversions. Convert your `Color` into the desired color space, perform your operations there, and then convert it back into a polymorphic `Color` enum. - `Color::hex` is now `Srgba::hex`. Call `.into` or construct a `Color::Srgba` variant manually to convert it. - `WireframeMaterial`, `ExtractedUiNode`, `ExtractedDirectionalLight`, `ExtractedPointLight`, `ExtractedSpotLight` and `ExtractedSprite` now store a `LinearRgba`, rather than a polymorphic `Color` - `Color::rgb_linear` and `Color::rgba_linear` are now `Color::linear_rgb` and `Color::linear_rgba` - The various CSS color constants are no longer stored directly on `Color`. Instead, they're defined in the `Srgba` color space, and accessed via `bevy::color::palettes::css`. Call `.into()` on them to convert them into a `Color` for quick debugging use, and consider using the much prettier `tailwind` palette for prototyping. - The `LIME_GREEN` color has been renamed to `LIMEGREEN` to comply with the standard naming. - Vector field arithmetic operations on `Color` (add, subtract, multiply and divide by a f32) have been removed. Instead, convert your colors into `LinearRgba` space, and perform your operations explicitly there. This is particularly relevant when working with emissive or HDR colors, whose color channel values are routinely outside of the ordinary 0 to 1 range. - `Color::as_linear_rgba_f32` has been removed. Call `LinearRgba::to_f32_array` instead, converting if needed. - `Color::as_linear_rgba_u32` has been removed. Call `LinearRgba::to_u32` instead, converting if needed. - Several other color conversion methods to transform LCH or HSL colors into float arrays or `Vec` types have been removed. Please reimplement these externally or open a PR to re-add them if you found them particularly useful. - Various methods on `Color` such as `rgb` or `hsl` to convert the color into a specific color space have been removed. Convert into `LinearRgba`, then to the color space of your choice. - Various implicitly-converting color value methods on `Color` such as `r`, `g`, `b` or `h` have been removed. Please convert it into the color space of your choice, then check these properties. - `Color` no longer implements `AsBindGroup`. Store a `LinearRgba` internally instead to avoid conversion costs. --------- Co-authored-by: Alice Cecile <alice.i.cecil@gmail.com> Co-authored-by: Afonso Lage <lage.afonso@gmail.com> Co-authored-by: Rob Parrett <robparrett@gmail.com> Co-authored-by: Zachary Harrold <zac@harrold.com.au> |
||
François
|
d261a86b9f
|
compute shader game of life example: use R32Float instead of Rgba8Unorm (#12155)
# Objective - Fixes #9670 - Avoid a crash in CI due to ``` thread 'Compute Task Pool (0)' panicked at /Users/runner/.cargo/registry/src/index.crates.io-6f17d22bba15001f/wgpu-0.19.1/src/backend/wgpu_core.rs:3009:5: wgpu error: Validation Error Caused by: In Device::create_bind_group The adapter does not support read access for storages texture of format Rgba8Unorm ``` ## Solution - Use an `R32Float` texture instead of an `Rgba8Unorm` as it's a tier 1 texture format https://github.com/gpuweb/gpuweb/issues/3838 and is more supported - This should also improve support for webgpu in the next wgpu version |
||
Nicola Papale
|
f7f7e326e5
|
Add methods to directly load assets from World (#12023)
# Objective `FromWorld` is often used to group loading and creation of assets for resources. With this setup, users often end up repetitively calling `.resource::<AssetServer>` and `.resource_mut::<Assets<T>>`, and may have difficulties handling lifetimes of the returned references. ## Solution Add extension methods to `World` to add and load assets, through a new extension trait defined in `bevy_asset`. ### Other considerations * This might be a bit too "magic", as it makes implicit the resource access. * We could also implement `DirectAssetAccessExt` on `App`, but it didn't feel necessary, as `FromWorld` is the principal use-case here. --- ## Changelog * Add the `DirectAssetAccessExt` trait, which adds the `add_asset`, `load_asset` and `load_asset_with_settings` method to the `World` type. |
||
Alice Cecile
|
de004da8d5
|
Rename bevy_render::Color to LegacyColor (#12069)
# Objective The migration process for `bevy_color` (#12013) will be fairly involved: there will be hundreds of affected files, and a large number of APIs. ## Solution To allow us to proceed granularly, we're going to keep both `bevy_color::Color` (new) and `bevy_render::Color` (old) around until the migration is complete. However, simply doing this directly is confusing! They're both called `Color`, making it very hard to tell when a portion of the code has been ported. As discussed in #12056, by renaming the old `Color` type, we can make it easier to gradually migrate over, one API at a time. ## Migration Guide THIS MIGRATION GUIDE INTENTIONALLY LEFT BLANK. This change should not be shipped to end users: delete this section in the final migration guide! --------- Co-authored-by: Alice Cecile <alice.i.cecil@gmail.com> |
||
Carter Anderson
|
f83de49b7a
|
Rename Core Render Graph Labels (#11882)
# Objective #10644 introduced nice "statically typed" labels that replace the old strings. I would like to propose some changes to the names introduced: * `SubGraph2d` -> `Core2d` and `SubGraph3d` -> `Core3d`. The names of these graphs have been / should continue to be the "core 2d" graph not the "sub graph 2d" graph. The crate is called `bevy_core_pipeline`, the modules are still `core_2d` and `core_3d`, etc. * `Labels2d` and `Labels3d`, at the very least, should not be plural to follow naming conventions. A Label enum is not a "collection of labels", it is a _specific_ Label. However I think `Label2d` and `Label3d` is significantly less clear than `Node2d` and `Node3d`, so I propose those changes here. I've done the same for `LabelsPbr` -> `NodePbr` and `LabelsUi` -> `NodeUi` Additionally, #10644 accidentally made one of the Camera2dBundle constructors use the 3D graph instead of the 2D graph. I've fixed that here. --- ## Changelog * Renamed `SubGraph2d` -> `Core2d`, `SubGraph3d` -> `Core3d`, `Labels2d` -> `Node2d`, `Labels3d` -> `Node3d`, `LabelsUi` -> `NodeUi`, `LabelsPbr` -> `NodePbr` |
||
Carter Anderson
|
dd619a1087
|
New Exposure and Lighting Defaults (and calibrate examples) (#11868)
# Objective After adding configurable exposure, we set the default ev100 value to `7` (indoor). This brought us out of sync with Blender's configuration and defaults. This PR changes the default to `9.7` (bright indoor or very overcast outdoors), as I calibrated in #11577. This feels like a very reasonable default. The other changes generally center around tweaking Bevy's lighting defaults and examples to play nicely with this number, alongside a few other tweaks and improvements. Note that for artistic reasons I have reverted some examples, which changed to directional lights in #11581, back to point lights. Fixes #11577 --- ## Changelog - Changed `Exposure::ev100` from `7` to `9.7` to better match Blender - Renamed `ExposureSettings` to `Exposure` - `Camera3dBundle` now includes `Exposure` for discoverability - Bumped `FULL_DAYLIGHT ` and `DIRECT_SUNLIGHT` to represent the middle-to-top of those ranges instead of near the bottom - Added new `AMBIENT_DAYLIGHT` constant and set that as the new `DirectionalLight` default illuminance. - `PointLight` and `SpotLight` now have a default `intensity` of 1,000,000 lumens. This makes them actually useful in the context of the new "semi-outdoor" exposure and puts them in the "cinema lighting" category instead of the "common household light" category. They are also reasonably close to the Blender default. - `AmbientLight` default has been bumped from `20` to `80`. ## Migration Guide - The increased `Exposure::ev100` means that all existing 3D lighting will need to be adjusted to match (DirectionalLights, PointLights, SpotLights, EnvironmentMapLights, etc). Or alternatively, you can adjust the `Exposure::ev100` on your cameras to work nicely with your current lighting values. If you are currently relying on default intensity values, you might need to change the intensity to achieve the same effect. Note that in Bevy 0.12, point/spot lights had a different hard coded ev100 value than directional lights. In Bevy 0.13, they use the same ev100, so if you have both in your scene, the _scale_ between these light types has changed and you will likely need to adjust one or both of them. |
||
Doonv
|
dc9b486650
|
Change light defaults & fix light examples (#11581)
# Objective Fix https://github.com/bevyengine/bevy/issues/11577. ## Solution Fix the examples, add a few constants to make setting light values easier, and change the default lighting settings to be more realistic. (Now designed for an overcast day instead of an indoor environment) --- I did not include any example-related changes in here. ## Changelogs (not including breaking changes) ### bevy_pbr - Added `light_consts` module (included in prelude), which contains common lux and lumen values for lights. - Added `AmbientLight::NONE` constant, which is an ambient light with a brightness of 0. - Added non-EV100 variants for `ExposureSettings`'s EV100 constants, which allow easier construction of an `ExposureSettings` from a EV100 constant. ## Breaking changes ### bevy_pbr The several default lighting values were changed: - `PointLight`'s default `intensity` is now `2000.0` - `SpotLight`'s default `intensity` is now `2000.0` - `DirectionalLight`'s default `illuminance` is now `light_consts::lux::OVERCAST_DAY` (`1000.`) - `AmbientLight`'s default `brightness` is now `20.0` |
||
Patrick Walton
|
3af8526786
|
Stop extracting mesh entities to the render world. (#11803)
This fixes a `FIXME` in `extract_meshes` and results in a performance improvement. As a result of this change, meshes in the render world might not be attached to entities anymore. Therefore, the `entity` parameter to `RenderCommand::render()` is now wrapped in an `Option`. Most applications that use the render app's ECS can simply unwrap the `Option`. Note that for now sprites, gizmos, and UI elements still use the render world as usual. ## Migration guide * For efficiency reasons, some meshes in the render world may not have corresponding `Entity` IDs anymore. As a result, the `entity` parameter to `RenderCommand::render()` is now wrapped in an `Option`. Custom rendering code may need to be updated to handle the case in which no `Entity` exists for an object that is to be rendered. |
||
Joona Aalto
|
0166db33f7
|
Deprecate shapes in bevy_render::mesh::shape (#11773)
# Objective #11431 and #11688 implemented meshing support for Bevy's new geometric primitives. The next step is to deprecate the shapes in `bevy_render::mesh::shape` and to later remove them completely for 0.14. ## Solution Deprecate the shapes and reduce code duplication by utilizing the primitive meshing API for the old shapes where possible. Note that some shapes have behavior that can't be exactly reproduced with the new primitives yet: - `Box` is more of an AABB with min/max extents - `Plane` supports a subdivision count - `Quad` has a `flipped` property These types have not been changed to utilize the new primitives yet. --- ## Changelog - Deprecated all shapes in `bevy_render::mesh::shape` - Changed all examples to use new primitives for meshing ## Migration Guide Bevy has previously used rendering-specific types like `UVSphere` and `Quad` for primitive mesh shapes. These have now been deprecated to use the geometric primitives newly introduced in version 0.13. Some examples: ```rust let before = meshes.add(shape::Box::new(5.0, 0.15, 5.0)); let after = meshes.add(Cuboid::new(5.0, 0.15, 5.0)); let before = meshes.add(shape::Quad::default()); let after = meshes.add(Rectangle::default()); let before = meshes.add(shape::Plane::from_size(5.0)); // The surface normal can now also be specified when using `new` let after = meshes.add(Plane3d::default().mesh().size(5.0, 5.0)); let before = meshes.add( Mesh::try_from(shape::Icosphere { radius: 0.5, subdivisions: 5, }) .unwrap(), ); let after = meshes.add(Sphere::new(0.5).mesh().ico(5).unwrap()); ``` |
||
Tristan Guichaoua
|
694c06f3d0
|
Inverse missing_docs logic (#11676)
# Objective Currently the `missing_docs` lint is allowed-by-default and enabled at crate level when their documentations is complete (see #3492). This PR proposes to inverse this logic by making `missing_docs` warn-by-default and mark crates with imcomplete docs allowed. ## Solution Makes `missing_docs` warn at workspace level and allowed at crate level when the docs is imcomplete. |
||
Lixou
|
16d28ccb91
|
RenderGraph Labelization (#10644)
# Objective The whole `Cow<'static, str>` naming for nodes and subgraphs in `RenderGraph` is a mess. ## Solution Replaces hardcoded and potentially overlapping strings for nodes and subgraphs inside `RenderGraph` with bevy's labelsystem. --- ## Changelog * Two new labels: `RenderLabel` and `RenderSubGraph`. * Replaced all uses for hardcoded strings with those labels * Moved `Taa` label from its own mod to all the other `Labels3d` * `add_render_graph_edges` now needs a tuple of labels * Moved `ScreenSpaceAmbientOcclusion` label from its own mod with the `ShadowPass` label to `LabelsPbr` * Removed `NodeId` * Renamed `Edges.id()` to `Edges.label()` * Removed `NodeLabel` * Changed examples according to the new label system * Introduced new `RenderLabel`s: `Labels2d`, `Labels3d`, `LabelsPbr`, `LabelsUi` * Introduced new `RenderSubGraph`s: `SubGraph2d`, `SubGraph3d`, `SubGraphUi` * Removed `Reflect` and `Default` derive from `CameraRenderGraph` component struct * Improved some error messages ## Migration Guide For Nodes and SubGraphs, instead of using hardcoded strings, you now pass labels, which can be derived with structs and enums. ```rs // old #[derive(Default)] struct MyRenderNode; impl MyRenderNode { pub const NAME: &'static str = "my_render_node" } render_app .add_render_graph_node::<ViewNodeRunner<MyRenderNode>>( core_3d::graph::NAME, MyRenderNode::NAME, ) .add_render_graph_edges( core_3d::graph::NAME, &[ core_3d::graph::node::TONEMAPPING, MyRenderNode::NAME, core_3d::graph::node::END_MAIN_PASS_POST_PROCESSING, ], ); // new use bevy::core_pipeline::core_3d::graph::{Labels3d, SubGraph3d}; #[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)] pub struct MyRenderLabel; #[derive(Default)] struct MyRenderNode; render_app .add_render_graph_node::<ViewNodeRunner<MyRenderNode>>( SubGraph3d, MyRenderLabel, ) .add_render_graph_edges( SubGraph3d, ( Labels3d::Tonemapping, MyRenderLabel, Labels3d::EndMainPassPostProcessing, ), ); ``` ### SubGraphs #### in `bevy_core_pipeline::core_2d::graph` | old string-based path | new label | |-----------------------|-----------| | `NAME` | `SubGraph2d` | #### in `bevy_core_pipeline::core_3d::graph` | old string-based path | new label | |-----------------------|-----------| | `NAME` | `SubGraph3d` | #### in `bevy_ui::render` | old string-based path | new label | |-----------------------|-----------| | `draw_ui_graph::NAME` | `graph::SubGraphUi` | ### Nodes #### in `bevy_core_pipeline::core_2d::graph` | old string-based path | new label | |-----------------------|-----------| | `node::MSAA_WRITEBACK` | `Labels2d::MsaaWriteback` | | `node::MAIN_PASS` | `Labels2d::MainPass` | | `node::BLOOM` | `Labels2d::Bloom` | | `node::TONEMAPPING` | `Labels2d::Tonemapping` | | `node::FXAA` | `Labels2d::Fxaa` | | `node::UPSCALING` | `Labels2d::Upscaling` | | `node::CONTRAST_ADAPTIVE_SHARPENING` | `Labels2d::ConstrastAdaptiveSharpening` | | `node::END_MAIN_PASS_POST_PROCESSING` | `Labels2d::EndMainPassPostProcessing` | #### in `bevy_core_pipeline::core_3d::graph` | old string-based path | new label | |-----------------------|-----------| | `node::MSAA_WRITEBACK` | `Labels3d::MsaaWriteback` | | `node::PREPASS` | `Labels3d::Prepass` | | `node::DEFERRED_PREPASS` | `Labels3d::DeferredPrepass` | | `node::COPY_DEFERRED_LIGHTING_ID` | `Labels3d::CopyDeferredLightingId` | | `node::END_PREPASSES` | `Labels3d::EndPrepasses` | | `node::START_MAIN_PASS` | `Labels3d::StartMainPass` | | `node::MAIN_OPAQUE_PASS` | `Labels3d::MainOpaquePass` | | `node::MAIN_TRANSMISSIVE_PASS` | `Labels3d::MainTransmissivePass` | | `node::MAIN_TRANSPARENT_PASS` | `Labels3d::MainTransparentPass` | | `node::END_MAIN_PASS` | `Labels3d::EndMainPass` | | `node::BLOOM` | `Labels3d::Bloom` | | `node::TONEMAPPING` | `Labels3d::Tonemapping` | | `node::FXAA` | `Labels3d::Fxaa` | | `node::UPSCALING` | `Labels3d::Upscaling` | | `node::CONTRAST_ADAPTIVE_SHARPENING` | `Labels3d::ContrastAdaptiveSharpening` | | `node::END_MAIN_PASS_POST_PROCESSING` | `Labels3d::EndMainPassPostProcessing` | #### in `bevy_core_pipeline` | old string-based path | new label | |-----------------------|-----------| | `taa::draw_3d_graph::node::TAA` | `Labels3d::Taa` | #### in `bevy_pbr` | old string-based path | new label | |-----------------------|-----------| | `draw_3d_graph::node::SHADOW_PASS` | `LabelsPbr::ShadowPass` | | `ssao::draw_3d_graph::node::SCREEN_SPACE_AMBIENT_OCCLUSION` | `LabelsPbr::ScreenSpaceAmbientOcclusion` | | `deferred::DEFFERED_LIGHTING_PASS` | `LabelsPbr::DeferredLightingPass` | #### in `bevy_render` | old string-based path | new label | |-----------------------|-----------| | `main_graph::node::CAMERA_DRIVER` | `graph::CameraDriverLabel` | #### in `bevy_ui::render` | old string-based path | new label | |-----------------------|-----------| | `draw_ui_graph::node::UI_PASS` | `graph::LabelsUi::UiPass` | --- ## Future work * Make `NodeSlot`s also use types. Ideally, we have an enum with unit variants where every variant resembles one slot. Then to make sure you are using the right slot enum and make rust-analyzer play nicely with it, we should make an associated type in the `Node` trait. With today's system, we can introduce 3rd party slots to a node, and i wasnt sure if this was used, so I didn't do this in this PR. ## Unresolved Questions When looking at the `post_processing` example, we have a struct for the label and a struct for the node, this seems like boilerplate and on discord, @IceSentry (sowy for the ping) [asked](https://discord.com/channels/691052431525675048/743663924229963868/1175197016947699742) if a node could automatically introduce a label (or i completely misunderstood that). The problem with that is, that nodes like `EmptyNode` exist multiple times *inside the same* (sub)graph, so there we need extern labels to distinguish between those. Hopefully we can find a way to reduce boilerplate and still have everything unique. For EmptyNode, we could maybe make a macro which implements an "empty node" for a type, but for nodes which contain code and need to be present multiple times, this could get nasty... |
||
Brian Reavis
|
6b40b6749e
|
RenderAssetPersistencePolicy → RenderAssetUsages (#11399)
# Objective Right now, all assets in the main world get extracted and prepared in the render world (if the asset's using the RenderAssetPlugin). This is unfortunate for two cases: 1. **TextureAtlas** / **FontAtlas**: This one's huge. The individual `Image` assets that make up the atlas are cloned and prepared individually when there's no reason for them to be. The atlas textures are built on the CPU in the main world. *There can be hundreds of images that get prepared for rendering only not to be used.* 2. If one loads an Image and needs to transform it in a system before rendering it, kind of like the [decompression example](https://github.com/bevyengine/bevy/blob/main/examples/asset/asset_decompression.rs#L120), there's a price paid for extracting & preparing the asset that's not intended to be rendered yet. ------ * References #10520 * References #1782 ## Solution This changes the `RenderAssetPersistencePolicy` enum to bitflags. I felt that the objective with the parameter is so similar in nature to wgpu's [`TextureUsages`](https://docs.rs/wgpu/latest/wgpu/struct.TextureUsages.html) and [`BufferUsages`](https://docs.rs/wgpu/latest/wgpu/struct.BufferUsages.html), that it may as well be just like that. ```rust // This asset only needs to be in the main world. Don't extract and prepare it. RenderAssetUsages::MAIN_WORLD // Keep this asset in the main world and RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD // This asset is only needed in the render world. Remove it from the asset server once extracted. RenderAssetUsages::RENDER_WORLD ``` ### Alternate Solution I considered introducing a third field to `RenderAssetPersistencePolicy` enum: ```rust enum RenderAssetPersistencePolicy { /// Keep the asset in the main world after extracting to the render world. Keep, /// Remove the asset from the main world after extracting to the render world. Unload, /// This doesn't need to be in the render world at all. NoExtract, // <----- } ``` Functional, but this seemed like shoehorning. Another option is renaming the enum to something like: ```rust enum RenderAssetExtractionPolicy { /// Extract the asset and keep it in the main world. Extract, /// Remove the asset from the main world after extracting to the render world. ExtractAndUnload, /// This doesn't need to be in the render world at all. NoExtract, } ``` I think this last one could be a good option if the bitflags are too clunky. ## Migration Guide * `RenderAssetPersistencePolicy::Keep` → `RenderAssetUsage::MAIN_WORLD | RenderAssetUsage::RENDER_WORLD` (or `RenderAssetUsage::default()`) * `RenderAssetPersistencePolicy::Unload` → `RenderAssetUsage::RENDER_WORLD` * For types implementing the `RenderAsset` trait, change `fn persistence_policy(&self) -> RenderAssetPersistencePolicy` to `fn asset_usage(&self) -> RenderAssetUsages`. * Change any references to `cpu_persistent_access` (`RenderAssetPersistencePolicy`) to `asset_usage` (`RenderAssetUsage`). This applies to `Image`, `Mesh`, and a few other types. |
||
Alice Cecile
|
eb07d16871
|
Revert rendering-related associated type name changes (#11027)
# Objective > Can anyone explain to me the reasoning of renaming all the types named Query to Data. I'm talking about this PR https://github.com/bevyengine/bevy/pull/10779 It doesn't make sense to me that a bunch of types that are used to run queries aren't named Query anymore. Like ViewQuery on the ViewNode is the type of the Query. I don't really understand the point of the rename, it just seems like it hides the fact that a query will run based on those types. [@IceSentry](https://discord.com/channels/691052431525675048/692572690833473578/1184946251431694387) ## Solution Revert several renames in #10779. ## Changelog - `ViewNode::ViewData` is now `ViewNode::ViewQuery` again. ## Migration Guide - This PR amends the migration guide in https://github.com/bevyengine/bevy/pull/10779 --------- Co-authored-by: atlas dostal <rodol@rivalrebels.com> |
||
HugoPeters1024
|
8afb3ceb89
|
add storage_texture option to as_bind_group macro (#9943)
# Objective - Add the ability to describe storage texture bindings when deriving `AsBindGroup`. - This is especially valuable for the compute story of bevy which deserves some extra love imo. ## Solution - This add the ability to annotate struct fields with a `#[storage_texture(0)]` annotation. - Instead of adding specific option parsing for all the image formats and access modes, I simply accept a token stream and defer checking to see if the option is valid to the compiler. This still results in useful and friendly errors and is free to maintain and always compatible with wgpu changes. --- ## Changelog - The `#[storage_texture(..)]` annotation is now accepted for fields of `Handle<Image>` in structs that derive `AsBindGroup`. - The game_of_life compute shader example has been updated to use `AsBindGroup` together with `[storage_texture(..)]` to obtain the `BindGroupLayout`. ## Migration Guide |
||
JMS55
|
fcd7c0fc3d
|
Exposure settings (adopted) (#11347)
Rebased and finished version of https://github.com/bevyengine/bevy/pull/8407. Huge thanks to @GitGhillie for adjusting all the examples, and the many other people who helped write this PR (@superdump , @coreh , among others) :) Fixes https://github.com/bevyengine/bevy/issues/8369 --- ## Changelog - Added a `brightness` control to `Skybox`. - Added an `intensity` control to `EnvironmentMapLight`. - Added `ExposureSettings` and `PhysicalCameraParameters` for controlling exposure of 3D cameras. - Removed the baked-in `DirectionalLight` exposure Bevy previously hardcoded internally. ## Migration Guide - If using a `Skybox` or `EnvironmentMapLight`, use the new `brightness` and `intensity` controls to adjust their strength. - All 3D scene will now have different apparent brightnesses due to Bevy implementing proper exposure controls. You will have to adjust the intensity of your lights and/or your camera exposure via the new `ExposureSettings` component to compensate. --------- Co-authored-by: Robert Swain <robert.swain@gmail.com> Co-authored-by: GitGhillie <jillisnoordhoek@gmail.com> Co-authored-by: Marco Buono <thecoreh@gmail.com> Co-authored-by: vero <email@atlasdostal.com> Co-authored-by: atlas dostal <rodol@rivalrebels.com> |
||
Joona Aalto
|
a795de30b4
|
Use impl Into<A> for Assets::add (#10878)
# Motivation When spawning entities into a scene, it is very common to create assets like meshes and materials and to add them via asset handles. A common setup might look like this: ```rust fn setup( mut commands: Commands, mut meshes: ResMut<Assets<Mesh>>, mut materials: ResMut<Assets<StandardMaterial>>, ) { commands.spawn(PbrBundle { mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), material: materials.add(StandardMaterial::from(Color::RED)), ..default() }); } ``` Let's take a closer look at the part that adds the assets using `add`. ```rust mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), material: materials.add(StandardMaterial::from(Color::RED)), ``` Here, "mesh" and "material" are both repeated three times. It's very explicit, but I find it to be a bit verbose. In addition to being more code to read and write, the extra characters can sometimes also lead to the code being formatted to span multiple lines even though the core task, adding e.g. a primitive mesh, is extremely simple. A way to address this is by using `.into()`: ```rust mesh: meshes.add(shape::Cube { size: 1.0 }.into()), material: materials.add(Color::RED.into()), ``` This is fine, but from the names and the type of `meshes`, we already know what the type should be. It's very clear that `Cube` should be turned into a `Mesh` because of the context it's used in. `.into()` is just seven characters, but it's so common that it quickly adds up and gets annoying. It would be nice if you could skip all of the conversion and let Bevy handle it for you: ```rust mesh: meshes.add(shape::Cube { size: 1.0 }), material: materials.add(Color::RED), ``` # Objective Make adding assets more ergonomic by making `Assets::add` take an `impl Into<A>` instead of `A`. ## Solution `Assets::add` now takes an `impl Into<A>` instead of `A`, so e.g. this works: ```rust commands.spawn(PbrBundle { mesh: meshes.add(shape::Cube { size: 1.0 }), material: materials.add(Color::RED), ..default() }); ``` I also changed all examples to use this API, which increases consistency as well because `Mesh::from` and `into` were being used arbitrarily even in the same file. This also gets rid of some lines of code because formatting is nicer. --- ## Changelog - `Assets::add` now takes an `impl Into<A>` instead of `A` - Examples don't use `T::from(K)` or `K.into()` when adding assets ## Migration Guide Some `into` calls that worked previously might now be broken because of the new trait bounds. You need to either remove `into` or perform the conversion explicitly with `from`: ```rust // Doesn't compile let mesh_handle = meshes.add(shape::Cube { size: 1.0 }.into()), // These compile let mesh_handle = meshes.add(shape::Cube { size: 1.0 }), let mesh_handle = meshes.add(Mesh::from(shape::Cube { size: 1.0 })), ``` ## Concerns I believe the primary concerns might be: 1. Is this too implicit? 2. Does this increase codegen bloat? Previously, the two APIs were using `into` or `from`, and now it's "nothing" or `from`. You could argue that `into` is slightly more explicit than "nothing" in cases like the earlier examples where a `Color` gets converted to e.g. a `StandardMaterial`, but I personally don't think `into` adds much value even in this case, and you could still see the actual type from the asset type. As for codegen bloat, I doubt it adds that much, but I'm not very familiar with the details of codegen. I personally value the user-facing code reduction and ergonomics improvements that these changes would provide, but it might be worth checking the other effects in more detail. Another slight concern is migration pain; apps might have a ton of `into` calls that would need to be removed, and it did take me a while to do so for Bevy itself (maybe around 20-40 minutes). However, I think the fact that there *are* so many `into` calls just highlights that the API could be made nicer, and I'd gladly migrate my own projects for it. |
||
JMS55
|
44424391fe
|
Unload render assets from RAM (#10520)
# Objective - No point in keeping Meshes/Images in RAM once they're going to be sent to the GPU, and kept in VRAM. This saves a _significant_ amount of memory (several GBs) on scenes like bistro. - References - https://github.com/bevyengine/bevy/pull/1782 - https://github.com/bevyengine/bevy/pull/8624 ## Solution - Augment RenderAsset with the capability to unload the underlying asset after extracting to the render world. - Mesh/Image now have a cpu_persistent_access field. If this field is RenderAssetPersistencePolicy::Unload, the asset will be unloaded from Assets<T>. - A new AssetEvent is sent upon dropping the last strong handle for the asset, which signals to the RenderAsset to remove the GPU version of the asset. --- ## Changelog - Added `AssetEvent::NoLongerUsed` and `AssetEvent::is_no_longer_used()`. This event is sent when the last strong handle of an asset is dropped. - Rewrote the API for `RenderAsset` to allow for unloading the asset data from the CPU. - Added `RenderAssetPersistencePolicy`. - Added `Mesh::cpu_persistent_access` for memory savings when the asset is not needed except for on the GPU. - Added `Image::cpu_persistent_access` for memory savings when the asset is not needed except for on the GPU. - Added `ImageLoaderSettings::cpu_persistent_access`. - Added `ExrTextureLoaderSettings`. - Added `HdrTextureLoaderSettings`. ## Migration Guide - Asset loaders (GLTF, etc) now load meshes and textures without `cpu_persistent_access`. These assets will be removed from `Assets<Mesh>` and `Assets<Image>` once `RenderAssets<Mesh>` and `RenderAssets<Image>` contain the GPU versions of these assets, in order to reduce memory usage. If you require access to the asset data from the CPU in future frames after the GLTF asset has been loaded, modify all dependent `Mesh` and `Image` assets and set `cpu_persistent_access` to `RenderAssetPersistencePolicy::Keep`. - `Mesh` now requires a new `cpu_persistent_access` field. Set it to `RenderAssetPersistencePolicy::Keep` to mimic the previous behavior. - `Image` now requires a new `cpu_persistent_access` field. Set it to `RenderAssetPersistencePolicy::Keep` to mimic the previous behavior. - `MorphTargetImage::new()` now requires a new `cpu_persistent_access` parameter. Set it to `RenderAssetPersistencePolicy::Keep` to mimic the previous behavior. - `DynamicTextureAtlasBuilder::add_texture()` now requires that the `TextureAtlas` you pass has an `Image` with `cpu_persistent_access: RenderAssetPersistencePolicy::Keep`. Ensure you construct the image properly for the texture atlas. - The `RenderAsset` trait has significantly changed, and requires adapting your existing implementations. - The trait now requires `Clone`. - The `ExtractedAsset` associated type has been removed (the type itself is now extracted). - The signature of `prepare_asset()` is slightly different - A new `persistence_policy()` method is now required (return RenderAssetPersistencePolicy::Unload to match the previous behavior). - Match on the new `NoLongerUsed` variant for exhaustive matches of `AssetEvent`. |
||
JMS55
|
70b0eacc3b
|
Keep track of when a texture is first cleared (#10325)
# Objective - Custom render passes, or future passes in the engine (such as https://github.com/bevyengine/bevy/pull/10164) need a better way to know and indicate to the core passes whether the view color/depth/prepass attachments have been cleared or not yet this frame, to know if they should clear it themselves or load it. ## Solution - For all render targets (depth textures, shadow textures, prepass textures, main textures) use an atomic bool to track whether or not each texture has been cleared this frame. Abstracted away in the new ColorAttachment and DepthAttachment wrappers. --- ## Changelog - Changed `ViewTarget::get_color_attachment()`, removed arguments. - Changed `ViewTarget::get_unsampled_color_attachment()`, removed arguments. - Removed `Camera3d::clear_color`. - Removed `Camera2d::clear_color`. - Added `Camera::clear_color`. - Added `ExtractedCamera::clear_color`. - Added `ColorAttachment` and `DepthAttachment` wrappers. - Moved `ClearColor` and `ClearColorConfig` from `bevy::core_pipeline::clear_color` to `bevy::render::camera`. - Core render passes now track when a texture is first bound as an attachment in order to decide whether to clear or load it. ## Migration Guide - Remove arguments to `ViewTarget::get_color_attachment()` and `ViewTarget::get_unsampled_color_attachment()`. - Configure clear color on `Camera` instead of on `Camera3d` and `Camera2d`. - Moved `ClearColor` and `ClearColorConfig` from `bevy::core_pipeline::clear_color` to `bevy::render::camera`. - `ViewDepthTexture` must now be created via the `new()` method --------- Co-authored-by: vero <email@atlasdostal.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
Elabajaba
|
70a592f31a
|
Update to wgpu 0.18 (#10266)
# Objective Keep up to date with wgpu. ## Solution Update the wgpu version. Currently blocked on naga_oil updating to naga 0.14 and releasing a new version. 3d scenes (or maybe any scene with lighting?) currently don't render anything due to ``` error: naga_oil bug, please file a report: composer failed to build a valid header: Type [2] '' is invalid = Capability Capabilities(CUBE_ARRAY_TEXTURES) is required ``` I'm not sure what should be passed in for `wgpu::InstanceFlags`, or if we want to make the gles3minorversion configurable (might be useful for debugging?) Currently blocked on https://github.com/bevyengine/naga_oil/pull/63, and https://github.com/gfx-rs/wgpu/issues/4569 to be fixed upstream in wgpu first. ## Known issues Amd+windows+vulkan has issues with texture_binding_arrays (see the image [here](https://github.com/bevyengine/bevy/pull/10266#issuecomment-1819946278)), but that'll be fixed in the next wgpu/naga version, and you can just use dx12 as a workaround for now (Amd+linux mesa+vulkan texture_binding_arrays are fixed though). --- ## Changelog Updated wgpu to 0.18, naga to 0.14.2, and naga_oil to 0.11. - Windows desktop GL should now be less painful as it no longer requires Angle. - You can now toggle shader validation and debug information for debug and release builds using `WgpuSettings.instance_flags` and [InstanceFlags](https://docs.rs/wgpu/0.18.0/wgpu/struct.InstanceFlags.html) ## Migration Guide - `RenderPassDescriptor` `color_attachments` (as well as `RenderPassColorAttachment`, and `RenderPassDepthStencilAttachment`) now use `StoreOp::Store` or `StoreOp::Discard` instead of a `boolean` to declare whether or not they should be stored. - `RenderPassDescriptor` now have `timestamp_writes` and `occlusion_query_set` fields. These can safely be set to `None`. - `ComputePassDescriptor` now have a `timestamp_writes` field. This can be set to `None` for now. - See the [wgpu changelog](https://github.com/gfx-rs/wgpu/blob/trunk/CHANGELOG.md#v0180-2023-10-25) for additional details |
||
Mantas
|
5af2f022d8
|
Rename WorldQueryData & WorldQueryFilter to QueryData & QueryFilter (#10779)
# Rename `WorldQueryData` & `WorldQueryFilter` to `QueryData` & `QueryFilter` Fixes #10776 ## Solution Traits `WorldQueryData` & `WorldQueryFilter` were renamed to `QueryData` and `QueryFilter`, respectively. Related Trait types were also renamed. --- ## Changelog - Trait `WorldQueryData` has been renamed to `QueryData`. Derive macro's `QueryData` attribute `world_query_data` has been renamed to `query_data`. - Trait `WorldQueryFilter` has been renamed to `QueryFilter`. Derive macro's `QueryFilter` attribute `world_query_filter` has been renamed to `query_filter`. - Trait's `ExtractComponent` type `Query` has been renamed to `Data`. - Trait's `GetBatchData` types `Query` & `QueryFilter` has been renamed to `Data` & `Filter`, respectively. - Trait's `ExtractInstance` type `Query` has been renamed to `Data`. - Trait's `ViewNode` type `ViewQuery` has been renamed to `ViewData`. - Trait's `RenderCommand` types `ViewWorldQuery` & `ItemWorldQuery` has been renamed to `ViewData` & `ItemData`, respectively. ## Migration Guide Note: if merged before 0.13 is released, this should instead modify the migration guide of #10776 with the updated names. - Rename `WorldQueryData` & `WorldQueryFilter` trait usages to `QueryData` & `QueryFilter` and their respective derive macro attributes `world_query_data` & `world_query_filter` to `query_data` & `query_filter`. - Rename the following trait type usages: - Trait's `ExtractComponent` type `Query` to `Data`. - Trait's `GetBatchData` type `Query` to `Data`. - Trait's `ExtractInstance` type `Query` to `Data`. - Trait's `ViewNode` type `ViewQuery` to `ViewData`' - Trait's `RenderCommand` types `ViewWolrdQuery` & `ItemWorldQuery` to `ViewData` & `ItemData`, respectively. ```rust // Before #[derive(WorldQueryData)] #[world_query_data(derive(Debug))] struct EmptyQuery { empty: (), } // After #[derive(QueryData)] #[query_data(derive(Debug))] struct EmptyQuery { empty: (), } // Before #[derive(WorldQueryFilter)] struct CustomQueryFilter<T: Component, P: Component> { _c: With<ComponentC>, _d: With<ComponentD>, _or: Or<(Added<ComponentC>, Changed<ComponentD>, Without<ComponentZ>)>, _generic_tuple: (With<T>, With<P>), } // After #[derive(QueryFilter)] struct CustomQueryFilter<T: Component, P: Component> { _c: With<ComponentC>, _d: With<ComponentD>, _or: Or<(Added<ComponentC>, Changed<ComponentD>, Without<ComponentZ>)>, _generic_tuple: (With<T>, With<P>), } // Before impl ExtractComponent for ContrastAdaptiveSharpeningSettings { type Query = &'static Self; type Filter = With<Camera>; type Out = (DenoiseCAS, CASUniform); fn extract_component(item: QueryItem<Self::Query>) -> Option<Self::Out> { //... } } // After impl ExtractComponent for ContrastAdaptiveSharpeningSettings { type Data = &'static Self; type Filter = With<Camera>; type Out = (DenoiseCAS, CASUniform); fn extract_component(item: QueryItem<Self::Data>) -> Option<Self::Out> { //... } } // Before impl GetBatchData for MeshPipeline { type Param = SRes<RenderMeshInstances>; type Query = Entity; type QueryFilter = With<Mesh3d>; type CompareData = (MaterialBindGroupId, AssetId<Mesh>); type BufferData = MeshUniform; fn get_batch_data( mesh_instances: &SystemParamItem<Self::Param>, entity: &QueryItem<Self::Query>, ) -> (Self::BufferData, Option<Self::CompareData>) { // .... } } // After impl GetBatchData for MeshPipeline { type Param = SRes<RenderMeshInstances>; type Data = Entity; type Filter = With<Mesh3d>; type CompareData = (MaterialBindGroupId, AssetId<Mesh>); type BufferData = MeshUniform; fn get_batch_data( mesh_instances: &SystemParamItem<Self::Param>, entity: &QueryItem<Self::Data>, ) -> (Self::BufferData, Option<Self::CompareData>) { // .... } } // Before impl<A> ExtractInstance for AssetId<A> where A: Asset, { type Query = Read<Handle<A>>; type Filter = (); fn extract(item: QueryItem<'_, Self::Query>) -> Option<Self> { Some(item.id()) } } // After impl<A> ExtractInstance for AssetId<A> where A: Asset, { type Data = Read<Handle<A>>; type Filter = (); fn extract(item: QueryItem<'_, Self::Data>) -> Option<Self> { Some(item.id()) } } // Before impl ViewNode for PostProcessNode { type ViewQuery = ( &'static ViewTarget, &'static PostProcessSettings, ); fn run( &self, _graph: &mut RenderGraphContext, render_context: &mut RenderContext, (view_target, _post_process_settings): QueryItem<Self::ViewQuery>, world: &World, ) -> Result<(), NodeRunError> { // ... } } // After impl ViewNode for PostProcessNode { type ViewData = ( &'static ViewTarget, &'static PostProcessSettings, ); fn run( &self, _graph: &mut RenderGraphContext, render_context: &mut RenderContext, (view_target, _post_process_settings): QueryItem<Self::ViewData>, world: &World, ) -> Result<(), NodeRunError> { // ... } } // Before impl<P: CachedRenderPipelinePhaseItem> RenderCommand<P> for SetItemPipeline { type Param = SRes<PipelineCache>; type ViewWorldQuery = (); type ItemWorldQuery = (); #[inline] fn render<'w>( item: &P, _view: (), _entity: (), pipeline_cache: SystemParamItem<'w, '_, Self::Param>, pass: &mut TrackedRenderPass<'w>, ) -> RenderCommandResult { // ... } } // After impl<P: CachedRenderPipelinePhaseItem> RenderCommand<P> for SetItemPipeline { type Param = SRes<PipelineCache>; type ViewData = (); type ItemData = (); #[inline] fn render<'w>( item: &P, _view: (), _entity: (), pipeline_cache: SystemParamItem<'w, '_, Self::Param>, pass: &mut TrackedRenderPass<'w>, ) -> RenderCommandResult { // ... } } ``` |
||
Mateusz Wachowiak
|
1f97717a3d
|
Rename Input to ButtonInput (#10859)
# Objective - Resolves #10853 ## Solution - ~~Changed the name of `Input` struct to `PressableInput`.~~ - Changed the name of `Input` struct to `ButtonInput`. ## Migration Guide - Breaking Change: Users need to rename `Input` to `ButtonInput` in their projects. |
||
JMS55
|
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. |
||
Kanabenki
|
0e9f6e92ea
|
Add clippy::manual_let_else at warn level to lints (#10684)
# Objective Related to #10612. Enable the [`clippy::manual_let_else`](https://rust-lang.github.io/rust-clippy/master/#manual_let_else) lint as a warning. The `let else` form seems more idiomatic to me than a `match`/`if else` that either match a pattern or diverge, and from the clippy doc, the lint doesn't seem to have any possible false positive. ## Solution Add the lint as warning in `Cargo.toml`, refactor places where the lint triggers. |
||
IceSentry
|
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:
|
||
Torstein Grindvik
|
73bb310304
|
impl From<Color> for ClearColorConfig (#10734)
# Objective I tried setting `ClearColorConfig` in my app via `Color::FOO.into()` expecting it to work, but the impl was missing. ## Solution - Add `impl From<Color> for ClearColorConfig` - Change examples to use this impl ## Changelog ### Added - `ClearColorConfig` can be constructed via `.into()` on a `Color` --------- Signed-off-by: Torstein Grindvik <torstein.grindvik@muybridge.com> Co-authored-by: Torstein Grindvik <torstein.grindvik@muybridge.com> |
||
IceSentry
|
83a358bf33
|
Improve shader_material example (#10547)
# Objective - The current shader code is misleading since it makes it look like a struct is passed to the bind group 0 but in reality only the color is passed. They just happen to have the exact same memory layout so wgsl doesn't complain and it works. - The struct is defined after the `impl Material` block which is backwards from pretty much every other usage of the `impl` block in bevy. ## Solution - Remove the unnecessary struct in the shader - move the impl block |
||
BrayMatter
|
bad55c1ad8
|
Ensure ExtendedMaterial works with reflection (to enable bevy_egui_inspector integration) (#10548)
# Objective - Ensure ExtendedMaterial can be referenced in bevy_egui_inspector correctly ## Solution Add a more manual `TypePath` implementation to work around bugs in the derive macro. |
||
IceSentry
|
b1fd9eddbb
|
Fix post processing example to only run effect on camera with settings component (#10560)
# Objective - The example says it will only run on a camera with the `PostProcessingSettings` component but the node never filters it. ## Solution - Add the component to the `ViewQuery` closes: https://github.com/bevyengine/bevy/issues/10541 |
||
IceSentry
|
b1aa74d511
|
Add shader_material_2d example (#10542)
# Objective - 2d materials have subtle differences with 3d materials that aren't obvious to beginners ## Solution - Add an example that shows how to make a 2d material |
||
Aevyrie
|
1918608b02
|
Update default ClearColor to better match Bevy's branding (#10339)
# Objective - Changes the default clear color to match the code block color on Bevy's website. ## Solution - Changed the clear color, updated text in examples to ensure adequate contrast. Inconsistent usage of white text color set to use the default color instead, which is already white. - Additionally, updated the `3d_scene` example to make it look a bit better, and use bevy's branding colors. ![image](https://github.com/bevyengine/bevy/assets/2632925/540a22c0-826c-4c33-89aa-34905e3e313a) |
||
robtfm
|
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> |
||
robtfm
|
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> |
||
Carter Anderson
|
35073cf7aa
|
Multiple Asset Sources (#9885)
This adds support for **Multiple Asset Sources**. You can now register a named `AssetSource`, which you can load assets from like you normally would: ```rust let shader: Handle<Shader> = asset_server.load("custom_source://path/to/shader.wgsl"); ``` Notice that `AssetPath` now supports `some_source://` syntax. This can now be accessed through the `asset_path.source()` accessor. Asset source names _are not required_. If one is not specified, the default asset source will be used: ```rust let shader: Handle<Shader> = asset_server.load("path/to/shader.wgsl"); ``` The behavior of the default asset source has not changed. Ex: the `assets` folder is still the default. As referenced in #9714 ## Why? **Multiple Asset Sources** enables a number of often-asked-for scenarios: * **Loading some assets from other locations on disk**: you could create a `config` asset source that reads from the OS-default config folder (not implemented in this PR) * **Loading some assets from a remote server**: you could register a new `remote` asset source that reads some assets from a remote http server (not implemented in this PR) * **Improved "Binary Embedded" Assets**: we can use this system for "embedded-in-binary assets", which allows us to replace the old `load_internal_asset!` approach, which couldn't support asset processing, didn't support hot-reloading _well_, and didn't make embedded assets accessible to the `AssetServer` (implemented in this pr) ## Adding New Asset Sources An `AssetSource` is "just" a collection of `AssetReader`, `AssetWriter`, and `AssetWatcher` entries. You can configure new asset sources like this: ```rust app.register_asset_source( "other", AssetSource::build() .with_reader(|| Box::new(FileAssetReader::new("other"))) ) ) ``` Note that `AssetSource` construction _must_ be repeatable, which is why a closure is accepted. `AssetSourceBuilder` supports `with_reader`, `with_writer`, `with_watcher`, `with_processed_reader`, `with_processed_writer`, and `with_processed_watcher`. Note that the "asset source" system replaces the old "asset providers" system. ## Processing Multiple Sources The `AssetProcessor` now supports multiple asset sources! Processed assets can refer to assets in other sources and everything "just works". Each `AssetSource` defines an unprocessed and processed `AssetReader` / `AssetWriter`. Currently this is all or nothing for a given `AssetSource`. A given source is either processed or it is not. Later we might want to add support for "lazy asset processing", where an `AssetSource` (such as a remote server) can be configured to only process assets that are directly referenced by local assets (in order to save local disk space and avoid doing extra work). ## A new `AssetSource`: `embedded` One of the big features motivating **Multiple Asset Sources** was improving our "embedded-in-binary" asset loading. To prove out the **Multiple Asset Sources** implementation, I chose to build a new `embedded` `AssetSource`, which replaces the old `load_interal_asset!` system. The old `load_internal_asset!` approach had a number of issues: * The `AssetServer` was not aware of (or capable of loading) internal assets. * Because internal assets weren't visible to the `AssetServer`, they could not be processed (or used by assets that are processed). This would prevent things "preprocessing shaders that depend on built in Bevy shaders", which is something we desperately need to start doing. * Each "internal asset" needed a UUID to be defined in-code to reference it. This was very manual and toilsome. The new `embedded` `AssetSource` enables the following pattern: ```rust // Called in `crates/bevy_pbr/src/render/mesh.rs` embedded_asset!(app, "mesh.wgsl"); // later in the app let shader: Handle<Shader> = asset_server.load("embedded://bevy_pbr/render/mesh.wgsl"); ``` Notice that this always treats the crate name as the "root path", and it trims out the `src` path for brevity. This is generally predictable, but if you need to debug you can use the new `embedded_path!` macro to get a `PathBuf` that matches the one used by `embedded_asset`. You can also reference embedded assets in arbitrary assets, such as WGSL shaders: ```rust #import "embedded://bevy_pbr/render/mesh.wgsl" ``` This also makes `embedded` assets go through the "normal" asset lifecycle. They are only loaded when they are actually used! We are also discussing implicitly converting asset paths to/from shader modules, so in the future (not in this PR) you might be able to load it like this: ```rust #import bevy_pbr::render::mesh::Vertex ``` Compare that to the old system! ```rust pub const MESH_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(3252377289100772450); load_internal_asset!(app, MESH_SHADER_HANDLE, "mesh.wgsl", Shader::from_wgsl); // The mesh asset is the _only_ accessible via MESH_SHADER_HANDLE and _cannot_ be loaded via the AssetServer. ``` ## Hot Reloading `embedded` You can enable `embedded` hot reloading by enabling the `embedded_watcher` cargo feature: ``` cargo run --features=embedded_watcher ``` ## Improved Hot Reloading Workflow First: the `filesystem_watcher` cargo feature has been renamed to `file_watcher` for brevity (and to match the `FileAssetReader` naming convention). More importantly, hot asset reloading is no longer configured in-code by default. If you enable any asset watcher feature (such as `file_watcher` or `rust_source_watcher`), asset watching will be automatically enabled. This removes the need to _also_ enable hot reloading in your app code. That means you can replace this: ```rust app.add_plugins(DefaultPlugins.set(AssetPlugin::default().watch_for_changes())) ``` with this: ```rust app.add_plugins(DefaultPlugins) ``` If you want to hot reload assets in your app during development, just run your app like this: ``` cargo run --features=file_watcher ``` This means you can use the same code for development and deployment! To deploy an app, just don't include the watcher feature ``` cargo build --release ``` My intent is to move to this approach for pretty much all dev workflows. In a future PR I would like to replace `AssetMode::ProcessedDev` with a `runtime-processor` cargo feature. We could then group all common "dev" cargo features under a single `dev` feature: ```sh # this would enable file_watcher, embedded_watcher, runtime-processor, and more cargo run --features=dev ``` ## AssetMode `AssetPlugin::Unprocessed`, `AssetPlugin::Processed`, and `AssetPlugin::ProcessedDev` have been replaced with an `AssetMode` field on `AssetPlugin`. ```rust // before app.add_plugins(DefaultPlugins.set(AssetPlugin::Processed { /* fields here */ }) // after app.add_plugins(DefaultPlugins.set(AssetPlugin { mode: AssetMode::Processed, ..default() }) ``` This aligns `AssetPlugin` with our other struct-like plugins. The old "source" and "destination" `AssetProvider` fields in the enum variants have been replaced by the "asset source" system. You no longer need to configure the AssetPlugin to "point" to custom asset providers. ## AssetServerMode To improve the implementation of **Multiple Asset Sources**, `AssetServer` was made aware of whether or not it is using "processed" or "unprocessed" assets. You can check that like this: ```rust if asset_server.mode() == AssetServerMode::Processed { /* do something */ } ``` Note that this refactor should also prepare the way for building "one to many processed output files", as it makes the server aware of whether it is loading from processed or unprocessed sources. Meaning we can store and read processed and unprocessed assets differently! ## AssetPath can now refer to folders The "file only" restriction has been removed from `AssetPath`. The `AssetServer::load_folder` API now accepts an `AssetPath` instead of a `Path`, meaning you can load folders from other asset sources! ## Improved AssetPath Parsing AssetPath parsing was reworked to support sources, improve error messages, and to enable parsing with a single pass over the string. `AssetPath::new` was replaced by `AssetPath::parse` and `AssetPath::try_parse`. ## AssetWatcher broken out from AssetReader `AssetReader` is no longer responsible for constructing `AssetWatcher`. This has been moved to `AssetSourceBuilder`. ## Duplicate Event Debouncing Asset V2 already debounced duplicate filesystem events, but this was _input_ events. Multiple input event types can produce the same _output_ `AssetSourceEvent`. Now that we have `embedded_watcher`, which does expensive file io on events, it made sense to debounce output events too, so I added that! This will also benefit the AssetProcessor by preventing integrity checks for duplicate events (and helps keep the noise down in trace logs). ## Next Steps * **Port Built-in Shaders**: Currently the primary (and essentially only) user of `load_interal_asset` in Bevy's source code is "built-in shaders". I chose not to do that in this PR for a few reasons: 1. We need to add the ability to pass shader defs in to shaders via meta files. Some shaders (such as MESH_VIEW_TYPES) need to pass shader def values in that are defined in code. 2. We need to revisit the current shader module naming system. I think we _probably_ want to imply modules from source structure (at least by default). Ideally in a way that can losslessly convert asset paths to/from shader modules (to enable the asset system to resolve modules using the asset server). 3. I want to keep this change set minimal / get this merged first. * **Deprecate `load_internal_asset`**: we can't do that until we do (1) and (2) * **Relative Asset Paths**: This PR significantly increases the need for relative asset paths (which was already pretty high). Currently when loading dependencies, it is assumed to be an absolute path, which means if in an `AssetLoader` you call `context.load("some/path/image.png")` it will assume that is the "default" asset source, _even if the current asset is in a different asset source_. This will cause breakage for AssetLoaders that are not designed to add the current source to whatever paths are being used. AssetLoaders should generally not need to be aware of the name of their current asset source, or need to think about the "current asset source" generally. We should build apis that support relative asset paths and then encourage using relative paths as much as possible (both via api design and docs). Relative paths are also important because they will allow developers to move folders around (even across providers) without reprocessing, provided there is no path breakage. |
||
Marco Buono
|
12a2f83edd
|
Add consuming builder methods for more ergonomic Mesh creation (#10056)
# Objective - This PR aims to make creating meshes a little bit more ergonomic, specifically by removing the need for intermediate mutable variables. ## Solution - We add methods that consume the `Mesh` and return a mesh with the specified changes, so that meshes can be entirely constructed via builder-style calls, without intermediate variables; - Methods are flagged with `#[must_use]` to ensure proper use; - Examples are updated to use the new methods where applicable. Some examples are kept with the mutating methods so that users can still easily discover them, and also where the new methods wouldn't really be an improvement. ## Examples Before: ```rust let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, vs); mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, vns); mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, vts); mesh.set_indices(Some(Indices::U32(tris))); mesh ``` After: ```rust Mesh::new(PrimitiveTopology::TriangleList) .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vs) .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, vns) .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, vts) .with_indices(Some(Indices::U32(tris))) ``` Before: ```rust let mut cube = Mesh::from(shape::Cube { size: 1.0 }); cube.generate_tangents().unwrap(); PbrBundle { mesh: meshes.add(cube), ..default() } ``` After: ```rust PbrBundle { mesh: meshes.add( Mesh::from(shape::Cube { size: 1.0 }) .with_generated_tangents() .unwrap(), ), ..default() } ``` --- ## Changelog - Added consuming builder methods for more ergonomic `Mesh` creation: `with_inserted_attribute()`, `with_removed_attribute()`, `with_indices()`, `with_duplicated_vertices()`, `with_computed_flat_normals()`, `with_generated_tangents()`, `with_morph_targets()`, `with_morph_target_names()`. |
||
Ame :]
|
7541bf862c
|
Fix some warnings shown in nightly (#10012)
# Objective Fix warnings: - #[warn(clippy::needless_pass_by_ref_mut)] - #[warn(elided_lifetimes_in_associated_constant)] ## Solution - Remove mut - add &'static ## Errors ```rust warning: this argument is a mutable reference, but not used mutably --> crates/bevy_hierarchy/src/child_builder.rs:672:31 | 672 | fn assert_children(world: &mut World, parent: Entity, children: Option<&[Entity]>) { | ^^^^^^^^^^ help: consider changing to: `&World` | = note: this is cfg-gated and may require further changes = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut = note: `#[warn(clippy::needless_pass_by_ref_mut)]` on by default ``` ```rust warning: `&` without an explicit lifetime name cannot be used here --> examples/shader/post_processing.rs:120:21 | 120 | pub const NAME: &str = "post_process"; | ^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #115010 <https://github.com/rust-lang/rust/issues/115010> = note: `#[warn(elided_lifetimes_in_associated_constant)]` on by default help: use the `'static` lifetime | 120 | pub const NAME: &'static str = "post_process"; | +++++++ ``` |
||
Robert Swain
|
b6ead2be95
|
Use EntityHashMap<Entity, T> for render world entity storage for better performance (#9903)
# Objective - Improve rendering performance, particularly by avoiding the large system commands costs of using the ECS in the way that the render world does. ## Solution - Define `EntityHasher` that calculates a hash from the `Entity.to_bits()` by `i | (i.wrapping_mul(0x517cc1b727220a95) << 32)`. `0x517cc1b727220a95` is something like `u64::MAX / N` for N that gives a value close to π and that works well for hashing. Thanks for @SkiFire13 for the suggestion and to @nicopap for alternative suggestions and discussion. This approach comes from `rustc-hash` (a.k.a. `FxHasher`) with some tweaks for the case of hashing an `Entity`. `FxHasher` and `SeaHasher` were also tested but were significantly slower. - Define `EntityHashMap` type that uses the `EntityHashser` - Use `EntityHashMap<Entity, T>` for render world entity storage, including: - `RenderMaterialInstances` - contains the `AssetId<M>` of the material associated with the entity. Also for 2D. - `RenderMeshInstances` - contains mesh transforms, flags and properties about mesh entities. Also for 2D. - `SkinIndices` and `MorphIndices` - contains the skin and morph index for an entity, respectively - `ExtractedSprites` - `ExtractedUiNodes` ## Benchmarks All benchmarks have been conducted on an M1 Max connected to AC power. The tests are run for 1500 frames. The 1000th frame is captured for comparison to check for visual regressions. There were none. ### 2D Meshes `bevymark --benchmark --waves 160 --per-wave 1000 --mode mesh2d` #### `--ordered-z` This test spawns the 2D meshes with z incrementing back to front, which is the ideal arrangement allocation order as it matches the sorted render order which means lookups have a high cache hit rate. <img width="1112" alt="Screenshot 2023-09-27 at 07 50 45" src="https://github.com/bevyengine/bevy/assets/302146/e140bc98-7091-4a3b-8ae1-ab75d16d2ccb"> -39.1% median frame time. #### Random This test spawns the 2D meshes with random z. This not only makes the batching and transparent 2D pass lookups get a lot of cache misses, it also currently means that the meshes are almost certain to not be batchable. <img width="1108" alt="Screenshot 2023-09-27 at 07 51 28" src="https://github.com/bevyengine/bevy/assets/302146/29c2e813-645a-43ce-982a-55df4bf7d8c4"> -7.2% median frame time. ### 3D Meshes `many_cubes --benchmark` <img width="1112" alt="Screenshot 2023-09-27 at 07 51 57" src="https://github.com/bevyengine/bevy/assets/302146/1a729673-3254-4e2a-9072-55e27c69f0fc"> -7.7% median frame time. ### Sprites **NOTE: On `main` sprites are using `SparseSet<Entity, T>`!** `bevymark --benchmark --waves 160 --per-wave 1000 --mode sprite` #### `--ordered-z` This test spawns the sprites with z incrementing back to front, which is the ideal arrangement allocation order as it matches the sorted render order which means lookups have a high cache hit rate. <img width="1116" alt="Screenshot 2023-09-27 at 07 52 31" src="https://github.com/bevyengine/bevy/assets/302146/bc8eab90-e375-4d31-b5cd-f55f6f59ab67"> +13.0% median frame time. #### Random This test spawns the sprites with random z. This makes the batching and transparent 2D pass lookups get a lot of cache misses. <img width="1109" alt="Screenshot 2023-09-27 at 07 53 01" src="https://github.com/bevyengine/bevy/assets/302146/22073f5d-99a7-49b0-9584-d3ac3eac3033"> +0.6% median frame time. ### UI **NOTE: On `main` UI is using `SparseSet<Entity, T>`!** `many_buttons` <img width="1111" alt="Screenshot 2023-09-27 at 07 53 26" src="https://github.com/bevyengine/bevy/assets/302146/66afd56d-cbe4-49e7-8b64-2f28f6043d85"> +15.1% median frame time. ## Alternatives - Cart originally suggested trying out `SparseSet<Entity, T>` and indeed that is slightly faster under ideal conditions. However, `PassHashMap<Entity, T>` has better worst case performance when data is randomly distributed, rather than in sorted render order, and does not have the worst case memory usage that `SparseSet`'s dense `Vec<usize>` that maps from the `Entity` index to sparse index into `Vec<T>`. This dense `Vec` has to be as large as the largest Entity index used with the `SparseSet`. - I also tested `PassHashMap<u32, T>`, intending to use `Entity.index()` as the key, but this proved to sometimes be slower and mostly no different. - The only outstanding approach that has not been implemented and tested is to _not_ clear the render world of its entities each frame. That has its own problems, though they could perhaps be solved. - Performance-wise, if the entities and their component data were not cleared, then they would incur table moves on spawn, and should not thereafter, rather just their component data would be overwritten. Ideally we would have a neat way of either updating data in-place via `&mut T` queries, or inserting components if not present. This would likely be quite cumbersome to have to remember to do everywhere, but perhaps it only needs to be done in the more performance-sensitive systems. - The main problem to solve however is that we want to both maintain a mapping between main world entities and render world entities, be able to run the render app and world in parallel with the main app and world for pipelined rendering, and at the same time be able to spawn entities in the render world in such a way that those Entity ids do not collide with those spawned in the main world. This is potentially quite solvable, but could well be a lot of ECS work to do it in a way that makes sense. --- ## Changelog - Changed: Component data for entities to be drawn are no longer stored on entities in the render world. Instead, data is stored in a `EntityHashMap<Entity, T>` in various resources. This brings significant performance benefits due to the way the render app clears entities every frame. Resources of most interest are `RenderMeshInstances` and `RenderMaterialInstances`, and their 2D counterparts. ## Migration Guide Previously the render app extracted mesh entities and their component data from the main world and stored them as entities and components in the render world. Now they are extracted into essentially `EntityHashMap<Entity, T>` where `T` are structs containing an appropriate group of data. This means that while extract set systems will continue to run extract queries against the main world they will store their data in hash maps. Also, systems in later sets will either need to look up entities in the available resources such as `RenderMeshInstances`, or maintain their own `EntityHashMap<Entity, T>` for their own data. Before: ```rust fn queue_custom( material_meshes: Query<(Entity, &MeshTransforms, &Handle<Mesh>), With<InstanceMaterialData>>, ) { ... for (entity, mesh_transforms, mesh_handle) in &material_meshes { ... } } ``` After: ```rust fn queue_custom( render_mesh_instances: Res<RenderMeshInstances>, instance_entities: Query<Entity, With<InstanceMaterialData>>, ) { ... for entity in &instance_entities { let Some(mesh_instance) = render_mesh_instances.get(&entity) else { continue; }; // The mesh handle in `AssetId<Mesh>` form, and the `MeshTransforms` can now // be found in `mesh_instance` which is a `RenderMeshInstance` ... } } ``` --------- Co-authored-by: robtfm <50659922+robtfm@users.noreply.github.com> |
||
Robert Swain
|
5c884c5a15
|
Automatic batching/instancing of draw commands (#9685)
# Objective - Implement the foundations of automatic batching/instancing of draw commands as the next step from #89 - NOTE: More performance improvements will come when more data is managed and bound in ways that do not require rebinding such as mesh, material, and texture data. ## Solution - The core idea for batching of draw commands is to check whether any of the information that has to be passed when encoding a draw command changes between two things that are being drawn according to the sorted render phase order. These should be things like the pipeline, bind groups and their dynamic offsets, index/vertex buffers, and so on. - The following assumptions have been made: - Only entities with prepared assets (pipelines, materials, meshes) are queued to phases - View bindings are constant across a phase for a given draw function as phases are per-view - `batch_and_prepare_render_phase` is the only system that performs this batching and has sole responsibility for preparing the per-object data. As such the mesh binding and dynamic offsets are assumed to only vary as a result of the `batch_and_prepare_render_phase` system, e.g. due to having to split data across separate uniform bindings within the same buffer due to the maximum uniform buffer binding size. - Implement `GpuArrayBuffer` for `Mesh2dUniform` to store Mesh2dUniform in arrays in GPU buffers rather than each one being at a dynamic offset in a uniform buffer. This is the same optimisation that was made for 3D not long ago. - Change batch size for a range in `PhaseItem`, adding API for getting or mutating the range. This is more flexible than a size as the length of the range can be used in place of the size, but the start and end can be otherwise whatever is needed. - Add an optional mesh bind group dynamic offset to `PhaseItem`. This avoids having to do a massive table move just to insert `GpuArrayBufferIndex` components. ## Benchmarks All tests have been run on an M1 Max on AC power. `bevymark` and `many_cubes` were modified to use 1920x1080 with a scale factor of 1. I run a script that runs a separate Tracy capture process, and then runs the bevy example with `--features bevy_ci_testing,trace_tracy` and `CI_TESTING_CONFIG=../benchmark.ron` with the contents of `../benchmark.ron`: ```rust ( exit_after: Some(1500) ) ``` ...in order to run each test for 1500 frames. The recent changes to `many_cubes` and `bevymark` added reproducible random number generation so that with the same settings, the same rng will occur. They also added benchmark modes that use a fixed delta time for animations. Combined this means that the same frames should be rendered both on main and on the branch. The graphs compare main (yellow) to this PR (red). ### 3D Mesh `many_cubes --benchmark` <img width="1411" alt="Screenshot 2023-09-03 at 23 42 10" src="https://github.com/bevyengine/bevy/assets/302146/2088716a-c918-486c-8129-090b26fd2bc4"> The mesh and material are the same for all instances. This is basically the best case for the initial batching implementation as it results in 1 draw for the ~11.7k visible meshes. It gives a ~30% reduction in median frame time. The 1000th frame is identical using the flip tool: ![flip many_cubes-main-mesh3d many_cubes-batching-mesh3d 67ppd ldr](https://github.com/bevyengine/bevy/assets/302146/2511f37a-6df8-481a-932f-706ca4de7643) ``` Mean: 0.000000 Weighted median: 0.000000 1st weighted quartile: 0.000000 3rd weighted quartile: 0.000000 Min: 0.000000 Max: 0.000000 Evaluation time: 0.4615 seconds ``` ### 3D Mesh `many_cubes --benchmark --material-texture-count 10` <img width="1404" alt="Screenshot 2023-09-03 at 23 45 18" src="https://github.com/bevyengine/bevy/assets/302146/5ee9c447-5bd2-45c6-9706-ac5ff8916daf"> This run uses 10 different materials by varying their textures. The materials are randomly selected, and there is no sorting by material bind group for opaque 3D so any batching is 'random'. The PR produces a ~5% reduction in median frame time. If we were to sort the opaque phase by the material bind group, then this should be a lot faster. This produces about 10.5k draws for the 11.7k visible entities. This makes sense as randomly selecting from 10 materials gives a chance that two adjacent entities randomly select the same material and can be batched. The 1000th frame is identical in flip: ![flip many_cubes-main-mesh3d-mtc10 many_cubes-batching-mesh3d-mtc10 67ppd ldr](https://github.com/bevyengine/bevy/assets/302146/2b3a8614-9466-4ed8-b50c-d4aa71615dbb) ``` Mean: 0.000000 Weighted median: 0.000000 1st weighted quartile: 0.000000 3rd weighted quartile: 0.000000 Min: 0.000000 Max: 0.000000 Evaluation time: 0.4537 seconds ``` ### 3D Mesh `many_cubes --benchmark --vary-per-instance` <img width="1394" alt="Screenshot 2023-09-03 at 23 48 44" src="https://github.com/bevyengine/bevy/assets/302146/f02a816b-a444-4c18-a96a-63b5436f3b7f"> This run varies the material data per instance by randomly-generating its colour. This is the worst case for batching and that it performs about the same as `main` is a good thing as it demonstrates that the batching has minimal overhead when dealing with ~11k visible mesh entities. The 1000th frame is identical according to flip: ![flip many_cubes-main-mesh3d-vpi many_cubes-batching-mesh3d-vpi 67ppd ldr](https://github.com/bevyengine/bevy/assets/302146/ac5f5c14-9bda-4d1a-8219-7577d4aac68c) ``` Mean: 0.000000 Weighted median: 0.000000 1st weighted quartile: 0.000000 3rd weighted quartile: 0.000000 Min: 0.000000 Max: 0.000000 Evaluation time: 0.4568 seconds ``` ### 2D Mesh `bevymark --benchmark --waves 160 --per-wave 1000 --mode mesh2d` <img width="1412" alt="Screenshot 2023-09-03 at 23 59 56" src="https://github.com/bevyengine/bevy/assets/302146/cb02ae07-237b-4646-ae9f-fda4dafcbad4"> This spawns 160 waves of 1000 quad meshes that are shaded with ColorMaterial. Each wave has a different material so 160 waves currently should result in 160 batches. This results in a 50% reduction in median frame time. Capturing a screenshot of the 1000th frame main vs PR gives: ![flip bevymark-main-mesh2d bevymark-batching-mesh2d 67ppd ldr](https://github.com/bevyengine/bevy/assets/302146/80102728-1217-4059-87af-14d05044df40) ``` Mean: 0.001222 Weighted median: 0.750432 1st weighted quartile: 0.453494 3rd weighted quartile: 0.969758 Min: 0.000000 Max: 0.990296 Evaluation time: 0.4255 seconds ``` So they seem to produce the same results. I also double-checked the number of draws. `main` does 160000 draws, and the PR does 160, as expected. ### 2D Mesh `bevymark --benchmark --waves 160 --per-wave 1000 --mode mesh2d --material-texture-count 10` <img width="1392" alt="Screenshot 2023-09-04 at 00 09 22" src="https://github.com/bevyengine/bevy/assets/302146/4358da2e-ce32-4134-82df-3ab74c40849c"> This generates 10 textures and generates materials for each of those and then selects one material per wave. The median frame time is reduced by 50%. Similar to the plain run above, this produces 160 draws on the PR and 160000 on `main` and the 1000th frame is identical (ignoring the fps counter text overlay). ![flip bevymark-main-mesh2d-mtc10 bevymark-batching-mesh2d-mtc10 67ppd ldr](https://github.com/bevyengine/bevy/assets/302146/ebed2822-dce7-426a-858b-b77dc45b986f) ``` Mean: 0.002877 Weighted median: 0.964980 1st weighted quartile: 0.668871 3rd weighted quartile: 0.982749 Min: 0.000000 Max: 0.992377 Evaluation time: 0.4301 seconds ``` ### 2D Mesh `bevymark --benchmark --waves 160 --per-wave 1000 --mode mesh2d --vary-per-instance` <img width="1396" alt="Screenshot 2023-09-04 at 00 13 53" src="https://github.com/bevyengine/bevy/assets/302146/b2198b18-3439-47ad-919a-cdabe190facb"> This creates unique materials per instance by randomly-generating the material's colour. This is the worst case for 2D batching. Somehow, this PR manages a 7% reduction in median frame time. Both main and this PR issue 160000 draws. The 1000th frame is the same: ![flip bevymark-main-mesh2d-vpi bevymark-batching-mesh2d-vpi 67ppd ldr](https://github.com/bevyengine/bevy/assets/302146/a2ec471c-f576-4a36-a23b-b24b22578b97) ``` Mean: 0.001214 Weighted median: 0.937499 1st weighted quartile: 0.635467 3rd weighted quartile: 0.979085 Min: 0.000000 Max: 0.988971 Evaluation time: 0.4462 seconds ``` ### 2D Sprite `bevymark --benchmark --waves 160 --per-wave 1000 --mode sprite` <img width="1396" alt="Screenshot 2023-09-04 at 12 21 12" src="https://github.com/bevyengine/bevy/assets/302146/8b31e915-d6be-4cac-abf5-c6a4da9c3d43"> This just spawns 160 waves of 1000 sprites. There should be and is no notable difference between main and the PR. ### 2D Sprite `bevymark --benchmark --waves 160 --per-wave 1000 --mode sprite --material-texture-count 10` <img width="1389" alt="Screenshot 2023-09-04 at 12 36 08" src="https://github.com/bevyengine/bevy/assets/302146/45fe8d6d-c901-4062-a349-3693dd044413"> This spawns the sprites selecting a texture at random per instance from the 10 generated textures. This has no significant change vs main and shouldn't. ### 2D Sprite `bevymark --benchmark --waves 160 --per-wave 1000 --mode sprite --vary-per-instance` <img width="1401" alt="Screenshot 2023-09-04 at 12 29 52" src="https://github.com/bevyengine/bevy/assets/302146/762c5c60-352e-471f-8dbe-bbf10e24ebd6"> This sets the sprite colour as being unique per instance. This can still all be drawn using one batch. There should be no difference but the PR produces median frame times that are 4% higher. Investigation showed no clear sources of cost, rather a mix of give and take that should not happen. It seems like noise in the results. ### Summary | Benchmark | % change in median frame time | | ------------- | ------------- | | many_cubes | 🟩 -30% | | many_cubes 10 materials | 🟩 -5% | | many_cubes unique materials | 🟩 ~0% | | bevymark mesh2d | 🟩 -50% | | bevymark mesh2d 10 materials | 🟩 -50% | | bevymark mesh2d unique materials | 🟩 -7% | | bevymark sprite | 🟥 2% | | bevymark sprite 10 materials | 🟥 0.6% | | bevymark sprite unique materials | 🟥 4.1% | --- ## Changelog - Added: 2D and 3D mesh entities that share the same mesh and material (same textures, same data) are now batched into the same draw command for better performance. --------- Co-authored-by: robtfm <50659922+robtfm@users.noreply.github.com> Co-authored-by: Nicola Papale <nico@nicopap.ch> |
||
Carter Anderson
|
17edf4f7c7
|
Copy on Write AssetPaths (#9729)
# Objective The `AssetServer` and `AssetProcessor` do a lot of `AssetPath` cloning (across many threads). To store the path on the handle, to store paths in dependency lists, to pass an owned path to the offloaded thread, to pass a path to the LoadContext, etc , etc. Cloning multiple string allocations multiple times like this will add up. It is worth optimizing this. Referenced in #9714 ## Solution Added a new `CowArc<T>` type to `bevy_util`, which behaves a lot like `Cow<T>`, but the Owned variant is an `Arc<T>`. Use this in place of `Cow<str>` and `Cow<Path>` on `AssetPath`. --- ## Changelog - `AssetPath` now internally uses `CowArc`, making clone operations much cheaper - `AssetPath` now serializes as `AssetPath("some_path.extension#Label")` instead of as `AssetPath { path: "some_path.extension", label: Some("Label) }` ## Migration Guide ```rust // Old AssetPath::new("logo.png", None); // New AssetPath::new("logo.png"); // Old AssetPath::new("scene.gltf", Some("Mesh0"); // New AssetPath::new("scene.gltf").with_label("Mesh0"); ``` `AssetPath` now serializes as `AssetPath("some_path.extension#Label")` instead of as `AssetPath { path: "some_path.extension", label: Some("Label) }` --------- Co-authored-by: Pascal Hertleif <killercup@gmail.com> |